diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-baltos.dtsi b/arch/arm/boot/dts/am335x-baltos.dtsi --- a/arch/arm/boot/dts/am335x-baltos.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-baltos.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -168,7 +168,7 @@ AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE0) /* gpmc_ad6.gpmc_ad6 */ AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE0) /* gpmc_ad7.gpmc_ad7 */ AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE0) /* gpmc_wait0.gpmc_wait0 */ - AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) /* gpmc_wpn.gpio0_30 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) /* gpmc_wpn.gpio0_31 */ AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT, MUX_MODE0) /* gpmc_csn0.gpmc_csn0 */ AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE, PIN_OUTPUT, MUX_MODE0) /* gpmc_advn_ale.gpmc_advn_ale */ AM33XX_PADCONF(AM335X_PIN_GPMC_OEN_REN, PIN_OUTPUT, MUX_MODE0) /* gpmc_oen_ren.gpmc_oen_ren */ diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi b/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi --- a/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * Copyright (C) 2021 Robert Nelson + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#include +#include + +// For dummy refrence when peripheral is not available. +&{/} { + not_available: not_available { + // Use ¬_available when required. + // This node is responsible to create these entries, + // /sys/firmware/devicetree/base/__symbols__/not_available + // /sys/firmware/devicetree/base/not_available + }; +}; + +// For compatible bone pinmuxing +bone_pinmux: &am33xx_pinmux { + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN1, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN2, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD0, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD1, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD2, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD4, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD5, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; + + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +// ADC +bone_adc: &tscadc { + +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-common.dtsi b/arch/arm/boot/dts/am335x-boneblack-common.dtsi --- a/arch/arm/boot/dts/am335x-boneblack-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -3,9 +3,6 @@ * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ */ -#include -#include - &ldo3_reg { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -25,145 +22,13 @@ non-removable; }; -&am33xx_pinmux { - nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - >; - }; - - nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - >; - }; - - mcasp0_pins: mcasp0_pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ - AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ - >; - }; -}; - -&lcdc { - status = "okay"; - - /* If you want to get 24 bit RGB and 16 BGR mode instead of - * current 16 bit RGB and 24 BGR modes, set the propety - * below to "crossed" and uncomment the video-ports -property - * in tda19988 node. - */ - blue-and-red-wiring = "straight"; - - port { - lcdc_0: endpoint@0 { - remote-endpoint = <&hdmi_0>; - }; - }; -}; - -&i2c0 { - tda19988: tda19988@70 { - compatible = "nxp,tda998x"; - reg = <0x70>; - nxp,calib-gpios = <&gpio1 25 0>; - interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-names = "default", "off"; - pinctrl-0 = <&nxp_hdmi_bonelt_pins>; - pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; - - /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ - /* video-ports = <0x234501>; */ - - #sound-dai-cells = <0>; - audio-ports = < TDA998x_I2S 0x03>; - - ports { - port@0 { - hdmi_0: endpoint@0 { - remote-endpoint = <&lcdc_0>; - }; - }; - }; - }; -}; - &rtc { system-power-controller; }; -&mcasp0 { - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&mcasp0_pins>; - status = "okay"; - op-mode = <0>; /* MCASP_IIS_MODE */ - tdm-slots = <2>; - serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ - 0 0 1 0 - >; - tx-num-evt = <32>; - rx-num-evt = <32>; -}; - / { memory@80000000 { device_type = "memory"; reg = <0x80000000 0x20000000>; /* 512 MB */ }; - - clk_mcasp0_fixed: clk_mcasp0_fixed { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <24576000>; - }; - - clk_mcasp0: clk_mcasp0 { - #clock-cells = <0>; - compatible = "gpio-gate-clock"; - clocks = <&clk_mcasp0_fixed>; - enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "TI BeagleBone Black"; - simple-audio-card,format = "i2s"; - simple-audio-card,bitclock-master = <&dailink0_master>; - simple-audio-card,frame-master = <&dailink0_master>; - - dailink0_master: simple-audio-card,cpu { - sound-dai = <&mcasp0>; - clocks = <&clk_mcasp0>; - }; - - simple-audio-card,codec { - sound-dai = <&tda19988>; - }; - }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts --- a/arch/arm/boot/dts/am335x-boneblack.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack.dts 2022-01-06 12:45:53.806318073 -0500 @@ -7,10 +7,16 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" #include "am335x-boneblack-common.dtsi" +#include "am335x-boneblack-hdmi.dtsi" / { model = "TI AM335x BeagleBone Black"; compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-boneblack.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &cpu0_opp_table { @@ -26,54 +32,54 @@ &gpio0 { gpio-line-names = - "[ethernet]", - "[ethernet]", + "[mdio_data]", + "[mdio_clk]", "P9_22 [spi0_sclk]", "P9_21 [spi0_d0]", "P9_18 [spi0_d1]", "P9_17 [spi0_cs0]", - "[sd card]", - "P9_42A [ecappwm0]", - "P8_35 [hdmi]", - "P8_33 [hdmi]", - "P8_31 [hdmi]", - "P8_32 [hdmi]", + "[mmc0_cd]", + "P8_42A [ecappwm0]", + "P8_35 [lcd d12]", + "P8_33 [lcd d13]", + "P8_31 [lcd d14]", + "P8_32 [lcd d15]", "P9_20 [i2c2_sda]", "P9_19 [i2c2_scl]", "P9_26 [uart1_rxd]", "P9_24 [uart1_txd]", - "[ethernet]", - "[ethernet]", - "[usb]", - "[hdmi]", + "[rmii1_txd3]", + "[rmii1_txd2]", + "[usb0_drvvbus]", + "[hdmi cec]", "P9_41B", - "[ethernet]", + "[rmii1_txd1]", "P8_19 [ehrpwm2a]", "P8_13 [ehrpwm2b]", - "[NC]", - "[NC]", + "NC", + "NC", "P8_14", "P8_17", - "[ethernet]", - "[ethernet]", + "[rmii1_txd0]", + "[rmii1_refclk]", "P9_11 [uart4_rxd]", "P9_13 [uart4_txd]"; }; &gpio1 { gpio-line-names = - "P8_25 [emmc]", - "[emmc]", - "P8_5 [emmc]", - "P8_6 [emmc]", - "P8_23 [emmc]", - "P8_22 [emmc]", - "P8_3 [emmc]", - "P8_4 [emmc]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "P8_25 [mmc1_dat0]", + "[mmc1_dat1]", + "P8_5 [mmc1_dat2]", + "P8_6 [mmc1_dat3]", + "P8_23 [mmc1_dat4]", + "P8_22 [mmc1_dat5]", + "P8_3 [mmc1_dat6]", + "P8_4 [mmc1_dat7]", + "NC", + "NC", + "NC", + "NC", "P8_12", "P8_11", "P8_16", @@ -82,13 +88,13 @@ "P9_23", "P9_14 [ehrpwm1a]", "P9_16 [ehrpwm1b]", - "[emmc]", + "[emmc rst]", "[usr0 led]", "[usr1 led]", "[usr2 led]", "[usr3 led]", - "[hdmi]", - "[usb]", + "[hdmi irq]", + "[usb vbus oc]", "[hdmi audio]", "P9_12", "P8_26", @@ -116,38 +122,38 @@ "P8_38 [hdmi]", "P8_36 [hdmi]", "P8_34 [hdmi]", - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[ethernet]", + "[rmii1_rxd3]", + "[rmii1_rxd2]", + "[rmii1_rxd1]", + "[rmii1_rxd0]", "P8_27 [hdmi]", "P8_29 [hdmi]", "P8_28 [hdmi]", "P8_30 [hdmi]", - "[emmc]", - "[emmc]", - "[emmc]", - "[emmc]", - "[emmc]", - "[emmc]"; + "[mmc0_dat3]", + "[mmc0_dat2]", + "[mmc0_dat1]", + "[mmc0_dat0]", + "[mmc0_clk]", + "[mmc0_cmd]"; }; &gpio3 { gpio-line-names = - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[i2c0]", - "[i2c0]", - "[emu]", - "[emu]", - "[ethernet]", - "[ethernet]", - "[NC]", - "[NC]", - "[usb]", + "[mii col]", + "[mii crs]", + "[mii rx err]", + "[mii tx en]", + "[mii rx dv]", + "[i2c0 sda]", + "[i2c0 scl]", + "[jtag emu0]", + "[jtag emu1]", + "[mii tx clk]", + "[mii rx clk]", + "NC", + "NC", + "[usb vbus en]", "P9_31 [spi1_sclk]", "P9_29 [spi1_d0]", "P9_30 [spi1_d1]", @@ -156,14 +162,14 @@ "P9_27", "P9_41A", "P9_25", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]"; + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi b/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi --- a/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +&lcdc { + status = "okay"; + + /* If you want to get 24 bit RGB and 16 BGR mode instead of + * current 16 bit RGB and 24 BGR modes, set the propety + * below to "crossed" and uncomment the video-ports -property + * in tda19988 node. + */ + blue-and-red-wiring = "straight"; + + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + tda19988: tda19988@70 { + compatible = "nxp,tda998x"; + reg = <0x70>; + nxp,calib-gpios = <&gpio1 25 0>; + interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ + /* video-ports = <0x234501>; */ + + #sound-dai-cells = <0>; + audio-ports = < TDA998x_I2S 0x03>; + + ports { + port@0 { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; + }; +}; + +&mcasp0 { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + status = "okay"; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 1 0 + >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; + +/ { + clk_mcasp0_fixed: clk_mcasp0_fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk_mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "TI BeagleBone Black"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&dailink0_master>; + simple-audio-card,frame-master = <&dailink0_master>; + + dailink0_master: simple-audio-card,cpu { + sound-dai = <&mcasp0>; + clocks = <&clk_mcasp0>; + }; + + simple-audio-card,codec { + sound-dai = <&tda19988>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-pps.dts b/arch/arm/boot/dts/am335x-boneblack-pps.dts --- a/arch/arm/boot/dts/am335x-boneblack-pps.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-pps.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include "am335x-boneblack.dts" + +&am33xx_pinmux { + pwm7_pins: pinmux_pwm7_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_OEN_REN, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + >; + }; +}; + +/{ + pwm7: dmtimer-pwm7 { + compatible = "ti,omap-dmtimer-pwm"; + #pwm-cells = <3>; + ti,timers = <&timer7>; + ti,clock-source = <0x00>; /* timer_sys_ck */ + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-pruswuart.dts b/arch/arm/boot/dts/am335x-boneblack-pruswuart.dts --- a/arch/arm/boot/dts/am335x-boneblack-pruswuart.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-pruswuart.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include "am335x-boneblack.dts" + +&am33xx_pinmux { + pru_uart0_bone_pins: pru-uart0-bone-pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLUP | MUX_MODE5) /* mcasp0_aclkx.pr1_pru0_pru_r30_0, port0 TXD, P9.31 */ + AM33XX_IOPAD(0x994, PIN_INPUT_PULLUP | MUX_MODE6) /* mcasp0_fsx.pr1_pru0_pru_r31_1, port0 RXD, P9.29 */ + + AM33XX_IOPAD(0x998, PIN_OUTPUT_PULLUP | MUX_MODE5) /* mcasp0_axr0.pr1_pru0_pru_r30_2, port1 TXD, P9.30 */ + AM33XX_IOPAD(0x99c, PIN_INPUT_PULLUP | MUX_MODE6) /* mcasp0_ahclkr.pr1_pru0_pru_r31_3, port1 RXD, P9.28 */ + + AM33XX_IOPAD(0x9a4, PIN_OUTPUT_PULLUP | MUX_MODE5) /* mcasp0_fsr.pr1_pru0_pru_r30_5, port2 TXD, P9.27 */ + AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE6) /* mcasp0_ahclkx.pr1_pru0_pru_r31_7, port2 RXD, P9.25 */ + >; + }; + pru_uart1_bone_pins: pru-uart1-bone-pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a0, PIN_OUTPUT_PULLUP | MUX_MODE5) /* lcd_data0.pru_pru1_pru_r30_0, port0 TXD, P8.45 */ + AM33XX_IOPAD(0x8a4, PIN_INPUT_PULLUP | MUX_MODE6) /* lcd_data1.pru_pru1_pru_r31_1, port0 RXD, P8.46 */ + AM33XX_IOPAD(0x8a8, PIN_INPUT_PULLDOWN | MUX_MODE6) /* lcd_data2.pru_pru1_pru_r30_2, port0 CTS, P8.43 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT_PULLDOWN | MUX_MODE5) /* lcd_data3.pru_pru1_pru_r31_3, port0 RTS, P8.44 */ + + AM33XX_IOPAD(0x8b0, PIN_OUTPUT_PULLUP | MUX_MODE5) /* lcd_data4.pru_pru1_pru_r30_4, port1 TXD, P8.41 */ + AM33XX_IOPAD(0x8b4, PIN_INPUT_PULLUP | MUX_MODE6) /* lcd_data5.pru_pru1_pru_r31_5, port1 RXD, P8.42 */ + AM33XX_IOPAD(0x8b8, PIN_INPUT_PULLDOWN | MUX_MODE6) /* lcd_data6.pru_pru1_pru_r30_6, port1 CTS, P8.39 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT_PULLDOWN | MUX_MODE5) /* lcd_data7.pru_pru1_pru_r31_7, port1 RTS, P8.40 */ + + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLUP | MUX_MODE5) /* lcd_vsync.pru_pru1_pru_r30_8, port2 TXD, P8.27 */ + AM33XX_IOPAD(0x8e4, PIN_INPUT_PULLUP | MUX_MODE6) /* lcd_hsync.pru_pru1_pru_r31_9, port2 RXD, P8.29 */ + AM33XX_IOPAD(0x8e8, PIN_INPUT_PULLDOWN | MUX_MODE6) /* lcd_pclk.pru_pru1_pru_r30_10, port2 CTS, P8.28 */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE5) /* lcd_ac_bias_en.pru_pru1_pru_r31_11, port2 RTS, P8.30 */ + >; + }; +}; + +/{ + pru-swuart0 { + compatible = "ti,pru-swuart"; + interrupt-parent = <&pruss_intc>; + ti,prus = <&pru0>; + firmware-name = "ti-pruss/pru_swuart-fw.elf"; + pinctrl-0 = <&pru_uart0_bone_pins>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + pru0_port0: port@0 { + reg = <0>; + interrupts = <21 2 2>; + ti,pru-swuart-pins = /bits/ 8 <0 1>; + }; + pru0_port1: port@1 { + reg = <1>; + interrupts = <22 3 3>; + ti,pru-swuart-pins = /bits/ 8 <2 3>; + }; + pru0_port2: port@2 { + reg = <2>; + interrupts = <23 4 4>; + ti,pru-swuart-pins = /bits/ 8 <5 7>; + }; + }; + + pru-swuart1 { + compatible = "ti,pru-swuart"; + interrupt-parent = <&pruss_intc>; + ti,prus = <&pru1>; + firmware-name = "ti-pruss/pru_swuart-fw.elf"; + pinctrl-0 = <&pru_uart1_bone_pins>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + pru1_port0: port@0 { + reg = <0>; + interrupts = <24 5 5>; + ti,pru-swuart-pins = /bits/ 8 <0 1 2 3>; + }; + pru1_port1: port@1 { + reg = <1>; + interrupts = <25 6 6>; + ti,pru-swuart-pins = /bits/ 8 <4 5 6 7>; + }; + pru1_port2: port@2 { + reg = <2>; + interrupts = <26 7 7>; + ti,pru-swuart-pins = /bits/ 8 <8 9 10 11>; + }; + }; +}; + +/* Disable the following nodes due to pin mux conflicts with + * PRU Software UART signals + */ +&tda19988 { + status = "disabled"; +}; + +&mcasp0 { + status = "disabled"; +}; + +&gpio2 { + gpio-line-names = + "P9_15B", + "P8_18", + "P8_7", + "P8_8", + "P8_10", + "P8_9", + "P8_45 [pru1_swuart0_txd]", + "P8_46 [pru1_swuart0_rxd]", + "P8_43 [pru1_swuart0_cts]", + "P8_44 [pru1_swuart0_rts]", + "P8_41 [pru1_swuart1_txd]", + "P8_42 [pru1_swuart1_rxd]", + "P8_39 [pru1_swuart1_cts]", + "P8_40 [pru1_swuart1_rts]", + "P8_37 [hdmi]", + "P8_38 [hdmi]", + "P8_36 [hdmi]", + "P8_34 [hdmi]", + "[ethernet]", + "[ethernet]", + "[ethernet]", + "[ethernet]", + "P8_27 [pru1_swuart2_txd]", + "P8_29 [pru1_swuart2_rxd]", + "P8_28 [pru1_swuart2_cts]", + "P8_30 [pru1_swuart2_rts]", + "[emmc]", + "[emmc]", + "[emmc]", + "[emmc]", + "[emmc]", + "[emmc]"; +}; + +&gpio3 { + gpio-line-names = + "[ethernet]", + "[ethernet]", + "[ethernet]", + "[ethernet]", + "[ethernet]", + "[i2c0]", + "[i2c0]", + "[emu]", + "[emu]", + "[ethernet]", + "[ethernet]", + "[NC]", + "[NC]", + "[usb]", + "P9_31 [pru0_swuart0_txd]", + "P9_29 [pru0_swuart0_rxd]", + "P9_30 [pru0_swuart1_txd]", + "P9_28 [pru0_swuart1_rxd]", + "P9_42B [ecappwm0]", + "P9_27 [pru0_swuart2_txd]", + "P9_41A", + "P9_25 [pru0_swuart2_rxd]", + "[NC]", + "[NC]", + "[NC]", + "[NC]", + "[NC]", + "[NC]", + "[NC]", + "[NC]", + "[NC]", + "[NC]"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-uboot.dts b/arch/arm/boot/dts/am335x-boneblack-uboot.dts --- a/arch/arm/boot/dts/am335x-boneblack-uboot.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-uboot.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bbb-bone-buses.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-boneblack-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x20000000>; /* 512 MB */ + }; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro-1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&rtc { + system-power-controller; +}; + +&gpio0 { + gpio-line-names = + "[mdio_data]", + "[mdio_clk]", + "P9_22 [spi0_sclk]", + "P9_21 [spi0_d0]", + "P9_18 [spi0_d1]", + "P9_17 [spi0_cs0]", + "[mmc0_cd]", + "P8_42A [ecappwm0]", + "P8_35 [lcd d12]", + "P8_33 [lcd d13]", + "P8_31 [lcd d14]", + "P8_32 [lcd d15]", + "P9_20 [i2c2_sda]", + "P9_19 [i2c2_scl]", + "P9_26 [uart1_rxd]", + "P9_24 [uart1_txd]", + "[rmii1_txd3]", + "[rmii1_txd2]", + "[usb0_drvvbus]", + "[hdmi cec]", + "P9_41B", + "[rmii1_txd1]", + "P8_19 [ehrpwm2a]", + "P8_13 [ehrpwm2b]", + "NC", + "NC", + "P8_14", + "P8_17", + "[rmii1_txd0]", + "[rmii1_refclk]", + "P9_11 [uart4_rxd]", + "P9_13 [uart4_txd]"; +}; + +&gpio1 { + gpio-line-names = + "P8_25 [mmc1_dat0]", + "[mmc1_dat1]", + "P8_5 [mmc1_dat2]", + "P8_6 [mmc1_dat3]", + "P8_23 [mmc1_dat4]", + "P8_22 [mmc1_dat5]", + "P8_3 [mmc1_dat6]", + "P8_4 [mmc1_dat7]", + "NC", + "NC", + "NC", + "NC", + "P8_12", + "P8_11", + "P8_16", + "P8_15", + "P9_15A", + "P9_23", + "P9_14 [ehrpwm1a]", + "P9_16 [ehrpwm1b]", + "[emmc rst]", + "[usr0 led]", + "[usr1 led]", + "[usr2 led]", + "[usr3 led]", + "[hdmi irq]", + "[usb vbus oc]", + "[hdmi audio]", + "P9_12", + "P8_26", + "P8_21 [emmc]", + "P8_20 [emmc]"; +}; + +&gpio2 { + gpio-line-names = + "P9_15B", + "P8_18", + "P8_7", + "P8_8", + "P8_10", + "P8_9", + "P8_45 [hdmi]", + "P8_46 [hdmi]", + "P8_43 [hdmi]", + "P8_44 [hdmi]", + "P8_41 [hdmi]", + "P8_42 [hdmi]", + "P8_39 [hdmi]", + "P8_40 [hdmi]", + "P8_37 [hdmi]", + "P8_38 [hdmi]", + "P8_36 [hdmi]", + "P8_34 [hdmi]", + "[rmii1_rxd3]", + "[rmii1_rxd2]", + "[rmii1_rxd1]", + "[rmii1_rxd0]", + "P8_27 [hdmi]", + "P8_29 [hdmi]", + "P8_28 [hdmi]", + "P8_30 [hdmi]", + "[mmc0_dat3]", + "[mmc0_dat2]", + "[mmc0_dat1]", + "[mmc0_dat0]", + "[mmc0_clk]", + "[mmc0_cmd]"; +}; + +&gpio3 { + gpio-line-names = + "[mii col]", + "[mii crs]", + "[mii rx err]", + "[mii tx en]", + "[mii rx dv]", + "[i2c0 sda]", + "[i2c0 scl]", + "[jtag emu0]", + "[jtag emu1]", + "[mii tx clk]", + "[mii rx clk]", + "NC", + "NC", + "[usb vbus en]", + "P9_31 [spi1_sclk]", + "P9_29 [spi1_d0]", + "P9_30 [spi1_d1]", + "P9_28 [spi1_cs0]", + "P9_42B [ecappwm0]", + "P9_27", + "P9_41A", + "P9_25", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts b/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts --- a/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bone-common-univ.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-boneblack-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro-1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&gpio0 { + gpio-line-names = + "[mdio_data]", + "[mdio_clk]", + "P9_22 [spi0_sclk]", + "P9_21 [spi0_d0]", + "P9_18 [spi0_d1]", + "P9_17 [spi0_cs0]", + "[mmc0_cd]", + "P8_42A [ecappwm0]", + "P8_35 [lcd d12]", + "P8_33 [lcd d13]", + "P8_31 [lcd d14]", + "P8_32 [lcd d15]", + "P9_20 [i2c2_sda]", + "P9_19 [i2c2_scl]", + "P9_26 [uart1_rxd]", + "P9_24 [uart1_txd]", + "[rmii1_txd3]", + "[rmii1_txd2]", + "[usb0_drvvbus]", + "[hdmi cec]", + "P9_41B", + "[rmii1_txd1]", + "P8_19 [ehrpwm2a]", + "P8_13 [ehrpwm2b]", + "NC", + "NC", + "P8_14", + "P8_17", + "[rmii1_txd0]", + "[rmii1_refclk]", + "P9_11 [uart4_rxd]", + "P9_13 [uart4_txd]"; +}; + +&gpio1 { + gpio-line-names = + "P8_25 [mmc1_dat0]", + "[mmc1_dat1]", + "P8_5 [mmc1_dat2]", + "P8_6 [mmc1_dat3]", + "P8_23 [mmc1_dat4]", + "P8_22 [mmc1_dat5]", + "P8_3 [mmc1_dat6]", + "P8_4 [mmc1_dat7]", + "NC", + "NC", + "NC", + "NC", + "P8_12", + "P8_11", + "P8_16", + "P8_15", + "P9_15A", + "P9_23", + "P9_14 [ehrpwm1a]", + "P9_16 [ehrpwm1b]", + "[emmc rst]", + "[usr0 led]", + "[usr1 led]", + "[usr2 led]", + "[usr3 led]", + "[hdmi irq]", + "[usb vbus oc]", + "[hdmi audio]", + "P9_12", + "P8_26", + "P8_21 [emmc]", + "P8_20 [emmc]"; +}; + +&gpio2 { + gpio-line-names = + "P9_15B", + "P8_18", + "P8_7", + "P8_8", + "P8_10", + "P8_9", + "P8_45 [hdmi]", + "P8_46 [hdmi]", + "P8_43 [hdmi]", + "P8_44 [hdmi]", + "P8_41 [hdmi]", + "P8_42 [hdmi]", + "P8_39 [hdmi]", + "P8_40 [hdmi]", + "P8_37 [hdmi]", + "P8_38 [hdmi]", + "P8_36 [hdmi]", + "P8_34 [hdmi]", + "[rmii1_rxd3]", + "[rmii1_rxd2]", + "[rmii1_rxd1]", + "[rmii1_rxd0]", + "P8_27 [hdmi]", + "P8_29 [hdmi]", + "P8_28 [hdmi]", + "P8_30 [hdmi]", + "[mmc0_dat3]", + "[mmc0_dat2]", + "[mmc0_dat1]", + "[mmc0_dat0]", + "[mmc0_clk]", + "[mmc0_cmd]"; +}; + +&gpio3 { + gpio-line-names = + "[mii col]", + "[mii crs]", + "[mii rx err]", + "[mii tx en]", + "[mii rx dv]", + "[i2c0 sda]", + "[i2c0 scl]", + "[jtag emu0]", + "[jtag emu1]", + "[mii tx clk]", + "[mii rx clk]", + "NC", + "NC", + "[usb vbus en]", + "P9_31 [spi1_sclk]", + "P9_29 [spi1_d0]", + "P9_30 [spi1_d1]", + "P9_28 [spi1_cs0]", + "P9_42B [ecappwm0]", + "P9_27", + "P9_41A", + "P9_25", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblack-wireless.dts b/arch/arm/boot/dts/am335x-boneblack-wireless.dts --- a/arch/arm/boot/dts/am335x-boneblack-wireless.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblack-wireless.dts 2022-01-06 12:45:53.806318073 -0500 @@ -7,12 +7,18 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" #include "am335x-boneblack-common.dtsi" +#include "am335x-boneblack-hdmi.dtsi" #include / { model = "TI AM335x BeagleBone Black Wireless"; compatible = "ti,am335x-bone-black-wireless", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + chosen { + base_dtb = "am335x-boneblack-wireless.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + wlan_en_reg: fixedregulator@2 { compatible = "regulator-fixed"; regulator-name = "wlan-en-regulator"; @@ -101,7 +107,7 @@ }; &gpio3 { - ls_buf_en { + ls-buf-en-hog { gpio-hog; gpios = <10 GPIO_ACTIVE_HIGH>; output-high; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-boneblue.dts b/arch/arm/boot/dts/am335x-boneblue.dts --- a/arch/arm/boot/dts/am335x-boneblue.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-boneblue.dts 2022-01-06 12:45:53.806318073 -0500 @@ -14,6 +14,8 @@ chosen { stdout-path = &uart0; + base_dtb = "am335x-boneblue.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; leds { @@ -128,7 +130,6 @@ AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE7) /* (T5) lcd_data15.gpio0[11] - P8.32, BATT_LED_2 */ AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT, MUX_MODE7) /* (V6) gpmc_csn0.gpio1[29] - P8.26, BATT_LED_3 */ AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_OUTPUT, MUX_MODE7) /* (T11) gpmc_ad10.gpio0[26] - P8.14, BATT_LED_4 */ - >; }; @@ -241,6 +242,30 @@ AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_OUTPUT, MUX_MODE7) /* (M16) gmii1_rxd0.gpio2[21] */ >; }; + + /* E1 */ + eqep0_pins: pinmux_eqep0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE1) /* (B12) mcasp0_aclkr.eQEP0A_in */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_INPUT, MUX_MODE1) /* (C13) mcasp0_fsr.eQEP0B_in */ + >; + }; + + /* E2 */ + eqep1_pins: pinmux_eqep1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_INPUT, MUX_MODE2) /* (V2) lcd_data12.eQEP1A_in */ + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_INPUT, MUX_MODE2) /* (V3) lcd_data13.eQEP1B_in */ + >; + }; + + /* E3 */ + eqep2_pins: pinmux_eqep2_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT, MUX_MODE4) /* (T12) gpmc_ad12.eQEP2A_in */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT, MUX_MODE4) /* (R12) gpmc_ad13.eQEP2B_in */ + >; + }; }; &uart0 { @@ -411,11 +436,182 @@ status = "okay"; }; +&gpio0 { + gpio-line-names = + "UART3_CTS", /* M17 */ + "UART3_RTS", /* M18 */ + "UART2_RX", /* A17 */ + "UART2_TX", /* B17 */ + "I2C1_SDA", /* B16 */ + "I2C1_SCL", /* A16 */ + "MMC0_CD", /* C15 */ + "SPI1_SS2", /* C18 */ + "EQEP_1A", /* V2 */ + "EQEP_1B", /* V3 */ + "MDIR_2B", /* V4 */ + "BATT_LED_2", /* T5 */ + "I2C2_SDA", /* D18 */ + "I2C2_SCL", /* D17 */ + "UART1_RX", /* D16 */ + "UART1_TX", /* D15 */ + "MMC2_DAT1", /* J18 */ + "MMC2_DAT2", /* K15 */ + "NC", /* F16 */ + "WIFI_LED", /* A15 */ + "MOT_STBY", /* D14 */ + "WLAN_IRQ", /* K16 */ + "PWM_2A", /* U10 */ + "PWM_2B", /* T10 */ + "", + "", + "BATT_LED_4", /* T11 */ + "BATT_LED_1", /* U12 */ + "BT_EN", /* K17 */ + "SPI1_SS1", /* H18 */ + "UART4_RX", /* T17 */ + "MDIR_1B"; /* U17 */ +}; + +&gpio1 { + gpio-line-names = + "MMC1_DAT0", /* U7 */ + "MMC1_DAT1", /* V7 */ + "MMC1_DAT2", /* R8 */ + "MMC1_DAT3", /* T8 */ + "MMC1_DAT4", /* U8 */ + "MMC1_DAT5", /* V8 */ + "MMC1_DAT6", /* R9 */ + "MMC1_DAT7", /* T9 */ + "DCAN1_TX", /* E18 */ + "DCAN1_RX", /* E17 */ + "UART0_RX", /* E15 */ + "UART0_TX", /* E16 */ + "EQEP_2A", /* T12 */ + "EQEP_2B", /* R12 */ + "PRU_E_A", /* V13 */ + "PRU_E_B", /* U13 */ + "MDIR_2A", /* R13 */ + "GPIO1_17", /* V14 */ + "PWM_1A", /* U14 */ + "PWM_1B", /* T14 */ + "EMMC_RST", /* R14 */ + "USR_LED_0", /* V15 */ + "USR_LED_1", /* U15 */ + "USR_LED_2", /* T15 */ + "USR_LED_3", /* V16 */ + "GPIO1_25", /* U16 */ + "MCASP0_AXR0", /* T16 */ + "MCASP0_AXR1", /* V17 */ + "MCASP0_ACLKR", /* U18 */ + "BATT_LED_3", /* V6 */ + "MMC1_CLK", /* U9 */ + "MMC1_CMD"; /* V9 */ +}; + +&gpio2 { + gpio-line-names = + "MDIR_1A", /* T13 */ + "MCASP0_FSR", /* V12 */ + "LED_RED", /* R7 */ + "LED_GREEN", /* T7 */ + "MODE_BTN", /* U6 */ + "PAUSE_BTN", /* T6 */ + "MDIR_4A", /* R1 */ + "MDIR_4B", /* R2 */ + "MDIR_3B", /* R3 */ + "MDIR_3A", /* R4 */ + "SVO7", /* T1 */ + "SVO8", /* T2 */ + "SVO5", /* T3 */ + "SVO6", /* T4 */ + "UART5_TX", /* U1 */ + "UART5_RX", /* U2 */ + "SERVO_EN", /* U3 */ + "NC", /* U4 */ + "UART3_RX", /* L17 */ + "UART3_TX", /* L16 */ + "MMC2_CLK", /* L15 */ + "DCAN1_SILENT", /* M16 */ + "SVO1", /* U5 */ + "SVO3", /* R5 */ + "SVO2", /* V5 */ + "SVO4", /* R6 */ + "MMC0_DAT3", /* F17 */ + "MMC0_DAT2", /* F18 */ + "MMC0_DAT1", /* G15 */ + "MMC0_DAT0", /* G16 */ + "MMC0_CLK", /* G17 */ + "MMC0_CMD"; /* G18 */ +}; + &gpio3 { - ls_buf_en { + gpio-line-names = + "MMC2_DAT3", /* H16 */ + "GPIO3_1", /* H17 */ + "GPIO3_2", /* J15 */ + "MMC2_CMD", /* J16 */ + "MMC2_DAT0", /* J17 */ + "I2C0_SDA", /* C17 */ + "I2C0_SCL", /* C16 */ + "EMU1", /* C14 */ + "EMU0", /* B14 */ + "WL_EN", /* K18 */ + "WL_BT_OE", /* L18 */ + "", + "", + "NC", /* F15 */ + "SPI1_SCK", /* A13 */ + "SPI1_MISO", /* B13 */ + "SPI1_MOSI", /* D12 */ + "GPIO3_17", /* C12 */ + "EQEP_0A", /* B12 */ + "EQEP_0B", /* C13 */ + "GPIO3_20", /* D13 */ + "IMU_INT", /* A14 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""; + + ls-buf-en-hog { gpio-hog; gpios = <10 GPIO_ACTIVE_HIGH>; output-high; - line-name = "LS_BUF_EN"; }; }; + +&epwmss0 { + status = "okay"; +}; + +&eqep0 { + pinctrl-names = "default"; + pinctrl-0 = <&eqep0_pins>; + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&eqep1 { + pinctrl-names = "default"; + pinctrl-0 = <&eqep1_pins>; + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +&eqep2 { + pinctrl-names = "default"; + pinctrl-0 = <&eqep2_pins>; + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi --- a/arch/arm/boot/dts/am335x-bone-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bone-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -26,14 +26,14 @@ compatible = "gpio-leds"; led2 { - label = "beaglebone:green:heartbeat"; + label = "beaglebone:green:usr0"; gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; default-state = "off"; }; led3 { - label = "beaglebone:green:mmc0"; + label = "beaglebone:green:usr1"; gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; linux,default-trigger = "mmc0"; default-state = "off"; @@ -63,9 +63,6 @@ }; &am33xx_pinmux { - pinctrl-names = "default"; - pinctrl-0 = <&clkout2_pin>; - user_leds_s0: user_leds_s0 { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ @@ -96,12 +93,6 @@ >; }; - clkout2_pin: pinmux_clkout2_pin { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* xdma_event_intr1.clkout2 */ - >; - }; - cpsw_default: cpsw_default { pinctrl-single,pins = < /* Slave 1 */ @@ -189,6 +180,7 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; + symlink = "bone/uart/0"; }; &usb0 { @@ -207,6 +199,7 @@ status = "okay"; clock-frequency = <400000>; + symlink = "bone/i2c/0"; tps: tps@24 { reg = <0x24>; @@ -230,6 +223,7 @@ status = "okay"; clock-frequency = <100000>; + symlink = "bone/i2c/2"; cape_eeprom0: cape_eeprom0@54 { compatible = "atmel,24c256"; @@ -396,4 +390,22 @@ &rtc { clocks = <&clk_32768_ck>, <&clk_24mhz_clkctrl AM3_CLK_24MHZ_CLKDIV32K_CLKCTRL 0>; clock-names = "ext-clk", "int-clk"; + system-power-controller; +}; + +&pruss_tm { + status = "okay"; +}; + +&wkup_m3_ipc { + ti,scale-data-fw = "am335x-bone-scale-data.bin"; +}; + +&tscadc { + adc { + ti,adc-channels = <0 1 2 3 4 5 6 7>; + ti,chan-step-avg = <16 16 16 16 16 16 16 16>; + ti,chan-step-opendelay = <0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98>; + ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>; + }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bone-common-univ.dtsi b/arch/arm/boot/dts/am335x-bone-common-univ.dtsi --- a/arch/arm/boot/dts/am335x-bone-common-univ.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bone-common-univ.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,2909 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&am33xx_pinmux { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) emmc */ + P8_03_default_pin: pinmux_P8_03_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_pin: pinmux_P8_03_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_pu_pin: pinmux_P8_03_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_pd_pin: pinmux_P8_03_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_input_pin: pinmux_P8_03_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + + /* P8_04 (ZCZ ball T9) emmc */ + P8_04_default_pin: pinmux_P8_04_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_pin: pinmux_P8_04_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_pu_pin: pinmux_P8_04_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_pd_pin: pinmux_P8_04_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_input_pin: pinmux_P8_04_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + + /* P8_05 (ZCZ ball R8) emmc */ + P8_05_default_pin: pinmux_P8_05_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_pin: pinmux_P8_05_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_pu_pin: pinmux_P8_05_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_pd_pin: pinmux_P8_05_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_input_pin: pinmux_P8_05_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + + /* P8_06 (ZCZ ball T8) emmc */ + P8_06_default_pin: pinmux_P8_06_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_pin: pinmux_P8_06_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_pu_pin: pinmux_P8_06_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_pd_pin: pinmux_P8_06_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_input_pin: pinmux_P8_06_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + + /* P8_07 (ZCZ ball R7) gpio2_2 */ + P8_07_default_pin: pinmux_P8_07_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_pin: pinmux_P8_07_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_pu_pin: pinmux_P8_07_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_pd_pin: pinmux_P8_07_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_input_pin: pinmux_P8_07_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_timer_pin: pinmux_P8_07_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_advn_ale.timer4 */ + + /* P8_08 (ZCZ ball T7) gpio2_3 */ + P8_08_default_pin: pinmux_P8_08_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_pin: pinmux_P8_08_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_pu_pin: pinmux_P8_08_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_pd_pin: pinmux_P8_08_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_input_pin: pinmux_P8_08_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_timer_pin: pinmux_P8_08_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_oen_ren.timer7 */ + + /* P8_09 (ZCZ ball T6) gpio2_5 */ + P8_09_default_pin: pinmux_P8_09_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_pin: pinmux_P8_09_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_pu_pin: pinmux_P8_09_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_pd_pin: pinmux_P8_09_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_input_pin: pinmux_P8_09_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_timer_pin: pinmux_P8_09_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_be0n_cle.timer5 */ + + /* P8_10 (ZCZ ball U6) gpio2_4 */ + P8_10_default_pin: pinmux_P8_10_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_pin: pinmux_P8_10_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_pu_pin: pinmux_P8_10_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_pd_pin: pinmux_P8_10_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_input_pin: pinmux_P8_10_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_timer_pin: pinmux_P8_10_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_wen.timer6 */ + + /* P8_11 (ZCZ ball R12) gpio1_13 */ + P8_11_default_pin: pinmux_P8_11_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_pin: pinmux_P8_11_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_pu_pin: pinmux_P8_11_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_pd_pin: pinmux_P8_11_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_input_pin: pinmux_P8_11_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_qep_pin: pinmux_P8_11_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad13.eqep2b_in */ + P8_11_pruout_pin: pinmux_P8_11_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_ad13.pru0_out15 */ + + /* P8_12 (ZCZ ball T12) gpio1_12 */ + P8_12_default_pin: pinmux_P8_12_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_pin: pinmux_P8_12_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_pu_pin: pinmux_P8_12_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_pd_pin: pinmux_P8_12_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_input_pin: pinmux_P8_12_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_qep_pin: pinmux_P8_12_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad12.eqep2a_in */ + P8_12_pruout_pin: pinmux_P8_12_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_ad12.pru0_out14 */ + + /* P8_13 (ZCZ ball T10) gpio0_23 */ + P8_13_default_pin: pinmux_P8_13_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_pin: pinmux_P8_13_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_pu_pin: pinmux_P8_13_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_pd_pin: pinmux_P8_13_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_input_pin: pinmux_P8_13_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_pwm_pin: pinmux_P8_13_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad9.ehrpwm2b */ + + /* P8_14 (ZCZ ball T11) gpio0_26 */ + P8_14_default_pin: pinmux_P8_14_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P8_14_gpio_pin: pinmux_P8_14_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P8_14_gpio_pu_pin: pinmux_P8_14_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P8_14_gpio_pd_pin: pinmux_P8_14_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P8_14_gpio_input_pin: pinmux_P8_14_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P8_14_pwm_pin: pinmux_P8_14_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad10.ehrpwm2_tripzone_input */ + + /* P8_15 (ZCZ ball U13) gpio1_15 */ + P8_15_default_pin: pinmux_P8_15_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_pin: pinmux_P8_15_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_pu_pin: pinmux_P8_15_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_pd_pin: pinmux_P8_15_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_input_pin: pinmux_P8_15_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_qep_pin: pinmux_P8_15_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad15.eqep2_strobe */ + P8_15_pru_ecap_pin: pinmux_P8_15_pru_ecap_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_ad15.pr1_ecap0_ecap_capin_apwm_o */ + P8_15_pruin_pin: pinmux_P8_15_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_ad15.pru0_in15 */ + + /* P8_16 (ZCZ ball V13) gpio1_14 */ + P8_16_default_pin: pinmux_P8_16_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_pin: pinmux_P8_16_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_pu_pin: pinmux_P8_16_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_pd_pin: pinmux_P8_16_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_input_pin: pinmux_P8_16_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_qep_pin: pinmux_P8_16_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad14.eqep2_index */ + P8_16_pruin_pin: pinmux_P8_16_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_ad14.pru0_in14 */ + + /* P8_17 (ZCZ ball U12) gpio0_27 */ + P8_17_default_pin: pinmux_P8_17_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P8_17_gpio_pin: pinmux_P8_17_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P8_17_gpio_pu_pin: pinmux_P8_17_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P8_17_gpio_pd_pin: pinmux_P8_17_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P8_17_gpio_input_pin: pinmux_P8_17_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P8_17_pwm_pin: pinmux_P8_17_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad11.ehrpwm0_synco */ + + /* P8_18 (ZCZ ball V12) gpio2_1 */ + P8_18_default_pin: pinmux_P8_18_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_pin: pinmux_P8_18_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_pu_pin: pinmux_P8_18_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_pd_pin: pinmux_P8_18_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_input_pin: pinmux_P8_18_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + + /* P8_19 (ZCZ ball U10) gpio0_22 */ + P8_19_default_pin: pinmux_P8_19_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_pin: pinmux_P8_19_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_pu_pin: pinmux_P8_19_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_pd_pin: pinmux_P8_19_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_input_pin: pinmux_P8_19_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_pwm_pin: pinmux_P8_19_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad8.ehrpwm2a */ + + /* P8_20 (ZCZ ball V9) emmc */ + P8_20_default_pin: pinmux_P8_20_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_pin: pinmux_P8_20_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_pu_pin: pinmux_P8_20_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_pd_pin: pinmux_P8_20_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_input_pin: pinmux_P8_20_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_pruout_pin: pinmux_P8_20_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_csn2.pru1_out13 */ + P8_20_pruin_pin: pinmux_P8_20_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_csn2.pru1_in13 */ + + /* P8_21 (ZCZ ball U9) emmc */ + P8_21_default_pin: pinmux_P8_21_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_pin: pinmux_P8_21_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_pu_pin: pinmux_P8_21_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_pd_pin: pinmux_P8_21_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_input_pin: pinmux_P8_21_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_pruout_pin: pinmux_P8_21_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_csn1.pru1_out12 */ + P8_21_pruin_pin: pinmux_P8_21_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_csn1.pru1_in12 */ + + /* P8_22 (ZCZ ball V8) emmc */ + P8_22_default_pin: pinmux_P8_22_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_pin: pinmux_P8_22_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_pu_pin: pinmux_P8_22_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_pd_pin: pinmux_P8_22_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_input_pin: pinmux_P8_22_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + + /* P8_23 (ZCZ ball U8) emmc */ + P8_23_default_pin: pinmux_P8_23_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_pin: pinmux_P8_23_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_pu_pin: pinmux_P8_23_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_pd_pin: pinmux_P8_23_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_input_pin: pinmux_P8_23_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + + /* P8_24 (ZCZ ball V7) emmc */ + P8_24_default_pin: pinmux_P8_24_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_pin: pinmux_P8_24_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_pu_pin: pinmux_P8_24_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_pd_pin: pinmux_P8_24_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_input_pin: pinmux_P8_24_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + + /* P8_25 (ZCZ ball U7) emmc */ + P8_25_default_pin: pinmux_P8_25_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_pin: pinmux_P8_25_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_pu_pin: pinmux_P8_25_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_pd_pin: pinmux_P8_25_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_input_pin: pinmux_P8_25_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + + /* P8_26 (ZCZ ball V6) gpio1_29 */ + P8_26_default_pin: pinmux_P8_26_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x087c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn0.gpio1_29 */ + P8_26_gpio_pin: pinmux_P8_26_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x087c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn0.gpio1_29 */ + P8_26_gpio_pu_pin: pinmux_P8_26_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x087c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn0.gpio1_29 */ + P8_26_gpio_pd_pin: pinmux_P8_26_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x087c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn0.gpio1_29 */ + P8_26_gpio_input_pin: pinmux_P8_26_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x087c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_csn0.gpio1_29 */ + + /* P8_27 (ZCZ ball U5) hdmi */ + P8_27_default_pin: pinmux_P8_27_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_pin: pinmux_P8_27_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_pu_pin: pinmux_P8_27_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_pd_pin: pinmux_P8_27_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_input_pin: pinmux_P8_27_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_pruout_pin: pinmux_P8_27_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_vsync.pru1_out8 */ + P8_27_pruin_pin: pinmux_P8_27_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_vsync.pru1_in8 */ + + /* P8_28 (ZCZ ball V5) hdmi */ + P8_28_default_pin: pinmux_P8_28_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_pin: pinmux_P8_28_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_pu_pin: pinmux_P8_28_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_pd_pin: pinmux_P8_28_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_input_pin: pinmux_P8_28_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_pruout_pin: pinmux_P8_28_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_pclk.pru1_out10 */ + P8_28_pruin_pin: pinmux_P8_28_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_pclk.pru1_in10 */ + + /* P8_29 (ZCZ ball R5) hdmi */ + P8_29_default_pin: pinmux_P8_29_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_pin: pinmux_P8_29_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_pu_pin: pinmux_P8_29_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_pd_pin: pinmux_P8_29_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_input_pin: pinmux_P8_29_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_pruout_pin: pinmux_P8_29_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_hsync.pru1_out9 */ + P8_29_pruin_pin: pinmux_P8_29_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_hsync.pru1_in9 */ + + /* P8_30 (ZCZ ball R6) hdmi */ + P8_30_default_pin: pinmux_P8_30_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_pin: pinmux_P8_30_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_pu_pin: pinmux_P8_30_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_pd_pin: pinmux_P8_30_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_input_pin: pinmux_P8_30_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_INPUT | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_pruout_pin: pinmux_P8_30_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_ac_bias_en.pru1_out11 */ + P8_30_pruin_pin: pinmux_P8_30_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_INPUT | MUX_MODE6) >; }; /* lcd_ac_bias_en.pru1_in11 */ + + /* P8_31 (ZCZ ball V4) hdmi */ + P8_31_default_pin: pinmux_P8_31_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_pin: pinmux_P8_31_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_pu_pin: pinmux_P8_31_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_pd_pin: pinmux_P8_31_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_input_pin: pinmux_P8_31_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_qep_pin: pinmux_P8_31_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data14.eqep1_index */ + P8_31_uart_pin: pinmux_P8_31_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* lcd_data14.uart5_rxd */ + + /* P8_32 (ZCZ ball T5) hdmi */ + P8_32_default_pin: pinmux_P8_32_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_pin: pinmux_P8_32_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_pu_pin: pinmux_P8_32_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_pd_pin: pinmux_P8_32_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_input_pin: pinmux_P8_32_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_qep_pin: pinmux_P8_32_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data15.eqep1_strobe */ + + /* P8_33 (ZCZ ball V3) hdmi */ + P8_33_default_pin: pinmux_P8_33_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_pin: pinmux_P8_33_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_pu_pin: pinmux_P8_33_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_pd_pin: pinmux_P8_33_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_input_pin: pinmux_P8_33_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_qep_pin: pinmux_P8_33_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data13.eqep1b_in */ + + /* P8_34 (ZCZ ball U4) hdmi */ + P8_34_default_pin: pinmux_P8_34_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_pin: pinmux_P8_34_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_pu_pin: pinmux_P8_34_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_pd_pin: pinmux_P8_34_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_input_pin: pinmux_P8_34_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_pwm_pin: pinmux_P8_34_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data11.ehrpwm1b */ + + /* P8_35 (ZCZ ball V2) hdmi */ + P8_35_default_pin: pinmux_P8_35_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_pin: pinmux_P8_35_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_pu_pin: pinmux_P8_35_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_pd_pin: pinmux_P8_35_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_input_pin: pinmux_P8_35_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_qep_pin: pinmux_P8_35_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data12.eqep1a_in */ + + /* P8_36 (ZCZ ball U3) hdmi */ + P8_36_default_pin: pinmux_P8_36_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_pin: pinmux_P8_36_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_pu_pin: pinmux_P8_36_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_pd_pin: pinmux_P8_36_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_input_pin: pinmux_P8_36_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_pwm_pin: pinmux_P8_36_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data10.ehrpwm1a */ + + /* P8_37 (ZCZ ball U1) hdmi */ + P8_37_default_pin: pinmux_P8_37_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_pin: pinmux_P8_37_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_pu_pin: pinmux_P8_37_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_pd_pin: pinmux_P8_37_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_input_pin: pinmux_P8_37_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_pwm_pin: pinmux_P8_37_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data8.ehrpwm1_tripzone_input */ + P8_37_uart_pin: pinmux_P8_37_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* lcd_data8.uart5_txd */ + + /* P8_38 (ZCZ ball U2) hdmi */ + P8_38_default_pin: pinmux_P8_38_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_pin: pinmux_P8_38_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_pu_pin: pinmux_P8_38_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_pd_pin: pinmux_P8_38_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_input_pin: pinmux_P8_38_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_pwm_pin: pinmux_P8_38_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data9.ehrpwm0_synco */ + P8_38_uart_pin: pinmux_P8_38_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* lcd_data9.uart5_rxd */ + + /* P8_39 (ZCZ ball T3) hdmi */ + P8_39_default_pin: pinmux_P8_39_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_pin: pinmux_P8_39_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_pu_pin: pinmux_P8_39_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_pd_pin: pinmux_P8_39_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_input_pin: pinmux_P8_39_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_qep_pin: pinmux_P8_39_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data6.eqep2_index */ + P8_39_pruout_pin: pinmux_P8_39_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data6.pru1_out6 */ + P8_39_pruin_pin: pinmux_P8_39_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data6.pru1_in6 */ + + /* P8_40 (ZCZ ball T4) hdmi */ + P8_40_default_pin: pinmux_P8_40_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_pin: pinmux_P8_40_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_pu_pin: pinmux_P8_40_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_pd_pin: pinmux_P8_40_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_input_pin: pinmux_P8_40_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_qep_pin: pinmux_P8_40_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data7.eqep2_strobe */ + P8_40_pruout_pin: pinmux_P8_40_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data7.pru1_out7 */ + P8_40_pruin_pin: pinmux_P8_40_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data7.pru1_in7 */ + + /* P8_41 (ZCZ ball T1) hdmi */ + P8_41_default_pin: pinmux_P8_41_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_pin: pinmux_P8_41_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_pu_pin: pinmux_P8_41_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_pd_pin: pinmux_P8_41_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_input_pin: pinmux_P8_41_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_qep_pin: pinmux_P8_41_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data4.eqep2a_in */ + P8_41_pruout_pin: pinmux_P8_41_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data4.pru1_out4 */ + P8_41_pruin_pin: pinmux_P8_41_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data4.pru1_in4 */ + + /* P8_42 (ZCZ ball T2) hdmi */ + P8_42_default_pin: pinmux_P8_42_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_pin: pinmux_P8_42_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_pu_pin: pinmux_P8_42_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_pd_pin: pinmux_P8_42_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_input_pin: pinmux_P8_42_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_qep_pin: pinmux_P8_42_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data5.eqep2b_in */ + P8_42_pruout_pin: pinmux_P8_42_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data5.pru1_out5 */ + P8_42_pruin_pin: pinmux_P8_42_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data5.pru1_in5 */ + + /* P8_43 (ZCZ ball R3) hdmi */ + P8_43_default_pin: pinmux_P8_43_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_pin: pinmux_P8_43_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_pu_pin: pinmux_P8_43_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_pd_pin: pinmux_P8_43_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_input_pin: pinmux_P8_43_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_pwm_pin: pinmux_P8_43_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data2.ehrpwm2_tripzone_input */ + P8_43_pruout_pin: pinmux_P8_43_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data2.pru1_out2 */ + P8_43_pruin_pin: pinmux_P8_43_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data2.pru1_in2 */ + + /* P8_44 (ZCZ ball R4) hdmi */ + P8_44_default_pin: pinmux_P8_44_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_pin: pinmux_P8_44_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_pu_pin: pinmux_P8_44_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_pd_pin: pinmux_P8_44_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_input_pin: pinmux_P8_44_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_pwm_pin: pinmux_P8_44_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data3.ehrpwm0_synco */ + P8_44_pruout_pin: pinmux_P8_44_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data3.pru1_out3 */ + P8_44_pruin_pin: pinmux_P8_44_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data3.pru1_in3 */ + + /* P8_45 (ZCZ ball R1) hdmi */ + P8_45_default_pin: pinmux_P8_45_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_pin: pinmux_P8_45_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_pu_pin: pinmux_P8_45_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_pd_pin: pinmux_P8_45_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_input_pin: pinmux_P8_45_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_pwm_pin: pinmux_P8_45_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data0.ehrpwm2a */ + P8_45_pruout_pin: pinmux_P8_45_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data0.pru1_out0 */ + P8_45_pruin_pin: pinmux_P8_45_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data0.pru1_in0 */ + + /* P8_46 (ZCZ ball R2) hdmi */ + P8_46_default_pin: pinmux_P8_46_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_pin: pinmux_P8_46_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_pu_pin: pinmux_P8_46_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_pd_pin: pinmux_P8_46_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_input_pin: pinmux_P8_46_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_pwm_pin: pinmux_P8_46_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data1.ehrpwm2b */ + P8_46_pruout_pin: pinmux_P8_46_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data1.pru1_out1 */ + P8_46_pruin_pin: pinmux_P8_46_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data1.pru1_in1 */ + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) gpio0_30 */ + P9_11_default_pin: pinmux_P9_11_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_pin: pinmux_P9_11_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_pu_pin: pinmux_P9_11_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_pd_pin: pinmux_P9_11_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_input_pin: pinmux_P9_11_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_uart_pin: pinmux_P9_11_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wait0.uart4_rxd */ + + /* P9_12 (ZCZ ball U18) gpio1_28 */ + P9_12_default_pin: pinmux_P9_12_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_pin: pinmux_P9_12_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_pu_pin: pinmux_P9_12_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_pd_pin: pinmux_P9_12_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_input_pin: pinmux_P9_12_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + + /* P9_13 (ZCZ ball U17) gpio0_31 */ + P9_13_default_pin: pinmux_P9_13_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_pin: pinmux_P9_13_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_pu_pin: pinmux_P9_13_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_pd_pin: pinmux_P9_13_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_input_pin: pinmux_P9_13_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_uart_pin: pinmux_P9_13_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wpn.uart4_txd */ + + /* P9_14 (ZCZ ball U14) gpio1_18 */ + P9_14_default_pin: pinmux_P9_14_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_pin: pinmux_P9_14_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_pu_pin: pinmux_P9_14_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_pd_pin: pinmux_P9_14_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_input_pin: pinmux_P9_14_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_pwm_pin: pinmux_P9_14_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a2.ehrpwm1a */ + + /* P9_15 (ZCZ ball R13) gpio1_16 */ + P9_15_default_pin: pinmux_P9_15_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_pin: pinmux_P9_15_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_pu_pin: pinmux_P9_15_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_pd_pin: pinmux_P9_15_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_input_pin: pinmux_P9_15_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_pwm_pin: pinmux_P9_15_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a0.ehrpwm1_tripzone_input */ + + /* P9_16 (ZCZ ball T14) gpio1_19 */ + P9_16_default_pin: pinmux_P9_16_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_pin: pinmux_P9_16_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_pu_pin: pinmux_P9_16_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_pd_pin: pinmux_P9_16_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_input_pin: pinmux_P9_16_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_pwm_pin: pinmux_P9_16_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a3.ehrpwm1b */ + + /* P9_17 (ZCZ ball A16) gpio0_5 */ + P9_17_default_pin: pinmux_P9_17_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_pin: pinmux_P9_17_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_pu_pin: pinmux_P9_17_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_pd_pin: pinmux_P9_17_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_input_pin: pinmux_P9_17_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_INPUT | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_spi_cs_pin: pinmux_P9_17_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_cs0.spi0_cs0 */ + P9_17_i2c_pin: pinmux_P9_17_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_cs0.i2c1_scl */ + P9_17_pwm_pin: pinmux_P9_17_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_cs0.ehrpwm0_synci */ + P9_17_pru_uart_pin: pinmux_P9_17_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_cs0.pr1_uart0_txd */ + + /* P9_18 (ZCZ ball B16) gpio0_4 */ + P9_18_default_pin: pinmux_P9_18_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_pin: pinmux_P9_18_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_pu_pin: pinmux_P9_18_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_pd_pin: pinmux_P9_18_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_input_pin: pinmux_P9_18_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_INPUT | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_spi_pin: pinmux_P9_18_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d1.spi0_d1 */ + P9_18_i2c_pin: pinmux_P9_18_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_d1.i2c1_sda */ + P9_18_pwm_pin: pinmux_P9_18_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_d1.ehrpwm0_tripzone_input */ + P9_18_pru_uart_pin: pinmux_P9_18_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_d1.pr1_uart0_rxd */ + + /* P9_19 (ZCZ ball D17) i2c2_scl */ + P9_19_default_pin: pinmux_P9_19_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rtsn.i2c2_scl */ + P9_19_gpio_pin: pinmux_P9_19_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_gpio_pu_pin: pinmux_P9_19_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_gpio_pd_pin: pinmux_P9_19_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_gpio_input_pin: pinmux_P9_19_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_INPUT | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_timer_pin: pinmux_P9_19_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart1_rtsn.timer5 */ + P9_19_can_pin: pinmux_P9_19_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart1_rtsn.dcan0_rx */ + P9_19_i2c_pin: pinmux_P9_19_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rtsn.i2c2_scl */ + P9_19_spi_cs_pin: pinmux_P9_19_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart1_rtsn.spi1_cs1 */ + P9_19_pru_uart_pin: pinmux_P9_19_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_rtsn.pr1_uart0_rts_n */ + + /* P9_20 (ZCZ ball D18) i2c2_sda */ + P9_20_default_pin: pinmux_P9_20_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_ctsn.i2c2_sda */ + P9_20_gpio_pin: pinmux_P9_20_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_gpio_pu_pin: pinmux_P9_20_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_gpio_pd_pin: pinmux_P9_20_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_gpio_input_pin: pinmux_P9_20_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_INPUT | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_timer_pin: pinmux_P9_20_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart1_ctsn.timer6 */ + P9_20_can_pin: pinmux_P9_20_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart1_ctsn.dcan0_tx */ + P9_20_i2c_pin: pinmux_P9_20_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_ctsn.i2c2_sda */ + P9_20_spi_cs_pin: pinmux_P9_20_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart1_ctsn.spi1_cs0 */ + P9_20_pru_uart_pin: pinmux_P9_20_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_ctsn.pr1_uart0_cts_n */ + + /* P9_21 (ZCZ ball B17) gpio0_3 */ + P9_21_default_pin: pinmux_P9_21_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_pin: pinmux_P9_21_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_pu_pin: pinmux_P9_21_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_pd_pin: pinmux_P9_21_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_input_pin: pinmux_P9_21_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_INPUT | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_spi_pin: pinmux_P9_21_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d0.spi0_d0 */ + P9_21_uart_pin: pinmux_P9_21_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* spi0_d0.uart2_txd */ + P9_21_i2c_pin: pinmux_P9_21_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_d0.i2c2_scl */ + P9_21_pwm_pin: pinmux_P9_21_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_d0.ehrpwm0b */ + P9_21_pru_uart_pin: pinmux_P9_21_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_d0.pr1_uart0_rts_n */ + + /* P9_22 (ZCZ ball A17) gpio0_2 */ + P9_22_default_pin: pinmux_P9_22_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_pin: pinmux_P9_22_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_pu_pin: pinmux_P9_22_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_pd_pin: pinmux_P9_22_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_input_pin: pinmux_P9_22_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_INPUT | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_spi_sclk_pin: pinmux_P9_22_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_sclk.spi0_sclk */ + P9_22_uart_pin: pinmux_P9_22_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* spi0_sclk.uart2_rxd */ + P9_22_i2c_pin: pinmux_P9_22_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_sclk.i2c2_sda */ + P9_22_pwm_pin: pinmux_P9_22_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_sclk.ehrpwm0a */ + P9_22_pru_uart_pin: pinmux_P9_22_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_sclk.pr1_uart0_cts_n */ + + /* P9_23 (ZCZ ball V14) gpio1_17 */ + P9_23_default_pin: pinmux_P9_23_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_pin: pinmux_P9_23_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_pu_pin: pinmux_P9_23_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_pd_pin: pinmux_P9_23_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_input_pin: pinmux_P9_23_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_pwm_pin: pinmux_P9_23_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a1.ehrpwm0_synco */ + + /* P9_24 (ZCZ ball D15) gpio0_15 */ + P9_24_default_pin: pinmux_P9_24_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_pin: pinmux_P9_24_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_pu_pin: pinmux_P9_24_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_pd_pin: pinmux_P9_24_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_input_pin: pinmux_P9_24_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_uart_pin: pinmux_P9_24_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart1_txd.uart1_txd */ + P9_24_can_pin: pinmux_P9_24_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart1_txd.dcan1_rx */ + P9_24_i2c_pin: pinmux_P9_24_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_txd.i2c1_scl */ + P9_24_pru_uart_pin: pinmux_P9_24_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_txd.pr1_uart0_txd */ + P9_24_pruin_pin: pinmux_P9_24_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT | MUX_MODE6) >; }; /* uart1_txd.pru0_in16 */ + + /* P9_25 (ZCZ ball A14) audio */ + P9_25_default_pin: pinmux_P9_25_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_pin: pinmux_P9_25_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_pu_pin: pinmux_P9_25_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_pd_pin: pinmux_P9_25_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_input_pin: pinmux_P9_25_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_qep_pin: pinmux_P9_25_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_ahclkx.eqep0_strobe */ + P9_25_pruout_pin: pinmux_P9_25_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_ahclkx.pru0_out7 */ + P9_25_pruin_pin: pinmux_P9_25_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkx.pru0_in7 */ + + /* P9_26 (ZCZ ball D16) gpio0_14 */ + P9_26_default_pin: pinmux_P9_26_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_pin: pinmux_P9_26_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_pu_pin: pinmux_P9_26_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_pd_pin: pinmux_P9_26_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_input_pin: pinmux_P9_26_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_INPUT | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_uart_pin: pinmux_P9_26_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart1_rxd.uart1_rxd */ + P9_26_can_pin: pinmux_P9_26_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart1_rxd.dcan1_tx */ + P9_26_i2c_pin: pinmux_P9_26_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rxd.i2c1_sda */ + P9_26_pru_uart_pin: pinmux_P9_26_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_rxd.pr1_uart0_rxd */ + P9_26_pruin_pin: pinmux_P9_26_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_INPUT | MUX_MODE6) >; }; /* uart1_rxd.pru1_in16 */ + + /* P9_27 (ZCZ ball C13) gpio3_19 */ + P9_27_default_pin: pinmux_P9_27_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_pin: pinmux_P9_27_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_pu_pin: pinmux_P9_27_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_pd_pin: pinmux_P9_27_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_input_pin: pinmux_P9_27_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_qep_pin: pinmux_P9_27_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_fsr.eqep0b_in */ + P9_27_pruout_pin: pinmux_P9_27_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_fsr.pru0_out5 */ + P9_27_pruin_pin: pinmux_P9_27_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsr.pru0_in5 */ + + /* P9_28 (ZCZ ball C12) audio */ + P9_28_default_pin: pinmux_P9_28_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_pin: pinmux_P9_28_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_pu_pin: pinmux_P9_28_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_pd_pin: pinmux_P9_28_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_input_pin: pinmux_P9_28_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_pwm_pin: pinmux_P9_28_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_ahclkr.ehrpwm0_synci */ + P9_28_spi_cs_pin: pinmux_P9_28_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_ahclkr.spi1_cs0 */ + P9_28_pwm2_pin: pinmux_P9_28_pwm2_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* mcasp0_ahclkr.ecap2_in_pwm2_out */ + P9_28_pruout_pin: pinmux_P9_28_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_ahclkr.pru0_out3 */ + P9_28_pruin_pin: pinmux_P9_28_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkr.pru0_in3 */ + + /* P9_29 (ZCZ ball B13) audio */ + P9_29_default_pin: pinmux_P9_29_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_pin: pinmux_P9_29_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_pu_pin: pinmux_P9_29_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_pd_pin: pinmux_P9_29_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_input_pin: pinmux_P9_29_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_pwm_pin: pinmux_P9_29_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_fsx.ehrpwm0b */ + P9_29_spi_pin: pinmux_P9_29_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_fsx.spi1_d0 */ + P9_29_pruout_pin: pinmux_P9_29_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_fsx.pru0_out1 */ + P9_29_pruin_pin: pinmux_P9_29_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsx.pru0_in1 */ + + /* P9_30 (ZCZ ball D12) gpio3_16 */ + P9_30_default_pin: pinmux_P9_30_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P9_30_gpio_pin: pinmux_P9_30_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P9_30_gpio_pu_pin: pinmux_P9_30_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P9_30_gpio_pd_pin: pinmux_P9_30_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P9_30_gpio_input_pin: pinmux_P9_30_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P9_30_pwm_pin: pinmux_P9_30_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_axr0.ehrpwm0_tripzone_input */ + P9_30_spi_pin: pinmux_P9_30_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_axr0.spi1_d1 */ + P9_30_pruout_pin: pinmux_P9_30_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_axr0.pru0_out2 */ + P9_30_pruin_pin: pinmux_P9_30_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr0.pru0_in2 */ + + /* P9_31 (ZCZ ball A13) audio */ + P9_31_default_pin: pinmux_P9_31_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_pin: pinmux_P9_31_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_pu_pin: pinmux_P9_31_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_pd_pin: pinmux_P9_31_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_input_pin: pinmux_P9_31_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_pwm_pin: pinmux_P9_31_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkx.ehrpwm0a */ + P9_31_spi_sclk_pin: pinmux_P9_31_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_aclkx.spi1_sclk */ + P9_31_pruout_pin: pinmux_P9_31_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_aclkx.pru0_out0 */ + P9_31_pruin_pin: pinmux_P9_31_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkx.pru0_in0 */ + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) gpio0_20 */ + P9_41_default_pin: pinmux_P9_41_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_pin: pinmux_P9_41_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_pu_pin: pinmux_P9_41_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_pd_pin: pinmux_P9_41_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_input_pin: pinmux_P9_41_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_INPUT | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_timer_pin: pinmux_P9_41_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* xdma_event_intr1.timer7 */ + P9_41_pruin_pin: pinmux_P9_41_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_INPUT | MUX_MODE5) >; }; /* xdma_event_intr1.pru0_in16 */ + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) gpio3_20 */ + P9_91_default_pin: pinmux_P9_91_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_pin: pinmux_P9_91_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_pu_pin: pinmux_P9_91_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_pd_pin: pinmux_P9_91_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_input_pin: pinmux_P9_91_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_qep_pin: pinmux_P9_91_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_axr1.eqep0_index */ + P9_91_pruout_pin: pinmux_P9_91_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_axr1.pru0_out6 */ + P9_91_pruin_pin: pinmux_P9_91_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr1.pru0_in6 */ + + /* P9_42 (ZCZ ball C18) gpio0_7 */ + P9_42_default_pin: pinmux_P9_42_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_pin: pinmux_P9_42_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_pu_pin: pinmux_P9_42_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_pd_pin: pinmux_P9_42_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_input_pin: pinmux_P9_42_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_INPUT | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_pwm_pin: pinmux_P9_42_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE0) >; }; /* eCAP0_in_PWM0_out.ecap0_in_pwm0_out */ + P9_42_uart_pin: pinmux_P9_42_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* eCAP0_in_PWM0_out.uart3_txd */ + P9_42_spi_cs_pin: pinmux_P9_42_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* eCAP0_in_PWM0_out.spi1_cs1 */ + P9_42_pru_ecap_pin: pinmux_P9_42_pru_ecap_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* eCAP0_in_PWM0_out.pr1_ecap0_ecap_capin_apwm_o */ + P9_42_spi_sclk_pin: pinmux_P9_42_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* eCAP0_in_PWM0_out.spi1_sclk */ + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) gpio3_18 */ + P9_92_default_pin: pinmux_P9_92_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_pin: pinmux_P9_92_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_pu_pin: pinmux_P9_92_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_pd_pin: pinmux_P9_92_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_input_pin: pinmux_P9_92_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_qep_pin: pinmux_P9_92_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkr.eqep0a_in */ + P9_92_pruout_pin: pinmux_P9_92_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_aclkr.pru0_out4 */ + P9_92_pruin_pin: pinmux_P9_92_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkr.pru0_in4 */ + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/1"; +}; + +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/2"; +}; + +&uart1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/1"; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/2"; +}; + +&uart3 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/3"; +}; + +&uart4 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/4"; +}; + +&uart5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/5"; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/0"; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/1"; +}; + +&eqep0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/0"; +}; + +&eqep1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/1"; +}; + +&eqep2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/2"; +}; + +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&spi0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +&spi1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +/**********************************************************************/ +/* Pin Multiplex Helpers */ +/* */ +/* These provide userspace runtime pin configuration for the */ +/* BeagleBone cape expansion headers */ +/**********************************************************************/ + +&ocp { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) emmc */ + P8_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_03_default_pin>; + pinctrl-1 = <&P8_03_gpio_pin>; + pinctrl-2 = <&P8_03_gpio_pu_pin>; + pinctrl-3 = <&P8_03_gpio_pd_pin>; + pinctrl-4 = <&P8_03_gpio_input_pin>; + }; + + /* P8_04 (ZCZ ball T9) emmc */ + P8_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_04_default_pin>; + pinctrl-1 = <&P8_04_gpio_pin>; + pinctrl-2 = <&P8_04_gpio_pu_pin>; + pinctrl-3 = <&P8_04_gpio_pd_pin>; + pinctrl-4 = <&P8_04_gpio_input_pin>; + }; + + /* P8_05 (ZCZ ball R8) emmc */ + P8_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_05_default_pin>; + pinctrl-1 = <&P8_05_gpio_pin>; + pinctrl-2 = <&P8_05_gpio_pu_pin>; + pinctrl-3 = <&P8_05_gpio_pd_pin>; + pinctrl-4 = <&P8_05_gpio_input_pin>; + }; + + /* P8_06 (ZCZ ball T8) emmc */ + P8_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_06_default_pin>; + pinctrl-1 = <&P8_06_gpio_pin>; + pinctrl-2 = <&P8_06_gpio_pu_pin>; + pinctrl-3 = <&P8_06_gpio_pd_pin>; + pinctrl-4 = <&P8_06_gpio_input_pin>; + }; + + /* P8_07 (ZCZ ball R7) */ + P8_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_07_default_pin>; + pinctrl-1 = <&P8_07_gpio_pin>; + pinctrl-2 = <&P8_07_gpio_pu_pin>; + pinctrl-3 = <&P8_07_gpio_pd_pin>; + pinctrl-4 = <&P8_07_gpio_input_pin>; + pinctrl-5 = <&P8_07_timer_pin>; + }; + + /* P8_08 (ZCZ ball T7) */ + P8_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_08_default_pin>; + pinctrl-1 = <&P8_08_gpio_pin>; + pinctrl-2 = <&P8_08_gpio_pu_pin>; + pinctrl-3 = <&P8_08_gpio_pd_pin>; + pinctrl-4 = <&P8_08_gpio_input_pin>; + pinctrl-5 = <&P8_08_timer_pin>; + }; + + /* P8_09 (ZCZ ball T6) */ + P8_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_09_default_pin>; + pinctrl-1 = <&P8_09_gpio_pin>; + pinctrl-2 = <&P8_09_gpio_pu_pin>; + pinctrl-3 = <&P8_09_gpio_pd_pin>; + pinctrl-4 = <&P8_09_gpio_input_pin>; + pinctrl-5 = <&P8_09_timer_pin>; + }; + + /* P8_10 (ZCZ ball U6) */ + P8_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_10_default_pin>; + pinctrl-1 = <&P8_10_gpio_pin>; + pinctrl-2 = <&P8_10_gpio_pu_pin>; + pinctrl-3 = <&P8_10_gpio_pd_pin>; + pinctrl-4 = <&P8_10_gpio_input_pin>; + pinctrl-5 = <&P8_10_timer_pin>; + }; + + /* P8_11 (ZCZ ball R12) */ + P8_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout"; + pinctrl-0 = <&P8_11_default_pin>; + pinctrl-1 = <&P8_11_gpio_pin>; + pinctrl-2 = <&P8_11_gpio_pu_pin>; + pinctrl-3 = <&P8_11_gpio_pd_pin>; + pinctrl-4 = <&P8_11_gpio_input_pin>; + pinctrl-5 = <&P8_11_qep_pin>; + pinctrl-6 = <&P8_11_pruout_pin>; + }; + + /* P8_12 (ZCZ ball T12) */ + P8_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout"; + pinctrl-0 = <&P8_12_default_pin>; + pinctrl-1 = <&P8_12_gpio_pin>; + pinctrl-2 = <&P8_12_gpio_pu_pin>; + pinctrl-3 = <&P8_12_gpio_pd_pin>; + pinctrl-4 = <&P8_12_gpio_input_pin>; + pinctrl-5 = <&P8_12_qep_pin>; + pinctrl-6 = <&P8_12_pruout_pin>; + }; + + /* P8_13 (ZCZ ball T10) */ + P8_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_13_default_pin>; + pinctrl-1 = <&P8_13_gpio_pin>; + pinctrl-2 = <&P8_13_gpio_pu_pin>; + pinctrl-3 = <&P8_13_gpio_pd_pin>; + pinctrl-4 = <&P8_13_gpio_input_pin>; + pinctrl-5 = <&P8_13_pwm_pin>; + }; + + /* P8_14 (ZCZ ball T11) */ + P8_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_14_default_pin>; + pinctrl-1 = <&P8_14_gpio_pin>; + pinctrl-2 = <&P8_14_gpio_pu_pin>; + pinctrl-3 = <&P8_14_gpio_pd_pin>; + pinctrl-4 = <&P8_14_gpio_input_pin>; + pinctrl-5 = <&P8_14_pwm_pin>; + }; + + /* P8_15 (ZCZ ball U13) */ + P8_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pru_ecap", "pruin"; + pinctrl-0 = <&P8_15_default_pin>; + pinctrl-1 = <&P8_15_gpio_pin>; + pinctrl-2 = <&P8_15_gpio_pu_pin>; + pinctrl-3 = <&P8_15_gpio_pd_pin>; + pinctrl-4 = <&P8_15_gpio_input_pin>; + pinctrl-5 = <&P8_15_qep_pin>; + pinctrl-6 = <&P8_15_pru_ecap_pin>; + pinctrl-7 = <&P8_15_pruin_pin>; + }; + + /* P8_16 (ZCZ ball V13) */ + P8_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruin"; + pinctrl-0 = <&P8_16_default_pin>; + pinctrl-1 = <&P8_16_gpio_pin>; + pinctrl-2 = <&P8_16_gpio_pu_pin>; + pinctrl-3 = <&P8_16_gpio_pd_pin>; + pinctrl-4 = <&P8_16_gpio_input_pin>; + pinctrl-5 = <&P8_16_qep_pin>; + pinctrl-6 = <&P8_16_pruin_pin>; + }; + + /* P8_17 (ZCZ ball U12) */ + P8_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_17_default_pin>; + pinctrl-1 = <&P8_17_gpio_pin>; + pinctrl-2 = <&P8_17_gpio_pu_pin>; + pinctrl-3 = <&P8_17_gpio_pd_pin>; + pinctrl-4 = <&P8_17_gpio_input_pin>; + pinctrl-5 = <&P8_17_pwm_pin>; + }; + + /* P8_18 (ZCZ ball V12) */ + P8_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_18_default_pin>; + pinctrl-1 = <&P8_18_gpio_pin>; + pinctrl-2 = <&P8_18_gpio_pu_pin>; + pinctrl-3 = <&P8_18_gpio_pd_pin>; + pinctrl-4 = <&P8_18_gpio_input_pin>; + }; + + /* P8_19 (ZCZ ball U10) */ + P8_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_19_default_pin>; + pinctrl-1 = <&P8_19_gpio_pin>; + pinctrl-2 = <&P8_19_gpio_pu_pin>; + pinctrl-3 = <&P8_19_gpio_pd_pin>; + pinctrl-4 = <&P8_19_gpio_input_pin>; + pinctrl-5 = <&P8_19_pwm_pin>; + }; + + /* P8_20 (ZCZ ball V9) emmc */ + P8_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_20_default_pin>; + pinctrl-1 = <&P8_20_gpio_pin>; + pinctrl-2 = <&P8_20_gpio_pu_pin>; + pinctrl-3 = <&P8_20_gpio_pd_pin>; + pinctrl-4 = <&P8_20_gpio_input_pin>; + pinctrl-5 = <&P8_20_pruout_pin>; + pinctrl-6 = <&P8_20_pruin_pin>; + }; + + /* P8_21 (ZCZ ball U9) emmc */ + P8_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_21_default_pin>; + pinctrl-1 = <&P8_21_gpio_pin>; + pinctrl-2 = <&P8_21_gpio_pu_pin>; + pinctrl-3 = <&P8_21_gpio_pd_pin>; + pinctrl-4 = <&P8_21_gpio_input_pin>; + pinctrl-5 = <&P8_21_pruout_pin>; + pinctrl-6 = <&P8_21_pruin_pin>; + }; + + /* P8_22 (ZCZ ball V8) emmc */ + P8_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_22_default_pin>; + pinctrl-1 = <&P8_22_gpio_pin>; + pinctrl-2 = <&P8_22_gpio_pu_pin>; + pinctrl-3 = <&P8_22_gpio_pd_pin>; + pinctrl-4 = <&P8_22_gpio_input_pin>; + }; + + /* P8_23 (ZCZ ball U8) emmc */ + P8_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_23_default_pin>; + pinctrl-1 = <&P8_23_gpio_pin>; + pinctrl-2 = <&P8_23_gpio_pu_pin>; + pinctrl-3 = <&P8_23_gpio_pd_pin>; + pinctrl-4 = <&P8_23_gpio_input_pin>; + }; + + /* P8_24 (ZCZ ball V7) emmc */ + P8_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_24_default_pin>; + pinctrl-1 = <&P8_24_gpio_pin>; + pinctrl-2 = <&P8_24_gpio_pu_pin>; + pinctrl-3 = <&P8_24_gpio_pd_pin>; + pinctrl-4 = <&P8_24_gpio_input_pin>; + }; + + /* P8_25 (ZCZ ball U7) emmc */ + P8_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_25_default_pin>; + pinctrl-1 = <&P8_25_gpio_pin>; + pinctrl-2 = <&P8_25_gpio_pu_pin>; + pinctrl-3 = <&P8_25_gpio_pd_pin>; + pinctrl-4 = <&P8_25_gpio_input_pin>; + }; + + /* P8_26 (ZCZ ball V6) */ + P8_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_26_default_pin>; + pinctrl-1 = <&P8_26_gpio_pin>; + pinctrl-2 = <&P8_26_gpio_pu_pin>; + pinctrl-3 = <&P8_26_gpio_pd_pin>; + pinctrl-4 = <&P8_26_gpio_input_pin>; + }; + + /* P8_27 (ZCZ ball U5) hdmi */ + P8_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_27_default_pin>; + pinctrl-1 = <&P8_27_gpio_pin>; + pinctrl-2 = <&P8_27_gpio_pu_pin>; + pinctrl-3 = <&P8_27_gpio_pd_pin>; + pinctrl-4 = <&P8_27_gpio_input_pin>; + pinctrl-5 = <&P8_27_pruout_pin>; + pinctrl-6 = <&P8_27_pruin_pin>; + }; + + /* P8_28 (ZCZ ball V5) hdmi */ + P8_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_28_default_pin>; + pinctrl-1 = <&P8_28_gpio_pin>; + pinctrl-2 = <&P8_28_gpio_pu_pin>; + pinctrl-3 = <&P8_28_gpio_pd_pin>; + pinctrl-4 = <&P8_28_gpio_input_pin>; + pinctrl-5 = <&P8_28_pruout_pin>; + pinctrl-6 = <&P8_28_pruin_pin>; + }; + + /* P8_29 (ZCZ ball R5) hdmi */ + P8_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_29_default_pin>; + pinctrl-1 = <&P8_29_gpio_pin>; + pinctrl-2 = <&P8_29_gpio_pu_pin>; + pinctrl-3 = <&P8_29_gpio_pd_pin>; + pinctrl-4 = <&P8_29_gpio_input_pin>; + pinctrl-5 = <&P8_29_pruout_pin>; + pinctrl-6 = <&P8_29_pruin_pin>; + }; + + /* P8_30 (ZCZ ball R6) hdmi */ + P8_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_30_default_pin>; + pinctrl-1 = <&P8_30_gpio_pin>; + pinctrl-2 = <&P8_30_gpio_pu_pin>; + pinctrl-3 = <&P8_30_gpio_pd_pin>; + pinctrl-4 = <&P8_30_gpio_input_pin>; + pinctrl-5 = <&P8_30_pruout_pin>; + pinctrl-6 = <&P8_30_pruin_pin>; + }; + + /* P8_31 (ZCZ ball V4) hdmi */ + P8_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "qep"; + pinctrl-0 = <&P8_31_default_pin>; + pinctrl-1 = <&P8_31_gpio_pin>; + pinctrl-2 = <&P8_31_gpio_pu_pin>; + pinctrl-3 = <&P8_31_gpio_pd_pin>; + pinctrl-4 = <&P8_31_gpio_input_pin>; + pinctrl-5 = <&P8_31_uart_pin>; + pinctrl-6 = <&P8_31_qep_pin>; + }; + + /* P8_32 (ZCZ ball T5) hdmi */ + P8_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P8_32_default_pin>; + pinctrl-1 = <&P8_32_gpio_pin>; + pinctrl-2 = <&P8_32_gpio_pu_pin>; + pinctrl-3 = <&P8_32_gpio_pd_pin>; + pinctrl-4 = <&P8_32_gpio_input_pin>; + pinctrl-5 = <&P8_32_qep_pin>; + }; + + /* P8_33 (ZCZ ball V3) hdmi */ + P8_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P8_33_default_pin>; + pinctrl-1 = <&P8_33_gpio_pin>; + pinctrl-2 = <&P8_33_gpio_pu_pin>; + pinctrl-3 = <&P8_33_gpio_pd_pin>; + pinctrl-4 = <&P8_33_gpio_input_pin>; + pinctrl-5 = <&P8_33_qep_pin>; + }; + + /* P8_34 (ZCZ ball U4) hdmi */ + P8_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_34_default_pin>; + pinctrl-1 = <&P8_34_gpio_pin>; + pinctrl-2 = <&P8_34_gpio_pu_pin>; + pinctrl-3 = <&P8_34_gpio_pd_pin>; + pinctrl-4 = <&P8_34_gpio_input_pin>; + pinctrl-5 = <&P8_34_pwm_pin>; + }; + + /* P8_35 (ZCZ ball V2) hdmi */ + P8_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P8_35_default_pin>; + pinctrl-1 = <&P8_35_gpio_pin>; + pinctrl-2 = <&P8_35_gpio_pu_pin>; + pinctrl-3 = <&P8_35_gpio_pd_pin>; + pinctrl-4 = <&P8_35_gpio_input_pin>; + pinctrl-5 = <&P8_35_qep_pin>; + }; + + /* P8_36 (ZCZ ball U3) hdmi */ + P8_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_36_default_pin>; + pinctrl-1 = <&P8_36_gpio_pin>; + pinctrl-2 = <&P8_36_gpio_pu_pin>; + pinctrl-3 = <&P8_36_gpio_pd_pin>; + pinctrl-4 = <&P8_36_gpio_input_pin>; + pinctrl-5 = <&P8_36_pwm_pin>; + }; + + /* P8_37 (ZCZ ball U1) hdmi */ + P8_37_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "pwm"; + pinctrl-0 = <&P8_37_default_pin>; + pinctrl-1 = <&P8_37_gpio_pin>; + pinctrl-2 = <&P8_37_gpio_pu_pin>; + pinctrl-3 = <&P8_37_gpio_pd_pin>; + pinctrl-4 = <&P8_37_gpio_input_pin>; + pinctrl-5 = <&P8_37_uart_pin>; + pinctrl-6 = <&P8_37_pwm_pin>; + }; + + /* P8_38 (ZCZ ball U2) hdmi */ + P8_38_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "pwm"; + pinctrl-0 = <&P8_38_default_pin>; + pinctrl-1 = <&P8_38_gpio_pin>; + pinctrl-2 = <&P8_38_gpio_pu_pin>; + pinctrl-3 = <&P8_38_gpio_pd_pin>; + pinctrl-4 = <&P8_38_gpio_input_pin>; + pinctrl-5 = <&P8_38_uart_pin>; + pinctrl-6 = <&P8_38_pwm_pin>; + }; + + /* P8_39 (ZCZ ball T3) hdmi */ + P8_39_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_39_default_pin>; + pinctrl-1 = <&P8_39_gpio_pin>; + pinctrl-2 = <&P8_39_gpio_pu_pin>; + pinctrl-3 = <&P8_39_gpio_pd_pin>; + pinctrl-4 = <&P8_39_gpio_input_pin>; + pinctrl-5 = <&P8_39_qep_pin>; + pinctrl-6 = <&P8_39_pruout_pin>; + pinctrl-7 = <&P8_39_pruin_pin>; + }; + + /* P8_40 (ZCZ ball T4) hdmi */ + P8_40_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_40_default_pin>; + pinctrl-1 = <&P8_40_gpio_pin>; + pinctrl-2 = <&P8_40_gpio_pu_pin>; + pinctrl-3 = <&P8_40_gpio_pd_pin>; + pinctrl-4 = <&P8_40_gpio_input_pin>; + pinctrl-5 = <&P8_40_qep_pin>; + pinctrl-6 = <&P8_40_pruout_pin>; + pinctrl-7 = <&P8_40_pruin_pin>; + }; + + /* P8_41 (ZCZ ball T1) hdmi */ + P8_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_41_default_pin>; + pinctrl-1 = <&P8_41_gpio_pin>; + pinctrl-2 = <&P8_41_gpio_pu_pin>; + pinctrl-3 = <&P8_41_gpio_pd_pin>; + pinctrl-4 = <&P8_41_gpio_input_pin>; + pinctrl-5 = <&P8_41_qep_pin>; + pinctrl-6 = <&P8_41_pruout_pin>; + pinctrl-7 = <&P8_41_pruin_pin>; + }; + + /* P8_42 (ZCZ ball T2) hdmi */ + P8_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_42_default_pin>; + pinctrl-1 = <&P8_42_gpio_pin>; + pinctrl-2 = <&P8_42_gpio_pu_pin>; + pinctrl-3 = <&P8_42_gpio_pd_pin>; + pinctrl-4 = <&P8_42_gpio_input_pin>; + pinctrl-5 = <&P8_42_qep_pin>; + pinctrl-6 = <&P8_42_pruout_pin>; + pinctrl-7 = <&P8_42_pruin_pin>; + }; + + /* P8_43 (ZCZ ball R3) hdmi */ + P8_43_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_43_default_pin>; + pinctrl-1 = <&P8_43_gpio_pin>; + pinctrl-2 = <&P8_43_gpio_pu_pin>; + pinctrl-3 = <&P8_43_gpio_pd_pin>; + pinctrl-4 = <&P8_43_gpio_input_pin>; + pinctrl-5 = <&P8_43_pwm_pin>; + pinctrl-6 = <&P8_43_pruout_pin>; + pinctrl-7 = <&P8_43_pruin_pin>; + }; + + /* P8_44 (ZCZ ball R4) hdmi */ + P8_44_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_44_default_pin>; + pinctrl-1 = <&P8_44_gpio_pin>; + pinctrl-2 = <&P8_44_gpio_pu_pin>; + pinctrl-3 = <&P8_44_gpio_pd_pin>; + pinctrl-4 = <&P8_44_gpio_input_pin>; + pinctrl-5 = <&P8_44_pwm_pin>; + pinctrl-6 = <&P8_44_pruout_pin>; + pinctrl-7 = <&P8_44_pruin_pin>; + }; + + /* P8_45 (ZCZ ball R1) hdmi */ + P8_45_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_45_default_pin>; + pinctrl-1 = <&P8_45_gpio_pin>; + pinctrl-2 = <&P8_45_gpio_pu_pin>; + pinctrl-3 = <&P8_45_gpio_pd_pin>; + pinctrl-4 = <&P8_45_gpio_input_pin>; + pinctrl-5 = <&P8_45_pwm_pin>; + pinctrl-6 = <&P8_45_pruout_pin>; + pinctrl-7 = <&P8_45_pruin_pin>; + }; + + /* P8_46 (ZCZ ball R2) hdmi */ + P8_46_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_46_default_pin>; + pinctrl-1 = <&P8_46_gpio_pin>; + pinctrl-2 = <&P8_46_gpio_pu_pin>; + pinctrl-3 = <&P8_46_gpio_pd_pin>; + pinctrl-4 = <&P8_46_gpio_input_pin>; + pinctrl-5 = <&P8_46_pwm_pin>; + pinctrl-6 = <&P8_46_pruout_pin>; + pinctrl-7 = <&P8_46_pruin_pin>; + }; + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) */ + P9_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart"; + pinctrl-0 = <&P9_11_default_pin>; + pinctrl-1 = <&P9_11_gpio_pin>; + pinctrl-2 = <&P9_11_gpio_pu_pin>; + pinctrl-3 = <&P9_11_gpio_pd_pin>; + pinctrl-4 = <&P9_11_gpio_input_pin>; + pinctrl-5 = <&P9_11_uart_pin>; + }; + + /* P9_12 (ZCZ ball U18) */ + P9_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P9_12_default_pin>; + pinctrl-1 = <&P9_12_gpio_pin>; + pinctrl-2 = <&P9_12_gpio_pu_pin>; + pinctrl-3 = <&P9_12_gpio_pd_pin>; + pinctrl-4 = <&P9_12_gpio_input_pin>; + }; + + /* P9_13 (ZCZ ball U17) */ + P9_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart"; + pinctrl-0 = <&P9_13_default_pin>; + pinctrl-1 = <&P9_13_gpio_pin>; + pinctrl-2 = <&P9_13_gpio_pu_pin>; + pinctrl-3 = <&P9_13_gpio_pd_pin>; + pinctrl-4 = <&P9_13_gpio_input_pin>; + pinctrl-5 = <&P9_13_uart_pin>; + }; + + /* P9_14 (ZCZ ball U14) */ + P9_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_14_default_pin>; + pinctrl-1 = <&P9_14_gpio_pin>; + pinctrl-2 = <&P9_14_gpio_pu_pin>; + pinctrl-3 = <&P9_14_gpio_pd_pin>; + pinctrl-4 = <&P9_14_gpio_input_pin>; + pinctrl-5 = <&P9_14_pwm_pin>; + }; + + /* P9_15 (ZCZ ball R13) */ + P9_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_15_default_pin>; + pinctrl-1 = <&P9_15_gpio_pin>; + pinctrl-2 = <&P9_15_gpio_pu_pin>; + pinctrl-3 = <&P9_15_gpio_pd_pin>; + pinctrl-4 = <&P9_15_gpio_input_pin>; + pinctrl-5 = <&P9_15_pwm_pin>; + }; + + /* P9_16 (ZCZ ball T14) */ + P9_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_16_default_pin>; + pinctrl-1 = <&P9_16_gpio_pin>; + pinctrl-2 = <&P9_16_gpio_pu_pin>; + pinctrl-3 = <&P9_16_gpio_pd_pin>; + pinctrl-4 = <&P9_16_gpio_input_pin>; + pinctrl-5 = <&P9_16_pwm_pin>; + }; + + /* P9_17 (ZCZ ball A16) */ + P9_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_17_default_pin>; + pinctrl-1 = <&P9_17_gpio_pin>; + pinctrl-2 = <&P9_17_gpio_pu_pin>; + pinctrl-3 = <&P9_17_gpio_pd_pin>; + pinctrl-4 = <&P9_17_gpio_input_pin>; + pinctrl-5 = <&P9_17_spi_cs_pin>; + pinctrl-6 = <&P9_17_i2c_pin>; + pinctrl-7 = <&P9_17_pwm_pin>; + pinctrl-8 = <&P9_17_pru_uart_pin>; + }; + + /* P9_18 (ZCZ ball B16) */ + P9_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_18_default_pin>; + pinctrl-1 = <&P9_18_gpio_pin>; + pinctrl-2 = <&P9_18_gpio_pu_pin>; + pinctrl-3 = <&P9_18_gpio_pd_pin>; + pinctrl-4 = <&P9_18_gpio_input_pin>; + pinctrl-5 = <&P9_18_spi_pin>; + pinctrl-6 = <&P9_18_i2c_pin>; + pinctrl-7 = <&P9_18_pwm_pin>; + pinctrl-8 = <&P9_18_pru_uart_pin>; + }; + + /* P9_19 (ZCZ ball D17) i2c */ + P9_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_19_default_pin>; + pinctrl-1 = <&P9_19_gpio_pin>; + pinctrl-2 = <&P9_19_gpio_pu_pin>; + pinctrl-3 = <&P9_19_gpio_pd_pin>; + pinctrl-4 = <&P9_19_gpio_input_pin>; + pinctrl-5 = <&P9_19_spi_cs_pin>; + pinctrl-6 = <&P9_19_can_pin>; + pinctrl-7 = <&P9_19_i2c_pin>; + pinctrl-8 = <&P9_19_pru_uart_pin>; + pinctrl-9 = <&P9_19_timer_pin>; + }; + + /* P9_20 (ZCZ ball D18) i2c */ + P9_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_20_default_pin>; + pinctrl-1 = <&P9_20_gpio_pin>; + pinctrl-2 = <&P9_20_gpio_pu_pin>; + pinctrl-3 = <&P9_20_gpio_pd_pin>; + pinctrl-4 = <&P9_20_gpio_input_pin>; + pinctrl-5 = <&P9_20_spi_cs_pin>; + pinctrl-6 = <&P9_20_can_pin>; + pinctrl-7 = <&P9_20_i2c_pin>; + pinctrl-8 = <&P9_20_pru_uart_pin>; + pinctrl-9 = <&P9_20_timer_pin>; + }; + + /* P9_21 (ZCZ ball B17) */ + P9_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_21_default_pin>; + pinctrl-1 = <&P9_21_gpio_pin>; + pinctrl-2 = <&P9_21_gpio_pu_pin>; + pinctrl-3 = <&P9_21_gpio_pd_pin>; + pinctrl-4 = <&P9_21_gpio_input_pin>; + pinctrl-5 = <&P9_21_spi_pin>; + pinctrl-6 = <&P9_21_uart_pin>; + pinctrl-7 = <&P9_21_i2c_pin>; + pinctrl-8 = <&P9_21_pwm_pin>; + pinctrl-9 = <&P9_21_pru_uart_pin>; + }; + + /* P9_22 (ZCZ ball A17) */ + P9_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_sclk", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_22_default_pin>; + pinctrl-1 = <&P9_22_gpio_pin>; + pinctrl-2 = <&P9_22_gpio_pu_pin>; + pinctrl-3 = <&P9_22_gpio_pd_pin>; + pinctrl-4 = <&P9_22_gpio_input_pin>; + pinctrl-5 = <&P9_22_spi_sclk_pin>; + pinctrl-6 = <&P9_22_uart_pin>; + pinctrl-7 = <&P9_22_i2c_pin>; + pinctrl-8 = <&P9_22_pwm_pin>; + pinctrl-9 = <&P9_22_pru_uart_pin>; + }; + + /* P9_23 (ZCZ ball V14) */ + P9_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_23_default_pin>; + pinctrl-1 = <&P9_23_gpio_pin>; + pinctrl-2 = <&P9_23_gpio_pu_pin>; + pinctrl-3 = <&P9_23_gpio_pd_pin>; + pinctrl-4 = <&P9_23_gpio_input_pin>; + pinctrl-5 = <&P9_23_pwm_pin>; + }; + + /* P9_24 (ZCZ ball D15) */ + P9_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_24_default_pin>; + pinctrl-1 = <&P9_24_gpio_pin>; + pinctrl-2 = <&P9_24_gpio_pu_pin>; + pinctrl-3 = <&P9_24_gpio_pd_pin>; + pinctrl-4 = <&P9_24_gpio_input_pin>; + pinctrl-5 = <&P9_24_uart_pin>; + pinctrl-6 = <&P9_24_can_pin>; + pinctrl-7 = <&P9_24_i2c_pin>; + pinctrl-8 = <&P9_24_pru_uart_pin>; + pinctrl-9 = <&P9_24_pruin_pin>; + }; + + /* P9_25 (ZCZ ball A14) audio */ + P9_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_25_default_pin>; + pinctrl-1 = <&P9_25_gpio_pin>; + pinctrl-2 = <&P9_25_gpio_pu_pin>; + pinctrl-3 = <&P9_25_gpio_pd_pin>; + pinctrl-4 = <&P9_25_gpio_input_pin>; + pinctrl-5 = <&P9_25_qep_pin>; + pinctrl-6 = <&P9_25_pruout_pin>; + pinctrl-7 = <&P9_25_pruin_pin>; + }; + + /* P9_26 (ZCZ ball D16) */ + P9_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_26_default_pin>; + pinctrl-1 = <&P9_26_gpio_pin>; + pinctrl-2 = <&P9_26_gpio_pu_pin>; + pinctrl-3 = <&P9_26_gpio_pd_pin>; + pinctrl-4 = <&P9_26_gpio_input_pin>; + pinctrl-5 = <&P9_26_uart_pin>; + pinctrl-6 = <&P9_26_can_pin>; + pinctrl-7 = <&P9_26_i2c_pin>; + pinctrl-8 = <&P9_26_pru_uart_pin>; + pinctrl-9 = <&P9_26_pruin_pin>; + }; + + /* P9_27 (ZCZ ball C13) */ + P9_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_27_default_pin>; + pinctrl-1 = <&P9_27_gpio_pin>; + pinctrl-2 = <&P9_27_gpio_pu_pin>; + pinctrl-3 = <&P9_27_gpio_pd_pin>; + pinctrl-4 = <&P9_27_gpio_input_pin>; + pinctrl-5 = <&P9_27_qep_pin>; + pinctrl-6 = <&P9_27_pruout_pin>; + pinctrl-7 = <&P9_27_pruin_pin>; + }; + + /* P9_28 (ZCZ ball C12) audio */ + P9_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "pwm", "pwm2", "pruout", "pruin"; + pinctrl-0 = <&P9_28_default_pin>; + pinctrl-1 = <&P9_28_gpio_pin>; + pinctrl-2 = <&P9_28_gpio_pu_pin>; + pinctrl-3 = <&P9_28_gpio_pd_pin>; + pinctrl-4 = <&P9_28_gpio_input_pin>; + pinctrl-5 = <&P9_28_spi_cs_pin>; + pinctrl-6 = <&P9_28_pwm_pin>; + pinctrl-7 = <&P9_28_pwm2_pin>; + pinctrl-8 = <&P9_28_pruout_pin>; + pinctrl-9 = <&P9_28_pruin_pin>; + }; + + /* P9_29 (ZCZ ball B13) audio */ + P9_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_29_default_pin>; + pinctrl-1 = <&P9_29_gpio_pin>; + pinctrl-2 = <&P9_29_gpio_pu_pin>; + pinctrl-3 = <&P9_29_gpio_pd_pin>; + pinctrl-4 = <&P9_29_gpio_input_pin>; + pinctrl-5 = <&P9_29_spi_pin>; + pinctrl-6 = <&P9_29_pwm_pin>; + pinctrl-7 = <&P9_29_pruout_pin>; + pinctrl-8 = <&P9_29_pruin_pin>; + }; + + /* P9_30 (ZCZ ball D12) */ + P9_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_30_default_pin>; + pinctrl-1 = <&P9_30_gpio_pin>; + pinctrl-2 = <&P9_30_gpio_pu_pin>; + pinctrl-3 = <&P9_30_gpio_pd_pin>; + pinctrl-4 = <&P9_30_gpio_input_pin>; + pinctrl-5 = <&P9_30_spi_pin>; + pinctrl-6 = <&P9_30_pwm_pin>; + pinctrl-7 = <&P9_30_pruout_pin>; + pinctrl-8 = <&P9_30_pruin_pin>; + }; + + /* P9_31 (ZCZ ball A13) audio */ + P9_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_sclk", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_31_default_pin>; + pinctrl-1 = <&P9_31_gpio_pin>; + pinctrl-2 = <&P9_31_gpio_pu_pin>; + pinctrl-3 = <&P9_31_gpio_pd_pin>; + pinctrl-4 = <&P9_31_gpio_input_pin>; + pinctrl-5 = <&P9_31_spi_sclk_pin>; + pinctrl-6 = <&P9_31_pwm_pin>; + pinctrl-7 = <&P9_31_pruout_pin>; + pinctrl-8 = <&P9_31_pruin_pin>; + }; + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) */ + P9_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer", "pruin"; + pinctrl-0 = <&P9_41_default_pin>; + pinctrl-1 = <&P9_41_gpio_pin>; + pinctrl-2 = <&P9_41_gpio_pu_pin>; + pinctrl-3 = <&P9_41_gpio_pd_pin>; + pinctrl-4 = <&P9_41_gpio_input_pin>; + pinctrl-5 = <&P9_41_timer_pin>; + pinctrl-6 = <&P9_41_pruin_pin>; + }; + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) */ + P9_91_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_91_default_pin>; + pinctrl-1 = <&P9_91_gpio_pin>; + pinctrl-2 = <&P9_91_gpio_pu_pin>; + pinctrl-3 = <&P9_91_gpio_pd_pin>; + pinctrl-4 = <&P9_91_gpio_input_pin>; + pinctrl-5 = <&P9_91_qep_pin>; + pinctrl-6 = <&P9_91_pruout_pin>; + pinctrl-7 = <&P9_91_pruin_pin>; + }; + + /* P9_42 (ZCZ ball C18) */ + P9_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "spi_sclk", "uart", "pwm", "pru_ecap"; + pinctrl-0 = <&P9_42_default_pin>; + pinctrl-1 = <&P9_42_gpio_pin>; + pinctrl-2 = <&P9_42_gpio_pu_pin>; + pinctrl-3 = <&P9_42_gpio_pd_pin>; + pinctrl-4 = <&P9_42_gpio_input_pin>; + pinctrl-5 = <&P9_42_spi_cs_pin>; + pinctrl-6 = <&P9_42_spi_sclk_pin>; + pinctrl-7 = <&P9_42_uart_pin>; + pinctrl-8 = <&P9_42_pwm_pin>; + pinctrl-9 = <&P9_42_pru_ecap_pin>; + }; + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) */ + P9_92_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_92_default_pin>; + pinctrl-1 = <&P9_92_gpio_pin>; + pinctrl-2 = <&P9_92_gpio_pu_pin>; + pinctrl-3 = <&P9_92_gpio_pd_pin>; + pinctrl-4 = <&P9_92_gpio_input_pin>; + pinctrl-5 = <&P9_92_qep_pin>; + pinctrl-6 = <&P9_92_pruout_pin>; + pinctrl-7 = <&P9_92_pruin_pin>; + }; + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P8_03 { + gpio-name = "P8_03"; + gpio = <&gpio1 6 0>; + input; + dir-changeable; + }; + + P8_04 { + gpio-name = "P8_04"; + gpio = <&gpio1 7 0>; + input; + dir-changeable; + }; + + P8_05 { + gpio-name = "P8_05"; + gpio = <&gpio1 2 0>; + input; + dir-changeable; + }; + + P8_06 { + gpio-name = "P8_06"; + gpio = <&gpio1 3 0>; + input; + dir-changeable; + }; + + P8_07 { + gpio-name = "P8_07"; + gpio = <&gpio2 2 0>; + input; + dir-changeable; + }; + + P8_08 { + gpio-name = "P8_08"; + gpio = <&gpio2 3 0>; + input; + dir-changeable; + }; + + P8_09 { + gpio-name = "P8_09"; + gpio = <&gpio2 5 0>; + input; + dir-changeable; + }; + + P8_10 { + gpio-name = "P8_10"; + gpio = <&gpio2 4 0>; + input; + dir-changeable; + }; + + P8_11 { + gpio-name = "P8_11"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + + P8_12 { + gpio-name = "P8_12"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + + P8_13 { + gpio-name = "P8_13"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + + P8_14 { + gpio-name = "P8_14"; + gpio = <&gpio0 26 0>; + input; + dir-changeable; + }; + + P8_15 { + gpio-name = "P8_15"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + + P8_16 { + gpio-name = "P8_16"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + + P8_17 { + gpio-name = "P8_17"; + gpio = <&gpio0 27 0>; + input; + dir-changeable; + }; + + P8_18 { + gpio-name = "P8_18"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + + P8_19 { + gpio-name = "P8_19"; + gpio = <&gpio0 22 0>; + input; + dir-changeable; + }; + + P8_20 { + gpio-name = "P8_20"; + gpio = <&gpio1 31 0>; + input; + dir-changeable; + }; + + P8_21 { + gpio-name = "P8_21"; + gpio = <&gpio1 30 0>; + input; + dir-changeable; + }; + + P8_22 { + gpio-name = "P8_22"; + gpio = <&gpio1 5 0>; + input; + dir-changeable; + }; + + P8_23 { + gpio-name = "P8_23"; + gpio = <&gpio1 4 0>; + input; + dir-changeable; + }; + + P8_24 { + gpio-name = "P8_24"; + gpio = <&gpio1 1 0>; + input; + dir-changeable; + }; + + P8_25 { + gpio-name = "P8_25"; + gpio = <&gpio1 0 0>; + input; + dir-changeable; + }; + + P8_26 { + gpio-name = "P8_26"; + gpio = <&gpio1 29 0>; + input; + dir-changeable; + }; + + P8_27 { + gpio-name = "P8_27"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + + P8_28 { + gpio-name = "P8_28"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + + P8_29 { + gpio-name = "P8_29"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + + P8_30 { + gpio-name = "P8_30"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + + P8_31 { + gpio-name = "P8_31"; + gpio = <&gpio0 10 0>; + input; + dir-changeable; + }; + + P8_32 { + gpio-name = "P8_32"; + gpio = <&gpio0 11 0>; + input; + dir-changeable; + }; + + P8_33 { + gpio-name = "P8_33"; + gpio = <&gpio0 9 0>; + input; + dir-changeable; + }; + + P8_34 { + gpio-name = "P8_34"; + gpio = <&gpio2 17 0>; + input; + dir-changeable; + }; + + P8_35 { + gpio-name = "P8_35"; + gpio = <&gpio0 8 0>; + input; + dir-changeable; + }; + + P8_36 { + gpio-name = "P8_36"; + gpio = <&gpio2 16 0>; + input; + dir-changeable; + }; + + P8_37 { + gpio-name = "P8_37"; + gpio = <&gpio2 14 0>; + input; + dir-changeable; + }; + + P8_38 { + gpio-name = "P8_38"; + gpio = <&gpio2 15 0>; + input; + dir-changeable; + }; + + P8_39 { + gpio-name = "P8_39"; + gpio = <&gpio2 12 0>; + input; + dir-changeable; + }; + + P8_40 { + gpio-name = "P8_40"; + gpio = <&gpio2 13 0>; + input; + dir-changeable; + }; + + P8_41 { + gpio-name = "P8_41"; + gpio = <&gpio2 10 0>; + input; + dir-changeable; + }; + + P8_42 { + gpio-name = "P8_42"; + gpio = <&gpio2 11 0>; + input; + dir-changeable; + }; + + P8_43 { + gpio-name = "P8_43"; + gpio = <&gpio2 8 0>; + input; + dir-changeable; + }; + + P8_44 { + gpio-name = "P8_44"; + gpio = <&gpio2 9 0>; + input; + dir-changeable; + }; + + P8_45 { + gpio-name = "P8_45"; + gpio = <&gpio2 6 0>; + input; + dir-changeable; + }; + + P8_46 { + gpio-name = "P8_46"; + gpio = <&gpio2 7 0>; + input; + dir-changeable; + }; + + P9_11 { + gpio-name = "P9_11"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + + P9_12 { + gpio-name = "P9_12"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + + P9_13 { + gpio-name = "P9_13"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + + P9_14 { + gpio-name = "P9_14"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + + P9_15 { + gpio-name = "P9_15"; + gpio = <&gpio1 16 0>; + input; + dir-changeable; + }; + + P9_16 { + gpio-name = "P9_16"; + gpio = <&gpio1 19 0>; + input; + dir-changeable; + }; + + P9_17 { + gpio-name = "P9_17"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + + P9_18 { + gpio-name = "P9_18"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + + P9_19 { + gpio-name = "P9_19"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + + P9_20 { + gpio-name = "P9_20"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + + P9_21 { + gpio-name = "P9_21"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + + P9_22 { + gpio-name = "P9_22"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + + P9_23 { + gpio-name = "P9_23"; + gpio = <&gpio1 17 0>; + input; + dir-changeable; + }; + + P9_24 { + gpio-name = "P9_24"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + + P9_25 { + gpio-name = "P9_25"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + + P9_26 { + gpio-name = "P9_26"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + + P9_27 { + gpio-name = "P9_27"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + + P9_28 { + gpio-name = "P9_28"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + + P9_29 { + gpio-name = "P9_29"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + + P9_30 { + gpio-name = "P9_30"; + gpio = <&gpio3 16 0>; + input; + dir-changeable; + }; + + P9_31 { + gpio-name = "P9_31"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + + P9_41 { + gpio-name = "P9_41"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + + P9_91 { + gpio-name = "P9_91"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + + P9_42 { + gpio-name = "P9_42"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + + P9_92 { + gpio-name = "P9_92"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts --- a/arch/arm/boot/dts/am335x-bone.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bone.dts 2022-01-06 12:45:53.806318073 -0500 @@ -10,6 +10,11 @@ / { model = "TI AM335x BeagleBone"; compatible = "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bone.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &ldo3_reg { diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bonegreen-common.dtsi b/arch/arm/boot/dts/am335x-bonegreen-common.dtsi --- a/arch/arm/boot/dts/am335x-bonegreen-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bonegreen-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -34,6 +34,7 @@ pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; status = "okay"; + symlink = "bone/uart/2"; }; &rtc { diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bonegreen.dts b/arch/arm/boot/dts/am335x-bonegreen.dts --- a/arch/arm/boot/dts/am335x-bonegreen.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bonegreen.dts 2022-01-06 12:45:53.806318073 -0500 @@ -11,4 +11,9 @@ / { model = "TI AM335x BeagleBone Green"; compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bonegreen.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bonegreen-gateway.dts b/arch/arm/boot/dts/am335x-bonegreen-gateway.dts --- a/arch/arm/boot/dts/am335x-bonegreen-gateway.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bonegreen-gateway.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bonegreen-common.dtsi" +#include + +/ { + model = "SeeedStudio BeagleBone Green Gateway"; + compatible = "ti,am335x-bone-green-gateway", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + aliases { + rtc0 = &extrtc; + rtc1 = &rtc; + }; + + chosen { + base_dtb = "am335x-bonegreen-gateway.dts"; + base_dtb_timestamp = __TIMESTAMP__; + wl1835_bt = "S3-texas-300000"; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + //pinctrl-0 = <&user_leds_s0>; + pinctrl-0 = <&user_leds_s0 &bt_pins>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + led6 { + label = "beaglebone:green:usr4"; + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "netdev"; + default-state = "off"; + }; + + wl18xx_bt_en: led7 { + label = "wl18xx_bt_en"; + gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; +}; + +&cpu0_opp_table { + /* + * Octavo Systems: + * The EFUSE_SMA register is not programmed for any of the AM335x wafers + * we get and we are not programming them during our production test. + * Therefore, from a DEVICE_ID revision point of view, the silicon looks + * like it is Revision 2.1. However, from an EFUSE_SMA point of view for + * the HW OPP table, the silicon looks like it is Revision 1.0 (ie the + * EFUSE_SMA register reads as all zeros). + */ + oppnitro-1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&usbhost_pins>; + + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a8.gpio1_24 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* WL_Active_LED / USR4 */ + >; + }; + + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txd0.gpio0_28 - BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + uart2_grove_pins: pinmux_uart2_grove_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x90c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + AM33XX_IOPAD(0x910, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + usbhost_pins: pinmux_usbhost_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_txd1.gpio0[21] */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txclk.gpio3_9 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) /* rmii1_refclk.gpio0_29 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_rxclk.gpio3_10 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_grove_pins>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + //pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + //bluetooth { + // compatible = "ti,wl1835-st"; + // enable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + //}; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + extrtc: rtc@68 { + compatible = "dallas,ds1340"; + reg = <0x68>; + }; +}; + +// (K16) gmii1_txd1.gpio0[21] +&gpio0 { + usb-reset-hog { + gpio-hog; + gpios = <21 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb_reset"; + }; +}; + +&gpio3 { + ls-buf-en-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +&usb1 { + #address-cells = <1>; + #size-cells = <0>; + + hub@1 { + compatible = "usb424,9512"; + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + ethernet: ethernet@1 { + compatible = "usb424,ec00"; + reg = <1>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi b/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi --- a/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,2783 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&am33xx_pinmux { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) emmc */ + P8_03_default_pin: pinmux_P8_03_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_pin: pinmux_P8_03_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_pu_pin: pinmux_P8_03_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_pd_pin: pinmux_P8_03_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + P8_03_gpio_input_pin: pinmux_P8_03_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0818, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad6.gpio1_6 */ + + /* P8_04 (ZCZ ball T9) emmc */ + P8_04_default_pin: pinmux_P8_04_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_pin: pinmux_P8_04_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_pu_pin: pinmux_P8_04_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_pd_pin: pinmux_P8_04_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + P8_04_gpio_input_pin: pinmux_P8_04_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x081c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad7.gpio1_7 */ + + /* P8_05 (ZCZ ball R8) emmc */ + P8_05_default_pin: pinmux_P8_05_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_pin: pinmux_P8_05_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_pu_pin: pinmux_P8_05_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_pd_pin: pinmux_P8_05_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + P8_05_gpio_input_pin: pinmux_P8_05_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0808, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad2.gpio1_2 */ + + /* P8_06 (ZCZ ball T8) emmc */ + P8_06_default_pin: pinmux_P8_06_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_pin: pinmux_P8_06_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_pu_pin: pinmux_P8_06_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_pd_pin: pinmux_P8_06_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + P8_06_gpio_input_pin: pinmux_P8_06_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x080c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad3.gpio1_3 */ + + /* P8_07 (ZCZ ball R7) gpio2_2 */ + P8_07_default_pin: pinmux_P8_07_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_pin: pinmux_P8_07_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_pu_pin: pinmux_P8_07_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_pd_pin: pinmux_P8_07_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_gpio_input_pin: pinmux_P8_07_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_advn_ale.gpio2_2 */ + P8_07_timer_pin: pinmux_P8_07_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0890, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_advn_ale.timer4 */ + + /* P8_08 (ZCZ ball T7) gpio2_3 */ + P8_08_default_pin: pinmux_P8_08_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_pin: pinmux_P8_08_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_pu_pin: pinmux_P8_08_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_pd_pin: pinmux_P8_08_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_gpio_input_pin: pinmux_P8_08_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_oen_ren.gpio2_3 */ + P8_08_timer_pin: pinmux_P8_08_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0894, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_oen_ren.timer7 */ + + /* P8_09 (ZCZ ball T6) gpio2_5 */ + P8_09_default_pin: pinmux_P8_09_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_pin: pinmux_P8_09_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_pu_pin: pinmux_P8_09_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_pd_pin: pinmux_P8_09_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_gpio_input_pin: pinmux_P8_09_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_be0n_cle.gpio2_5 */ + P8_09_timer_pin: pinmux_P8_09_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x089c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_be0n_cle.timer5 */ + + /* P8_10 (ZCZ ball U6) gpio2_4 */ + P8_10_default_pin: pinmux_P8_10_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_pin: pinmux_P8_10_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_pu_pin: pinmux_P8_10_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_pd_pin: pinmux_P8_10_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_gpio_input_pin: pinmux_P8_10_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wen.gpio2_4 */ + P8_10_timer_pin: pinmux_P8_10_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0898, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* gpmc_wen.timer6 */ + + /* P8_11 (ZCZ ball R12) gpio1_13 */ + P8_11_default_pin: pinmux_P8_11_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_pin: pinmux_P8_11_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_pu_pin: pinmux_P8_11_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_pd_pin: pinmux_P8_11_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_gpio_input_pin: pinmux_P8_11_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P8_11_qep_pin: pinmux_P8_11_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad13.eqep2b_in */ + P8_11_pruout_pin: pinmux_P8_11_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_ad13.pru0_out15 */ + + /* P8_12 (ZCZ ball T12) gpio1_12 */ + P8_12_default_pin: pinmux_P8_12_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_pin: pinmux_P8_12_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_pu_pin: pinmux_P8_12_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_pd_pin: pinmux_P8_12_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_gpio_input_pin: pinmux_P8_12_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P8_12_qep_pin: pinmux_P8_12_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad12.eqep2a_in */ + P8_12_pruout_pin: pinmux_P8_12_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_ad12.pru0_out14 */ + + /* P8_13 (ZCZ ball T10) gpio0_23 */ + P8_13_default_pin: pinmux_P8_13_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_pin: pinmux_P8_13_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_pu_pin: pinmux_P8_13_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_pd_pin: pinmux_P8_13_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_gpio_input_pin: pinmux_P8_13_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P8_13_pwm_pin: pinmux_P8_13_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad9.ehrpwm2b */ + + /* P8_14 (ZCZ ball T11) wl1835: wl_en */ + + /* P8_15 (ZCZ ball U13) gpio1_15 */ + P8_15_default_pin: pinmux_P8_15_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_pin: pinmux_P8_15_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_pu_pin: pinmux_P8_15_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_pd_pin: pinmux_P8_15_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_gpio_input_pin: pinmux_P8_15_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P8_15_qep_pin: pinmux_P8_15_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad15.eqep2_strobe */ + P8_15_pru_ecap_pin: pinmux_P8_15_pru_ecap_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_ad15.pr1_ecap0_ecap_capin_apwm_o */ + P8_15_pruin_pin: pinmux_P8_15_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_ad15.pru0_in15 */ + + /* P8_16 (ZCZ ball V13) gpio1_14 */ + P8_16_default_pin: pinmux_P8_16_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_pin: pinmux_P8_16_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_pu_pin: pinmux_P8_16_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_pd_pin: pinmux_P8_16_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_gpio_input_pin: pinmux_P8_16_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P8_16_qep_pin: pinmux_P8_16_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad14.eqep2_index */ + P8_16_pruin_pin: pinmux_P8_16_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_ad14.pru0_in14 */ + + /* P8_17 (ZCZ ball U12) wl1835: wl_irq */ + + /* P8_18 (ZCZ ball V12) gpio2_1 */ + P8_18_default_pin: pinmux_P8_18_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_pin: pinmux_P8_18_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_pu_pin: pinmux_P8_18_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_pd_pin: pinmux_P8_18_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P8_18_gpio_input_pin: pinmux_P8_18_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + + /* P8_19 (ZCZ ball U10) gpio0_22 */ + P8_19_default_pin: pinmux_P8_19_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_pin: pinmux_P8_19_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_pu_pin: pinmux_P8_19_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_pd_pin: pinmux_P8_19_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_gpio_input_pin: pinmux_P8_19_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad8.gpio0_22 */ + P8_19_pwm_pin: pinmux_P8_19_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0820, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad8.ehrpwm2a */ + + /* P8_20 (ZCZ ball V9) emmc */ + P8_20_default_pin: pinmux_P8_20_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_pin: pinmux_P8_20_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_pu_pin: pinmux_P8_20_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_pd_pin: pinmux_P8_20_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_gpio_input_pin: pinmux_P8_20_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_csn2.gpio1_31 */ + P8_20_pruout_pin: pinmux_P8_20_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_csn2.pru1_out13 */ + P8_20_pruin_pin: pinmux_P8_20_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0884, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_csn2.pru1_in13 */ + + /* P8_21 (ZCZ ball U9) emmc */ + P8_21_default_pin: pinmux_P8_21_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_pin: pinmux_P8_21_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_pu_pin: pinmux_P8_21_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_pd_pin: pinmux_P8_21_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_gpio_input_pin: pinmux_P8_21_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_csn1.gpio1_30 */ + P8_21_pruout_pin: pinmux_P8_21_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_csn1.pru1_out12 */ + P8_21_pruin_pin: pinmux_P8_21_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0880, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_csn1.pru1_in12 */ + + /* P8_22 (ZCZ ball V8) emmc */ + P8_22_default_pin: pinmux_P8_22_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_pin: pinmux_P8_22_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_pu_pin: pinmux_P8_22_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_pd_pin: pinmux_P8_22_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + P8_22_gpio_input_pin: pinmux_P8_22_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0814, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad5.gpio1_5 */ + + /* P8_23 (ZCZ ball U8) emmc */ + P8_23_default_pin: pinmux_P8_23_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_pin: pinmux_P8_23_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_pu_pin: pinmux_P8_23_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_pd_pin: pinmux_P8_23_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + P8_23_gpio_input_pin: pinmux_P8_23_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0810, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad4.gpio1_4 */ + + /* P8_24 (ZCZ ball V7) emmc */ + P8_24_default_pin: pinmux_P8_24_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_pin: pinmux_P8_24_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_pu_pin: pinmux_P8_24_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_pd_pin: pinmux_P8_24_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + P8_24_gpio_input_pin: pinmux_P8_24_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0804, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad1.gpio1_1 */ + + /* P8_25 (ZCZ ball U7) emmc */ + P8_25_default_pin: pinmux_P8_25_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_pin: pinmux_P8_25_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_pu_pin: pinmux_P8_25_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_pd_pin: pinmux_P8_25_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + P8_25_gpio_input_pin: pinmux_P8_25_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0800, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad0.gpio1_0 */ + + /* P8_26 (ZCZ ball V6) gpio-hog wl1835 */ + + /* P8_27 (ZCZ ball U5) hdmi */ + P8_27_default_pin: pinmux_P8_27_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_pin: pinmux_P8_27_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_pu_pin: pinmux_P8_27_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_pd_pin: pinmux_P8_27_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_gpio_input_pin: pinmux_P8_27_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P8_27_pruout_pin: pinmux_P8_27_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_vsync.pru1_out8 */ + P8_27_pruin_pin: pinmux_P8_27_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_vsync.pru1_in8 */ + + /* P8_28 (ZCZ ball V5) hdmi */ + P8_28_default_pin: pinmux_P8_28_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_pin: pinmux_P8_28_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_pu_pin: pinmux_P8_28_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_pd_pin: pinmux_P8_28_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_gpio_input_pin: pinmux_P8_28_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P8_28_pruout_pin: pinmux_P8_28_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_pclk.pru1_out10 */ + P8_28_pruin_pin: pinmux_P8_28_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_pclk.pru1_in10 */ + + /* P8_29 (ZCZ ball R5) hdmi */ + P8_29_default_pin: pinmux_P8_29_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_pin: pinmux_P8_29_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_pu_pin: pinmux_P8_29_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_pd_pin: pinmux_P8_29_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_gpio_input_pin: pinmux_P8_29_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P8_29_pruout_pin: pinmux_P8_29_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_hsync.pru1_out9 */ + P8_29_pruin_pin: pinmux_P8_29_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_hsync.pru1_in9 */ + + /* P8_30 (ZCZ ball R6) hdmi */ + P8_30_default_pin: pinmux_P8_30_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_pin: pinmux_P8_30_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_pu_pin: pinmux_P8_30_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_pd_pin: pinmux_P8_30_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_gpio_input_pin: pinmux_P8_30_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_INPUT | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P8_30_pruout_pin: pinmux_P8_30_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_ac_bias_en.pru1_out11 */ + P8_30_pruin_pin: pinmux_P8_30_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_INPUT | MUX_MODE6) >; }; /* lcd_ac_bias_en.pru1_in11 */ + + /* P8_31 (ZCZ ball V4) hdmi */ + P8_31_default_pin: pinmux_P8_31_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_pin: pinmux_P8_31_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_pu_pin: pinmux_P8_31_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_pd_pin: pinmux_P8_31_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_gpio_input_pin: pinmux_P8_31_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data14.gpio0_10 */ + P8_31_qep_pin: pinmux_P8_31_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data14.eqep1_index */ + P8_31_uart_pin: pinmux_P8_31_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* lcd_data14.uart5_rxd */ + + /* P8_32 (ZCZ ball T5) hdmi */ + P8_32_default_pin: pinmux_P8_32_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_pin: pinmux_P8_32_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_pu_pin: pinmux_P8_32_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_pd_pin: pinmux_P8_32_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_gpio_input_pin: pinmux_P8_32_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data15.gpio0_11 */ + P8_32_qep_pin: pinmux_P8_32_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08dc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data15.eqep1_strobe */ + + /* P8_33 (ZCZ ball V3) hdmi */ + P8_33_default_pin: pinmux_P8_33_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_pin: pinmux_P8_33_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_pu_pin: pinmux_P8_33_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_pd_pin: pinmux_P8_33_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_gpio_input_pin: pinmux_P8_33_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data13.gpio0_9 */ + P8_33_qep_pin: pinmux_P8_33_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data13.eqep1b_in */ + + /* P8_34 (ZCZ ball U4) hdmi */ + P8_34_default_pin: pinmux_P8_34_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_pin: pinmux_P8_34_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_pu_pin: pinmux_P8_34_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_pd_pin: pinmux_P8_34_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_gpio_input_pin: pinmux_P8_34_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data11.gpio2_17 */ + P8_34_pwm_pin: pinmux_P8_34_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08cc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data11.ehrpwm1b */ + + /* P8_35 (ZCZ ball V2) hdmi */ + P8_35_default_pin: pinmux_P8_35_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_pin: pinmux_P8_35_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_pu_pin: pinmux_P8_35_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_pd_pin: pinmux_P8_35_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_gpio_input_pin: pinmux_P8_35_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data12.gpio0_8 */ + P8_35_qep_pin: pinmux_P8_35_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08d0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* lcd_data12.eqep1a_in */ + + /* P8_36 (ZCZ ball U3) hdmi */ + P8_36_default_pin: pinmux_P8_36_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_pin: pinmux_P8_36_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_pu_pin: pinmux_P8_36_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_pd_pin: pinmux_P8_36_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_gpio_input_pin: pinmux_P8_36_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data10.gpio2_16 */ + P8_36_pwm_pin: pinmux_P8_36_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data10.ehrpwm1a */ + + /* P8_37 (ZCZ ball U1) hdmi */ + P8_37_default_pin: pinmux_P8_37_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_pin: pinmux_P8_37_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_pu_pin: pinmux_P8_37_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_pd_pin: pinmux_P8_37_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_gpio_input_pin: pinmux_P8_37_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data8.gpio2_14 */ + P8_37_pwm_pin: pinmux_P8_37_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data8.ehrpwm1_tripzone_input */ + P8_37_uart_pin: pinmux_P8_37_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* lcd_data8.uart5_txd */ + + /* P8_38 (ZCZ ball U2) hdmi */ + P8_38_default_pin: pinmux_P8_38_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_pin: pinmux_P8_38_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_pu_pin: pinmux_P8_38_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_pd_pin: pinmux_P8_38_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_gpio_input_pin: pinmux_P8_38_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data9.gpio2_15 */ + P8_38_pwm_pin: pinmux_P8_38_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2) >; }; /* lcd_data9.ehrpwm0_synco */ + P8_38_uart_pin: pinmux_P8_38_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08c4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* lcd_data9.uart5_rxd */ + + /* P8_39 (ZCZ ball T3) hdmi */ + P8_39_default_pin: pinmux_P8_39_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_pin: pinmux_P8_39_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_pu_pin: pinmux_P8_39_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_pd_pin: pinmux_P8_39_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_gpio_input_pin: pinmux_P8_39_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data6.gpio2_12 */ + P8_39_qep_pin: pinmux_P8_39_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data6.eqep2_index */ + P8_39_pruout_pin: pinmux_P8_39_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data6.pru1_out6 */ + P8_39_pruin_pin: pinmux_P8_39_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data6.pru1_in6 */ + + /* P8_40 (ZCZ ball T4) hdmi */ + P8_40_default_pin: pinmux_P8_40_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_pin: pinmux_P8_40_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_pu_pin: pinmux_P8_40_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_pd_pin: pinmux_P8_40_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_gpio_input_pin: pinmux_P8_40_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data7.gpio2_13 */ + P8_40_qep_pin: pinmux_P8_40_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data7.eqep2_strobe */ + P8_40_pruout_pin: pinmux_P8_40_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data7.pru1_out7 */ + P8_40_pruin_pin: pinmux_P8_40_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08bc, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data7.pru1_in7 */ + + /* P8_41 (ZCZ ball T1) hdmi */ + P8_41_default_pin: pinmux_P8_41_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_pin: pinmux_P8_41_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_pu_pin: pinmux_P8_41_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_pd_pin: pinmux_P8_41_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_gpio_input_pin: pinmux_P8_41_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data4.gpio2_10 */ + P8_41_qep_pin: pinmux_P8_41_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data4.eqep2a_in */ + P8_41_pruout_pin: pinmux_P8_41_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data4.pru1_out4 */ + P8_41_pruin_pin: pinmux_P8_41_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data4.pru1_in4 */ + + /* P8_42 (ZCZ ball T2) hdmi */ + P8_42_default_pin: pinmux_P8_42_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_pin: pinmux_P8_42_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_pu_pin: pinmux_P8_42_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_pd_pin: pinmux_P8_42_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_gpio_input_pin: pinmux_P8_42_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data5.gpio2_11 */ + P8_42_qep_pin: pinmux_P8_42_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* lcd_data5.eqep2b_in */ + P8_42_pruout_pin: pinmux_P8_42_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data5.pru1_out5 */ + P8_42_pruin_pin: pinmux_P8_42_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08b4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data5.pru1_in5 */ + + /* P8_43 (ZCZ ball R3) hdmi */ + P8_43_default_pin: pinmux_P8_43_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_pin: pinmux_P8_43_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_pu_pin: pinmux_P8_43_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_pd_pin: pinmux_P8_43_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_gpio_input_pin: pinmux_P8_43_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data2.gpio2_8 */ + P8_43_pwm_pin: pinmux_P8_43_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data2.ehrpwm2_tripzone_input */ + P8_43_pruout_pin: pinmux_P8_43_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data2.pru1_out2 */ + P8_43_pruin_pin: pinmux_P8_43_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data2.pru1_in2 */ + + /* P8_44 (ZCZ ball R4) hdmi */ + P8_44_default_pin: pinmux_P8_44_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_pin: pinmux_P8_44_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_pu_pin: pinmux_P8_44_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_pd_pin: pinmux_P8_44_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_gpio_input_pin: pinmux_P8_44_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data3.gpio2_9 */ + P8_44_pwm_pin: pinmux_P8_44_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data3.ehrpwm0_synco */ + P8_44_pruout_pin: pinmux_P8_44_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data3.pru1_out3 */ + P8_44_pruin_pin: pinmux_P8_44_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ac, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data3.pru1_in3 */ + + /* P8_45 (ZCZ ball R1) hdmi */ + P8_45_default_pin: pinmux_P8_45_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_pin: pinmux_P8_45_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_pu_pin: pinmux_P8_45_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_pd_pin: pinmux_P8_45_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_gpio_input_pin: pinmux_P8_45_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data0.gpio2_6 */ + P8_45_pwm_pin: pinmux_P8_45_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data0.ehrpwm2a */ + P8_45_pruout_pin: pinmux_P8_45_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data0.pru1_out0 */ + P8_45_pruin_pin: pinmux_P8_45_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data0.pru1_in0 */ + + /* P8_46 (ZCZ ball R2) hdmi */ + P8_46_default_pin: pinmux_P8_46_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_pin: pinmux_P8_46_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_pu_pin: pinmux_P8_46_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_pd_pin: pinmux_P8_46_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_gpio_input_pin: pinmux_P8_46_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_data1.gpio2_7 */ + P8_46_pwm_pin: pinmux_P8_46_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* lcd_data1.ehrpwm2b */ + P8_46_pruout_pin: pinmux_P8_46_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_data1.pru1_out1 */ + P8_46_pruin_pin: pinmux_P8_46_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08a4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_data1.pru1_in1 */ + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) gpio0_30 */ + P9_11_default_pin: pinmux_P9_11_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_pin: pinmux_P9_11_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_pu_pin: pinmux_P9_11_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_pd_pin: pinmux_P9_11_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_gpio_input_pin: pinmux_P9_11_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P9_11_uart_pin: pinmux_P9_11_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wait0.uart4_rxd */ + + /* P9_12 (ZCZ ball U18) gpio1_28 */ + P9_12_default_pin: pinmux_P9_12_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_pin: pinmux_P9_12_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_pu_pin: pinmux_P9_12_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_pd_pin: pinmux_P9_12_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P9_12_gpio_input_pin: pinmux_P9_12_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + + /* P9_13 (ZCZ ball U17) gpio0_31 */ + P9_13_default_pin: pinmux_P9_13_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_pin: pinmux_P9_13_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_pu_pin: pinmux_P9_13_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_pd_pin: pinmux_P9_13_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_gpio_input_pin: pinmux_P9_13_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P9_13_uart_pin: pinmux_P9_13_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wpn.uart4_txd */ + + /* P9_14 (ZCZ ball U14) gpio1_18 */ + P9_14_default_pin: pinmux_P9_14_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_pin: pinmux_P9_14_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_pu_pin: pinmux_P9_14_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_pd_pin: pinmux_P9_14_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_gpio_input_pin: pinmux_P9_14_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P9_14_pwm_pin: pinmux_P9_14_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a2.ehrpwm1a */ + + /* P9_15 (ZCZ ball R13) gpio1_16 */ + P9_15_default_pin: pinmux_P9_15_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_pin: pinmux_P9_15_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_pu_pin: pinmux_P9_15_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_pd_pin: pinmux_P9_15_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_gpio_input_pin: pinmux_P9_15_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a0.gpio1_16 */ + P9_15_pwm_pin: pinmux_P9_15_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0840, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a0.ehrpwm1_tripzone_input */ + + /* P9_16 (ZCZ ball T14) gpio1_19 */ + P9_16_default_pin: pinmux_P9_16_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_pin: pinmux_P9_16_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_pu_pin: pinmux_P9_16_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_pd_pin: pinmux_P9_16_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_gpio_input_pin: pinmux_P9_16_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a3.gpio1_19 */ + P9_16_pwm_pin: pinmux_P9_16_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x084c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a3.ehrpwm1b */ + + /* P9_17 (ZCZ ball A16) gpio0_5 */ + P9_17_default_pin: pinmux_P9_17_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_pin: pinmux_P9_17_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_pu_pin: pinmux_P9_17_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_pd_pin: pinmux_P9_17_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_gpio_input_pin: pinmux_P9_17_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_INPUT | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P9_17_spi_cs_pin: pinmux_P9_17_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_cs0.spi0_cs0 */ + P9_17_i2c_pin: pinmux_P9_17_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_cs0.i2c1_scl */ + P9_17_pwm_pin: pinmux_P9_17_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_cs0.ehrpwm0_synci */ + P9_17_pru_uart_pin: pinmux_P9_17_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_cs0.pr1_uart0_txd */ + + /* P9_18 (ZCZ ball B16) gpio0_4 */ + P9_18_default_pin: pinmux_P9_18_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_pin: pinmux_P9_18_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_pu_pin: pinmux_P9_18_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_pd_pin: pinmux_P9_18_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_gpio_input_pin: pinmux_P9_18_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_INPUT | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P9_18_spi_pin: pinmux_P9_18_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d1.spi0_d1 */ + P9_18_i2c_pin: pinmux_P9_18_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_d1.i2c1_sda */ + P9_18_pwm_pin: pinmux_P9_18_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_d1.ehrpwm0_tripzone_input */ + P9_18_pru_uart_pin: pinmux_P9_18_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_d1.pr1_uart0_rxd */ + + /* P9_19 (ZCZ ball D17) i2c2_scl */ + P9_19_default_pin: pinmux_P9_19_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rtsn.i2c2_scl */ + P9_19_gpio_pin: pinmux_P9_19_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_gpio_pu_pin: pinmux_P9_19_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_gpio_pd_pin: pinmux_P9_19_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_gpio_input_pin: pinmux_P9_19_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_INPUT | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P9_19_timer_pin: pinmux_P9_19_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart1_rtsn.timer5 */ + P9_19_can_pin: pinmux_P9_19_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart1_rtsn.dcan0_rx */ + P9_19_i2c_pin: pinmux_P9_19_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rtsn.i2c2_scl */ + P9_19_spi_cs_pin: pinmux_P9_19_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart1_rtsn.spi1_cs1 */ + P9_19_pru_uart_pin: pinmux_P9_19_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_rtsn.pr1_uart0_rts_n */ + + /* P9_20 (ZCZ ball D18) i2c2_sda */ + P9_20_default_pin: pinmux_P9_20_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_ctsn.i2c2_sda */ + P9_20_gpio_pin: pinmux_P9_20_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_gpio_pu_pin: pinmux_P9_20_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_gpio_pd_pin: pinmux_P9_20_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_gpio_input_pin: pinmux_P9_20_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_INPUT | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P9_20_timer_pin: pinmux_P9_20_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart1_ctsn.timer6 */ + P9_20_can_pin: pinmux_P9_20_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart1_ctsn.dcan0_tx */ + P9_20_i2c_pin: pinmux_P9_20_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_ctsn.i2c2_sda */ + P9_20_spi_cs_pin: pinmux_P9_20_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart1_ctsn.spi1_cs0 */ + P9_20_pru_uart_pin: pinmux_P9_20_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_ctsn.pr1_uart0_cts_n */ + + /* P9_21 (ZCZ ball B17) gpio0_3 */ + P9_21_default_pin: pinmux_P9_21_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_pin: pinmux_P9_21_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_pu_pin: pinmux_P9_21_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_pd_pin: pinmux_P9_21_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_gpio_input_pin: pinmux_P9_21_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_INPUT | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P9_21_spi_pin: pinmux_P9_21_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d0.spi0_d0 */ + P9_21_uart_pin: pinmux_P9_21_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* spi0_d0.uart2_txd */ + P9_21_i2c_pin: pinmux_P9_21_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_d0.i2c2_scl */ + P9_21_pwm_pin: pinmux_P9_21_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_d0.ehrpwm0b */ + P9_21_pru_uart_pin: pinmux_P9_21_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_d0.pr1_uart0_rts_n */ + + /* P9_22 (ZCZ ball A17) gpio0_2 */ + P9_22_default_pin: pinmux_P9_22_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_pin: pinmux_P9_22_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_pu_pin: pinmux_P9_22_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_pd_pin: pinmux_P9_22_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_gpio_input_pin: pinmux_P9_22_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_INPUT | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P9_22_spi_sclk_pin: pinmux_P9_22_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_sclk.spi0_sclk */ + P9_22_uart_pin: pinmux_P9_22_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* spi0_sclk.uart2_rxd */ + P9_22_i2c_pin: pinmux_P9_22_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_sclk.i2c2_sda */ + P9_22_pwm_pin: pinmux_P9_22_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_sclk.ehrpwm0a */ + P9_22_pru_uart_pin: pinmux_P9_22_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_sclk.pr1_uart0_cts_n */ + + /* P9_23 (ZCZ ball V14) gpio1_17 */ + P9_23_default_pin: pinmux_P9_23_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_pin: pinmux_P9_23_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_pu_pin: pinmux_P9_23_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_pd_pin: pinmux_P9_23_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_gpio_input_pin: pinmux_P9_23_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a1.gpio1_17 */ + P9_23_pwm_pin: pinmux_P9_23_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0844, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a1.ehrpwm0_synco */ + + /* P9_24 (ZCZ ball D15) gpio0_15 */ + P9_24_default_pin: pinmux_P9_24_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_pin: pinmux_P9_24_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_pu_pin: pinmux_P9_24_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_pd_pin: pinmux_P9_24_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_gpio_input_pin: pinmux_P9_24_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P9_24_uart_pin: pinmux_P9_24_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart1_txd.uart1_txd */ + P9_24_can_pin: pinmux_P9_24_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart1_txd.dcan1_rx */ + P9_24_i2c_pin: pinmux_P9_24_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_txd.i2c1_scl */ + P9_24_pru_uart_pin: pinmux_P9_24_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_txd.pr1_uart0_txd */ + P9_24_pruin_pin: pinmux_P9_24_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT | MUX_MODE6) >; }; /* uart1_txd.pru0_in16 */ + + /* P9_25 (ZCZ ball A14) audio */ + P9_25_default_pin: pinmux_P9_25_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_pin: pinmux_P9_25_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_pu_pin: pinmux_P9_25_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_pd_pin: pinmux_P9_25_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_gpio_input_pin: pinmux_P9_25_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P9_25_qep_pin: pinmux_P9_25_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_ahclkx.eqep0_strobe */ + P9_25_pruout_pin: pinmux_P9_25_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_ahclkx.pru0_out7 */ + P9_25_pruin_pin: pinmux_P9_25_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkx.pru0_in7 */ + + /* P9_26 (ZCZ ball D16) gpio0_14 */ + P9_26_default_pin: pinmux_P9_26_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_pin: pinmux_P9_26_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_pu_pin: pinmux_P9_26_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_pd_pin: pinmux_P9_26_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_gpio_input_pin: pinmux_P9_26_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_INPUT | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P9_26_uart_pin: pinmux_P9_26_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart1_rxd.uart1_rxd */ + P9_26_can_pin: pinmux_P9_26_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart1_rxd.dcan1_tx */ + P9_26_i2c_pin: pinmux_P9_26_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rxd.i2c1_sda */ + P9_26_pru_uart_pin: pinmux_P9_26_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_rxd.pr1_uart0_rxd */ + P9_26_pruin_pin: pinmux_P9_26_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_INPUT | MUX_MODE6) >; }; /* uart1_rxd.pru1_in16 */ + + /* P9_27 (ZCZ ball C13) gpio3_19 */ + P9_27_default_pin: pinmux_P9_27_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_pin: pinmux_P9_27_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_pu_pin: pinmux_P9_27_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_pd_pin: pinmux_P9_27_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_gpio_input_pin: pinmux_P9_27_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P9_27_qep_pin: pinmux_P9_27_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_fsr.eqep0b_in */ + P9_27_pruout_pin: pinmux_P9_27_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_fsr.pru0_out5 */ + P9_27_pruin_pin: pinmux_P9_27_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsr.pru0_in5 */ + + /* P9_28 (ZCZ ball C12) audio */ + P9_28_default_pin: pinmux_P9_28_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_pin: pinmux_P9_28_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_pu_pin: pinmux_P9_28_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_pd_pin: pinmux_P9_28_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_gpio_input_pin: pinmux_P9_28_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P9_28_pwm_pin: pinmux_P9_28_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_ahclkr.ehrpwm0_synci */ + P9_28_spi_cs_pin: pinmux_P9_28_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_ahclkr.spi1_cs0 */ + P9_28_pwm2_pin: pinmux_P9_28_pwm2_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* mcasp0_ahclkr.ecap2_in_pwm2_out */ + P9_28_pruout_pin: pinmux_P9_28_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_ahclkr.pru0_out3 */ + P9_28_pruin_pin: pinmux_P9_28_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkr.pru0_in3 */ + + /* P9_29 (ZCZ ball B13) audio */ + P9_29_default_pin: pinmux_P9_29_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_pin: pinmux_P9_29_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_pu_pin: pinmux_P9_29_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_pd_pin: pinmux_P9_29_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_gpio_input_pin: pinmux_P9_29_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P9_29_pwm_pin: pinmux_P9_29_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_fsx.ehrpwm0b */ + P9_29_spi_pin: pinmux_P9_29_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_fsx.spi1_d0 */ + P9_29_pruout_pin: pinmux_P9_29_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_fsx.pru0_out1 */ + P9_29_pruin_pin: pinmux_P9_29_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsx.pru0_in1 */ + + /* P9_30 (ZCZ ball D12) gpio-hog wl1835 */ + + /* P9_31 (ZCZ ball A13) audio */ + P9_31_default_pin: pinmux_P9_31_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_pin: pinmux_P9_31_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_pu_pin: pinmux_P9_31_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_pd_pin: pinmux_P9_31_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_gpio_input_pin: pinmux_P9_31_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P9_31_pwm_pin: pinmux_P9_31_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkx.ehrpwm0a */ + P9_31_spi_sclk_pin: pinmux_P9_31_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_aclkx.spi1_sclk */ + P9_31_pruout_pin: pinmux_P9_31_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_aclkx.pru0_out0 */ + P9_31_pruin_pin: pinmux_P9_31_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkx.pru0_in0 */ + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) gpio0_20 */ + P9_41_default_pin: pinmux_P9_41_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_pin: pinmux_P9_41_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_pu_pin: pinmux_P9_41_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_pd_pin: pinmux_P9_41_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_gpio_input_pin: pinmux_P9_41_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_INPUT | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P9_41_timer_pin: pinmux_P9_41_timer_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* xdma_event_intr1.timer7 */ + P9_41_pruin_pin: pinmux_P9_41_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_INPUT | MUX_MODE5) >; }; /* xdma_event_intr1.pru0_in16 */ + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) gpio3_20 */ + P9_91_default_pin: pinmux_P9_91_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_pin: pinmux_P9_91_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_pu_pin: pinmux_P9_91_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_pd_pin: pinmux_P9_91_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_gpio_input_pin: pinmux_P9_91_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P9_91_qep_pin: pinmux_P9_91_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_axr1.eqep0_index */ + P9_91_pruout_pin: pinmux_P9_91_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_axr1.pru0_out6 */ + P9_91_pruin_pin: pinmux_P9_91_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr1.pru0_in6 */ + + /* P9_42 (ZCZ ball C18) gpio0_7 */ + P9_42_default_pin: pinmux_P9_42_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_pin: pinmux_P9_42_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_pu_pin: pinmux_P9_42_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_pd_pin: pinmux_P9_42_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_gpio_input_pin: pinmux_P9_42_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_INPUT | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P9_42_pwm_pin: pinmux_P9_42_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE0) >; }; /* eCAP0_in_PWM0_out.ecap0_in_pwm0_out */ + P9_42_uart_pin: pinmux_P9_42_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* eCAP0_in_PWM0_out.uart3_txd */ + P9_42_spi_cs_pin: pinmux_P9_42_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* eCAP0_in_PWM0_out.spi1_cs1 */ + P9_42_pru_ecap_pin: pinmux_P9_42_pru_ecap_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* eCAP0_in_PWM0_out.pr1_ecap0_ecap_capin_apwm_o */ + P9_42_spi_sclk_pin: pinmux_P9_42_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* eCAP0_in_PWM0_out.spi1_sclk */ + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) gpio3_18 */ + P9_92_default_pin: pinmux_P9_92_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_pin: pinmux_P9_92_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_pu_pin: pinmux_P9_92_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_pd_pin: pinmux_P9_92_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_gpio_input_pin: pinmux_P9_92_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P9_92_qep_pin: pinmux_P9_92_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkr.eqep0a_in */ + P9_92_pruout_pin: pinmux_P9_92_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_aclkr.pru0_out4 */ + P9_92_pruin_pin: pinmux_P9_92_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkr.pru0_in4 */ + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/1"; +}; + +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/2"; +}; + +&uart1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/1"; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/2"; +}; + +&uart3 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/3"; +}; + +&uart4 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/4"; +}; + +&uart5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/5"; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/0"; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/1"; +}; + +&eqep0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/0"; +}; + +&eqep1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/1"; +}; + +&eqep2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/2"; +}; + +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&spi0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +&spi1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +/**********************************************************************/ +/* Pin Multiplex Helpers */ +/* */ +/* These provide userspace runtime pin configuration for the */ +/* BeagleBone cape expansion headers */ +/**********************************************************************/ + +&ocp { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) emmc */ + P8_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_03_default_pin>; + pinctrl-1 = <&P8_03_gpio_pin>; + pinctrl-2 = <&P8_03_gpio_pu_pin>; + pinctrl-3 = <&P8_03_gpio_pd_pin>; + pinctrl-4 = <&P8_03_gpio_input_pin>; + }; + + /* P8_04 (ZCZ ball T9) emmc */ + P8_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_04_default_pin>; + pinctrl-1 = <&P8_04_gpio_pin>; + pinctrl-2 = <&P8_04_gpio_pu_pin>; + pinctrl-3 = <&P8_04_gpio_pd_pin>; + pinctrl-4 = <&P8_04_gpio_input_pin>; + }; + + /* P8_05 (ZCZ ball R8) emmc */ + P8_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_05_default_pin>; + pinctrl-1 = <&P8_05_gpio_pin>; + pinctrl-2 = <&P8_05_gpio_pu_pin>; + pinctrl-3 = <&P8_05_gpio_pd_pin>; + pinctrl-4 = <&P8_05_gpio_input_pin>; + }; + + /* P8_06 (ZCZ ball T8) emmc */ + P8_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_06_default_pin>; + pinctrl-1 = <&P8_06_gpio_pin>; + pinctrl-2 = <&P8_06_gpio_pu_pin>; + pinctrl-3 = <&P8_06_gpio_pd_pin>; + pinctrl-4 = <&P8_06_gpio_input_pin>; + }; + + /* P8_07 (ZCZ ball R7) */ + P8_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_07_default_pin>; + pinctrl-1 = <&P8_07_gpio_pin>; + pinctrl-2 = <&P8_07_gpio_pu_pin>; + pinctrl-3 = <&P8_07_gpio_pd_pin>; + pinctrl-4 = <&P8_07_gpio_input_pin>; + pinctrl-5 = <&P8_07_timer_pin>; + }; + + /* P8_08 (ZCZ ball T7) */ + P8_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_08_default_pin>; + pinctrl-1 = <&P8_08_gpio_pin>; + pinctrl-2 = <&P8_08_gpio_pu_pin>; + pinctrl-3 = <&P8_08_gpio_pd_pin>; + pinctrl-4 = <&P8_08_gpio_input_pin>; + pinctrl-5 = <&P8_08_timer_pin>; + }; + + /* P8_09 (ZCZ ball T6) */ + P8_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_09_default_pin>; + pinctrl-1 = <&P8_09_gpio_pin>; + pinctrl-2 = <&P8_09_gpio_pu_pin>; + pinctrl-3 = <&P8_09_gpio_pd_pin>; + pinctrl-4 = <&P8_09_gpio_input_pin>; + pinctrl-5 = <&P8_09_timer_pin>; + }; + + /* P8_10 (ZCZ ball U6) */ + P8_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer"; + pinctrl-0 = <&P8_10_default_pin>; + pinctrl-1 = <&P8_10_gpio_pin>; + pinctrl-2 = <&P8_10_gpio_pu_pin>; + pinctrl-3 = <&P8_10_gpio_pd_pin>; + pinctrl-4 = <&P8_10_gpio_input_pin>; + pinctrl-5 = <&P8_10_timer_pin>; + }; + + /* P8_11 (ZCZ ball R12) */ + P8_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout"; + pinctrl-0 = <&P8_11_default_pin>; + pinctrl-1 = <&P8_11_gpio_pin>; + pinctrl-2 = <&P8_11_gpio_pu_pin>; + pinctrl-3 = <&P8_11_gpio_pd_pin>; + pinctrl-4 = <&P8_11_gpio_input_pin>; + pinctrl-5 = <&P8_11_qep_pin>; + pinctrl-6 = <&P8_11_pruout_pin>; + }; + + /* P8_12 (ZCZ ball T12) */ + P8_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout"; + pinctrl-0 = <&P8_12_default_pin>; + pinctrl-1 = <&P8_12_gpio_pin>; + pinctrl-2 = <&P8_12_gpio_pu_pin>; + pinctrl-3 = <&P8_12_gpio_pd_pin>; + pinctrl-4 = <&P8_12_gpio_input_pin>; + pinctrl-5 = <&P8_12_qep_pin>; + pinctrl-6 = <&P8_12_pruout_pin>; + }; + + /* P8_13 (ZCZ ball T10) */ + P8_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_13_default_pin>; + pinctrl-1 = <&P8_13_gpio_pin>; + pinctrl-2 = <&P8_13_gpio_pu_pin>; + pinctrl-3 = <&P8_13_gpio_pd_pin>; + pinctrl-4 = <&P8_13_gpio_input_pin>; + pinctrl-5 = <&P8_13_pwm_pin>; + }; + + /* P8_14 (ZCZ ball T11) wl1835: wl_en */ + + /* P8_15 (ZCZ ball U13) */ + P8_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pru_ecap", "pruin"; + pinctrl-0 = <&P8_15_default_pin>; + pinctrl-1 = <&P8_15_gpio_pin>; + pinctrl-2 = <&P8_15_gpio_pu_pin>; + pinctrl-3 = <&P8_15_gpio_pd_pin>; + pinctrl-4 = <&P8_15_gpio_input_pin>; + pinctrl-5 = <&P8_15_qep_pin>; + pinctrl-6 = <&P8_15_pru_ecap_pin>; + pinctrl-7 = <&P8_15_pruin_pin>; + }; + + /* P8_16 (ZCZ ball V13) */ + P8_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruin"; + pinctrl-0 = <&P8_16_default_pin>; + pinctrl-1 = <&P8_16_gpio_pin>; + pinctrl-2 = <&P8_16_gpio_pu_pin>; + pinctrl-3 = <&P8_16_gpio_pd_pin>; + pinctrl-4 = <&P8_16_gpio_input_pin>; + pinctrl-5 = <&P8_16_qep_pin>; + pinctrl-6 = <&P8_16_pruin_pin>; + }; + + /* P8_17 (ZCZ ball U12) wl1835: wl_irq */ + + /* P8_18 (ZCZ ball V12) */ + P8_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_18_default_pin>; + pinctrl-1 = <&P8_18_gpio_pin>; + pinctrl-2 = <&P8_18_gpio_pu_pin>; + pinctrl-3 = <&P8_18_gpio_pd_pin>; + pinctrl-4 = <&P8_18_gpio_input_pin>; + }; + + /* P8_19 (ZCZ ball U10) */ + P8_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_19_default_pin>; + pinctrl-1 = <&P8_19_gpio_pin>; + pinctrl-2 = <&P8_19_gpio_pu_pin>; + pinctrl-3 = <&P8_19_gpio_pd_pin>; + pinctrl-4 = <&P8_19_gpio_input_pin>; + pinctrl-5 = <&P8_19_pwm_pin>; + }; + + /* P8_20 (ZCZ ball V9) emmc */ + P8_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_20_default_pin>; + pinctrl-1 = <&P8_20_gpio_pin>; + pinctrl-2 = <&P8_20_gpio_pu_pin>; + pinctrl-3 = <&P8_20_gpio_pd_pin>; + pinctrl-4 = <&P8_20_gpio_input_pin>; + pinctrl-5 = <&P8_20_pruout_pin>; + pinctrl-6 = <&P8_20_pruin_pin>; + }; + + /* P8_21 (ZCZ ball U9) emmc */ + P8_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_21_default_pin>; + pinctrl-1 = <&P8_21_gpio_pin>; + pinctrl-2 = <&P8_21_gpio_pu_pin>; + pinctrl-3 = <&P8_21_gpio_pd_pin>; + pinctrl-4 = <&P8_21_gpio_input_pin>; + pinctrl-5 = <&P8_21_pruout_pin>; + pinctrl-6 = <&P8_21_pruin_pin>; + }; + + /* P8_22 (ZCZ ball V8) emmc */ + P8_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_22_default_pin>; + pinctrl-1 = <&P8_22_gpio_pin>; + pinctrl-2 = <&P8_22_gpio_pu_pin>; + pinctrl-3 = <&P8_22_gpio_pd_pin>; + pinctrl-4 = <&P8_22_gpio_input_pin>; + }; + + /* P8_23 (ZCZ ball U8) emmc */ + P8_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_23_default_pin>; + pinctrl-1 = <&P8_23_gpio_pin>; + pinctrl-2 = <&P8_23_gpio_pu_pin>; + pinctrl-3 = <&P8_23_gpio_pd_pin>; + pinctrl-4 = <&P8_23_gpio_input_pin>; + }; + + /* P8_24 (ZCZ ball V7) emmc */ + P8_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_24_default_pin>; + pinctrl-1 = <&P8_24_gpio_pin>; + pinctrl-2 = <&P8_24_gpio_pu_pin>; + pinctrl-3 = <&P8_24_gpio_pd_pin>; + pinctrl-4 = <&P8_24_gpio_input_pin>; + }; + + /* P8_25 (ZCZ ball U7) emmc */ + P8_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P8_25_default_pin>; + pinctrl-1 = <&P8_25_gpio_pin>; + pinctrl-2 = <&P8_25_gpio_pu_pin>; + pinctrl-3 = <&P8_25_gpio_pd_pin>; + pinctrl-4 = <&P8_25_gpio_input_pin>; + }; + + /* P8_26 (ZCZ ball V6) gpio-hog wl1835 */ + + /* P8_27 (ZCZ ball U5) hdmi */ + P8_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_27_default_pin>; + pinctrl-1 = <&P8_27_gpio_pin>; + pinctrl-2 = <&P8_27_gpio_pu_pin>; + pinctrl-3 = <&P8_27_gpio_pd_pin>; + pinctrl-4 = <&P8_27_gpio_input_pin>; + pinctrl-5 = <&P8_27_pruout_pin>; + pinctrl-6 = <&P8_27_pruin_pin>; + }; + + /* P8_28 (ZCZ ball V5) hdmi */ + P8_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_28_default_pin>; + pinctrl-1 = <&P8_28_gpio_pin>; + pinctrl-2 = <&P8_28_gpio_pu_pin>; + pinctrl-3 = <&P8_28_gpio_pd_pin>; + pinctrl-4 = <&P8_28_gpio_input_pin>; + pinctrl-5 = <&P8_28_pruout_pin>; + pinctrl-6 = <&P8_28_pruin_pin>; + }; + + /* P8_29 (ZCZ ball R5) hdmi */ + P8_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_29_default_pin>; + pinctrl-1 = <&P8_29_gpio_pin>; + pinctrl-2 = <&P8_29_gpio_pu_pin>; + pinctrl-3 = <&P8_29_gpio_pd_pin>; + pinctrl-4 = <&P8_29_gpio_input_pin>; + pinctrl-5 = <&P8_29_pruout_pin>; + pinctrl-6 = <&P8_29_pruin_pin>; + }; + + /* P8_30 (ZCZ ball R6) hdmi */ + P8_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P8_30_default_pin>; + pinctrl-1 = <&P8_30_gpio_pin>; + pinctrl-2 = <&P8_30_gpio_pu_pin>; + pinctrl-3 = <&P8_30_gpio_pd_pin>; + pinctrl-4 = <&P8_30_gpio_input_pin>; + pinctrl-5 = <&P8_30_pruout_pin>; + pinctrl-6 = <&P8_30_pruin_pin>; + }; + + /* P8_31 (ZCZ ball V4) hdmi */ + P8_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "qep"; + pinctrl-0 = <&P8_31_default_pin>; + pinctrl-1 = <&P8_31_gpio_pin>; + pinctrl-2 = <&P8_31_gpio_pu_pin>; + pinctrl-3 = <&P8_31_gpio_pd_pin>; + pinctrl-4 = <&P8_31_gpio_input_pin>; + pinctrl-5 = <&P8_31_uart_pin>; + pinctrl-6 = <&P8_31_qep_pin>; + }; + + /* P8_32 (ZCZ ball T5) hdmi */ + P8_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P8_32_default_pin>; + pinctrl-1 = <&P8_32_gpio_pin>; + pinctrl-2 = <&P8_32_gpio_pu_pin>; + pinctrl-3 = <&P8_32_gpio_pd_pin>; + pinctrl-4 = <&P8_32_gpio_input_pin>; + pinctrl-5 = <&P8_32_qep_pin>; + }; + + /* P8_33 (ZCZ ball V3) hdmi */ + P8_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P8_33_default_pin>; + pinctrl-1 = <&P8_33_gpio_pin>; + pinctrl-2 = <&P8_33_gpio_pu_pin>; + pinctrl-3 = <&P8_33_gpio_pd_pin>; + pinctrl-4 = <&P8_33_gpio_input_pin>; + pinctrl-5 = <&P8_33_qep_pin>; + }; + + /* P8_34 (ZCZ ball U4) hdmi */ + P8_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_34_default_pin>; + pinctrl-1 = <&P8_34_gpio_pin>; + pinctrl-2 = <&P8_34_gpio_pu_pin>; + pinctrl-3 = <&P8_34_gpio_pd_pin>; + pinctrl-4 = <&P8_34_gpio_input_pin>; + pinctrl-5 = <&P8_34_pwm_pin>; + }; + + /* P8_35 (ZCZ ball V2) hdmi */ + P8_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P8_35_default_pin>; + pinctrl-1 = <&P8_35_gpio_pin>; + pinctrl-2 = <&P8_35_gpio_pu_pin>; + pinctrl-3 = <&P8_35_gpio_pd_pin>; + pinctrl-4 = <&P8_35_gpio_input_pin>; + pinctrl-5 = <&P8_35_qep_pin>; + }; + + /* P8_36 (ZCZ ball U3) hdmi */ + P8_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P8_36_default_pin>; + pinctrl-1 = <&P8_36_gpio_pin>; + pinctrl-2 = <&P8_36_gpio_pu_pin>; + pinctrl-3 = <&P8_36_gpio_pd_pin>; + pinctrl-4 = <&P8_36_gpio_input_pin>; + pinctrl-5 = <&P8_36_pwm_pin>; + }; + + /* P8_37 (ZCZ ball U1) hdmi */ + P8_37_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "pwm"; + pinctrl-0 = <&P8_37_default_pin>; + pinctrl-1 = <&P8_37_gpio_pin>; + pinctrl-2 = <&P8_37_gpio_pu_pin>; + pinctrl-3 = <&P8_37_gpio_pd_pin>; + pinctrl-4 = <&P8_37_gpio_input_pin>; + pinctrl-5 = <&P8_37_uart_pin>; + pinctrl-6 = <&P8_37_pwm_pin>; + }; + + /* P8_38 (ZCZ ball U2) hdmi */ + P8_38_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "pwm"; + pinctrl-0 = <&P8_38_default_pin>; + pinctrl-1 = <&P8_38_gpio_pin>; + pinctrl-2 = <&P8_38_gpio_pu_pin>; + pinctrl-3 = <&P8_38_gpio_pd_pin>; + pinctrl-4 = <&P8_38_gpio_input_pin>; + pinctrl-5 = <&P8_38_uart_pin>; + pinctrl-6 = <&P8_38_pwm_pin>; + }; + + /* P8_39 (ZCZ ball T3) hdmi */ + P8_39_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_39_default_pin>; + pinctrl-1 = <&P8_39_gpio_pin>; + pinctrl-2 = <&P8_39_gpio_pu_pin>; + pinctrl-3 = <&P8_39_gpio_pd_pin>; + pinctrl-4 = <&P8_39_gpio_input_pin>; + pinctrl-5 = <&P8_39_qep_pin>; + pinctrl-6 = <&P8_39_pruout_pin>; + pinctrl-7 = <&P8_39_pruin_pin>; + }; + + /* P8_40 (ZCZ ball T4) hdmi */ + P8_40_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_40_default_pin>; + pinctrl-1 = <&P8_40_gpio_pin>; + pinctrl-2 = <&P8_40_gpio_pu_pin>; + pinctrl-3 = <&P8_40_gpio_pd_pin>; + pinctrl-4 = <&P8_40_gpio_input_pin>; + pinctrl-5 = <&P8_40_qep_pin>; + pinctrl-6 = <&P8_40_pruout_pin>; + pinctrl-7 = <&P8_40_pruin_pin>; + }; + + /* P8_41 (ZCZ ball T1) hdmi */ + P8_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_41_default_pin>; + pinctrl-1 = <&P8_41_gpio_pin>; + pinctrl-2 = <&P8_41_gpio_pu_pin>; + pinctrl-3 = <&P8_41_gpio_pd_pin>; + pinctrl-4 = <&P8_41_gpio_input_pin>; + pinctrl-5 = <&P8_41_qep_pin>; + pinctrl-6 = <&P8_41_pruout_pin>; + pinctrl-7 = <&P8_41_pruin_pin>; + }; + + /* P8_42 (ZCZ ball T2) hdmi */ + P8_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P8_42_default_pin>; + pinctrl-1 = <&P8_42_gpio_pin>; + pinctrl-2 = <&P8_42_gpio_pu_pin>; + pinctrl-3 = <&P8_42_gpio_pd_pin>; + pinctrl-4 = <&P8_42_gpio_input_pin>; + pinctrl-5 = <&P8_42_qep_pin>; + pinctrl-6 = <&P8_42_pruout_pin>; + pinctrl-7 = <&P8_42_pruin_pin>; + }; + + /* P8_43 (ZCZ ball R3) hdmi */ + P8_43_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_43_default_pin>; + pinctrl-1 = <&P8_43_gpio_pin>; + pinctrl-2 = <&P8_43_gpio_pu_pin>; + pinctrl-3 = <&P8_43_gpio_pd_pin>; + pinctrl-4 = <&P8_43_gpio_input_pin>; + pinctrl-5 = <&P8_43_pwm_pin>; + pinctrl-6 = <&P8_43_pruout_pin>; + pinctrl-7 = <&P8_43_pruin_pin>; + }; + + /* P8_44 (ZCZ ball R4) hdmi */ + P8_44_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_44_default_pin>; + pinctrl-1 = <&P8_44_gpio_pin>; + pinctrl-2 = <&P8_44_gpio_pu_pin>; + pinctrl-3 = <&P8_44_gpio_pd_pin>; + pinctrl-4 = <&P8_44_gpio_input_pin>; + pinctrl-5 = <&P8_44_pwm_pin>; + pinctrl-6 = <&P8_44_pruout_pin>; + pinctrl-7 = <&P8_44_pruin_pin>; + }; + + /* P8_45 (ZCZ ball R1) hdmi */ + P8_45_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_45_default_pin>; + pinctrl-1 = <&P8_45_gpio_pin>; + pinctrl-2 = <&P8_45_gpio_pu_pin>; + pinctrl-3 = <&P8_45_gpio_pd_pin>; + pinctrl-4 = <&P8_45_gpio_input_pin>; + pinctrl-5 = <&P8_45_pwm_pin>; + pinctrl-6 = <&P8_45_pruout_pin>; + pinctrl-7 = <&P8_45_pruin_pin>; + }; + + /* P8_46 (ZCZ ball R2) hdmi */ + P8_46_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_46_default_pin>; + pinctrl-1 = <&P8_46_gpio_pin>; + pinctrl-2 = <&P8_46_gpio_pu_pin>; + pinctrl-3 = <&P8_46_gpio_pd_pin>; + pinctrl-4 = <&P8_46_gpio_input_pin>; + pinctrl-5 = <&P8_46_pwm_pin>; + pinctrl-6 = <&P8_46_pruout_pin>; + pinctrl-7 = <&P8_46_pruin_pin>; + }; + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) */ + P9_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart"; + pinctrl-0 = <&P9_11_default_pin>; + pinctrl-1 = <&P9_11_gpio_pin>; + pinctrl-2 = <&P9_11_gpio_pu_pin>; + pinctrl-3 = <&P9_11_gpio_pd_pin>; + pinctrl-4 = <&P9_11_gpio_input_pin>; + pinctrl-5 = <&P9_11_uart_pin>; + }; + + /* P9_12 (ZCZ ball U18) */ + P9_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P9_12_default_pin>; + pinctrl-1 = <&P9_12_gpio_pin>; + pinctrl-2 = <&P9_12_gpio_pu_pin>; + pinctrl-3 = <&P9_12_gpio_pd_pin>; + pinctrl-4 = <&P9_12_gpio_input_pin>; + }; + + /* P9_13 (ZCZ ball U17) */ + P9_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart"; + pinctrl-0 = <&P9_13_default_pin>; + pinctrl-1 = <&P9_13_gpio_pin>; + pinctrl-2 = <&P9_13_gpio_pu_pin>; + pinctrl-3 = <&P9_13_gpio_pd_pin>; + pinctrl-4 = <&P9_13_gpio_input_pin>; + pinctrl-5 = <&P9_13_uart_pin>; + }; + + /* P9_14 (ZCZ ball U14) */ + P9_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_14_default_pin>; + pinctrl-1 = <&P9_14_gpio_pin>; + pinctrl-2 = <&P9_14_gpio_pu_pin>; + pinctrl-3 = <&P9_14_gpio_pd_pin>; + pinctrl-4 = <&P9_14_gpio_input_pin>; + pinctrl-5 = <&P9_14_pwm_pin>; + }; + + /* P9_15 (ZCZ ball R13) */ + P9_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_15_default_pin>; + pinctrl-1 = <&P9_15_gpio_pin>; + pinctrl-2 = <&P9_15_gpio_pu_pin>; + pinctrl-3 = <&P9_15_gpio_pd_pin>; + pinctrl-4 = <&P9_15_gpio_input_pin>; + pinctrl-5 = <&P9_15_pwm_pin>; + }; + + /* P9_16 (ZCZ ball T14) */ + P9_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_16_default_pin>; + pinctrl-1 = <&P9_16_gpio_pin>; + pinctrl-2 = <&P9_16_gpio_pu_pin>; + pinctrl-3 = <&P9_16_gpio_pd_pin>; + pinctrl-4 = <&P9_16_gpio_input_pin>; + pinctrl-5 = <&P9_16_pwm_pin>; + }; + + /* P9_17 (ZCZ ball A16) */ + P9_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_17_default_pin>; + pinctrl-1 = <&P9_17_gpio_pin>; + pinctrl-2 = <&P9_17_gpio_pu_pin>; + pinctrl-3 = <&P9_17_gpio_pd_pin>; + pinctrl-4 = <&P9_17_gpio_input_pin>; + pinctrl-5 = <&P9_17_spi_cs_pin>; + pinctrl-6 = <&P9_17_i2c_pin>; + pinctrl-7 = <&P9_17_pwm_pin>; + pinctrl-8 = <&P9_17_pru_uart_pin>; + }; + + /* P9_18 (ZCZ ball B16) */ + P9_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_18_default_pin>; + pinctrl-1 = <&P9_18_gpio_pin>; + pinctrl-2 = <&P9_18_gpio_pu_pin>; + pinctrl-3 = <&P9_18_gpio_pd_pin>; + pinctrl-4 = <&P9_18_gpio_input_pin>; + pinctrl-5 = <&P9_18_spi_pin>; + pinctrl-6 = <&P9_18_i2c_pin>; + pinctrl-7 = <&P9_18_pwm_pin>; + pinctrl-8 = <&P9_18_pru_uart_pin>; + }; + + /* P9_19 (ZCZ ball D17) i2c */ + P9_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_19_default_pin>; + pinctrl-1 = <&P9_19_gpio_pin>; + pinctrl-2 = <&P9_19_gpio_pu_pin>; + pinctrl-3 = <&P9_19_gpio_pd_pin>; + pinctrl-4 = <&P9_19_gpio_input_pin>; + pinctrl-5 = <&P9_19_spi_cs_pin>; + pinctrl-6 = <&P9_19_can_pin>; + pinctrl-7 = <&P9_19_i2c_pin>; + pinctrl-8 = <&P9_19_pru_uart_pin>; + pinctrl-9 = <&P9_19_timer_pin>; + }; + + /* P9_20 (ZCZ ball D18) i2c */ + P9_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_20_default_pin>; + pinctrl-1 = <&P9_20_gpio_pin>; + pinctrl-2 = <&P9_20_gpio_pu_pin>; + pinctrl-3 = <&P9_20_gpio_pd_pin>; + pinctrl-4 = <&P9_20_gpio_input_pin>; + pinctrl-5 = <&P9_20_spi_cs_pin>; + pinctrl-6 = <&P9_20_can_pin>; + pinctrl-7 = <&P9_20_i2c_pin>; + pinctrl-8 = <&P9_20_pru_uart_pin>; + pinctrl-9 = <&P9_20_timer_pin>; + }; + + /* P9_21 (ZCZ ball B17) */ + P9_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_21_default_pin>; + pinctrl-1 = <&P9_21_gpio_pin>; + pinctrl-2 = <&P9_21_gpio_pu_pin>; + pinctrl-3 = <&P9_21_gpio_pd_pin>; + pinctrl-4 = <&P9_21_gpio_input_pin>; + pinctrl-5 = <&P9_21_spi_pin>; + pinctrl-6 = <&P9_21_uart_pin>; + pinctrl-7 = <&P9_21_i2c_pin>; + pinctrl-8 = <&P9_21_pwm_pin>; + pinctrl-9 = <&P9_21_pru_uart_pin>; + }; + + /* P9_22 (ZCZ ball A17) */ + P9_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_sclk", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_22_default_pin>; + pinctrl-1 = <&P9_22_gpio_pin>; + pinctrl-2 = <&P9_22_gpio_pu_pin>; + pinctrl-3 = <&P9_22_gpio_pd_pin>; + pinctrl-4 = <&P9_22_gpio_input_pin>; + pinctrl-5 = <&P9_22_spi_sclk_pin>; + pinctrl-6 = <&P9_22_uart_pin>; + pinctrl-7 = <&P9_22_i2c_pin>; + pinctrl-8 = <&P9_22_pwm_pin>; + pinctrl-9 = <&P9_22_pru_uart_pin>; + }; + + /* P9_23 (ZCZ ball V14) */ + P9_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P9_23_default_pin>; + pinctrl-1 = <&P9_23_gpio_pin>; + pinctrl-2 = <&P9_23_gpio_pu_pin>; + pinctrl-3 = <&P9_23_gpio_pd_pin>; + pinctrl-4 = <&P9_23_gpio_input_pin>; + pinctrl-5 = <&P9_23_pwm_pin>; + }; + + /* P9_24 (ZCZ ball D15) */ + P9_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_24_default_pin>; + pinctrl-1 = <&P9_24_gpio_pin>; + pinctrl-2 = <&P9_24_gpio_pu_pin>; + pinctrl-3 = <&P9_24_gpio_pd_pin>; + pinctrl-4 = <&P9_24_gpio_input_pin>; + pinctrl-5 = <&P9_24_uart_pin>; + pinctrl-6 = <&P9_24_can_pin>; + pinctrl-7 = <&P9_24_i2c_pin>; + pinctrl-8 = <&P9_24_pru_uart_pin>; + pinctrl-9 = <&P9_24_pruin_pin>; + }; + + /* P9_25 (ZCZ ball A14) audio */ + P9_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_25_default_pin>; + pinctrl-1 = <&P9_25_gpio_pin>; + pinctrl-2 = <&P9_25_gpio_pu_pin>; + pinctrl-3 = <&P9_25_gpio_pd_pin>; + pinctrl-4 = <&P9_25_gpio_input_pin>; + pinctrl-5 = <&P9_25_qep_pin>; + pinctrl-6 = <&P9_25_pruout_pin>; + pinctrl-7 = <&P9_25_pruin_pin>; + }; + + /* P9_26 (ZCZ ball D16) */ + P9_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_26_default_pin>; + pinctrl-1 = <&P9_26_gpio_pin>; + pinctrl-2 = <&P9_26_gpio_pu_pin>; + pinctrl-3 = <&P9_26_gpio_pd_pin>; + pinctrl-4 = <&P9_26_gpio_input_pin>; + pinctrl-5 = <&P9_26_uart_pin>; + pinctrl-6 = <&P9_26_can_pin>; + pinctrl-7 = <&P9_26_i2c_pin>; + pinctrl-8 = <&P9_26_pru_uart_pin>; + pinctrl-9 = <&P9_26_pruin_pin>; + }; + + /* P9_27 (ZCZ ball C13) */ + P9_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_27_default_pin>; + pinctrl-1 = <&P9_27_gpio_pin>; + pinctrl-2 = <&P9_27_gpio_pu_pin>; + pinctrl-3 = <&P9_27_gpio_pd_pin>; + pinctrl-4 = <&P9_27_gpio_input_pin>; + pinctrl-5 = <&P9_27_qep_pin>; + pinctrl-6 = <&P9_27_pruout_pin>; + pinctrl-7 = <&P9_27_pruin_pin>; + }; + + /* P9_28 (ZCZ ball C12) audio */ + P9_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "pwm", "pwm2", "pruout", "pruin"; + pinctrl-0 = <&P9_28_default_pin>; + pinctrl-1 = <&P9_28_gpio_pin>; + pinctrl-2 = <&P9_28_gpio_pu_pin>; + pinctrl-3 = <&P9_28_gpio_pd_pin>; + pinctrl-4 = <&P9_28_gpio_input_pin>; + pinctrl-5 = <&P9_28_spi_cs_pin>; + pinctrl-6 = <&P9_28_pwm_pin>; + pinctrl-7 = <&P9_28_pwm2_pin>; + pinctrl-8 = <&P9_28_pruout_pin>; + pinctrl-9 = <&P9_28_pruin_pin>; + }; + + /* P9_29 (ZCZ ball B13) audio */ + P9_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_29_default_pin>; + pinctrl-1 = <&P9_29_gpio_pin>; + pinctrl-2 = <&P9_29_gpio_pu_pin>; + pinctrl-3 = <&P9_29_gpio_pd_pin>; + pinctrl-4 = <&P9_29_gpio_input_pin>; + pinctrl-5 = <&P9_29_spi_pin>; + pinctrl-6 = <&P9_29_pwm_pin>; + pinctrl-7 = <&P9_29_pruout_pin>; + pinctrl-8 = <&P9_29_pruin_pin>; + }; + + /* P9_30 (ZCZ ball D12) gpio-hog wl1835 */ + + /* P9_31 (ZCZ ball A13) audio */ + P9_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_sclk", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_31_default_pin>; + pinctrl-1 = <&P9_31_gpio_pin>; + pinctrl-2 = <&P9_31_gpio_pu_pin>; + pinctrl-3 = <&P9_31_gpio_pd_pin>; + pinctrl-4 = <&P9_31_gpio_input_pin>; + pinctrl-5 = <&P9_31_spi_sclk_pin>; + pinctrl-6 = <&P9_31_pwm_pin>; + pinctrl-7 = <&P9_31_pruout_pin>; + pinctrl-8 = <&P9_31_pruin_pin>; + }; + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) */ + P9_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "timer", "pruin"; + pinctrl-0 = <&P9_41_default_pin>; + pinctrl-1 = <&P9_41_gpio_pin>; + pinctrl-2 = <&P9_41_gpio_pu_pin>; + pinctrl-3 = <&P9_41_gpio_pd_pin>; + pinctrl-4 = <&P9_41_gpio_input_pin>; + pinctrl-5 = <&P9_41_timer_pin>; + pinctrl-6 = <&P9_41_pruin_pin>; + }; + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) */ + P9_91_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_91_default_pin>; + pinctrl-1 = <&P9_91_gpio_pin>; + pinctrl-2 = <&P9_91_gpio_pu_pin>; + pinctrl-3 = <&P9_91_gpio_pd_pin>; + pinctrl-4 = <&P9_91_gpio_input_pin>; + pinctrl-5 = <&P9_91_qep_pin>; + pinctrl-6 = <&P9_91_pruout_pin>; + pinctrl-7 = <&P9_91_pruin_pin>; + }; + + /* P9_42 (ZCZ ball C18) */ + P9_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "spi_sclk", "uart", "pwm", "pru_ecap"; + pinctrl-0 = <&P9_42_default_pin>; + pinctrl-1 = <&P9_42_gpio_pin>; + pinctrl-2 = <&P9_42_gpio_pu_pin>; + pinctrl-3 = <&P9_42_gpio_pd_pin>; + pinctrl-4 = <&P9_42_gpio_input_pin>; + pinctrl-5 = <&P9_42_spi_cs_pin>; + pinctrl-6 = <&P9_42_spi_sclk_pin>; + pinctrl-7 = <&P9_42_uart_pin>; + pinctrl-8 = <&P9_42_pwm_pin>; + pinctrl-9 = <&P9_42_pru_ecap_pin>; + }; + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) */ + P9_92_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_92_default_pin>; + pinctrl-1 = <&P9_92_gpio_pin>; + pinctrl-2 = <&P9_92_gpio_pu_pin>; + pinctrl-3 = <&P9_92_gpio_pd_pin>; + pinctrl-4 = <&P9_92_gpio_input_pin>; + pinctrl-5 = <&P9_92_qep_pin>; + pinctrl-6 = <&P9_92_pruout_pin>; + pinctrl-7 = <&P9_92_pruin_pin>; + }; + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P8_03 { + gpio-name = "P8_03"; + gpio = <&gpio1 6 0>; + input; + dir-changeable; + }; + + P8_04 { + gpio-name = "P8_04"; + gpio = <&gpio1 7 0>; + input; + dir-changeable; + }; + + P8_05 { + gpio-name = "P8_05"; + gpio = <&gpio1 2 0>; + input; + dir-changeable; + }; + + P8_06 { + gpio-name = "P8_06"; + gpio = <&gpio1 3 0>; + input; + dir-changeable; + }; + + P8_07 { + gpio-name = "P8_07"; + gpio = <&gpio2 2 0>; + input; + dir-changeable; + }; + + P8_08 { + gpio-name = "P8_08"; + gpio = <&gpio2 3 0>; + input; + dir-changeable; + }; + + P8_09 { + gpio-name = "P8_09"; + gpio = <&gpio2 5 0>; + input; + dir-changeable; + }; + + P8_10 { + gpio-name = "P8_10"; + gpio = <&gpio2 4 0>; + input; + dir-changeable; + }; + + P8_11 { + gpio-name = "P8_11"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + + P8_12 { + gpio-name = "P8_12"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + + P8_13 { + gpio-name = "P8_13"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + + P8_15 { + gpio-name = "P8_15"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + + P8_16 { + gpio-name = "P8_16"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + + P8_18 { + gpio-name = "P8_18"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + + P8_19 { + gpio-name = "P8_19"; + gpio = <&gpio0 22 0>; + input; + dir-changeable; + }; + + P8_20 { + gpio-name = "P8_20"; + gpio = <&gpio1 31 0>; + input; + dir-changeable; + }; + + P8_21 { + gpio-name = "P8_21"; + gpio = <&gpio1 30 0>; + input; + dir-changeable; + }; + + P8_22 { + gpio-name = "P8_22"; + gpio = <&gpio1 5 0>; + input; + dir-changeable; + }; + + P8_23 { + gpio-name = "P8_23"; + gpio = <&gpio1 4 0>; + input; + dir-changeable; + }; + + P8_24 { + gpio-name = "P8_24"; + gpio = <&gpio1 1 0>; + input; + dir-changeable; + }; + + P8_25 { + gpio-name = "P8_25"; + gpio = <&gpio1 0 0>; + input; + dir-changeable; + }; + + P8_27 { + gpio-name = "P8_27"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + + P8_28 { + gpio-name = "P8_28"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + + P8_29 { + gpio-name = "P8_29"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + + P8_30 { + gpio-name = "P8_30"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + + P8_31 { + gpio-name = "P8_31"; + gpio = <&gpio0 10 0>; + input; + dir-changeable; + }; + + P8_32 { + gpio-name = "P8_32"; + gpio = <&gpio0 11 0>; + input; + dir-changeable; + }; + + P8_33 { + gpio-name = "P8_33"; + gpio = <&gpio0 9 0>; + input; + dir-changeable; + }; + + P8_34 { + gpio-name = "P8_34"; + gpio = <&gpio2 17 0>; + input; + dir-changeable; + }; + + P8_35 { + gpio-name = "P8_35"; + gpio = <&gpio0 8 0>; + input; + dir-changeable; + }; + + P8_36 { + gpio-name = "P8_36"; + gpio = <&gpio2 16 0>; + input; + dir-changeable; + }; + + P8_37 { + gpio-name = "P8_37"; + gpio = <&gpio2 14 0>; + input; + dir-changeable; + }; + + P8_38 { + gpio-name = "P8_38"; + gpio = <&gpio2 15 0>; + input; + dir-changeable; + }; + + P8_39 { + gpio-name = "P8_39"; + gpio = <&gpio2 12 0>; + input; + dir-changeable; + }; + + P8_40 { + gpio-name = "P8_40"; + gpio = <&gpio2 13 0>; + input; + dir-changeable; + }; + + P8_41 { + gpio-name = "P8_41"; + gpio = <&gpio2 10 0>; + input; + dir-changeable; + }; + + P8_42 { + gpio-name = "P8_42"; + gpio = <&gpio2 11 0>; + input; + dir-changeable; + }; + + P8_43 { + gpio-name = "P8_43"; + gpio = <&gpio2 8 0>; + input; + dir-changeable; + }; + + P8_44 { + gpio-name = "P8_44"; + gpio = <&gpio2 9 0>; + input; + dir-changeable; + }; + + P8_45 { + gpio-name = "P8_45"; + gpio = <&gpio2 6 0>; + input; + dir-changeable; + }; + + P8_46 { + gpio-name = "P8_46"; + gpio = <&gpio2 7 0>; + input; + dir-changeable; + }; + + P9_11 { + gpio-name = "P9_11"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + + P9_12 { + gpio-name = "P9_12"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + + P9_13 { + gpio-name = "P9_13"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + + P9_14 { + gpio-name = "P9_14"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + + P9_15 { + gpio-name = "P9_15"; + gpio = <&gpio1 16 0>; + input; + dir-changeable; + }; + + P9_16 { + gpio-name = "P9_16"; + gpio = <&gpio1 19 0>; + input; + dir-changeable; + }; + + P9_17 { + gpio-name = "P9_17"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + + P9_18 { + gpio-name = "P9_18"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + + P9_19 { + gpio-name = "P9_19"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + + P9_20 { + gpio-name = "P9_20"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + + P9_21 { + gpio-name = "P9_21"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + + P9_22 { + gpio-name = "P9_22"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + + P9_23 { + gpio-name = "P9_23"; + gpio = <&gpio1 17 0>; + input; + dir-changeable; + }; + + P9_24 { + gpio-name = "P9_24"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + + P9_25 { + gpio-name = "P9_25"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + + P9_26 { + gpio-name = "P9_26"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + + P9_27 { + gpio-name = "P9_27"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + + P9_28 { + gpio-name = "P9_28"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + + P9_29 { + gpio-name = "P9_29"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + + P9_31 { + gpio-name = "P9_31"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + + P9_41 { + gpio-name = "P9_41"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + + P9_91 { + gpio-name = "P9_91"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + + P9_42 { + gpio-name = "P9_42"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + + P9_92 { + gpio-name = "P9_92"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bonegreen-wireless.dts b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts --- a/arch/arm/boot/dts/am335x-bonegreen-wireless.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts 2022-01-06 12:45:53.806318073 -0500 @@ -13,6 +13,11 @@ model = "TI AM335x BeagleBone Green Wireless"; compatible = "ti,am335x-bone-green-wireless", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + chosen { + base_dtb = "am335x-bonegreen-wireless.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + wlan_en_reg: fixedregulator@2 { compatible = "regulator-fixed"; regulator-name = "wlan-en-regulator"; @@ -101,7 +106,7 @@ }; &gpio1 { - ls_buf_en { + ls-buf-en-hog { gpio-hog; gpios = <29 GPIO_ACTIVE_HIGH>; output-high; @@ -118,7 +123,7 @@ /* an external pulldown on U21 pin 4. */ &gpio3 { - bt_aud_in { + bt-aud-in-hog { gpio-hog; gpios = <16 GPIO_ACTIVE_HIGH>; output-low; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts b/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts --- a/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bonegreen-wireless-common-univ.dtsi" +#include + +/ { + model = "TI AM335x BeagleBone Green Wireless"; + compatible = "ti,am335x-bone-green-wireless", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bonegreen-wireless-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&gpio1 { + ls-buf-en-hog { + gpio-hog; + gpios = <29 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +/* BT_AUD_OUT from wl1835 has to be pulled low when WL_EN is activated.*/ +/* in case it isn't, wilink8 ends up in one of the test modes that */ +/* intruces various issues (elp wkaeup timeouts etc.) */ +/* On the BBGW this pin is routed through the level shifter (U21) that */ +/* introduces a pullup on the line and wilink8 ends up in a bad state. */ +/* use a gpio hog to force this pin low. An alternative may be adding */ +/* an external pulldown on U21 pin 4. */ + +&gpio3 { + bt-aud-in-hog { + gpio-hog; + gpios = <16 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "MCASP0_AHCLKR"; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bone-jtag.dtsi b/arch/arm/boot/dts/am335x-bone-jtag.dtsi --- a/arch/arm/boot/dts/am335x-bone-jtag.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bone-jtag.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&clkout2_pin>; + + clkout2_pin: pinmux_clkout2_pin { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* xdma_event_intr1.clkout2 */ + >; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-bone-uboot-univ.dts b/arch/arm/boot/dts/am335x-bone-uboot-univ.dts --- a/arch/arm/boot/dts/am335x-bone-uboot-univ.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-bone-uboot-univ.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bone-common-univ.dtsi" + +/ { + model = "TI AM335x BeagleBone"; + compatible = "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bone-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&ldo3_reg>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-cm-t335.dts b/arch/arm/boot/dts/am335x-cm-t335.dts --- a/arch/arm/boot/dts/am335x-cm-t335.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-cm-t335.dts 2022-01-06 12:45:53.806318073 -0500 @@ -122,7 +122,7 @@ AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE0) - /* gpmc_wpn.gpio0_30 */ + /* gpmc_wpn.gpio0_31 */ AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE, PIN_OUTPUT, MUX_MODE0) @@ -333,7 +333,7 @@ &epwmss0 { status = "okay"; - ecap0: ecap@100 { + ecap0: pwm@100 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&ecap0_pins>; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts --- a/arch/arm/boot/dts/am335x-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -229,7 +229,7 @@ AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) /* gpmc_wpn.gpio0_30 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) /* gpmc_wpn.gpio0_31 */ AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE, PIN_OUTPUT, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_OEN_REN, PIN_OUTPUT, MUX_MODE0) @@ -495,7 +495,7 @@ &epwmss0 { status = "okay"; - ecap0: ecap@100 { + ecap0: pwm@100 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&ecap0_pins>; @@ -684,28 +684,31 @@ }; }; -&mac { +&mac_sw { pinctrl-names = "default", "sleep"; pinctrl-0 = <&cpsw_default>; pinctrl-1 = <&cpsw_sleep>; status = "okay"; - slaves = <1>; }; -&davinci_mdio { +&davinci_mdio_sw { pinctrl-names = "default", "sleep"; pinctrl-0 = <&davinci_mdio_default>; pinctrl-1 = <&davinci_mdio_sleep>; - status = "okay"; ethphy0: ethernet-phy@0 { reg = <0>; }; }; -&cpsw_emac0 { +&cpsw_port1 { phy-handle = <ðphy0>; phy-mode = "rgmii-id"; + ti,dual-emac-pvid = <1>; +}; + +&cpsw_port2 { + status = "disabled"; }; &tscadc { @@ -775,3 +778,11 @@ clocks = <&clk_32768_ck>, <&clk_24mhz_clkctrl AM3_CLK_24MHZ_CLKDIV32K_CLKCTRL 0>; clock-names = "ext-clk", "int-clk"; }; + +&pruss_tm { + status = "okay"; +}; + +&wkup_m3_ipc { + ti,scale-data-fw = "am335x-evm-scale-data.bin"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts --- a/arch/arm/boot/dts/am335x-evmsk.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-evmsk.dts 2022-01-06 12:45:53.806318073 -0500 @@ -441,6 +441,22 @@ AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_csn0.gpio1_29 */ >; }; + + pruss_uart_pins: pruss_uart_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT_PULLDOWN, MUX_MODE4) /* spi0_sclk.pr1_uart0_cts_n */ + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_OUTPUT_PULLDOWN, MUX_MODE4) /* spi0_d0.pr1_uart0_rts_n */ + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE4) /* spi0_d1.pr1_uart0_rxd */ + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_OUTPUT_PULLUP, MUX_MODE4) /* spi0_cs9.pr1_uart0_txd */ + >; + }; +}; + +&pruss_uart { + interrupts = <6 2 2>; + pinctrl-names = "default"; + pinctrl-0 = <&pruss_uart_pins>; + status = "okay"; }; &uart0 { @@ -510,13 +526,17 @@ &epwmss2 { status = "okay"; - ecap2: ecap@100 { + ecap2: pwm@100 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&ecap2_pins>; }; }; +&wkup_m3_ipc { + ti,scale-data-fw = "am335x-evm-scale-data.bin"; +}; + #include "tps65910.dtsi" &tps { @@ -596,19 +616,17 @@ }; }; -&mac { +&mac_sw { pinctrl-names = "default", "sleep"; pinctrl-0 = <&cpsw_default>; pinctrl-1 = <&cpsw_sleep>; - dual_emac = <1>; status = "okay"; }; -&davinci_mdio { +&davinci_mdio_sw { pinctrl-names = "default", "sleep"; pinctrl-0 = <&davinci_mdio_default>; pinctrl-1 = <&davinci_mdio_sleep>; - status = "okay"; ethphy0: ethernet-phy@0 { reg = <0>; @@ -619,16 +637,16 @@ }; }; -&cpsw_emac0 { +&cpsw_port1 { phy-handle = <ðphy0>; phy-mode = "rgmii-id"; - dual_emac_res_vlan = <1>; + ti,dual-emac-pvid = <1>; }; -&cpsw_emac1 { +&cpsw_port2 { phy-handle = <ðphy1>; phy-mode = "rgmii-id"; - dual_emac_res_vlan = <2>; + ti,dual-emac-pvid = <2>; }; &mmc1 { @@ -717,3 +735,7 @@ clocks = <&clk_32768_ck>, <&clk_24mhz_clkctrl AM3_CLK_24MHZ_CLKDIV32K_CLKCTRL 0>; clock-names = "ext-clk", "int-clk"; }; + +&pruss_tm { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-icev2.dts b/arch/arm/boot/dts/am335x-icev2.dts --- a/arch/arm/boot/dts/am335x-icev2.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-icev2.dts 2022-01-06 12:45:53.806318073 -0500 @@ -474,31 +474,29 @@ }; }; -&cpsw_emac0 { +&cpsw_port1 { phy-handle = <ðphy0>; phy-mode = "rmii"; - dual_emac_res_vlan = <1>; + ti,dual-emac-pvid = <1>; }; -&cpsw_emac1 { +&cpsw_port2 { phy-handle = <ðphy1>; phy-mode = "rmii"; - dual_emac_res_vlan = <2>; + ti,dual-emac-pvid = <2>; }; -&mac { +&mac_sw { pinctrl-names = "default", "sleep"; pinctrl-0 = <&cpsw_default>; pinctrl-1 = <&cpsw_sleep>; status = "okay"; - dual_emac; }; -&davinci_mdio { +&davinci_mdio_sw { pinctrl-names = "default", "sleep"; pinctrl-0 = <&davinci_mdio_default>; pinctrl-1 = <&davinci_mdio_sleep>; - status = "okay"; reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; reset-delay-us = <2>; /* PHY datasheet states 1uS min */ @@ -510,3 +508,7 @@ reg = <3>; }; }; + +&pruss_tm { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-icev2-prueth.dts b/arch/arm/boot/dts/am335x-icev2-prueth.dts --- a/arch/arm/boot/dts/am335x-icev2-prueth.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-icev2-prueth.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/* + * AM335x ICE V2 board + * http://www.ti.com/tool/tmdsice3359 + */ + +/dts-v1/; + +#include "am335x-icev2.dts" + +/ { + aliases { + ethernet0 = &pruss_emac0; + ethernet1 = &pruss_emac1; + }; + + /* Dual mac ethernet application node on icss */ + pruss-eth { + compatible = "ti,am3359-prueth"; + ti,prus = <&pru0>, <&pru1>; + sram = <&ocmcram>; + interrupt-parent = <&pruss_intc>; + mii-rt = <&pruss_mii_rt>; + iep = <&pruss_iep>; + pinctrl-0 = <&pruss_eth_default>; + pinctrl-names = "default"; + interrupts = <20 2 2>, <21 3 3>; + interrupt-names = "rx_lre_hp", "rx_lre_lp"; + + pruss_emac0: ethernet-mii0 { + phy-handle = <&pruss_eth0_phy>; + phy-mode = "mii"; + interrupts = <20 2 2>; + interrupt-names = "rx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + pruss_emac1: ethernet-mii1 { + phy-handle = <&pruss_eth1_phy>; + phy-mode = "mii"; + interrupts = <21 3 3>; + interrupt-names = "rx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; +}; + +&am33xx_pinmux { + pruss_mdio_default: pruss-mdio-default { + pinctrl-single,pins = < + AM33XX_IOPAD(0x88c, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_clk.pr1_mdio_mdclk */ + AM33XX_IOPAD(0x888, (PIN_INPUT | MUX_MODE5)) /* gpmc_csn3.pr1_mdio_data */ + AM33XX_IOPAD(0x89c, (PIN_INPUT_PULLUP | MUX_MODE7)) /* gpmc_ben0_cle.gpio2_5 */ + /* disable CPSW MDIO */ + AM33XX_IOPAD(0x948, (PIN_INPUT_PULLUP | MUX_MODE7)) /* mdio_data.gpio0_0 */ + AM33XX_IOPAD(0x94c, (PIN_INPUT_PULLUP | MUX_MODE7)) /* mdio_clk.gpio0_1 */ + >; + }; + + pruss_eth_default: pruss-eth-default { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a0, (PIN_INPUT | MUX_MODE2)) /* dss_data0.pr1_mii_mt0_clk */ + AM33XX_IOPAD(0x8b4, (PIN_OUTPUT | MUX_MODE2)) /* dss_data5.pr1_mii0_txd0 */ + AM33XX_IOPAD(0x8b0, (PIN_OUTPUT | MUX_MODE2)) /* dss_data4.pr1_mii0_txd1 */ + AM33XX_IOPAD(0x8ac, (PIN_OUTPUT | MUX_MODE2)) /* dss_data3.pr1_mii0_txd2 */ + AM33XX_IOPAD(0x8a8, (PIN_OUTPUT | MUX_MODE2)) /* dss_data2.pr1_mii0_txd3 */ + AM33XX_IOPAD(0x8cc, (PIN_INPUT | MUX_MODE5)) /* dss_data11.pr1_mii0_rxd0 */ + AM33XX_IOPAD(0x8c8, (PIN_INPUT | MUX_MODE5)) /* dss_data10.pr1_mii0_rxd1 */ + AM33XX_IOPAD(0x8c4, (PIN_INPUT | MUX_MODE5)) /* dss_data9.pr1_mii0_rxd2 */ + AM33XX_IOPAD(0x8c0, (PIN_INPUT | MUX_MODE5)) /* dss_data8.pr1_mii0_rxd3 */ + AM33XX_IOPAD(0x8a4, (PIN_OUTPUT | MUX_MODE2)) /* dss_data1.pr1_mii0_txen */ + AM33XX_IOPAD(0x8d8, (PIN_INPUT | MUX_MODE5)) /* dss_data14.pr1_mii_mr0_clk */ + AM33XX_IOPAD(0x8dc, (PIN_INPUT | MUX_MODE5)) /* dss_data15.pr1_mii0_rxdv */ + AM33XX_IOPAD(0x8d4, (PIN_INPUT | MUX_MODE5)) /* dss_data13.pr1_mii0_rxer */ + AM33XX_IOPAD(0x8d0, (PIN_INPUT | MUX_MODE5)) /* dss_data12.pr1_mii0_rxlink */ + AM33XX_IOPAD(0x8e8, (PIN_INPUT | MUX_MODE2)) /* dss_pclk.pr1_mii0_crs */ + + AM33XX_IOPAD(0x840, (PIN_INPUT | MUX_MODE5)) /* gpmc_a0.pr1_mii_mt1_clk */ + AM33XX_IOPAD(0x850, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a4.pr1_mii1_txd0 */ + AM33XX_IOPAD(0x84c, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a3.pr1_mii1_txd1 */ + AM33XX_IOPAD(0x848, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a2.pr1_mii1_txd2 */ + AM33XX_IOPAD(0x844, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a1.pr1_mii1_txd3 */ + AM33XX_IOPAD(0x860, (PIN_INPUT | MUX_MODE5)) /* gpmc_a8.pr1_mii1_rxd0 */ + AM33XX_IOPAD(0x85c, (PIN_INPUT | MUX_MODE5)) /* gpmc_a7.pr1_mii1_rxd1 */ + AM33XX_IOPAD(0x858, (PIN_INPUT | MUX_MODE5)) /* gpmc_a6.pr1_mii1_rxd2 */ + AM33XX_IOPAD(0x854, (PIN_INPUT | MUX_MODE5)) /* gpmc_a5.pr1_mii1_rxd3 */ + AM33XX_IOPAD(0x874, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_wpn.pr1_mii1_txen */ + AM33XX_IOPAD(0x864, (PIN_INPUT | MUX_MODE5)) /* gpmc_a9.pr1_mii_mr1_clk */ + AM33XX_IOPAD(0x868, (PIN_INPUT | MUX_MODE5)) /* gpmc_a10.pr1_mii1_rxdv */ + AM33XX_IOPAD(0x86c, (PIN_INPUT | MUX_MODE5)) /* gpmc_a11.pr1_mii1_rxer */ + AM33XX_IOPAD(0x878, (PIN_INPUT | MUX_MODE5)) /* gpmc_ben1.pr1_mii1_rxlink */ + AM33XX_IOPAD(0x8ec, (PIN_INPUT | MUX_MODE2)) /* lcd_ac_bias_en.pr1_mii1_crs */ + AM33XX_IOPAD(0x870, (PIN_INPUT | MUX_MODE5)) /* gpmc_wait0.pr1_mii1_col */ + >; + }; +}; + +&gpio3 { + p4 { + gpio-hog; + gpios = <4 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "PR1_MII_CTRL"; + }; + + p10 { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + /* ETH1 mux: Low for MII-PRU, high for RMII-CPSW */ + output-low; + line-name = "MUX_MII_CTL1"; + }; +}; + +&mac { + status = "disabled"; +}; + +&davinci_mdio { + status = "disabled"; +}; + +&mac_sw { + status = "disabled"; +}; + +&pruss_mdio { + pinctrl-0 = <&pruss_mdio_default>; + pinctrl-names = "default"; + reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + reset-delay-us = <2>; /* PHY datasheet states 1uS min */ + status = "okay"; + + pruss_eth0_phy: ethernet-phy@1 { + reg = <1>; + }; + + pruss_eth1_phy: ethernet-phy@3 { + reg = <3>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-igep0033.dtsi b/arch/arm/boot/dts/am335x-igep0033.dtsi --- a/arch/arm/boot/dts/am335x-igep0033.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-igep0033.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -70,7 +70,7 @@ AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) /* gpmc_wpn.gpio0_30 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_INPUT_PULLUP, MUX_MODE7) /* gpmc_wpn.gpio0_31 */ AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE, PIN_OUTPUT, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_GPMC_OEN_REN, PIN_OUTPUT, MUX_MODE0) diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-osd3358-sm-red.dts b/arch/arm/boot/dts/am335x-osd3358-sm-red.dts --- a/arch/arm/boot/dts/am335x-osd3358-sm-red.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-osd3358-sm-red.dts 2022-01-06 12:45:53.806318073 -0500 @@ -10,13 +10,16 @@ #include "am33xx.dtsi" #include "am335x-osd335x-common.dtsi" -#include - -#include +#include "am335x-boneblack-hdmi.dtsi" / { model = "Octavo Systems OSD3358-SM-RED"; compatible = "oct,osd3358-sm-refdesign", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-osd3358-sm-red.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &ldo3_reg { @@ -25,10 +28,6 @@ regulator-always-on; }; -&mmc1 { - vmmc-supply = <&vmmcsd_fixed>; -}; - &mmc2 { vmmc-supply = <&vmmcsd_fixed>; pinctrl-names = "default"; @@ -37,110 +36,7 @@ status = "okay"; }; -&am33xx_pinmux { - nxp_hdmi_bonelt_pins: nxp-hdmi-bonelt-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - >; - }; - - nxp_hdmi_bonelt_off_pins: nxp-hdmi-bonelt-off-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - >; - }; - - mcasp0_pins: mcasp0-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ - AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ - >; - }; - - flash_enable: flash-enable { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* rmii1_ref_clk.gpio0_29 */ - >; - }; - - imu_interrupt: imu-interrupt { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MII1_RX_ER, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_rx_er.gpio3_2 */ - >; - }; - - ethernet_interrupt: ethernet-interrupt{ - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_col.gpio3_0 */ - >; - }; -}; - -&lcdc { - status = "okay"; - - /* If you want to get 24 bit RGB and 16 BGR mode instead of - * current 16 bit RGB and 24 BGR modes, set the propety - * below to "crossed" and uncomment the video-ports -property - * in tda19988 node. - * AM335x errata for wiring: - * https://www.ti.com/lit/er/sprz360i/sprz360i.pdf - */ - - blue-and-red-wiring = "straight"; - - port { - lcdc_0: endpoint { - remote-endpoint = <&hdmi_0>; - }; - }; -}; - &i2c0 { - tda19988: hdmi-encoder@70 { - compatible = "nxp,tda998x"; - reg = <0x70>; - - pinctrl-names = "default", "off"; - pinctrl-0 = <&nxp_hdmi_bonelt_pins>; - pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; - - /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ - /* video-ports = <0x234501>; */ - - #sound-dai-cells = <0>; - audio-ports = < TDA998x_I2S 0x03>; - - port { - hdmi_0: endpoint { - remote-endpoint = <&lcdc_0>; - }; - }; - }; - mpu9250: imu@68 { compatible = "invensense,mpu6050"; reg = <0x68>; @@ -167,55 +63,7 @@ }; }; -&rtc { - system-power-controller; -}; - -&mcasp0 { - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&mcasp0_pins>; - status = "okay"; - op-mode = <0>; /* MCASP_IIS_MODE */ - tdm-slots = <2>; - serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ - 0 0 1 0 - >; - tx-num-evt = <32>; - rx-num-evt = <32>; -}; - / { - clk_mcasp0_fixed: clk-mcasp0-fixed { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <24576000>; - }; - - clk_mcasp0: clk-mcasp0 { - #clock-cells = <0>; - compatible = "gpio-gate-clock"; - clocks = <&clk_mcasp0_fixed>; - enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "TI BeagleBone Black"; - simple-audio-card,format = "i2s"; - simple-audio-card,bitclock-master = <&dailink0_master>; - simple-audio-card,frame-master = <&dailink0_master>; - - dailink0_master: simple-audio-card,cpu { - sound-dai = <&mcasp0>; - clocks = <&clk_mcasp0>; - }; - - simple-audio-card,codec { - sound-dai = <&tda19988>; - }; - }; - chosen { stdout-path = &uart0; }; @@ -264,8 +112,23 @@ }; &am33xx_pinmux { - pinctrl-names = "default"; - pinctrl-0 = <&clkout2_pin>; + flash_enable: flash-enable { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* rmii1_ref_clk.gpio0_29 */ + >; + }; + + imu_interrupt: imu-interrupt { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RX_ER, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_rx_er.gpio3_2 */ + >; + }; + + ethernet_interrupt: ethernet-interrupt{ + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_col.gpio3_0 */ + >; + }; user_leds_s0: user-leds-s0 { pinctrl-single,pins = < @@ -290,12 +153,6 @@ >; }; - clkout2_pin: pinmux-clkout2-pin { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* xdma_event_intr1.clkout2 */ - >; - }; - cpsw_default: cpsw-default { pinctrl-single,pins = < /* Slave 1 */ @@ -382,6 +239,7 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; + symlink = "bone/uart/0"; }; &usb0 { @@ -399,6 +257,7 @@ pinctrl-0 = <&i2c2_pins>; status = "okay"; clock-frequency = <100000>; + symlink = "bone/i2c/2"; }; &cpsw_emac0 { @@ -427,6 +286,7 @@ &mmc1 { status = "okay"; + vmmc-supply = <&vmmcsd_fixed>; bus-width = <0x4>; pinctrl-names = "default"; pinctrl-0 = <&mmc1_pins>; @@ -434,6 +294,7 @@ }; &rtc { + system-power-controller; clocks = <&clk_32768_ck>, <&clk_24mhz_clkctrl AM3_CLK_24MHZ_CLKDIV32K_CLKCTRL 0>; clock-names = "ext-clk", "int-clk"; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-osd335x-common.dtsi b/arch/arm/boot/dts/am335x-osd335x-common.dtsi --- a/arch/arm/boot/dts/am335x-osd335x-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-osd335x-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -48,6 +48,7 @@ status = "okay"; clock-frequency = <400000>; + symlink = "bone/i2c/0"; tps: tps@24 { reg = <0x24>; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-pocketbeagle.dts b/arch/arm/boot/dts/am335x-pocketbeagle.dts --- a/arch/arm/boot/dts/am335x-pocketbeagle.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-pocketbeagle.dts 2022-01-06 12:45:53.806318073 -0500 @@ -15,6 +15,8 @@ chosen { stdout-path = &uart0; + base_dtb = "am335x-pocketbeagle.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; leds { @@ -61,51 +63,51 @@ &gpio0 { gpio-line-names = - "[NC]", - "[NC]", + "NC", + "NC", "P1.08 [SPI0_CLK]", "P1.10 [SPI0_MISO]", "P1.12 [SPI0_MOSI]", "P1.06 [SPI0_CS]", "[MMC0_CD]", "P2.29 [SPI1_CLK]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", + "[SYSBOOT 12]", + "[SYSBOOT 13]", + "[SYSBOOT 14]", + "[SYSBOOT 15]", "P1.26 [I2C2_SDA]", "P1.28 [I2C2_SCL]", "P2.11 [I2C1_SDA]", "P2.09 [I2C1_SCL]", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", "P2.31 [SPI1_CS]", "P1.20 [PRU0.16]", - "[NC]", - "[NC]", + "NC", + "NC", "P2.03", - "[NC]", - "[NC]", + "NC", + "NC", "P1.34", "P2.19", - "[NC]", - "[NC]", + "NC", + "NC", "P2.05 [UART4_RX]", "P2.07 [UART4_TX]"; }; &gpio1 { gpio-line-names = - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", "P2.25 [SPI1_MOSI]", "P1.32 [UART0_RX]", "P1.30 [UART0_TX]", @@ -113,10 +115,10 @@ "P2.33", "P2.22", "P2.18", - "[NC]", - "[NC]", + "NC", + "NC", "P2.01 [PWM1A]", - "[NC]", + "NC", "P2.10", "[USR LED 0]", "[USR LED 1]", @@ -126,35 +128,35 @@ "P2.04", "P2.02", "P2.08", - "[NC]", - "[NC]", - "[NC]"; + "NC", + "NC", + "NC"; }; &gpio2 { gpio-line-names = "P2.20", "P2.17", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", "[EEPROM_WP]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "[SYSBOOT 0]", + "[SYSBOOT 1]", + "[SYSBOOT 2]", + "[SYSBOOT 3]", + "[SYSBOOT 4]", + "[SYSBOOT 5]", + "[SYSBOOT 6]", + "[SYSBOOT 7]", + "[SYSBOOT 8]", + "[SYSBOOT 9]", + "[SYSBOOT 10]", + "[SYSBOOT 11]", + "NC", + "NC", + "NC", + "NC", "P2.35 [AIN5]", "P1.02 [AIN6]", "P1.35 [PRU1.10]", @@ -169,19 +171,19 @@ &gpio3 { gpio-line-names = - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", + "NC", + "NC", "[I2C0_SDA]", "[I2C0_SCL]", - "[JTAG]", - "[JTAG]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "[JTAG EMU0]", + "[JTAG EMU1]", + "NC", + "NC", + "NC", + "NC", "P1.03 [USB1]", "P1.36 [PWM0A]", "P1.33 [PRU0.1]", @@ -191,162 +193,37 @@ "P2.34 [PRU0.5]", "P2.28 [PRU0.6]", "P1.29 [PRU0.7]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]"; + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; }; &am33xx_pinmux { - - pinctrl-names = "default"; - - pinctrl-0 = < &P2_03_gpio &P1_34_gpio &P2_19_gpio &P2_24_gpio - &P2_33_gpio &P2_22_gpio &P2_18_gpio &P2_10_gpio - &P2_06_gpio &P2_04_gpio &P2_02_gpio &P2_08_gpio - &P2_17_gpio >; - - /* P2_03 (ZCZ ball T10) gpio0_23 0x824 PIN 9 */ - P2_03_gpio: pinmux_P2_03_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD9, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P1_34 (ZCZ ball T11) gpio0_26 0x828 PIN 10 */ - P1_34_gpio: pinmux_P1_34_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_19 (ZCZ ball U12) gpio0_27 0x82c PIN 11 */ - P2_19_gpio: pinmux_P2_19_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD11, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_24 (ZCZ ball T12) gpio1_12 0x830 PIN 12 */ - P2_24_gpio: pinmux_P2_24_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_33 (ZCZ ball R12) gpio1_13 0x834 PIN 13 */ - P2_33_gpio: pinmux_P2_33_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_22 (ZCZ ball V13) gpio1_14 0x838 PIN 14 */ - P2_22_gpio: pinmux_P2_22_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_18 (ZCZ ball U13) gpio1_15 0x83c PIN 15 */ - P2_18_gpio: pinmux_P2_18_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_10 (ZCZ ball R14) gpio1_20 0x850 PIN 20 */ - P2_10_gpio: pinmux_P2_10_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A4, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_06 (ZCZ ball U16) gpio1_25 0x864 PIN 25 */ - P2_06_gpio: pinmux_P2_06_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_04 (ZCZ ball T16) gpio1_26 0x868 PIN 26 */ - P2_04_gpio: pinmux_P2_04_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A10, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_02 (ZCZ ball V17) gpio1_27 0x86c PIN 27 */ - P2_02_gpio: pinmux_P2_02_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_08 (ZCZ ball U18) gpio1_28 0x878 PIN 30 */ - P2_08_gpio: pinmux_P2_08_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_INPUT_PULLDOWN, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x00 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x00 0x00 0x10 0x18>; - }; - - /* P2_17 (ZCZ ball V12) gpio2_1 0x88c PIN 35 */ - P2_17_gpio: pinmux_P2_17_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - i2c2_pins: pinmux-i2c2-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D17) uart1_rtsn.I2C2_SCL */ - AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D18) uart1_ctsn.I2C2_SDA */ - >; - }; - - ehrpwm0_pins: pinmux-ehrpwm0-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* (A13) mcasp0_aclkx.ehrpwm0A */ - >; - }; - - ehrpwm1_pins: pinmux-ehrpwm1-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U14) gpmc_a2.ehrpwm1A */ - >; - }; +// i2c2_pins: pinmux-i2c2-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D17) uart1_rtsn.I2C2_SCL */ +// AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D18) uart1_ctsn.I2C2_SDA */ +// >; +// }; + +// ehrpwm0_pins: pinmux-ehrpwm0-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* (A13) mcasp0_aclkx.ehrpwm0A */ +// >; +// }; + +// ehrpwm1_pins: pinmux-ehrpwm1-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U14) gpmc_a2.ehrpwm1A */ +// >; +// }; mmc0_pins: pinmux-mmc0-pins { pinctrl-single,pins = < @@ -360,23 +237,23 @@ >; }; - spi0_pins: pinmux-spi0-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE0) - >; - }; - - spi1_pins: pinmux-spi1-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_ECAP0_IN_PWM0_OUT, PIN_INPUT_PULLUP, MUX_MODE4) /* (C18) eCAP0_in_PWM0_out.spi1_sclk */ - AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E18) uart0_ctsn.spi1_d0 */ - AM33XX_PADCONF(AM335X_PIN_UART0_RTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E17) uart0_rtsn.spi1_d1 */ - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE4) /* (A15) xdma_event_intr0.spi1_cs1 */ - >; - }; +// spi0_pins: pinmux-spi0-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT_PULLUP, MUX_MODE0) /* (A17) spi0_sclk.spi0_sclk */ +// AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT_PULLUP, MUX_MODE0) /* (B17) spi0_d0.spi0_d0 */ +// AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE0) /* (B16) spi0_d1.spi0_d1 */ +// AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE0) /* (A16) spi0_cs0.spi0_cs0 */ +// >; +// }; + +// spi1_pins: pinmux-spi1-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_ECAP0_IN_PWM0_OUT, PIN_INPUT_PULLUP, MUX_MODE4) /* (C18) eCAP0_in_PWM0_out.spi1_sclk */ +// AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E18) uart0_ctsn.spi1_d0 */ +// AM33XX_PADCONF(AM335X_PIN_UART0_RTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E17) uart0_rtsn.spi1_d1 */ +// AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE4) /* (A15) xdma_event_intr0.spi1_cs1 */ +// >; +// }; usr_leds_pins: pinmux-usr-leds-pins { pinctrl-single,pins = < @@ -394,12 +271,839 @@ >; }; - uart4_pins: pinmux-uart4-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE6) /* (T17) gpmc_wait0.uart4_rxd */ - AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U17) gpmc_wpn.uart4_txd */ - >; - }; +// uart4_pins: pinmux-uart4-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE6) /* (T17) gpmc_wait0.uart4_rxd */ +// AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U17) gpmc_wpn.uart4_txd */ +// >; +// }; + + /************************/ + /* P1 Header */ + /************************/ + + /* P1_01 VIN-AC */ + + /* P1_02 (ZCZ ball R5) gpio2_23 */ + P1_02_default_pin: pinmux_P1_02_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P1_02_gpio_pin: pinmux_P1_02_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P1_02_gpio_pu_pin: pinmux_P1_02_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P1_02_gpio_pd_pin: pinmux_P1_02_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P1_02_gpio_input_pin: pinmux_P1_02_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE7) >; }; /* lcd_hsync.gpio2_23 */ + P1_02_pruout_pin: pinmux_P1_02_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_hsync.pru1_out9 */ + P1_02_pruin_pin: pinmux_P1_02_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e4, PIN_INPUT | MUX_MODE6) >; }; /* lcd_hsync.pru1_in9 */ + + /* P1_03 (ZCZ ball F15) usb1_vbus_out */ + + /* P1_04 (ZCZ ball R6) gpio2_25 */ + P1_04_default_pin: pinmux_P1_04_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P1_04_gpio_pin: pinmux_P1_04_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P1_04_gpio_pu_pin: pinmux_P1_04_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P1_04_gpio_pd_pin: pinmux_P1_04_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P1_04_gpio_input_pin: pinmux_P1_04_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_INPUT | MUX_MODE7) >; }; /* lcd_ac_bias_en.gpio2_25 */ + P1_04_pruout_pin: pinmux_P1_04_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_ac_bias_en.pru1_out11 */ + P1_04_pruin_pin: pinmux_P1_04_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08ec, PIN_INPUT | MUX_MODE6) >; }; /* lcd_ac_bias_en.pru1_in11 */ + + /* P1_05 (ZCZ ball T18) usb1_vbus_in */ + + /* P1_06 (ZCZ ball A16) spi0_cs0 */ + P1_06_default_pin: pinmux_P1_06_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_cs0.spi0_cs0 */ + P1_06_gpio_pin: pinmux_P1_06_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P1_06_gpio_pu_pin: pinmux_P1_06_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P1_06_gpio_pd_pin: pinmux_P1_06_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P1_06_gpio_input_pin: pinmux_P1_06_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_INPUT | MUX_MODE7) >; }; /* spi0_cs0.gpio0_5 */ + P1_06_spi_cs_pin: pinmux_P1_06_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_cs0.spi0_cs0 */ + P1_06_i2c_pin: pinmux_P1_06_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_cs0.i2c1_scl */ + P1_06_pwm_pin: pinmux_P1_06_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_cs0.ehrpwm0_synci */ + P1_06_pru_uart_pin: pinmux_P1_06_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x095c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_cs0.pr1_uart0_txd */ + + /* P1_07 VIN-USB */ + + /* P1_08 (ZCZ ball A17) spi0_sclk */ + P1_08_default_pin: pinmux_P1_08_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_sclk.spi0_sclk */ + P1_08_gpio_pin: pinmux_P1_08_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P1_08_gpio_pu_pin: pinmux_P1_08_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P1_08_gpio_pd_pin: pinmux_P1_08_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P1_08_gpio_input_pin: pinmux_P1_08_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_INPUT | MUX_MODE7) >; }; /* spi0_sclk.gpio0_2 */ + P1_08_spi_sclk_pin: pinmux_P1_08_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_sclk.spi0_sclk */ + P1_08_uart_pin: pinmux_P1_08_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* spi0_sclk.uart2_rxd */ + P1_08_i2c_pin: pinmux_P1_08_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_sclk.i2c2_sda */ + P1_08_pwm_pin: pinmux_P1_08_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_sclk.ehrpwm0a */ + P1_08_pru_uart_pin: pinmux_P1_08_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0950, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_sclk.pr1_uart0_cts_n */ + + /* P1_09 (ZCZ ball R18) USB1-DN */ + + /* P1_10 (ZCZ ball B17) spi0_d0 */ + P1_10_default_pin: pinmux_P1_10_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d0.spi0_d0 */ + P1_10_gpio_pin: pinmux_P1_10_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P1_10_gpio_pu_pin: pinmux_P1_10_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P1_10_gpio_pd_pin: pinmux_P1_10_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P1_10_gpio_input_pin: pinmux_P1_10_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_INPUT | MUX_MODE7) >; }; /* spi0_d0.gpio0_3 */ + P1_10_spi_pin: pinmux_P1_10_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d0.spi0_d0 */ + P1_10_uart_pin: pinmux_P1_10_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* spi0_d0.uart2_txd */ + P1_10_i2c_pin: pinmux_P1_10_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_d0.i2c2_scl */ + P1_10_pwm_pin: pinmux_P1_10_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_d0.ehrpwm0b */ + P1_10_pru_uart_pin: pinmux_P1_10_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0954, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_d0.pr1_uart0_rts_n */ + + /* P1_11 (ZCZ ball R17) USB1-DP */ + + /* P1_12 (ZCZ ball B16) spi0_d1 */ + P1_12_default_pin: pinmux_P1_12_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d1.spi0_d1 */ + P1_12_gpio_pin: pinmux_P1_12_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P1_12_gpio_pu_pin: pinmux_P1_12_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P1_12_gpio_pd_pin: pinmux_P1_12_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P1_12_gpio_input_pin: pinmux_P1_12_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_INPUT | MUX_MODE7) >; }; /* spi0_d1.gpio0_4 */ + P1_12_spi_pin: pinmux_P1_12_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* spi0_d1.spi0_d1 */ + P1_12_i2c_pin: pinmux_P1_12_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* spi0_d1.i2c1_sda */ + P1_12_pwm_pin: pinmux_P1_12_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* spi0_d1.ehrpwm0_tripzone_input */ + P1_12_pru_uart_pin: pinmux_P1_12_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0958, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* spi0_d1.pr1_uart0_rxd */ + + /* P1_13 (ZCZ ball P17) USB1-ID */ + + /* P1_14 VOUT-3.3V */ + + /* P1_15 GND */ + + /* P1_16 GND */ + + /* P1_17 (ZCZ ball A9) VREFN */ + + /* P1_18 (ZCZ ball B9) VREFP */ + + /* P1_19 (ZCZ ball B6) AIN0 */ + + /* P1_20 (ZCZ ball D14) gpio0_20 */ + P1_20_default_pin: pinmux_P1_20_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P1_20_gpio_pin: pinmux_P1_20_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P1_20_gpio_pu_pin: pinmux_P1_20_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P1_20_gpio_pd_pin: pinmux_P1_20_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P1_20_gpio_input_pin: pinmux_P1_20_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_INPUT | MUX_MODE7) >; }; /* xdma_event_intr1.gpio0_20 */ + P1_20_pruin_pin: pinmux_P1_20_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b4, PIN_INPUT | MUX_MODE5) >; }; /* xdma_event_intr1.pru0_in16 */ + + /* P1_21 (ZCZ ball C7) AIN1 */ + + /* P1_22 GND */ + + /* P1_23 (ZCZ ball B7) AIN2 */ + + /* P1_24 VOUT-5V */ + + /* P1_25 (ZCZ ball A7) AIN3 */ + + /* P1_26 (ZCZ ball D18) i2c2_sda */ + P1_26_default_pin: pinmux_P1_26_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_ctsn.i2c2_sda */ + P1_26_gpio_pin: pinmux_P1_26_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P1_26_gpio_pu_pin: pinmux_P1_26_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P1_26_gpio_pd_pin: pinmux_P1_26_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P1_26_gpio_input_pin: pinmux_P1_26_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_INPUT | MUX_MODE7) >; }; /* uart1_ctsn.gpio0_12 */ + P1_26_can_pin: pinmux_P1_26_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart1_ctsn.dcan0_tx */ + P1_26_i2c_pin: pinmux_P1_26_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_ctsn.i2c2_sda */ + P1_26_spi_cs_pin: pinmux_P1_26_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart1_ctsn.spi1_cs0 */ + P1_26_pru_uart_pin: pinmux_P1_26_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0978, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_ctsn.pr1_uart0_cts_n */ + + /* P1_27 (ZCZ ball C8) AIN4 */ + + /* P1_28 (ZCZ ball D17) i2c2_scl */ + P1_28_default_pin: pinmux_P1_28_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rtsn.i2c2_scl */ + P1_28_gpio_pin: pinmux_P1_28_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P1_28_gpio_pu_pin: pinmux_P1_28_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P1_28_gpio_pd_pin: pinmux_P1_28_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P1_28_gpio_input_pin: pinmux_P1_28_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_INPUT | MUX_MODE7) >; }; /* uart1_rtsn.gpio0_13 */ + P1_28_can_pin: pinmux_P1_28_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart1_rtsn.dcan0_rx */ + P1_28_i2c_pin: pinmux_P1_28_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rtsn.i2c2_scl */ + P1_28_spi_cs_pin: pinmux_P1_28_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart1_rtsn.spi1_cs1 */ + P1_28_pru_uart_pin: pinmux_P1_28_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x097c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_rtsn.pr1_uart0_rts_n */ + + /* P1_29 (ZCZ ball A14) pru0_in7 */ + P1_29_default_pin: pinmux_P1_29_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkx.pru0_in7 */ + P1_29_gpio_pin: pinmux_P1_29_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P1_29_gpio_pu_pin: pinmux_P1_29_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P1_29_gpio_pd_pin: pinmux_P1_29_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P1_29_gpio_input_pin: pinmux_P1_29_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_ahclkx.gpio3_21 */ + P1_29_qep_pin: pinmux_P1_29_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_ahclkx.eqep0_strobe */ + P1_29_pruout_pin: pinmux_P1_29_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_ahclkx.pru0_out7 */ + P1_29_pruin_pin: pinmux_P1_29_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09ac, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkx.pru0_in7 */ + + /* P1_30 (ZCZ ball E16) uart0_txd */ + P1_30_default_pin: pinmux_P1_30_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart0_txd.uart0_txd */ + P1_30_gpio_pin: pinmux_P1_30_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart0_txd.gpio1_11 */ + P1_30_gpio_pu_pin: pinmux_P1_30_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart0_txd.gpio1_11 */ + P1_30_gpio_pd_pin: pinmux_P1_30_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart0_txd.gpio1_11 */ + P1_30_gpio_input_pin: pinmux_P1_30_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_INPUT | MUX_MODE7) >; }; /* uart0_txd.gpio1_11 */ + P1_30_uart_pin: pinmux_P1_30_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart0_txd.uart0_txd */ + P1_30_spi_cs_pin: pinmux_P1_30_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart0_txd.spi1_cs1 */ + P1_30_can_pin: pinmux_P1_30_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart0_txd.dcan0_rx */ + P1_30_i2c_pin: pinmux_P1_30_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart0_txd.i2c2_scl */ + P1_30_pruout_pin: pinmux_P1_30_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* uart0_txd.pru1_out15 */ + P1_30_pruin_pin: pinmux_P1_30_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0974, PIN_INPUT | MUX_MODE6) >; }; /* uart0_txd.pru1_in15 */ + + /* P1_31 (ZCZ ball B12) pru0_in4 */ + P1_31_default_pin: pinmux_P1_31_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkr.pru0_in4 */ + P1_31_gpio_pin: pinmux_P1_31_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P1_31_gpio_pu_pin: pinmux_P1_31_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P1_31_gpio_pd_pin: pinmux_P1_31_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P1_31_gpio_input_pin: pinmux_P1_31_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_aclkr.gpio3_18 */ + P1_31_qep_pin: pinmux_P1_31_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkr.eqep0a_in */ + P1_31_pruout_pin: pinmux_P1_31_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_aclkr.pru0_out4 */ + P1_31_pruin_pin: pinmux_P1_31_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a0, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkr.pru0_in4 */ + + /* P1_32 (ZCZ ball E15) uart0_rxd */ + P1_32_default_pin: pinmux_P1_32_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart0_rxd.uart0_rxd */ + P1_32_gpio_pin: pinmux_P1_32_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart0_rxd.gpio1_10 */ + P1_32_gpio_pu_pin: pinmux_P1_32_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart0_rxd.gpio1_10 */ + P1_32_gpio_pd_pin: pinmux_P1_32_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart0_rxd.gpio1_10 */ + P1_32_gpio_input_pin: pinmux_P1_32_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_INPUT | MUX_MODE7) >; }; /* uart0_rxd.gpio1_10 */ + P1_32_uart_pin: pinmux_P1_32_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart0_rxd.uart0_rxd */ + P1_32_spi_cs_pin: pinmux_P1_32_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart0_rxd.spi1_cs0 */ + P1_32_can_pin: pinmux_P1_32_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart0_rxd.dcan0_tx */ + P1_32_i2c_pin: pinmux_P1_32_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart0_rxd.i2c2_sda */ + P1_32_pruout_pin: pinmux_P1_32_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* uart0_rxd.pru1_out14 */ + P1_32_pruin_pin: pinmux_P1_32_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0970, PIN_INPUT | MUX_MODE6) >; }; /* uart0_rxd.pru1_in14 */ + + /* P1_33 (ZCZ ball B13) pru0_in1 */ + P1_33_default_pin: pinmux_P1_33_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsx.pru0_in1 */ + P1_33_gpio_pin: pinmux_P1_33_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P1_33_gpio_pu_pin: pinmux_P1_33_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P1_33_gpio_pd_pin: pinmux_P1_33_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P1_33_gpio_input_pin: pinmux_P1_33_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_fsx.gpio3_15 */ + P1_33_pwm_pin: pinmux_P1_33_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_fsx.ehrpwm0b */ + P1_33_spi_pin: pinmux_P1_33_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_fsx.spi1_d0 */ + P1_33_pruout_pin: pinmux_P1_33_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_fsx.pru0_out1 */ + P1_33_pruin_pin: pinmux_P1_33_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0994, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsx.pru0_in1 */ + + /* P1_34 (ZCZ ball T11) gpio0_26 */ + P1_34_default_pin: pinmux_P1_34_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P1_34_gpio_pin: pinmux_P1_34_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P1_34_gpio_pu_pin: pinmux_P1_34_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P1_34_gpio_pd_pin: pinmux_P1_34_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P1_34_gpio_input_pin: pinmux_P1_34_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad10.gpio0_26 */ + P1_34_pwm_pin: pinmux_P1_34_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0828, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad10.ehrpwm2_tripzone_input */ + + /* P1_35 (ZCZ ball V5) pru1_in10 */ + P1_35_default_pin: pinmux_P1_35_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_pclk.pru1_in10 */ + P1_35_gpio_pin: pinmux_P1_35_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P1_35_gpio_pu_pin: pinmux_P1_35_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P1_35_gpio_pd_pin: pinmux_P1_35_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P1_35_gpio_input_pin: pinmux_P1_35_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE7) >; }; /* lcd_pclk.gpio2_24 */ + P1_35_pruout_pin: pinmux_P1_35_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_pclk.pru1_out10 */ + P1_35_pruin_pin: pinmux_P1_35_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e8, PIN_INPUT | MUX_MODE6) >; }; /* lcd_pclk.pru1_in10 */ + + /* P1_36 (ZCZ ball A13) ehrpwm0a */ + P1_36_default_pin: pinmux_P1_36_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkx.ehrpwm0a */ + P1_36_gpio_pin: pinmux_P1_36_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P1_36_gpio_pu_pin: pinmux_P1_36_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P1_36_gpio_pd_pin: pinmux_P1_36_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P1_36_gpio_input_pin: pinmux_P1_36_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_aclkx.gpio3_14 */ + P1_36_pwm_pin: pinmux_P1_36_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_aclkx.ehrpwm0a */ + P1_36_spi_sclk_pin: pinmux_P1_36_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_aclkx.spi1_sclk */ + P1_36_pruout_pin: pinmux_P1_36_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_aclkx.pru0_out0 */ + P1_36_pruin_pin: pinmux_P1_36_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_aclkx.pru0_in0 */ + + + /************************/ + /* P2 Header */ + /************************/ + + /* P2_01 (ZCZ ball U14) ehrpwm1a */ + P2_01_default_pin: pinmux_P2_01_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a2.ehrpwm1a */ + P2_01_gpio_pin: pinmux_P2_01_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P2_01_gpio_pu_pin: pinmux_P2_01_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P2_01_gpio_pd_pin: pinmux_P2_01_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P2_01_gpio_input_pin: pinmux_P2_01_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a2.gpio1_18 */ + P2_01_pwm_pin: pinmux_P2_01_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a2.ehrpwm1a */ + + /* P2_02 (ZCZ ball V17) gpio1_27 */ + P2_02_default_pin: pinmux_P2_02_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x086c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a11.gpio1_27 */ + P2_02_gpio_pin: pinmux_P2_02_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x086c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a11.gpio1_27 */ + P2_02_gpio_pu_pin: pinmux_P2_02_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x086c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a11.gpio1_27 */ + P2_02_gpio_pd_pin: pinmux_P2_02_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x086c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a11.gpio1_27 */ + P2_02_gpio_input_pin: pinmux_P2_02_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x086c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a11.gpio1_27 */ + + /* P2_03 (ZCZ ball T10) gpio0_23 */ + P2_03_default_pin: pinmux_P2_03_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P2_03_gpio_pin: pinmux_P2_03_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P2_03_gpio_pu_pin: pinmux_P2_03_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P2_03_gpio_pd_pin: pinmux_P2_03_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P2_03_gpio_input_pin: pinmux_P2_03_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad9.gpio0_23 */ + P2_03_pwm_pin: pinmux_P2_03_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0824, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad9.ehrpwm2b */ + + /* P2_04 (ZCZ ball T16) gpio1_26 */ + P2_04_default_pin: pinmux_P2_04_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0868, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a10.gpio1_26 */ + P2_04_gpio_pin: pinmux_P2_04_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0868, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a10.gpio1_26 */ + P2_04_gpio_pu_pin: pinmux_P2_04_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0868, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a10.gpio1_26 */ + P2_04_gpio_pd_pin: pinmux_P2_04_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0868, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a10.gpio1_26 */ + P2_04_gpio_input_pin: pinmux_P2_04_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0868, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a10.gpio1_26 */ + + /* P2_05 (ZCZ ball T17) uart4_rxd */ + P2_05_default_pin: pinmux_P2_05_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wait0.uart4_rxd */ + P2_05_gpio_pin: pinmux_P2_05_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P2_05_gpio_pu_pin: pinmux_P2_05_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P2_05_gpio_pd_pin: pinmux_P2_05_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P2_05_gpio_input_pin: pinmux_P2_05_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wait0.gpio0_30 */ + P2_05_uart_pin: pinmux_P2_05_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0870, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wait0.uart4_rxd */ + + /* P2_06 (ZCZ ball U16) gpio1_25 */ + P2_06_default_pin: pinmux_P2_06_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0864, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a9.gpio1_25 */ + P2_06_gpio_pin: pinmux_P2_06_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0864, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a9.gpio1_25 */ + P2_06_gpio_pu_pin: pinmux_P2_06_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0864, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a9.gpio1_25 */ + P2_06_gpio_pd_pin: pinmux_P2_06_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0864, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a9.gpio1_25 */ + P2_06_gpio_input_pin: pinmux_P2_06_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0864, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a9.gpio1_25 */ + + /* P2_07 (ZCZ ball U17) uart4_txd */ + P2_07_default_pin: pinmux_P2_07_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wpn.uart4_txd */ + P2_07_gpio_pin: pinmux_P2_07_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P2_07_gpio_pu_pin: pinmux_P2_07_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P2_07_gpio_pd_pin: pinmux_P2_07_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P2_07_gpio_input_pin: pinmux_P2_07_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_wpn.gpio0_31 */ + P2_07_uart_pin: pinmux_P2_07_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0874, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_wpn.uart4_txd */ + + /* P2_08 (ZCZ ball U18) gpio1_28 */ + P2_08_default_pin: pinmux_P2_08_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P2_08_gpio_pin: pinmux_P2_08_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P2_08_gpio_pu_pin: pinmux_P2_08_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P2_08_gpio_pd_pin: pinmux_P2_08_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + P2_08_gpio_input_pin: pinmux_P2_08_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0878, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_be1n.gpio1_28 */ + + /* P2_09 (ZCZ ball D15) i2c1_scl */ + P2_09_default_pin: pinmux_P2_09_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_txd.i2c1_scl */ + P2_09_gpio_pin: pinmux_P2_09_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P2_09_gpio_pu_pin: pinmux_P2_09_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P2_09_gpio_pd_pin: pinmux_P2_09_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P2_09_gpio_input_pin: pinmux_P2_09_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT | MUX_MODE7) >; }; /* uart1_txd.gpio0_15 */ + P2_09_uart_pin: pinmux_P2_09_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart1_txd.uart1_txd */ + P2_09_can_pin: pinmux_P2_09_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart1_txd.dcan1_rx */ + P2_09_i2c_pin: pinmux_P2_09_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_txd.i2c1_scl */ + P2_09_pru_uart_pin: pinmux_P2_09_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_txd.pr1_uart0_txd */ + P2_09_pruin_pin: pinmux_P2_09_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0984, PIN_INPUT | MUX_MODE6) >; }; /* uart1_txd.pru0_in16 */ + + /* P2_10 (ZCZ ball R14) gpio1_20 */ + P2_10_default_pin: pinmux_P2_10_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0850, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a4.gpio1_20 */ + P2_10_gpio_pin: pinmux_P2_10_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0850, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a4.gpio1_20 */ + P2_10_gpio_pu_pin: pinmux_P2_10_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0850, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a4.gpio1_20 */ + P2_10_gpio_pd_pin: pinmux_P2_10_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0850, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_a4.gpio1_20 */ + P2_10_gpio_input_pin: pinmux_P2_10_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0850, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_a4.gpio1_20 */ + P2_10_qep_pin: pinmux_P2_10_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0850, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) >; }; /* gpmc_a4.eqep1a_in */ + + /* P2_11 (ZCZ ball D16) i2c1_sda */ + P2_11_default_pin: pinmux_P2_11_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rxd.i2c1_sda */ + P2_11_gpio_pin: pinmux_P2_11_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P2_11_gpio_pu_pin: pinmux_P2_11_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P2_11_gpio_pd_pin: pinmux_P2_11_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P2_11_gpio_input_pin: pinmux_P2_11_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_INPUT | MUX_MODE7) >; }; /* uart1_rxd.gpio0_14 */ + P2_11_uart_pin: pinmux_P2_11_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) >; }; /* uart1_rxd.uart1_rxd */ + P2_11_can_pin: pinmux_P2_11_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart1_rxd.dcan1_tx */ + P2_11_i2c_pin: pinmux_P2_11_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart1_rxd.i2c1_sda */ + P2_11_pru_uart_pin: pinmux_P2_11_pru_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart1_rxd.pr1_uart0_rxd */ + P2_11_pruin_pin: pinmux_P2_11_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0980, PIN_INPUT | MUX_MODE6) >; }; /* uart1_rxd.pru1_in16 */ + + /* P2_12 POWER_BUTTON */ + + /* P2_13 VOUT-5V */ + + /* P2_14 BAT-VIN */ + + /* P2_15 GND */ + + /* P2_16 BAT-TEMP */ + + /* P2_17 (ZCZ ball V12) gpio2_1 */ + P2_17_default_pin: pinmux_P2_17_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P2_17_gpio_pin: pinmux_P2_17_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P2_17_gpio_pu_pin: pinmux_P2_17_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P2_17_gpio_pd_pin: pinmux_P2_17_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + P2_17_gpio_input_pin: pinmux_P2_17_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x088c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_clk.gpio2_1 */ + + /* P2_18 (ZCZ ball U13) gpio1_15 */ + P2_18_default_pin: pinmux_P2_18_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P2_18_gpio_pin: pinmux_P2_18_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P2_18_gpio_pu_pin: pinmux_P2_18_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P2_18_gpio_pd_pin: pinmux_P2_18_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P2_18_gpio_input_pin: pinmux_P2_18_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad15.gpio1_15 */ + P2_18_qep_pin: pinmux_P2_18_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad15.eqep2_strobe */ + P2_18_pru_ecap_pin: pinmux_P2_18_pru_ecap_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* gpmc_ad15.pr1_ecap0_ecap_capin_apwm_o */ + P2_18_pruin_pin: pinmux_P2_18_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x083c, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_ad15.pru0_in15 */ + + /* P2_19 (ZCZ ball U12) gpio0_27 */ + P2_19_default_pin: pinmux_P2_19_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P2_19_gpio_pin: pinmux_P2_19_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P2_19_gpio_pu_pin: pinmux_P2_19_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P2_19_gpio_pd_pin: pinmux_P2_19_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P2_19_gpio_input_pin: pinmux_P2_19_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad11.gpio0_27 */ + P2_19_pwm_pin: pinmux_P2_19_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x082c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad11.ehrpwm0_synco */ + + /* P2_20 (ZCZ ball T13) gpio2_0 */ + P2_20_default_pin: pinmux_P2_20_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0888, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn3.gpio2_0 */ + P2_20_gpio_pin: pinmux_P2_20_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0888, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn3.gpio2_0 */ + P2_20_gpio_pu_pin: pinmux_P2_20_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0888, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn3.gpio2_0 */ + P2_20_gpio_pd_pin: pinmux_P2_20_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0888, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_csn3.gpio2_0 */ + P2_20_gpio_input_pin: pinmux_P2_20_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0888, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_csn3.gpio2_0 */ + + /* P2_21 GND */ + + /* P2_22 (ZCZ ball V13) gpio1_14 */ + P2_22_default_pin: pinmux_P2_22_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P2_22_gpio_pin: pinmux_P2_22_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P2_22_gpio_pu_pin: pinmux_P2_22_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P2_22_gpio_pd_pin: pinmux_P2_22_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P2_22_gpio_input_pin: pinmux_P2_22_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad14.gpio1_14 */ + P2_22_qep_pin: pinmux_P2_22_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad14.eqep2_index */ + P2_22_pruin_pin: pinmux_P2_22_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0838, PIN_INPUT | MUX_MODE6) >; }; /* gpmc_ad14.pru0_in14 */ + + /* P2_23 VOUT-3.3V */ + + /* P2_24 (ZCZ ball T12) gpio1_12 */ + P2_24_default_pin: pinmux_P2_24_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P2_24_gpio_pin: pinmux_P2_24_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P2_24_gpio_pu_pin: pinmux_P2_24_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P2_24_gpio_pd_pin: pinmux_P2_24_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P2_24_gpio_input_pin: pinmux_P2_24_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad12.gpio1_12 */ + P2_24_qep_pin: pinmux_P2_24_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad12.eqep2a_in */ + P2_24_pruout_pin: pinmux_P2_24_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0830, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_ad12.pru0_out14 */ + + /* P2_25 (ZCZ ball E17) spi1_d1 */ + P2_25_default_pin: pinmux_P2_25_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart0_rtsn.spi1_d1 */ + P2_25_gpio_pin: pinmux_P2_25_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart0_rtsn.gpio1_9 */ + P2_25_gpio_pu_pin: pinmux_P2_25_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart0_rtsn.gpio1_9 */ + P2_25_gpio_pd_pin: pinmux_P2_25_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart0_rtsn.gpio1_9 */ + P2_25_gpio_input_pin: pinmux_P2_25_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_INPUT | MUX_MODE7) >; }; /* uart0_rtsn.gpio1_9 */ + P2_25_uart_pin: pinmux_P2_25_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart0_rtsn.uart4_txd */ + P2_25_can_pin: pinmux_P2_25_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_INPUT_PULLUP | MUX_MODE2) >; }; /* uart0_rtsn.dcan1_rx */ + P2_25_i2c_pin: pinmux_P2_25_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart0_rtsn.i2c1_scl */ + P2_25_spi_pin: pinmux_P2_25_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart0_rtsn.spi1_d1 */ + P2_25_spi_cs_pin: pinmux_P2_25_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x096c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5) >; }; /* uart0_rtsn.spi1_cs0 */ + + /* P2_26 RESET# */ + + /* P2_27 (ZCZ ball E18) spi1_d0 */ + P2_27_default_pin: pinmux_P2_27_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart0_ctsn.spi1_d0 */ + P2_27_gpio_pin: pinmux_P2_27_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* uart0_ctsn.gpio1_8 */ + P2_27_gpio_pu_pin: pinmux_P2_27_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* uart0_ctsn.gpio1_8 */ + P2_27_gpio_pd_pin: pinmux_P2_27_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* uart0_ctsn.gpio1_8 */ + P2_27_gpio_input_pin: pinmux_P2_27_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_INPUT | MUX_MODE7) >; }; /* uart0_ctsn.gpio1_8 */ + P2_27_uart_pin: pinmux_P2_27_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* uart0_ctsn.uart4_rxd */ + P2_27_can_pin: pinmux_P2_27_can_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLUP | MUX_MODE2) >; }; /* uart0_ctsn.dcan1_tx */ + P2_27_i2c_pin: pinmux_P2_27_i2c_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* uart0_ctsn.i2c1_sda */ + P2_27_spi_pin: pinmux_P2_27_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0968, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* uart0_ctsn.spi1_d0 */ + + /* P2_28 (ZCZ ball D13) pru0_in6 */ + P2_28_default_pin: pinmux_P2_28_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr1.pru0_in6 */ + P2_28_gpio_pin: pinmux_P2_28_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P2_28_gpio_pu_pin: pinmux_P2_28_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P2_28_gpio_pd_pin: pinmux_P2_28_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P2_28_gpio_input_pin: pinmux_P2_28_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_axr1.gpio3_20 */ + P2_28_qep_pin: pinmux_P2_28_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_axr1.eqep0_index */ + P2_28_pruout_pin: pinmux_P2_28_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_axr1.pru0_out6 */ + P2_28_pruin_pin: pinmux_P2_28_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a8, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr1.pru0_in6 */ + + /* P2_29 (ZCZ ball C18) spi1_sclk */ + P2_29_default_pin: pinmux_P2_29_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* eCAP0_in_PWM0_out.spi1_sclk */ + P2_29_gpio_pin: pinmux_P2_29_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P2_29_gpio_pu_pin: pinmux_P2_29_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P2_29_gpio_pd_pin: pinmux_P2_29_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P2_29_gpio_input_pin: pinmux_P2_29_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_INPUT | MUX_MODE7) >; }; /* eCAP0_in_PWM0_out.gpio0_7 */ + P2_29_pwm_pin: pinmux_P2_29_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE0) >; }; /* eCAP0_in_PWM0_out.ecap0_in_pwm0_out */ + P2_29_uart_pin: pinmux_P2_29_uart_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* eCAP0_in_PWM0_out.uart3_txd */ + P2_29_spi_cs_pin: pinmux_P2_29_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2) >; }; /* eCAP0_in_PWM0_out.spi1_cs1 */ + P2_29_pru_ecap_pin: pinmux_P2_29_pru_ecap_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3) >; }; /* eCAP0_in_PWM0_out.pr1_ecap0_ecap_capin_apwm_o */ + P2_29_spi_sclk_pin: pinmux_P2_29_spi_sclk_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0964, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* eCAP0_in_PWM0_out.spi1_sclk */ + + /* P2_30 (ZCZ ball C12) pru0_in3 */ + P2_30_default_pin: pinmux_P2_30_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkr.pru0_in3 */ + P2_30_gpio_pin: pinmux_P2_30_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P2_30_gpio_pu_pin: pinmux_P2_30_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P2_30_gpio_pd_pin: pinmux_P2_30_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P2_30_gpio_input_pin: pinmux_P2_30_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_ahclkr.gpio3_17 */ + P2_30_pwm_pin: pinmux_P2_30_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_ahclkr.ehrpwm0_synci */ + P2_30_spi_cs_pin: pinmux_P2_30_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_ahclkr.spi1_cs0 */ + P2_30_pruout_pin: pinmux_P2_30_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_ahclkr.pru0_out3 */ + P2_30_pruin_pin: pinmux_P2_30_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x099c, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_ahclkr.pru0_in3 */ + + /* P2_31 (ZCZ ball A15) spi1_cs1 */ + P2_31_default_pin: pinmux_P2_31_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* xdma_event_intr0.spi1_cs1 */ + P2_31_gpio_pin: pinmux_P2_31_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr0.gpio0_19 */ + P2_31_gpio_pu_pin: pinmux_P2_31_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr0.gpio0_19 */ + P2_31_gpio_pd_pin: pinmux_P2_31_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* xdma_event_intr0.gpio0_19 */ + P2_31_gpio_input_pin: pinmux_P2_31_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_INPUT | MUX_MODE7) >; }; /* xdma_event_intr0.gpio0_19 */ + P2_31_spi_cs_pin: pinmux_P2_31_spi_cs_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* xdma_event_intr0.spi1_cs1 */ + P2_31_pruin_pin: pinmux_P2_31_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09b0, PIN_INPUT | MUX_MODE5) >; }; /* xdma_event_intr0.pru1_in16 */ + + /* P2_32 (ZCZ ball D12) pru0_in2 */ + P2_32_default_pin: pinmux_P2_32_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr0.pru0_in2 */ + P2_32_gpio_pin: pinmux_P2_32_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P2_32_gpio_pu_pin: pinmux_P2_32_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P2_32_gpio_pd_pin: pinmux_P2_32_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P2_32_gpio_input_pin: pinmux_P2_32_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_axr0.gpio3_16 */ + P2_32_pwm_pin: pinmux_P2_32_pwm_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_axr0.ehrpwm0_tripzone_input */ + P2_32_spi_pin: pinmux_P2_32_spi_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; }; /* mcasp0_axr0.spi1_d1 */ + P2_32_pruout_pin: pinmux_P2_32_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_axr0.pru0_out2 */ + P2_32_pruin_pin: pinmux_P2_32_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0998, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_axr0.pru0_in2 */ + + /* P2_33 (ZCZ ball R12) gpio1_13 */ + P2_33_default_pin: pinmux_P2_33_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P2_33_gpio_pin: pinmux_P2_33_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P2_33_gpio_pu_pin: pinmux_P2_33_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P2_33_gpio_pd_pin: pinmux_P2_33_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P2_33_gpio_input_pin: pinmux_P2_33_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_INPUT | MUX_MODE7) >; }; /* gpmc_ad13.gpio1_13 */ + P2_33_qep_pin: pinmux_P2_33_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) >; }; /* gpmc_ad13.eqep2b_in */ + P2_33_pruout_pin: pinmux_P2_33_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x0834, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; /* gpmc_ad13.pru0_out15 */ + + /* P2_34 (ZCZ ball C13) pru0_in5 */ + P2_34_default_pin: pinmux_P2_34_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsr.pru0_in5 */ + P2_34_gpio_pin: pinmux_P2_34_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P2_34_gpio_pu_pin: pinmux_P2_34_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P2_34_gpio_pd_pin: pinmux_P2_34_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P2_34_gpio_input_pin: pinmux_P2_34_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE7) >; }; /* mcasp0_fsr.gpio3_19 */ + P2_34_qep_pin: pinmux_P2_34_qep_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) >; }; /* mcasp0_fsr.eqep0b_in */ + P2_34_pruout_pin: pinmux_P2_34_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* mcasp0_fsr.pru0_out5 */ + P2_34_pruin_pin: pinmux_P2_34_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x09a4, PIN_INPUT | MUX_MODE6) >; }; /* mcasp0_fsr.pru0_in5 */ + + /* P2_35 (ZCZ ball U5) gpio2_22 */ + P2_35_default_pin: pinmux_P2_35_default_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P2_35_gpio_pin: pinmux_P2_35_gpio_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P2_35_gpio_pu_pin: pinmux_P2_35_gpio_pu_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P2_35_gpio_pd_pin: pinmux_P2_35_gpio_pd_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P2_35_gpio_input_pin: pinmux_P2_35_gpio_input_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE7) >; }; /* lcd_vsync.gpio2_22 */ + P2_35_pruout_pin: pinmux_P2_35_pruout_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; }; /* lcd_vsync.pru1_out8 */ + P2_35_pruin_pin: pinmux_P2_35_pruin_pin { pinctrl-single,pins = < + AM33XX_IOPAD(0x08e0, PIN_INPUT | MUX_MODE6) >; }; /* lcd_vsync.pru1_in8 */ + + /* P2_36 (ZCZ ball C9) AIN7 */ }; &epwmss0 { @@ -409,7 +1113,8 @@ &ehrpwm0 { status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&ehrpwm0_pins>; + //pinctrl-0 = <&ehrpwm0_pins>; + pinctrl-0 = <>; }; &epwmss1 { @@ -419,7 +1124,18 @@ &ehrpwm1 { status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&ehrpwm1_pins>; + //pinctrl-0 = <&ehrpwm1_pins>; + pinctrl-0 = <>; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; }; &i2c0 { @@ -429,9 +1145,18 @@ }; }; +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <>; + + status = "okay"; + clock-frequency = <400000>; +}; + &i2c2 { pinctrl-names = "default"; - pinctrl-0 = <&i2c2_pins>; +// pinctrl-0 = <&i2c2_pins>; + pinctrl-0 = <>; status = "okay"; clock-frequency = <400000>; @@ -450,6 +1175,10 @@ system-power-controller; }; +&pruss_tm { + status = "okay"; +}; + &tscadc { status = "okay"; adc { @@ -462,14 +1191,30 @@ &uart0 { pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; + //pinctrl-0 = <&uart0_pins>; + pinctrl-0 = <>; + + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <>; + + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <>; status = "okay"; }; &uart4 { pinctrl-names = "default"; - pinctrl-0 = <&uart4_pins>; + //pinctrl-0 = <&uart4_pins>; + pinctrl-0 = <>; status = "okay"; }; @@ -481,3 +1226,1092 @@ &usb1 { dr_mode = "host"; }; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "spidev"; + symlink = "bone/spi/0.0"; + reg = <0>; + spi-max-frequency = <24000000>; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "spidev"; + symlink = "bone/spi/0.1"; + reg = <1>; + spi-max-frequency = <24000000>; + status = "disabled"; + }; +}; + +&spi1 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "spidev"; + symlink = "bone/spi/1.0"; + reg = <0>; + spi-max-frequency = <24000000>; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "spidev"; + symlink = "bone/spi/1.1"; + reg = <1>; + spi-max-frequency = <24000000>; + }; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ocp { + /************************/ + /* P1 Header */ + /************************/ + + /* P1_01 VIN-AC */ + + /* P1_02 (ZCZ ball R5) gpio_input */ + P1_02_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P1_02_default_pin>; + pinctrl-1 = <&P1_02_gpio_pin>; + pinctrl-2 = <&P1_02_gpio_pu_pin>; + pinctrl-3 = <&P1_02_gpio_pd_pin>; + pinctrl-4 = <&P1_02_gpio_input_pin>; + pinctrl-5 = <&P1_02_pruout_pin>; + pinctrl-6 = <&P1_02_pruin_pin>; + }; + + /* P1_03 (ZCZ ball F15) usb1_vbus_out */ + + /* P1_04 (ZCZ ball R6) */ + P1_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P1_04_default_pin>; + pinctrl-1 = <&P1_04_gpio_pin>; + pinctrl-2 = <&P1_04_gpio_pu_pin>; + pinctrl-3 = <&P1_04_gpio_pd_pin>; + pinctrl-4 = <&P1_04_gpio_input_pin>; + pinctrl-5 = <&P1_04_pruout_pin>; + pinctrl-6 = <&P1_04_pruin_pin>; + }; + + /* P1_05 (ZCZ ball T18) usb1_vbus_in */ + + /* P1_06 (ZCZ ball A16) spi_cs */ + P1_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_06_default_pin>; + pinctrl-1 = <&P1_06_gpio_pin>; + pinctrl-2 = <&P1_06_gpio_pu_pin>; + pinctrl-3 = <&P1_06_gpio_pd_pin>; + pinctrl-4 = <&P1_06_gpio_input_pin>; + pinctrl-5 = <&P1_06_spi_cs_pin>; + pinctrl-6 = <&P1_06_i2c_pin>; + pinctrl-7 = <&P1_06_pwm_pin>; + pinctrl-8 = <&P1_06_pru_uart_pin>; + }; + + /* P1_07 VIN-USB */ + + /* P1_08 (ZCZ ball A17) spi_sclk */ + P1_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_sclk", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_08_default_pin>; + pinctrl-1 = <&P1_08_gpio_pin>; + pinctrl-2 = <&P1_08_gpio_pu_pin>; + pinctrl-3 = <&P1_08_gpio_pd_pin>; + pinctrl-4 = <&P1_08_gpio_input_pin>; + pinctrl-5 = <&P1_08_spi_sclk_pin>; + pinctrl-6 = <&P1_08_uart_pin>; + pinctrl-7 = <&P1_08_i2c_pin>; + pinctrl-8 = <&P1_08_pwm_pin>; + pinctrl-9 = <&P1_08_pru_uart_pin>; + }; + + /* P1_09 (ZCZ ball R18) USB1-DN */ + + /* P1_10 (ZCZ ball B17) spi */ + P1_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_10_default_pin>; + pinctrl-1 = <&P1_10_gpio_pin>; + pinctrl-2 = <&P1_10_gpio_pu_pin>; + pinctrl-3 = <&P1_10_gpio_pd_pin>; + pinctrl-4 = <&P1_10_gpio_input_pin>; + pinctrl-5 = <&P1_10_spi_pin>; + pinctrl-6 = <&P1_10_uart_pin>; + pinctrl-7 = <&P1_10_i2c_pin>; + pinctrl-8 = <&P1_10_pwm_pin>; + pinctrl-9 = <&P1_10_pru_uart_pin>; + }; + + /* P1_11 (ZCZ ball R17) USB1-DP */ + + /* P1_12 (ZCZ ball B16) spi */ + P1_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_12_default_pin>; + pinctrl-1 = <&P1_12_gpio_pin>; + pinctrl-2 = <&P1_12_gpio_pu_pin>; + pinctrl-3 = <&P1_12_gpio_pd_pin>; + pinctrl-4 = <&P1_12_gpio_input_pin>; + pinctrl-5 = <&P1_12_spi_pin>; + pinctrl-6 = <&P1_12_i2c_pin>; + pinctrl-7 = <&P1_12_pwm_pin>; + pinctrl-8 = <&P1_12_pru_uart_pin>; + }; + + /* P1_13 (ZCZ ball P17) USB1-ID */ + + /* P1_14 VOUT-3.3V */ + + /* P1_15 GND */ + + /* P1_16 GND */ + + /* P1_17 (ZCZ ball A9) VREFN */ + + /* P1_18 (ZCZ ball B9) VREFP */ + + /* P1_19 (ZCZ ball B6) AIN0 */ + + /* P1_20 (ZCZ ball D14) */ + P1_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruin"; + pinctrl-0 = <&P1_20_default_pin>; + pinctrl-1 = <&P1_20_gpio_pin>; + pinctrl-2 = <&P1_20_gpio_pu_pin>; + pinctrl-3 = <&P1_20_gpio_pd_pin>; + pinctrl-4 = <&P1_20_gpio_input_pin>; + pinctrl-5 = <&P1_20_pruin_pin>; + }; + + /* P1_21 (ZCZ ball C7) AIN1 */ + + /* P1_22 GND */ + + /* P1_23 (ZCZ ball B7) AIN2 */ + + /* P1_24 VOUT-5V */ + + /* P1_25 (ZCZ ball A7) AIN3 */ + + /* P1_26 (ZCZ ball D18) i2c */ + P1_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "can", "i2c", "pru_uart"; + pinctrl-0 = <&P1_26_default_pin>; + pinctrl-1 = <&P1_26_gpio_pin>; + pinctrl-2 = <&P1_26_gpio_pu_pin>; + pinctrl-3 = <&P1_26_gpio_pd_pin>; + pinctrl-4 = <&P1_26_gpio_input_pin>; + pinctrl-5 = <&P1_26_spi_cs_pin>; + pinctrl-6 = <&P1_26_can_pin>; + pinctrl-7 = <&P1_26_i2c_pin>; + pinctrl-8 = <&P1_26_pru_uart_pin>; + }; + + /* P1_27 (ZCZ ball C8) AIN4 */ + + /* P1_28 (ZCZ ball D17) i2c */ + P1_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "can", "i2c", "pru_uart"; + pinctrl-0 = <&P1_28_default_pin>; + pinctrl-1 = <&P1_28_gpio_pin>; + pinctrl-2 = <&P1_28_gpio_pu_pin>; + pinctrl-3 = <&P1_28_gpio_pd_pin>; + pinctrl-4 = <&P1_28_gpio_input_pin>; + pinctrl-5 = <&P1_28_spi_cs_pin>; + pinctrl-6 = <&P1_28_can_pin>; + pinctrl-7 = <&P1_28_i2c_pin>; + pinctrl-8 = <&P1_28_pru_uart_pin>; + }; + + /* P1_29 (ZCZ ball A14) pruin */ + P1_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P1_29_default_pin>; + pinctrl-1 = <&P1_29_gpio_pin>; + pinctrl-2 = <&P1_29_gpio_pu_pin>; + pinctrl-3 = <&P1_29_gpio_pd_pin>; + pinctrl-4 = <&P1_29_gpio_input_pin>; + pinctrl-5 = <&P1_29_qep_pin>; + pinctrl-6 = <&P1_29_pruout_pin>; + pinctrl-7 = <&P1_29_pruin_pin>; + }; + + /* P1_30 (ZCZ ball E16) uart */ + P1_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "uart", "can", "i2c", "pruout", "pruin"; + pinctrl-0 = <&P1_30_default_pin>; + pinctrl-1 = <&P1_30_gpio_pin>; + pinctrl-2 = <&P1_30_gpio_pu_pin>; + pinctrl-3 = <&P1_30_gpio_pd_pin>; + pinctrl-4 = <&P1_30_gpio_input_pin>; + pinctrl-5 = <&P1_30_spi_cs_pin>; + pinctrl-6 = <&P1_30_uart_pin>; + pinctrl-7 = <&P1_30_can_pin>; + pinctrl-8 = <&P1_30_i2c_pin>; + pinctrl-9 = <&P1_30_pruout_pin>; + pinctrl-10 = <&P1_30_pruin_pin>; + }; + + /* P1_31 (ZCZ ball B12) pruin */ + P1_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P1_31_default_pin>; + pinctrl-1 = <&P1_31_gpio_pin>; + pinctrl-2 = <&P1_31_gpio_pu_pin>; + pinctrl-3 = <&P1_31_gpio_pd_pin>; + pinctrl-4 = <&P1_31_gpio_input_pin>; + pinctrl-5 = <&P1_31_qep_pin>; + pinctrl-6 = <&P1_31_pruout_pin>; + pinctrl-7 = <&P1_31_pruin_pin>; + }; + + /* P1_32 (ZCZ ball E15) uart */ + P1_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "uart", "can", "i2c", "pruout", "pruin"; + pinctrl-0 = <&P1_32_default_pin>; + pinctrl-1 = <&P1_32_gpio_pin>; + pinctrl-2 = <&P1_32_gpio_pu_pin>; + pinctrl-3 = <&P1_32_gpio_pd_pin>; + pinctrl-4 = <&P1_32_gpio_input_pin>; + pinctrl-5 = <&P1_32_spi_cs_pin>; + pinctrl-6 = <&P1_32_uart_pin>; + pinctrl-7 = <&P1_32_can_pin>; + pinctrl-8 = <&P1_32_i2c_pin>; + pinctrl-9 = <&P1_32_pruout_pin>; + pinctrl-10 = <&P1_32_pruin_pin>; + }; + + /* P1_33 (ZCZ ball B13) pruin */ + P1_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P1_33_default_pin>; + pinctrl-1 = <&P1_33_gpio_pin>; + pinctrl-2 = <&P1_33_gpio_pu_pin>; + pinctrl-3 = <&P1_33_gpio_pd_pin>; + pinctrl-4 = <&P1_33_gpio_input_pin>; + pinctrl-5 = <&P1_33_spi_pin>; + pinctrl-6 = <&P1_33_pwm_pin>; + pinctrl-7 = <&P1_33_pruout_pin>; + pinctrl-8 = <&P1_33_pruin_pin>; + }; + + /* P1_34 (ZCZ ball T11) */ + P1_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P1_34_default_pin>; + pinctrl-1 = <&P1_34_gpio_pin>; + pinctrl-2 = <&P1_34_gpio_pu_pin>; + pinctrl-3 = <&P1_34_gpio_pd_pin>; + pinctrl-4 = <&P1_34_gpio_input_pin>; + pinctrl-5 = <&P1_34_pwm_pin>; + }; + + /* P1_35 (ZCZ ball V5) pruin */ + P1_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P1_35_default_pin>; + pinctrl-1 = <&P1_35_gpio_pin>; + pinctrl-2 = <&P1_35_gpio_pu_pin>; + pinctrl-3 = <&P1_35_gpio_pd_pin>; + pinctrl-4 = <&P1_35_gpio_input_pin>; + pinctrl-5 = <&P1_35_pruout_pin>; + pinctrl-6 = <&P1_35_pruin_pin>; + }; + + /* P1_36 (ZCZ ball A13) pwm */ + P1_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_sclk", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P1_36_default_pin>; + pinctrl-1 = <&P1_36_gpio_pin>; + pinctrl-2 = <&P1_36_gpio_pu_pin>; + pinctrl-3 = <&P1_36_gpio_pd_pin>; + pinctrl-4 = <&P1_36_gpio_input_pin>; + pinctrl-5 = <&P1_36_spi_sclk_pin>; + pinctrl-6 = <&P1_36_pwm_pin>; + pinctrl-7 = <&P1_36_pruout_pin>; + pinctrl-8 = <&P1_36_pruin_pin>; + }; + + + /************************/ + /* P2 Header */ + /************************/ + + /* P2_01 (ZCZ ball U14) pwm */ + P2_01_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P2_01_default_pin>; + pinctrl-1 = <&P2_01_gpio_pin>; + pinctrl-2 = <&P2_01_gpio_pu_pin>; + pinctrl-3 = <&P2_01_gpio_pd_pin>; + pinctrl-4 = <&P2_01_gpio_input_pin>; + pinctrl-5 = <&P2_01_pwm_pin>; + }; + + /* P2_02 (ZCZ ball V17) */ + P2_02_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P2_02_default_pin>; + pinctrl-1 = <&P2_02_gpio_pin>; + pinctrl-2 = <&P2_02_gpio_pu_pin>; + pinctrl-3 = <&P2_02_gpio_pd_pin>; + pinctrl-4 = <&P2_02_gpio_input_pin>; + }; + + /* P2_03 (ZCZ ball T10) */ + P2_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P2_03_default_pin>; + pinctrl-1 = <&P2_03_gpio_pin>; + pinctrl-2 = <&P2_03_gpio_pu_pin>; + pinctrl-3 = <&P2_03_gpio_pd_pin>; + pinctrl-4 = <&P2_03_gpio_input_pin>; + pinctrl-5 = <&P2_03_pwm_pin>; + }; + + /* P2_04 (ZCZ ball T16) */ + P2_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P2_04_default_pin>; + pinctrl-1 = <&P2_04_gpio_pin>; + pinctrl-2 = <&P2_04_gpio_pu_pin>; + pinctrl-3 = <&P2_04_gpio_pd_pin>; + pinctrl-4 = <&P2_04_gpio_input_pin>; + }; + + /* P2_05 (ZCZ ball T17) uart */ + P2_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart"; + pinctrl-0 = <&P2_05_default_pin>; + pinctrl-1 = <&P2_05_gpio_pin>; + pinctrl-2 = <&P2_05_gpio_pu_pin>; + pinctrl-3 = <&P2_05_gpio_pd_pin>; + pinctrl-4 = <&P2_05_gpio_input_pin>; + pinctrl-5 = <&P2_05_uart_pin>; + }; + + /* P2_06 (ZCZ ball U16) */ + P2_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P2_06_default_pin>; + pinctrl-1 = <&P2_06_gpio_pin>; + pinctrl-2 = <&P2_06_gpio_pu_pin>; + pinctrl-3 = <&P2_06_gpio_pd_pin>; + pinctrl-4 = <&P2_06_gpio_input_pin>; + }; + + /* P2_07 (ZCZ ball U17) uart */ + P2_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart"; + pinctrl-0 = <&P2_07_default_pin>; + pinctrl-1 = <&P2_07_gpio_pin>; + pinctrl-2 = <&P2_07_gpio_pu_pin>; + pinctrl-3 = <&P2_07_gpio_pd_pin>; + pinctrl-4 = <&P2_07_gpio_input_pin>; + pinctrl-5 = <&P2_07_uart_pin>; + }; + + /* P2_08 (ZCZ ball U18) */ + P2_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P2_08_default_pin>; + pinctrl-1 = <&P2_08_gpio_pin>; + pinctrl-2 = <&P2_08_gpio_pu_pin>; + pinctrl-3 = <&P2_08_gpio_pd_pin>; + pinctrl-4 = <&P2_08_gpio_input_pin>; + }; + + /* P2_09 (ZCZ ball D15) i2c */ + P2_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P2_09_default_pin>; + pinctrl-1 = <&P2_09_gpio_pin>; + pinctrl-2 = <&P2_09_gpio_pu_pin>; + pinctrl-3 = <&P2_09_gpio_pd_pin>; + pinctrl-4 = <&P2_09_gpio_input_pin>; + pinctrl-5 = <&P2_09_uart_pin>; + pinctrl-6 = <&P2_09_can_pin>; + pinctrl-7 = <&P2_09_i2c_pin>; + pinctrl-8 = <&P2_09_pru_uart_pin>; + pinctrl-9 = <&P2_09_pruin_pin>; + }; + + /* P2_10 (ZCZ ball R14) */ + P2_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep"; + pinctrl-0 = <&P2_10_default_pin>; + pinctrl-1 = <&P2_10_gpio_pin>; + pinctrl-2 = <&P2_10_gpio_pu_pin>; + pinctrl-3 = <&P2_10_gpio_pd_pin>; + pinctrl-4 = <&P2_10_gpio_input_pin>; + pinctrl-5 = <&P2_10_qep_pin>; + }; + + /* P2_11 (ZCZ ball D16) i2c */ + P2_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P2_11_default_pin>; + pinctrl-1 = <&P2_11_gpio_pin>; + pinctrl-2 = <&P2_11_gpio_pu_pin>; + pinctrl-3 = <&P2_11_gpio_pd_pin>; + pinctrl-4 = <&P2_11_gpio_input_pin>; + pinctrl-5 = <&P2_11_uart_pin>; + pinctrl-6 = <&P2_11_can_pin>; + pinctrl-7 = <&P2_11_i2c_pin>; + pinctrl-8 = <&P2_11_pru_uart_pin>; + pinctrl-9 = <&P2_11_pruin_pin>; + }; + + /* P2_12 POWER_BUTTON */ + + /* P2_13 VOUT-5V */ + + /* P2_14 BAT-VIN */ + + /* P2_15 GND */ + + /* P2_16 BAT-TEMP */ + + /* P2_17 (ZCZ ball V12) */ + P2_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P2_17_default_pin>; + pinctrl-1 = <&P2_17_gpio_pin>; + pinctrl-2 = <&P2_17_gpio_pu_pin>; + pinctrl-3 = <&P2_17_gpio_pd_pin>; + pinctrl-4 = <&P2_17_gpio_input_pin>; + }; + + /* P2_18 (ZCZ ball U13) */ + P2_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pru_ecap", "pruin"; + pinctrl-0 = <&P2_18_default_pin>; + pinctrl-1 = <&P2_18_gpio_pin>; + pinctrl-2 = <&P2_18_gpio_pu_pin>; + pinctrl-3 = <&P2_18_gpio_pd_pin>; + pinctrl-4 = <&P2_18_gpio_input_pin>; + pinctrl-5 = <&P2_18_qep_pin>; + pinctrl-6 = <&P2_18_pru_ecap_pin>; + pinctrl-7 = <&P2_18_pruin_pin>; + }; + + /* P2_19 (ZCZ ball U12) */ + P2_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; + pinctrl-0 = <&P2_19_default_pin>; + pinctrl-1 = <&P2_19_gpio_pin>; + pinctrl-2 = <&P2_19_gpio_pu_pin>; + pinctrl-3 = <&P2_19_gpio_pd_pin>; + pinctrl-4 = <&P2_19_gpio_input_pin>; + pinctrl-5 = <&P2_19_pwm_pin>; + }; + + /* P2_20 (ZCZ ball T13) */ + P2_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input"; + pinctrl-0 = <&P2_20_default_pin>; + pinctrl-1 = <&P2_20_gpio_pin>; + pinctrl-2 = <&P2_20_gpio_pu_pin>; + pinctrl-3 = <&P2_20_gpio_pd_pin>; + pinctrl-4 = <&P2_20_gpio_input_pin>; + }; + + /* P2_21 GND */ + + /* P2_22 (ZCZ ball V13) */ + P2_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruin"; + pinctrl-0 = <&P2_22_default_pin>; + pinctrl-1 = <&P2_22_gpio_pin>; + pinctrl-2 = <&P2_22_gpio_pu_pin>; + pinctrl-3 = <&P2_22_gpio_pd_pin>; + pinctrl-4 = <&P2_22_gpio_input_pin>; + pinctrl-5 = <&P2_22_qep_pin>; + pinctrl-6 = <&P2_22_pruin_pin>; + }; + + /* P2_23 VOUT-3.3V */ + + /* P2_24 (ZCZ ball T12) */ + P2_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout"; + pinctrl-0 = <&P2_24_default_pin>; + pinctrl-1 = <&P2_24_gpio_pin>; + pinctrl-2 = <&P2_24_gpio_pu_pin>; + pinctrl-3 = <&P2_24_gpio_pd_pin>; + pinctrl-4 = <&P2_24_gpio_input_pin>; + pinctrl-5 = <&P2_24_qep_pin>; + pinctrl-6 = <&P2_24_pruout_pin>; + }; + + /* P2_25 (ZCZ ball E17) spi */ + P2_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "spi_cs", "uart", "can", "i2c"; + pinctrl-0 = <&P2_25_default_pin>; + pinctrl-1 = <&P2_25_gpio_pin>; + pinctrl-2 = <&P2_25_gpio_pu_pin>; + pinctrl-3 = <&P2_25_gpio_pd_pin>; + pinctrl-4 = <&P2_25_gpio_input_pin>; + pinctrl-5 = <&P2_25_spi_pin>; + pinctrl-6 = <&P2_25_spi_cs_pin>; + pinctrl-7 = <&P2_25_uart_pin>; + pinctrl-8 = <&P2_25_can_pin>; + pinctrl-9 = <&P2_25_i2c_pin>; + }; + + /* P2_26 RESET# */ + + /* P2_27 (ZCZ ball E18) spi */ + P2_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "uart", "can", "i2c"; + pinctrl-0 = <&P2_27_default_pin>; + pinctrl-1 = <&P2_27_gpio_pin>; + pinctrl-2 = <&P2_27_gpio_pu_pin>; + pinctrl-3 = <&P2_27_gpio_pd_pin>; + pinctrl-4 = <&P2_27_gpio_input_pin>; + pinctrl-5 = <&P2_27_spi_pin>; + pinctrl-6 = <&P2_27_uart_pin>; + pinctrl-7 = <&P2_27_can_pin>; + pinctrl-8 = <&P2_27_i2c_pin>; + }; + + /* P2_28 (ZCZ ball D13) pruin */ + P2_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P2_28_default_pin>; + pinctrl-1 = <&P2_28_gpio_pin>; + pinctrl-2 = <&P2_28_gpio_pu_pin>; + pinctrl-3 = <&P2_28_gpio_pd_pin>; + pinctrl-4 = <&P2_28_gpio_input_pin>; + pinctrl-5 = <&P2_28_qep_pin>; + pinctrl-6 = <&P2_28_pruout_pin>; + pinctrl-7 = <&P2_28_pruin_pin>; + }; + + /* P2_29 (ZCZ ball C18) spi_sclk */ + P2_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "spi_sclk", "uart", "pwm", "pru_ecap"; + pinctrl-0 = <&P2_29_default_pin>; + pinctrl-1 = <&P2_29_gpio_pin>; + pinctrl-2 = <&P2_29_gpio_pu_pin>; + pinctrl-3 = <&P2_29_gpio_pd_pin>; + pinctrl-4 = <&P2_29_gpio_input_pin>; + pinctrl-5 = <&P2_29_spi_cs_pin>; + pinctrl-6 = <&P2_29_spi_sclk_pin>; + pinctrl-7 = <&P2_29_uart_pin>; + pinctrl-8 = <&P2_29_pwm_pin>; + pinctrl-9 = <&P2_29_pru_ecap_pin>; + }; + + /* P2_30 (ZCZ ball C12) pruin */ + P2_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P2_30_default_pin>; + pinctrl-1 = <&P2_30_gpio_pin>; + pinctrl-2 = <&P2_30_gpio_pu_pin>; + pinctrl-3 = <&P2_30_gpio_pd_pin>; + pinctrl-4 = <&P2_30_gpio_input_pin>; + pinctrl-5 = <&P2_30_spi_cs_pin>; + pinctrl-6 = <&P2_30_pwm_pin>; + pinctrl-7 = <&P2_30_pruout_pin>; + pinctrl-8 = <&P2_30_pruin_pin>; + }; + + /* P2_31 (ZCZ ball A15) spi_cs */ + P2_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi_cs", "pruin"; + pinctrl-0 = <&P2_31_default_pin>; + pinctrl-1 = <&P2_31_gpio_pin>; + pinctrl-2 = <&P2_31_gpio_pu_pin>; + pinctrl-3 = <&P2_31_gpio_pd_pin>; + pinctrl-4 = <&P2_31_gpio_input_pin>; + pinctrl-5 = <&P2_31_spi_cs_pin>; + pinctrl-6 = <&P2_31_pruin_pin>; + }; + + /* P2_32 (ZCZ ball D12) pruin */ + P2_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P2_32_default_pin>; + pinctrl-1 = <&P2_32_gpio_pin>; + pinctrl-2 = <&P2_32_gpio_pu_pin>; + pinctrl-3 = <&P2_32_gpio_pd_pin>; + pinctrl-4 = <&P2_32_gpio_input_pin>; + pinctrl-5 = <&P2_32_spi_pin>; + pinctrl-6 = <&P2_32_pwm_pin>; + pinctrl-7 = <&P2_32_pruout_pin>; + pinctrl-8 = <&P2_32_pruin_pin>; + }; + + /* P2_33 (ZCZ ball R12) */ + P2_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout"; + pinctrl-0 = <&P2_33_default_pin>; + pinctrl-1 = <&P2_33_gpio_pin>; + pinctrl-2 = <&P2_33_gpio_pu_pin>; + pinctrl-3 = <&P2_33_gpio_pd_pin>; + pinctrl-4 = <&P2_33_gpio_input_pin>; + pinctrl-5 = <&P2_33_qep_pin>; + pinctrl-6 = <&P2_33_pruout_pin>; + }; + + /* P2_34 (ZCZ ball C13) pruin */ + P2_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "qep", "pruout", "pruin"; + pinctrl-0 = <&P2_34_default_pin>; + pinctrl-1 = <&P2_34_gpio_pin>; + pinctrl-2 = <&P2_34_gpio_pu_pin>; + pinctrl-3 = <&P2_34_gpio_pd_pin>; + pinctrl-4 = <&P2_34_gpio_input_pin>; + pinctrl-5 = <&P2_34_qep_pin>; + pinctrl-6 = <&P2_34_pruout_pin>; + pinctrl-7 = <&P2_34_pruin_pin>; + }; + + /* P2_35 (ZCZ ball U5) gpio_input */ + P2_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pruout", "pruin"; + pinctrl-0 = <&P2_35_default_pin>; + pinctrl-1 = <&P2_35_gpio_pin>; + pinctrl-2 = <&P2_35_gpio_pu_pin>; + pinctrl-3 = <&P2_35_gpio_pd_pin>; + pinctrl-4 = <&P2_35_gpio_input_pin>; + pinctrl-5 = <&P2_35_pruout_pin>; + pinctrl-6 = <&P2_35_pruin_pin>; + }; + + /* P2_36 (ZCZ ball C9) AIN7 */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P1_02 { + gpio-name = "P1_02"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + + P1_04 { + gpio-name = "P1_04"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + + P1_06 { + gpio-name = "P1_06"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + + P1_08 { + gpio-name = "P1_08"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + + P1_10 { + gpio-name = "P1_10"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + + P1_12 { + gpio-name = "P1_12"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + + P1_20 { + gpio-name = "P1_20"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + + P1_26 { + gpio-name = "P1_26"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + + P1_28 { + gpio-name = "P1_28"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + + P1_29 { + gpio-name = "P1_29"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + + P1_30 { + gpio-name = "P1_30"; + gpio = <&gpio1 11 0>; + input; + dir-changeable; + }; + + P1_31 { + gpio-name = "P1_31"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + + P1_32 { + gpio-name = "P1_32"; + gpio = <&gpio1 10 0>; + input; + dir-changeable; + }; + + P1_33 { + gpio-name = "P1_33"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + + P1_34 { + gpio-name = "P1_34"; + gpio = <&gpio0 26 0>; + input; + dir-changeable; + }; + + P1_35 { + gpio-name = "P1_35"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + + P1_36 { + gpio-name = "P1_36"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + + P2_01 { + gpio-name = "P2_01"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + + P2_02 { + gpio-name = "P2_02"; + gpio = <&gpio1 27 0>; + input; + dir-changeable; + }; + + P2_03 { + gpio-name = "P2_03"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + + P2_04 { + gpio-name = "P2_04"; + gpio = <&gpio1 26 0>; + input; + dir-changeable; + }; + + P2_05 { + gpio-name = "P2_05"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + + P2_06 { + gpio-name = "P2_06"; + gpio = <&gpio1 25 0>; + input; + dir-changeable; + }; + + P2_07 { + gpio-name = "P2_07"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + + P2_08 { + gpio-name = "P2_08"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + + P2_09 { + gpio-name = "P2_09"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + + P2_10 { + gpio-name = "P2_10"; + gpio = <&gpio1 20 0>; + input; + dir-changeable; + }; + + P2_11 { + gpio-name = "P2_11"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + + P2_17 { + gpio-name = "P2_17"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + + P2_18 { + gpio-name = "P2_18"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + + P2_19 { + gpio-name = "P2_19"; + gpio = <&gpio0 27 0>; + input; + dir-changeable; + }; + + P2_20 { + gpio-name = "P2_20"; + gpio = <&gpio2 0 0>; + input; + dir-changeable; + }; + + P2_22 { + gpio-name = "P2_22"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + + P2_24 { + gpio-name = "P2_24"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + + P2_25 { + gpio-name = "P2_25"; + gpio = <&gpio1 9 0>; + input; + dir-changeable; + }; + + P2_27 { + gpio-name = "P2_27"; + gpio = <&gpio1 8 0>; + input; + dir-changeable; + }; + + P2_28 { + gpio-name = "P2_28"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + + P2_29 { + gpio-name = "P2_29"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + + P2_30 { + gpio-name = "P2_30"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + + P2_31 { + gpio-name = "P2_31"; + gpio = <&gpio0 19 0>; + input; + dir-changeable; + }; + + P2_32 { + gpio-name = "P2_32"; + gpio = <&gpio3 16 0>; + input; + dir-changeable; + }; + + P2_33 { + gpio-name = "P2_33"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + + P2_34 { + gpio-name = "P2_34"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + + P2_35 { + gpio-name = "P2_35"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi b/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi --- a/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&am33xx_pinmux { + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txen.rgmii1_tctl */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxdv.rgmii1_rctl */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd3.rgmii1_td3 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd2.rgmii1_td2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd1.rgmii1_td1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd0.rgmii1_td0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txclk.rgmii1_tclk */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxclk.rgmii1_rclk */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd3.rgmii1_rd3 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd2.rgmii1_rd2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd1.rgmii1_rd1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd0.rgmii1_rd0 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) + >; + }; + + usb_hub_ctrl: usb_hub_ctrl { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* rmii1_refclk.gpio0_29 */ + >; + }; +}; + +&mac { + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; +}; + +&cpsw_emac0 { + phy-mode = "rgmii-id"; +}; + +&i2c0 { + usb2512b: usb-hub@2c { + pinctrl-names = "default"; + pinctrl-0 = <&usb_hub_ctrl>; + compatible = "microchip,usb2512b"; + reg = <0x2c>; + reset-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe.dts b/arch/arm/boot/dts/am335x-sancloud-bbe.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe.dts 2022-01-06 12:45:53.806318073 -0500 @@ -7,74 +7,21 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" #include "am335x-boneblack-common.dtsi" +#include "am335x-boneblack-hdmi.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" #include / { model = "SanCloud BeagleBone Enhanced"; compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; -}; - -&am33xx_pinmux { - pinctrl-names = "default"; - cpsw_default: cpsw_default { - pinctrl-single,pins = < - /* Slave 1 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txen.rgmii1_tctl */ - AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxdv.rgmii1_rctl */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd3.rgmii1_td3 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd2.rgmii1_td2 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd1.rgmii1_td1 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd0.rgmii1_td0 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txclk.rgmii1_tclk */ - AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxclk.rgmii1_rclk */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd3.rgmii1_rd3 */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd2.rgmii1_rd2 */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd1.rgmii1_rd1 */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd0.rgmii1_rd0 */ - >; - }; - - cpsw_sleep: cpsw_sleep { - pinctrl-single,pins = < - /* Slave 1 reset value */ - AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) - >; - }; - - davinci_mdio_default: davinci_mdio_default { - pinctrl-single,pins = < - /* MDIO */ - AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT_PULLUP | SLEWCTRL_FAST, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLUP, MUX_MODE0) - >; - }; - - davinci_mdio_sleep: davinci_mdio_sleep { - pinctrl-single,pins = < - /* MDIO reset value */ - AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MDC, PIN_INPUT_PULLDOWN, MUX_MODE7) - >; - }; - - usb_hub_ctrl: usb_hub_ctrl { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* rmii1_refclk.gpio0_29 */ - >; + chosen { + base_dtb = "am335x-sancloud-bbe.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; +}; +&am33xx_pinmux { mpu6050_pins: pinmux_mpu6050_pins { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT, MUX_MODE7) /* uart0_ctsn.gpio1_8 */ @@ -88,31 +35,10 @@ }; }; -&mac { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&cpsw_default>; - pinctrl-1 = <&cpsw_sleep>; - status = "okay"; -}; - -&davinci_mdio { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&davinci_mdio_default>; - pinctrl-1 = <&davinci_mdio_sleep>; - status = "okay"; - - ethphy0: ethernet-phy@0 { - reg = <0>; - }; -}; - -&cpsw_emac0 { - phy-handle = <ðphy0>; - phy-mode = "rgmii-id"; -}; - &i2c0 { lps331ap: barometer@5c { + pinctrl-names = "default"; + pinctrl-0 = <&lps3331ap_pins>; compatible = "st,lps331ap-press"; st,drdy-int-pin = <1>; reg = <0x5c>; @@ -121,17 +47,12 @@ }; mpu6050: accelerometer@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; compatible = "invensense,mpu6050"; reg = <0x68>; interrupt-parent = <&gpio0>; interrupts = <2 IRQ_TYPE_EDGE_RISING>; orientation = <0xff 0 0 0 1 0 0 0 0xff>; }; - - usb2512b: usb-hub@2c { - compatible = "microchip,usb2512b"; - reg = <0x2c>; - reset-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; - /* wifi on port 4 */ - }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sancloud Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-boneblack-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Extended WiFi"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-extended-wifi.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us= <100000>; + }; +}; + +&am33xx_pinmux { + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a9.gpio1_25: RADIO_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + bluetooth_pins: pinmux_bluetooth_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE7) /* event_intr0.gpio0_19 */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* uart1_rxd */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE0) /* uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLDOWN, MUX_MODE0) /* uart1_ctsn */ + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) /* uart1_rtsn */ + >; + }; +}; + +&i2c2 { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + ti,needs-special-hs-handling; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins>; + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + clock-frequency = <50000000>; + max-frequency = <50000000>; +}; + +&uart1 { + status = "okay"; + + bluetooth { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bluetooth_pins>; + compatible = "qcom,qca6174-bt"; + enable-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + clocks = <&l4ls_clkctrl AM3_L4LS_UART2_CLKCTRL 0>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sancloud Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Extended WiFi"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-extended-wifi-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us= <100000>; + }; +}; + +&am33xx_pinmux { + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a9.gpio1_25: RADIO_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + bluetooth_pins: pinmux_bluetooth_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE7) /* event_intr0.gpio0_19 */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* uart1_rxd */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE0) /* uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLDOWN, MUX_MODE0) /* uart1_ctsn */ + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) /* uart1_rtsn */ + >; + }; +}; + +&i2c2 { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + ti,needs-special-hs-handling; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins>; + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + clock-frequency = <50000000>; + max-frequency = <50000000>; +}; + +&uart1 { + status = "okay"; + + bluetooth { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bluetooth_pins>; + compatible = "qcom,qca6174-bt"; + enable-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + clocks = <&l4ls_clkctrl AM3_L4LS_UART2_CLKCTRL 0>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sancloud Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Extended WiFi"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-extended-wifi-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us= <100000>; + }; +}; + +&am33xx_pinmux { + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a9.gpio1_25: RADIO_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + bluetooth_pins: pinmux_bluetooth_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE7) /* event_intr0.gpio0_19 */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* uart1_rxd */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE0) /* uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLDOWN, MUX_MODE0) /* uart1_ctsn */ + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) /* uart1_rtsn */ + >; + }; +}; + +&i2c2 { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + ti,needs-special-hs-handling; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins>; + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + clock-frequency = <50000000>; + max-frequency = <50000000>; +}; + +&uart1 { + status = "okay"; + + bluetooth { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bluetooth_pins>; + compatible = "qcom,qca6174-bt"; + enable-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + clocks = <&l4ls_clkctrl AM3_L4LS_UART2_CLKCTRL 0>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 SanCloud Ltd + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-boneblack-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" + +/ { + model = "SanCloud BeagleBone Enhanced Lite"; + compatible = "sancloud,am335x-boneenhanced", + "ti,am335x-bone-black", + "ti,am335x-bone", + "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-lite.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) + >; + }; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "micron,spi-authenta"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 SanCloud Ltd + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Lite"; + compatible = "sancloud,am335x-boneenhanced", + "ti,am335x-bone-black", + "ti,am335x-bone", + "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-lite-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) + >; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "micron,spi-authenta"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Lite"; + compatible = "sancloud,am335x-boneenhanced", + "ti,am335x-bone-black", + "ti,am335x-bone", + "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-lite-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* P9_17 (A16) spi0_cs0.spi0_cs0 */ + P9_18_pinmux { status = "disabled"; }; /* P9_18 (B16) spi0_d1.spi0_d1 */ + P9_21_pinmux { status = "disabled"; }; /* P9_21 (B17) spi0_d0.spi0_d0 */ + P9_22_pinmux { status = "disabled"; }; /* P9_22 (A17) spi0_sclk.spi0_sclk */ +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) + >; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "micron,spi-authenta"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + mpu6050_pins: pinmux_mpu6050_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT, MUX_MODE7) /* uart0_ctsn.gpio1_8 */ + >; + }; + + lps3331ap_pins: pinmux_lps3331ap_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A10, PIN_INPUT, MUX_MODE7) /* gpmc_a10.gpio1_26 */ + >; + }; +}; + +&i2c0 { + lps331ap: barometer@5c { + pinctrl-names = "default"; + pinctrl-0 = <&lps3331ap_pins>; + compatible = "st,lps331ap-press"; + st,drdy-int-pin = <1>; + reg = <0x5c>; + interrupt-parent = <&gpio1>; + interrupts = <26 IRQ_TYPE_EDGE_RISING>; + }; + + mpu6050: accelerometer@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; + compatible = "invensense,mpu6050"; + reg = <0x68>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + orientation = <0xff 0 0 0 1 0 0 0 0xff>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts --- a/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + mpu6050_pins: pinmux_mpu6050_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT, MUX_MODE7) /* uart0_ctsn.gpio1_8 */ + >; + }; + + lps3331ap_pins: pinmux_lps3331ap_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A10, PIN_INPUT, MUX_MODE7) /* gpmc_a10.gpio1_26 */ + >; + }; +}; + +&i2c0 { + lps331ap: barometer@5c { + pinctrl-names = "default"; + pinctrl-0 = <&lps3331ap_pins>; + compatible = "st,lps331ap-press"; + st,drdy-int-pin = <1>; + reg = <0x5c>; + interrupt-parent = <&gpio1>; + interrupts = <26 IRQ_TYPE_EDGE_RISING>; + }; + + mpu6050: accelerometer@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; + compatible = "invensense,mpu6050"; + reg = <0x68>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + orientation = <0xff 0 0 0 1 0 0 0 0xff>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi --- a/arch/arm/boot/dts/am33xx.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am33xx.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -592,6 +592,13 @@ * Closed source PowerVR driver, no child device * binding or driver in mainline */ + gpu: gpu@0 { + compatible = "ti,am3352-sgx530", "img,sgx530"; + reg = <0x0 0x10000>; + interrupts = <37>; + clocks = <&gfx_fck_div_ck>; + clock-names = "fclk"; + }; }; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am33xx-l4.dtsi b/arch/arm/boot/dts/am33xx-l4.dtsi --- a/arch/arm/boot/dts/am33xx-l4.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am33xx-l4.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -252,22 +252,22 @@ ranges = <0x00000000 0x0000d000 0x00001000>, <0x00001000 0x0000e000 0x00001000>; - tscadc: tscadc@0 { - compatible = "ti,am3359-tscadc"; - reg = <0x0 0x1000>; - interrupts = <16>; - status = "disabled"; - dmas = <&edma 53 0>, <&edma 57 0>; - dma-names = "fifo0", "fifo1"; + tscadc: tscadc@0 { + compatible = "ti,am3359-tscadc"; + reg = <0x0 0x1000>; + interrupts = <16>; + status = "disabled"; + dmas = <&edma 53 0>, <&edma 57 0>; + dma-names = "fifo0", "fifo1"; - tsc { - compatible = "ti,am3359-tsc"; - }; - am335x_adc: adc { - #io-channel-cells = <1>; - compatible = "ti,am3359-adc"; - }; + tsc { + compatible = "ti,am3359-tsc"; + }; + am335x_adc: adc { + #io-channel-cells = <1>; + compatible = "ti,am3359-adc"; }; + }; }; target-module@10000 { /* 0x44e10000, ap 22 0c.0 */ @@ -290,7 +290,7 @@ am33xx_pinmux: pinmux@800 { compatible = "pinctrl-single"; reg = <0x800 0x238>; - #pinctrl-cells = <2>; + #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0x7f>; }; @@ -751,6 +751,55 @@ phys = <&phy_gmii_sel 2 1>; }; }; + + mac_sw: switch@0 { + compatible = "ti,am335x-cpsw-switch", "ti,cpsw-switch"; + reg = <0x0 0x4000>; + ranges = <0 0 0x4000>; + clocks = <&cpsw_125mhz_gclk>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <1>; + syscon = <&scm_conf>; + status = "disabled"; + + interrupts = <40 41 42 43>; + interrupt-names = "rx_thresh", "rx", "tx", "misc"; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + cpsw_port1: port@1 { + reg = <1>; + label = "port1"; + mac-address = [ 00 00 00 00 00 00 ]; + phys = <&phy_gmii_sel 1 1>; + }; + + cpsw_port2: port@2 { + reg = <2>; + label = "port2"; + mac-address = [ 00 00 00 00 00 00 ]; + phys = <&phy_gmii_sel 2 1>; + }; + }; + + davinci_mdio_sw: mdio@1000 { + compatible = "ti,cpsw-mdio","ti,davinci_mdio"; + clocks = <&cpsw_125mhz_gclk>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + reg = <0x1000 0x100>; + }; + + cpts { + clocks = <&cpsw_cpts_rft_clk>; + clock-names = "cpts"; + }; + }; }; target-module@180000 { /* 0x4a180000, ap 5 10.0 */ @@ -789,7 +838,114 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x300000 0x80000>; - status = "disabled"; + status = "okay"; + + pruss: pruss@0 { + compatible = "ti,am3356-pruss"; + reg = <0x0 0x80000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pruss_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x3000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + pruss_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&l3_gclk>, /* icss_iep_gclk */ + <&pruss_ocp_gclk>; /* icss_ocp_gclk */ + }; + }; + }; + + pruss_uart: serial@28000 { + compatible = "ti,pruss-uart"; + reg = <0x28000 0x38>; + clocks = <&dpll_per_m2_ck>; + interrupt-parent = <&pruss_intc>; + status = "disabled"; + }; + + pruss_iep: iep@2e000 { + compatible = "ti,am3356-icss-iep"; + reg = <0x2e000 0x31c>; + clocks = <&pruss_iepclk_mux>; + }; + + pruss_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; + + pruss_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x58>; + }; + + pruss_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupts = <20 21 22 23 24 25 26 27>; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + interrupt-controller; + #interrupt-cells = <3>; + }; + + pru0: pru@34000 { + compatible = "ti,am3356-pru"; + reg = <0x34000 0x2000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am335x-pru0-fw"; + interrupt-parent = <&pruss_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + pru1: pru@38000 { + compatible = "ti,am3356-pru"; + reg = <0x38000 0x2000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am335x-pru1-fw"; + interrupt-parent = <&pruss_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + pruss_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x90>; + clocks = <&dpll_core_m4_ck>; + clock-names = "fck"; + bus_freq = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; }; }; }; @@ -1420,7 +1576,7 @@ #mbox-cells = <1>; ti,mbox-num-users = <4>; ti,mbox-num-fifos = <8>; - mbox_wkupm3: wkup_m3 { + mbox_wkupm3: mbox-wkup-m3 { ti,mbox-send-noirq; ti,mbox-tx = <0 0 0>; ti,mbox-rx = <0 0 3>; @@ -1911,21 +2067,26 @@ status = "disabled"; ranges = <0 0 0x1000>; - ecap0: ecap@100 { - compatible = "ti,am3352-ecap", - "ti,am33xx-ecap"; + ecap0: pwm@100 { + compatible = "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x100 0x80>; clocks = <&l4ls_gclk>; clock-names = "fck"; - interrupts = <31>; - interrupt-names = "ecap0"; + status = "disabled"; + }; + + eqep0: counter@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <79>; status = "disabled"; }; ehrpwm0: pwm@200 { - compatible = "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + compatible = "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; @@ -1963,21 +2124,26 @@ status = "disabled"; ranges = <0 0 0x1000>; - ecap1: ecap@100 { - compatible = "ti,am3352-ecap", - "ti,am33xx-ecap"; + ecap1: pwm@100 { + compatible = "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x100 0x80>; clocks = <&l4ls_gclk>; clock-names = "fck"; - interrupts = <47>; - interrupt-names = "ecap1"; + status = "disabled"; + }; + + eqep1: counter@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <88>; status = "disabled"; }; ehrpwm1: pwm@200 { - compatible = "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + compatible = "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm1_tbclk>, <&l4ls_gclk>; @@ -2015,21 +2181,26 @@ status = "disabled"; ranges = <0 0 0x1000>; - ecap2: ecap@100 { - compatible = "ti,am3352-ecap", - "ti,am33xx-ecap"; + ecap2: pwm@100 { + compatible = "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x100 0x80>; clocks = <&l4ls_gclk>; clock-names = "fck"; - interrupts = <61>; - interrupt-names = "ecap2"; + status = "disabled"; + }; + + eqep2: counter@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <89>; status = "disabled"; }; ehrpwm2: pwm@200 { - compatible = "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + compatible = "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm2_tbclk>, <&l4ls_gclk>; diff -Naur --no-dereference a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi --- a/arch/arm/boot/dts/am4372.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am4372.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -432,6 +432,202 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x54400000 0x80000>; + + pruss1: pruss@0 { + compatible = "ti,am4376-pruss1"; + reg = <0x0 0x40000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pruss1_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x8000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + pruss1_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss1_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&sysclk_div>, /* icss_iep_gclk */ + <&pruss_ocp_gclk>; /* icss_ocp_gclk */ + }; + }; + }; + + pruss1_iep: iep@2e000 { + compatible = "ti,am3356-icss-iep"; + reg = <0x2e000 0x31c>; + clocks = <&pruss1_iepclk_mux>; + }; + + pruss1_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; + + pruss1_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x58>; + }; + + pruss1_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", + "host_intr6", "host_intr7"; + ti,irqs-reserved = /bits/ 8 <0x20>; /* BIT(5) */ + }; + + pru1_0: pru@34000 { + compatible = "ti,am4376-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am437x-pru1_0-fw"; + interrupt-parent = <&pruss1_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + pru1_1: pru@38000 { + compatible = "ti,am4376-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am437x-pru1_1-fw"; + interrupt-parent = <&pruss1_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + pruss1_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x90>; + clocks = <&dpll_core_m4_ck>; + clock-names = "fck"; + bus_freq = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; + + pruss0: pruss@40000 { + compatible = "ti,am4376-pruss0"; + reg = <0x40000 0x40000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pruss0_mem: memories@40000 { + reg = <0x40000 0x1000>, + <0x42000 0x1000>; + reg-names = "dram0", "dram1"; + }; + + pruss0_cfg: cfg@66000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x66000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x66000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss0_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&sysclk_div>, /* icss_iep_gclk */ + <&pruss_ocp_gclk>; /* icss_ocp_gclk */ + }; + }; + }; + + pruss0_iep: iep@6e000 { + compatible = "ti,am3356-icss-iep"; + reg = <0x6e000 0x31c>; + clocks = <&pruss0_iepclk_mux>; + status = "disabled"; + }; + + pruss0_mii_rt: mii-rt@72000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x72000 0x58>; + status = "disabled"; + }; + + pruss0_intc: interrupt-controller@60000 { + compatible = "ti,pruss-intc"; + reg = <0x60000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", + "host_intr6", "host_intr7"; + ti,irqs-reserved = /bits/ 8 <0x20>; /* BIT(5) */ + }; + + pru0_0: pru@74000 { + compatible = "ti,am4376-pru"; + reg = <0x74000 0x1000>, + <0x62000 0x400>, + <0x62400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am437x-pru0_0-fw"; + interrupt-parent = <&pruss0_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + pru0_1: pru@78000 { + compatible = "ti,am4376-pru"; + reg = <0x78000 0x1000>, + <0x64000 0x400>, + <0x64400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am437x-pru0_1-fw"; + interrupt-parent = <&pruss0_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + }; }; gpmc: gpmc@50000000 { @@ -523,6 +719,14 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0 0x56000000 0x1000000>; + + gpu: gpu@0 { + compatible = "ti,am4376-sgx530", "img,sgx530"; + reg = <0x0 0x10000>; + interrupts = ; + clocks = <&gfx_fck_div_ck>; + clock-names = "fclk"; + }; }; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts --- a/arch/arm/boot/dts/am437x-gp-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am437x-gp-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -1118,3 +1118,8 @@ &cpu { cpu0-supply = <&dcdc2>; }; + +&wkup_m3_ipc { + ti,set-io-isolation; + ti,scale-data-fw = "am43x-evm-scale-data.bin"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am437x-gp-evm-hdmi.dts b/arch/arm/boot/dts/am437x-gp-evm-hdmi.dts --- a/arch/arm/boot/dts/am437x-gp-evm-hdmi.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am437x-gp-evm-hdmi.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/* AM437x GP EVM with HDMI output */ + +#include "am437x-gp-evm.dts" + +/delete-node/ &lcd0; + +/ { + aliases { + display0 = &hdmi; + }; + + hdmi: connector { + compatible = "hdmi-connector"; + label = "hdmi"; + + type = "b"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&sii9022_out>; + }; + }; + }; + + sound@1 { + compatible = "simple-audio-card"; + simple-audio-card,name = "HDMI"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&hdmi_dailink_master>; + simple-audio-card,frame-master = <&hdmi_dailink_master>; + hdmi_dailink_master: simple-audio-card,cpu { + sound-dai = <&mcasp1>; + system-clock-frequency = <24000000>; + system-clock-direction-out; + }; + + simple-audio-card,codec { + sound-dai = <&sii9022>; + }; + }; + + sii9022_mclk: sii9022-mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; +}; + +&lcd_bl { + status = "disabled"; +}; + +&sound0 { + status = "disabled"; +}; + +&tlv320aic3106 { + status = "disabled"; +}; + +&i2c1 { + sii9022: sii9022@3b { + #sound-dai-cells = <0>; + compatible = "sil,sii9022"; + reg = <0x3b>; + + interrupt-parent = <&gpio3>; + interrupts = <24 IRQ_TYPE_LEVEL_LOW>; + + sil,i2s-data-lanes = < 0 >; + clocks = <&sii9022_mclk>; + clock-names = "mclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + sii9022_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + + port@1 { + reg = <1>; + + sii9022_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&dpi_out { + remote-endpoint = <&sii9022_in>; + data-lines = <24>; +}; + +/* Override SelLCDorHDMI from am437x-gp-evm.dts to select HDMI */ +&gpio5 { + p8 { + output-low; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am437x-idk-evm.dts b/arch/arm/boot/dts/am437x-idk-evm.dts --- a/arch/arm/boot/dts/am437x-idk-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am437x-idk-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -19,6 +19,11 @@ stdout-path = &uart0; }; + aliases { + ethernet2 = &pruss1_emac0; + ethernet3 = &pruss1_emac1; + }; + v24_0d: fixed-regulator-v24_0d { compatible = "regulator-fixed"; regulator-name = "V24_0D"; @@ -170,6 +175,41 @@ default-state = "off"; }; }; + + /* Dual-MAC Ethernet application node on PRU-ICSS1 */ + pruss1-eth { + compatible = "ti,am4376-prueth"; + ti,prus = <&pru1_0>, <&pru1_1>; + sram = <&ocmcram>; + interrupt-parent = <&pruss1_intc>; + mii-rt = <&pruss1_mii_rt>; + iep = <&pruss1_iep>; + + pinctrl-0 = <&pruss1_eth_default>; + pinctrl-names = "default"; + interrupts = <20 2 2>, <21 3 3>; + interrupt-names = "rx_lre_hp", "rx_lre_lp"; + + pruss1_emac0: ethernet-mii0 { + phy-handle = <&pruss1_eth0_phy>; + phy-mode = "mii"; + interrupts = <20 2 2>; + interrupt-names = "rx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + pruss1_emac1: ethernet-mii1 { + phy-handle = <&pruss1_eth1_phy>; + phy-mode = "mii"; + interrupts = <21 3 3>; + interrupt-names = "rx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; }; &am43xx_pinmux { @@ -305,6 +345,51 @@ >; }; + pruss1_mdio_default: pruss1-mdio-default { + pinctrl-single,pins = < + AM4372_IOPAD(0x88c, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_clk.pr1_mdio_mdclk */ + AM4372_IOPAD(0xa70, (PIN_INPUT | MUX_MODE8)) /* xdma_event_intr0.pr1_mdio_data */ + AM4372_IOPAD(0xa00, (PIN_INPUT_PULLUP | MUX_MODE7)) /* cam1_data6.gpio4_20 */ + >; + }; + + pruss1_eth_default: pruss1-eth-default { + pinctrl-single,pins = < + AM4372_IOPAD(0x8a0, (PIN_INPUT | MUX_MODE2)) /* dss_data0.pr1_mii_mt0_clk */ + AM4372_IOPAD(0x8b4, (PIN_OUTPUT | MUX_MODE2)) /* dss_data5.pr1_mii0_txd0 */ + AM4372_IOPAD(0x8b0, (PIN_OUTPUT | MUX_MODE2)) /* dss_data4.pr1_mii0_txd1 */ + AM4372_IOPAD(0x8ac, (PIN_OUTPUT | MUX_MODE2)) /* dss_data3.pr1_mii0_txd2 */ + AM4372_IOPAD(0x8a8, (PIN_OUTPUT | MUX_MODE2)) /* dss_data2.pr1_mii0_txd3 */ + AM4372_IOPAD(0x8cc, (PIN_INPUT | MUX_MODE5)) /* dss_data11.pr1_mii0_rxd0 */ + AM4372_IOPAD(0x8c8, (PIN_INPUT | MUX_MODE5)) /* dss_data10.pr1_mii0_rxd1 */ + AM4372_IOPAD(0x8c4, (PIN_INPUT | MUX_MODE5)) /* dss_data9.pr1_mii0_rxd2 */ + AM4372_IOPAD(0x8c0, (PIN_INPUT | MUX_MODE5)) /* dss_data8.pr1_mii0_rxd3 */ + AM4372_IOPAD(0x8a4, (PIN_OUTPUT | MUX_MODE2)) /* dss_data1.pr1_mii0_txen */ + AM4372_IOPAD(0x8d8, (PIN_INPUT | MUX_MODE5)) /* dss_data14.pr1_mii_mr0_clk */ + AM4372_IOPAD(0x8dc, (PIN_INPUT | MUX_MODE5)) /* dss_data15.pr1_mii0_rxdv */ + AM4372_IOPAD(0x8d4, (PIN_INPUT | MUX_MODE5)) /* dss_data13.pr1_mii0_rxer */ + AM4372_IOPAD(0x8d0, (PIN_INPUT | MUX_MODE5)) /* dss_data12.pr1_mii0_rxlink */ + AM4372_IOPAD(0xa40, (PIN_INPUT | MUX_MODE5)) /* gpio5_10.pr1_mii0_crs */ + AM4372_IOPAD(0xa38, (PIN_INPUT | MUX_MODE5)) /* gpio5_8.pr1_mii0_col */ + AM4372_IOPAD(0x858, (PIN_INPUT | MUX_MODE5)) /* gpmc_a6.pr1_mii_mt1_clk */ + AM4372_IOPAD(0x854, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a5.pr1_mii1_txd0 */ + AM4372_IOPAD(0x850, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a4.pr1_mii1_txd1 */ + AM4372_IOPAD(0x84c, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a3.pr1_mii1_txd2 */ + AM4372_IOPAD(0x848, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a2.pr1_mii1_txd3 */ + AM4372_IOPAD(0x86c, (PIN_INPUT | MUX_MODE5)) /* gpmc_a11.pr1_mii1_rxd0 */ + AM4372_IOPAD(0x868, (PIN_INPUT | MUX_MODE5)) /* gpmc_a10.pr1_mii1_rxd1 */ + AM4372_IOPAD(0x864, (PIN_INPUT | MUX_MODE5)) /* gpmc_a9.pr1_mii1_rxd2 */ + AM4372_IOPAD(0x860, (PIN_INPUT | MUX_MODE5)) /* gpmc_a8.pr1_mii1_rxd3 */ + AM4372_IOPAD(0x840, (PIN_OUTPUT | MUX_MODE5)) /* gpmc_a0.pr1_mii1_txen */ + AM4372_IOPAD(0x85c, (PIN_INPUT | MUX_MODE5)) /* gpmc_a7.pr1_mii_mr1_clk */ + AM4372_IOPAD(0x844, (PIN_INPUT | MUX_MODE5)) /* gpmc_a1.pr1_mii1_rxdv */ + AM4372_IOPAD(0x874, (PIN_INPUT | MUX_MODE5)) /* gpmc_wpn.pr1_mii1_rxer */ + AM4372_IOPAD(0xa4c, (PIN_INPUT | MUX_MODE5)) /* gpio5_13.pr1_mii1_rxlink */ + AM4372_IOPAD(0xa44, (PIN_INPUT | MUX_MODE5)) /* gpio5_11.pr1_mii1_crs */ + AM4372_IOPAD(0x878, (PIN_INPUT | MUX_MODE5)) /* gpmc_be1n.pr1_mii1_col */ + >; + }; + qspi_pins_default: qspi_pins_default { pinctrl-single,pins = < AM4372_IOPAD(0x87c, PIN_OUTPUT_PULLUP | MUX_MODE3) /* gpmc_csn0.qspi_csn */ @@ -537,3 +622,20 @@ opp-suspend; }; }; + +&pruss1_mdio { + pinctrl-0 = <&pruss1_mdio_default>; + pinctrl-names = "default"; + status = "okay"; + + reset-gpios = <&gpio4 20 GPIO_ACTIVE_LOW>; + reset-delay-us = <2>; /* PHY datasheet states 1uS min */ + + pruss1_eth0_phy: ethernet-phy@0 { + reg = <0>; + }; + + pruss1_eth1_phy: ethernet-phy@1 { + reg = <1>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am437x-l4.dtsi b/arch/arm/boot/dts/am437x-l4.dtsi --- a/arch/arm/boot/dts/am437x-l4.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am437x-l4.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -1149,7 +1149,7 @@ #mbox-cells = <1>; ti,mbox-num-users = <4>; ti,mbox-num-fifos = <8>; - mbox_wkupm3: wkup_m3 { + mbox_wkupm3: mbox-wkup-m3 { ti,mbox-send-noirq; ti,mbox-tx = <0 0 0>; ti,mbox-rx = <0 0 3>; @@ -1710,10 +1710,9 @@ ranges = <0 0 0x1000>; status = "disabled"; - ecap0: ecap@100 { + ecap0: pwm@100 { compatible = "ti,am4372-ecap", - "ti,am3352-ecap", - "ti,am33xx-ecap"; + "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x100 0x80>; clocks = <&l4ls_gclk>; @@ -1723,8 +1722,7 @@ ehrpwm0: pwm@200 { compatible = "ti,am4372-ehrpwm", - "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; @@ -1762,10 +1760,9 @@ ranges = <0 0 0x1000>; status = "disabled"; - ecap1: ecap@100 { + ecap1: pwm@100 { compatible = "ti,am4372-ecap", - "ti,am3352-ecap", - "ti,am33xx-ecap"; + "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x100 0x80>; clocks = <&l4ls_gclk>; @@ -1775,8 +1772,7 @@ ehrpwm1: pwm@200 { compatible = "ti,am4372-ehrpwm", - "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm1_tbclk>, <&l4ls_gclk>; @@ -1814,10 +1810,9 @@ ranges = <0 0 0x1000>; status = "disabled"; - ecap2: ecap@100 { + ecap2: pwm@100 { compatible = "ti,am4372-ecap", - "ti,am3352-ecap", - "ti,am33xx-ecap"; + "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x100 0x80>; clocks = <&l4ls_gclk>; @@ -1827,8 +1822,7 @@ ehrpwm2: pwm@200 { compatible = "ti,am4372-ehrpwm", - "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm2_tbclk>, <&l4ls_gclk>; @@ -1868,8 +1862,7 @@ ehrpwm3: pwm@200 { compatible = "ti,am4372-ehrpwm", - "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm3_tbclk>, <&l4ls_gclk>; @@ -1909,8 +1902,7 @@ ehrpwm4: pwm@48308200 { compatible = "ti,am4372-ehrpwm", - "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm4_tbclk>, <&l4ls_gclk>; @@ -1950,8 +1942,7 @@ ehrpwm5: pwm@200 { compatible = "ti,am4372-ehrpwm", - "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x200 0x80>; clocks = <&ehrpwm5_tbclk>, <&l4ls_gclk>; @@ -2388,7 +2379,7 @@ ranges = <0 0 0x20000>; usb1: usb@10000 { - compatible = "synopsys,dwc3"; + compatible = "snps,dwc3"; reg = <0x10000 0x10000>; interrupts = , , @@ -2468,7 +2459,7 @@ ranges = <0 0 0x20000>; usb2: usb@10000 { - compatible = "synopsys,dwc3"; + compatible = "snps,dwc3"; reg = <0x10000 0x10000>; interrupts = , , diff -Naur --no-dereference a/arch/arm/boot/dts/am437x-sk-evm.dts b/arch/arm/boot/dts/am437x-sk-evm.dts --- a/arch/arm/boot/dts/am437x-sk-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am437x-sk-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -892,3 +892,7 @@ }; }; }; + +&wkup_m3_ipc { + ti,scale-data-fw = "am43x-evm-scale-data.bin"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts --- a/arch/arm/boot/dts/am43x-epos-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am43x-epos-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -1018,3 +1018,7 @@ &cpu { cpu0-supply = <&dcdc2>; }; + +&wkup_m3_ipc { + ti,scale-data-fw = "am43x-evm-scale-data.bin"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am43x-epos-evm-hdmi.dts b/arch/arm/boot/dts/am43x-epos-evm-hdmi.dts --- a/arch/arm/boot/dts/am43x-epos-evm-hdmi.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am43x-epos-evm-hdmi.dts 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/* AM437x EPOS EVM with HDMI output */ + +#include "am43x-epos-evm.dts" + +/delete-node/ &lcd0; + +/ { + aliases { + display0 = &hdmi; + }; + + hdmi: connector { + compatible = "hdmi-connector"; + label = "hdmi"; + + type = "b"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&sii9022_out>; + }; + }; + }; + + sound@1 { + compatible = "simple-audio-card"; + simple-audio-card,name = "HDMI"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&hdmi_dailink_master>; + simple-audio-card,frame-master = <&hdmi_dailink_master>; + hdmi_dailink_master: simple-audio-card,cpu { + sound-dai = <&mcasp1>; + system-clock-frequency = <24000000>; + system-clock-direction-out; + }; + + simple-audio-card,codec { + sound-dai = <&sii9022>; + }; + }; + + sii9022_mclk: sii9022-mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; +}; + +&lcd_bl { + status = "disabled"; +}; + +&sound0 { + status = "disabled"; +}; + +&tlv320aic3111 { + status = "disabled"; +}; + +&am43xx_pinmux { + sii9022_pins: sii9022-pins { + pinctrl-single,pins = < + AM4372_IOPAD(0x848, PIN_INPUT | MUX_MODE7) /* gpmc_a2.gpio1_18 */ + >; + }; +}; + +&i2c2 { + sii9022: sii9022@3b { + #sound-dai-cells = <0>; + compatible = "sil,sii9022"; + reg = <0x3b>; + + interrupt-parent = <&gpio1>; + interrupts = <18 IRQ_TYPE_LEVEL_LOW>; + + sil,i2s-data-lanes = < 0 >; + clocks = <&sii9022_mclk>; + clock-names = "mclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + sii9022_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + + port@1 { + reg = <1>; + + sii9022_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&dpi_out { + remote-endpoint = <&sii9022_in>; + data-lines = <24>; +}; + +/* Override SelLCDorHDMI from am437x-epos-evm.dts to select HDMI */ +&gpio2 { + p1 { + output-low; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am571x-idk.dts b/arch/arm/boot/dts/am571x-idk.dts --- a/arch/arm/boot/dts/am571x-idk.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am571x-idk.dts 2022-01-06 12:45:53.806318073 -0500 @@ -16,6 +16,11 @@ model = "TI AM5718 IDK"; compatible = "ti,am5718-idk", "ti,am5718", "ti,dra7"; + aliases { + ethernet4 = "/pruss1_eth/ethernet-mii0"; + ethernet5 = "/pruss1_eth/ethernet-mii1"; + }; + memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x40000000>; @@ -165,6 +170,47 @@ default-state = "off"; }; }; + + /* Dual mac ethernet application node on icss1 */ + pruss1_eth { + status = "okay"; + compatible = "ti,am57-prueth"; + ti,prus = <&pru1_0>, <&pru1_1>; + ti,pruss-gp-mux-sel = <0>, /* GP, default */ + <4>; /* MII2, needed for PRUSS1_MII1 */ + sram = <&ocmcram1>; + mii-rt = <&pruss1_mii_rt>; + interrupts = <20 2 2>, <21 3 3>; + interrupt-names = "rx_lre_hp", "rx_lre_lp"; + iep = <&pruss1_iep>; + interrupt-parent = <&pruss1_intc>; + + ethernet-mii0 { + phy-handle = <&pruss1_eth0_phy>; + phy-mode = "mii"; + interrupts = <20 2 2>, <26 6 6>, <23 6 6>; + interrupt-names = "rx", "emac_ptp_tx", "hsr_ptp_tx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + ethernet-mii1 { + phy-handle = <&pruss1_eth1_phy>; + phy-mode = "mii"; + interrupts = <21 3 3>, <27 9 7>, <24 9 7>; + interrupt-names = "rx", "emac_ptp_tx", "hsr_ptp_tx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; +}; + +&pruss1_iep { + interrupt-parent = <&pruss1_intc>; + interrupts = <7 7 8>; + interrupt-names = "iep_cap_cmp"; }; &extcon_usb2 { @@ -208,3 +254,33 @@ pinctrl-1 = <&mmc2_pins_hs>; pinctrl-2 = <&mmc2_pins_ddr_rev20 &mmc2_iodelay_ddr_conf>; }; + +&pruss2_mdio { + reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + reset-delay-us = <2>; /* PHY datasheet states 1uS min */ +}; + +&pruss1_mdio { + status = "okay"; + reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; + reset-delay-us = <2>; /* PHY datasheet states 1uS min */ + #address-cells = <1>; + #size-cells = <0>; + + pruss1_eth0_phy: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&gpio3>; + interrupts = <28 IRQ_TYPE_EDGE_FALLING>; + }; + + pruss1_eth1_phy: ethernet-phy@1 { + reg = <1>; + interrupt-parent = <&gpio3>; + interrupts = <29 IRQ_TYPE_EDGE_FALLING>; + }; +}; + +&pruss2_eth { + ti,pruss-gp-mux-sel = <4>, /* MII2, needed for PRUSS1_MII0 */ + <4>; /* MII2, needed for PRUSS1_MII1 */ +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am5729-beagleboneai.dts b/arch/arm/boot/dts/am5729-beagleboneai.dts --- a/arch/arm/boot/dts/am5729-beagleboneai.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am5729-beagleboneai.dts 2022-01-06 12:45:53.806318073 -0500 @@ -12,6 +12,7 @@ #include #include #include +#include "am57xx-cmem.dtsi" / { model = "BeagleBoard.org BeagleBone AI"; @@ -22,10 +23,15 @@ rtc0 = &tps659038_rtc; rtc1 = &rtc; display0 = &hdmi_conn; + mmc0 = &mmc1; + mmc1 = &mmc2; + mmc2 = &mmc4; }; chosen { stdout-path = &uart1; + base_dtb = "am5729-beagleboneai.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; memory@0 { @@ -103,6 +109,8 @@ leds { compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_pins_default>; led0 { label = "beaglebone:green:usr0"; @@ -186,10 +194,14 @@ emmc_pwrseq: emmc_pwrseq { compatible = "mmc-pwrseq-emmc"; reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pwrseq_pins_default>; }; brcmf_pwrseq: brcmf_pwrseq { compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&brcmf_pwrseq_pins_default>; reset-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>, /* BT-REG-ON */ <&gpio3 18 GPIO_ACTIVE_LOW>; /* WL-REG-ON */ }; @@ -198,12 +210,84 @@ compatible = "linux,extcon-usb-gpio"; ti,enable-id-detection; id-gpio = <&gpio3 13 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&extcon_usb1_pins_default>; + }; +}; + +&dra7_pmx_core { + extcon_usb1_pins_default: extcon_usb1_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3518, PIN_INPUT | MUX_MODE14) /* AG2: vin1a_d9.gpio3_13 - USR0 */ + >; + }; + + led_pins_default: led_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3528, PIN_OUTPUT | MUX_MODE14) /* AF6: vin1a_d13.gpio3_17 - USR0 */ + DRA7XX_CORE_IOPAD(0x36c0, PIN_OUTPUT | MUX_MODE14) /* J11: mcasp1_axr3.gpio5_5 - USR1 */ + DRA7XX_CORE_IOPAD(0x3520, PIN_OUTPUT | MUX_MODE14) /* AG5: vin1a_d12.gpio3_15 - USR2 */ + DRA7XX_CORE_IOPAD(0x351c, PIN_OUTPUT | MUX_MODE14) /* AG3: vin1a_d10.gpio3_14 - USR3 */ + DRA7XX_CORE_IOPAD(0x3500, PIN_OUTPUT | MUX_MODE14) /* AH6: vin1a_d3.gpio3_7 - USR4 */ + >; + }; + + emmc_pwrseq_pins_default: emmc_pwrseq_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x36c8, PIN_OUTPUT_PULLUP | MUX_MODE14) /* F13: mcasp1_axr5.gpio5_7 - eMMC_RSTn */ + >; + }; + + brcmf_pwrseq_pins_default: brcmf_pwrseq_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x352c, PIN_OUTPUT_PULLUP | MUX_MODE14) /* AF3: vin1a_d14.gpio3_18 - WL_REG_ON */ + DRA7XX_CORE_IOPAD(0x353c, PIN_OUTPUT_PULLUP | MUX_MODE14) /* AE5: vin1a_d18.gpio3_22 - BT_REG_ON */ + >; + }; + + wifibt_extra_pins_default: wifibt_extra_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3540, PIN_INPUT | MUX_MODE14) /* AE1: vin1a_d19.gpio3_23 - WL_HOST_WAKE */ + DRA7XX_CORE_IOPAD(0x3450, PIN_INPUT | MUX_MODE8) /* P6: vin1a_d20.uart6_rxd - UART6_RXD */ + DRA7XX_CORE_IOPAD(0x3454, PIN_INPUT | MUX_MODE8) /* R9: vin1a_d21.uart6_txd - UART6_TXD */ + DRA7XX_CORE_IOPAD(0x3458, PIN_INPUT | MUX_MODE8) /* R5: vin1a_d22.uart6_ctsn - UART6_CTSN */ + DRA7XX_CORE_IOPAD(0x345c, PIN_INPUT | MUX_MODE8) /* P5: vin1a_d23.uart6_rtsn - UART6_RTSN */ + DRA7XX_CORE_IOPAD(0x3534, PIN_INPUT_PULLDOWN | MUX_MODE14) /* AF1: vin1a_d16.gpio3_20 - BT_HOST_WAKE */ + DRA7XX_CORE_IOPAD(0x3538, PIN_OUTPUT_PULLDOWN | MUX_MODE14) /* AE3: vin1a_d6.gpio3_21 - BT_WAKE */ + >; + }; + + adc_pins_default: adc_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3550, PIN_OUTPUT | MUX_MODE14) /* AD3: vin1a_d23.gpio3_27 - VDD_ADC_SEL */ + DRA7XX_CORE_IOPAD(0x34DC, PIN_INPUT_PULLUP | MUX_MODE14) /* AG8: vin1a_clk0.gpio2_30 - INT_ADC */ + >; + }; + + pmic_pins_default: pmic_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3690, PIN_INPUT_PULLUP | MUX_MODE14) /* F21: gpio6_16.gpio6_16 - PMIC_INT */ + >; + }; + + hdmi_pins_default: hdmi_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3808, PIN_INPUT | MUX_MODE1) /* C25: i2c2_sda.hdmi1_ddc_scl - HDMI_DDC_SCL */ + DRA7XX_CORE_IOPAD(0x380C, PIN_INPUT | MUX_MODE1) /* F17: i2c2_scl.hdmi1_ddc_sda - HDMI_DDC_SDA */ + DRA7XX_CORE_IOPAD(0x37BC, PIN_INPUT | MUX_MODE6) /* B20: spi1_cs3.hdmi1_cec - HDMI_DDC_CEC */ +#if 0 + DRA7XX_CORE_IOPAD(0x37B8, PIN_INPUT | MUX_MODE6) /* B21: spi1_cs2.hdmi1_hpd - HDMI_DDC_HPD */ +#else + DRA7XX_CORE_IOPAD(0x37B8, PIN_INPUT | MUX_MODE14) /* B21: spi1_cs2.gpio7_12 - HDMI_DDC_HPD */ +#endif + >; }; }; &i2c1 { status = "okay"; clock-frequency = <400000>; + symlink = "bone/i2c/0"; tps659038: tps659038@58 { compatible = "ti,tps659038"; @@ -211,6 +295,9 @@ interrupt-parent = <&gpio6>; interrupts = <16 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins_default>; + #interrupt-cells = <2>; interrupt-controller; @@ -424,12 +511,15 @@ st,adc-freq = <1>; /* 3.25 MHz ADC clock speed */ st,sample-time = <4>; /* ADC converstion time: 80 clocks */ + pinctrl-names = "default"; + pinctrl-0 = <&adc_pins_default>; + stmpe_adc { compatible = "st,stmpe-adc"; st,norequest-mask = <0x00>; /* mask any channels to be used by touchscreen */ adc0: iio-device@0 { #io-channel-cells = <1>; - iio-channels = <&adc0 4>, <&adc0 1>, <&adc0 2>, <&adc0 3>, <&adc0 4>, <&adc0 5>, <&adc0 6>; + iio-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>, <&adc0 4>, <&adc0 5>, <&adc0 6>; iio-channel-names = "AIN0_P9_39", "AIN1_P9_40", "AIN2_P9_37", "AIN3_P9_38", "AIN4_P9_33", "AIN5_P9_36", "AIN6_P9_35"; }; @@ -462,6 +552,11 @@ #pwm-cells = <2>; }; }; + + eeprom: eeprom@50 { + compatible = "atmel,24c32"; + reg = <0x50>; + }; }; &mcspi3 { @@ -486,6 +581,7 @@ &uart1 { status = "okay"; + symlink = "bone/uart/0"; }; &davinci_mdio_sw { @@ -550,7 +646,11 @@ ti,needs-special-reset; dmas = <&sdma_xbar 47>, <&sdma_xbar 48>; dma-names = "tx", "rx"; - + pinctrl-names = "default", "hs", "ddr_1_8v", "hs200_1_8v"; + pinctrl-0 = <&mmc2_pins_default>; + pinctrl-1 = <&mmc2_pins_hs>; + pinctrl-2 = <&mmc2_pins_ddr_rev20>; + pinctrl-3 = <&mmc2_pins_hs200>; }; &mmc4 { @@ -563,6 +663,10 @@ /* DDR50: DDR up to 50 MHz (1.8 V signaling). */ status = "okay"; + pinctrl-names = "default", "hs"; + pinctrl-0 = <&mmc4_pins_default &wifibt_extra_pins_default>; + pinctrl-1 = <&mmc4_pins_hs &wifibt_extra_pins_default>; + ti,needs-special-reset; vmmc-supply = <&vdd_3v3>; cap-power-off-card; @@ -614,6 +718,10 @@ dr_mode = "host"; }; +&bb2d { + status = "okay"; +}; + &dss { status = "okay"; vdda_video-supply = <&vdd_1v8_pll>; @@ -622,6 +730,8 @@ &hdmi { status = "okay"; vdda-supply = <&vdd_1v8_phy_ldo4>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_pins_default>; port { hdmi_out: endpoint { @@ -675,6 +785,7 @@ &i2c4 { status = "okay"; clock-frequency = <100000>; + symlink = "bone/i2c/2"; }; &cpu0_opp_table { diff -Naur --no-dereference a/arch/arm/boot/dts/am572x-idk.dts b/arch/arm/boot/dts/am572x-idk.dts --- a/arch/arm/boot/dts/am572x-idk.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am572x-idk.dts 2022-01-06 12:45:53.806318073 -0500 @@ -27,3 +27,13 @@ pinctrl-1 = <&mmc2_pins_hs>; pinctrl-2 = <&mmc2_pins_ddr_rev20>; }; + +&pruss2_eth0_phy { + reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; + reset-assert-us = <2>; /* PHY datasheet states 1uS min */ +}; + +&pruss2_eth1_phy { + reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + reset-assert-us = <2>; /* PHY datasheet states 1uS min */ +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am5748.dtsi b/arch/arm/boot/dts/am5748.dtsi --- a/arch/arm/boot/dts/am5748.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am5748.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -3,7 +3,7 @@ * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ */ -#include "dra76x.dtsi" +#include "dra74x-p.dtsi" #include "am57-pruss.dtsi" / { @@ -25,10 +25,6 @@ status = "disabled"; }; -&usb4_tm { - status = "disabled"; -}; - &atl_tm { status = "disabled"; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am574x-idk.dts b/arch/arm/boot/dts/am574x-idk.dts --- a/arch/arm/boot/dts/am574x-idk.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am574x-idk.dts 2022-01-06 12:45:53.806318073 -0500 @@ -36,6 +36,16 @@ pinctrl-2 = <&mmc2_pins_default>; }; -&m_can0 { - status = "disabled"; +&emif1 { + status = "okay"; +}; + +&pruss2_eth0_phy { + reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; + reset-assert-us = <2>; /* PHY datasheet states 1uS min */ +}; + +&pruss2_eth1_phy { + reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + reset-assert-us = <2>; /* PHY datasheet states 1uS min */ }; diff -Naur --no-dereference a/arch/arm/boot/dts/am57-pruss.dtsi b/arch/arm/boot/dts/am57-pruss.dtsi --- a/arch/arm/boot/dts/am57-pruss.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am57-pruss.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ * * Common PRUSS data for TI AM57xx platforms */ @@ -25,6 +25,112 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x00000000 0x4b200000 0x80000>; + + pruss1: pruss@0 { + compatible = "ti,am5728-pruss"; + reg = <0x0 0x80000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pruss1_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x8000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + pruss1_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss1_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&dpll_gmac_m3x2_ck>, /* icss_iep_clk */ + <&dpll_gmac_h13x2_ck>; /* icss_clk */ + }; + }; + }; + + pruss1_iep: iep@2e000 { + compatible = "ti,am5728-icss-iep"; + reg = <0x2e000 0x31c>; + clocks = <&pruss1_iepclk_mux>; + }; + + pruss1_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; + + pruss1_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x58>; + }; + + pruss1_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru1_0: pru@34000 { + compatible = "ti,am5728-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am57xx-pru1_0-fw"; + interrupt-parent = <&pruss1_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + pru1_1: pru@38000 { + compatible = "ti,am5728-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am57xx-pru1_1-fw"; + interrupt-parent = <&pruss1_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + pruss1_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&dpll_gmac_h13x2_ck>; + clock-names = "fck"; + bus_freq = <1000000>; + reg = <0x32400 0x90>; + status = "disabled"; + }; + }; }; pruss2_tm: target-module@4b2a6000 { @@ -46,5 +152,111 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x00000000 0x4b280000 0x80000>; + + pruss2: pruss@0 { + compatible = "ti,am5728-pruss"; + reg = <0x0 0x80000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pruss2_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x8000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + pruss2_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss2_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&dpll_gmac_m3x2_ck>, /* icss_iep_clk */ + <&dpll_gmac_h13x2_ck>; /* icss_clk */ + }; + }; + }; + + pruss2_iep: iep@2e000 { + compatible = "ti,am5728-icss-iep"; + reg = <0x2e000 0x31c>; + clocks = <&pruss2_iepclk_mux>; + }; + + pruss2_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; + + pruss2_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x58>; + }; + + pruss2_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru2_0: pru@34000 { + compatible = "ti,am5728-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am57xx-pru2_0-fw"; + interrupt-parent = <&pruss2_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + pru2_1: pru@38000 { + compatible = "ti,am5728-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am57xx-pru2_1-fw"; + interrupt-parent = <&pruss2_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + pruss2_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&dpll_gmac_h13x2_ck>; + clock-names = "fck"; + bus_freq = <1000000>; + reg = <0x32400 0x90>; + status = "disabled"; + }; + }; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi --- a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -637,3 +637,15 @@ status = "okay"; memory-region = <&dsp2_memory_region>; }; + +&bb2d { + status = "okay"; +}; + +&pruss1_mdio { + status = "disabled"; +}; + +&pruss2_mdio { + status = "disabled"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts --- a/arch/arm/boot/dts/am57xx-beagle-x15.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts 2022-01-06 12:45:53.806318073 -0500 @@ -4,10 +4,16 @@ */ #include "am57xx-beagle-x15-common.dtsi" +#include "am57xx-cmem.dtsi" / { /* NOTE: This describes the "original" pre-production A2 revision */ model = "TI AM5728 BeagleBoard-X15"; + + chosen { + base_dtb = "am57xx-beagle-x15.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &tpd12s015 { diff -Naur --no-dereference a/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts b/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts --- a/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts 2022-01-06 12:45:53.806318073 -0500 @@ -4,9 +4,15 @@ */ #include "am57xx-beagle-x15-common.dtsi" +#include "am57xx-cmem.dtsi" / { model = "TI AM5728 BeagleBoard-X15 rev B1"; + + chosen { + base_dtb = "am57xx-beagle-x15-revb1.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &tpd12s015 { diff -Naur --no-dereference a/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts b/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts --- a/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts 2022-01-06 12:45:53.806318073 -0500 @@ -4,9 +4,15 @@ */ #include "am57xx-beagle-x15-common.dtsi" +#include "am57xx-cmem.dtsi" / { model = "TI AM5728 BeagleBoard-X15 rev C"; + + chosen { + base_dtb = "am57xx-beagle-x15-revc.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &tpd12s015 { diff -Naur --no-dereference a/arch/arm/boot/dts/am57xx-cmem.dtsi b/arch/arm/boot/dts/am57xx-cmem.dtsi --- a/arch/arm/boot/dts/am57xx-cmem.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/am57xx-cmem.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,43 @@ +/* https://git.yoctoproject.org/cgit/cgit.cgi/meta-ti/plain/recipes-kernel/linux/files/dra7xx/cmem-am5729-beagleboneai.dtsi?h=dunfell */ +/* 0x0c000000 -> 0x18000000 for tild */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cmem_block_mem_0: cmem_block_mem@a0000000 { + reg = <0x0 0xa0000000 0x0 0x18000000>; + no-map; + status = "okay"; + }; + + cmem_block_mem_1_ocmc3: cmem_block_mem@40500000 { + reg = <0x0 0x40500000 0x0 0x100000>; + no-map; + status = "okay"; + }; + }; + + cmem { + compatible = "ti,cmem"; + #address-cells = <1>; + #size-cells = <0>; + + #pool-size-cells = <2>; + + status = "okay"; + + cmem_block_0: cmem_block@0 { + reg = <0>; + memory-region = <&cmem_block_mem_0>; + cmem-buf-pools = <1 0x0 0x18000000>; + }; + + cmem_block_1: cmem_block@1 { + reg = <1>; + memory-region = <&cmem_block_mem_1_ocmc3>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/am57xx-idk-common.dtsi b/arch/arm/boot/dts/am57xx-idk-common.dtsi --- a/arch/arm/boot/dts/am57xx-idk-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/am57xx-idk-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -10,6 +10,8 @@ rtc0 = &tps659038_rtc; rtc1 = &rtc; display0 = &hdmi0; + ethernet2 = &pruss2_emac0; + ethernet3 = &pruss2_emac1; }; chosen { @@ -155,6 +157,44 @@ compatible = "fixed-clock"; clock-frequency = <20000000>; }; + + /* Dual-MAC Ethernet application node on PRU-ICSS2 */ + pruss2_eth: pruss2_eth { + compatible = "ti,am57-prueth"; + ti,prus = <&pru2_0>, <&pru2_1>; + sram = <&ocmcram1>; + mii-rt = <&pruss2_mii_rt>; + iep = <&pruss2_iep>; + interrupts = <20 2 2>, <21 3 3>; + interrupt-names = "rx_lre_hp", "rx_lre_lp"; + interrupt-parent = <&pruss2_intc>; + + pruss2_emac0: ethernet-mii0 { + phy-handle = <&pruss2_eth0_phy>; + phy-mode = "mii"; + interrupts = <20 2 2>, <26 6 6>; + interrupt-names = "rx", "emac_ptp_tx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + pruss2_emac1: ethernet-mii1 { + phy-handle = <&pruss2_eth1_phy>; + phy-mode = "mii"; + interrupts = <21 3 3>, <27 9 7>; + interrupt-names = "rx", "emac_ptp_tx"; + ti,no-half-duplex; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; +}; + +&pruss2_iep { + interrupt-parent = <&pruss2_intc>; + interrupts = <7 7 8>; + interrupt-names = "iep_cap_cmp"; }; &dra7_pmx_core { @@ -606,3 +646,22 @@ }; }; }; + +&pruss2_mdio { + status = "okay"; + pruss2_eth0_phy: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&gpio3>; + interrupts = <30 IRQ_TYPE_EDGE_FALLING>; + }; + + pruss2_eth1_phy: ethernet-phy@1 { + reg = <1>; + interrupt-parent = <&gpio3>; + interrupts = <31 IRQ_TYPE_EDGE_FALLING>; + }; +}; + +&bb2d { + status = "ok"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi --- a/arch/arm/boot/dts/da850.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/da850.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -9,7 +9,9 @@ #address-cells = <1>; #size-cells = <1>; chosen { }; - aliases { }; + aliases { + rproc0 = &dsp; + }; memory@c0000000 { device_type = "memory"; @@ -574,8 +576,7 @@ status = "disabled"; }; ehrpwm0: pwm@300000 { - compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x300000 0x2000>; clocks = <&psc1 17>, <&ehrpwm_tbclk>; @@ -584,8 +585,7 @@ status = "disabled"; }; ehrpwm1: pwm@302000 { - compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm", - "ti,am33xx-ehrpwm"; + compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm"; #pwm-cells = <3>; reg = <0x302000 0x2000>; clocks = <&psc1 17>, <&ehrpwm_tbclk>; @@ -593,9 +593,8 @@ power-domains = <&psc1 17>; status = "disabled"; }; - ecap0: ecap@306000 { - compatible = "ti,da850-ecap", "ti,am3352-ecap", - "ti,am33xx-ecap"; + ecap0: pwm@306000 { + compatible = "ti,da850-ecap", "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x306000 0x80>; clocks = <&psc1 20>; @@ -603,9 +602,8 @@ power-domains = <&psc1 20>; status = "disabled"; }; - ecap1: ecap@307000 { - compatible = "ti,da850-ecap", "ti,am3352-ecap", - "ti,am33xx-ecap"; + ecap1: pwm@307000 { + compatible = "ti,da850-ecap", "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x307000 0x80>; clocks = <&psc1 20>; @@ -613,9 +611,8 @@ power-domains = <&psc1 20>; status = "disabled"; }; - ecap2: ecap@308000 { - compatible = "ti,da850-ecap", "ti,am3352-ecap", - "ti,am33xx-ecap"; + ecap2: pwm@308000 { + compatible = "ti,da850-ecap", "ti,am3352-ecap"; #pwm-cells = <3>; reg = <0x308000 0x80>; clocks = <&psc1 20>; diff -Naur --no-dereference a/arch/arm/boot/dts/da850-lego-ev3.dts b/arch/arm/boot/dts/da850-lego-ev3.dts --- a/arch/arm/boot/dts/da850-lego-ev3.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/da850-lego-ev3.dts 2022-01-06 12:45:53.806318073 -0500 @@ -412,14 +412,14 @@ status = "okay"; /* Don't pull down battery voltage adc io channel */ - batt_volt_en { + batt-volt-en-hog { gpio-hog; gpios = <6 GPIO_ACTIVE_HIGH>; output-high; }; /* Don't impede Bluetooth clock signal */ - bt_clock_en { + bt-clock-en-hog { gpio-hog; gpios = <5 GPIO_ACTIVE_HIGH>; input; @@ -433,19 +433,19 @@ * anything, but they are present in the source code from LEGO. */ - bt_pic_en { + bt-pic-en-hog { gpio-hog; gpios = <51 GPIO_ACTIVE_HIGH>; output-low; }; - bt_pic_rst { + bt-pic-rst-hog { gpio-hog; gpios = <78 GPIO_ACTIVE_HIGH>; output-high; }; - bt_pic_cts { + bt-pic-cts-hog { gpio-hog; gpios = <87 GPIO_ACTIVE_HIGH>; input; diff -Naur --no-dereference a/arch/arm/boot/dts/dra71-evm.dts b/arch/arm/boot/dts/dra71-evm.dts --- a/arch/arm/boot/dts/dra71-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra71-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -112,6 +112,8 @@ regulator-name = "lp8733-ldo0"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; }; lp8733_ldo1_reg: ldo1 { diff -Naur --no-dereference a/arch/arm/boot/dts/dra72-evm-common.dtsi b/arch/arm/boot/dts/dra72-evm-common.dtsi --- a/arch/arm/boot/dts/dra72-evm-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra72-evm-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -535,6 +535,10 @@ status = "okay"; }; +&bb2d { + status = "okay"; +}; + &hdmi { status = "okay"; diff -Naur --no-dereference a/arch/arm/boot/dts/dra74x-p.dtsi b/arch/arm/boot/dts/dra74x-p.dtsi --- a/arch/arm/boot/dts/dra74x-p.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/dra74x-p.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "dra74x.dtsi" + +/ { + compatible = "ti,dra762", "ti,dra7"; + + ocp { + emif1: emif@4c000000 { + compatible = "ti,emif-dra7xx"; + reg = <0x4c000000 0x200>; + interrupts = ; + status = "disabled"; + }; + }; +}; + +/* MCAN interrupts are hard-wired to irqs 67, 68 */ +&crossbar_mpu { + ti,irqs-skip = <10 67 68 133 139 140>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/dra76-evm.dts b/arch/arm/boot/dts/dra76-evm.dts --- a/arch/arm/boot/dts/dra76-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra76-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -158,12 +158,6 @@ regulator-max-microvolt = <1800000>; }; - clk_ov5640_fixed: clock { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <24000000>; - }; - hdmi0: connector { compatible = "hdmi-connector"; label = "hdmi"; @@ -406,27 +400,6 @@ }; }; -&i2c5 { - status = "okay"; - clock-frequency = <400000>; - - ov5640@3c { - compatible = "ovti,ov5640"; - reg = <0x3c>; - - clocks = <&clk_ov5640_fixed>; - clock-names = "xclk"; - - port { - csi2_cam0: endpoint { - remote-endpoint = <&csi2_phy0>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - }; -}; - &cpu0 { vdd-supply = <&buck10_reg>; }; @@ -573,14 +546,6 @@ }; }; -&csi2_0 { - csi2_phy0: endpoint { - remote-endpoint = <&csi2_cam0>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; -}; - &ipu2 { status = "okay"; memory-region = <&ipu2_cma_pool>; diff -Naur --no-dereference a/arch/arm/boot/dts/dra76x.dtsi b/arch/arm/boot/dts/dra76x.dtsi --- a/arch/arm/boot/dts/dra76x.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra76x.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -83,11 +83,6 @@ }; }; -/* MCAN interrupts are hard-wired to irqs 67, 68 */ -&crossbar_mpu { - ti,irqs-skip = <10 67 68 133 139 140>; -}; - &scm_conf_clocks { dpll_gmac_h14x2_ctrl_ck: dpll_gmac_h14x2_ctrl_ck@3fc { #clock-cells = <0>; @@ -133,3 +128,32 @@ /* dra76x is not affected by i887 */ max-frequency = <96000000>; }; + +&cpu0_opp_table { + opp_plus@1800000000 { + opp-hz = /bits/ 64 <1800000000>; + opp-microvolt = <1250000 950000 1250000>, + <1250000 950000 1250000>; + opp-supported-hw = <0xFF 0x08>; + }; +}; + +&opp_supply_mpu { + ti,efuse-settings = < + /* uV offset */ + 1060000 0x0 + 1160000 0x4 + 1210000 0x8 + 1250000 0xC + >; +}; + +&abb_mpu { + ti,abb_info = < + /*uV ABB efuse rbb_m fbb_m vset_m*/ + 1060000 0 0x0 0 0x02000000 0x01F00000 + 1160000 0 0x4 0 0x02000000 0x01F00000 + 1210000 0 0x8 0 0x02000000 0x01F00000 + 1250000 0 0xC 0 0x02000000 0x01F00000 + >; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi --- a/arch/arm/boot/dts/dra7.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra7.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -759,6 +759,18 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0 0x56000000 0x2000000>; + + gpu: gpu@0 { + compatible = "ti,dra7-sgx544", "img,sgx544"; + reg = <0x0 0x10000>; + interrupts = ; + clocks = <&l3_iclk_div>, + <&gpu_core_gclk_mux>, + <&gpu_hyd_gclk_mux>; + clock-names = "iclk", + "fclk1", + "fclk2"; + }; }; crossbar_mpu: crossbar@4a002a48 { @@ -871,6 +883,16 @@ }; }; + bb2d: bb2d@59000000 { + compatible = "ti,dra7-bb2d", "vivante,gc"; + reg = <0x59000000 0x0700>; + interrupts = ; + ti,hwmods = "bb2d"; + clocks = <&dss_clkctrl DRA7_BB2D_CLKCTRL 0>; + clock-names = "fck"; + status = "disabled"; + }; + aes1_target: target-module@4b500000 { compatible = "ti,sysc-omap2", "ti,sysc"; reg = <0x4b500080 0x4>, @@ -933,7 +955,7 @@ }; }; - sham_target: target-module@4b101000 { + sham1_target: target-module@4b101000 { compatible = "ti,sysc-omap3-sham", "ti,sysc"; reg = <0x4b101100 0x4>, <0x4b101110 0x4>, @@ -952,7 +974,7 @@ #size-cells = <1>; ranges = <0x0 0x4b101000 0x1000>; - sham: sham@0 { + sham1: sham@0 { compatible = "ti,omap5-sham"; reg = <0 0x300>; interrupts = ; @@ -960,6 +982,36 @@ dma-names = "rx"; clocks = <&l3_iclk_div>; clock-names = "fck"; + }; + }; + + sham2_target: target-module@42701000 { + compatible = "ti,sysc-omap3-sham", "ti,sysc"; + reg = <0x42701100 0x4>, + <0x42701110 0x4>, + <0x42701114 0x4>; + reg-names = "rev", "sysc", "syss"; + ti,sysc-mask = <(SYSC_OMAP2_SOFTRESET | + SYSC_OMAP2_AUTOIDLE)>; + ti,sysc-sidle = , + , + ; + ti,syss-mask = <1>; + /* Domains (P, C): l4per_pwrdm, l4sec_clkdm */ + clocks = <&l4sec_clkctrl DRA7_L4SEC_SHAM2_CLKCTRL 0>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x42701000 0x1000>; + + sham2: sham@0 { + compatible = "ti,omap5-sham"; + reg = <0 0x300>; + interrupts = ; + dmas = <&edma_xbar 165 0>; + dma-names = "rx"; + clocks = <&l3_iclk_div>; + clock-names = "fck"; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts --- a/arch/arm/boot/dts/dra7-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra7-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -590,3 +590,7 @@ status = "okay"; memory-region = <&dsp2_memory_region>; }; + +&bb2d { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/dra7-l4.dtsi b/arch/arm/boot/dts/dra7-l4.dtsi --- a/arch/arm/boot/dts/dra7-l4.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/dra7-l4.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -2530,7 +2530,7 @@ status = "disabled"; ranges = <0 0 0x1000>; - ecap0: ecap@100 { + ecap0: pwm@100 { compatible = "ti,dra746-ecap", "ti,am3352-ecap"; #pwm-cells = <3>; @@ -2576,7 +2576,7 @@ status = "disabled"; ranges = <0 0 0x1000>; - ecap1: ecap@100 { + ecap1: pwm@100 { compatible = "ti,dra746-ecap", "ti,am3352-ecap"; #pwm-cells = <3>; @@ -2622,7 +2622,7 @@ status = "disabled"; ranges = <0 0 0x1000>; - ecap2: ecap@100 { + ecap2: pwm@100 { compatible = "ti,dra746-ecap", "ti,am3352-ecap"; #pwm-cells = <3>; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone.dtsi b/arch/arm/boot/dts/keystone.dtsi --- a/arch/arm/boot/dts/keystone.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -217,8 +217,8 @@ dma-ranges; status = "disabled"; - usb0: dwc3@2690000 { - compatible = "synopsys,dwc3"; + usb0: usb@2690000 { + compatible = "snps,dwc3"; reg = <0x2690000 0x70000>; interrupts = ; usb-phy = <&usb_phy>, <&usb_phy>; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2e.dtsi b/arch/arm/boot/dts/keystone-k2e.dtsi --- a/arch/arm/boot/dts/keystone-k2e.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2e.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -52,7 +52,7 @@ usb: usb@2680000 { interrupts = ; - dwc3@2690000 { + usb@2690000 { interrupts = ; }; }; @@ -78,8 +78,8 @@ dma-ranges; status = "disabled"; - usb1: dwc3@25010000 { - compatible = "synopsys,dwc3"; + usb1: usb@25010000 { + compatible = "snps,dwc3"; reg = <0x25010000 0x70000>; interrupts = ; usb-phy = <&usb1_phy>, <&usb1_phy>; @@ -93,6 +93,11 @@ #address-cells = <1>; #size-cells = <1>; + mpm-sram@0 { + compatible = "ti,keystone-dsp-msm-ram"; + reg = <0x0 0x80000>; + }; + bm-sram@1f0000 { reg = <0x001f0000 0x8000>; }; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2e-evm.dts b/arch/arm/boot/dts/keystone-k2e-evm.dts --- a/arch/arm/boot/dts/keystone-k2e-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2e-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -24,6 +24,13 @@ reusable; status = "okay"; }; + + dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 { + compatible = "ti,keystone-dsp-mem-pool"; + reg = <0x00000008 0x20000000 0x00000000 0x10000000>; + no-map; + status = "okay"; + }; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2g.dtsi b/arch/arm/boot/dts/keystone-k2g.dtsi --- a/arch/arm/boot/dts/keystone-k2g.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2g.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -2,7 +2,7 @@ /* * Device Tree Source for K2G SOC * - * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include @@ -102,6 +102,11 @@ #address-cells = <1>; #size-cells = <1>; + mpm-sram@0 { + compatible = "ti,keystone-dsp-msm-ram"; + reg = <0x0 0x80000>; + }; + bm-sram@f7000 { reg = <0x000f7000 0x8000>; }; @@ -283,6 +288,14 @@ }; }; + clock_event: timer@2210000 { + compatible = "ti,k2g-timer"; + reg = <0x02210000 0x50>; + interrupts = , + ; + clocks = <&k2g_clks 0x1e 0>; + }; + gpio0: gpio@2603000 { compatible = "ti,k2g-gpio", "ti,keystone-gpio"; reg = <0x02603000 0x100>; @@ -639,6 +652,211 @@ status = "disabled"; bus_freq = <2500000>; }; + + pruss0: pruss@20a80000 { + compatible = "ti,k2g-pruss"; + reg = <0x20a80000 0x40000>; + power-domains = <&k2g_pds 0x0014>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x20a80000 0x40000>; + dma-ranges; + dma-coherent; + + pruss0_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", "shrdram2"; + }; + + pruss0_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss0_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k2g_clks 0x14 5>, /* icss_iep_clk */ + <&k2g_clks 0x14 0>; /* icss_vclk_clk */ + }; + }; + }; + + pruss0_iep: iep@2e000 { + compatible = "ti,am5728-icss-iep"; + reg = <0x2e000 0x31c>; + clocks = <&pruss0_iepclk_mux>; + }; + + pruss0_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; + + pruss0_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x70>; + }; + + pruss0_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", + "host_intr6", "host_intr7"; + ti,irqs-reserved = /bits/ 8 <0x20>; /* BIT(5) */ + }; + + pru0_0: pru@34000 { + compatible = "ti,k2g-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "k2g-pru0_0-fw"; + }; + + pru0_1: pru@38000 { + compatible = "ti,k2g-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "k2g-pru0_1-fw"; + }; + + pruss0_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x90>; + clocks = <&k2g_clks 0x0014 1>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <2500000>; + status = "disabled"; + }; + }; + + pruss1: pruss@20ac0000 { + compatible = "ti,k2g-pruss"; + reg = <0x20ac0000 0x40000>; + power-domains = <&k2g_pds 0x0015>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x20ac0000 0x40000>; + dma-ranges; + dma-coherent; + + pruss1_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", "shrdram2"; + }; + + pruss1_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pruss1_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k2g_clks 0x15 5>, /* icss_iep_clk */ + <&k2g_clks 0x15 0>; /* icss_vclk_clk */ + }; + }; + }; + + pruss1_iep: iep@2e000 { + compatible = "ti,am5728-icss-iep"; + reg = <0x2e000 0x31c>; + clocks = <&pruss1_iepclk_mux>; + }; + + pruss1_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; + + pruss1_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x70>; + }; + + pruss1_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", + "host_intr6", "host_intr7"; + ti,irqs-reserved = /bits/ 8 <0x20>; /* BIT(5) */ + }; + + pru1_0: pru@34000 { + compatible = "ti,k2g-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "k2g-pru1_0-fw"; + }; + + pru1_1: pru@38000 { + compatible = "ti,k2g-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "k2g-pru1_1-fw"; + }; + + pruss1_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x90>; + clocks = <&k2g_clks 0x0015 1>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <2500000>; + status = "disabled"; + }; + }; + #include "keystone-k2g-netcp.dtsi" }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2g-evm.dts b/arch/arm/boot/dts/keystone-k2g-evm.dts --- a/arch/arm/boot/dts/keystone-k2g-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2g-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -22,6 +22,13 @@ #size-cells = <2>; ranges; + dsp_common_mpm_memory: dsp-common-mpm-memory@81d000000 { + compatible = "ti,keystone-dsp-mem-pool"; + reg = <0x00000008 0x1d000000 0x00000000 0x2800000>; + no-map; + status = "okay"; + }; + dsp_common_memory: dsp-common-memory@81f800000 { compatible = "shared-dma-pool"; reg = <0x00000008 0x1f800000 0x00000000 0x800000>; @@ -46,6 +53,14 @@ regulator-always-on; }; + vcc1v8_ldo2_reg: fixedregulator-vcc1v8-ldo2 { + compatible = "regulator-fixed"; + regulator-name = "ldo2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + hdmi: connector { compatible = "hdmi-connector"; label = "hdmi"; @@ -58,6 +73,57 @@ }; }; }; + + aud_mclk: aud_mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12288000>; + }; + + sound0: sound@0 { + compatible = "simple-audio-card"; + simple-audio-card,name = "K2G-EVM"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack", + "Line", "Line In"; + simple-audio-card,routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "LINE1L", "Line In", + "LINE1R", "Line In"; + + simple-audio-card,dai-link@0 { + format = "i2s"; + bitclock-master = <&sound0_0_master>; + frame-master = <&sound0_0_master>; + sound0_0_master: cpu { + sound-dai = <&mcasp2>; + clocks = <&k2g_clks 0x6 1>; + system-clock-direction-out; + }; + + codec { + sound-dai = <&tlv320aic3106>; + clocks = <&aud_mclk>; + }; + }; + + simple-audio-card,dai-link@1 { + format = "i2s"; + bitclock-master = <&sound0_1_master>; + frame-master = <&sound0_1_master>; + sound0_1_master: cpu { + sound-dai = <&mcasp2>; + clocks = <&k2g_clks 0x6 1>; + system-clock-direction-out; + }; + + codec { + sound-dai = <&sii9022>; + clocks = <&aud_mclk>; + }; + }; + }; }; &k2g_pinctrl { @@ -214,6 +280,15 @@ K2G_CORE_IOPAD(0x10e8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssfid.dssfid */ >; }; + + mcasp2_pins: pinmux_mcasp2_pins { + pinctrl-single,pins = < + K2G_CORE_IOPAD(0x1234) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4) /* pr0_pru_gpo2.mcasp2_axr2 */ + K2G_CORE_IOPAD(0x1238) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4) /* pr0_pru_gpo3.mcasp2_axr3 */ + K2G_CORE_IOPAD(0x1254) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4) /* pr0_pru_gpo10.mcasp2_afsx */ + K2G_CORE_IOPAD(0x125c) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4) /* pr0_pru_gpo12.mcasp2_aclkx */ + >; + }; }; &uart0 { @@ -423,6 +498,10 @@ compatible = "sil,sii9022"; reg = <0x3b>; + sil,i2s-data-lanes = < 0 >; + clocks = <&aud_mclk>; + clock-names = "mclk"; + ports { #address-cells = <1>; #size-cells = <0>; @@ -444,6 +523,19 @@ }; }; }; + + tlv320aic3106: tlv320aic3106@1b { + #sound-dai-cells = <0>; + compatible = "ti,tlv320aic3106"; + reg = <0x1b>; + status = "okay"; + + /* Regulators */ + AVDD-supply = <&vcc3v3_dcin_reg>; + IOVDD-supply = <&vcc3v3_dcin_reg>; + DRVDD-supply = <&vcc3v3_dcin_reg>; + DVDD-supply = <&vcc1v8_ldo2_reg>; + }; }; &dss { @@ -458,3 +550,30 @@ }; }; }; + +&k2g_clks { + /* on the board 22.5792MHz is connected to AUDOSC_IN */ + assigned-clocks = <&k2g_clks 0x4c 2>; + assigned-clock-rates = <22579200>; +}; + +&mcasp2 { + #sound-dai-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&mcasp2_pins>; + + assigned-clocks = <&k2g_clks 0x6 1>; + assigned-clock-parents = <&k2g_clks 0x6 2>; + + status = "okay"; + + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + /* 6 serializer */ + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 1 2 0 0 // AXR2: TX, AXR3: rx + >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2g-ice.dts b/arch/arm/boot/dts/keystone-k2g-ice.dts --- a/arch/arm/boot/dts/keystone-k2g-ice.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2g-ice.dts 2022-01-06 12:45:53.806318073 -0500 @@ -23,6 +23,13 @@ #size-cells = <2>; ranges; + dsp_common_mpm_memory: dsp-common-mpm-memory@81d000000 { + compatible = "ti,keystone-dsp-mem-pool"; + reg = <0x00000008 0x1d000000 0x00000000 0x2800000>; + no-map; + status = "okay"; + }; + dsp_common_memory: dsp-common-memory@81f800000 { compatible = "shared-dma-pool"; reg = <0x00000008 0x1f800000 0x00000000 0x800000>; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2hk.dtsi b/arch/arm/boot/dts/keystone-k2hk.dtsi --- a/arch/arm/boot/dts/keystone-k2hk.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2hk.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -64,6 +64,11 @@ #address-cells = <1>; #size-cells = <1>; + mpm-sram@0 { + compatible = "ti,keystone-dsp-msm-ram"; + reg = <0x0 0x80000>; + }; + bm-sram@5f0000 { reg = <0x5f0000 0x8000>; }; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2hk-evm.dts b/arch/arm/boot/dts/keystone-k2hk-evm.dts --- a/arch/arm/boot/dts/keystone-k2hk-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2hk-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -24,6 +24,13 @@ reusable; status = "okay"; }; + + dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 { + compatible = "ti,keystone-dsp-mem-pool"; + reg = <0x00000008 0x20000000 0x00000000 0x10000000>; + no-map; + status = "okay"; + }; }; leds { diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2l.dtsi b/arch/arm/boot/dts/keystone-k2l.dtsi --- a/arch/arm/boot/dts/keystone-k2l.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2l.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -262,6 +262,11 @@ #address-cells = <1>; #size-cells = <1>; + mpm-sram@0 { + compatible = "ti,keystone-dsp-msm-ram"; + reg = <0x0 0x80000>; + }; + bm-sram@1f8000 { reg = <0x001f8000 0x8000>; }; diff -Naur --no-dereference a/arch/arm/boot/dts/keystone-k2l-evm.dts b/arch/arm/boot/dts/keystone-k2l-evm.dts --- a/arch/arm/boot/dts/keystone-k2l-evm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/keystone-k2l-evm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -24,6 +24,13 @@ reusable; status = "okay"; }; + + dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 { + compatible = "ti,keystone-dsp-mem-pool"; + reg = <0x00000008 0x20000000 0x00000000 0x10000000>; + no-map; + status = "okay"; + }; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile --- a/arch/arm/boot/dts/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/Makefile 2022-01-06 12:45:53.806318073 -0500 @@ -1,4 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 + +ifeq ($(CONFIG_OF_OVERLAY),y) +DTC_FLAGS += -@ +endif + dtb-$(CONFIG_ARCH_ALPINE) += \ alpine-db.dtb dtb-$(CONFIG_MACH_ARTPEC6) += \ @@ -794,16 +799,32 @@ am335x-base0033.dtb \ am335x-bone.dtb \ am335x-boneblack.dtb \ + am335x-sancloud-bbe-extended-wifi-uboot-univ.dtb \ + am335x-sancloud-bbe-lite-uboot-univ.dtb \ + am335x-sancloud-bbe-uboot-univ.dtb \ + am335x-bonegreen-wireless-uboot-univ.dtb \ + am335x-boneblack-uboot-univ.dtb \ + am335x-bone-uboot-univ.dtb \ + am335x-sancloud-bbe-extended-wifi-uboot.dtb \ + am335x-sancloud-bbe-lite-uboot.dtb \ + am335x-sancloud-bbe-uboot.dtb \ + am335x-boneblack-uboot.dtb \ + am335x-sancloud-bbe-extended-wifi.dtb \ + am335x-sancloud-bbe-lite.dtb \ + am335x-bonegreen-gateway.dtb \ am335x-boneblack-wireless.dtb \ + am335x-boneblack-pruswuart.dtb \ am335x-boneblue.dtb \ am335x-bonegreen.dtb \ am335x-bonegreen-wireless.dtb \ + am335x-boneblack-pps.dtb \ am335x-chiliboard.dtb \ am335x-cm-t335.dtb \ am335x-evm.dtb \ am335x-evmsk.dtb \ am335x-guardian.dtb \ am335x-icev2.dtb \ + am335x-icev2-prueth.dtb \ am335x-lxm.dtb \ am335x-moxa-uc-2101.dtb \ am335x-moxa-uc-8100-me-t.dtb \ @@ -836,8 +857,10 @@ omap4-var-stk-om44.dtb dtb-$(CONFIG_SOC_AM43XX) += \ am43x-epos-evm.dtb \ + am43x-epos-evm-hdmi.dtb \ am437x-cm-t43.dtb \ am437x-gp-evm.dtb \ + am437x-gp-evm-hdmi.dtb \ am437x-idk-evm.dtb \ am437x-sbc-t43.dtb \ am437x-sk-evm.dtb @@ -1408,3 +1431,8 @@ aspeed-bmc-opp-zaius.dtb \ aspeed-bmc-portwell-neptune.dtb \ aspeed-bmc-quanta-q71l.dtb + +targets += dtbs dtbs_install +targets += $(dtb-y) + +subdir-y := overlays diff -Naur --no-dereference a/arch/arm/boot/dts/motorola-mapphone-common.dtsi b/arch/arm/boot/dts/motorola-mapphone-common.dtsi --- a/arch/arm/boot/dts/motorola-mapphone-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/motorola-mapphone-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -113,32 +113,9 @@ enable-active-high; }; - gpio_keys { - compatible = "gpio-keys"; - - volume_down { - label = "Volume Down"; - gpios = <&gpio5 26 GPIO_ACTIVE_LOW>; /* gpio154 */ - linux,code = ; - linux,can-disable; - /* Value above 7.95ms for no GPIO hardware debounce */ - debounce-interval = <10>; - }; - - slider { - label = "Keypad Slide"; - gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>; /* gpio122 */ - linux,input-type = ; - linux,code = ; - linux,can-disable; - /* Value above 7.95ms for no GPIO hardware debounce */ - debounce-interval = <10>; - }; - }; - soundcard { compatible = "audio-graph-card"; - label = "Droid 4 Audio"; + label = "Mapphone Audio"; widgets = "Speaker", "Earpiece", @@ -282,80 +259,6 @@ }; }; -&keypad { - keypad,num-rows = <8>; - keypad,num-columns = <8>; - linux,keymap = < - - /* Row 1 */ - MATRIX_KEY(0, 2, KEY_1) - MATRIX_KEY(0, 6, KEY_2) - MATRIX_KEY(2, 3, KEY_3) - MATRIX_KEY(0, 7, KEY_4) - MATRIX_KEY(0, 4, KEY_5) - MATRIX_KEY(5, 5, KEY_6) - MATRIX_KEY(0, 1, KEY_7) - MATRIX_KEY(0, 5, KEY_8) - MATRIX_KEY(0, 0, KEY_9) - MATRIX_KEY(1, 6, KEY_0) - - /* Row 2 */ - MATRIX_KEY(3, 4, KEY_APOSTROPHE) - MATRIX_KEY(7, 6, KEY_Q) - MATRIX_KEY(7, 7, KEY_W) - MATRIX_KEY(7, 2, KEY_E) - MATRIX_KEY(1, 0, KEY_R) - MATRIX_KEY(4, 4, KEY_T) - MATRIX_KEY(1, 2, KEY_Y) - MATRIX_KEY(6, 7, KEY_U) - MATRIX_KEY(2, 2, KEY_I) - MATRIX_KEY(5, 6, KEY_O) - MATRIX_KEY(3, 7, KEY_P) - MATRIX_KEY(6, 5, KEY_BACKSPACE) - - /* Row 3 */ - MATRIX_KEY(5, 4, KEY_TAB) - MATRIX_KEY(5, 7, KEY_A) - MATRIX_KEY(2, 7, KEY_S) - MATRIX_KEY(7, 0, KEY_D) - MATRIX_KEY(2, 6, KEY_F) - MATRIX_KEY(6, 2, KEY_G) - MATRIX_KEY(6, 6, KEY_H) - MATRIX_KEY(1, 4, KEY_J) - MATRIX_KEY(3, 1, KEY_K) - MATRIX_KEY(2, 1, KEY_L) - MATRIX_KEY(4, 6, KEY_ENTER) - - /* Row 4 */ - MATRIX_KEY(3, 6, KEY_LEFTSHIFT) /* KEY_CAPSLOCK */ - MATRIX_KEY(6, 1, KEY_Z) - MATRIX_KEY(7, 4, KEY_X) - MATRIX_KEY(5, 1, KEY_C) - MATRIX_KEY(1, 7, KEY_V) - MATRIX_KEY(2, 4, KEY_B) - MATRIX_KEY(4, 1, KEY_N) - MATRIX_KEY(1, 1, KEY_M) - MATRIX_KEY(3, 5, KEY_COMMA) - MATRIX_KEY(5, 2, KEY_DOT) - MATRIX_KEY(6, 3, KEY_UP) - MATRIX_KEY(7, 3, KEY_OK) - - /* Row 5 */ - MATRIX_KEY(2, 5, KEY_LEFTCTRL) /* KEY_LEFTSHIFT */ - MATRIX_KEY(4, 5, KEY_LEFTALT) /* SYM */ - MATRIX_KEY(6, 0, KEY_MINUS) - MATRIX_KEY(4, 7, KEY_EQUAL) - MATRIX_KEY(1, 5, KEY_SPACE) - MATRIX_KEY(3, 2, KEY_SLASH) - MATRIX_KEY(4, 3, KEY_LEFT) - MATRIX_KEY(5, 3, KEY_DOWN) - MATRIX_KEY(3, 3, KEY_RIGHT) - - /* Side buttons, KEY_VOLUMEDOWN and KEY_PWER are on CPCAP? */ - MATRIX_KEY(5, 0, KEY_VOLUMEUP) - >; -}; - &mmc1 { vmmc-supply = <&vwlan2>; bus-width = <4>; @@ -395,34 +298,6 @@ }; }; -&i2c1 { - led-controller@38 { - compatible = "ti,lm3532"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0x38>; - - enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>; - - ramp-up-us = <1024>; - ramp-down-us = <8193>; - - backlight_led: led@0 { - reg = <0>; - led-sources = <2>; - ti,led-mode = <0>; - label = ":backlight"; - }; - - led@1 { - reg = <1>; - led-sources = <1>; - ti,led-mode = <0>; - label = ":kbd_backlight"; - }; - }; -}; - &i2c2 { touchscreen@4a { compatible = "atmel,maxtouch"; @@ -796,20 +671,6 @@ "0", "0", "-1"; }; - - lis3dh: accelerometer@18 { - compatible = "st,lis3dh-accel"; - reg = <0x18>; - - vdd-supply = <&vhvio>; - - interrupt-parent = <&gpio2>; - interrupts = <2 IRQ_TYPE_EDGE_BOTH>; /* gpio34 */ - - rotation-matrix = "0", "-1", "0", - "1", "0", "0", - "0", "0", "1"; - }; }; &mcbsp2 { diff -Naur --no-dereference a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi --- a/arch/arm/boot/dts/omap2420.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap2420.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -192,16 +192,15 @@ compatible = "ti,omap2-mailbox"; reg = <0x48094000 0x200>; interrupts = <26>, <34>; - interrupt-names = "dsp", "iva"; ti,hwmods = "mailbox"; #mbox-cells = <1>; ti,mbox-num-users = <4>; ti,mbox-num-fifos = <6>; - mbox_dsp: dsp { + mbox_dsp: mbox-dsp { ti,mbox-tx = <0 0 0>; ti,mbox-rx = <1 0 0>; }; - mbox_iva: iva { + mbox_iva: mbox-iva { ti,mbox-tx = <2 1 3>; ti,mbox-rx = <3 1 3>; }; diff -Naur --no-dereference a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi --- a/arch/arm/boot/dts/omap2430.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap2430.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -284,7 +284,7 @@ #mbox-cells = <1>; ti,mbox-num-users = <4>; ti,mbox-num-fifos = <6>; - mbox_dsp: dsp { + mbox_dsp: mbox-dsp { ti,mbox-tx = <0 0 0>; ti,mbox-rx = <1 0 0>; }; diff -Naur --no-dereference a/arch/arm/boot/dts/omap2430-sdp.dts b/arch/arm/boot/dts/omap2430-sdp.dts --- a/arch/arm/boot/dts/omap2430-sdp.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap2430-sdp.dts 2022-01-06 12:45:53.806318073 -0500 @@ -43,8 +43,8 @@ gpmc,sync-clk-ps = <0>; gpmc,mux-add-data = <2>; gpmc,device-width = <1>; - gpmc,cycle2cycle-samecsen = <1>; - gpmc,cycle2cycle-diffcsen = <1>; + gpmc,cycle2cycle-samecsen; + gpmc,cycle2cycle-diffcsen; gpmc,cs-on-ns = <6>; gpmc,cs-rd-off-ns = <187>; gpmc,cs-wr-off-ns = <187>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts --- a/arch/arm/boot/dts/omap3-beagle-xm.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap3-beagle-xm.dts 2022-01-06 12:45:53.806318073 -0500 @@ -34,26 +34,26 @@ clock-frequency = <26000000>; }; - leds { + led-controller-1 { compatible = "gpio-leds"; - heartbeat { + led-1 { label = "beagleboard::usr0"; gpios = <&gpio5 22 GPIO_ACTIVE_HIGH>; /* 150 -> D6 LED */ linux,default-trigger = "heartbeat"; }; - mmc { + led-2 { label = "beagleboard::usr1"; gpios = <&gpio5 21 GPIO_ACTIVE_HIGH>; /* 149 -> D7 LED */ linux,default-trigger = "mmc0"; }; }; - pwmleds { + led-controller-2 { compatible = "pwm-leds"; - pmu_stat { + led-3 { label = "beagleboard::pmu_stat"; pwms = <&twl_pwmled 1 7812500>; max-brightness = <127>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap3-devkit8000-common.dtsi b/arch/arm/boot/dts/omap3-devkit8000-common.dtsi --- a/arch/arm/boot/dts/omap3-devkit8000-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap3-devkit8000-common.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -267,8 +267,8 @@ gpmc,mux-add-data = <0>; gpmc,device-width = <1>; gpmc,wait-pin = <0>; - gpmc,cycle2cycle-samecsen = <1>; - gpmc,cycle2cycle-diffcsen = <1>; + gpmc,cycle2cycle-samecsen; + gpmc,cycle2cycle-diffcsen; gpmc,cs-on-ns = <6>; gpmc,cs-rd-off-ns = <180>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi --- a/arch/arm/boot/dts/omap3.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap3.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -440,7 +440,7 @@ #mbox-cells = <1>; ti,mbox-num-users = <2>; ti,mbox-num-fifos = <2>; - mbox_dsp: dsp { + mbox_dsp: mbox-dsp { ti,mbox-tx = <0 0 0>; ti,mbox-rx = <1 0 0>; }; diff -Naur --no-dereference a/arch/arm/boot/dts/omap3-gta04.dtsi b/arch/arm/boot/dts/omap3-gta04.dtsi --- a/arch/arm/boot/dts/omap3-gta04.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap3-gta04.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -488,8 +488,8 @@ }; twl_power: power { - compatible = "ti,twl4030-power"; - ti,use_poweroff; + compatible = "ti,twl4030-power-idle"; + ti,system-power-controller; }; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/omap3-overo-base.dtsi b/arch/arm/boot/dts/omap3-overo-base.dtsi --- a/arch/arm/boot/dts/omap3-overo-base.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap3-overo-base.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -14,10 +14,10 @@ reg = <0 0>; }; - pwmleds { + led-controller { compatible = "pwm-leds"; - overo { + led-1 { label = "overo:blue:COM"; pwms = <&twl_pwmled 1 7812500>; max-brightness = <127>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap3-sb-t35.dtsi b/arch/arm/boot/dts/omap3-sb-t35.dtsi --- a/arch/arm/boot/dts/omap3-sb-t35.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap3-sb-t35.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -108,8 +108,8 @@ reg = <4 0 0xff>; bank-width = <2>; gpmc,device-width = <1>; - gpmc,cycle2cycle-samecsen = <1>; - gpmc,cycle2cycle-diffcsen = <1>; + gpmc,cycle2cycle-samecsen; + gpmc,cycle2cycle-diffcsen; gpmc,cs-on-ns = <5>; gpmc,cs-rd-off-ns = <150>; gpmc,cs-wr-off-ns = <150>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap4-droid4-xt894.dts b/arch/arm/boot/dts/omap4-droid4-xt894.dts --- a/arch/arm/boot/dts/omap4-droid4-xt894.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap4-droid4-xt894.dts 2022-01-06 12:45:53.810318090 -0500 @@ -4,6 +4,149 @@ #include "motorola-mapphone-common.dtsi" / { + gpio_keys { + compatible = "gpio-keys"; + + volume_down { + label = "Volume Down"; + gpios = <&gpio5 26 GPIO_ACTIVE_LOW>; /* gpio154 */ + linux,code = ; + linux,can-disable; + /* Value above 7.95ms for no GPIO hardware debounce */ + debounce-interval = <10>; + }; + + slider { + label = "Keypad Slide"; + gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>; /* gpio122 */ + linux,input-type = ; + linux,code = ; + linux,can-disable; + /* Value above 7.95ms for no GPIO hardware debounce */ + debounce-interval = <10>; + }; + }; +}; + +/ { model = "Motorola Droid 4 XT894"; compatible = "motorola,droid4", "ti,omap4430", "ti,omap4"; }; + +&keypad { + keypad,num-rows = <8>; + keypad,num-columns = <8>; + linux,keymap = < + + /* Row 1 */ + MATRIX_KEY(0, 2, KEY_1) + MATRIX_KEY(0, 6, KEY_2) + MATRIX_KEY(2, 3, KEY_3) + MATRIX_KEY(0, 7, KEY_4) + MATRIX_KEY(0, 4, KEY_5) + MATRIX_KEY(5, 5, KEY_6) + MATRIX_KEY(0, 1, KEY_7) + MATRIX_KEY(0, 5, KEY_8) + MATRIX_KEY(0, 0, KEY_9) + MATRIX_KEY(1, 6, KEY_0) + + /* Row 2 */ + MATRIX_KEY(3, 4, KEY_APOSTROPHE) + MATRIX_KEY(7, 6, KEY_Q) + MATRIX_KEY(7, 7, KEY_W) + MATRIX_KEY(7, 2, KEY_E) + MATRIX_KEY(1, 0, KEY_R) + MATRIX_KEY(4, 4, KEY_T) + MATRIX_KEY(1, 2, KEY_Y) + MATRIX_KEY(6, 7, KEY_U) + MATRIX_KEY(2, 2, KEY_I) + MATRIX_KEY(5, 6, KEY_O) + MATRIX_KEY(3, 7, KEY_P) + MATRIX_KEY(6, 5, KEY_BACKSPACE) + + /* Row 3 */ + MATRIX_KEY(5, 4, KEY_TAB) + MATRIX_KEY(5, 7, KEY_A) + MATRIX_KEY(2, 7, KEY_S) + MATRIX_KEY(7, 0, KEY_D) + MATRIX_KEY(2, 6, KEY_F) + MATRIX_KEY(6, 2, KEY_G) + MATRIX_KEY(6, 6, KEY_H) + MATRIX_KEY(1, 4, KEY_J) + MATRIX_KEY(3, 1, KEY_K) + MATRIX_KEY(2, 1, KEY_L) + MATRIX_KEY(4, 6, KEY_ENTER) + + /* Row 4 */ + MATRIX_KEY(3, 6, KEY_LEFTSHIFT) /* KEY_CAPSLOCK */ + MATRIX_KEY(6, 1, KEY_Z) + MATRIX_KEY(7, 4, KEY_X) + MATRIX_KEY(5, 1, KEY_C) + MATRIX_KEY(1, 7, KEY_V) + MATRIX_KEY(2, 4, KEY_B) + MATRIX_KEY(4, 1, KEY_N) + MATRIX_KEY(1, 1, KEY_M) + MATRIX_KEY(3, 5, KEY_COMMA) + MATRIX_KEY(5, 2, KEY_DOT) + MATRIX_KEY(6, 3, KEY_UP) + MATRIX_KEY(7, 3, KEY_OK) + + /* Row 5 */ + MATRIX_KEY(2, 5, KEY_LEFTCTRL) /* KEY_LEFTSHIFT */ + MATRIX_KEY(4, 5, KEY_LEFTALT) /* SYM */ + MATRIX_KEY(6, 0, KEY_MINUS) + MATRIX_KEY(4, 7, KEY_EQUAL) + MATRIX_KEY(1, 5, KEY_SPACE) + MATRIX_KEY(3, 2, KEY_SLASH) + MATRIX_KEY(4, 3, KEY_LEFT) + MATRIX_KEY(5, 3, KEY_DOWN) + MATRIX_KEY(3, 3, KEY_RIGHT) + + /* Side buttons, KEY_VOLUMEDOWN and KEY_PWER are on CPCAP? */ + MATRIX_KEY(5, 0, KEY_VOLUMEUP) + >; +}; + +&i2c1 { + led-controller@38 { + compatible = "ti,lm3532"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x38>; + + enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>; + + ramp-up-us = <1024>; + ramp-down-us = <8193>; + + backlight_led: led@0 { + reg = <0>; + led-sources = <2>; + ti,led-mode = <0>; + label = ":backlight"; + }; + + led@1 { + reg = <1>; + led-sources = <1>; + ti,led-mode = <0>; + label = ":kbd_backlight"; + }; + }; +}; + +&i2c4 { + lis3dh: accelerometer@18 { + compatible = "st,lis3dh-accel"; + reg = <0x18>; + + vdd-supply = <&vhvio>; + + interrupt-parent = <&gpio2>; + interrupts = <2 IRQ_TYPE_EDGE_BOTH>; /* gpio34 */ + + rotation-matrix = "0", "-1", "0", + "1", "0", "0", + "0", "0", "1"; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/omap4-droid-bionic-xt875.dts b/arch/arm/boot/dts/omap4-droid-bionic-xt875.dts --- a/arch/arm/boot/dts/omap4-droid-bionic-xt875.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap4-droid-bionic-xt875.dts 2022-01-06 12:45:53.810318090 -0500 @@ -7,3 +7,49 @@ model = "Motorola Droid Bionic XT875"; compatible = "motorola,droid-bionic", "ti,omap4430", "ti,omap4"; }; + +&keypad { + keypad,num-rows = <8>; + keypad,num-columns = <8>; + linux,keymap = < + MATRIX_KEY(5, 0, KEY_VOLUMEUP) + MATRIX_KEY(3, 0, KEY_VOLUMEDOWN) + >; +}; + +&i2c1 { + led-controller@38 { + compatible = "ti,lm3532"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x38>; + + enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>; + + ramp-up-us = <1024>; + ramp-down-us = <8193>; + + backlight_led: led@0 { + reg = <0>; + led-sources = <2>; + ti,led-mode = <0>; + label = ":backlight"; + }; + }; +}; + +&i2c4 { + kxtf9: accelerometer@f { + compatible = "kionix,kxtf9"; + reg = <0x0f>; + + vdd-supply = <&vhvio>; + + interrupt-parent = <&gpio2>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + + rotation-matrix = "0", "-1", "0", + "1", "0", "0", + "0", "0", "1"; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/omap4-kc1.dts b/arch/arm/boot/dts/omap4-kc1.dts --- a/arch/arm/boot/dts/omap4-kc1.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap4-kc1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -15,16 +15,16 @@ reg = <0x80000000 0x20000000>; /* 512 MB */ }; - pwmleds { + led-controller { compatible = "pwm-leds"; - green { + led-1 { label = "green"; pwms = <&twl_pwm 0 7812500>; max-brightness = <127>; }; - orange { + led-2 { label = "orange"; pwms = <&twl_pwm 1 7812500>; max-brightness = <127>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts --- a/arch/arm/boot/dts/omap4-panda-es.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap4-panda-es.dts 2022-01-06 12:45:53.810318090 -0500 @@ -49,6 +49,22 @@ OMAP4_IOPAD(0x0fc, PIN_INPUT_PULLUP | MUX_MODE3) /* gpio_113 */ >; }; + + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x06c, PIN_OUTPUT | MUX_MODE3) /* gpmc_a22.gpio_46 - BTEN */ + OMAP4_IOPAD(0x072, PIN_OUTPUT_PULLUP | MUX_MODE3) /* gpmc_a25.gpio_49 - BTWAKEUP */ + >; + }; + + uart2_pins: pinmux_uart2_pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x118, PIN_INPUT_PULLUP | MUX_MODE0) /* uart2_cts.uart2_cts - HCI */ + OMAP4_IOPAD(0x11a, PIN_OUTPUT | MUX_MODE0) /* uart2_rts.uart2_rts */ + OMAP4_IOPAD(0x11c, PIN_INPUT_PULLUP | MUX_MODE0) /* uart2_rx.uart2_rx */ + OMAP4_IOPAD(0x11e, PIN_OUTPUT | MUX_MODE0) /* uart2_tx.uart2_tx */ + >; + }; }; &led_wkgpio_pins { @@ -80,3 +96,19 @@ &gpio1_target { ti,no-reset-on-init; }; + +&wl12xx_gpio { + pinctrl-single,pins = < + OMAP4_IOPAD(0x066, PIN_OUTPUT | MUX_MODE3) /* gpmc_a19.gpio_43 */ + OMAP4_IOPAD(0x070, PIN_OUTPUT_PULLUP | MUX_MODE3) /* gpmc_a24.gpio_48 */ + >; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins &bt_pins>; + bluetooth: tiwi { + compatible = "ti,wl1271-st"; + enable-gpios = <&gpio2 14 GPIO_ACTIVE_HIGH>; /* GPIO_46 */ + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts --- a/arch/arm/boot/dts/omap4-sdp.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap4-sdp.dts 2022-01-06 12:45:53.810318090 -0500 @@ -45,58 +45,60 @@ regulator-boot-on; }; - leds { + led-controller-1 { compatible = "gpio-leds"; - debug0 { + + led-1 { label = "omap4:green:debug0"; gpios = <&gpio2 29 GPIO_ACTIVE_HIGH>; /* 61 */ }; - debug1 { + led-2 { label = "omap4:green:debug1"; gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>; /* 30 */ }; - debug2 { + led-3 { label = "omap4:green:debug2"; gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; /* 7 */ }; - debug3 { + led-4 { label = "omap4:green:debug3"; gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; /* 8 */ }; - debug4 { + led-5 { label = "omap4:green:debug4"; gpios = <&gpio2 18 GPIO_ACTIVE_HIGH>; /* 50 */ }; - user1 { + led-6 { label = "omap4:blue:user"; gpios = <&gpio6 9 GPIO_ACTIVE_HIGH>; /* 169 */ }; - user2 { + led-7 { label = "omap4:red:user"; gpios = <&gpio6 10 GPIO_ACTIVE_HIGH>; /* 170 */ }; - user3 { + led-8 { label = "omap4:green:user"; gpios = <&gpio5 11 GPIO_ACTIVE_HIGH>; /* 139 */ }; }; - pwmleds { + led-controller-2 { compatible = "pwm-leds"; - kpad { + + led-9 { label = "omap4::keypad"; pwms = <&twl_pwm 0 7812500>; max-brightness = <127>; }; - charging { + led-10 { label = "omap4:green:chrg"; pwms = <&twl_pwmled 0 7812500>; max-brightness = <255>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap5-board-common.dtsi b/arch/arm/boot/dts/omap5-board-common.dtsi --- a/arch/arm/boot/dts/omap5-board-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap5-board-common.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -141,7 +141,7 @@ &gpio8 { /* TI trees use GPIO instead of msecure, see also muxing */ - p234 { + msecure-hog { gpio-hog; gpios = <10 GPIO_ACTIVE_HIGH>; output-high; diff -Naur --no-dereference a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi --- a/arch/arm/boot/dts/omap5.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap5.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -522,6 +522,9 @@ clocks = <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 8>, <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 10>; clock-names = "fck", "sys_clk"; + + #address-cells = <1>; + #size-cells = <0>; }; }; @@ -554,6 +557,9 @@ clocks = <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 8>, <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 10>; clock-names = "fck", "sys_clk"; + + #address-cells = <1>; + #size-cells = <0>; }; }; diff -Naur --no-dereference a/arch/arm/boot/dts/omap5-l4.dtsi b/arch/arm/boot/dts/omap5-l4.dtsi --- a/arch/arm/boot/dts/omap5-l4.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap5-l4.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -194,7 +194,7 @@ #size-cells = <1>; utmi-mode = <2>; ranges = <0 0 0x20000>; - dwc3: dwc3@10000 { + dwc3: usb@10000 { compatible = "snps,dwc3"; reg = <0x10000 0x10000>; interrupts = , diff -Naur --no-dereference a/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi b/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi --- a/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -25,8 +25,8 @@ compatible = "smsc,lan9221", "smsc,lan9115"; bank-width = <2>; gpmc,device-width = <1>; - gpmc,cycle2cycle-samecsen = <1>; - gpmc,cycle2cycle-diffcsen = <1>; + gpmc,cycle2cycle-samecsen; + gpmc,cycle2cycle-diffcsen; gpmc,cs-on-ns = <5>; gpmc,cs-rd-off-ns = <150>; gpmc,cs-wr-off-ns = <150>; diff -Naur --no-dereference a/arch/arm/boot/dts/omap-zoom-common.dtsi b/arch/arm/boot/dts/omap-zoom-common.dtsi --- a/arch/arm/boot/dts/omap-zoom-common.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/boot/dts/omap-zoom-common.dtsi 2022-01-06 12:45:53.806318073 -0500 @@ -27,8 +27,8 @@ gpmc,mux-add-data = <0>; gpmc,device-width = <1>; gpmc,wait-pin = <1>; - gpmc,cycle2cycle-samecsen = <1>; - gpmc,cycle2cycle-diffcsen = <1>; + gpmc,cycle2cycle-samecsen; + gpmc,cycle2cycle-diffcsen; gpmc,cs-on-ns = <5>; gpmc,cs-rd-off-ns = <155>; gpmc,cs-wr-off-ns = <155>; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts b/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-ADC-00A0.kernel = __TIMESTAMP__; + }; +}; + +&tscadc { + status = "okay"; + adc { + ti,adc-channels = <0 1 2 3 4 5 6 7>; + ti,chan-step-avg = <16 16 16 16 16 16 16 16>; + ti,chan-step-opendelay = <0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98>; + ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts b/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BBBW-WL1835-00A0.kernel = __TIMESTAMP__; + }; +}; + +&{/} { + model = "TI AM335x BeagleBone Black Wireless"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; +}; + +&am33xx_pinmux { + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_txd0.gpio0_28 - BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txclk.gpio3_9 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) /* rmii1_refclk.gpio0_29 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_rxclk.gpio3_10 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + bluetooth { + compatible = "ti,wl1835-st"; + enable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio3 { + ls-buf-en-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts b/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BBGG-WL1835-00A0.kernel = __TIMESTAMP__; + wl1835_bt = "S3-texas-300000"; + }; +}; + +&{/} { + model = "SeeedStudio BeagleBone Green Gateway"; + compatible = "ti,am335x-bone-green-gateway", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + aliases { + rtc0 = &extrtc; + rtc1 = "/ocp/rtc@44e3e000"; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + //pinctrl-0 = <&user_leds_s0>; + pinctrl-0 = <&user_leds_s0 &bt_pins>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + led6 { + label = "beaglebone:green:usr4"; + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "netdev"; + default-state = "off"; + }; + + wl18xx_bt_en: led7 { + label = "wl18xx_bt_en"; + gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&usbhost_pins>; + + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a8.gpio1_24 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* WL_Active_LED / USR4 */ + >; + }; + + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txd0.gpio0_28 - BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + uart2_grove_pins: pinmux_uart2_grove_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x90c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + AM33XX_IOPAD(0x910, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + usbhost_pins: pinmux_usbhost_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_txd1.gpio0[21] */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txclk.gpio3_9 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) /* rmii1_refclk.gpio0_29 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_rxclk.gpio3_10 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_grove_pins>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + //pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + //bluetooth { + // compatible = "ti,wl1835-st"; + // enable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + //}; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + extrtc: rtc@68 { + compatible = "dallas,ds1340"; + reg = <0x68>; + }; +}; + +// (K16) gmii1_txd1.gpio0[21] +&gpio0 { + usb-reset-hog { + gpio-hog; + gpios = <21 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb_reset"; + }; +}; + +&gpio3 { + ls-buf-en-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +&usb1 { + #address-cells = <1>; + #size-cells = <0>; + + hub@1 { + compatible = "usb424,9512"; + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + ethernet: ethernet@1 { + compatible = "usb424,ec00"; + reg = <1>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts b/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BBGW-WL1835-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_12_pinmux { status = "disabled"; }; /* gpmc_ad12.gpio1_28 BT_EN */ + P8_12_pinmux { status = "disabled"; }; /* gpmc_ad12.mmc2_dat0 */ + P8_11_pinmux { status = "disabled"; }; /* gpmc_ad13.mmc2_dat1 */ + P8_16_pinmux { status = "disabled"; }; /* gpmc_ad14.mmc2_dat2 */ + P8_15_pinmux { status = "disabled"; }; /* gpmc_ad15.mmc2_dat3 */ + + P8_18_pinmux { status = "disabled"; }; /* gpmc_clk.mmc2_clk */ + + //Audio... + P9_28_pinmux { status = "disabled"; }; + P9_29_pinmux { status = "disabled"; }; + P9_31_pinmux { status = "disabled"; }; +}; + +&{/} { + model = "TI AM335x BeagleBone Green Wireless"; + compatible = "ti,am335x-bone-green-wireless", "ti,am335x-bone-green", "ti,am335x-bone", "ti,am33xx"; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio0 26 0>; + enable-active-high; + }; +}; + +&am33xx_pinmux { + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_ad12.gpio1_28 BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD11, PIN_INPUT_PULLDOWN, MUX_MODE7) /* gpmc_ad11.gpio0_27 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_csn0.gpio1_29 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <27 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + bluetooth { + compatible = "ti,wl1835-st"; + enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio1 { + ls-buf-en-hog { + gpio-hog; + gpios = <29 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +/* BT_AUD_OUT from wl1835 has to be pulled low when WL_EN is activated.*/ +/* in case it isn't, wilink8 ends up in one of the test modes that */ +/* intruces various issues (elp wkaeup timeouts etc.) */ +/* On the BBGW this pin is routed through the level shifter (U21) that */ +/* introduces a pullup on the line and wilink8 ends up in a bad state. */ +/* use a gpio hog to force this pin low. An alternative may be adding */ +/* an external pulldown on U21 pin 4. */ + +&gpio3 { + bt-aud-in-hog { + gpio-hog; + gpios = <16 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "MCASP0_AHCLKR"; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts b/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts --- a/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-4D5R-01-00A1.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P9_27_pinmux { status = "disabled"; }; /* lcd: gpio3_19 DISPEN */ + + P9_14_pinmux { status = "disabled"; }; /* pwm: ehrpwm1a PWM_BL */ + + P9_18_pinmux { status = "disabled"; }; /* i2c1_sda */ + P9_17_pinmux { status = "disabled"; }; /* i2c1_scl */ + P9_26_pinmux { status = "disabled"; }; /* touch interrupt on gpio0_14 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT, MUX_MODE6) /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + /*LCD enable */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT, MUX_MODE7) /* mcasp0_fsr.gpio3_19, OUTPUT | MODE7 LCD DISEN */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + bb_i2c1_pins: pinmux_bb_i2c1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | SLEWCTRL_SLOW | MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_IOPAD(0x95C, PIN_INPUT_PULLUP | SLEWCTRL_SLOW | MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; + + ar1021_pins: pinmux_ar1021_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE7) + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_i2c1_pins>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + ar1021: ar1021@4d { + status = "okay"; + compatible = "microchip,ar1021-i2c"; + reg = <0x4d>; + pinctrl-names = "default"; + pinctrl-0 = <&ar1021_pins>; + interrupt-parent = <&gpio0>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH>; + + touchscreen-offset-x=<250>; + touchscreen-offset-y=<300>; + + touchscreen-inverted-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + enable-gpios = <&gpio3 19 0>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + + display-timings { + native-mode = <&timing0>; + /* Settings for ThreeFive S9700RTWV35TR / LCD7 cape: */ + timing0: 800x480 { + clock-frequency = <30000000>; + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <40>; + hsync-len = <48>; + vback-porch = <30>; + vfront-porch = <13>; + vsync-len = <3>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts b/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-eMMC1-01-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_21_pinmux { status = "disabled"; }; /* mmc1_clk */ + P8_20_pinmux { status = "disabled"; }; /* mmc1_cmd */ + P8_25_pinmux { status = "disabled"; }; /* mmc1_dat0 */ + P8_24_pinmux { status = "disabled"; }; /* mmc1_dat1 */ + P8_05_pinmux { status = "disabled"; }; /* mmc1_dat2 */ + P8_06_pinmux { status = "disabled"; }; /* mmc1_dat3 */ + P8_23_pinmux { status = "disabled"; }; /* mmc1_dat4 */ + P8_22_pinmux { status = "disabled"; }; /* mmc1_dat5 */ + P8_03_pinmux { status = "disabled"; }; /* mmc1_dat6 */ + P8_04_pinmux { status = "disabled"; }; /* mmc1_dat7 */ +}; + +&am33xx_pinmux { + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN1, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN2, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD0, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD1, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD2, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD4, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD5, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; + non-removable; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts b/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts --- a/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-LCD4-01-00A1.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_12_pinmux { status = "disabled"; }; /* P9_12: gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */ + + P9_14_pinmux { status = "disabled"; }; /* P9_14: gpmc_a2.ehrpwm1a */ + + P9_27_pinmux { status = "disabled"; }; /* P9_27: mcasp0_fsr.gpio3_19 */ + + P8_45_pinmux { status = "disabled"; }; /* P8_45: lcd_data0.lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* P8_46: lcd_data1.lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* P8_43: lcd_data2.lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* P8_44: lcd_data3.lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* P8_41: lcd_data4.lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* P8_42: lcd_data5.lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* P8_39: lcd_data6.lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* P8_40: lcd_data7.lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* P8_37: lcd_data8.lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* P8_38: lcd_data9.lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* P8_36: lcd_data10.lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* P8_34: lcd_data11.lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* P8_35: lcd_data12.lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* P8_33: lcd_data13.lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* P8_31: lcd_data14.lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* P8_32: lcd_data15.lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* P8_27: lcd_vsync.lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* P8_29: lcd_hsync.lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* P8_28: lcd_pclk.lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* P8_30: lcd_ac_bias_en.lcd_ac_bias_en */ + + P9_15_pinmux { status = "disabled"; }; /* P9_15: gpmc_a0.gpio1_16 */ + P9_23_pinmux { status = "disabled"; }; /* P9_23: gpmc_a1.gpio1_17 */ + P9_16_pinmux { status = "disabled"; }; /* P9_16: gpmc_a3.gpio1_19 */ + P9_30_pinmux { status = "disabled"; }; /* P9_30: mcasp0_axr0.gpio3_16 */ + P9_24_pinmux { status = "disabled"; }; /* P9_24: uart1_txd.gpio0_15 */ +}; + +&am33xx_pinmux { + bb_lcd_led_pins: pinmux_bb_lcd_led_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_INPUT, MUX_MODE7) /* P9_12: gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */ + >; + }; + + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* P9_14: gpmc_a2.ehrpwm1a */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT_PULLUP, MUX_MODE7) /* P9_27: mcasp0_fsr.gpio3_19 */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + bb_lcd_keymap_pins: pinmux_bb_lcd_keymap_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A0, PIN_INPUT, MUX_MODE7) /* P9_15: gpmc_a0.gpio1_16 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A1, PIN_INPUT, MUX_MODE7) /* P9_23: gpmc_a1.gpio1_17 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A3, PIN_INPUT, MUX_MODE7) /* P9_16: gpmc_a3.gpio1_19 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE7) /* P9_30: mcasp0_axr0.gpio3_16 */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE7) /* P9_24: uart1_txd.gpio0_15 */ + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&tscadc { + status = "okay"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordinate-readouts = <5>; + ti,wire-config = <0x00 0x11 0x22 0x33>; + ti,charge-delay = <0x400>; + }; + + adc { + ti,adc-channels = <4 5 6 7>; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + display-timings { + native-mode = <&timing0>; + /* www.newhavendisplay.com/app_notes/OTA5180A.pdf */ + timing0: 480x272 { + clock-frequency = <9200000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <8>; + hback-porch = <47>; + hsync-len = <41>; + vback-porch = <2>; + vfront-porch = <3>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_led_pins>; + + led-ld0 { + label = "lcd:green:usr0"; + gpios = <&gpio1 28 0>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_keymap_pins>; + + button-1 { + debounce_interval = <50>; + linux,code = <105>; + label = "left"; + gpios = <&gpio1 16 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-2 { + debounce_interval = <50>; + linux,code = <106>; + label = "right"; + gpios = <&gpio1 17 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-3 { + debounce_interval = <50>; + linux,code = <103>; + label = "up"; + gpios = <&gpio1 19 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-4 { + debounce_interval = <50>; + linux,code = <108>; + label = "down"; + gpios = <&gpio3 16 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-5 { + debounce_interval = <50>; + linux,code = <28>; + label = "enter"; + gpios = <&gpio0 15 0x1>; + gpio-key,wakeup; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts b/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts --- a/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-NH7C-01-A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_15_pinmux { status = "disabled"; }; /* gpmc_ad15.lcd_data16 */ + P8_16_pinmux { status = "disabled"; }; /* gpmc_ad14.lcd_data17 */ + P8_11_pinmux { status = "disabled"; }; /* gpmc_ad13.lcd_data18 */ + P8_12_pinmux { status = "disabled"; }; /* gpmc_ad12.lcd_data19 */ + P8_17_pinmux { status = "disabled"; }; /* gpmc_ad11.lcd_data20 */ + P8_14_pinmux { status = "disabled"; }; /* gpmc_ad10.lcd_data21 */ + P8_13_pinmux { status = "disabled"; }; /* gpmc_ad9.lcd_data22 */ + P8_19_pinmux { status = "disabled"; }; /* gpmc_ad8.lcd_data23 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P8_18_pinmux { status = "disabled"; }; /* lcd: enable */ + + P9_14_pinmux { status = "disabled"; }; /* pwm: ehrpwm1a */ + + P9_27_pinmux { status = "disabled"; }; /* ft5336: gpio3_15 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* P9_14: gpmc_a2.ehrpwm1a */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + /*LCD enable */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_clk_mux0.gpio2_1 */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_OUTPUT, MUX_MODE1) /* P8_15: gpmc_ad15.lcd_data16 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_OUTPUT, MUX_MODE1) /* P8_16: gpmc_ad14.lcd_data17 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_OUTPUT, MUX_MODE1) /* P8_11: gpmc_ad13.lcd_data18 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_OUTPUT, MUX_MODE1) /* P8_12: gpmc_ad12.lcd_data19 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD11, PIN_OUTPUT, MUX_MODE1) /* P8_17: gpmc_ad11.lcd_data20 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_OUTPUT, MUX_MODE1) /* P8_14: gpmc_ad10.lcd_data21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD9, PIN_OUTPUT, MUX_MODE1) /* P8_13: gpmc_ad9.lcd_data22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD8, PIN_OUTPUT, MUX_MODE1) /* P8_19: gpmc_ad8.lcd_data23 */ + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + edt_ft5x06_pins: pinmux_edt_ft5x06_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mcasp0_fsr.gpio3_19 */ + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "crossed"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c2 { + status = "okay"; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + edt-ft5x06@38 { + status = "okay"; + compatible = "edt,edt-ft5406"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5x06_pins>; + interrupt-parent = <&gpio3>; + interrupts = <19 0>; + //reset-gpios = <&gpio3 14 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + //touchscreen-swapped-x-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + /* NHD-7.0-800480EF-ATXL# */ + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + enable-gpios = <&gpio2 1 0>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <32>; + fdd = <0x80>; + tft-alt-mode = <0>; + stn-565-mode = <0>; + mono-8bit-mode = <0>; + sync-edge = <0>; + sync-ctrl = <0>; + raster-order = <0>; + fifo-th = <0>; + }; + + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <45000000>; + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <40>; + hsync-len = <48>; + vback-porch = <29>; + vfront-porch = <13>; + vsync-len = <3>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts b/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-CAPE-DISP-CT4-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P9_28_pinmux { status = "disabled"; }; /* pwm: eCAP2_in_PWM2_out */ + + P9_29_pinmux { status = "disabled"; }; /* ft5336: gpio3_15 */ + P9_31_pinmux { status = "disabled"; }; /* ft5336: gpio3_14 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE4) /* mcasp0_ahclkr.eCAP2_in_PWM2_out */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + edt_ft5336_ts_pins: pinmux_edt_ft5336_ts_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mcasp0_fsx.gpio3_15 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mcasp0_aclkx.gpio3_14 */ + >; + }; +}; + +&epwmss2 { + status = "okay"; +}; + +&ecap2 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c2 { + status = "okay"; + + /* this is the configuration part */ + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + edt-ft5336@38 { + status = "okay"; + compatible = "edt,edt-ft5336", "edt,edt-ft5306", "edt,edt-ft5x06"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5336_ts_pins>; + interrupt-parent = <&gpio3>; + interrupts = <15 0>; + reset-gpios = <&gpio3 14 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <272>; + touchscreen-size-y = <480>; + touchscreen-swapped-x-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ecap2 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <50>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + tft-alt-mode = <0>; + stn-565-mode = <0>; + mono-8bit-mode = <0>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + /* ILI6480 */ + display-timings { + native-mode = <&timing0>; + timing0: 480x272 { + clock-frequency = <9000000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <5>; + hback-porch = <40>; + hsync-len = <1>; + vback-porch = <8>; + vfront-porch = <8>; + vsync-len = <1>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts b/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-HDMI-TDA998x-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_25_pinmux { status = "disabled"; }; /* mcasp0_ahclkx */ + P9_28_pinmux { status = "disabled"; }; /* mcasp0_axr2 */ + P9_29_pinmux { status = "disabled"; }; /* mcasp0_fsx */ + P9_31_pinmux { status = "disabled"; }; /* mcasp0_aclkx */ + P8_45_pinmux { status = "disabled"; }; /* lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd_data15 */ + P8_27_pinmux { status = "disabled"; }; /* lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd_ac_bias_en */ +}; + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLUP, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +&lcdc { + status = "okay"; + + /* If you want to get 24 bit RGB and 16 BGR mode instead of + * current 16 bit RGB and 24 BGR modes, set the propety + * below to "crossed" and uncomment the video-ports -property + * in tda19988 node. + */ + blue-and-red-wiring = "straight"; + + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + tda19988: tda19988@70 { + compatible = "nxp,tda998x"; + reg = <0x70>; + nxp,calib-gpios = <&gpio1 25 0>; + interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ + /* video-ports = <0x234501>; */ + + #sound-dai-cells = <0>; + audio-ports = < TDA998x_I2S 0x03>; + + ports { + port@0 { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; + }; +}; + +&mcasp0 { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + status = "okay"; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 1 0 + >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; + +&{/} { + clk_mcasp0_fixed: clk_mcasp0_fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk_mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "TI BeagleBone Black"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&dailink0_master>; + simple-audio-card,frame-master = <&dailink0_master>; + + dailink0_master: simple-audio-card,cpu { + sound-dai = <&mcasp0>; + clocks = <&clk_mcasp0>; + }; + + simple-audio-card,codec { + sound-dai = <&tda19988>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts b/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts --- a/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012,2019 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2015 Robert Nelson + * Copyright (C) 2015 Sebastian JegerÃ¥s + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BBORG_COMMS-00A2.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_24_pinmux { status = "disabled"; }; /* P9_24: uart1_txd.d_can1_rx */ + P9_26_pinmux { status = "disabled"; }; /* P9_26: uart1_rxd.d_can1_tx */ + P9_13_pinmux { status = "disabled"; }; /* P9_13: gpmc_wpn.uart4_txd_mux2 */ + P9_11_pinmux { status = "disabled"; }; /* P9_11: gpmc_wait0.uart4_rxd_mux2 */ +}; + +&am33xx_pinmux { + bborg_comms_can_pins: pinmux_comms_can_pins { + pinctrl-single,pins = < + 0x184 (PIN_INPUT_PULLUP | MUX_MODE2) /* P9_24: uart1_txd.d_can1_rx */ + 0x180 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* P9_26: uart1_rxd.d_can1_tx */ + >; + }; + + bborg_comms_rs485_pins: pinmux_comms_rs485_pins { + pinctrl-single,pins = < + 0x074 (PIN_OUTPUT | MUX_MODE6) /* P9_13: gpmc_wpn.uart4_txd_mux2 */ + 0x070 (PIN_INPUT | MUX_MODE6) /* P9_11: gpmc_wait0.uart4_rxd_mux2 */ + >; + }; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bborg_comms_can_pins>; +}; + +&uart4 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bborg_comms_rs485_pins>; + //rs485-rts-delay = <0 0>; + //rts-gpio = <&gpio3 19 1>; /* GPIO_ACTIVE_HIGH>; */ + //rs485-rts-active-high; + //linux,rs485-enabled-at-boot-time; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts b/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts --- a/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BBORG_FAN-A000.kernel = __TIMESTAMP__; + }; +}; + +/* From dra7.dtsi opp_nom-1000000000 */ +&cpu0_opp_table { + opp_slow-500000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <1060000 850000 1150000>, + <1060000 850000 1150000>; + opp-supported-hw = <0xFF 0x01>; + opp-suspend; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts b/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts --- a/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Robert Nelson + * Copyright (C) 2019 Amilcar Lucas + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BBORG_RELAY-00A2.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_41_pinmux { status = "disabled"; }; /* P9_41: gpmc_a0.gpio0_20 */ + P9_42_pinmux { status = "disabled"; }; /* P9_42: gpmc_a1.gpio0_07 */ + P9_30_pinmux { status = "disabled"; }; /* P9_30: gpmc_be1n.gpio3_16 */ + P9_27_pinmux { status = "disabled"; }; /* P9_27: mcasp0_fsr.gpio3_19 */ +}; + +&am33xx_pinmux { + bb_gpio_relay_pins: pinmux_bb_gpio_relay_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* P9_41: Relay1 */ + AM33XX_PADCONF(AM335X_PIN_ECAP0_IN_PWM0_OUT, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* P9_42: Relay2 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* P9_30: Relay3 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* P9_27: Relay4 */ + >; + }; +}; + +&{/} { + leds { + pinctrl-names = "default"; + pinctrl-0 = <&bb_gpio_relay_pins>; + + compatible = "gpio-leds"; + + jp@1 { + label = "relay-jp1"; + gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + jp@2 { + label = "relay-jp2"; + gpios = <&gpio0 07 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + jp@3 { + label = "relay-jp3"; + gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + jp@4 { + label = "relay-jp4"; + gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts b/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for SPI0 on connector pins P9.22 P9.21 P9.18 P9.17 + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-SPIDEV0-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* P9_17 (A16) spi0_cs0.spi0_cs0 */ + P9_18_pinmux { status = "disabled"; }; /* P9_18 (B16) spi0_d1.spi0_d1 */ + P9_21_pinmux { status = "disabled"; }; /* P9_21 (B17) spi0_d0.spi0_d0 */ + P9_22_pinmux { status = "disabled"; }; /* P9_22 (A17) spi0_sclk.spi0_sclk */ +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) /* P9_22 (A17) spi0_sclk.spi0_sclk */ + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) /* P9_21 (B17) spi0_d0.spi0_d0 */ + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) /* P9_18 (B16) spi0_d1.spi0_d1 */ + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) /* P9_17 (A16) spi0_cs0.spi0_cs0 */ + >; + }; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + /* + * Select the D0 pin as output and D1 as + * input. The default is D0 as input and + * D1 as output. + */ + //ti,pindir-d0-out-d1-in; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts b/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts --- a/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for SPI1 on connector pins P9.29 P9.31 P9.30 P9.28 + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-SPIDEV1-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_28_pinmux { status = "disabled"; }; /* P9_28 (C12) mcasp0_ahclkr.spi1_cs0 */ + P9_30_pinmux { status = "disabled"; }; /* P9_30 (D12) mcasp0_axr0.spi1_d1 */ + P9_29_pinmux { status = "disabled"; }; /* P9_29 (B13) mcasp0_fsx.spi1_d0 */ + P9_31_pinmux { status = "disabled"; }; /* P9_31 (A13) mcasp0_aclkx.spi1_sclk */ +}; + +&am33xx_pinmux { + bb_spi1_pins: pinmux_bb_spi1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT, MUX_MODE3) /* P9_31 (A13) mcasp0_aclkx.spi1_sclk */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT, MUX_MODE3) /* P9_29 (B13) mcasp0_fsx.spi1_d0 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE3) /* P9_30 (D12) mcasp0_axr0.spi1_d1 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_INPUT, MUX_MODE3) /* P9_28 (C12) mcasp0_ahclkr.spi1_cs0 */ + >; + }; +}; + +&spi1 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi1_pins>; + + /* + * Select the D0 pin as output and D1 as + * input. The default is D0 as input and + * D1 as output. + */ + //ti,pindir-d0-out-d1-in; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/BONE-ADC.dts b/arch/arm/boot/dts/overlays/BONE-ADC.dts --- a/arch/arm/boot/dts/overlays/BONE-ADC.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/BONE-ADC.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + * + * Virtual cape for Bone ADC + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BONE-ADC.kernel = __TIMESTAMP__; + }; +}; + +/* + * See these files for the phandles (&bone_*) and other bone bus nodes + * am335x-bbb-bone-buses.dtsi + */ +&bone_adc { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile --- a/arch/arm/boot/dts/overlays/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,30 @@ +# Overlays for the BeagleBone platform + +dtbo-$(CONFIG_ARCH_OMAP2PLUS) += \ + BB-ADC-00A0.dtbo \ + BB-BBBW-WL1835-00A0.dtbo \ + BB-BBGG-WL1835-00A0.dtbo \ + BB-BBGW-WL1835-00A0.dtbo \ + BB-BONE-4D5R-01-00A1.dtbo \ + BB-BONE-eMMC1-01-00A0.dtbo \ + BB-BONE-LCD4-01-00A1.dtbo \ + BB-BONE-NH7C-01-A0.dtbo \ + BB-CAPE-DISP-CT4-00A0.dtbo \ + BB-HDMI-TDA998x-00A0.dtbo \ + BB-SPIDEV0-00A0.dtbo \ + BB-SPIDEV1-00A0.dtbo \ + BBORG_COMMS-00A2.dtbo \ + BBORG_FAN-A000.dtbo \ + BBORG_RELAY-00A2.dtbo \ + BONE-ADC.dtbo \ + M-BB-BBG-00A0.dtbo \ + M-BB-BBGG-00A0.dtbo \ + PB-HACKADAY-2021.dtbo \ + PB-MIKROBUS-0.dtbo \ + PB-MIKROBUS-1.dtbo + +targets += dtbs dtbs_install +targets += $(dtbo-y) + +always-y := $(dtbo-y) +clean-files := *.dtbo diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts b/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts --- a/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + M-BB-BBG-00A0.kernel = __TIMESTAMP__; + }; +}; + +&{/} { + model = "TI AM335x BeagleBone Green"; + compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts b/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts --- a/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + M-BB-BBGG-00A0.kernel = __TIMESTAMP__; + }; +}; + +&{/} { + model = "SeeedStudio BeagleBone Green Gateway"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts b/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts --- a/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + PB-HACKADAY-2021.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P1_28_pinmux { status = "disabled"; }; + P1_26_pinmux { status = "disabled"; }; + P2_05_pinmux { status = "disabled"; }; + P2_07_pinmux { status = "disabled"; }; +}; + +&am33xx_pinmux { + i2c2_pins: pinmux-i2c2-pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D17) uart1_rtsn.I2C2_SCL */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D18) uart1_ctsn.I2C2_SDA */ + >; + }; + + grove_button_uart4_pins: pinmux_grove_button_uart4_pins { + pinctrl-single,pins = < + /* slot UART4 */ + AM33XX_IOPAD(0x0870, PIN_INPUT | MUX_MODE7) /* gpmc_wait0.gpio0_30 */ + AM33XX_IOPAD(0x0874, PIN_INPUT | MUX_MODE7) /* gpmc_wpn.gpio0_31 */ + >; + }; +}; + +&{/} { + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&grove_button_uart4_pins>; + + #address-cells = <1>; + #size-cells = <0>; + + grove_button_uart4_0@6 { + debounce_interval = <50>; + linux,code = <0x106>; + label = "grove:usr6"; + gpios = <&gpio0 30 0x0>; + gpio-key,wakeup; + autorepeat; + }; + + grove_button_uart4_1@7 { + debounce_interval = <50>; + linux,code = <0x107>; + label = "grove:usr7"; + gpios = <&gpio0 31 0x1>; + gpio-key,wakeup; + autorepeat; + }; + }; +}; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + jhd1802@3e { + compatible = "seeed-hd44780"; + reg = <0x3e>; + status = "okay"; + }; + + adxl345@53 { + compatible = "adi,adxl345"; + reg = <0x53>; + status = "okay"; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + #address-cells = <1>; + #size-cells = <0>; + + sht3x@45 { + compatible = "sensirion,sht3x"; + reg = <0x45>; + status = "okay"; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts b/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts --- a/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + PB-MIKROBUS-0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P2_01_pinmux { status = "disabled"; }; + P2_03_pinmux { status = "disabled"; }; + P2_05_pinmux { status = "disabled"; }; + P2_07_pinmux { status = "disabled"; }; + P2_09_pinmux { status = "disabled"; }; + P2_11_pinmux { status = "disabled"; }; + P1_12_pinmux { status = "disabled"; }; + P1_10_pinmux { status = "disabled"; }; + P1_08_pinmux { status = "disabled"; }; + P1_06_pinmux { status = "disabled"; }; + P1_04_pinmux { status = "disabled"; }; + P1_02_pinmux { status = "disabled"; }; +}; + +&{/} { + aliases { + mikrobus0 = "/mikrobus-0"; + }; + + mikrobus-0 { + compatible = "linux,mikrobus"; + status = "okay"; + pinctrl-names = "default", "pwm_default", "pwm_gpio", + "uart_default", "uart_gpio", "i2c_default", + "i2c_gpio", "spi_default", "spi_gpio"; + pinctrl-0 = < + &P2_03_gpio_input_pin + &P1_04_gpio_pin + &P1_02_gpio_pin + >; + pinctrl-1 = <&P2_01_pwm_pin>; + pinctrl-2 = <&P2_01_gpio_pin>; + pinctrl-3 = < + &P2_05_uart_pin + &P2_07_uart_pin + >; + pinctrl-4 = < + &P2_05_gpio_pin + &P2_07_gpio_pin + >; + pinctrl-5 = < + &P2_09_i2c_pin + &P2_11_i2c_pin + >; + pinctrl-6 = < + &P2_09_gpio_pin + &P2_11_gpio_pin + >; + pinctrl-7 = < + &P1_12_spi_pin + &P1_10_spi_pin + &P1_08_spi_sclk_pin + &P1_06_spi_cs_pin + >; + pinctrl-8 = < + &P1_12_gpio_pin + &P1_10_gpio_pin + &P1_08_gpio_pin + &P1_06_gpio_pin + >; + i2c-adapter = <&i2c1>; + spi-master = <0>; + spi-cs = <0 1>; + uart = <&uart4>; + pwms = <&ehrpwm1 0 500000 0>; + mikrobus-gpios = <&gpio1 18 0> , <&gpio0 23 0>, + <&gpio0 30 0> , <&gpio0 31 0>, + <&gpio0 15 0> , <&gpio0 14 0>, + <&gpio0 4 0> , <&gpio0 3 0>, + <&gpio0 2 0> , <&gpio0 5 0>, + <&gpio2 25 0> , <&gpio2 3 0>; + }; +}; + +&spi0 { + status = "okay"; + channel@0{ status = "disabled"; }; +}; + +&uart4 { + status = "okay"; + force-empty-serdev-controller; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts b/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts --- a/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + PB-MIKROBUS-1 = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P1_36_pinmux { status = "disabled"; }; + P1_34_pinmux { status = "disabled"; }; + P1_32_pinmux { status = "disabled"; }; + P1_30_pinmux { status = "disabled"; }; + P1_28_pinmux { status = "disabled"; }; + P1_26_pinmux { status = "disabled"; }; + P2_25_pinmux { status = "disabled"; }; + P2_27_pinmux { status = "disabled"; }; + P2_29_pinmux { status = "disabled"; }; + P2_31_pinmux { status = "disabled"; }; + P2_33_pinmux { status = "disabled"; }; + P2_35_pinmux { status = "disabled"; }; +}; + +&{/} { + aliases { + mikrobus1 = "/mikrobus-1"; + }; + + mikrobus-1 { + compatible = "linux,mikrobus"; + status = "okay"; + pinctrl-names = "default", "pwm_default", "pwm_gpio", + "uart_default", "uart_gpio", "i2c_default", + "i2c_gpio", "spi_default", "spi_gpio"; + pinctrl-0 = < + &P1_34_gpio_input_pin + &P2_33_gpio_pin + &P2_35_gpio_pin + >; + pinctrl-1 = <&P1_36_pwm_pin>; + pinctrl-2 = <&P1_36_gpio_pin>; + pinctrl-3 = < + &P1_32_uart_pin + &P1_30_uart_pin + >; + pinctrl-4 = < + &P1_32_gpio_pin + &P1_30_gpio_pin + >; + pinctrl-5 = < + &P1_26_i2c_pin + &P1_28_i2c_pin + >; + pinctrl-6 = < + &P1_26_gpio_pin + &P1_28_gpio_pin + >; + pinctrl-7 = < + &P2_25_spi_pin + &P2_27_spi_pin + &P2_29_spi_sclk_pin + &P2_31_spi_cs_pin + >; + pinctrl-8 = < + &P2_25_gpio_pin + &P2_27_gpio_pin + &P2_29_gpio_pin + &P2_31_gpio_pin + >; + i2c-adapter = <&i2c2>; + spi-master = <1>; + spi-cs = <1 2>; + uart = <&uart0>; + pwms = <&ehrpwm0 0 500000 0>; + mikrobus-gpios = <&gpio3 14 0> , <&gpio0 26 0>, + <&gpio1 10 0> , <&gpio1 11 0>, + <&gpio0 13 0> , <&gpio0 12 0>, + <&gpio1 9 0> , <&gpio1 8 0>, + <&gpio0 7 0> , <&gpio0 19 0>, + <&gpio1 13 0> , <&gpio2 22 0>; + }; +}; + +&spi1 { + status = "okay"; + channel@0{ status = "disabled"; }; + channel@1{ status = "disabled"; }; +}; + +&uart0 { + status = "okay"; + force-empty-serdev-controller; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/am571x-idk-touchscreen.dts b/arch/arm/boot/dts/ti/am571x-idk-touchscreen.dts --- a/arch/arm/boot/dts/ti/am571x-idk-touchscreen.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/am571x-idk-touchscreen.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include + +&touchscreen { + interrupt-parent = <&gpio5>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; +}; + +/* pruss1_eth conflicts with display, so we need to disable it */ +/ { + fragment@101 { + target-path = "/pruss1_eth"; + + __overlay__ { + + status = "disabled"; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/am572x-idk-touchscreen.dts b/arch/arm/boot/dts/ti/am572x-idk-touchscreen.dts --- a/arch/arm/boot/dts/ti/am572x-idk-touchscreen.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/am572x-idk-touchscreen.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include + +&touchscreen { + interrupt-parent = <&gpio3>; + interrupts = <14 IRQ_TYPE_EDGE_FALLING>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/am57xx-evm-common.dts b/arch/arm/boot/dts/ti/am57xx-evm-common.dts --- a/arch/arm/boot/dts/ti/am57xx-evm-common.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/am57xx-evm-common.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common DT overlay for AM57xx GP EVM boards + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + compatible = "ti,am5728-evm", "ti,am572x-beagle-x15", "ti,am5728", "ti,dra742", "ti,dra74", "ti,dra7"; + model = "TI AM5728 EVM"; + + aliases { + display0 = "/display"; + display1 = "/connector"; + }; + + gpio-keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + autorepeat; + + user1 { + gpios = <&gpio2 23 GPIO_ACTIVE_LOW>; + label = "Up"; + linux,code = ; + }; + + user2 { + gpios = <&gpio2 25 GPIO_ACTIVE_LOW>; + label = "Down"; + linux,code = ; + }; + + user3 { + gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; + label = "Left"; + linux,code = ; + }; + + user4 { + gpios = <&gpio2 24 GPIO_ACTIVE_LOW>; + label = "Right"; + linux,code = ; + }; + + user5 { + gpios = <&gpio2 20 GPIO_ACTIVE_LOW>; + label = "Home"; + linux,code = ; + }; + }; + + lcd0: display { + compatible = "osddisplays,osd070t1718-19ts", "panel-dpi"; + backlight = <&lcd_bl>; + enable-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; + label = "lcd"; + + port { + lcd_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; + + lcd_bl: backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 243 245 247 249 251 252 253 255>; + default-brightness-level = <8>; + pwms = <&ehrpwm1 0 50000 0>; + }; + + com_3v6: fixedregulator-com_3v6 { + compatible = "regulator-fixed"; + regulator-name = "com_3v6"; + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + vin-supply = <&evm_5v0>; + regulator-always-on; + regulator-boot-on; + }; + + vmmcwl_fixed: fixedregulator-mmcwl { + compatible = "regulator-fixed"; + regulator-name = "vmmcwl_fixed"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + gpio = <&gpio5 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + }; + }; +}; + +&ehrpwm1 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&i2c5 { + status = "okay"; + clock-frequency = <400000>; + #address-cells = <1>; + #size-cells = <0>; + + pixcir_ts@5c { + compatible = "pixcir,pixcir_tangoc"; + attb-gpio = <&gpio2 4 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&gpio2>; + interrupts = <4 0>; + reg = <0x5c>; + reset-gpio = <&gpio2 6 GPIO_ACTIVE_HIGH>; + touchscreen-size-x = <1024>; + touchscreen-size-y = <600>; + }; +}; + +&uart8 { + status = "okay"; +}; + +&dss { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpi_out: endpoint { + data-lines = <24>; + remote-endpoint = <&lcd_in>; + }; + }; + }; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&com_3v6>; + vqmmc-supply = <&vmmcwl_fixed>; + bus-width = <4>; + cap-power-off-card; + keep-power-in-suspend; + non-removable; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "hs", "sdr12", "sdr25", "sdr50"; + pinctrl-0 = <&mmc3_pins_default>; + pinctrl-1 = <&mmc3_pins_hs>; + pinctrl-2 = <&mmc3_pins_sdr12>; + pinctrl-3 = <&mmc3_pins_sdr25>; + pinctrl-4 = <&mmc3_pins_sdr50 &mmc3_iodelay_manual1_rev11_conf>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio5>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/am57xx-evm-reva3.dts b/arch/arm/boot/dts/ti/am57xx-evm-reva3.dts --- a/arch/arm/boot/dts/ti/am57xx-evm-reva3.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/am57xx-evm-reva3.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&mmc3 { + pinctrl-names = "default", "hs", "sdr12", "sdr25", "sdr50"; + pinctrl-0 = <&mmc3_pins_default>; + pinctrl-1 = <&mmc3_pins_hs>; + pinctrl-2 = <&mmc3_pins_sdr12>; + pinctrl-3 = <&mmc3_pins_sdr25>; + pinctrl-4 = <&mmc3_pins_sdr50 &mmc3_iodelay_manual1_rev20_conf>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/am57xx-idk-osd-lcd-common.dts b/arch/arm/boot/dts/ti/am57xx-idk-osd-lcd-common.dts --- a/arch/arm/boot/dts/ti/am57xx-idk-osd-lcd-common.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/am57xx-idk-osd-lcd-common.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + aliases { + display0 = "/display"; + display1 = "/connector"; + }; + + lcd_bl: backlight { + compatible = "pwm-backlight"; + pwms = <&ecap0 0 50000 1>; + brightness-levels = <0 51 53 56 62 75 101 152 255>; + default-brightness-level = <8>; + }; + }; + }; +}; + +&dsi_bridge { + status = "okay"; +}; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen: edt-ft5506@38 { + status = "okay"; + compatible = "edt,edt-ft5506", "edt,edt-ft5x06"; + + reg = <0x38>; + + /* GPIO line is inverted before going to touch panel */ + reset-gpios = <&gpio6 15 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <1920>; + touchscreen-size-y = <1200>; + + wakeup-source; + }; +}; + +&epwmss0 { + status = "okay"; +}; + +&ecap0 { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dra71-evm-lcd-auo-g101evn01.0.dts b/arch/arm/boot/dts/ti/dra71-evm-lcd-auo-g101evn01.0.dts --- a/arch/arm/boot/dts/ti/dra71-evm-lcd-auo-g101evn01.0.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dra71-evm-lcd-auo-g101evn01.0.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,115 @@ +/dts-v1/; +/plugin/; +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + model = "TI DRA71 EVM-LCD-AUO-Display"; + + aliases { + display0 = "/display"; + display1 = "/connector"; + }; + + lcd_bl: backlight { + compatible = "led-backlight"; + leds = <&backlight_led>; + brightness-levels = <0 2 38 74 110 146 182 218 256>; + default-brightness = <8>; + }; + + lcd: display { + compatible = "auo,g101evn010"; + enable-gpios = <&pcf_lcd 13 GPIO_ACTIVE_LOW>; + label = "lcd"; + backlight = <&lcd_bl>; + + port { + lcd_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; + }; + }; +}; + +&pcf_gpio_21 { + p0 { + gpio-hog; + gpios = <0 GPIO_ACTIVE_HIGH>; + line-name = "sel_gpmc_ad_vid_s0"; + output-low; + }; + + p7 { + gpio-hog; + gpios = <7 GPIO_ACTIVE_HIGH>; + line-name = "sel_gpmc_ad_vid_s2"; + output-high; + }; +}; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + pcf_display_board: gpio@27 { + compatible = "nxp,pcf8575"; + #gpio-cells = <2>; + gpio-controller; + reg = <0x27>; + + backlight-hog { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + line-name = "enable_backlight"; + output-high; + }; + }; + + tlc59108@40 { + compatible = "ti,tlc59108"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40>; + + backlight_led: bl@2 { + label = "backlight"; + reg = <0x2>; + }; + }; + + touchscreen: goodix-gt9271@14 { + compatible = "goodix,gt9271"; + interrupt-parent = <&gpio1>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; + irq-gpios = <&pcf_display_board 6 GPIO_ACTIVE_HIGH>; + reg = <0x14>; + reset-gpios = <&pcf_display_board 5 GPIO_ACTIVE_LOW>; + status = "okay"; + touchscreen-inverted-y; + touchscreen-size-x = <1280>; + touchscreen-size-y = <800>; + }; +}; + +&dss { + ports { + #address-cells = <1>; + #size-cells = <0>; + + dss_port: port@2 { + reg = <2>; + + dpi_out: endpoint { + data-lines = <24>; + remote-endpoint = <&lcd_in>; + }; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dra71-evm-nand.dts b/arch/arm/boot/dts/ti/dra71-evm-nand.dts --- a/arch/arm/boot/dts/ti/dra71-evm-nand.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dra71-evm-nand.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,6 @@ +/dts-v1/; +/plugin/; + +&gpmc { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dra72-evm-touchscreen.dts b/arch/arm/boot/dts/ti/dra72-evm-touchscreen.dts --- a/arch/arm/boot/dts/ti/dra72-evm-touchscreen.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dra72-evm-touchscreen.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include + +&touchscreen { + interrupt-parent = <&gpio1>; + interrupts = <15 IRQ_TYPE_EDGE_FALLING>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dra74-evm-touchscreen.dts b/arch/arm/boot/dts/ti/dra74-evm-touchscreen.dts --- a/arch/arm/boot/dts/ti/dra74-evm-touchscreen.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dra74-evm-touchscreen.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include + +&touchscreen { + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dra76-evm-tfp410.dts b/arch/arm/boot/dts/ti/dra76-evm-tfp410.dts --- a/arch/arm/boot/dts/ti/dra76-evm-tfp410.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dra76-evm-tfp410.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + aliases { + display0 = "/display"; + display1 = "/connector"; + }; + + tfp410: encoder@0 { + compatible = "ti,tfp410"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + tfp410_in: endpoint@0 { + remote-endpoint = <&dpi_out>; + }; + }; + + port@1 { + reg = <1>; + + tfp410_out: endpoint@0 { + remote-endpoint = <&dvi_connector_in>; + }; + }; + }; + }; + + dvi0: connector@0 { + compatible = "dvi-connector"; + label = "dvi"; + + digital; + + ddc-i2c-bus = <&i2c3>; + + hpd-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; /* wakeup2/sys_nirq2/gpio1_2 HPD */ + + port { + dvi_connector_in: endpoint { + remote-endpoint = <&tfp410_out>; + }; + }; + }; + }; + }; +}; + +&dss { + status = "ok"; + ports { + #address-cells = <1>; + #size-cells = <0>; + status = "ok"; + + port@0 { + reg = <0>; + + dpi_out: endpoint { + remote-endpoint = <&tfp410_in>; + data-lines = <24>; + }; + }; + }; +}; + +&gpio3 { + p1 { + /* GPIO3_1 CON_LCD_PWR_DN */ + /* This affects the TFP410 and the USB */ + gpio-hog; + gpios = <1 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "CON_LCD_PWR_DN"; + }; +}; + +&i2c3 { + #address-cells = <1>; + #size-cells = <0>; + + clock-frequency = <100000>; + + pcf_tfp: pcf8757@20 { + compatible = "ti,pcf8575", "nxp,pcf8575"; + reg = <0x27>; + gpio-controller; + #gpio-cells = <2>; + + p2 { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "ct_hpd"; + }; + + p3 { + gpio-hog; + gpios = <3 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "ls_oe"; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dra7x-evm-osd-lcd-common.dts b/arch/arm/boot/dts/ti/dra7x-evm-osd-lcd-common.dts --- a/arch/arm/boot/dts/ti/dra7x-evm-osd-lcd-common.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dra7x-evm-osd-lcd-common.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + aliases { + display0 = "/display"; + display1 = "/connector"; + }; + + lcd_bl: backlight { + compatible = "led-backlight"; + leds = <&backlight_led>; + brightness-levels = <0 243 245 247 248 249 251 252 256>; + default-brightness = <8>; + }; + + tc358768_refclk: tc358768_refclk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <20000000>; + }; + }; + }; +}; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + dsi_bridge: tc358768@0e { + compatible = "toshiba,tc358768"; + reg = <0x0e>; + + clocks = <&tc358768_refclk>; + clock-names = "refclk"; + + reset-gpios = <&pcf_display_board 0 GPIO_ACTIVE_LOW>; + + dsi_bridge_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + rgb_in: endpoint { + remote-endpoint = <&dpi_out>; + data-lines = <24>; + }; + }; + }; + }; + + tlc59108: tlc59116@40 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "ti,tlc59108"; + reg = <0x40>; + + backlight_led: bl@2 { + label = "backlight"; + reg = <0x2>; + }; + }; + + pcf_display_board: gpio@27 { + compatible = "nxp,pcf8575"; + reg = <0x27>; + gpio-controller; + #gpio-cells = <2>; + }; + + touchscreen: edt-ft5506@38 { + status = "okay"; + compatible = "edt,edt-ft5506", "edt,edt-ft5x06"; + + reg = <0x38>; + + /* GPIO line is inverted before going to touch panel */ + reset-gpios = <&pcf_display_board 5 GPIO_ACTIVE_HIGH>; + + touchscreen-size-x = <1920>; + touchscreen-size-y = <1200>; + + wakeup-source; + }; +}; + +&dss { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpi_out: endpoint { + remote-endpoint = <&rgb_in>; + data-lines = <24>; + }; + }; + + }; +}; + +&pcf_lcd { + backlight-hog { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + line-name = "enable_backlight"; + output-high; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/dtb-merge.cfg b/arch/arm/boot/dts/ti/dtb-merge.cfg --- a/arch/arm/boot/dts/ti/dtb-merge.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/dtb-merge.cfg 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,3 @@ +am57xx-evm: am57xx-beagle-x15.dtb am57xx-evm-common.dtbo +am57xx-evm-reva3: am57xx-beagle-x15-revc.dtb am57xx-evm-common.dtbo am57xx-evm-reva3.dtbo +dra71-evm-nand: dra71-evm.dtb dra71-evm-nand.dtb diff -Naur --no-dereference a/arch/arm/boot/dts/ti/lcd-osd101t2045.dts b/arch/arm/boot/dts/ti/lcd-osd101t2045.dts --- a/arch/arm/boot/dts/ti/lcd-osd101t2045.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/lcd-osd101t2045.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +&dsi_bridge { + #address-cells = <1>; + #size-cells = <0>; + + lcd: display { + compatible = "osddisplays,osd101t2045-53ts"; + reg = <0>; + + label = "lcd"; + + backlight = <&lcd_bl>; + + port { + lcd_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; +}; + +&dsi_bridge_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + dsi_out: endpoint { + remote-endpoint = <&lcd_in>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/lcd-osd101t2587.dts b/arch/arm/boot/dts/ti/lcd-osd101t2587.dts --- a/arch/arm/boot/dts/ti/lcd-osd101t2587.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/lcd-osd101t2587.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +&dsi_bridge { + #address-cells = <1>; + #size-cells = <0>; + + lcd: display { + compatible = "osddisplays,osd101t2587-53ts"; + reg = <0>; + + label = "lcd"; + + backlight = <&lcd_bl>; + + port { + lcd_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; +}; + +&dsi_bridge_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + dsi_out: endpoint { + remote-endpoint = <&lcd_in>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm/boot/dts/ti/Makefile b/arch/arm/boot/dts/ti/Makefile --- a/arch/arm/boot/dts/ti/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/boot/dts/ti/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile to build device tree overlay binaries for boards based on +# Texas Instruments Inc processors +# +# Copyright (C) 2017-2021 Texas Instruments Incorporated - https://www.ti.com/ +# + +DTC_FLAGS += -@ + +# base dtbs with symbols enabled +dtb-$(CONFIG_SOC_DRA7XX) += \ + am571x-idk.dtb \ + am572x-idk.dtb \ + am574x-idk.dtb \ + am57xx-beagle-x15.dtb \ + am57xx-beagle-x15-revb1.dtb \ + am57xx-beagle-x15-revc.dtb \ + dra71-evm.dtb \ + dra72-evm.dtb \ + dra72-evm-revc.dtb \ + dra76-evm.dtb \ + dra7-evm.dtb + +# overlays +dtb-$(CONFIG_SOC_DRA7XX) += \ + am57xx-evm-common.dtbo \ + am57xx-evm-reva3.dtbo \ + lcd-osd101t2045.dtbo \ + lcd-osd101t2587.dtbo \ + dra72-evm-touchscreen.dtbo \ + dra74-evm-touchscreen.dtbo \ + dra7x-evm-osd-lcd-common.dtbo \ + am571x-idk-touchscreen.dtbo \ + am572x-idk-touchscreen.dtbo \ + am57xx-idk-osd-lcd-common.dtbo \ + dra71-evm-lcd-auo-g101evn01.0.dtbo \ + dra76-evm-tfp410.dtbo \ + dra71-evm-nand.dtb + +$(obj)/%.dtb: $(src)/../%.dts FORCE + $(call if_changed_dep,dtc,dtb) + +$(obj)/%.dt.yaml: $(src)/../%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE + $(call if_changed_rule,dtc,yaml) + +clean-files := *.dtb *.dtbo + +always-y := $(dtb-y) diff -Naur --no-dereference a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig --- a/arch/arm/configs/multi_v7_defconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/configs/multi_v7_defconfig 2022-01-06 12:45:53.810318090 -0500 @@ -154,6 +154,7 @@ CONFIG_IPV6_MIP6=m CONFIG_IPV6_TUNNEL=m CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NET_SWITCHDEV=y CONFIG_NET_DSA=m CONFIG_CAN=y CONFIG_CAN_AT91=m @@ -270,9 +271,12 @@ CONFIG_STMMAC_ETH=y CONFIG_DWMAC_DWC_QOS_ETH=y CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y +CONFIG_TI_CPTS=y CONFIG_XILINX_EMACLITE=y CONFIG_BROADCOM_PHY=y CONFIG_ICPLUS_PHY=y +CONFIG_DP83867_PHY=y CONFIG_MARVELL_PHY=y CONFIG_MICREL_PHY=y CONFIG_AT803X_PHY=y @@ -436,6 +440,7 @@ CONFIG_SPI_XILINX=y CONFIG_SPI_SPIDEV=y CONFIG_SPMI=y +CONFIG_PTP_1588_CLOCK=y CONFIG_PINCTRL_AS3722=y CONFIG_PINCTRL_RZA2=y CONFIG_PINCTRL_STMFX=y diff -Naur --no-dereference a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig --- a/arch/arm/configs/omap2plus_defconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/configs/omap2plus_defconfig 2022-01-06 12:45:53.810318090 -0500 @@ -279,6 +279,7 @@ CONFIG_SERIAL_DEV_BUS=y CONFIG_I2C_CHARDEV=y CONFIG_SPI=y +CONFIG_SPI_GPIO=m CONFIG_SPI_OMAP24XX=y CONFIG_SPI_TI_QSPI=m CONFIG_HSI=m @@ -296,7 +297,6 @@ CONFIG_W1=m CONFIG_HDQ_MASTER_OMAP=m CONFIG_W1_SLAVE_DS250X=m -CONFIG_POWER_AVS=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO=y CONFIG_BATTERY_BQ27XXX=m @@ -313,6 +313,7 @@ CONFIG_THERMAL_GOV_USER_SPACE=y CONFIG_CPU_THERMAL=y CONFIG_TI_THERMAL=y +CONFIG_OMAP3_THERMAL=y CONFIG_OMAP4_THERMAL=y CONFIG_OMAP5_THERMAL=y CONFIG_DRA752_THERMAL=y @@ -523,6 +524,8 @@ CONFIG_TWL4030_MADC=m CONFIG_SENSORS_ISL29028=m CONFIG_BMP280=m +CONFIG_KXCJK1013=m +CONFIG_AK8975=m CONFIG_PWM=y CONFIG_PWM_OMAP_DMTIMER=m CONFIG_PWM_TIECAP=m @@ -535,6 +538,8 @@ CONFIG_OMAP_USB2=m CONFIG_TI_PIPE3=y CONFIG_TWL4030_USB=m +CONFIG_COUNTER=m +CONFIG_TI_EQEP=m CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y diff -Naur --no-dereference a/arch/arm/configs/rcn-ee_defconfig b/arch/arm/configs/rcn-ee_defconfig --- a/arch/arm/configs/rcn-ee_defconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm/configs/rcn-ee_defconfig 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,2706 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_PSI=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_IKHEADERS=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_RDMA=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_SYSFS_SYSCALL is not set +CONFIG_BPF_LSM=y +CONFIG_BPF_SYSCALL=y +CONFIG_USERFAULTFD=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_PROFILING=y +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_SOC_OMAP5=y +CONFIG_SOC_AM33XX=y +CONFIG_SOC_DRA7XX=y +CONFIG_SOC_HAS_OMAP2_SDRC=y +CONFIG_OMAP5_ERRATA_801819=y +CONFIG_ARM_THUMBEE=y +CONFIG_PL310_ERRATA_588369=y +CONFIG_PL310_ERRATA_727915=y +CONFIG_PL310_ERRATA_753970=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_773022=y +CONFIG_ARM_ERRATA_814220=y +CONFIG_SMP=y +# CONFIG_ARM_CPU_TOPOLOGY is not set +CONFIG_MCPM=y +CONFIG_NR_CPUS=2 +CONFIG_ARM_PSCI=y +CONFIG_HZ_250=y +# CONFIG_ATAGS is not set +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_EFI=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPUFREQ_DT=m +# CONFIG_ARM_OMAP2PLUS_CPUFREQ is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_HIBERNATION=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_APM_EMULATION=y +CONFIG_ENERGY_MODEL=y +CONFIG_DMI_SYSFS=y +CONFIG_TRUSTED_FOUNDATIONS=y +CONFIG_EFI_BOOTLOADER_CONTROL=m +CONFIG_EFI_CAPSULE_LOADER=m +CONFIG_RESET_ATTACK_MITIGATION=y +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_SHA256_ARM=m +CONFIG_CRYPTO_SHA512_ARM=m +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m +CONFIG_CRYPTO_GHASH_ARM_CE=m +CONFIG_CRYPTO_CHACHA20_NEON=y +CONFIG_CRYPTO_POLY1305_ARM=y +CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_CURVE25519_NEON=y +CONFIG_OPROFILE=m +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_COMPRESS=y +CONFIG_MODULE_COMPRESS_XZ=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_WBT=y +CONFIG_BLK_CGROUP_IOCOST=y +CONFIG_BLK_SED_OPAL=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_KARMA_PARTITION=y +CONFIG_MQ_IOSCHED_KYBER=m +CONFIG_IOSCHED_BFQ=m +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_BINFMT_MISC=m +CONFIG_KSM=y +CONFIG_FRONTSWAP=y +CONFIG_ZSWAP=y +CONFIG_Z3FOLD=m +CONFIG_ZSMALLOC=m +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=m +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=m +CONFIG_XFRM_USER=m +CONFIG_XFRM_INTERFACE=m +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=m +CONFIG_NET_KEY_MIGRATE=y +CONFIG_XDP_SOCKETS=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_NET_IPVTI=m +CONFIG_NET_FOU_IP_TUNNELS=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_INET_RAW_DIAG=m +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_NV=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_YEAH=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_DCTCP=m +CONFIG_TCP_CONG_CDG=m +CONFIG_TCP_CONG_BBR=m +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_ILA=m +CONFIG_IPV6_VTI=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_GRE=m +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPV6_SEG6_HMAC=y +CONFIG_NETLABEL=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_LOG_NETDEV=m +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_CT_NETLINK_TIMEOUT=m +CONFIG_NF_CT_NETLINK_HELPER=m +CONFIG_NETFILTER_NETLINK_GLUE_CT=y +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_NETDEV=y +CONFIG_NFT_NUMGEN=m +CONFIG_NFT_CT=m +CONFIG_NFT_FLOW_OFFLOAD=m +CONFIG_NFT_COUNTER=m +CONFIG_NFT_CONNLIMIT=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +CONFIG_NFT_TUNNEL=m +CONFIG_NFT_OBJREF=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_QUOTA=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_COMPAT=m +CONFIG_NFT_HASH=m +CONFIG_NFT_FIB_INET=m +CONFIG_NFT_XFRM=m +CONFIG_NFT_SOCKET=m +CONFIG_NFT_OSF=m +CONFIG_NFT_TPROXY=m +CONFIG_NFT_SYNPROXY=m +CONFIG_NFT_DUP_NETDEV=m +CONFIG_NFT_FWD_NETDEV=m +CONFIG_NFT_FIB_NETDEV=m +CONFIG_NF_FLOW_TABLE_INET=m +CONFIG_NF_FLOW_TABLE=m +CONFIG_NETFILTER_XT_SET=m +CONFIG_NETFILTER_XT_TARGET_AUDIT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPMARK=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_IPMAC=m +CONFIG_IP_SET_HASH_MAC=m +CONFIG_IP_SET_HASH_NETPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETNET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_FO=m +CONFIG_IP_VS_OVF=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_MH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m +CONFIG_IP_VS_FTP=m +CONFIG_IP_VS_PE_SIP=m +CONFIG_NFT_DUP_IPV4=m +CONFIG_NFT_FIB_IPV4=m +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_FLOW_TABLE_IPV4=m +CONFIG_NF_LOG_ARP=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_SECURITY=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NFT_DUP_IPV6=m +CONFIG_NFT_FIB_IPV6=m +CONFIG_NF_FLOW_TABLE_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_SRH=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_SECURITY=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NFT_BRIDGE_META=m +CONFIG_NFT_BRIDGE_REJECT=m +CONFIG_NF_LOG_BRIDGE=m +CONFIG_NF_CONNTRACK_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_DCCP=m +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1=y +CONFIG_SCTP_COOKIE_HMAC_MD5=y +CONFIG_RDS=m +CONFIG_RDS_TCP=m +CONFIG_TIPC=m +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_LLC2=m +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_PHONET=m +CONFIG_6LOWPAN=m +CONFIG_6LOWPAN_GHC_EXT_HDR_HOP=m +CONFIG_6LOWPAN_GHC_UDP=m +CONFIG_6LOWPAN_GHC_ICMPV6=m +CONFIG_6LOWPAN_GHC_EXT_HDR_DEST=m +CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG=m +CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m +CONFIG_IEEE802154=m +CONFIG_IEEE802154_6LOWPAN=m +CONFIG_MAC802154=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_CBS=m +CONFIG_NET_SCH_ETF=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_SKBPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_CAKE=m +CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_HHF=m +CONFIG_NET_SCH_PIE=m +CONFIG_NET_SCH_FQ_PIE=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_PLUG=m +CONFIG_NET_SCH_ETS=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_MATCHALL=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_CANID=m +CONFIG_NET_EMATCH_IPSET=m +CONFIG_NET_EMATCH_IPT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SAMPLE=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_MPLS=m +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_CONNMARK=m +CONFIG_NET_ACT_CTINFO=m +CONFIG_NET_ACT_SKBMOD=m +CONFIG_NET_ACT_IFE=m +CONFIG_NET_ACT_TUNNEL_KEY=m +CONFIG_NET_ACT_CT=m +CONFIG_NET_ACT_GATE=m +CONFIG_NET_IFE_SKBMARK=m +CONFIG_NET_IFE_SKBPRIO=m +CONFIG_NET_IFE_SKBTCINDEX=m +CONFIG_DCB=y +CONFIG_BATMAN_ADV=m +# CONFIG_BATMAN_ADV_BATMAN_V is not set +CONFIG_BATMAN_ADV_NC=y +CONFIG_BATMAN_ADV_DEBUGFS=y +CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_NETLINK_DIAG=m +CONFIG_NET_MPLS_GSO=y +CONFIG_MPLS_ROUTING=m +CONFIG_MPLS_IPTUNNEL=m +CONFIG_NET_SWITCHDEV=y +CONFIG_QRTR=m +CONFIG_NET_NCSI=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_BPF_JIT=y +CONFIG_BPF_STREAM_PARSER=y +CONFIG_NET_PKTGEN=m +CONFIG_NET_DROP_MONITOR=y +CONFIG_HAMRADIO=y +CONFIG_AX25=m +CONFIG_NETROM=m +CONFIG_ROSE=m +CONFIG_MKISS=m +CONFIG_6PACK=m +CONFIG_BPQETHER=m +CONFIG_BAYCOM_SER_FDX=m +CONFIG_BAYCOM_SER_HDX=m +CONFIG_YAM=m +CONFIG_CAN=m +CONFIG_CAN_J1939=m +CONFIG_CAN_ISOTP=m +CONFIG_CAN_VCAN=m +CONFIG_CAN_VXCAN=m +CONFIG_CAN_SLCAN=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_HI311X=m +CONFIG_CAN_MCP251X=m +CONFIG_CAN_MCP251XFD=m +CONFIG_CAN_8DEV_USB=m +CONFIG_CAN_EMS_USB=m +CONFIG_CAN_ESD_USB2=m +CONFIG_CAN_GS_USB=m +CONFIG_CAN_KVASER_USB=m +CONFIG_CAN_MCBA_USB=m +CONFIG_CAN_PEAK_USB=m +CONFIG_CAN_UCAN=m +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +CONFIG_BT_HS=y +CONFIG_BT_6LOWPAN=m +CONFIG_BT_LEDS=y +CONFIG_BT_MSFTEXT=y +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_MTK=y +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_INTEL=y +CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_HCIUART_RTL=y +CONFIG_BT_HCIUART_QCA=y +CONFIG_BT_HCIUART_AG6XX=y +CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m +CONFIG_BT_MTKSDIO=m +CONFIG_BT_MTKUART=m +CONFIG_AF_RXRPC_IPV6=y +CONFIG_RXKAD=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_DEFAULT_PS is not set +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_MAC80211_MESH=y +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_NET_9P=m +CONFIG_NET_9P_VIRTIO=m +CONFIG_RPMSG_PROTO=m +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_NCI_SPI=m +CONFIG_NFC_NCI_UART=m +CONFIG_NFC_TRF7970A=m +CONFIG_NFC_SIM=m +CONFIG_NFC_PORT100=m +CONFIG_NFC_PN533_USB=m +CONFIG_NFC_PN533_I2C=m +CONFIG_NFC_ST_NCI_I2C=m +CONFIG_NFC_ST_NCI_SPI=m +CONFIG_NFC_NXP_NCI=m +CONFIG_NFC_NXP_NCI_I2C=m +CONFIG_NFC_ST95HF=m +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s am335x-pm-firmware.elf am335x-bone-scale-data.bin am335x-evm-scale-data.bin am43x-evm-scale-data.bin" +CONFIG_EXTRA_FIRMWARE_DIR="firmware" +CONFIG_OMAP_OCP2SCP=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_CONNECTOR=y +CONFIG_GNSS=m +CONFIG_GNSS_MTK_SERIAL=m +CONFIG_GNSS_SIRF_SERIAL=m +CONFIG_GNSS_UBX_SERIAL=m +CONFIG_MTD=y +CONFIG_MTD_AR7_PARTS=m +CONFIG_MTD_OF_PARTS=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_RFD_FTL=m +CONFIG_SSFDC=m +CONFIG_MTD_OOPS=m +CONFIG_MTD_SWAP=m +CONFIG_MTD_PHYSMAP=m +CONFIG_MTD_PLATRAM=m +CONFIG_MTD_DATAFLASH=m +CONFIG_MTD_SST25L=m +CONFIG_MTD_ONENAND=y +CONFIG_MTD_ONENAND_VERIFY_WRITE=y +CONFIG_MTD_ONENAND_2X_PROGRAM=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_ECC_SW_BCH=y +CONFIG_MTD_NAND_OMAP2=m +CONFIG_MTD_NAND_NANDSIM=m +CONFIG_MTD_LPDDR=m +CONFIG_MTD_SPI_NOR=m +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_OF_OVERLAY=y +CONFIG_BLK_DEV_NULL_BLK=m +CONFIG_ZRAM=m +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_DRBD=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_ATA_OVER_ETH=m +CONFIG_VIRTIO_BLK=m +CONFIG_BLK_DEV_RBD=m +CONFIG_AD525X_DPOT=m +CONFIG_AD525X_DPOT_I2C=m +CONFIG_AD525X_DPOT_SPI=m +CONFIG_ICS932S401=m +CONFIG_APDS9802ALS=m +CONFIG_ISL29003=m +CONFIG_ISL29020=m +CONFIG_SENSORS_TSL2550=m +CONFIG_SENSORS_BH1770=m +CONFIG_SENSORS_APDS990X=m +CONFIG_HMC6352=m +CONFIG_DS1682=m +CONFIG_SRAM=y +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +CONFIG_EEPROM_LEGACY=m +CONFIG_EEPROM_MAX6875=m +CONFIG_EEPROM_93XX46=m +CONFIG_TI_ST=m +CONFIG_BEAGLEBONE_PINMUX_HELPER=y +CONFIG_RAID_ATTRS=m +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=m +CONFIG_BLK_DEV_SR=m +CONFIG_CHR_DEV_SG=m +CONFIG_CHR_DEV_SCH=m +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_ISCSI_ATTRS=m +CONFIG_SCSI_SAS_LIBSAS=m +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_SRP_ATTRS=m +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_MD_CLUSTER=m +CONFIG_BCACHE=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_UNSTRIPED=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_DM_CACHE=m +CONFIG_DM_WRITECACHE=m +CONFIG_DM_ERA=m +CONFIG_DM_MIRROR=m +CONFIG_DM_LOG_USERSPACE=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_QL=m +CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_DELAY=m +CONFIG_DM_UEVENT=y +CONFIG_DM_FLAKEY=m +CONFIG_DM_VERITY=m +CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y +CONFIG_DM_SWITCH=m +CONFIG_DM_LOG_WRITES=m +CONFIG_DM_INTEGRITY=m +CONFIG_DM_ZONED=m +CONFIG_TARGET_CORE=m +CONFIG_TCM_IBLOCK=m +CONFIG_TCM_FILEIO=m +CONFIG_TCM_PSCSI=m +CONFIG_TCM_USER2=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_ISCSI_TARGET=m +CONFIG_NETDEVICES=y +CONFIG_BONDING=m +CONFIG_DUMMY=m +CONFIG_WIREGUARD=m +CONFIG_EQUALIZER=m +CONFIG_IFB=m +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_IPVLAN=m +CONFIG_IPVTAP=m +CONFIG_VXLAN=m +CONFIG_GENEVE=m +CONFIG_GTP=m +CONFIG_MACSEC=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m +CONFIG_VETH=m +CONFIG_VIRTIO_NET=m +CONFIG_NLMON=m +CONFIG_NET_VRF=m +CONFIG_ATM_DUMMY=m +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_AURORA is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +CONFIG_KS8851=m +CONFIG_ENC28J60=y +CONFIG_ENCX24J600=y +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +CONFIG_SMC91X=m +CONFIG_SMC911X=m +CONFIG_SMSC911X=m +# CONFIG_NET_VENDOR_SOCIONEXT is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +CONFIG_TI_CPSW_PHY_SEL=y +CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y +CONFIG_TI_CPTS=y +# CONFIG_TI_ICSS_IEP is not set +# CONFIG_NET_VENDOR_VIA is not set +CONFIG_WIZNET_W5100=y +CONFIG_WIZNET_W5100_SPI=y +# CONFIG_NET_VENDOR_XILINX is not set +CONFIG_LED_TRIGGER_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_PHY=y +CONFIG_MICROSEMI_PHY=m +CONFIG_AT803X_PHY=y +CONFIG_DP83867_PHY=y +CONFIG_DP83869_PHY=m +CONFIG_VITESSE_PHY=y +CONFIG_MDIO_BCM_UNIMAC=m +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOATM=m +CONFIG_PPPOE=m +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9700=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_CDC_PHONET=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m +CONFIG_USB_NET_AQC111=m +CONFIG_ATH9K=m +CONFIG_ATH9K_CHANNEL_CONTEXT=y +CONFIG_ATH9K_HTC=m +CONFIG_ATH9K_HWRNG=y +CONFIG_CARL9170=m +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +CONFIG_AR5523=m +CONFIG_ATH10K=m +CONFIG_ATH10K_USB=m +CONFIG_ATH11K=m +CONFIG_ATH11K_AHB=m +CONFIG_AT76C50X_USB=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_HOSTAP=m +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_USB=m +CONFIG_LIBERTAS_SDIO=m +CONFIG_LIBERTAS_MESH=y +CONFIG_LIBERTAS_THINFIRM=m +CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_MT7601U=m +CONFIG_MT76x0U=m +CONFIG_MT76x2U=m +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RTL8187=m +CONFIG_RTL8192CU=m +# CONFIG_RTLWIFI_DEBUG is not set +CONFIG_RTL8XXXU=m +CONFIG_RTW88=m +CONFIG_RSI_91X=m +# CONFIG_RSI_SDIO is not set +CONFIG_WL1251=m +CONFIG_WL1251_SPI=m +CONFIG_WL1251_SDIO=m +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SPI=m +CONFIG_WLCORE_SDIO=m +CONFIG_ZD1211RW=m +CONFIG_MAC80211_HWSIM=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_IEEE802154_AT86RF230=m +CONFIG_IEEE802154_MRF24J40=m +CONFIG_IEEE802154_CC2520=m +CONFIG_IEEE802154_ATUSB=m +CONFIG_IEEE802154_WPANUSB=m +CONFIG_IEEE802154_ADF7242=m +CONFIG_IEEE802154_CA8210=m +CONFIG_IEEE802154_MCR20A=m +CONFIG_IEEE802154_HWSIM=m +CONFIG_IEEE802154_BCFSERIAL=m +CONFIG_INPUT_POLLDEV=y +CONFIG_INPUT_SPARSEKMAP=m +CONFIG_INPUT_JOYDEV=m +CONFIG_INPUT_EVDEV=m +CONFIG_KEYBOARD_ADP5588=m +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_QT2160=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_TCA8418=m +CONFIG_KEYBOARD_LM8323=m +CONFIG_KEYBOARD_MAX7359=m +CONFIG_KEYBOARD_STOWAWAY=m +# CONFIG_MOUSE_PS2 is not set +CONFIG_MOUSE_APPLETOUCH=m +CONFIG_MOUSE_ELAN_I2C=m +CONFIG_MOUSE_SYNAPTICS_I2C=m +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=m +CONFIG_JOYSTICK_IFORCE_232=m +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_ZHENHUA=m +CONFIG_JOYSTICK_AS5011=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_JOYSTICK_PSXPAD_SPI=y +CONFIG_JOYSTICK_PSXPAD_SPI_FF=y +CONFIG_JOYSTICK_PXRC=m +CONFIG_JOYSTICK_FSIA6B=m +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_GTCO=m +CONFIG_TABLET_USB_HANWANG=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_USB_PEGASUS=m +CONFIG_TABLET_SERIAL_WACOM4=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=m +CONFIG_TOUCHSCREEN_AD7877=m +CONFIG_TOUCHSCREEN_AD7879=m +CONFIG_TOUCHSCREEN_AD7879_I2C=m +CONFIG_TOUCHSCREEN_AR1021_I2C=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_HAMPSHIRE=m +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_GOODIX=m +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_MCS5000=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +CONFIG_TOUCHSCREEN_EDT_FT5X06=y +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_TSC2005=m +CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_TOUCHSCREEN_SILEAD=y +CONFIG_TOUCHSCREEN_STMPE=y +CONFIG_TOUCHSCREEN_TPS6507X=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=m +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m +CONFIG_INPUT_POWERMATE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_TPS65218_PWRBUTTON=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_PALMAS_PWRBUTTON=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_16550A_VARIANTS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=6 +CONFIG_SERIAL_8250_RUNTIME_UARTS=6 +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_MAX3100=m +CONFIG_SERIAL_MAX310X=m +CONFIG_N_GSM=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=m +CONFIG_VIRTIO_CONSOLE=m +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_ATMEL=y +CONFIG_RANDOM_TRUST_BOOTLOADER=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +CONFIG_I2C_MUX_GPIO=y +CONFIG_I2C_MUX_PCA954x=m +CONFIG_I2C_MUX_PINCTRL=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_DIOLAN_U2C=m +CONFIG_I2C_ROBOTFUZZ_OSIF=m +CONFIG_I2C_TAOS_EVM=m +CONFIG_I2C_TINY_USB=m +CONFIG_I2C_SLAVE=y +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_SPI=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_OMAP24XX=y +CONFIG_SPI_TI_QSPI=y +CONFIG_SPI_SPIDEV=m +CONFIG_SPI_SLAVE=y +CONFIG_SPI_SLAVE_TIME=m +CONFIG_SPI_SLAVE_SYSTEM_CONTROL=m +CONFIG_PPS_CLIENT_LDISC=m +CONFIG_PPS_CLIENT_GPIO=m +CONFIG_PTP_1588_CLOCK=y +CONFIG_PINCTRL_MCP23S08=m +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_PALMAS=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_OF_HELPER=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_SYSCON=y +CONFIG_GPIO_ADP5588=m +CONFIG_GPIO_ADNP=m +CONFIG_GPIO_MAX7300=m +CONFIG_GPIO_MAX732X=m +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_PCF857X=m +CONFIG_GPIO_TPIC2810=m +CONFIG_GPIO_PALMAS=y +CONFIG_GPIO_STMPE=y +CONFIG_GPIO_TPS65218=y +CONFIG_GPIO_74X164=m +CONFIG_GPIO_MAX3191X=m +CONFIG_GPIO_MAX7301=m +CONFIG_GPIO_MC33880=m +CONFIG_GPIO_PISOSR=m +CONFIG_GPIO_XRA1403=m +CONFIG_GPIO_AGGREGATOR=m +CONFIG_W1=y +CONFIG_W1_MASTER_DS2490=m +CONFIG_W1_MASTER_DS2482=m +CONFIG_W1_MASTER_GPIO=y +CONFIG_W1_SLAVE_THERM=m +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2405=m +CONFIG_W1_SLAVE_DS2408=m +CONFIG_W1_SLAVE_DS2413=m +CONFIG_W1_SLAVE_DS2406=m +CONFIG_W1_SLAVE_DS2423=m +CONFIG_W1_SLAVE_DS2805=m +CONFIG_W1_SLAVE_DS2431=m +CONFIG_W1_SLAVE_DS2433=m +CONFIG_W1_SLAVE_DS2433_CRC=y +CONFIG_W1_SLAVE_DS2438=m +CONFIG_W1_SLAVE_DS250X=m +CONFIG_W1_SLAVE_DS2780=m +CONFIG_W1_SLAVE_DS2781=m +CONFIG_W1_SLAVE_DS28E04=m +CONFIG_W1_SLAVE_DS28E17=m +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_GENERIC_ADC_BATTERY=m +CONFIG_BATTERY_DS2760=m +CONFIG_CHARGER_GPIO=m +CONFIG_SENSORS_AD7314=m +CONFIG_SENSORS_AD7414=m +CONFIG_SENSORS_AD7418=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1026=m +CONFIG_SENSORS_ADM1029=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ADM1177=m +CONFIG_SENSORS_ADM9240=m +CONFIG_SENSORS_ADT7310=m +CONFIG_SENSORS_ADT7410=m +CONFIG_SENSORS_ADT7411=m +CONFIG_SENSORS_ADT7462=m +CONFIG_SENSORS_ADT7470=m +CONFIG_SENSORS_ADT7475=m +CONFIG_SENSORS_AS370=m +CONFIG_SENSORS_ASC7621=m +CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_ATXP1=m +CONFIG_SENSORS_DRIVETEMP=m +CONFIG_SENSORS_DS620=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_F71805F=m +CONFIG_SENSORS_F71882FG=m +CONFIG_SENSORS_F75375S=m +CONFIG_SENSORS_FTSTEUTATES=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_GL520SM=m +CONFIG_SENSORS_G760A=m +CONFIG_SENSORS_G762=m +CONFIG_SENSORS_GPIO_FAN=y +CONFIG_SENSORS_HIH6130=m +CONFIG_SENSORS_IIO_HWMON=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_JC42=m +CONFIG_SENSORS_POWR1220=m +CONFIG_SENSORS_LINEAGE=m +CONFIG_SENSORS_LTC2945=m +CONFIG_SENSORS_LTC2990=m +CONFIG_SENSORS_LTC4151=m +CONFIG_SENSORS_LTC4215=m +CONFIG_SENSORS_LTC4222=m +CONFIG_SENSORS_LTC4245=m +CONFIG_SENSORS_LTC4260=m +CONFIG_SENSORS_LTC4261=m +CONFIG_SENSORS_MAX1111=m +CONFIG_SENSORS_MAX16065=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_MAX1668=m +CONFIG_SENSORS_MAX197=m +CONFIG_SENSORS_MAX31722=m +CONFIG_SENSORS_MAX31730=m +CONFIG_SENSORS_MAX6621=m +CONFIG_SENSORS_MAX6639=m +CONFIG_SENSORS_MAX6642=m +CONFIG_SENSORS_MAX6650=m +CONFIG_SENSORS_MAX6697=m +CONFIG_SENSORS_MAX31790=m +CONFIG_SENSORS_MCP3021=m +CONFIG_SENSORS_TC654=m +CONFIG_SENSORS_MR75203=m +CONFIG_SENSORS_ADCXX=m +CONFIG_SENSORS_LM63=m +CONFIG_SENSORS_LM70=m +CONFIG_SENSORS_LM73=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM87=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_LM92=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_LM95234=m +CONFIG_SENSORS_LM95241=m +CONFIG_SENSORS_LM95245=m +CONFIG_SENSORS_PC87360=m +CONFIG_SENSORS_PC87427=m +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_NCT6683=m +CONFIG_SENSORS_NCT6775=m +CONFIG_SENSORS_NCT7802=m +CONFIG_SENSORS_NCT7904=m +CONFIG_SENSORS_NPCM7XX=m +CONFIG_SENSORS_PCF8591=m +CONFIG_PMBUS=m +CONFIG_SENSORS_ADM1266=m +CONFIG_SENSORS_ADM1275=m +CONFIG_SENSORS_IBM_CFFPS=m +CONFIG_SENSORS_INSPUR_IPSPS=m +CONFIG_SENSORS_IR35221=m +CONFIG_SENSORS_IR38064=m +CONFIG_SENSORS_IRPS5401=m +CONFIG_SENSORS_ISL68137=m +CONFIG_SENSORS_LM25066=m +CONFIG_SENSORS_LTC2978=m +CONFIG_SENSORS_LTC2978_REGULATOR=y +CONFIG_SENSORS_LTC3815=m +CONFIG_SENSORS_MAX16064=m +CONFIG_SENSORS_MAX20730=m +CONFIG_SENSORS_MAX20751=m +CONFIG_SENSORS_MAX31785=m +CONFIG_SENSORS_MAX34440=m +CONFIG_SENSORS_MAX8688=m +CONFIG_SENSORS_MP2975=m +CONFIG_SENSORS_PXE1610=m +CONFIG_SENSORS_TPS40422=m +CONFIG_SENSORS_TPS53679=m +CONFIG_SENSORS_UCD9000=m +CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_XDPE122=m +CONFIG_SENSORS_ZL6100=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_SHT15=m +CONFIG_SENSORS_SHT21=m +CONFIG_SENSORS_SHT3x=m +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_DME1737=m +CONFIG_SENSORS_EMC1403=m +CONFIG_SENSORS_EMC2103=m +CONFIG_SENSORS_EMC6W201=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_SMSC47M192=m +CONFIG_SENSORS_SMSC47B397=m +CONFIG_SENSORS_SCH5627=m +CONFIG_SENSORS_SCH5636=m +CONFIG_SENSORS_STTS751=m +CONFIG_SENSORS_SMM665=m +CONFIG_SENSORS_ADC128D818=m +CONFIG_SENSORS_ADS7828=m +CONFIG_SENSORS_ADS7871=m +CONFIG_SENSORS_AMC6821=m +CONFIG_SENSORS_INA209=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA3221=m +CONFIG_SENSORS_TC74=m +CONFIG_SENSORS_THMC50=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_TMP103=m +CONFIG_SENSORS_TMP108=m +CONFIG_SENSORS_TMP401=m +CONFIG_SENSORS_TMP421=m +CONFIG_SENSORS_VT1211=m +CONFIG_SENSORS_W83773G=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83791D=m +CONFIG_SENSORS_W83792D=m +CONFIG_SENSORS_W83793=m +CONFIG_SENSORS_W83795=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83L786NG=m +CONFIG_SENSORS_W83627HF=m +CONFIG_SENSORS_W83627EHF=m +CONFIG_THERMAL_STATISTICS=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_TI_THERMAL=y +CONFIG_OMAP5_THERMAL=y +CONFIG_DRA752_THERMAL=y +CONFIG_GENERIC_ADC_THERMAL=m +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=m +CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y +CONFIG_SOFT_WATCHDOG=y +CONFIG_OMAP_WATCHDOG=y +CONFIG_MFD_STMPE=y +CONFIG_MFD_TI_AM335X_TSCADC=y +CONFIG_MFD_PALMAS=y +CONFIG_MFD_TPS65217=y +CONFIG_MFD_TPS65218=y +CONFIG_MFD_WL1273_CORE=m +CONFIG_REGULATOR_USERSPACE_CONSUMER=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_PALMAS=y +CONFIG_REGULATOR_PBIAS=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_TI_ABB=y +CONFIG_REGULATOR_TPS65217=y +CONFIG_REGULATOR_TPS65218=y +CONFIG_RC_CORE=m +CONFIG_LIRC=y +CONFIG_RC_DECODERS=y +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_IR_IMON_DECODER=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_IMON=m +CONFIG_IR_IMON_RAW=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_GPIO_CIR=m +CONFIG_USB_PULSE8_CEC=m +CONFIG_USB_RAINSHADOW_CEC=m +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_BENQ=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m +CONFIG_USB_GSPCA_DTCS033=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_JEILINJ=m +CONFIG_USB_GSPCA_JL2005BCD=m +CONFIG_USB_GSPCA_KINECT=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_MR97310A=m +CONFIG_USB_GSPCA_NW80X=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7302=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SE401=m +CONFIG_USB_GSPCA_SN9C2028=m +CONFIG_USB_GSPCA_SN9C20X=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_SPCA1528=m +CONFIG_USB_GSPCA_SQ905=m +CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STK1135=m +CONFIG_USB_GSPCA_STV0680=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TOPRO=m +CONFIG_USB_GSPCA_TOUPTEK=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_VICAM=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_USB_PWC=m +CONFIG_VIDEO_CPIA2=m +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_VIDEO_USBTV=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_STK1160_COMMON=m +CONFIG_VIDEO_GO7007=m +CONFIG_VIDEO_GO7007_USB=m +CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m +CONFIG_VIDEO_AU0828=m +CONFIG_VIDEO_AU0828_RC=y +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_DVB_USB=m +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_PCTV452E=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_USB_V2=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_AF9035=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_AZ6007=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_USB_MXL111SF=m +CONFIG_DVB_USB_RTL28XXU=m +CONFIG_DVB_USB_DVBSKY=m +CONFIG_DVB_USB_ZD1301=m +CONFIG_SMS_USB_DRV=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +CONFIG_DVB_AS102=m +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_V4L2=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_USB_AIRSPY=m +CONFIG_USB_HACKRF=m +CONFIG_USB_MSI2500=m +CONFIG_RADIO_SI470X=y +CONFIG_USB_SI470X=m +CONFIG_RADIO_SI4713=m +CONFIG_I2C_SI4713=m +CONFIG_USB_MR800=m +CONFIG_RADIO_SHARK=m +CONFIG_RADIO_SHARK2=m +CONFIG_USB_KEENE=m +CONFIG_USB_RAREMONO=m +CONFIG_USB_MA901=m +CONFIG_RADIO_WL128X=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_TI_VPE=m +CONFIG_SMS_SDIO_DRV=m +CONFIG_VIDEO_TVAUDIO=m +CONFIG_VIDEO_TDA7432=m +CONFIG_VIDEO_TDA9840=m +CONFIG_VIDEO_TEA6415C=m +CONFIG_VIDEO_TEA6420=m +CONFIG_VIDEO_CS3308=m +CONFIG_VIDEO_CS5345=m +CONFIG_VIDEO_TLV320AIC23B=m +CONFIG_VIDEO_WM8739=m +CONFIG_VIDEO_VP27SMPX=m +CONFIG_VIDEO_SAA6588=m +CONFIG_VIDEO_BT819=m +CONFIG_VIDEO_BT856=m +CONFIG_VIDEO_KS0127=m +CONFIG_VIDEO_SAA7110=m +CONFIG_VIDEO_VPX3220=m +CONFIG_VIDEO_SAA717X=m +CONFIG_VIDEO_SAA7127=m +CONFIG_VIDEO_SAA7185=m +CONFIG_VIDEO_ADV7170=m +CONFIG_VIDEO_ADV7175=m +CONFIG_VIDEO_UPD64031A=m +CONFIG_VIDEO_UPD64083=m +CONFIG_VIDEO_SAA6752HS=m +CONFIG_VIDEO_M52790=m +CONFIG_VIDEO_OV7670=m +CONFIG_VIDEO_MT9P031=m +CONFIG_VIDEO_SR030PC30=m +CONFIG_VIDEO_NOON010PC30=m +CONFIG_MEDIA_TUNER_MT2131=m +CONFIG_MEDIA_TUNER_M88RS6000T=m +CONFIG_MEDIA_TUNER_MXL301RF=m +CONFIG_MEDIA_TUNER_QM1D1B0004=m +CONFIG_DVB_STV0910=m +CONFIG_DVB_STV6111=m +CONFIG_DVB_MXL5XX=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_ZL10036=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA8261=m +CONFIG_DVB_VES1X93=m +CONFIG_DVB_TUA6100=m +CONFIG_DVB_CX24117=m +CONFIG_DVB_MB86A16=m +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_S5H1432=m +CONFIG_DVB_L64781=m +CONFIG_DVB_DIB9000=m +CONFIG_DVB_STV0367=m +CONFIG_DVB_CXD2880=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_OR51211=m +CONFIG_DVB_OR51132=m +CONFIG_DVB_MN88443X=m +CONFIG_DVB_LNBH25=m +CONFIG_DVB_LNBH29=m +CONFIG_DVB_ISL6405=m +CONFIG_DVB_LGS8GL5=m +CONFIG_DVB_TDA665x=m +CONFIG_DVB_HORUS3A=m +CONFIG_DVB_ASCOT2E=m +CONFIG_DVB_HELENE=m +CONFIG_DVB_CXD2099=m +CONFIG_DVB_DUMMY_FE=m +CONFIG_DRM=y +CONFIG_DRM_DP_AUX_CHARDEV=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_VGEM=m +CONFIG_DRM_UDL=m +CONFIG_DRM_OMAP=y +CONFIG_DRM_OMAP_WB=y +CONFIG_OMAP5_DSS_HDMI=y +CONFIG_DRM_TILCDC=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_LG_LB035Q02=m +CONFIG_DRM_PANEL_NEC_NL8048HL11=m +CONFIG_DRM_PANEL_ORISETECH_OTM8009A=y +CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS=y +CONFIG_DRM_PANEL_SHARP_LS037V7DW01=m +CONFIG_DRM_PANEL_SONY_ACX565AKM=m +CONFIG_DRM_PANEL_TPO_TD028TTEC1=m +CONFIG_DRM_PANEL_TPO_TD043MTEA1=m +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_LVDS_CODEC=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_SIMPLE_BRIDGE=m +CONFIG_DRM_TOSHIBA_TC358767=y +CONFIG_DRM_TOSHIBA_TC358768=y +CONFIG_DRM_TI_TFP410=y +CONFIG_DRM_TI_TPD12S015=y +CONFIG_DRM_I2C_ADV7511=y +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_ETNAVIV=y +CONFIG_DRM_GM12U320=m +CONFIG_TINYDRM_HX8357D=m +CONFIG_TINYDRM_ILI9225=m +CONFIG_TINYDRM_ILI9341=m +CONFIG_TINYDRM_ILI9486=m +CONFIG_TINYDRM_MI0283QT=m +CONFIG_TINYDRM_REPAPER=m +CONFIG_TINYDRM_ST7586=m +CONFIG_TINYDRM_ST7735R=m +CONFIG_DRM_TIDSS=y +CONFIG_DRM_LEGACY=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_SMSCUFX=m +CONFIG_FB_UDL=m +CONFIG_FB_SIMPLE=y +CONFIG_FB_SSD1307=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_LOGO=y +CONFIG_SOUND=m +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +CONFIG_SND=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_HRTIMER=m +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_DUMMY=m +CONFIG_SND_ALOOP=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m +CONFIG_SND_HDA_PREALLOC_SIZE=2048 +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_USB_6FIRE=m +CONFIG_SND_USB_HIFACE=m +CONFIG_SND_BCD2000=m +CONFIG_SND_USB_POD=m +CONFIG_SND_USB_PODHD=m +CONFIG_SND_USB_TONEPORT=m +CONFIG_SND_USB_VARIAX=m +CONFIG_SND_SOC=m +CONFIG_SND_SOC_DAVINCI_MCASP=m +CONFIG_SND_SOC_OMAP_DMIC=m +CONFIG_SND_SOC_OMAP_MCBSP=m +CONFIG_SND_SOC_OMAP_MCPDM=m +CONFIG_SND_SOC_OMAP_HDMI=m +CONFIG_SND_SOC_ADAU1701=m +CONFIG_SND_SOC_ADAU7002=m +CONFIG_SND_SOC_AK4554=m +CONFIG_SND_SOC_CS42L51_I2C=m +CONFIG_SND_SOC_CS4265=m +CONFIG_SND_SOC_CS4271_I2C=m +CONFIG_SND_SOC_DMIC=m +CONFIG_SND_SOC_ES8328_I2C=m +CONFIG_SND_SOC_ES8328_SPI=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_PCM5102A=m +CONFIG_SND_SOC_PCM512x_I2C=m +CONFIG_SND_SOC_SGTL5000=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_TLV320AIC31XX=m +CONFIG_SND_SOC_TLV320AIC3X=m +CONFIG_SND_SOC_TS3A227E=m +CONFIG_SND_SOC_WM8753=m +CONFIG_SND_SOC_WM8804_I2C=m +CONFIG_SND_SOC_WM8903=m +CONFIG_SND_SOC_TPA6130A2=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=m +CONFIG_HID_ACCUTOUCH=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=m +CONFIG_HID_ASUS=m +CONFIG_HID_AUREAL=m +CONFIG_HID_BELKIN=m +CONFIG_HID_BETOP_FF=m +CONFIG_HID_BIGBEN_FF=m +CONFIG_HID_CHERRY=m +CONFIG_HID_CHICONY=m +CONFIG_HID_CORSAIR=m +CONFIG_HID_COUGAR=m +CONFIG_HID_MACALLY=m +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_CMEDIA=m +CONFIG_HID_CP2112=m +CONFIG_HID_CYPRESS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=m +CONFIG_HID_ELAN=m +CONFIG_HID_ELECOM=m +CONFIG_HID_ELO=m +CONFIG_HID_EZKEY=m +CONFIG_HID_GEMBIRD=m +CONFIG_HID_GFRM=m +CONFIG_HID_HOLTEK=m +CONFIG_HOLTEK_FF=y +CONFIG_HID_GT683R=m +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_VIEWSONIC=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_ITE=m +CONFIG_HID_JABRA=m +CONFIG_HID_TWINHAN=m +CONFIG_HID_KENSINGTON=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LENOVO=m +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=m +CONFIG_HID_MALTRON=m +CONFIG_HID_MAYFLASH=m +CONFIG_HID_REDRAGON=m +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTI=m +CONFIG_HID_NTRIG=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PENMOUNT=m +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_PICOLCD_FB=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LEDS=y +CONFIG_HID_PICOLCD_CIR=y +CONFIG_HID_PLANTRONICS=m +CONFIG_HID_PRIMAX=m +CONFIG_HID_RETRODE=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_SAITEK=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_SONY_FF=y +CONFIG_HID_SPEEDLINK=m +CONFIG_HID_STEAM=m +CONFIG_HID_STEELSERIES=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_RMI=m +CONFIG_HID_GREENASIA=m +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_UDRAW_PS3=m +CONFIG_HID_U2FZERO=m +CONFIG_HID_WACOM=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_ZEROPLUS_FF=y +CONFIG_HID_ZYDACRON=m +CONFIG_HID_SENSOR_HUB=m +CONFIG_HID_SENSOR_CUSTOM_SENSOR=m +CONFIG_HID_ALPS=m +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_I2C_HID=y +CONFIG_USB_LED_TRIG=y +CONFIG_USB_ULPI_BUS=m +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_OTG=y +CONFIG_USB_LEDS_TRIGGER_USBPORT=m +CONFIG_USB_MON=m +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_PRINTER=m +CONFIG_USB_TMC=m +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_REALTEK=m +CONFIG_USB_STORAGE_DATAFAB=m +CONFIG_USB_STORAGE_FREECOM=m +CONFIG_USB_STORAGE_ISD200=m +CONFIG_USB_STORAGE_USBAT=m +CONFIG_USB_STORAGE_SDDR09=m +CONFIG_USB_STORAGE_SDDR55=m +CONFIG_USB_STORAGE_JUMPSHOT=m +CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_CYPRESS_ATACB=m +CONFIG_USB_STORAGE_ENE_UB6250=m +CONFIG_USB_UAS=m +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USBIP_CORE=m +CONFIG_USBIP_VHCI_HCD=m +CONFIG_USBIP_VHCI_HC_PORTS=15 +CONFIG_USBIP_VHCI_NR_HCS=8 +CONFIG_USBIP_HOST=m +CONFIG_USBIP_VUDC=m +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_DSPS=y +CONFIG_MUSB_PIO_ONLY=y +CONFIG_USB_DWC3=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_SIMPLE=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F81232=m +CONFIG_USB_SERIAL_F8153X=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_METRO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MXUPORT=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_WISHBONE=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +CONFIG_USB_SERIAL_UPD78F0730=m +CONFIG_USB_SERIAL_DEBUG=m +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +CONFIG_USB_APPLEDISPLAY=m +CONFIG_APPLE_MFI_FASTCHARGE=m +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_IOWARRIOR=m +CONFIG_USB_TEST=m +CONFIG_USB_EHSET_TEST_FIXTURE=m +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_YUREX=m +CONFIG_USB_HSIC_USB3503=m +CONFIG_USB_CHAOSKEY=m +CONFIG_NOP_USB_XCEIV=y +CONFIG_AM335X_PHY_USB=y +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_DUMMY_HCD=m +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_PHONET=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y +CONFIG_USB_ZERO=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_NOKIA=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_TYPEC=y +CONFIG_TYPEC_HD3SS3220=y +CONFIG_MMC=y +CONFIG_PWRSEQ_SD8787=m +CONFIG_MMC_BLOCK_MINORS=256 +CONFIG_SDIO_UART=m +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SPI=m +CONFIG_MMC_VUB300=m +CONFIG_MMC_USHC=m +CONFIG_MMC_SDHCI_OMAP=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_LP3944=m +CONFIG_LEDS_LP55XX_COMMON=m +CONFIG_LEDS_LP5523=m +CONFIG_LEDS_PCA955X=m +CONFIG_LEDS_DAC124S085=m +CONFIG_LEDS_PWM=m +CONFIG_LEDS_REGULATOR=m +CONFIG_LEDS_BD2802=m +CONFIG_LEDS_LT3593=m +CONFIG_LEDS_TCA6507=m +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_ACTIVITY=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=m +CONFIG_LEDS_TRIGGER_CAMERA=m +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_PATTERN=m +CONFIG_LEDS_TRIGGER_AUDIO=m +CONFIG_ACCESSIBILITY=y +CONFIG_A11Y_BRAILLE_CONSOLE=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_ABB5ZES3=y +CONFIG_RTC_DRV_ABEOZ9=y +CONFIG_RTC_DRV_ABX80X=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_DS1374=y +CONFIG_RTC_DRV_DS1374_WDT=y +CONFIG_RTC_DRV_DS1672=y +CONFIG_RTC_DRV_HYM8563=y +CONFIG_RTC_DRV_MAX6900=y +CONFIG_RTC_DRV_RS5C372=y +CONFIG_RTC_DRV_ISL1208=y +CONFIG_RTC_DRV_ISL12022=y +CONFIG_RTC_DRV_ISL12026=y +CONFIG_RTC_DRV_X1205=y +CONFIG_RTC_DRV_PCF8523=y +CONFIG_RTC_DRV_PCF85063=y +CONFIG_RTC_DRV_PCF85363=y +CONFIG_RTC_DRV_PCF8563=y +CONFIG_RTC_DRV_PCF8583=y +CONFIG_RTC_DRV_M41T80=y +CONFIG_RTC_DRV_M41T80_WDT=y +CONFIG_RTC_DRV_BQ32K=y +CONFIG_RTC_DRV_PALMAS=y +CONFIG_RTC_DRV_S35390A=y +CONFIG_RTC_DRV_FM3130=y +CONFIG_RTC_DRV_RX8010=y +CONFIG_RTC_DRV_RX8581=y +CONFIG_RTC_DRV_RX8025=y +CONFIG_RTC_DRV_EM3027=y +CONFIG_RTC_DRV_RV8803=y +CONFIG_RTC_DRV_M41T93=y +CONFIG_RTC_DRV_M41T94=y +CONFIG_RTC_DRV_DS1302=y +CONFIG_RTC_DRV_DS1305=y +CONFIG_RTC_DRV_DS1343=y +CONFIG_RTC_DRV_DS1347=y +CONFIG_RTC_DRV_DS1390=y +CONFIG_RTC_DRV_MAX6916=y +CONFIG_RTC_DRV_R9701=y +CONFIG_RTC_DRV_RX4581=y +CONFIG_RTC_DRV_RX6110=y +CONFIG_RTC_DRV_RS5C348=y +CONFIG_RTC_DRV_MAX6902=y +CONFIG_RTC_DRV_PCF2123=y +CONFIG_RTC_DRV_MCP795=y +CONFIG_RTC_DRV_DS3232=y +CONFIG_RTC_DRV_PCF2127=y +CONFIG_RTC_DRV_RV3029C2=y +CONFIG_RTC_DRV_DS1286=m +CONFIG_RTC_DRV_DS1511=m +CONFIG_RTC_DRV_DS1553=m +CONFIG_RTC_DRV_DS1685_FAMILY=m +CONFIG_RTC_DRV_DS1742=m +CONFIG_RTC_DRV_DS2404=m +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_STK17TA8=m +CONFIG_RTC_DRV_M48T86=m +CONFIG_RTC_DRV_M48T35=m +CONFIG_RTC_DRV_M48T59=m +CONFIG_RTC_DRV_MSM6242=m +CONFIG_RTC_DRV_BQ4802=m +CONFIG_RTC_DRV_RP5C01=m +CONFIG_RTC_DRV_V3020=m +CONFIG_RTC_DRV_OMAP=y +CONFIG_RTC_DRV_HID_SENSOR_TIME=m +CONFIG_DMADEVICES=y +CONFIG_TI_CPPI41=y +CONFIG_ASYNC_TX_DMA=y +CONFIG_AUXDISPLAY=y +CONFIG_HD44780=m +CONFIG_SEEED_I2C_HD44780=m +CONFIG_IMG_ASCII_LCD=m +CONFIG_HT16K33=m +CONFIG_UIO=m +CONFIG_VIRT_DRIVERS=y +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=m +CONFIG_GREYBUS=m +CONFIG_GREYBUS_ES2=m +CONFIG_STAGING=y +CONFIG_RTLLIB=m +CONFIG_RTL8723BS=m +CONFIG_R8712U=m +CONFIG_R8188EU=m +CONFIG_ADIS16203=m +CONFIG_ADIS16240=m +CONFIG_AD7816=m +CONFIG_AD7280=m +CONFIG_ADT7316=m +CONFIG_ADT7316_I2C=m +CONFIG_AD7150=m +CONFIG_AD7746=m +CONFIG_AD9832=m +CONFIG_AD9834=m +CONFIG_AD5933=m +CONFIG_ADE7854=m +CONFIG_AD2S1210=m +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9163=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SH1106=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1305=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_ST7789V=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1611=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_GREYBUS_AUDIO=m +CONFIG_GREYBUS_BOOTROM=m +CONFIG_GREYBUS_FIRMWARE=m +CONFIG_GREYBUS_HID=m +CONFIG_GREYBUS_LIGHT=m +CONFIG_GREYBUS_LOG=m +CONFIG_GREYBUS_LOOPBACK=m +CONFIG_GREYBUS_POWER=m +CONFIG_GREYBUS_RAW=m +CONFIG_GREYBUS_VIBRATOR=m +CONFIG_GREYBUS_BRIDGED_PHY=m +CONFIG_GREYBUS_GPIO=m +CONFIG_GREYBUS_I2C=m +CONFIG_GREYBUS_PWM=m +CONFIG_GREYBUS_SDIO=m +CONFIG_GREYBUS_SPI=m +CONFIG_GREYBUS_UART=m +CONFIG_GREYBUS_USB=m +CONFIG_COMMON_CLK_PALMAS=y +CONFIG_COMMON_CLK_TI_ADPLL=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_OMAP=y +CONFIG_OMAP2PLUS_MBOX=y +CONFIG_OMAP_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_OMAP_REMOTEPROC=m +CONFIG_WKUP_M3_RPROC=m +CONFIG_RPMSG_VIRTIO=m +CONFIG_RPMSG_PRU=m +CONFIG_SOC_TI=y +CONFIG_AMX3_PM=m +CONFIG_WKUP_M3_IPC=m +CONFIG_TI_PRUSS=m +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_PM_DEVFREQ_EVENT=y +CONFIG_EXTCON_GPIO=y +CONFIG_EXTCON_PALMAS=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_TI_EMIF=y +CONFIG_TI_EMIF_SRAM=y +CONFIG_IIO=y +CONFIG_IIO_SW_DEVICE=m +CONFIG_IIO_SW_TRIGGER=m +CONFIG_ADIS16201=m +CONFIG_ADIS16209=m +CONFIG_ADXL345_I2C=m +CONFIG_ADXL345_SPI=m +CONFIG_ADXL372_SPI=m +CONFIG_ADXL372_I2C=m +CONFIG_BMA180=m +CONFIG_BMA220=m +CONFIG_BMA400=m +CONFIG_BMC150_ACCEL=m +CONFIG_DA280=m +CONFIG_DA311=m +CONFIG_DMARD06=m +CONFIG_DMARD09=m +CONFIG_DMARD10=m +CONFIG_HID_SENSOR_ACCEL_3D=m +CONFIG_IIO_ST_ACCEL_3AXIS=m +CONFIG_KXSD9=m +CONFIG_KXCJK1013=m +CONFIG_MC3230=m +CONFIG_MMA7455_I2C=m +CONFIG_MMA7455_SPI=m +CONFIG_MMA7660=m +CONFIG_MMA8452=m +CONFIG_MMA9551=m +CONFIG_MMA9553=m +CONFIG_MXC4005=m +CONFIG_MXC6255=m +CONFIG_SCA3000=m +CONFIG_STK8312=m +CONFIG_STK8BA50=m +CONFIG_AD7091R5=m +CONFIG_AD7124=m +CONFIG_AD7192=m +CONFIG_AD7266=m +CONFIG_AD7291=m +CONFIG_AD7292=m +CONFIG_AD7298=m +CONFIG_AD7476=m +CONFIG_AD7606_IFACE_PARALLEL=m +CONFIG_AD7606_IFACE_SPI=m +CONFIG_AD7766=m +CONFIG_AD7768_1=m +CONFIG_AD7780=m +CONFIG_AD7791=m +CONFIG_AD7793=m +CONFIG_AD7887=m +CONFIG_AD7923=m +CONFIG_AD7949=m +CONFIG_AD799X=m +CONFIG_AD9467=m +CONFIG_ADI_AXI_ADC=m +CONFIG_CC10001_ADC=m +CONFIG_ENVELOPE_DETECTOR=m +CONFIG_HI8435=m +CONFIG_HX711=m +CONFIG_INA2XX_ADC=m +CONFIG_LTC2471=m +CONFIG_LTC2485=m +CONFIG_LTC2496=m +CONFIG_LTC2497=m +CONFIG_MAX1027=m +CONFIG_MAX11100=m +CONFIG_MAX1118=m +CONFIG_MAX1241=m +CONFIG_MAX1363=m +CONFIG_MAX9611=m +CONFIG_MCP320X=m +CONFIG_MCP3422=m +CONFIG_MCP3911=m +CONFIG_NAU7802=m +CONFIG_PALMAS_GPADC=m +CONFIG_SD_ADC_MODULATOR=m +CONFIG_STMPE_ADC=m +CONFIG_TI_ADC081C=m +CONFIG_TI_ADC0832=m +CONFIG_TI_ADC084S021=m +CONFIG_TI_ADC12138=m +CONFIG_TI_ADC108S102=m +CONFIG_TI_ADC128S052=m +CONFIG_TI_ADC161S626=m +CONFIG_TI_ADS1015=m +CONFIG_TI_ADS7950=m +CONFIG_TI_ADS8344=m +CONFIG_TI_ADS8688=m +CONFIG_TI_ADS124S08=m +CONFIG_TI_AM335X_ADC=y +CONFIG_TI_TLC4541=m +CONFIG_IIO_RESCALE=m +CONFIG_AD8366=m +CONFIG_HMC425=m +CONFIG_ATLAS_PH_SENSOR=m +CONFIG_ATLAS_EZO_SENSOR=m +CONFIG_BME680=m +CONFIG_CCS811=m +CONFIG_IAQCORE=m +CONFIG_PMS7003=m +CONFIG_SCD30_CORE=m +CONFIG_SCD30_I2C=m +CONFIG_SCD30_SERIAL=m +CONFIG_SENSIRION_SGP30=m +CONFIG_SPS30=m +CONFIG_VZ89X=m +CONFIG_AD5064=m +CONFIG_AD5360=m +CONFIG_AD5380=m +CONFIG_AD5421=m +CONFIG_AD5446=m +CONFIG_AD5449=m +CONFIG_AD5592R=m +CONFIG_AD5593R=m +CONFIG_AD5504=m +CONFIG_AD5624R_SPI=m +CONFIG_AD5686_SPI=m +CONFIG_AD5696_I2C=m +CONFIG_AD5755=m +CONFIG_AD5758=m +CONFIG_AD5761=m +CONFIG_AD5764=m +CONFIG_AD5770R=m +CONFIG_AD5791=m +CONFIG_AD7303=m +CONFIG_AD8801=m +CONFIG_DPOT_DAC=m +CONFIG_DS4424=m +CONFIG_LTC1660=m +CONFIG_LTC2632=m +CONFIG_M62332=m +CONFIG_MAX517=m +CONFIG_MAX5821=m +CONFIG_MCP4725=m +CONFIG_MCP4922=m +CONFIG_TI_DAC082S085=m +CONFIG_TI_DAC5571=m +CONFIG_TI_DAC7311=m +CONFIG_TI_DAC7612=m +CONFIG_AD9523=m +CONFIG_ADF4350=m +CONFIG_ADF4371=m +CONFIG_ADIS16080=m +CONFIG_ADIS16130=m +CONFIG_ADIS16136=m +CONFIG_ADIS16260=m +CONFIG_ADXRS290=m +CONFIG_ADXRS450=m +CONFIG_BMG160=m +CONFIG_FXAS21002C=m +CONFIG_HID_SENSOR_GYRO_3D=m +CONFIG_MPU3050_I2C=m +CONFIG_IIO_ST_GYRO_3AXIS=m +CONFIG_ITG3200=m +CONFIG_AFE4403=m +CONFIG_AFE4404=m +CONFIG_MAX30100=m +CONFIG_MAX30102=m +CONFIG_AM2315=m +CONFIG_DHT11=m +CONFIG_HDC100X=m +CONFIG_HDC2010=m +CONFIG_HID_SENSOR_HUMIDITY=m +CONFIG_HTS221=m +CONFIG_HTU21=m +CONFIG_SI7005=m +CONFIG_SI7020=m +CONFIG_ADIS16400=m +CONFIG_ADIS16460=m +CONFIG_ADIS16475=m +CONFIG_ADIS16480=m +CONFIG_BMI160_I2C=m +CONFIG_BMI160_SPI=m +CONFIG_FXOS8700_I2C=m +CONFIG_FXOS8700_SPI=m +CONFIG_KMX61=m +CONFIG_INV_ICM42600_I2C=m +CONFIG_INV_ICM42600_SPI=m +CONFIG_INV_MPU6050_I2C=m +CONFIG_INV_MPU6050_SPI=m +CONFIG_IIO_ST_LSM6DSX=m +CONFIG_ADJD_S311=m +CONFIG_ADUX1020=m +CONFIG_AL3010=m +CONFIG_AL3320A=m +CONFIG_APDS9300=m +CONFIG_APDS9960=m +CONFIG_AS73211=m +CONFIG_BH1750=m +CONFIG_BH1780=m +CONFIG_CM32181=m +CONFIG_CM3232=m +CONFIG_CM3323=m +CONFIG_CM3605=m +CONFIG_CM36651=m +CONFIG_GP2AP002=m +CONFIG_GP2AP020A00F=m +CONFIG_SENSORS_ISL29018=m +CONFIG_SENSORS_ISL29028=m +CONFIG_ISL29125=m +CONFIG_HID_SENSOR_ALS=m +CONFIG_HID_SENSOR_PROX=m +CONFIG_JSA1212=m +CONFIG_RPR0521=m +CONFIG_LTR501=m +CONFIG_LV0104CS=m +CONFIG_MAX44000=m +CONFIG_MAX44009=m +CONFIG_NOA1305=m +CONFIG_OPT3001=m +CONFIG_PA12203001=m +CONFIG_SI1133=m +CONFIG_SI1145=m +CONFIG_STK3310=m +CONFIG_ST_UVIS25=m +CONFIG_TCS3414=m +CONFIG_TCS3472=m +CONFIG_SENSORS_TSL2563=m +CONFIG_TSL2583=m +CONFIG_TSL2772=m +CONFIG_TSL4531=m +CONFIG_US5182D=m +CONFIG_VCNL4000=m +CONFIG_VCNL4035=m +CONFIG_VEML6030=m +CONFIG_VEML6070=m +CONFIG_VL6180=m +CONFIG_ZOPT2201=m +CONFIG_AK8974=m +CONFIG_AK09911=m +CONFIG_BMC150_MAGN_I2C=m +CONFIG_BMC150_MAGN_SPI=m +CONFIG_MAG3110=m +CONFIG_HID_SENSOR_MAGNETOMETER_3D=m +CONFIG_MMC35240=m +CONFIG_IIO_ST_MAGN_3AXIS=m +CONFIG_SENSORS_HMC5843_I2C=m +CONFIG_SENSORS_HMC5843_SPI=m +CONFIG_SENSORS_RM3100_I2C=m +CONFIG_SENSORS_RM3100_SPI=m +CONFIG_HID_SENSOR_INCLINOMETER_3D=m +CONFIG_HID_SENSOR_DEVICE_ROTATION=m +CONFIG_IIO_HRTIMER_TRIGGER=m +CONFIG_IIO_INTERRUPT_TRIGGER=m +CONFIG_IIO_TIGHTLOOP_TRIGGER=m +CONFIG_IIO_SYSFS_TRIGGER=m +CONFIG_AD5272=m +CONFIG_DS1803=m +CONFIG_MAX5432=m +CONFIG_MAX5481=m +CONFIG_MAX5487=m +CONFIG_MCP4018=m +CONFIG_MCP4131=m +CONFIG_MCP4531=m +CONFIG_MCP41010=m +CONFIG_TPL0102=m +CONFIG_LMP91000=m +CONFIG_ABP060MG=m +CONFIG_BMP280=m +CONFIG_DLHL60D=m +CONFIG_DPS310=m +CONFIG_HID_SENSOR_PRESS=m +CONFIG_HP03=m +CONFIG_ICP10100=m +CONFIG_MPL115_I2C=m +CONFIG_MPL115_SPI=m +CONFIG_MPL3115=m +CONFIG_MS5611=m +CONFIG_MS5611_I2C=m +CONFIG_MS5611_SPI=m +CONFIG_MS5637=m +CONFIG_IIO_ST_PRESS=m +CONFIG_T5403=m +CONFIG_HP206C=m +CONFIG_ZPA2326=m +CONFIG_AS3935=m +CONFIG_ISL29501=m +CONFIG_LIDAR_LITE_V2=m +CONFIG_MB1232=m +CONFIG_PING=m +CONFIG_RFD77402=m +CONFIG_SRF04=m +CONFIG_SX9310=m +CONFIG_SX9500=m +CONFIG_SRF08=m +CONFIG_VCNL3020=m +CONFIG_VL53L0X_I2C=m +CONFIG_AD2S90=m +CONFIG_AD2S1200=m +CONFIG_LTC2983=m +CONFIG_MAXIM_THERMOCOUPLE=m +CONFIG_HID_SENSOR_TEMP=m +CONFIG_MLX90614=m +CONFIG_MLX90632=m +CONFIG_TMP006=m +CONFIG_TMP007=m +CONFIG_TSYS01=m +CONFIG_TSYS02D=m +CONFIG_MAX31856=m +CONFIG_PWM_OMAP_DMTIMER=y +CONFIG_PWM_PCA9685=y +CONFIG_PWM_STMPE=y +CONFIG_PWM_TIECAP=y +CONFIG_PWM_TIEHRPWM=y +CONFIG_RESET_TI_SYSCON=y +CONFIG_PHY_CAN_TRANSCEIVER=m +CONFIG_OMAP_USB2=y +CONFIG_TI_PIPE3=y +CONFIG_RAS=y +CONFIG_FPGA=m +CONFIG_ALTERA_PR_IP_CORE=m +CONFIG_ALTERA_PR_IP_CORE_PLAT=m +CONFIG_FPGA_MGR_ALTERA_PS_SPI=m +CONFIG_FPGA_MGR_XILINX_SPI=m +CONFIG_FPGA_MGR_ICE40_SPI=m +CONFIG_FPGA_MGR_MACHXO2_SPI=m +CONFIG_FPGA_BRIDGE=m +CONFIG_ALTERA_FREEZE_BRIDGE=m +CONFIG_XILINX_PR_DECOUPLER=m +CONFIG_FPGA_REGION=m +CONFIG_OF_FPGA_REGION=m +CONFIG_COUNTER=m +CONFIG_TI_EQEP=m +CONFIG_VALIDATE_FS_PARSER=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FS_VERITY=y +CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=m +CONFIG_VIRTIO_FS=m +CONFIG_OVERLAY_FS=y +CONFIG_FSCACHE=m +CONFIG_FSCACHE_STATS=y +CONFIG_CACHEFILES=m +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +CONFIG_FAT_DEFAULT_UTF8=y +CONFIG_EXFAT_FS=m +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=m +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RUBIN=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_SQUASHFS=m +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_ROMFS_FS=m +CONFIG_ROMFS_BACKED_BY_BOTH=y +CONFIG_AUFS_FS=m +CONFIG_AUFS_EXPORT=y +CONFIG_AUFS_XATTR=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=m +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_V4_SECURITY_LABEL=y +CONFIG_SUNRPC_DEBUG=y +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_CEPH_FS_POSIX_ACL=y +CONFIG_CIFS=m +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_DFS_UPCALL=y +CONFIG_CIFS_FSCACHE=y +CONFIG_SMB_SERVER=m +CONFIG_SMB_INSECURE_SERVER=y +CONFIG_CODA_FS=m +CONFIG_AFS_FS=m +CONFIG_AFS_FSCACHE=y +CONFIG_9P_FS=m +CONFIG_9P_FSCACHE=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_9P_FS_SECURITY=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m +CONFIG_DLM=m +CONFIG_DLM_DEBUG=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK_XFRM=y +CONFIG_HARDENED_USERCOPY=y +# CONFIG_HARDENED_USERCOPY_FALLBACK is not set +CONFIG_FORTIFY_SOURCE=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_TOMOYO=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_YAMA=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +# CONFIG_INTEGRITY_TRUSTED_KEYRING is not set +CONFIG_DEFAULT_SECURITY_APPARMOR=y +CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_LIB_BLAKE2S=y +CONFIG_CRYPTO_LIB_CURVE25519=y +CONFIG_CRYPTO_LIB_CHACHA20POLY1305=y +CONFIG_CRYPTO_DEV_OMAP=y +CONFIG_CRYPTO_DEV_OMAP_SHAM=y +CONFIG_CRYPTO_DEV_OMAP_AES=y +CONFIG_CRYPTO_DEV_OMAP_DES=y +CONFIG_CRYPTO_DEV_ATMEL_ECC=y +CONFIG_CRYPTO_DEV_ATMEL_SHA204A=y +CONFIG_CRYPTO_DEV_VIRTIO=m +# CONFIG_RAID6_PQ_BENCHMARK is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_CMA_SIZE_MBYTES=48 +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_FONT_TER16x32=y +CONFIG_PRINTK_TIME=y +CONFIG_BOOT_PRINTK_DELAY=y +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_PAGE_EXTENSION=y +CONFIG_DEBUG_WX=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_SCHEDSTATS=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_RUNTIME_TESTING_MENU is not set diff -Naur --no-dereference a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h --- a/arch/arm/include/asm/pgtable-3level.h 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/include/asm/pgtable-3level.h 2022-01-06 12:45:53.810318090 -0500 @@ -68,7 +68,12 @@ #define L_PTE_VALID (_AT(pteval_t, 1) << 0) /* Valid */ #define L_PTE_PRESENT (_AT(pteval_t, 3) << 0) /* Present */ #define L_PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */ +#ifdef CONFIG_KEYSTONE2_DMA_COHERENT +/* SH[1:0], outer shareable */ +#define L_PTE_SHARED (_AT(pteval_t, 2) << 8) +#else #define L_PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ +#endif #define L_PTE_YOUNG (_AT(pteval_t, 1) << 10) /* AF */ #define L_PTE_XN (_AT(pteval_t, 1) << 54) /* XN */ #define L_PTE_DIRTY (_AT(pteval_t, 1) << 55) diff -Naur --no-dereference a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h --- a/arch/arm/include/asm/pgtable-3level-hwdef.h 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/include/asm/pgtable-3level-hwdef.h 2022-01-06 12:45:53.810318090 -0500 @@ -32,7 +32,11 @@ #define PMD_SECT_CACHEABLE (_AT(pmdval_t, 1) << 3) #define PMD_SECT_USER (_AT(pmdval_t, 1) << 6) /* AP[1] */ #define PMD_SECT_AP2 (_AT(pmdval_t, 1) << 7) /* read only */ +#ifdef CONFIG_KEYSTONE2_DMA_COHERENT +#define PMD_SECT_S (_AT(pmdval_t, 2) << 8) +#else #define PMD_SECT_S (_AT(pmdval_t, 3) << 8) +#endif #define PMD_SECT_AF (_AT(pmdval_t, 1) << 10) #define PMD_SECT_nG (_AT(pmdval_t, 1) << 11) #define PMD_SECT_PXN (_AT(pmdval_t, 1) << 53) @@ -62,7 +66,12 @@ #define PTE_BUFFERABLE (_AT(pteval_t, 1) << 2) /* AttrIndx[0] */ #define PTE_CACHEABLE (_AT(pteval_t, 1) << 3) /* AttrIndx[1] */ #define PTE_AP2 (_AT(pteval_t, 1) << 7) /* AP[2] */ +#ifdef CONFIG_KEYSTONE2_DMA_COHERENT +/* SH[1:0], outer shareable */ +#define PTE_EXT_SHARED (_AT(pteval_t, 2) << 8) +#else #define PTE_EXT_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ +#endif #define PTE_EXT_AF (_AT(pteval_t, 1) << 10) /* Access Flag */ #define PTE_EXT_NG (_AT(pteval_t, 1) << 11) /* nG */ #define PTE_EXT_PXN (_AT(pteval_t, 1) << 53) /* PXN */ diff -Naur --no-dereference a/arch/arm/include/asm/xor.h b/arch/arm/include/asm/xor.h --- a/arch/arm/include/asm/xor.h 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/include/asm/xor.h 2022-01-06 12:45:53.810318090 -0500 @@ -209,3 +209,9 @@ #else #define NEON_TEMPLATES #endif + +#ifdef CONFIG_KERNEL_MODE_NEON +#define XOR_SELECT_TEMPLATE(FASTEST) (&xor_block_neon) +#else +#define XOR_SELECT_TEMPLATE(FASTEST) (&xor_block_arm4regs) +#endif diff -Naur --no-dereference a/arch/arm/mach-keystone/Kconfig b/arch/arm/mach-keystone/Kconfig --- a/arch/arm/mach-keystone/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/mach-keystone/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -12,6 +12,10 @@ select ZONE_DMA if ARM_LPAE select PINCTRL select PM_GENERIC_DOMAINS if PM + select KEYSTONE2_DMA_COHERENT help Support for boards based on the Texas Instruments Keystone family of SoCs. + +config KEYSTONE2_DMA_COHERENT + bool diff -Naur --no-dereference a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c --- a/arch/arm/mach-omap1/clock.c 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/mach-omap1/clock.c 2022-01-06 12:45:53.810318090 -0500 @@ -612,7 +612,7 @@ unsigned long flags; int ret; - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return -EINVAL; spin_lock_irqsave(&clockfw_lock, flags); @@ -627,7 +627,7 @@ { unsigned long flags; - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return; spin_lock_irqsave(&clockfw_lock, flags); @@ -650,7 +650,7 @@ unsigned long flags; unsigned long ret; - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return 0; spin_lock_irqsave(&clockfw_lock, flags); @@ -670,7 +670,7 @@ unsigned long flags; long ret; - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return 0; spin_lock_irqsave(&clockfw_lock, flags); @@ -686,7 +686,7 @@ unsigned long flags; int ret = -EINVAL; - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return ret; spin_lock_irqsave(&clockfw_lock, flags); @@ -791,7 +791,7 @@ int clk_register(struct clk *clk) { - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return -EINVAL; /* @@ -817,7 +817,7 @@ void clk_unregister(struct clk *clk) { - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return; mutex_lock(&clocks_mutex); diff -Naur --no-dereference a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c --- a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c 2022-01-06 12:45:53.810318090 -0500 @@ -235,7 +235,7 @@ hw = kzalloc(sizeof(*hw), GFP_KERNEL); if (!hw) - goto cleanup; + return; init.name = "virt_prcm_set"; init.ops = &virt_prcm_set_ops; init.parent_names = &parent_name; @@ -244,9 +244,12 @@ hw->hw.init = &init; clk = clk_register(NULL, &hw->hw); + if (IS_ERR(clk)) { + printk(KERN_ERR "Failed to register clock\n"); + kfree(hw); + return; + } + clkdev_create(clk, "cpufreq_ck", NULL); - return; -cleanup: - kfree(hw); } #endif diff -Naur --no-dereference a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c --- a/arch/arm/mach-omap2/display.c 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/mach-omap2/display.c 2022-01-06 12:45:53.810318090 -0500 @@ -385,8 +385,7 @@ } for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) - if (oc->_clk) - clk_prepare_enable(oc->_clk); + clk_prepare_enable(oc->_clk); dispc_disable_outputs(); @@ -412,8 +411,7 @@ pr_debug("dss_core: softreset done\n"); for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) - if (oc->_clk) - clk_disable_unprepare(oc->_clk); + clk_disable_unprepare(oc->_clk); r = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0; diff -Naur --no-dereference a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c --- a/arch/arm/mach-omap2/omap_device.c 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/mach-omap2/omap_device.c 2022-01-06 12:45:53.810318090 -0500 @@ -336,10 +336,9 @@ struct omap_hwmod **hwmods; od = kzalloc(sizeof(struct omap_device), GFP_KERNEL); - if (!od) { - ret = -ENOMEM; + if (!od) goto oda_exit1; - } + od->hwmods_cnt = oh_cnt; hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); diff -Naur --no-dereference a/arch/arm/Makefile b/arch/arm/Makefile --- a/arch/arm/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm/Makefile 2022-01-06 12:45:53.806318073 -0500 @@ -56,6 +56,9 @@ # KBUILD_CFLAGS += $(call cc-option,-fno-ipa-sra) +# Need -msoft-float for gcc 11 for the below instruction set selection +KBUILD_CFLAGS += -msoft-float + # This selects which instruction set is used. # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes @@ -125,7 +128,7 @@ endif # Need -Uarm for gcc < 3.x -KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm +KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -Uarm KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float CHECKFLAGS += -D__arm__ diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am642.dtsi b/arch/arm64/boot/dts/ti/k3-am642.dtsi --- a/arch/arm64/boot/dts/ti/k3-am642.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am642.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for AM642 SoC family in Dual core configuration + * + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "k3-am64.dtsi" + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu-map { + cluster0: cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + }; + }; + + cpu0: cpu@0 { + compatible = "arm,cortex-a53"; + reg = <0x000>; + device_type = "cpu"; + enable-method = "psci"; + i-cache-size = <0x8000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <128>; + next-level-cache = <&L2_0>; + }; + + cpu1: cpu@1 { + compatible = "arm,cortex-a53"; + reg = <0x001>; + device_type = "cpu"; + enable-method = "psci"; + i-cache-size = <0x8000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <128>; + next-level-cache = <&L2_0>; + }; + }; + + L2_0: l2-cache0 { + compatible = "cache"; + cache-level = <2>; + cache-size = <0x40000>; + cache-line-size = <64>; + cache-sets = <512>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am642-evm.dts b/arch/arm64/boot/dts/ti/k3-am642-evm.dts --- a/arch/arm64/boot/dts/ti/k3-am642-evm.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am642-evm.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include +#include +#include +#include +#include +#include "k3-am642.dtsi" + +/ { + compatible = "ti,am642-evm", "ti,am642"; + model = "Texas Instruments AM642 EVM"; + + chosen { + stdout-path = "serial2:115200n8"; + bootargs = "console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000"; + }; + + aliases { + ethernet2 = &icssg1_emac0; + ethernet3 = &icssg1_emac1; + }; + + memory@80000000 { + device_type = "memory"; + /* 2G RAM */ + reg = <0x00000000 0x80000000 0x00000000 0x80000000>; + + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + secure_ddr: optee@9e800000 { + reg = <0x00 0x9e800000 0x00 0x01800000>; /* for OP-TEE */ + alignment = <0x1000>; + no-map; + }; + + main_r5fss0_core0_dma_memory_region: r5f-dma-memory@a0000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core0_memory_region: r5f-memory@a0100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core1_dma_memory_region: r5f-dma-memory@a1000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core1_memory_region: r5f-memory@a1100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core0_dma_memory_region: r5f-dma-memory@a2000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core0_memory_region: r5f-memory@a2100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core1_dma_memory_region: r5f-dma-memory@a3000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core1_memory_region: r5f-memory@a3100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3100000 0x00 0xf00000>; + no-map; + }; + + mcu_m4fss_dma_memory_region: m4f-dma-memory@a4000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4000000 0x00 0x100000>; + no-map; + }; + + mcu_m4fss_memory_region: m4f-memory@a4100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4100000 0x00 0xf00000>; + no-map; + }; + + rtos_ipc_memory_region: ipc-memories@a5000000 { + reg = <0x00 0xa5000000 0x00 0x00800000>; + alignment = <0x1000>; + no-map; + }; + }; + + evm_12v0: fixedregulator-evm12v0 { + /* main DC jack */ + compatible = "regulator-fixed"; + regulator-name = "evm_12v0"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_5v0: fixedregulator-vsys5v0 { + /* output of LM5140 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&evm_12v0>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_3v3: fixedregulator-vsys3v3 { + /* output of LM5140 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&evm_12v0>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_mmc1: fixed-regulator-sd { + /* TPS2051BD */ + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vsys_3v3>; + gpio = <&exp1 6 GPIO_ACTIVE_HIGH>; + }; + + vddb: fixedregulator-vddb { + compatible = "regulator-fixed"; + regulator-name = "vddb_3v3_display"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vsys_3v3>; + regulator-always-on; + regulator-boot-on; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + label = "am64-evm:red:heartbeat"; + gpios = <&exp1 16 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + function = LED_FUNCTION_HEARTBEAT; + default-state = "off"; + }; + }; + + mdio_mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = <&exp1 12 GPIO_ACTIVE_HIGH>; + }; + + mdio_mux_1: mdio-mux-1 { + compatible = "mdio-mux-multiplexer"; + mux-controls = <&mdio_mux>; + mdio-parent-bus = <&cpsw3g_mdio>; + #address-cells = <1>; + #size-cells = <0>; + + mdio@1 { + reg = <0x1>; + #address-cells = <1>; + #size-cells = <0>; + + cpsw3g_phy3: ethernet-phy@3 { + reg = <3>; + }; + }; + }; + + transceiver1: can-phy0 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + standby-gpios = <&exp1 8 GPIO_ACTIVE_HIGH>; + }; + + transceiver2: can-phy1 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + standby-gpios = <&exp1 9 GPIO_ACTIVE_HIGH>; + }; + + icssg1_eth: icssg1-eth { + compatible = "ti,am642-icssg-prueth"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg1_rgmii1_pins_default>; + + sram = <&oc_sram>; + ti,prus = <&pru1_0>, <&rtu1_0>, <&tx_pru1_0>, <&pru1_1>, <&rtu1_1>, <&tx_pru1_1>; + firmware-name = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf"; + + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, + <2>, /* MII mode */ + <2>, + <2>; + + mii-g-rt = <&icssg1_mii_g_rt>; + mii-rt = <&icssg1_mii_rt>; + iep = <&icssg1_iep0>, <&icssg1_iep1>; + + interrupt-parent = <&icssg1_intc>; + interrupts = <24 0 2>, <25 1 3>; + interrupt-names = "tx_ts0", "tx_ts1"; + + dmas = <&main_pktdma 0xc200 15>, /* egress slice 0 */ + <&main_pktdma 0xc201 15>, /* egress slice 0 */ + <&main_pktdma 0xc202 15>, /* egress slice 0 */ + <&main_pktdma 0xc203 15>, /* egress slice 0 */ + <&main_pktdma 0xc204 15>, /* egress slice 1 */ + <&main_pktdma 0xc205 15>, /* egress slice 1 */ + <&main_pktdma 0xc206 15>, /* egress slice 1 */ + <&main_pktdma 0xc207 15>, /* egress slice 1 */ + <&main_pktdma 0x4200 15>, /* ingress slice 0 */ + <&main_pktdma 0x4201 15>, /* ingress slice 1 */ + <&main_pktdma 0x4202 0>, /* mgmnt rsp slice 0 */ + <&main_pktdma 0x4203 0>; /* mgmnt rsp slice 1 */ + dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1", + "rxmgm0", "rxmgm1"; + + icssg1_emac0: ethernet-mii0 { + phy-handle = <&icssg1_phy1>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&main_conf 0x4110>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + icssg1_emac1: ethernet-mii1 { + syscon-rgmii-delay = <&main_conf 0x4114>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + status = "disabled"; + }; + }; +}; + +&main_r5fss0_core0 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss0_core0>; + memory-region = <&main_r5fss0_core0_dma_memory_region>, + <&main_r5fss0_core0_memory_region>; +}; + +&main_r5fss0_core1 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss0_core1>; + memory-region = <&main_r5fss0_core1_dma_memory_region>, + <&main_r5fss0_core1_memory_region>; +}; + +&main_r5fss1_core0 { + mboxes = <&mailbox0_cluster4 &mbox_main_r5fss1_core0>; + memory-region = <&main_r5fss1_core0_dma_memory_region>, + <&main_r5fss1_core0_memory_region>; +}; + +&main_r5fss1_core1 { + mboxes = <&mailbox0_cluster4 &mbox_main_r5fss1_core1>; + memory-region = <&main_r5fss1_core1_dma_memory_region>, + <&main_r5fss1_core1_memory_region>; +}; + +&mcu_m4fss { + mboxes = <&mailbox0_cluster6 &mbox_m4_0>; + memory-region = <&mcu_m4fss_dma_memory_region>, + <&mcu_m4fss_memory_region>; +}; + +&main_pmx0 { + main_mmc1_pins_default: main-mmc1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0294, PIN_INPUT_PULLUP, 0) /* (J19) MMC1_CMD */ + AM64X_IOPAD(0x028c, PIN_INPUT_PULLDOWN, 0) /* (L20) MMC1_CLK */ + AM64X_IOPAD(0x0288, PIN_INPUT_PULLUP, 0) /* (K21) MMC1_DAT0 */ + AM64X_IOPAD(0x0284, PIN_INPUT_PULLUP, 0) /* (L21) MMC1_DAT1 */ + AM64X_IOPAD(0x0280, PIN_INPUT_PULLUP, 0) /* (K19) MMC1_DAT2 */ + AM64X_IOPAD(0x027c, PIN_INPUT_PULLUP, 0) /* (K18) MMC1_DAT3 */ + AM64X_IOPAD(0x0298, PIN_INPUT_PULLUP, 0) /* (D19) MMC1_SDCD */ + AM64X_IOPAD(0x029c, PIN_INPUT, 0) /* (C20) MMC1_SDWP */ + AM64X_IOPAD(0x0290, PIN_INPUT, 0) /* MMC1_CLKLB */ + >; + }; + + main_uart0_pins_default: main-uart0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0238, PIN_INPUT, 0) /* (B16) UART0_CTSn */ + AM64X_IOPAD(0x023c, PIN_OUTPUT, 0) /* (A16) UART0_RTSn */ + AM64X_IOPAD(0x0230, PIN_INPUT, 0) /* (D15) UART0_RXD */ + AM64X_IOPAD(0x0234, PIN_OUTPUT, 0) /* (C16) UART0_TXD */ + >; + }; + + main_spi0_pins_default: main-spi0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0210, PIN_INPUT, 0) /* (D13) SPI0_CLK */ + AM64X_IOPAD(0x0208, PIN_OUTPUT, 0) /* (D12) SPI0_CS0 */ + AM64X_IOPAD(0x0214, PIN_OUTPUT, 0) /* (A13) SPI0_D0 */ + AM64X_IOPAD(0x0218, PIN_INPUT, 0) /* (A14) SPI0_D1 */ + >; + }; + + main_i2c1_pins_default: main-i2c1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0268, PIN_INPUT_PULLUP, 0) /* (C18) I2C1_SCL */ + AM64X_IOPAD(0x026c, PIN_INPUT_PULLUP, 0) /* (B19) I2C1_SDA */ + >; + }; + + mdio1_pins_default: mdio1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x01fc, PIN_OUTPUT, 4) /* (R2) PRG0_PRU1_GPO19.MDIO0_MDC */ + AM64X_IOPAD(0x01f8, PIN_INPUT, 4) /* (P5) PRG0_PRU1_GPO18.MDIO0_MDIO */ + >; + }; + + rgmii1_pins_default: rgmii1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x01cc, PIN_INPUT, 4) /* (W5) PRG0_PRU1_GPO7.RGMII1_RD0 */ + AM64X_IOPAD(0x01d4, PIN_INPUT, 4) /* (Y5) PRG0_PRU1_GPO9.RGMII1_RD1 */ + AM64X_IOPAD(0x01d8, PIN_INPUT, 4) /* (V6) PRG0_PRU1_GPO10.RGMII1_RD2 */ + AM64X_IOPAD(0x01f4, PIN_INPUT, 4) /* (V5) PRG0_PRU1_GPO17.RGMII1_RD3 */ + AM64X_IOPAD(0x0188, PIN_INPUT, 4) /* (AA5) PRG0_PRU0_GPO10.RGMII1_RXC */ + AM64X_IOPAD(0x0184, PIN_INPUT, 4) /* (W6) PRG0_PRU0_GPO9.RGMII1_RX_CTL */ + AM64X_IOPAD(0x0124, PIN_OUTPUT, 4) /* (V15) PRG1_PRU1_GPO7.RGMII1_TD0 */ + AM64X_IOPAD(0x012c, PIN_OUTPUT, 4) /* (V14) PRG1_PRU1_GPO9.RGMII1_TD1 */ + AM64X_IOPAD(0x0130, PIN_OUTPUT, 4) /* (W14) PRG1_PRU1_GPO10.RGMII1_TD2 */ + AM64X_IOPAD(0x014c, PIN_OUTPUT, 4) /* (AA14) PRG1_PRU1_GPO17.RGMII1_TD3 */ + AM64X_IOPAD(0x00e0, PIN_OUTPUT, 4) /* (U14) PRG1_PRU0_GPO10.RGMII1_TXC */ + AM64X_IOPAD(0x00dc, PIN_OUTPUT, 4) /* (U15) PRG1_PRU0_GPO9.RGMII1_TX_CTL */ + >; + }; + + rgmii2_pins_default: rgmii2-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0108, PIN_INPUT, 4) /* (W11) PRG1_PRU1_GPO0.RGMII2_RD0 */ + AM64X_IOPAD(0x010c, PIN_INPUT, 4) /* (V11) PRG1_PRU1_GPO1.RGMII2_RD1 */ + AM64X_IOPAD(0x0110, PIN_INPUT, 4) /* (AA12) PRG1_PRU1_GPO2.RGMII2_RD2 */ + AM64X_IOPAD(0x0114, PIN_INPUT, 4) /* (Y12) PRG1_PRU1_GPO3.RGMII2_RD3 */ + AM64X_IOPAD(0x0120, PIN_INPUT, 4) /* (U11) PRG1_PRU1_GPO6.RGMII2_RXC */ + AM64X_IOPAD(0x0118, PIN_INPUT, 4) /* (W12) PRG1_PRU1_GPO4.RGMII2_RX_CTL */ + AM64X_IOPAD(0x0134, PIN_OUTPUT, 4) /* (AA10) PRG1_PRU1_GPO11.RGMII2_TD0 */ + AM64X_IOPAD(0x0138, PIN_OUTPUT, 4) /* (V10) PRG1_PRU1_GPO12.RGMII2_TD1 */ + AM64X_IOPAD(0x013c, PIN_OUTPUT, 4) /* (U10) PRG1_PRU1_GPO13.RGMII2_TD2 */ + AM64X_IOPAD(0x0140, PIN_OUTPUT, 4) /* (AA11) PRG1_PRU1_GPO14.RGMII2_TD3 */ + AM64X_IOPAD(0x0148, PIN_OUTPUT, 4) /* (Y10) PRG1_PRU1_GPO16.RGMII2_TXC */ + AM64X_IOPAD(0x0144, PIN_OUTPUT, 4) /* (Y11) PRG1_PRU1_GPO15.RGMII2_TX_CTL */ + >; + }; + + main_usb0_pins_default: main-usb0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x02a8, PIN_OUTPUT, 0) /* (E19) USB0_DRVVBUS */ + >; + }; + + ospi0_pins_default: ospi0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0000, PIN_OUTPUT, 0) /* (N20) OSPI0_CLK */ + AM64X_IOPAD(0x002c, PIN_OUTPUT, 0) /* (L19) OSPI0_CSn0 */ + AM64X_IOPAD(0x000c, PIN_INPUT, 0) /* (M19) OSPI0_D0 */ + AM64X_IOPAD(0x0010, PIN_INPUT, 0) /* (M18) OSPI0_D1 */ + AM64X_IOPAD(0x0014, PIN_INPUT, 0) /* (M20) OSPI0_D2 */ + AM64X_IOPAD(0x0018, PIN_INPUT, 0) /* (M21) OSPI0_D3 */ + AM64X_IOPAD(0x001c, PIN_INPUT, 0) /* (P21) OSPI0_D4 */ + AM64X_IOPAD(0x0020, PIN_INPUT, 0) /* (P20) OSPI0_D5 */ + AM64X_IOPAD(0x0024, PIN_INPUT, 0) /* (N18) OSPI0_D6 */ + AM64X_IOPAD(0x0028, PIN_INPUT, 0) /* (M17) OSPI0_D7 */ + AM64X_IOPAD(0x0008, PIN_INPUT, 0) /* (N19) OSPI0_DQS */ + >; + }; + + main_mcan0_pins_default: main-mcan0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0254, PIN_INPUT, 0) /* (B17) MCAN0_RX */ + AM64X_IOPAD(0x0250, PIN_OUTPUT, 0) /* (A17) MCAN0_TX */ + >; + }; + + main_mcan1_pins_default: main-mcan1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x025c, PIN_INPUT, 0) /* (D17) MCAN1_RX */ + AM64X_IOPAD(0x0258, PIN_OUTPUT, 0) /* (C17) MCAN1_TX */ + >; + }; + + icssg1_mdio1_pins_default: icssg1-mdio1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x015c, PIN_OUTPUT, 0) /* (Y6) PRG1_MDIO0_MDC */ + AM64X_IOPAD(0x0158, PIN_INPUT, 0) /* (AA6) PRG1_MDIO0_MDIO */ + >; + }; + + icssg1_rgmii1_pins_default: icssg1-rgmii1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x00b8, PIN_INPUT, 2) /* (Y7) PRG1_PRU0_GPO0.PRG1_RGMII1_RD0 */ + AM64X_IOPAD(0x00bc, PIN_INPUT, 2) /* (U8) PRG1_PRU0_GPO1.PRG1_RGMII1_RD1 */ + AM64X_IOPAD(0x00c0, PIN_INPUT, 2) /* (W8) PRG1_PRU0_GPO2.PRG1_RGMII1_RD2 */ + AM64X_IOPAD(0x00c4, PIN_INPUT, 2) /* (V8) PRG1_PRU0_GPO3.PRG1_RGMII1_RD3 */ + AM64X_IOPAD(0x00d0, PIN_INPUT, 2) /* (AA7) PRG1_PRU0_GPO6.PRG1_RGMII1_RXC */ + AM64X_IOPAD(0x00c8, PIN_INPUT, 2) /* (Y8) PRG1_PRU0_GPO4.PRG1_RGMII1_RX_CTL */ + AM64X_IOPAD(0x00e4, PIN_INPUT, 2) /* (AA8) PRG1_PRU0_GPO11.PRG1_RGMII1_TD0 */ + AM64X_IOPAD(0x00e8, PIN_INPUT, 2) /* (U9) PRG1_PRU0_GPO12.PRG1_RGMII1_TD1 */ + AM64X_IOPAD(0x00ec, PIN_INPUT, 2) /* (W9) PRG1_PRU0_GPO13.PRG1_RGMII1_TD2 */ + AM64X_IOPAD(0x00f0, PIN_INPUT, 2) /* (AA9) PRG1_PRU0_GPO14.PRG1_RGMII1_TD3 */ + AM64X_IOPAD(0x00f8, PIN_INPUT, 2) /* (V9) PRG1_PRU0_GPO16.PRG1_RGMII1_TXC */ + AM64X_IOPAD(0x00f4, PIN_INPUT, 2) /* (Y9) PRG1_PRU0_GPO15.PRG1_RGMII1_TX_CTL */ + >; + }; + + main_ecap0_pins_default: main-ecap0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0270, PIN_INPUT, 0) /* (D18) ECAP0_IN_APWM_OUT */ + >; + }; +}; + +&main_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_uart0_pins_default>; +}; + +/* main_uart1 is reserved for firmware usage */ +&main_uart1 { + status = "reserved"; +}; + +&main_uart2 { + status = "disabled"; +}; + +&main_uart3 { + status = "disabled"; +}; + +&main_uart4 { + status = "disabled"; +}; + +&main_uart5 { + status = "disabled"; +}; + +&main_uart6 { + status = "disabled"; +}; + +&mcu_uart0 { + status = "disabled"; +}; + +&mcu_uart1 { + status = "disabled"; +}; + +&main_i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c1_pins_default>; + clock-frequency = <400000>; + + exp1: gpio@22 { + compatible = "ti,tca6424"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "GPIO_eMMC_RSTn", "CAN_MUX_SEL", + "GPIO_CPSW1_RST", "GPIO_RGMII1_RST", + "GPIO_RGMII2_RST", "GPIO_PCIe_RST_OUT", + "MMC1_SD_EN", "FSI_FET_SEL", + "MCAN0_STB_3V3", "MCAN1_STB_3V3", + "CPSW_FET_SEL", "CPSW_FET2_SEL", + "PRG1_RGMII2_FET_SEL", "TEST_GPIO2", + "GPIO_OLED_RESETn", "VPP_LDO_EN", + "TEST_LED1", "TP92", "TP90", "TP88", + "TP87", "TP86", "TP89", "TP91"; + }; + + /* osd9616p0899-10 */ + display@3c { + compatible = "solomon,ssd1306fb-i2c"; + reg = <0x3c>; + reset-gpios = <&exp1 14 GPIO_ACTIVE_LOW>; + vbat-supply = <&vddb>; + solomon,height = <16>; + solomon,width = <96>; + solomon,com-seq; + solomon,com-invdir; + solomon,page-offset = <0>; + solomon,prechargep1 = <2>; + solomon,prechargep2 = <13>; + }; +}; + +/* mcu_gpio0 is reserved for mcu firmware usage */ +&mcu_gpio0 { + status = "reserved"; +}; + +&mcu_i2c0 { + status = "disabled"; +}; + +&mcu_i2c1 { + status = "disabled"; +}; + +&mcu_spi0 { + status = "disabled"; +}; + +&mcu_spi1 { + status = "disabled"; +}; + +&main_spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_spi0_pins_default>; + ti,pindir-d0-out-d1-in = <1>; + eeprom@0 { + compatible = "microchip,93lc46b"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cs-high; + data-size = <16>; + }; +}; + +&sdhci0 { + /* emmc */ + bus-width = <8>; + non-removable; + ti,driver-strength-ohm = <50>; + disable-wp; +}; + +&sdhci1 { + /* SD/MMC */ + vmmc-supply = <&vdd_mmc1>; + pinctrl-names = "default"; + bus-width = <4>; + pinctrl-0 = <&main_mmc1_pins_default>; + ti,driver-strength-ohm = <50>; + disable-wp; +}; + +&usbss0 { + ti,vbus-divider; + ti,usb2-only; +}; + +&usb0 { + dr_mode = "otg"; + maximum-speed = "high-speed"; + pinctrl-names = "default"; + pinctrl-0 = <&main_usb0_pins_default>; +}; + +&cpsw3g { + pinctrl-names = "default"; + pinctrl-0 = <&mdio1_pins_default + &rgmii1_pins_default + &rgmii2_pins_default>; + + cpts@3d000 { + ti,pps = <7 1>; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-rxid"; + phy-handle = <&cpsw3g_phy0>; +}; + +&cpsw_port2 { + phy-mode = "rgmii-rxid"; + phy-handle = <&cpsw3g_phy3>; +}; + +&cpsw3g_mdio { + cpsw3g_phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; +}; + +#define TS_OFFSET(pa, val) (0x4+(pa)*4) (0x10000 | val) + +×ync_router { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_cpts_pps>; + + /* Example of the timesync routing */ + mcu_cpts_pps: mcu-cpts-pps { + pinctrl-single,pins = < + /* pps [cpts genf1] in22 -> out37 [cpts hw8_push] */ + TS_OFFSET(37, 22) + /* pps [cpts genf1] in22 -> out25 [SYNC1_OUT pin] */ + TS_OFFSET(25, 22) + >; + }; +}; + +&mailbox0_cluster2 { + mbox_main_r5fss0_core0: mbox-main-r5fss0-core0 { + ti,mbox-rx = <0 0 2>; + ti,mbox-tx = <1 0 2>; + }; + + mbox_main_r5fss0_core1: mbox-main-r5fss0-core1 { + ti,mbox-rx = <2 0 2>; + ti,mbox-tx = <3 0 2>; + }; +}; + +&mailbox0_cluster3 { + status = "disabled"; +}; + +&mailbox0_cluster4 { + mbox_main_r5fss1_core0: mbox-main-r5fss1-core0 { + ti,mbox-rx = <0 0 2>; + ti,mbox-tx = <1 0 2>; + }; + + mbox_main_r5fss1_core1: mbox-main-r5fss1-core1 { + ti,mbox-rx = <2 0 2>; + ti,mbox-tx = <3 0 2>; + }; +}; + +&mailbox0_cluster5 { + status = "disabled"; +}; + +&mailbox0_cluster6 { + mbox_m4_0: mbox-m4-0 { + ti,mbox-rx = <0 0 2>; + ti,mbox-tx = <1 0 2>; + }; +}; + +&mailbox0_cluster7 { + status = "disabled"; +}; + +&serdes_ln_ctrl { + idle-states = ; +}; + +&serdes0 { + serdes0_pcie_link: phy@0 { + reg = <0>; + cdns,num-lanes = <1>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz0 1>; + }; +}; + +&pcie0_rc { + reset-gpios = <&exp1 5 GPIO_ACTIVE_HIGH>; + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <1>; +}; + +&pcie0_ep { + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <1>; + status = "disabled"; +}; + +&tscadc0 { + /* ADC is reserved for R5 usage */ + status = "reserved"; +}; + +&ospi0 { + pinctrl-names = "default"; + pinctrl-0 = <&ospi0_pins_default>; + + flash@0{ + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + spi-max-frequency = <25000000>; + cdns,tshsl-ns = <60>; + cdns,tsd2d-ns = <60>; + cdns,tchsh-ns = <60>; + cdns,tslch-ns = <60>; + cdns,read-delay = <4>; + cdns,phy-mode; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&main_mcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan0_pins_default>; + phys = <&transceiver1>; +}; + +&main_mcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan1_pins_default>; + phys = <&transceiver2>; +}; + +&icssg0_mdio { + status = "disabled"; +}; + +&icssg1_mdio { + pinctrl-names = "default"; + pinctrl-0 = <&icssg1_mdio1_pins_default>; + + icssg1_phy1: ethernet-phy@0 { + reg = <0xf>; + }; +}; + +&ecap0 { + /* PWM is available on Pin 1 of header J12 */ + pinctrl-names = "default"; + pinctrl-0 = <&main_ecap0_pins_default>; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac.dts b/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac.dts --- a/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT overlay for 2nd ICSSG1 port enabling on AM642 EVM + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + aliases { + ethernet1 = "/icssg1-eth/ethernet-mii0"; + ethernet2 = "/icssg1-eth/ethernet-mii1"; + }; + + mdio-mux-2 { + compatible = "mdio-mux-multiplexer"; + mux-controls = <&mdio_mux>; + mdio-parent-bus = <&icssg1_mdio>; + #address-cells = <1>; + #size-cells = <0>; + + mdio@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + + icssg1_phy2: ethernet-phy@3 { + reg = <3>; + }; + }; + }; + }; + }; +}; + +&main_pmx0 { + icssg1_rgmii2_pins_default: icssg1-rgmii2-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0108, PIN_INPUT, 2) /* (W11) PRG1_PRU1_GPO0.RGMII2_RD0 */ + AM64X_IOPAD(0x010c, PIN_INPUT, 2) /* (V11) PRG1_PRU1_GPO1.RGMII2_RD1 */ + AM64X_IOPAD(0x0110, PIN_INPUT, 2) /* (AA12) PRG1_PRU1_GPO2.RGMII2_RD2 */ + AM64X_IOPAD(0x0114, PIN_INPUT, 2) /* (Y12) PRG1_PRU1_GPO3.RGMII2_RD3 */ + AM64X_IOPAD(0x0120, PIN_INPUT, 2) /* (U11) PRG1_PRU1_GPO6.RGMII2_RXC */ + AM64X_IOPAD(0x0118, PIN_INPUT, 2) /* (W12) PRG1_PRU1_GPO4.RGMII2_RX_CTL */ + AM64X_IOPAD(0x0134, PIN_OUTPUT, 2) /* (AA10) PRG1_PRU1_GPO11.RGMII2_TD0 */ + AM64X_IOPAD(0x0138, PIN_OUTPUT, 2) /* (V10) PRG1_PRU1_GPO12.RGMII2_TD1 */ + AM64X_IOPAD(0x013c, PIN_OUTPUT, 2) /* (U10) PRG1_PRU1_GPO13.RGMII2_TD2 */ + AM64X_IOPAD(0x0140, PIN_OUTPUT, 2) /* (AA11) PRG1_PRU1_GPO14.RGMII2_TD3 */ + AM64X_IOPAD(0x0148, PIN_OUTPUT, 2) /* (Y10) PRG1_PRU1_GPO16.RGMII2_TXC */ + AM64X_IOPAD(0x0144, PIN_OUTPUT, 2) /* (Y11) PRG1_PRU1_GPO15.RGMII2_TX_CTL */ + >; + }; +}; + +&cpsw3g { + pinctrl-0 = <&mdio1_pins_default &rgmii1_pins_default>; +}; + +&cpsw_port2 { + status = "disabled"; +}; + +&mdio_mux_1 { + status = "disabled"; +}; + +&icssg1_eth { + pinctrl-0 = <&icssg1_rgmii1_pins_default &icssg1_rgmii2_pins_default>; +}; + +&icssg1_emac1 { + status = "okay"; + phy-handle = <&icssg1_phy2>; + phy-mode = "rgmii-rxid"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac-mii.dts b/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac-mii.dts --- a/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac-mii.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am642-evm-icssg1-dualemac-mii.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT overlay for 2nd ICSSG1 port enabling on AM642 EVM + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + aliases { + ethernet1 = "/icssg1-eth/ethernet-mii0"; + ethernet2 = "/icssg1-eth/ethernet-mii1"; + }; + + mdio-mux-2 { + compatible = "mdio-mux-multiplexer"; + mux-controls = <&mdio_mux>; + mdio-parent-bus = <&icssg1_mdio>; + #address-cells = <1>; + #size-cells = <0>; + + mdio@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + + icssg1_phy2: ethernet-phy@3 { + reg = <3>; + }; + }; + }; + }; + }; +}; + +&main_pmx0 { + icssg1_mii1_pins_default: icssg1-mii1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x00f8, PIN_INPUT, 1) /* (V9) PRG1_PRU0_GPO16.PR1_MII_MT0_CLK */ + AM64X_IOPAD(0x00f4, PIN_OUTPUT, 0) /* (Y9) PRG1_PRU0_GPO15.PR1_MII0_TXEN */ + AM64X_IOPAD(0x00f0, PIN_OUTPUT, 0) /* (AA9) PRG1_PRU0_GPO14.PR1_MII0_TXD3 */ + AM64X_IOPAD(0x00ec, PIN_OUTPUT, 0) /* (W9) PRG1_PRU0_GPO13.PR1_MII0_TXD2 */ + AM64X_IOPAD(0x00e8, PIN_OUTPUT, 0) /* (U9) PRG1_PRU0_GPO12.PR1_MII0_TXD1 */ + AM64X_IOPAD(0x00e4, PIN_OUTPUT, 0) /* (AA8) PRG1_PRU0_GPO11.PR1_MII0_TXD0 */ + AM64X_IOPAD(0x00c8, PIN_INPUT, 1) /* (Y8) PRG1_PRU0_GPO4.PR1_MII0_RXDV */ + AM64X_IOPAD(0x00d0, PIN_INPUT, 1) /* (AA7) PRG1_PRU0_GPO6.PR1_MII_MR0_CLK */ + AM64X_IOPAD(0x00c4, PIN_INPUT, 1) /* (V8) PRG1_PRU0_GPO3.PR1_MII0_RXD3 */ + AM64X_IOPAD(0x00c0, PIN_INPUT, 1) /* (W8) PRG1_PRU0_GPO2.PR1_MII0_RXD2 */ + AM64X_IOPAD(0x00cc, PIN_INPUT, 1) /* (V13) PRG1_PRU0_GPO5.PR1_MII0_RXER */ + AM64X_IOPAD(0x00bc, PIN_INPUT, 1) /* (U8) PRG1_PRU0_GPO1.PR1_MII0_RXD1 */ + AM64X_IOPAD(0x00b8, PIN_INPUT, 1) /* (Y7) PRG1_PRU0_GPO0.PR1_MII0_RXD0 */ + AM64X_IOPAD(0x00d8, PIN_INPUT, 1) /* (W13) PRG1_PRU0_GPO8.PR1_MII0_RXLINK */ + >; + }; + + icssg1_mii2_pins_default: icssg1-mii-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0148, PIN_INPUT, 1) /* (Y10) PRG1_PRU1_GPO16.PR1_MII_MT1_CLK */ + AM64X_IOPAD(0x0144, PIN_OUTPUT, 0) /* (Y11) PRG1_PRU1_GPO15.PR1_MII1_TXEN */ + AM64X_IOPAD(0x0140, PIN_OUTPUT, 0) /* (AA11) PRG1_PRU1_GPO14.PR1_MII1_TXD3 */ + AM64X_IOPAD(0x013c, PIN_OUTPUT, 0) /* (U10) PRG1_PRU1_GPO13.PR1_MII1_TXD2 */ + AM64X_IOPAD(0x0138, PIN_OUTPUT, 0) /* (V10) PRG1_PRU1_GPO12.PR1_MII1_TXD1 */ + AM64X_IOPAD(0x0134, PIN_OUTPUT, 0) /* (AA10) PRG1_PRU1_GPO11.PR1_MII1_TXD0 */ + AM64X_IOPAD(0x0118, PIN_INPUT, 1) /* (W12) PRG1_PRU1_GPO4.PR1_MII1_RXDV */ + AM64X_IOPAD(0x0120, PIN_INPUT, 1) /* (U11) PRG1_PRU1_GPO6.PR1_MII_MR1_CLK */ + AM64X_IOPAD(0x0114, PIN_INPUT, 1) /* (Y12) PRG1_PRU1_GPO3.PR1_MII1_RXD3 */ + AM64X_IOPAD(0x0110, PIN_INPUT, 1) /* (AA12) PRG1_PRU1_GPO2.PR1_MII1_RXD2 */ + AM64X_IOPAD(0x011c, PIN_INPUT, 1) /* (AA13) PRG1_PRU1_GPO5.PR1_MII1_RXER */ + AM64X_IOPAD(0x010c, PIN_INPUT, 1) /* (V11) PRG1_PRU1_GPO1.PR1_MII1_RXD1 */ + AM64X_IOPAD(0x0108, PIN_INPUT, 1) /* (W11) PRG1_PRU1_GPO0.PR1_MII1_RXD0 */ + AM64X_IOPAD(0x0128, PIN_INPUT, 1) /* (U12) PRG1_PRU1_GPO8.PR1_MII1_RXLINK */ + >; + }; +}; + +&cpsw3g { + pinctrl-0 = <&mdio1_pins_default &rgmii1_pins_default>; +}; + +&cpsw_port2 { + status = "disabled"; +}; + +&mdio_mux_1 { + status = "disabled"; +}; + +&icssg1_eth { + pinctrl-0 = <&icssg1_mii1_pins_default &icssg1_mii2_pins_default>; +}; + +&icssg1_emac0 { + phy-mode = "mii"; +}; + +&icssg1_emac1 { + status = "okay"; + phy-handle = <&icssg1_phy2>; + phy-mode = "mii"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am642-evm-nand.dts b/arch/arm64/boot/dts/ti/k3-am642-evm-nand.dts --- a/arch/arm64/boot/dts/ti/k3-am642-evm-nand.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am642-evm-nand.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT overlay for HSE NAND expansion card on AM642 EVM + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include +#include + +&main_pmx0 { + gpmc0_pins_default: gpmc0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0094, PIN_INPUT, 7) /* (T19) GPMC0_BE1n.GPIO0_36 */ + + AM64X_IOPAD(0x003c, PIN_INPUT, 0) /* (T20) GPMC0_AD0 */ + AM64X_IOPAD(0x0040, PIN_INPUT, 0) /* (U21) GPMC0_AD1 */ + AM64X_IOPAD(0x0064, PIN_INPUT, 0) /* (R16) GPMC0_AD10 */ + AM64X_IOPAD(0x0068, PIN_INPUT, 0) /* (W20) GPMC0_AD11 */ + AM64X_IOPAD(0x006c, PIN_INPUT, 0) /* (W21) GPMC0_AD12 */ + AM64X_IOPAD(0x0070, PIN_INPUT, 0) /* (V18) GPMC0_AD13 */ + AM64X_IOPAD(0x0074, PIN_INPUT, 0) /* (Y21) GPMC0_AD14 */ + AM64X_IOPAD(0x0078, PIN_INPUT, 0) /* (Y20) GPMC0_AD15 */ + AM64X_IOPAD(0x0044, PIN_INPUT, 0) /* (T18) GPMC0_AD2 */ + AM64X_IOPAD(0x0048, PIN_INPUT, 0) /* (U20) GPMC0_AD3 */ + AM64X_IOPAD(0x004c, PIN_INPUT, 0) /* (U18) GPMC0_AD4 */ + AM64X_IOPAD(0x0050, PIN_INPUT, 0) /* (U19) GPMC0_AD5 */ + AM64X_IOPAD(0x0054, PIN_INPUT, 0) /* (V20) GPMC0_AD6 */ + AM64X_IOPAD(0x0058, PIN_INPUT, 0) /* (V21) GPMC0_AD7 */ + AM64X_IOPAD(0x005c, PIN_INPUT, 0) /* (V19) GPMC0_AD8 */ + AM64X_IOPAD(0x0060, PIN_INPUT, 0) /* (T17) GPMC0_AD9 */ + AM64X_IOPAD(0x0098, PIN_INPUT_PULLUP, 0) /* (W19) GPMC0_WAIT0 */ + AM64X_IOPAD(0x009c, PIN_INPUT_PULLUP, 0) /* (Y18) GPMC0_WAIT1 */ + AM64X_IOPAD(0x00a8, PIN_OUTPUT_PULLUP, 0) /* (R19) GPMC0_CSn0 */ + AM64X_IOPAD(0x00ac, PIN_OUTPUT_PULLUP, 0) /* (R20) GPMC0_CSn1 */ + AM64X_IOPAD(0x00b0, PIN_OUTPUT_PULLUP, 0) /* (P19) GPMC0_CSn2 */ + AM64X_IOPAD(0x00b4, PIN_OUTPUT_PULLUP, 0) /* (R21) GPMC0_CSn3 */ + AM64X_IOPAD(0x007c, PIN_OUTPUT, 0) /* (R17) GPMC0_CLK */ + AM64X_IOPAD(0x0084, PIN_OUTPUT, 0) /* (P16) GPMC0_ADVn_ALE */ + AM64X_IOPAD(0x0088, PIN_OUTPUT, 0) /* (R18) GPMC0_OEn_REn */ + AM64X_IOPAD(0x008c, PIN_OUTPUT, 0) /* (T21) GPMC0_WEn */ + AM64X_IOPAD(0x0090, PIN_OUTPUT, 0) /* (P17) GPMC0_BE0n_CLE */ + AM64X_IOPAD(0x00a0, PIN_OUTPUT_PULLUP, 0) /* (N16) GPMC0_WPn */ + AM64X_IOPAD(0x00a4, PIN_OUTPUT, 0) /* (N17) GPMC0_DIR */ + >; + }; +}; + +&main_gpio0 { + gpio0-36 { + gpio-hog; + gpios = <36 0>; + input; + line-name = "GPMC0_MUX_DIR"; + }; +}; + +&gpmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&gpmc0_pins_default>; + ranges = <0 0 0x00 0x51000000 0x01000000>; /* CS0 space. Min partition = 16MB */ + #address-cells = <2>; + #size-cells = <1>; + + nand@0,0 { + compatible = "ti,am64-nand"; + reg = <0 0 64>; /* device IO registers */ + interrupt-parent = <&gpmc0>; + interrupts = <0 IRQ_TYPE_NONE>, /* fifoevent */ + <1 IRQ_TYPE_NONE>; /* termcount */ + rb-gpios = <&gpmc0 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */ + ti,nand-xfer-type = "prefetch-polled"; + ti,nand-ecc-opt = "bch8"; /* BCH8: Bootrom limitation */ + ti,elm-id = <&elm0>; + nand-bus-width = <8>; + gpmc,device-width = <1>; + gpmc,sync-clk-ps = <0>; + gpmc,cs-on-ns = <0>; + gpmc,cs-rd-off-ns = <40>; + gpmc,cs-wr-off-ns = <40>; + gpmc,adv-on-ns = <0>; + gpmc,adv-rd-off-ns = <25>; + gpmc,adv-wr-off-ns = <25>; + gpmc,we-on-ns = <0>; + gpmc,we-off-ns = <20>; + gpmc,oe-on-ns = <3>; + gpmc,oe-off-ns = <30>; + gpmc,access-ns = <30>; + gpmc,rd-cycle-ns = <40>; + gpmc,wr-cycle-ns = <40>; + gpmc,bus-turnaround-ns = <0>; + gpmc,cycle2cycle-delay-ns = <0>; + gpmc,clk-activation-ns = <0>; + gpmc,wr-access-ns = <40>; + gpmc,wr-data-mux-bus-ns = <0>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am642-sk.dts b/arch/arm64/boot/dts/ti/k3-am642-sk.dts --- a/arch/arm64/boot/dts/ti/k3-am642-sk.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am642-sk.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include +#include +#include +#include +#include "k3-am642.dtsi" + +/ { + compatible = "ti,am642-sk", "ti,am642"; + model = "Texas Instruments AM642 SK"; + + chosen { + stdout-path = "serial2:115200n8"; + bootargs = "console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000"; + }; + + memory@80000000 { + device_type = "memory"; + /* 2G RAM */ + reg = <0x00000000 0x80000000 0x00000000 0x80000000>; + + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + secure_ddr: optee@9e800000 { + reg = <0x00 0x9e800000 0x00 0x01800000>; /* for OP-TEE */ + alignment = <0x1000>; + no-map; + }; + + main_r5fss0_core0_dma_memory_region: r5f-dma-memory@a0000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core0_memory_region: r5f-memory@a0100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core1_dma_memory_region: r5f-dma-memory@a1000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core1_memory_region: r5f-memory@a1100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core0_dma_memory_region: r5f-dma-memory@a2000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core0_memory_region: r5f-memory@a2100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core1_dma_memory_region: r5f-dma-memory@a3000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core1_memory_region: r5f-memory@a3100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3100000 0x00 0xf00000>; + no-map; + }; + + mcu_m4fss_dma_memory_region: m4f-dma-memory@a4000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4000000 0x00 0x100000>; + no-map; + }; + + mcu_m4fss_memory_region: m4f-memory@a4100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4100000 0x00 0xf00000>; + no-map; + }; + + rtos_ipc_memory_region: ipc-memories@a5000000 { + reg = <0x00 0xa5000000 0x00 0x00800000>; + alignment = <0x1000>; + no-map; + }; + }; + + vusb_main: fixed-regulator-vusb-main5v0 { + /* USB MAIN INPUT 5V DC */ + compatible = "regulator-fixed"; + regulator-name = "vusb_main5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; + + vcc_3v3_sys: fixedregulator-vcc-3v3-sys { + /* output of LP8733xx */ + compatible = "regulator-fixed"; + regulator-name = "vcc_3v3_sys"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vusb_main>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_mmc1: fixed-regulator-sd { + /* TPS2051BD */ + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vcc_3v3_sys>; + gpio = <&exp1 3 GPIO_ACTIVE_HIGH>; + }; +}; + +&main_pmx0 { + main_mmc1_pins_default: main-mmc1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0294, PIN_INPUT, 0) /* (J19) MMC1_CMD */ + AM64X_IOPAD(0x0290, PIN_INPUT, 0) /* (#N/A) MMC1_CLKLB */ + AM64X_IOPAD(0x028c, PIN_INPUT, 0) /* (L20) MMC1_CLK */ + AM64X_IOPAD(0x0288, PIN_INPUT, 0) /* (K21) MMC1_DAT0 */ + AM64X_IOPAD(0x0284, PIN_INPUT, 0) /* (L21) MMC1_DAT1 */ + AM64X_IOPAD(0x0280, PIN_INPUT, 0) /* (K19) MMC1_DAT2 */ + AM64X_IOPAD(0x027c, PIN_INPUT, 0) /* (K18) MMC1_DAT3 */ + AM64X_IOPAD(0x0298, PIN_INPUT, 0) /* (D19) MMC1_SDCD */ + >; + }; + + main_usb0_pins_default: main-usb0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x02a8, PIN_OUTPUT, 0) /* (E19) USB0_DRVVBUS */ + >; + }; + + main_i2c1_pins_default: main-i2c1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0268, PIN_INPUT_PULLUP, 0) /* (C18) I2C1_SCL */ + AM64X_IOPAD(0x026c, PIN_INPUT_PULLUP, 0) /* (B19) I2C1_SDA */ + >; + }; + + mdio1_pins_default: mdio1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x01fc, PIN_OUTPUT, 4) /* (R2) PRG0_PRU1_GPO19.MDIO0_MDC */ + AM64X_IOPAD(0x01f8, PIN_INPUT, 4) /* (P5) PRG0_PRU1_GPO18.MDIO0_MDIO */ + >; + }; + + rgmii1_pins_default: rgmii1-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x011c, PIN_INPUT, 4) /* (AA13) PRG1_PRU1_GPO5.RGMII1_RD0 */ + AM64X_IOPAD(0x0128, PIN_INPUT, 4) /* (U12) PRG1_PRU1_GPO8.RGMII1_RD1 */ + AM64X_IOPAD(0x0150, PIN_INPUT, 4) /* (Y13) PRG1_PRU1_GPO18.RGMII1_RD2 */ + AM64X_IOPAD(0x0154, PIN_INPUT, 4) /* (V12) PRG1_PRU1_GPO19.RGMII1_RD3 */ + AM64X_IOPAD(0x00d8, PIN_INPUT, 4) /* (W13) PRG1_PRU0_GPO8.RGMII1_RXC */ + AM64X_IOPAD(0x00cc, PIN_INPUT, 4) /* (V13) PRG1_PRU0_GPO5.RGMII1_RX_CTL */ + AM64X_IOPAD(0x0124, PIN_OUTPUT, 4) /* (V15) PRG1_PRU1_GPO7.RGMII1_TD0 */ + AM64X_IOPAD(0x012c, PIN_OUTPUT, 4) /* (V14) PRG1_PRU1_GPO9.RGMII1_TD1 */ + AM64X_IOPAD(0x0130, PIN_OUTPUT, 4) /* (W14) PRG1_PRU1_GPO10.RGMII1_TD2 */ + AM64X_IOPAD(0x014c, PIN_OUTPUT, 4) /* (AA14) PRG1_PRU1_GPO17.RGMII1_TD3 */ + AM64X_IOPAD(0x00e0, PIN_OUTPUT, 4) /* (U14) PRG1_PRU0_GPO10.RGMII1_TXC */ + AM64X_IOPAD(0x00dc, PIN_OUTPUT, 4) /* (U15) PRG1_PRU0_GPO9.RGMII1_TX_CTL */ + >; + }; + + rgmii2_pins_default: rgmii2-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0108, PIN_INPUT, 4) /* (W11) PRG1_PRU1_GPO0.RGMII2_RD0 */ + AM64X_IOPAD(0x010c, PIN_INPUT, 4) /* (V11) PRG1_PRU1_GPO1.RGMII2_RD1 */ + AM64X_IOPAD(0x0110, PIN_INPUT, 4) /* (AA12) PRG1_PRU1_GPO2.RGMII2_RD2 */ + AM64X_IOPAD(0x0114, PIN_INPUT, 4) /* (Y12) PRG1_PRU1_GPO3.RGMII2_RD3 */ + AM64X_IOPAD(0x0120, PIN_INPUT, 4) /* (U11) PRG1_PRU1_GPO6.RGMII2_RXC */ + AM64X_IOPAD(0x0118, PIN_INPUT, 4) /* (W12) PRG1_PRU1_GPO4.RGMII2_RX_CTL */ + AM64X_IOPAD(0x0134, PIN_OUTPUT, 4) /* (AA10) PRG1_PRU1_GPO11.RGMII2_TD0 */ + AM64X_IOPAD(0x0138, PIN_OUTPUT, 4) /* (V10) PRG1_PRU1_GPO12.RGMII2_TD1 */ + AM64X_IOPAD(0x013c, PIN_OUTPUT, 4) /* (U10) PRG1_PRU1_GPO13.RGMII2_TD2 */ + AM64X_IOPAD(0x0140, PIN_OUTPUT, 4) /* (AA11) PRG1_PRU1_GPO14.RGMII2_TD3 */ + AM64X_IOPAD(0x0148, PIN_OUTPUT, 4) /* (Y10) PRG1_PRU1_GPO16.RGMII2_TXC */ + AM64X_IOPAD(0x0144, PIN_OUTPUT, 4) /* (Y11) PRG1_PRU1_GPO15.RGMII2_TX_CTL */ + >; + }; + + ospi0_pins_default: ospi0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0000, PIN_OUTPUT, 0) /* (N20) OSPI0_CLK */ + AM64X_IOPAD(0x002c, PIN_OUTPUT, 0) /* (L19) OSPI0_CSn0 */ + AM64X_IOPAD(0x000c, PIN_INPUT, 0) /* (M19) OSPI0_D0 */ + AM64X_IOPAD(0x0010, PIN_INPUT, 0) /* (M18) OSPI0_D1 */ + AM64X_IOPAD(0x0014, PIN_INPUT, 0) /* (M20) OSPI0_D2 */ + AM64X_IOPAD(0x0018, PIN_INPUT, 0) /* (M21) OSPI0_D3 */ + AM64X_IOPAD(0x001c, PIN_INPUT, 0) /* (P21) OSPI0_D4 */ + AM64X_IOPAD(0x0020, PIN_INPUT, 0) /* (P20) OSPI0_D5 */ + AM64X_IOPAD(0x0024, PIN_INPUT, 0) /* (N18) OSPI0_D6 */ + AM64X_IOPAD(0x0028, PIN_INPUT, 0) /* (M17) OSPI0_D7 */ + AM64X_IOPAD(0x0008, PIN_INPUT, 0) /* (N19) OSPI0_DQS */ + >; + }; + + main_ecap0_pins_default: main-ecap0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0270, PIN_INPUT, 0) /* (D18) ECAP0_IN_APWM_OUT */ + >; + }; +}; + +&mcu_uart0 { + status = "disabled"; +}; + +&mcu_uart1 { + status = "disabled"; +}; + +&main_uart1 { + /* main_uart1 is reserved for firmware usage */ + status = "reserved"; +}; + +&main_uart2 { + status = "disabled"; +}; + +&main_uart3 { + status = "disabled"; +}; + +&main_uart4 { + status = "disabled"; +}; + +&main_uart5 { + status = "disabled"; +}; + +&main_uart6 { + status = "disabled"; +}; + +&mcu_i2c0 { + status = "disabled"; +}; + +&mcu_i2c1 { + status = "disabled"; +}; + +&main_i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c1_pins_default>; + clock-frequency = <400000>; + + exp1: gpio@70 { + compatible = "nxp,pca9538"; + reg = <0x70>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "GPIO_CPSW2_RST", "GPIO_CPSW1_RST", + "PRU_DETECT", "MMC1_SD_EN", + "VPP_LDO_EN", "RPI_PS_3V3_En", + "RPI_PS_5V0_En", "RPI_HAT_DETECT"; + }; +}; + +&main_i2c3 { + status = "disabled"; +}; + +&mcu_spi0 { + status = "disabled"; +}; + +&mcu_spi1 { + status = "disabled"; +}; + +/* mcu_gpio0 is reserved for mcu firmware usage */ +&mcu_gpio0 { + status = "reserved"; +}; + +&sdhci1 { + /* SD/MMC */ + vmmc-supply = <&vdd_mmc1>; + pinctrl-names = "default"; + bus-width = <4>; + pinctrl-0 = <&main_mmc1_pins_default>; + ti,driver-strength-ohm = <50>; + disable-wp; +}; + +&serdes_ln_ctrl { + idle-states = ; +}; + +&serdes_wiz0 { + status = "okay"; +}; + +&serdes0 { + serdes0_usb_link: phy@0 { + reg = <0>; + cdns,num-lanes = <1>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz0 1>; + }; +}; + +&usbss0 { + ti,vbus-divider; +}; + +&usb0 { + dr_mode = "host"; + maximum-speed = "super-speed"; + pinctrl-names = "default"; + pinctrl-0 = <&main_usb0_pins_default>; + phys = <&serdes0_usb_link>; + phy-names = "cdns3,usb3-phy"; +}; + +&cpsw3g { + pinctrl-names = "default"; + pinctrl-0 = <&mdio1_pins_default + &rgmii1_pins_default + &rgmii2_pins_default>; + + cpts@3d000 { + ti,pps = <7 1>; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-rxid"; + phy-handle = <&cpsw3g_phy0>; +}; + +&cpsw_port2 { + phy-mode = "rgmii-rxid"; + phy-handle = <&cpsw3g_phy1>; +}; + +&cpsw3g_mdio { + cpsw3g_phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; + + cpsw3g_phy1: ethernet-phy@1 { + reg = <1>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; +}; + +#define TS_OFFSET(pa, val) (0x4+(pa)*4) (0x10000 | val) + +×ync_router { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_cpts_pps>; + + /* Example of the timesync routing */ + mcu_cpts_pps: mcu-cpts-pps { + pinctrl-single,pins = < + /* pps [cpts genf1] in22 -> out37 [cpts hw8_push] */ + TS_OFFSET(37, 22) + /* pps [cpts genf1] in22 -> out25 [SYNC1_OUT pin] */ + TS_OFFSET(25, 22) + >; + }; +}; + +&mailbox0_cluster2 { + mbox_main_r5fss0_core0: mbox-main-r5fss0-core0 { + ti,mbox-rx = <0 0 2>; + ti,mbox-tx = <1 0 2>; + }; + + mbox_main_r5fss0_core1: mbox-main-r5fss0-core1 { + ti,mbox-rx = <2 0 2>; + ti,mbox-tx = <3 0 2>; + }; +}; + +&mailbox0_cluster3 { + status = "disabled"; +}; + +&mailbox0_cluster4 { + mbox_main_r5fss1_core0: mbox-main-r5fss1-core0 { + ti,mbox-rx = <0 0 2>; + ti,mbox-tx = <1 0 2>; + }; + + mbox_main_r5fss1_core1: mbox-main-r5fss1-core1 { + ti,mbox-rx = <2 0 2>; + ti,mbox-tx = <3 0 2>; + }; +}; + +&mailbox0_cluster5 { + status = "disabled"; +}; + +&mailbox0_cluster6 { + mbox_m4_0: mbox-m4-0 { + ti,mbox-rx = <0 0 2>; + ti,mbox-tx = <1 0 2>; + }; +}; + +&mailbox0_cluster7 { + status = "disabled"; +}; + +&pcie0_rc { + status = "disabled"; +}; + +&pcie0_ep { + status = "disabled"; +}; + +&main_r5fss0_core0 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss0_core0>; + memory-region = <&main_r5fss0_core0_dma_memory_region>, + <&main_r5fss0_core0_memory_region>; +}; + +&main_r5fss0_core1 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss0_core1>; + memory-region = <&main_r5fss0_core1_dma_memory_region>, + <&main_r5fss0_core1_memory_region>; +}; + +&main_r5fss1_core0 { + mboxes = <&mailbox0_cluster4 &mbox_main_r5fss1_core0>; + memory-region = <&main_r5fss1_core0_dma_memory_region>, + <&main_r5fss1_core0_memory_region>; +}; + +&main_r5fss1_core1 { + mboxes = <&mailbox0_cluster4 &mbox_main_r5fss1_core1>; + memory-region = <&main_r5fss1_core1_dma_memory_region>, + <&main_r5fss1_core1_memory_region>; +}; + +&mcu_m4fss { + mboxes = <&mailbox0_cluster6 &mbox_m4_0>; + memory-region = <&mcu_m4fss_dma_memory_region>, + <&mcu_m4fss_memory_region>; +}; + +&tscadc0 { + status = "disabled"; +}; + +&ospi0 { + pinctrl-names = "default"; + pinctrl-0 = <&ospi0_pins_default>; + + flash@0{ + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + spi-max-frequency = <25000000>; + cdns,tshsl-ns = <60>; + cdns,tsd2d-ns = <60>; + cdns,tchsh-ns = <60>; + cdns,tslch-ns = <60>; + cdns,read-delay = <4>; + cdns,phy-mode; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&main_mcan0 { + status = "disabled"; +}; + +&main_mcan1 { + status = "disabled"; +}; + +&icssg0 { + status = "disabled"; +}; + +&icssg1 { + status = "disabled"; +}; + +&icssg0_mdio { + status = "disabled"; +}; + +&icssg1_mdio { + status = "disabled"; +}; + +&ecap0 { + /* PWM is available on Pin 1 of header J3 */ + pinctrl-names = "default"; + pinctrl-0 = <&main_ecap0_pins_default>; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am64.dtsi b/arch/arm64/boot/dts/ti/k3-am64.dtsi --- a/arch/arm64/boot/dts/ti/k3-am64.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am64.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for AM642 SoC Family + * + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include +#include +#include +#include + +/ { + model = "Texas Instruments K3 AM642 SoC"; + compatible = "ti,am642"; + interrupt-parent = <&gic500>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &mcu_uart0; + serial1 = &mcu_uart1; + serial2 = &main_uart0; + serial3 = &main_uart1; + serial4 = &main_uart2; + serial5 = &main_uart3; + serial6 = &main_uart4; + serial7 = &main_uart5; + serial8 = &main_uart6; + ethernet0 = &cpsw_port1; + ethernet1 = &cpsw_port2; + }; + + chosen { }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + + psci: psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + }; + + a53_timer0: timer-cl0-cpu0 { + compatible = "arm,armv8-timer"; + interrupts = , /* cntpsirq */ + , /* cntpnsirq */ + , /* cntvirq */ + ; /* cnthpirq */ + }; + + pmu: pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = ; + }; + + cbass_main: bus@f4000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x000f4000 0x00 0x000f4000 0x00 0x000002d0>, /* PINCTRL */ + <0x00 0x00600000 0x00 0x00600000 0x00 0x00001100>, /* GPIO */ + <0x00 0x00a40000 0x00 0x00a40000 0x00 0x00000800>, /* Timesync router */ + <0x00 0x01000000 0x00 0x01000000 0x00 0x02330400>, /* First peripheral window */ + <0x00 0x08000000 0x00 0x08000000 0x00 0x00200000>, /* Main CPSW */ + <0x00 0x0d000000 0x00 0x0d000000 0x00 0x00800000>, /* PCIE_CORE */ + <0x00 0x0f000000 0x00 0x0f000000 0x00 0x00c44200>, /* Second peripheral window */ + <0x00 0x20000000 0x00 0x20000000 0x00 0x0a008000>, /* Third peripheral window */ + <0x00 0x30000000 0x00 0x30000000 0x00 0x000bc100>, /* ICSSG0/1 */ + <0x00 0x37000000 0x00 0x37000000 0x00 0x00040000>, /* TIMERMGR0 TIMERS */ + <0x00 0x39000000 0x00 0x39000000 0x00 0x00000400>, /* CPTS0 */ + <0x00 0x3b000000 0x00 0x3b000000 0x00 0x00000400>, /* GPMC0_CFG */ + <0x00 0x3cd00000 0x00 0x3cd00000 0x00 0x00000200>, /* TIMERMGR0_CONFIG */ + <0x00 0x3f004000 0x00 0x3f004000 0x00 0x00000400>, /* GICSS0_REGS */ + <0x00 0x40900000 0x00 0x40900000 0x00 0x00030000>, /* SA2_UL0 */ + <0x00 0x43000000 0x00 0x43000000 0x00 0x00020000>, /* CTRL_MMR0 */ + <0x00 0x44043000 0x00 0x44043000 0x00 0x00000fe0>, /* TI SCI DEBUG */ + <0x00 0x48000000 0x00 0x48000000 0x00 0x06400000>, /* DMASS */ + <0x00 0x50000000 0x00 0x50000000 0x00 0x08000000>, /* GPMC0 DATA */ + <0x00 0x60000000 0x00 0x60000000 0x00 0x08000000>, /* FSS0 DAT1 */ + <0x00 0x68000000 0x00 0x68000000 0x00 0x08000000>, /* PCIe DAT0 */ + <0x00 0x70000000 0x00 0x70000000 0x00 0x00200000>, /* OC SRAM */ + <0x00 0x78000000 0x00 0x78000000 0x00 0x00800000>, /* Main R5FSS */ + <0x06 0x00000000 0x06 0x00000000 0x01 0x00000000>, /* PCIe DAT1 */ + <0x05 0x00000000 0x05 0x00000000 0x01 0x00000000>, /* FSS0 DAT3 */ + + /* MCU Domain Range */ + <0x00 0x04000000 0x00 0x04000000 0x00 0x01ff1400>; + + cbass_mcu: bus@4000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x04000000 0x00 0x04000000 0x00 0x01ff1400>; /* Peripheral window */ + }; + }; +}; + +/* Now include the peripherals for each bus segments */ +#include "k3-am64-main.dtsi" +#include "k3-am64-mcu.dtsi" diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am64-main.dtsi b/arch/arm64/boot/dts/ti/k3-am64-main.dtsi --- a/arch/arm64/boot/dts/ti/k3-am64-main.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am64-main.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,1427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for AM642 SoC Family Main Domain peripherals + * + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include + +/ { + serdes_refclk: serdes-refclk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <0>; + }; +}; + +&cbass_main { + oc_sram: sram@70000000 { + compatible = "mmio-sram"; + reg = <0x00 0x70000000 0x00 0x200000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x70000000 0x200000>; + + atf-sram@1c0000 { + reg = <0x1c0000 0x20000>; + }; + + dmsc-sram@1e0000 { + reg = <0x1e0000 0x1c000>; + }; + + sproxy-sram@1fc000 { + reg = <0x1fc000 0x4000>; + }; + }; + + main_conf: syscon@43000000 { + compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; + reg = <0x0 0x43000000 0x0 0x20000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x43000000 0x20000>; + + serdes_ln_ctrl: mux { + compatible = "mmio-mux"; + #mux-control-cells = <1>; + mux-reg-masks = <0x4080 0x3>; /* SERDES0 lane0 select */ + }; + }; + + gic500: interrupt-controller@1800000 { + compatible = "arm,gic-v3"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x00 0x01800000 0x00 0x10000>, /* GICD */ + <0x00 0x01840000 0x00 0xC0000>; /* GICR */ + /* + * vcpumntirq: + * virtual CPU interface maintenance interrupt + */ + interrupts = ; + + gic_its: msi-controller@1820000 { + compatible = "arm,gic-v3-its"; + reg = <0x00 0x01820000 0x00 0x10000>; + socionext,synquacer-pre-its = <0x1000000 0x400000>; + msi-controller; + #msi-cells = <1>; + }; + }; + + dmss: dmss { + compatible = "simple-mfd"; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges; + ranges; + + ti,sci-dev-id = <25>; + + secure_proxy_main: mailbox@4d000000 { + compatible = "ti,am654-secure-proxy"; + #mbox-cells = <1>; + reg-names = "target_data", "rt", "scfg"; + reg = <0x00 0x4d000000 0x00 0x80000>, + <0x00 0x4a600000 0x00 0x80000>, + <0x00 0x4a400000 0x00 0x80000>; + interrupt-names = "rx_012"; + interrupts = ; + }; + + inta_main_dmss: interrupt-controller@48000000 { + compatible = "ti,sci-inta"; + reg = <0x00 0x48000000 0x00 0x100000>; + #address-cells = <0>; + #interrupt-cells = <0>; + interrupt-controller; + interrupt-parent = <&gic500>; + msi-controller; + ti,sci = <&dmsc>; + ti,sci-dev-id = <28>; + ti,interrupt-ranges = <4 68 36>; + ti,unmapped-event-sources = <&main_bcdma>, <&main_pktdma>; + }; + + main_bcdma: dma-controller@485c0100 { + compatible = "ti,am64-dmss-bcdma"; + reg = <0x00 0x485c0100 0x00 0x100>, + <0x00 0x4c000000 0x00 0x20000>, + <0x00 0x4a820000 0x00 0x20000>, + <0x00 0x4aa40000 0x00 0x20000>, + <0x00 0x4bc00000 0x00 0x100000>; + reg-names = "gcfg", "bchanrt", "rchanrt", "tchanrt", "ringrt"; + msi-parent = <&inta_main_dmss>; + #dma-cells = <3>; + + ti,sci = <&dmsc>; + ti,sci-dev-id = <26>; + ti,sci-rm-range-bchan = <0x20>; /* BLOCK_COPY_CHAN */ + ti,sci-rm-range-rchan = <0x21>; /* SPLIT_TR_RX_CHAN */ + ti,sci-rm-range-tchan = <0x22>; /* SPLIT_TR_TX_CHAN */ + }; + + main_pktdma: dma-controller@485c0000 { + compatible = "ti,am64-dmss-pktdma"; + reg = <0x00 0x485c0000 0x00 0x100>, + <0x00 0x4a800000 0x00 0x20000>, + <0x00 0x4aa00000 0x00 0x40000>, + <0x00 0x4b800000 0x00 0x400000>; + reg-names = "gcfg", "rchanrt", "tchanrt", "ringrt"; + msi-parent = <&inta_main_dmss>; + #dma-cells = <2>; + + ti,sci = <&dmsc>; + ti,sci-dev-id = <30>; + ti,sci-rm-range-tchan = <0x23>, /* UNMAPPED_TX_CHAN */ + <0x24>, /* CPSW_TX_CHAN */ + <0x25>, /* SAUL_TX_0_CHAN */ + <0x26>, /* SAUL_TX_1_CHAN */ + <0x27>, /* ICSSG_0_TX_CHAN */ + <0x28>; /* ICSSG_1_TX_CHAN */ + ti,sci-rm-range-tflow = <0x10>, /* RING_UNMAPPED_TX_CHAN */ + <0x11>, /* RING_CPSW_TX_CHAN */ + <0x12>, /* RING_SAUL_TX_0_CHAN */ + <0x13>, /* RING_SAUL_TX_1_CHAN */ + <0x14>, /* RING_ICSSG_0_TX_CHAN */ + <0x15>; /* RING_ICSSG_1_TX_CHAN */ + ti,sci-rm-range-rchan = <0x29>, /* UNMAPPED_RX_CHAN */ + <0x2b>, /* CPSW_RX_CHAN */ + <0x2d>, /* SAUL_RX_0_CHAN */ + <0x2f>, /* SAUL_RX_1_CHAN */ + <0x31>, /* SAUL_RX_2_CHAN */ + <0x33>, /* SAUL_RX_3_CHAN */ + <0x35>, /* ICSSG_0_RX_CHAN */ + <0x37>; /* ICSSG_1_RX_CHAN */ + ti,sci-rm-range-rflow = <0x2a>, /* FLOW_UNMAPPED_RX_CHAN */ + <0x2c>, /* FLOW_CPSW_RX_CHAN */ + <0x2e>, /* FLOW_SAUL_RX_0/1_CHAN */ + <0x32>, /* FLOW_SAUL_RX_2/3_CHAN */ + <0x36>, /* FLOW_ICSSG_0_RX_CHAN */ + <0x38>; /* FLOW_ICSSG_1_RX_CHAN */ + }; + }; + + dmsc: dmsc@44043000 { + compatible = "ti,k2g-sci"; + ti,host-id = <12>; + mbox-names = "rx", "tx"; + mboxes= <&secure_proxy_main 12>, + <&secure_proxy_main 13>; + reg-names = "debug_messages"; + reg = <0x00 0x44043000 0x00 0xfe0>; + + k3_pds: power-controller { + compatible = "ti,sci-pm-domain"; + #power-domain-cells = <2>; + }; + + k3_clks: clocks { + compatible = "ti,k2g-sci-clk"; + #clock-cells = <2>; + }; + + k3_reset: reset-controller { + compatible = "ti,sci-reset"; + #reset-cells = <2>; + }; + }; + + main_pmx0: pinctrl@f4000 { + compatible = "pinctrl-single"; + reg = <0x00 0xf4000 0x00 0x2d0>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0xffffffff>; + }; + + main_conf: syscon@43000000 { + compatible = "syscon", "simple-mfd"; + reg = <0x00 0x43000000 0x00 0x20000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x00 0x00 0x43000000 0x20000>; + + chipid@14 { + compatible = "ti,am654-chipid"; + reg = <0x00000014 0x4>; + }; + + phy_gmii_sel: phy@4044 { + compatible = "ti,am654-phy-gmii-sel"; + reg = <0x4044 0x8>; + #phy-cells = <1>; + }; + + epwm_tbclk: clock@4140 { + compatible = "ti,am64-epwm-tbclk", "syscon"; + reg = <0x4130 0x4>; + #clock-cells = <1>; + }; + }; + + main_uart0: serial@2800000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02800000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 146 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 146 0>; + clock-names = "fclk"; + }; + + main_uart1: serial@2810000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02810000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 152 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 152 0>; + clock-names = "fclk"; + }; + + main_uart2: serial@2820000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02820000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 153 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 153 0>; + clock-names = "fclk"; + }; + + main_uart3: serial@2830000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02830000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 154 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 154 0>; + clock-names = "fclk"; + }; + + main_uart4: serial@2840000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02840000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 155 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 155 0>; + clock-names = "fclk"; + }; + + main_uart5: serial@2850000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02850000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 156 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 156 0>; + clock-names = "fclk"; + }; + + main_uart6: serial@2860000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x02860000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 158 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 158 0>; + clock-names = "fclk"; + }; + + main_i2c0: i2c@20000000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x20000000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 102 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 102 2>; + clock-names = "fck"; + }; + + main_i2c1: i2c@20010000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x20010000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 103 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 103 2>; + clock-names = "fck"; + }; + + main_i2c2: i2c@20020000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x20020000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 104 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 104 2>; + clock-names = "fck"; + }; + + main_i2c3: i2c@20030000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x20030000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 105 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 105 2>; + clock-names = "fck"; + }; + + main_spi0: spi@20100000 { + compatible = "ti,am654-mcspi", "ti,omap4-mcspi"; + reg = <0x00 0x20100000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 141 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 141 0>; + dmas = <&main_pktdma 0xc300 0>, <&main_pktdma 0x4300 0>; + dma-names = "tx0", "rx0"; + }; + + main_spi1: spi@20110000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x00 0x20110000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 142 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 142 0>; + }; + + main_spi2: spi@20120000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x00 0x20120000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 143 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 143 0>; + }; + + main_spi3: spi@20130000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x00 0x20130000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 144 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 144 0>; + }; + + main_spi4: spi@20140000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x00 0x20140000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 145 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 145 0>; + }; + + main_gpio_intr: interrupt-controller0 { + compatible = "ti,sci-intr"; + ti,intr-trigger-type = <1>; + interrupt-controller; + interrupt-parent = <&gic500>; + #interrupt-cells = <1>; + ti,sci = <&dmsc>; + ti,sci-dev-id = <3>; + ti,interrupt-ranges = <0 32 16>; + }; + + main_gpio0: gpio@600000 { + compatible = "ti,am64-gpio", "ti,keystone-gpio"; + reg = <0x0 0x00600000 0x0 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <190>, <191>, <192>, + <193>, <194>, <195>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <87>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 77 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 77 0>; + clock-names = "gpio"; + }; + + main_gpio1: gpio@601000 { + compatible = "ti,am64-gpio", "ti,keystone-gpio"; + reg = <0x0 0x00601000 0x0 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <180>, <181>, <182>, + <183>, <184>, <185>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <88>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 78 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 78 0>; + clock-names = "gpio"; + }; + + sdhci0: mmc@fa10000 { + compatible = "ti,am64-sdhci-8bit"; + reg = <0x00 0xfa10000 0x00 0x260>, <0x00 0xfa18000 0x00 0x134>; + interrupts = ; + power-domains = <&k3_pds 57 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 57 0>, <&k3_clks 57 1>; + clock-names = "clk_ahb", "clk_xin"; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + ti,trm-icp = <0x2>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-mmc-hs = <0x0>; + ti,otap-del-sel-ddr52 = <0x6>; + ti,otap-del-sel-hs200 = <0x7>; + ti,otap-del-sel-hs400 = <0x4>; + }; + + sdhci1: mmc@fa00000 { + compatible = "ti,am64-sdhci-4bit"; + reg = <0x00 0xfa00000 0x00 0x260>, <0x00 0xfa08000 0x00 0x134>; + interrupts = ; + power-domains = <&k3_pds 58 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 58 3>, <&k3_clks 58 4>; + clock-names = "clk_ahb", "clk_xin"; + ti,trm-icp = <0x2>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-sd-hs = <0xf>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-sdr104 = <0x6>; + ti,otap-del-sel-ddr50 = <0x9>; + ti,clkbuf-sel = <0x7>; + }; + + cpsw3g: ethernet@8000000 { + compatible = "ti,am642-cpsw-nuss"; + #address-cells = <2>; + #size-cells = <2>; + reg = <0x0 0x8000000 0x0 0x200000>; + reg-names = "cpsw_nuss"; + ranges = <0x0 0x0 0x0 0x8000000 0x0 0x200000>; + clocks = <&k3_clks 13 0>; + assigned-clocks = <&k3_clks 13 1>; + assigned-clock-parents = <&k3_clks 13 9>; + clock-names = "fck"; + power-domains = <&k3_pds 13 TI_SCI_PD_EXCLUSIVE>; + + dmas = <&main_pktdma 0xC500 15>, + <&main_pktdma 0xC501 15>, + <&main_pktdma 0xC502 15>, + <&main_pktdma 0xC503 15>, + <&main_pktdma 0xC504 15>, + <&main_pktdma 0xC505 15>, + <&main_pktdma 0xC506 15>, + <&main_pktdma 0xC507 15>, + <&main_pktdma 0x4500 15>; + dma-names = "tx0", "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", + "tx7", "rx"; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + cpsw_port1: port@1 { + reg = <1>; + ti,mac-only; + label = "port1"; + phys = <&phy_gmii_sel 1>; + mac-address = [00 00 00 00 00 00]; + ti,syscon-efuse = <&main_conf 0x200>; + }; + + cpsw_port2: port@2 { + reg = <2>; + ti,mac-only; + label = "port2"; + phys = <&phy_gmii_sel 2>; + mac-address = [00 00 00 00 00 00]; + }; + }; + + cpsw3g_mdio: mdio@f00 { + compatible = "ti,cpsw-mdio","ti,davinci_mdio"; + reg = <0x0 0xf00 0x0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 13 0>; + clock-names = "fck"; + bus_freq = <1000000>; + }; + + cpts@3d000 { + compatible = "ti,j721e-cpts"; + reg = <0x0 0x3d000 0x0 0x400>; + clocks = <&k3_clks 13 1>; + clock-names = "cpts"; + interrupts-extended = <&gic500 GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "cpts"; + ti,cpts-ext-ts-inputs = <4>; + ti,cpts-periodic-outputs = <2>; + }; + }; + + cpts@39000000 { + compatible = "ti,j721e-cpts"; + reg = <0x0 0x39000000 0x0 0x400>; + reg-names = "cpts"; + power-domains = <&k3_pds 84 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 84 0>; + clock-names = "cpts"; + assigned-clocks = <&k3_clks 84 0>; + assigned-clock-parents = <&k3_clks 84 8>; + interrupts = ; + interrupt-names = "cpts"; + ti,cpts-periodic-outputs = <6>; + ti,cpts-ext-ts-inputs = <8>; + }; + + timesync_router: timesync-router@a40000 { + compatible = "pinctrl-single"; + reg = <0x0 0xa40000 0x0 0x800>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x000107ff>; + }; + + hwspinlock: spinlock@2a000000 { + compatible = "ti,am64-hwspinlock"; + reg = <0x00 0x2a000000 0x00 0x1000>; + #hwlock-cells = <1>; + }; + + mailbox0_cluster2: mailbox@29020000 { + compatible = "ti,am64-mailbox"; + reg = <0x00 0x29020000 0x00 0x200>; + interrupts = , + ; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + }; + + mailbox0_cluster3: mailbox@29030000 { + compatible = "ti,am64-mailbox"; + reg = <0x00 0x29030000 0x00 0x200>; + interrupts = , + ; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + }; + + mailbox0_cluster4: mailbox@29040000 { + compatible = "ti,am64-mailbox"; + reg = <0x00 0x29040000 0x00 0x200>; + interrupts = , + ; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + }; + + mailbox0_cluster5: mailbox@29050000 { + compatible = "ti,am64-mailbox"; + reg = <0x00 0x29050000 0x00 0x200>; + interrupts = , + ; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + }; + + mailbox0_cluster6: mailbox@29060000 { + compatible = "ti,am64-mailbox"; + reg = <0x00 0x29060000 0x00 0x200>; + interrupts = ; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + }; + + mailbox0_cluster7: mailbox@29070000 { + compatible = "ti,am64-mailbox"; + reg = <0x00 0x29070000 0x00 0x200>; + interrupts = ; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + }; + + main_r5fss0: r5fss@78000000 { + compatible = "ti,am64-r5fss"; + ti,cluster-mode = <0>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x78000000 0x00 0x78000000 0x10000>, + <0x78100000 0x00 0x78100000 0x10000>, + <0x78200000 0x00 0x78200000 0x08000>, + <0x78300000 0x00 0x78300000 0x08000>; + power-domains = <&k3_pds 119 TI_SCI_PD_EXCLUSIVE>; + + main_r5fss0_core0: r5f@78000000 { + compatible = "ti,am64-r5f"; + reg = <0x78000000 0x00010000>, + <0x78100000 0x00010000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <121>; + ti,sci-proc-ids = <0x01 0xff>; + resets = <&k3_reset 121 1>; + firmware-name = "am64-main-r5f0_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + main_r5fss0_core1: r5f@78200000 { + compatible = "ti,am64-r5f"; + reg = <0x78200000 0x00008000>, + <0x78300000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <122>; + ti,sci-proc-ids = <0x02 0xff>; + resets = <&k3_reset 122 1>; + firmware-name = "am64-main-r5f0_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + + main_r5fss1: r5fss@78400000 { + compatible = "ti,am64-r5fss"; + ti,cluster-mode = <0>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x78400000 0x00 0x78400000 0x10000>, + <0x78500000 0x00 0x78500000 0x10000>, + <0x78600000 0x00 0x78600000 0x08000>, + <0x78700000 0x00 0x78700000 0x08000>; + power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>; + + main_r5fss1_core0: r5f@78400000 { + compatible = "ti,am64-r5f"; + reg = <0x78400000 0x00010000>, + <0x78500000 0x00010000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <123>; + ti,sci-proc-ids = <0x06 0xff>; + resets = <&k3_reset 123 1>; + firmware-name = "am64-main-r5f1_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + main_r5fss1_core1: r5f@78600000 { + compatible = "ti,am64-r5f"; + reg = <0x78600000 0x00008000>, + <0x78700000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <124>; + ti,sci-proc-ids = <0x07 0xff>; + resets = <&k3_reset 124 1>; + firmware-name = "am64-main-r5f1_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + + serdes_wiz0: wiz@f000000 { + compatible = "ti,am64-wiz-10g"; + #address-cells = <1>; + #size-cells = <1>; + power-domains = <&k3_pds 162 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 162 0>, <&k3_clks 162 1>, <&serdes_refclk>; + clock-names = "fck", "core_ref_clk", "ext_ref_clk"; + num-lanes = <1>; + #reset-cells = <1>; + #clock-cells = <1>; + ranges = <0x0f000000 0x0 0x0f000000 0x00010000>; + + assigned-clocks = <&k3_clks 162 1>; + assigned-clock-parents = <&k3_clks 162 5>; + + serdes0: serdes@f000000 { + compatible = "ti,j721e-serdes-10g"; + reg = <0x0f000000 0x00010000>; + reg-names = "torrent_phy"; + resets = <&serdes_wiz0 0>; + reset-names = "torrent_reset"; + clocks = <&serdes_wiz0 TI_WIZ_PLL0_REFCLK>, <&serdes_wiz0 TI_WIZ_PHY_EN_REFCLK>; + clock-names = "refclk", "phy_en_refclk"; + assigned-clocks = <&serdes_wiz0 TI_WIZ_PLL0_REFCLK>, <&serdes_wiz0 TI_WIZ_PLL1_REFCLK>, <&serdes_wiz0 TI_WIZ_REFCLK_DIG>; + assigned-clock-parents = <&k3_clks 162 1>, <&k3_clks 162 1>, <&k3_clks 162 1>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + }; + }; + + pcie0_rc: pcie@f102000 { + compatible = "ti,am64-pcie-host", "ti,j721e-pcie-host"; + reg = <0x00 0x0f102000 0x00 0x1000>, + <0x00 0x0f100000 0x00 0x400>, + <0x00 0x0d000000 0x00 0x00800000>, + <0x00 0x68000000 0x00 0x00001000>; + reg-names = "intd_cfg", "user_cfg", "reg", "cfg"; + interrupt-names = "link_state"; + interrupts = ; + device_type = "pci"; + ti,syscon-pcie-ctrl = <&main_conf 0x4070>; + max-link-speed = <2>; + num-lanes = <1>; + power-domains = <&k3_pds 114 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 114 0>, <&serdes0 CDNS_TORRENT_REFCLK_DRIVER>; + clock-names = "fck", "pcie_refclk"; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x0 0xff>; + cdns,no-bar-match-nbits = <64>; + vendor-id = <0x104c>; + device-id = <0xb010>; + msi-map = <0x0 &gic_its 0x0 0x10000>; + ranges = <0x01000000 0x00 0x68001000 0x00 0x68001000 0x00 0x0010000>, + <0x02000000 0x00 0x68011000 0x00 0x68011000 0x00 0x7fef000>; + dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x00000010 0x0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0_intc 0>, /* INT A */ + <0 0 0 2 &pcie0_intc 0>, /* INT B */ + <0 0 0 3 &pcie0_intc 0>, /* INT C */ + <0 0 0 4 &pcie0_intc 0>; /* INT D */ + + pcie0_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic500>; + interrupts = ; + }; + }; + + pcie0_ep: pcie-ep@f102000 { + compatible = "ti,am64-pcie-ep", "ti,j721e-pcie-ep"; + reg = <0x00 0x0f102000 0x00 0x1000>, + <0x00 0x0f100000 0x00 0x400>, + <0x00 0x0d000000 0x00 0x00800000>, + <0x00 0x68000000 0x00 0x08000000>; + reg-names = "intd_cfg", "user_cfg", "reg", "mem"; + interrupt-names = "link_state"; + interrupts = ; + ti,syscon-pcie-ctrl = <&main_conf 0x4070>; + max-link-speed = <2>; + num-lanes = <1>; + power-domains = <&k3_pds 114 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 114 0>; + clock-names = "fck"; + max-functions = /bits/ 8 <1>; + }; + + usbss0: cdns-usb@f900000{ + compatible = "ti,am64-usb"; + reg = <0x00 0xf900000 0x00 0x100>; + power-domains = <&k3_pds 161 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 161 9>, <&k3_clks 161 1>; + clock-names = "ref", "lpm"; + assigned-clocks = <&k3_clks 161 9>; /* USB2_REFCLK */ + assigned-clock-parents = <&k3_clks 161 10>; /* HF0SC0 */ + #address-cells = <2>; + #size-cells = <2>; + ranges; + usb0: usb@f400000{ + compatible = "cdns,usb3"; + reg = <0x00 0xf400000 0x00 0x10000>, + <0x00 0xf410000 0x00 0x10000>, + <0x00 0xf420000 0x00 0x10000>; + reg-names = "otg", + "xhci", + "dev"; + interrupts = , /* irq.0 */ + , /* irq.6 */ + ; /* otgirq */ + interrupt-names = "host", + "peripheral", + "otg"; + maximum-speed = "super-speed"; + dr_mode = "otg"; + }; + }; + + tscadc0: tscadc@28001000 { + compatible = "ti,am654-tscadc", "ti,am3359-tscadc"; + reg = <0x00 0x28001000 0x00 0x1000>; + interrupts = ; + power-domains = <&k3_pds 0 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 0 0>; + assigned-clocks = <&k3_clks 0 0>; + assigned-clock-parents = <&k3_clks 0 3>; + assigned-clock-rates = <60000000>; + clock-names = "adc_tsc_fck"; + + adc { + #io-channel-cells = <1>; + compatible = "ti,am654-adc", "ti,am3359-adc"; + }; + }; + + fss: bus@fc00000 { + compatible = "simple-bus"; + reg = <0x00 0x0fc00000 0x00 0x70000>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + ospi0: spi@fc40000 { + compatible = "ti,am654-ospi", "cdns,qspi-nor"; + reg = <0x00 0x0fc40000 0x00 0x100>, + <0x05 0x00000000 0x01 0x00000000>; + interrupts = ; + cdns,fifo-depth = <256>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x0>; + #address-cells = <0x1>; + #size-cells = <0x0>; + clocks = <&k3_clks 75 6>; + assigned-clocks = <&k3_clks 75 6>; + assigned-clock-parents = <&k3_clks 75 7>; + assigned-clock-rates = <166666666>; + power-domains = <&k3_pds 75 TI_SCI_PD_EXCLUSIVE>; + }; + }; + + crypto: crypto@40900000 { + compatible = "ti,am64-sa2ul"; + reg = <0x00 0x40900000 0x00 0x1200>; + power-domains = <&k3_pds 133 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x40900000 0x00 0x40900000 0x00 0x30000>; + dmas = <&main_pktdma 0xc001 0>, <&main_pktdma 0x4002 0>, + <&main_pktdma 0x4003 0>; + dma-names = "tx", "rx1", "rx2"; + + rng: rng@40910000 { + compatible = "inside-secure,safexcel-eip76"; + reg = <0x00 0x40910000 0x00 0x7d>; + interrupts = ; + clocks = <&k3_clks 133 0>; + }; + }; + + main_mcan0: can@20701000 { + compatible = "bosch,m_can"; + reg = <0x00 0x20701000 0x00 0x200>, + <0x00 0x20708000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 98 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 98 5>, <&k3_clks 98 0>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan1: can@20711000 { + compatible = "bosch,m_can"; + reg = <0x00 0x20711000 0x00 0x200>, + <0x00 0x20718000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 99 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 99 5>, <&k3_clks 99 0>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + icssg0: icssg@30000000 { + compatible = "ti,am642-icssg"; + reg = <0x00 0x30000000 0x00 0x80000>; + power-domains = <&k3_pds 81 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x30000000 0x80000>; + + icssg0_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", "shrdram2"; + }; + + icssg0_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg0_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 81 0>, /* icssg0_core_clk */ + <&k3_clks 81 20>; /* icssg0_iclk */ + assigned-clocks = <&icssg0_coreclk_mux>; + assigned-clock-parents = <&k3_clks 81 20>; + }; + + icssg0_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 81 3>, /* icssg0_iep_clk */ + <&icssg0_coreclk_mux>; /* icssg0_coreclk_mux */ + assigned-clocks = <&icssg0_iepclk_mux>; + assigned-clock-parents = <&icssg0_coreclk_mux>; + }; + }; + }; + + icssg0_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; + + icssg0_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; + + icssg0_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg0_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg0_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru0_0: pru@34000 { + compatible = "ti,am642-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-pru0_0-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu0_0: rtu@4000 { + compatible = "ti,am642-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-rtu0_0-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru0_0: txpru@a000 { + compatible = "ti,am642-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-txpru0_0-fw"; + }; + + pru0_1: pru@38000 { + compatible = "ti,am642-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-pru0_1-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu0_1: rtu@6000 { + compatible = "ti,am642-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-rtu0_1-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru0_1: txpru@c000 { + compatible = "ti,am642-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-txpru0_1-fw"; + }; + + icssg0_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + clocks = <&k3_clks 62 3>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + }; + }; + + icssg1: icssg@30080000 { + compatible = "ti,am642-icssg"; + reg = <0x00 0x30080000 0x00 0x80000>; + power-domains = <&k3_pds 82 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x30080000 0x80000>; + + icssg1_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", "shrdram2"; + }; + + icssg1_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg1_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 82 0>, /* icssg1_core_clk */ + <&k3_clks 82 20>; /* icssg1_iclk */ + assigned-clocks = <&icssg1_coreclk_mux>; + assigned-clock-parents = <&k3_clks 82 20>; + }; + + icssg1_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 82 3>, /* icssg1_iep_clk */ + <&icssg1_coreclk_mux>; /* icssg1_coreclk_mux */ + assigned-clocks = <&icssg1_iepclk_mux>; + assigned-clock-parents = <&icssg1_coreclk_mux>; + }; + }; + }; + + icssg1_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg1_iepclk_mux>; + }; + + icssg1_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg1_iepclk_mux>; + }; + + icssg1_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg1_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg1_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru1_0: pru@34000 { + compatible = "ti,am642-pru"; + reg = <0x34000 0x4000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-pru1_0-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu1_0: rtu@4000 { + compatible = "ti,am642-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-rtu1_0-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru1_0: txpru@a000 { + compatible = "ti,am642-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-txpru1_0-fw"; + }; + + pru1_1: pru@38000 { + compatible = "ti,am642-pru"; + reg = <0x38000 0x4000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-pru1_1-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu1_1: rtu@6000 { + compatible = "ti,am642-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-rtu1_1-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru1_1: txpru@c000 { + compatible = "ti,am642-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am64x-txpru1_1-fw"; + }; + + icssg1_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 82 0>; + clock-names = "fck"; + bus_freq = <1000000>; + }; + }; + + epwm0: pwm@23000000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23000000 0x0 0x100>; + power-domains = <&k3_pds 86 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 0>, <&k3_clks 86 0>; + clock-names = "tbclk", "fck"; + }; + + epwm1: pwm@23010000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23010000 0x0 0x100>; + power-domains = <&k3_pds 87 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 1>, <&k3_clks 87 0>; + clock-names = "tbclk", "fck"; + }; + + epwm2: pwm@23020000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23020000 0x0 0x100>; + power-domains = <&k3_pds 88 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 2>, <&k3_clks 88 0>; + clock-names = "tbclk", "fck"; + }; + + epwm3: pwm@23030000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23030000 0x0 0x100>; + power-domains = <&k3_pds 89 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 3>, <&k3_clks 89 0>; + clock-names = "tbclk", "fck"; + }; + + epwm4: pwm@23040000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23040000 0x0 0x100>; + power-domains = <&k3_pds 90 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 4>, <&k3_clks 90 0>; + clock-names = "tbclk", "fck"; + }; + + epwm5: pwm@23050000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23050000 0x0 0x100>; + power-domains = <&k3_pds 91 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 5>, <&k3_clks 91 0>; + clock-names = "tbclk", "fck"; + }; + + epwm6: pwm@23060000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23060000 0x0 0x100>; + power-domains = <&k3_pds 92 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 6>, <&k3_clks 92 0>; + clock-names = "tbclk", "fck"; + }; + + epwm7: pwm@23070000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23070000 0x0 0x100>; + power-domains = <&k3_pds 93 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 7>, <&k3_clks 93 0>; + clock-names = "tbclk", "fck"; + }; + + epwm8: pwm@23080000 { + compatible = "ti,am64-epwm", "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x0 0x23080000 0x0 0x100>; + power-domains = <&k3_pds 94 TI_SCI_PD_EXCLUSIVE>; + clocks = <&epwm_tbclk 8>, <&k3_clks 94 0>; + clock-names = "tbclk", "fck"; + }; + + ecap0: pwm@23100000 { + compatible = "ti,am64-ecap", "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x0 0x23100000 0x0 0x60>; + power-domains = <&k3_pds 51 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 51 0>; + clock-names = "fck"; + }; + + ecap1: pwm@23110000 { + compatible = "ti,am64-ecap", "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x0 0x23110000 0x0 0x60>; + power-domains = <&k3_pds 52 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 52 0>; + clock-names = "fck"; + }; + + ecap2: pwm@23120000 { + compatible = "ti,am64-ecap", "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x0 0x23120000 0x0 0x60>; + power-domains = <&k3_pds 53 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 53 0>; + clock-names = "fck"; + }; + + main_rti0: watchdog@e000000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x00 0x0e000000 0x00 0x100>; + clocks = <&k3_clks 125 0>; + power-domains = <&k3_pds 125 TI_SCI_PD_EXCLUSIVE>; + assigned-clocks = <&k3_clks 125 0>; + assigned-clock-parents = <&k3_clks 125 2>; + }; + + main_rti1: watchdog@e010000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x00 0x0e010000 0x00 0x100>; + clocks = <&k3_clks 126 0>; + power-domains = <&k3_pds 126 TI_SCI_PD_EXCLUSIVE>; + assigned-clocks = <&k3_clks 126 0>; + assigned-clock-parents = <&k3_clks 126 2>; + }; + + gpmc0: memory-controller@3b000000 { + compatible = "ti,am64-gpmc"; + power-domains = <&k3_pds 80 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 80 0>; + clock-names = "fck"; + reg = <0x00 0x03b000000 0x00 0x2000>, + <0x00 0x050000000 0x00 0x7FFFFFF>; + reg-names = "cfg", "data"; + interrupts = ; + gpmc,num-cs = <3>; + gpmc,num-waitpins = <2>; + #address-cells = <2>; + #size-cells = <1>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + }; + + elm0: ecc@25010000 { + compatible = "ti,am3352-elm"; + reg = <0x00 0x25010000 0x00 0x2000>; + interrupts = ; + power-domains = <&k3_pds 54 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 54 0>; + clock-names = "fck"; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am64-mcu.dtsi b/arch/arm64/boot/dts/ti/k3-am64-mcu.dtsi --- a/arch/arm64/boot/dts/ti/k3-am64-mcu.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am64-mcu.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for AM64 SoC Family MCU Domain peripherals + * + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&cbass_mcu { + mcu_uart0: serial@4a00000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x04a00000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 149 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 149 0>; + clock-names = "fclk"; + }; + + mcu_uart1: serial@4a10000 { + compatible = "ti,am64-uart", "ti,am654-uart"; + reg = <0x00 0x04a10000 0x00 0x100>; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + clock-frequency = <48000000>; + current-speed = <115200>; + power-domains = <&k3_pds 160 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 160 0>; + clock-names = "fclk"; + }; + + mcu_i2c0: i2c@4900000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x04900000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 106 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 106 2>; + clock-names = "fck"; + }; + + mcu_i2c1: i2c@4910000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x04910000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 107 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 107 2>; + clock-names = "fck"; + }; + + mcu_spi0: spi@4b00000 { + compatible = "ti,am654-mcspi", "ti,omap4-mcspi"; + reg = <0x00 0x04b00000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 147 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 147 0>; + }; + + mcu_spi1: spi@4b10000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x00 0x04b10000 0x00 0x400>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 148 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 148 0>; + }; + + mcu_gpio_intr: interrupt-controller1 { + compatible = "ti,sci-intr"; + ti,intr-trigger-type = <1>; + interrupt-controller; + interrupt-parent = <&gic500>; + #interrupt-cells = <1>; + ti,sci = <&dmsc>; + ti,sci-dev-id = <5>; + ti,interrupt-ranges = <0 104 4>; + }; + + mcu_gpio0: gpio@4201000 { + compatible = "ti,am64-gpio", "keystone-gpio"; + reg = <0x0 0x4201000 0x0 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&mcu_gpio_intr>; + interrupts = <30>, <31>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <23>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 79 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 79 0>; + clock-names = "gpio"; + }; + + mcu_m4fss: m4fss@5000000 { + compatible = "ti,am64-m4fss"; + reg = <0x00 0x5000000 0x00 0x30000>, + <0x00 0x5040000 0x00 0x10000>; + reg-names = "iram", "dram"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <9>; + ti,sci-proc-ids = <0x18 0xff>; + resets = <&k3_reset 9 1>; + firmware-name = "am64-mcu-m4f0_0-fw"; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-base-board.dts b/arch/arm64/boot/dts/ti/k3-am654-base-board.dts --- a/arch/arm64/boot/dts/ti/k3-am654-base-board.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-base-board.dts 2022-01-06 12:45:53.810318090 -0500 @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2016-2020 Texas Instruments Incorporated - https://www.ti.com/ */ /dts-v1/; @@ -13,6 +13,11 @@ compatible = "ti,am654-evm", "ti,am654"; model = "Texas Instruments AM654 Base Board"; + aliases { + ethernet1 = &icssg2_emac0; + ethernet2 = &icssg2_emac1; + }; + chosen { stdout-path = "serial2:115200n8"; bootargs = "earlycon=ns16550a,mmio32,0x02800000"; @@ -29,11 +34,42 @@ #address-cells = <2>; #size-cells = <2>; ranges; + secure_ddr: secure-ddr@9e800000 { reg = <0 0x9e800000 0 0x01800000>; /* for OP-TEE */ alignment = <0x1000>; no-map; }; + + mcu_r5fss0_core0_dma_memory_region: r5f-dma-memory@a0000000 { + compatible = "shared-dma-pool"; + reg = <0 0xa0000000 0 0x100000>; + no-map; + }; + + mcu_r5fss0_core0_memory_region: r5f-memory@a0100000 { + compatible = "shared-dma-pool"; + reg = <0 0xa0100000 0 0xf00000>; + no-map; + }; + + mcu_r5fss0_core1_dma_memory_region: r5f-dma-memory@a1000000 { + compatible = "shared-dma-pool"; + reg = <0 0xa1000000 0 0x100000>; + no-map; + }; + + mcu_r5fss0_core1_memory_region: r5f-memory@a1100000 { + compatible = "shared-dma-pool"; + reg = <0 0xa1100000 0 0xf00000>; + no-map; + }; + + rtos_ipc_memory_region: ipc-memories@a2000000 { + reg = <0x00 0xa2000000 0x00 0x00100000>; + alignment = <0x1000>; + no-map; + }; }; gpio-keys { @@ -55,10 +91,101 @@ }; }; - clk_ov5640_fixed: clock { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <24000000>; + evm_12v0: fixedregulator-evm12v0 { + /* main supply */ + compatible = "regulator-fixed"; + regulator-name = "evm_12v0"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + regulator-boot-on; + }; + + vcc3v3_io: fixedregulator-vcc3v3io { + /* Output of TPS54334 */ + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&evm_12v0>; + }; + + vdd_mmc1_sd: fixedregulator-sd { + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vcc3v3_io>; + gpio = <&pca9554 4 GPIO_ACTIVE_HIGH>; + }; + + /* Dual Ethernet application node on PRU-ICSSG2 */ + icssg2_eth: icssg2-eth { + compatible = "ti,am654-icssg-prueth"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg2_rgmii_pins_default>; + sram = <&msmc_ram>; + ti,prus = <&pru2_0>, <&rtu2_0>, <&tx_pru2_0>, + <&pru2_1>, <&rtu2_1>, <&tx_pru2_1>; + firmware-name = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf"; + + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, + <2>, /* MII mode */ + <2>, + <2>; + + mii-g-rt = <&icssg2_mii_g_rt>; + mii-rt = <&icssg2_mii_rt>; + iep = <&icssg2_iep0>, <&icssg2_iep1>; + + interrupt-parent = <&icssg2_intc>; + interrupts = <24 0 2>, <25 1 3>; + interrupt-names = "tx_ts0", "tx_ts1"; + + dmas = <&main_udmap 0xc300>, /* egress slice 0 */ + <&main_udmap 0xc301>, /* egress slice 0 */ + <&main_udmap 0xc302>, /* egress slice 0 */ + <&main_udmap 0xc303>, /* egress slice 0 */ + <&main_udmap 0xc304>, /* egress slice 1 */ + <&main_udmap 0xc305>, /* egress slice 1 */ + <&main_udmap 0xc306>, /* egress slice 1 */ + <&main_udmap 0xc307>, /* egress slice 1 */ + + <&main_udmap 0x4300>, /* ingress slice 0 */ + <&main_udmap 0x4301>, /* ingress slice 1 */ + <&main_udmap 0x4302>, /* mgmnt rsp slice 0 */ + <&main_udmap 0x4303>; /* mgmnt rsp slice 1 */ + dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1", + "rxmgm0", "rxmgm1"; + + icssg2_emac0: ethernet-mii0 { + phy-handle = <&icssg2_phy0>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&scm_conf 0x4120>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + icssg2_emac1: ethernet-mii1 { + phy-handle = <&icssg2_phy1>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&scm_conf 0x4124>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; }; }; @@ -185,6 +312,43 @@ AM65X_IOPAD(0x02c0, PIN_OUTPUT, 0) /* (AC8) USB1_DRVVBUS */ >; }; + + icssg2_mdio_pins_default: icssg2-mdio-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0094, PIN_INPUT, 2) /* (AC19) PRG2_PRU0_GPO7.PRG2_MDIO0_MDIO */ + AM65X_IOPAD(0x00c8, PIN_OUTPUT, 2) /* (AE15) PRG2_PRU1_GPO7.PRG2_MDIO0_MDC */ + >; + }; + + icssg2_rgmii_pins_default: icssg2-rgmii-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x00ac, PIN_INPUT, 2) /* (AH15) PRG2_PRU1_GPO0.PRG2_RGMII2_RD0 */ + AM65X_IOPAD(0x00b0, PIN_INPUT, 2) /* (AC16) PRG2_PRU1_GPO1.PRG2_RGMII2_RD1 */ + AM65X_IOPAD(0x00b4, PIN_INPUT, 2) /* (AD17) PRG2_PRU1_GPO2.PRG2_RGMII2_RD2 */ + AM65X_IOPAD(0x00b8, PIN_INPUT, 2) /* (AH14) PRG2_PRU1_GPO3.PRG2_RGMII2_RD3 */ + AM65X_IOPAD(0x00cc, PIN_OUTPUT, 2) /* (AD15) PRG2_PRU1_GPO8.PRG2_RGMII2_TD0 */ + AM65X_IOPAD(0x00d0, PIN_OUTPUT, 2) /* (AF14) PRG2_PRU1_GPO9.PRG2_RGMII2_TD1 */ + AM65X_IOPAD(0x00d4, PIN_OUTPUT, 2) /* (AC15) PRG2_PRU1_GPO10.PRG2_RGMII2_TD2 */ + AM65X_IOPAD(0x00d8, PIN_OUTPUT, 2) /* (AD14) PRG2_PRU1_GPO11.PRG2_RGMII2_TD3 */ + AM65X_IOPAD(0x00dc, PIN_INPUT, 2) /* (AE14) PRG2_PRU1_GPO16.PRG2_RGMII2_TXC */ + AM65X_IOPAD(0x00c4, PIN_OUTPUT, 2) /* (AC17) PRG2_PRU1_GPO6.PRG2_RGMII2_TX_CTL */ + AM65X_IOPAD(0x00c0, PIN_INPUT, 2) /* (AG15) PRG2_PRU1_GPO5.PRG2_RGMII2_RXC */ + AM65X_IOPAD(0x00bc, PIN_INPUT, 2) /* (AG14) PRG2_PRU1_GPO4.PRG2_RGMII2_RX_CTL */ + + AM65X_IOPAD(0x0078, PIN_INPUT, 2) /* (AF18) PRG2_PRU0_GPO0.PRG2_RGMII1_RD0 */ + AM65X_IOPAD(0x007c, PIN_INPUT, 2) /* (AE18) PRG2_PRU0_GPO1.PRG2_RGMII1_RD1 */ + AM65X_IOPAD(0x0080, PIN_INPUT, 2) /* (AH17) PRG2_PRU0_GPO2.PRG2_RGMII1_RD2 */ + AM65X_IOPAD(0x0084, PIN_INPUT, 2) /* (AG18) PRG2_PRU0_GPO3.PRG2_RGMII1_RD3 */ + AM65X_IOPAD(0x0098, PIN_OUTPUT, 2) /* (AH16) PRG2_PRU0_GPO8.PRG2_RGMII1_TD0 */ + AM65X_IOPAD(0x009c, PIN_OUTPUT, 2) /* (AG16) PRG2_PRU0_GPO9.PRG2_RGMII1_TD1 */ + AM65X_IOPAD(0x00a0, PIN_OUTPUT, 2) /* (AF16) PRG2_PRU0_GPO10.PRG2_RGMII1_TD2 */ + AM65X_IOPAD(0x00a4, PIN_OUTPUT, 2) /* (AE16) PRG2_PRU0_GPO11.PRG2_RGMII1_TD3 */ + AM65X_IOPAD(0x00a8, PIN_INPUT, 2) /* (AD16) PRG2_PRU0_GPO16.PRG2_RGMII1_TXC */ + AM65X_IOPAD(0x0090, PIN_OUTPUT, 2) /* (AE17) PRG2_PRU0_GPO6.PRG2_RGMII1_TX_CTL */ + AM65X_IOPAD(0x008c, PIN_INPUT, 2) /* (AF17) PRG2_PRU0_GPO5.PRG2_RGMII1_RXC */ + AM65X_IOPAD(0x0088, PIN_INPUT, 2) /* (AG17) PRG2_PRU0_GPO4.PRG2_RGMII1_RX_CTL */ + >; + }; }; &main_pmx1 { @@ -211,7 +375,7 @@ &wkup_uart0 { /* Wakeup UART is used by System firmware */ - status = "disabled"; + status = "reserved"; }; &main_uart0 { @@ -256,23 +420,6 @@ pinctrl-names = "default"; pinctrl-0 = <&main_i2c1_pins_default>; clock-frequency = <400000>; - - ov5640: camera@3c { - compatible = "ovti,ov5640"; - reg = <0x3c>; - - clocks = <&clk_ov5640_fixed>; - clock-names = "xclk"; - - port { - csi2_cam0: endpoint { - remote-endpoint = <&csi2_phy0>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - }; - }; &main_i2c2 { @@ -319,20 +466,13 @@ * disable sdhci1 */ &sdhci1 { + vmmc-supply = <&vdd_mmc1_sd>; pinctrl-names = "default"; pinctrl-0 = <&main_mmc1_pins_default>; ti,driver-strength-ohm = <50>; disable-wp; }; -&dwc3_1 { - status = "okay"; -}; - -&usb1_phy { - status = "okay"; -}; - &usb1 { pinctrl-names = "default"; pinctrl-0 = <&usb1_pins_default>; @@ -441,6 +581,18 @@ status = "disabled"; }; +&mcu_r5fss0_core0 { + memory-region = <&mcu_r5fss0_core0_dma_memory_region>, + <&mcu_r5fss0_core0_memory_region>; + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; +}; + +&mcu_r5fss0_core1 { + memory-region = <&mcu_r5fss0_core1_dma_memory_region>, + <&mcu_r5fss0_core1_memory_region>; + mboxes = <&mailbox0_cluster1 &mbox_mcu_r5fss0_core1>; +}; + &ospi0 { pinctrl-names = "default"; pinctrl-0 = <&mcu_fss0_ospi0_pins_default>; @@ -448,9 +600,9 @@ flash@0{ compatible = "jedec,spi-nor"; reg = <0x0>; - spi-tx-bus-width = <1>; + spi-tx-bus-width = <8>; spi-rx-bus-width = <8>; - spi-max-frequency = <40000000>; + spi-max-frequency = <25000000>; cdns,tshsl-ns = <60>; cdns,tsd2d-ns = <60>; cdns,tchsh-ns = <60>; @@ -461,14 +613,6 @@ }; }; -&csi2_0 { - csi2_phy0: endpoint { - remote-endpoint = <&csi2_cam0>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; -}; - &mcu_cpsw { pinctrl-names = "default"; pinctrl-0 = <&mcu_cpsw_pins_default &mcu_mdio_pins_default>; @@ -486,3 +630,37 @@ phy-mode = "rgmii-rxid"; phy-handle = <&phy0>; }; + +&mcasp0 { + status = "disabled"; +}; + +&mcasp1 { + status = "disabled"; +}; + +&mcasp2 { + status = "disabled"; +}; + +&dss { + status = "disabled"; +}; + +&icssg2_mdio { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg2_mdio_pins_default>; + + icssg2_phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; + + icssg2_phy1: ethernet-phy@3 { + reg = <3>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-base-board-sr1.dts b/arch/arm64/boot/dts/ti/k3-am654-base-board-sr1.dts --- a/arch/arm64/boot/dts/ti/k3-am654-base-board-sr1.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-base-board-sr1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include "k3-am654-sr1.dts" + +&sdhci1 { + no-1-8-v; +}; + +&icssg2_eth { + compatible = "ti,am654-icssg-prueth-sr1"; + ti,prus = <&pru2_0>, <&rtu2_0>, <&pru2_1>, <&rtu2_1>; + firmware-name = "ti-pruss/am65x-pru0-prueth-fw.elf", + "ti-pruss/am65x-rtu0-prueth-fw.elf", + "ti-pruss/am65x-pru1-prueth-fw.elf", + "ti-pruss/am65x-rtu1-prueth-fw.elf"; + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, /* MII mode */ + <2>; +}; + +&icssg2_iep0 { + interrupt-parent = <&icssg2_intc>; + interrupts = <7 7 8>; + interrupt-names = "iep_cap_cmp"; +}; + +&icssg2_iep1 { + interrupt-parent = <&icssg2_intc>; + interrupts = <56 8 9>; + interrupt-names = "iep_cap_cmp"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-evm-oldi-lcd1evm.dts b/arch/arm64/boot/dts/ti/k3-am654-evm-oldi-lcd1evm.dts --- a/arch/arm64/boot/dts/ti/k3-am654-evm-oldi-lcd1evm.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-evm-oldi-lcd1evm.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * OLDI-LCD1EVM Rocktech integrated panel and touch DT overlay for AM654-EVM. + * + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + display0 { + compatible = "rocktech,rk101ii01d-ct"; + backlight = <&lcd_bl>; + enable-gpios = <&pca9555 8 GPIO_ACTIVE_HIGH>; + + port { + lcd_in0: endpoint { + remote-endpoint = <&oldi_out0>; + }; + }; + }; + + lcd_bl: backlight { + compatible = "pwm-backlight"; + pwms = <&ecap0 0 50000 PWM_POLARITY_INVERTED>; + brightness-levels = + <0 32 64 96 128 160 192 224 255>; + default-brightness-level = <8>; + }; + }; + }; +}; + +&dss { + status = "okay"; +}; + +&dss_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + oldi_out0: endpoint { + remote-endpoint = <&lcd_in0>; + }; + }; +}; + +&main_i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + gt928: touchscreen@14 { + status = "okay"; + compatible = "goodix,gt928"; + reg = <0x14>; + + interrupt-parent = <&pca9554>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + touchscreen-size-x = <1280>; + touchscreen-size-y = <800>; + + reset-gpios = <&pca9555 9 GPIO_ACTIVE_HIGH>; + irq-gpios = <&pca9554 3 GPIO_ACTIVE_HIGH>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-evm-ov5640.dts b/arch/arm64/boot/dts/ti/k3-am654-evm-ov5640.dts --- a/arch/arm64/boot/dts/ti/k3-am654-evm-ov5640.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-evm-ov5640.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_ov5640_fixed: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; + }; +}; + +&main_i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + ov5640: camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + + clocks = <&clk_ov5640_fixed>; + clock-names = "xclk"; + + port { + csi2_cam0: endpoint { + remote-endpoint = <&csi2_phy0>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; +}; + +&csi2_0 { + csi2_phy0: endpoint { + remote-endpoint = <&csi2_cam0>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-evm-tc358876.dts b/arch/arm64/boot/dts/ti/k3-am654-evm-tc358876.dts --- a/arch/arm64/boot/dts/ti/k3-am654-evm-tc358876.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-evm-tc358876.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Toshiba TC358867 expansion board for AM654-EVM. + * + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + dp_refclk: fixed-clock-tc358767 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <19200000>; + }; + }; + }; +}; + +&main_pmx0 { + dss_vout1_pins_default: dss-vout1-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0000, PIN_OUTPUT, 1) /* VOUT1_DATA0 */ + AM65X_IOPAD(0x0004, PIN_OUTPUT, 1) /* VOUT1_DATA1 */ + AM65X_IOPAD(0x0008, PIN_OUTPUT, 1) /* VOUT1_DATA2 */ + AM65X_IOPAD(0x000c, PIN_OUTPUT, 1) /* VOUT1_DATA3 */ + AM65X_IOPAD(0x0010, PIN_OUTPUT, 1) /* VOUT1_DATA4 */ + AM65X_IOPAD(0x0014, PIN_OUTPUT, 1) /* VOUT1_DATA5 */ + AM65X_IOPAD(0x0018, PIN_OUTPUT, 1) /* VOUT1_DATA6 */ + AM65X_IOPAD(0x001c, PIN_OUTPUT, 1) /* VOUT1_DATA7 */ + AM65X_IOPAD(0x0020, PIN_OUTPUT, 1) /* VOUT1_DATA8 */ + AM65X_IOPAD(0x0024, PIN_OUTPUT, 1) /* VOUT1_DATA9 */ + AM65X_IOPAD(0x0028, PIN_OUTPUT, 1) /* VOUT1_DATA10 */ + AM65X_IOPAD(0x002c, PIN_OUTPUT, 1) /* VOUT1_DATA11 */ + AM65X_IOPAD(0x0030, PIN_OUTPUT, 1) /* VOUT1_DATA12 */ + AM65X_IOPAD(0x0034, PIN_OUTPUT, 1) /* VOUT1_DATA13 */ + AM65X_IOPAD(0x0038, PIN_OUTPUT, 1) /* VOUT1_DATA14 */ + AM65X_IOPAD(0x003c, PIN_OUTPUT, 1) /* VOUT1_DATA15 */ + AM65X_IOPAD(0x0040, PIN_OUTPUT, 1) /* VOUT1_DATA16 */ + AM65X_IOPAD(0x0044, PIN_OUTPUT, 1) /* VOUT1_DATA17 */ + AM65X_IOPAD(0x0048, PIN_OUTPUT, 1) /* VOUT1_DATA18 */ + AM65X_IOPAD(0x004c, PIN_OUTPUT, 1) /* VOUT1_DATA19 */ + AM65X_IOPAD(0x0050, PIN_OUTPUT, 1) /* VOUT1_DATA20 */ + AM65X_IOPAD(0x0054, PIN_OUTPUT, 1) /* VOUT1_DATA21 */ + AM65X_IOPAD(0x0058, PIN_OUTPUT, 1) /* VOUT1_DATA22 */ + AM65X_IOPAD(0x005c, PIN_OUTPUT, 1) /* VOUT1_DATA23 */ + AM65X_IOPAD(0x0060, PIN_OUTPUT, 1) /* VOUT1_VSYNC */ + AM65X_IOPAD(0x0064, PIN_OUTPUT, 1) /* VOUT1_HSYNC */ + AM65X_IOPAD(0x0068, PIN_OUTPUT, 1) /* VOUT1_PCLK */ + AM65X_IOPAD(0x006c, PIN_OUTPUT, 1) /* VOUT1_DE */ + >; + }; +}; + +&main_i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + edp-bridge@f { + compatible = "toshiba,tc358767"; + reg = <0x0f>; + + reset-gpios = <&pca9555 6 GPIO_ACTIVE_HIGH>; + + clock-names = "ref"; + clocks = <&dp_refclk>; + + toshiba,hpd-pin = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + bridge_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; + }; +}; + +&dss { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&dss_vout1_pins_default>; + + assigned-clocks = <&k3_clks 67 2>; + assigned-clock-parents = <&k3_clks 67 5>; +}; + +&dss_ports { + #address-cells = <1>; + #size-cells = <0>; + port@1 { + reg = <1>; + + dpi_out: endpoint { + remote-endpoint = <&bridge_in>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-gp.dts b/arch/arm64/boot/dts/ti/k3-am654-gp.dts --- a/arch/arm64/boot/dts/ti/k3-am654-gp.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-gp.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DT overlay for GP application board on AM654 EVM + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + gp_vcc_5v0: fixedregulator-gp_vcc_5v0 { + compatible = "regulator-fixed"; + regulator-name = "gp_vcc_5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; + + codec_vcc_3v3: fixedregulator-codec_vcc_3v3 { + /* LP5912-3.3DRVT */ + compatible = "regulator-fixed"; + regulator-name = "codec_vcc_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&gp_vcc_5v0>; + regulator-always-on; + regulator-boot-on; + }; + + gp_vcc_1v8: fixedregulator-gp_vcc_1v8 { + compatible = "regulator-fixed"; + regulator-name = "gp_vcc_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "AM65x-GPEVM"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack", + "Line", "Line In"; + simple-audio-card,routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "LINE1L", "Line In", + "LINE1R", "Line In"; + simple-audio-card,format = "dsp_b"; + simple-audio-card,bitclock-master = <&sound_master>; + simple-audio-card,frame-master = <&sound_master>; + simple-audio-card,bitclock-inversion; + + simple-audio-card,cpu { + sound-dai = <&mcasp0>; + }; + + sound_master: simple-audio-card,codec { + sound-dai = <&tlv320aic3106>; + system-clock-frequency = <12000000>; + }; + }; + }; + }; +}; + +&main_pmx0 { + mcasp0_pins: mcasp0-pins { + pinctrl-single,pins = < + AM65X_IOPAD(0x01f4, PIN_INPUT, 5) /* (V24) PRG0_PRU0_GPO0.MCASP0_ACLKX */ + AM65X_IOPAD(0x01f8, PIN_INPUT, 5) /* (W25) PRG0_PRU0_GPO1.MCASP0_AFSX */ + AM65X_IOPAD(0x0204, PIN_OUTPUT, 5) /* (Y24) PRG0_PRU0_GPO4.MCASP0_AXR0 */ + AM65X_IOPAD(0x0208, PIN_INPUT, 5) /* (V28) PRG0_PRU0_GPO5.MCASP0_AXR1 */ + >; + }; + + aic3106_pins: aic3106-pins { + pinctrl-single,pins = < + AM65X_IOPAD(0x011c, PIN_OUTPUT, 7) /* (AD19) PRG1_PRU0_GPO15.GPIO0_71 */ + >; + }; +}; + +&main_i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + tlv320aic3106: tlv320aic3106@1b { + #sound-dai-cells = <0>; + compatible = "ti,tlv320aic3106"; + reg = <0x1b>; + pinctrl-names = "default"; + pinctrl-0 = <&aic3106_pins>; + gpio-reset = <&main_gpio0 71 GPIO_ACTIVE_LOW>; /* gpio0_71 */ + /* Regulators */ + AVDD-supply = <&codec_vcc_3v3>; + IOVDD-supply = <&gp_vcc_1v8>; + DRVDD-supply = <&codec_vcc_3v3>; + DVDD-supply = <&gp_vcc_1v8>; + }; +}; + +&mcasp0 { + status = "okay"; + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + /* 16 serializers */ + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 1 2 0 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-idk.dts b/arch/arm64/boot/dts/ti/k3-am654-idk.dts --- a/arch/arm64/boot/dts/ti/k3-am654-idk.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-idk.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT overlay for IDK application board on AM654 EVM + * + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + aliases { + ethernet3 = "/icssg0-eth/ethernet-mii0"; + ethernet4 = "/icssg0-eth/ethernet-mii1"; + ethernet5 = "/icssg1-eth/ethernet-mii0"; + ethernet6 = "/icssg1-eth/ethernet-mii1"; + }; + + transceiver1: can-phy0 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcan0_gpio_pins_default>; + standby-gpios = <&main_gpio1 47 GPIO_ACTIVE_LOW>; + }; + + transceiver2: can-phy1 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcan1_gpio_pins_default>; + standby-gpios = <&main_gpio1 67 GPIO_ACTIVE_LOW>; + }; + + /* Dual Ethernet application node on PRU-ICSSG0 */ + icssg0_eth: icssg0-eth { + compatible = "ti,am654-icssg-prueth"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg0_rgmii_pins_default>; + sram = <&msmc_ram>; + ti,prus = <&pru0_0>, <&rtu0_0>, <&tx_pru0_0>, <&pru0_1>, <&rtu0_1>, <&tx_pru0_1>; + firmware-name = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf"; + + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, + <2>, /* MII mode */ + <2>, + <2>; + + mii-g-rt = <&icssg0_mii_g_rt>; + mii-rt = <&icssg0_mii_rt>; + iep = <&icssg0_iep0>, <&icssg0_iep1>; + + interrupt-parent = <&icssg0_intc>; + interrupts = <24 0 2>, <25 1 3>; + interrupt-names = "tx_ts0", "tx_ts1"; + + dmas = <&main_udmap 0xc100>, /* egress slice 0 */ + <&main_udmap 0xc101>, /* egress slice 0 */ + <&main_udmap 0xc102>, /* egress slice 0 */ + <&main_udmap 0xc103>, /* egress slice 0 */ + <&main_udmap 0xc104>, /* egress slice 1 */ + <&main_udmap 0xc105>, /* egress slice 1 */ + <&main_udmap 0xc106>, /* egress slice 1 */ + <&main_udmap 0xc107>, /* egress slice 1 */ + + <&main_udmap 0x4100>, /* ingress slice 0 */ + <&main_udmap 0x4101>, /* ingress slice 1 */ + <&main_udmap 0x4102>, /* mgmnt rsp slice 0 */ + <&main_udmap 0x4103>; /* mgmnt rsp slice 1 */ + dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1", + "rxmgm0", "rxmgm1"; + + icssg0_emac0: ethernet-mii0 { + phy-handle = <&icssg0_phy0>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&scm_conf 0x4100>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + icssg0_emac1: ethernet-mii1 { + phy-handle = <&icssg0_phy1>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&scm_conf 0x4104>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; + + /* Dual Ethernet application node on PRU-ICSSG1 */ + icssg1_eth: icssg1-eth { + compatible = "ti,am654-icssg-prueth"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg1_rgmii_pins_default>; + sram = <&msmc_ram>; + ti,prus = <&pru1_0>, <&rtu1_0>, <&tx_pru1_0>, <&pru1_1>, <&rtu1_1>, <&tx_pru1_1>; + firmware-name = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf"; + + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, + <2>, /* MII mode */ + <2>, + <2>; + + mii-g-rt = <&icssg1_mii_g_rt>; + mii-rt = <&icssg1_mii_rt>; + iep = <&icssg1_iep0>, <&icssg1_iep1>; + + interrupt-parent = <&icssg1_intc>; + interrupts = <24 0 2>, <25 1 3>; + + interrupt-names = "tx_ts0", "tx_ts1"; + + dmas = <&main_udmap 0xc200>, /* egress slice 0 */ + <&main_udmap 0xc201>, /* egress slice 0 */ + <&main_udmap 0xc202>, /* egress slice 0 */ + <&main_udmap 0xc203>, /* egress slice 0 */ + <&main_udmap 0xc204>, /* egress slice 1 */ + <&main_udmap 0xc205>, /* egress slice 1 */ + <&main_udmap 0xc206>, /* egress slice 1 */ + <&main_udmap 0xc207>, /* egress slice 1 */ + + <&main_udmap 0x4200>, /* ingress slice 0 */ + <&main_udmap 0x4201>, /* ingress slice 1 */ + <&main_udmap 0x4202>, /* mgmnt rsp slice 0 */ + <&main_udmap 0x4203>; /* mgmnt rsp slice 1 */ + dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1", + "rxmgm0", "rxmgm1"; + + icssg1_emac0: ethernet-mii0 { + phy-handle = <&icssg1_phy0>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&scm_conf 0x4110>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + icssg1_emac1: ethernet-mii1 { + phy-handle = <&icssg1_phy1>; + phy-mode = "rgmii-rxid"; + syscon-rgmii-delay = <&scm_conf 0x4114>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; + }; + }; +}; + +&main_pmx0 { + mcan0_gpio_pins_default: mcan0-gpio-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x023c, PIN_INPUT, 7) /* (V25) PRG0_PRU0_GPIO18:GPIO1_47 */ + >; + }; + + mcan1_gpio_pins_default: mcan1-gpio-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x028c, PIN_INPUT, 7) /* (Y26) PRG0_PRU1_GPIO18.GPIO1_67 */ + >; + }; + + icssg0_mdio_pins_default: icssg0-mdio-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0294, PIN_INPUT, 0) /* (AE26) PRG0_MDIO0_MDIO */ + AM65X_IOPAD(0x0298, PIN_OUTPUT, 0) /* (AE28) PRG0_MDIO0_MDC */ + >; + }; + + icssg0_rgmii_pins_default: icssg0-rgmii-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0244, PIN_INPUT, 2) /* (AB28) PRG0_PRU1_GPO0.PRG0_RGMII2_RD0 */ + AM65X_IOPAD(0x0248, PIN_INPUT, 2) /* (AC28) PRG0_PRU1_GPO1.PRG0_RGMII2_RD1 */ + AM65X_IOPAD(0x024c, PIN_INPUT, 2) /* (AC27) PRG0_PRU1_GPO2.PRG0_RGMII2_RD2 */ + AM65X_IOPAD(0x0250, PIN_INPUT, 2) /* (AB26) PRG0_PRU1_GPO3.PRG0_RGMII2_RD3 */ + AM65X_IOPAD(0x0274, PIN_OUTPUT, 2) /* (AC25) PRG0_PRU1_GPO12.PRG0_RGMII2_TD0 */ + AM65X_IOPAD(0x0278, PIN_OUTPUT, 2) /* (AD25) PRG0_PRU1_GPO13.PRG0_RGMII2_TD1 */ + AM65X_IOPAD(0x027c, PIN_OUTPUT, 2) /* (AD24) PRG0_PRU1_GPO14.PRG0_RGMII2_TD2 */ + AM65X_IOPAD(0x0280, PIN_OUTPUT, 2) /* (AE27) PRG0_PRU1_GPO15.PRG0_RGMII2_TD3 */ + AM65X_IOPAD(0x0284, PIN_INPUT, 2) /* (AC24) PRG0_PRU1_GPO16.PRG0_RGMII2_TXC */ + AM65X_IOPAD(0x0270, PIN_OUTPUT, 2) /* (AB24) PRG0_PRU1_GPO11.PRG0_RGMII2_TX_CTL */ + AM65X_IOPAD(0x025c, PIN_INPUT, 2) /* (AB27) PRG0_PRU1_GPO6.PRG0_RGMII2_RXC */ + AM65X_IOPAD(0x0254, PIN_INPUT, 2) /* (AA25) PRG0_PRU1_GPO4.PRG0_RGMII2_RX_CTL */ + + AM65X_IOPAD(0x01f4, PIN_INPUT, 2) /* (V24) PRG0_PRU0_GPO0.PRG0_RGMII1_RD0 */ + AM65X_IOPAD(0x01f8, PIN_INPUT, 2) /* (W25) PRG0_PRU0_GPO1.PRG0_RGMII1_RD1 */ + AM65X_IOPAD(0x01fc, PIN_INPUT, 2) /* (W24) PRG0_PRU0_GPO2.PRG0_RGMII1_RD2 */ + AM65X_IOPAD(0x0200, PIN_INPUT, 2) /* (AA27) PRG0_PRU0_GPO3.PRG0_RGMII1_RD3 */ + AM65X_IOPAD(0x0224, PIN_OUTPUT, 2) /* (AD27) PRG0_PRU0_GPO12.PRG0_RGMII1_TD0 */ + AM65X_IOPAD(0x0228, PIN_OUTPUT, 2) /* (AC26) PRG0_PRU0_GPO13.PRG0_RGMII1_TD1 */ + AM65X_IOPAD(0x022c, PIN_OUTPUT, 2) /* (AD26) PRG0_PRU0_GPO14.PRG0_RGMII1_TD2 */ + AM65X_IOPAD(0x0230, PIN_OUTPUT, 2) /* (AA24) PRG0_PRU0_GPO15.PRG0_RGMII1_TD3 */ + AM65X_IOPAD(0x0234, PIN_INPUT, 2) /* (AD28) PRG0_PRU0_GPO16.PRG0_RGMII1_TXC */ + AM65X_IOPAD(0x0220, PIN_OUTPUT, 2) /* (AB25) PRG0_PRU0_GPO11.PRG0_RGMII1_TX_CTL */ + AM65X_IOPAD(0x020c, PIN_INPUT, 2) /* (Y25) PRG0_PRU0_GPO6.PRG0_RGMII1_RXC */ + AM65X_IOPAD(0x0204, PIN_INPUT, 2) /* (Y24) PRG0_PRU0_GPO4.PRG0_RGMII1_RX_CTL */ + >; + }; + + icssg0_iep0_pins_default: icssg0-iep0-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0240, PIN_INPUT, 2) /* (U24) PRG0_PRU0_GPO19.PRG0_IEP0_EDC_SYNC_OUT0 */ + >; + }; + + icssg1_mdio_pins_default: icssg1-mdio-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0180, PIN_INPUT, 0) /* (AD18) PRG1_MDIO0_MDIO */ + AM65X_IOPAD(0x0184, PIN_OUTPUT, 0) /* (AH18) PRG1_MDIO0_MDC */ + >; + }; + + icssg1_rgmii_pins_default: icssg1-rgmii-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x0130, PIN_INPUT, 2) /* (AH24) PRG1_PRU1_GPO0.PRG1_RGMII2_RD0 */ + AM65X_IOPAD(0x0134, PIN_INPUT, 2) /* (AH23) PRG1_PRU1_GPO1.PRG1_RGMII2_RD1 */ + AM65X_IOPAD(0x0138, PIN_INPUT, 2) /* (AG21) PRG1_PRU1_GPO2.PRG1_RGMII2_RD2 */ + AM65X_IOPAD(0x013c, PIN_INPUT, 2) /* (AH22) PRG1_PRU1_GPO3.PRG1_RGMII2_RD3 */ + AM65X_IOPAD(0x0160, PIN_OUTPUT, 2) /* (AE20) PRG1_PRU1_GPO12.PRG1_RGMII2_TD0 */ + AM65X_IOPAD(0x0164, PIN_OUTPUT, 2) /* (AF19) PRG1_PRU1_GPO13.PRG1_RGMII2_TD1 */ + AM65X_IOPAD(0x0168, PIN_OUTPUT, 2) /* (AH19) PRG1_PRU1_GPO14.PRG1_RGMII2_TD2 */ + AM65X_IOPAD(0x016c, PIN_OUTPUT, 2) /* (AG19) PRG1_PRU1_GPO15.PRG1_RGMII2_TD3 */ + AM65X_IOPAD(0x0170, PIN_INPUT, 2) /* (AE19) PRG1_PRU1_GPO16.PRG1_RGMII2_TXC */ + AM65X_IOPAD(0x015c, PIN_OUTPUT, 2) /* (AC20) PRG1_PRU1_GPO11.PRG1_RGMII2_TX_CTL */ + AM65X_IOPAD(0x0148, PIN_INPUT, 2) /* (AG22) PRG1_PRU1_GPO6.PRG1_RGMII2_RXC */ + AM65X_IOPAD(0x0140, PIN_INPUT, 2) /* (AE21) PRG1_PRU1_GPO4.PRG1_RGMII2_RX_CTL */ + + AM65X_IOPAD(0x00e0, PIN_INPUT, 2) /* (AE22) PRG1_PRU0_GPO0.PRG1_RGMII1_RD0 */ + AM65X_IOPAD(0x00e4, PIN_INPUT, 2) /* (AG24) PRG1_PRU0_GPO1.PRG1_RGMII1_RD1 */ + AM65X_IOPAD(0x00e8, PIN_INPUT, 2) /* (AF23) PRG1_PRU0_GPO2.PRG1_RGMII1_RD2 */ + AM65X_IOPAD(0x00ec, PIN_INPUT, 2) /* (AD21) PRG1_PRU0_GPO3.PRG1_RGMII1_RD3 */ + AM65X_IOPAD(0x0110, PIN_OUTPUT, 2) /* (AH20) PRG1_PRU0_GPO12.PRG1_RGMII1_TD0 */ + AM65X_IOPAD(0x0114, PIN_OUTPUT, 2) /* (AH21) PRG1_PRU0_GPO13.PRG1_RGMII1_TD1 */ + AM65X_IOPAD(0x0118, PIN_OUTPUT, 2) /* (AG20) PRG1_PRU0_GPO14.PRG1_RGMII1_TD2 */ + AM65X_IOPAD(0x011c, PIN_OUTPUT, 2) /* (AD19) PRG1_PRU0_GPO15.PRG1_RGMII1_TD3 */ + AM65X_IOPAD(0x0120, PIN_INPUT, 2) /* (AD20) PRG1_PRU0_GPO16.PRG1_RGMII1_TXC */ + AM65X_IOPAD(0x010c, PIN_OUTPUT, 2) /* (AF21) PRG1_PRU0_GPO11.PRG1_RGMII1_TX_CTL */ + AM65X_IOPAD(0x00f8, PIN_INPUT, 2) /* (AF22) PRG1_PRU0_GPO6.PRG1_RGMII1_RXC */ + AM65X_IOPAD(0x00f0, PIN_INPUT, 2) /* (AG23) PRG1_PRU0_GPO4.PRG1_RGMII1_RX_CTL */ + >; + }; + + icssg1_iep0_pins_default: icssg1-iep0-pins-default { + pinctrl-single,pins = < + AM65X_IOPAD(0x012c, PIN_INPUT, 2) /* (AG26) PRG1_PRU0_GPO19.PRG1_IEP0_EDC_SYNC_OUT0 */ + >; + }; +}; + +&wkup_pmx0 { + mcu_mcan0_pins_default: mcu-mcan0-pins-default { + pinctrl-single,pins = < + AM65X_WKUP_IOPAD(0x00ac, PIN_INPUT_PULLUP, 0) /* (W2) MCU_MCAN0_RX */ + AM65X_WKUP_IOPAD(0x00a8, PIN_OUTPUT_PULLUP, 0) /* (W1) MCU_MCAN0_TX */ + >; + }; + + mcu_mcan1_pins_default: mcu-mcan1-pins-default { + pinctrl-single,pins = < + AM65X_WKUP_IOPAD(0x00c4, PIN_INPUT_PULLUP, 1) /* (AD3) WKUP_GPIO0_5.MCU_MCAN1_RX */ + AM65X_WKUP_IOPAD(0x00c0, PIN_OUTPUT_PULLUP, 1) /* (AC3) WKUP_GPIO0_4.MCU_MCAN1_TX */ + >; + }; +}; + +&m_can0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_pins_default>; + phys = <&transceiver1>; +}; + +&m_can1 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan1_pins_default>; + phys = <&transceiver2>; +}; + +&icssg0_mdio { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg0_mdio_pins_default>; + #address-cells = <1>; + #size-cells = <0>; + + icssg0_phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; + + icssg0_phy1: ethernet-phy@3 { + reg = <3>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; +}; + +&icssg0_iep0 { + pinctrl-names = "default"; + pinctrl-0 = <&icssg0_iep0_pins_default>; +}; + +&icssg1_mdio { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg1_mdio_pins_default>; + #address-cells = <1>; + #size-cells = <0>; + + icssg1_phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; + + icssg1_phy1: ethernet-phy@3 { + reg = <3>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; +}; + +&icssg1_iep0 { + pinctrl-names = "default"; + pinctrl-0 = <&icssg1_iep0_pins_default>; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-idk-sr1.dts b/arch/arm64/boot/dts/ti/k3-am654-idk-sr1.dts --- a/arch/arm64/boot/dts/ti/k3-am654-idk-sr1.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-idk-sr1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DT overlay for IDK application board on AM654 EVM SR1.0 + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include "k3-am654-idk.dts" + +&icssg0_eth { + compatible = "ti,am654-icssg-prueth-sr1"; + ti,prus = <&pru0_0>, <&rtu0_0>, <&pru0_1>, <&rtu0_1>; + firmware-name = "ti-pruss/am65x-pru0-prueth-fw.elf", + "ti-pruss/am65x-rtu0-prueth-fw.elf", + "ti-pruss/am65x-pru1-prueth-fw.elf", + "ti-pruss/am65x-rtu1-prueth-fw.elf"; + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, /* MII mode */ + <2>; +}; + +&icssg0_iep0 { + interrupt-parent = <&icssg0_intc>; + interrupts = <7 7 8>; + interrupt-names = "iep_cap_cmp"; +}; + +&icssg0_iep1 { + interrupt-parent = <&icssg0_intc>; + interrupts = <56 8 9>; + interrupt-names = "iep_cap_cmp"; +}; + +&icssg1_eth { + compatible = "ti,am654-icssg-prueth-sr1"; + ti,prus = <&pru1_0>, <&rtu1_0>, <&pru1_1>, <&rtu1_1>; + firmware-name = "ti-pruss/am65x-pru0-prueth-fw.elf", + "ti-pruss/am65x-rtu0-prueth-fw.elf", + "ti-pruss/am65x-pru1-prueth-fw.elf", + "ti-pruss/am65x-rtu1-prueth-fw.elf"; + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, /* MII mode */ + <2>; +}; + +&icssg1_iep0 { + interrupt-parent = <&icssg1_intc>; + interrupts = <7 7 8>; + interrupt-names = "iep_cap_cmp"; +}; + +&icssg1_iep1 { + interrupt-parent = <&icssg1_intc>; + interrupts = <56 8 9>; + interrupt-names = "iep_cap_cmp"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dts b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dts --- a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT overlay for SERDES personality card: 2lane PCIe + USB2.0 Host on AM654 EVM + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include +#include +#include + +&serdes0 { + assigned-clocks = <&k3_clks 153 4>, <&serdes0 AM654_SERDES_CMU_REFCLK>, <&serdes0 AM654_SERDES_RO_REFCLK>; + assigned-clock-parents = <&k3_clks 153 8>, <&k3_clks 153 4>, <&k3_clks 153 4>; + status = "okay"; +}; + +&serdes1 { + assigned-clocks = <&serdes1 AM654_SERDES_CMU_REFCLK>; + assigned-clock-parents = <&serdes0 AM654_SERDES_RO_REFCLK>; + status = "okay"; +}; + +&pcie0_rc { + num-lanes = <2>; + phys = <&serdes0 PHY_TYPE_PCIE 1>, <&serdes1 PHY_TYPE_PCIE 1>; + phy-names = "pcie-phy0", "pcie-phy1"; + reset-gpios = <&pca9555 5 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&pcie0_ep { + num-lanes = <2>; + phys = <&serdes0 PHY_TYPE_PCIE 1>, <&serdes1 PHY_TYPE_PCIE 1>; + phy-names = "pcie-phy0", "pcie-phy1"; +}; + +&main_pmx0 { + usb0_pins_default: usb0_pins_default { + pinctrl-single,pins = < + AM65X_IOPAD(0x02bc, PIN_OUTPUT, 0) /* (AD9) USB0_DRVVBUS */ + >; + }; +}; + +&dwc3_0 { + status = "okay"; +}; + +&usb0_phy { + status = "okay"; +}; + +&usb0 { + pinctrl-names = "default"; + pinctrl-0 = <&usb0_pins_default>; + dr_mode = "host"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dts b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dts --- a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT overlay for SERDES personality card: 1lane PCIe + USB3.0 DRD on AM654 EVM + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; +#include +#include +#include +#include + +&serdes1 { + status = "okay"; +}; + +&pcie1_rc { + num-lanes = <1>; + phys = <&serdes1 PHY_TYPE_PCIE 0>; + phy-names = "pcie-phy0"; + reset-gpios = <&pca9555 5 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&pcie1_ep { + num-lanes = <1>; + phys = <&serdes1 PHY_TYPE_PCIE 0>; + phy-names = "pcie-phy0"; +}; + +&main_pmx0 { + usb0_pins_default: usb0_pins_default { + pinctrl-single,pins = < + AM65X_IOPAD(0x02bc, PIN_OUTPUT, 0) /* (AD9) USB0_DRVVBUS */ + >; + }; +}; + +&serdes0 { + status = "okay"; + assigned-clocks = <&k3_clks 153 4>, <&serdes0 AM654_SERDES_CMU_REFCLK>; + assigned-clock-parents = <&k3_clks 153 7>, <&k3_clks 153 4>; +}; + +&dwc3_0 { + status = "okay"; + assigned-clock-parents = <&k3_clks 151 4>, /* set REF_CLK to 20MHz i.e. PER0_PLL/48 */ + <&k3_clks 151 8>; /* set PIPE3_TXB_CLK to WIZ8B2M4VSB */ + phys = <&serdes0 PHY_TYPE_USB3 0>; + phy-names = "usb3-phy"; +}; + +&usb0 { + pinctrl-names = "default"; + pinctrl-0 = <&usb0_pins_default>; + dr_mode = "host"; + maximum-speed = "super-speed"; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; +}; + +&usb0_phy { + status = "okay"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am654-sr1.dts b/arch/arm64/boot/dts/ti/k3-am654-sr1.dts --- a/arch/arm64/boot/dts/ti/k3-am654-sr1.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am654-sr1.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DT overlay for AM65x SR1.0 Silicon + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +&tx_pru0_0 { + status = "disabled"; +}; + +&tx_pru0_1 { + status = "disabled"; +}; + +&tx_pru1_0 { + status = "disabled"; +}; + +&tx_pru1_1 { + status = "disabled"; +}; + +&tx_pru2_0 { + status = "disabled"; +}; + +&tx_pru2_1 { + status = "disabled"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am65.dtsi b/arch/arm64/boot/dts/ti/k3-am65.dtsi --- a/arch/arm64/boot/dts/ti/k3-am65.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am65.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -56,7 +56,7 @@ }; pmu: pmu { - compatible = "arm,armv8-pmuv3"; + compatible = "arm,cortex-a53-pmu"; /* Recommendation from GIC500 TRM Table A.3 */ interrupts = ; }; @@ -68,6 +68,7 @@ ranges = <0x00 0x00100000 0x00 0x00100000 0x00 0x00020000>, /* ctrl mmr */ <0x00 0x00600000 0x00 0x00600000 0x00 0x00001100>, /* GPIO */ <0x00 0x00900000 0x00 0x00900000 0x00 0x00012000>, /* serdes */ + <0x00 0x00A40000 0x00 0x00A40000 0x00 0x00000800>, /* timesync router */ <0x00 0x01000000 0x00 0x01000000 0x00 0x0af02400>, /* Most peripherals */ <0x00 0x30800000 0x00 0x30800000 0x00 0x0bc00000>, /* MAIN NAVSS */ <0x00 0x70000000 0x00 0x70000000 0x00 0x00200000>, /* MSMC SRAM */ diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am65-main.dtsi b/arch/arm64/boot/dts/ti/k3-am65-main.dtsi --- a/arch/arm64/boot/dts/ti/k3-am65-main.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am65-main.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -119,7 +119,6 @@ #address-cells = <2>; #size-cells = <2>; ranges = <0x0 0x04e00000 0x00 0x04e00000 0x0 0x30000>; - status = "okay"; dmas = <&main_udmap 0xc000>, <&main_udmap 0x4000>, <&main_udmap 0x4001>; @@ -257,7 +256,7 @@ #size-cells = <0>; }; - sdhci0: sdhci@4f80000 { + sdhci0: mmc@4f80000 { compatible = "ti,am654-sdhci-5.1"; reg = <0x0 0x4f80000 0x0 0x260>, <0x0 0x4f90000 0x0 0x134>; power-domains = <&k3_pds 47 TI_SCI_PD_EXCLUSIVE>; @@ -281,7 +280,7 @@ dma-coherent; }; - sdhci1: sdhci@4fa0000 { + sdhci1: mmc@4fa0000 { compatible = "ti,am654-sdhci-5.1"; reg = <0x0 0x4fa0000 0x0 0x260>, <0x0 0x4fb0000 0x0 0x134>; power-domains = <&k3_pds 48 TI_SCI_PD_EXCLUSIVE>; @@ -302,7 +301,6 @@ ti,otap-del-sel = <0x2>; ti,trm-icp = <0x8>; dma-coherent; - no-1-8-v; }; scm_conf: scm-conf@100000 { @@ -473,6 +471,7 @@ interrupt-controller; interrupt-parent = <&intr_main_navss>; msi-controller; + #interrupt-cells = <0>; ti,sci = <&dmsc>; ti,sci-dev-id = <179>; ti,interrupt-ranges = <0 0 256>; @@ -612,7 +611,6 @@ reg-names = "rt", "fifos", "proxy_gcfg", "proxy_target"; ti,num-rings = <818>; ti,sci-rm-range-gp-rings = <0x1>; /* GP ring range */ - ti,dma-ring-reset-quirk; ti,sci = <&dmsc>; ti,sci-dev-id = <187>; msi-parent = <&inta_main_udmass>; @@ -698,8 +696,8 @@ power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>; #address-cells = <3>; #size-cells = <2>; - ranges = <0x81000000 0 0 0x0 0x10020000 0 0x00010000 - 0x82000000 0 0x10030000 0x0 0x10030000 0 0x07FD0000>; + ranges = <0x81000000 0 0 0x0 0x10020000 0 0x00010000>, + <0x82000000 0 0x10030000 0x0 0x10030000 0 0x07FD0000>; ti,syscon-pcie-id = <&pcie_devid>; ti,syscon-pcie-mode = <&pcie0_mode>; bus-range = <0x0 0xff>; @@ -708,6 +706,19 @@ dma-coherent; interrupts = ; msi-map = <0x0 &gic_its 0x0 0x10000>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0_intc 0>, /* INT A */ + <0 0 0 2 &pcie0_intc 0>, /* INT B */ + <0 0 0 3 &pcie0_intc 0>, /* INT C */ + <0 0 0 4 &pcie0_intc 0>; /* INT D */ + + pcie0_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; pcie0_ep: pcie-ep@5500000 { @@ -730,8 +741,8 @@ power-domains = <&k3_pds 121 TI_SCI_PD_EXCLUSIVE>; #address-cells = <3>; #size-cells = <2>; - ranges = <0x81000000 0 0 0x0 0x18020000 0 0x00010000 - 0x82000000 0 0x18030000 0x0 0x18030000 0 0x07FD0000>; + ranges = <0x81000000 0 0 0x0 0x18020000 0 0x00010000>, + <0x82000000 0 0x18030000 0x0 0x18030000 0 0x07FD0000>; ti,syscon-pcie-id = <&pcie_devid>; ti,syscon-pcie-mode = <&pcie1_mode>; bus-range = <0x0 0xff>; @@ -740,6 +751,19 @@ dma-coherent; interrupts = ; msi-map = <0x0 &gic_its 0x10000 0x10000>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, /* INT A */ + <0 0 0 2 &pcie1_intc 0>, /* INT B */ + <0 0 0 3 &pcie1_intc 0>, /* INT C */ + <0 0 0 4 &pcie1_intc 0>; /* INT D */ + + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; pcie1_ep: pcie-ep@5600000 { @@ -770,8 +794,6 @@ clocks = <&k3_clks 104 0>; clock-names = "fck"; power-domains = <&k3_pds 104 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp1: mcasp@2b10000 { @@ -789,8 +811,6 @@ clocks = <&k3_clks 105 0>; clock-names = "fck"; power-domains = <&k3_pds 105 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp2: mcasp@2b20000 { @@ -808,8 +828,6 @@ clocks = <&k3_clks 106 0>; clock-names = "fck"; power-domains = <&k3_pds 106 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; cal: cal@6f03000 { @@ -863,9 +881,7 @@ assigned-clocks = <&k3_clks 67 2>; assigned-clock-parents = <&k3_clks 67 5>; - interrupts = ; - - status = "disabled"; + interrupts = ; dma-coherent; @@ -928,4 +944,524 @@ clocks = <&ehrpwm_tbclk 5>, <&k3_clks 45 0>; clock-names = "tbclk", "fck"; }; + + icssg0: icssg@b000000 { + compatible = "ti,am654-icssg"; + reg = <0x00 0xb000000 0x00 0x80000>; + power-domains = <&k3_pds 62 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0xb000000 0x80000>; + + icssg0_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + icssg0_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg0_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 62 19>, /* icssg0_core_clk */ + <&k3_clks 62 3>; /* icssg0_iclk */ + assigned-clocks = <&icssg0_coreclk_mux>; + assigned-clock-parents = <&k3_clks 62 3>; + }; + + icssg0_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 62 10>, /* icssg0_iep_clk */ + <&icssg0_coreclk_mux>; /* core_clk */ + assigned-clocks = <&icssg0_iepclk_mux>; + assigned-clock-parents = <&icssg0_coreclk_mux>; + }; + }; + }; + + icssg0_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; + + icssg0_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; + + icssg0_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg0_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg0_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru0_0: pru@34000 { + compatible = "ti,am654-pru"; + reg = <0x34000 0x4000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru0_0-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu0_0: rtu@4000 { + compatible = "ti,am654-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu0_0-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru0_0: txpru@a000 { + compatible = "ti,am654-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru0_0-fw"; + }; + + pru0_1: pru@38000 { + compatible = "ti,am654-pru"; + reg = <0x38000 0x4000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru0_1-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu0_1: rtu@6000 { + compatible = "ti,am654-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu0_1-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru0_1: txpru@c000 { + compatible = "ti,am654-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru0_1-fw"; + }; + + icssg0_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + clocks = <&k3_clks 62 3>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + status = "disabled"; + }; + }; + + icssg1: icssg@b100000 { + compatible = "ti,am654-icssg"; + reg = <0x00 0xb100000 0x00 0x80000>; + power-domains = <&k3_pds 63 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0xb100000 0x80000>; + + icssg1_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + icssg1_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg1_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 63 19>, /* icssg1_core_clk */ + <&k3_clks 63 3>; /* icssg1_iclk */ + assigned-clocks = <&icssg1_coreclk_mux>; + assigned-clock-parents = <&k3_clks 63 3>; + }; + + icssg1_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 63 10>, /* icssg1_iep_clk */ + <&icssg1_coreclk_mux>; /* core_clk */ + assigned-clocks = <&icssg1_iepclk_mux>; + assigned-clock-parents = <&icssg1_coreclk_mux>; + }; + }; + }; + + icssg1_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg1_iepclk_mux>; + }; + + icssg1_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg1_iepclk_mux>; + }; + + icssg1_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg1_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg1_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru1_0: pru@34000 { + compatible = "ti,am654-pru"; + reg = <0x34000 0x4000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru1_0-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu1_0: rtu@4000 { + compatible = "ti,am654-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu1_0-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru1_0: txpru@a000 { + compatible = "ti,am654-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru1_0-fw"; + }; + + pru1_1: pru@38000 { + compatible = "ti,am654-pru"; + reg = <0x38000 0x4000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru1_1-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu1_1: rtu@6000 { + compatible = "ti,am654-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu1_1-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru1_1: txpru@c000 { + compatible = "ti,am654-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru1_1-fw"; + }; + + icssg1_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + clocks = <&k3_clks 63 3>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + status = "disabled"; + }; + }; + + icssg2: icssg@b200000 { + compatible = "ti,am654-icssg"; + reg = <0x00 0xb200000 0x00 0x80000>; + power-domains = <&k3_pds 64 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0xb200000 0x80000>; + + icssg2_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + icssg2_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg2_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 64 19>, /* icssg1_core_clk */ + <&k3_clks 64 3>; /* icssg1_iclk */ + assigned-clocks = <&icssg2_coreclk_mux>; + assigned-clock-parents = <&k3_clks 64 3>; + }; + + icssg2_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 64 10>, /* icssg1_iep_clk */ + <&icssg2_coreclk_mux>; /* core_clk */ + assigned-clocks = <&icssg2_iepclk_mux>; + assigned-clock-parents = <&icssg2_coreclk_mux>; + }; + }; + }; + + icssg2_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg2_iepclk_mux>; + }; + + icssg2_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg2_iepclk_mux>; + }; + + icssg2_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg2_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg2_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru2_0: pru@34000 { + compatible = "ti,am654-pru"; + reg = <0x34000 0x4000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru2_0-fw"; + interrupt-parent = <&icssg2_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu2_0: rtu@4000 { + compatible = "ti,am654-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu2_0-fw"; + interrupt-parent = <&icssg2_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru2_0: txpru@a000 { + compatible = "ti,am654-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru2_0-fw"; + }; + + pru2_1: pru@38000 { + compatible = "ti,am654-pru"; + reg = <0x38000 0x4000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru2_1-fw"; + interrupt-parent = <&icssg2_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu2_1: rtu@6000 { + compatible = "ti,am654-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu2_1-fw"; + interrupt-parent = <&icssg2_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru2_1: txpru@c000 { + compatible = "ti,am654-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru2_1-fw"; + }; + + icssg2_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + clocks = <&k3_clks 64 3>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + status = "disabled"; + }; + }; + + timesync_router: timesync_router@A40000 { + compatible = "pinctrl-single"; + reg = <0x0 0xA40000 0x0 0x800>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x800007ff>; + status = "disabled"; + }; + + gpu: gpu@7000000 { + compatible = "ti,am654-sgx544", "img,sgx544"; + reg = <0x00 0x7000000 0x00 0x10000>; + interrupts = ; + power-domains = <&k3_pds 65 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 65 0>, <&k3_clks 65 1>, + <&k3_clks 65 2>, <&k3_clks 65 3>; + clock-names = "mem_clk", "hyd_clk", + "sgx_clk", "sys_clk"; + }; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-am65-mcu.dtsi b/arch/arm64/boot/dts/ti/k3-am65-mcu.dtsi --- a/arch/arm64/boot/dts/ti/k3-am65-mcu.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-am65-mcu.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -2,7 +2,7 @@ /* * Device Tree Source for AM6 SoC Family MCU Domain peripherals * - * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2016-2020 Texas Instruments Incorporated - https://www.ti.com/ */ &cbass_mcu { @@ -37,6 +37,10 @@ ranges = <0x0 0x00 0x41c00000 0x80000>; #address-cells = <1>; #size-cells = <1>; + + mcu_r5fss0_core0_sram: r5f-sram@0 { + reg = <0x0 0x40000>; + }; }; mcu_i2c0: i2c@40b00000 { @@ -135,7 +139,6 @@ reg-names = "rt", "fifos", "proxy_gcfg", "proxy_target"; ti,num-rings = <286>; ti,sci-rm-range-gp-rings = <0x1>; /* GP ring range */ - ti,dma-ring-reset-quirk; ti,sci = <&dmsc>; ti,sci-dev-id = <195>; msi-parent = <&inta_main_udmass>; @@ -162,6 +165,36 @@ }; }; + m_can0: mcan@40528000 { + compatible = "bosch,m_can"; + reg = <0x0 0x40528000 0x0 0x400>, + <0x0 0x40500000 0x0 0x4400>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 102 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 102 5>, <&k3_clks 102 0>; + clock-names = "hclk", "cclk"; + interrupt-parent = <&gic500>; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + m_can1: mcan@40568000 { + compatible = "bosch,m_can"; + reg = <0x0 0x40568000 0x0 0x400>, + <0x0 0x40540000 0x0 0x4400>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 103 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 103 5>, <&k3_clks 103 0>; + clock-names = "hclk", "cclk"; + interrupt-parent = <&gic500>; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + fss: fss@47000000 { compatible = "simple-bus"; #address-cells = <2>; @@ -269,4 +302,45 @@ }; }; }; + + mcu_r5fss0: r5fss@41000000 { + compatible = "ti,am654-r5fss"; + ti,cluster-mode = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x41000000 0x00 0x41000000 0x20000>, + <0x41400000 0x00 0x41400000 0x20000>; + power-domains = <&k3_pds 129 TI_SCI_PD_EXCLUSIVE>; + + mcu_r5fss0_core0: r5f@41000000 { + compatible = "ti,am654-r5f"; + reg = <0x41000000 0x00008000>, + <0x41010000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <159>; + ti,sci-proc-ids = <0x01 0xff>; + resets = <&k3_reset 159 1>; + firmware-name = "am65x-mcu-r5f0_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + sram = <&mcu_r5fss0_core0_sram>; + }; + + mcu_r5fss0_core1: r5f@41400000 { + compatible = "ti,am654-r5f"; + reg = <0x41400000 0x00008000>, + <0x41410000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <245>; + ti,sci-proc-ids = <0x02 0xff>; + resets = <&k3_reset 245 1>; + firmware-name = "am65x-mcu-r5f0_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts --- a/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts 2022-01-06 12:45:53.810318090 -0500 @@ -6,14 +6,125 @@ /dts-v1/; #include "k3-j7200-som-p0.dtsi" +#include #include #include +#include / { chosen { stdout-path = "serial2:115200n8"; bootargs = "console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000"; }; + + cpsw5g_virt_mac: main_r5fss_cpsw5g_virt_mac0 { + compatible = "ti,j721e-cpsw-virt-mac"; + dma-coherent; + ti,psil-base = <0x4a00>; + ti,remote-name = "mpu_1_0_ethswitch-device-0"; + + dmas = <&main_udmap 0xca00>, + <&main_udmap 0xca01>, + <&main_udmap 0xca02>, + <&main_udmap 0xca03>, + <&main_udmap 0xca04>, + <&main_udmap 0xca05>, + <&main_udmap 0xca06>, + <&main_udmap 0xca07>, + <&main_udmap 0x4a00>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + virt_emac_port { + ti,label = "virt-port"; + /* local-mac-address = [0 0 0 0 0 0]; */ + }; + }; + + cpsw9g_virt_maconly: main-r5fss-cpsw9g-virt-mac1 { + compatible = "ti,j721e-cpsw-virt-mac"; + dma-coherent; + ti,psil-base = <0x4a00>; + ti,remote-name = "mpu_1_0_ethmac-device-1"; + + dmas = <&main_udmap 0xca00>, + <&main_udmap 0xca01>, + <&main_udmap 0xca02>, + <&main_udmap 0xca03>, + <&main_udmap 0xca04>, + <&main_udmap 0xca05>, + <&main_udmap 0xca06>, + <&main_udmap 0xca07>, + <&main_udmap 0x4a00>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + virt_emac_port { + ti,label = "virt-port"; + /* local-mac-address = [0 0 0 0 0 0]; */ + }; + }; + + evm_12v0: fixedregulator-evm12v0 { + /* main supply */ + compatible = "regulator-fixed"; + regulator-name = "evm_12v0"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_3v3: fixedregulator-vsys3v3 { + /* Output of LM5140 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&evm_12v0>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_5v0: fixedregulator-vsys5v0 { + /* Output of LM5140 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&evm_12v0>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_mmc1: fixedregulator-sd { + /* Output of TPS22918 */ + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vsys_3v3>; + gpio = <&exp2 2 GPIO_ACTIVE_HIGH>; + }; + + vdd_sd_dv: gpio-regulator-TLV71033 { + /* Output of TLV71033 */ + compatible = "regulator-gpio"; + regulator-name = "tlv71033"; + pinctrl-names = "default"; + pinctrl-0 = <&vdd_sd_dv_pins_default>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + vin-supply = <&vsys_5v0>; + gpios = <&main_gpio0 55 GPIO_ACTIVE_HIGH>; + states = <1800000 0x0>, + <3300000 0x1>; + }; }; &wkup_pmx0 { @@ -75,11 +186,17 @@ J721E_IOPAD(0x120, PIN_OUTPUT, 0) /* (T4) USB0_DRVVBUS */ >; }; + + vdd_sd_dv_pins_default: vdd-sd-dv-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0xd0, PIN_OUTPUT, 7) /* (T5) SPI0_D1.GPIO0_55 */ + >; + }; }; &wkup_uart0 { /* Wakeup UART is used by System firmware */ - status = "disabled"; + status = "reserved"; }; &main_uart0 { @@ -89,7 +206,7 @@ &main_uart2 { /* MAIN UART 2 is used by R5F firmware */ - status = "disabled"; + status = "reserved"; }; &main_uart3 { @@ -127,12 +244,30 @@ status = "disabled"; }; +&main_gpio2 { + status = "disabled"; +}; + +&main_gpio4 { + status = "disabled"; +}; + +&main_gpio6 { + status = "disabled"; +}; + +&wkup_gpio1 { + status = "disabled"; +}; + &mcu_cpsw { pinctrl-names = "default"; pinctrl-0 = <&mcu_cpsw_pins_default &mcu_mdio_pins_default>; }; &davinci_mdio { + bus_freq = <20000>; + phy0: ethernet-phy@0 { reg = <0>; ti,rx-internal-delay = ; @@ -165,16 +300,26 @@ }; }; +/* + * The j7200 CPB board is identical to the CPB used for J721E, the SOMs can be + * swapped on the CPB. + * + * main_i2c1 of J7200 is connected to the CPB i2c bus labeled as i2c3. + * The i2c1 of the CPB (as it is labeled) is not connected to j7200. + */ &main_i2c1 { pinctrl-names = "default"; pinctrl-0 = <&main_i2c1_pins_default>; clock-frequency = <400000>; - exp4: gpio@20 { + exp3: gpio@20 { compatible = "ti,tca6408"; reg = <0x20>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "CODEC_RSTz", "CODEC_SPARE1", "UB926_RESETn", + "UB926_LOCK", "UB926_PWR_SW_CNTRL", + "UB926_TUNER_RESET", "UB926_GPIO_SPARE", ""; }; }; @@ -189,6 +334,8 @@ /* SD card */ pinctrl-0 = <&main_mmc1_pins_default>; pinctrl-names = "default"; + vmmc-supply = <&vdd_mmc1>; + vqmmc-supply = <&vdd_sd_dv>; ti,driver-strength-ohm = <50>; disable-wp; }; @@ -213,3 +360,45 @@ dr_mode = "otg"; maximum-speed = "high-speed"; }; + +&tscadc0 { + adc { + ti,adc-channels = <0 1 2 3 4 5 6 7>; + }; +}; + +&serdes_refclk { + clock-frequency = <100000000>; +}; + +&serdes0 { + serdes0_pcie_link: phy@0 { + reg = <0>; + cdns,num-lanes = <2>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz0 1>, <&serdes_wiz0 2>; + }; + + serdes0_qsgmii_link: phy@1 { + reg = <2>; + cdns,num-lanes = <1>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz0 3>; + }; +}; + +&pcie1_rc { + reset-gpios = <&exp1 2 GPIO_ACTIVE_HIGH>; + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <2>; +}; + +&pcie1_ep { + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <2>; + status = "disabled"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j7200.dtsi b/arch/arm64/boot/dts/ti/k3-j7200.dtsi --- a/arch/arm64/boot/dts/ti/k3-j7200.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j7200.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -114,7 +114,7 @@ }; pmu: pmu { - compatible = "arm,armv8-pmuv3"; + compatible = "arm,cortex-a72-pmu"; interrupts = ; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi b/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi --- a/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -2,9 +2,16 @@ /* * Device Tree Source for J7200 SoC Family Main Domain peripherals * - * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ */ +/ { + serdes_refclk: serdes-refclk { + #clock-cells = <0>; + compatible = "fixed-clock"; + }; +}; + &cbass_main { msmc_ram: sram@70000000 { compatible = "mmio-sram"; @@ -117,6 +124,120 @@ interrupts = ; }; + hwspinlock: spinlock@30e00000 { + compatible = "ti,am654-hwspinlock"; + reg = <0x00 0x30e00000 0x00 0x1000>; + #hwlock-cells = <1>; + }; + + mailbox0_cluster0: mailbox@31f80000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f80000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster1: mailbox@31f81000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f81000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster2: mailbox@31f82000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f82000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster3: mailbox@31f83000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f83000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster4: mailbox@31f84000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f84000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster5: mailbox@31f85000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f85000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster6: mailbox@31f86000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f86000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster7: mailbox@31f87000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f87000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster8: mailbox@31f88000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f88000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster9: mailbox@31f89000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f89000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster10: mailbox@31f8a000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f8a000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster11: mailbox@31f8b000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f8b000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + main_ringacc: ringacc@3c000000 { compatible = "ti,am654-navss-ringacc"; reg = <0x00 0x3c000000 0x00 0x400000>, @@ -382,22 +503,45 @@ power-domains = <&k3_pds 193 TI_SCI_PD_EXCLUSIVE>; }; + main_rti0: watchdog@2200000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x00 0x2200000 0x00 0x100>; + clocks = <&k3_clks 252 1>; + power-domains = <&k3_pds 252 TI_SCI_PD_EXCLUSIVE>; + assigned-clocks = <&k3_clks 252 1>; + assigned-clock-parents = <&k3_clks 252 5>; + }; + + main_rti1: watchdog@2210000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x00 0x2210000 0x00 0x100>; + clocks = <&k3_clks 253 1>; + power-domains = <&k3_pds 253 TI_SCI_PD_EXCLUSIVE>; + assigned-clocks = <&k3_clks 253 1>; + assigned-clock-parents = <&k3_clks 253 5>; + }; + main_sdhci0: mmc@4f80000 { compatible = "ti,j7200-sdhci-8bit", "ti,j721e-sdhci-8bit"; reg = <0x00 0x04f80000 0x00 0x260>, <0x00 0x4f88000 0x00 0x134>; interrupts = ; power-domains = <&k3_pds 91 TI_SCI_PD_EXCLUSIVE>; - clock-names = "clk_xin", "clk_ahb"; - clocks = <&k3_clks 91 3>, <&k3_clks 91 0>; + clock-names = "clk_ahb", "clk_xin"; + clocks = <&k3_clks 91 0>, <&k3_clks 91 3>; ti,otap-del-sel-legacy = <0x0>; ti,otap-del-sel-mmc-hs = <0x0>; ti,otap-del-sel-ddr52 = <0x6>; ti,otap-del-sel-hs200 = <0x8>; - ti,otap-del-sel-hs400 = <0x0>; + ti,otap-del-sel-hs400 = <0x5>; + ti,itap-del-sel-legacy = <0x10>; + ti,itap-del-sel-mmc-hs = <0xa>; ti,strobe-sel = <0x77>; + ti,clkbuf-sel = <0x7>; ti,trm-icp = <0x8>; bus-width = <8>; mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; dma-coherent; }; @@ -406,8 +550,8 @@ reg = <0x00 0x04fb0000 0x00 0x260>, <0x00 0x4fb8000 0x00 0x134>; interrupts = ; power-domains = <&k3_pds 92 TI_SCI_PD_EXCLUSIVE>; - clock-names = "clk_xin", "clk_ahb"; - clocks = <&k3_clks 92 2>, <&k3_clks 92 1>; + clock-names = "clk_ahb", "clk_xin"; + clocks = <&k3_clks 92 1>, <&k3_clks 92 2>; ti,otap-del-sel-legacy = <0x0>; ti,otap-del-sel-sd-hs = <0x0>; ti,otap-del-sel-sdr12 = <0xf>; @@ -415,7 +559,130 @@ ti,otap-del-sel-sdr50 = <0xc>; ti,otap-del-sel-sdr104 = <0x5>; ti,otap-del-sel-ddr50 = <0xc>; - no-1-8-v; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-sd-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + ti,clkbuf-sel = <0x7>; + ti,trm-icp = <0x8>; + dma-coherent; + }; + + serdes_wiz0: wiz@5060000 { + compatible = "ti,j721e-wiz-10g"; + #address-cells = <1>; + #size-cells = <1>; + power-domains = <&k3_pds 292 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 292 11>, <&k3_clks 292 85>, <&serdes_refclk>; + clock-names = "fck", "core_ref_clk", "ext_ref_clk"; + num-lanes = <4>; + #reset-cells = <1>; + ranges = <0x5060000 0x0 0x5060000 0x10000>; + + assigned-clocks = <&k3_clks 292 85>; + assigned-clock-parents = <&k3_clks 292 89>; + + wiz0_pll0_refclk: pll0-refclk { + clocks = <&k3_clks 292 85>, <&serdes_refclk>; + clock-output-names = "wiz0_pll0_refclk"; + #clock-cells = <0>; + assigned-clocks = <&wiz0_pll0_refclk>; + assigned-clock-parents = <&k3_clks 292 85>; + }; + + wiz0_pll1_refclk: pll1-refclk { + clocks = <&k3_clks 292 85>, <&serdes_refclk>; + clock-output-names = "wiz0_pll1_refclk"; + #clock-cells = <0>; + assigned-clocks = <&wiz0_pll1_refclk>; + assigned-clock-parents = <&k3_clks 292 85>; + }; + + wiz0_refclk_dig: refclk-dig { + clocks = <&k3_clks 292 85>, <&serdes_refclk>; + clock-output-names = "wiz0_refclk_dig"; + #clock-cells = <0>; + assigned-clocks = <&wiz0_refclk_dig>; + assigned-clock-parents = <&k3_clks 292 85>; + }; + + wiz0_cmn_refclk_dig_div: cmn-refclk-dig-div { + clocks = <&wiz0_refclk_dig>; + #clock-cells = <0>; + }; + + serdes0: serdes@5060000 { + compatible = "ti,j721e-serdes-10g"; + reg = <0x05060000 0x00010000>; + reg-names = "torrent_phy"; + resets = <&serdes_wiz0 0>; + reset-names = "torrent_reset"; + clocks = <&wiz0_pll0_refclk>; + clock-names = "refclk"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + pcie1_rc: pcie@2910000 { + compatible = "ti,j7200-pcie-host", "ti,j721e-pcie-host"; + reg = <0x00 0x02910000 0x00 0x1000>, + <0x00 0x02917000 0x00 0x400>, + <0x00 0x0d800000 0x00 0x00800000>, + <0x00 0x18000000 0x00 0x00001000>; + reg-names = "intd_cfg", "user_cfg", "reg", "cfg"; + interrupt-names = "link_state"; + interrupts = ; + device_type = "pci"; + ti,syscon-pcie-ctrl = <&scm_conf 0x4074>; + max-link-speed = <3>; + num-lanes = <4>; + power-domains = <&k3_pds 240 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 240 6>; + clock-names = "fck"; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x0 0xf>; + cdns,no-bar-match-nbits = <64>; + vendor-id = <0x104c>; + device-id = <0xb00f>; + msi-map = <0x0 &gic_its 0x0 0x10000>; + dma-coherent; + ranges = <0x01000000 0x0 0x18001000 0x00 0x18001000 0x0 0x0010000>, + <0x02000000 0x0 0x18011000 0x00 0x18011000 0x0 0x7fef000>; + dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, /* INT A */ + <0 0 0 2 &pcie1_intc 0>, /* INT B */ + <0 0 0 3 &pcie1_intc 0>, /* INT C */ + <0 0 0 4 &pcie1_intc 0>; /* INT D */ + + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic500>; + interrupts = ; + }; + }; + + pcie1_ep: pcie-ep@2910000 { + compatible = "ti,j7200-pcie-ep", "ti,j721e-pcie-ep"; + reg = <0x00 0x02910000 0x00 0x1000>, + <0x00 0x02917000 0x00 0x400>, + <0x00 0x0d800000 0x00 0x00800000>, + <0x00 0x18000000 0x00 0x08000000>; + reg-names = "intd_cfg", "user_cfg", "reg", "mem"; + interrupt-names = "link_state"; + interrupts = ; + ti,syscon-pcie-ctrl = <&scm_conf 0x4074>; + max-link-speed = <3>; + num-lanes = <4>; + power-domains = <&k3_pds 240 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 240 6>; + clock-names = "fck"; + max-functions = /bits/ 8 <6>; + max-virtual-functions = /bits/ 8 <4 4 4 4 0 0>; dma-coherent; }; @@ -449,4 +716,127 @@ cdns,phyrst-a-enable; }; }; + + main_gpio0: gpio@600000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00600000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <145>, <146>, <147>, <148>, + <149>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <0>; + ti,ngpio = <69>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 105 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 105 0>; + clock-names = "gpio"; + }; + + main_gpio2: gpio@610000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00610000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <154>, <155>, <156>, <157>, + <158>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <0>; + ti,ngpio = <69>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 107 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 107 0>; + clock-names = "gpio"; + }; + + main_gpio4: gpio@620000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00620000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <163>, <164>, <165>, <166>, + <167>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <0>; + ti,ngpio = <69>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 109 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 109 0>; + clock-names = "gpio"; + }; + + main_gpio6: gpio@630000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00630000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <172>, <173>, <174>, <175>, + <176>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <0>; + ti,ngpio = <69>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 111 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 111 0>; + clock-names = "gpio"; + }; + + main_r5fss0: r5fss@5c00000 { + compatible = "ti,j7200-r5fss"; + ti,cluster-mode = <0>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x5c00000 0x00 0x5c00000 0x20000>, + <0x5d00000 0x00 0x5d00000 0x20000>; + power-domains = <&k3_pds 243 TI_SCI_PD_EXCLUSIVE>; + + main_r5fss0_core0: r5f@5c00000 { + compatible = "ti,j7200-r5f"; + reg = <0x5c00000 0x00010000>, + <0x5c10000 0x00010000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <245>; + ti,sci-proc-ids = <0x06 0xff>; + resets = <&k3_reset 245 1>; + firmware-name = "j7200-main-r5f0_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + main_r5fss0_core1: r5f@5d00000 { + compatible = "ti,j7200-r5f"; + reg = <0x5d00000 0x00008000>, + <0x5d10000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <246>; + ti,sci-proc-ids = <0x07 0xff>; + resets = <&k3_reset 246 1>; + firmware-name = "j7200-main-r5f0_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + + timesync_router: timesync_router@a40000 { + compatible = "pinctrl-single"; + reg = <0x0 0xa40000 0x0 0x800>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x000107ff>; + status = "disabled"; + }; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j7200-mcu-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-j7200-mcu-wakeup.dtsi --- a/arch/arm64/boot/dts/ti/k3-j7200-mcu-wakeup.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j7200-mcu-wakeup.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -2,7 +2,7 @@ /* * Device Tree Source for J7200 SoC Family MCU/WAKEUP Domain peripherals * - * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ */ &cbass_mcu_wakeup { @@ -107,6 +107,40 @@ ti,interrupt-ranges = <16 960 16>; }; + wkup_gpio0: gpio@42110000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x42110000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&wkup_gpio_intr>; + interrupts = <103>, <104>, <105>, <106>, <107>, <108>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <0>; + ti,ngpio = <85>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 113 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 113 0>; + clock-names = "gpio"; + }; + + wkup_gpio1: gpio@42100000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x42100000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&wkup_gpio_intr>; + interrupts = <112>, <113>, <114>, <115>, <116>, <117>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <0>; + ti,ngpio = <85>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 114 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 114 0>; + clock-names = "gpio"; + }; + mcu_navss: bus@28380000 { compatible = "simple-mfd"; #address-cells = <2>; @@ -269,5 +303,101 @@ #size-cells = <1>; mux-controls = <&hbmc_mux 0>; }; + + ospi0: spi@47040000 { + compatible = "ti,am654-ospi"; + reg = <0x0 0x47040000 0x0 0x100>, + <0x5 0x00000000 0x1 0x0000000>; + interrupts = ; + cdns,fifo-depth = <256>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x0>; + clocks = <&k3_clks 103 0>; + assigned-clocks = <&k3_clks 103 0>; + assigned-clock-parents = <&k3_clks 103 2>; + assigned-clock-rates = <166666666>; + power-domains = <&k3_pds 103 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + tscadc0: tscadc@40200000 { + compatible = "ti,am3359-tscadc"; + reg = <0x00 0x40200000 0x00 0x1000>; + interrupts = ; + power-domains = <&k3_pds 0 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 0 1>; + assigned-clocks = <&k3_clks 0 3>; + assigned-clock-rates = <60000000>; + clock-names = "adc_tsc_fck"; + dmas = <&main_udmap 0x7400>, + <&main_udmap 0x7401>; + dma-names = "fifo0", "fifo1"; + + adc { + #io-channel-cells = <1>; + compatible = "ti,am3359-adc"; + }; + }; + + mcu_r5fss0: r5fss@41000000 { + compatible = "ti,j7200-r5fss"; + ti,cluster-mode = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x41000000 0x00 0x41000000 0x20000>, + <0x41400000 0x00 0x41400000 0x20000>; + power-domains = <&k3_pds 249 TI_SCI_PD_EXCLUSIVE>; + + mcu_r5fss0_core0: r5f@41000000 { + compatible = "ti,j7200-r5f"; + reg = <0x41000000 0x00010000>, + <0x41010000 0x00010000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <250>; + ti,sci-proc-ids = <0x01 0xff>; + resets = <&k3_reset 250 1>; + firmware-name = "j7200-mcu-r5f0_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + mcu_r5fss0_core1: r5f@41400000 { + compatible = "ti,j7200-r5f"; + reg = <0x41400000 0x00008000>, + <0x41410000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <251>; + ti,sci-proc-ids = <0x02 0xff>; + resets = <&k3_reset 251 1>; + firmware-name = "j7200-mcu-r5f0_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + + mcu_crypto: crypto@40900000 { + compatible = "ti,j721e-sa2ul"; + reg = <0x00 0x40900000 0x00 0x1200>; + power-domains = <&k3_pds 265 TI_SCI_PD_SHARED>; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x40900000 0x00 0x40900000 0x00 0x30000>; + dmas = <&mcu_udmap 0xf501>, <&mcu_udmap 0x7502>, + <&mcu_udmap 0x7503>; + dma-names = "tx", "rx1", "rx2"; + dma-coherent; + + rng: rng@40910000 { + compatible = "inside-secure,safexcel-eip76"; + reg = <0x00 0x40910000 0x00 0x7d>; + interrupts = ; + clocks = <&k3_clks 265 1>; + }; }; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi b/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi --- a/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ */ /dts-v1/; @@ -25,6 +25,73 @@ alignment = <0x1000>; no-map; }; + + mcu_r5fss0_core0_dma_memory_region: r5f-dma-memory@a0000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0000000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core0_memory_region: r5f-memory@a0100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0100000 0x00 0xf00000>; + no-map; + }; + + mcu_r5fss0_core1_dma_memory_region: r5f-dma-memory@a1000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1000000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core1_memory_region: r5f-memory@a1100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core0_dma_memory_region: r5f-dma-memory@a2000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core0_memory_region: r5f-memory@a2100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core1_dma_memory_region: r5f-dma-memory@a3000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core1_memory_region: r5f-memory@a3100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3100000 0x00 0xf00000>; + no-map; + }; + + rtos_ipc_memory_region: ipc-memories@a4000000 { + reg = <0x00 0xa4000000 0x00 0x00800000>; + alignment = <0x1000>; + no-map; + }; + + main_r5fss0_core0_shared_memory_queue_region:r5f-virtual-eth-queues@a5000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa5000000 0x00 0x200000>; + no-map; + }; + + main_r5fss0_core0_shared_memory_bufpool_region:r5f-virtual-eth-buffers@a5200000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa5200000 0x00 0x1e00000>; + no-map; + }; + }; }; @@ -46,6 +113,31 @@ J721E_WKUP_IOPAD(0x28, PIN_INPUT, 1) /* (A7) MCU_OSPI0_D7.MCU_HYPERBUS0_DQ7 */ >; }; + + mcu_fss0_ospi0_pins_default: mcu-fss0-ospi0-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0x0000, PIN_OUTPUT, 0) /* MCU_OSPI0_CLK */ + J721E_WKUP_IOPAD(0x002c, PIN_OUTPUT, 0) /* MCU_OSPI0_CSn0 */ + J721E_WKUP_IOPAD(0x000c, PIN_INPUT, 0) /* MCU_OSPI0_D0 */ + J721E_WKUP_IOPAD(0x0010, PIN_INPUT, 0) /* MCU_OSPI0_D1 */ + J721E_WKUP_IOPAD(0x0014, PIN_INPUT, 0) /* MCU_OSPI0_D2 */ + J721E_WKUP_IOPAD(0x0018, PIN_INPUT, 0) /* MCU_OSPI0_D3 */ + J721E_WKUP_IOPAD(0x001c, PIN_INPUT, 0) /* MCU_OSPI0_D4 */ + J721E_WKUP_IOPAD(0x0020, PIN_INPUT, 0) /* MCU_OSPI0_D5 */ + J721E_WKUP_IOPAD(0x0024, PIN_INPUT, 0) /* MCU_OSPI0_D6 */ + J721E_WKUP_IOPAD(0x0028, PIN_INPUT, 0) /* MCU_OSPI0_D7 */ + J721E_WKUP_IOPAD(0x0008, PIN_INPUT, 0) /* MCU_OSPI0_DQS */ + >; + }; +}; + +&main_pmx0 { + main_i2c0_pins_default: main-i2c0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0xd4, PIN_INPUT_PULLUP, 0) /* (V3) I2C0_SCL */ + J721E_IOPAD(0xd8, PIN_INPUT_PULLUP, 0) /* (W2) I2C0_SDA */ + >; + }; }; &hbmc { @@ -63,3 +155,134 @@ reg = <0x00 0x00 0x4000000>; }; }; + +&mailbox0_cluster0 { + interrupts = <436>; + + mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; + + mbox_mcu_r5fss0_core1: mbox-mcu-r5fss0-core1 { + ti,mbox-rx = <2 0 0>; + ti,mbox-tx = <3 0 0>; + }; +}; + +&mailbox0_cluster1 { + interrupts = <432>; + + mbox_main_r5fss0_core0: mbox-main-r5fss0-core0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; + + mbox_main_r5fss0_core1: mbox-main-r5fss0-core1 { + ti,mbox-rx = <2 0 0>; + ti,mbox-tx = <3 0 0>; + }; +}; + +&mailbox0_cluster2 { + status = "disabled"; +}; + +&mailbox0_cluster3 { + status = "disabled"; +}; + +&mailbox0_cluster4 { + status = "disabled"; +}; + +&mailbox0_cluster5 { + status = "disabled"; +}; + +&mailbox0_cluster6 { + status = "disabled"; +}; + +&mailbox0_cluster7 { + status = "disabled"; +}; + +&mailbox0_cluster8 { + status = "disabled"; +}; + +&mailbox0_cluster9 { + status = "disabled"; +}; + +&mailbox0_cluster10 { + status = "disabled"; +}; + +&mailbox0_cluster11 { + status = "disabled"; +}; + +&mcu_r5fss0_core0 { + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; + memory-region = <&mcu_r5fss0_core0_dma_memory_region>, + <&mcu_r5fss0_core0_memory_region>; +}; + +&mcu_r5fss0_core1 { + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; + memory-region = <&mcu_r5fss0_core1_dma_memory_region>, + <&mcu_r5fss0_core1_memory_region>; +}; + +&main_r5fss0_core0 { + mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; + memory-region = <&main_r5fss0_core0_dma_memory_region>, + <&main_r5fss0_core0_memory_region>, + <&main_r5fss0_core0_shared_memory_queue_region>, + <&main_r5fss0_core0_shared_memory_bufpool_region>; +}; + +&main_r5fss0_core1 { + mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; + memory-region = <&main_r5fss0_core1_dma_memory_region>, + <&main_r5fss0_core1_memory_region>; +}; + +&main_i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c0_pins_default>; + clock-frequency = <400000>; + + exp_som: gpio@21 { + compatible = "ti,tca6408"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "USB2.0_MUX_SEL", "CANUART_MUX1_SEL0", + "CANUART_MUX2_SEL0", "CANUART_MUX_SEL1", + "UART/LIN_MUX_SEL", "TRC_D17/AUDIO_REFCLK_SEL", + "GPIO_LIN_EN", "CAN_STB"; + }; +}; + +&ospi0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_fss0_ospi0_pins_default>; + + flash@0{ + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + spi-max-frequency = <25000000>; + cdns,tshsl-ns = <60>; + cdns,tsd2d-ns = <60>; + cdns,tchsh-ns = <60>; + cdns,tslch-ns = <60>; + cdns,read-delay = <4>; + #address-cells = <1>; + #size-cells = <1>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts 2022-01-06 12:45:53.810318090 -0500 @@ -9,6 +9,7 @@ #include #include #include +#include / { chosen { @@ -67,6 +68,31 @@ regulator-boot-on; }; + vdd_mmc1: fixedregulator-sd { + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vsys_3v3>; + gpio = <&exp2 2 GPIO_ACTIVE_HIGH>; + }; + + vdd_sd_dv_alt: gpio-regulator-TLV71033 { + compatible = "regulator-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&vdd_sd_dv_alt_pins_default>; + regulator-name = "tlv71033"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + vin-supply = <&vsys_5v0>; + gpios = <&main_gpio0 117 GPIO_ACTIVE_HIGH>; + states = <1800000 0x0>, + <3300000 0x1>; + }; + sound0: sound@0 { compatible = "ti,j721e-cpb-audio"; model = "j721e-cpb"; @@ -83,6 +109,117 @@ "cpb-codec-scki", "cpb-codec-scki-48000", "cpb-codec-scki-44100"; }; + + cpsw9g_virt_mac: main_r5fss_cpsw9g_virt_mac0 { + compatible = "ti,j721e-cpsw-virt-mac"; + dma-coherent; + ti,psil-base = <0x4a00>; + ti,remote-name = "mpu_1_0_ethswitch-device-0"; + + dmas = <&main_udmap 0xca00>, + <&main_udmap 0xca01>, + <&main_udmap 0xca02>, + <&main_udmap 0xca03>, + <&main_udmap 0xca04>, + <&main_udmap 0xca05>, + <&main_udmap 0xca06>, + <&main_udmap 0xca07>, + <&main_udmap 0x4a00>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + virt_emac_port { + ti,label = "virt-port"; + /* local-mac-address = [0 0 0 0 0 0]; */ + }; + }; + + cpsw9g_virt_maconly: main-r5fss-cpsw9g-virt-mac1 { + compatible = "ti,j721e-cpsw-virt-mac"; + dma-coherent; + ti,psil-base = <0x4a00>; + ti,remote-name = "mpu_1_0_ethmac-device-1"; + + dmas = <&main_udmap 0xca00>, + <&main_udmap 0xca01>, + <&main_udmap 0xca02>, + <&main_udmap 0xca03>, + <&main_udmap 0xca04>, + <&main_udmap 0xca05>, + <&main_udmap 0xca06>, + <&main_udmap 0xca07>, + <&main_udmap 0x4a00>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + virt_emac_port { + ti,label = "virt-port"; + /* local-mac-address = [0 0 0 0 0 0]; */ + }; + }; + + transceiver1: can-phy0 { + compatible = "ti,tcan1043"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_gpio_pins_default>; + standby-gpios = <&wkup_gpio0 54 GPIO_ACTIVE_LOW>; + enable-gpios = <&wkup_gpio0 0 GPIO_ACTIVE_HIGH>; + }; + + transceiver2: can-phy1 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan1_gpio_pins_default>; + standby-gpios = <&wkup_gpio0 2 GPIO_ACTIVE_HIGH>; + }; + + transceiver3: can-phy2 { + compatible = "ti,tcan1043"; + #phy-cells = <0>; + max-bitrate = <5000000>; + standby-gpios = <&exp2 7 GPIO_ACTIVE_LOW>; + enable-gpios = <&exp2 6 GPIO_ACTIVE_HIGH>; + }; + + transceiver4: can-phy3 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan2_gpio_pins_default>; + standby-gpios = <&main_gpio0 127 GPIO_ACTIVE_HIGH>; + }; + + dp_pwr_3v3: fixedregulator-dp-prw { + compatible = "regulator-fixed"; + regulator-name = "dp-pwr"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&exp4 0 0>; /* P0 - DP0_PWR_SW_EN */ + enable-active-high; + + /* Always on for now, until dp-connector driver can handle this */ + regulator-always-on; + }; + + dp0: connector { + compatible = "dp-connector"; + label = "DP0"; + type = "full-size"; + dp-pwr-supply = <&dp_pwr_3v3>; + + port { + dp_connector_in: endpoint { + remote-endpoint = <&dp0_out>; + }; + }; + }; }; &main_pmx0 { @@ -106,6 +243,12 @@ >; }; + vdd_sd_dv_alt_pins_default: vdd-sd-dv-alt-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1d8, PIN_INPUT, 7) /* (W4) SPI1_CS1.GPIO0_117 */ + >; + }; + main_usbss0_pins_default: main-usbss0-pins-default { pinctrl-single,pins = < J721E_IOPAD(0x290, PIN_OUTPUT, 0) /* (U6) USB0_DRVVBUS */ @@ -119,6 +262,12 @@ >; }; + dp0_pins_default: dp0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1c4, PIN_INPUT, 5) /* SPI0_CS1.DP0_HPD */ + >; + }; + main_i2c1_exp4_pins_default: main-i2c1-exp4-pins-default { pinctrl-single,pins = < J721E_IOPAD(0x230, PIN_INPUT, 7) /* (U2) ECAP0_IN_APWM_OUT.GPIO1_11 */ @@ -172,6 +321,26 @@ J721E_IOPAD(0x1a4, PIN_OUTPUT, 3) /* (W26) RGMII6_RXC.AUDIO_EXT_REFCLK2 */ >; }; + + main_mcan0_pins_default: main-mcan0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x208, PIN_INPUT, 0) /* (W5) MCAN0_RX */ + J721E_IOPAD(0x20c, PIN_OUTPUT, 0) /* (W6) MCAN0_TX */ + >; + }; + + main_mcan2_pins_default: main-mcan2-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x01f0, PIN_INPUT, 3) /* (AC2) MCAN2_RX.GPIO0_123 */ + J721E_IOPAD(0x01f4, PIN_OUTPUT, 3) /* (AB1) MCAN2_TX.GPIO0_124 */ + >; + }; + + main_mcan2_gpio_pins_default: main-mcan2-gpio-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x200, PIN_INPUT, 7) /* (AC4) UART1_CTSn.GPIO0_127 */ + >; + }; }; &wkup_pmx0 { @@ -217,11 +386,38 @@ J721E_WKUP_IOPAD(0x0088, PIN_INPUT, 0) /* MCU_MDIO0_MDIO */ >; }; + + mcu_mcan0_pins_default: mcu-mcan0-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xac, PIN_INPUT, 0) /* (C29) MCU_MCAN0_RX */ + J721E_WKUP_IOPAD(0xa8, PIN_OUTPUT, 0) /* (D29) MCU_MCAN0_TX */ + >; + }; + + mcu_mcan0_gpio_pins_default: mcu-mcan0-gpio-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xb0, PIN_INPUT, 7) /* (F26) WKUP_GPIO0_0 */ + J721E_WKUP_IOPAD(0x98, PIN_INPUT, 7) /* (E28) MCU_SPI0_D1.WKUP_GPIO0_54 */ + >; + }; + + mcu_mcan1_pins_default: mcu-mcan1-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xc4, PIN_INPUT, 0) /* (G24) WKUP_GPIO0_5.MCU_MCAN1_RX */ + J721E_WKUP_IOPAD(0xc0, PIN_OUTPUT, 0) /* (G25) WKUP_GPIO0_4.MCU_MCAN1_TX */ + >; + }; + + mcu_mcan1_gpio_pins_default: mcu-mcan1-gpio-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xb8, PIN_INPUT, 7) /* (F28) WKUP_GPIO0_2 */ + >; + }; }; &wkup_uart0 { /* Wakeup UART is used by System firmware */ - status = "disabled"; + status = "reserved"; }; &main_uart0 { @@ -295,6 +491,8 @@ &main_sdhci1 { /* SD/MMC */ + vmmc-supply = <&vdd_mmc1>; + vqmmc-supply = <&vdd_sd_dv_alt>; pinctrl-names = "default"; pinctrl-0 = <&main_mmc1_pins_default>; ti,driver-strength-ohm = <50>; @@ -325,7 +523,7 @@ }; &serdes3 { - serdes3_usb_link: link@0 { + serdes3_usb_link: phy@0 { reg = <0>; cdns,num-lanes = <2>; #phy-cells = <0>; @@ -540,6 +738,80 @@ <&k3_clks 152 18>; /* PLL23_HSDIV0 */ }; +&dss_ports { + port@0 { + reg = <0>; + + dpi0_out: endpoint { + remote-endpoint = <&dp0_in>; + }; + }; +}; + +&mhdp { + pinctrl-names = "default"; + pinctrl-0 = <&dp0_pins_default>; +}; + +&dp0_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dp0_in: endpoint { + remote-endpoint = <&dpi0_out>; + }; + }; + + port@4 { + reg = <4>; + dp0_out: endpoint { + remote-endpoint = <&dp_connector_in>; + }; + }; +}; + +&mcasp0 { + status = "disabled"; +}; + +&mcasp1 { + status = "disabled"; +}; + +&mcasp2 { + status = "disabled"; +}; + +&mcasp3 { + status = "disabled"; +}; + +&mcasp4 { + status = "disabled"; +}; + +&mcasp5 { + status = "disabled"; +}; + +&mcasp6 { + status = "disabled"; +}; + +&mcasp7 { + status = "disabled"; +}; + +&mcasp8 { + status = "disabled"; +}; + +&mcasp9 { + status = "disabled"; +}; + &mcasp10 { #sound-dai-cells = <0>; @@ -556,8 +828,44 @@ >; tx-num-evt = <0>; rx-num-evt = <0>; +}; + +&mcasp11 { + status = "disabled"; +}; + +&cmn_refclk1 { + clock-frequency = <100000000>; +}; + +&wiz0_pll1_refclk { + assigned-clocks = <&wiz0_pll1_refclk>; + assigned-clock-parents = <&cmn_refclk1>; +}; + +&wiz0_refclk_dig { + assigned-clocks = <&wiz0_refclk_dig>; + assigned-clock-parents = <&cmn_refclk1>; +}; + +&wiz1_pll1_refclk { + assigned-clocks = <&wiz1_pll1_refclk>; + assigned-clock-parents = <&cmn_refclk1>; +}; - status = "okay"; +&wiz1_refclk_dig { + assigned-clocks = <&wiz1_refclk_dig>; + assigned-clock-parents = <&cmn_refclk1>; +}; + +&wiz2_pll1_refclk { + assigned-clocks = <&wiz2_pll1_refclk>; + assigned-clock-parents = <&cmn_refclk1>; +}; + +&wiz2_refclk_dig { + assigned-clocks = <&wiz2_refclk_dig>; + assigned-clock-parents = <&cmn_refclk1>; }; &cmn_refclk1 { @@ -565,7 +873,10 @@ }; &serdes0 { - serdes0_pcie_link: link@0 { + assigned-clocks = <&serdes0 CDNS_SIERRA_PLL_CMNLC>; + assigned-clock-parents = <&wiz0_pll1_refclk>; + + serdes0_pcie_link: phy@0 { reg = <0>; cdns,num-lanes = <1>; #phy-cells = <0>; @@ -575,7 +886,10 @@ }; &serdes1 { - serdes1_pcie_link: link@0 { + assigned-clocks = <&serdes1 CDNS_SIERRA_PLL_CMNLC>; + assigned-clock-parents = <&wiz1_pll1_refclk>; + + serdes1_pcie_link: phy@0 { reg = <0>; cdns,num-lanes = <2>; #phy-cells = <0>; @@ -585,7 +899,10 @@ }; &serdes2 { - serdes2_pcie_link: link@0 { + assigned-clocks = <&serdes2 CDNS_SIERRA_PLL_CMNLC>; + assigned-clock-parents = <&wiz2_pll1_refclk>; + + serdes2_pcie_link: phy@0 { reg = <0>; cdns,num-lanes = <2>; #phy-cells = <0>; @@ -643,3 +960,100 @@ &pcie3_ep { status = "disabled"; }; + +/* uart2 assigned to cpsw9g eth-switch fw running on remote CPU core */ +&main_uart2 { + status = "disabled"; +}; + +&mcu_mcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_pins_default>; + phys = <&transceiver1>; +}; + +&mcu_mcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan1_pins_default>; + phys = <&transceiver2>; +}; + +&main_mcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan0_pins_default>; + phys = <&transceiver3>; +}; + +&main_mcan1 { + status = "disabled"; +}; + +&main_mcan2 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan2_pins_default>; + phys = <&transceiver4>; +}; + +&main_mcan3 { + status = "disabled"; +}; + +&main_mcan4 { + status = "disabled"; +}; + +&main_mcan5 { + status = "disabled"; +}; + +&main_mcan6 { + status = "disabled"; +}; + +&main_mcan7 { + status = "disabled"; +}; + +&main_mcan8 { + status = "disabled"; +}; + +&main_mcan9 { + status = "disabled"; +}; + +&main_mcan10 { + status = "disabled"; +}; + +&main_mcan11 { + status = "disabled"; +}; + +&main_mcan12 { + status = "disabled"; +}; + +&main_mcan13 { + status = "disabled"; +}; + +&csi0_port0 { + status = "disabled"; +}; + +&csi0_port1 { + status = "disabled"; +}; + +&csi0_port2 { + status = "disabled"; +}; + +&csi0_port3 { + status = "disabled"; +}; + +&csi0_port4 { + status = "disabled"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board-infotainment.dts b/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board-infotainment.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board-infotainment.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board-infotainment.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Infotainment Expansion Board for j721e-evm + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + + ddc-i2c-bus = <&main_i2c1>; + digital; + + /* P12 - HDMI_HPD */ + hpd-gpios = <&vout_exp 10 GPIO_ACTIVE_HIGH>; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&tfp410_out>; + }; + }; + }; + + dvi-bridge { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,tfp410"; + + /* P10 - HDMI_PDn */ + powerdown-gpios = <&vout_exp 8 GPIO_ACTIVE_LOW>; + + port@0 { + reg = <0>; + + tfp410_in: endpoint { + remote-endpoint = <&dpi_out0>; + pclk-sample = <1>; + }; + }; + + port@1 { + reg = <1>; + + tfp410_out: endpoint { + remote-endpoint = + <&hdmi_connector_in>; + }; + }; + }; + }; + }; +}; + +&main_pmx0 { + main_i2c1_vout_exp_pins_default: main-i2c1-vout-exp-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x264, PIN_INPUT, 7) /* (T29) MMC2_DAT2.GPIO1_24 */ + >; + }; + + dss_vout0_pins_default: dss-vout0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x58, PIN_OUTPUT, 10) /* (AE22) PRG1_PRU1_GPO0.VOUT0_DATA0 */ + J721E_IOPAD(0x5c, PIN_OUTPUT, 10) /* (AG23) PRG1_PRU1_GPO1.VOUT0_DATA1 */ + J721E_IOPAD(0x60, PIN_OUTPUT, 10) /* (AF23) PRG1_PRU1_GPO2.VOUT0_DATA2 */ + J721E_IOPAD(0x64, PIN_OUTPUT, 10) /* (AD23) PRG1_PRU1_GPO3.VOUT0_DATA3 */ + J721E_IOPAD(0x68, PIN_OUTPUT, 10) /* (AH24) PRG1_PRU1_GPO4.VOUT0_DATA4 */ + J721E_IOPAD(0x6c, PIN_OUTPUT, 10) /* (AG21) PRG1_PRU1_GPO5.VOUT0_DATA5 */ + J721E_IOPAD(0x70, PIN_OUTPUT, 10) /* (AE23) PRG1_PRU1_GPO6.VOUT0_DATA6 */ + J721E_IOPAD(0x74, PIN_OUTPUT, 10) /* (AC21) PRG1_PRU1_GPO7.VOUT0_DATA7 */ + J721E_IOPAD(0x78, PIN_OUTPUT, 10) /* (Y23) PRG1_PRU1_GPO8.VOUT0_DATA8 */ + J721E_IOPAD(0x7c, PIN_OUTPUT, 10) /* (AF21) PRG1_PRU1_GPO9.VOUT0_DATA9 */ + J721E_IOPAD(0x80, PIN_OUTPUT, 10) /* (AB23) PRG1_PRU1_GPO10.VOUT0_DATA10 */ + J721E_IOPAD(0x84, PIN_OUTPUT, 10) /* (AJ25) PRG1_PRU1_GPO11.VOUT0_DATA11 */ + J721E_IOPAD(0x88, PIN_OUTPUT, 10) /* (AH25) PRG1_PRU1_GPO12.VOUT0_DATA12 */ + J721E_IOPAD(0x8c, PIN_OUTPUT, 10) /* (AG25) PRG1_PRU1_GPO13.VOUT0_DATA13 */ + J721E_IOPAD(0x90, PIN_OUTPUT, 10) /* (AH26) PRG1_PRU1_GPO14.VOUT0_DATA14 */ + J721E_IOPAD(0x94, PIN_OUTPUT, 10) /* (AJ27) PRG1_PRU1_GPO15.VOUT0_DATA15 */ + J721E_IOPAD(0x30, PIN_OUTPUT, 10) /* (AF24) PRG1_PRU0_GPO11.VOUT0_DATA16 */ + J721E_IOPAD(0x34, PIN_OUTPUT, 10) /* (AJ24) PRG1_PRU0_GPO12.VOUT0_DATA17 */ + J721E_IOPAD(0x38, PIN_OUTPUT, 10) /* (AG24) PRG1_PRU0_GPO13.VOUT0_DATA18 */ + J721E_IOPAD(0x3c, PIN_OUTPUT, 10) /* (AD24) PRG1_PRU0_GPO14.VOUT0_DATA19 */ + J721E_IOPAD(0x40, PIN_OUTPUT, 10) /* (AC24) PRG1_PRU0_GPO15.VOUT0_DATA20 */ + J721E_IOPAD(0x44, PIN_OUTPUT, 10) /* (AE24) PRG1_PRU0_GPO16.VOUT0_DATA21 */ + J721E_IOPAD(0x24, PIN_OUTPUT, 10) /* (AJ20) PRG1_PRU0_GPO8.VOUT0_DATA22 */ + J721E_IOPAD(0x28, PIN_OUTPUT, 10) /* (AG20) PRG1_PRU0_GPO9.VOUT0_DATA23 */ + J721E_IOPAD(0x9c, PIN_OUTPUT, 10) /* (AC22) PRG1_PRU1_GPO17.VOUT0_DE */ + J721E_IOPAD(0x98, PIN_OUTPUT, 10) /* (AJ26) PRG1_PRU1_GPO16.VOUT0_HSYNC */ + J721E_IOPAD(0xa4, PIN_OUTPUT, 10) /* (AH22) PRG1_PRU1_GPO19.VOUT0_PCLK */ + J721E_IOPAD(0xa0, PIN_OUTPUT, 10) /* (AJ22) PRG1_PRU1_GPO18.VOUT0_VSYNC */ + >; + }; +}; + +&main_i2c1 { + /* i2c1 is used for DVI DDC, so we need to use 100kHz */ + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + vout_exp: gpio@21 { + compatible = "ti,tca6416"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c1_vout_exp_pins_default>; + interrupt-parent = <&main_gpio1>; + interrupts = <24 IRQ_TYPE_EDGE_FALLING>; + interrupt-controller; + #interrupt-cells = <2>; + }; +}; + +&vout_exp { + p11 { + /* P11 - HDMI_DDC_OE */ + gpio-hog; + gpios = <9 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "HDMI_DDC_OE"; + }; +}; + +&exp1 { + p14 { + /* P14 - VINOUT_MUX_SEL0 */ + gpio-hog; + gpios = <12 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "VINOUT_MUX_SEL0"; + }; + + p15 { + /* P15 - VINOUT_MUX_SEL1 */ + gpio-hog; + gpios = <13 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "VINOUT_MUX_SEL1"; + }; +}; + +&dss { + pinctrl-names = "default"; + pinctrl-0 = <&dss_vout0_pins_default>; +}; + +&dss_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + dpi_out0: endpoint { + remote-endpoint = <&tfp410_in>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-cpb-csi2-ov5640.dts b/arch/arm64/boot/dts/ti/k3-j721e-cpb-csi2-ov5640.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-cpb-csi2-ov5640.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-cpb-csi2-ov5640.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_ov5640_fixed: ov5640-xclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + }; + }; +}; + +&main_i2c6 { + #address-cells = <1>; + #size-cells = <0>; + + ov5640: camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + + clocks = <&clk_ov5640_fixed>; + clock-names = "xclk"; + reset-gpios = <&exp5 0 GPIO_ACTIVE_LOW>; + + port { + csi2_cam0: endpoint { + remote-endpoint = <&csi2rx0_in_sensor>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; +}; + +&csi0_port0 { + status = "okay"; + + csi2rx0_in_sensor: endpoint { + remote-endpoint = <&csi2_cam0>; + bus-type = <4>; /* CSI2 DPHY. */ + clock-lanes = <0>; + data-lanes = <1 2>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e.dtsi b/arch/arm64/boot/dts/ti/k3-j721e.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721e.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -115,7 +115,7 @@ }; pmu: pmu { - compatible = "arm,armv8-pmuv3"; + compatible = "arm,cortex-a72-pmu"; /* Recommendation from GIC500 TRM Table A.3 */ interrupts = ; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi b/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -2,7 +2,7 @@ /* * Device Tree Source for J721E SoC Family Main Domain peripherals * - * Copyright (C) 2016-2019 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2016-2020 Texas Instruments Incorporated - https://www.ti.com/ */ #include #include @@ -42,38 +42,6 @@ #size-cells = <1>; ranges = <0x0 0x0 0x00100000 0x1c000>; - pcie0_ctrl: syscon@4070 { - compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; - reg = <0x00004070 0x4>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x4070 0x4070 0x4>; - }; - - pcie1_ctrl: syscon@4074 { - compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; - reg = <0x00004074 0x4>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x4074 0x4074 0x4>; - }; - - pcie2_ctrl: syscon@4078 { - compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; - reg = <0x00004078 0x4>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x4078 0x4078 0x4>; - }; - - pcie3_ctrl: syscon@407c { - compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; - reg = <0x0000407c 0x4>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x407c 0x407c 0x4>; - }; - serdes_ln_ctrl: mux@4080 { compatible = "mmio-mux"; reg = <0x00004080 0x50>; @@ -162,6 +130,7 @@ interrupt-controller; interrupt-parent = <&main_navss_intr>; msi-controller; + #interrupt-cells = <0>; ti,sci = <&dmsc>; ti,sci-dev-id = <209>; ti,interrupt-ranges = <0 0 256>; @@ -359,8 +328,6 @@ #size-cells = <2>; ranges = <0x0 0x04e00000 0x00 0x04e00000 0x0 0x30000>; - status = "okay"; - dmas = <&main_udmap 0xc000>, <&main_udmap 0x4000>, <&main_udmap 0x4001>; dma-names = "tx", "rx1", "rx2"; @@ -433,10 +400,11 @@ reg = <0x5000000 0x10000>; #address-cells = <1>; #size-cells = <0>; + #clock-cells = <1>; resets = <&serdes_wiz0 0>; reset-names = "sierra_reset"; - clocks = <&wiz0_cmn_refclk_dig_div>, <&wiz0_cmn_refclk1_dig_div>; - clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div"; + clocks = <&wiz0_cmn_refclk_dig_div>, <&wiz0_cmn_refclk1_dig_div>, <&wiz0_pll0_refclk>, <&wiz0_pll1_refclk>; + clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div", "pll0_refclk", "pll1_refclk"; }; }; @@ -490,10 +458,11 @@ reg = <0x5010000 0x10000>; #address-cells = <1>; #size-cells = <0>; + #clock-cells = <1>; resets = <&serdes_wiz1 0>; reset-names = "sierra_reset"; - clocks = <&wiz1_cmn_refclk_dig_div>, <&wiz1_cmn_refclk1_dig_div>; - clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div"; + clocks = <&wiz1_cmn_refclk_dig_div>, <&wiz1_cmn_refclk1_dig_div>, <&wiz1_pll0_refclk>, <&wiz1_pll1_refclk>; + clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div", "pll0_refclk", "pll1_refclk"; }; }; @@ -547,10 +516,11 @@ reg = <0x5020000 0x10000>; #address-cells = <1>; #size-cells = <0>; + #clock-cells = <1>; resets = <&serdes_wiz2 0>; reset-names = "sierra_reset"; - clocks = <&wiz2_cmn_refclk_dig_div>, <&wiz2_cmn_refclk1_dig_div>; - clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div"; + clocks = <&wiz2_cmn_refclk_dig_div>, <&wiz2_cmn_refclk1_dig_div>, <&wiz2_pll0_refclk>, <&wiz2_pll1_refclk>; + clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div", "pll0_refclk", "pll1_refclk"; }; }; @@ -604,10 +574,11 @@ reg = <0x5030000 0x10000>; #address-cells = <1>; #size-cells = <0>; + #clock-cells = <1>; resets = <&serdes_wiz3 0>; reset-names = "sierra_reset"; - clocks = <&wiz3_cmn_refclk_dig_div>, <&wiz3_cmn_refclk1_dig_div>; - clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div"; + clocks = <&wiz3_cmn_refclk_dig_div>, <&wiz3_cmn_refclk1_dig_div>, <&wiz3_pll0_refclk>, <&wiz3_pll1_refclk>; + clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div", "pll0_refclk", "pll1_refclk"; }; }; @@ -621,7 +592,7 @@ interrupt-names = "link_state"; interrupts = ; device_type = "pci"; - ti,syscon-pcie-ctrl = <&pcie0_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x4070>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; @@ -637,6 +608,19 @@ ranges = <0x01000000 0x0 0x10001000 0x0 0x10001000 0x0 0x0010000>, <0x02000000 0x0 0x10011000 0x0 0x10011000 0x0 0x7fef000>; dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0_intc 0>, /* INT A */ + <0 0 0 2 &pcie0_intc 0>, /* INT B */ + <0 0 0 3 &pcie0_intc 0>, /* INT C */ + <0 0 0 4 &pcie0_intc 0>; /* INT D */ + + pcie0_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; pcie0_ep: pcie-ep@2900000 { @@ -648,13 +632,12 @@ reg-names = "intd_cfg", "user_cfg", "reg", "mem"; interrupt-names = "link_state"; interrupts = ; - ti,syscon-pcie-ctrl = <&pcie0_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x4070>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; clocks = <&k3_clks 239 1>; clock-names = "fck"; - cdns,max-outbound-regions = <16>; max-functions = /bits/ 8 <6>; max-virtual-functions = /bits/ 8 <4 4 4 4 0 0>; dma-coherent; @@ -670,7 +653,7 @@ interrupt-names = "link_state"; interrupts = ; device_type = "pci"; - ti,syscon-pcie-ctrl = <&pcie1_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x4074>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 240 TI_SCI_PD_EXCLUSIVE>; @@ -686,6 +669,19 @@ ranges = <0x01000000 0x0 0x18001000 0x0 0x18001000 0x0 0x0010000>, <0x02000000 0x0 0x18011000 0x0 0x18011000 0x0 0x7fef000>; dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, /* INT A */ + <0 0 0 2 &pcie1_intc 0>, /* INT B */ + <0 0 0 3 &pcie1_intc 0>, /* INT C */ + <0 0 0 4 &pcie1_intc 0>; /* INT D */ + + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; pcie1_ep: pcie-ep@2910000 { @@ -697,13 +693,12 @@ reg-names = "intd_cfg", "user_cfg", "reg", "mem"; interrupt-names = "link_state"; interrupts = ; - ti,syscon-pcie-ctrl = <&pcie1_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x4074>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 240 TI_SCI_PD_EXCLUSIVE>; clocks = <&k3_clks 240 1>; clock-names = "fck"; - cdns,max-outbound-regions = <16>; max-functions = /bits/ 8 <6>; max-virtual-functions = /bits/ 8 <4 4 4 4 0 0>; dma-coherent; @@ -719,7 +714,7 @@ interrupt-names = "link_state"; interrupts = ; device_type = "pci"; - ti,syscon-pcie-ctrl = <&pcie2_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x4078>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 241 TI_SCI_PD_EXCLUSIVE>; @@ -735,6 +730,19 @@ ranges = <0x01000000 0x0 0x00001000 0x44 0x00001000 0x0 0x0010000>, <0x02000000 0x0 0x00011000 0x44 0x00011000 0x0 0x7fef000>; dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, /* INT A */ + <0 0 0 2 &pcie2_intc 0>, /* INT B */ + <0 0 0 3 &pcie2_intc 0>, /* INT C */ + <0 0 0 4 &pcie2_intc 0>; /* INT D */ + + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; pcie2_ep: pcie-ep@2920000 { @@ -746,13 +754,12 @@ reg-names = "intd_cfg", "user_cfg", "reg", "mem"; interrupt-names = "link_state"; interrupts = ; - ti,syscon-pcie-ctrl = <&pcie2_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x4078>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 241 TI_SCI_PD_EXCLUSIVE>; clocks = <&k3_clks 241 1>; clock-names = "fck"; - cdns,max-outbound-regions = <16>; max-functions = /bits/ 8 <6>; max-virtual-functions = /bits/ 8 <4 4 4 4 0 0>; dma-coherent; @@ -768,7 +775,7 @@ interrupt-names = "link_state"; interrupts = ; device_type = "pci"; - ti,syscon-pcie-ctrl = <&pcie3_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x407c>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 242 TI_SCI_PD_EXCLUSIVE>; @@ -784,6 +791,19 @@ ranges = <0x01000000 0x0 0x00001000 0x44 0x10001000 0x0 0x0010000>, <0x02000000 0x0 0x00011000 0x44 0x10011000 0x0 0x7fef000>; dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, /* INT A */ + <0 0 0 2 &pcie3_intc 0>, /* INT B */ + <0 0 0 3 &pcie3_intc 0>, /* INT C */ + <0 0 0 4 &pcie3_intc 0>; /* INT D */ + + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; pcie3_ep: pcie-ep@2930000 { @@ -795,13 +815,12 @@ reg-names = "intd_cfg", "user_cfg", "reg", "mem"; interrupt-names = "link_state"; interrupts = ; - ti,syscon-pcie-ctrl = <&pcie3_ctrl>; + ti,syscon-pcie-ctrl = <&scm_conf 0x407c>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 242 TI_SCI_PD_EXCLUSIVE>; clocks = <&k3_clks 242 1>; clock-names = "fck"; - cdns,max-outbound-regions = <16>; max-functions = /bits/ 8 <6>; max-virtual-functions = /bits/ 8 <4 4 4 4 0 0>; dma-coherent; @@ -809,6 +828,82 @@ #size-cells = <2>; }; + serdes_wiz4: wiz@5050000 { + compatible = "ti,j721e-wiz-10g"; + #address-cells = <1>; + #size-cells = <1>; + power-domains = <&k3_pds 297 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 297 1>, <&k3_clks 297 9>, <&cmn_refclk>; + clock-names = "fck", "core_ref_clk", "ext_ref_clk"; + assigned-clocks = <&k3_clks 297 9>; + assigned-clock-parents = <&k3_clks 297 10>; + assigned-clock-rates = <19200000>; + num-lanes = <4>; + #reset-cells = <1>; + ranges = <0x5050000 0x0 0x5050000 0x10000>, + <0xa030a00 0x0 0xa030a00 0x40>; + + wiz4_pll0_refclk: pll0-refclk { + clocks = <&k3_clks 297 9>, <&cmn_refclk>; + clock-output-names = "wiz4_pll0_refclk"; + #clock-cells = <0>; + assigned-clocks = <&wiz4_pll0_refclk>; + assigned-clock-parents = <&k3_clks 297 9>; + }; + + wiz4_pll1_refclk: pll1-refclk { + clocks = <&k3_clks 297 9>, <&cmn_refclk>; + clock-output-names = "wiz4_pll1_refclk"; + #clock-cells = <0>; + assigned-clocks = <&wiz4_pll1_refclk>; + assigned-clock-parents = <&k3_clks 297 9>; + }; + + wiz4_refclk_dig: refclk-dig { + clocks = <&k3_clks 297 9>, <&cmn_refclk>; + clock-output-names = "wiz4_refclk_dig"; + #clock-cells = <0>; + assigned-clocks = <&wiz4_refclk_dig>; + assigned-clock-parents = <&k3_clks 297 9>; + }; + + wiz4_cmn_refclk_dig_div: cmn-refclk-dig-div { + clocks = <&wiz4_refclk_dig>; + #clock-cells = <0>; + }; + + wiz4_cmn_refclk1_dig_div: cmn-refclk1-dig-div { + clocks = <&wiz4_pll1_refclk>; + #clock-cells = <0>; + }; + + serdes4: serdes@5050000 { + /* + * Note: we also map DPTX PHY registers as the Torrent + * needs to manage those. + */ + compatible = "ti,j721e-serdes-10g"; + reg = <0x5050000 0x10000>, + <0xa030a00 0x40>; /* DPTX PHY */ + reg-names = "torrent_phy", "dptx_phy"; + + resets = <&serdes_wiz4 0>; + reset-names = "torrent_reset"; + clocks = <&wiz4_pll0_refclk>; + clock-names = "refclk"; + #address-cells = <1>; + #size-cells = <0>; + torrent_phy_dp: phy@0 { + reg = <0>; + resets = <&serdes_wiz4 1>; + cdns,phy-type = ; + cdns,num-lanes = <4>; + cdns,max-bit-rate = <5400>; + #phy-cells = <0>; + }; + }; + }; + main_uart0: serial@2800000 { compatible = "ti,j721e-uart", "ti,am654-uart"; reg = <0x00 0x02800000 0x00 0x100>; @@ -1071,54 +1166,81 @@ clock-names = "gpio"; }; - main_sdhci0: sdhci@4f80000 { + main_sdhci0: mmc@4f80000 { compatible = "ti,j721e-sdhci-8bit"; reg = <0x0 0x4f80000 0x0 0x1000>, <0x0 0x4f88000 0x0 0x400>; interrupts = ; power-domains = <&k3_pds 91 TI_SCI_PD_EXCLUSIVE>; - clock-names = "clk_xin", "clk_ahb"; - clocks = <&k3_clks 91 1>, <&k3_clks 91 0>; + clock-names = "clk_ahb", "clk_xin"; + clocks = <&k3_clks 91 0>, <&k3_clks 91 1>; assigned-clocks = <&k3_clks 91 1>; assigned-clock-parents = <&k3_clks 91 2>; bus-width = <8>; - mmc-hs400-1_8v; + mmc-hs200-1_8v; mmc-ddr-1_8v; - ti,otap-del-sel = <0x2>; + ti,otap-del-sel-legacy = <0xf>; + ti,otap-del-sel-mmc-hs = <0xf>; + ti,otap-del-sel-ddr52 = <0x5>; + ti,otap-del-sel-hs200 = <0x6>; + ti,otap-del-sel-hs400 = <0x0>; + ti,itap-del-sel-legacy = <0x10>; + ti,itap-del-sel-mmc-hs = <0xa>; + ti,itap-del-sel-ddr52 = <0x3>; ti,trm-icp = <0x8>; ti,strobe-sel = <0x77>; dma-coherent; }; - main_sdhci1: sdhci@4fb0000 { + main_sdhci1: mmc@4fb0000 { compatible = "ti,j721e-sdhci-4bit"; reg = <0x0 0x04fb0000 0x0 0x1000>, <0x0 0x4fb8000 0x0 0x400>; interrupts = ; power-domains = <&k3_pds 92 TI_SCI_PD_EXCLUSIVE>; - clock-names = "clk_xin", "clk_ahb"; - clocks = <&k3_clks 92 0>, <&k3_clks 92 5>; + clock-names = "clk_ahb", "clk_xin"; + clocks = <&k3_clks 92 5>, <&k3_clks 92 0>; assigned-clocks = <&k3_clks 92 0>; assigned-clock-parents = <&k3_clks 92 1>; - ti,otap-del-sel = <0x2>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-sd-hs = <0xf>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-ddr50 = <0xc>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-sd-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + ti,itap-del-sel-ddr50 = <0x2>; ti,trm-icp = <0x8>; ti,clkbuf-sel = <0x7>; dma-coherent; - no-1-8-v; + sdhci-caps-mask = <0x2 0x0>; }; - main_sdhci2: sdhci@4f98000 { + main_sdhci2: mmc@4f98000 { compatible = "ti,j721e-sdhci-4bit"; reg = <0x0 0x4f98000 0x0 0x1000>, <0x0 0x4f90000 0x0 0x400>; interrupts = ; power-domains = <&k3_pds 93 TI_SCI_PD_EXCLUSIVE>; - clock-names = "clk_xin", "clk_ahb"; - clocks = <&k3_clks 93 0>, <&k3_clks 93 5>; + clock-names = "clk_ahb", "clk_xin"; + clocks = <&k3_clks 93 5>, <&k3_clks 93 0>; assigned-clocks = <&k3_clks 93 0>; assigned-clock-parents = <&k3_clks 93 1>; - ti,otap-del-sel = <0x2>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-sd-hs = <0xf>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-ddr50 = <0xc>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-sd-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + ti,itap-del-sel-ddr50 = <0x2>; ti,trm-icp = <0x8>; ti,clkbuf-sel = <0x7>; dma-coherent; - no-1-8-v; + sdhci-caps-mask = <0x2 0x0>; }; usbss0: cdns-usb@4104000 { @@ -1258,6 +1380,23 @@ power-domains = <&k3_pds 193 TI_SCI_PD_EXCLUSIVE>; }; + d5520: video-decoder@4300000 { + /* IMG D5520 driver configuration */ + compatible = "img,d5500-vxd"; + reg = <0x00 0x04300000>, + <0x00 0x100000>; + power-domains = <&k3_pds 144 TI_SCI_PD_EXCLUSIVE>; + interrupts = ; + }; + + vxe384: video-encoder@4200000 { + compatible = "img,vxe384"; + reg = <0x00 0x04200000>, + <0x00 0x100000>; + power-domains = <&k3_pds 153 TI_SCI_PD_EXCLUSIVE>; + interrupts = ; + }; + ufs_wrapper: ufs-wrapper@4e80000 { compatible = "ti,j721e-ufs"; reg = <0x0 0x4e80000 0x0 0x100>; @@ -1280,6 +1419,32 @@ }; }; + mhdp: dp-bridge@a000000 { + compatible = "ti,j721e-mhdp8546"; + /* + * Note: we do not map DPTX PHY area, as that is handled by + * the PHY driver. + */ + reg = <0x0 0xa000000 0x0 0x30a00>, /* DSS_EDP0_V2A_CORE_VP_REGS_APB */ + <0x0 0x4f40000 0x0 0x20>; /* DSS_EDP0_INTG_CFG_VP */ + reg-names = "mhdptx", "j721e-intg"; + + clocks = <&k3_clks 151 36>; + + phys = <&torrent_phy_dp>; + phy-names = "dpphy"; + + interrupt-parent = <&gic500>; + interrupts = ; + + power-domains = <&k3_pds 151 TI_SCI_PD_EXCLUSIVE>; + + dp0_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + dss: dss@4a00000 { compatible = "ti,j721e-dss"; reg = @@ -1329,8 +1494,6 @@ "common_s1", "common_s2"; - status = "disabled"; - dss_ports: ports { #address-cells = <1>; #size-cells = <0>; @@ -1352,8 +1515,6 @@ clocks = <&k3_clks 174 1>; clock-names = "fck"; power-domains = <&k3_pds 174 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp1: mcasp@2b10000 { @@ -1371,8 +1532,6 @@ clocks = <&k3_clks 175 1>; clock-names = "fck"; power-domains = <&k3_pds 175 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp2: mcasp@2b20000 { @@ -1390,8 +1549,6 @@ clocks = <&k3_clks 176 1>; clock-names = "fck"; power-domains = <&k3_pds 176 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp3: mcasp@2b30000 { @@ -1409,8 +1566,6 @@ clocks = <&k3_clks 177 1>; clock-names = "fck"; power-domains = <&k3_pds 177 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp4: mcasp@2b40000 { @@ -1428,8 +1583,6 @@ clocks = <&k3_clks 178 1>; clock-names = "fck"; power-domains = <&k3_pds 178 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp5: mcasp@2b50000 { @@ -1447,8 +1600,6 @@ clocks = <&k3_clks 179 1>; clock-names = "fck"; power-domains = <&k3_pds 179 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp6: mcasp@2b60000 { @@ -1466,8 +1617,6 @@ clocks = <&k3_clks 180 1>; clock-names = "fck"; power-domains = <&k3_pds 180 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp7: mcasp@2b70000 { @@ -1485,8 +1634,6 @@ clocks = <&k3_clks 181 1>; clock-names = "fck"; power-domains = <&k3_pds 181 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp8: mcasp@2b80000 { @@ -1504,8 +1651,6 @@ clocks = <&k3_clks 182 1>; clock-names = "fck"; power-domains = <&k3_pds 182 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp9: mcasp@2b90000 { @@ -1523,8 +1668,6 @@ clocks = <&k3_clks 183 1>; clock-names = "fck"; power-domains = <&k3_pds 183 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp10: mcasp@2ba0000 { @@ -1542,8 +1685,6 @@ clocks = <&k3_clks 184 1>; clock-names = "fck"; power-domains = <&k3_pds 184 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; mcasp11: mcasp@2bb0000 { @@ -1561,8 +1702,6 @@ clocks = <&k3_clks 185 1>; clock-names = "fck"; power-domains = <&k3_pds 185 TI_SCI_PD_EXCLUSIVE>; - - status = "disabled"; }; watchdog0: watchdog@2200000 { @@ -1583,6 +1722,86 @@ assigned-clock-parents = <&k3_clks 253 5>; }; + main_r5fss0: r5fss@5c00000 { + compatible = "ti,j721e-r5fss"; + ti,cluster-mode = <0>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x5c00000 0x00 0x5c00000 0x20000>, + <0x5d00000 0x00 0x5d00000 0x20000>; + power-domains = <&k3_pds 243 TI_SCI_PD_EXCLUSIVE>; + + main_r5fss0_core0: r5f@5c00000 { + compatible = "ti,j721e-r5f"; + reg = <0x5c00000 0x00008000>, + <0x5c10000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <245>; + ti,sci-proc-ids = <0x06 0xff>; + resets = <&k3_reset 245 1>; + firmware-name = "j7-main-r5f0_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + main_r5fss0_core1: r5f@5d00000 { + compatible = "ti,j721e-r5f"; + reg = <0x5d00000 0x00008000>, + <0x5d10000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <246>; + ti,sci-proc-ids = <0x07 0xff>; + resets = <&k3_reset 246 1>; + firmware-name = "j7-main-r5f0_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + + main_r5fss1: r5fss@5e00000 { + compatible = "ti,j721e-r5fss"; + ti,cluster-mode = <0>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x5e00000 0x00 0x5e00000 0x20000>, + <0x5f00000 0x00 0x5f00000 0x20000>; + power-domains = <&k3_pds 244 TI_SCI_PD_EXCLUSIVE>; + + main_r5fss1_core0: r5f@5e00000 { + compatible = "ti,j721e-r5f"; + reg = <0x5e00000 0x00008000>, + <0x5e10000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <247>; + ti,sci-proc-ids = <0x08 0xff>; + resets = <&k3_reset 247 1>; + firmware-name = "j7-main-r5f1_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + main_r5fss1_core1: r5f@5f00000 { + compatible = "ti,j721e-r5f"; + reg = <0x5f00000 0x00008000>, + <0x5f10000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <248>; + ti,sci-proc-ids = <0x09 0xff>; + resets = <&k3_reset 248 1>; + firmware-name = "j7-main-r5f1_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + c66_0: dsp@4d80800000 { compatible = "ti,j721e-c66-dsp"; reg = <0x4d 0x80800000 0x00 0x00048000>, @@ -1620,4 +1839,677 @@ resets = <&k3_reset 15 1>; firmware-name = "j7-c71_0-fw"; }; + + icssg0: icssg@b000000 { + compatible = "ti,j721e-icssg"; + reg = <0x00 0xb000000 0x00 0x80000>; + power-domains = <&k3_pds 119 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x0b000000 0x100000>; + + icssg0_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + icssg0_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg0_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 119 24>, /* icssg0_core_clk */ + <&k3_clks 119 1>; /* icssg0_iclk */ + assigned-clocks = <&icssg0_coreclk_mux>; + assigned-clock-parents = <&k3_clks 119 1>; + }; + + icssg0_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 119 3>, /* icssg0_iep_clk */ + <&icssg0_coreclk_mux>; /* core_clk */ + assigned-clocks = <&icssg0_iepclk_mux>; + assigned-clock-parents = <&icssg0_coreclk_mux>; + }; + }; + }; + + icssg0_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; + + icssg0_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; + + icssg0_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg0_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg0_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru0_0: pru@34000 { + compatible = "ti,j721e-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-pru0_0-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu0_0: rtu@4000 { + compatible = "ti,j721e-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-rtu0_0-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru0_0: txpru@a000 { + compatible = "ti,j721e-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-txpru0_0-fw"; + }; + + pru0_1: pru@38000 { + compatible = "ti,j721e-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-pru0_1-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu0_1: rtu@6000 { + compatible = "ti,j721e-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-rtu0_1-fw"; + interrupt-parent = <&icssg0_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru0_1: txpru@c000 { + compatible = "ti,j721e-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-txpru0_1-fw"; + }; + + icssg0_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + clocks = <&k3_clks 119 1>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + status = "disabled"; + }; + }; + + icssg1: icssg@b100000 { + compatible = "ti,j721e-icssg"; + reg = <0x00 0xb100000 0x00 0x80000>; + power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x0b100000 0x100000>; + + icssg1_mem: memories@b100000 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", + "shrdram2"; + }; + + icssg1_cfg: cfg@26000 { + compatible = "ti,pruss-cfg", "syscon"; + reg = <0x26000 0x200>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x26000 0x2000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + icssg1_coreclk_mux: coreclk-mux@3c { + reg = <0x3c>; + #clock-cells = <0>; + clocks = <&k3_clks 120 54>, /* icssg1_core_clk */ + <&k3_clks 120 4>; /* icssg1_iclk */ + assigned-clocks = <&icssg1_coreclk_mux>; + assigned-clock-parents = <&k3_clks 120 4>; + }; + + icssg1_iepclk_mux: iepclk-mux@30 { + reg = <0x30>; + #clock-cells = <0>; + clocks = <&k3_clks 120 9>, /* icssg1_iep_clk */ + <&icssg1_coreclk_mux>; /* core_clk */ + assigned-clocks = <&icssg1_iepclk_mux>; + assigned-clock-parents = <&icssg1_coreclk_mux>; + }; + }; + }; + + icssg1_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg1_iepclk_mux>; + }; + + icssg1_iep1: iep@2f000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2f000 0x1000>; + clocks = <&icssg1_iepclk_mux>; + }; + + icssg1_mii_rt: mii-rt@32000 { + compatible = "ti,pruss-mii", "syscon"; + reg = <0x32000 0x100>; + }; + + icssg1_mii_g_rt: mii-g-rt@33000 { + compatible = "ti,pruss-mii-g", "syscon"; + reg = <0x33000 0x1000>; + }; + + icssg1_intc: interrupt-controller@20000 { + compatible = "ti,icssg-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru1_0: pru@34000 { + compatible = "ti,j721e-pru"; + reg = <0x34000 0x4000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-pru1_0-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + rtu1_0: rtu@4000 { + compatible = "ti,j721e-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-rtu1_0-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <20 4 4>; + interrupt-names = "vring"; + }; + + tx_pru1_0: txpru@a000 { + compatible = "ti,j721e-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-txpru1_0-fw"; + }; + + pru1_1: pru@38000 { + compatible = "ti,j721e-pru"; + reg = <0x38000 0x4000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-pru1_1-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + + rtu1_1: rtu@6000 { + compatible = "ti,j721e-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-rtu1_1-fw"; + interrupt-parent = <&icssg1_intc>; + interrupts = <22 5 5>; + interrupt-names = "vring"; + }; + + tx_pru1_1: txpru@c000 { + compatible = "ti,j721e-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "j7-txpru1_1-fw"; + }; + + icssg1_mdio: mdio@32400 { + compatible = "ti,davinci_mdio"; + reg = <0x32400 0x100>; + clocks = <&k3_clks 120 4>; + clock-names = "fck"; + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + status = "disabled"; + }; + }; + + timesync_router: timesync_router@A40000 { + compatible = "pinctrl-single"; + reg = <0x0 0xa40000 0x0 0x800>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x000107ff>; + status = "disabled"; + }; + + gpu: gpu@4e20000000 { + compatible = "ti,j721e-pvr", "img,pvr-ge8430"; + reg = <0x4e 0x20000000 0x00 0x80000>; + reg-names = "gpu_regs"; + interrupts = ; + power-domains = <&k3_pds 125 TI_SCI_PD_EXCLUSIVE>, + <&k3_pds 126 TI_SCI_PD_EXCLUSIVE>; + power-domain-names = "gpu_0", "gpucore_0"; + clocks = <&k3_clks 125 0>; + clock-names = "ctrl"; + }; + + main_mcan0: can@2701000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02701000 0x00 0x200>, + <0x00 0x02708000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 156 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 156 0>, <&k3_clks 156 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan1: can@2711000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02711000 0x00 0x200>, + <0x00 0x02718000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 158 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 158 0>, <&k3_clks 158 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan2: can@2721000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02721000 0x00 0x200>, + <0x00 0x02728000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 160 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 160 0>, <&k3_clks 160 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan3: can@2731000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02731000 0x00 0x200>, + <0x00 0x02738000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 161 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 161 0>, <&k3_clks 161 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan4: can@2741000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02741000 0x00 0x200>, + <0x00 0x02748000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 162 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 162 0>, <&k3_clks 162 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan5: can@2751000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02751000 0x00 0x200>, + <0x00 0x02758000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 163 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 163 0>, <&k3_clks 163 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan6: can@2761000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02761000 0x00 0x200>, + <0x00 0x02768000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 164 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 164 0>, <&k3_clks 164 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan7: can@2771000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02771000 0x00 0x200>, + <0x00 0x02778000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 165 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 165 0>, <&k3_clks 165 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan8: can@2781000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02781000 0x00 0x200>, + <0x00 0x02788000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 166 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 166 0>, <&k3_clks 166 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan9: can@2791000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02791000 0x00 0x200>, + <0x00 0x02798000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 167 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 167 0>, <&k3_clks 167 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan10: can@27a1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027a1000 0x00 0x200>, + <0x00 0x027a8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 168 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 168 0>, <&k3_clks 168 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan11: can@27b1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027b1000 0x00 0x200>, + <0x00 0x027b8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 169 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 169 0>, <&k3_clks 169 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan12: can@27c1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027c1000 0x00 0x200>, + <0x00 0x027c8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 170 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 170 0>, <&k3_clks 170 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan13: can@27d1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027d1000 0x00 0x200>, + <0x00 0x027d8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 171 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 171 0>, <&k3_clks 171 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + ti_csi2rx0: ticsi2rx@4500000 { + compatible = "ti,j721e-csi2rx"; + dmas = <&main_udmap 0x4940>, <&main_udmap 0x4941>, <&main_udmap 0x4942>, + <&main_udmap 0x4943>, <&main_udmap 0x4944>, <&main_udmap 0x4945>, + <&main_udmap 0x4946>, <&main_udmap 0x4947>, <&main_udmap 0x4948>, + <&main_udmap 0x4949>, <&main_udmap 0x494a>, <&main_udmap 0x494b>, + <&main_udmap 0x494c>, <&main_udmap 0x494d>, <&main_udmap 0x494e>, + <&main_udmap 0x494f>; + dma-names = "rx0", "rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7", + "rx8", "rx9", "rx10", "rx11", "rx12", "rx13", "rx14", "rx15"; + reg = <0x0 0x4500000 0x0 0x1000>; + power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cdns_csi2rx0: csi-bridge@4504000 { + compatible = "cdns,csi2rx"; + reg = <0x0 0x4504000 0x0 0x1000>; + clocks = <&k3_clks 26 2>, <&k3_clks 26 0>, <&k3_clks 26 2>, + <&k3_clks 26 2>, <&k3_clks 26 3>, <&k3_clks 26 3>; + clock-names = "sys_clk", "p_clk", "pixel_if0_clk", + "pixel_if1_clk", "pixel_if2_clk", "pixel_if3_clk"; + phys = <&dphy0>; + phy-names = "dphy"; + power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + csi0_port0: port@0 { + reg = <0>; + }; + + csi0_port1: port@1 { + reg = <1>; + }; + + csi0_port2: port@2 { + reg = <2>; + }; + + csi0_port3: port@3 { + reg = <3>; + }; + + csi0_port4: port@4 { + reg = <4>; + }; + }; + }; + }; + + ti_csi2rx1: ticsi2rx@4510000 { + compatible = "ti,j721e-csi2rx"; + dmas = <&main_udmap 0x4960>, <&main_udmap 0x4961>, <&main_udmap 0x4962>, + <&main_udmap 0x4963>, <&main_udmap 0x4964>, <&main_udmap 0x4965>, + <&main_udmap 0x4966>, <&main_udmap 0x4967>, <&main_udmap 0x4968>, + <&main_udmap 0x4969>, <&main_udmap 0x496a>, <&main_udmap 0x496b>, + <&main_udmap 0x496c>, <&main_udmap 0x496d>, <&main_udmap 0x496e>, + <&main_udmap 0x496f>; + dma-names = "rx0", "rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7", + "rx8", "rx9", "rx10", "rx11", "rx12", "rx13", "rx14", "rx15"; + reg = <0x0 0x4510000 0x0 0x1000>; + power-domains = <&k3_pds 27 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cdns_csi2rx1: csi-bridge@4514000 { + compatible = "cdns,csi2rx"; + reg = <0x0 0x4514000 0x0 0x1000>; + clocks = <&k3_clks 27 2>, <&k3_clks 27 0>, <&k3_clks 27 2>, + <&k3_clks 27 2>, <&k3_clks 27 3>, <&k3_clks 27 3>; + clock-names = "sys_clk", "p_clk", "pixel_if0_clk", + "pixel_if1_clk", "pixel_if2_clk", "pixel_if3_clk"; + phys = <&dphy1>; + phy-names = "dphy"; + power-domains = <&k3_pds 27 TI_SCI_PD_EXCLUSIVE>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + csi1_port0: port@0 { + reg = <0>; + }; + + csi1_port1: port@1 { + reg = <1>; + }; + + csi1_port2: port@2 { + reg = <2>; + }; + + csi1_port3: port@3 { + reg = <3>; + }; + + csi1_port4: port@4 { + reg = <4>; + }; + }; + }; + }; + + dphy0: phy@4580000 { + compatible = "ti,j721e-dphy", "cdns,dphy"; + reg = <0x0 0x4580000 0x0 0x1100>; + #phy-cells = <0>; + power-domains = <&k3_pds 147 TI_SCI_PD_EXCLUSIVE>; + }; + + dphy1: phy@4590000 { + compatible = "ti,j721e-dphy", "cdns,dphy"; + reg = <0x0 0x4590000 0x0 0x1100>; + #phy-cells = <0>; + power-domains = <&k3_pds 148 TI_SCI_PD_EXCLUSIVE>; + }; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-mcu-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-j721e-mcu-wakeup.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721e-mcu-wakeup.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-mcu-wakeup.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -2,7 +2,7 @@ /* * Device Tree Source for J721E SoC Family MCU/WAKEUP Domain peripherals * - * Copyright (C) 2016-2019 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2016-2020 Texas Instruments Incorporated - https://www.ti.com/ */ &cbass_mcu_wakeup { @@ -353,4 +353,72 @@ ti,cpts-periodic-outputs = <2>; }; }; + + mcu_r5fss0: r5fss@41000000 { + compatible = "ti,j721e-r5fss"; + ti,cluster-mode = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x41000000 0x00 0x41000000 0x20000>, + <0x41400000 0x00 0x41400000 0x20000>; + power-domains = <&k3_pds 249 TI_SCI_PD_EXCLUSIVE>; + + mcu_r5fss0_core0: r5f@41000000 { + compatible = "ti,j721e-r5f"; + reg = <0x41000000 0x00008000>, + <0x41010000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <250>; + ti,sci-proc-ids = <0x01 0xff>; + resets = <&k3_reset 250 1>; + firmware-name = "j7-mcu-r5f0_0-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + + mcu_r5fss0_core1: r5f@41400000 { + compatible = "ti,j721e-r5f"; + reg = <0x41400000 0x00008000>, + <0x41410000 0x00008000>; + reg-names = "atcm", "btcm"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <251>; + ti,sci-proc-ids = <0x02 0xff>; + resets = <&k3_reset 251 1>; + firmware-name = "j7-mcu-r5f0_1-fw"; + ti,atcm-enable = <1>; + ti,btcm-enable = <1>; + ti,loczrama = <1>; + }; + }; + + mcu_mcan0: can@40528000 { + compatible = "bosch,m_can"; + reg = <0x00 0x40528000 0x00 0x200>, + <0x00 0x40500000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 172 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 172 0>, <&k3_clks 172 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + mcu_mcan1: can@40568000 { + compatible = "bosch,m_can"; + reg = <0x00 0x40568000 0x00 0x200>, + <0x00 0x40540000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 173 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 173 0>, <&k3_clks 173 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; }; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-proc-board-tps65917.dts b/arch/arm64/boot/dts/ti/k3-j721e-proc-board-tps65917.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-proc-board-tps65917.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-proc-board-tps65917.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "k3-j721e-common-proc-board.dts" + +&wkup_i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&wkup_i2c0_pins_default>; + clock-frequency = <400000>; + + tps65917: tps65917@58 { + reg = <0x58>; + compatible = "ti,tps65917"; + + tps65917-pmic { + compatible = "ti,tps65917-pmic"; + + ldo1-in-supply = <&vsys_3v3>; + ldo2-in-supply = <&vsys_3v3>; + + tps65917_regulators: regulators { + ldo1_reg: ldo1 { + /* LDO1_OUT --> VDD_SD_DV_REG */ + regulator-name = "ldo1"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-allow-bypass; + }; + + ldo2_reg: ldo2 { + /* LDO2_OUT --> VDA_USB_3V3_REG */ + regulator-name = "ldo2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-allow-bypass; + regulator-always-on; + regulator-boot-on; + }; + }; + }; + }; +}; + +&vdd_sd_dv_alt { + status = "disabled"; +}; + +&main_sdhci1 { + vqmmc-supply = <&ldo1_reg>; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-sk-csi2-ov5640.dts b/arch/arm64/boot/dts/ti/k3-j721e-sk-csi2-ov5640.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-sk-csi2-ov5640.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-sk-csi2-ov5640.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT Overlay for OV5640 Camera on I2C bus interfaced to CSI2 with J721E-EAIK board. + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +&main_pmx0 { + csi2_exp_reset_pins_default: csi2-exp-reset-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x140, PIN_OUTPUT, 7) /* (AG29) PRG0_PRU1_GPO16.GPIO0_79 */ + >; + }; + + csi2_exp_refclk_pins_default: csi2-exp-refclk-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1A4, PIN_OUTPUT, 3) /* (W26) RGMII6_RXC.AUDIO_EXT_REFCLK2 */ + >; + }; +}; + +&k3_clks { + /* Confiure AUDIO_EXT_REFCLK2 pin as output */ + pinctrl-names = "default"; + pinctrl-0 = <&csi2_exp_refclk_pins_default>; +}; + +&main_i2c3 { + #address-cells = <1>; + #size-cells = <0>; + + i2c-switch@70 { + compatible = "nxp,pca9543"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + + /* CAM0 I2C */ + ti_cam0_i2c: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + ov5640: camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + + pinctrl-names = "default"; + pinctrl-0 = <&csi2_exp_reset_pins_default>; + reset-gpios = <&main_gpio0 79 GPIO_ACTIVE_LOW>; + + /* C_AUDIO_REFCLK3 -> RGMII6_RXC (W26) */ + clocks = <&k3_clks 157 371>; + clock-names = "xclk"; + + /* HSDIV3_16FFT_MAIN_4_HSDIVOUT2_CLK -> REFCLK2 */ + assigned-clocks = <&k3_clks 157 371>; + assigned-clock-parents = <&k3_clks 157 400>; + assigned-clock-rates = <25000000>; + + port { + csi2_cam0: endpoint { + remote-endpoint = <&csi2rx0_in_sensor>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; + }; + + }; +}; + +&csi0_port0 { + csi2rx0_in_sensor: endpoint { + remote-endpoint = <&csi2_cam0>; + bus-type = <4>; /* CSI2 DPHY. */ + clock-lanes = <0>; + data-lanes = <1 2>; + }; + +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-sk.dts b/arch/arm64/boot/dts/ti/k3-j721e-sk.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-sk.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-sk.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,1270 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "k3-j721e.dtsi" +#include +#include +#include + +/ { + compatible = "ti,j721e-sk", "ti,j721e"; + model = "Texas Instruments J721E SK"; + + chosen { + stdout-path = "serial2:115200n8"; + bootargs = "console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000"; + }; + + memory@80000000 { + device_type = "memory"; + /* 4G RAM */ + reg = <0x00000000 0x80000000 0x00000000 0x80000000>, + <0x00000008 0x80000000 0x00000000 0x80000000>; + }; + + reserved_memory: reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + secure_ddr: optee@9e800000 { + reg = <0x00 0x9e800000 0x00 0x01800000>; + alignment = <0x1000>; + no-map; + }; + + mcu_r5fss0_core0_dma_memory_region: r5f-dma-memory@a0000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0000000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core0_memory_region: r5f-memory@a0100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0100000 0x00 0xf00000>; + no-map; + }; + + mcu_r5fss0_core1_dma_memory_region: r5f-dma-memory@a1000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1000000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core1_memory_region: r5f-memory@a1100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core0_dma_memory_region: r5f-dma-memory@a2000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core0_memory_region: r5f-memory@a2100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core1_dma_memory_region: r5f-dma-memory@a3000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core1_memory_region: r5f-memory@a3100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core0_dma_memory_region: r5f-dma-memory@a4000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core0_memory_region: r5f-memory@a4100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core1_dma_memory_region: r5f-dma-memory@a5000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa5000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core1_memory_region: r5f-memory@a5100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa5100000 0x00 0xf00000>; + no-map; + }; + + c66_1_dma_memory_region: c66-dma-memory@a6000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa6000000 0x00 0x100000>; + no-map; + }; + + c66_0_memory_region: c66-memory@a6100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa6100000 0x00 0xf00000>; + no-map; + }; + + c66_0_dma_memory_region: c66-dma-memory@a7000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa7000000 0x00 0x100000>; + no-map; + }; + + c66_1_memory_region: c66-memory@a7100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa7100000 0x00 0xf00000>; + no-map; + }; + + c71_0_dma_memory_region: c71-dma-memory@a8000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa8000000 0x00 0x100000>; + no-map; + }; + + c71_0_memory_region: c71-memory@a8100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa8100000 0x00 0xf00000>; + no-map; + }; + + rtos_ipc_memory_region: ipc-memories@aa000000 { + reg = <0x00 0xaa000000 0x00 0x01c00000>; + alignment = <0x1000>; + no-map; + }; + }; + + vusb_main: fixedregulator-vusb-main5v0 { + /* USB MAIN INPUT 5V DC */ + compatible = "regulator-fixed"; + regulator-name = "vusb-main5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_3v3: fixedregulator-vsys3v3 { + /* Output of LM5141 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vusb_main>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_mmc1: fixedregulator-sd { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&vdd_mmc1_en_pins_default>; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vsys_3v3>; + gpio = <&wkup_gpio0 8 GPIO_ACTIVE_HIGH>; + }; + + vdd_sd_dv_alt: gpio-regulator-tps659411 { + compatible = "regulator-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&vdd_sd_dv_alt_pins_default>; + regulator-name = "tps659411"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + vin-supply = <&vsys_3v3>; + gpios = <&wkup_gpio0 9 GPIO_ACTIVE_HIGH>; + states = <1800000 0x0>, + <3300000 0x1>; + }; + + transceiver1: can-phy0 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_gpio_pins_default>; + standby-gpios = <&wkup_gpio0 3 GPIO_ACTIVE_HIGH>; + }; + + transceiver2: can-phy1 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan0_gpio_pins_default>; + standby-gpios = <&main_gpio0 65 GPIO_ACTIVE_HIGH>; + }; + + transceiver3: can-phy2 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan5_gpio_pins_default>; + standby-gpios = <&main_gpio0 66 GPIO_ACTIVE_HIGH>; + }; + + transceiver4: can-phy3 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan9_gpio_pins_default>; + standby-gpios = <&main_gpio0 67 GPIO_ACTIVE_HIGH>; + }; + + dp_pwr_3v3: fixedregulator-dp-prw { + compatible = "regulator-fixed"; + regulator-name = "dp-pwr"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + pinctrl-names = "default"; + pinctrl-0 = <&dp_pwr_en_pins_default>; + gpio = <&main_gpio0 111 0>; /* DP0_3V3 _EN */ + enable-active-high; + }; + + dp0: connector { + compatible = "dp-connector"; + label = "DP0"; + type = "full-size"; + dp-pwr-supply = <&dp_pwr_3v3>; + + port { + dp_connector_in: endpoint { + remote-endpoint = <&dp0_out>; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_hpd_pins_default>; + + ddc-i2c-bus = <&main_i2c1>; + digital; + + /* HDMI_HPD */ + hpd-gpios = <&main_gpio1 0 GPIO_ACTIVE_HIGH>; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&tfp410_out>; + }; + }; + }; + + dvi-bridge { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,tfp410"; + + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_pdn_pins_default>; + + /* HDMI_PDn */ + powerdown-gpios = <&main_gpio0 127 GPIO_ACTIVE_LOW>; + + port@0 { + reg = <0>; + + tfp410_in: endpoint { + remote-endpoint = <&dpi_out0>; + pclk-sample = <1>; + }; + }; + + port@1 { + reg = <1>; + + tfp410_out: endpoint { + remote-endpoint = + <&hdmi_connector_in>; + }; + }; + }; +}; + +&main_pmx0 { + main_mmc1_pins_default: main-mmc1-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x254, PIN_INPUT, 0) /* (R29) MMC1_CMD */ + J721E_IOPAD(0x250, PIN_INPUT, 0) /* (P25) MMC1_CLK */ + J721E_IOPAD(0x2ac, PIN_INPUT, 0) /* (P25) MMC1_CLKLB */ + J721E_IOPAD(0x24c, PIN_INPUT, 0) /* (R24) MMC1_DAT0 */ + J721E_IOPAD(0x248, PIN_INPUT, 0) /* (P24) MMC1_DAT1 */ + J721E_IOPAD(0x244, PIN_INPUT, 0) /* (R25) MMC1_DAT2 */ + J721E_IOPAD(0x240, PIN_INPUT, 0) /* (R26) MMC1_DAT3 */ + J721E_IOPAD(0x258, PIN_INPUT, 0) /* (P23) MMC1_SDCD */ + >; + }; + + main_uart0_pins_default: main-uart0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1f0, PIN_INPUT, 0) /* (AC2) UART0_CTSn */ + J721E_IOPAD(0x1f4, PIN_OUTPUT, 0) /* (AB1) UART0_RTSn */ + J721E_IOPAD(0x1e8, PIN_INPUT, 0) /* (AB2) UART0_RXD */ + J721E_IOPAD(0x1ec, PIN_OUTPUT, 0) /* (AB3) UART0_TXD */ + >; + }; + + main_i2c0_pins_default: main-i2c0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x220, PIN_INPUT_PULLUP, 0) /* (AC5) I2C0_SCL */ + J721E_IOPAD(0x224, PIN_INPUT_PULLUP, 0) /* (AA5) I2C0_SDA */ + >; + }; + + main_i2c1_pins_default: main-i2c1-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x228, PIN_INPUT_PULLUP, 0) /* (Y6) I2C1_SCL */ + J721E_IOPAD(0x22c, PIN_INPUT_PULLUP, 0) /* (AA6) I2C1_SDA */ + >; + }; + + main_i2c3_pins_default: main-i2c3-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x270, PIN_INPUT_PULLUP, 4) /* (T26) MMC2_CLK.I2C3_SCL */ + J721E_IOPAD(0x274, PIN_INPUT_PULLUP, 4) /* (T25) MMC2_CMD.I2C3_SDA */ + >; + }; + + main_usbss0_pins_default: main-usbss0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x290, PIN_OUTPUT, 0) /* (U6) USB0_DRVVBUS */ + J721E_IOPAD(0x210, PIN_INPUT, 7) /* (W3) MCAN1_RX.GPIO1_3 */ + >; + }; + + main_usbss1_pins_default: main-usbss1-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x214, PIN_OUTPUT, 4) /* (V4) MCAN1_TX.USB1_DRVVBUS */ + >; + }; + + main_mcan0_pins_default: main-mcan0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x208, PIN_INPUT, 0) /* (W5) MCAN0_RX */ + J721E_IOPAD(0x20c, PIN_OUTPUT, 0) /* (W6) MCAN0_TX */ + >; + }; + + main_mcan0_gpio_pins_default: main-mcan0-gpio-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x108, PIN_INPUT, 7) /* (AD27) PRG0_PRU1_GPO2.GPIO0_65 */ + >; + }; + + main_mcan5_pins_default: main-mcan5-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x50, PIN_INPUT, 6) /* (AE21) PRG1_PRU0_GPO18.MCAN5_RX */ + J721E_IOPAD(0x4c, PIN_OUTPUT, 6) /* (AJ21) PRG1_PRU0_GPO17.MCAN5_TX */ + >; + }; + + main_mcan5_gpio_pins_default: main-mcan5-gpio-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x10c, PIN_INPUT, 7) /* (AC25) PRG0_PRU1_GPO3.GPIO0_66 */ + >; + }; + + main_mcan9_pins_default: main-mcan9-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0xd0, PIN_INPUT, 6) /* (AC27) PRG0_PRU0_GPO8.MCAN9_RX */ + J721E_IOPAD(0xcc, PIN_OUTPUT, 6) /* (AC28) PRG0_PRU0_GPO7.MCAN9_TX */ + >; + }; + + main_mcan9_gpio_pins_default: main-mcan9-gpio-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x110, PIN_INPUT, 7) /* (AD29) PRG0_PRU1_GPO4.GPIO0_67 */ + >; + }; + + dp0_pins_default: dp0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1c4, PIN_INPUT, 5) /* SPI0_CS1.DP0_HPD */ + >; + }; + + dp_pwr_en_pins_default: dp-pwr-en-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1c0, PIN_INPUT, 7) /* (AA2) SPI0_CS0.GPIO0_111 */ + >; + }; + + dss_vout0_pins_default: dss-vout0-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x58, PIN_OUTPUT, 10) /* (AE22) PRG1_PRU1_GPO0.VOUT0_DATA0 */ + J721E_IOPAD(0x5c, PIN_OUTPUT, 10) /* (AG23) PRG1_PRU1_GPO1.VOUT0_DATA1 */ + J721E_IOPAD(0x60, PIN_OUTPUT, 10) /* (AF23) PRG1_PRU1_GPO2.VOUT0_DATA2 */ + J721E_IOPAD(0x64, PIN_OUTPUT, 10) /* (AD23) PRG1_PRU1_GPO3.VOUT0_DATA3 */ + J721E_IOPAD(0x68, PIN_OUTPUT, 10) /* (AH24) PRG1_PRU1_GPO4.VOUT0_DATA4 */ + J721E_IOPAD(0x6c, PIN_OUTPUT, 10) /* (AG21) PRG1_PRU1_GPO5.VOUT0_DATA5 */ + J721E_IOPAD(0x70, PIN_OUTPUT, 10) /* (AE23) PRG1_PRU1_GPO6.VOUT0_DATA6 */ + J721E_IOPAD(0x74, PIN_OUTPUT, 10) /* (AC21) PRG1_PRU1_GPO7.VOUT0_DATA7 */ + J721E_IOPAD(0x78, PIN_OUTPUT, 10) /* (Y23) PRG1_PRU1_GPO8.VOUT0_DATA8 */ + J721E_IOPAD(0x7c, PIN_OUTPUT, 10) /* (AF21) PRG1_PRU1_GPO9.VOUT0_DATA9 */ + J721E_IOPAD(0x80, PIN_OUTPUT, 10) /* (AB23) PRG1_PRU1_GPO10.VOUT0_DATA10 */ + J721E_IOPAD(0x84, PIN_OUTPUT, 10) /* (AJ25) PRG1_PRU1_GPO11.VOUT0_DATA11 */ + J721E_IOPAD(0x88, PIN_OUTPUT, 10) /* (AH25) PRG1_PRU1_GPO12.VOUT0_DATA12 */ + J721E_IOPAD(0x8c, PIN_OUTPUT, 10) /* (AG25) PRG1_PRU1_GPO13.VOUT0_DATA13 */ + J721E_IOPAD(0x90, PIN_OUTPUT, 10) /* (AH26) PRG1_PRU1_GPO14.VOUT0_DATA14 */ + J721E_IOPAD(0x94, PIN_OUTPUT, 10) /* (AJ27) PRG1_PRU1_GPO15.VOUT0_DATA15 */ + J721E_IOPAD(0x30, PIN_OUTPUT, 10) /* (AF24) PRG1_PRU0_GPO11.VOUT0_DATA16 */ + J721E_IOPAD(0x34, PIN_OUTPUT, 10) /* (AJ24) PRG1_PRU0_GPO12.VOUT0_DATA17 */ + J721E_IOPAD(0x38, PIN_OUTPUT, 10) /* (AG24) PRG1_PRU0_GPO13.VOUT0_DATA18 */ + J721E_IOPAD(0x3c, PIN_OUTPUT, 10) /* (AD24) PRG1_PRU0_GPO14.VOUT0_DATA19 */ + J721E_IOPAD(0x40, PIN_OUTPUT, 10) /* (AC24) PRG1_PRU0_GPO15.VOUT0_DATA20 */ + J721E_IOPAD(0x44, PIN_OUTPUT, 10) /* (AE24) PRG1_PRU0_GPO16.VOUT0_DATA21 */ + J721E_IOPAD(0x24, PIN_OUTPUT, 10) /* (AJ20) PRG1_PRU0_GPO8.VOUT0_DATA22 */ + J721E_IOPAD(0x28, PIN_OUTPUT, 10) /* (AG20) PRG1_PRU0_GPO9.VOUT0_DATA23 */ + J721E_IOPAD(0x9c, PIN_OUTPUT, 10) /* (AC22) PRG1_PRU1_GPO17.VOUT0_DE */ + J721E_IOPAD(0x98, PIN_OUTPUT, 10) /* (AJ26) PRG1_PRU1_GPO16.VOUT0_HSYNC */ + J721E_IOPAD(0xa4, PIN_OUTPUT, 10) /* (AH22) PRG1_PRU1_GPO19.VOUT0_PCLK */ + J721E_IOPAD(0xa0, PIN_OUTPUT, 10) /* (AJ22) PRG1_PRU1_GPO18.VOUT0_VSYNC */ + >; + }; + + hdmi_hpd_pins_default: hdmi-hpd-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x204, PIN_INPUT, 7) /* (AD5) UART1_RTSn.GPIO1_0 */ + >; + }; + + hdmi_pdn_pins_default: hdmi-pdn-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x200, PIN_INPUT, 7) /* (AC4) UART1_CTSn.GPIO0_127 */ + >; + }; + + /* Reset for M.2 E Key slot on PCIe0 */ + ekey_reset_pins_default: ekey-reset-pns-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x124, PIN_INPUT, 7) /* (Y24) PRG0_PRU1_GPO9.GPIO0_72 */ + >; + }; +}; + +&wkup_pmx0 { + mcu_cpsw_pins_default: mcu-cpsw-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0x84, PIN_INPUT, 0) /* (B24) MCU_RGMII1_RD0 */ + J721E_WKUP_IOPAD(0x80, PIN_INPUT, 0) /* (A24) MCU_RGMII1_RD1 */ + J721E_WKUP_IOPAD(0x7c, PIN_INPUT, 0) /* (D24) MCU_RGMII1_RD2 */ + J721E_WKUP_IOPAD(0x78, PIN_INPUT, 0) /* (A25) MCU_RGMII1_RD3 */ + J721E_WKUP_IOPAD(0x74, PIN_INPUT, 0) /* (C24) MCU_RGMII1_RXC */ + J721E_WKUP_IOPAD(0x5c, PIN_INPUT, 0) /* (C25) MCU_RGMII1_RX_CTL */ + J721E_WKUP_IOPAD(0x6c, PIN_OUTPUT, 0) /* (B25) MCU_RGMII1_TD0 */ + J721E_WKUP_IOPAD(0x68, PIN_OUTPUT, 0) /* (A26) MCU_RGMII1_TD1 */ + J721E_WKUP_IOPAD(0x64, PIN_OUTPUT, 0) /* (A27) MCU_RGMII1_TD2 */ + J721E_WKUP_IOPAD(0x60, PIN_OUTPUT, 0) /* (A28) MCU_RGMII1_TD3 */ + J721E_WKUP_IOPAD(0x70, PIN_OUTPUT, 0) /* (B26) MCU_RGMII1_TXC */ + J721E_WKUP_IOPAD(0x58, PIN_OUTPUT, 0) /* (B27) MCU_RGMII1_TX_CTL */ + >; + }; + + mcu_mdio_pins_default: mcu-mdio1-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0x8c, PIN_OUTPUT, 0) /* (F23) MCU_MDIO0_MDC */ + J721E_WKUP_IOPAD(0x88, PIN_INPUT, 0) /* (E23) MCU_MDIO0_MDIO */ + >; + }; + + mcu_fss0_ospi0_pins_default: mcu-fss0-ospi0-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0x0, PIN_OUTPUT, 0) /* (E20) MCU_OSPI0_CLK */ + J721E_WKUP_IOPAD(0x2c, PIN_OUTPUT, 0) /* (F19) MCU_OSPI0_CSn0 */ + J721E_WKUP_IOPAD(0xc, PIN_INPUT, 0) /* (D20) MCU_OSPI0_D0 */ + J721E_WKUP_IOPAD(0x10, PIN_INPUT, 0) /* (G19) MCU_OSPI0_D1 */ + J721E_WKUP_IOPAD(0x14, PIN_INPUT, 0) /* (G20) MCU_OSPI0_D2 */ + J721E_WKUP_IOPAD(0x18, PIN_INPUT, 0) /* (F20) MCU_OSPI0_D3 */ + J721E_WKUP_IOPAD(0x1c, PIN_INPUT, 0) /* (F21) MCU_OSPI0_D4 */ + J721E_WKUP_IOPAD(0x20, PIN_INPUT, 0) /* (E21) MCU_OSPI0_D5 */ + J721E_WKUP_IOPAD(0x24, PIN_INPUT, 0) /* (B22) MCU_OSPI0_D6 */ + J721E_WKUP_IOPAD(0x28, PIN_INPUT, 0) /* (G21) MCU_OSPI0_D7 */ + J721E_WKUP_IOPAD(0x8, PIN_INPUT, 0) /* (D21) MCU_OSPI0_DQS */ + >; + }; + + vdd_mmc1_en_pins_default: vdd-mmc1-en-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xd0, PIN_OUTPUT, 7) /* (G27) WKUP_GPIO0_8 */ + >; + }; + + vdd_sd_dv_alt_pins_default: vdd-sd-dv-alt-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xd4, PIN_OUTPUT, 7) /* (G26) WKUP_GPIO0_9 */ + >; + }; + + wkup_i2c0_pins_default: wkup-i2c0-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xf8, PIN_INPUT_PULLUP, 0) /* (J25) WKUP_I2C0_SCL */ + J721E_WKUP_IOPAD(0xfc, PIN_INPUT_PULLUP, 0) /* (H24) WKUP_I2C0_SDA */ + >; + }; + + mcu_mcan0_pins_default: mcu-mcan0-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xac, PIN_INPUT, 0) /* (C29) MCU_MCAN0_RX */ + J721E_WKUP_IOPAD(0xa8, PIN_OUTPUT, 0) /* (D29) MCU_MCAN0_TX */ + >; + }; + + mcu_mcan0_gpio_pins_default: mcu-mcan0-gpio-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xbc, PIN_INPUT, 7) /* (F27) WKUP_GPIO0_3 */ + >; + }; + + /* Reset for M.2 M Key slot on PCIe1 */ + mkey_reset_pins_default: mkey-reset-pns-pins-default { + pinctrl-single,pins = < + J721E_WKUP_IOPAD(0xdc, PIN_INPUT, 7) /* (H27) WKUP_GPIO0_11 */ + >; + }; +}; + +&wkup_uart0 { + /* Wakeup UART is used by System firmware */ + status = "reserved"; +}; + +&main_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_uart0_pins_default>; + /* Shared with ATF on this platform */ + power-domains = <&k3_pds 146 TI_SCI_PD_SHARED>; +}; + +&main_uart2 { + /* Brought out on RPi header */ + status = "disabled"; +}; + +&main_uart3 { + /* UART not brought out */ + status = "disabled"; +}; + +&main_uart5 { + /* UART not brought out */ + status = "disabled"; +}; + +&main_uart6 { + /* UART not brought out */ + status = "disabled"; +}; + +&main_uart7 { + /* UART not brought out */ + status = "disabled"; +}; + +&main_uart8 { + /* UART not brought out */ + status = "disabled"; +}; + +&main_uart9 { + /* Brought out on M.2 E Key */ + status = "disabled"; +}; + +&main_sdhci0 { + /* Unused */ + status = "disabled"; +}; + +&main_sdhci1 { + /* SD Card */ + vmmc-supply = <&vdd_mmc1>; + vqmmc-supply = <&vdd_sd_dv_alt>; + pinctrl-names = "default"; + pinctrl-0 = <&main_mmc1_pins_default>; + ti,driver-strength-ohm = <50>; + disable-wp; +}; + +&main_sdhci2 { + /* Unused */ + status = "disabled"; +}; + +&ospi0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_fss0_ospi0_pins_default>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + spi-max-frequency = <25000000>; + cdns,tshsl-ns = <60>; + cdns,tsd2d-ns = <60>; + cdns,tchsh-ns = <60>; + cdns,tslch-ns = <60>; + cdns,read-delay = <4>; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&ospi1 { + /* Unused */ + status = "disabled"; +}; + +&main_i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c0_pins_default>; + clock-frequency = <400000>; + + i2c-switch@71 { + compatible = "nxp,pca9543"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x71>; + + /* PCIe1 M.2 M Key I2C */ + pcie1_m2_i2c: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + /* PCIe0 M.2 E Key I2C */ + pcie0_m2_i2c: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; +}; + +&main_i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c1_pins_default>; + /* i2c1 is used for DVI DDC, so we need to use 100kHz */ + clock-frequency = <100000>; +}; + +&main_i2c2 { + /* Unused */ + status = "disabled"; +}; + +&main_i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c3_pins_default>; + clock-frequency = <400000>; + + i2c-switch@70 { + compatible = "nxp,pca9543"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + + /* CAM0 I2C */ + ti_cam0_i2c: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + /* CAM1 I2C */ + rpi_cam0_i2c: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; +}; + +&main_i2c4 { + /* Unused */ + status = "disabled"; +}; + +&main_i2c5 { + /* Brought out on RPi Header */ + status = "disabled"; +}; + +&main_i2c6 { + /* Unused */ + status = "disabled"; +}; + +&main_gpio2 { + status = "disabled"; +}; + +&main_gpio3 { + status = "disabled"; +}; + +&main_gpio4 { + status = "disabled"; +}; + +&main_gpio5 { + status = "disabled"; +}; + +&main_gpio6 { + status = "disabled"; +}; + +&main_gpio7 { + status = "disabled"; +}; + +&wkup_gpio1 { + status = "disabled"; +}; + +&usb_serdes_mux { + idle-states = <1>, <1>; /* USB0 to SERDES3, USB1 to SERDES2 */ +}; + +&serdes_ln_ctrl { + idle-states = , , + , , + , , + , , + , , + , ; +}; + +&serdes_wiz3 { + typec-dir-gpios = <&main_gpio1 3 GPIO_ACTIVE_HIGH>; + typec-dir-debounce-ms = <700>; /* TUSB321, tCCB_DEFAULT 133 ms */ +}; + +&serdes3 { + serdes3_usb_link: phy@0 { + reg = <0>; + cdns,num-lanes = <2>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz3 1>, <&serdes_wiz3 2>; + }; +}; + +&usbss0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_usbss0_pins_default>; + ti,vbus-divider; +}; + +&usb0 { + dr_mode = "otg"; + maximum-speed = "super-speed"; + phys = <&serdes3_usb_link>; + phy-names = "cdns3,usb3-phy"; +}; + +&serdes2 { + serdes2_usb_link: phy@1 { + reg = <1>; + cdns,num-lanes = <1>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz2 2>; + }; +}; + +&usbss1 { + pinctrl-names = "default"; + pinctrl-0 = <&main_usbss1_pins_default>; + ti,vbus-divider; +}; + +&usb1 { + dr_mode = "host"; + maximum-speed = "super-speed"; + phys = <&serdes2_usb_link>; + phy-names = "cdns3,usb3-phy"; +}; + +&tscadc0 { + /* Unused */ + status = "disabled"; +}; + +&tscadc1 { + /* Unused */ + status = "disabled"; +}; + +&mcu_cpsw { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_cpsw_pins_default &mcu_mdio_pins_default>; +}; + +&davinci_mdio { + phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-rxid"; + phy-handle = <&phy0>; +}; + +&dss { + pinctrl-names = "default"; + pinctrl-0 = <&dss_vout0_pins_default>; + + assigned-clocks = <&k3_clks 152 1>, /* VP 1 pixel clock */ + <&k3_clks 152 4>, /* VP 2 pixel clock */ + <&k3_clks 152 9>, /* VP 3 pixel clock */ + <&k3_clks 152 13>; /* VP 4 pixel clock */ + assigned-clock-parents = <&k3_clks 152 2>, /* PLL16_HSDIV0 */ + <&k3_clks 152 6>, /* DPI0_EXT_CLKSEL_OUT0 */ + <&k3_clks 152 11>, /* PLL18_HSDIV0 */ + <&k3_clks 152 18>; /* DPI1_EXT_CLKSEL_OUT0 */ +}; + +&dss_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* DP */ + port@0 { + reg = <0>; + + dpi0_out: endpoint { + remote-endpoint = <&dp0_in>; + }; + }; + + /* HDMI */ + port@1 { + reg = <1>; + + dpi_out0: endpoint { + remote-endpoint = <&tfp410_in>; + }; + }; +}; + +&mhdp { + pinctrl-names = "default"; + pinctrl-0 = <&dp0_pins_default>; +}; + +&dp0_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dp0_in: endpoint { + remote-endpoint = <&dpi0_out>; + }; + }; + + port@4 { + reg = <4>; + dp0_out: endpoint { + remote-endpoint = <&dp_connector_in>; + }; + }; +}; + +&mcasp0 { + /* Unused */ + status = "disabled"; +}; + +&mcasp1 { + /* Unused */ + status = "disabled"; +}; + +&mcasp2 { + /* Unused */ + status = "disabled"; +}; + +&mcasp3 { + /* Unused */ + status = "disabled"; +}; + +&mcasp4 { + /* Unused */ + status = "disabled"; +}; + +&mcasp5 { + /* Unused */ + status = "disabled"; +}; + +&mcasp6 { + /* Brought out on RPi header */ + status = "disabled"; +}; + +&mcasp7 { + /* Unused */ + status = "disabled"; +}; + +&mcasp8 { + /* Unused */ + status = "disabled"; +}; + +&mcasp9 { + /* Unused */ + status = "disabled"; +}; + +&mcasp10 { + /* Unused */ + status = "disabled"; +}; + +&mcasp11 { + /* Brought out on M.2 E Key */ + status = "disabled"; +}; + +&serdes0 { + serdes0_pcie_link: phy@0 { + reg = <0>; + cdns,num-lanes = <1>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz0 1>; + }; +}; + +&serdes1 { + serdes1_pcie_link: phy@0 { + reg = <0>; + cdns,num-lanes = <2>; + #phy-cells = <0>; + cdns,phy-type = ; + resets = <&serdes_wiz1 1>, <&serdes_wiz1 2>; + }; +}; + +&pcie0_rc { + pinctrl-names = "default"; + pinctrl-0 = <&ekey_reset_pins_default>; + reset-gpios = <&main_gpio0 72 GPIO_ACTIVE_HIGH>; + + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <1>; +}; + +&pcie1_rc { + pinctrl-names = "default"; + pinctrl-0 = <&mkey_reset_pins_default>; + reset-gpios = <&wkup_gpio0 11 GPIO_ACTIVE_HIGH>; + + phys = <&serdes1_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <2>; +}; + +&pcie2_rc { + /* Unused */ + status = "disabled"; +}; + +&pcie0_ep { + status = "disabled"; + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <1>; +}; + +&pcie1_ep { + status = "disabled"; + phys = <&serdes1_pcie_link>; + phy-names = "pcie-phy"; + num-lanes = <2>; +}; + +&pcie2_ep { + /* Unused */ + status = "disabled"; +}; + +&pcie3_rc { + /* Unused */ + status = "disabled"; +}; + +&pcie3_ep { + /* Unused */ + status = "disabled"; +}; + +&mcu_mcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_pins_default>; + phys = <&transceiver1>; +}; + +&mcu_mcan1 { + status = "disabled"; +}; + +&main_mcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan0_pins_default>; + phys = <&transceiver2>; +}; + +&main_mcan1 { + status = "disabled"; +}; + +&main_mcan2 { + status = "disabled"; +}; + +&main_mcan3 { + status = "disabled"; +}; + +&main_mcan4 { + status = "disabled"; +}; + +&main_mcan5 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan5_pins_default>; + phys = <&transceiver3>; +}; + +&main_mcan6 { + status = "disabled"; +}; + +&main_mcan7 { + status = "disabled"; +}; + +&main_mcan8 { + status = "disabled"; +}; + +&main_mcan9 { + pinctrl-names = "default"; + pinctrl-0 = <&main_mcan9_pins_default>; + phys = <&transceiver4>; +}; + +&main_mcan10 { + status = "disabled"; +}; + +&main_mcan11 { + status = "disabled"; +}; + +&main_mcan12 { + status = "disabled"; +}; + +&main_mcan13 { + status = "disabled"; +}; + +&ufs_wrapper { + status = "disabled"; +}; + +&main_r5fss0_core0{ + firmware-name = "pdk-ipc/ipc_echo_test_mcu2_0_release_strip.xer5f"; +}; + +&mailbox0_cluster0 { + interrupts = <436>; + + mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; + + mbox_mcu_r5fss0_core1: mbox-mcu-r5fss0-core1 { + ti,mbox-rx = <2 0 0>; + ti,mbox-tx = <3 0 0>; + }; +}; + +&mailbox0_cluster1 { + interrupts = <432>; + + mbox_main_r5fss0_core0: mbox-main-r5fss0-core0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; + + mbox_main_r5fss0_core1: mbox-main-r5fss0-core1 { + ti,mbox-rx = <2 0 0>; + ti,mbox-tx = <3 0 0>; + }; +}; + +&mailbox0_cluster2 { + interrupts = <428>; + + mbox_main_r5fss1_core0: mbox-main-r5fss1-core0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; + + mbox_main_r5fss1_core1: mbox-main-r5fss1-core1 { + ti,mbox-rx = <2 0 0>; + ti,mbox-tx = <3 0 0>; + }; +}; + +&mailbox0_cluster3 { + interrupts = <424>; + + mbox_c66_0: mbox-c66-0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; + + mbox_c66_1: mbox-c66-1 { + ti,mbox-rx = <2 0 0>; + ti,mbox-tx = <3 0 0>; + }; +}; + +&mailbox0_cluster4 { + interrupts = <420>; + + mbox_c71_0: mbox-c71-0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; +}; + +&mailbox0_cluster5 { + status = "disabled"; +}; + +&mailbox0_cluster6 { + status = "disabled"; +}; + +&mailbox0_cluster7 { + status = "disabled"; +}; + +&mailbox0_cluster8 { + status = "disabled"; +}; + +&mailbox0_cluster9 { + status = "disabled"; +}; + +&mailbox0_cluster10 { + status = "disabled"; +}; + +&mailbox0_cluster11 { + status = "disabled"; +}; + +&mcu_r5fss0_core0 { + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; + memory-region = <&mcu_r5fss0_core0_dma_memory_region>, + <&mcu_r5fss0_core0_memory_region>; +}; + +&mcu_r5fss0_core1 { + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; + memory-region = <&mcu_r5fss0_core1_dma_memory_region>, + <&mcu_r5fss0_core1_memory_region>; +}; + +&main_r5fss0_core0 { + mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; + memory-region = <&main_r5fss0_core0_dma_memory_region>, + <&main_r5fss0_core0_memory_region>; +}; + +&main_r5fss0_core1 { + mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; + memory-region = <&main_r5fss0_core1_dma_memory_region>, + <&main_r5fss0_core1_memory_region>; +}; + +&main_r5fss1_core0 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core0>; + memory-region = <&main_r5fss1_core0_dma_memory_region>, + <&main_r5fss1_core0_memory_region>; +}; + +&main_r5fss1_core1 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core1>; + memory-region = <&main_r5fss1_core1_dma_memory_region>, + <&main_r5fss1_core1_memory_region>; +}; + +&c66_0 { + mboxes = <&mailbox0_cluster3 &mbox_c66_0>; + memory-region = <&c66_0_dma_memory_region>, + <&c66_0_memory_region>; +}; + +&c66_1 { + mboxes = <&mailbox0_cluster3 &mbox_c66_1>; + memory-region = <&c66_1_dma_memory_region>, + <&c66_1_memory_region>; +}; + +&c71_0 { + mboxes = <&mailbox0_cluster4 &mbox_c71_0>; + memory-region = <&c71_0_dma_memory_region>, + <&c71_0_memory_region>; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-sk-rpi-cam-imx219.dts b/arch/arm64/boot/dts/ti/k3-j721e-sk-rpi-cam-imx219.dts --- a/arch/arm64/boot/dts/ti/k3-j721e-sk-rpi-cam-imx219.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-sk-rpi-cam-imx219.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * DT Overlay for RPi Camera V2.1 (Sony IMX219) interfaced with CSI2 on J721E-EAIK board. + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_imx219_fixed: imx219-xclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; + }; +}; + +&main_pmx0 { + main_rpi_cam0_reset_pins_default: main-rpi-cam0-reset-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1D4, PIN_OUTPUT, 7) /* (Y3) SPI1_CS0 */ + >; + }; + + main_rpi_cam1_reset_pins_default: main-rpi-cam1-reset-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x1E0, PIN_OUTPUT, 7) /* (Y5) SPI1_D0 */ + >; + }; + + main_csi_mux_sel2_pins_default: main-csi-mux-sel2-pins-default { + pinctrl-single,pins = < + J721E_IOPAD(0x164, PIN_OUTPUT, 7) /* (V29) RGMII5_TD2 */ + >; + }; +}; + +&main_gpio0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_csi_mux_sel2_pins_default>; + + csi-mux-hog { + /* CSI_MUX_SEL_2 */ + gpio-hog; + gpios = <88 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "CSI_MUX_SEL_2"; + }; +}; + +&main_i2c3 { + #address-cells = <1>; + #size-cells = <0>; + + i2c-switch@70 { + compatible = "nxp,pca9543"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + + i2c-alias-pool = /bits/ 16 <0x10 0x11>; + + /* CAM0 I2C */ + cam0_i2c: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + imx219_0: imx219_0@10 { + compatible = "sony,imx219"; + reg = <0x10>; + + clocks = <&clk_imx219_fixed>; + clock-names = "xclk"; + + pinctrl-names = "default"; + pinctrl-0 = <&main_rpi_cam0_reset_pins_default>; + reset-gpios = <&main_gpio0 116 GPIO_ACTIVE_HIGH>; + + port { + csi2_cam0: endpoint { + remote-endpoint = <&csi2rx0_in_sensor>; + link-frequencies = /bits/ 64 <456000000>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; + }; + + /* CAM1 I2C */ + cam1_i2c: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + imx219_1: imx219_1@10 { + compatible = "sony,imx219"; + reg = <0x10>; + + clocks = <&clk_imx219_fixed>; + clock-names = "xclk"; + + pinctrl-names = "default"; + pinctrl-0 = <&main_rpi_cam1_reset_pins_default>; + reset-gpios = <&main_gpio0 119 GPIO_ACTIVE_HIGH>; + + port { + csi2_cam1: endpoint { + remote-endpoint = <&csi2rx1_in_sensor>; + link-frequencies = /bits/ 64 <456000000>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; + }; + }; +}; + +&csi0_port0 { + csi2rx0_in_sensor: endpoint { + remote-endpoint = <&csi2_cam0>; + bus-type = <4>; /* CSI2 DPHY. */ + clock-lanes = <0>; + data-lanes = <1 2>; + }; +}; + +&csi1_port0 { + csi2rx1_in_sensor: endpoint { + remote-endpoint = <&csi2_cam1>; + bus-type = <4>; /* CSI2 DPHY. */ + clock-lanes = <0>; + data-lanes = <1 2>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi b/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2019-2020 Texas Instruments Incorporated - https://www.ti.com/ */ /dts-v1/; @@ -26,6 +26,78 @@ no-map; }; + mcu_r5fss0_core0_dma_memory_region: r5f-dma-memory@a0000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0000000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core0_memory_region: r5f-memory@a0100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0100000 0x00 0xf00000>; + no-map; + }; + + mcu_r5fss0_core1_dma_memory_region: r5f-dma-memory@a1000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1000000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core1_memory_region: r5f-memory@a1100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core0_dma_memory_region: r5f-dma-memory@a2000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core0_memory_region: r5f-memory@a2100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa2100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss0_core1_dma_memory_region: r5f-dma-memory@a3000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3000000 0x00 0x100000>; + no-map; + }; + + main_r5fss0_core1_memory_region: r5f-memory@a3100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa3100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core0_dma_memory_region: r5f-dma-memory@a4000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core0_memory_region: r5f-memory@a4100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa4100000 0x00 0xf00000>; + no-map; + }; + + main_r5fss1_core1_dma_memory_region: r5f-dma-memory@a5000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa5000000 0x00 0x100000>; + no-map; + }; + + main_r5fss1_core1_memory_region: r5f-memory@a5100000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa5100000 0x00 0xf00000>; + no-map; + }; + c66_1_dma_memory_region: c66-dma-memory@a6000000 { compatible = "shared-dma-pool"; reg = <0x00 0xa6000000 0x00 0x100000>; @@ -67,6 +139,18 @@ alignment = <0x1000>; no-map; }; + + main_r5fss0_core0_shared_memory_queue_region:r5f-virtual-eth-queues@ac000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xac000000 0x00 0x200000>; + no-map; + }; + + main_r5fss0_core0_shared_memory_bufpool_region:r5f-virtual-eth-buffers@ac200000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xac200000 0x00 0x1e00000>; + no-map; + }; }; }; @@ -102,9 +186,9 @@ flash@0{ compatible = "jedec,spi-nor"; reg = <0x0>; - spi-tx-bus-width = <1>; + spi-tx-bus-width = <8>; spi-rx-bus-width = <8>; - spi-max-frequency = <40000000>; + spi-max-frequency = <25000000>; cdns,tshsl-ns = <60>; cdns,tsd2d-ns = <60>; cdns,tchsh-ns = <60>; @@ -208,6 +292,44 @@ status = "disabled"; }; +&mcu_r5fss0_core0 { + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; + memory-region = <&mcu_r5fss0_core0_dma_memory_region>, + <&mcu_r5fss0_core0_memory_region>; +}; + +&mcu_r5fss0_core1 { + mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; + memory-region = <&mcu_r5fss0_core1_dma_memory_region>, + <&mcu_r5fss0_core1_memory_region>; +}; + +&main_r5fss0_core0 { + mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; + memory-region = <&main_r5fss0_core0_dma_memory_region>, + <&main_r5fss0_core0_memory_region>, + <&main_r5fss0_core0_shared_memory_queue_region>, + <&main_r5fss0_core0_shared_memory_bufpool_region>; +}; + +&main_r5fss0_core1 { + mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; + memory-region = <&main_r5fss0_core1_dma_memory_region>, + <&main_r5fss0_core1_memory_region>; +}; + +&main_r5fss1_core0 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core0>; + memory-region = <&main_r5fss1_core0_dma_memory_region>, + <&main_r5fss1_core0_memory_region>; +}; + +&main_r5fss1_core1 { + mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core1>; + memory-region = <&main_r5fss1_core1_dma_memory_region>, + <&main_r5fss1_core1_memory_region>; +}; + &c66_0 { mboxes = <&mailbox0_cluster3 &mbox_c66_0>; memory-region = <&c66_0_dma_memory_region>, diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts --- a/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * + * Common Processor Board: https://www.ti.com/tool/J721EXCPXEVM + */ + +/dts-v1/; + +#include "k3-j721s2-som-p0.dtsi" +#include + +/ { + compatible = "ti,j721s2-evm", "ti,j721s2"; + model = "Texas Instruments J721S2 EVM"; + + chosen { + stdout-path = "serial10:115200n8"; + bootargs = "console=ttyS10,115200n8 earlycon=ns16550a,mmio32,2880000"; + }; + + evm_12v0: fixedregulator-evm12v0 { + /* main supply */ + compatible = "regulator-fixed"; + regulator-name = "evm_12v0"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_3v3: fixedregulator-vsys3v3 { + /* Output of LM5140 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&evm_12v0>; + regulator-always-on; + regulator-boot-on; + }; + + vsys_5v0: fixedregulator-vsys5v0 { + /* Output of LM5140 */ + compatible = "regulator-fixed"; + regulator-name = "vsys_5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&evm_12v0>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_mmc1: fixedregulator-sd { + /* Output of TPS22918 */ + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + vin-supply = <&vsys_3v3>; + gpio = <&exp2 2 GPIO_ACTIVE_HIGH>; + }; + + vdd_sd_dv: gpio-regulator-TLV71033 { + /* Output of TLV71033 */ + compatible = "regulator-gpio"; + regulator-name = "tlv71033"; + pinctrl-names = "default"; + pinctrl-0 = <&vdd_sd_dv_pins_default>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + vin-supply = <&vsys_5v0>; + gpios = <&main_gpio0 8 GPIO_ACTIVE_HIGH>; + states = <1800000 0x0>, + <3300000 0x1>; + }; + + transceiver1: can-phy1 { + compatible = "ti,tcan1043"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_gpio_pins_default>; + standby-gpios = <&wkup_gpio0 69 GPIO_ACTIVE_LOW>; + enable-gpios = <&wkup_gpio0 0 GPIO_ACTIVE_HIGH>; + }; + + transceiver2: can-phy2 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan1_gpio_pins_default>; + standby-gpios = <&wkup_gpio0 2 GPIO_ACTIVE_HIGH>; + }; + +}; + +&main_pmx0 { + main_uart8_pins_default: main-uart8-pins-default { + pinctrl-single,pins = < + J721S2_IOPAD(0x040, PIN_INPUT, 14) /* (AC28) MCASP0_AXR0.UART8_CTSn */ + J721S2_IOPAD(0x044, PIN_OUTPUT, 14) /* (Y26) MCASP0_AXR1.UART8_RTSn */ + J721S2_IOPAD(0x0d0, PIN_INPUT, 11) /* (AF26) SPI0_CS1.UART8_RXD */ + J721S2_IOPAD(0x0d4, PIN_OUTPUT, 11) /* (AH27) SPI0_CLK.UART8_TXD */ + >; + }; + + main_i2c3_pins_default: main-i2c3-pins-default { + pinctrl-single,pins = < + J721S2_IOPAD(0x064, PIN_INPUT_PULLUP, 13) /* (W28) MCAN0_TX.I2C3_SCL */ + J721S2_IOPAD(0x060, PIN_INPUT_PULLUP, 13) /* (AC27) MCASP2_AXR1.I2C3_SDA */ + >; + }; + + main_mmc1_pins_default: main-mmc1-pins-default { + pinctrl-single,pins = < + J721S2_IOPAD(0x104, PIN_INPUT, 0) /* (P23) MMC1_CLK */ + J721S2_IOPAD(0x108, PIN_INPUT, 0) /* (N24) MMC1_CMD */ + J721S2_IOPAD(0x100, PIN_INPUT, 0) /* (###) MMC1_CLKLB */ + J721S2_IOPAD(0x0fc, PIN_INPUT, 0) /* (M23) MMC1_DAT0 */ + J721S2_IOPAD(0x0f8, PIN_INPUT, 0) /* (P24) MMC1_DAT1 */ + J721S2_IOPAD(0x0f4, PIN_INPUT, 0) /* (R24) MMC1_DAT2 */ + J721S2_IOPAD(0x0f0, PIN_INPUT, 0) /* (R22) MMC1_DAT3 */ + J721S2_IOPAD(0x0e8, PIN_INPUT, 8) /* (AE25) TIMER_IO0.MMC1_SDCD */ + >; + }; + + vdd_sd_dv_pins_default: vdd-sd-dv-pins-default { + pinctrl-single,pins = < + J721S2_IOPAD(0x020, PIN_INPUT, 7) /* (AA23) MCAN15_RX.GPIO0_8 */ + >; + }; +}; + +&wkup_pmx0 { + mcu_cpsw_pins_default: mcu-cpsw-pins-default { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x094, PIN_INPUT, 0) /* (B22) MCU_RGMII1_RD0 */ + J721S2_WKUP_IOPAD(0x090, PIN_INPUT, 0) /* (B21) MCU_RGMII1_RD1 */ + J721S2_WKUP_IOPAD(0x08c, PIN_INPUT, 0) /* (C22) MCU_RGMII1_RD2 */ + J721S2_WKUP_IOPAD(0x088, PIN_INPUT, 0) /* (D23) MCU_RGMII1_RD3 */ + J721S2_WKUP_IOPAD(0x084, PIN_INPUT, 0) /* (D22) MCU_RGMII1_RXC */ + J721S2_WKUP_IOPAD(0x06c, PIN_INPUT, 0) /* (E23) MCU_RGMII1_RX_CTL */ + J721S2_WKUP_IOPAD(0x07c, PIN_OUTPUT, 0) /* (F23) MCU_RGMII1_TD0 */ + J721S2_WKUP_IOPAD(0x078, PIN_OUTPUT, 0) /* (G22) MCU_RGMII1_TD1 */ + J721S2_WKUP_IOPAD(0x074, PIN_OUTPUT, 0) /* (E21) MCU_RGMII1_TD2 */ + J721S2_WKUP_IOPAD(0x070, PIN_OUTPUT, 0) /* (E22) MCU_RGMII1_TD3 */ + J721S2_WKUP_IOPAD(0x080, PIN_OUTPUT, 0) /* (F21) MCU_RGMII1_TXC */ + J721S2_WKUP_IOPAD(0x068, PIN_OUTPUT, 0) /* (F22) MCU_RGMII1_TX_CTL */ + >; + }; + + mcu_mdio_pins_default: mcu-mdio-pins-default { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x09c, PIN_OUTPUT, 0) /* (A21) MCU_MDIO0_MDC */ + J721S2_WKUP_IOPAD(0x098, PIN_INPUT, 0) /* (A22) MCU_MDIO0_MDIO */ + >; + }; + + mcu_mcan0_pins_default: mcu-mcan0-pins-default { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x0bc, PIN_INPUT, 0) /* (E28) MCU_MCAN0_RX */ + J721S2_WKUP_IOPAD(0x0b8, PIN_OUTPUT, 0) /* (E27) MCU_MCAN0_TX */ + >; + }; + + mcu_mcan1_pins_default: mcu-mcan1-pins-default { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x0d4, PIN_INPUT, 0) /* (F26) WKUP_GPIO0_5.MCU_MCAN1_RX */ + J721S2_WKUP_IOPAD(0x0d0, PIN_OUTPUT, 0) /* (C23) WKUP_GPIO0_4.MCU_MCAN1_TX */ + >; + }; + + mcu_mcan0_gpio_pins_default: mcu-mcan0-gpio-pins-default { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x0c0, PIN_INPUT, 7) /* (D26) WKUP_GPIO0_0 */ + J721S2_WKUP_IOPAD(0x0a8, PIN_INPUT, 7) /* (B25) MCU_SPI0_D1.WKUP_GPIO0_69 */ + >; + }; + + mcu_mcan1_gpio_pins_default: mcu-mcan1-gpio-pins-default { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x0c8, PIN_INPUT, 7) /* (C28) WKUP_GPIO0_2 */ + >; + }; +}; + +&main_gpio2 { + status = "disabled"; +}; + +&main_gpio4 { + status = "disabled"; +}; + +&main_gpio6 { + status = "disabled"; +}; + +&wkup_gpio1 { + status = "disabled"; +}; + +&wkup_uart0 { + status = "reserved"; +}; + +&main_uart0 { + status = "disabled"; +}; + +&main_uart1 { + status = "disabled"; +}; + +&main_uart2 { + status = "disabled"; +}; + +&main_uart3 { + status = "disabled"; +}; + +&main_uart4 { + status = "disabled"; +}; + +&main_uart5 { + status = "disabled"; +}; + +&main_uart6 { + status = "disabled"; +}; + +&main_uart7 { + status = "disabled"; +}; + +&main_uart8 { + pinctrl-names = "default"; + pinctrl-0 = <&main_uart8_pins_default>; + /* Shared with TFA on this platform */ + power-domains = <&k3_pds 357 TI_SCI_PD_SHARED>; +}; + +&main_uart9 { + status = "disabled"; +}; + +&main_i2c0 { + clock-frequency = <400000>; + + exp1: gpio@20 { + compatible = "ti,tca6416"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "PCIE_2L_MODE_SEL", "PCIE_2L_PERSTZ", "PCIE_2L_RC_RSTZ", + "PCIE_2L_EP_RST_EN", "PCIE_1L_MODE_SEL", "PCIE_1L_PERSTZ", + "PCIE_1L_RC_RSTZ", "PCIE_1L_EP_RST_EN", "PCIE_2L_PRSNT#", + "PCIE_1L_PRSNT#", "CDCI1_OE1/OE4", "CDCI1_OE2/OE3", "EXP_MUX1", + "EXP_MUX2", "EXP_MUX3", "GESI_EXP_PHY_RSTz"; + }; + + exp2: gpio@22 { + compatible = "ti,tca6424"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "APPLE_AUTH_RSTZ", "MLB_RSTZ", "GPIO_USD_PWR_EN", "USBC_PWR_EN", + "USBC_MODE_SEL1", "USBC_MODE_SEL0", "MCAN0_EN", "MCAN0_STB#", + "MUX_SPAREMUX_SPARE", "MCASP/TRACE_MUX_S0", "MCASP/TRACE_MUX_S1", + "MLB_MUX_SEL", "MCAN_MUX_SEL", "MCASP2/SPI3_MUX_SEL", "PCIe_CLKREQn_MUX_SEL", + "CDCI2_RSTZ", "ENET_EXP_PWRDN", "ENET_EXP_RESETZ", "ENET_I2CMUX_SEL", + "ENET_EXP_SPARE2", "M2PCIE_RTSZ", "USER_INPUT1", "USER_LED1", "USER_LED2"; + }; +}; + +&main_i2c1 { + status = "disabled"; +}; + +&main_i2c2 { + status = "disabled"; +}; + +&main_i2c3 { + status = "disabled"; +}; + +&main_i2c4 { + status = "disabled"; +}; + +&main_i2c5 { + status = "disabled"; +}; + +&main_i2c6 { + status = "disabled"; +}; + +&main_sdhci0 { + /* eMMC */ + non-removable; + ti,driver-strength-ohm = <50>; + disable-wp; +}; + +&main_sdhci1 { + /* SD card */ + pinctrl-0 = <&main_mmc1_pins_default>; + pinctrl-names = "default"; + disable-wp; + vmmc-supply = <&vdd_mmc1>; + vqmmc-supply = <&vdd_sd_dv>; +}; + +&mcu_cpsw { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_cpsw_pins_default &mcu_mdio_pins_default>; +}; + +&davinci_mdio { + phy0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + ti,min-output-impedance; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-rxid"; + phy-handle = <&phy0>; +}; + +&mcu_mcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan0_pins_default>; + phys = <&transceiver1>; +}; + +&mcu_mcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_mcan1_pins_default>; + phys = <&transceiver2>; +}; + +&main_mcan0 { + status = "disabled"; +}; + +&main_mcan1 { + status = "disabled"; +}; + +&main_mcan2 { + status = "disabled"; +}; + +&main_mcan3 { + status = "disabled"; +}; + +&main_mcan4 { + status = "disabled"; +}; + +&main_mcan5 { + status = "disabled"; +}; + +&main_mcan6 { + status = "disabled"; +}; + +&main_mcan7 { + status = "disabled"; +}; + +&main_mcan8 { + status = "disabled"; +}; + +&main_mcan9 { + status = "disabled"; +}; + +&main_mcan10 { + status = "disabled"; +}; + +&main_mcan11 { + status = "disabled"; +}; + +&main_mcan12 { + status = "disabled"; +}; + +&main_mcan13 { + status = "disabled"; +}; + +&main_mcan14 { + status = "disabled"; +}; + +&main_mcan15 { + status = "disabled"; +}; + +&main_mcan17 { + status = "disabled"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721s2.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721s2.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721s2.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for J721S2 SoC Family + * + * TRM (SPRUJ28 – NOVEMBER 2021) : http://www.ti.com/lit/pdf/spruj28 + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include +#include + +/ { + + model = "Texas Instruments K3 J721S2 SoC"; + compatible = "ti,j721s2"; + interrupt-parent = <&gic500>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &wkup_uart0; + serial1 = &mcu_uart0; + serial2 = &main_uart0; + serial3 = &main_uart1; + serial4 = &main_uart2; + serial5 = &main_uart3; + serial6 = &main_uart4; + serial7 = &main_uart5; + serial8 = &main_uart6; + serial9 = &main_uart7; + serial10 = &main_uart8; + serial11 = &main_uart9; + mmc0 = &main_sdhci0; + mmc1 = &main_sdhci1; + can0 = &main_mcan16; + can1 = &mcu_mcan0; + can2 = &mcu_mcan1; + can3 = &main_mcan3; + can4 = &main_mcan5; + }; + + chosen { }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu-map { + cluster0: cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + }; + }; + + cpu0: cpu@0 { + compatible = "arm,cortex-a72"; + reg = <0x000>; + device_type = "cpu"; + enable-method = "psci"; + i-cache-size = <0xc000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <256>; + next-level-cache = <&L2_0>; + }; + + cpu1: cpu@1 { + compatible = "arm,cortex-a72"; + reg = <0x001>; + device_type = "cpu"; + enable-method = "psci"; + i-cache-size = <0xc000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <256>; + next-level-cache = <&L2_0>; + }; + }; + + L2_0: l2-cache0 { + compatible = "cache"; + cache-level = <2>; + cache-size = <0x100000>; + cache-line-size = <64>; + cache-sets = <1024>; + next-level-cache = <&msmc_l3>; + }; + + msmc_l3: l3-cache0 { + compatible = "cache"; + cache-level = <3>; + }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + + psci: psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + }; + + a72_timer0: timer-cl0-cpu0 { + compatible = "arm,armv8-timer"; + interrupts = , /* cntpsirq */ + , /* cntpnsirq */ + , /* cntvirq */ + ; /* cnthpirq */ + + }; + + pmu: pmu { + compatible = "arm,cortex-a72-pmu"; + /* Recommendation from GIC500 TRM Table A.3 */ + interrupts = ; + }; + + cbass_main: bus@100000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x00100000 0x00 0x00100000 0x00 0x00020000>, /* ctrl mmr */ + <0x00 0x00600000 0x00 0x00600000 0x00 0x00031100>, /* GPIO */ + <0x00 0x01000000 0x00 0x01000000 0x00 0x0d000000>, /* Most peripherals */ + <0x00 0x0d800000 0x00 0x0d800000 0x00 0x00800000>, /* PCIe Core*/ + <0x00 0x18000000 0x00 0x18000000 0x00 0x08000000>, /* PCIe1 DAT0 */ + <0x00 0x64800000 0x00 0x64800000 0x00 0x0070c000>, /* C71_1 */ + <0x00 0x65800000 0x00 0x65800000 0x00 0x0070c000>, /* C71_2 */ + <0x00 0x70000000 0x00 0x70000000 0x00 0x00400000>, /* MSMC RAM */ + <0x00 0x30000000 0x00 0x30000000 0x00 0x0c400000>, /* MAIN NAVSS */ + <0x41 0x00000000 0x41 0x00000000 0x01 0x00000000>, /* PCIe1 DAT1 */ + <0x4e 0x20000000 0x4e 0x20000000 0x00 0x00080000>, /* GPU */ + + /* MCUSS_WKUP Range */ + <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>, + <0x00 0x40200000 0x00 0x40200000 0x00 0x00998400>, + <0x00 0x40f00000 0x00 0x40f00000 0x00 0x00020000>, + <0x00 0x41000000 0x00 0x41000000 0x00 0x00020000>, + <0x00 0x41400000 0x00 0x41400000 0x00 0x00020000>, + <0x00 0x41c00000 0x00 0x41c00000 0x00 0x00100000>, + <0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>, + <0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>, + <0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>, + <0x00 0x47000000 0x00 0x47000000 0x00 0x00068400>, + <0x00 0x50000000 0x00 0x50000000 0x00 0x10000000>, + <0x05 0x00000000 0x05 0x00000000 0x01 0x00000000>, + <0x07 0x00000000 0x07 0x00000000 0x01 0x00000000>; + + cbass_mcu_wakeup: bus@28380000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>, /* MCU NAVSS*/ + <0x00 0x40200000 0x00 0x40200000 0x00 0x00998400>, /* First peripheral window */ + <0x00 0x40f00000 0x00 0x40f00000 0x00 0x00020000>, /* CTRL_MMR0 */ + <0x00 0x41000000 0x00 0x41000000 0x00 0x00020000>, /* MCU R5F Core0 */ + <0x00 0x41400000 0x00 0x41400000 0x00 0x00020000>, /* MCU R5F Core1 */ + <0x00 0x41c00000 0x00 0x41c00000 0x00 0x00100000>, /* MCU SRAM */ + <0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>, /* WKUP peripheral window */ + <0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>, /* MMRs, remaining NAVSS */ + <0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>, /* CPSW */ + <0x00 0x47000000 0x00 0x47000000 0x00 0x00068400>, /* OSPI register space */ + <0x00 0x50000000 0x00 0x50000000 0x00 0x10000000>, /* FSS OSPI0/1 data region 0 */ + <0x05 0x00000000 0x05 0x00000000 0x01 0x00000000>, /* FSS OSPI0 data region 3 */ + <0x07 0x00000000 0x07 0x00000000 0x01 0x00000000>; /* FSS OSPI1 data region 3*/ + + }; + + }; +}; + +/* Now include peripherals from each bus segment */ +#include "k3-j721s2-main.dtsi" +#include "k3-j721s2-mcu-wakeup.dtsi" diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,937 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for J721S2 SoC Family Main Domain peripherals + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&cbass_main { + msmc_ram: sram@70000000 { + compatible = "mmio-sram"; + reg = <0x0 0x70000000 0x0 0x400000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x70000000 0x400000>; + + atf-sram@0 { + reg = <0x0 0x20000>; + }; + + tifs-sram@1f0000 { + reg = <0x1f0000 0x10000>; + }; + + l3cache-sram@200000 { + reg = <0x200000 0x200000>; + }; + }; + + gic500: interrupt-controller@1800000 { + compatible = "arm,gic-v3"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x00 0x01800000 0x00 0x200000>, /* GICD */ + <0x00 0x01900000 0x00 0x100000>; /* GICR */ + + /* vcpumntirq: virtual CPU interface maintenance interrupt */ + interrupts = ; + + gic_its: msi-controller@1820000 { + compatible = "arm,gic-v3-its"; + reg = <0x00 0x01820000 0x00 0x10000>; + socionext,synquacer-pre-its = <0x1000000 0x400000>; + msi-controller; + #msi-cells = <1>; + }; + }; + + main_gpio_intr: interrupt-controller@a00000 { + compatible = "ti,sci-intr"; + reg = <0x00 0x00a00000 0x00 0x800>; + ti,intr-trigger-type = <1>; + interrupt-controller; + interrupt-parent = <&gic500>; + #interrupt-cells = <1>; + ti,sci = <&sms>; + ti,sci-dev-id = <148>; + ti,interrupt-ranges = <8 360 56>; + }; + + main_pmx0: pinctrl@11c000 { + compatible = "pinctrl-single"; + /* Proxy 0 addressing */ + reg = <0x0 0x11c000 0x0 0x120>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0xffffffff>; + }; + + main_uart0: serial@2800000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02800000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 146 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 146 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart1: serial@2810000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02810000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 350 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 350 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart2: serial@2820000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02820000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 351 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 351 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart3: serial@2830000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02830000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 352 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 352 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart4: serial@2840000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02840000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 353 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 353 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart5: serial@2850000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02850000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 354 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 354 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart6: serial@2860000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02860000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 355 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 355 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart7: serial@2870000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02870000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 356 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 356 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart8: serial@2880000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02880000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 357 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 357 TI_SCI_PD_EXCLUSIVE>; + }; + + main_uart9: serial@2890000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x02890000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 358 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 358 TI_SCI_PD_EXCLUSIVE>; + }; + + main_gpio0: gpio@600000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00600000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <145>, <146>, <147>, <148>, <149>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <66>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 111 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 111 0>; + clock-names = "gpio"; + }; + + main_gpio2: gpio@610000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00610000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <154>, <155>, <156>, <157>, <158>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <66>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 112 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 112 0>; + clock-names = "gpio"; + }; + + main_gpio4: gpio@620000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00620000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <163>, <164>, <165>, <166>, <167>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <66>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 113 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 113 0>; + clock-names = "gpio"; + }; + + main_gpio6: gpio@630000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x00630000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <172>, <173>, <174>, <175>, <176>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <66>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 114 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 114 0>; + clock-names = "gpio"; + }; + + main_i2c0: i2c@2000000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02000000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 214 1>; + clock-names = "fck"; + power-domains = <&k3_pds 214 TI_SCI_PD_EXCLUSIVE>; + }; + + main_i2c1: i2c@2010000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02010000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 215 1>; + clock-names = "fck"; + power-domains = <&k3_pds 215 TI_SCI_PD_EXCLUSIVE>; + }; + + main_i2c2: i2c@2020000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02020000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 216 1>; + clock-names = "fck"; + power-domains = <&k3_pds 216 TI_SCI_PD_EXCLUSIVE>; + }; + + main_i2c3: i2c@2030000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02030000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 217 1>; + clock-names = "fck"; + power-domains = <&k3_pds 217 TI_SCI_PD_EXCLUSIVE>; + }; + + main_i2c4: i2c@2040000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02040000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 218 1>; + clock-names = "fck"; + power-domains = <&k3_pds 218 TI_SCI_PD_EXCLUSIVE>; + }; + + main_i2c5: i2c@2050000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02050000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 219 1>; + clock-names = "fck"; + power-domains = <&k3_pds 219 TI_SCI_PD_EXCLUSIVE>; + }; + + main_i2c6: i2c@2060000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x02060000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 220 1>; + clock-names = "fck"; + power-domains = <&k3_pds 220 TI_SCI_PD_EXCLUSIVE>; + }; + + main_sdhci0: mmc@4f80000 { + compatible = "ti,j721e-sdhci-8bit"; + reg = <0x00 0x04f80000 0x00 0x1000>, + <0x00 0x04f88000 0x00 0x400>; + interrupts = ; + power-domains = <&k3_pds 98 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 98 7>, <&k3_clks 98 1>; + clock-names = "clk_ahb", "clk_xin"; + assigned-clocks = <&k3_clks 98 1>; + assigned-clock-parents = <&k3_clks 98 2>; + bus-width = <8>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-mmc-hs = <0x0>; + ti,otap-del-sel-ddr52 = <0x6>; + ti,otap-del-sel-hs200 = <0x8>; + ti,otap-del-sel-hs400 = <0x5>; + ti,itap-del-sel-legacy = <0x10>; + ti,itap-del-sel-mmc-hs = <0xa>; + ti,strobe-sel = <0x77>; + ti,clkbuf-sel = <0x7>; + ti,trm-icp = <0x8>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + dma-coherent; + }; + + main_sdhci1: mmc@4fb0000 { + compatible = "ti,j721e-sdhci-4bit"; + reg = <0x00 0x04fb0000 0x00 0x1000>, + <0x00 0x04fb8000 0x00 0x400>; + interrupts = ; + power-domains = <&k3_pds 99 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 99 8>, <&k3_clks 99 1>; + clock-names = "clk_ahb", "clk_xin"; + assigned-clocks = <&k3_clks 99 1>; + assigned-clock-parents = <&k3_clks 99 2>; + bus-width = <4>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-sd-hs = <0x0>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-sdr104 = <0x5>; + ti,otap-del-sel-ddr50 = <0xc>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-sd-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + ti,clkbuf-sel = <0x7>; + ti,trm-icp = <0x8>; + dma-coherent; + /* Masking support for SDR104 capability */ + sdhci-caps-mask = <0x00000003 0x00000000>; + }; + + main_navss: bus@30000000 { + compatible = "simple-mfd"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x30000000 0x00 0x30000000 0x00 0x0c400000>; + ti,sci-dev-id = <224>; + dma-coherent; + dma-ranges; + + main_navss_intr: interrupt-controller@310e0000 { + compatible = "ti,sci-intr"; + reg = <0x00 0x310e0000 0x00 0x4000>; + ti,intr-trigger-type = <4>; + interrupt-controller; + interrupt-parent = <&gic500>; + #interrupt-cells = <1>; + ti,sci = <&sms>; + ti,sci-dev-id = <227>; + ti,interrupt-ranges = <0 64 64>, + <64 448 64>, + <128 672 64>; + }; + + main_udmass_inta: msi-controller@33d00000 { + compatible = "ti,sci-inta"; + reg = <0x00 0x33d00000 0x00 0x100000>; + interrupt-controller; + #interrupt-cells = <0>; + interrupt-parent = <&main_navss_intr>; + msi-controller; + ti,sci = <&sms>; + ti,sci-dev-id = <265>; + ti,interrupt-ranges = <0 0 256>; + }; + + secure_proxy_main: mailbox@32c00000 { + compatible = "ti,am654-secure-proxy"; + #mbox-cells = <1>; + reg-names = "target_data", "rt", "scfg"; + reg = <0x00 0x32c00000 0x00 0x100000>, + <0x00 0x32400000 0x00 0x100000>, + <0x00 0x32800000 0x00 0x100000>; + interrupt-names = "rx_011"; + interrupts = ; + }; + + hwspinlock: spinlock@30e00000 { + compatible = "ti,am654-hwspinlock"; + reg = <0x00 0x30e00000 0x00 0x1000>; + #hwlock-cells = <1>; + }; + + mailbox0_cluster0: mailbox@31f80000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f80000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster1: mailbox@31f81000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f81000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster2: mailbox@31f82000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f82000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster3: mailbox@31f83000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f83000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster4: mailbox@31f84000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f84000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster5: mailbox@31f85000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f85000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster6: mailbox@31f86000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f86000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster7: mailbox@31f87000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f87000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster8: mailbox@31f88000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f88000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster9: mailbox@31f89000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f89000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster10: mailbox@31f8a000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f8a000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox0_cluster11: mailbox@31f8b000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f8b000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster0: mailbox@31f90000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f90000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster1: mailbox@31f91000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f91000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster2: mailbox@31f92000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f92000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster3: mailbox@31f93000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f93000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster4: mailbox@31f94000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f94000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster5: mailbox@31f95000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f95000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster6: mailbox@31f96000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f96000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster7: mailbox@31f97000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f97000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster8: mailbox@31f98000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f98000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster9: mailbox@31f99000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f99000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster10: mailbox@31f9a000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f9a000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + mailbox1_cluster11: mailbox@31f9b000 { + compatible = "ti,am654-mailbox"; + reg = <0x00 0x31f9b000 0x00 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&main_navss_intr>; + }; + + main_ringacc: ringacc@3c000000 { + compatible = "ti,am654-navss-ringacc"; + reg = <0x0 0x3c000000 0x0 0x400000>, + <0x0 0x38000000 0x0 0x400000>, + <0x0 0x31120000 0x0 0x100>, + <0x0 0x33000000 0x0 0x40000>; + reg-names = "rt", "fifos", "proxy_gcfg", "proxy_target"; + ti,num-rings = <1024>; + ti,sci-rm-range-gp-rings = <0x1>; + ti,sci = <&sms>; + ti,sci-dev-id = <259>; + msi-parent = <&main_udmass_inta>; + }; + + main_udmap: dma-controller@31150000 { + compatible = "ti,j721e-navss-main-udmap"; + reg = <0x0 0x31150000 0x0 0x100>, + <0x0 0x34000000 0x0 0x80000>, + <0x0 0x35000000 0x0 0x200000>; + reg-names = "gcfg", "rchanrt", "tchanrt"; + msi-parent = <&main_udmass_inta>; + #dma-cells = <1>; + + ti,sci = <&sms>; + ti,sci-dev-id = <263>; + ti,ringacc = <&main_ringacc>; + + ti,sci-rm-range-tchan = <0x0d>, /* TX_CHAN */ + <0x0f>, /* TX_HCHAN */ + <0x10>; /* TX_UHCHAN */ + ti,sci-rm-range-rchan = <0x0a>, /* RX_CHAN */ + <0x0b>, /* RX_HCHAN */ + <0x0c>; /* RX_UHCHAN */ + ti,sci-rm-range-rflow = <0x00>; /* GP RFLOW */ + }; + + cpts@310d0000 { + compatible = "ti,j721e-cpts"; + reg = <0x0 0x310d0000 0x0 0x400>; + reg-names = "cpts"; + clocks = <&k3_clks 226 5>; + clock-names = "cpts"; + interrupts-extended = <&main_navss_intr 391>; + interrupt-names = "cpts"; + ti,cpts-periodic-outputs = <6>; + ti,cpts-ext-ts-inputs = <8>; + }; + }; + + main_mcan0: can@2701000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02701000 0x00 0x200>, + <0x00 0x02708000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 182 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 182 0>, <&k3_clks 182 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan1: can@2711000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02711000 0x00 0x200>, + <0x00 0x02718000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 183 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 183 0>, <&k3_clks 183 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan2: can@2721000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02721000 0x00 0x200>, + <0x00 0x02728000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 184 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 184 0>, <&k3_clks 184 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan3: can@2731000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02731000 0x00 0x200>, + <0x00 0x02738000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 185 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 185 0>, <&k3_clks 185 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan4: can@2741000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02741000 0x00 0x200>, + <0x00 0x02748000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 186 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 186 0>, <&k3_clks 186 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan5: can@2751000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02751000 0x00 0x200>, + <0x00 0x02758000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 187 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 187 0>, <&k3_clks 187 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan6: can@2761000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02761000 0x00 0x200>, + <0x00 0x02768000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 188 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 188 0>, <&k3_clks 188 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan7: can@2771000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02771000 0x00 0x200>, + <0x00 0x02778000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 189 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 189 0>, <&k3_clks 189 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan8: can@2781000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02781000 0x00 0x200>, + <0x00 0x02788000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 190 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 190 0>, <&k3_clks 190 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan9: can@2791000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02791000 0x00 0x200>, + <0x00 0x02798000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 191 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 191 0>, <&k3_clks 191 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan10: can@27a1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027a1000 0x00 0x200>, + <0x00 0x027a8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 192 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 192 0>, <&k3_clks 192 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan11: can@27b1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027b1000 0x00 0x200>, + <0x00 0x027b8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 193 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 193 0>, <&k3_clks 193 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan12: can@27c1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027c1000 0x00 0x200>, + <0x00 0x027c8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 194 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 194 0>, <&k3_clks 194 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan13: can@27d1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x027d1000 0x00 0x200>, + <0x00 0x027d8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 195 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 195 0>, <&k3_clks 195 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan14: can@2681000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02681000 0x00 0x200>, + <0x00 0x02688000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 197 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 197 0>, <&k3_clks 197 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan15: can@2691000 { + compatible = "bosch,m_can"; + reg = <0x00 0x02691000 0x00 0x200>, + <0x00 0x02698000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 199 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 199 0>, <&k3_clks 199 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan16: can@26a1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x026a1000 0x00 0x200>, + <0x00 0x026a8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 201 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 201 0>, <&k3_clks 201 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + main_mcan17: can@26b1000 { + compatible = "bosch,m_can"; + reg = <0x00 0x026b1000 0x00 0x200>, + <0x00 0x026b8000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 206 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 206 0>, <&k3_clks 206 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for J721S2 SoC Family MCU/WAKEUP Domain peripherals + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&cbass_mcu_wakeup { + sms: system-controller@44083000 { + compatible = "ti,k2g-sci"; + ti,host-id = <12>; + + mbox-names = "rx", "tx"; + + mboxes= <&secure_proxy_main 11>, + <&secure_proxy_main 13>; + + reg-names = "debug_messages"; + reg = <0x00 0x44083000 0x00 0x1000>; + + k3_pds: power-controller { + compatible = "ti,sci-pm-domain"; + #power-domain-cells = <2>; + }; + + k3_clks: clock-controller { + compatible = "ti,k2g-sci-clk"; + #clock-cells = <2>; + }; + + k3_reset: reset-controller { + compatible = "ti,sci-reset"; + #reset-cells = <2>; + }; + }; + + chipid@43000014 { + compatible = "ti,am654-chipid"; + reg = <0x00 0x43000014 0x00 0x4>; + }; + + mcu_ram: sram@41c00000 { + compatible = "mmio-sram"; + reg = <0x00 0x41c00000 0x00 0x100000>; + ranges = <0x00 0x00 0x41c00000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + }; + + wkup_pmx0: pinctrl@4301c000 { + compatible = "pinctrl-single"; + /* Proxy 0 addressing */ + reg = <0x00 0x4301c000 0x00 0x178>; + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0xffffffff>; + }; + + wkup_gpio_intr: interrupt-controller@42200000 { + compatible = "ti,sci-intr"; + reg = <0x00 0x42200000 0x00 0x400>; + ti,intr-trigger-type = <1>; + interrupt-controller; + interrupt-parent = <&gic500>; + #interrupt-cells = <1>; + ti,sci = <&sms>; + ti,sci-dev-id = <125>; + ti,interrupt-ranges = <16 928 16>; + }; + + mcu_conf: syscon@40f00000 { + compatible = "syscon", "simple-mfd"; + reg = <0x0 0x40f00000 0x0 0x20000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x40f00000 0x20000>; + + phy_gmii_sel: phy@4040 { + compatible = "ti,am654-phy-gmii-sel"; + reg = <0x4040 0x4>; + #phy-cells = <1>; + }; + + }; + + wkup_uart0: serial@42300000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x42300000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 359 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 359 TI_SCI_PD_EXCLUSIVE>; + }; + + mcu_uart0: serial@40a00000 { + compatible = "ti,j721e-uart", "ti,am654-uart"; + reg = <0x00 0x40a00000 0x00 0x200>; + interrupts = ; + current-speed = <115200>; + clocks = <&k3_clks 149 3>; + clock-names = "fclk"; + power-domains = <&k3_pds 149 TI_SCI_PD_EXCLUSIVE>; + }; + + wkup_gpio0: gpio@42110000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x42110000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <103>, <104>, <105>, <106>, <107>, <108>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <89>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 115 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 115 0>; + clock-names = "gpio"; + }; + + wkup_gpio1: gpio@42100000 { + compatible = "ti,j721e-gpio", "ti,keystone-gpio"; + reg = <0x00 0x42100000 0x00 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <112>, <113>, <114>, <115>, <116>, <117>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <89>; + ti,davinci-gpio-unbanked = <0>; + power-domains = <&k3_pds 116 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 116 0>; + clock-names = "gpio"; + }; + + wkup_i2c0: i2c@42120000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x42120000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 223 1>; + clock-names = "fck"; + power-domains = <&k3_pds 223 TI_SCI_PD_EXCLUSIVE>; + }; + + mcu_i2c0: i2c@40b00000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x40b00000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 221 1>; + clock-names = "fck"; + power-domains = <&k3_pds 221 TI_SCI_PD_EXCLUSIVE>; + }; + + mcu_i2c1: i2c@40b10000 { + compatible = "ti,j721e-i2c", "ti,omap4-i2c"; + reg = <0x00 0x40b10000 0x00 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 222 1>; + clock-names = "fck"; + power-domains = <&k3_pds 222 TI_SCI_PD_EXCLUSIVE>; + }; + + mcu_mcan0: can@40528000 { + compatible = "bosch,m_can"; + reg = <0x00 0x40528000 0x00 0x200>, + <0x00 0x40500000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 207 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 207 0>, <&k3_clks 207 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + mcu_mcan1: can@40568000 { + compatible = "bosch,m_can"; + reg = <0x00 0x40568000 0x00 0x200>, + <0x00 0x40540000 0x00 0x8000>; + reg-names = "m_can", "message_ram"; + power-domains = <&k3_pds 208 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 208 0>, <&k3_clks 208 1>; + clock-names = "hclk", "cclk"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>; + }; + + mcu_navss: bus@28380000{ + compatible = "simple-mfd"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>; + dma-coherent; + dma-ranges; + + ti,sci-dev-id = <267>; + + mcu_ringacc: ringacc@2b800000 { + compatible = "ti,am654-navss-ringacc"; + reg = <0x0 0x2b800000 0x0 0x400000>, + <0x0 0x2b000000 0x0 0x400000>, + <0x0 0x28590000 0x0 0x100>, + <0x0 0x2a500000 0x0 0x40000>; + reg-names = "rt", "fifos", "proxy_gcfg", "proxy_target"; + ti,num-rings = <286>; + ti,sci-rm-range-gp-rings = <0x1>; + ti,sci = <&sms>; + ti,sci-dev-id = <272>; + msi-parent = <&main_udmass_inta>; + }; + + mcu_udmap: dma-controller@285c0000 { + compatible = "ti,j721e-navss-mcu-udmap"; + reg = <0x0 0x285c0000 0x0 0x100>, + <0x0 0x2a800000 0x0 0x40000>, + <0x0 0x2aa00000 0x0 0x40000>; + reg-names = "gcfg", "rchanrt", "tchanrt"; + msi-parent = <&main_udmass_inta>; + #dma-cells = <1>; + + ti,sci = <&sms>; + ti,sci-dev-id = <273>; + ti,ringacc = <&mcu_ringacc>; + ti,sci-rm-range-tchan = <0x0d>, /* TX_CHAN */ + <0x0f>; /* TX_HCHAN */ + ti,sci-rm-range-rchan = <0x0a>, /* RX_CHAN */ + <0x0b>; /* RX_HCHAN */ + ti,sci-rm-range-rflow = <0x00>; /* GP RFLOW */ + }; + }; + + mcu_cpsw: ethernet@46000000 { + compatible = "ti,j721e-cpsw-nuss"; + #address-cells = <2>; + #size-cells = <2>; + reg = <0x0 0x46000000 0x0 0x200000>; + reg-names = "cpsw_nuss"; + ranges = <0x0 0x0 0x0 0x46000000 0x0 0x200000>; + dma-coherent; + clocks = <&k3_clks 29 28>; + clock-names = "fck"; + power-domains = <&k3_pds 29 TI_SCI_PD_EXCLUSIVE>; + + dmas = <&mcu_udmap 0xf000>, + <&mcu_udmap 0xf001>, + <&mcu_udmap 0xf002>, + <&mcu_udmap 0xf003>, + <&mcu_udmap 0xf004>, + <&mcu_udmap 0xf005>, + <&mcu_udmap 0xf006>, + <&mcu_udmap 0xf007>, + <&mcu_udmap 0x7000>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + cpsw_port1: port@1 { + reg = <1>; + ti,mac-only; + label = "port1"; + ti,syscon-efuse = <&mcu_conf 0x200>; + phys = <&phy_gmii_sel 1>; + }; + }; + + davinci_mdio: mdio@f00 { + compatible = "ti,cpsw-mdio","ti,davinci_mdio"; + reg = <0x0 0xf00 0x0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&k3_clks 29 28>; + clock-names = "fck"; + bus_freq = <1000000>; + }; + + cpts@3d000 { + compatible = "ti,am65-cpts"; + reg = <0x0 0x3d000 0x0 0x400>; + clocks = <&k3_clks 29 3>; + clock-names = "cpts"; + interrupts-extended = <&gic500 GIC_SPI 858 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "cpts"; + ti,cpts-ext-ts-inputs = <4>; + ti,cpts-periodic-outputs = <2>; + }; + }; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/k3-j721s2-som-p0.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-som-p0.dtsi --- a/arch/arm64/boot/dts/ti/k3-j721s2-som-p0.dtsi 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/k3-j721s2-som-p0.dtsi 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SoM: https://www.ti.com/lit/zip/sprr439 + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "k3-j721s2.dtsi" +#include + +/ { + memory@80000000 { + device_type = "memory"; + /* 16 GB RAM */ + reg = <0x00 0x80000000 0x00 0x80000000>, + <0x08 0x80000000 0x03 0x80000000>; + }; + + /* Reserving memory regions still pending */ + reserved_memory: reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + secure_ddr: optee@9e800000 { + reg = <0x00 0x9e800000 0x00 0x01800000>; + alignment = <0x1000>; + no-map; + }; + }; + + transceiver0: can-phy0 { + /* standby pin has been grounded by default */ + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + }; +}; + +&main_pmx0 { + main_i2c0_pins_default: main-i2c0-pins-default { + pinctrl-single,pins = < + J721S2_IOPAD(0x0e0, PIN_INPUT_PULLUP, 0) /* (AH25) I2C0_SCL */ + J721S2_IOPAD(0x0e4, PIN_INPUT_PULLUP, 0) /* (AE24) I2C0_SDA */ + >; + }; + + main_mcan16_pins_default: main-mcan16-pins-default { + pinctrl-single,pins = < + J721S2_IOPAD(0x028, PIN_INPUT, 0) /* (AB24) MCAN16_RX */ + J721S2_IOPAD(0x024, PIN_OUTPUT, 0) /* (Y28) MCAN16_TX */ + >; + }; +}; + +&main_i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c0_pins_default>; + clock-frequency = <400000>; + + exp_som: gpio@21 { + compatible = "ti,tca6408"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "USB2.0_MUX_SEL", "CANUART_MUX1_SEL0", + "CANUART_MUX2_SEL0", "CANUART_MUX_SEL1", + "GPIO_RGMII1_RST", "GPIO_eDP_ENABLE", + "GPIO_LIN_EN", "CAN_STB"; + }; +}; + +&main_mcan16 { + pinctrl-0 = <&main_mcan16_pins_default>; + pinctrl-names = "default"; + phys = <&transceiver0>; +}; + +&mailbox0_cluster0 { + status = "disabled"; +}; + +&mailbox0_cluster1 { + status = "disabled"; +}; + +&mailbox0_cluster2 { + status = "disabled"; +}; + +&mailbox0_cluster3 { + status = "disabled"; +}; + +&mailbox0_cluster4 { + status = "disabled"; +}; + +&mailbox0_cluster5 { + status = "disabled"; +}; + +&mailbox0_cluster6 { + status = "disabled"; +}; + +&mailbox0_cluster7 { + status = "disabled"; +}; + +&mailbox0_cluster8 { + status = "disabled"; +}; + +&mailbox0_cluster9 { + status = "disabled"; +}; + +&mailbox0_cluster10 { + status = "disabled"; +}; + +&mailbox0_cluster11 { + status = "disabled"; +}; + +&mailbox1_cluster0 { + status = "disabled"; +}; + +&mailbox1_cluster1 { + status = "disabled"; +}; + +&mailbox1_cluster2 { + status = "disabled"; +}; + +&mailbox1_cluster3 { + status = "disabled"; +}; + +&mailbox1_cluster4 { + status = "disabled"; +}; + +&mailbox1_cluster5 { + status = "disabled"; +}; + +&mailbox1_cluster6 { + status = "disabled"; +}; + +&mailbox1_cluster7 { + status = "disabled"; +}; + +&mailbox1_cluster8 { + status = "disabled"; +}; + +&mailbox1_cluster9 { + status = "disabled"; +}; + +&mailbox1_cluster10 { + status = "disabled"; +}; + +&mailbox1_cluster11 { + status = "disabled"; +}; diff -Naur --no-dereference a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile --- a/arch/arm64/boot/dts/ti/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/boot/dts/ti/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -3,11 +3,38 @@ # Make file to build device tree binaries for boards based on # Texas Instruments Inc processors # -# Copyright (C) 2016-2020 Texas Instruments Incorporated - https://www.ti.com/ +# Copyright (C) 2016-2021 Texas Instruments Incorporated - https://www.ti.com/ # +DTC_FLAGS += -@ + dtb-$(CONFIG_ARCH_K3) += k3-am654-base-board.dtb +dtb-$(CONFIG_ARCH_K3) += k3-am654-sr1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-base-board-sr1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-pcie-usb3.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-pcie-usb2.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-gp.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-idk.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-idk-sr1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-evm-tc358876.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-evm-oldi-lcd1evm.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am654-evm-ov5640.dtbo dtb-$(CONFIG_ARCH_K3) += k3-j721e-common-proc-board.dtb +dtb-$(CONFIG_ARCH_K3) += k3-j721e-proc-board-tps65917.dtb +dtb-$(CONFIG_ARCH_K3) += k3-j721e-common-proc-board-infotainment.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-cpb-csi2-ov5640.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-sk.dtb +dtb-$(CONFIG_ARCH_K3) += k3-j721e-sk-csi2-ov5640.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-sk-rpi-cam-imx219.dtbo dtb-$(CONFIG_ARCH_K3) += k3-j7200-common-proc-board.dtb + +dtb-$(CONFIG_ARCH_K3) += k3-j721s2-common-proc-board.dtb + +dtb-$(CONFIG_ARCH_K3) += k3-am642-evm.dtb +dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-icssg1-dualemac.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-icssg1-dualemac-mii.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-nand.dtbo + +dtb-$(CONFIG_ARCH_K3) += k3-am642-sk.dtb diff -Naur --no-dereference a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig --- a/arch/arm64/configs/defconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/arch/arm64/configs/defconfig 2022-01-06 12:45:53.810318090 -0500 @@ -395,6 +395,8 @@ CONFIG_SERIAL_8250_BCM2835AUX=y CONFIG_SERIAL_8250_DW=y CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_8250_NR_UARTS=16 +CONFIG_SERIAL_8250_RUNTIME_UARTS=16 CONFIG_SERIAL_8250_MT6577=y CONFIG_SERIAL_8250_UNIPHIER=y CONFIG_SERIAL_OF_PLATFORM=y @@ -438,6 +440,7 @@ CONFIG_I2C_IMX_LPI2C=y CONFIG_I2C_MESON=y CONFIG_I2C_MV64XXX=y +CONFIG_I2C_OMAP=y CONFIG_I2C_OWL=y CONFIG_I2C_PXA=y CONFIG_I2C_QCOM_CCI=m @@ -497,6 +500,7 @@ CONFIG_PINCTRL_SM8150=y CONFIG_PINCTRL_SM8250=y CONFIG_GPIO_ALTERA=m +CONFIG_GPIO_DAVINCI=y CONFIG_GPIO_DWAPB=y CONFIG_GPIO_MB86S7X=y CONFIG_GPIO_MPC8XXX=y @@ -959,8 +963,6 @@ CONFIG_ARCH_TEGRA_210_SOC=y CONFIG_ARCH_TEGRA_186_SOC=y CONFIG_ARCH_TEGRA_194_SOC=y -CONFIG_ARCH_K3_AM6_SOC=y -CONFIG_ARCH_K3_J721E_SOC=y CONFIG_TI_SCI_PM_DOMAINS=y CONFIG_EXTCON_PTN5150=m CONFIG_EXTCON_USB_GPIO=y diff -Naur --no-dereference a/crypto/tcrypt.c b/crypto/tcrypt.c --- a/crypto/tcrypt.c 2021-12-17 04:14:42.000000000 -0500 +++ b/crypto/tcrypt.c 2022-01-06 12:45:53.810318090 -0500 @@ -868,9 +868,13 @@ if (klen) crypto_ahash_setkey(tfm, tvmem[0], klen); - for (k = 0; k < num_mb; k++) + for (k = 0; k < num_mb; k++) { + sg_set_buf(data[k].sg, data[k].xbuf[0], + speed[i].blen > PAGE_SIZE ? PAGE_SIZE : + speed[i].blen); ahash_request_set_crypt(data[k].req, data[k].sg, data[k].result, speed[i].blen); + } pr_info("test%3u " "(%5u byte blocks,%5u bytes per update,%4u updates): ", @@ -1107,6 +1111,7 @@ "(%5u byte blocks,%5u bytes per update,%4u updates): ", i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen); + sg_set_buf(sg, tvmem[0], speed[i].plen); ahash_request_set_crypt(req, sg, output, speed[i].plen); if (secs) { diff -Naur --no-dereference a/Documentation/ABI/testing/debugfs-aufs b/Documentation/ABI/testing/debugfs-aufs --- a/Documentation/ABI/testing/debugfs-aufs 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/ABI/testing/debugfs-aufs 2022-01-06 12:45:53.802318058 -0500 @@ -0,0 +1,55 @@ +What: /debug/aufs/si_/ +Date: March 2009 +Contact: J. R. Okajima +Description: + Under /debug/aufs, a directory named si_ is created + per aufs mount, where is a unique id generated + internally. + +What: /debug/aufs/si_/plink +Date: Apr 2013 +Contact: J. R. Okajima +Description: + It has three lines and shows the information about the + pseudo-link. The first line is a single number + representing a number of buckets. The second line is a + number of pseudo-links per buckets (separated by a + blank). The last line is a single number representing a + total number of psedo-links. + When the aufs mount option 'noplink' is specified, it + will show "1\n0\n0\n". + +What: /debug/aufs/si_/xib +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xib (External Inode Number + Bitmap), its block size and file size. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. + +What: /debug/aufs/si_/xi +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xino (External Inode Number + Translation Table), its link count, block size and file + size. + Due to the file size limit, there may exist multiple + xino files per branch. In this case, "-N" is added to + the filename and it corresponds to the index of the + internal xino array. "-0" is omitted. + When the aufs mount option 'noxino' is specified, Those + entries won't exist. About XINO files, see the aufs + manual. + +What: /debug/aufs/si_/xigen +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xigen (External Inode + Generation Table), its block size and file size. + If CONFIG_AUFS_EXPORT is disabled, this entry will not + be created. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. diff -Naur --no-dereference a/Documentation/ABI/testing/sysfs-aufs b/Documentation/ABI/testing/sysfs-aufs --- a/Documentation/ABI/testing/sysfs-aufs 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/ABI/testing/sysfs-aufs 2022-01-06 12:45:53.802318058 -0500 @@ -0,0 +1,31 @@ +What: /sys/fs/aufs/si_/ +Date: March 2009 +Contact: J. R. Okajima +Description: + Under /sys/fs/aufs, a directory named si_ is created + per aufs mount, where is a unique id generated + internally. + +What: /sys/fs/aufs/si_/br +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the abolute path of a member directory (which + is called branch) in aufs, and its permission. + +What: /sys/fs/aufs/si_/brid +Date: July 2013 +Contact: J. R. Okajima +Description: + It shows the id of a member directory (which is called + branch) in aufs. + +What: /sys/fs/aufs/si_/xi_path +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the abolute path of XINO (External Inode Number + Bitmap, Translation Table and Generation Table) file + even if it is the default path. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. diff -Naur --no-dereference a/Documentation/ABI/testing/sysfs-bus-rpmsg b/Documentation/ABI/testing/sysfs-bus-rpmsg --- a/Documentation/ABI/testing/sysfs-bus-rpmsg 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/ABI/testing/sysfs-bus-rpmsg 2022-01-06 12:45:53.802318058 -0500 @@ -93,3 +93,32 @@ This sysfs entry allows the rpmsg driver for a rpmsg device to be specified which will override standard OF, ID table and name matching. + +What: /sys/bus/rpmsg/devices/.../desc +Date: March 2021 +KernelVersion: 5.10 +Contact: Bjorn Andersson +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels are identified by a textual name (see + /sys/bus/rpmsg/devices/.../name above) and have a local + ("source") rpmsg address, and remote ("destination") rpmsg + address. + + A channel is first created when an entity, whether local + or remote, starts listening on it for messages (and is thus + called an rpmsg server). When that happens, a "name service" + announcement is sent to the other processor, in order to let + it know about the creation of the channel (this way remote + clients know they can start sending messages). + + The listening entity (or client) which communicates with a + remote processor is referred as rpmsg driver. The rpmsg device + and rpmsg driver are matched based on rpmsg device name (see + /sys/bus/rpmsg/devices/.../name above) and rpmsg driver ID table. + + This sysfs entry contains an additional optional description of + the rpmsg device that can be optionally included as part of the + "name service" announcement. This description is then passed on + to the corresponding rpmsg drivers to further distinguish multiple + devices associated with the same rpmsg driver. diff -Naur --no-dereference a/Documentation/devicetree/bindings/arm/ti/k3.yaml b/Documentation/devicetree/bindings/arm/ti/k3.yaml --- a/Documentation/devicetree/bindings/arm/ti/k3.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/arm/ti/k3.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -27,12 +27,27 @@ - description: K3 J721E SoC items: + - enum: + - ti,j721e-eaik - const: ti,j721e - description: K3 J7200 SoC items: - const: ti,j7200 + - description: K3 AM642 SoC + items: + - enum: + - ti,am642-evm + - ti,am642-sk + - const: ti,am642 + + - description: K3 J721s2 SoC + items: + - enum: + - ti,j721s2-evm + - const: ti,j721s2 + additionalProperties: true ... diff -Naur --no-dereference a/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clks.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clks.yaml --- a/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clks.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clks.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0+ +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/allwinner,sun9i-a80-usb-clks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A80 USB Clock Controller Device Tree Bindings + +maintainers: + - Chen-Yu Tsai + - Maxime Ripard + +properties: + "#clock-cells": + const: 1 + + "#reset-cells": + const: 1 + + compatible: + const: allwinner,sun9i-a80-usb-clks + + reg: + maxItems: 1 + + clocks: + items: + - description: Bus Clock + - description: High Frequency Oscillator + + clock-names: + items: + - const: bus + - const: hosc + +required: + - "#clock-cells" + - "#reset-cells" + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + usb_clocks: clock@a08000 { + compatible = "allwinner,sun9i-a80-usb-clks"; + reg = <0x00a08000 0x8>; + clocks = <&ccu CLK_BUS_USB>, <&osc24M>; + clock-names = "bus", "hosc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clocks.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clocks.yaml --- a/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clocks.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/clock/allwinner,sun9i-a80-usb-clocks.yaml 1969-12-31 19:00:00.000000000 -0500 @@ -1,59 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/clock/allwinner,sun9i-a80-usb-clocks.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Allwinner A80 USB Clock Controller Device Tree Bindings - -maintainers: - - Chen-Yu Tsai - - Maxime Ripard - -properties: - "#clock-cells": - const: 1 - - "#reset-cells": - const: 1 - - compatible: - const: allwinner,sun9i-a80-usb-clocks - - reg: - maxItems: 1 - - clocks: - items: - - description: Bus Clock - - description: High Frequency Oscillator - - clock-names: - items: - - const: bus - - const: hosc - -required: - - "#clock-cells" - - "#reset-cells" - - compatible - - reg - - clocks - - clock-names - -additionalProperties: false - -examples: - - | - #include - - usb_clocks: clock@a08000 { - compatible = "allwinner,sun9i-a80-usb-clks"; - reg = <0x00a08000 0x8>; - clocks = <&ccu CLK_BUS_USB>, <&osc24M>; - clock-names = "bus", "hosc"; - #clock-cells = <1>; - #reset-cells = <1>; - }; - -... diff -Naur --no-dereference a/Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml b/Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml --- a/Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -66,8 +66,8 @@ - arm,syscon-icst525-integratorcp-cm-mem - arm,integrator-cm-auxosc - arm,versatile-cm-auxosc - - arm,impd-vco1 - - arm,impd-vco2 + - arm,impd1-vco1 + - arm,impd1-vco2 clocks: description: Parent clock for the ICST VCO diff -Naur --no-dereference a/Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml b/Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml --- a/Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -12,7 +12,9 @@ properties: compatible: items: - - const: ti,am654-ehrpwm-tbclk + - enum: + - ti,am654-ehrpwm-tbclk + - ti,am64-epwm-tbclk - const: syscon "#clock-cells": diff -Naur --no-dereference a/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml b/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml --- a/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -14,6 +14,7 @@ enum: - ti,j721e-sa2ul - ti,am654-sa2ul + - ti,am64-sa2ul reg: maxItems: 1 @@ -45,6 +46,18 @@ description: Address translation for the possible RNG child node for SA2UL + clocks: + items: + - description: Clock used by PKA + - description: Main Input Clock + - description: Clock used by rng + + clock-names: + items: + - const: pka_in_clk + - const: x1_clk + - const: x2_clk + patternProperties: "^rng@[a-f0-9]+$": type: object @@ -57,7 +70,16 @@ - power-domains - dmas - dma-names - - dma-coherent + +if: + properties: + compatible: + enum: + - ti,j721e-sa2ul + - ti,am654-sa2ul +then: + required: + - dma-coherent additionalProperties: false @@ -66,7 +88,7 @@ #include main_crypto: crypto@4e00000 { - compatible = "ti,j721-sa2ul"; + compatible = "ti,j721e-sa2ul"; reg = <0x4e00000 0x1200>; power-domains = <&k3_pds 264 TI_SCI_PD_EXCLUSIVE>; dmas = <&main_udmap 0xc000>, <&main_udmap 0x4000>, diff -Naur --no-dereference a/Documentation/devicetree/bindings/display/connector/dp-connector.yaml b/Documentation/devicetree/bindings/display/connector/dp-connector.yaml --- a/Documentation/devicetree/bindings/display/connector/dp-connector.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/display/connector/dp-connector.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/connector/dp-connector.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DisplayPort Connector + +maintainers: + - Tomi Valkeinen + +properties: + compatible: + const: dp-connector + + label: true + + type: + enum: + - full-size + - mini + + hpd-gpios: + description: A GPIO line connected to HPD + maxItems: 1 + + dp-pwr-supply: + description: Power supply for the DP_PWR pin + maxItems: 1 + + port: + description: Connection to controller providing DP signals + +required: + - compatible + - type + - port + +additionalProperties: false + +examples: + - | + connector { + compatible = "dp-connector"; + label = "dp0"; + type = "full-size"; + + port { + dp_connector_in: endpoint { + remote-endpoint = <&dp_out>; + }; + }; + }; + +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml --- a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -55,6 +55,14 @@ - const: vp1 - const: vp2 + assigned-clocks: + minItems: 1 + maxItems: 3 + + assigned-clock-parents: + minItems: 1 + maxItems: 3 + interrupts: maxItems: 1 @@ -62,6 +70,9 @@ maxItems: 1 description: phandle to the associated power domain + dma-coherent: + type: boolean + ports: type: object description: diff -Naur --no-dereference a/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml --- a/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -77,6 +77,14 @@ - const: vp3 - const: vp4 + assigned-clocks: + minItems: 1 + maxItems: 5 + + assigned-clock-parents: + minItems: 1 + maxItems: 5 + interrupts: items: - description: common_m DSS Master common @@ -95,6 +103,9 @@ maxItems: 1 description: phandle to the associated power domain + dma-coherent: + type: boolean + ports: type: object description: diff -Naur --no-dereference a/Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml b/Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml --- a/Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated +# Author: Peter Ujfalusi +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/ti/k3-bcdma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments K3 DMSS BCDMA Device Tree Bindings + +maintainers: + - Peter Ujfalusi + +description: | + The Block Copy DMA (BCDMA) is intended to perform similar functions as the TR + mode channels of K3 UDMA-P. + BCDMA includes block copy channels and Split channels. + + Block copy channels mainly used for memory to memory transfers, but with + optional triggers a block copy channel can service peripherals by accessing + directly to memory mapped registers or area. + + Split channels can be used to service PSI-L based peripherals. + The peripherals can be PSI-L native or legacy, non PSI-L native peripherals + with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the + legacy peripheral. + + PDMAs can be configured via BCDMA split channel's peer registers to match with + the configuration of the legacy peripheral. + +allOf: + - $ref: /schemas/dma/dma-controller.yaml# + +properties: + compatible: + const: ti,am64-dmss-bcdma + + "#dma-cells": + const: 3 + description: | + cell 1: type of the BCDMA channel to be used to service the peripheral: + 0 - split channel + 1 - block copy channel using global trigger 1 + 2 - block copy channel using global trigger 2 + 3 - block copy channel using local trigger + + cell 2: parameter for the channel: + if cell 1 is 0 (split channel): + PSI-L thread ID of the remote (to BCDMA) end. + Valid ranges for thread ID depends on the data movement direction: + for source thread IDs (rx): 0 - 0x7fff + for destination thread IDs (tx): 0x8000 - 0xffff + + Please refer to the device documentation for the PSI-L thread map and + also the PSI-L peripheral chapter for the correct thread ID. + if cell 1 is 1 or 2 (block copy channel using global trigger): + Unused, ignored + + The trigger must be configured for the channel externally to BCDMA, + channels using global triggers should not be requested directly, but + via DMA event router. + if cell 1 is 3 (block copy channel using local trigger): + bchan number of the locally triggered channel + + cell 3: ASEL value for the channel + + reg: + maxItems: 5 + + reg-names: + items: + - const: gcfg + - const: bchanrt + - const: rchanrt + - const: tchanrt + - const: ringrt + + msi-parent: true + + ti,asel: + $ref: /schemas/types.yaml#/definitions/uint32 + description: ASEL value for non slave channels + + ti,sci-rm-range-bchan: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of BCDMA block-copy channel resource subtypes for resource + allocation for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + + ti,sci-rm-range-tchan: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of BCDMA split tx channel resource subtypes for resource allocation + for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + + ti,sci-rm-range-rchan: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of BCDMA split rx channel resource subtypes for resource allocation + for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + +required: + - compatible + - "#dma-cells" + - reg + - reg-names + - msi-parent + - ti,sci + - ti,sci-dev-id + - ti,sci-rm-range-bchan + - ti,sci-rm-range-tchan + - ti,sci-rm-range-rchan + +unevaluatedProperties: false + +examples: + - |+ + cbass_main { + #address-cells = <2>; + #size-cells = <2>; + + main_dmss { + compatible = "simple-mfd"; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges; + ranges; + + ti,sci-dev-id = <25>; + + main_bcdma: dma-controller@485c0100 { + compatible = "ti,am64-dmss-bcdma"; + + reg = <0x0 0x485c0100 0x0 0x100>, + <0x0 0x4c000000 0x0 0x20000>, + <0x0 0x4a820000 0x0 0x20000>, + <0x0 0x4aa40000 0x0 0x20000>, + <0x0 0x4bc00000 0x0 0x100000>; + reg-names = "gcfg", "bchanrt", "rchanrt", "tchanrt", "ringrt"; + msi-parent = <&inta_main_dmss>; + #dma-cells = <3>; + + ti,sci = <&dmsc>; + ti,sci-dev-id = <26>; + + ti,sci-rm-range-bchan = <0x20>; /* BLOCK_COPY_CHAN */ + ti,sci-rm-range-rchan = <0x21>; /* SPLIT_TR_RX_CHAN */ + ti,sci-rm-range-tchan = <0x22>; /* SPLIT_TR_TX_CHAN */ + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml b/Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml --- a/Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,174 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated +# Author: Peter Ujfalusi +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/ti/k3-pktdma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments K3 DMSS PKTDMA Device Tree Bindings + +maintainers: + - Peter Ujfalusi + +description: | + The Packet DMA (PKTDMA) is intended to perform similar functions as the packet + mode channels of K3 UDMA-P. + PKTDMA only includes Split channels to service PSI-L based peripherals. + + The peripherals can be PSI-L native or legacy, non PSI-L native peripherals + with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the + legacy peripheral. + + PDMAs can be configured via PKTDMA split channel's peer registers to match + with the configuration of the legacy peripheral. + +allOf: + - $ref: /schemas/dma/dma-controller.yaml# + +properties: + compatible: + const: ti,am64-dmss-pktdma + + "#dma-cells": + const: 2 + description: | + The first cell is the PSI-L thread ID of the remote (to PKTDMA) end. + Valid ranges for thread ID depends on the data movement direction: + for source thread IDs (rx): 0 - 0x7fff + for destination thread IDs (tx): 0x8000 - 0xffff + + Please refer to the device documentation for the PSI-L thread map and also + the PSI-L peripheral chapter for the correct thread ID. + + The second cell is the ASEL value for the channel + + reg: + maxItems: 4 + + reg-names: + items: + - const: gcfg + - const: rchanrt + - const: tchanrt + - const: ringrt + + msi-parent: true + + ti,sci-rm-range-tchan: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of PKTDMA split tx channel resource subtypes for resource allocation + for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + + ti,sci-rm-range-tflow: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of PKTDMA split tx flow resource subtypes for resource allocation + for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + + ti,sci-rm-range-rchan: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of PKTDMA split rx channel resource subtypes for resource allocation + for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + + ti,sci-rm-range-rflow: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Array of PKTDMA split rx flow resource subtypes for resource allocation + for this host + minItems: 1 + # Should be enough + maxItems: 255 + items: + maximum: 0x3f + +required: + - compatible + - "#dma-cells" + - reg + - reg-names + - msi-parent + - ti,sci + - ti,sci-dev-id + - ti,sci-rm-range-tchan + - ti,sci-rm-range-tflow + - ti,sci-rm-range-rchan + - ti,sci-rm-range-rflow + +unevaluatedProperties: false + +examples: + - |+ + cbass_main { + #address-cells = <2>; + #size-cells = <2>; + + main_dmss { + compatible = "simple-mfd"; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges; + ranges; + + ti,sci-dev-id = <25>; + + main_pktdma: dma-controller@485c0000 { + compatible = "ti,am64-dmss-pktdma"; + + reg = <0x0 0x485c0000 0x0 0x100>, + <0x0 0x4a800000 0x0 0x20000>, + <0x0 0x4aa00000 0x0 0x40000>, + <0x0 0x4b800000 0x0 0x400000>; + reg-names = "gcfg", "rchanrt", "tchanrt", "ringrt"; + msi-parent = <&inta_main_dmss>; + #dma-cells = <2>; + + ti,sci = <&dmsc>; + ti,sci-dev-id = <30>; + + ti,sci-rm-range-tchan = <0x23>, /* UNMAPPED_TX_CHAN */ + <0x24>, /* CPSW_TX_CHAN */ + <0x25>, /* SAUL_TX_0_CHAN */ + <0x26>, /* SAUL_TX_1_CHAN */ + <0x27>, /* ICSSG_0_TX_CHAN */ + <0x28>; /* ICSSG_1_TX_CHAN */ + ti,sci-rm-range-tflow = <0x10>, /* RING_UNMAPPED_TX_CHAN */ + <0x11>, /* RING_CPSW_TX_CHAN */ + <0x12>, /* RING_SAUL_TX_0_CHAN */ + <0x13>, /* RING_SAUL_TX_1_CHAN */ + <0x14>, /* RING_ICSSG_0_TX_CHAN */ + <0x15>; /* RING_ICSSG_1_TX_CHAN */ + ti,sci-rm-range-rchan = <0x29>, /* UNMAPPED_RX_CHAN */ + <0x2b>, /* CPSW_RX_CHAN */ + <0x2d>, /* SAUL_RX_0_CHAN */ + <0x2f>, /* SAUL_RX_1_CHAN */ + <0x31>, /* SAUL_RX_2_CHAN */ + <0x33>, /* SAUL_RX_3_CHAN */ + <0x35>, /* ICSSG_0_RX_CHAN */ + <0x37>; /* ICSSG_1_RX_CHAN */ + ti,sci-rm-range-rflow = <0x2a>, /* FLOW_UNMAPPED_RX_CHAN */ + <0x2c>, /* FLOW_CPSW_RX_CHAN */ + <0x2e>, /* FLOW_SAUL_RX_0/1_CHAN */ + <0x32>, /* FLOW_SAUL_RX_2/3_CHAN */ + <0x36>, /* FLOW_ICSSG_0_RX_CHAN */ + <0x38>; /* FLOW_ICSSG_1_RX_CHAN */ + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/dma/ti/k3-udma.yaml b/Documentation/devicetree/bindings/dma/ti/k3-udma.yaml --- a/Documentation/devicetree/bindings/dma/ti/k3-udma.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/dma/ti/k3-udma.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -1,4 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019 Texas Instruments Incorporated +# Author: Peter Ujfalusi %YAML 1.2 --- $id: http://devicetree.org/schemas/dma/ti/k3-udma.yaml# @@ -7,7 +9,7 @@ title: Texas Instruments K3 NAVSS Unified DMA Device Tree Bindings maintainers: - - Peter Ujfalusi + - Peter Ujfalusi description: | The UDMA-P is intended to perform similar (but significantly upgraded) diff -Naur --no-dereference a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt --- a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,166 +0,0 @@ -Davinci/Keystone GPIO controller bindings - -Required Properties: -- compatible: should be "ti,dm6441-gpio": for Davinci da850 SoCs - "ti,keystone-gpio": for Keystone 2 66AK2H/K, 66AK2L, - 66AK2E SoCs - "ti,k2g-gpio", "ti,keystone-gpio": for 66AK2G - "ti,am654-gpio", "ti,keystone-gpio": for TI K3 AM654 - "ti,j721e-gpio", "ti,keystone-gpio": for J721E SoCs - -- reg: Physical base address of the controller and the size of memory mapped - registers. - -- gpio-controller : Marks the device node as a gpio controller. - -- #gpio-cells : Should be two. - - first cell is the pin number - - second cell is used to specify optional parameters (unused) - -- interrupts: Array of GPIO interrupt number. Only banked or unbanked IRQs are - supported at a time. - -- ti,ngpio: The number of GPIO pins supported. - -- ti,davinci-gpio-unbanked: The number of GPIOs that have an individual interrupt - line to processor. - -- clocks: Should contain the device's input clock, and should be defined as per - the appropriate clock bindings consumer usage in, - - Documentation/devicetree/bindings/clock/keystone-gate.txt - for 66AK2HK/66AK2L/66AK2E SoCs or, - - Documentation/devicetree/bindings/clock/ti,sci-clk.txt - for 66AK2G SoCs - -- clock-names: Name should be "gpio"; - -Currently clock-names and clocks are needed for all keystone 2 platforms -Davinci platforms do not have DT clocks as of now. - -The GPIO controller also acts as an interrupt controller. It uses the default -two cells specifier as described in Documentation/devicetree/bindings/ -interrupt-controller/interrupts.txt. - -Example: - -gpio: gpio@1e26000 { - compatible = "ti,dm6441-gpio"; - gpio-controller; - #gpio-cells = <2>; - reg = <0x226000 0x1000>; - interrupt-parent = <&intc>; - interrupts = <42 IRQ_TYPE_EDGE_BOTH 43 IRQ_TYPE_EDGE_BOTH - 44 IRQ_TYPE_EDGE_BOTH 45 IRQ_TYPE_EDGE_BOTH - 46 IRQ_TYPE_EDGE_BOTH 47 IRQ_TYPE_EDGE_BOTH - 48 IRQ_TYPE_EDGE_BOTH 49 IRQ_TYPE_EDGE_BOTH - 50 IRQ_TYPE_EDGE_BOTH>; - ti,ngpio = <144>; - ti,davinci-gpio-unbanked = <0>; - interrupt-controller; - #interrupt-cells = <2>; -}; - -leds { - compatible = "gpio-leds"; - - led1 { - label = "davinci:green:usr1"; - gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; - ... - }; - - led2 { - label = "davinci:red:debug1"; - gpios = <&gpio 11 GPIO_ACTIVE_HIGH>; - ... - }; -}; - -Example for 66AK2G: - -gpio0: gpio@2603000 { - compatible = "ti,k2g-gpio", "ti,keystone-gpio"; - reg = <0x02603000 0x100>; - gpio-controller; - #gpio-cells = <2>; - interrupts = , - , - , - , - , - , - , - , - ; - interrupt-controller; - #interrupt-cells = <2>; - ti,ngpio = <144>; - ti,davinci-gpio-unbanked = <0>; - clocks = <&k2g_clks 0x001b 0x0>; - clock-names = "gpio"; -}; - -Example for 66AK2HK/66AK2L/66AK2E: - -gpio0: gpio@260bf00 { - compatible = "ti,keystone-gpio"; - reg = <0x0260bf00 0x100>; - gpio-controller; - #gpio-cells = <2>; - /* HW Interrupts mapped to GPIO pins */ - interrupts = , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - ; - clocks = <&clkgpio>; - clock-names = "gpio"; - ti,ngpio = <32>; - ti,davinci-gpio-unbanked = <32>; -}; - -Example for K3 AM654: - -wkup_gpio0: wkup_gpio0@42110000 { - compatible = "ti,am654-gpio", "ti,keystone-gpio"; - reg = <0x42110000 0x100>; - gpio-controller; - #gpio-cells = <2>; - interrupt-parent = <&intr_wkup_gpio>; - interrupts = <59 128>, <59 129>, <59 130>, <59 131>; - interrupt-controller; - #interrupt-cells = <2>; - ti,ngpio = <56>; - ti,davinci-gpio-unbanked = <0>; - clocks = <&k3_clks 59 0>; - clock-names = "gpio"; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/gpio/gpio-davinci.yaml b/Documentation/devicetree/bindings/gpio/gpio-davinci.yaml --- a/Documentation/devicetree/bindings/gpio/gpio-davinci.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/gpio/gpio-davinci.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,185 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-davinci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO controller for Davinci and keystone devices + +maintainers: + - Keerthy + +properties: + compatible: + oneOf: + - items: + - enum: + - ti,k2g-gpio + - ti,am654-gpio + - ti,j721e-gpio + - ti,am64-gpio + - const: ti,keystone-gpio + + - items: + - enum: + - ti,dm6441-gpio + - ti,keystone-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + gpio-ranges: true + + gpio-line-names: + description: strings describing the names of each gpio line. + minItems: 1 + maxItems: 100 + + "#gpio-cells": + const: 2 + description: + first cell is the pin number and second cell is used to specify optional parameters (unused). + + interrupts: + description: + The interrupts are specified as per the interrupt parent. Only banked + or unbanked IRQs are supported at a time. If the interrupts are + banked then provide list of interrupts corresponding to each bank, else + provide the list of interrupts for each gpio. + minItems: 1 + maxItems: 100 + + ti,ngpio: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The number of GPIO pins supported consecutively. + minimum: 1 + + ti,davinci-gpio-unbanked: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The number of GPIOs that have an individual interrupt line to processor. + minimum: 0 + + clocks: + maxItems: 1 + + clock-names: + const: gpio + + interrupt-controller: true + + power-domains: + maxItems: 1 + + "#interrupt-cells": + const: 2 + +patternProperties: + "^(.+-hog(-[0-9]+)?)$": + type: object + + required: + - gpio-hog + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - interrupts + - ti,ngpio + - ti,davinci-gpio-unbanked + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + gpio0: gpio@2603000 { + compatible = "ti,k2g-gpio", "ti,keystone-gpio"; + reg = <0x02603000 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <144>; + ti,davinci-gpio-unbanked = <0>; + clocks = <&k2g_clks 0x001b 0x0>; + clock-names = "gpio"; + }; + + - | + #include + + gpio1: gpio@260bf00 { + compatible = "ti,keystone-gpio"; + reg = <0x0260bf00 0x100>; + gpio-controller; + #gpio-cells = <2>; + /* HW Interrupts mapped to GPIO pins */ + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + clocks = <&clkgpio>; + clock-names = "gpio"; + ti,ngpio = <32>; + ti,davinci-gpio-unbanked = <32>; + }; + + - | + wkup_gpio0: gpio0@42110000 { + compatible = "ti,am654-gpio", "ti,keystone-gpio"; + reg = <0x42110000 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&intr_wkup_gpio>; + interrupts = <60>, <61>, <62>, <63>; + interrupt-controller; + #interrupt-cells = <2>; + ti,ngpio = <56>; + ti,davinci-gpio-unbanked = <0>; + clocks = <&k3_clks 59 0>; + clock-names = "gpio"; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/gpu/ti,dra7-bb2d.yaml b/Documentation/devicetree/bindings/gpu/ti,dra7-bb2d.yaml --- a/Documentation/devicetree/bindings/gpu/ti,dra7-bb2d.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/gpu/ti,dra7-bb2d.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpu/ti,dra7-bb2d.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments BB2D blitter module + +maintainers: + - Gowtham Tammana + +properties: + compatible: + const: ti,dra7-bb2d + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clock-names: + items: + - const: fck + + clocks: + maxItems: 1 + + ti,hwmods: + description: TI hwmod name + deprecated: true + $ref: /schemas/types.yaml#/definitions/string-array + items: + const: bb2d + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + bb2d: bb2d@59000000 { + compatible = "ti,dra7-bb2d"; + interrupts = ; + clocks = <&dss_clkctrl_0>; + clock-names = "fck"; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/gpu/ti,pvr.yaml b/Documentation/devicetree/bindings/gpu/ti,pvr.yaml --- a/Documentation/devicetree/bindings/gpu/ti,pvr.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/gpu/ti,pvr.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,196 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpu/ti,pvr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments PowerVR Rogue GPU Bindings + +description: | + PowerVR is a family of 3D graphics processing units from Imagination + Technologies. Texas Instruments SoCs have integrated different generations of + PowerVR GPUs and this binding describes the GPU's integrated in Texas + Instruments SoCs. + +maintainers: + - Gowtham Tammana + +properties: + $nodename: + pattern: '^gpu@[a-f0-9]+$' + + compatible: + oneOf: + - items: + - enum: + - ti,j721e-pvr + - const: img,pvr-ge8430 + - items: + - enum: + - ti,am654-sgx544 + - ti,dra7-sgx544 + - const: img,sgx544 + - items: + - enum: + - ti,am3352-sgx530 + - ti,am4376-sgx530 + - const: img,sgx530 + + reg: + maxItems: 1 + + reg-names: + const: gpu_regs + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 4 + + clock-names: + minItems: 1 + maxItems: 4 + + power-domains: + minItems: 1 + maxItems: 2 + + power-domain-names: + minItems: 1 + maxItems: 2 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + enum: + - ti,j721e-pvr + then: + properties: + clocks: + minItems: 1 + power-domains: + maxItems: 2 + items: + - description: power domain for register access + - description: power domain for gpu internal cores + power-domain-names: + maxItems: 2 + items: + - const: gpu_0 + - const: gpucore_0 + required: + - power-domains + - power-domain-names + - if: + properties: + compatible: + contains: + enum: + - ti,am654-sgx544 + then: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + items: + - const: mem_clk + - const: hyd_clk + - const: sgx_clk + - const: sys_clk + power-domains: + maxItems: 1 + required: + - power-domains + - if: + properties: + compatible: + contains: + enum: + - ti,dra7-sgx544 + then: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + items: + - const: iclk + - const: fclk1 + - const: fclk2 + - if: + properties: + compatible: + contains: + enum: + - ti,am3352-sgx530 + - ti,am4376-sgx530 + then: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + items: + - const: fclk +examples: + - | + #include + #include + #include + + gpu: gpu@0 { + compatible = "ti,j721e-pvr", "img,pvr-ge8430"; + reg = <0x00 0x80000>; + reg-names = "gpu_regs"; + interrupts = ; + power-domains = <&k3_pds 125 TI_SCI_PD_EXCLUSIVE>, + <&k3_pds 126 TI_SCI_PD_EXCLUSIVE>; + power-domain-names = "gpu_0", "gpucore_0"; + clocks = <&k3_clks 125 0>; + clock-names = "ctrl"; + }; + + - | + #include + #include + + gpu@70000000 { + compatible = "ti,am654-sgx544", "img,sgx544"; + reg = <0x0 0x10000>; + interrupts = ; + power-domains = <&k3_pds 65>; + clocks = <&k3_clks 65 0>, <&k3_clks 65 1>, + <&k3_clks 65 2>, <&k3_clks 65 3>; + clock-names = "mem_clk", "hyd_clk", + "sgx_clk", "sys_clk"; + }; + + - | + #include + #include + + gpu@56000000 { + compatible = "ti,dra7-sgx544", "img,sgx544"; + reg = <0x0 0x10000>; + interrupts = ; + clocks = <&l3_iclk_div>, + <&gpu_core_gclk_mux>, + <&gpu_hyd_gclk_mux>; + clock-names = "iclk", + "fclk1", + "fclk2"; + }; +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/hwlock/ti,omap-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/ti,omap-hwspinlock.yaml --- a/Documentation/devicetree/bindings/hwlock/ti,omap-hwspinlock.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/hwlock/ti,omap-hwspinlock.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -13,6 +13,7 @@ compatible: enum: - ti,omap4-hwspinlock # for OMAP44xx, OMAP54xx, AM33xx, AM43xx, DRA7xx SoCs + - ti,am64-hwspinlock # for K3 AM64x SoCs - ti,am654-hwspinlock # for K3 AM65x, J721E and J7200 SoCs reg: @@ -74,3 +75,28 @@ }; }; }; + + - | + / { + /* K3 AM64x SoCs */ + model = "Texas Instruments K3 AM642 SoC"; + compatible = "ti,am642-evm", "ti,am642"; + #address-cells = <2>; + #size-cells = <2>; + + bus@f4000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x00 0x000f4000 0x00 0x000f4000 0x00 0x000002e4>, /* PINCTRL */ + <0x00 0x01000000 0x00 0x01000000 0x00 0x02330400>, /* First peripheral window */ + <0x00 0x0f000000 0x00 0x0f000000 0x00 0x00c44200>, /* Second peripheral window */ + <0x00 0x20000000 0x00 0x20000000 0x00 0x0a008000>; /* Third peripheral window */ + + spinlock@2a000000 { + compatible = "ti,am64-hwspinlock"; + reg = <0x00 0x2a000000 0x00 0x1000>; + #hwlock-cells = <1>; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,74 +0,0 @@ -* NXP PCA954x I2C bus switch - -The driver supports NXP PCA954x and PCA984x I2C mux/switch devices. - -Required Properties: - - - compatible: Must contain one of the following. - "nxp,pca9540", - "nxp,pca9542", - "nxp,pca9543", - "nxp,pca9544", - "nxp,pca9545", - "nxp,pca9546", "nxp,pca9846", - "nxp,pca9547", "nxp,pca9847", - "nxp,pca9548", "nxp,pca9848", - "nxp,pca9849" - - - reg: The I2C address of the device. - - The following required properties are defined externally: - - - Standard I2C mux properties. See i2c-mux.txt in this directory. - - I2C child bus nodes. See i2c-mux.txt in this directory. - -Optional Properties: - - - reset-gpios: Reference to the GPIO connected to the reset input. - - idle-state: if present, overrides i2c-mux-idle-disconnect, - Please refer to Documentation/devicetree/bindings/mux/mux-controller.txt - - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all - children in idle state. This is necessary for example, if there are several - multiplexers on the bus and the devices behind them use same I2C addresses. - - interrupts: Interrupt mapping for IRQ. - - interrupt-controller: Marks the device node as an interrupt controller. - - #interrupt-cells : Should be two. - - first cell is the pin number - - second cell is used to specify flags. - See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt - -Example: - - i2c-switch@74 { - compatible = "nxp,pca9548"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0x74>; - - interrupt-parent = <&ipic>; - interrupts = <17 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; - #interrupt-cells = <2>; - - i2c@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - - eeprom@54 { - compatible = "atmel,24c08"; - reg = <0x54>; - }; - }; - - i2c@4 { - #address-cells = <1>; - #size-cells = <0>; - reg = <4>; - - rtc@51 { - compatible = "nxp,pcf8563"; - reg = <0x51>; - }; - }; - }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-mux-pca954x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PCA954x I2C bus switch + +maintainers: + - Laurent Pinchart + +description: + The binding supports NXP PCA954x and PCA984x I2C mux/switch devices. + +allOf: + - $ref: /schemas/i2c/i2c-mux.yaml# + +properties: + compatible: + oneOf: + - enum: + - nxp,pca9540 + - nxp,pca9542 + - nxp,pca9543 + - nxp,pca9544 + - nxp,pca9545 + - nxp,pca9546 + - nxp,pca9547 + - nxp,pca9548 + - nxp,pca9846 + - nxp,pca9847 + - nxp,pca9848 + - nxp,pca9849 + - items: + - const: nxp,pca9646 + - const: nxp,pca9546 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + "#interrupt-cells": + const: 2 + + interrupt-controller: true + + reset-gpios: + maxItems: 1 + + i2c-mux-idle-disconnect: + type: boolean + description: Forces mux to disconnect all children in idle state. This is + necessary for example, if there are several multiplexers on the bus and + the devices behind them use same I2C addresses. + + idle-state: + description: if present, overrides i2c-mux-idle-disconnect + $ref: /schemas/mux/mux-controller.yaml#/properties/idle-state + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + i2c-mux@74 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x74>; + + interrupt-parent = <&ipic>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <2>; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + eeprom@54 { + compatible = "atmel,24c08"; + reg = <0x54>; + }; + }; + + i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + + rtc@51 { + compatible = "nxp,pcf8563"; + reg = <0x51>; + }; + }; + }; + }; +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/i2c/i2c-omap.txt b/Documentation/devicetree/bindings/i2c/i2c-omap.txt --- a/Documentation/devicetree/bindings/i2c/i2c-omap.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/i2c/i2c-omap.txt 2022-01-06 12:45:53.806318073 -0500 @@ -8,6 +8,7 @@ "ti,omap4-i2c" for OMAP4+ SoCs "ti,am654-i2c", "ti,omap4-i2c" for AM654 SoCs "ti,j721e-i2c", "ti,omap4-i2c" for J721E SoCs + "ti,am64-i2c", "ti,omap4-i2c" for AM64 SoCs - ti,hwmods : Must be "i2c", n being the instance number (1-based) - #address-cells = <1>; - #size-cells = <0>; diff -Naur --no-dereference a/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml b/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml --- a/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -17,7 +17,7 @@ properties: compatible: - const: nuvoton,npcm7xx-i2c + const: nuvoton,npcm750-i2c reg: maxItems: 1 diff -Naur --no-dereference a/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.yaml --- a/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -33,6 +33,9 @@ corresponding PRUSS node. The node should be named "interrupt-controller". properties: + $nodename: + pattern: "^interrupt-controller@[0-9a-f]+$" + compatible: enum: - ti,pruss-intc @@ -43,7 +46,7 @@ AM437x family of SoCs, AM57xx family of SoCs 66AK2G family of SoCs - Use "ti,icssg-intc" for K3 AM65x & J721E family of SoCs + Use "ti,icssg-intc" for K3 AM65x, J721E and AM64x family of SoCs reg: maxItems: 1 @@ -80,7 +83,7 @@ mapping is provided. ti,irqs-reserved: - $ref: /schemas/types.yaml#definitions/uint8 + $ref: /schemas/types.yaml#/definitions/uint8 description: | Bitmask of host interrupts between 0 and 7 (corresponding to PRUSS INTC output interrupts 2 through 9) that are not connected to the Arm interrupt @@ -92,6 +95,8 @@ - AM65x and J721E SoCs have "host_intr5", "host_intr6" and "host_intr7" interrupts connected to MPU, and other ICSSG instances. + - AM64x SoCs have all the 8 host interrupts connected to various + other SoC entities required: - compatible diff -Naur --no-dereference a/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.yaml b/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.yaml --- a/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -93,7 +93,7 @@ #include ipmmu_mx: iommu@fe951000 { - compatible = "renasas,ipmmu-r8a7791", "renasas,ipmmu-vmsa"; + compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; reg = <0xfe951000 0x1000>; interrupts = , ; diff -Naur --no-dereference a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt --- a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,180 +0,0 @@ -OMAP2+ and K3 Mailbox -===================== - -The OMAP mailbox hardware facilitates communication between different processors -using a queued mailbox interrupt mechanism. The IP block is external to the -various processor subsystems and is connected on an interconnect bus. The -communication is achieved through a set of registers for message storage and -interrupt configuration registers. - -Each mailbox IP block/cluster has a certain number of h/w fifo queues and output -interrupt lines. An output interrupt line is routed to an interrupt controller -within a processor subsystem, and there can be more than one line going to a -specific processor's interrupt controller. The interrupt line connections are -fixed for an instance and are dictated by the IP integration into the SoC -(excluding the SoCs that have a Interrupt Crossbar IP). Each interrupt line is -programmable through a set of interrupt configuration registers, and have a rx -and tx interrupt source per h/w fifo. Communication between different processors -is achieved through the appropriate programming of the rx and tx interrupt -sources on the appropriate interrupt lines. - -The number of h/w fifo queues and interrupt lines dictate the usable registers. -All the current OMAP SoCs except for the newest DRA7xx SoC has a single IP -instance. DRA7xx has multiple instances with different number of h/w fifo queues -and interrupt lines between different instances. The interrupt lines can also be -routed to different processor sub-systems on DRA7xx as they are routed through -the Crossbar, a kind of interrupt router/multiplexer. The K3 AM65x and J721E -SoCs has each of these instances form a cluster and combine multiple clusters -into a single IP block present within the Main NavSS. The interrupt lines from -all these clusters are multiplexed and routed to different processor subsystems -over a limited number of common interrupt output lines of an Interrupt Router. - -Mailbox Device Node: -==================== -A Mailbox device node is used to represent a Mailbox IP instance/cluster within -a SoC. The sub-mailboxes are represented as child nodes of this parent node. - -Required properties: --------------------- -- compatible: Should be one of the following, - "ti,omap2-mailbox" for OMAP2420, OMAP2430 SoCs - "ti,omap3-mailbox" for OMAP3430, OMAP3630 SoCs - "ti,omap4-mailbox" for OMAP44xx, OMAP54xx, AM33xx, - AM43xx and DRA7xx SoCs - "ti,am654-mailbox" for K3 AM65x and J721E SoCs -- reg: Contains the mailbox register address range (base - address and length) -- interrupts: Contains the interrupt information for the mailbox - device. The format is dependent on which interrupt - controller the Mailbox device uses -- #mbox-cells: Common mailbox binding property to identify the number - of cells required for the mailbox specifier. Should be - 1 -- ti,mbox-num-users: Number of targets (processor devices) that the mailbox - device can interrupt -- ti,mbox-num-fifos: Number of h/w fifo queues within the mailbox IP block - -SoC-specific Required properties: ---------------------------------- -The following are mandatory properties for the OMAP architecture based SoCs -only: -- ti,hwmods: Name of the hwmod associated with the mailbox. This - should be defined in the mailbox node only if the node - is not defined as a child node of a corresponding sysc - interconnect node. - -The following are mandatory properties for the K3 AM65x and J721E SoCs only: -- interrupt-parent: Should contain a phandle to the TI-SCI interrupt - controller node that is used to dynamically program - the interrupt routes between the IP and the main GIC - controllers. See the following binding for additional - details, - Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.yaml - -Child Nodes: -============ -A child node is used for representing the actual sub-mailbox device that is -used for the communication between the host processor and a remote processor. -Each child node should have a unique node name across all the different -mailbox device nodes. - -Required properties: --------------------- -- ti,mbox-tx: sub-mailbox descriptor property defining a Tx fifo -- ti,mbox-rx: sub-mailbox descriptor property defining a Rx fifo - -Sub-mailbox Descriptor Data ---------------------------- -Each of the above ti,mbox-tx and ti,mbox-rx properties should have 3 cells of -data that represent the following: - Cell #1 (fifo_id) - mailbox fifo id used either for transmitting - (ti,mbox-tx) or for receiving (ti,mbox-rx) - Cell #2 (irq_id) - irq identifier index number to use from the parent's - interrupts data. Should be 0 for most of the cases, a - positive index value is seen only on mailboxes that have - multiple interrupt lines connected to the MPU processor. - Cell #3 (usr_id) - mailbox user id for identifying the interrupt line - associated with generating a tx/rx fifo interrupt. - -Optional Properties: --------------------- -- ti,mbox-send-noirq: Quirk flag to allow the client user of this sub-mailbox - to send messages without triggering a Tx ready interrupt, - and to control the Tx ticker. Should be used only on - sub-mailboxes used to communicate with WkupM3 remote - processor on AM33xx/AM43xx SoCs. - -Mailbox Users: -============== -A device needing to communicate with a target processor device should specify -them using the common mailbox binding properties, "mboxes" and the optional -"mbox-names" (please see Documentation/devicetree/bindings/mailbox/mailbox.txt -for details). Each value of the mboxes property should contain a phandle to the -mailbox controller device node and an args specifier that will be the phandle to -the intended sub-mailbox child node to be used for communication. The equivalent -"mbox-names" property value can be used to give a name to the communication channel -to be used by the client user. - - -Example: --------- - -1. /* OMAP4 */ -mailbox: mailbox@4a0f4000 { - compatible = "ti,omap4-mailbox"; - reg = <0x4a0f4000 0x200>; - interrupts = ; - ti,hwmods = "mailbox"; - #mbox-cells = <1>; - ti,mbox-num-users = <3>; - ti,mbox-num-fifos = <8>; - mbox_ipu: mbox_ipu { - ti,mbox-tx = <0 0 0>; - ti,mbox-rx = <1 0 0>; - }; - mbox_dsp: mbox_dsp { - ti,mbox-tx = <3 0 0>; - ti,mbox-rx = <2 0 0>; - }; -}; - -dsp { - ... - mboxes = <&mailbox &mbox_dsp>; - ... -}; - -2. /* AM33xx */ -mailbox: mailbox@480c8000 { - compatible = "ti,omap4-mailbox"; - reg = <0x480C8000 0x200>; - interrupts = <77>; - ti,hwmods = "mailbox"; - #mbox-cells = <1>; - ti,mbox-num-users = <4>; - ti,mbox-num-fifos = <8>; - mbox_wkupm3: wkup_m3 { - ti,mbox-tx = <0 0 0>; - ti,mbox-rx = <0 0 3>; - }; -}; - -3. /* AM65x */ -&cbass_main { - cbass_main_navss: interconnect0 { - mailbox0_cluster0: mailbox@31f80000 { - compatible = "ti,am654-mailbox"; - reg = <0x00 0x31f80000 0x00 0x200>; - #mbox-cells = <1>; - ti,mbox-num-users = <4>; - ti,mbox-num-fifos = <16>; - interrupt-parent = <&intr_main_navss>; - interrupts = <164 0>; - - mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 { - ti,mbox-tx = <1 0 0>; - ti,mbox-rx = <0 0 0>; - }; - }; - }; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml --- a/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,308 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/ti,omap-mailbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI OMAP2+ and K3 Mailbox devices + +maintainers: + - Suman Anna + +description: | + The OMAP Mailbox hardware facilitates communication between different + processors using a queued mailbox interrupt mechanism. The IP block is + external to the various processor subsystems and is connected on an + interconnect bus. The communication is achieved through a set of registers + for message storage and interrupt configuration registers. + + Each mailbox IP block/cluster has a certain number of h/w fifo queues and + output interrupt lines. An output interrupt line is routed to an interrupt + controller within a processor subsystem, and there can be more than one line + going to a specific processor's interrupt controller. The interrupt line + connections are fixed for an instance and are dictated by the IP integration + into the SoC (excluding the SoCs that have an Interrupt Crossbar or an + Interrupt Router IP). Each interrupt line is programmable through a set of + interrupt configuration registers, and have a rx and tx interrupt source per + h/w fifo. Communication between different processors is achieved through the + appropriate programming of the rx and tx interrupt sources on the appropriate + interrupt lines. + + The number of h/w fifo queues and interrupt lines dictate the usable + registers. All the current OMAP SoCs except for the newest DRA7xx SoC has a + single IP instance. DRA7xx has multiple instances with different number of + h/w fifo queues and interrupt lines between different instances. The interrupt + lines can also be routed to different processor sub-systems on DRA7xx as they + are routed through the Crossbar, a kind of interrupt router/multiplexer. The + K3 AM65x, J721E and J7200 SoCs has each of these instances form a cluster and + combine multiple clusters into a single IP block present within the Main + NavSS. The interrupt lines from all these clusters are multiplexed and routed + to different processor subsystems over a limited number of common interrupt + output lines of an Interrupt Router. The AM64x SoCS also uses a single IP + block comprising of multiple clusters, but the number of clusters are + smaller, and the interrupt output lines are connected directly to various + processors. + + Mailbox Controller Nodes + ========================= + A Mailbox device node is used to represent a Mailbox IP instance/cluster + within a SoC. The sub-mailboxes (actual communication channels) are + represented as child nodes of this parent node. + + Mailbox Users + ============== + A device needing to communicate with a target processor device should specify + them using the common mailbox binding properties, "mboxes" and the optional + "mbox-names" (please see Documentation/devicetree/bindings/mailbox/mailbox.txt + for details). Each value of the mboxes property should contain a phandle to + the mailbox controller device node and an args specifier that will be the + phandle to the intended sub-mailbox child node to be used for communication. + The equivalent "mbox-names" property value can be used to give a name to the + communication channel to be used by the client user. + +$defs: + omap-mbox-descriptor: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + The omap-mbox-descriptor is made of up of 3 cells and represents a single + uni-directional communication channel. A typical sub-mailbox device uses + two such channels - one for transmitting (Tx) and one for receiving (Rx). + items: + - description: + mailbox fifo id used either for transmitting on ti,mbox-tx channel or + for receiving on ti,mbox-rx channel (fifo_id). This is the hardware + fifo number within a mailbox cluster. + - description: + irq identifier index number to use from the parent's interrupts data. + Should be 0 for most of the cases, a positive index value is seen only + on mailboxes that have multiple interrupt lines connected to the MPU + processor (irq_id). This is an index number in the listed interrupts + property in the DT nodes. + - description: + mailbox user id for identifying the interrupt line associated with + generating a tx/rx fifo interrupt (usr_id). This is the hardware + user id number within a mailbox cluster. + + omap-sub-mailbox: + type: object + description: + The omap-sub-mailbox is a child node within a Mailbox controller device + node and represents the actual communication channel used to send and + receive messages between the host processor and a remote processor. Each + child node should have a unique node name across all the different mailbox + device nodes. + + properties: + ti,mbox-tx: + $ref: "#/$defs/omap-mbox-descriptor" + description: sub-mailbox descriptor property defining a Tx fifo. + + ti,mbox-rx: + $ref: "#/$defs/omap-mbox-descriptor" + description: sub-mailbox descriptor property defining a Rx fifo. + + ti,mbox-send-noirq: + type: boolean + description: + Quirk flag to allow the client user of this sub-mailbox to send + messages without triggering a Tx ready interrupt, and to control + the Tx ticker. Should be used only on sub-mailboxes used to + communicate with WkupM3 remote processor on AM33xx/AM43xx SoCs. + + required: + - ti,mbox-tx + - ti,mbox-rx + +properties: + compatible: + enum: + - ti,omap2-mailbox # for OMAP2420, OMAP2430 SoCs + - ti,omap3-mailbox # for OMAP3430, OMAP3630 SoCs + - ti,omap4-mailbox # for OMAP44xx, OMAP54xx, AM33xx, AM43xx and DRA7xx SoCs + - ti,am654-mailbox # for K3 AM65x, J721E and J7200 SoCs + - ti,am64-mailbox # for K3 AM64x SoCs + + reg: + maxItems: 1 + + interrupts: + description: + Contains the interrupt information for the mailbox device. The format is + dependent on which interrupt controller the Mailbox device uses. The + number of interrupts listed will at most be the value specified in + ti,mbox-num-users property, but is usually limited by the number of + interrupts reaching the main processor. An interrupt-parent property + is required on SoCs where the interrupt lines are connected through a + Interrupt Router before reaching the main processor's GIC. + + "#mbox-cells": + const: 1 + description: + The specifier is a phandle to an omap-sub-mailbox device. + + ti,mbox-num-users: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Number of targets (processor devices) that the mailbox device can + interrupt. + + ti,mbox-num-fifos: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of h/w fifo queues within the mailbox IP block. + + ti,hwmods: + $ref: /schemas/types.yaml#/definitions/string + deprecated: true + description: + Name of the hwmod associated with the mailbox. This should be defined + in the mailbox node only if the node is not defined as a child node of + a corresponding sysc interconnect node. + + This property is only needed on some legacy OMAP SoCs which have not + yet been converted to the ti,sysc interconnect hierarachy, but is + otherwise considered obsolete. + +patternProperties: + "^mbox-[a-z0-9-]+$": + $ref: "#/$defs/omap-sub-mailbox" + +required: + - compatible + - reg + - interrupts + - "#mbox-cells" + - ti,mbox-num-users + - ti,mbox-num-fifos + +allOf: + - if: + properties: + compatible: + enum: + - ti,am654-mailbox + then: + required: + - interrupt-parent + + - if: + properties: + compatible: + enum: + - ti,am654-mailbox + - ti,am64-mailbox + then: + properties: + ti,mbox-num-users: + const: 4 + ti,mbox-num-fifos: + const: 16 + interrupts: + minItems: 1 + maxItems: 4 + + - if: + properties: + compatible: + enum: + - ti,omap4-mailbox + then: + properties: + ti,mbox-num-users: + enum: [3, 4] + ti,mbox-num-fifos: + enum: [8, 12] + interrupts: + minItems: 1 + maxItems: 4 + + - if: + properties: + compatible: + enum: + - ti,omap3-mailbox + then: + properties: + ti,mbox-num-users: + const: 2 + ti,mbox-num-fifos: + const: 2 + interrupts: + minItems: 1 + maxItems: 1 + + - if: + properties: + compatible: + enum: + - ti,omap2-mailbox + then: + properties: + ti,mbox-num-users: + const: 4 + ti,mbox-num-fifos: + const: 6 + interrupts: + minItems: 1 + maxItems: 2 + +additionalProperties: false + +examples: + - | + /* OMAP4 */ + #include + mailbox: mailbox@4a0f4000 { + compatible = "ti,omap4-mailbox"; + reg = <0x4a0f4000 0x200>; + interrupts = ; + #mbox-cells = <1>; + ti,mbox-num-users = <3>; + ti,mbox-num-fifos = <8>; + + mbox_ipu: mbox-ipu { + ti,mbox-tx = <0 0 0>; + ti,mbox-rx = <1 0 0>; + }; + mbox_dsp: mbox-dsp { + ti,mbox-tx = <3 0 0>; + ti,mbox-rx = <2 0 0>; + }; + }; + + dsp { + mboxes = <&mailbox &mbox_dsp>; + }; + + - | + /* AM33xx */ + mailbox1: mailbox@480c8000 { + compatible = "ti,omap4-mailbox"; + reg = <0x480c8000 0x200>; + interrupts = <77>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <8>; + + mbox_wkupm3: mbox-wkup-m3 { + ti,mbox-tx = <0 0 0>; + ti,mbox-rx = <0 0 3>; + ti,mbox-send-noirq; + }; + }; + + - | + /* AM65x */ + mailbox0_cluster0: mailbox@31f80000 { + compatible = "ti,am654-mailbox"; + reg = <0x31f80000 0x200>; + #mbox-cells = <1>; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <16>; + interrupt-parent = <&intr_main_navss>; + interrupts = <436>; + + mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 { + ti,mbox-tx = <1 0 0>; + ti,mbox-rx = <0 0 0>; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt b/Documentation/devicetree/bindings/media/cdns,csi2rx.txt --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,100 +0,0 @@ -Cadence MIPI-CSI2 RX controller -=============================== - -The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI -lanes in input, and 4 different pixel streams in output. - -Required properties: - - compatible: must be set to "cdns,csi2rx" and an SoC-specific compatible - - reg: base address and size of the memory mapped region - - clocks: phandles to the clocks driving the controller - - clock-names: must contain: - * sys_clk: main clock - * p_clk: register bank clock - * pixel_if[0-3]_clk: pixel stream output clock, one for each stream - implemented in hardware, between 0 and 3 - -Optional properties: - - phys: phandle to the external D-PHY, phy-names must be provided - - phy-names: must contain "dphy", if the implementation uses an - external D-PHY - -Required subnodes: - - ports: A ports node with one port child node per device input and output - port, in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The - port nodes are numbered as follows: - - Port Description - ----------------------------- - 0 CSI-2 input - 1 Stream 0 output - 2 Stream 1 output - 3 Stream 2 output - 4 Stream 3 output - - The stream output port nodes are optional if they are not - connected to anything at the hardware level or implemented - in the design.Since there is only one endpoint per port, - the endpoints are not numbered. - - -Example: - -csi2rx: csi-bridge@0d060000 { - compatible = "cdns,csi2rx"; - reg = <0x0d060000 0x1000>; - clocks = <&byteclock>, <&byteclock> - <&coreclock>, <&coreclock>, - <&coreclock>, <&coreclock>; - clock-names = "sys_clk", "p_clk", - "pixel_if0_clk", "pixel_if1_clk", - "pixel_if2_clk", "pixel_if3_clk"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi2rx_in_sensor: endpoint { - remote-endpoint = <&sensor_out_csi2rx>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - - port@1 { - reg = <1>; - - csi2rx_out_grabber0: endpoint { - remote-endpoint = <&grabber0_in_csi2rx>; - }; - }; - - port@2 { - reg = <2>; - - csi2rx_out_grabber1: endpoint { - remote-endpoint = <&grabber1_in_csi2rx>; - }; - }; - - port@3 { - reg = <3>; - - csi2rx_out_grabber2: endpoint { - remote-endpoint = <&grabber2_in_csi2rx>; - }; - }; - - port@4 { - reg = <4>; - - csi2rx_out_grabber3: endpoint { - remote-endpoint = <&grabber3_in_csi2rx>; - }; - }; - }; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,172 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/cdns,csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MIPI-CSI2 RX controller + +description: | + The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI + lanes in input, and 4 different pixel streams in output. + +maintainers: + - Pratyush Yadav + +properties: + compatible: + contains: + const: cdns,csi2rx + + reg: + maxItems: 1 + + clocks: + minItems: 3 + maxItems: 6 + + clock-names: + minItems: 3 + maxItems: 6 + items: + - const: sys_clk # main clock + - const: p_clk # register bank clock + - const: pixel_if0_clk # pixel stream 0 output clock + - const: pixel_if1_clk # pixel stream 1 output clock + - const: pixel_if2_clk # pixel stream 2 output clock + - const: pixel_if3_clk # pixel stream 3 output clock + + phys: + maxItems: 1 + description: phandle to the external D-PHY + + phy-names: + items: + - const: dphy + + power-domains: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 input + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + clock-lanes: + maxItems: 1 + + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + uniqueItems: true + items: + maximum: 4 + + required: + - clock-lanes + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Stream 0 output + + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: Stream 1 output + + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: Stream 2 output + + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: Stream 3 output + + required: + - port@0 + + +dependencies: + phys: [ 'phy-names' ] + phy-names: [ 'phys' ] + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + csi2rx: csi-bridge@d060000 { + compatible = "cdns,csi2rx"; + reg = <0x0d060000 0x1000>; + clocks = <&byteclock>, <&byteclock>, + <&coreclock>, <&coreclock>, + <&coreclock>, <&coreclock>; + clock-names = "sys_clk", "p_clk", + "pixel_if0_clk", "pixel_if1_clk", + "pixel_if2_clk", "pixel_if3_clk"; + phys = <&dphy0>; + phy-names = "dphy"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2rx_in_sensor: endpoint { + remote-endpoint = <&sensor_out_csi2rx>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + + csi2rx_out_grabber0: endpoint { + remote-endpoint = <&grabber0_in_csi2rx>; + }; + }; + + port@2 { + reg = <2>; + + csi2rx_out_grabber1: endpoint { + remote-endpoint = <&grabber1_in_csi2rx>; + }; + }; + + port@3 { + reg = <3>; + + csi2rx_out_grabber2: endpoint { + remote-endpoint = <&grabber2_in_csi2rx>; + }; + }; + + port@4 { + reg = <4>; + + csi2rx_out_grabber3: endpoint { + remote-endpoint = <&grabber3_in_csi2rx>; + }; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/img,d5500-vxd.yaml b/Documentation/devicetree/bindings/media/img,d5500-vxd.yaml --- a/Documentation/devicetree/bindings/media/img,d5500-vxd.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/img,d5500-vxd.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/img,d5500-vxd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Imagination D5520-VXD Driver + +maintainers: + - Sidraya Jayagond + - Prashanth Kumar Amai + +description: | + The IMG VXD video decode driver for the D5500-VXD is a video decoder for + multiple video formats including H.264 and HEVC on the TI J721E family + of SoCs. + +properties: + compatible: + const: img,d5500-vxd + + reg: + maxItems: 2 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + d5520: video-decoder@4300000 { + /* IMG D5520 driver configuration */ + compatible = "img,d5500-vxd"; + reg = <0x00 0x04300000>, + <0x00 0x100000>; + power-domains = <&k3_pds 144>; + interrupts = ; + }; + +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/img,vxe384.yaml b/Documentation/devicetree/bindings/media/img,vxe384.yaml --- a/Documentation/devicetree/bindings/media/img,vxe384.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/img,vxe384.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/img,vxe384.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Imagination VXE384 Driver + +maintainers: + - Sidraya Jayagond + +description: | + The IMG VXE384 video encode driver for the VXE384 is a video encoder for + multiple video formats including H.264 on the TI J721E family of SoCs. + +properties: + compatible: + const: img,vxe384 + + reg: + maxItems: 2 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + vxe384: video-encoder@4200000 { + compatible = "img,vxe384"; + reg = <0x00 0x04200000>, + <0x00 0x100000>; + power-domains = <&k3_pds 153>; + interrupts = ; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx.yaml b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx.yaml --- a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/ti,j721e-csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI J721E CSI2RX Wrapper Device Tree Bindings + +description: | + The TI J721E CSI2RX Wrapper is a wrapper around Cadence CSI2RX bridge that + enables sending captured frames to memory over PSI-L DMA. In the J721E + Technical Reference Manual (SPRUIL1B) it is referred to as "SHIM" under the + CSI_RX_IF section. + +maintainers: + - Pratyush Yadav + +properties: + compatible: + items: + - const: ti,j721e-csi2rx + + dmas: + maxItems: 1 + + dma-names: + items: + - const: rx0 + + reg: + maxItems: 1 + + power-domains: + maxItems: 1 + + ranges: true + + "#address-cells": true + + "#size-cells": true + +patternProperties: + "^csi-bridge@": + type: object + description: CSI2 bridge node. + $ref: cdns,csi2rx.yaml# + +required: + - compatible + - reg + - dmas + - dma-names + - power-domains + - ranges + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + #include + + ti_csi2rx0: ticsi2rx@4500000 { + compatible = "ti,j721e-csi2rx"; + dmas = <&main_udmap 0x4940>; + dma-names = "rx0"; + reg = <0x4500000 0x1000>; + power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + cdns_csi2rx: csi-bridge@4504000 { + compatible = "cdns,csi2rx"; + reg = <0x4504000 0x1000>; + clocks = <&k3_clks 26 2>, <&k3_clks 26 0>, <&k3_clks 26 2>, + <&k3_clks 26 2>, <&k3_clks 26 3>, <&k3_clks 26 3>; + clock-names = "sys_clk", "p_clk", "pixel_if0_clk", + "pixel_if1_clk", "pixel_if2_clk", "pixel_if3_clk"; + phys = <&dphy0>; + phy-names = "dphy"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + csi2_0: port@0 { + + reg = <0>; + + csi2rx0_in_sensor: endpoint { + remote-endpoint = <&csi2_cam0>; + bus-type = <4>; /* CSI2 DPHY. */ + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/video-interface-devices.yaml b/Documentation/devicetree/bindings/media/video-interface-devices.yaml --- a/Documentation/devicetree/bindings/media/video-interface-devices.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/video-interface-devices.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,406 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/video-interface-devices.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common bindings for video receiver and transmitter devices + +maintainers: + - Jacopo Mondi + - Sakari Ailus + +properties: + flash-leds: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + An array of phandles, each referring to a flash LED, a sub-node of the LED + driver device node. + + lens-focus: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle to the node of the focus lens controller. + + rotation: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 90, 180, 270 ] + description: | + The camera rotation is expressed as the angular difference in degrees + between two reference systems, one relative to the camera module, and one + defined on the external world scene to be captured when projected on the + image sensor pixel array. + + A camera sensor has a 2-dimensional reference system 'Rc' defined by its + pixel array read-out order. The origin is set to the first pixel being + read out, the X-axis points along the column read-out direction towards + the last columns, and the Y-axis along the row read-out direction towards + the last row. + + A typical example for a sensor with a 2592x1944 pixel array matrix + observed from the front is: + + 2591 X-axis 0 + <------------------------+ 0 + .......... ... ..........! + .......... ... ..........! Y-axis + ... ! + .......... ... ..........! + .......... ... ..........! 1943 + V + + The external world scene reference system 'Rs' is a 2-dimensional + reference system on the focal plane of the camera module. The origin is + placed on the top-left corner of the visible scene, the X-axis points + towards the right, and the Y-axis points towards the bottom of the scene. + The top, bottom, left and right directions are intentionally not defined + and depend on the environment in which the camera is used. + + A typical example of a (very common) picture of a shark swimming from left + to right, as seen from the camera, is: + + 0 X-axis + 0 +-------------------------------------> + ! + ! + ! + ! |\____)\___ + ! ) _____ __`< + ! |/ )/ + ! + ! + ! + V + Y-axis + + with the reference system 'Rs' placed on the camera focal plane: + + ¸.·˙! + ¸.·˙ ! + _ ¸.·˙ ! + +-/ \-+¸.·˙ ! + | (o) | ! Camera focal plane + +-----+˙·.¸ ! + ˙·.¸ ! + ˙·.¸ ! + ˙·.¸! + + When projected on the sensor's pixel array, the image and the associated + reference system 'Rs' are typically (but not always) inverted, due to the + camera module's lens optical inversion effect. + + Assuming the above represented scene of the swimming shark, the lens + inversion projects the scene and its reference system onto the sensor + pixel array, seen from the front of the camera sensor, as follows: + + Y-axis + ^ + ! + ! + ! + ! |\_____)\__ + ! ) ____ ___.< + ! |/ )/ + ! + ! + ! + 0 +-------------------------------------> + 0 X-axis + + Note the shark being upside-down. + + The resulting projected reference system is named 'Rp'. + + The camera rotation property is then defined as the angular difference in + the counter-clockwise direction between the camera reference system 'Rc' + and the projected scene reference system 'Rp'. It is expressed in degrees + as a number in the range [0, 360[. + + Examples + + 0 degrees camera rotation: + + + Y-Rp + ^ + Y-Rc ! + ^ ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + 0 +-------------------------------------> + 0 X-Rc + + + X-Rc 0 + <------------------------------------+ 0 + X-Rp 0 ! + <------------------------------------+ 0 ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! V + ! Y-Rc + V + Y-Rp + + 90 degrees camera rotation: + + 0 Y-Rc + 0 +--------------------> + ! Y-Rp + ! ^ + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + ! + ! + ! + ! + V + X-Rc + + 180 degrees camera rotation: + + 0 + <------------------------------------+ 0 + X-Rc ! + Y-Rp ! + ^ ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! V + ! Y-Rc + 0 +-------------------------------------> + 0 X-Rp + + 270 degrees camera rotation: + + 0 Y-Rc + 0 +--------------------> + ! 0 + ! <-----------------------------------+ 0 + ! X-Rp ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! V + ! Y-Rp + ! + ! + ! + ! + V + X-Rc + + + Example one - Webcam + + A camera module installed on the user facing part of a laptop screen + casing used for video calls. The captured images are meant to be displayed + in landscape mode (width > height) on the laptop screen. + + The camera is typically mounted upside-down to compensate the lens optical + inversion effect: + + Y-Rp + Y-Rc ^ + ^ ! + ! ! + ! ! |\_____)\__ + ! ! ) ____ ___.< + ! ! |/ )/ + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + 0 +-------------------------------------> + 0 X-Rc + + The two reference systems are aligned, the resulting camera rotation is + 0 degrees, no rotation correction needs to be applied to the resulting + image once captured to memory buffers to correctly display it to users: + + +--------------------------------------+ + ! ! + ! ! + ! ! + ! |\____)\___ ! + ! ) _____ __`< ! + ! |/ )/ ! + ! ! + ! ! + ! ! + +--------------------------------------+ + + If the camera sensor is not mounted upside-down to compensate for the lens + optical inversion, the two reference systems will not be aligned, with + 'Rp' being rotated 180 degrees relatively to 'Rc': + + + X-Rc 0 + <------------------------------------+ 0 + ! + Y-Rp ! + ^ ! + ! ! + ! |\_____)\__ ! + ! ) ____ ___.< ! + ! |/ )/ ! + ! ! + ! ! + ! V + ! Y-Rc + 0 +-------------------------------------> + 0 X-Rp + + The image once captured to memory will then be rotated by 180 degrees: + + +--------------------------------------+ + ! ! + ! ! + ! ! + ! __/(_____/| ! + ! >.___ ____ ( ! + ! \( \| ! + ! ! + ! ! + ! ! + +--------------------------------------+ + + A software rotation correction of 180 degrees should be applied to + correctly display the image: + + +--------------------------------------+ + ! ! + ! ! + ! ! + ! |\____)\___ ! + ! ) _____ __`< ! + ! |/ )/ ! + ! ! + ! ! + ! ! + +--------------------------------------+ + + Example two - Phone camera + + A camera installed on the back side of a mobile device facing away from + the user. The captured images are meant to be displayed in portrait mode + (height > width) to match the device screen orientation and the device + usage orientation used when taking the picture. + + The camera sensor is typically mounted with its pixel array longer side + aligned to the device longer side, upside-down mounted to compensate for + the lens optical inversion effect: + + 0 Y-Rc + 0 +--------------------> + ! Y-Rp + ! ^ + ! ! + ! ! + ! ! + ! ! |\_____)\__ + ! ! ) ____ ___.< + ! ! |/ )/ + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + ! + ! + ! + ! + V + X-Rc + + The two reference systems are not aligned and the 'Rp' reference system is + rotated by 90 degrees in the counter-clockwise direction relatively to the + 'Rc' reference system. + + The image once captured to memory will be rotated: + + +-------------------------------------+ + | _ _ | + | \ / | + | | | | + | | | | + | | > | + | < | | + | | | | + | . | + | V | + +-------------------------------------+ + + A correction of 90 degrees in counter-clockwise direction has to be + applied to correctly display the image in portrait mode on the device + screen: + + +--------------------+ + | | + | | + | | + | | + | | + | | + | |\____)\___ | + | ) _____ __`< | + | |/ )/ | + | | + | | + | | + | | + | | + +--------------------+ + + orientation: + description: + The orientation of a device (typically an image sensor or a flash LED) + describing its mounting position relative to the usage orientation of the + system where the device is installed on. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + # Front. The device is mounted on the front facing side of the system. For + # mobile devices such as smartphones, tablets and laptops the front side + # is the user facing side. + - 0 + # Back. The device is mounted on the back side of the system, which is + # defined as the opposite side of the front facing one. + - 1 + # External. The device is not attached directly to the system but is + # attached in a way that allows it to move freely. + - 2 + +additionalProperties: true + +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt --- a/Documentation/devicetree/bindings/media/video-interfaces.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt 2022-01-06 12:45:53.806318073 -0500 @@ -1,639 +1 @@ -Common bindings for video receiver and transmitter interfaces - -General concept ---------------- - -Video data pipelines usually consist of external devices, e.g. camera sensors, -controlled over an I2C, SPI or UART bus, and SoC internal IP blocks, including -video DMA engines and video data processors. - -SoC internal blocks are described by DT nodes, placed similarly to other SoC -blocks. External devices are represented as child nodes of their respective -bus controller nodes, e.g. I2C. - -Data interfaces on all video devices are described by their child 'port' nodes. -Configuration of a port depends on other devices participating in the data -transfer and is described by 'endpoint' subnodes. - -device { - ... - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - ... - endpoint@0 { ... }; - endpoint@1 { ... }; - }; - port@1 { ... }; - }; -}; - -If a port can be configured to work with more than one remote device on the same -bus, an 'endpoint' child node must be provided for each of them. If more than -one port is present in a device node or there is more than one endpoint at a -port, or port node needs to be associated with a selected hardware interface, -a common scheme using '#address-cells', '#size-cells' and 'reg' properties is -used. - -All 'port' nodes can be grouped under optional 'ports' node, which allows to -specify #address-cells, #size-cells properties independently for the 'port' -and 'endpoint' nodes and any child device nodes a device might have. - -Two 'endpoint' nodes are linked with each other through their 'remote-endpoint' -phandles. An endpoint subnode of a device contains all properties needed for -configuration of this device for data exchange with other device. In most -cases properties at the peer 'endpoint' nodes will be identical, however they -might need to be different when there is any signal modifications on the bus -between two devices, e.g. there are logic signal inverters on the lines. - -It is allowed for multiple endpoints at a port to be active simultaneously, -where supported by a device. For example, in case where a data interface of -a device is partitioned into multiple data busses, e.g. 16-bit input port -divided into two separate ITU-R BT.656 8-bit busses. In such case bus-width -and data-shift properties can be used to assign physical data lines to each -endpoint node (logical bus). - -Documenting bindings for devices --------------------------------- - -All required and optional bindings the device supports shall be explicitly -documented in device DT binding documentation. This also includes port and -endpoint nodes for the device, including unit-addresses and reg properties where -relevant. - -Please also see Documentation/devicetree/bindings/graph.txt . - -Required properties -------------------- - -If there is more than one 'port' or more than one 'endpoint' node or 'reg' -property is present in port and/or endpoint nodes the following properties -are required in a relevant parent node: - - - #address-cells : number of cells required to define port/endpoint - identifier, should be 1. - - #size-cells : should be zero. - - -Optional properties -------------------- - -- flash-leds: An array of phandles, each referring to a flash LED, a sub-node - of the LED driver device node. - -- lens-focus: A phandle to the node of the focus lens controller. - -- rotation: The camera rotation is expressed as the angular difference in - degrees between two reference systems, one relative to the camera module, and - one defined on the external world scene to be captured when projected on the - image sensor pixel array. - - A camera sensor has a 2-dimensional reference system 'Rc' defined by - its pixel array read-out order. The origin is set to the first pixel - being read out, the X-axis points along the column read-out direction - towards the last columns, and the Y-axis along the row read-out - direction towards the last row. - - A typical example for a sensor with a 2592x1944 pixel array matrix - observed from the front is: - - 2591 X-axis 0 - <------------------------+ 0 - .......... ... ..........! - .......... ... ..........! Y-axis - ... ! - .......... ... ..........! - .......... ... ..........! 1943 - V - - The external world scene reference system 'Rs' is a 2-dimensional - reference system on the focal plane of the camera module. The origin is - placed on the top-left corner of the visible scene, the X-axis points - towards the right, and the Y-axis points towards the bottom of the - scene. The top, bottom, left and right directions are intentionally not - defined and depend on the environment in which the camera is used. - - A typical example of a (very common) picture of a shark swimming from - left to right, as seen from the camera, is: - - 0 X-axis - 0 +-------------------------------------> - ! - ! - ! - ! |\____)\___ - ! ) _____ __`< - ! |/ )/ - ! - ! - ! - V - Y-axis - - with the reference system 'Rs' placed on the camera focal plane: - - ¸.·˙! - ¸.·˙ ! - _ ¸.·˙ ! - +-/ \-+¸.·˙ ! - | (o) | ! Camera focal plane - +-----+˙·.¸ ! - ˙·.¸ ! - ˙·.¸ ! - ˙·.¸! - - When projected on the sensor's pixel array, the image and the associated - reference system 'Rs' are typically (but not always) inverted, due to - the camera module's lens optical inversion effect. - - Assuming the above represented scene of the swimming shark, the lens - inversion projects the scene and its reference system onto the sensor - pixel array, seen from the front of the camera sensor, as follows: - - Y-axis - ^ - ! - ! - ! - ! |\_____)\__ - ! ) ____ ___.< - ! |/ )/ - ! - ! - ! - 0 +-------------------------------------> - 0 X-axis - - Note the shark being upside-down. - - The resulting projected reference system is named 'Rp'. - - The camera rotation property is then defined as the angular difference - in the counter-clockwise direction between the camera reference system - 'Rc' and the projected scene reference system 'Rp'. It is expressed in - degrees as a number in the range [0, 360[. - - Examples - - 0 degrees camera rotation: - - - Y-Rp - ^ - Y-Rc ! - ^ ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - 0 +-------------------------------------> - 0 X-Rc - - - X-Rc 0 - <------------------------------------+ 0 - X-Rp 0 ! - <------------------------------------+ 0 ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! V - ! Y-Rc - V - Y-Rp - - 90 degrees camera rotation: - - 0 Y-Rc - 0 +--------------------> - ! Y-Rp - ! ^ - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - ! - ! - ! - ! - V - X-Rc - - 180 degrees camera rotation: - - 0 - <------------------------------------+ 0 - X-Rc ! - Y-Rp ! - ^ ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! V - ! Y-Rc - 0 +-------------------------------------> - 0 X-Rp - - 270 degrees camera rotation: - - 0 Y-Rc - 0 +--------------------> - ! 0 - ! <-----------------------------------+ 0 - ! X-Rp ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! V - ! Y-Rp - ! - ! - ! - ! - V - X-Rc - - - Example one - Webcam - - A camera module installed on the user facing part of a laptop screen - casing used for video calls. The captured images are meant to be - displayed in landscape mode (width > height) on the laptop screen. - - The camera is typically mounted upside-down to compensate the lens - optical inversion effect: - - Y-Rp - Y-Rc ^ - ^ ! - ! ! - ! ! |\_____)\__ - ! ! ) ____ ___.< - ! ! |/ )/ - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - 0 +-------------------------------------> - 0 X-Rc - - The two reference systems are aligned, the resulting camera rotation is - 0 degrees, no rotation correction needs to be applied to the resulting - image once captured to memory buffers to correctly display it to users: - - +--------------------------------------+ - ! ! - ! ! - ! ! - ! |\____)\___ ! - ! ) _____ __`< ! - ! |/ )/ ! - ! ! - ! ! - ! ! - +--------------------------------------+ - - If the camera sensor is not mounted upside-down to compensate for the - lens optical inversion, the two reference systems will not be aligned, - with 'Rp' being rotated 180 degrees relatively to 'Rc': - - - X-Rc 0 - <------------------------------------+ 0 - ! - Y-Rp ! - ^ ! - ! ! - ! |\_____)\__ ! - ! ) ____ ___.< ! - ! |/ )/ ! - ! ! - ! ! - ! V - ! Y-Rc - 0 +-------------------------------------> - 0 X-Rp - - The image once captured to memory will then be rotated by 180 degrees: - - +--------------------------------------+ - ! ! - ! ! - ! ! - ! __/(_____/| ! - ! >.___ ____ ( ! - ! \( \| ! - ! ! - ! ! - ! ! - +--------------------------------------+ - - A software rotation correction of 180 degrees should be applied to - correctly display the image: - - +--------------------------------------+ - ! ! - ! ! - ! ! - ! |\____)\___ ! - ! ) _____ __`< ! - ! |/ )/ ! - ! ! - ! ! - ! ! - +--------------------------------------+ - - Example two - Phone camera - - A camera installed on the back side of a mobile device facing away from - the user. The captured images are meant to be displayed in portrait mode - (height > width) to match the device screen orientation and the device - usage orientation used when taking the picture. - - The camera sensor is typically mounted with its pixel array longer side - aligned to the device longer side, upside-down mounted to compensate for - the lens optical inversion effect: - - 0 Y-Rc - 0 +--------------------> - ! Y-Rp - ! ^ - ! ! - ! ! - ! ! - ! ! |\_____)\__ - ! ! ) ____ ___.< - ! ! |/ )/ - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - ! - ! - ! - ! - V - X-Rc - - The two reference systems are not aligned and the 'Rp' reference - system is rotated by 90 degrees in the counter-clockwise direction - relatively to the 'Rc' reference system. - - The image once captured to memory will be rotated: - - +-------------------------------------+ - | _ _ | - | \ / | - | | | | - | | | | - | | > | - | < | | - | | | | - | . | - | V | - +-------------------------------------+ - - A correction of 90 degrees in counter-clockwise direction has to be - applied to correctly display the image in portrait mode on the device - screen: - - +--------------------+ - | | - | | - | | - | | - | | - | | - | |\____)\___ | - | ) _____ __`< | - | |/ )/ | - | | - | | - | | - | | - | | - +--------------------+ - -- orientation: The orientation of a device (typically an image sensor or a flash - LED) describing its mounting position relative to the usage orientation of the - system where the device is installed on. - Possible values are: - 0 - Front. The device is mounted on the front facing side of the system. - For mobile devices such as smartphones, tablets and laptops the front side is - the user facing side. - 1 - Back. The device is mounted on the back side of the system, which is - defined as the opposite side of the front facing one. - 2 - External. The device is not attached directly to the system but is - attached in a way that allows it to move freely. - -Optional endpoint properties ----------------------------- - -- remote-endpoint: phandle to an 'endpoint' subnode of a remote device node. -- slave-mode: a boolean property indicating that the link is run in slave mode. - The default when this property is not specified is master mode. In the slave - mode horizontal and vertical synchronization signals are provided to the - slave device (data source) by the master device (data sink). In the master - mode the data source device is also the source of the synchronization signals. -- bus-type: data bus type. Possible values are: - 1 - MIPI CSI-2 C-PHY - 2 - MIPI CSI1 - 3 - CCP2 - 4 - MIPI CSI-2 D-PHY - 5 - Parallel - 6 - Bt.656 -- bus-width: number of data lines actively used, valid for the parallel busses. -- data-shift: on the parallel data busses, if bus-width is used to specify the - number of data lines, data-shift can be used to specify which data lines are - used, e.g. "bus-width=<8>; data-shift=<2>;" means, that lines 9:2 are used. -- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. -- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. - Note, that if HSYNC and VSYNC polarities are not specified, embedded - synchronization may be required, where supported. -- data-active: similar to HSYNC and VSYNC, specifies data line polarity. -- data-enable-active: similar to HSYNC and VSYNC, specifies the data enable - signal polarity. -- field-even-active: field signal level during the even field data transmission. -- pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock - signal. -- sync-on-green-active: active state of Sync-on-green (SoG) signal, 0/1 for - LOW/HIGH respectively. -- data-lanes: an array of physical data lane indexes. Position of an entry - determines the logical lane number, while the value of an entry indicates - physical lane, e.g. for 2-lane MIPI CSI-2 bus we could have - "data-lanes = <1 2>;", assuming the clock lane is on hardware lane 0. - If the hardware does not support lane reordering, monotonically - incremented values shall be used from 0 or 1 onwards, depending on - whether or not there is also a clock lane. This property is valid for - serial busses only (e.g. MIPI CSI-2). -- clock-lanes: an array of physical clock lane indexes. Position of an entry - determines the logical lane number, while the value of an entry indicates - physical lane, e.g. for a MIPI CSI-2 bus we could have "clock-lanes = <0>;", - which places the clock lane on hardware lane 0. This property is valid for - serial busses only (e.g. MIPI CSI-2). Note that for the MIPI CSI-2 bus this - array contains only one entry. -- clock-noncontinuous: a boolean property to allow MIPI CSI-2 non-continuous - clock mode. -- link-frequencies: Allowed data bus frequencies. For MIPI CSI-2, for - instance, this is the actual frequency of the bus, not bits per clock per - lane value. An array of 64-bit unsigned integers. -- lane-polarities: an array of polarities of the lanes starting from the clock - lane and followed by the data lanes in the same order as in data-lanes. - Valid values are 0 (normal) and 1 (inverted). The length of the array - should be the combined length of data-lanes and clock-lanes properties. - If the lane-polarities property is omitted, the value must be interpreted - as 0 (normal). This property is valid for serial busses only. -- strobe: Whether the clock signal is used as clock (0) or strobe (1). Used - with CCP2, for instance. - -Example -------- - -The example snippet below describes two data pipelines. ov772x and imx074 are -camera sensors with a parallel and serial (MIPI CSI-2) video bus respectively. -Both sensors are on the I2C control bus corresponding to the i2c0 controller -node. ov772x sensor is linked directly to the ceu0 video host interface. -imx074 is linked to ceu0 through the MIPI CSI-2 receiver (csi2). ceu0 has a -(single) DMA engine writing captured data to memory. ceu0 node has a single -'port' node which may indicate that at any time only one of the following data -pipelines can be active: ov772x -> ceu0 or imx074 -> csi2 -> ceu0. - - ceu0: ceu@fe910000 { - compatible = "renesas,sh-mobile-ceu"; - reg = <0xfe910000 0xa0>; - interrupts = <0x880>; - - mclk: master_clock { - compatible = "renesas,ceu-clock"; - #clock-cells = <1>; - clock-frequency = <50000000>; /* Max clock frequency */ - clock-output-names = "mclk"; - }; - - port { - #address-cells = <1>; - #size-cells = <0>; - - /* Parallel bus endpoint */ - ceu0_1: endpoint@1 { - reg = <1>; /* Local endpoint # */ - remote = <&ov772x_1_1>; /* Remote phandle */ - bus-width = <8>; /* Used data lines */ - data-shift = <2>; /* Lines 9:2 are used */ - - /* If hsync-active/vsync-active are missing, - embedded BT.656 sync is used */ - hsync-active = <0>; /* Active low */ - vsync-active = <0>; /* Active low */ - data-active = <1>; /* Active high */ - pclk-sample = <1>; /* Rising */ - }; - - /* MIPI CSI-2 bus endpoint */ - ceu0_0: endpoint@0 { - reg = <0>; - remote = <&csi2_2>; - }; - }; - }; - - i2c0: i2c@fff20000 { - ... - ov772x_1: camera@21 { - compatible = "ovti,ov772x"; - reg = <0x21>; - vddio-supply = <®ulator1>; - vddcore-supply = <®ulator2>; - - clock-frequency = <20000000>; - clocks = <&mclk 0>; - clock-names = "xclk"; - - port { - /* With 1 endpoint per port no need for addresses. */ - ov772x_1_1: endpoint { - bus-width = <8>; - remote-endpoint = <&ceu0_1>; - hsync-active = <1>; - vsync-active = <0>; /* Who came up with an - inverter here ?... */ - data-active = <1>; - pclk-sample = <1>; - }; - }; - }; - - imx074: camera@1a { - compatible = "sony,imx074"; - reg = <0x1a>; - vddio-supply = <®ulator1>; - vddcore-supply = <®ulator2>; - - clock-frequency = <30000000>; /* Shared clock with ov772x_1 */ - clocks = <&mclk 0>; - clock-names = "sysclk"; /* Assuming this is the - name in the datasheet */ - port { - imx074_1: endpoint { - clock-lanes = <0>; - data-lanes = <1 2>; - remote-endpoint = <&csi2_1>; - }; - }; - }; - }; - - csi2: csi2@ffc90000 { - compatible = "renesas,sh-mobile-csi2"; - reg = <0xffc90000 0x1000>; - interrupts = <0x17a0>; - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - compatible = "renesas,csi2c"; /* One of CSI2I and CSI2C. */ - reg = <1>; /* CSI-2 PHY #1 of 2: PHY_S, - PHY_M has port address 0, - is unused. */ - csi2_1: endpoint { - clock-lanes = <0>; - data-lanes = <2 1>; - remote-endpoint = <&imx074_1>; - }; - }; - port@2 { - reg = <2>; /* port 2: link to the CEU */ - - csi2_2: endpoint { - remote-endpoint = <&ceu0_0>; - }; - }; - }; +This file has moved to video-interfaces.yaml and video-interface-devices.yaml. diff -Naur --no-dereference a/Documentation/devicetree/bindings/media/video-interfaces.yaml b/Documentation/devicetree/bindings/media/video-interfaces.yaml --- a/Documentation/devicetree/bindings/media/video-interfaces.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/media/video-interfaces.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,217 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/video-interfaces.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common bindings for video receiver and transmitter interface endpoints + +maintainers: + - Sakari Ailus + - Laurent Pinchart + +description: | + Video data pipelines usually consist of external devices, e.g. camera sensors, + controlled over an I2C, SPI or UART bus, and SoC internal IP blocks, including + video DMA engines and video data processors. + + SoC internal blocks are described by DT nodes, placed similarly to other SoC + blocks. External devices are represented as child nodes of their respective + bus controller nodes, e.g. I2C. + + Data interfaces on all video devices are described by their child 'port' nodes. + Configuration of a port depends on other devices participating in the data + transfer and is described by 'endpoint' subnodes. + + device { + ... + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + ... + endpoint@0 { ... }; + endpoint@1 { ... }; + }; + port@1 { ... }; + }; + }; + + If a port can be configured to work with more than one remote device on the same + bus, an 'endpoint' child node must be provided for each of them. If more than + one port is present in a device node or there is more than one endpoint at a + port, or port node needs to be associated with a selected hardware interface, + a common scheme using '#address-cells', '#size-cells' and 'reg' properties is + used. + + All 'port' nodes can be grouped under optional 'ports' node, which allows to + specify #address-cells, #size-cells properties independently for the 'port' + and 'endpoint' nodes and any child device nodes a device might have. + + Two 'endpoint' nodes are linked with each other through their 'remote-endpoint' + phandles. An endpoint subnode of a device contains all properties needed for + configuration of this device for data exchange with other device. In most + cases properties at the peer 'endpoint' nodes will be identical, however they + might need to be different when there is any signal modifications on the bus + between two devices, e.g. there are logic signal inverters on the lines. + + It is allowed for multiple endpoints at a port to be active simultaneously, + where supported by a device. For example, in case where a data interface of + a device is partitioned into multiple data busses, e.g. 16-bit input port + divided into two separate ITU-R BT.656 8-bit busses. In such case bus-width + and data-shift properties can be used to assign physical data lines to each + endpoint node (logical bus). + + Documenting bindings for devices + -------------------------------- + + All required and optional bindings the device supports shall be explicitly + documented in device DT binding documentation. This also includes port and + endpoint nodes for the device, including unit-addresses and reg properties + where relevant. + +allOf: + - $ref: /schemas/graph.yaml#/$defs/endpoint-base + +properties: + slave-mode: + type: boolean + description: + Indicates that the link is run in slave mode. The default when this + property is not specified is master mode. In the slave mode horizontal and + vertical synchronization signals are provided to the slave device (data + source) by the master device (data sink). In the master mode the data + source device is also the source of the synchronization signals. + + bus-type: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 1 # MIPI CSI-2 C-PHY + - 2 # MIPI CSI1 + - 3 # CCP2 + - 4 # MIPI CSI-2 D-PHY + - 5 # Parallel + - 6 # BT.656 + description: + Data bus type. + + bus-width: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 64 + description: + Number of data lines actively used, valid for the parallel busses. + + data-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 64 + description: + On the parallel data busses, if bus-width is used to specify the number of + data lines, data-shift can be used to specify which data lines are used, + e.g. "bus-width=<8>; data-shift=<2>;" means, that lines 9:2 are used. + + hsync-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. + + vsync-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. Note, + that if HSYNC and VSYNC polarities are not specified, embedded + synchronization may be required, where supported. + + data-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Similar to HSYNC and VSYNC, specifies data line polarity. + + data-enable-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Similar to HSYNC and VSYNC, specifies the data enable signal polarity. + + field-even-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Field signal level during the even field data transmission. + + pclk-sample: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Sample data on rising (1) or falling (0) edge of the pixel clock signal. + + sync-on-green-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. + + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 8 + items: + # Assume up to 9 physical lane indices + maximum: 8 + description: + An array of physical data lane indexes. Position of an entry determines + the logical lane number, while the value of an entry indicates physical + lane, e.g. for 2-lane MIPI CSI-2 bus we could have "data-lanes = <1 2>;", + assuming the clock lane is on hardware lane 0. If the hardware does not + support lane reordering, monotonically incremented values shall be used + from 0 or 1 onwards, depending on whether or not there is also a clock + lane. This property is valid for serial busses only (e.g. MIPI CSI-2). + + clock-lanes: + $ref: /schemas/types.yaml#/definitions/uint32 + # Assume up to 9 physical lane indices + maximum: 8 + description: + Physical clock lane index. Position of an entry determines the logical + lane number, while the value of an entry indicates physical lane, e.g. for + a MIPI CSI-2 bus we could have "clock-lanes = <0>;", which places the + clock lane on hardware lane 0. This property is valid for serial busses + only (e.g. MIPI CSI-2). + + clock-noncontinuous: + type: boolean + description: + Allow MIPI CSI-2 non-continuous clock mode. + + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: + Allowed data bus frequencies. For MIPI CSI-2, for instance, this is the + actual frequency of the bus, not bits per clock per lane value. An array + of 64-bit unsigned integers. + + lane-polarities: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 9 + items: + enum: [ 0, 1 ] + description: + An array of polarities of the lanes starting from the clock lane and + followed by the data lanes in the same order as in data-lanes. Valid + values are 0 (normal) and 1 (inverted). The length of the array should be + the combined length of data-lanes and clock-lanes properties. If the + lane-polarities property is omitted, the value must be interpreted as 0 + (normal). This property is valid for serial busses only. + + strobe: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Whether the clock signal is used as clock (0) or strobe (1). Used with + CCP2, for instance. + +additionalProperties: true diff -Naur --no-dereference a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt --- a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,157 +0,0 @@ -Device tree bindings for OMAP general purpose memory controllers (GPMC) - -The actual devices are instantiated from the child nodes of a GPMC node. - -Required properties: - - - compatible: Should be set to one of the following: - - ti,omap2420-gpmc (omap2420) - ti,omap2430-gpmc (omap2430) - ti,omap3430-gpmc (omap3430 & omap3630) - ti,omap4430-gpmc (omap4430 & omap4460 & omap543x) - ti,am3352-gpmc (am335x devices) - - - reg: A resource specifier for the register space - (see the example below) - - ti,hwmods: Should be set to "ti,gpmc" until the DT transition is - completed. - - #address-cells: Must be set to 2 to allow memory address translation - - #size-cells: Must be set to 1 to allow CS address passing - - gpmc,num-cs: The maximum number of chip-select lines that controller - can support. - - gpmc,num-waitpins: The maximum number of wait pins that controller can - support. - - ranges: Must be set up to reflect the memory layout with four - integer values for each chip-select line in use: - - 0 - - Currently, calculated values derived from the contents - of the per-CS register GPMC_CONFIG7 (as set up by the - bootloader) are used for the physical address decoding. - As this will change in the future, filling correct - values here is a requirement. - - interrupt-controller: The GPMC driver implements and interrupt controller for - the NAND events "fifoevent" and "termcount" plus the - rising/falling edges on the GPMC_WAIT pins. - The interrupt number mapping is as follows - 0 - NAND_fifoevent - 1 - NAND_termcount - 2 - GPMC_WAIT0 pin edge - 3 - GPMC_WAIT1 pin edge, and so on. - - interrupt-cells: Must be set to 2 - - gpio-controller: The GPMC driver implements a GPIO controller for the - GPMC WAIT pins that can be used as general purpose inputs. - 0 maps to GPMC_WAIT0 pin. - - gpio-cells: Must be set to 2 - -Required properties when using NAND prefetch dma: - - dmas GPMC NAND prefetch dma channel - - dma-names Must be set to "rxtx" - -Timing properties for child nodes. All are optional and default to 0. - - - gpmc,sync-clk-ps: Minimum clock period for synchronous mode, in picoseconds - - Chip-select signal timings (in nanoseconds) corresponding to GPMC_CONFIG2: - - gpmc,cs-on-ns: Assertion time - - gpmc,cs-rd-off-ns: Read deassertion time - - gpmc,cs-wr-off-ns: Write deassertion time - - ADV signal timings (in nanoseconds) corresponding to GPMC_CONFIG3: - - gpmc,adv-on-ns: Assertion time - - gpmc,adv-rd-off-ns: Read deassertion time - - gpmc,adv-wr-off-ns: Write deassertion time - - gpmc,adv-aad-mux-on-ns: Assertion time for AAD - - gpmc,adv-aad-mux-rd-off-ns: Read deassertion time for AAD - - gpmc,adv-aad-mux-wr-off-ns: Write deassertion time for AAD - - WE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4: - - gpmc,we-on-ns Assertion time - - gpmc,we-off-ns: Deassertion time - - OE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4: - - gpmc,oe-on-ns: Assertion time - - gpmc,oe-off-ns: Deassertion time - - gpmc,oe-aad-mux-on-ns: Assertion time for AAD - - gpmc,oe-aad-mux-off-ns: Deassertion time for AAD - - Access time and cycle time timings (in nanoseconds) corresponding to - GPMC_CONFIG5: - - gpmc,page-burst-access-ns: Multiple access word delay - - gpmc,access-ns: Start-cycle to first data valid delay - - gpmc,rd-cycle-ns: Total read cycle time - - gpmc,wr-cycle-ns: Total write cycle time - - gpmc,bus-turnaround-ns: Turn-around time between successive accesses - - gpmc,cycle2cycle-delay-ns: Delay between chip-select pulses - - gpmc,clk-activation-ns: GPMC clock activation time - - gpmc,wait-monitoring-ns: Start of wait monitoring with regard to valid - data - -Boolean timing parameters. If property is present parameter enabled and -disabled if omitted: - - gpmc,adv-extra-delay: ADV signal is delayed by half GPMC clock - - gpmc,cs-extra-delay: CS signal is delayed by half GPMC clock - - gpmc,cycle2cycle-diffcsen: Add "cycle2cycle-delay" between successive - accesses to a different CS - - gpmc,cycle2cycle-samecsen: Add "cycle2cycle-delay" between successive - accesses to the same CS - - gpmc,oe-extra-delay: OE signal is delayed by half GPMC clock - - gpmc,we-extra-delay: WE signal is delayed by half GPMC clock - - gpmc,time-para-granularity: Multiply all access times by 2 - -The following are only applicable to OMAP3+ and AM335x: - - gpmc,wr-access-ns: In synchronous write mode, for single or - burst accesses, defines the number of - GPMC_FCLK cycles from start access time - to the GPMC_CLK rising edge used by the - memory device for the first data capture. - - gpmc,wr-data-mux-bus-ns: In address-data multiplex mode, specifies - the time when the first data is driven on - the address-data bus. - -GPMC chip-select settings properties for child nodes. All are optional. - -- gpmc,burst-length Page/burst length. Must be 4, 8 or 16. -- gpmc,burst-wrap Enables wrap bursting -- gpmc,burst-read Enables read page/burst mode -- gpmc,burst-write Enables write page/burst mode -- gpmc,device-width Total width of device(s) connected to a GPMC - chip-select in bytes. The GPMC supports 8-bit - and 16-bit devices and so this property must be - 1 or 2. -- gpmc,mux-add-data Address and data multiplexing configuration. - Valid values are 1 for address-address-data - multiplexing mode and 2 for address-data - multiplexing mode. -- gpmc,sync-read Enables synchronous read. Defaults to asynchronous - is this is not set. -- gpmc,sync-write Enables synchronous writes. Defaults to asynchronous - is this is not set. -- gpmc,wait-pin Wait-pin used by client. Must be less than - "gpmc,num-waitpins". -- gpmc,wait-on-read Enables wait monitoring on reads. -- gpmc,wait-on-write Enables wait monitoring on writes. - -Example for an AM33xx board: - - gpmc: gpmc@50000000 { - compatible = "ti,am3352-gpmc"; - ti,hwmods = "gpmc"; - reg = <0x50000000 0x2000>; - interrupts = <100>; - dmas = <&edma 52 0>; - dma-names = "rxtx"; - gpmc,num-cs = <8>; - gpmc,num-waitpins = <2>; - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ - interrupt-controller; - #interrupt-cells = <2>; - gpio-controller; - #gpio-cells = <2>; - - /* child nodes go here */ - }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/memory-controllers/ti,gpmc-child.yaml b/Documentation/devicetree/bindings/memory-controllers/ti,gpmc-child.yaml --- a/Documentation/devicetree/bindings/memory-controllers/ti,gpmc-child.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/memory-controllers/ti,gpmc-child.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,245 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/ti,gpmc-child.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: device tree bindings for children of the Texas Instruments GPMC + +maintainers: + - Tony Lindgren + - Roger Quadros + +description: + This binding is meant for the child nodes of the GPMC node. The node + represents any device connected to the GPMC bus. It may be a Flash chip, + RAM chip or Ethernet controller, etc. These properties are meant for + configuring the GPMC settings/timings and will accompany the bindings + supported by the respective device. + +properties: + reg: true + +# GPMC Timing properties for child nodes. All are optional and default to 0. + gpmc,sync-clk-ps: + description: Minimum clock period for synchronous mode + default: 0 + +# Chip-select signal timings corresponding to GPMC_CONFIG2: + gpmc,cs-on-ns: + description: Assertion time + default: 0 + + gpmc,cs-rd-off-ns: + description: Read deassertion time + default: 0 + + gpmc,cs-wr-off-ns: + description: Write deassertion time + default: 0 + +# ADV signal timings corresponding to GPMC_CONFIG3: + gpmc,adv-on-ns: + description: Assertion time + default: 0 + + gpmc,adv-rd-off-ns: + description: Read deassertion time + default: 0 + + gpmc,adv-wr-off-ns: + description: Write deassertion time + default: 0 + + gpmc,adv-aad-mux-on-ns: + description: Assertion time for AAD + default: 0 + + gpmc,adv-aad-mux-rd-off-ns: + description: Read deassertion time for AAD + default: 0 + + gpmc,adv-aad-mux-wr-off-ns: + description: Write deassertion time for AAD + default: 0 + +# WE signals timings corresponding to GPMC_CONFIG4: + gpmc,we-on-ns: + description: Assertion time + default: 0 + + gpmc,we-off-ns: + description: Deassertion time + default: 0 + +# OE signals timings corresponding to GPMC_CONFIG4: + gpmc,oe-on-ns: + description: Assertion time + default: 0 + + gpmc,oe-off-ns: + description: Deassertion time + default: 0 + + gpmc,oe-aad-mux-on-ns: + description: Assertion time for AAD + default: 0 + + gpmc,oe-aad-mux-off-ns: + description: Deassertion time for AAD + default: 0 + +# Access time and cycle time timings (in nanoseconds) corresponding to +# GPMC_CONFIG5: + gpmc,page-burst-access-ns: + description: Multiple access word delay + default: 0 + + gpmc,access-ns: + description: Start-cycle to first data valid delay + default: 0 + + gpmc,rd-cycle-ns: + description: Total read cycle time + default: 0 + + gpmc,wr-cycle-ns: + description: Total write cycle time + default: 0 + + gpmc,bus-turnaround-ns: + description: Turn-around time between successive accesses + default: 0 + + gpmc,cycle2cycle-delay-ns: + description: Delay between chip-select pulses + default: 0 + + gpmc,clk-activation-ns: + description: GPMC clock activation time + default: 0 + + gpmc,wait-monitoring-ns: + description: Start of wait monitoring with regard to valid data + default: 0 + +# Boolean timing parameters. If property is present, parameter is enabled +# otherwise disabled. + gpmc,adv-extra-delay: + description: ADV signal is delayed by half GPMC clock + type: boolean + + gpmc,cs-extra-delay: + description: CS signal is delayed by half GPMC clock + type: boolean + + gpmc,cycle2cycle-diffcsen: + description: | + Add "cycle2cycle-delay" between successive accesses + to a different CS + type: boolean + + gpmc,cycle2cycle-samecsen: + description: | + Add "cycle2cycle-delay" between successive accesses + to the same CS + type: boolean + + gpmc,oe-extra-delay: + description: OE signal is delayed by half GPMC clock + type: boolean + + gpmc,we-extra-delay: + description: WE signal is delayed by half GPMC clock + type: boolean + + gpmc,time-para-granularity: + description: Multiply all access times by 2 + type: boolean + +# The following two properties are applicable only to OMAP3+ and AM335x: + gpmc,wr-access-ns: + description: | + In synchronous write mode, for single or + burst accesses, defines the number of + GPMC_FCLK cycles from start access time + to the GPMC_CLK rising edge used by the + memory device for the first data capture. + default: 0 + + gpmc,wr-data-mux-bus-ns: + description: | + In address-data multiplex mode, specifies + the time when the first data is driven on + the address-data bus. + default: 0 + +# GPMC chip-select settings properties for child nodes. All are optional. + gpmc,burst-length: + description: Page/burst length. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 4, 8, 16] + default: 0 + + gpmc,burst-wrap: + description: Enables wrap bursting + type: boolean + + gpmc,burst-read: + description: Enables read page/burst mode + type: boolean + + gpmc,burst-write: + description: Enables write page/burst mode + type: boolean + + gpmc,device-width: + description: | + Total width of device(s) connected to a GPMC + chip-select in bytes. The GPMC supports 8-bit + and 16-bit devices and so this property must be + 1 or 2. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + default: 1 + + gpmc,mux-add-data: + description: | + Address and data multiplexing configuration. + Valid values are + 0 for Non multiplexed mode + 1 for address-address-data multiplexing mode and + 2 for address-data multiplexing mode. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + gpmc,sync-read: + description: | + Enables synchronous read. Defaults to asynchronous + is this is not set. + type: boolean + + gpmc,sync-write: + description: | + Enables synchronous writes. Defaults to asynchronous + is this is not set. + type: boolean + + gpmc,wait-pin: + description: | + Wait-pin used by client. Must be less than "gpmc,num-waitpins". + $ref: /schemas/types.yaml#/definitions/uint32 + + gpmc,wait-on-read: + description: Enables wait monitoring on reads. + type: boolean + + gpmc,wait-on-write: + description: Enables wait monitoring on writes. + type: boolean + +required: + - reg + +# the GPMC child will have its own native properties +additionalProperties: true diff -Naur --no-dereference a/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml b/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml --- a/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,182 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/ti,gpmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments GPMC Memory Controller device-tree bindings + +maintainers: + - Tony Lindgren + - Roger Quadros + +description: + The GPMC is a unified memory controller dedicated for interfacing + with external memory devices like + - Asynchronous SRAM-like memories and ASICs + - Asynchronous, synchronous, and page mode burst NOR flash + - NAND flash + - Pseudo-SRAM devices + +properties: + compatible: + items: + - enum: + - ti,am3352-gpmc + - ti,am64-gpmc + - ti,omap2420-gpmc + - ti,omap2430-gpmc + - ti,omap3430-gpmc + - ti,omap4430-gpmc + + reg: + minItems: 1 + maxItems: 2 + + reg-names: + items: + - const: cfg + - const: data + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + description: | + Functional clock. Used for bus timing calculations and + GPMC configuration. + + clock-names: + items: + - const: fck + + power-domains: + maxItems: 1 + + dmas: + items: + - description: DMA channel for GPMC NAND prefetch + + dma-names: + items: + - const: rxtx + + "#address-cells": true + + "#size-cells": true + + gpmc,num-cs: + description: maximum number of supported chip-select lines. + $ref: /schemas/types.yaml#/definitions/uint32 + + gpmc,num-waitpins: + description: maximum number of supported wait pins. + $ref: /schemas/types.yaml#/definitions/uint32 + + ranges: + minItems: 1 + description: | + Must be set up to reflect the memory layout with four + integer values for each chip-select line in use, + 0 + items: + - description: NAND bank 0 + - description: NOR/SRAM bank 0 + - description: NOR/SRAM bank 1 + + '#interrupt-cells': + const: 2 + + interrupt-controller: + description: | + The GPMC driver implements and interrupt controller for + the NAND events "fifoevent" and "termcount" plus the + rising/falling edges on the GPMC_WAIT pins. + The interrupt number mapping is as follows + 0 - NAND_fifoevent + 1 - NAND_termcount + 2 - GPMC_WAIT0 pin edge + 3 - GPMC_WAIT1 pin edge, and so on. + + '#gpio-cells': + const: 2 + + gpio-controller: + description: | + The GPMC driver implements a GPIO controller for the + GPMC WAIT pins that can be used as general purpose inputs. + 0 maps to GPMC_WAIT0 pin. + + ti,hwmods: + description: + Name of the HWMOD associated with GPMC. This is for legacy + omap2/3 platforms only. + $ref: /schemas/types.yaml#/definitions/string + deprecated: true + + ti,no-idle-on-init: + description: + Prevent idling the module at init. This is for legacy omap2/3 + platforms only. + type: boolean + deprecated: true + +patternProperties: + "@[0-7],[a-f0-9]+$": + type: object + description: | + The child device node represents the device connected to the GPMC + bus. The device can be a NAND chip, SRAM device, NOR device + or an ASIC. + + allOf: + - $ref: "ti,gpmc-child.yaml" + + unevaluatedProperties: false + +required: + - compatible + - reg + - gpmc,num-cs + - gpmc,num-waitpins + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + #include + #include + + gpmc: memory-controller@50000000 { + compatible = "ti,am3352-gpmc"; + reg = <0x50000000 0x2000>; + interrupts = <100>; + clocks = <&l3s_clkctrl>; + clock-names = "fck"; + dmas = <&edma 52 0>; + dma-names = "rxtx"; + gpmc,num-cs = <8>; + gpmc,num-waitpins = <2>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + + nand@0,0 { + compatible = "ti,omap2-nand"; + reg = <0 0 4>; + interrupt-parent = <&gpmc>; + interrupts = <0 IRQ_TYPE_NONE>, /* fifoevent */ + <1 IRQ_TYPE_NONE>; /* termcount */ + ti,nand-xfer-type = "prefetch-dma"; + ti,nand-ecc-opt = "bch16"; + ti,elm-id = <&elm>; + rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 pin */ + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt b/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt --- a/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt 2022-01-06 12:45:53.806318073 -0500 @@ -4,6 +4,7 @@ - compatible : shall be one of: "atmel,at93c46d" "eeprom-93xx46" + "microchip,93lc46b" - data-size : number of data bits per word (either 8 or 16) Optional properties: diff -Naur --no-dereference a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml --- a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -15,12 +15,19 @@ properties: compatible: - enum: - - ti,am654-sdhci-5.1 - - ti,j721e-sdhci-8bit - - ti,j721e-sdhci-4bit - - ti,j7200-sdhci-8bit - - ti,j721e-sdhci-4bit + oneOf: + - const: ti,am654-sdhci-5.1 + - const: ti,j721e-sdhci-8bit + - const: ti,j721e-sdhci-4bit + - const: ti,j721e-sdhci-4bit + - const: ti,am64-sdhci-8bit + - const: ti,am64-sdhci-4bit + - items: + - const: ti,j7200-sdhci-8bit + - const: ti,j721e-sdhci-8bit + - items: + - const: ti,j7200-sdhci-4bit + - const: ti,j721e-sdhci-4bit reg: maxItems: 2 diff -Naur --no-dereference a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt --- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,147 +0,0 @@ -Device tree bindings for GPMC connected NANDs - -GPMC connected NAND (found on OMAP boards) are represented as child nodes of -the GPMC controller with a name of "nand". - -All timing relevant properties as well as generic gpmc child properties are -explained in a separate documents - please refer to -Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt - -For NAND specific properties such as ECC modes or bus width, please refer to -Documentation/devicetree/bindings/mtd/nand-controller.yaml - - -Required properties: - - - compatible: "ti,omap2-nand" - - reg: range id (CS number), base offset and length of the - NAND I/O space - - interrupts: Two interrupt specifiers, one for fifoevent, one for termcount. - -Optional properties: - - - nand-bus-width: Set this numeric value to 16 if the hardware - is wired that way. If not specified, a bus - width of 8 is assumed. - - - ti,nand-ecc-opt: A string setting the ECC layout to use. One of: - "sw" 1-bit Hamming ecc code via software - "hw" use "ham1" instead - "hw-romcode" use "ham1" instead - "ham1" 1-bit Hamming ecc code - "bch4" 4-bit BCH ecc code - "bch8" 8-bit BCH ecc code - "bch16" 16-bit BCH ECC code - Refer below "How to select correct ECC scheme for your device ?" - - - ti,nand-xfer-type: A string setting the data transfer type. One of: - - "prefetch-polled" Prefetch polled mode (default) - "polled" Polled mode, without prefetch - "prefetch-dma" Prefetch enabled DMA mode - "prefetch-irq" Prefetch enabled irq mode - - - elm_id: use "ti,elm-id" instead - - ti,elm-id: Specifies phandle of the ELM devicetree node. - ELM is an on-chip hardware engine on TI SoC which is used for - locating ECC errors for BCHx algorithms. SoC devices which have - ELM hardware engines should specify this device node in .dtsi - Using ELM for ECC error correction frees some CPU cycles. - - rb-gpios: GPIO specifier for the ready/busy# pin. - -For inline partition table parsing (optional): - - - #address-cells: should be set to 1 - - #size-cells: should be set to 1 - -Example for an AM33xx board: - - gpmc: gpmc@50000000 { - compatible = "ti,am3352-gpmc"; - ti,hwmods = "gpmc"; - reg = <0x50000000 0x36c>; - interrupts = <100>; - gpmc,num-cs = <8>; - gpmc,num-waitpins = <2>; - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x08000000 0x1000000>; /* CS0 space, 16MB */ - elm_id = <&elm>; - interrupt-controller; - #interrupt-cells = <2>; - - nand@0,0 { - compatible = "ti,omap2-nand"; - reg = <0 0 4>; /* CS0, offset 0, NAND I/O window 4 */ - interrupt-parent = <&gpmc>; - interrupts = <0 IRQ_TYPE_NONE>, <1 IRQ_TYPE NONE>; - nand-bus-width = <16>; - ti,nand-ecc-opt = "bch8"; - ti,nand-xfer-type = "polled"; - rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */ - - gpmc,sync-clk-ps = <0>; - gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <44>; - gpmc,cs-wr-off-ns = <44>; - gpmc,adv-on-ns = <6>; - gpmc,adv-rd-off-ns = <34>; - gpmc,adv-wr-off-ns = <44>; - gpmc,we-off-ns = <40>; - gpmc,oe-off-ns = <54>; - gpmc,access-ns = <64>; - gpmc,rd-cycle-ns = <82>; - gpmc,wr-cycle-ns = <82>; - gpmc,wr-access-ns = <40>; - gpmc,wr-data-mux-bus-ns = <0>; - - #address-cells = <1>; - #size-cells = <1>; - - /* partitions go here */ - }; - }; - -How to select correct ECC scheme for your device ? --------------------------------------------------- -Higher ECC scheme usually means better protection against bit-flips and -increased system lifetime. However, selection of ECC scheme is dependent -on various other factors also like; - -(1) support of built in hardware engines. - Some legacy OMAP SoC do not have ELM harware engine, so those SoC cannot - support ecc-schemes with hardware error-correction (BCHx_HW). However - such SoC can use ecc-schemes with software library for error-correction - (BCHx_HW_DETECTION_SW). The error correction capability with software - library remains equivalent to their hardware counter-part, but there is - slight CPU penalty when too many bit-flips are detected during reads. - -(2) Device parameters like OOBSIZE. - Other factor which governs the selection of ecc-scheme is oob-size. - Higher ECC schemes require more OOB/Spare area to store ECC syndrome, - so the device should have enough free bytes available its OOB/Spare - area to accommodate ECC for entire page. In general following expression - helps in determining if given device can accommodate ECC syndrome: - "2 + (PAGESIZE / 512) * ECC_BYTES" <= OOBSIZE" - where - OOBSIZE number of bytes in OOB/spare area - PAGESIZE number of bytes in main-area of device page - ECC_BYTES number of ECC bytes generated to protect - 512 bytes of data, which is: - '3' for HAM1_xx ecc schemes - '7' for BCH4_xx ecc schemes - '14' for BCH8_xx ecc schemes - '26' for BCH16_xx ecc schemes - - Example(a): For a device with PAGESIZE = 2048 and OOBSIZE = 64 and - trying to use BCH16 (ECC_BYTES=26) ecc-scheme. - Number of ECC bytes per page = (2 + (2048 / 512) * 26) = 106 B - which is greater than capacity of NAND device (OOBSIZE=64) - Hence, BCH16 cannot be supported on given device. But it can - probably use lower ecc-schemes like BCH8. - - Example(b): For a device with PAGESIZE = 2048 and OOBSIZE = 128 and - trying to use BCH16 (ECC_BYTES=26) ecc-scheme. - Number of ECC bytes per page = (2 + (2048 / 512) * 26) = 106 B - which can be accommodated in the OOB/Spare area of this device - (OOBSIZE=128). So this device can use BCH16 ecc-scheme. diff -Naur --no-dereference a/Documentation/devicetree/bindings/mtd/gpmc-nor.txt b/Documentation/devicetree/bindings/mtd/gpmc-nor.txt --- a/Documentation/devicetree/bindings/mtd/gpmc-nor.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/mtd/gpmc-nor.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,98 +0,0 @@ -Device tree bindings for NOR flash connect to TI GPMC - -NOR flash connected to the TI GPMC (found on OMAP boards) are represented as -child nodes of the GPMC controller with a name of "nor". - -All timing relevant properties as well as generic GPMC child properties are -explained in a separate documents. Please refer to -Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt - -Required properties: -- bank-width: Width of NOR flash in bytes. GPMC supports 8-bit and - 16-bit devices and so must be either 1 or 2 bytes. -- compatible: Documentation/devicetree/bindings/mtd/mtd-physmap.txt -- gpmc,cs-on-ns: Chip-select assertion time -- gpmc,cs-rd-off-ns: Chip-select de-assertion time for reads -- gpmc,cs-wr-off-ns: Chip-select de-assertion time for writes -- gpmc,oe-on-ns: Output-enable assertion time -- gpmc,oe-off-ns: Output-enable de-assertion time -- gpmc,we-on-ns Write-enable assertion time -- gpmc,we-off-ns: Write-enable de-assertion time -- gpmc,access-ns: Start cycle to first data capture (read access) -- gpmc,rd-cycle-ns: Total read cycle time -- gpmc,wr-cycle-ns: Total write cycle time -- linux,mtd-name: Documentation/devicetree/bindings/mtd/mtd-physmap.txt -- reg: Chip-select, base address (relative to chip-select) - and size of NOR flash. Note that base address will be - typically 0 as this is the start of the chip-select. - -Optional properties: -- gpmc,XXX Additional GPMC timings and settings parameters. See - Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt - -Optional properties for partition table parsing: -- #address-cells: should be set to 1 -- #size-cells: should be set to 1 - -Example: - -gpmc: gpmc@6e000000 { - compatible = "ti,omap3430-gpmc", "simple-bus"; - ti,hwmods = "gpmc"; - reg = <0x6e000000 0x1000>; - interrupts = <20>; - gpmc,num-cs = <8>; - gpmc,num-waitpins = <4>; - #address-cells = <2>; - #size-cells = <1>; - - ranges = <0 0 0x10000000 0x08000000>; - - nor@0,0 { - compatible = "cfi-flash"; - linux,mtd-name= "intel,pf48f6000m0y1be"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0 0 0x08000000>; - bank-width = <2>; - - gpmc,mux-add-data; - gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <186>; - gpmc,cs-wr-off-ns = <186>; - gpmc,adv-on-ns = <12>; - gpmc,adv-rd-off-ns = <48>; - gpmc,adv-wr-off-ns = <48>; - gpmc,oe-on-ns = <54>; - gpmc,oe-off-ns = <168>; - gpmc,we-on-ns = <54>; - gpmc,we-off-ns = <168>; - gpmc,rd-cycle-ns = <186>; - gpmc,wr-cycle-ns = <186>; - gpmc,access-ns = <114>; - gpmc,page-burst-access-ns = <6>; - gpmc,bus-turnaround-ns = <12>; - gpmc,cycle2cycle-delay-ns = <18>; - gpmc,wr-data-mux-bus-ns = <90>; - gpmc,wr-access-ns = <186>; - gpmc,cycle2cycle-samecsen; - gpmc,cycle2cycle-diffcsen; - - partition@0 { - label = "bootloader-nor"; - reg = <0 0x40000>; - }; - partition@40000 { - label = "params-nor"; - reg = <0x40000 0x40000>; - }; - partition@80000 { - label = "kernel-nor"; - reg = <0x80000 0x200000>; - }; - partition@280000 { - label = "filesystem-nor"; - reg = <0x240000 0x7d80000>; - }; - }; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt b/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt --- a/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,48 +0,0 @@ -Device tree bindings for GPMC connected OneNANDs - -GPMC connected OneNAND (found on OMAP boards) are represented as child nodes of -the GPMC controller with a name of "onenand". - -All timing relevant properties as well as generic gpmc child properties are -explained in a separate documents - please refer to -Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt - -Required properties: - - - compatible: "ti,omap2-onenand" - - reg: The CS line the peripheral is connected to - - gpmc,device-width: Width of the ONENAND device connected to the GPMC - in bytes. Must be 1 or 2. - -Optional properties: - - - int-gpios: GPIO specifier for the INT pin. - -For inline partition table parsing (optional): - - - #address-cells: should be set to 1 - - #size-cells: should be set to 1 - -Example for an OMAP3430 board: - - gpmc: gpmc@6e000000 { - compatible = "ti,omap3430-gpmc"; - ti,hwmods = "gpmc"; - reg = <0x6e000000 0x1000000>; - interrupts = <20>; - gpmc,num-cs = <8>; - gpmc,num-waitpins = <4>; - #address-cells = <2>; - #size-cells = <1>; - - onenand@0 { - compatible = "ti,omap2-onenand"; - reg = <0 0 0>; /* CS0, offset 0 */ - gpmc,device-width = <2>; - - #address-cells = <1>; - #size-cells = <1>; - - /* partitions go here */ - }; - }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml b/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml --- a/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/ti,gpmc-nand.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments GPMC NAND Flash controller. + +maintainers: + - Tony Lindgren + - Roger Quadros + +description: + GPMC NAND controller/Flash is represented as a child of the + GPMC controller node. + +properties: + compatible: + items: + - enum: + - ti,am64-nand + - ti,omap2-nand + + reg: + maxItems: 1 + + interrupts: + items: + - description: Interrupt for fifoevent + - description: Interrupt for termcount + + "#address-cells": true + + "#size-cells": true + + ti,nand-ecc-opt: + description: Desired ECC algorithm + $ref: /schemas/types.yaml#/definitions/string + enum: [sw, ham1, bch4, bch8, bch16] + + ti,nand-xfer-type: + description: Data transfer method between controller and chip. + $ref: /schemas/types.yaml#/definitions/string + enum: [prefetch-polled, polled, prefetch-dma, prefetch-irq] + default: prefetch-polled + + ti,elm-id: + description: + phandle to the ELM (Error Location Module). + $ref: /schemas/types.yaml#/definitions/phandle + + nand-bus-width: + description: + Bus width to the NAND chip + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [8, 16] + default: 8 + +patternProperties: + "@[0-9a-f]+$": + $ref: "/schemas/mtd/partitions/partition.yaml" + +allOf: + - $ref: "/schemas/memory-controllers/ti,gpmc-child.yaml" + +required: + - compatible + - reg + - ti,nand-ecc-opt + +unevaluatedProperties: false + +examples: + - | + #include + #include + + gpmc: memory-controller@50000000 { + compatible = "ti,am3352-gpmc"; + dmas = <&edma 52 0>; + dma-names = "rxtx"; + clocks = <&l3s_gclk>; + clock-names = "fck"; + reg = <0x50000000 0x2000>; + interrupts = ; + gpmc,num-cs = <7>; + gpmc,num-waitpins = <2>; + #address-cells = <2>; + #size-cells = <1>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + + ranges = <0 0 0x08000000 0x01000000>; /* CS0 space. Min partition = 16MB */ + nand@0,0 { + compatible = "ti,omap2-nand"; + reg = <0 0 4>; /* device IO registers */ + interrupt-parent = <&gpmc>; + interrupts = <0 IRQ_TYPE_NONE>, /* fifoevent */ + <1 IRQ_TYPE_NONE>; /* termcount */ + ti,nand-xfer-type = "prefetch-dma"; + ti,nand-ecc-opt = "bch16"; + ti,elm-id = <&elm>; + #address-cells = <1>; + #size-cells = <1>; + + /* NAND generic properties */ + nand-bus-width = <8>; + rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */ + + /* GPMC properties*/ + gpmc,device-width = <1>; + + partition@0 { + label = "NAND.SPL"; + reg = <0x00000000 0x00040000>; + }; + partition@1 { + label = "NAND.SPL.backup1"; + reg = <0x00040000 0x00040000>; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/mtd/ti,gpmc-onenand.yaml b/Documentation/devicetree/bindings/mtd/ti,gpmc-onenand.yaml --- a/Documentation/devicetree/bindings/mtd/ti,gpmc-onenand.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/mtd/ti,gpmc-onenand.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/ti,gpmc-onenand.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OneNAND over Texas Instruments GPMC bus. + +maintainers: + - Tony Lindgren + - Roger Quadros + +description: + GPMC connected OneNAND (found on OMAP boards) are represented + as child nodes of the GPMC controller. + +properties: + compatible: + const: ti,omap2-onenand + + reg: + items: + - description: | + Chip Select number, register offset and size of + OneNAND register window. + + "#address-cells": true + + "#size-cells": true + + int-gpios: + description: GPIO specifier for the INT pin. + +patternProperties: + "@[0-9a-f]+$": + $ref: "/schemas/mtd/partitions/partition.yaml" + +allOf: + - $ref: "/schemas/memory-controllers/ti,gpmc-child.yaml" + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + +unevaluatedProperties: false + +examples: + - | + gpmc: memory-controller@6e000000 { + compatible = "ti,omap3430-gpmc"; + reg = <0x6e000000 0x02d0>; + interrupts = <20>; + gpmc,num-cs = <8>; + gpmc,num-waitpins = <4>; + clocks = <&l3s_clkctrl>; + clock-names = "fck"; + #address-cells = <2>; + #size-cells = <1>; + + ranges = <0 0 0x01000000 0x01000000>, /* 16 MB for OneNAND */ + <1 0 0x02000000 0x01000000>; /* 16 MB for smc91c96 */ + + onenand@0,0 { + compatible = "ti,omap2-onenand"; + reg = <0 0 0x20000>; /* CS0, offset 0, IO size 128K */ + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bootloader"; + reg = <0x00000000 0x00100000>; + }; + + partition@100000 { + label = "config"; + reg = <0x00100000 0x002c0000>; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml b/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml --- a/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -106,9 +106,18 @@ maximum: 32 maxItems: 1 + power-domains: + description: + Power domain provider node and an args specifier containing + the can device id value. + maxItems: 1 + can-transceiver: $ref: can-transceiver.yaml# + phys: + maxItems: 1 + required: - compatible - reg diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/gpmc-eth.txt b/Documentation/devicetree/bindings/net/gpmc-eth.txt --- a/Documentation/devicetree/bindings/net/gpmc-eth.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/gpmc-eth.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,97 +0,0 @@ -Device tree bindings for Ethernet chip connected to TI GPMC - -Besides being used to interface with external memory devices, the -General-Purpose Memory Controller can be used to connect Pseudo-SRAM devices -such as ethernet controllers to processors using the TI GPMC as a data bus. - -Ethernet controllers connected to TI GPMC are represented as child nodes of -the GPMC controller with an "ethernet" name. - -All timing relevant properties as well as generic GPMC child properties are -explained in a separate documents. Please refer to -Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt - -For the properties relevant to the ethernet controller connected to the GPMC -refer to the binding documentation of the device. For example, the documentation -for the SMSC 911x is Documentation/devicetree/bindings/net/smsc911x.txt - -Child nodes need to specify the GPMC bus address width using the "bank-width" -property but is possible that an ethernet controller also has a property to -specify the I/O registers address width. Even when the GPMC has a maximum 16-bit -address width, it supports devices with 32-bit word registers. -For example with an SMSC LAN911x/912x controller connected to the TI GPMC on an -OMAP2+ board, "bank-width = <2>;" and "reg-io-width = <4>;". - -Required properties: -- bank-width: Address width of the device in bytes. GPMC supports 8-bit - and 16-bit devices and so must be either 1 or 2 bytes. -- compatible: Compatible string property for the ethernet child device. -- gpmc,cs-on-ns: Chip-select assertion time -- gpmc,cs-rd-off-ns: Chip-select de-assertion time for reads -- gpmc,cs-wr-off-ns: Chip-select de-assertion time for writes -- gpmc,oe-on-ns: Output-enable assertion time -- gpmc,oe-off-ns: Output-enable de-assertion time -- gpmc,we-on-ns: Write-enable assertion time -- gpmc,we-off-ns: Write-enable de-assertion time -- gpmc,access-ns: Start cycle to first data capture (read access) -- gpmc,rd-cycle-ns: Total read cycle time -- gpmc,wr-cycle-ns: Total write cycle time -- reg: Chip-select, base address (relative to chip-select) - and size of the memory mapped for the device. - Note that base address will be typically 0 as this - is the start of the chip-select. - -Optional properties: -- gpmc,XXX Additional GPMC timings and settings parameters. See - Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt - -Example: - -gpmc: gpmc@6e000000 { - compatible = "ti,omap3430-gpmc"; - ti,hwmods = "gpmc"; - reg = <0x6e000000 0x1000>; - interrupts = <20>; - gpmc,num-cs = <8>; - gpmc,num-waitpins = <4>; - #address-cells = <2>; - #size-cells = <1>; - - ranges = <5 0 0x2c000000 0x1000000>; - - ethernet@5,0 { - compatible = "smsc,lan9221", "smsc,lan9115"; - reg = <5 0 0xff>; - bank-width = <2>; - - gpmc,mux-add-data; - gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <186>; - gpmc,cs-wr-off-ns = <186>; - gpmc,adv-on-ns = <12>; - gpmc,adv-rd-off-ns = <48>; - gpmc,adv-wr-off-ns = <48>; - gpmc,oe-on-ns = <54>; - gpmc,oe-off-ns = <168>; - gpmc,we-on-ns = <54>; - gpmc,we-off-ns = <168>; - gpmc,rd-cycle-ns = <186>; - gpmc,wr-cycle-ns = <186>; - gpmc,access-ns = <114>; - gpmc,page-burst-access-ns = <6>; - gpmc,bus-turnaround-ns = <12>; - gpmc,cycle2cycle-delay-ns = <18>; - gpmc,wr-data-mux-bus-ns = <90>; - gpmc,wr-access-ns = <186>; - gpmc,cycle2cycle-samecsen; - gpmc,cycle2cycle-diffcsen; - - interrupt-parent = <&gpio6>; - interrupts = <16>; - vmmc-supply = <&vddvario>; - vmmc_aux-supply = <&vdd33a>; - reg-io-width = <4>; - - smsc,save-mac-address; - }; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti,cpsw-virt-mac.txt b/Documentation/devicetree/bindings/net/ti,cpsw-virt-mac.txt --- a/Documentation/devicetree/bindings/net/ti,cpsw-virt-mac.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti,cpsw-virt-mac.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,55 @@ +TI J721E VIRT CPSWxg Ethernet mac +====================================== + +Required properties: +- compatible : Should be "ti,j721e-cpsw-virt-mac" for J721E Family SoCs +- dma-coherent : indicates that device operates with coherent memory. +- dmas : list of UDMA controller channel specifiers [2] +- dma-names : should be "tx0", "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", + "tx7", "rx"; +- ti,psil-base : CPSWxg NUSS PSI-L endpoint thread ID base of the UDMA-P + channels. The PSI-L endpoint node thread configuration + subnodes must be present present with ti,psil-configX naming + convention, where X is the thread ID offset [2]. +- ti,remote-name: Name of the connected rpmsg-kdrv device represented + by Eth switch FW running on the on of R5F cores. + Should be "mpu_1_0_ethswitch-device-0" for J721E Family SoCs. + +Required Sub-nodes: +- virt_emac_port: contains VIRT CPSWxg Ethernet mac port descriptions + Optional properties - all ports: + - ti,label : Describes the label associated with this port + - local-mac-address: array of 6 bytes, the assigned MAC address [3]. + +The MAC address provided by Eth switch FW will be used if neither of +"mac-address" and "local-mac-address" is defined. + +References: + [2] Documentation/devicetree/bindings/dma/ti/k3-udma.yaml + [3] Documentation/devicetree/bindings/net/ethernet-controller.yaml + +Examples: + cpsw9g_virt_mac: main_r5fss_cpsw9g_virt_mac0 { + compatible = "ti,j721e-cpsw-virt-mac"; + dma-coherent; + ti,psil-base = <0x4a00>; + ti,remote-name = "mpu_1_0_ethswitch-device-0"; + + dmas = <&main_udmap 0xca00>, + <&main_udmap 0xca01>, + <&main_udmap 0xca02>, + <&main_udmap 0xca03>, + <&main_udmap 0xca04>, + <&main_udmap 0xca05>, + <&main_udmap 0xca06>, + <&main_udmap 0xca07>, + <&main_udmap 0x4a00>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + virt_emac_port { + ti,label = "virt-port"; + /* local-mac-address = [0 0 0 0 0 0]; */ + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti,icssg-prueth.txt b/Documentation/devicetree/bindings/net/ti,icssg-prueth.txt --- a/Documentation/devicetree/bindings/net/ti,icssg-prueth.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti,icssg-prueth.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,126 @@ +Texas Instruments ICSSG PRUSS Ethernet +====================================== + +Required properties: +- compatible : Should be "ti,am654-icssg-prueth" for AM65x Family SoCs + "ti,am654-icssg-prueth-sr1" for SR1.0 + "ti,am642-icssg-prueth" for AM64x +- ti,prus : list of pHandles to the PRU, RTU and TX_PRU nodes +- firmware-name : should contain the name of the firmware image + file located in the firmware search path +- sram : phandle to MSMC SRAM node +- dmas : list of phandles and specifiers to UDMA as specified in + bindings/dma/ti/k3-udma.txt. +- dma-names : Names for the DMA channels. + Should be "tx0-0", "tx0-1", "tx0-2", "0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1", "rxmgm0", "rxmgm1" +- mii-g-rt : phandle to MII_G_RT module's syscon regmap. +- mii-rt : phandle to MII_RT module's syscon regmap. +- ti,pruss-gp-mux-sel : GP-MUX value required for both ports. +- iep : list of pHandles to the to IEP modules +- interrupts : (SR2.0 only) Interrupt specifiers to TX timestamp IRQ. +- interrupt-names : "tx_ts0", "tx_ts1" + +Must contain children, one for each of the MAC ports. +Children must be named ethernet-mii0 and ethernet-mii1. +Either one or both children can be present. If only one +child is present driver operates in single EMAC mode. + +For single mode operation with the 2nd SLICE, you still need +to provide both PRUs and RTUs and firmware-names but the firmware-name +for the first PRU & RTU can be NULL. + +Required properties for children: +- phy-handle : See ethernet.txt file in the same directory. +- phy-mode : See ethernet.txt file in the same directory. +- syscon-rgmii-delay : phandle to system controller node and register offset + to ICSSG control register for RGMII transmit delay. + +Optional properties for children: +- local-mac-address : mac address for the port. +- ti,half-duplex-capable : Enable half duplex operation on ICSSG MII port. + This requires board modification to route PHY + output pin (COL) to ICSSG GPIO pin + (PRG0_PRU1_GPIO10) as input. + +Example (k3-am654 base board SR2.0, dual-emac): +============================================== + + /* Dual Ethernet application node on PRU-ICSSG2 */ + pruss2_eth: pruss2_eth { + compatible = "ti,am654-icssg-prueth"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg2_rgmii_pins_default>; + sram = <&msmc_ram>; + + prus = <&pru2_0>, <&rtu2_0>, <&tx_pru2_0>, <&pru2_1>, <&rtu2_1>, <&tx_pru2_1>; + firmware-name = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + "ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf"; + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, + <2>, /* MII mode */ + <2>, + <2>; + mii-g-rt = <&icssg2_mii_g_rt>; + dma-coherent; + dmas = <&main_udmap 0xc300>, /* egress slice 0 */ + <&main_udmap 0xc301>, /* egress slice 0 */ + <&main_udmap 0xc302>, /* egress slice 0 */ + <&main_udmap 0xc303>, /* egress slice 0 */ + <&main_udmap 0xc304>, /* egress slice 1 */ + <&main_udmap 0xc305>, /* egress slice 1 */ + <&main_udmap 0xc306>, /* egress slice 1 */ + <&main_udmap 0xc307>, /* egress slice 1 */ + + <&main_udmap 0x4300>, /* ingress slice 0 */ + <&main_udmap 0x4301>, /* ingress slice 1 */ + <&main_udmap 0x4302>, /* mgmnt rsp slice 0 */ + <&main_udmap 0x4303>; /* mgmnt rsp slice 1 */ + dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1", + "rxmgm0", "rxmgm1"; + + pruss2_emac0: ethernet-mii0 { + phy-handle = <&pruss2_eth0_phy>; + phy-mode = "rgmii-rxid"; + interrupts-extended = <&icssg2_intc 24>; + syscon-rgmii-delay = <&scm_conf 0x4120>; + iep = <&icssg2_iep0>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + pruss2_emac1: ethernet-mii1 { + phy-handle = <&pruss2_eth1_phy>; + phy-mode = "rgmii-rxid"; + interrupts-extended = <&icssg2_intc 25>; + syscon-rgmii-delay = <&scm_conf 0x4124>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; + + &icssg2_mdio { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&icssg2_mdio_pins_default>; + + pruss2_eth0_phy: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; + + pruss2_eth1_phy: ethernet-phy@3 { + reg = <3>; + ti,rx-internal-delay = ; + ti,fifo-depth = ; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti,icss-iep.yaml b/Documentation/devicetree/bindings/net/ti,icss-iep.yaml --- a/Documentation/devicetree/bindings/net/ti,icss-iep.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti,icss-iep.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/ti,icss-iep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments ICSS Industrial Ethernet Peripheral (IEP) module + +maintainers: + - Lokesh Vutla + +properties: + compatible: + enum: + - ti,am3356-icss-iep # for AM335x and AM437x SoCs + - ti,am5728-icss-iep # for AM57xx and 66AK2G SoCs + - ti,am654-icss-iep # for K3 AM65x, J721E and AM64x SoCs + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: phandle to the IEP source clock + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + icssg0_iep0: iep@2e000 { + compatible = "ti,am654-icss-iep"; + reg = <0x2e000 0x1000>; + clocks = <&icssg0_iepclk_mux>; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml b/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml --- a/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/net/ti,k3-am654-cpsw-nuss.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: The TI AM654x/J721E SoC Gigabit Ethernet MAC (Media Access Controller) Device Tree Bindings +title: The TI AM654x/J721E/AM642x SoC Gigabit Ethernet MAC (Media Access Controller) Device Tree Bindings maintainers: - Grygorii Strashko @@ -13,19 +13,16 @@ description: The TI AM654x/J721E SoC Gigabit Ethernet MAC (CPSW2G NUSS) has two ports (one external) and provides Ethernet packet communication for the device. - CPSW2G NUSS features - the Reduced Gigabit Media Independent Interface (RGMII), - Reduced Media Independent Interface (RMII), the Management Data - Input/Output (MDIO) interface for physical layer device (PHY) management, - new version of Common Platform Time Sync (CPTS), updated Address Lookup - Engine (ALE). - One external Ethernet port (port 1) with selectable RGMII/RMII interfaces and - an internal Communications Port Programming Interface (CPPI5) (Host port 0). + The TI AM642x SoC Gigabit Ethernet MAC (CPSW3G NUSS) has three ports + (two external) and provides Ethernet packet communication and switching. + + The internal Communications Port Programming Interface (CPPI5) (Host port 0). Host Port 0 CPPI Packet Streaming Interface interface supports 8 TX channels - and one RX channels and operating by TI AM654x/J721E NAVSS Unified DMA - Peripheral Root Complex (UDMA-P) controller. - The CPSW2G NUSS is integrated into device MCU domain named MCU_CPSW0. + and one RX channels and operating by NAVSS Unified DMA Peripheral Root + Complex (UDMA-P) controller. - Additional features + CPSWxG features + updated Address Lookup Engine (ALE). priority level Quality Of Service (QOS) support (802.1p) Support for Audio/Video Bridging (P802.1Qav/D6.0) Support for IEEE 1588 Clock Synchronization (2008 Annex D, Annex E and Annex F) @@ -38,10 +35,18 @@ VLAN support, 802.1Q compliant, Auto add port VLAN for untagged frames on ingress, Auto VLAN removal on egress and auto pad to minimum frame size. RX/TX csum offload + Management Data Input/Output (MDIO) interface for PHYs management + RMII/RGMII Interfaces support + new version of Common Platform Time Sync (CPTS) + + The CPSWxG NUSS is integrated into + device MCU domain named MCU_CPSW0 on AM654x/J721E SoC. + device MAIN domain named CPSW0 on AM642x SoC. Specifications can be found at - http://www.ti.com/lit/ug/spruid7e/spruid7e.pdf - http://www.ti.com/lit/ug/spruil1a/spruil1a.pdf + https://www.ti.com/lit/pdf/spruid7 + https://www.ti.com/lit/zip/spruil1 + https://www.ti.com/lit/pdf/spruim2 properties: "#address-cells": true @@ -51,11 +56,12 @@ oneOf: - const: ti,am654-cpsw-nuss - const: ti,j721e-cpsw-nuss + - const: ti,am642-cpsw-nuss reg: maxItems: 1 description: - The physical base address and size of full the CPSW2G NUSS IO range + The physical base address and size of full the CPSWxG NUSS IO range reg-names: items: @@ -66,12 +72,16 @@ dma-coherent: true clocks: - description: CPSW2G NUSS functional clock + description: CPSWxG NUSS functional clock clock-names: items: - const: fck + assigned-clock-parents: true + + assigned-clocks: true + power-domains: maxItems: 1 @@ -99,16 +109,16 @@ const: 0 patternProperties: - port@1: + port@[1-2]: type: object - description: CPSW2G NUSS external ports + description: CPSWxG NUSS external ports $ref: ethernet-controller.yaml# properties: reg: - items: - - const: 1 + minimum: 1 + maximum: 2 description: CPSW port number phys: diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml b/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml --- a/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -73,6 +73,13 @@ items: - const: cpts + assigned-clock-parents: true + + assigned-clocks: true + + power-domains: + maxItems: 1 + ti,cpts-ext-ts-inputs: $ref: /schemas/types.yaml#/definitions/uint32 maximum: 8 @@ -85,6 +92,15 @@ description: Number of timestamp Generator function outputs (TS_GENFx) + ti,pps: + allOf: + - $ref: /schemas/types.yaml#definitions/uint32-array + - minItems: 2 + maxItems: 2 + description: + The pair of HWx_TS_PUSH input and TS_GENFy output indexes used for + PPS events generation. Platform/board specific. + refclk-mux: type: object description: CPTS reference clock multiplexer clock diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti-prueth.txt b/Documentation/devicetree/bindings/net/ti-prueth.txt --- a/Documentation/devicetree/bindings/net/ti-prueth.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti-prueth.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,92 @@ +Texas Instruments PRUSS Ethernet MAC +==================================== + +Required properties: +- compatible : Should be one of the following, + "ti,am3359-prueth" for AM335x SoCs + "ti,am4376-prueth" for AM437x SoCs + "ti,am57-prueth" for AM57xx SoCs + "ti,k2g-prueth" for 66AK2G SoCs + +- ti,prus : list of pHandles to the PRU nodes +- sram : pHandle to OCMC SRAM node +- interrupt-parent : pHandle to the PRUSS INTC node +- mii-rt : pHandle to MII_RT module's syscon regmap +- iep : pHandle to IEP module's syscon regmap + +Must contain children, one for each of the MAC ports. +Children must be named ethernet-mii0 and ethernet-mii1. +Either one or both children can be present. If only one +child is present driver operates in single EMAC mode. + +For single mode operation with the 2nd PRU, you still need +to provide both PRUs. See 2nd example. + +Required properties for children: +- phy-handle : See ethernet.txt file in the same directory. +- phy-mode : See ethernet.txt file in the same directory. +- interrupt-names : should be "rx" +- interrupts : should contain an array of PRUSS system event + numbers used as the interrupt sources for Rx. + +Optional properties for children: +- local-mac-address : mac address for the port. +- ti,no-half-duplex : disable half-duplex. +- interrupt-names : add "tx" when using Dual EMAC firmware to + get a better egress performance at MTU size. + Note that this reduces the performance + for small size frames. So use it only if + specific application uses mostly MTU or near + MTU size frames. + add "emac_ptp_tx" when ptp is needed with Dual EMAC +- interrupts : should contain an array of PRUSS system event + numbers for each entry in interrupt-names. + +Example (am572x-idk board, dual-emac): +====================================== + pruss2_eth { + compatible = "ti,am57-prueth"; + ti,prus = <&pru2_0>, <&pru2_1>; + sram = <&ocmcram1>; + interrupt-parent = <&pruss2_intc>; + mii-rt = <&pruss2_mii_rt>; + iep = <&pruss2_iep>; + + pruss2_emac0: ethernet-mii0 { + phy-handle = <&pruss2_eth0_phy>; + phy-mode = "mii"; + interrupts = <20 2 2>, <22 4 4 >, <26 6 6>; + interrupt-names = "rx", "tx", "emac_ptp_tx"; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + pruss2_emac1: ethernet-mii1 { + phy-handle = <&pruss2_eth1_phy>; + phy-mode = "mii"; + interrupts = <21 3 3>, <23 5 5>, <27 9 7>; + interrupt-names = "rx", "tx", "emac_ptp_tx"; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; + +Example (am572x-idk board, single-emac): +======================================= + pruss2_eth { + compatible = "ti,am57-prueth"; + ti,prus = <&pru2_0>, <&pru2_1>; + sram = <&ocmcram1>; + interrupt-parent = <&pruss2_intc>; + mii-rt = <&pruss2_mii_rt>; + iep = <&pruss2_iep>; + + pruss2_emac1: ethernet-mii1 { + phy-handle = <&pruss2_eth1_phy>; + phy-mode = "mii"; + interrupts = <21 2 2>, <23 3 3>, <27 4 4>; + interrupt-names = "rx", "tx", "emac_ptp_tx"; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/net/ti,pruss-ecap.yaml b/Documentation/devicetree/bindings/net/ti,pruss-ecap.yaml --- a/Documentation/devicetree/bindings/net/ti,pruss-ecap.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/net/ti,pruss-ecap.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/ti,pruss-ecap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments PRU-ICSS Enhanced Capture (eCAP) event module + +description: | + Each PRUSS includes an Enhanced Capture (eCAP) module that can provide some + time-stamp features using its capture input. The eCAP module used within the + PRU-ICSS is similar to other instances used within the PWM subsystems. This + is typically used for providing interrupt pacing for host traffic on various + PRU Ethernet usecases such as Dual-EMAC, HSR or PRP. + +maintainers: + - Lokesh Vutla + +properties: + compatible: + const: ti,pruss-ecap + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + pruss0_ecap: ecap@30000 { + compatible = "ti,pruss-ecap"; + reg = <0x30000 0x60>; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pci/cdns-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/cdns-pcie-ep.yaml --- a/Documentation/devicetree/bindings/pci/cdns-pcie-ep.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pci/cdns-pcie-ep.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -20,7 +20,4 @@ maximum: 32 default: 32 -required: - - cdns,max-outbound-regions - additionalProperties: true diff -Naur --no-dereference a/Documentation/devicetree/bindings/pci/pci-ep.yaml b/Documentation/devicetree/bindings/pci/pci-ep.yaml --- a/Documentation/devicetree/bindings/pci/pci-ep.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pci/pci-ep.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -23,6 +23,13 @@ default: 1 maximum: 255 + max-virtual-functions: + description: Array representing the number of virtual functions corresponding to each physical + function + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 1 + maxItems: 255 + max-link-speed: $ref: /schemas/types.yaml#/definitions/uint32 enum: [ 1, 2, 3, 4 ] diff -Naur --no-dereference a/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml b/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml --- a/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/pci/ti,am65-pci-ep.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI AM65 PCI Endpoint + +maintainers: + - Kishon Vijay Abraham I + +allOf: + - $ref: "pci-ep.yaml#" + +properties: + compatible: + enum: + - ti,am654-pcie-ep + + reg: + maxItems: 4 + + reg-names: + items: + - const: app + - const: dbics + - const: addr_space + - const: atu + + power-domains: + maxItems: 1 + + ti,syscon-pcie-mode: + description: Phandle to the SYSCON entry required for configuring PCIe in RC or EP mode. + $ref: /schemas/types.yaml#/definitions/phandle + + interrupts: + minItems: 1 + + phys: + description: + One per lane if more than one in the list. If only one PHY listed it must + manage all lanes. + minItems: 1 + maxItems: 2 + + phy-names: + items: + - const: pcie-phy0 + + dma-coherent: true + +required: + - compatible + - reg + - reg-names + - max-link-speed + - num-lanes + - power-domains + - ti,syscon-pcie-mode + - phys + - phy-names + - dma-coherent + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie0_ep: pcie-ep@5500000 { + compatible = "ti,am654-pcie-ep"; + reg = <0x0 0x5500000 0x0 0x1000>, + <0x0 0x5501000 0x0 0x1000>, + <0x0 0x10000000 0x0 0x8000000>, + <0x0 0x5506000 0x0 0x1000>; + reg-names = "app", "dbics", "addr_space", "atu"; + power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>; + ti,syscon-pcie-mode = <&pcie0_mode>; + max-link-speed = <2>; + num-lanes = <1>; + phys = <&serdes1 PHY_TYPE_PCIE 0>; + phy-names = "pcie-phy0"; + dma-coherent; + interrupts = ; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml --- a/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/pci/ti,am65-pci-host.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI AM65 PCI Host + +maintainers: + - Kishon Vijay Abraham I + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + +properties: + compatible: + enum: + - ti,am654-pcie-rc + + reg: + maxItems: 4 + + reg-names: + items: + - const: app + - const: dbics + - const: config + - const: atu + + power-domains: + maxItems: 1 + + ti,syscon-pcie-id: + description: Phandle to the SYSCON entry required for getting PCIe device/vendor ID + $ref: /schemas/types.yaml#/definitions/phandle + + ti,syscon-pcie-mode: + description: Phandle to the SYSCON entry required for configuring PCIe in RC or EP mode. + $ref: /schemas/types.yaml#/definitions/phandle + + phys: + description: + One per lane if more than one in the list. If only one PHY listed it must + manage all lanes. + minItems: 1 + maxItems: 16 + + phy-names: + items: + - const: pcie-phy0 + + msi-map: true + + dma-coherent: true + +patternProperties: + interrupt-controller: + type: object + description: interrupt controller to handle legacy interrupts. + +required: + - compatible + - reg + - reg-names + - max-link-speed + - num-lanes + - power-domains + - ti,syscon-pcie-id + - ti,syscon-pcie-mode + - msi-map + - ranges + - reset-gpios + - phys + - phy-names + - dma-coherent + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie0_rc: pcie@5500000 { + compatible = "ti,am654-pcie-rc"; + reg = <0x0 0x5500000 0x0 0x1000>, + <0x0 0x5501000 0x0 0x1000>, + <0x0 0x10000000 0x0 0x2000>, + <0x0 0x5506000 0x0 0x1000>; + reg-names = "app", "dbics", "config", "atu"; + power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x81000000 0 0 0x0 0x10020000 0 0x00010000>, + <0x82000000 0 0x10030000 0x0 0x10030000 0 0x07FD0000>; + ti,syscon-pcie-id = <&pcie_devid>; + ti,syscon-pcie-mode = <&pcie0_mode>; + bus-range = <0x0 0xff>; + num-viewport = <16>; + max-link-speed = <2>; + dma-coherent; + device_type = "pci"; + num-lanes = <1>; + phys = <&serdes1 PHY_TYPE_PCIE 0>; + phy-names = "pcie-phy0"; + reset-gpios = <&pca9555 5 GPIO_ACTIVE_HIGH>; + interrupts = ; + msi-map = <0x0 &gic_its 0x0 0x10000>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0_intc 0>, /* INT A */ + <0 0 0 2 &pcie0_intc 0>, /* INT B */ + <0 0 0 3 &pcie0_intc 0>, /* INT C */ + <0 0 0 4 &pcie0_intc 0>; /* INT D */ + + pcie0_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic500>; + interrupts = ; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml --- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -15,8 +15,16 @@ properties: compatible: - enum: - - ti,j721e-pcie-ep + oneOf: + - const: ti,j721e-pcie-ep + - description: PCIe EP controller in AM64 + items: + - const: ti,am64-pcie-ep + - const: ti,j721e-pcie-ep + - description: PCIe EP controller in J7200 + items: + - const: ti,j7200-pcie-ep + - const: ti,j721e-pcie-ep reg: maxItems: 4 @@ -29,9 +37,12 @@ - const: mem ti,syscon-pcie-ctrl: - description: Phandle to the SYSCON entry required for configuring PCIe mode - and link speed. - $ref: /schemas/types.yaml#/definitions/phandle + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to the SYSCON entry + - description: pcie_ctrl register offset within SYSCON + description: Specifier for configuring PCIe mode and link speed. power-domains: maxItems: 1 @@ -57,8 +68,6 @@ - power-domains - clocks - clock-names - - cdns,max-outbound-regions - - dma-coherent - max-functions - phys - phy-names @@ -80,13 +89,12 @@ <0x00 0x0d000000 0x00 0x00800000>, <0x00 0x10000000 0x00 0x08000000>; reg-names = "intd_cfg", "user_cfg", "reg", "mem"; - ti,syscon-pcie-ctrl = <&pcie0_ctrl>; + ti,syscon-pcie-ctrl = <&pcie0_ctrl 0x4070>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; clocks = <&k3_clks 239 1>; clock-names = "fck"; - cdns,max-outbound-regions = <16>; max-functions = /bits/ 8 <6>; dma-coherent; phys = <&serdes0_pcie_link>; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml --- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -15,8 +15,16 @@ properties: compatible: - enum: - - ti,j721e-pcie-host + oneOf: + - const: ti,j721e-pcie-host + - description: PCIe controller in AM64 + items: + - const: ti,am64-pcie-host + - const: ti,j721e-pcie-host + - description: PCIe controller in J7200 + items: + - const: ti,j7200-pcie-host + - const: ti,j721e-pcie-host reg: maxItems: 4 @@ -29,29 +37,47 @@ - const: cfg ti,syscon-pcie-ctrl: - description: Phandle to the SYSCON entry required for configuring PCIe mode - and link speed. - $ref: /schemas/types.yaml#/definitions/phandle + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to the SYSCON entry + - description: pcie_ctrl register offset within SYSCON + description: Specifier for configuring PCIe mode and link speed. power-domains: maxItems: 1 clocks: - maxItems: 1 - description: clock-specifier to represent input to the PCIe + minItems: 1 + maxItems: 2 + description: clock-specifier to represent input to the PCIe for 1 item. + 2nd item if present represents reference clock to the connector. clock-names: + minItems: 1 items: - const: fck + - const: pcie_refclk vendor-id: const: 0x104c device-id: - const: 0xb00d + oneOf: + - items: + - const: 0xb00d + - items: + - const: 0xb00f + - items: + - const: 0xb010 msi-map: true +patternProperties: + "interrupt-controller": + type: object + description: interrupt controller to handle legacy interrupts. + required: - compatible - reg @@ -65,7 +91,6 @@ - vendor-id - device-id - msi-map - - dma-coherent - dma-ranges - ranges - reset-gpios @@ -76,6 +101,8 @@ examples: - | + #include + #include #include #include @@ -90,7 +117,7 @@ <0x00 0x0d000000 0x00 0x00800000>, <0x00 0x10000000 0x00 0x00001000>; reg-names = "intd_cfg", "user_cfg", "reg", "cfg"; - ti,syscon-pcie-ctrl = <&pcie0_ctrl>; + ti,syscon-pcie-ctrl = <&pcie0_ctrl 0x4070>; max-link-speed = <3>; num-lanes = <2>; power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; @@ -110,5 +137,13 @@ ranges = <0x01000000 0x0 0x10001000 0x00 0x10001000 0x0 0x0010000>, <0x02000000 0x0 0x10011000 0x00 0x10011000 0x0 0x7fef000>; dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + + + pcie0_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic500>; + interrupts = ; + }; }; }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/cdns,dphy.txt b/Documentation/devicetree/bindings/phy/cdns,dphy.txt --- a/Documentation/devicetree/bindings/phy/cdns,dphy.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/cdns,dphy.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,20 +0,0 @@ -Cadence DPHY -============ - -Cadence DPHY block. - -Required properties: -- compatible: should be set to "cdns,dphy". -- reg: physical base address and length of the DPHY registers. -- clocks: DPHY reference clocks. -- clock-names: must contain "psm" and "pll_ref". -- #phy-cells: must be set to 0. - -Example: - dphy0: dphy@fd0e0000{ - compatible = "cdns,dphy"; - reg = <0x0 0xfd0e0000 0x0 0x1000>; - clocks = <&psm_clk>, <&pll_ref_clk>; - clock-names = "psm", "pll_ref"; - #phy-cells = <0>; - }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/cdns,dphy.yaml b/Documentation/devicetree/bindings/phy/cdns,dphy.yaml --- a/Documentation/devicetree/bindings/phy/cdns,dphy.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/cdns,dphy.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/cdns,dphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence DPHY Device Tree Bindings + +maintainers: + - Pratyush Yadav + +properties: + compatible: + oneOf: + - items: + - enum: + - ti,j721e-dphy + - const: cdns,dphy + - const: cdns,dphy + + reg: + maxItems: 1 + + clocks: + items: + - description: PMA state machine clock + - description: PLL reference clock + + clock-names: + items: + - const: psm + - const: pll_ref + + "#phy-cells": + const: 0 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + #include + + dphy0: phy@fd0e0000{ + compatible = "cdns,dphy"; + reg = <0xfd0e0000 0x1000>; + clocks = <&psm_clk>, <&pll_ref_clk>; + clock-names = "psm", "pll_ref"; + power-domains = <&k3_pds 147 TI_SCI_PD_EXCLUSIVE>; + #phy-cells = <0>; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt --- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,70 +0,0 @@ -Cadence Sierra PHY ------------------------ - -Required properties: -- compatible: Must be "cdns,sierra-phy-t0" for Sierra in Cadence platform - Must be "ti,sierra-phy-t0" for Sierra in TI's J721E SoC. -- resets: Must contain an entry for each in reset-names. - See ../reset/reset.txt for details. -- reset-names: Must include "sierra_reset" and "sierra_apb". - "sierra_reset" must control the reset line to the PHY. - "sierra_apb" must control the reset line to the APB PHY - interface ("sierra_apb" is optional). -- reg: register range for the PHY. -- #address-cells: Must be 1 -- #size-cells: Must be 0 - -Optional properties: -- clocks: Must contain an entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names: Must contain "cmn_refclk_dig_div" and - "cmn_refclk1_dig_div" for configuring the frequency of - the clock to the lanes. "phy_clk" is deprecated. -- cdns,autoconf: A boolean property whose presence indicates that the - PHY registers will be configured by hardware. If not - present, all sub-node optional properties must be - provided. - -Sub-nodes: - Each group of PHY lanes with a single master lane should be represented as - a sub-node. Note that the actual configuration of each lane is determined by - hardware strapping, and must match the configuration specified here. - -Sub-node required properties: -- #phy-cells: Generic PHY binding; must be 0. -- reg: The master lane number. This is the lowest numbered lane - in the lane group. -- resets: Must contain one entry which controls the reset line for the - master lane of the sub-node. - See ../reset/reset.txt for details. - -Sub-node optional properties: -- cdns,num-lanes: Number of lanes in this group. From 1 to 4. The - group is made up of consecutive lanes. -- cdns,phy-type: Can be PHY_TYPE_PCIE or PHY_TYPE_USB3, depending on - configuration of lanes. - -Example: - pcie_phy4: pcie-phy@fd240000 { - compatible = "cdns,sierra-phy-t0"; - reg = <0x0 0xfd240000 0x0 0x40000>; - resets = <&phyrst 0>, <&phyrst 1>; - reset-names = "sierra_reset", "sierra_apb"; - clocks = <&phyclock>; - clock-names = "phy_clk"; - #address-cells = <1>; - #size-cells = <0>; - pcie0_phy0: pcie-phy@0 { - reg = <0>; - resets = <&phyrst 2>; - cdns,num-lanes = <2>; - #phy-cells = <0>; - cdns,phy-type = ; - }; - pcie0_phy1: pcie-phy@2 { - reg = <2>; - resets = <&phyrst 4>; - cdns,num-lanes = <1>; - #phy-cells = <0>; - cdns,phy-type = ; - }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml --- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,167 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/phy-cadence-sierra.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Cadence Sierra PHY binding + +description: + This binding describes the Cadence Sierra PHY. Sierra PHY supports multilink + multiprotocol combinations including protocols such as PCIe, USB etc. + +maintainers: + - Swapnil Jakhade + - Yuti Amonkar + +properties: + compatible: + enum: + - cdns,sierra-phy-t0 + - ti,sierra-phy-t0 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + '#clock-cells': + const: 1 + + resets: + minItems: 1 + maxItems: 2 + items: + - description: Sierra PHY reset. + - description: Sierra APB reset. This is optional. + + reset-names: + minItems: 1 + maxItems: 2 + items: + - const: sierra_reset + - const: sierra_apb + + reg: + maxItems: 1 + description: + Offset of the Sierra PHY configuration registers. + + reg-names: + const: serdes + + clocks: + minItems: 2 + maxItems: 4 + + clock-names: + minItems: 2 + items: + - const: cmn_refclk_dig_div + - const: cmn_refclk1_dig_div + - const: pll0_refclk + - const: pll1_refclk + + assigned-clocks: + minItems: 1 + maxItems: 2 + + assigned-clock-parents: + minItems: 1 + maxItems: 2 + + cdns,autoconf: + type: boolean + description: + A boolean property whose presence indicates that the PHY registers will be + configured by hardware. If not present, all sub-node optional properties + must be provided. + +patternProperties: + '^phy@[0-9a-f]$': + type: object + description: + Each group of PHY lanes with a single master lane should be represented as + a sub-node. Note that the actual configuration of each lane is determined + by hardware strapping, and must match the configuration specified here. + properties: + reg: + description: + The master lane number. This is the lowest numbered lane in the lane group. + minimum: 0 + maximum: 15 + + resets: + minItems: 1 + maxItems: 4 + description: + Contains list of resets, one per lane, to get all the link lanes out of reset. + + "#phy-cells": + const: 0 + + cdns,phy-type: + description: + Specifies the type of PHY for which the group of PHY lanes is used. + Refer include/dt-bindings/phy/phy.h. Constants from the header should be used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [2, 4] + + cdns,num-lanes: + description: + Number of lanes in this group. The group is made up of consecutive lanes. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 16 + + required: + - reg + - resets + - "#phy-cells" + + additionalProperties: false + +required: + - compatible + - "#address-cells" + - "#size-cells" + - reg + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + sierra-phy@fd240000 { + compatible = "cdns,sierra-phy-t0"; + reg = <0x0 0xfd240000 0x0 0x40000>; + resets = <&phyrst 0>, <&phyrst 1>; + reset-names = "sierra_reset", "sierra_apb"; + clocks = <&cmn_refclk_dig_div>, <&cmn_refclk1_dig_div>; + clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div"; + #address-cells = <1>; + #size-cells = <0>; + pcie0_phy0: phy@0 { + reg = <0>; + resets = <&phyrst 2>; + cdns,num-lanes = <2>; + #phy-cells = <0>; + cdns,phy-type = ; + }; + pcie0_phy1: phy@2 { + reg = <2>; + resets = <&phyrst 4>; + cdns,num-lanes = <1>; + #phy-cells = <0>; + cdns,phy-type = ; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml --- a/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -28,13 +28,27 @@ '#size-cells': const: 0 + '#clock-cells': + const: 1 + clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 description: - PHY reference clock. Must contain an entry in clock-names. + PHY reference clock for 1 item. Must contain an entry in clock-names. + Optional Parent to enable output reference clock. clock-names: - const: refclk + minItems: 1 + items: + - const: refclk + - const: phy_en_refclk + + assigned-clocks: + maxItems: 3 + + assigned-clock-parents: + maxItems: 3 reg: minItems: 1 @@ -170,7 +184,7 @@ }; - | #include - #include + #include bus { #address-cells = <2>; diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml --- a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -15,6 +15,7 @@ enum: - ti,j721e-wiz-16g - ti,j721e-wiz-10g + - ti,am64-wiz-10g power-domains: maxItems: 1 @@ -42,6 +43,9 @@ "#reset-cells": const: 1 + "#clock-cells": + const: 1 + ranges: true assigned-clocks: diff -Naur --no-dereference a/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml b/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml --- a/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/ti,tcan104x-can.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TCAN104x CAN TRANSCEIVER PHY + +maintainers: + - Aswath Govindraju + +properties: + $nodename: + pattern: "^can-phy" + + compatible: + enum: + - ti,tcan1042 + - ti,tcan1043 + + '#phy-cells': + const: 0 + + standby-gpios: + description: + gpio node to toggle standby signal on transceiver + maxItems: 1 + + enable-gpios: + description: + gpio node to toggle enable signal on transceiver + maxItems: 1 + + max-bitrate: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + max bit rate supported in bps + minimum: 1 + +required: + - compatible + - '#phy-cells' + +additionalProperties: false + +examples: + - | + #include + + transceiver1: can-phy { + compatible = "ti,tcan1043"; + #phy-cells = <0>; + max-bitrate = <5000000>; + standby-gpios = <&wakeup_gpio1 16 GPIO_ACTIVE_LOW>; + enable-gpios = <&main_gpio1 67 GPIO_ACTIVE_HIGH>; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml --- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -63,7 +63,7 @@ reg = <0x1e6e2000 0x1a8>; pinctrl: pinctrl { - compatible = "aspeed,g4-pinctrl"; + compatible = "aspeed,ast2400-pinctrl"; pinctrl_i2c3_default: i2c3_default { function = "I2C3"; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml --- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -81,7 +81,7 @@ reg = <0x1e6e2000 0x1a8>; pinctrl: pinctrl { - compatible = "aspeed,g5-pinctrl"; + compatible = "aspeed,ast2500-pinctrl"; aspeed,external-nodes = <&gfx>, <&lhc>; pinctrl_i2c3_default: i2c3_default { diff -Naur --no-dereference a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml --- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -95,7 +95,7 @@ reg = <0x1e6e2000 0xf6c>; pinctrl: pinctrl { - compatible = "aspeed,g6-pinctrl"; + compatible = "aspeed,ast2600-pinctrl"; pinctrl_pwm10g1_default: pwm10g1_default { function = "PWM10"; diff -Naur --no-dereference a/Documentation/devicetree/bindings/ptp/ptp-idtcm.yaml b/Documentation/devicetree/bindings/ptp/ptp-idtcm.yaml --- a/Documentation/devicetree/bindings/ptp/ptp-idtcm.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/ptp/ptp-idtcm.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -59,9 +59,7 @@ examples: - | - i2c@1 { - compatible = "abc,acme-1234"; - reg = <0x01 0x400>; + i2c { #address-cells = <1>; #size-cells = <0>; phc@5b { diff -Naur --no-dereference a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt --- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,51 +0,0 @@ -TI SOC ECAP based APWM controller - -Required properties: -- compatible: Must be "ti,-ecap". - for am33xx - compatible = "ti,am3352-ecap", "ti,am33xx-ecap"; - for am4372 - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - for da850 - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - for dra746 - compatible = "ti,dra746-ecap", "ti,am3352-ecap"; - for 66ak2g - compatible = "ti,k2g-ecap", "ti,am3352-ecap"; - for am654 - compatible = "ti,am654-ecap", "ti,am3352-ecap"; -- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of - the cells format. The PWM channel index ranges from 0 to 4. The only third - cell flag supported by this binding is PWM_POLARITY_INVERTED. -- reg: physical base address and size of the registers map. - -Optional properties: -- clocks: Handle to the ECAP's functional clock. -- clock-names: Must be set to "fck". - -Example: - -ecap0: ecap@48300100 { /* ECAP on am33xx */ - compatible = "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x48300100 0x80>; - clocks = <&l4ls_gclk>; - clock-names = "fck"; -}; - -ecap0: ecap@48300100 { /* ECAP on am4372 */ - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x48300100 0x80>; - ti,hwmods = "ecap0"; - clocks = <&l4ls_gclk>; - clock-names = "fck"; -}; - -ecap0: ecap@1f06000 { /* ECAP on da850 */ - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x1f06000 0x80>; -}; - -ecap0: ecap@4843e100 { - compatible = "ti,dra746-ecap", "ti,am3352-ecap"; - #pwm-cells = <3>; - reg = <0x4843e100 0x80>; - clocks = <&l4_root_clk_div>; - clock-names = "fck"; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml --- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-tiecap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI SOC ECAP based APWM controller + +maintainers: + - Vignesh R + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: ti,am3352-ecap + - items: + - enum: + - ti,da850-ecap + - ti,am4372-ecap + - ti,dra746-ecap + - ti,k2g-ecap + - ti,am654-ecap + - ti,am64-ecap + - const: ti,am3352-ecap + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + description: | + See pwm.yaml in this directory for a description of the cells format. + The only third cell flag supported by this binding is PWM_POLARITY_INVERTED. + + clock-names: + const: fck + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + ecap0: pwm@48300100 { /* ECAP on am33xx */ + compatible = "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "fck"; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt --- a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,50 +0,0 @@ -TI SOC EHRPWM based PWM controller - -Required properties: -- compatible: Must be "ti,-ehrpwm". - for am33xx - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for am4372 - compatible = "ti,am4372-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for am654 - compatible = "ti,am654-ehrpwm", "ti-am3352-ehrpwm"; - for da850 - compatible = "ti,da850-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for dra746 - compatible = "ti,dra746-ehrpwm", "ti-am3352-ehrpwm"; -- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of - the cells format. The only third cell flag supported by this binding is - PWM_POLARITY_INVERTED. -- reg: physical base address and size of the registers map. - -Optional properties: -- clocks: Handle to the PWM's time-base and functional clock. -- clock-names: Must be set to "tbclk" and "fck". - -Example: - -ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */ - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x48300200 0x100>; - clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; - clock-names = "tbclk", "fck"; -}; - -ehrpwm0: pwm@48300200 { /* EHRPWM on am4372 */ - compatible = "ti,am4372-ehrpwm", "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x48300200 0x80>; - clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; - clock-names = "tbclk", "fck"; - ti,hwmods = "ehrpwm0"; -}; - -ehrpwm0: pwm@1f00000 { /* EHRPWM on da850 */ - compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x1f00000 0x2000>; -}; - -ehrpwm0: pwm@4843e200 { /* EHRPWM on dra746 */ - compatible = "ti,dra746-ehrpwm", "ti,am3352-ehrpwm"; - #pwm-cells = <3>; - reg = <0x4843e200 0x80>; - clocks = <&ehrpwm0_tbclk>, <&l4_root_clk_div>; - clock-names = "tbclk", "fck"; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml --- a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-tiehrpwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI SOC EHRPWM based PWM controller + +maintainers: + - Vignesh R + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: ti,am3352-ehrpwm + - items: + - enum: + - ti,da850-ehrpwm + - ti,am4372-ehrpwm + - ti,dra746-ehrpwm + - ti,am654-ehrpwm + - ti,am64-epwm + - const: ti,am3352-ehrpwm + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + description: | + See pwm.yaml in this directory for a description of the cells format. + The only third cell flag supported by this binding is PWM_POLARITY_INVERTED. + + clock-names: + items: + - const: tbclk + - const: fck + + clocks: + maxItems: 2 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */ + compatible = "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; + clock-names = "tbclk", "fck"; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/remoteproc/ti,k3-m4f-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-m4f-rproc.yaml --- a/Documentation/devicetree/bindings/remoteproc/ti,k3-m4f-rproc.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-m4f-rproc.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/ti,k3-m4f-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI K3 M4F processor subsystems + +maintainers: + - Hari Nagalla + +description: | + Some K3 family SoCs have Arm Cortex M4F cores. AM64x is a SoC in K3 + family with a M4F core. Typically safety oriented applications may use + the M4F core in isolation with out an IPC. Where as some Industrial and + home automation applications, may use the M4F core as a remote processor + with IPC communications. + +properties: + $nodename: + pattern: "^m4fss(@.*)?" + + compatible: + enum: + - ti,am64-m4fss + + power-domains: + description: | + Should contain a phandle to a PM domain provider node and an args + specifier containing the M4FSS device id value. + maxItems: 1 + + "#address-cells": + const: 2 + + "#size-cells": + const: 2 + + reg: + items: + - description: Address and Size of the IRAM internal memory region + - description: Address and Size of the DRAM internal memory region + + reg-names: + items: + - const: iram + - const: dram + + resets: + description: | + Should contain the phandle to the reset controller node managing the + local resets for this device, and a reset specifier. + maxItems: 1 + + firmware-name: + description: | + Should contain the name of the default firmware image + file located on the firmware search path + + mboxes: + description: | + OMAP Mailbox specifier denoting the sub-mailbox, to be used for + communication with the remote processor. This property should match + with the sub-mailbox node used in the firmware image. + maxItems: 1 + + memory-region: + description: | + phandle to the reserved memory nodes to be associated with the + remoteproc device. There should be at least two reserved memory nodes + defined. The reserved memory nodes should be carveout nodes, and + should be defined with a "no-map" property as per the bindings in + Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt + minItems: 2 + maxItems: 8 +# items: +# - description: region used for dynamic DMA allocations like vrings and +# vring buffers +# - description: region reserved for firmware image sections + additionalItems: true + + unevaluatedProperties: false + + +required: + - compatible + - power-domains + - "#address-cells" + - "#size-cells" + - reg + - reg-names + - ti,sci + - ti,sci-dev-id + - ti,sci-proc-ids + - resets + - firmware-name + +additionalProperties: false + +examples: + - | + cbass_main: bus@f4000 { + #address-cells = <2>; + #size-cells = <2>; + mcu_m4fss: m4fss@5000000 { + compatible = "ti,am64-m4fss"; + reg = <0x00 0x5000000 0x00 0x30000>, + <0x00 0x5040000 0x00 0x10000>; + reg-names = "iram", "dram"; + ti,sci = <&dmsc>; + ti,sci-dev-id = <9>; + ti,sci-proc-ids = <0x18 0xff>; + resets = <&k3_reset 9 1>; + firmware-name = "am64-mcu-m4f0_0-fw"; + mboxes = <&mailbox0_cluster6 &mbox_m4_0>; + memory-region = <&mcu_m4fss_dma_memory_region>, + <&mcu_m4fss_memory_region>; + }; + }; + +... diff -Naur --no-dereference a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml --- a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -14,8 +14,12 @@ processor subsystems/clusters (R5FSS). The dual core cluster can be used either in a LockStep mode providing safety/fault tolerance features or in a Split mode providing two individual compute cores for doubling the compute - capacity. These are used together with other processors present on the SoC - to achieve various system level goals. + capacity on most SoCs. These are used together with other processors present + on the SoC to achieve various system level goals. + + AM64x SoCs do not support LockStep mode, but rather a new non-safety mode + called "Single-CPU" mode, where only Core0 is used, but with ability to use + Core1's TCMs as well. Each Dual-Core R5F sub-system is represented as a single DTS node representing the cluster, with a pair of child DT nodes representing @@ -32,6 +36,8 @@ enum: - ti,am654-r5fss - ti,j721e-r5fss + - ti,j7200-r5fss + - ti,am64-r5fss power-domains: description: | @@ -55,11 +61,12 @@ ti,cluster-mode: $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1] description: | Configuration Mode for the Dual R5F cores within the R5F cluster. - Should be either a value of 1 (LockStep mode) or 0 (Split mode), - default is LockStep mode if omitted. + Should be either a value of 1 (LockStep mode) or 0 (Split mode) on + most SoCs (AM65x, J721E, J7200), default is LockStep mode if omitted; + and should be either a value of 0 (Split mode) or 2 (Single-CPU mode) + on AM64x SoCs, default is Split mode if omitted. # R5F Processor Child Nodes: # ========================== @@ -95,6 +102,8 @@ enum: - ti,am654-r5f - ti,j721e-r5f + - ti,j7200-r5f + - ti,am64-r5f reg: items: @@ -196,6 +205,20 @@ unevaluatedProperties: false +if: + properties: + compatible: + enum: + - ti,am64-r5fss +then: + properties: + ti,cluster-mode: + enum: [0, 2] +else: + properties: + ti,cluster-mode: + enum: [0, 1] + required: - compatible - power-domains diff -Naur --no-dereference a/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml --- a/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -65,7 +65,7 @@ OMAP Mailbox specifier denoting the sub-mailbox, to be used for communication with the remote processor. The specifier format is as per the bindings, - Documentation/devicetree/bindings/mailbox/omap-mailbox.txt + Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml This property should match with the sub-mailbox node used in the firmware image. diff -Naur --no-dereference a/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml b/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml --- a/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/ti,pru-consumer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common TI PRU Consumer Binding + +maintainers: + - Suman Anna + +description: | + A PRU application/consumer/user node typically uses one or more PRU device + nodes to implement a PRU application/functionality. Each application/client + node would need a reference to at least a PRU node, and optionally define + some properties needed for hardware/firmware configuration. The below + properties are a list of common properties supported by the PRU remoteproc + infrastructure. + + The application nodes shall define their own bindings like regular platform + devices, so below are in addition to each node's bindings. + +properties: + ti,prus: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: phandles to the PRU, RTU or Tx_PRU nodes used + + firmware-name: + $ref: /schemas/types.yaml#/definitions/string-array + description: | + firmwares for the PRU cores, the default firmware for the core from + the PRU node will be used if not provided. The firmware names should + correspond to the PRU cores listed in the 'ti,prus' property + + ti,pruss-gp-mux-sel: + $ref: /schemas/types.yaml#/definitions/uint32-array + enum: [0, 1, 2, 3, 4] + description: | + array of values for the GP_MUX_SEL under PRUSS_GPCFG register for a PRU. + This selects the internal muxing scheme for the PRU instance. Values + should correspond to the PRU cores listed in the 'ti,prus' property. The + GP_MUX_SEL setting is a per-slice setting (one setting for PRU0, RTU0, + and Tx_PRU0 on K3 SoCs). Use the same value for all cores within the + same slice in the associative array. If the array size is smaller than + the size of 'ti,prus' property, the default out-of-reset value (0) for the + PRU core is used. + +required: + - ti,prus + +dependencies: + firmware-name: [ 'ti,prus' ] + ti,pruss-gp-mux-sel: [ 'ti,prus' ] + +additionalProperties: true + +examples: + - | + /* PRU application node example */ + pru-app { + ti,prus = <&pru0>, <&rtu0>, <&pru1>, <&rtu1>; + firmware-name = "pruss-app-fw0", "pruss-app-fw1", "pruss-app-fw2", "pruss-app-fw3"; + ti,pruss-gp-mux-sel = <2>, <1>, <2>, <1>; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml --- a/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,248 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/ti,pru-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI Programmable Realtime Unit (PRU) cores + +maintainers: + - Suman Anna + +description: | + Each Programmable Real-Time Unit and Industrial Communication Subsystem + (PRU-ICSS or PRUSS) has two 32-bit load/store RISC CPU cores called + Programmable Real-Time Units (PRUs), each represented by a node. Each PRU + core has a dedicated Instruction RAM, Control and Debug register sets, and + use the Data RAMs present within the PRU-ICSS for code execution. + + The K3 SoCs containing ICSSG v1.0 (eg: AM65x SR1.0) also have two Auxiliary + PRU cores called RTUs with slightly different IP integration. The K3 SoCs + containing the revised ICSSG v1.1 (eg: J721E, AM65x SR2.0) have an extra two + auxiliary Transmit PRU cores called Tx_PRUs that augment the PRUs. Each RTU + or Tx_PRU core can also be used independently like a PRU, or alongside a + corresponding PRU core to provide/implement auxiliary functionality/support. + + Each PRU, RTU or Tx_PRU core node should be defined as a child node of the + corresponding PRU-ICSS node. Each node can optionally be rendered inactive by + using the standard DT string property, "status". + + Please see the overall PRU-ICSS bindings document for additional details + including a complete example, + Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml + +allOf: + - $ref: /schemas/interrupts.yaml# + +properties: + compatible: + enum: + - ti,am3356-pru # for AM335x SoC family (AM3356+ SoCs only) + - ti,am4376-pru # for AM437x SoC family (AM4376+ SoCs only) + - ti,am5728-pru # for AM57xx SoC family + - ti,k2g-pru # for 66AK2G SoC family + - ti,am654-pru # for PRUs in K3 AM65x SoC family + - ti,am654-rtu # for RTUs in K3 AM65x SoC family + - ti,am654-tx-pru # for Tx_PRUs in K3 AM65x SR2.0 SoCs + - ti,j721e-pru # for PRUs in K3 J721E SoC family + - ti,j721e-rtu # for RTUs in K3 J721E SoC family + - ti,j721e-tx-pru # for Tx_PRUs in K3 J721E SoC family + - ti,am642-pru # for PRUs in K3 AM64x SoC family + - ti,am642-rtu # for RTUs in K3 AM64x SoC family + - ti,am642-tx-pru # for Tx_PRUs in K3 AM64x SoC family + + reg: + items: + - description: Address and Size of the PRU Instruction RAM + - description: Address and Size of the PRU CTRL sub-module registers + - description: Address and Size of the PRU Debug sub-module registers + + reg-names: + items: + - const: iram + - const: control + - const: debug + + firmware-name: + description: | + Should contain the name of the default firmware image + file located on the firmware search path. + +# Optional Properties: +# -------------------- +# The virtio based communication between the MPU and a PRU core _requires_ the +# set of 'interrupt-parent', 'interrupts' and 'interrupt-names' properties. + + interrupts: + description: | + Array of interrupt specifiers if using PRU system events for IPC + signalling between host and a PRU core. This property should match + with the PRU system event used in the corresponding firmware image. + items: + - description: PRU event number used to receive virtqueue events + + interrupt-names: + items: + - const: vring # for PRU to HOST virtqueue signalling + +if: + properties: + compatible: + enum: + - ti,am654-rtu + - ti,j721e-rtu + - ti,am642-rtu +then: + properties: + $nodename: + pattern: "^rtu@[0-9a-f]+$" +else: + if: + properties: + compatible: + enum: + - ti,am654-tx-pru + - ti,j721e-tx-pru + - ti,am642-tx-pru + then: + properties: + $nodename: + pattern: "^txpru@[0-9a-f]+" + else: + properties: + $nodename: + pattern: "^pru@[0-9a-f]+$" + +dependencies: + interrupts: [interrupt-names, interrupt-parent] + +required: + - compatible + - reg + - reg-names + - firmware-name + +additionalProperties: false + +examples: + - | + /* AM33xx PRU-ICSS */ + pruss_tm: target-module@300000 { /* 0x4a300000, ap 9 04.0 */ + compatible = "ti,sysc-pruss", "ti,sysc"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x300000 0x80000>; + + pruss: pruss@0 { + compatible = "ti,am3356-pruss"; + reg = <0x0 0x80000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pruss_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x3000>; + reg-names = "dram0", "dram1", "shrdram2"; + }; + + pru0: pru@34000 { + compatible = "ti,am3356-pru"; + reg = <0x34000 0x2000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am335x-pru0-fw"; + interrupt-parent = <&pruss_intc>; + interrupts = <16 2 2>; + interrupt-names = "vring"; + }; + + pru1: pru@38000 { + compatible = "ti,am3356-pru"; + reg = <0x38000 0x2000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am335x-pru1-fw"; + interrupt-parent = <&pruss_intc>; + interrupts = <18 3 3>; + interrupt-names = "vring"; + }; + }; + }; + + - | + /* AM65x SR2.0 ICSSG */ + #include + + icssg0: icssg@b000000 { + compatible = "ti,am654-icssg"; + reg = <0xb000000 0x80000>; + power-domains = <&k3_pds 62 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xb000000 0x80000>; + + icssg0_mem: memories@0 { + reg = <0x0 0x2000>, + <0x2000 0x2000>, + <0x10000 0x10000>; + reg-names = "dram0", "dram1", "shrdram2"; + }; + + pru0_0: pru@34000 { + compatible = "ti,am654-pru"; + reg = <0x34000 0x4000>, + <0x22000 0x100>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru0_0-fw"; + }; + + rtu0_0: rtu@4000 { + compatible = "ti,am654-rtu"; + reg = <0x4000 0x2000>, + <0x23000 0x100>, + <0x23400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu0_0-fw"; + }; + + tx_pru0_0: txpru@a000 { + compatible = "ti,am654-tx-pru"; + reg = <0xa000 0x1800>, + <0x25000 0x100>, + <0x25400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru0_0-fw"; + }; + + pru0_1: pru@38000 { + compatible = "ti,am654-pru"; + reg = <0x38000 0x4000>, + <0x24000 0x100>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-pru0_1-fw"; + }; + + rtu0_1: rtu@6000 { + compatible = "ti,am654-rtu"; + reg = <0x6000 0x2000>, + <0x23800 0x100>, + <0x23c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-rtu0_1-fw"; + }; + + tx_pru0_1: txpru@c000 { + compatible = "ti,am654-tx-pru"; + reg = <0xc000 0x1800>, + <0x25800 0x100>, + <0x25c00 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am65x-txpru0_1-fw"; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/serial/omap_serial.txt b/Documentation/devicetree/bindings/serial/omap_serial.txt --- a/Documentation/devicetree/bindings/serial/omap_serial.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/serial/omap_serial.txt 2022-01-06 12:45:53.806318073 -0500 @@ -1,6 +1,7 @@ OMAP UART controller Required properties: +- compatible : should be "ti,am64-uart", "ti,am654-uart" for AM64 controllers - compatible : should be "ti,j721e-uart", "ti,am654-uart" for J721E controllers - compatible : should be "ti,am654-uart" for AM654 controllers - compatible : should be "ti,omap2-uart" for OMAP2 controllers diff -Naur --no-dereference a/Documentation/devicetree/bindings/serial/ti,pruss-uart.yaml b/Documentation/devicetree/bindings/serial/ti,pruss-uart.yaml --- a/Documentation/devicetree/bindings/serial/ti,pruss-uart.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/serial/ti,pruss-uart.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/ti,pruss-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI PRUSS serial UART + +maintainers: + - Bin Liu + +description: | + The PRU-ICSS module has a serial UART peripheral, which is based on + industry standard TL16C550, with 16-bytes TX/RX FIFOs. + +allOf: + - $ref: /schemas/serial.yaml# + +properties: + compatible: + items: + - const: ti,pruss-uart + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + minItems: 3 + maxItems: 3 + description: | + PRU UART interrupt mappings, containing an entry of 3 cell-values. + The first is the PRU System Event id for PRU UART Interrupt Request. + The second is the PRU interrupt channel id. + The third is the PRU host interrupt id. + +required: + - compatible + - reg + - clocks + - interrupt-parent + - interrupts + +examples: + - | + pruss_uart: serial@28000 { + compatible = "ti,pruss-uart"; + reg = <0x28000 0x38>; + clocks = <&dpll_per_m2_ck>; + interrupt-parent = <&pruss_intc>; + interrupts = <6 2 2>; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/serial/ti,pru-swuart.txt b/Documentation/devicetree/bindings/serial/ti,pru-swuart.txt --- a/Documentation/devicetree/bindings/serial/ti,pru-swuart.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/serial/ti,pru-swuart.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,60 @@ +PRU Software UART on TI SoCs + +The PRU can be programmed as multi-ports UART. + +Each PRU Software UART (SWUART) node should define the PRU Application node +properties as detailed in bindings ti,pru-consumer.yaml and one or more port +nodes describing the emulated UART port(s). + +Required properties: +-------------------- +- compatible : should be "ti,pru-swuart". +- interrupt-parent : phandle to the PRUSS INTC node. +- ti,prus : phandle to the PRU node used. +- firmware-name : SWUART firmware for the PRU core. + +UART Port Node +============== +Required properties: +-------------------- +- reg : index of the port, 0-based. +- interrupts : interrupt specifier for PRU signaling the host, + containing an single entry of 3 cell-values. + The First one is the PRU System Event id. + The second is the PRU interrupt channel id. + The third is the PRU host interrupt id. +- ti,pru-swuart-pins : PRU pin mapping for UART signals, containing a single + entry of 2 or 4 byte cell-values. The first two values + are for txd and rxd pins, the next two values are + optional which are for cts and rts pins. The value is + the index of the PRU GPIOs defined in PRU R30 and R31. + +Example (AM335x BeagleBone Black board): +--------------------------------------- + + pru-swuart0 { + compatible = "ti,pru-swuart"; + interrupt-parent = <&pruss_intc>; + ti,prus = <&pru0>; + firmware-name = "ti-pruss/pru0_swuart-fw.out"; + pinctrl-0 = <&pru_uart0_bone_pins>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + pru0_port0: port@0 { + reg = <0>; + interrupts = <21 2 2>; + ti,pru-swuart-pins = /bits/ 8 <0 1>; + }; + pru0_port1: port@1 { + reg = <1>; + interrupts = <22 3 3>; + ti,pru-swuart-pins = /bits/ 8 <2 3 4 5>; + }; + pru0_port2: port@2 { + reg = <2>; + interrupts = <23 4 4>; + ti,pru-swuart-pins = /bits/ 8 <8 9 10 11>; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt b/Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt --- a/Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,73 @@ +TI Keystone DSP Memory Mapping +============================== + +Binding status: Unstable - ABI compatibility may be broken in the future + +The Keystone DSP Memory Mapping binding defines the memory regions that +are reserved to be used with DSPs. These regions can be mapped into +userspace for providing direct user-mode access to these regions for the +purposes of shared memory communication with the DSP remote processor +devices on the SoC. These memory regions can also be used for supporting +user-space based loading of the DSP remoteproc devices. + +The memory regions can either be from regular DDR memory or from the +On-chip RAM, and there can be one or more regions of each memory type. + +DDR Memory usage (Optional): +---------------------------- +One or more memory regions from DDR memory can be reserved specifically +to be used by the DSPs on the SoC. Each region should be defined as a +child node of the reserved-memory node with the following required +properties: + +- compatible : Should be "ti,keystone-dsp-mem-pool" +- reg : Should contain the region's start address and size following + the #address-cells and #size-cells defined in the parent + reserved-memory node. +- no-map : Should be defined to remove this region from kernel + +Please see Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt +for more details on reserved-memory node. + +SRAM usage (Optional): +---------------------- +The On-chip Multicore Shared Memory (MSM) RAM can also be exposed to +userspace by defining specific child nodes under the corresponding parent +SRAM node. The generic SRAM binding is as per the binding document +Documentation/devicetree/bindings/misc/sram.txt. Following properties +should be used in each corresponding child node for the userspace mapping +usecase: + +- compatible : Should be "ti,keystone-dsp-msm-ram" +- reg : Should contain a pair of values for the address and size + of the region, following the parent-child ranges convention. + +Example: +-------- + /* 66AK2H EVM */ + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 { + compatible = "ti,keystone-dsp-mem-pool"; + reg = <0x00000008 0x20000000 0x00000000 0x20000000>; + no-map; + }; + }; + + soc { + msm_ram: msmram@c000000 { + compatible = "mmio-sram"; + reg = <0x0c000000 0x600000>; + ranges = <0x0 0x0c000000 0x600000>; + #address-cells = <1>; + #size-cells = <1>; + + mpm-sram@0 { + compatible = "ti,keystone-dsp-msm-ram"; + reg = <0x0 0x80000>; + }; + }; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml b/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml --- a/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -68,6 +68,7 @@ - ti,k2g-pruss # for 66AK2G SoC family - ti,am654-icssg # for K3 AM65x SoC family - ti,j721e-icssg # for K3 J721E SoC family + - ti,am642-icssg # for K3 AM64x SoC family reg: maxItems: 1 @@ -81,6 +82,11 @@ ranges: maxItems: 1 + dma-ranges: + maxItems: 1 + + dma-coherent: true + power-domains: description: | This property is as per sci-pm-domain.txt. @@ -230,8 +236,20 @@ description: | Industrial Ethernet Peripheral to manage/generate Industrial Ethernet functions such as time stamping. Each PRUSS has either 1 IEP (on AM335x, - AM437x, AM57xx & 66AK2G SoCs) or 2 IEPs (on K3 AM65x & J721E SoCs ). IEP - is used for creating PTP clocks and generating PPS signals. + AM437x, AM57xx & 66AK2G SoCs) or 2 IEPs (on K3 AM65x, J721E & AM64x SoCs). + IEP is used for creating PTP clocks and generating PPS signals. + + type: object + + ecap@[a-f0-9]+$: + description: | + Each PRUSS can optionally have an Enhanced Capture (eCAP) module that can + provide some time-stamp features using its capture input. The eCAP module + used within the PRU-ICSS is similar to other instances used within the PWM + subsystems. + + allOf: + - $ref: /schemas/net/ti,pruss-ecap.yaml# type: object @@ -278,6 +296,9 @@ that is common to all the PRU cores. This should be represented as an interrupt-controller node. + allOf: + - $ref: /schemas/interrupt-controller/ti,pruss-intc.yaml# + type: object mdio@[a-f0-9]+$: @@ -299,6 +320,9 @@ present on K3 SoCs have additional auxiliary PRU cores with slightly different IP integration. + allOf: + - $ref: /schemas/remoteproc/ti,pru-rproc.yaml# + type: object required: @@ -324,6 +348,7 @@ - ti,k2g-pruss - ti,am654-icssg - ti,j721e-icssg + - ti,am642-icssg then: required: - power-domains @@ -371,6 +396,36 @@ reg = <0x32000 0x58>; }; + pruss_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = <20 21 22 23 24 25 26 27>; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", "host_intr5", + "host_intr6", "host_intr7"; + }; + + pru0: pru@34000 { + compatible = "ti,am3356-pru"; + reg = <0x34000 0x2000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am335x-pru0-fw"; + }; + + pru1: pru@38000 { + compatible = "ti,am3356-pru"; + reg = <0x38000 0x2000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am335x-pru1-fw"; + }; + pruss_mdio: mdio@32400 { compatible = "ti,davinci_mdio"; reg = <0x32400 0x90>; @@ -425,6 +480,43 @@ reg = <0x32000 0x58>; }; + pruss1_intc: interrupt-controller@20000 { + compatible = "ti,pruss-intc"; + reg = <0x20000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "host_intr0", "host_intr1", + "host_intr2", "host_intr3", + "host_intr4", + "host_intr6", "host_intr7"; + ti,irqs-reserved = /bits/ 8 <0x20>; /* BIT(5) */ + }; + + pru1_0: pru@34000 { + compatible = "ti,am4376-pru"; + reg = <0x34000 0x3000>, + <0x22000 0x400>, + <0x22400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am437x-pru1_0-fw"; + }; + + pru1_1: pru@38000 { + compatible = "ti,am4376-pru"; + reg = <0x38000 0x3000>, + <0x24000 0x400>, + <0x24400 0x100>; + reg-names = "iram", "control", "debug"; + firmware-name = "am437x-pru1_1-fw"; + }; + pruss1_mdio: mdio@32400 { compatible = "ti,davinci_mdio"; reg = <0x32400 0x90>; diff -Naur --no-dereference a/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt b/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt --- a/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt 2022-01-06 12:45:53.806318073 -0500 @@ -55,3 +55,94 @@ ... }; }; + +Support for I2C PMIC Voltage Scaling +-------------------- +It is possible to pass the name of a binary file to laod to the CM3 firmware +in order to provide I2C sequences for the CM3 to send out to the PMIC during +low power mode entry. + +Optional properties: +-------------------- +- scale-data-fw: Name of the firmware binary in /lib/firmware to copy to m3 + aux data. + +Support for VTT Toggle +================================== +In order to enable the support for VTT toggle during Suspend/Resume +sequence needed by some boards (like AM335x EVM-SK & AM437x GP EVM), +the below DT properties are required. It is possible to toggle VTT +using one of two methods depending on the SoC being used, either +GPIO0 toggle (AM335x and AM437x), or any GPIO with DS_PAD_CONFIG +bits in the control module (AM437x only). + +VTT Toggle using GPIO0 +---------------------------------- +Supported by: AM335x and AM437x +Used on: AM335x EVM-SK + +Optional properties: +- ti,needs-vtt-toggle: Indicates that the boards requires VTT toggling + during suspend/resume. +- ti,vtt-gpio-pin: Specifies the GPIO0 pin used for VTT toggle. + +Important Note: +- Here it is assumed that VTT Toggle will be done using a pin on GPIO-0 Instance. + It will not work on any other GPIO using the above properties, regardless of + which part is being used. + +Example: + wkup_m3_ipc: wkup_m3_ipc@1324 { + compatible = "ti,am3352-wkup-m3-ipc"; + ... + ... + ti,needs-vtt-toggle; + ti,vtt-gpio-pin = <7>; + ... + }; + + +IO Isolation +============ +Supported by: AM43xx SoCs + +It is possible to configure any pin with a corresponding CTRL_CONF_* +register in the control module to use the states defined in the DS_PAD_CONFIG +bits by enabling IO isolation on the SoC. The 'ti,set-io-isolation' property +tells the wkup_m3_ipc driver to enable IO isolation late in the suspend path +after all drivers have been disabled. + +Optional properties: +- ti,set-io-isolation: Indicates that the IO's should be placed into + isolation and the DS_PAD_CONFIG values should be + used during suspend. + +Example (VTT Toggle using any GPIO on am437x-gp-evm): +----------------------------------------------------- + +On the AM437x GP EVM, the VTT enable line must be held low to disable VTT +regulator and held high to enable, so the following pinctrl entry is used. +The DS pull is enabled which uses a pull down by default and DS off mode is +used which outputs a low by default. For the normal state, a pull up is +specified so that the VTT enable line gets pulled high immediately after +the DS states are removed upon exit from DeepSleep0. + + &am43xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&ddr3_vtt_toggle_default>; + + ddr3_vtt_toggle_default: ddr_vtt_toggle_default { + pinctrl-single,pins = < + 0x25C (DS0_PULL_UP_DOWN_EN | PIN_OUTPUT_PULLUP | + DS0_FORCE_OFF_MODE | MUX_MODE7)>; + }; + ... + }; + + wkup_m3_ipc: wkup_m3_ipc@1324 { + compatible = "ti,am4372-wkup-m3-ipc"; + ... + ... + ti,set-io-isolation; + ... + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/sound/ti,j721e-cpb-audio.yaml b/Documentation/devicetree/bindings/sound/ti,j721e-cpb-audio.yaml --- a/Documentation/devicetree/bindings/sound/ti,j721e-cpb-audio.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/sound/ti,j721e-cpb-audio.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -1,4 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated +# Author: Peter Ujfalusi %YAML 1.2 --- $id: http://devicetree.org/schemas/sound/ti,j721e-cpb-audio.yaml# @@ -7,7 +9,7 @@ title: Texas Instruments J721e Common Processor Board Audio Support maintainers: - - Peter Ujfalusi + - Peter Ujfalusi description: | The audio support on the board is using pcm3168a codec connected to McASP10 diff -Naur --no-dereference a/Documentation/devicetree/bindings/sound/ti,j721e-cpb-ivi-audio.yaml b/Documentation/devicetree/bindings/sound/ti,j721e-cpb-ivi-audio.yaml --- a/Documentation/devicetree/bindings/sound/ti,j721e-cpb-ivi-audio.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/sound/ti,j721e-cpb-ivi-audio.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -1,4 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated +# Author: Peter Ujfalusi %YAML 1.2 --- $id: http://devicetree.org/schemas/sound/ti,j721e-cpb-ivi-audio.yaml# @@ -7,7 +9,7 @@ title: Texas Instruments J721e Common Processor Board Audio Support maintainers: - - Peter Ujfalusi + - Peter Ujfalusi description: | The Infotainment board plugs into the Common Processor Board, the support of the diff -Naur --no-dereference a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt --- a/Documentation/devicetree/bindings/spi/omap-spi.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt 1969-12-31 19:00:00.000000000 -0500 @@ -1,48 +0,0 @@ -OMAP2+ McSPI device - -Required properties: -- compatible : - - "ti,am654-mcspi" for AM654. - - "ti,omap2-mcspi" for OMAP2 & OMAP3. - - "ti,omap4-mcspi" for OMAP4+. -- ti,spi-num-cs : Number of chipselect supported by the instance. -- ti,hwmods: Name of the hwmod associated to the McSPI -- ti,pindir-d0-out-d1-in: Select the D0 pin as output and D1 as - input. The default is D0 as input and - D1 as output. - -Optional properties: -- dmas: List of DMA specifiers with the controller specific format - as described in the generic DMA client binding. A tx and rx - specifier is required for each chip select. -- dma-names: List of DMA request names. These strings correspond - 1:1 with the DMA specifiers listed in dmas. The string naming - is to be "rxN" and "txN" for RX and TX requests, - respectively, where N equals the chip select number. - -Examples: - -[hwmod populated DMA resources] - -mcspi1: mcspi@1 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,omap4-mcspi"; - ti,hwmods = "mcspi1"; - ti,spi-num-cs = <4>; -}; - -[generic DMA request binding] - -mcspi1: mcspi@1 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,omap4-mcspi"; - ti,hwmods = "mcspi1"; - ti,spi-num-cs = <2>; - dmas = <&edma 42 - &edma 43 - &edma 44 - &edma 45>; - dma-names = "tx0", "rx0", "tx1", "rx1"; -}; diff -Naur --no-dereference a/Documentation/devicetree/bindings/spi/omap-spi.yaml b/Documentation/devicetree/bindings/spi/omap-spi.yaml --- a/Documentation/devicetree/bindings/spi/omap-spi.yaml 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/devicetree/bindings/spi/omap-spi.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/omap-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SPI controller bindings for OMAP and K3 SoCs + +maintainers: + - Aswath Govindraju + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - ti,am654-mcspi + - ti,am4372-mcspi + - const: ti,omap4-mcspi + - items: + - enum: + - ti,omap2-mcspi + - ti,omap4-mcspi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + ti,spi-num-cs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of chipselect supported by the instance. + minimum: 1 + maximum: 4 + + ti,hwmods: + $ref: /schemas/types.yaml#/definitions/string + description: + Must be "mcspi", n being the instance number (1-based). + This property is applicable only on legacy platforms mainly omap2/3 + and ti81xx and should not be used on other platforms. + deprecated: true + + ti,pindir-d0-out-d1-in: + description: + Select the D0 pin as output and D1 as input. The default is D0 + as input and D1 as output. + type: boolean + + dmas: + description: + List of DMA specifiers with the controller specific format as + described in the generic DMA client binding. A tx and rx + specifier is required for each chip select. + minItems: 1 + maxItems: 8 + + dma-names: + description: + List of DMA request names. These strings correspond 1:1 with + the DMA sepecifiers listed in dmas. The string names is to be + "rxN" and "txN" for RX and TX requests, respectively. Where N + is the chip select number. + minItems: 1 + maxItems: 8 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +if: + properties: + compatible: + oneOf: + - const: ti,omap2-mcspi + - const: ti,omap4-mcspi + +then: + properties: + ti,hwmods: + items: + - pattern: "^mcspi([1-9])$" + +else: + properties: + ti,hwmods: false + +examples: + - | + #include + #include + #include + + spi@2100000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x2100000 0x400>; + interrupts = ; + clocks = <&k3_clks 137 1>; + power-domains = <&k3_pds 137 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <0>; + dmas = <&main_udmap 0xc500>, <&main_udmap 0x4500>; + dma-names = "tx0", "rx0"; + }; diff -Naur --no-dereference a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml --- a/Documentation/devicetree/bindings/sram/sram.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/sram/sram.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -108,6 +108,14 @@ manipulation of the page attributes. type: boolean + dma-heap-export: + description: | + Similar to 'pool' and 'export' this region will be exported + for use by drivers, devices, and userspace using the + DMA-Heaps framework. NOTE: This region must be page aligned + on start and end in order to properly allow manipulation of + the page attributes. + label: description: The name for the reserved partition, if omitted, the label is taken diff -Naur --no-dereference a/Documentation/devicetree/bindings/timer/ti,keystone-timer.txt b/Documentation/devicetree/bindings/timer/ti,keystone-timer.txt --- a/Documentation/devicetree/bindings/timer/ti,keystone-timer.txt 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/timer/ti,keystone-timer.txt 2022-01-06 12:45:53.806318073 -0500 @@ -14,7 +14,7 @@ Required properties: -- compatible : should be "ti,keystone-timer". +- compatible : should be "ti,keystone-timer" or "ti,k2g-timer" - reg : specifies base physical address and count of the registers. - interrupts : interrupt generated by the timer. - clocks : the clock feeding the timer clock. diff -Naur --no-dereference a/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml b/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml --- a/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -11,12 +11,18 @@ properties: compatible: - items: + oneOf: - const: ti,j721e-usb + - const: ti,am64-usb + - items: + - const: ti,j721e-usb + - const: ti,am64-usb reg: description: module registers + ranges: true + power-domains: description: PM domain provider node and an args specifier containing @@ -58,6 +64,8 @@ '#size-cells': const: 2 + dma-coherent: true + patternProperties: "^usb@": type: object diff -Naur --no-dereference a/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml b/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml --- a/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml 2022-01-06 12:45:53.806318073 -0500 @@ -57,8 +57,8 @@ */ #include - watchdog0: rti@2200000 { - compatible = "ti,rti-wdt"; + watchdog@2200000 { + compatible = "ti,j7-rti-wdt"; reg = <0x2200000 0x100>; clocks = <&k3_clks 252 1>; power-domains = <&k3_pds 252 TI_SCI_PD_EXCLUSIVE>; diff -Naur --no-dereference a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst --- a/Documentation/driver-api/dmaengine/client.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/driver-api/dmaengine/client.rst 2022-01-06 12:45:53.806318073 -0500 @@ -120,7 +120,9 @@ .. code-block:: c - nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len); + struct device *dma_dev = dmaengine_get_dma_device(chan); + + nr_sg = dma_map_sg(dma_dev, sgl, sg_len); if (nr_sg == 0) /* error */ diff -Naur --no-dereference a/Documentation/driver-api/media/mc-core.rst b/Documentation/driver-api/media/mc-core.rst --- a/Documentation/driver-api/media/mc-core.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/driver-api/media/mc-core.rst 2022-01-06 12:45:53.806318073 -0500 @@ -167,10 +167,13 @@ :c:func:`media_graph_walk_start()` The graph structure, provided by the caller, is initialized to start graph -traversal at the given entity. +traversal at the given pad in an entity. -Drivers can then retrieve the next entity by calling -:c:func:`media_graph_walk_next()` +Drivers can then retrieve the next pad by calling +:c:func:`media_graph_walk_next()`. Only the pad through which the entity +is first reached is returned. If the caller is interested in knowing which +further pads would be connected, the :c:func:`media_entity_has_route()` +function can be used for that. When the graph traversal is complete the function will return ``NULL``. @@ -210,11 +213,11 @@ prevent link states from being modified during streaming by calling :c:func:`media_pipeline_start()`. -The function will mark all entities connected to the given entity through -enabled links, either directly or indirectly, as streaming. +The function will mark all entities connected to the given pad through +enabled routes and links, either directly or indirectly, as streaming. The struct media_pipeline instance pointed to by -the pipe argument will be stored in every entity in the pipeline. +the pipe argument will be stored in every pad in the pipeline. Drivers should embed the struct media_pipeline in higher-level pipeline structures and can then access the pipeline through the struct media_entity diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/01intro.txt b/Documentation/filesystems/aufs/design/01intro.txt --- a/Documentation/filesystems/aufs/design/01intro.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/01intro.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,171 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Introduction +---------------------------------------- + +aufs [ei ju: ef es] | /ey-yoo-ef-es/ | [a u f s] +1. abbrev. for "advanced multi-layered unification filesystem". +2. abbrev. for "another unionfs". +3. abbrev. for "auf das" in German which means "on the" in English. + Ex. "Butter aufs Brot"(G) means "butter onto bread"(E). + But "Filesystem aufs Filesystem" is hard to understand. +4. abbrev. for "African Urban Fashion Show". + +AUFS is a filesystem with features: +- multi layered stackable unification filesystem, the member directory + is called as a branch. +- branch permission and attribute, 'readonly', 'real-readonly', + 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their + combination. +- internal "file copy-on-write". +- logical deletion, whiteout. +- dynamic branch manipulation, adding, deleting and changing permission. +- allow bypassing aufs, user's direct branch access. +- external inode number translation table and bitmap which maintains the + persistent aufs inode number. +- seekable directory, including NFS readdir. +- file mapping, mmap and sharing pages. +- pseudo-link, hardlink over branches. +- loopback mounted filesystem as a branch. +- several policies to select one among multiple writable branches. +- revert a single systemcall when an error occurs in aufs. +- and more... + + +Multi Layered Stackable Unification Filesystem +---------------------------------------------------------------------- +Most people already knows what it is. +It is a filesystem which unifies several directories and provides a +merged single directory. When users access a file, the access will be +passed/re-directed/converted (sorry, I am not sure which English word is +correct) to the real file on the member filesystem. The member +filesystem is called 'lower filesystem' or 'branch' and has a mode +'readonly' and 'readwrite.' And the deletion for a file on the lower +readonly branch is handled by creating 'whiteout' on the upper writable +branch. + +On LKML, there have been discussions about UnionMount (Jan Blunck, +Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took +different approaches to implement the merged-view. +The former tries putting it into VFS, and the latter implements as a +separate filesystem. +(If I misunderstand about these implementations, please let me know and +I shall correct it. Because it is a long time ago when I read their +source files last time). + +UnionMount's approach will be able to small, but may be hard to share +branches between several UnionMount since the whiteout in it is +implemented in the inode on branch filesystem and always +shared. According to Bharata's post, readdir does not seems to be +finished yet. +There are several missing features known in this implementations such as +- for users, the inode number may change silently. eg. copy-up. +- link(2) may break by copy-up. +- read(2) may get an obsoleted filedata (fstat(2) too). +- fcntl(F_SETLK) may be broken by copy-up. +- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after + open(O_RDWR). + +In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was +merged into mainline. This is another implementation of UnionMount as a +separated filesystem. All the limitations and known problems which +UnionMount are equally inherited to "overlay" filesystem. + +Unionfs has a longer history. When I started implementing a stackable +filesystem (Aug 2005), it already existed. It has virtual super_block, +inode, dentry and file objects and they have an array pointing lower +same kind objects. After contributing many patches for Unionfs, I +re-started my project AUFS (Jun 2006). + +In AUFS, the structure of filesystem resembles to Unionfs, but I +implemented my own ideas, approaches and enhancements and it became +totally different one. + +Comparing DM snapshot and fs based implementation +- the number of bytes to be copied between devices is much smaller. +- the type of filesystem must be one and only. +- the fs must be writable, no readonly fs, even for the lower original + device. so the compression fs will not be usable. but if we use + loopback mount, we may address this issue. + for instance, + mount /cdrom/squashfs.img /sq + losetup /sq/ext2.img + losetup /somewhere/cow + dmsetup "snapshot /dev/loop0 /dev/loop1 ..." +- it will be difficult (or needs more operations) to extract the + difference between the original device and COW. +- DM snapshot-merge may help a lot when users try merging. in the + fs-layer union, users will use rsync(1). + +You may want to read my old paper "Filesystems in LiveCD" +(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). + + +Several characters/aspects/persona of aufs +---------------------------------------------------------------------- + +Aufs has several characters, aspects or persona. +1. a filesystem, callee of VFS helper +2. sub-VFS, caller of VFS helper for branches +3. a virtual filesystem which maintains persistent inode number +4. reader/writer of files on branches such like an application + +1. Callee of VFS Helper +As an ordinary linux filesystem, aufs is a callee of VFS. For instance, +unlink(2) from an application reaches sys_unlink() kernel function and +then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it +calls filesystem specific unlink operation. Actually aufs implements the +unlink operation but it behaves like a redirector. + +2. Caller of VFS Helper for Branches +aufs_unlink() passes the unlink request to the branch filesystem as if +it were called from VFS. So the called unlink operation of the branch +filesystem acts as usual. As a caller of VFS helper, aufs should handle +every necessary pre/post operation for the branch filesystem. +- acquire the lock for the parent dir on a branch +- lookup in a branch +- revalidate dentry on a branch +- mnt_want_write() for a branch +- vfs_unlink() for a branch +- mnt_drop_write() for a branch +- release the lock on a branch + +3. Persistent Inode Number +One of the most important issue for a filesystem is to maintain inode +numbers. This is particularly important to support exporting a +filesystem via NFS. Aufs is a virtual filesystem which doesn't have a +backend block device for its own. But some storage is necessary to +keep and maintain the inode numbers. It may be a large space and may not +suit to keep in memory. Aufs rents some space from its first writable +branch filesystem (by default) and creates file(s) on it. These files +are created by aufs internally and removed soon (currently) keeping +opened. +Note: Because these files are removed, they are totally gone after + unmounting aufs. It means the inode numbers are not persistent + across unmount or reboot. I have a plan to make them really + persistent which will be important for aufs on NFS server. + +4. Read/Write Files Internally (copy-on-write) +Because a branch can be readonly, when you write a file on it, aufs will +"copy-up" it to the upper writable branch internally. And then write the +originally requested thing to the file. Generally kernel doesn't +open/read/write file actively. In aufs, even a single write may cause a +internal "file copy". This behaviour is very similar to cp(1) command. + +Some people may think it is better to pass such work to user space +helper, instead of doing in kernel space. Actually I am still thinking +about it. But currently I have implemented it in kernel space. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/02struct.txt b/Documentation/filesystems/aufs/design/02struct.txt --- a/Documentation/filesystems/aufs/design/02struct.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/02struct.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,258 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Basic Aufs Internal Structure + +Superblock/Inode/Dentry/File Objects +---------------------------------------------------------------------- +As like an ordinary filesystem, aufs has its own +superblock/inode/dentry/file objects. All these objects have a +dynamically allocated array and store the same kind of pointers to the +lower filesystem, branch. +For example, when you build a union with one readwrite branch and one +readonly, mounted /au, /rw and /ro respectively. +- /au = /rw + /ro +- /ro/fileA exists but /rw/fileA + +Aufs lookup operation finds /ro/fileA and gets dentry for that. These +pointers are stored in a aufs dentry. The array in aufs dentry will be, +- [0] = NULL (because /rw/fileA doesn't exist) +- [1] = /ro/fileA + +This style of an array is essentially same to the aufs +superblock/inode/dentry/file objects. + +Because aufs supports manipulating branches, ie. add/delete/change +branches dynamically, these objects has its own generation. When +branches are changed, the generation in aufs superblock is +incremented. And a generation in other object are compared when it is +accessed. When a generation in other objects are obsoleted, aufs +refreshes the internal array. + + +Superblock +---------------------------------------------------------------------- +Additionally aufs superblock has some data for policies to select one +among multiple writable branches, XIB files, pseudo-links and kobject. +See below in detail. +About the policies which supports copy-down a directory, see +wbr_policy.txt too. + + +Branch and XINO(External Inode Number Translation Table) +---------------------------------------------------------------------- +Every branch has its own xino (external inode number translation table) +file. The xino file is created and unlinked by aufs internally. When two +members of a union exist on the same filesystem, they share the single +xino file. +The struct of a xino file is simple, just a sequence of aufs inode +numbers which is indexed by the lower inode number. +In the above sample, assume the inode number of /ro/fileA is i111 and +aufs assigns the inode number i999 for fileA. Then aufs writes 999 as +4(8) bytes at 111 * 4(8) bytes offset in the xino file. + +When the inode numbers are not contiguous, the xino file will be sparse +which has a hole in it and doesn't consume as much disk space as it +might appear. If your branch filesystem consumes disk space for such +holes, then you should specify 'xino=' option at mounting aufs. + +Aufs has a mount option to free the disk blocks for such holes in XINO +files on tmpfs or ramdisk. But it is not so effective actually. If you +meet a problem of disk shortage due to XINO files, then you should try +"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. +The patch localizes the assignment inumbers per tmpfs-mount and avoid +the holes in XINO files. + +Also a writable branch has three kinds of "whiteout bases". All these +are existed when the branch is joined to aufs, and their names are +whiteout-ed doubly, so that users will never see their names in aufs +hierarchy. +1. a regular file which will be hardlinked to all whiteouts. +2. a directory to store a pseudo-link. +3. a directory to store an "orphan"-ed file temporary. + +1. Whiteout Base + When you remove a file on a readonly branch, aufs handles it as a + logical deletion and creates a whiteout on the upper writable branch + as a hardlink of this file in order not to consume inode on the + writable branch. +2. Pseudo-link Dir + See below, Pseudo-link. +3. Step-Parent Dir + When "fileC" exists on the lower readonly branch only and it is + opened and removed with its parent dir, and then user writes + something into it, then aufs copies-up fileC to this + directory. Because there is no other dir to store fileC. After + creating a file under this dir, the file is unlinked. + +Because aufs supports manipulating branches, ie. add/delete/change +dynamically, a branch has its own id. When the branch order changes, +aufs finds the new index by searching the branch id. + + +Pseudo-link +---------------------------------------------------------------------- +Assume "fileA" exists on the lower readonly branch only and it is +hardlinked to "fileB" on the branch. When you write something to fileA, +aufs copies-up it to the upper writable branch. Additionally aufs +creates a hardlink under the Pseudo-link Directory of the writable +branch. The inode of a pseudo-link is kept in aufs super_block as a +simple list. If fileB is read after unlinking fileA, aufs returns +filedata from the pseudo-link instead of the lower readonly +branch. Because the pseudo-link is based upon the inode, to keep the +inode number by xino (see above) is essentially necessary. + +All the hardlinks under the Pseudo-link Directory of the writable branch +should be restored in a proper location later. Aufs provides a utility +to do this. The userspace helpers executed at remounting and unmounting +aufs by default. +During this utility is running, it puts aufs into the pseudo-link +maintenance mode. In this mode, only the process which began the +maintenance mode (and its child processes) is allowed to operate in +aufs. Some other processes which are not related to the pseudo-link will +be allowed to run too, but the rest have to return an error or wait +until the maintenance mode ends. If a process already acquires an inode +mutex (in VFS), it has to return an error. + + +XIB(external inode number bitmap) +---------------------------------------------------------------------- +Addition to the xino file per a branch, aufs has an external inode number +bitmap in a superblock object. It is also an internal file such like a +xino file. +It is a simple bitmap to mark whether the aufs inode number is in-use or +not. +To reduce the file I/O, aufs prepares a single memory page to cache xib. + +As well as XINO files, aufs has a feature to truncate/refresh XIB to +reduce the number of consumed disk blocks for these files. + + +Virtual or Vertical Dir, and Readdir in Userspace +---------------------------------------------------------------------- +In order to support multiple layers (branches), aufs readdir operation +constructs a virtual dir block on memory. For readdir, aufs calls +vfs_readdir() internally for each dir on branches, merges their entries +with eliminating the whiteout-ed ones, and sets it to file (dir) +object. So the file object has its entry list until it is closed. The +entry list will be updated when the file position is zero and becomes +obsoleted. This decision is made in aufs automatically. + +The dynamically allocated memory block for the name of entries has a +unit of 512 bytes (by default) and stores the names contiguously (no +padding). Another block for each entry is handled by kmem_cache too. +During building dir blocks, aufs creates hash list and judging whether +the entry is whiteouted by its upper branch or already listed. +The merged result is cached in the corresponding inode object and +maintained by a customizable life-time option. + +Some people may call it can be a security hole or invite DoS attack +since the opened and once readdir-ed dir (file object) holds its entry +list and becomes a pressure for system memory. But I'd say it is similar +to files under /proc or /sys. The virtual files in them also holds a +memory page (generally) while they are opened. When an idea to reduce +memory for them is introduced, it will be applied to aufs too. +For those who really hate this situation, I've developed readdir(3) +library which operates this merging in userspace. You just need to set +LD_PRELOAD environment variable, and aufs will not consume no memory in +kernel space for readdir(3). + + +Workqueue +---------------------------------------------------------------------- +Aufs sometimes requires privilege access to a branch. For instance, +in copy-up/down operation. When a user process is going to make changes +to a file which exists in the lower readonly branch only, and the mode +of one of ancestor directories may not be writable by a user +process. Here aufs copy-up the file with its ancestors and they may +require privilege to set its owner/group/mode/etc. +This is a typical case of a application character of aufs (see +Introduction). + +Aufs uses workqueue synchronously for this case. It creates its own +workqueue. The workqueue is a kernel thread and has privilege. Aufs +passes the request to call mkdir or write (for example), and wait for +its completion. This approach solves a problem of a signal handler +simply. +If aufs didn't adopt the workqueue and changed the privilege of the +process, then the process may receive the unexpected SIGXFSZ or other +signals. + +Also aufs uses the system global workqueue ("events" kernel thread) too +for asynchronous tasks, such like handling inotify/fsnotify, re-creating a +whiteout base and etc. This is unrelated to a privilege. +Most of aufs operation tries acquiring a rw_semaphore for aufs +superblock at the beginning, at the same time waits for the completion +of all queued asynchronous tasks. + + +Whiteout +---------------------------------------------------------------------- +The whiteout in aufs is very similar to Unionfs's. That is represented +by its filename. UnionMount takes an approach of a file mode, but I am +afraid several utilities (find(1) or something) will have to support it. + +Basically the whiteout represents "logical deletion" which stops aufs to +lookup further, but also it represents "dir is opaque" which also stop +further lookup. + +In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. +In order to make several functions in a single systemcall to be +revertible, aufs adopts an approach to rename a directory to a temporary +unique whiteouted name. +For example, in rename(2) dir where the target dir already existed, aufs +renames the target dir to a temporary unique whiteouted name before the +actual rename on a branch, and then handles other actions (make it opaque, +update the attributes, etc). If an error happens in these actions, aufs +simply renames the whiteouted name back and returns an error. If all are +succeeded, aufs registers a function to remove the whiteouted unique +temporary name completely and asynchronously to the system global +workqueue. + + +Copy-up +---------------------------------------------------------------------- +It is a well-known feature or concept. +When user modifies a file on a readonly branch, aufs operate "copy-up" +internally and makes change to the new file on the upper writable branch. +When the trigger systemcall does not update the timestamps of the parent +dir, aufs reverts it after copy-up. + + +Move-down (aufs3.9 and later) +---------------------------------------------------------------------- +"Copy-up" is one of the essential feature in aufs. It copies a file from +the lower readonly branch to the upper writable branch when a user +changes something about the file. +"Move-down" is an opposite action of copy-up. Basically this action is +ran manually instead of automatically and internally. +For desgin and implementation, aufs has to consider these issues. +- whiteout for the file may exist on the lower branch. +- ancestor directories may not exist on the lower branch. +- diropq for the ancestor directories may exist on the upper branch. +- free space on the lower branch will reduce. +- another access to the file may happen during moving-down, including + UDBA (see "Revalidate Dentry and UDBA"). +- the file should not be hard-linked nor pseudo-linked. they should be + handled by auplink utility later. + +Sometimes users want to move-down a file from the upper writable branch +to the lower readonly or writable branch. For instance, +- the free space of the upper writable branch is going to run out. +- create a new intermediate branch between the upper and lower branch. +- etc. + +For this purpose, use "aumvdown" command in aufs-util.git. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/03atomic_open.txt b/Documentation/filesystems/aufs/design/03atomic_open.txt --- a/Documentation/filesystems/aufs/design/03atomic_open.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/03atomic_open.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,85 @@ + +# Copyright (C) 2015-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Support for a branch who has its ->atomic_open() +---------------------------------------------------------------------- +The filesystems who implement its ->atomic_open() are not majority. For +example NFSv4 does, and aufs should call NFSv4 ->atomic_open, +particularly for open(O_CREAT|O_EXCL, 0400) case. Other than +->atomic_open(), NFSv4 returns an error for this open(2). While I am not +sure whether all filesystems who have ->atomic_open() behave like this, +but NFSv4 surely returns the error. + +In order to support ->atomic_open() for aufs, there are a few +approaches. + +A. Introduce aufs_atomic_open() + - calls one of VFS:do_last(), lookup_open() or atomic_open() for + branch fs. +B. Introduce aufs_atomic_open() calling create, open and chmod. this is + an aufs user Pip Cet's approach + - calls aufs_create(), VFS finish_open() and notify_change(). + - pass fake-mode to finish_open(), and then correct the mode by + notify_change(). +C. Extend aufs_open() to call branch fs's ->atomic_open() + - no aufs_atomic_open(). + - aufs_lookup() registers the TID to an aufs internal object. + - aufs_create() does nothing when the matching TID is registered, but + registers the mode. + - aufs_open() calls branch fs's ->atomic_open() when the matching + TID is registered. +D. Extend aufs_open() to re-try branch fs's ->open() with superuser's + credential + - no aufs_atomic_open(). + - aufs_create() registers the TID to an internal object. this info + represents "this process created this file just now." + - when aufs gets EACCES from branch fs's ->open(), then confirm the + registered TID and re-try open() with superuser's credential. + +Pros and cons for each approach. + +A. + - straightforward but highly depends upon VFS internal. + - the atomic behavaiour is kept. + - some of parameters such as nameidata are hard to reproduce for + branch fs. + - large overhead. +B. + - easy to implement. + - the atomic behavaiour is lost. +C. + - the atomic behavaiour is kept. + - dirty and tricky. + - VFS checks whether the file is created correctly after calling + ->create(), which means this approach doesn't work. +D. + - easy to implement. + - the atomic behavaiour is lost. + - to open a file with superuser's credential and give it to a user + process is a bad idea, since the file object keeps the credential + in it. It may affect LSM or something. This approach doesn't work + either. + +The approach A is ideal, but it hard to implement. So here is a +variation of A, which is to be implemented. + +A-1. Introduce aufs_atomic_open() + - calls branch fs ->atomic_open() if exists. otherwise calls + vfs_create() and finish_open(). + - the demerit is that the several checks after branch fs + ->atomic_open() are lost. in the ordinary case, the checks are + done by VFS:do_last(), lookup_open() and atomic_open(). some can + be implemented in aufs, but not all I am afraid. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/03lookup.txt b/Documentation/filesystems/aufs/design/03lookup.txt --- a/Documentation/filesystems/aufs/design/03lookup.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/03lookup.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,113 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Lookup in a Branch +---------------------------------------------------------------------- +Since aufs has a character of sub-VFS (see Introduction), it operates +lookup for branches as VFS does. It may be a heavy work. But almost all +lookup operation in aufs is the simplest case, ie. lookup only an entry +directly connected to its parent. Digging down the directory hierarchy +is unnecessary. VFS has a function lookup_one_len() for that use, and +aufs calls it. + +When a branch is a remote filesystem, aufs basically relies upon its +->d_revalidate(), also aufs forces the hardest revalidate tests for +them. +For d_revalidate, aufs implements three levels of revalidate tests. See +"Revalidate Dentry and UDBA" in detail. + + +Test Only the Highest One for the Directory Permission (dirperm1 option) +---------------------------------------------------------------------- +Let's try case study. +- aufs has two branches, upper readwrite and lower readonly. + /au = /rw + /ro +- "dirA" exists under /ro, but /rw. and its mode is 0700. +- user invoked "chmod a+rx /au/dirA" +- the internal copy-up is activated and "/rw/dirA" is created and its + permission bits are set to world readable. +- then "/au/dirA" becomes world readable? + +In this case, /ro/dirA is still 0700 since it exists in readonly branch, +or it may be a natively readonly filesystem. If aufs respects the lower +branch, it should not respond readdir request from other users. But user +allowed it by chmod. Should really aufs rejects showing the entries +under /ro/dirA? + +To be honest, I don't have a good solution for this case. So aufs +implements 'dirperm1' and 'nodirperm1' mount options, and leave it to +users. +When dirperm1 is specified, aufs checks only the highest one for the +directory permission, and shows the entries. Otherwise, as usual, checks +every dir existing on all branches and rejects the request. + +As a side effect, dirperm1 option improves the performance of aufs +because the number of permission check is reduced when the number of +branch is many. + + +Revalidate Dentry and UDBA (User's Direct Branch Access) +---------------------------------------------------------------------- +Generally VFS helpers re-validate a dentry as a part of lookup. +0. digging down the directory hierarchy. +1. lock the parent dir by its i_mutex. +2. lookup the final (child) entry. +3. revalidate it. +4. call the actual operation (create, unlink, etc.) +5. unlock the parent dir + +If the filesystem implements its ->d_revalidate() (step 3), then it is +called. Actually aufs implements it and checks the dentry on a branch is +still valid. +But it is not enough. Because aufs has to release the lock for the +parent dir on a branch at the end of ->lookup() (step 2) and +->d_revalidate() (step 3) while the i_mutex of the aufs dir is still +held by VFS. +If the file on a branch is changed directly, eg. bypassing aufs, after +aufs released the lock, then the subsequent operation may cause +something unpleasant result. + +This situation is a result of VFS architecture, ->lookup() and +->d_revalidate() is separated. But I never say it is wrong. It is a good +design from VFS's point of view. It is just not suitable for sub-VFS +character in aufs. + +Aufs supports such case by three level of revalidation which is +selectable by user. +1. Simple Revalidate + Addition to the native flow in VFS's, confirm the child-parent + relationship on the branch just after locking the parent dir on the + branch in the "actual operation" (step 4). When this validation + fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still + checks the validation of the dentry on branches. +2. Monitor Changes Internally by Inotify/Fsnotify + Addition to above, in the "actual operation" (step 4) aufs re-lookup + the dentry on the branch, and returns EBUSY if it finds different + dentry. + Additionally, aufs sets the inotify/fsnotify watch for every dir on branches + during it is in cache. When the event is notified, aufs registers a + function to kernel 'events' thread by schedule_work(). And the + function sets some special status to the cached aufs dentry and inode + private data. If they are not cached, then aufs has nothing to + do. When the same file is accessed through aufs (step 0-3) later, + aufs will detect the status and refresh all necessary data. + In this mode, aufs has to ignore the event which is fired by aufs + itself. +3. No Extra Validation + This is the simplest test and doesn't add any additional revalidation + test, and skip the revalidation in step 4. It is useful and improves + aufs performance when system surely hide the aufs branches from user, + by over-mounting something (or another method). diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/04branch.txt b/Documentation/filesystems/aufs/design/04branch.txt --- a/Documentation/filesystems/aufs/design/04branch.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/04branch.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,74 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Branch Manipulation + +Since aufs supports dynamic branch manipulation, ie. add/remove a branch +and changing its permission/attribute, there are a lot of works to do. + + +Add a Branch +---------------------------------------------------------------------- +o Confirm the adding dir exists outside of aufs, including loopback + mount, and its various attributes. +o Initialize the xino file and whiteout bases if necessary. + See struct.txt. + +o Check the owner/group/mode of the directory + When the owner/group/mode of the adding directory differs from the + existing branch, aufs issues a warning because it may impose a + security risk. + For example, when a upper writable branch has a world writable empty + top directory, a malicious user can create any files on the writable + branch directly, like copy-up and modify manually. If something like + /etc/{passwd,shadow} exists on the lower readonly branch but the upper + writable branch, and the writable branch is world-writable, then a + malicious guy may create /etc/passwd on the writable branch directly + and the infected file will be valid in aufs. + I am afraid it can be a security issue, but aufs can do nothing except + producing a warning. + + +Delete a Branch +---------------------------------------------------------------------- +o Confirm the deleting branch is not busy + To be general, there is one merit to adopt "remount" interface to + manipulate branches. It is to discard caches. At deleting a branch, + aufs checks the still cached (and connected) dentries and inodes. If + there are any, then they are all in-use. An inode without its + corresponding dentry can be alive alone (for example, inotify/fsnotify case). + + For the cached one, aufs checks whether the same named entry exists on + other branches. + If the cached one is a directory, because aufs provides a merged view + to users, as long as one dir is left on any branch aufs can show the + dir to users. In this case, the branch can be removed from aufs. + Otherwise aufs rejects deleting the branch. + + If any file on the deleting branch is opened by aufs, then aufs + rejects deleting. + + +Modify the Permission of a Branch +---------------------------------------------------------------------- +o Re-initialize or remove the xino file and whiteout bases if necessary. + See struct.txt. + +o rw --> ro: Confirm the modifying branch is not busy + Aufs rejects the request if any of these conditions are true. + - a file on the branch is mmap-ed. + - a regular file on the branch is opened for write and there is no + same named entry on the upper branch. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/05wbr_policy.txt b/Documentation/filesystems/aufs/design/05wbr_policy.txt --- a/Documentation/filesystems/aufs/design/05wbr_policy.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/05wbr_policy.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,64 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Policies to Select One among Multiple Writable Branches +---------------------------------------------------------------------- +When the number of writable branch is more than one, aufs has to decide +the target branch for file creation or copy-up. By default, the highest +writable branch which has the parent (or ancestor) dir of the target +file is chosen (top-down-parent policy). +By user's request, aufs implements some other policies to select the +writable branch, for file creation several policies, round-robin, +most-free-space, and other policies. For copy-up, top-down-parent, +bottom-up-parent, bottom-up and others. + +As expected, the round-robin policy selects the branch in circular. When +you have two writable branches and creates 10 new files, 5 files will be +created for each branch. mkdir(2) systemcall is an exception. When you +create 10 new directories, all will be created on the same branch. +And the most-free-space policy selects the one which has most free +space among the writable branches. The amount of free space will be +checked by aufs internally, and users can specify its time interval. + +The policies for copy-up is more simple, +top-down-parent is equivalent to the same named on in create policy, +bottom-up-parent selects the writable branch where the parent dir +exists and the nearest upper one from the copyup-source, +bottom-up selects the nearest upper writable branch from the +copyup-source, regardless the existence of the parent dir. + +There are some rules or exceptions to apply these policies. +- If there is a readonly branch above the policy-selected branch and + the parent dir is marked as opaque (a variation of whiteout), or the + target (creating) file is whiteout-ed on the upper readonly branch, + then the result of the policy is ignored and the target file will be + created on the nearest upper writable branch than the readonly branch. +- If there is a writable branch above the policy-selected branch and + the parent dir is marked as opaque or the target file is whiteouted + on the branch, then the result of the policy is ignored and the target + file will be created on the highest one among the upper writable + branches who has diropq or whiteout. In case of whiteout, aufs removes + it as usual. +- link(2) and rename(2) systemcalls are exceptions in every policy. + They try selecting the branch where the source exists as possible + since copyup a large file will take long time. If it can't be, + ie. the branch where the source exists is readonly, then they will + follow the copyup policy. +- There is an exception for rename(2) when the target exists. + If the rename target exists, aufs compares the index of the branches + where the source and the target exists and selects the higher + one. If the selected branch is readonly, then aufs follows the + copyup policy. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/06dirren.dot b/Documentation/filesystems/aufs/design/06dirren.dot --- a/Documentation/filesystems/aufs/design/06dirren.dot 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/06dirren.dot 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,31 @@ + +// to view this graph, run dot(1) command in GRAPHVIZ. + +digraph G { +node [shape=box]; +whinfo [label="detailed info file\n(lower_brid_root-hinum, h_inum, namelen, old name)"]; + +node [shape=oval]; + +aufs_rename -> whinfo [label="store/remove"]; + +node [shape=oval]; +inode_list [label="h_inum list in branch\ncache"]; + +node [shape=box]; +whinode [label="h_inum list file"]; + +node [shape=oval]; +brmgmt [label="br_add/del/mod/umount"]; + +brmgmt -> inode_list [label="create/remove"]; +brmgmt -> whinode [label="load/store"]; + +inode_list -> whinode [style=dashed,dir=both]; + +aufs_rename -> inode_list [label="add/del"]; + +aufs_lookup -> inode_list [label="search"]; + +aufs_lookup -> whinfo [label="load/remove"]; +} diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/06dirren.txt b/Documentation/filesystems/aufs/design/06dirren.txt --- a/Documentation/filesystems/aufs/design/06dirren.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/06dirren.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,102 @@ + +# Copyright (C) 2017-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Special handling for renaming a directory (DIRREN) +---------------------------------------------------------------------- +First, let's assume we have a simple usecase. + +- /u = /rw + /ro +- /rw/dirA exists +- /ro/dirA and /ro/dirA/file exist too +- there is no dirB on both branches +- a user issues rename("dirA", "dirB") + +Now, what should aufs behave against this rename(2)? +There are a few possible cases. + +A. returns EROFS. + since dirA exists on a readonly branch which cannot be renamed. +B. returns EXDEV. + it is possible to copy-up dirA (only the dir itself), but the child + entries ("file" in this case) should not be. it must be a bad + approach to copy-up recursively. +C. returns a success. + even the branch /ro is readonly, aufs tries renaming it. Obviously it + is a violation of aufs' policy. +D. construct an extra information which indicates that /ro/dirA should + be handled as the name of dirB. + overlayfs has a similar feature called REDIRECT. + +Until now, aufs implements the case B only which returns EXDEV, and +expects the userspace application behaves like mv(1) which tries +issueing rename(2) recursively. + +A new aufs feature called DIRREN is introduced which implements the case +D. There are several "extra information" added. + +1. detailed info per renamed directory + path: /rw/dirB/$AUFS_WH_DR_INFO_PFX. +2. the inode-number list of directories on a branch + path: /rw/dirB/$AUFS_WH_DR_BRHINO + +The filename of "detailed info per directory" represents the lower +branch, and its format is +- a type of the branch id + one of these. + + uuid (not implemented yet) + + fsid + + dev +- the inode-number of the branch root dir + +And it contains these info in a single regular file. +- magic number +- branch's inode-number of the logically renamed dir +- the name of the before-renamed dir + +The "detailed info per directory" file is created in aufs rename(2), and +loaded in any lookup. +The info is considered in lookup for the matching case only. Here +"matching" means that the root of branch (in the info filename) is same +to the current looking-up branch. After looking-up the before-renamed +name, the inode-number is compared. And the matched dentry is used. + +The "inode-number list of directories" is a regular file which contains +simply the inode-numbers on the branch. The file is created or updated +in removing the branch, and loaded in adding the branch. Its lifetime is +equal to the branch. +The list is refered in lookup, and when the current target inode is +found in the list, the aufs tries loading the "detailed info per +directory" and get the changed and valid name of the dir. + +Theoretically these "extra informaiton" may be able to be put into XATTR +in the dir inode. But aufs doesn't choose this way because +1. XATTR may not be supported by the branch (or its configuration) +2. XATTR may have its size limit. +3. XATTR may be less easy to convert than a regular file, when the + format of the info is changed in the future. +At the same time, I agree that the regular file approach is much slower +than XATTR approach. So, in the future, aufs may take the XATTR or other +better approach. + +This DIRREN feature is enabled by aufs configuration, and is activated +by a new mount option. + +For the more complicated case, there is a work with UDBA option, which +is to dected the direct access to the branches (by-passing aufs) and to +maintain the cashes in aufs. Since a single cached aufs dentry may +contains two names, before- and after-rename, the name comparision in +UDBA handler may not work correctly. In this case, the behaviour will be +equivalen to udba=reval case. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/06fhsm.txt b/Documentation/filesystems/aufs/design/06fhsm.txt --- a/Documentation/filesystems/aufs/design/06fhsm.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/06fhsm.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,120 @@ + +# Copyright (C) 2011-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +File-based Hierarchical Storage Management (FHSM) +---------------------------------------------------------------------- +Hierarchical Storage Management (or HSM) is a well-known feature in the +storage world. Aufs provides this feature as file-based with multiple +writable branches, based upon the principle of "Colder, the Lower". +Here the word "colder" means that the less used files, and "lower" means +that the position in the order of the stacked branches vertically. +These multiple writable branches are prioritized, ie. the topmost one +should be the fastest drive and be used heavily. + +o Characters in aufs FHSM story +- aufs itself and a new branch attribute. +- a new ioctl interface to move-down and to establish a connection with + the daemon ("move-down" is a converse of "copy-up"). +- userspace tool and daemon. + +The userspace daemon establishes a connection with aufs and waits for +the notification. The notified information is very similar to struct +statfs containing the number of consumed blocks and inodes. +When the consumed blocks/inodes of a branch exceeds the user-specified +upper watermark, the daemon activates its move-down process until the +consumed blocks/inodes reaches the user-specified lower watermark. + +The actual move-down is done by aufs based upon the request from +user-space since we need to maintain the inode number and the internal +pointer arrays in aufs. + +Currently aufs FHSM handles the regular files only. Additionally they +must not be hard-linked nor pseudo-linked. + + +o Cowork of aufs and the user-space daemon + During the userspace daemon established the connection, aufs sends a + small notification to it whenever aufs writes something into the + writable branch. But it may cost high since aufs issues statfs(2) + internally. So user can specify a new option to cache the + info. Actually the notification is controlled by these factors. + + the specified cache time. + + classified as "force" by aufs internally. + Until the specified time expires, aufs doesn't send the info + except the forced cases. When aufs decide forcing, the info is always + notified to userspace. + For example, the number of free inodes is generally large enough and + the shortage of it happens rarely. So aufs doesn't force the + notification when creating a new file, directory and others. This is + the typical case which aufs doesn't force. + When aufs writes the actual filedata and the files consumes any of new + blocks, the aufs forces notifying. + + +o Interfaces in aufs +- New branch attribute. + + fhsm + Specifies that the branch is managed by FHSM feature. In other word, + participant in the FHSM. + When nofhsm is set to the branch, it will not be the source/target + branch of the move-down operation. This attribute is set + independently from coo and moo attributes, and if you want full + FHSM, you should specify them as well. +- New mount option. + + fhsm_sec + Specifies a second to suppress many less important info to be + notified. +- New ioctl. + + AUFS_CTL_FHSM_FD + create a new file descriptor which userspace can read the notification + (a subset of struct statfs) from aufs. +- Module parameter 'brs' + It has to be set to 1. Otherwise the new mount option 'fhsm' will not + be set. +- mount helpers /sbin/mount.aufs and /sbin/umount.aufs + When there are two or more branches with fhsm attributes, + /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs + terminates it. As a result of remounting and branch-manipulation, the + number of branches with fhsm attribute can be one. In this case, + /sbin/mount.aufs will terminate the user-space daemon. + + +Finally the operation is done as these steps in kernel-space. +- make sure that, + + no one else is using the file. + + the file is not hard-linked. + + the file is not pseudo-linked. + + the file is a regular file. + + the parent dir is not opaqued. +- find the target writable branch. +- make sure the file is not whiteout-ed by the upper (than the target) + branch. +- make the parent dir on the target branch. +- mutex lock the inode on the branch. +- unlink the whiteout on the target branch (if exists). +- lookup and create the whiteout-ed temporary name on the target branch. +- copy the file as the whiteout-ed temporary name on the target branch. +- rename the whiteout-ed temporary name to the original name. +- unlink the file on the source branch. +- maintain the internal pointer array and the external inode number + table (XINO). +- maintain the timestamps and other attributes of the parent dir and the + file. + +And of course, in every step, an error may happen. So the operation +should restore the original file state after an error happens. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/06mmap.txt b/Documentation/filesystems/aufs/design/06mmap.txt --- a/Documentation/filesystems/aufs/design/06mmap.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/06mmap.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,72 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +mmap(2) -- File Memory Mapping +---------------------------------------------------------------------- +In aufs, the file-mapped pages are handled by a branch fs directly, no +interaction with aufs. It means aufs_mmap() calls the branch fs's +->mmap(). +This approach is simple and good, but there is one problem. +Under /proc, several entries show the mmapped files by its path (with +device and inode number), and the printed path will be the path on the +branch fs's instead of virtual aufs's. +This is not a problem in most cases, but some utilities lsof(1) (and its +user) may expect the path on aufs. + +To address this issue, aufs adds a new member called vm_prfile in struct +vm_area_struct (and struct vm_region). The original vm_file points to +the file on the branch fs in order to handle everything correctly as +usual. The new vm_prfile points to a virtual file in aufs, and the +show-functions in procfs refers to vm_prfile if it is set. +Also we need to maintain several other places where touching vm_file +such like +- fork()/clone() copies vma and the reference count of vm_file is + incremented. +- merging vma maintains the ref count too. + +This is not a good approach. It just fakes the printed path. But it +leaves all behaviour around f_mapping unchanged. This is surely an +advantage. +Actually aufs had adopted another complicated approach which calls +generic_file_mmap() and handles struct vm_operations_struct. In this +approach, aufs met a hard problem and I could not solve it without +switching the approach. + +There may be one more another approach which is +- bind-mount the branch-root onto the aufs-root internally +- grab the new vfsmount (ie. struct mount) +- lazy-umount the branch-root internally +- in open(2) the aufs-file, open the branch-file with the hidden + vfsmount (instead of the original branch's vfsmount) +- ideally this "bind-mount and lazy-umount" should be done atomically, + but it may be possible from userspace by the mount helper. + +Adding the internal hidden vfsmount and using it in opening a file, the +file path under /proc will be printed correctly. This approach looks +smarter, but is not possible I am afraid. +- aufs-root may be bind-mount later. when it happens, another hidden + vfsmount will be required. +- it is hard to get the chance to bind-mount and lazy-umount + + in kernel-space, FS can have vfsmount in open(2) via + file->f_path, and aufs can know its vfsmount. But several locks are + already acquired, and if aufs tries to bind-mount and lazy-umount + here, then it may cause a deadlock. + + in user-space, bind-mount doesn't invoke the mount helper. +- since /proc shows dev and ino, aufs has to give vma these info. it + means a new member vm_prinode will be necessary. this is essentially + equivalent to vm_prfile described above. + +I have to give up this "looks-smater" approach. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/06xattr.txt b/Documentation/filesystems/aufs/design/06xattr.txt --- a/Documentation/filesystems/aufs/design/06xattr.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/06xattr.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,96 @@ + +# Copyright (C) 2014-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Listing XATTR/EA and getting the value +---------------------------------------------------------------------- +For the inode standard attributes (owner, group, timestamps, etc.), aufs +shows the values from the topmost existing file. This behaviour is good +for the non-dir entries since the bahaviour exactly matches the shown +information. But for the directories, aufs considers all the same named +entries on the lower branches. Which means, if one of the lower entry +rejects readdir call, then aufs returns an error even if the topmost +entry allows it. This behaviour is necessary to respect the branch fs's +security, but can make users confused since the user-visible standard +attributes don't match the behaviour. +To address this issue, aufs has a mount option called dirperm1 which +checks the permission for the topmost entry only, and ignores the lower +entry's permission. + +A similar issue can happen around XATTR. +getxattr(2) and listxattr(2) families behave as if dirperm1 option is +always set. Otherwise these very unpleasant situation would happen. +- listxattr(2) may return the duplicated entries. +- users may not be able to remove or reset the XATTR forever, + + +XATTR/EA support in the internal (copy,move)-(up,down) +---------------------------------------------------------------------- +Generally the extended attributes of inode are categorized as these. +- "security" for LSM and capability. +- "system" for posix ACL, 'acl' mount option is required for the branch + fs generally. +- "trusted" for userspace, CAP_SYS_ADMIN is required. +- "user" for userspace, 'user_xattr' mount option is required for the + branch fs generally. + +Moreover there are some other categories. Aufs handles these rather +unpopular categories as the ordinary ones, ie. there is no special +condition nor exception. + +In copy-up, the support for XATTR on the dst branch may differ from the +src branch. In this case, the copy-up operation will get an error and +the original user operation which triggered the copy-up will fail. It +can happen that even all copy-up will fail. +When both of src and dst branches support XATTR and if an error occurs +during copying XATTR, then the copy-up should fail obviously. That is a +good reason and aufs should return an error to userspace. But when only +the src branch support that XATTR, aufs should not return an error. +For example, the src branch supports ACL but the dst branch doesn't +because the dst branch may natively un-support it or temporary +un-support it due to "noacl" mount option. Of course, the dst branch fs +may NOT return an error even if the XATTR is not supported. It is +totally up to the branch fs. + +Anyway when the aufs internal copy-up gets an error from the dst branch +fs, then aufs tries removing the just copied entry and returns the error +to the userspace. The worst case of this situation will be all copy-up +will fail. + +For the copy-up operation, there two basic approaches. +- copy the specified XATTR only (by category above), and return the + error unconditionally if it happens. +- copy all XATTR, and ignore the error on the specified category only. + +In order to support XATTR and to implement the correct behaviour, aufs +chooses the latter approach and introduces some new branch attributes, +"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". +They correspond to the XATTR namespaces (see above). Additionally, to be +convenient, "icex" is also provided which means all "icex*" attributes +are set (here the word "icex" stands for "ignore copy-error on XATTR"). + +The meaning of these attributes is to ignore the error from setting +XATTR on that branch. +Note that aufs tries copying all XATTR unconditionally, and ignores the +error from the dst branch according to the specified attributes. + +Some XATTR may have its default value. The default value may come from +the parent dir or the environment. If the default value is set at the +file creating-time, it will be overwritten by copy-up. +Some contradiction may happen I am afraid. +Do we need another attribute to stop copying XATTR? I am unsure. For +now, aufs implements the branch attributes to ignore the error. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/07export.txt b/Documentation/filesystems/aufs/design/07export.txt --- a/Documentation/filesystems/aufs/design/07export.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/07export.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,58 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Export Aufs via NFS +---------------------------------------------------------------------- +Here is an approach. +- like xino/xib, add a new file 'xigen' which stores aufs inode + generation. +- iget_locked(): initialize aufs inode generation for a new inode, and + store it in xigen file. +- destroy_inode(): increment aufs inode generation and store it in xigen + file. it is necessary even if it is not unlinked, because any data of + inode may be changed by UDBA. +- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise + build file handle by + + branch id (4 bytes) + + superblock generation (4 bytes) + + inode number (4 or 8 bytes) + + parent dir inode number (4 or 8 bytes) + + inode generation (4 bytes)) + + return value of exportfs_encode_fh() for the parent on a branch (4 + bytes) + + file handle for a branch (by exportfs_encode_fh()) +- fh_to_dentry(): + + find the index of a branch from its id in handle, and check it is + still exist in aufs. + + 1st level: get the inode number from handle and search it in cache. + + 2nd level: if not found in cache, get the parent inode number from + the handle and search it in cache. and then open the found parent + dir, find the matching inode number by vfs_readdir() and get its + name, and call lookup_one_len() for the target dentry. + + 3rd level: if the parent dir is not cached, call + exportfs_decode_fh() for a branch and get the parent on a branch, + build a pathname of it, convert it a pathname in aufs, call + path_lookup(). now aufs gets a parent dir dentry, then handle it as + the 2nd level. + + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount + for every branch, but not itself. to get this, (currently) aufs + searches in current->nsproxy->mnt_ns list. it may not be a good + idea, but I didn't get other approach. + + test the generation of the gotten inode. +- every inode operation: they may get EBUSY due to UDBA. in this case, + convert it into ESTALE for NFSD. +- readdir(): call lockdep_on/off() because filldir in NFSD calls + lookup_one_len(), vfs_getattr(), encode_fh() and others. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/08shwh.txt b/Documentation/filesystems/aufs/design/08shwh.txt --- a/Documentation/filesystems/aufs/design/08shwh.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/08shwh.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,52 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Show Whiteout Mode (shwh) +---------------------------------------------------------------------- +Generally aufs hides the name of whiteouts. But in some cases, to show +them is very useful for users. For instance, creating a new middle layer +(branch) by merging existing layers. + +(borrowing aufs1 HOW-TO from a user, Michael Towers) +When you have three branches, +- Bottom: 'system', squashfs (underlying base system), read-only +- Middle: 'mods', squashfs, read-only +- Top: 'overlay', ram (tmpfs), read-write + +The top layer is loaded at boot time and saved at shutdown, to preserve +the changes made to the system during the session. +When larger changes have been made, or smaller changes have accumulated, +the size of the saved top layer data grows. At this point, it would be +nice to be able to merge the two overlay branches ('mods' and 'overlay') +and rewrite the 'mods' squashfs, clearing the top layer and thus +restoring save and load speed. + +This merging is simplified by the use of another aufs mount, of just the +two overlay branches using the 'shwh' option. +# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ + aufs /livesys/merge_union + +A merged view of these two branches is then available at +/livesys/merge_union, and the new feature is that the whiteouts are +visible! +Note that in 'shwh' mode the aufs mount must be 'ro', which will disable +writing to all branches. Also the default mode for all branches is 'ro'. +It is now possible to save the combined contents of the two overlay +branches to a new squashfs, e.g.: +# mksquashfs /livesys/merge_union /path/to/newmods.squash + +This new squashfs archive can be stored on the boot device and the +initramfs will use it to replace the old one at the next boot. diff -Naur --no-dereference a/Documentation/filesystems/aufs/design/10dynop.txt b/Documentation/filesystems/aufs/design/10dynop.txt --- a/Documentation/filesystems/aufs/design/10dynop.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/design/10dynop.txt 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,47 @@ + +# Copyright (C) 2010-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Dynamically customizable FS operations +---------------------------------------------------------------------- +Generally FS operations (struct inode_operations, struct +address_space_operations, struct file_operations, etc.) are defined as +"static const", but it never means that FS have only one set of +operation. Some FS have multiple sets of them. For instance, ext2 has +three sets, one for XIP, for NOBH, and for normal. +Since aufs overrides and redirects these operations, sometimes aufs has +to change its behaviour according to the branch FS type. More importantly +VFS acts differently if a function (member in the struct) is set or +not. It means aufs should have several sets of operations and select one +among them according to the branch FS definition. + +In order to solve this problem and not to affect the behaviour of VFS, +aufs defines these operations dynamically. For instance, aufs defines +dummy direct_IO function for struct address_space_operations, but it may +not be set to the address_space_operations actually. When the branch FS +doesn't have it, aufs doesn't set it to its address_space_operations +while the function definition itself is still alive. So the behaviour +itself will not change, and it will return an error when direct_IO is +not set. + +The lifetime of these dynamically generated operation object is +maintained by aufs branch object. When the branch is removed from aufs, +the reference counter of the object is decremented. When it reaches +zero, the dynamically generated operation object will be freed. + +This approach is designed to support AIO (io_submit), Direct I/O and +XIP (DAX) mainly. +Currently this approach is applied to address_space_operations for +regular files only. diff -Naur --no-dereference a/Documentation/filesystems/aufs/README b/Documentation/filesystems/aufs/README --- a/Documentation/filesystems/aufs/README 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/filesystems/aufs/README 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,394 @@ + +Aufs5 -- advanced multi layered unification filesystem version 5.x +http://aufs.sf.net +Junjiro R. Okajima + + +0. Introduction +---------------------------------------- +In the early days, aufs was entirely re-designed and re-implemented +Unionfs Version 1.x series. Adding many original ideas, approaches, +improvements and implementations, it became totally different from +Unionfs while keeping the basic features. +Later, Unionfs Version 2.x series began taking some of the same +approaches to aufs1's. +Unionfs was being developed by Professor Erez Zadok at Stony Brook +University and his team. + +Aufs5 supports linux-v5.0 and later, If you want older kernel version +support, +- for linux-v4.x series, try aufs4-linux.git or aufs4-standalone.git +- for linux-v3.x series, try aufs3-linux.git or aufs3-standalone.git +- for linux-v2.6.16 and later, try aufs2-2.6.git, aufs2-standalone.git + or aufs1 from CVS on SourceForge. + +Note: it becomes clear that "Aufs was rejected. Let's give it up." + According to Christoph Hellwig, linux rejects all union-type + filesystems but UnionMount. + + +PS. Al Viro seems have a plan to merge aufs as well as overlayfs and + UnionMount, and he pointed out an issue around a directory mutex + lock and aufs addressed it. But it is still unsure whether aufs will + be merged (or any other union solution). + + + +1. Features +---------------------------------------- +- unite several directories into a single virtual filesystem. The member + directory is called as a branch. +- you can specify the permission flags to the branch, which are 'readonly', + 'readwrite' and 'whiteout-able.' +- by upper writable branch, internal copyup and whiteout, files/dirs on + readonly branch are modifiable logically. +- dynamic branch manipulation, add, del. +- etc... + +Also there are many enhancements in aufs, such as: +- test only the highest one for the directory permission (dirperm1) +- copyup on open (coo=) +- 'move' policy for copy-up between two writable branches, after + checking free space. +- xattr, acl +- readdir(3) in userspace. +- keep inode number by external inode number table +- keep the timestamps of file/dir in internal copyup operation +- seekable directory, supporting NFS readdir. +- whiteout is hardlinked in order to reduce the consumption of inodes + on branch +- do not copyup, nor create a whiteout when it is unnecessary +- revert a single systemcall when an error occurs in aufs +- remount interface instead of ioctl +- maintain /etc/mtab by an external command, /sbin/mount.aufs. +- loopback mounted filesystem as a branch +- kernel thread for removing the dir who has a plenty of whiteouts +- support copyup sparse file (a file which has a 'hole' in it) +- default permission flags for branches +- selectable permission flags for ro branch, whether whiteout can + exist or not +- export via NFS. +- support /fs/aufs and /aufs. +- support multiple writable branches, some policies to select one + among multiple writable branches. +- a new semantics for link(2) and rename(2) to support multiple + writable branches. +- no glibc changes are required. +- pseudo hardlink (hardlink over branches) +- allow a direct access manually to a file on branch, e.g. bypassing aufs. + including NFS or remote filesystem branch. +- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. +- and more... + +Currently these features are dropped temporary from aufs5. +See design/08plan.txt in detail. +- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs + (robr) +- statistics of aufs thread (/sys/fs/aufs/stat) + +Features or just an idea in the future (see also design/*.txt), +- reorder the branch index without del/re-add. +- permanent xino files for NFSD +- an option for refreshing the opened files after add/del branches +- light version, without branch manipulation. (unnecessary?) +- copyup in userspace +- inotify in userspace +- readv/writev + + +2. Download +---------------------------------------- +There are three GIT trees for aufs5, aufs5-linux.git, +aufs5-standalone.git, and aufs-util.git. Note that there is no "5" in +"aufs-util.git." +While the aufs-util is always necessary, you need either of aufs5-linux +or aufs5-standalone. + +The aufs5-linux tree includes the whole linux mainline GIT tree, +git://git.kernel.org/.../torvalds/linux.git. +And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot +build aufs5 as an external kernel module. +Several extra patches are not included in this tree. Only +aufs5-standalone tree contains them. They are described in the later +section "Configuration and Compilation." + +On the other hand, the aufs5-standalone tree has only aufs source files +and necessary patches, and you can select CONFIG_AUFS_FS=m. +But you need to apply all aufs patches manually. + +You will find GIT branches whose name is in form of "aufs5.x" where "x" +represents the linux kernel version, "linux-5.x". For instance, +"aufs5.0" is for linux-5.0. For latest "linux-5.x-rcN", use +"aufs5.x-rcN" branch. + +o aufs5-linux tree +$ git clone --reference /your/linux/git/tree \ + git://github.com/sfjro/aufs5-linux.git aufs5-linux.git +- if you don't have linux GIT tree, then remove "--reference ..." +$ cd aufs5-linux.git +$ git checkout origin/aufs5.0 + +Or You may want to directly git-pull aufs into your linux GIT tree, and +leave the patch-work to GIT. +$ cd /your/linux/git/tree +$ git remote add aufs5 git://github.com/sfjro/aufs5-linux.git +$ git fetch aufs5 +$ git checkout -b my5.0 v5.0 +$ (add your local change...) +$ git pull aufs5 aufs5.0 +- now you have v5.0 + your_changes + aufs5.0 in you my5.0 branch. +- you may need to solve some conflicts between your_changes and + aufs5.0. in this case, git-rerere is recommended so that you can + solve the similar conflicts automatically when you upgrade to 5.1 or + later in the future. + +o aufs5-standalone tree +$ git clone git://github.com/sfjro/aufs5-standalone.git aufs5-standalone.git +$ cd aufs5-standalone.git +$ git checkout origin/aufs5.0 + +o aufs-util tree +$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git +- note that the public aufs-util.git is on SourceForge instead of + GitHUB. +$ cd aufs-util.git +$ git checkout origin/aufs5.0 + +Note: The 5.x-rcN branch is to be used with `rc' kernel versions ONLY. +The minor version number, 'x' in '5.x', of aufs may not always +follow the minor version number of the kernel. +Because changes in the kernel that cause the use of a new +minor version number do not always require changes to aufs-util. + +Since aufs-util has its own minor version number, you may not be +able to find a GIT branch in aufs-util for your kernel's +exact minor version number. +In this case, you should git-checkout the branch for the +nearest lower number. + +For (an unreleased) example: +If you are using "linux-5.10" and the "aufs5.10" branch +does not exist in aufs-util repository, then "aufs5.9", "aufs5.8" +or something numerically smaller is the branch for your kernel. + +Also you can view all branches by + $ git branch -a + + +3. Configuration and Compilation +---------------------------------------- +Make sure you have git-checkout'ed the correct branch. + +For aufs5-linux tree, +- enable CONFIG_AUFS_FS. +- set other aufs configurations if necessary. + +For aufs5-standalone tree, +There are several ways to build. + +1. +- apply ./aufs5-kbuild.patch to your kernel source files. +- apply ./aufs5-base.patch too. +- apply ./aufs5-mmap.patch too. +- apply ./aufs5-standalone.patch too, if you have a plan to set + CONFIG_AUFS_FS=m. otherwise you don't need ./aufs5-standalone.patch. +- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your + kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. +- enable CONFIG_AUFS_FS, you can select either + =m or =y. +- and build your kernel as usual. +- install the built kernel. +- install the header files too by "make headers_install" to the + directory where you specify. By default, it is $PWD/usr. + "make help" shows a brief note for headers_install. +- and reboot your system. + +2. +- module only (CONFIG_AUFS_FS=m). +- apply ./aufs5-base.patch to your kernel source files. +- apply ./aufs5-mmap.patch too. +- apply ./aufs5-standalone.patch too. +- build your kernel, don't forget "make headers_install", and reboot. +- edit ./config.mk and set other aufs configurations if necessary. + Note: You should read $PWD/fs/aufs/Kconfig carefully which describes + every aufs configurations. +- build the module by simple "make". +- you can specify ${KDIR} make variable which points to your kernel + source tree. +- install the files + + run "make install" to install the aufs module, or copy the built + $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). + + run "make install_headers" (instead of headers_install) to install + the modified aufs header file (you can specify DESTDIR which is + available in aufs standalone version's Makefile only), or copy + $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever + you like manually. By default, the target directory is $PWD/usr. +- no need to apply aufs5-kbuild.patch, nor copying source files to your + kernel source tree. + +Note: The header file aufs_type.h is necessary to build aufs-util + as well as "make headers_install" in the kernel source tree. + headers_install is subject to be forgotten, but it is essentially + necessary, not only for building aufs-util. + You may not meet problems without headers_install in some older + version though. + +And then, +- read README in aufs-util, build and install it +- note that your distribution may contain an obsoleted version of + aufs_type.h in /usr/include/linux or something. When you build aufs + utilities, make sure that your compiler refers the correct aufs header + file which is built by "make headers_install." +- if you want to use readdir(3) in userspace or pathconf(3) wrapper, + then run "make install_ulib" too. And refer to the aufs manual in + detail. + +There several other patches in aufs5-standalone.git. They are all +optional. When you meet some problems, they will help you. +- aufs5-loopback.patch + Supports a nested loopback mount in a branch-fs. This patch is + unnecessary until aufs produces a message like "you may want to try + another patch for loopback file". +- vfs-ino.patch + Modifies a system global kernel internal function get_next_ino() in + order to stop assigning 0 for an inode-number. Not directly related to + aufs, but recommended generally. +- tmpfs-idr.patch + Keeps the tmpfs inode number as the lowest value. Effective to reduce + the size of aufs XINO files for tmpfs branch. Also it prevents the + duplication of inode number, which is important for backup tools and + other utilities. When you find aufs XINO files for tmpfs branch + growing too much, try this patch. +- lockdep-debug.patch + Because aufs is not only an ordinary filesystem (callee of VFS), but + also a caller of VFS functions for branch filesystems, subclassing of + the internal locks for LOCKDEP is necessary. LOCKDEP is a debugging + feature of linux kernel. If you enable CONFIG_LOCKDEP, then you will + need to apply this debug patch to expand several constant values. + If you don't know what LOCKDEP is, then you don't have apply this + patch. + + +4. Usage +---------------------------------------- +At first, make sure aufs-util are installed, and please read the aufs +manual, aufs.5 in aufs-util.git tree. +$ man -l aufs.5 + +And then, +$ mkdir /tmp/rw /tmp/aufs +# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs + +Here is another example. The result is equivalent. +# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs + Or +# mount -t aufs -o br:/tmp/rw none /tmp/aufs +# mount -o remount,append:${HOME} /tmp/aufs + +Then, you can see whole tree of your home dir through /tmp/aufs. If +you modify a file under /tmp/aufs, the one on your home directory is +not affected, instead the same named file will be newly created under +/tmp/rw. And all of your modification to a file will be applied to +the one under /tmp/rw. This is called the file based Copy on Write +(COW) method. +Aufs mount options are described in aufs.5. +If you run chroot or something and make your aufs as a root directory, +then you need to customize the shutdown script. See the aufs manual in +detail. + +Additionally, there are some sample usages of aufs which are a +diskless system with network booting, and LiveCD over NFS. +See sample dir in CVS tree on SourceForge. + + +5. Contact +---------------------------------------- +When you have any problems or strange behaviour in aufs, please let me +know with: +- /proc/mounts (instead of the output of mount(8)) +- /sys/module/aufs/* +- /sys/fs/aufs/* (if you have them) +- /debug/aufs/* (if you have them) +- linux kernel version + if your kernel is not plain, for example modified by distributor, + the url where i can download its source is necessary too. +- aufs version which was printed at loading the module or booting the + system, instead of the date you downloaded. +- configuration (define/undefine CONFIG_AUFS_xxx) +- kernel configuration or /proc/config.gz (if you have it) +- LSM (linux security module, if you are using) +- behaviour which you think to be incorrect +- actual operation, reproducible one is better +- mailto: aufs-users at lists.sourceforge.net + +Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, +and Feature Requests) on SourceForge. Please join and write to +aufs-users ML. + + +6. Acknowledgements +---------------------------------------- +Thanks to everyone who have tried and are using aufs, whoever +have reported a bug or any feedback. + +Especially donators: +Tomas Matejicek(slax.org) made a donation (much more than once). + Since Apr 2010, Tomas M (the author of Slax and Linux Live + scripts) is making "doubling" donations. + Unfortunately I cannot list all of the donators, but I really + appreciate. + It ends Aug 2010, but the ordinary donation URL is still available. + +Dai Itasaka made a donation (2007/8). +Chuck Smith made a donation (2008/4, 10 and 12). +Henk Schoneveld made a donation (2008/9). +Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). +Francois Dupoux made a donation (2008/11). +Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public + aufs2 GIT tree (2009/2). +William Grant made a donation (2009/3). +Patrick Lane made a donation (2009/4). +The Mail Archive (mail-archive.com) made donations (2009/5). +Nippy Networks (Ed Wildgoose) made a donation (2009/7). +New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). +Pavel Pronskiy made a donation (2011/2). +Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy + Networks (Ed Wildgoose) made a donation for hardware (2011/3). +Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and +11). +Sam Liddicott made a donation (2011/9). +Era Scarecrow made a donation (2013/4). +Bor Ratajc made a donation (2013/4). +Alessandro Gorreta made a donation (2013/4). +POIRETTE Marc made a donation (2013/4). +Alessandro Gorreta made a donation (2013/4). +lauri kasvandik made a donation (2013/5). +"pemasu from Finland" made a donation (2013/7). +The Parted Magic Project made a donation (2013/9 and 11). +Pavel Barta made a donation (2013/10). +Nikolay Pertsev made a donation (2014/5). +James B made a donation (2014/7 and 2015/7). +Stefano Di Biase made a donation (2014/8). +Daniel Epellei made a donation (2015/1). +OmegaPhil made a donation (2016/1, 2018/4). +Tomasz Szewczyk made a donation (2016/4). +James Burry made a donation (2016/12). +Carsten Rose made a donation (2018/9). +Porteus Kiosk made a donation (2018/10). + +Thank you very much. +Donations are always, including future donations, very important and +helpful for me to keep on developing aufs. + + +7. +---------------------------------------- +If you are an experienced user, no explanation is needed. Aufs is +just a linux filesystem. + + +Enjoy! + +# Local variables: ; +# mode: text; +# End: ; diff -Naur --no-dereference a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst --- a/Documentation/networking/device_drivers/ethernet/index.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/networking/device_drivers/ethernet/index.rst 2022-01-06 12:45:53.806318073 -0500 @@ -49,6 +49,7 @@ stmicro/stmmac ti/cpsw ti/cpsw_switchdev + ti/am65_nuss_cpsw_switchdev ti/tlan toshiba/spider_net diff -Naur --no-dereference a/Documentation/networking/device_drivers/ethernet/ti/am65_nuss_cpsw_switchdev.rst b/Documentation/networking/device_drivers/ethernet/ti/am65_nuss_cpsw_switchdev.rst --- a/Documentation/networking/device_drivers/ethernet/ti/am65_nuss_cpsw_switchdev.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/networking/device_drivers/ethernet/ti/am65_nuss_cpsw_switchdev.rst 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,143 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================================================== +Texas Instruments K3 AM65 CPSW NUSS switchdev based ethernet driver +=================================================================== + +:Version: 1.0 + +Port renaming +============= + +In order to rename via udev:: + + ip -d link show dev sw0p1 | grep switchid + + SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==, \ + ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}" + + +Multi mac mode +============== + +- The driver is operating in multi-mac mode by default, thus + working as N individual network interfaces. + +Devlink configuration parameters +================================ + +See Documentation/networking/devlink/am65-nuss-cpsw-switch.rst + +Enabling "switch" +================= + +The Switch mode can be enabled by configuring devlink driver parameter +"switch_mode" to 1/true:: + + devlink dev param set platform/c000000.ethernet \ + name switch_mode value true cmode runtime + +This can be done regardless of the state of Port's netdev devices - UP/DOWN, but +Port's netdev devices have to be in UP before joining to the bridge to avoid +overwriting of bridge configuration as CPSW switch driver completely reloads its +configuration when first port changes its state to UP. + +When the both interfaces joined the bridge - CPSW switch driver will enable +marking packets with offload_fwd_mark flag. + +All configuration is implemented via switchdev API. + +Bridge setup +============ + +:: + + devlink dev param set platform/c000000.ethernet \ + name switch_mode value true cmode runtime + + ip link add name br0 type bridge + ip link set dev br0 type bridge ageing_time 1000 + ip link set dev sw0p1 up + ip link set dev sw0p2 up + ip link set dev sw0p1 master br0 + ip link set dev sw0p2 master br0 + + [*] bridge vlan add dev br0 vid 1 pvid untagged self + + [*] if vlan_filtering=1. where default_pvid=1 + + Note. Steps [*] are mandatory. + + +On/off STP +========== + +:: + + ip link set dev BRDEV type bridge stp_state 1/0 + +VLAN configuration +================== + +:: + + bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1 + +Note. This step is mandatory for bridge/default_pvid. + +Add extra VLANs +=============== + + 1. untagged:: + + bridge vlan add dev sw0p1 vid 100 pvid untagged master + bridge vlan add dev sw0p2 vid 100 pvid untagged master + bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100 + + 2. tagged:: + + bridge vlan add dev sw0p1 vid 100 master + bridge vlan add dev sw0p2 vid 100 master + bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100 + +FDBs +---- + +FDBs are automatically added on the appropriate switch port upon detection + +Manually adding FDBs:: + + bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100 + bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs + +MDBs +---- + +MDBs are automatically added on the appropriate switch port upon detection + +Manually adding MDBs:: + + bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100 + bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs + +Multicast flooding +================== +CPU port mcast_flooding is always on + +Turning flooding on/off on swithch ports: +bridge link set dev sw0p1 mcast_flood on/off + +Access and Trunk port +===================== + +:: + + bridge vlan add dev sw0p1 vid 100 pvid untagged master + bridge vlan add dev sw0p2 vid 100 master + + + bridge vlan add dev br0 vid 100 self + ip link add link br0 name br0.100 type vlan id 100 + +Note. Setting PVID on Bridge device itself works only for +default VLAN (default_pvid). diff -Naur --no-dereference a/Documentation/networking/devlink/am65-nuss-cpsw-switch.rst b/Documentation/networking/devlink/am65-nuss-cpsw-switch.rst --- a/Documentation/networking/devlink/am65-nuss-cpsw-switch.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/networking/devlink/am65-nuss-cpsw-switch.rst 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,26 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================== +am65-cpsw-nuss devlink support +============================== + +This document describes the devlink features implemented by the ``am65-cpsw-nuss`` +device driver. + +Parameters +========== + +The ``am65-cpsw-nuss`` driver implements the following driver-specific +parameters. + +.. list-table:: Driver-specific parameters implemented + :widths: 5 5 5 85 + + * - Name + - Type + - Mode + - Description + * - ``switch_mode`` + - Boolean + - runtime + - Enable switch mode diff -Naur --no-dereference a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst --- a/Documentation/networking/devlink/index.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/networking/devlink/index.rst 2022-01-06 12:45:53.806318073 -0500 @@ -44,3 +44,4 @@ sja1105 qed ti-cpsw-switch + am65-nuss-cpsw-switch diff -Naur --no-dereference a/Documentation/networking/netdev-features.rst b/Documentation/networking/netdev-features.rst --- a/Documentation/networking/netdev-features.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/networking/netdev-features.rst 2022-01-06 12:45:53.806318073 -0500 @@ -182,3 +182,24 @@ be re-segmentable by GSO or TSO back to the exact original packet stream. Hardware GRO is dependent on RXCSUM since every packet successfully merged by hardware must also have the checksum verified by hardware. + +* hsr-tag-ins-offload + +This should be set for devices which insert an HSR (High-availability Seamless +Redundancy) or PRP (Parallel Redundancy Protocol) tag automatically. + +* hsr-tag-rm-offload + +This should be set for devices which remove HSR (High-availability Seamless +Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically. + +* hsr-fwd-offload + +This should be set for devices which forward HSR (High-availability Seamless +Redundancy) frames from one port to another in hardware. + +* hsr-dup-offload + +This should be set for devices which duplicate outgoing HSR (High-availability +Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically +frames in hardware. diff -Naur --no-dereference a/Documentation/PCI/endpoint/function/binding/pci-ntb.rst b/Documentation/PCI/endpoint/function/binding/pci-ntb.rst --- a/Documentation/PCI/endpoint/function/binding/pci-ntb.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/PCI/endpoint/function/binding/pci-ntb.rst 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,38 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +PCI NTB Endpoint Function +========================== + +1) Create a subdirectory to pci_epf_ntb directory in configfs. + +Standard EPF Configurable Fields: + +================ =========================================================== +vendorid should be 0x104c +deviceid should be 0xb00d for TI's J721E SoC +revid don't care +progif_code don't care +subclass_code should be 0x00 +baseclass_code should be 0x5 +cache_line_size don't care +subsys_vendor_id don't care +subsys_id don't care +interrupt_pin don't care +msi_interrupts don't care +msix_interrupts don't care +================ =========================================================== + +2) Create a subdirectory to directory created in 1 + +NTB EPF specific configurable fields: + +================ =========================================================== +db_count Number of doorbells; default = 4 +mw1 size of memory window1 +mw2 size of memory window2 +mw3 size of memory window3 +mw4 size of memory window4 +num_mws Number of memory windows; max = 4 +spad_count Number of scratchpad registers; default = 64 +================ =========================================================== diff -Naur --no-dereference a/Documentation/PCI/endpoint/index.rst b/Documentation/PCI/endpoint/index.rst --- a/Documentation/PCI/endpoint/index.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/PCI/endpoint/index.rst 2022-01-06 12:45:53.806318073 -0500 @@ -11,5 +11,8 @@ pci-endpoint-cfs pci-test-function pci-test-howto + pci-ntb-function + pci-ntb-howto function/binding/pci-test + function/binding/pci-ntb diff -Naur --no-dereference a/Documentation/PCI/endpoint/pci-endpoint-cfs.rst b/Documentation/PCI/endpoint/pci-endpoint-cfs.rst --- a/Documentation/PCI/endpoint/pci-endpoint-cfs.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/PCI/endpoint/pci-endpoint-cfs.rst 2022-01-06 12:45:53.806318073 -0500 @@ -43,6 +43,7 @@ .. / ... / ... / + ... / .. / ... / ... / @@ -68,6 +69,24 @@ ... subsys_vendor_id ... subsys_id ... interrupt_pin + ... / + ... primary/ + ... / + ... secondary/ + ... / + +If an EPF device has to be associated with 2 EPCs (like in the case of +Non-transparent bridge), symlink of endpoint controller connected to primary +interface should be added in 'primary' directory and symlink of endpoint +controller connected to secondary interface should be added in 'secondary' +directory. + +The directory can have a list of symbolic links +() to other . These symbolic links should +be created by the user to represent the virtual functions that are bound to +the physical function. In the above directory structure is a +physical function and is a virtual function. An EPF device once +it's linked to another EPF device, cannot be linked to a EPC device. EPC Device ========== @@ -88,7 +107,8 @@ The directory will have a list of symbolic links to . These symbolic links should be created by the user to -represent the functions present in the endpoint device. +represent the functions present in the endpoint device. Only +that represents a physical function can be linked to a EPC device. The directory will also have a *start* field. Once "1" is written to this field, the endpoint device will be ready to diff -Naur --no-dereference a/Documentation/PCI/endpoint/pci-ntb-function.rst b/Documentation/PCI/endpoint/pci-ntb-function.rst --- a/Documentation/PCI/endpoint/pci-ntb-function.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/PCI/endpoint/pci-ntb-function.rst 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,348 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================= +PCI NTB Function +================= + +:Author: Kishon Vijay Abraham I + +PCI Non-Transparent Bridges (NTB) allow two host systems to communicate +with each other by exposing each host as a device to the other host. +NTBs typically support the ability to generate interrupts on the remote +machine, expose memory ranges as BARs, and perform DMA. They also support +scratchpads, which are areas of memory within the NTB that are accessible +from both machines. + +PCI NTB Function allows two different systems (or hosts) to communicate +with each other by configuring the endpoint instances in such a way that +transactions from one system are routed to the other system. + +In the below diagram, PCI NTB function configures the SoC with multiple +PCI Endpoint (EP) instances in such a way that transactions from one EP +controller are routed to the other EP controller. Once PCI NTB function +configures the SoC with multiple EP instances, HOST1 and HOST2 can +communicate with each other using SoC as a bridge. + +.. code-block:: text + + +-------------+ +-------------+ + | | | | + | HOST1 | | HOST2 | + | | | | + +------^------+ +------^------+ + | | + | | + +---------|-------------------------------------------------|---------+ + | +------v------+ +------v------+ | + | | | | | | + | | EP | | EP | | + | | CONTROLLER1 | | CONTROLLER2 | | + | | <-----------------------------------> | | + | | | | | | + | | | | | | + | | | SoC With Multiple EP Instances | | | + | | | (Configured using NTB Function) | | | + | +-------------+ +-------------+ | + +---------------------------------------------------------------------+ + +Constructs used for Implementing NTB +==================================== + + 1) Config Region + 2) Self Scratchpad Registers + 3) Peer Scratchpad Registers + 4) Doorbell (DB) Registers + 5) Memory Window (MW) + + +Config Region: +-------------- + +Config Region is a construct that is specific to NTB implemented using NTB +Endpoint Function Driver. The host and endpoint side NTB function driver will +exchange information with each other using this region. Config Region has +Control/Status Registers for configuring the Endpoint Controller. Host can +write into this region for configuring the outbound Address Translation Unit +(ATU) and to indicate the link status. Endpoint can indicate the status of +commands issued by host in this region. Endpoint can also indicate the +scratchpad offset and number of memory windows to the host using this region. + +The format of Config Region is given below. All the fields here are 32 bits. + +.. code-block:: text + + +------------------------+ + | COMMAND | + +------------------------+ + | ARGUMENT | + +------------------------+ + | STATUS | + +------------------------+ + | TOPOLOGY | + +------------------------+ + | ADDRESS (LOWER 32) | + +------------------------+ + | ADDRESS (UPPER 32) | + +------------------------+ + | SIZE | + +------------------------+ + | NO OF MEMORY WINDOW | + +------------------------+ + | MEMORY WINDOW1 OFFSET | + +------------------------+ + | SPAD OFFSET | + +------------------------+ + | SPAD COUNT | + +------------------------+ + | DB ENTRY SIZE | + +------------------------+ + | DB DATA | + +------------------------+ + | : | + +------------------------+ + | : | + +------------------------+ + | DB DATA | + +------------------------+ + + + COMMAND: + + NTB function supports three commands: + + CMD_CONFIGURE_DOORBELL (0x1): Command to configure doorbell. Before + invoking this command, the host should allocate and initialize + MSI/MSI-X vectors (i.e., initialize the MSI/MSI-X Capability in the + Endpoint). The endpoint on receiving this command will configure + the outbound ATU such that transactions to Doorbell BAR will be routed + to the MSI/MSI-X address programmed by the host. The ARGUMENT + register should be populated with number of DBs to configure (in the + lower 16 bits) and if MSI or MSI-X should be configured (BIT 16). + + CMD_CONFIGURE_MW (0x2): Command to configure memory window (MW). The + host invokes this command after allocating a buffer that can be + accessed by remote host. The allocated address should be programmed + in the ADDRESS register (64 bit), the size should be programmed in + the SIZE register and the memory window index should be programmed + in the ARGUMENT register. The endpoint on receiving this command + will configure the outbound ATU such that transactions to MW BAR + are routed to the address provided by the host. + + CMD_LINK_UP (0x3): Command to indicate an NTB application is + bound to the EP device on the host side. Once the endpoint + receives this command from both the hosts, the endpoint will + raise a LINK_UP event to both the hosts to indicate the host + NTB applications can start communicating with each other. + + ARGUMENT: + + The value of this register is based on the commands issued in + command register. See COMMAND section for more information. + + TOPOLOGY: + + Set to NTB_TOPO_B2B_USD for Primary interface + Set to NTB_TOPO_B2B_DSD for Secondary interface + + ADDRESS/SIZE: + + Address and Size to be used while configuring the memory window. + See "CMD_CONFIGURE_MW" for more info. + + MEMORY WINDOW1 OFFSET: + + Memory Window 1 and Doorbell registers are packed together in the + same BAR. The initial portion of the region will have doorbell + registers and the latter portion of the region is for memory window 1. + This register will specify the offset of the memory window 1. + + NO OF MEMORY WINDOW: + + Specifies the number of memory windows supported by the NTB device. + + SPAD OFFSET: + + Self scratchpad region and config region are packed together in the + same BAR. The initial portion of the region will have config region + and the latter portion of the region is for self scratchpad. This + register will specify the offset of the self scratchpad registers. + + SPAD COUNT: + + Specifies the number of scratchpad registers supported by the NTB + device. + + DB ENTRY SIZE: + + Used to determine the offset within the DB BAR that should be written + in order to raise doorbell. EPF NTB can use either MSI or MSI-X to + ring doorbell (MSI-X support will be added later). MSI uses same + address for all the interrupts and MSI-X can provide different + addresses for different interrupts. The MSI/MSI-X address is provided + by the host and the address it gives is based on the MSI/MSI-X + implementation supported by the host. For instance, ARM platform + using GIC ITS will have the same MSI-X address for all the interrupts. + In order to support all the combinations and use the same mechanism + for both MSI and MSI-X, EPF NTB allocates a separate region in the + Outbound Address Space for each of the interrupts. This region will + be mapped to the MSI/MSI-X address provided by the host. If a host + provides the same address for all the interrupts, all the regions + will be translated to the same address. If a host provides different + addresses, the regions will be translated to different addresses. This + will ensure there is no difference while raising the doorbell. + + DB DATA: + + EPF NTB supports 32 interrupts, so there are 32 DB DATA registers. + This holds the MSI/MSI-X data that has to be written to MSI address + for raising doorbell interrupt. This will be populated by EPF NTB + while invoking CMD_CONFIGURE_DOORBELL. + +Scratchpad Registers: +--------------------- + + Each host has its own register space allocated in the memory of NTB endpoint + controller. They are both readable and writable from both sides of the bridge. + They are used by applications built over NTB and can be used to pass control + and status information between both sides of a device. + + Scratchpad registers has 2 parts + 1) Self Scratchpad: Host's own register space + 2) Peer Scratchpad: Remote host's register space. + +Doorbell Registers: +------------------- + + Doorbell Registers are used by the hosts to interrupt each other. + +Memory Window: +-------------- + + Actual transfer of data between the two hosts will happen using the + memory window. + +Modeling Constructs: +==================== + +There are 5 or more distinct regions (config, self scratchpad, peer +scratchpad, doorbell, one or more memory windows) to be modeled to achieve +NTB functionality. At least one memory window is required while more than +one is permitted. All these regions should be mapped to BARs for hosts to +access these regions. + +If one 32-bit BAR is allocated for each of these regions, the scheme would +look like this: + +====== =============== +BAR NO CONSTRUCTS USED +====== =============== +BAR0 Config Region +BAR1 Self Scratchpad +BAR2 Peer Scratchpad +BAR3 Doorbell +BAR4 Memory Window 1 +BAR5 Memory Window 2 +====== =============== + +However if we allocate a separate BAR for each of the regions, there would not +be enough BARs for all the regions in a platform that supports only 64-bit +BARs. + +In order to be supported by most of the platforms, the regions should be +packed and mapped to BARs in a way that provides NTB functionality and +also makes sure the host doesn't access any region that it is not supposed +to. + +The following scheme is used in EPF NTB Function: + +====== =============================== +BAR NO CONSTRUCTS USED +====== =============================== +BAR0 Config Region + Self Scratchpad +BAR1 Peer Scratchpad +BAR2 Doorbell + Memory Window 1 +BAR3 Memory Window 2 +BAR4 Memory Window 3 +BAR5 Memory Window 4 +====== =============================== + +With this scheme, for the basic NTB functionality 3 BARs should be sufficient. + +Modeling Config/Scratchpad Region: +---------------------------------- + +.. code-block:: text + + +-----------------+------->+------------------+ +-----------------+ + | BAR0 | | CONFIG REGION | | BAR0 | + +-----------------+----+ +------------------+<-------+-----------------+ + | BAR1 | | |SCRATCHPAD REGION | | BAR1 | + +-----------------+ +-->+------------------+<-------+-----------------+ + | BAR2 | Local Memory | BAR2 | + +-----------------+ +-----------------+ + | BAR3 | | BAR3 | + +-----------------+ +-----------------+ + | BAR4 | | BAR4 | + +-----------------+ +-----------------+ + | BAR5 | | BAR5 | + +-----------------+ +-----------------+ + EP CONTROLLER 1 EP CONTROLLER 2 + +Above diagram shows Config region + Scratchpad region for HOST1 (connected to +EP controller 1) allocated in local memory. The HOST1 can access the config +region and scratchpad region (self scratchpad) using BAR0 of EP controller 1. +The peer host (HOST2 connected to EP controller 2) can also access this +scratchpad region (peer scratchpad) using BAR1 of EP controller 2. This +diagram shows the case where Config region and Scratchpad regions are allocated +for HOST1, however the same is applicable for HOST2. + +Modeling Doorbell/Memory Window 1: +---------------------------------- + +.. code-block:: text + + +-----------------+ +----->+----------------+-----------+-----------------+ + | BAR0 | | | Doorbell 1 +-----------> MSI-X ADDRESS 1 | + +-----------------+ | +----------------+ +-----------------+ + | BAR1 | | | Doorbell 2 +---------+ | | + +-----------------+----+ +----------------+ | | | + | BAR2 | | Doorbell 3 +-------+ | +-----------------+ + +-----------------+----+ +----------------+ | +-> MSI-X ADDRESS 2 | + | BAR3 | | | Doorbell 4 +-----+ | +-----------------+ + +-----------------+ | |----------------+ | | | | + | BAR4 | | | | | | +-----------------+ + +-----------------+ | | MW1 +---+ | +-->+ MSI-X ADDRESS 3|| + | BAR5 | | | | | | +-----------------+ + +-----------------+ +----->-----------------+ | | | | + EP CONTROLLER 1 | | | | +-----------------+ + | | | +---->+ MSI-X ADDRESS 4 | + +----------------+ | +-----------------+ + EP CONTROLLER 2 | | | + (OB SPACE) | | | + +-------> MW1 | + | | + | | + +-----------------+ + | | + | | + | | + | | + | | + +-----------------+ + PCI Address Space + (Managed by HOST2) + +Above diagram shows how the doorbell and memory window 1 is mapped so that +HOST1 can raise doorbell interrupt on HOST2 and also how HOST1 can access +buffers exposed by HOST2 using memory window1 (MW1). Here doorbell and +memory window 1 regions are allocated in EP controller 2 outbound (OB) address +space. Allocating and configuring BARs for doorbell and memory window1 +is done during the initialization phase of NTB endpoint function driver. +Mapping from EP controller 2 OB space to PCI address space is done when HOST2 +sends CMD_CONFIGURE_MW/CMD_CONFIGURE_DOORBELL. + +Modeling Optional Memory Windows: +--------------------------------- + +This is modeled the same was as MW1 but each of the additional memory windows +is mapped to separate BARs. diff -Naur --no-dereference a/Documentation/PCI/endpoint/pci-ntb-howto.rst b/Documentation/PCI/endpoint/pci-ntb-howto.rst --- a/Documentation/PCI/endpoint/pci-ntb-howto.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/PCI/endpoint/pci-ntb-howto.rst 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,161 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================================================== +PCI Non-Transparent Bridge (NTB) Endpoint Function (EPF) User Guide +=================================================================== + +:Author: Kishon Vijay Abraham I + +This document is a guide to help users use pci-epf-ntb function driver +and ntb_hw_epf host driver for NTB functionality. The list of steps to +be followed in the host side and EP side is given below. For the hardware +configuration and internals of NTB using configurable endpoints see +Documentation/PCI/endpoint/pci-ntb-function.rst + +Endpoint Device +=============== + +Endpoint Controller Devices +--------------------------- + +For implementing NTB functionality at least two endpoint controller devices +are required. + +To find the list of endpoint controller devices in the system:: + + # ls /sys/class/pci_epc/ + 2900000.pcie-ep 2910000.pcie-ep + +If PCI_ENDPOINT_CONFIGFS is enabled:: + + # ls /sys/kernel/config/pci_ep/controllers + 2900000.pcie-ep 2910000.pcie-ep + + +Endpoint Function Drivers +------------------------- + +To find the list of endpoint function drivers in the system:: + + # ls /sys/bus/pci-epf/drivers + pci_epf_ntb pci_epf_ntb + +If PCI_ENDPOINT_CONFIGFS is enabled:: + + # ls /sys/kernel/config/pci_ep/functions + pci_epf_ntb pci_epf_ntb + + +Creating pci-epf-ntb Device +---------------------------- + +PCI endpoint function device can be created using the configfs. To create +pci-epf-ntb device, the following commands can be used:: + + # mount -t configfs none /sys/kernel/config + # cd /sys/kernel/config/pci_ep/ + # mkdir functions/pci_epf_ntb/func1 + +The "mkdir func1" above creates the pci-epf-ntb function device that will +be probed by pci_epf_ntb driver. + +The PCI endpoint framework populates the directory with the following +configurable fields:: + + # ls functions/pci_epf_ntb/func1 + baseclass_code deviceid msi_interrupts pci-epf-ntb.0 + progif_code secondary subsys_id vendorid + cache_line_size interrupt_pin msix_interrupts primary + revid subclass_code subsys_vendor_id + +The PCI endpoint function driver populates these entries with default values +when the device is bound to the driver. The pci-epf-ntb driver populates +vendorid with 0xffff and interrupt_pin with 0x0001:: + + # cat functions/pci_epf_ntb/func1/vendorid + 0xffff + # cat functions/pci_epf_ntb/func1/interrupt_pin + 0x0001 + + +Configuring pci-epf-ntb Device +------------------------------- + +The user can configure the pci-epf-ntb device using its configfs entry. In order +to change the vendorid and the deviceid, the following +commands can be used:: + + # echo 0x104c > functions/pci_epf_ntb/func1/vendorid + # echo 0xb00d > functions/pci_epf_ntb/func1/deviceid + +In order to configure NTB specific attributes, a new sub-directory to func1 +should be created:: + + # mkdir functions/pci_epf_ntb/func1/pci_epf_ntb.0/ + +The NTB function driver will populate this directory with various attributes +that can be configured by the user:: + + # ls functions/pci_epf_ntb/func1/pci_epf_ntb.0/ + db_count mw1 mw2 mw3 mw4 num_mws + spad_count + +A sample configuration for NTB function is given below:: + + # echo 4 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/db_count + # echo 128 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/spad_count + # echo 2 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/num_mws + # echo 0x100000 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/mw1 + # echo 0x100000 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/mw2 + +Binding pci-epf-ntb Device to EP Controller +-------------------------------------------- + +NTB function device should be attached to two PCI endpoint controllers +connected to the two hosts. Use the 'primary' and 'secondary' entries +inside NTB function device to attach one PCI endpoint controller to +primary interface and the other PCI endpoint controller to the secondary +interface:: + + # ln -s controllers/2900000.pcie-ep/ functions/pci-epf-ntb/func1/primary + # ln -s controllers/2910000.pcie-ep/ functions/pci-epf-ntb/func1/secondary + +Once the above step is completed, both the PCI endpoint controllers are ready to +establish a link with the host. + + +Start the Link +-------------- + +In order for the endpoint device to establish a link with the host, the _start_ +field should be populated with '1'. For NTB, both the PCI endpoint controllers +should establish link with the host:: + + # echo 1 > controllers/2900000.pcie-ep/start + # echo 1 > controllers/2910000.pcie-ep/start + + +RootComplex Device +================== + +lspci Output +------------ + +Note that the devices listed here correspond to the values populated in +"Creating pci-epf-ntb Device" section above:: + + # lspci + 0000:00:00.0 PCI bridge: Texas Instruments Device b00d + 0000:01:00.0 RAM memory: Texas Instruments Device b00d + + +Using ntb_hw_epf Device +----------------------- + +The host side software follows the standard NTB software architecture in Linux. +All the existing client side NTB utilities like NTB Transport Client and NTB +Netdev, NTB Ping Pong Test Client and NTB Tool Test Client can be used with NTB +function device. + +For more information on NTB see +:doc:`Non-Transparent Bridge <../../driver-api/ntb>` diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst 2022-01-06 12:45:53.806318073 -0500 @@ -29,6 +29,8 @@ - negotiate image formats on individual pads +- inspect and modify internal data routing between pads of the same entity + Sub-device character device nodes, conventionally named ``/dev/v4l-subdev*``, use major number 81. @@ -499,3 +501,129 @@ :maxdepth: 1 subdev-formats + + +Multiplexed media pads and internal routing +------------------------------------------- + +Routing Table +^^^^^^^^^^^^^ + +Subdevice drivers may expose the internal connections between media pads of an +entity by exposing a routing table that applications can inspect and manipulate. +A routing table is described by a struct :c:type:`v4l2_subdev_routing`, which +contains ``num_routes`` route entries, each one represented by a struct +:c:type:`v4l2_subdev_route`. + +Data routes do not just connect one pad to another in an entity, but they refer +instead to the ``streams`` a media pad provides. Streams are data connection +endpoints in a media pad. Multiplexed media pads expose multiple ``streams`` +which represent, when the underlying hardware technology allows that, logical +data flows transported over a single physical media bus. + +A noteworthy example of logical stream multiplexing techniques is represented +by the data interleaving mechanism implemented by mean of Virtual Channels as +defined by the MIPI CSI-2 media bus specifications. A subdevice that implements +support for Virtual Channel data interleaving might expose up to 4 data +``streams``, one for each available Virtual Channel, on the source media pad +that represents a CSI-2 connection endpoint. + +A route is defined as a connection between a ``(sink_pad, sink_stream)`` pair +and a ``(source_pad, source_stream)`` pair, where ``sink_pad`` and +``source_pad`` are the indexes of two media pads part of the same media entity, +and ``sink_stream`` and ``source_stream`` are the identifiers of the data +streams to be connected in the media pads. The stream identifiers are arbitrary +numbers, i.e. they have no relevance to the hardware, but they must be unique on +a single pad, and the entity on the other side of the link must have a matching +stream identifier. + +The current routes are reported to applications in a routing table which can be +inspected using the :ref:`routing ` ioctl. + +Routes can be added or removed by adding or removing them to/from the routing +table. Also, a route in the routing table can be activated and deactivated by +setting or clearing the ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag in the ``flags`` +field of struct :c:type:`v4l2_subdev_route`. + +A subdev driver may create routes that cannot be modified by applications. Such +routes are identified by the presence of the ``V4L2_SUBDEV_ROUTE_FL_IMMUTABLE`` +flag in the ``flags`` field of struct :c:type:`v4l2_subdev_route`. Immutable +routes are always active. + +A special type of a route is a "source route", marked with +``V4L2_SUBDEV_ROUTE_FL_SOURCE`` flag. Such routes exists in e.g. sensors as the +routes' origins are internal to the device. A source route has valid +``source_pad`` and ``source_stream``, but ``sink_pad`` and ``sink_stream`` are +not used. The purpose of a source route is to describe the streams. + +As an example, a subdevice with two sink pads and one output pad has the pads +defined as follows: + +.. flat-table:: + :header-rows: 1 + + * - Pad Index + - Function + * - 0 + - SINK + * - 1 + - SINK + * - 2 + - SOURCE + +A case where the subdevice would receive a single stream via each sink pad, and +combine them to the source pad would result in a routing table as follows: + +.. flat-table:: routing table + :header-rows: 1 + + * - Sink Pad/Sink Stream + - Source Pad/Source Stream + * - 0/0 + - 2/0 + * - 1/0 + - 2/1 + +Whereas if the same subdev would receive two streams via each sink pad, and +output the combined 4 streams would result in a routing table as follows: + +.. flat-table:: routing table + :header-rows: 1 + + * - Sink Pad/Sink Stream + - Source Pad/Source Stream + * - 0/0 + - 2/0 + * - 0/1 + - 2/1 + * - 1/0 + - 2/2 + * - 1/1 + - 2/3 + +Some subdevices may have known set of routes, mutable or immutable, dictated by +the hardware. An example would be a sensor which produces pixel data and +metadata via CSI-2 bus. In such a case there can ever be only those two streams. + +A subdevice that does not produce the data is another matter. Consider a device +with two CSI-2 sink pads and two CSI-2 source pads, with the ability to route +streams freely between the sink and source pads based on HW configuration. Each +sink pad could receive streams for all four CSI-2 virtual channel. If we only +consider the virtual channels, we would have maximum number of routes of 8. + +But CSI-2 also defines a datatype for each CSI-2 packet, allowing one to send, +say, pixel data and metadata using the same virtual channel but different +datatype. Now we would have a maximum number of routes of 16. + +Generally speaking, the concept of "stream" is very flexible. As a contrived +example, you might even consider each line of a pixel frame to be a separate +stream, if your hardware would support such a thing. + +Multiplexed Streams +^^^^^^^^^^^^^^^^^^^ + +When a subdevice exposes multiple streams in a single pad (multiplexed streams), +the subdevice driver needs to have ``V4L2_SUBDEV_FL_MULTIPLEXED`` flag set. This +flag indicates that the subdev supports the uAPI extensions needed to support +multiple streams, and the driver must handle the ``stream`` field in the various +subdev ioctls. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/user-func.rst b/Documentation/userspace-api/media/v4l/user-func.rst --- a/Documentation/userspace-api/media/v4l/user-func.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/user-func.rst 2022-01-06 12:45:53.806318073 -0500 @@ -70,6 +70,7 @@ vidioc-subdev-g-crop vidioc-subdev-g-fmt vidioc-subdev-g-frame-interval + vidioc-subdev-g-routing vidioc-subdev-g-selection vidioc-subdev-querycap vidioc-subscribe-event diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst 2022-01-06 12:45:53.806318073 -0500 @@ -92,7 +92,10 @@ - Frame intervals to be enumerated, from enum :ref:`v4l2_subdev_format_whence `. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst 2022-01-06 12:45:53.806318073 -0500 @@ -97,7 +97,10 @@ - Frame sizes to be enumerated, from enum :ref:`v4l2_subdev_format_whence `. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst 2022-01-06 12:45:53.806318073 -0500 @@ -73,7 +73,10 @@ - ``flags`` - See :ref:`v4l2-subdev-mbus-code-flags` * - __u32 - - ``reserved``\ [7] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [6] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst 2022-01-06 12:45:53.806318073 -0500 @@ -96,7 +96,10 @@ - ``rect`` - Crop rectangle boundaries, in pixels. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst 2022-01-06 12:45:53.806318073 -0500 @@ -102,7 +102,10 @@ - Definition of an image format, see :c:type:`v4l2_mbus_framefmt` for details. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst 2022-01-06 12:45:53.806318073 -0500 @@ -90,7 +90,10 @@ - ``interval`` - Period, in seconds, between consecutive video frames. * - __u32 - - ``reserved``\ [9] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [8] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,142 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: V4L + +.. _VIDIOC_SUBDEV_G_ROUTING: + +****************************************************** +ioctl VIDIOC_SUBDEV_G_ROUTING, VIDIOC_SUBDEV_S_ROUTING +****************************************************** + +Name +==== + +VIDIOC_SUBDEV_G_ROUTING - VIDIOC_SUBDEV_S_ROUTING - Get or set routing between streams of media pads in a media entity. + + +Synopsis +======== + +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_G_ROUTING, struct v4l2_subdev_routing *argp ) + :name: VIDIOC_SUBDEV_G_ROUTING + +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_S_ROUTING, struct v4l2_subdev_routing *argp ) + :name: VIDIOC_SUBDEV_S_ROUTING + + +Arguments +========= + +``fd`` + File descriptor returned by :ref:`open() `. + +``argp`` + Pointer to struct :c:type:`v4l2_subdev_routing`. + + +Description +=========== + +These ioctls are used to get and set the routing in a media entity. +The routing configuration determines the flows of data inside an entity. + +Drivers report their current routing tables using the +``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes +with the VIDIOC_SUBDEV_S_ROUTING ioctl, by adding or removing routes and setting +or clearing the ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag of the ``flags`` field of +a struct :c:type:`v4l2_subdev_route`. + +When inspecting routes through VIDIOC_SUBDEV_G_ROUTING and the application +provided ``num_routes`` is not big enough to contain all the available routes +the subdevice exposes, drivers return the ENOSPC error code and adjust the +value of the ``num_routes`` field. Application should then reserve enough memory +for all the route entries and call VIDIOC_SUBDEV_G_ROUTING again. + +.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| + +.. c:type:: v4l2_subdev_routing + +.. flat-table:: struct v4l2_subdev_routing + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``which`` + - Format to modified, from enum + :ref:`v4l2_subdev_format_whence `. + * - struct :c:type:`v4l2_subdev_route` + - ``routes[]`` + - Array of struct :c:type:`v4l2_subdev_route` entries + * - __u32 + - ``num_routes`` + - Number of entries of the routes array + * - __u32 + - ``reserved``\ [5] + - Reserved for future extensions. Applications and drivers must set + the array to zero. + +.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| + +.. c:type:: v4l2_subdev_route + +.. flat-table:: struct v4l2_subdev_route + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``sink_pad`` + - Sink pad number. + * - __u32 + - ``sink_stream`` + - Sink pad stream number. + * - __u32 + - ``source_pad`` + - Source pad number. + * - __u32 + - ``source_stream`` + - Source pad stream number. + * - __u32 + - ``flags`` + - Route enable/disable flags + :ref:`v4l2_subdev_routing_flags `. + * - __u32 + - ``reserved``\ [5] + - Reserved for future extensions. Applications and drivers must set + the array to zero. + +.. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}| + +.. _v4l2-subdev-routing-flags: + +.. flat-table:: enum v4l2_subdev_routing_flags + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - 0 + - The route is enabled. Set by applications. + * - V4L2_SUBDEV_ROUTE_FL_IMMUTABLE + - 1 + - The route is immutable. Set by the driver. + * - V4L2_SUBDEV_ROUTE_FL_SOURCE + - 2 + - The route is a source route, and the ``sink_pad`` and ``sink_stream`` + fields are unused. Set by the driver. + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +ENOSPC + The number of provided route entries is less than the available ones. + +EINVAL + The sink or source pad identifiers reference a non-existing pad, or reference + pads of different types (ie. the sink_pad identifiers refers to a source pad) + or the sink or source stream identifiers reference a non-existing stream on + the sink or source pad. diff -Naur --no-dereference a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst 2021-12-17 04:14:42.000000000 -0500 +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst 2022-01-06 12:45:53.806318073 -0500 @@ -94,7 +94,10 @@ - ``r`` - Selection rectangle, in pixels. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff -Naur --no-dereference a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig --- a/drivers/auxdisplay/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/auxdisplay/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -27,6 +27,18 @@ kernel and started at boot. If you don't understand what all this is about, say N. +config SEEED_I2C_HD44780 + tristate "SEEED I2C HD44780 Character LCD support" + depends on I2C + select CHARLCD + default n + help + Enable support for Character LCDs using a HD44780 controller. + The LCD is accessible through the /dev/lcd char device (10, 156). + This code can either be compiled as a module, or linked into the + kernel and started at boot. + If you don't understand what all this is about, say N. + config KS0108 tristate "KS0108 LCD Controller" depends on PARPORT_PC diff -Naur --no-dereference a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile --- a/drivers/auxdisplay/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/auxdisplay/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -11,3 +11,4 @@ obj-$(CONFIG_HD44780) += hd44780.o obj-$(CONFIG_HT16K33) += ht16k33.o obj-$(CONFIG_PARPORT_PANEL) += panel.o +obj-$(CONFIG_SEEED_I2C_HD44780) += seeed-hd44780-i2c.o diff -Naur --no-dereference a/drivers/auxdisplay/seeed-hd44780.h b/drivers/auxdisplay/seeed-hd44780.h --- a/drivers/auxdisplay/seeed-hd44780.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/auxdisplay/seeed-hd44780.h 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,52 @@ +#ifndef _SEEED_HD44780_H_ +#define _SEEED_HD44780_H_ + +#define BUF_SIZE 64 +#define ESC_SEQ_BUF_SIZE 4 + +struct hd44780_geometry { + int cols; + int rows; + int start_addrs[]; +}; + +struct hd44780 { + struct cdev cdev; + struct device *device; + struct i2c_client *i2c_client; + struct hd44780_geometry *geometry; + + /* Current cursor positon on the display */ + struct { + int row; + int col; + } pos; + + char buf[BUF_SIZE]; + struct { + char buf[ESC_SEQ_BUF_SIZE]; + int length; + } esc_seq_buf; + bool is_in_esc_seq; + + bool backlight; + bool cursor_blink; + bool cursor_display; + + bool dirty; + + struct mutex lock; + struct list_head list; +}; + +void hd44780_write(struct hd44780 *, const char *, size_t); +void hd44780_init_lcd(struct hd44780 *); +void hd44780_print(struct hd44780 *, const char *); +void hd44780_flush(struct hd44780 *); +void hd44780_set_geometry(struct hd44780 *, struct hd44780_geometry *); +void hd44780_set_backlight(struct hd44780 *, bool); +void hd44780_set_cursor_blink(struct hd44780 *, bool); +void hd44780_set_cursor_display(struct hd44780 *, bool); + +extern struct hd44780_geometry *hd44780_geometries[]; +#endif diff -Naur --no-dereference a/drivers/auxdisplay/seeed-hd44780-i2c.c b/drivers/auxdisplay/seeed-hd44780-i2c.c --- a/drivers/auxdisplay/seeed-hd44780-i2c.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/auxdisplay/seeed-hd44780-i2c.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,713 @@ +/* + * hd44780-i2c.c + * + * Implements I2C interface for JHD1802/HD44780 LCD. + * Driver is based on driver written by gorskima + * (https://github.com/gorskima/hd44780-i2c). + * + * Port to support JHD1802 by Peter Yang + * Copyright (C) 2019 Seeed Studio + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "seeed-hd44780.h" + +#define CLASS_NAME "hd44780" +#define NAME "seeed-hd44780" +#define NUM_DEVICES 8 + +static struct class *hd44780_class; +static dev_t dev_no; +/* We start with -1 so that first returned minor is 0 */ +static atomic_t next_minor = ATOMIC_INIT(-1); + +static LIST_HEAD(hd44780_list); +static DEFINE_SPINLOCK(hd44780_list_lock); + + +#define BL 0x08 +#define E 0x04 +#define RW 0x02 +#define RS 0x01 +#define _DATA 0x40 +#define _CMD 0x80 + +#define HD44780_CLEAR_DISPLAY 0x01 +#define HD44780_RETURN_HOME 0x02 +#define HD44780_ENTRY_MODE_SET 0x04 +#define HD44780_DISPLAY_CTRL 0x08 +#define HD44780_SHIFT 0x10 +#define HD44780_FUNCTION_SET 0x20 +#define HD44780_CGRAM_ADDR 0x40 +#define HD44780_DDRAM_ADDR 0x80 + +#define HD44780_DL_8BITS 0x10 +#define HD44780_DL_4BITS 0x00 +#define HD44780_N_2LINES 0x08 +#define HD44780_N_1LINE 0x00 +#define HD44780_5x10DOTS 0x04 +#define HD44780_5x8DOTS 0x00 + +#define HD44780_D_DISPLAY_ON 0x04 +#define HD44780_D_DISPLAY_OFF 0x00 +#define HD44780_C_CURSOR_ON 0x02 +#define HD44780_C_CURSOR_OFF 0x00 +#define HD44780_B_BLINK_ON 0x01 +#define HD44780_B_BLINK_OFF 0x00 + +#define HD44780_ID_INCREMENT 0x02 +#define HD44780_ID_DECREMENT 0x00 +#define HD44780_S_SHIFT_ON 0x01 +#define HD44780_S_SHIFT_OFF 0x00 + +static struct hd44780_geometry hd44780_geometry_20x4 = { + .cols = 20, + .rows = 4, + .start_addrs = {0x00, 0x40, 0x14, 0x54}, +}; + +static struct hd44780_geometry hd44780_geometry_16x2 = { + .cols = 16, + .rows = 2, + .start_addrs = {0x00, 0x40}, +}; + +static struct hd44780_geometry hd44780_geometry_8x1 = { + .cols = 8, + .rows = 1, + .start_addrs = {0x00}, +}; + +struct hd44780_geometry *hd44780_geometries[] = { + &hd44780_geometry_20x4, + &hd44780_geometry_16x2, + &hd44780_geometry_8x1, + NULL +}; + +/* Defines possible register that we can write to */ +typedef enum { IR, DR } dest_reg; + +static void hd44780_write_nibble(struct hd44780 *lcd, dest_reg reg, u8 data) +{ + u8 first; + + /* + * First byte, select DATA or CMD + * Second byte, DATA or CMD + */ + first = (reg == DR)? _DATA: _CMD; + i2c_smbus_write_byte_data(lcd->i2c_client, first, data); + + return; +} + +/* + * JHD1802 lowlevel functions + */ +static void hd44780_write_instruction(struct hd44780 *lcd, u8 data) +{ + hd44780_write_nibble(lcd, IR, data); + udelay(37); +} + +static void hd44780_write_data(struct hd44780 *lcd, u8 data) +{ + hd44780_write_nibble(lcd, DR, data); + udelay(37 + 4); +} + +static void hd44780_write_char(struct hd44780 *lcd, char ch) +{ + struct hd44780_geometry *geo = lcd->geometry; + + hd44780_write_data(lcd, ch); + + lcd->pos.col++; + + if (lcd->pos.col == geo->cols) { + lcd->pos.row = (lcd->pos.row + 1) % geo->rows; + lcd->pos.col = 0; + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | geo->start_addrs[lcd->pos.row]); + } +} + +static void hd44780_clear_display(struct hd44780 *lcd) +{ + hd44780_write_instruction(lcd, HD44780_CLEAR_DISPLAY); + + /* Wait for 1.64 ms because this one needs more time */ + udelay(1640); + + /* + * CLEAR_DISPLAY instruction also returns cursor to home, + * so we need to update it locally. + */ + lcd->pos.row = 0; + lcd->pos.col = 0; +} + +static void hd44780_clear_line(struct hd44780 *lcd) +{ + struct hd44780_geometry *geo; + int start_addr, col; + + geo = lcd->geometry; + start_addr = geo->start_addrs[lcd->pos.row]; + + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | start_addr); + + for (col = 0; col < geo->cols; col++) + hd44780_write_data(lcd, ' '); + + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | start_addr); +} + +static void hd44780_handle_new_line(struct hd44780 *lcd) +{ + struct hd44780_geometry *geo = lcd->geometry; + + lcd->pos.row = (lcd->pos.row + 1) % geo->rows; + lcd->pos.col = 0; + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR + | geo->start_addrs[lcd->pos.row]); + hd44780_clear_line(lcd); +} + +static void hd44780_handle_carriage_return(struct hd44780 *lcd) +{ + struct hd44780_geometry *geo = lcd->geometry; + + lcd->pos.col = 0; + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR + | geo->start_addrs[lcd->pos.row]); +} + +static void hd44780_leave_esc_seq(struct hd44780 *lcd) +{ + memset(lcd->esc_seq_buf.buf, 0, ESC_SEQ_BUF_SIZE); + lcd->esc_seq_buf.length = 0; + lcd->is_in_esc_seq = false; +} + +static void hd44780_flush_esc_seq(struct hd44780 *lcd) +{ + char *buf_to_flush; + int buf_length; + + /* Copy and reset current esc seq */ + buf_to_flush = kmalloc(sizeof(char) * ESC_SEQ_BUF_SIZE, GFP_KERNEL); + memcpy(buf_to_flush, lcd->esc_seq_buf.buf, ESC_SEQ_BUF_SIZE); + buf_length = lcd->esc_seq_buf.length; + + hd44780_leave_esc_seq(lcd); + + /* Write \e that initiated current esc seq */ + hd44780_write_char(lcd, '\e'); + + /* Flush current esc seq */ + hd44780_write(lcd, buf_to_flush, buf_length); + + kfree(buf_to_flush); +} + +void hd44780_flush(struct hd44780 *lcd) +{ + while (lcd->is_in_esc_seq) + hd44780_flush_esc_seq(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_flush); + +static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch) +{ + int prev_row, prev_col; + + lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch; + + if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) { + prev_row = lcd->pos.row; + prev_col = lcd->pos.col; + + hd44780_clear_display(lcd); + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col)); + + hd44780_leave_esc_seq(lcd); + } else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) { + hd44780_write_instruction(lcd, HD44780_RETURN_HOME); + lcd->pos.row = 0; + lcd->pos.col = 0; + + hd44780_leave_esc_seq(lcd); + } else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) { + hd44780_flush_esc_seq(lcd); + } +} + +void hd44780_write(struct hd44780 *lcd, const char *buf, size_t count) +{ + size_t i; + char ch; + + if (lcd->dirty) { + hd44780_clear_display(lcd); + lcd->dirty = false; + } + + for (i = 0; i < count; i++) { + ch = buf[i]; + + if (lcd->is_in_esc_seq) { + hd44780_handle_esc_seq_char(lcd, ch); + } else { + switch (ch) { + case '\r': + hd44780_handle_carriage_return(lcd); + break; + case '\n': + hd44780_handle_new_line(lcd); + break; + case '\e': + lcd->is_in_esc_seq = true; + break; + default: + hd44780_write_char(lcd, ch); + break; + } + } + } +} +EXPORT_SYMBOL_GPL(hd44780_write); + +void hd44780_print(struct hd44780 *lcd, const char *str) +{ + hd44780_write(lcd, str, strlen(str)); +} +EXPORT_SYMBOL_GPL(hd44780_print); + +void hd44780_set_geometry(struct hd44780 *lcd, struct hd44780_geometry *geo) +{ + lcd->geometry = geo; + + if (lcd->is_in_esc_seq) + hd44780_leave_esc_seq(lcd); + + hd44780_clear_display(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_set_geometry); + +void hd44780_set_backlight(struct hd44780 *lcd, bool backlight) +{ + lcd->backlight = backlight; + /* TODO */ + hd44780_write_instruction(lcd, backlight ? BL : 0x00); +} +EXPORT_SYMBOL_GPL(hd44780_set_backlight); + +static void hd44780_update_display_ctrl(struct hd44780 *lcd) +{ + + hd44780_write_instruction(lcd, HD44780_DISPLAY_CTRL + | HD44780_D_DISPLAY_ON + | (lcd->cursor_display ? HD44780_C_CURSOR_ON + : HD44780_C_CURSOR_OFF) + | (lcd->cursor_blink ? HD44780_B_BLINK_ON + : HD44780_B_BLINK_OFF)); +} + +void hd44780_set_cursor_blink(struct hd44780 *lcd, bool cursor_blink) +{ + lcd->cursor_blink = cursor_blink; + hd44780_update_display_ctrl(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_set_cursor_blink); + +void hd44780_set_cursor_display(struct hd44780 *lcd, bool cursor_display) +{ + lcd->cursor_display= cursor_display; + hd44780_update_display_ctrl(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_set_cursor_display); + +void hd44780_init_lcd(struct hd44780 *lcd) +{ + /* HD44780 requires writing three times to initialize or reset + * according to the hardware errata on page 45 figure 23 of + * the Hitachi HD44780 datasheet */ + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET + | HD44780_DL_8BITS); + mdelay(5); + + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET + | HD44780_DL_8BITS); + udelay(100); + + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET + | HD44780_DL_8BITS); + + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET | HD44780_DL_8BITS + | HD44780_N_2LINES | HD44780_5x10DOTS); + + hd44780_write_instruction(lcd, HD44780_DISPLAY_CTRL | HD44780_D_DISPLAY_ON + | HD44780_C_CURSOR_ON | HD44780_B_BLINK_ON); + udelay(100); + + hd44780_clear_display(lcd); + + hd44780_write_instruction(lcd, HD44780_ENTRY_MODE_SET + | HD44780_ID_INCREMENT | HD44780_S_SHIFT_OFF); +} +EXPORT_SYMBOL_GPL(hd44780_init_lcd); + + +/* Device attributes */ + +static ssize_t geometry_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd; + struct hd44780_geometry *geo; + + lcd = dev_get_drvdata(dev); + geo = lcd->geometry; + + return scnprintf(buf, PAGE_SIZE, "%dx%d\n", geo->cols, geo->rows); +} + +static ssize_t geometry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd; + struct hd44780_geometry *geo; + int cols = 0, rows = 0, i; + + sscanf(buf, "%dx%d", &cols, &rows); + + for (i = 0; hd44780_geometries[i] != NULL; i++) { + geo = hd44780_geometries[i]; + + if (geo->cols == cols && geo->rows == rows) { + lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_geometry(lcd, geo); + mutex_unlock(&lcd->lock); + + break; + } + } + + return count; +} +static DEVICE_ATTR_RW(geometry); + +static ssize_t backlight_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", lcd->backlight); +} + +static ssize_t backlight_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_backlight(lcd, buf[0] == '1'); + mutex_unlock(&lcd->lock); + + return count; +} +static DEVICE_ATTR_RW(backlight); + +static ssize_t cursor_blink_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", lcd->cursor_blink); +} + +static ssize_t cursor_blink_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_cursor_blink(lcd, buf[0] == '1'); + mutex_unlock(&lcd->lock); + + return count; +} +static DEVICE_ATTR_RW(cursor_blink); + +static ssize_t cursor_display_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", lcd->cursor_display); +} + +static ssize_t cursor_display_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_cursor_display(lcd, buf[0] == '1'); + mutex_unlock(&lcd->lock); + + return count; +} +static DEVICE_ATTR_RW(cursor_display); + +static struct attribute *hd44780_device_attrs[] = { + &dev_attr_geometry.attr, + &dev_attr_backlight.attr, + &dev_attr_cursor_blink.attr, + &dev_attr_cursor_display.attr, + NULL +}; +ATTRIBUTE_GROUPS(hd44780_device); + +/* File operations */ + +static int hd44780_file_open(struct inode *inode, struct file *filp) +{ + filp->private_data = container_of(inode->i_cdev, struct hd44780, cdev); + return 0; +} + +static int hd44780_file_release(struct inode *inode, struct file *filp) +{ + struct hd44780 *lcd = filp->private_data; + hd44780_flush(lcd); + return 0; +} + +static ssize_t hd44780_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + struct hd44780 *lcd; + size_t n; + + lcd = filp->private_data; + n = min(count, (size_t)BUF_SIZE); + + // TODO: Consider using an interruptible lock + mutex_lock(&lcd->lock); + + // TODO: Support partial writes during errors? + if (copy_from_user(lcd->buf, buf, n)) { + mutex_unlock(&lcd->lock); + return -EFAULT; + } + + hd44780_write(lcd, lcd->buf, n); + + mutex_unlock(&lcd->lock); + + return n; +} + +static void hd44780_init(struct hd44780 *lcd, struct hd44780_geometry *geometry, + struct i2c_client *i2c_client) +{ + lcd->geometry = geometry; + lcd->i2c_client = i2c_client; + lcd->pos.row = 0; + lcd->pos.col = 0; + memset(lcd->esc_seq_buf.buf, 0, ESC_SEQ_BUF_SIZE); + lcd->esc_seq_buf.length = 0; + lcd->is_in_esc_seq = false; + lcd->backlight = true; + lcd->cursor_blink = true; + lcd->cursor_display = true; + mutex_init(&lcd->lock); +} + +static struct file_operations fops = { + .open = hd44780_file_open, + .release = hd44780_file_release, + .write = hd44780_file_write, +}; + +static int hd44780_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + dev_t devt; + struct hd44780 *lcd; + struct device *device; + int ret, minor; + + minor = atomic_inc_return(&next_minor); + devt = MKDEV(MAJOR(dev_no), minor); + + lcd = (struct hd44780 *)kmalloc(sizeof(struct hd44780), GFP_KERNEL); + if (!lcd) { + return -ENOMEM; + } + + /* JHD1802, default resolution 16x2 */ + hd44780_init(lcd, hd44780_geometries[1], client); + + spin_lock(&hd44780_list_lock); + list_add(&lcd->list, &hd44780_list); + spin_unlock(&hd44780_list_lock); + + cdev_init(&lcd->cdev, &fops); + ret = cdev_add(&lcd->cdev, devt, 1); + if (ret) { + pr_warn("Can't add cdev\n"); + goto exit; + } + + device = device_create_with_groups(hd44780_class, NULL, devt, NULL, + hd44780_device_groups, "lcd%d", MINOR(devt)); + + if (IS_ERR(device)) { + ret = PTR_ERR(device); + pr_warn("Can't create device\n"); + goto del_exit; + } + + dev_set_drvdata(device, lcd); + lcd->device = device; + + hd44780_init_lcd(lcd); + + hd44780_print(lcd, "Grove-16x2 LCD\nJHD1802M0"); + lcd->dirty = true; + + return 0; + +del_exit: + cdev_del(&lcd->cdev); + + spin_lock(&hd44780_list_lock); + list_del(&lcd->list); + spin_unlock(&hd44780_list_lock); +exit: + kfree(lcd); + + return ret; +} + +static struct hd44780 * get_hd44780_by_i2c_client(struct i2c_client *i2c_client) +{ + struct hd44780 *lcd; + + spin_lock(&hd44780_list_lock); + list_for_each_entry(lcd, &hd44780_list, list) { + if (lcd->i2c_client->addr == i2c_client->addr) { + spin_unlock(&hd44780_list_lock); + return lcd; + } + } + spin_unlock(&hd44780_list_lock); + + return NULL; +} + + +static int hd44780_remove(struct i2c_client *client) +{ + struct hd44780 *lcd; + lcd = get_hd44780_by_i2c_client(client); + device_destroy(hd44780_class, lcd->device->devt); + cdev_del(&lcd->cdev); + + spin_lock(&hd44780_list_lock); + list_del(&lcd->list); + spin_unlock(&hd44780_list_lock); + + kfree(lcd); + + return 0; +} + +static const struct i2c_device_id hd44780_id[] = { + { NAME, 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, hd44780_id); + +static struct i2c_driver hd44780_driver = { + .driver = { + .name = NAME, + .owner = THIS_MODULE, + }, + .probe = hd44780_probe, + .remove = hd44780_remove, + .id_table = hd44780_id, +}; + +static int __init hd44780_mod_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&dev_no, 0, NUM_DEVICES, NAME); + if (ret) { + pr_warn("Can't allocate chardev region"); + return ret; + } + + hd44780_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(hd44780_class)) { + ret = PTR_ERR(hd44780_class); + pr_warn("Can't create %s class\n", CLASS_NAME); + goto exit; + } + + ret = i2c_add_driver(&hd44780_driver); + if (ret) { + pr_warn("Can't register I2C driver %s\n", hd44780_driver.driver.name); + goto destroy_exit; + } + + return 0; + +destroy_exit: + class_destroy(hd44780_class); +exit: + unregister_chrdev_region(dev_no, NUM_DEVICES); + + return ret; +} +module_init(hd44780_mod_init); + +static void __exit hd44780_mod_exit(void) +{ + i2c_del_driver(&hd44780_driver); + class_destroy(hd44780_class); + unregister_chrdev_region(dev_no, NUM_DEVICES); +} +module_exit(hd44780_mod_exit); + +MODULE_AUTHOR("Mariusz Gorski "); +MODULE_AUTHOR("Peter Yang "); +MODULE_DESCRIPTION("JHD1802 HD44780 I2C driver"); +MODULE_LICENSE("GPL"); diff -Naur --no-dereference a/drivers/block/loop.c b/drivers/block/loop.c --- a/drivers/block/loop.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/block/loop.c 2022-01-06 12:45:53.810318090 -0500 @@ -752,6 +752,24 @@ return error; } +/* + * for AUFS + * no get/put for file. + */ +struct file *loop_backing_file(struct super_block *sb) +{ + struct file *ret; + struct loop_device *l; + + ret = NULL; + if (MAJOR(sb->s_dev) == LOOP_MAJOR) { + l = sb->s_bdev->bd_disk->private_data; + ret = l->lo_backing_file; + } + return ret; +} +EXPORT_SYMBOL_GPL(loop_backing_file); + /* loop sysfs attributes */ static ssize_t loop_attr_show(struct device *dev, char *page, diff -Naur --no-dereference a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig --- a/drivers/char/hw_random/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/char/hw_random/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -165,7 +165,7 @@ config HW_RANDOM_OMAP tristate "OMAP Random Number Generator support" - depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || ARCH_MVEBU + depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || ARCH_MVEBU || ARCH_K3 default HW_RANDOM help This driver provides kernel-side support for the Random Number diff -Naur --no-dereference a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c --- a/drivers/char/hw_random/omap-rng.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/char/hw_random/omap-rng.c 2022-01-06 12:45:53.810318090 -0500 @@ -30,8 +30,7 @@ #include #include #include - -#include +#include #define RNG_REG_STATUS_RDY (1 << 0) @@ -378,16 +377,13 @@ static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, struct platform_device *pdev) { - const struct of_device_id *match; struct device *dev = &pdev->dev; int irq, err; - match = of_match_device(of_match_ptr(omap_rng_of_match), dev); - if (!match) { - dev_err(dev, "no compatible OF match\n"); - return -EINVAL; - } - priv->pdata = match->data; + priv->pdata = of_device_get_match_data(dev); + if (!priv->pdata) + return -ENODEV; + if (of_device_is_compatible(dev->of_node, "ti,omap4-rng") || of_device_is_compatible(dev->of_node, "inside-secure,safexcel-eip76")) { diff -Naur --no-dereference a/drivers/clk/keystone/syscon-clk.c b/drivers/clk/keystone/syscon-clk.c --- a/drivers/clk/keystone/syscon-clk.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/clk/keystone/syscon-clk.c 2022-01-06 12:45:53.810318090 -0500 @@ -149,11 +149,28 @@ { /* Sentinel */ }, }; +static const struct ti_syscon_gate_clk_data am64_clk_data[] = { + TI_SYSCON_CLK_GATE("epwm_tbclk0", 0x0, 0), + TI_SYSCON_CLK_GATE("epwm_tbclk1", 0x0, 1), + TI_SYSCON_CLK_GATE("epwm_tbclk2", 0x0, 2), + TI_SYSCON_CLK_GATE("epwm_tbclk3", 0x0, 3), + TI_SYSCON_CLK_GATE("epwm_tbclk4", 0x0, 4), + TI_SYSCON_CLK_GATE("epwm_tbclk5", 0x0, 5), + TI_SYSCON_CLK_GATE("epwm_tbclk6", 0x0, 6), + TI_SYSCON_CLK_GATE("epwm_tbclk7", 0x0, 7), + TI_SYSCON_CLK_GATE("epwm_tbclk8", 0x0, 8), + { /* Sentinel */ }, +}; + static const struct of_device_id ti_syscon_gate_clk_ids[] = { { .compatible = "ti,am654-ehrpwm-tbclk", .data = &am654_clk_data, }, + { + .compatible = "ti,am64-epwm-tbclk", + .data = &am64_clk_data, + }, { } }; MODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids); diff -Naur --no-dereference a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c --- a/drivers/clocksource/timer-keystone.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/clocksource/timer-keystone.c 2022-01-06 12:45:53.810318090 -0500 @@ -13,6 +13,7 @@ #include #include #include +#include #define TIMER_NAME "timer-keystone" @@ -38,11 +39,13 @@ * @base: timer memory base address * @hz_period: cycles per HZ period * @event_dev: event device based on timer + * @registered: Flag to keep a track of registration status */ static struct keystone_timer { void __iomem *base; unsigned long hz_period; struct clock_event_device event_dev; + bool registered; } timer; static inline u32 keystone_timer_readl(unsigned long rg) @@ -140,13 +143,14 @@ return 0; } -static int __init keystone_timer_init(struct device_node *np) +static int keystone_timer_init(struct device_node *np) { struct clock_event_device *event_dev = &timer.event_dev; unsigned long rate; struct clk *clk; int irq, error; + timer.registered = false; irq = irq_of_parse_and_map(np, 0); if (!irq) { pr_err("%s: failed to map interrupts\n", __func__); @@ -215,6 +219,7 @@ clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX); pr_info("keystone timer clock @%lu Hz\n", rate); + timer.registered = true; return 0; err: clk_put(clk); @@ -224,3 +229,44 @@ TIMER_OF_DECLARE(keystone_timer, "ti,keystone-timer", keystone_timer_init); + +static const struct of_device_id keystone_clocksource_of_match[] = { + {.compatible = "ti,k2g-timer", }, + {}, +}; + +static int keystone_clocksource_probe(struct platform_device *pdev) +{ + struct clk *clk; + + if (timer.registered) + return 0; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(clk); + } + + clk_put(clk); + keystone_timer_init(pdev->dev.of_node); + if (!timer.registered) + return -EINVAL; + + return 0; +} + +static struct platform_driver keystone_clocksource_driver = { + .probe = keystone_clocksource_probe, + .driver = { + .name = "keystone_clocksource", + .of_match_table = keystone_clocksource_of_match, + }, +}; + +static int __init keystone_clocksource_init_driver(void) +{ + return platform_driver_register(&keystone_clocksource_driver); +} +device_initcall(keystone_clocksource_init_driver); diff -Naur --no-dereference a/drivers/clocksource/timer-ti-dm-systimer.c b/drivers/clocksource/timer-ti-dm-systimer.c --- a/drivers/clocksource/timer-ti-dm-systimer.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/clocksource/timer-ti-dm-systimer.c 2022-01-06 12:45:53.810318090 -0500 @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -29,6 +30,11 @@ static int counter_32k; static u32 clocksource; static u32 clockevent; +static struct irq_chip *clkev_irq_chip; +static struct irq_desc *clkev_irq_desc; + +#define AM43XX_GIC_CPU_BASE 0x48240100 +static void __iomem *gic_cpu_base; /* * Subset of the timer registers we use. Note that the register offsets @@ -507,12 +513,52 @@ return 0; } +static int omap_clockevent_late_ack_init(void) +{ + gic_cpu_base = ioremap(AM43XX_GIC_CPU_BASE, SZ_4K); + + if (!gic_cpu_base) + return -ENOMEM; + + return 0; +} + +static void omap_clockevent_late_ack(void) +{ + u32 val; + + if (!clkev_irq_chip) + return; + + /* + * For the gic to properly clear an interrupt it must be read + * from INTACK register + */ + if (gic_cpu_base) + val = readl_relaxed(gic_cpu_base + GIC_CPU_INTACK); + if (clkev_irq_chip->irq_ack) + clkev_irq_chip->irq_ack(&clkev_irq_desc->irq_data); + if (clkev_irq_chip->irq_eoi) + clkev_irq_chip->irq_eoi(&clkev_irq_desc->irq_data); + + clkev_irq_chip->irq_unmask(&clkev_irq_desc->irq_data); +} + static void omap_clockevent_idle(struct clock_event_device *evt) { struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt); struct dmtimer_systimer *t = &clkevt->t; dmtimer_systimer_disable(t); + + /* + * It is possible for a late interrupt to be generated which will + * cause a suspend failure. Let's ack it here both in the timer + * and the interrupt controller to avoid this. + */ + writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->irq_stat); + omap_clockevent_late_ack(); + clk_disable(t->fck); } @@ -621,8 +667,15 @@ of_machine_is_compatible("ti,am43")) { clkevt->dev.suspend = omap_clockevent_idle; clkevt->dev.resume = omap_clockevent_unidle; + + clkev_irq_desc = irq_to_desc(&clkevt->dev.irq); + if (clkev_irq_desc) + clkev_irq_chip = irq_desc_get_chip(clkev_irq_desc); } + if (of_machine_is_compatible("ti,am43")) + omap_clockevent_late_ack_init(); + return 0; err_out_free: diff -Naur --no-dereference a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig --- a/drivers/crypto/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/crypto/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -892,6 +892,7 @@ select CRYPTO_AES_ARM64 select CRYPTO_ALGAPI select CRYPTO_AUTHENC + select CRYPTO_DES select CRYPTO_SHA1 select CRYPTO_SHA256 select CRYPTO_SHA512 diff -Naur --no-dereference a/drivers/crypto/sa2ul.c b/drivers/crypto/sa2ul.c --- a/drivers/crypto/sa2ul.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/crypto/sa2ul.c 2022-01-06 12:45:53.810318090 -0500 @@ -66,8 +66,24 @@ /* Max Authentication tag size */ #define SA_MAX_AUTH_TAG_SZ 64 -#define PRIV_ID 0x1 -#define PRIV 0x1 +enum sa_algo_id { + SA_ALG_CBC_AES = 0, + SA_ALG_EBC_AES, + SA_ALG_CBC_DES3, + SA_ALG_ECB_DES3, + SA_ALG_SHA1, + SA_ALG_SHA256, + SA_ALG_SHA512, + SA_ALG_AUTHENC_SHA1_AES, + SA_ALG_AUTHENC_SHA256_AES, +}; + +struct sa_match_data { + u8 priv; + u8 priv_id; + u32 supported_algos; + bool skip_engine_control; +}; static struct device *sa_k3_dev; @@ -691,8 +707,9 @@ } static -int sa_init_sc(struct sa_ctx_info *ctx, const u8 *enc_key, - u16 enc_key_sz, const u8 *auth_key, u16 auth_key_sz, +int sa_init_sc(struct sa_ctx_info *ctx, const struct sa_match_data *match_data, + const u8 *enc_key, u16 enc_key_sz, + const u8 *auth_key, u16 auth_key_sz, struct algo_data *ad, u8 enc, u32 *swinfo) { int enc_sc_offset = 0; @@ -727,8 +744,8 @@ sc_buf[SA_CTX_SCCTL_OWNER_OFFSET] = 0; memcpy(&sc_buf[2], &sc_id, 2); sc_buf[4] = 0x0; - sc_buf[5] = PRIV_ID; - sc_buf[6] = PRIV; + sc_buf[5] = match_data->priv_id; + sc_buf[6] = match_data->priv; sc_buf[7] = 0x0; /* Prepare context for encryption engine */ @@ -884,8 +901,8 @@ return ret; /* Setup Encryption Security Context & Command label template */ - if (sa_init_sc(&ctx->enc, key, keylen, NULL, 0, ad, 1, - &ctx->enc.epib[1])) + if (sa_init_sc(&ctx->enc, ctx->dev_data->match_data, key, keylen, NULL, 0, + ad, 1, &ctx->enc.epib[1])) goto badkey; cmdl_len = sa_format_cmdl_gen(&cfg, @@ -897,8 +914,8 @@ ctx->enc.cmdl_size = cmdl_len; /* Setup Decryption Security Context & Command label template */ - if (sa_init_sc(&ctx->dec, key, keylen, NULL, 0, ad, 0, - &ctx->dec.epib[1])) + if (sa_init_sc(&ctx->dec, ctx->dev_data->match_data, key, keylen, NULL, 0, + ad, 0, &ctx->dec.epib[1])) goto badkey; cfg.enc_eng_id = ad->enc_eng.eng_id; @@ -1098,7 +1115,7 @@ else dma_rx = pdata->dma_rx1; - ddev = dma_rx->device->dev; + ddev = dmaengine_get_dma_device(pdata->dma_tx); rxd->ddev = ddev; memcpy(cmdl, sa_ctx->cmdl, sa_ctx->cmdl_size); @@ -1275,6 +1292,7 @@ struct crypto_alg *alg = req->base.tfm->__crt_alg; struct sa_req sa_req = { 0 }; int ret; + struct sa_crypto_data *data = dev_get_drvdata(sa_k3_dev); if (!req->cryptlen) return 0; @@ -1283,7 +1301,8 @@ return -EINVAL; /* Use SW fallback if the data size is not supported */ - if (req->cryptlen > SA_MAX_DATA_SZ || + if (req->cryptlen <= data->fallback_sz || + req->cryptlen > SA_MAX_DATA_SZ || (req->cryptlen >= SA_UNSAFE_DATA_SZ_MIN && req->cryptlen <= SA_UNSAFE_DATA_SZ_MAX)) { SYNC_SKCIPHER_REQUEST_ON_STACK(subreq, ctx->fallback.skcipher); @@ -1382,13 +1401,15 @@ struct sa_sha_req_ctx *rctx = ahash_request_ctx(req); struct sa_req sa_req = { 0 }; size_t auth_len; + struct sa_crypto_data *data = dev_get_drvdata(sa_k3_dev); auth_len = req->nbytes; if (!auth_len) return zero_message_process(req); - if (auth_len > SA_MAX_DATA_SZ || + if (auth_len <= data->fallback_sz || + auth_len > SA_MAX_DATA_SZ || (auth_len >= SA_UNSAFE_DATA_SZ_MIN && auth_len <= SA_UNSAFE_DATA_SZ_MAX)) { struct ahash_request *subreq = &rctx->fallback_req; @@ -1445,9 +1466,10 @@ cfg.akey = NULL; cfg.akey_len = 0; + ctx->dev_data = dev_get_drvdata(sa_k3_dev); /* Setup Encryption Security Context & Command label template */ - if (sa_init_sc(&ctx->enc, NULL, 0, NULL, 0, ad, 0, - &ctx->enc.epib[1])) + if (sa_init_sc(&ctx->enc, ctx->dev_data->match_data, NULL, 0, NULL, 0, + ad, 0, &ctx->enc.epib[1])) goto badkey; cmdl_len = sa_format_cmdl_gen(&cfg, @@ -1715,6 +1737,7 @@ int ret; memzero_explicit(ctx, sizeof(*ctx)); + ctx->dev_data = data; ctx->shash = crypto_alloc_shash(hash, 0, CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(ctx->shash)) { @@ -1816,8 +1839,8 @@ cfg.akey_len = keys.authkeylen; /* Setup Encryption Security Context & Command label template */ - if (sa_init_sc(&ctx->enc, keys.enckey, keys.enckeylen, - keys.authkey, keys.authkeylen, + if (sa_init_sc(&ctx->enc, ctx->dev_data->match_data, keys.enckey, + keys.enckeylen, keys.authkey, keys.authkeylen, ad, 1, &ctx->enc.epib[1])) return -EINVAL; @@ -1830,8 +1853,8 @@ ctx->enc.cmdl_size = cmdl_len; /* Setup Decryption Security Context & Command label template */ - if (sa_init_sc(&ctx->dec, keys.enckey, keys.enckeylen, - keys.authkey, keys.authkeylen, + if (sa_init_sc(&ctx->dec, ctx->dev_data->match_data, keys.enckey, + keys.enckeylen, keys.authkey, keys.authkeylen, ad, 0, &ctx->dec.epib[1])) return -EINVAL; @@ -1892,6 +1915,7 @@ struct sa_tfm_ctx *ctx = crypto_aead_ctx(tfm); struct sa_req sa_req = { 0 }; size_t auth_size, enc_size; + struct sa_crypto_data *data = dev_get_drvdata(sa_k3_dev); enc_size = req->cryptlen; auth_size = req->assoclen + req->cryptlen; @@ -1901,7 +1925,7 @@ auth_size -= crypto_aead_authsize(tfm); } - if (auth_size > SA_MAX_DATA_SZ || + if (auth_size <= data->fallback_sz || auth_size > SA_MAX_DATA_SZ || (auth_size >= SA_UNSAFE_DATA_SZ_MIN && auth_size <= SA_UNSAFE_DATA_SZ_MAX)) { struct aead_request *subreq = aead_request_ctx(req); @@ -1949,7 +1973,7 @@ } static struct sa_alg_tmpl sa_algs[] = { - { + [SA_ALG_CBC_AES] = { .type = CRYPTO_ALG_TYPE_SKCIPHER, .alg.skcipher = { .base.cra_name = "cbc(aes)", @@ -1972,7 +1996,7 @@ .decrypt = sa_decrypt, } }, - { + [SA_ALG_EBC_AES] = { .type = CRYPTO_ALG_TYPE_SKCIPHER, .alg.skcipher = { .base.cra_name = "ecb(aes)", @@ -1994,7 +2018,7 @@ .decrypt = sa_decrypt, } }, - { + [SA_ALG_CBC_DES3] = { .type = CRYPTO_ALG_TYPE_SKCIPHER, .alg.skcipher = { .base.cra_name = "cbc(des3_ede)", @@ -2017,7 +2041,7 @@ .decrypt = sa_decrypt, } }, - { + [SA_ALG_ECB_DES3] = { .type = CRYPTO_ALG_TYPE_SKCIPHER, .alg.skcipher = { .base.cra_name = "ecb(des3_ede)", @@ -2039,7 +2063,7 @@ .decrypt = sa_decrypt, } }, - { + [SA_ALG_SHA1] = { .type = CRYPTO_ALG_TYPE_AHASH, .alg.ahash = { .halg.base = { @@ -2068,7 +2092,7 @@ .import = sa_sha_import, }, }, - { + [SA_ALG_SHA256] = { .type = CRYPTO_ALG_TYPE_AHASH, .alg.ahash = { .halg.base = { @@ -2097,7 +2121,7 @@ .import = sa_sha_import, }, }, - { + [SA_ALG_SHA512] = { .type = CRYPTO_ALG_TYPE_AHASH, .alg.ahash = { .halg.base = { @@ -2126,7 +2150,7 @@ .import = sa_sha_import, }, }, - { + [SA_ALG_AUTHENC_SHA1_AES] = { .type = CRYPTO_ALG_TYPE_AEAD, .alg.aead = { .base = { @@ -2153,7 +2177,7 @@ .decrypt = sa_aead_decrypt, }, }, - { + [SA_ALG_AUTHENC_SHA256_AES] = { .type = CRYPTO_ALG_TYPE_AEAD, .alg.aead = { .base = { @@ -2184,13 +2208,19 @@ }; /* Register the algorithms in crypto framework */ -static void sa_register_algos(const struct device *dev) +static void sa_register_algos(struct sa_crypto_data *dev_data) { + const struct sa_match_data *match_data = dev_data->match_data; + struct device *dev = dev_data->dev; char *alg_name; u32 type; int i, err; for (i = 0; i < ARRAY_SIZE(sa_algs); i++) { + /* Skip unsupported algos */ + if (!(match_data->supported_algos & BIT(i))) + continue; + type = sa_algs[i].type; if (type == CRYPTO_ALG_TYPE_SKCIPHER) { alg_name = sa_algs[i].alg.skcipher.base.cra_name; @@ -2331,23 +2361,90 @@ return 0; } +static ssize_t fallback_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sa_crypto_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->fallback_sz); +} + +static ssize_t fallback_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sa_crypto_data *data = dev_get_drvdata(dev); + ssize_t status; + long value; + + status = kstrtol(buf, 0, &value); + if (status) + return status; + + data->fallback_sz = value; + + return size; +} + +static DEVICE_ATTR_RW(fallback); + +static struct attribute *sa_ul_attrs[] = { + &dev_attr_fallback.attr, + NULL, +}; + +static struct attribute_group sa_ul_attr_group = { + .attrs = sa_ul_attrs, +}; + +static struct sa_match_data am654_match_data = { + .priv = 1, + .priv_id = 1, + .supported_algos = GENMASK(SA_ALG_AUTHENC_SHA256_AES, 0), +}; + +static struct sa_match_data am64_match_data = { + .priv = 0, + .priv_id = 0, + .supported_algos = BIT(SA_ALG_CBC_AES) | + BIT(SA_ALG_EBC_AES) | + BIT(SA_ALG_SHA256) | + BIT(SA_ALG_SHA512) | + BIT(SA_ALG_AUTHENC_SHA256_AES), + .skip_engine_control = true, +}; + +static const struct of_device_id of_match[] = { + { .compatible = "ti,j721e-sa2ul", .data = &am654_match_data, }, + { .compatible = "ti,am654-sa2ul", .data = &am654_match_data, }, + { .compatible = "ti,am64-sa2ul", .data = &am64_match_data, }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_match); + static int sa_ul_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - struct resource *res; static void __iomem *saul_base; struct sa_crypto_data *dev_data; - u32 val; int ret; dev_data = devm_kzalloc(dev, sizeof(*dev_data), GFP_KERNEL); if (!dev_data) return -ENOMEM; + dev_data->match_data = of_device_get_match_data(dev); + if (!dev_data->match_data) + return -ENODEV; + + saul_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(saul_base)) + return PTR_ERR(saul_base); + sa_k3_dev = dev; dev_data->dev = dev; dev_data->pdev = pdev; + dev_data->base = saul_base; platform_set_drvdata(pdev, dev_data); dev_set_drvdata(sa_k3_dev, dev_data); @@ -2366,26 +2463,34 @@ goto destroy_dma_pool; spin_lock_init(&dev_data->scid_lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - saul_base = devm_ioremap_resource(dev, res); - dev_data->base = saul_base; - val = SA_EEC_ENCSS_EN | SA_EEC_AUTHSS_EN | SA_EEC_CTXCACH_EN | - SA_EEC_CPPI_PORT_IN_EN | SA_EEC_CPPI_PORT_OUT_EN | - SA_EEC_TRNG_EN; + if (!dev_data->match_data->skip_engine_control) { + u32 val = SA_EEC_ENCSS_EN | SA_EEC_AUTHSS_EN | SA_EEC_CTXCACH_EN | + SA_EEC_CPPI_PORT_IN_EN | SA_EEC_CPPI_PORT_OUT_EN | + SA_EEC_TRNG_EN; + + writel_relaxed(val, saul_base + SA_ENGINE_ENABLE_CONTROL); + } - writel_relaxed(val, saul_base + SA_ENGINE_ENABLE_CONTROL); + sa_register_algos(dev_data); - sa_register_algos(dev); + ret = sysfs_create_group(&dev->kobj, &sa_ul_attr_group); + if (ret) { + dev_err(dev, "failed to create sysfs attrs.\n"); + goto release_dma; + } ret = of_platform_populate(node, NULL, NULL, &pdev->dev); if (ret) - goto release_dma; + goto remove_sysfs; device_for_each_child(&pdev->dev, &pdev->dev, sa_link_child); return 0; +remove_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &sa_ul_attr_group); + release_dma: sa_unregister_algos(&pdev->dev); @@ -2406,6 +2511,10 @@ { struct sa_crypto_data *dev_data = platform_get_drvdata(pdev); + of_platform_depopulate(&pdev->dev); + + sysfs_remove_group(&pdev->dev.kobj, &sa_ul_attr_group); + sa_unregister_algos(&pdev->dev); dma_release_channel(dev_data->dma_rx2); @@ -2422,13 +2531,6 @@ return 0; } -static const struct of_device_id of_match[] = { - {.compatible = "ti,j721e-sa2ul",}, - {.compatible = "ti,am654-sa2ul",}, - {}, -}; -MODULE_DEVICE_TABLE(of, of_match); - static struct platform_driver sa_ul_driver = { .probe = sa_ul_probe, .remove = sa_ul_remove, diff -Naur --no-dereference a/drivers/crypto/sa2ul.h b/drivers/crypto/sa2ul.h --- a/drivers/crypto/sa2ul.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/crypto/sa2ul.h 2022-01-06 12:45:53.810318090 -0500 @@ -170,11 +170,14 @@ * the following range, so avoid using it. */ #define SA_UNSAFE_DATA_SZ_MIN 240 -#define SA_UNSAFE_DATA_SZ_MAX 256 +#define SA_UNSAFE_DATA_SZ_MAX 255 + +struct sa_match_data; /** * struct sa_crypto_data - Crypto driver instance data * @base: Base address of the register space + * @soc_data: Pointer to SoC specific data * @pdev: Platform device pointer * @sc_pool: security context pool * @dev: Device pointer @@ -187,9 +190,11 @@ * @dma_rx1: Pointer to DMA rx channel for sizes < 256 Bytes * @dma_rx2: Pointer to DMA rx channel for sizes > 256 Bytes * @dma_tx: Pointer to DMA TX channel + * @fallback_sz: SW fallback limit for crypto algorithms */ struct sa_crypto_data { void __iomem *base; + const struct sa_match_data *match_data; struct platform_device *pdev; struct dma_pool *sc_pool; struct device *dev; @@ -204,6 +209,7 @@ struct dma_chan *dma_rx1; struct dma_chan *dma_rx2; struct dma_chan *dma_tx; + int fallback_sz; }; /** diff -Naur --no-dereference a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c --- a/drivers/dma/dmatest.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/dmatest.c 2022-01-06 12:45:53.810318090 -0500 @@ -573,6 +573,7 @@ struct dmatest_params *params; struct dma_chan *chan; struct dma_device *dev; + struct device *dma_dev; unsigned int error_count; unsigned int failed_tests = 0; unsigned int total_tests = 0; @@ -606,6 +607,8 @@ params = &info->params; chan = thread->chan; dev = chan->device; + dma_dev = dmaengine_get_dma_device(chan); + src = &thread->src; dst = &thread->dst; if (thread->type == DMA_MEMCPY) { @@ -730,7 +733,7 @@ filltime = ktime_add(filltime, diff); } - um = dmaengine_get_unmap_data(dev->dev, src->cnt + dst->cnt, + um = dmaengine_get_unmap_data(dma_dev, src->cnt + dst->cnt, GFP_KERNEL); if (!um) { failed_tests++; @@ -745,10 +748,10 @@ struct page *pg = virt_to_page(buf); unsigned long pg_off = offset_in_page(buf); - um->addr[i] = dma_map_page(dev->dev, pg, pg_off, + um->addr[i] = dma_map_page(dma_dev, pg, pg_off, um->len, DMA_TO_DEVICE); srcs[i] = um->addr[i] + src->off; - ret = dma_mapping_error(dev->dev, um->addr[i]); + ret = dma_mapping_error(dma_dev, um->addr[i]); if (ret) { result("src mapping error", total_tests, src->off, dst->off, len, ret); @@ -763,9 +766,9 @@ struct page *pg = virt_to_page(buf); unsigned long pg_off = offset_in_page(buf); - dsts[i] = dma_map_page(dev->dev, pg, pg_off, um->len, + dsts[i] = dma_map_page(dma_dev, pg, pg_off, um->len, DMA_BIDIRECTIONAL); - ret = dma_mapping_error(dev->dev, dsts[i]); + ret = dma_mapping_error(dma_dev, dsts[i]); if (ret) { result("dst mapping error", total_tests, src->off, dst->off, len, ret); diff -Naur --no-dereference a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c --- a/drivers/dma/of-dma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/of-dma.c 2022-01-06 12:45:53.810318090 -0500 @@ -79,8 +79,18 @@ ofdma->dma_router->route_free(ofdma->dma_router->dev, route_data); } else { + int ret = 0; + chan->router = ofdma->dma_router; chan->route_data = route_data; + + if (chan->device->device_router_config) + ret = chan->device->device_router_config(chan); + + if (ret) { + dma_release_channel(chan); + chan = ERR_PTR(ret); + } } err: diff -Naur --no-dereference a/drivers/dma/ti/dma-crossbar.c b/drivers/dma/ti/dma-crossbar.c --- a/drivers/dma/ti/dma-crossbar.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/dma-crossbar.c 2022-01-06 12:45:53.810318090 -0500 @@ -122,7 +122,7 @@ return map; } -static const struct of_device_id ti_am335x_master_match[] = { +static const struct of_device_id ti_am335x_master_match[] __maybe_unused = { { .compatible = "ti,edma3-tpcc", }, {}, }; @@ -292,7 +292,7 @@ [TI_XBAR_SDMA_OFFSET] = 1, }; -static const struct of_device_id ti_dra7_master_match[] = { +static const struct of_device_id ti_dra7_master_match[] __maybe_unused = { { .compatible = "ti,omap4430-sdma", .data = &ti_dma_offset[TI_XBAR_SDMA_OFFSET], @@ -460,7 +460,7 @@ static struct platform_driver ti_dma_xbar_driver = { .driver = { .name = "ti-dma-crossbar", - .of_match_table = of_match_ptr(ti_dma_xbar_match), + .of_match_table = ti_dma_xbar_match, }, .probe = ti_dma_xbar_probe, }; diff -Naur --no-dereference a/drivers/dma/ti/k3-psil-am64.c b/drivers/dma/ti/k3-psil-am64.c --- a/drivers/dma/ti/k3-psil-am64.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/dma/ti/k3-psil-am64.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com + * Author: Peter Ujfalusi + */ + +#include + +#include "k3-psil-priv.h" + +#define PSIL_PDMA_XY_TR(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .mapped_channel_id = -1, \ + .default_flow_id = -1, \ + }, \ + } + +#define PSIL_PDMA_XY_PKT(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .mapped_channel_id = -1, \ + .default_flow_id = -1, \ + .pkt_mode = 1, \ + }, \ + } + +#define PSIL_ETHERNET(x, ch, flow_base, flow_cnt) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 16, \ + .mapped_channel_id = ch, \ + .flow_start = flow_base, \ + .flow_num = flow_cnt, \ + .default_flow_id = flow_base, \ + }, \ + } + +#define PSIL_SAUL(x, ch, flow_base, flow_cnt, default_flow, tx) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 64, \ + .mapped_channel_id = ch, \ + .flow_start = flow_base, \ + .flow_num = flow_cnt, \ + .default_flow_id = default_flow, \ + .notdpkt = tx, \ + }, \ + } + +/* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */ +static struct psil_ep am64_src_ep_map[] = { + /* SAUL */ + PSIL_SAUL(0x4000, 17, 32, 8, 32, 0), + PSIL_SAUL(0x4001, 18, 32, 8, 33, 0), + PSIL_SAUL(0x4002, 19, 40, 8, 40, 0), + PSIL_SAUL(0x4003, 20, 40, 8, 41, 0), + /* ICSS_G0 */ + PSIL_ETHERNET(0x4100, 21, 48, 16), + PSIL_ETHERNET(0x4101, 22, 64, 16), + PSIL_ETHERNET(0x4102, 23, 80, 16), + PSIL_ETHERNET(0x4103, 24, 96, 16), + /* ICSS_G1 */ + PSIL_ETHERNET(0x4200, 25, 112, 16), + PSIL_ETHERNET(0x4201, 26, 128, 16), + PSIL_ETHERNET(0x4202, 27, 144, 16), + PSIL_ETHERNET(0x4203, 28, 160, 16), + /* PDMA_MAIN0 - SPI0-3 */ + PSIL_PDMA_XY_PKT(0x4300), + PSIL_PDMA_XY_PKT(0x4301), + PSIL_PDMA_XY_PKT(0x4302), + PSIL_PDMA_XY_PKT(0x4303), + PSIL_PDMA_XY_PKT(0x4304), + PSIL_PDMA_XY_PKT(0x4305), + PSIL_PDMA_XY_PKT(0x4306), + PSIL_PDMA_XY_PKT(0x4307), + PSIL_PDMA_XY_PKT(0x4308), + PSIL_PDMA_XY_PKT(0x4309), + PSIL_PDMA_XY_PKT(0x430a), + PSIL_PDMA_XY_PKT(0x430b), + PSIL_PDMA_XY_PKT(0x430c), + PSIL_PDMA_XY_PKT(0x430d), + PSIL_PDMA_XY_PKT(0x430e), + PSIL_PDMA_XY_PKT(0x430f), + /* PDMA_MAIN0 - USART0-1 */ + PSIL_PDMA_XY_PKT(0x4310), + PSIL_PDMA_XY_PKT(0x4311), + /* PDMA_MAIN1 - SPI4 */ + PSIL_PDMA_XY_PKT(0x4400), + PSIL_PDMA_XY_PKT(0x4401), + PSIL_PDMA_XY_PKT(0x4402), + PSIL_PDMA_XY_PKT(0x4403), + /* PDMA_MAIN1 - USART2-6 */ + PSIL_PDMA_XY_PKT(0x4404), + PSIL_PDMA_XY_PKT(0x4405), + PSIL_PDMA_XY_PKT(0x4406), + PSIL_PDMA_XY_PKT(0x4407), + PSIL_PDMA_XY_PKT(0x4408), + /* PDMA_MAIN1 - ADCs */ + PSIL_PDMA_XY_TR(0x440f), + PSIL_PDMA_XY_TR(0x4410), + /* CPSW2 */ + PSIL_ETHERNET(0x4500, 16, 16, 16), +}; + +/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */ +static struct psil_ep am64_dst_ep_map[] = { + /* SAUL */ + PSIL_SAUL(0xc000, 24, 80, 8, 80, 1), + PSIL_SAUL(0xc001, 25, 88, 8, 88, 1), + /* ICSS_G0 */ + PSIL_ETHERNET(0xc100, 26, 96, 1), + PSIL_ETHERNET(0xc101, 27, 97, 1), + PSIL_ETHERNET(0xc102, 28, 98, 1), + PSIL_ETHERNET(0xc103, 29, 99, 1), + PSIL_ETHERNET(0xc104, 30, 100, 1), + PSIL_ETHERNET(0xc105, 31, 101, 1), + PSIL_ETHERNET(0xc106, 32, 102, 1), + PSIL_ETHERNET(0xc107, 33, 103, 1), + /* ICSS_G1 */ + PSIL_ETHERNET(0xc200, 34, 104, 1), + PSIL_ETHERNET(0xc201, 35, 105, 1), + PSIL_ETHERNET(0xc202, 36, 106, 1), + PSIL_ETHERNET(0xc203, 37, 107, 1), + PSIL_ETHERNET(0xc204, 38, 108, 1), + PSIL_ETHERNET(0xc205, 39, 109, 1), + PSIL_ETHERNET(0xc206, 40, 110, 1), + PSIL_ETHERNET(0xc207, 41, 111, 1), + /* CPSW2 */ + PSIL_ETHERNET(0xc500, 16, 16, 8), + PSIL_ETHERNET(0xc501, 17, 24, 8), + PSIL_ETHERNET(0xc502, 18, 32, 8), + PSIL_ETHERNET(0xc503, 19, 40, 8), + PSIL_ETHERNET(0xc504, 20, 48, 8), + PSIL_ETHERNET(0xc505, 21, 56, 8), + PSIL_ETHERNET(0xc506, 22, 64, 8), + PSIL_ETHERNET(0xc507, 23, 72, 8), +}; + +struct psil_ep_map am64_ep_map = { + .name = "am64", + .src = am64_src_ep_map, + .src_count = ARRAY_SIZE(am64_src_ep_map), + .dst = am64_dst_ep_map, + .dst_count = ARRAY_SIZE(am64_dst_ep_map), +}; diff -Naur --no-dereference a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c --- a/drivers/dma/ti/k3-psil.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-psil.c 2022-01-06 12:45:53.810318090 -0500 @@ -20,6 +20,8 @@ { .family = "AM65X", .data = &am654_ep_map }, { .family = "J721E", .data = &j721e_ep_map }, { .family = "J7200", .data = &j7200_ep_map }, + { .family = "AM64X", .data = &am64_ep_map }, + { .family = "J721S2", .data = &j721s2_ep_map }, { /* sentinel */ } }; diff -Naur --no-dereference a/drivers/dma/ti/k3-psil-j721e.c b/drivers/dma/ti/k3-psil-j721e.c --- a/drivers/dma/ti/k3-psil-j721e.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-psil-j721e.c 2022-01-06 12:45:53.810318090 -0500 @@ -58,6 +58,14 @@ }, \ } +#define PSIL_CSI2RX(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + }, \ + } + /* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */ static struct psil_ep j721e_src_ep_map[] = { /* SA2UL */ @@ -138,6 +146,71 @@ PSIL_PDMA_XY_PKT(0x4707), PSIL_PDMA_XY_PKT(0x4708), PSIL_PDMA_XY_PKT(0x4709), + /* CSI2RX */ + PSIL_CSI2RX(0x4940), + PSIL_CSI2RX(0x4941), + PSIL_CSI2RX(0x4942), + PSIL_CSI2RX(0x4943), + PSIL_CSI2RX(0x4944), + PSIL_CSI2RX(0x4945), + PSIL_CSI2RX(0x4946), + PSIL_CSI2RX(0x4947), + PSIL_CSI2RX(0x4948), + PSIL_CSI2RX(0x4949), + PSIL_CSI2RX(0x494a), + PSIL_CSI2RX(0x494b), + PSIL_CSI2RX(0x494c), + PSIL_CSI2RX(0x494d), + PSIL_CSI2RX(0x494e), + PSIL_CSI2RX(0x494f), + PSIL_CSI2RX(0x4950), + PSIL_CSI2RX(0x4951), + PSIL_CSI2RX(0x4952), + PSIL_CSI2RX(0x4953), + PSIL_CSI2RX(0x4954), + PSIL_CSI2RX(0x4955), + PSIL_CSI2RX(0x4956), + PSIL_CSI2RX(0x4957), + PSIL_CSI2RX(0x4958), + PSIL_CSI2RX(0x4959), + PSIL_CSI2RX(0x495a), + PSIL_CSI2RX(0x495b), + PSIL_CSI2RX(0x495c), + PSIL_CSI2RX(0x495d), + PSIL_CSI2RX(0x495e), + PSIL_CSI2RX(0x495f), + PSIL_CSI2RX(0x4960), + PSIL_CSI2RX(0x4961), + PSIL_CSI2RX(0x4962), + PSIL_CSI2RX(0x4963), + PSIL_CSI2RX(0x4964), + PSIL_CSI2RX(0x4965), + PSIL_CSI2RX(0x4966), + PSIL_CSI2RX(0x4967), + PSIL_CSI2RX(0x4968), + PSIL_CSI2RX(0x4969), + PSIL_CSI2RX(0x496a), + PSIL_CSI2RX(0x496b), + PSIL_CSI2RX(0x496c), + PSIL_CSI2RX(0x496d), + PSIL_CSI2RX(0x496e), + PSIL_CSI2RX(0x496f), + PSIL_CSI2RX(0x4970), + PSIL_CSI2RX(0x4971), + PSIL_CSI2RX(0x4972), + PSIL_CSI2RX(0x4973), + PSIL_CSI2RX(0x4974), + PSIL_CSI2RX(0x4975), + PSIL_CSI2RX(0x4976), + PSIL_CSI2RX(0x4977), + PSIL_CSI2RX(0x4978), + PSIL_CSI2RX(0x4979), + PSIL_CSI2RX(0x497a), + PSIL_CSI2RX(0x497b), + PSIL_CSI2RX(0x497c), + PSIL_CSI2RX(0x497d), + PSIL_CSI2RX(0x497e), + PSIL_CSI2RX(0x497f), /* CPSW9 */ PSIL_ETHERNET(0x4a00), /* CPSW0 */ diff -Naur --no-dereference a/drivers/dma/ti/k3-psil-j721s2.c b/drivers/dma/ti/k3-psil-j721s2.c --- a/drivers/dma/ti/k3-psil-j721s2.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/dma/ti/k3-psil-j721s2.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#include + +#include "k3-psil-priv.h" + +#define PSIL_PDMA_XY_TR(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + }, \ + } + +#define PSIL_PDMA_XY_PKT(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .pkt_mode = 1, \ + }, \ + } + +#define PSIL_PDMA_MCASP(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .pdma_acc32 = 1, \ + .pdma_burst = 1, \ + }, \ + } + +#define PSIL_ETHERNET(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 16, \ + }, \ + } + +#define PSIL_SA2UL(x, tx) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 64, \ + .notdpkt = tx, \ + }, \ + } + +/* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */ +static struct psil_ep j721s2_src_ep_map[] = { + /* PDMA_MCASP - McASP0-4 */ + PSIL_PDMA_MCASP(0x4400), + PSIL_PDMA_MCASP(0x4401), + PSIL_PDMA_MCASP(0x4402), + PSIL_PDMA_MCASP(0x4403), + PSIL_PDMA_MCASP(0x4404), + /* PDMA_SPI_G0 - SPI0-3 */ + PSIL_PDMA_XY_PKT(0x4600), + PSIL_PDMA_XY_PKT(0x4601), + PSIL_PDMA_XY_PKT(0x4602), + PSIL_PDMA_XY_PKT(0x4603), + PSIL_PDMA_XY_PKT(0x4604), + PSIL_PDMA_XY_PKT(0x4605), + PSIL_PDMA_XY_PKT(0x4606), + PSIL_PDMA_XY_PKT(0x4607), + PSIL_PDMA_XY_PKT(0x4608), + PSIL_PDMA_XY_PKT(0x4609), + PSIL_PDMA_XY_PKT(0x460a), + PSIL_PDMA_XY_PKT(0x460b), + PSIL_PDMA_XY_PKT(0x460c), + PSIL_PDMA_XY_PKT(0x460d), + PSIL_PDMA_XY_PKT(0x460e), + PSIL_PDMA_XY_PKT(0x460f), + /* PDMA_SPI_G1 - SPI4-7 */ + PSIL_PDMA_XY_PKT(0x4610), + PSIL_PDMA_XY_PKT(0x4611), + PSIL_PDMA_XY_PKT(0x4612), + PSIL_PDMA_XY_PKT(0x4613), + PSIL_PDMA_XY_PKT(0x4614), + PSIL_PDMA_XY_PKT(0x4615), + PSIL_PDMA_XY_PKT(0x4616), + PSIL_PDMA_XY_PKT(0x4617), + PSIL_PDMA_XY_PKT(0x4618), + PSIL_PDMA_XY_PKT(0x4619), + PSIL_PDMA_XY_PKT(0x461a), + PSIL_PDMA_XY_PKT(0x461b), + PSIL_PDMA_XY_PKT(0x461c), + PSIL_PDMA_XY_PKT(0x461d), + PSIL_PDMA_XY_PKT(0x461e), + PSIL_PDMA_XY_PKT(0x461f), + /* PDMA_USART_G0 - UART0-1 */ + PSIL_PDMA_XY_PKT(0x4700), + PSIL_PDMA_XY_PKT(0x4701), + /* PDMA_USART_G1 - UART2-3 */ + PSIL_PDMA_XY_PKT(0x4702), + PSIL_PDMA_XY_PKT(0x4703), + /* PDMA_USART_G2 - UART4-9 */ + PSIL_PDMA_XY_PKT(0x4704), + PSIL_PDMA_XY_PKT(0x4705), + PSIL_PDMA_XY_PKT(0x4706), + PSIL_PDMA_XY_PKT(0x4707), + PSIL_PDMA_XY_PKT(0x4708), + PSIL_PDMA_XY_PKT(0x4709), + /* CPSW0 */ + PSIL_ETHERNET(0x7000), + /* MCU_PDMA0 (MCU_PDMA_MISC_G0) - SPI0 */ + PSIL_PDMA_XY_PKT(0x7100), + PSIL_PDMA_XY_PKT(0x7101), + PSIL_PDMA_XY_PKT(0x7102), + PSIL_PDMA_XY_PKT(0x7103), + /* MCU_PDMA1 (MCU_PDMA_MISC_G1) - SPI1-2 */ + PSIL_PDMA_XY_PKT(0x7200), + PSIL_PDMA_XY_PKT(0x7201), + PSIL_PDMA_XY_PKT(0x7202), + PSIL_PDMA_XY_PKT(0x7203), + PSIL_PDMA_XY_PKT(0x7204), + PSIL_PDMA_XY_PKT(0x7205), + PSIL_PDMA_XY_PKT(0x7206), + PSIL_PDMA_XY_PKT(0x7207), + /* MCU_PDMA2 (MCU_PDMA_MISC_G2) - UART0 */ + PSIL_PDMA_XY_PKT(0x7300), + /* MCU_PDMA_ADC - ADC0-1 */ + PSIL_PDMA_XY_TR(0x7400), + PSIL_PDMA_XY_TR(0x7401), + PSIL_PDMA_XY_TR(0x7402), + PSIL_PDMA_XY_TR(0x7403), + /* SA2UL */ + PSIL_SA2UL(0x7500, 0), + PSIL_SA2UL(0x7501, 0), + PSIL_SA2UL(0x7502, 0), + PSIL_SA2UL(0x7503, 0), +}; + +/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */ +static struct psil_ep j721s2_dst_ep_map[] = { + /* CPSW0 */ + PSIL_ETHERNET(0xf000), + PSIL_ETHERNET(0xf001), + PSIL_ETHERNET(0xf002), + PSIL_ETHERNET(0xf003), + PSIL_ETHERNET(0xf004), + PSIL_ETHERNET(0xf005), + PSIL_ETHERNET(0xf006), + PSIL_ETHERNET(0xf007), + /* SA2UL */ + PSIL_SA2UL(0xf500, 1), + PSIL_SA2UL(0xf501, 1), +}; + +struct psil_ep_map j721s2_ep_map = { + .name = "j721s2", + .src = j721s2_src_ep_map, + .src_count = ARRAY_SIZE(j721s2_src_ep_map), + .dst = j721s2_dst_ep_map, + .dst_count = ARRAY_SIZE(j721s2_dst_ep_map), +}; diff -Naur --no-dereference a/drivers/dma/ti/k3-psil-priv.h b/drivers/dma/ti/k3-psil-priv.h --- a/drivers/dma/ti/k3-psil-priv.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-psil-priv.h 2022-01-06 12:45:53.810318090 -0500 @@ -40,5 +40,7 @@ extern struct psil_ep_map am654_ep_map; extern struct psil_ep_map j721e_ep_map; extern struct psil_ep_map j7200_ep_map; +extern struct psil_ep_map am64_ep_map; +extern struct psil_ep_map j721s2_ep_map; #endif /* K3_PSIL_PRIV_H_ */ diff -Naur --no-dereference a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c --- a/drivers/dma/ti/k3-udma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-udma.c 2022-01-06 12:45:53.810318090 -0500 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "../virt-dma.h" @@ -55,14 +56,26 @@ struct udma_chan; +enum k3_dma_type { + DMA_TYPE_UDMA = 0, + DMA_TYPE_BCDMA, + DMA_TYPE_PKTDMA, +}; + enum udma_mmr { MMR_GCFG = 0, + MMR_BCHANRT, MMR_RCHANRT, MMR_TCHANRT, MMR_LAST, }; -static const char * const mmr_names[] = { "gcfg", "rchanrt", "tchanrt" }; +static const char * const mmr_names[] = { + [MMR_GCFG] = "gcfg", + [MMR_BCHANRT] = "bchanrt", + [MMR_RCHANRT] = "rchanrt", + [MMR_TCHANRT] = "tchanrt", +}; struct udma_tchan { void __iomem *reg_rt; @@ -70,8 +83,12 @@ int id; struct k3_ring *t_ring; /* Transmit ring */ struct k3_ring *tc_ring; /* Transmit Completion ring */ + int tflow_id; /* applicable only for PKTDMA */ + }; +#define udma_bchan udma_tchan + struct udma_rflow { int id; struct k3_ring *fd_ring; /* Free Descriptor ring */ @@ -84,18 +101,44 @@ int id; }; +struct udma_oes_offsets { + /* K3 UDMA Output Event Offset */ + u32 udma_rchan; + + /* BCDMA Output Event Offsets */ + u32 bcdma_bchan_data; + u32 bcdma_bchan_ring; + u32 bcdma_tchan_data; + u32 bcdma_tchan_ring; + u32 bcdma_rchan_data; + u32 bcdma_rchan_ring; + + /* PKTDMA Output Event Offsets */ + u32 pktdma_tchan_flow; + u32 pktdma_rchan_flow; +}; + #define UDMA_FLAG_PDMA_ACC32 BIT(0) #define UDMA_FLAG_PDMA_BURST BIT(1) +#define UDMA_FLAG_TDTYPE BIT(2) +#define UDMA_FLAG_BURST_SIZE BIT(3) +#define UDMA_FLAGS_J7_CLASS (UDMA_FLAG_PDMA_ACC32 | \ + UDMA_FLAG_PDMA_BURST | \ + UDMA_FLAG_TDTYPE | \ + UDMA_FLAG_BURST_SIZE) struct udma_match_data { + enum k3_dma_type type; u32 psil_base; bool enable_memcpy_support; u32 flags; u32 statictr_z_mask; + u8 burst_size[3]; }; struct udma_soc_data { - u32 rchan_oes_offset; + struct udma_oes_offsets oes; + u32 bcdma_trigger_event_offset; }; struct udma_hwdesc { @@ -116,6 +159,11 @@ dma_addr_t buffer_paddr; }; +struct udma_tpl { + u8 levels; + u32 start_idx[3]; +}; + struct udma_dev { struct dma_device ddev; struct device *dev; @@ -123,8 +171,9 @@ const struct udma_match_data *match_data; const struct udma_soc_data *soc_data; - u8 tpl_levels; - u32 tpl_start_idx[3]; + struct udma_tpl bchan_tpl; + struct udma_tpl tchan_tpl; + struct udma_tpl rchan_tpl; size_t desc_align; /* alignment to use for descriptors */ @@ -138,16 +187,21 @@ struct udma_rx_flush rx_flush; + int bchan_cnt; int tchan_cnt; int echan_cnt; int rchan_cnt; int rflow_cnt; + int tflow_cnt; + unsigned long *bchan_map; unsigned long *tchan_map; unsigned long *rchan_map; unsigned long *rflow_gp_map; unsigned long *rflow_gp_map_allocated; unsigned long *rflow_in_use; + unsigned long *tflow_map; + struct udma_bchan *bchans; struct udma_tchan *tchans; struct udma_rchan *rchans; struct udma_rflow *rflows; @@ -155,6 +209,7 @@ struct udma_chan *channels; u32 psil_base; u32 atype; + u32 asel; }; struct udma_desc { @@ -199,6 +254,7 @@ bool notdpkt; /* Suppress sending TDC packet */ int remote_thread_id; u32 atype; + u32 asel; u32 src_thread; u32 dst_thread; enum psil_endpoint_type ep_type; @@ -206,6 +262,13 @@ bool enable_burst; enum udma_tp_level channel_tpl; /* Channel Throughput Level */ + u32 tr_trigger_type; + + /* PKDMA mapped channel */ + int mapped_channel_id; + /* PKTDMA default tflow or rflow for mapped channel */ + int default_flow_id; + enum dma_transfer_direction dir; }; @@ -213,11 +276,13 @@ struct virt_dma_chan vc; struct dma_slave_config cfg; struct udma_dev *ud; + struct device *dma_dev; struct udma_desc *desc; struct udma_desc *terminated_desc; struct udma_static_tr static_tr; char *name; + struct udma_bchan *bchan; struct udma_tchan *tchan; struct udma_rchan *rchan; struct udma_rflow *rflow; @@ -353,10 +418,48 @@ src_thread, dst_thread); } +static void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel) +{ + struct device *chan_dev = &chan->dev->device; + + if (asel == 0) { + /* No special handling for the channel */ + chan->dev->chan_dma_dev = false; + + chan_dev->dma_coherent = false; + chan_dev->dma_parms = NULL; + } else if (asel == 14 || asel == 15) { + chan->dev->chan_dma_dev = true; + + chan_dev->dma_coherent = true; + dma_coerce_mask_and_coherent(chan_dev, DMA_BIT_MASK(48)); + chan_dev->dma_parms = chan_dev->parent->dma_parms; + } else { + dev_warn(chan->device->dev, "Invalid ASEL value: %u\n", asel); + + chan_dev->dma_coherent = false; + chan_dev->dma_parms = NULL; + } +} + +static u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id) +{ + int i; + + for (i = 0; i < tpl_map->levels; i++) { + if (chan_id >= tpl_map->start_idx[i]) + return i; + } + + return 0; +} + static void udma_reset_uchan(struct udma_chan *uc) { memset(&uc->config, 0, sizeof(uc->config)); uc->config.remote_thread_id = -1; + uc->config.mapped_channel_id = -1; + uc->config.default_flow_id = -1; uc->state = UDMA_CHAN_IS_IDLE; } @@ -439,9 +542,7 @@ d->hwdesc[i].cppi5_desc_vaddr = NULL; } } else if (d->hwdesc[0].cppi5_desc_vaddr) { - struct udma_dev *ud = uc->ud; - - dma_free_coherent(ud->dev, d->hwdesc[0].cppi5_desc_size, + dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size, d->hwdesc[0].cppi5_desc_vaddr, d->hwdesc[0].cppi5_desc_paddr); @@ -670,8 +771,10 @@ val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PCNT_REG); udma_tchanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val); - val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); - udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val); + if (!uc->bchan) { + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); + udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val); + } } if (uc->rchan) { @@ -746,10 +849,16 @@ { struct udma_chan_config *ucc = &uc->config; - if (ucc->pkt_mode && (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) { + if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode && + (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) { int i; - /* Push all descriptors to ring for packet mode cyclic or RX */ + /* + * UDMA only: Push all descriptors to ring for packet mode + * cyclic or RX + * PKTDMA supports pre-linked descriptor and cyclic is not + * supported + */ for (i = 0; i < uc->desc->sglen; i++) udma_push_to_ring(uc, i); } else { @@ -1020,13 +1129,12 @@ { struct udma_chan *uc = data; struct udma_desc *d; - unsigned long flags; dma_addr_t paddr = 0; if (udma_pop_from_ring(uc, &paddr) || !paddr) return IRQ_HANDLED; - spin_lock_irqsave(&uc->vc.lock, flags); + spin_lock(&uc->vc.lock); /* Teardown completion message */ if (cppi5_desc_is_tdcm(paddr)) { @@ -1077,7 +1185,7 @@ } } out: - spin_unlock_irqrestore(&uc->vc.lock, flags); + spin_unlock(&uc->vc.lock); return IRQ_HANDLED; } @@ -1086,9 +1194,8 @@ { struct udma_chan *uc = data; struct udma_desc *d; - unsigned long flags; - spin_lock_irqsave(&uc->vc.lock, flags); + spin_lock(&uc->vc.lock); d = uc->desc; if (d) { d->tr_idx = (d->tr_idx + 1) % d->sglen; @@ -1103,7 +1210,7 @@ } } - spin_unlock_irqrestore(&uc->vc.lock, flags); + spin_unlock(&uc->vc.lock); return IRQ_HANDLED; } @@ -1181,10 +1288,12 @@ if (test_bit(id, ud->rflow_in_use)) return ERR_PTR(-ENOENT); - /* GP rflow has to be allocated first */ - if (!test_bit(id, ud->rflow_gp_map) && - !test_bit(id, ud->rflow_gp_map_allocated)) - return ERR_PTR(-EINVAL); + if (ud->rflow_gp_map) { + /* GP rflow has to be allocated first */ + if (!test_bit(id, ud->rflow_gp_map) && + !test_bit(id, ud->rflow_gp_map_allocated)) + return ERR_PTR(-EINVAL); + } dev_dbg(ud->dev, "get rflow%d\n", id); set_bit(id, ud->rflow_in_use); @@ -1215,10 +1324,10 @@ } else { \ int start; \ \ - if (tpl >= ud->tpl_levels) \ - tpl = ud->tpl_levels - 1; \ + if (tpl >= ud->res##_tpl.levels) \ + tpl = ud->res##_tpl.levels - 1; \ \ - start = ud->tpl_start_idx[tpl]; \ + start = ud->res##_tpl.start_idx[tpl]; \ \ id = find_next_zero_bit(ud->res##_map, ud->res##_cnt, \ start); \ @@ -1231,12 +1340,47 @@ return &ud->res##s[id]; \ } +UDMA_RESERVE_RESOURCE(bchan); UDMA_RESERVE_RESOURCE(tchan); UDMA_RESERVE_RESOURCE(rchan); +static int bcdma_get_bchan(struct udma_chan *uc) +{ + struct udma_dev *ud = uc->ud; + enum udma_tp_level tpl; + int ret; + + if (uc->bchan) { + dev_dbg(ud->dev, "chan%d: already have bchan%d allocated\n", + uc->id, uc->bchan->id); + return 0; + } + + /* + * Use normal channels for peripherals, and highest TPL channel for + * mem2mem + */ + if (uc->config.tr_trigger_type) + tpl = 0; + else + tpl = ud->bchan_tpl.levels - 1; + + uc->bchan = __udma_reserve_bchan(ud, tpl, -1); + if (IS_ERR(uc->bchan)) { + ret = PTR_ERR(uc->bchan); + uc->bchan = NULL; + return ret; + } + + uc->tchan = uc->bchan; + + return 0; +} + static int udma_get_tchan(struct udma_chan *uc) { struct udma_dev *ud = uc->ud; + int ret; if (uc->tchan) { dev_dbg(ud->dev, "chan%d: already have tchan%d allocated\n", @@ -1244,14 +1388,48 @@ return 0; } - uc->tchan = __udma_reserve_tchan(ud, uc->config.channel_tpl, -1); + /* + * mapped_channel_id is -1 for UDMA, BCDMA and PKTDMA unmapped channels. + * For PKTDMA mapped channels it is configured to a channel which must + * be used to service the peripheral. + */ + uc->tchan = __udma_reserve_tchan(ud, uc->config.channel_tpl, + uc->config.mapped_channel_id); + if (IS_ERR(uc->tchan)) { + ret = PTR_ERR(uc->tchan); + uc->tchan = NULL; + return ret; + } - return PTR_ERR_OR_ZERO(uc->tchan); + if (ud->tflow_cnt) { + int tflow_id; + + /* Only PKTDMA have support for tx flows */ + if (uc->config.default_flow_id >= 0) + tflow_id = uc->config.default_flow_id; + else + tflow_id = uc->tchan->id; + + if (test_bit(tflow_id, ud->tflow_map)) { + dev_err(ud->dev, "tflow%d is in use\n", tflow_id); + clear_bit(uc->tchan->id, ud->tchan_map); + uc->tchan = NULL; + return -ENOENT; + } + + uc->tchan->tflow_id = tflow_id; + set_bit(tflow_id, ud->tflow_map); + } else { + uc->tchan->tflow_id = -1; + } + + return 0; } static int udma_get_rchan(struct udma_chan *uc) { struct udma_dev *ud = uc->ud; + int ret; if (uc->rchan) { dev_dbg(ud->dev, "chan%d: already have rchan%d allocated\n", @@ -1259,9 +1437,20 @@ return 0; } - uc->rchan = __udma_reserve_rchan(ud, uc->config.channel_tpl, -1); + /* + * mapped_channel_id is -1 for UDMA, BCDMA and PKTDMA unmapped channels. + * For PKTDMA mapped channels it is configured to a channel which must + * be used to service the peripheral. + */ + uc->rchan = __udma_reserve_rchan(ud, uc->config.channel_tpl, + uc->config.mapped_channel_id); + if (IS_ERR(uc->rchan)) { + ret = PTR_ERR(uc->rchan); + uc->rchan = NULL; + return ret; + } - return PTR_ERR_OR_ZERO(uc->rchan); + return 0; } static int udma_get_chan_pair(struct udma_chan *uc) @@ -1287,8 +1476,11 @@ /* Can be optimized, but let's have it like this for now */ end = min(ud->tchan_cnt, ud->rchan_cnt); - /* Try to use the highest TPL channel pair for MEM_TO_MEM channels */ - chan_id = ud->tpl_start_idx[ud->tpl_levels - 1]; + /* + * Try to use the highest TPL channel pair for MEM_TO_MEM channels + * Note: in UDMAP the channel TPL is symmetric between tchan and rchan + */ + chan_id = ud->tchan_tpl.start_idx[ud->tchan_tpl.levels - 1]; for (; chan_id < end; chan_id++) { if (!test_bit(chan_id, ud->tchan_map) && !test_bit(chan_id, ud->rchan_map)) @@ -1303,12 +1495,16 @@ uc->tchan = &ud->tchans[chan_id]; uc->rchan = &ud->rchans[chan_id]; + /* UDMA does not use tx flows */ + uc->tchan->tflow_id = -1; + return 0; } static int udma_get_rflow(struct udma_chan *uc, int flow_id) { struct udma_dev *ud = uc->ud; + int ret; if (!uc->rchan) { dev_err(ud->dev, "chan%d: does not have rchan??\n", uc->id); @@ -1322,8 +1518,26 @@ } uc->rflow = __udma_get_rflow(ud, flow_id); + if (IS_ERR(uc->rflow)) { + ret = PTR_ERR(uc->rflow); + uc->rflow = NULL; + return ret; + } - return PTR_ERR_OR_ZERO(uc->rflow); + return 0; +} + +static void bcdma_put_bchan(struct udma_chan *uc) +{ + struct udma_dev *ud = uc->ud; + + if (uc->bchan) { + dev_dbg(ud->dev, "chan%d: put bchan%d\n", uc->id, + uc->bchan->id); + clear_bit(uc->bchan->id, ud->bchan_map); + uc->bchan = NULL; + uc->tchan = NULL; + } } static void udma_put_rchan(struct udma_chan *uc) @@ -1346,6 +1560,10 @@ dev_dbg(ud->dev, "chan%d: put tchan%d\n", uc->id, uc->tchan->id); clear_bit(uc->tchan->id, ud->tchan_map); + + if (uc->tchan->tflow_id >= 0) + clear_bit(uc->tchan->tflow_id, ud->tflow_map); + uc->tchan = NULL; } } @@ -1362,6 +1580,65 @@ } } +static void bcdma_free_bchan_resources(struct udma_chan *uc) +{ + if (!uc->bchan) + return; + + k3_ringacc_ring_free(uc->bchan->tc_ring); + k3_ringacc_ring_free(uc->bchan->t_ring); + uc->bchan->tc_ring = NULL; + uc->bchan->t_ring = NULL; + k3_configure_chan_coherency(&uc->vc.chan, 0); + + bcdma_put_bchan(uc); +} + +static int bcdma_alloc_bchan_resources(struct udma_chan *uc) +{ + struct k3_ring_cfg ring_cfg; + struct udma_dev *ud = uc->ud; + int ret; + + ret = bcdma_get_bchan(uc); + if (ret) + return ret; + + ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->bchan->id, -1, + &uc->bchan->t_ring, + &uc->bchan->tc_ring); + if (ret) { + ret = -EBUSY; + goto err_ring; + } + + memset(&ring_cfg, 0, sizeof(ring_cfg)); + ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE; + ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8; + ring_cfg.mode = K3_RINGACC_RING_MODE_RING; + + k3_configure_chan_coherency(&uc->vc.chan, ud->asel); + ring_cfg.asel = ud->asel; + ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan); + + ret = k3_ringacc_ring_cfg(uc->bchan->t_ring, &ring_cfg); + if (ret) + goto err_ringcfg; + + return 0; + +err_ringcfg: + k3_ringacc_ring_free(uc->bchan->tc_ring); + uc->bchan->tc_ring = NULL; + k3_ringacc_ring_free(uc->bchan->t_ring); + uc->bchan->t_ring = NULL; + k3_configure_chan_coherency(&uc->vc.chan, 0); +err_ring: + bcdma_put_bchan(uc); + + return ret; +} + static void udma_free_tx_resources(struct udma_chan *uc) { if (!uc->tchan) @@ -1379,15 +1656,22 @@ { struct k3_ring_cfg ring_cfg; struct udma_dev *ud = uc->ud; - int ret; + struct udma_tchan *tchan; + int ring_idx, ret; ret = udma_get_tchan(uc); if (ret) return ret; - ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->tchan->id, -1, - &uc->tchan->t_ring, - &uc->tchan->tc_ring); + tchan = uc->tchan; + if (tchan->tflow_id >= 0) + ring_idx = tchan->tflow_id; + else + ring_idx = ud->bchan_cnt + tchan->id; + + ret = k3_ringacc_request_rings_pair(ud->ringacc, ring_idx, -1, + &tchan->t_ring, + &tchan->tc_ring); if (ret) { ret = -EBUSY; goto err_ring; @@ -1396,10 +1680,18 @@ memset(&ring_cfg, 0, sizeof(ring_cfg)); ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE; ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8; - ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE; + if (ud->match_data->type == DMA_TYPE_UDMA) { + ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE; + } else { + ring_cfg.mode = K3_RINGACC_RING_MODE_RING; - ret = k3_ringacc_ring_cfg(uc->tchan->t_ring, &ring_cfg); - ret |= k3_ringacc_ring_cfg(uc->tchan->tc_ring, &ring_cfg); + k3_configure_chan_coherency(&uc->vc.chan, uc->config.asel); + ring_cfg.asel = uc->config.asel; + ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan); + } + + ret = k3_ringacc_ring_cfg(tchan->t_ring, &ring_cfg); + ret |= k3_ringacc_ring_cfg(tchan->tc_ring, &ring_cfg); if (ret) goto err_ringcfg; @@ -1452,14 +1744,23 @@ if (uc->config.dir == DMA_MEM_TO_MEM) return 0; - ret = udma_get_rflow(uc, uc->rchan->id); + if (uc->config.default_flow_id >= 0) + ret = udma_get_rflow(uc, uc->config.default_flow_id); + else + ret = udma_get_rflow(uc, uc->rchan->id); + if (ret) { ret = -EBUSY; goto err_rflow; } rflow = uc->rflow; - fd_ring_id = ud->tchan_cnt + ud->echan_cnt + uc->rchan->id; + if (ud->tflow_cnt) + fd_ring_id = ud->tflow_cnt + rflow->id; + else + fd_ring_id = ud->bchan_cnt + ud->tchan_cnt + ud->echan_cnt + + uc->rchan->id; + ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1, &rflow->fd_ring, &rflow->r_ring); if (ret) { @@ -1469,15 +1770,25 @@ memset(&ring_cfg, 0, sizeof(ring_cfg)); - if (uc->config.pkt_mode) - ring_cfg.size = SG_MAX_SEGMENTS; - else + ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8; + if (ud->match_data->type == DMA_TYPE_UDMA) { + if (uc->config.pkt_mode) + ring_cfg.size = SG_MAX_SEGMENTS; + else + ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE; + + ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE; + } else { ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE; + ring_cfg.mode = K3_RINGACC_RING_MODE_RING; - ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8; - ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE; + k3_configure_chan_coherency(&uc->vc.chan, uc->config.asel); + ring_cfg.asel = uc->config.asel; + ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan); + } ret = k3_ringacc_ring_cfg(rflow->fd_ring, &ring_cfg); + ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE; ret |= k3_ringacc_ring_cfg(rflow->r_ring, &ring_cfg); @@ -1499,7 +1810,18 @@ return ret; } -#define TISCI_TCHAN_VALID_PARAMS ( \ +#define TISCI_BCDMA_BCHAN_VALID_PARAMS ( \ + TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID | \ + TI_SCI_MSG_VALUE_RM_UDMAP_CH_EXTENDED_CH_TYPE_VALID) + +#define TISCI_BCDMA_TCHAN_VALID_PARAMS ( \ + TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID | \ + TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID) + +#define TISCI_BCDMA_RCHAN_VALID_PARAMS ( \ + TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID) + +#define TISCI_UDMA_TCHAN_VALID_PARAMS ( \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID | \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FILT_EINFO_VALID | \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FILT_PSWORDS_VALID | \ @@ -1509,7 +1831,7 @@ TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID | \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID) -#define TISCI_RCHAN_VALID_PARAMS ( \ +#define TISCI_UDMA_RCHAN_VALID_PARAMS ( \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID | \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID | \ TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID | \ @@ -1527,20 +1849,32 @@ const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops; struct udma_tchan *tchan = uc->tchan; struct udma_rchan *rchan = uc->rchan; - int ret = 0; + u8 burst_size = 0; + int ret; + u8 tpl; /* Non synchronized - mem to mem type of transfer */ int tc_ring = k3_ringacc_get_ring_id(tchan->tc_ring); struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 }; struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 }; - req_tx.valid_params = TISCI_TCHAN_VALID_PARAMS; + if (ud->match_data->flags & UDMA_FLAG_BURST_SIZE) { + tpl = udma_get_chan_tpl_index(&ud->tchan_tpl, tchan->id); + + burst_size = ud->match_data->burst_size[tpl]; + } + + req_tx.valid_params = TISCI_UDMA_TCHAN_VALID_PARAMS; req_tx.nav_id = tisci_rm->tisci_dev_id; req_tx.index = tchan->id; req_tx.tx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_3RDP_BCOPY_PBRR; req_tx.tx_fetch_size = sizeof(struct cppi5_desc_hdr_t) >> 2; req_tx.txcq_qnum = tc_ring; req_tx.tx_atype = ud->atype; + if (burst_size) { + req_tx.valid_params |= TI_SCI_MSG_VALUE_RM_UDMAP_CH_BURST_SIZE_VALID; + req_tx.tx_burst_size = burst_size; + } ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx); if (ret) { @@ -1548,13 +1882,17 @@ return ret; } - req_rx.valid_params = TISCI_RCHAN_VALID_PARAMS; + req_rx.valid_params = TISCI_UDMA_RCHAN_VALID_PARAMS; req_rx.nav_id = tisci_rm->tisci_dev_id; req_rx.index = rchan->id; req_rx.rx_fetch_size = sizeof(struct cppi5_desc_hdr_t) >> 2; req_rx.rxcq_qnum = tc_ring; req_rx.rx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_3RDP_BCOPY_PBRR; req_rx.rx_atype = ud->atype; + if (burst_size) { + req_rx.valid_params |= TI_SCI_MSG_VALUE_RM_UDMAP_CH_BURST_SIZE_VALID; + req_rx.rx_burst_size = burst_size; + } ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx); if (ret) @@ -1563,6 +1901,39 @@ return ret; } +static int bcdma_tisci_m2m_channel_config(struct udma_chan *uc) +{ + struct udma_dev *ud = uc->ud; + struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops; + struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 }; + struct udma_bchan *bchan = uc->bchan; + u8 burst_size = 0; + int ret; + u8 tpl; + + if (ud->match_data->flags & UDMA_FLAG_BURST_SIZE) { + tpl = udma_get_chan_tpl_index(&ud->bchan_tpl, bchan->id); + + burst_size = ud->match_data->burst_size[tpl]; + } + + req_tx.valid_params = TISCI_BCDMA_BCHAN_VALID_PARAMS; + req_tx.nav_id = tisci_rm->tisci_dev_id; + req_tx.extended_ch_type = TI_SCI_RM_BCDMA_EXTENDED_CH_TYPE_BCHAN; + req_tx.index = bchan->id; + if (burst_size) { + req_tx.valid_params |= TI_SCI_MSG_VALUE_RM_UDMAP_CH_BURST_SIZE_VALID; + req_tx.tx_burst_size = burst_size; + } + + ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx); + if (ret) + dev_err(ud->dev, "bchan%d cfg failed %d\n", bchan->id, ret); + + return ret; +} + static int udma_tisci_tx_channel_config(struct udma_chan *uc) { struct udma_dev *ud = uc->ud; @@ -1572,7 +1943,7 @@ int tc_ring = k3_ringacc_get_ring_id(tchan->tc_ring); struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 }; u32 mode, fetch_size; - int ret = 0; + int ret; if (uc->config.pkt_mode) { mode = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR; @@ -1583,7 +1954,7 @@ fetch_size = sizeof(struct cppi5_desc_hdr_t); } - req_tx.valid_params = TISCI_TCHAN_VALID_PARAMS; + req_tx.valid_params = TISCI_UDMA_TCHAN_VALID_PARAMS; req_tx.nav_id = tisci_rm->tisci_dev_id; req_tx.index = tchan->id; req_tx.tx_chan_type = mode; @@ -1591,6 +1962,40 @@ req_tx.tx_fetch_size = fetch_size >> 2; req_tx.txcq_qnum = tc_ring; req_tx.tx_atype = uc->config.atype; + if (uc->config.ep_type == PSIL_EP_PDMA_XY && + ud->match_data->flags & UDMA_FLAG_TDTYPE) { + /* wait for peer to complete the teardown for PDMAs */ + req_tx.valid_params |= + TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID; + req_tx.tx_tdtype = 1; + } + + ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx); + if (ret) + dev_err(ud->dev, "tchan%d cfg failed %d\n", tchan->id, ret); + + return ret; +} + +static int bcdma_tisci_tx_channel_config(struct udma_chan *uc) +{ + struct udma_dev *ud = uc->ud; + struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops; + struct udma_tchan *tchan = uc->tchan; + struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 }; + int ret; + + req_tx.valid_params = TISCI_BCDMA_TCHAN_VALID_PARAMS; + req_tx.nav_id = tisci_rm->tisci_dev_id; + req_tx.index = tchan->id; + req_tx.tx_supr_tdpkt = uc->config.notdpkt; + if (ud->match_data->flags & UDMA_FLAG_TDTYPE) { + /* wait for peer to complete the teardown for PDMAs */ + req_tx.valid_params |= + TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID; + req_tx.tx_tdtype = 1; + } ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx); if (ret) @@ -1599,6 +2004,8 @@ return ret; } +#define pktdma_tisci_tx_channel_config bcdma_tisci_tx_channel_config + static int udma_tisci_rx_channel_config(struct udma_chan *uc) { struct udma_dev *ud = uc->ud; @@ -1610,7 +2017,7 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 }; struct ti_sci_msg_rm_udmap_flow_cfg flow_req = { 0 }; u32 mode, fetch_size; - int ret = 0; + int ret; if (uc->config.pkt_mode) { mode = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR; @@ -1621,7 +2028,7 @@ fetch_size = sizeof(struct cppi5_desc_hdr_t); } - req_rx.valid_params = TISCI_RCHAN_VALID_PARAMS; + req_rx.valid_params = TISCI_UDMA_RCHAN_VALID_PARAMS; req_rx.nav_id = tisci_rm->tisci_dev_id; req_rx.index = rchan->id; req_rx.rx_fetch_size = fetch_size >> 2; @@ -1680,6 +2087,72 @@ return 0; } +static int bcdma_tisci_rx_channel_config(struct udma_chan *uc) +{ + struct udma_dev *ud = uc->ud; + struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops; + struct udma_rchan *rchan = uc->rchan; + struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 }; + int ret; + + req_rx.valid_params = TISCI_BCDMA_RCHAN_VALID_PARAMS; + req_rx.nav_id = tisci_rm->tisci_dev_id; + req_rx.index = rchan->id; + + ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx); + if (ret) + dev_err(ud->dev, "rchan%d cfg failed %d\n", rchan->id, ret); + + return ret; +} + +static int pktdma_tisci_rx_channel_config(struct udma_chan *uc) +{ + struct udma_dev *ud = uc->ud; + struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops; + struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 }; + struct ti_sci_msg_rm_udmap_flow_cfg flow_req = { 0 }; + int ret; + + req_rx.valid_params = TISCI_BCDMA_RCHAN_VALID_PARAMS; + req_rx.nav_id = tisci_rm->tisci_dev_id; + req_rx.index = uc->rchan->id; + + ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx); + if (ret) { + dev_err(ud->dev, "rchan%d cfg failed %d\n", uc->rchan->id, ret); + return ret; + } + + flow_req.valid_params = + TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_EINFO_PRESENT_VALID | + TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_PSINFO_PRESENT_VALID | + TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_ERROR_HANDLING_VALID; + + flow_req.nav_id = tisci_rm->tisci_dev_id; + flow_req.flow_index = uc->rflow->id; + + if (uc->config.needs_epib) + flow_req.rx_einfo_present = 1; + else + flow_req.rx_einfo_present = 0; + if (uc->config.psd_size) + flow_req.rx_psinfo_present = 1; + else + flow_req.rx_psinfo_present = 0; + flow_req.rx_error_handling = 1; + + ret = tisci_ops->rx_flow_cfg(tisci_rm->tisci, &flow_req); + + if (ret) + dev_err(ud->dev, "flow%d config failed: %d\n", uc->rflow->id, + ret); + + return ret; +} + static int udma_alloc_chan_resources(struct dma_chan *chan) { struct udma_chan *uc = to_udma_chan(chan); @@ -1689,6 +2162,8 @@ u32 irq_udma_idx; int ret; + uc->dma_dev = ud->dev; + if (uc->config.pkt_mode || uc->config.dir == DMA_MEM_TO_MEM) { uc->use_dma_pool = true; /* in case of MEM_TO_MEM we have maximum of two TRs */ @@ -1784,7 +2259,7 @@ K3_PSIL_DST_THREAD_ID_OFFSET; irq_ring = uc->rflow->r_ring; - irq_udma_idx = soc_data->rchan_oes_offset + uc->rchan->id; + irq_udma_idx = soc_data->oes.udma_rchan + uc->rchan->id; ret = udma_tisci_rx_channel_config(uc); break; @@ -1884,6 +2359,369 @@ return ret; } +static int bcdma_alloc_chan_resources(struct dma_chan *chan) +{ + struct udma_chan *uc = to_udma_chan(chan); + struct udma_dev *ud = to_udma_dev(chan->device); + const struct udma_oes_offsets *oes = &ud->soc_data->oes; + u32 irq_udma_idx, irq_ring_idx; + int ret; + + /* Only TR mode is supported */ + uc->config.pkt_mode = false; + + /* + * Make sure that the completion is in a known state: + * No teardown, the channel is idle + */ + reinit_completion(&uc->teardown_completed); + complete_all(&uc->teardown_completed); + uc->state = UDMA_CHAN_IS_IDLE; + + switch (uc->config.dir) { + case DMA_MEM_TO_MEM: + /* Non synchronized - mem to mem type of transfer */ + dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-MEM\n", __func__, + uc->id); + + ret = bcdma_alloc_bchan_resources(uc); + if (ret) + return ret; + + irq_ring_idx = uc->bchan->id + oes->bcdma_bchan_ring; + irq_udma_idx = uc->bchan->id + oes->bcdma_bchan_data; + + ret = bcdma_tisci_m2m_channel_config(uc); + break; + case DMA_MEM_TO_DEV: + /* Slave transfer synchronized - mem to dev (TX) trasnfer */ + dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__, + uc->id); + + ret = udma_alloc_tx_resources(uc); + if (ret) { + uc->config.remote_thread_id = -1; + return ret; + } + + uc->config.src_thread = ud->psil_base + uc->tchan->id; + uc->config.dst_thread = uc->config.remote_thread_id; + uc->config.dst_thread |= K3_PSIL_DST_THREAD_ID_OFFSET; + + irq_ring_idx = uc->tchan->id + oes->bcdma_tchan_ring; + irq_udma_idx = uc->tchan->id + oes->bcdma_tchan_data; + + ret = bcdma_tisci_tx_channel_config(uc); + break; + case DMA_DEV_TO_MEM: + /* Slave transfer synchronized - dev to mem (RX) trasnfer */ + dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__, + uc->id); + + ret = udma_alloc_rx_resources(uc); + if (ret) { + uc->config.remote_thread_id = -1; + return ret; + } + + uc->config.src_thread = uc->config.remote_thread_id; + uc->config.dst_thread = (ud->psil_base + uc->rchan->id) | + K3_PSIL_DST_THREAD_ID_OFFSET; + + irq_ring_idx = uc->rchan->id + oes->bcdma_rchan_ring; + irq_udma_idx = uc->rchan->id + oes->bcdma_rchan_data; + + ret = bcdma_tisci_rx_channel_config(uc); + break; + default: + /* Can not happen */ + dev_err(uc->ud->dev, "%s: chan%d invalid direction (%u)\n", + __func__, uc->id, uc->config.dir); + return -EINVAL; + } + + /* check if the channel configuration was successful */ + if (ret) + goto err_res_free; + + if (udma_is_chan_running(uc)) { + dev_warn(ud->dev, "chan%d: is running!\n", uc->id); + udma_reset_chan(uc, false); + if (udma_is_chan_running(uc)) { + dev_err(ud->dev, "chan%d: won't stop!\n", uc->id); + ret = -EBUSY; + goto err_res_free; + } + } + + uc->dma_dev = dmaengine_get_dma_device(chan); + if (uc->config.dir == DMA_MEM_TO_MEM && !uc->config.tr_trigger_type) { + uc->config.hdesc_size = cppi5_trdesc_calc_size( + sizeof(struct cppi5_tr_type15_t), 2); + + uc->hdesc_pool = dma_pool_create(uc->name, ud->ddev.dev, + uc->config.hdesc_size, + ud->desc_align, + 0); + if (!uc->hdesc_pool) { + dev_err(ud->ddev.dev, + "Descriptor pool allocation failed\n"); + uc->use_dma_pool = false; + return -ENOMEM; + } + + uc->use_dma_pool = true; + } else if (uc->config.dir != DMA_MEM_TO_MEM) { + /* PSI-L pairing */ + ret = navss_psil_pair(ud, uc->config.src_thread, + uc->config.dst_thread); + if (ret) { + dev_err(ud->dev, + "PSI-L pairing failed: 0x%04x -> 0x%04x\n", + uc->config.src_thread, uc->config.dst_thread); + goto err_res_free; + } + + uc->psil_paired = true; + } + + uc->irq_num_ring = ti_sci_inta_msi_get_virq(ud->dev, irq_ring_idx); + if (uc->irq_num_ring <= 0) { + dev_err(ud->dev, "Failed to get ring irq (index: %u)\n", + irq_ring_idx); + ret = -EINVAL; + goto err_psi_free; + } + + ret = request_irq(uc->irq_num_ring, udma_ring_irq_handler, + IRQF_TRIGGER_HIGH, uc->name, uc); + if (ret) { + dev_err(ud->dev, "chan%d: ring irq request failed\n", uc->id); + goto err_irq_free; + } + + /* Event from BCDMA (TR events) only needed for slave channels */ + if (is_slave_direction(uc->config.dir)) { + uc->irq_num_udma = ti_sci_inta_msi_get_virq(ud->dev, + irq_udma_idx); + if (uc->irq_num_udma <= 0) { + dev_err(ud->dev, "Failed to get bcdma irq (index: %u)\n", + irq_udma_idx); + free_irq(uc->irq_num_ring, uc); + ret = -EINVAL; + goto err_irq_free; + } + + ret = request_irq(uc->irq_num_udma, udma_udma_irq_handler, 0, + uc->name, uc); + if (ret) { + dev_err(ud->dev, "chan%d: BCDMA irq request failed\n", + uc->id); + free_irq(uc->irq_num_ring, uc); + goto err_irq_free; + } + } else { + uc->irq_num_udma = 0; + } + + udma_reset_rings(uc); + + INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work, + udma_check_tx_completion); + return 0; + +err_irq_free: + uc->irq_num_ring = 0; + uc->irq_num_udma = 0; +err_psi_free: + if (uc->psil_paired) + navss_psil_unpair(ud, uc->config.src_thread, + uc->config.dst_thread); + uc->psil_paired = false; +err_res_free: + bcdma_free_bchan_resources(uc); + udma_free_tx_resources(uc); + udma_free_rx_resources(uc); + + udma_reset_uchan(uc); + + if (uc->use_dma_pool) { + dma_pool_destroy(uc->hdesc_pool); + uc->use_dma_pool = false; + } + + return ret; +} + +static int bcdma_router_config(struct dma_chan *chan) +{ + struct k3_event_route_data *router_data = chan->route_data; + struct udma_chan *uc = to_udma_chan(chan); + u32 trigger_event; + + if (!uc->bchan) + return -EINVAL; + + if (uc->config.tr_trigger_type != 1 && uc->config.tr_trigger_type != 2) + return -EINVAL; + + trigger_event = uc->ud->soc_data->bcdma_trigger_event_offset; + trigger_event += (uc->bchan->id * 2) + uc->config.tr_trigger_type - 1; + + return router_data->set_event(router_data->priv, trigger_event); +} + +static int pktdma_alloc_chan_resources(struct dma_chan *chan) +{ + struct udma_chan *uc = to_udma_chan(chan); + struct udma_dev *ud = to_udma_dev(chan->device); + const struct udma_oes_offsets *oes = &ud->soc_data->oes; + u32 irq_ring_idx; + int ret; + + /* + * Make sure that the completion is in a known state: + * No teardown, the channel is idle + */ + reinit_completion(&uc->teardown_completed); + complete_all(&uc->teardown_completed); + uc->state = UDMA_CHAN_IS_IDLE; + + switch (uc->config.dir) { + case DMA_MEM_TO_DEV: + /* Slave transfer synchronized - mem to dev (TX) trasnfer */ + dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__, + uc->id); + + ret = udma_alloc_tx_resources(uc); + if (ret) { + uc->config.remote_thread_id = -1; + return ret; + } + + uc->config.src_thread = ud->psil_base + uc->tchan->id; + uc->config.dst_thread = uc->config.remote_thread_id; + uc->config.dst_thread |= K3_PSIL_DST_THREAD_ID_OFFSET; + + irq_ring_idx = uc->tchan->tflow_id + oes->pktdma_tchan_flow; + + ret = pktdma_tisci_tx_channel_config(uc); + break; + case DMA_DEV_TO_MEM: + /* Slave transfer synchronized - dev to mem (RX) trasnfer */ + dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__, + uc->id); + + ret = udma_alloc_rx_resources(uc); + if (ret) { + uc->config.remote_thread_id = -1; + return ret; + } + + uc->config.src_thread = uc->config.remote_thread_id; + uc->config.dst_thread = (ud->psil_base + uc->rchan->id) | + K3_PSIL_DST_THREAD_ID_OFFSET; + + irq_ring_idx = uc->rflow->id + oes->pktdma_rchan_flow; + + ret = pktdma_tisci_rx_channel_config(uc); + break; + default: + /* Can not happen */ + dev_err(uc->ud->dev, "%s: chan%d invalid direction (%u)\n", + __func__, uc->id, uc->config.dir); + return -EINVAL; + } + + /* check if the channel configuration was successful */ + if (ret) + goto err_res_free; + + if (udma_is_chan_running(uc)) { + dev_warn(ud->dev, "chan%d: is running!\n", uc->id); + udma_reset_chan(uc, false); + if (udma_is_chan_running(uc)) { + dev_err(ud->dev, "chan%d: won't stop!\n", uc->id); + ret = -EBUSY; + goto err_res_free; + } + } + + uc->dma_dev = dmaengine_get_dma_device(chan); + uc->hdesc_pool = dma_pool_create(uc->name, uc->dma_dev, + uc->config.hdesc_size, ud->desc_align, + 0); + if (!uc->hdesc_pool) { + dev_err(ud->ddev.dev, + "Descriptor pool allocation failed\n"); + uc->use_dma_pool = false; + ret = -ENOMEM; + goto err_res_free; + } + + uc->use_dma_pool = true; + + /* PSI-L pairing */ + ret = navss_psil_pair(ud, uc->config.src_thread, uc->config.dst_thread); + if (ret) { + dev_err(ud->dev, "PSI-L pairing failed: 0x%04x -> 0x%04x\n", + uc->config.src_thread, uc->config.dst_thread); + goto err_res_free; + } + + uc->psil_paired = true; + + uc->irq_num_ring = ti_sci_inta_msi_get_virq(ud->dev, irq_ring_idx); + if (uc->irq_num_ring <= 0) { + dev_err(ud->dev, "Failed to get ring irq (index: %u)\n", + irq_ring_idx); + ret = -EINVAL; + goto err_psi_free; + } + + ret = request_irq(uc->irq_num_ring, udma_ring_irq_handler, + IRQF_TRIGGER_HIGH, uc->name, uc); + if (ret) { + dev_err(ud->dev, "chan%d: ring irq request failed\n", uc->id); + goto err_irq_free; + } + + uc->irq_num_udma = 0; + + udma_reset_rings(uc); + + INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work, + udma_check_tx_completion); + + if (uc->tchan) + dev_dbg(ud->dev, + "chan%d: tchan%d, tflow%d, Remote thread: 0x%04x\n", + uc->id, uc->tchan->id, uc->tchan->tflow_id, + uc->config.remote_thread_id); + else if (uc->rchan) + dev_dbg(ud->dev, + "chan%d: rchan%d, rflow%d, Remote thread: 0x%04x\n", + uc->id, uc->rchan->id, uc->rflow->id, + uc->config.remote_thread_id); + return 0; + +err_irq_free: + uc->irq_num_ring = 0; +err_psi_free: + navss_psil_unpair(ud, uc->config.src_thread, uc->config.dst_thread); + uc->psil_paired = false; +err_res_free: + udma_free_tx_resources(uc); + udma_free_rx_resources(uc); + + udma_reset_uchan(uc); + + dma_pool_destroy(uc->hdesc_pool); + uc->use_dma_pool = false; + + return ret; +} + static int udma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { @@ -2028,6 +2866,7 @@ size_t tr_size; int num_tr = 0; int tr_idx = 0; + u64 asel; /* estimate the number of TRs we will need */ for_each_sg(sgl, sgent, sglen, i) { @@ -2045,6 +2884,11 @@ d->sglen = sglen; + if (uc->ud->match_data->type == DMA_TYPE_UDMA) + asel = 0; + else + asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT; + tr_req = d->hwdesc[0].tr_req_base; for_each_sg(sgl, sgent, sglen, i) { dma_addr_t sg_addr = sg_dma_address(sgent); @@ -2063,6 +2907,7 @@ false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0); cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT); + sg_addr |= asel; tr_req[tr_idx].addr = sg_addr; tr_req[tr_idx].icnt0 = tr0_cnt0; tr_req[tr_idx].icnt1 = tr0_cnt1; @@ -2092,6 +2937,205 @@ return d; } +static struct udma_desc * +udma_prep_slave_sg_triggered_tr(struct udma_chan *uc, struct scatterlist *sgl, + unsigned int sglen, + enum dma_transfer_direction dir, + unsigned long tx_flags, void *context) +{ + struct scatterlist *sgent; + struct cppi5_tr_type15_t *tr_req = NULL; + enum dma_slave_buswidth dev_width; + u16 tr_cnt0, tr_cnt1; + dma_addr_t dev_addr; + struct udma_desc *d; + unsigned int i; + size_t tr_size, sg_len; + int num_tr = 0; + int tr_idx = 0; + u32 burst, trigger_size, port_window; + u64 asel; + + if (dir == DMA_DEV_TO_MEM) { + dev_addr = uc->cfg.src_addr; + dev_width = uc->cfg.src_addr_width; + burst = uc->cfg.src_maxburst; + port_window = uc->cfg.src_port_window_size; + } else if (dir == DMA_MEM_TO_DEV) { + dev_addr = uc->cfg.dst_addr; + dev_width = uc->cfg.dst_addr_width; + burst = uc->cfg.dst_maxburst; + port_window = uc->cfg.dst_port_window_size; + } else { + dev_err(uc->ud->dev, "%s: bad direction?\n", __func__); + return NULL; + } + + if (!burst) + burst = 1; + + if (port_window) { + if (port_window != burst) { + dev_err(uc->ud->dev, + "The burst must be equal to port_window\n"); + return NULL; + } + + tr_cnt0 = dev_width * port_window; + tr_cnt1 = 1; + } else { + tr_cnt0 = dev_width; + tr_cnt1 = burst; + } + trigger_size = tr_cnt0 * tr_cnt1; + + /* estimate the number of TRs we will need */ + for_each_sg(sgl, sgent, sglen, i) { + sg_len = sg_dma_len(sgent); + + if (sg_len % trigger_size) { + dev_err(uc->ud->dev, + "Not aligned SG entry (%zu for %u)\n", sg_len, + trigger_size); + return NULL; + } + + if (sg_len / trigger_size < SZ_64K) + num_tr++; + else + num_tr += 2; + } + + /* Now allocate and setup the descriptor. */ + tr_size = sizeof(struct cppi5_tr_type15_t); + d = udma_alloc_tr_desc(uc, tr_size, num_tr, dir); + if (!d) + return NULL; + + d->sglen = sglen; + + if (uc->ud->match_data->type == DMA_TYPE_UDMA) { + asel = 0; + } else { + asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT; + dev_addr |= asel; + } + + tr_req = d->hwdesc[0].tr_req_base; + for_each_sg(sgl, sgent, sglen, i) { + u16 tr0_cnt2, tr0_cnt3, tr1_cnt2; + dma_addr_t sg_addr = sg_dma_address(sgent); + + sg_len = sg_dma_len(sgent); + num_tr = udma_get_tr_counters(sg_len / trigger_size, 0, + &tr0_cnt2, &tr0_cnt3, &tr1_cnt2); + if (num_tr < 0) { + dev_err(uc->ud->dev, "size %zu is not supported\n", + sg_len); + udma_free_hwdesc(uc, d); + kfree(d); + return NULL; + } + + cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE15, false, + true, CPPI5_TR_EVENT_SIZE_COMPLETION, 0); + cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT); + cppi5_tr_set_trigger(&tr_req[tr_idx].flags, + uc->config.tr_trigger_type, + CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC, 0, 0); + + sg_addr |= asel; + if (dir == DMA_DEV_TO_MEM) { + tr_req[tr_idx].addr = dev_addr; + tr_req[tr_idx].icnt0 = tr_cnt0; + tr_req[tr_idx].icnt1 = tr_cnt1; + tr_req[tr_idx].icnt2 = tr0_cnt2; + tr_req[tr_idx].icnt3 = tr0_cnt3; + tr_req[tr_idx].dim1 = (-1) * tr_cnt0; + + tr_req[tr_idx].daddr = sg_addr; + tr_req[tr_idx].dicnt0 = tr_cnt0; + tr_req[tr_idx].dicnt1 = tr_cnt1; + tr_req[tr_idx].dicnt2 = tr0_cnt2; + tr_req[tr_idx].dicnt3 = tr0_cnt3; + tr_req[tr_idx].ddim1 = tr_cnt0; + tr_req[tr_idx].ddim2 = trigger_size; + tr_req[tr_idx].ddim3 = trigger_size * tr0_cnt2; + } else { + tr_req[tr_idx].addr = sg_addr; + tr_req[tr_idx].icnt0 = tr_cnt0; + tr_req[tr_idx].icnt1 = tr_cnt1; + tr_req[tr_idx].icnt2 = tr0_cnt2; + tr_req[tr_idx].icnt3 = tr0_cnt3; + tr_req[tr_idx].dim1 = tr_cnt0; + tr_req[tr_idx].dim2 = trigger_size; + tr_req[tr_idx].dim3 = trigger_size * tr0_cnt2; + + tr_req[tr_idx].daddr = dev_addr; + tr_req[tr_idx].dicnt0 = tr_cnt0; + tr_req[tr_idx].dicnt1 = tr_cnt1; + tr_req[tr_idx].dicnt2 = tr0_cnt2; + tr_req[tr_idx].dicnt3 = tr0_cnt3; + tr_req[tr_idx].ddim1 = (-1) * tr_cnt0; + } + + tr_idx++; + + if (num_tr == 2) { + cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE15, + false, true, + CPPI5_TR_EVENT_SIZE_COMPLETION, 0); + cppi5_tr_csf_set(&tr_req[tr_idx].flags, + CPPI5_TR_CSF_SUPR_EVT); + cppi5_tr_set_trigger(&tr_req[tr_idx].flags, + uc->config.tr_trigger_type, + CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC, + 0, 0); + + sg_addr += trigger_size * tr0_cnt2 * tr0_cnt3; + if (dir == DMA_DEV_TO_MEM) { + tr_req[tr_idx].addr = dev_addr; + tr_req[tr_idx].icnt0 = tr_cnt0; + tr_req[tr_idx].icnt1 = tr_cnt1; + tr_req[tr_idx].icnt2 = tr1_cnt2; + tr_req[tr_idx].icnt3 = 1; + tr_req[tr_idx].dim1 = (-1) * tr_cnt0; + + tr_req[tr_idx].daddr = sg_addr; + tr_req[tr_idx].dicnt0 = tr_cnt0; + tr_req[tr_idx].dicnt1 = tr_cnt1; + tr_req[tr_idx].dicnt2 = tr1_cnt2; + tr_req[tr_idx].dicnt3 = 1; + tr_req[tr_idx].ddim1 = tr_cnt0; + tr_req[tr_idx].ddim2 = trigger_size; + } else { + tr_req[tr_idx].addr = sg_addr; + tr_req[tr_idx].icnt0 = tr_cnt0; + tr_req[tr_idx].icnt1 = tr_cnt1; + tr_req[tr_idx].icnt2 = tr1_cnt2; + tr_req[tr_idx].icnt3 = 1; + tr_req[tr_idx].dim1 = tr_cnt0; + tr_req[tr_idx].dim2 = trigger_size; + + tr_req[tr_idx].daddr = dev_addr; + tr_req[tr_idx].dicnt0 = tr_cnt0; + tr_req[tr_idx].dicnt1 = tr_cnt1; + tr_req[tr_idx].dicnt2 = tr1_cnt2; + tr_req[tr_idx].dicnt3 = 1; + tr_req[tr_idx].ddim1 = (-1) * tr_cnt0; + } + tr_idx++; + } + + d->residue += sg_len; + } + + cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags, + CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP); + + return d; +} + static int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d, enum dma_slave_buswidth dev_width, u16 elcnt) @@ -2156,6 +3200,7 @@ struct udma_desc *d; u32 ring_id; unsigned int i; + u64 asel; d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT); if (!d) @@ -2169,6 +3214,11 @@ else ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring); + if (uc->ud->match_data->type == DMA_TYPE_UDMA) + asel = 0; + else + asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT; + for_each_sg(sgl, sgent, sglen, i) { struct udma_hwdesc *hwdesc = &d->hwdesc[i]; dma_addr_t sg_addr = sg_dma_address(sgent); @@ -2203,14 +3253,16 @@ } /* attach the sg buffer to the descriptor */ + sg_addr |= asel; cppi5_hdesc_attach_buf(desc, sg_addr, sg_len, sg_addr, sg_len); /* Attach link as host buffer descriptor */ if (h_desc) cppi5_hdesc_link_hbdesc(h_desc, - hwdesc->cppi5_desc_paddr); + hwdesc->cppi5_desc_paddr | asel); - if (dir == DMA_MEM_TO_DEV) + if (uc->ud->match_data->type == DMA_TYPE_PKTDMA || + dir == DMA_MEM_TO_DEV) h_desc = desc; } @@ -2333,7 +3385,8 @@ struct udma_desc *d; u32 burst; - if (dir != uc->config.dir) { + if (dir != uc->config.dir && + (uc->config.dir == DMA_MEM_TO_MEM && !uc->config.tr_trigger_type)) { dev_err(chan->device->dev, "%s: chan%d is for %s, not supporting %s\n", __func__, uc->id, @@ -2359,9 +3412,12 @@ if (uc->config.pkt_mode) d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags, context); - else + else if (is_slave_direction(uc->config.dir)) d = udma_prep_slave_sg_tr(uc, sgl, sglen, dir, tx_flags, context); + else + d = udma_prep_slave_sg_triggered_tr(uc, sgl, sglen, dir, + tx_flags, context); if (!d) return NULL; @@ -2415,7 +3471,12 @@ return NULL; tr_req = d->hwdesc[0].tr_req_base; - period_addr = buf_addr; + if (uc->ud->match_data->type == DMA_TYPE_UDMA) + period_addr = buf_addr; + else + period_addr = buf_addr | + ((u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT); + for (i = 0; i < periods; i++) { int tr_idx = i * num_tr; @@ -2480,6 +3541,9 @@ else ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring); + if (uc->ud->match_data->type != DMA_TYPE_UDMA) + buf_addr |= (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT; + for (i = 0; i < periods; i++) { struct udma_hwdesc *hwdesc = &d->hwdesc[i]; dma_addr_t period_addr = buf_addr + (period_len * i); @@ -2621,6 +3685,11 @@ d->tr_idx = 0; d->residue = len; + if (uc->ud->match_data->type != DMA_TYPE_UDMA) { + src |= (u64)uc->ud->asel << K3_ADDRESS_ASEL_SHIFT; + dest |= (u64)uc->ud->asel << K3_ADDRESS_ASEL_SHIFT; + } + tr_req = d->hwdesc[0].tr_req_base; cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true, @@ -2978,6 +4047,7 @@ vchan_free_chan_resources(&uc->vc); tasklet_kill(&uc->vc.task); + bcdma_free_bchan_resources(uc); udma_free_tx_resources(uc); udma_free_rx_resources(uc); udma_reset_uchan(uc); @@ -2989,10 +4059,14 @@ } static struct platform_driver udma_driver; +static struct platform_driver bcdma_driver; +static struct platform_driver pktdma_driver; struct udma_filter_param { int remote_thread_id; u32 atype; + u32 asel; + u32 tr_trigger_type; }; static bool udma_dma_filter_fn(struct dma_chan *chan, void *param) @@ -3003,7 +4077,9 @@ struct udma_chan *uc; struct udma_dev *ud; - if (chan->device->dev->driver != &udma_driver.driver) + if (chan->device->dev->driver != &udma_driver.driver && + chan->device->dev->driver != &bcdma_driver.driver && + chan->device->dev->driver != &pktdma_driver.driver) return false; uc = to_udma_chan(chan); @@ -3017,13 +4093,25 @@ return false; } + if (filter_param->asel > 15) { + dev_err(ud->dev, "Invalid channel asel: %u\n", + filter_param->asel); + return false; + } + ucc->remote_thread_id = filter_param->remote_thread_id; ucc->atype = filter_param->atype; + ucc->asel = filter_param->asel; + ucc->tr_trigger_type = filter_param->tr_trigger_type; - if (ucc->remote_thread_id & K3_PSIL_DST_THREAD_ID_OFFSET) + if (ucc->tr_trigger_type) { + ucc->dir = DMA_MEM_TO_MEM; + goto triggered_bchan; + } else if (ucc->remote_thread_id & K3_PSIL_DST_THREAD_ID_OFFSET) { ucc->dir = DMA_MEM_TO_DEV; - else + } else { ucc->dir = DMA_DEV_TO_MEM; + } ep_config = psil_get_ep_config(ucc->remote_thread_id); if (IS_ERR(ep_config)) { @@ -3032,6 +4120,19 @@ ucc->dir = DMA_MEM_TO_MEM; ucc->remote_thread_id = -1; ucc->atype = 0; + ucc->asel = 0; + return false; + } + + if (ud->match_data->type == DMA_TYPE_BCDMA && + ep_config->pkt_mode) { + dev_err(ud->dev, + "Only TR mode is supported (psi-l thread 0x%04x)\n", + ucc->remote_thread_id); + ucc->dir = DMA_MEM_TO_MEM; + ucc->remote_thread_id = -1; + ucc->atype = 0; + ucc->asel = 0; return false; } @@ -3040,6 +4141,15 @@ ucc->notdpkt = ep_config->notdpkt; ucc->ep_type = ep_config->ep_type; + if (ud->match_data->type == DMA_TYPE_PKTDMA && + ep_config->mapped_channel_id >= 0) { + ucc->mapped_channel_id = ep_config->mapped_channel_id; + ucc->default_flow_id = ep_config->default_flow_id; + } else { + ucc->mapped_channel_id = -1; + ucc->default_flow_id = -1; + } + if (ucc->ep_type != PSIL_EP_NATIVE) { const struct udma_match_data *match_data = ud->match_data; @@ -3063,6 +4173,13 @@ ucc->remote_thread_id, dmaengine_get_direction_text(ucc->dir)); return true; + +triggered_bchan: + dev_dbg(ud->dev, "chan%d: triggered channel (type: %u)\n", uc->id, + ucc->tr_trigger_type); + + return true; + } static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec, @@ -3073,14 +4190,33 @@ struct udma_filter_param filter_param; struct dma_chan *chan; - if (dma_spec->args_count != 1 && dma_spec->args_count != 2) - return NULL; + if (ud->match_data->type == DMA_TYPE_BCDMA) { + if (dma_spec->args_count != 3) + return NULL; - filter_param.remote_thread_id = dma_spec->args[0]; - if (dma_spec->args_count == 2) - filter_param.atype = dma_spec->args[1]; - else + filter_param.tr_trigger_type = dma_spec->args[0]; + filter_param.remote_thread_id = dma_spec->args[1]; + filter_param.asel = dma_spec->args[2]; filter_param.atype = 0; + } else { + if (dma_spec->args_count != 1 && dma_spec->args_count != 2) + return NULL; + + filter_param.remote_thread_id = dma_spec->args[0]; + filter_param.tr_trigger_type = 0; + if (dma_spec->args_count == 2) { + if (ud->match_data->type == DMA_TYPE_UDMA) { + filter_param.atype = dma_spec->args[1]; + filter_param.asel = 0; + } else { + filter_param.atype = 0; + filter_param.asel = dma_spec->args[1]; + } + } else { + filter_param.atype = 0; + filter_param.asel = 0; + } + } chan = __dma_request_channel(&mask, udma_dma_filter_fn, &filter_param, ofdma->of_node); @@ -3093,29 +4229,79 @@ } static struct udma_match_data am654_main_data = { + .type = DMA_TYPE_UDMA, .psil_base = 0x1000, .enable_memcpy_support = true, .statictr_z_mask = GENMASK(11, 0), + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* H Channels */ + 0, /* No UH Channels */ + }, }; static struct udma_match_data am654_mcu_data = { + .type = DMA_TYPE_UDMA, .psil_base = 0x6000, .enable_memcpy_support = false, .statictr_z_mask = GENMASK(11, 0), + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* H Channels */ + 0, /* No UH Channels */ + }, }; static struct udma_match_data j721e_main_data = { + .type = DMA_TYPE_UDMA, .psil_base = 0x1000, .enable_memcpy_support = true, - .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST, + .flags = UDMA_FLAGS_J7_CLASS, .statictr_z_mask = GENMASK(23, 0), + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES, /* H Channels */ + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES, /* UH Channels */ + }, }; static struct udma_match_data j721e_mcu_data = { + .type = DMA_TYPE_UDMA, .psil_base = 0x6000, .enable_memcpy_support = false, /* MEM_TO_MEM is slow via MCU UDMA */ - .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST, + .flags = UDMA_FLAGS_J7_CLASS, + .statictr_z_mask = GENMASK(23, 0), + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES, /* H Channels */ + 0, /* No UH Channels */ + }, +}; + +static struct udma_match_data am64_bcdma_data = { + .type = DMA_TYPE_BCDMA, + .psil_base = 0x2000, /* for tchan and rchan, not applicable to bchan */ + .enable_memcpy_support = true, /* Supported via bchan */ + .flags = UDMA_FLAGS_J7_CLASS, + .statictr_z_mask = GENMASK(23, 0), + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + 0, /* No H Channels */ + 0, /* No UH Channels */ + }, +}; + +static struct udma_match_data am64_pktdma_data = { + .type = DMA_TYPE_PKTDMA, + .psil_base = 0x1000, + .enable_memcpy_support = false, /* PKTDMA does not support MEM_TO_MEM */ + .flags = UDMA_FLAGS_J7_CLASS, .statictr_z_mask = GENMASK(23, 0), + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + 0, /* No H Channels */ + 0, /* No UH Channels */ + }, }; static const struct of_device_id udma_of_match[] = { @@ -3136,30 +4322,107 @@ { /* Sentinel */ }, }; +static const struct of_device_id bcdma_of_match[] = { + { + .compatible = "ti,am64-dmss-bcdma", + .data = &am64_bcdma_data, + }, + { /* Sentinel */ }, +}; + +static const struct of_device_id pktdma_of_match[] = { + { + .compatible = "ti,am64-dmss-pktdma", + .data = &am64_pktdma_data, + }, + { /* Sentinel */ }, +}; + static struct udma_soc_data am654_soc_data = { - .rchan_oes_offset = 0x200, + .oes = { + .udma_rchan = 0x200, + }, }; static struct udma_soc_data j721e_soc_data = { - .rchan_oes_offset = 0x400, + .oes = { + .udma_rchan = 0x400, + }, }; static struct udma_soc_data j7200_soc_data = { - .rchan_oes_offset = 0x80, + .oes = { + .udma_rchan = 0x80, + }, +}; + +static struct udma_soc_data am64_soc_data = { + .oes = { + .bcdma_bchan_data = 0x2200, + .bcdma_bchan_ring = 0x2400, + .bcdma_tchan_data = 0x2800, + .bcdma_tchan_ring = 0x2a00, + .bcdma_rchan_data = 0x2e00, + .bcdma_rchan_ring = 0x3000, + .pktdma_tchan_flow = 0x1200, + .pktdma_rchan_flow = 0x1600, + }, + .bcdma_trigger_event_offset = 0xc400, }; static const struct soc_device_attribute k3_soc_devices[] = { { .family = "AM65X", .data = &am654_soc_data }, { .family = "J721E", .data = &j721e_soc_data }, { .family = "J7200", .data = &j7200_soc_data }, + { .family = "AM64X", .data = &am64_soc_data }, + { .family = "J721S2", .data = &j721e_soc_data}, { /* sentinel */ } }; static int udma_get_mmrs(struct platform_device *pdev, struct udma_dev *ud) { + u32 cap2, cap3, cap4; int i; - for (i = 0; i < MMR_LAST; i++) { + ud->mmrs[MMR_GCFG] = devm_platform_ioremap_resource_byname(pdev, mmr_names[MMR_GCFG]); + if (IS_ERR(ud->mmrs[MMR_GCFG])) + return PTR_ERR(ud->mmrs[MMR_GCFG]); + + cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28); + cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c); + + switch (ud->match_data->type) { + case DMA_TYPE_UDMA: + ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3); + ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2); + ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2); + ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2); + break; + case DMA_TYPE_BCDMA: + ud->bchan_cnt = BCDMA_CAP2_BCHAN_CNT(cap2); + ud->tchan_cnt = BCDMA_CAP2_TCHAN_CNT(cap2); + ud->rchan_cnt = BCDMA_CAP2_RCHAN_CNT(cap2); + ud->rflow_cnt = ud->rchan_cnt; + break; + case DMA_TYPE_PKTDMA: + cap4 = udma_read(ud->mmrs[MMR_GCFG], 0x30); + ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2); + ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2); + ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3); + ud->tflow_cnt = PKTDMA_CAP4_TFLOW_CNT(cap4); + break; + default: + return -EINVAL; + } + + for (i = 1; i < MMR_LAST; i++) { + if (i == MMR_BCHANRT && ud->bchan_cnt == 0) + continue; + if (i == MMR_TCHANRT && ud->tchan_cnt == 0) + continue; + if (i == MMR_RCHANRT && ud->rchan_cnt == 0) + continue; + ud->mmrs[i] = devm_platform_ioremap_resource_byname(pdev, mmr_names[i]); if (IS_ERR(ud->mmrs[i])) return PTR_ERR(ud->mmrs[i]); @@ -3168,47 +4431,58 @@ return 0; } +static void udma_mark_resource_ranges(struct udma_dev *ud, unsigned long *map, + struct ti_sci_resource_desc *rm_desc, + char *name) +{ + bitmap_clear(map, rm_desc->start, rm_desc->num); + bitmap_clear(map, rm_desc->start_sec, rm_desc->num_sec); + dev_dbg(ud->dev, "ti_sci resource range for %s: %d:%d | %d:%d\n", name, + rm_desc->start, rm_desc->num, rm_desc->start_sec, + rm_desc->num_sec); +} + +static const char * const range_names[] = { + [RM_RANGE_BCHAN] = "ti,sci-rm-range-bchan", + [RM_RANGE_TCHAN] = "ti,sci-rm-range-tchan", + [RM_RANGE_RCHAN] = "ti,sci-rm-range-rchan", + [RM_RANGE_RFLOW] = "ti,sci-rm-range-rflow", + [RM_RANGE_TFLOW] = "ti,sci-rm-range-tflow", +}; + static int udma_setup_resources(struct udma_dev *ud) { + int ret, i, j; struct device *dev = ud->dev; - int ch_count, ret, i, j; - u32 cap2, cap3; - struct ti_sci_resource_desc *rm_desc; struct ti_sci_resource *rm_res, irq_res; struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; - static const char * const range_names[] = { "ti,sci-rm-range-tchan", - "ti,sci-rm-range-rchan", - "ti,sci-rm-range-rflow" }; - - cap2 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(2)); - cap3 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(3)); - - ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3); - ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2); - ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2); - ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2); - ch_count = ud->tchan_cnt + ud->rchan_cnt; + u32 cap3; /* Set up the throughput level start indexes */ + cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c); if (of_device_is_compatible(dev->of_node, "ti,am654-navss-main-udmap")) { - ud->tpl_levels = 2; - ud->tpl_start_idx[0] = 8; + ud->tchan_tpl.levels = 2; + ud->tchan_tpl.start_idx[0] = 8; } else if (of_device_is_compatible(dev->of_node, "ti,am654-navss-mcu-udmap")) { - ud->tpl_levels = 2; - ud->tpl_start_idx[0] = 2; + ud->tchan_tpl.levels = 2; + ud->tchan_tpl.start_idx[0] = 2; } else if (UDMA_CAP3_UCHAN_CNT(cap3)) { - ud->tpl_levels = 3; - ud->tpl_start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3); - ud->tpl_start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); + ud->tchan_tpl.levels = 3; + ud->tchan_tpl.start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3); + ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); } else if (UDMA_CAP3_HCHAN_CNT(cap3)) { - ud->tpl_levels = 2; - ud->tpl_start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); + ud->tchan_tpl.levels = 2; + ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); } else { - ud->tpl_levels = 1; + ud->tchan_tpl.levels = 1; } + ud->rchan_tpl.levels = ud->tchan_tpl.levels; + ud->rchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0]; + ud->rchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1]; + ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), sizeof(unsigned long), GFP_KERNEL); ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans), @@ -3246,11 +4520,15 @@ bitmap_set(ud->rflow_gp_map, 0, ud->rflow_cnt); /* Get resource ranges from tisci */ - for (i = 0; i < RM_RANGE_LAST; i++) + for (i = 0; i < RM_RANGE_LAST; i++) { + if (i == RM_RANGE_BCHAN || i == RM_RANGE_TFLOW) + continue; + tisci_rm->rm_ranges[i] = devm_ti_sci_get_of_resource(tisci_rm->tisci, dev, tisci_rm->tisci_dev_id, (char *)range_names[i]); + } /* tchan ranges */ rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN]; @@ -3258,13 +4536,9 @@ bitmap_zero(ud->tchan_map, ud->tchan_cnt); } else { bitmap_fill(ud->tchan_map, ud->tchan_cnt); - for (i = 0; i < rm_res->sets; i++) { - rm_desc = &rm_res->desc[i]; - bitmap_clear(ud->tchan_map, rm_desc->start, - rm_desc->num); - dev_dbg(dev, "ti-sci-res: tchan: %d:%d\n", - rm_desc->start, rm_desc->num); - } + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->tchan_map, + &rm_res->desc[i], "tchan"); } irq_res.sets = rm_res->sets; @@ -3274,13 +4548,9 @@ bitmap_zero(ud->rchan_map, ud->rchan_cnt); } else { bitmap_fill(ud->rchan_map, ud->rchan_cnt); - for (i = 0; i < rm_res->sets; i++) { - rm_desc = &rm_res->desc[i]; - bitmap_clear(ud->rchan_map, rm_desc->start, - rm_desc->num); - dev_dbg(dev, "ti-sci-res: rchan: %d:%d\n", - rm_desc->start, rm_desc->num); - } + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->rchan_map, + &rm_res->desc[i], "rchan"); } irq_res.sets += rm_res->sets; @@ -3289,12 +4559,21 @@ for (i = 0; i < rm_res->sets; i++) { irq_res.desc[i].start = rm_res->desc[i].start; irq_res.desc[i].num = rm_res->desc[i].num; + irq_res.desc[i].start_sec = rm_res->desc[i].start_sec; + irq_res.desc[i].num_sec = rm_res->desc[i].num_sec; } rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN]; for (j = 0; j < rm_res->sets; j++, i++) { - irq_res.desc[i].start = rm_res->desc[j].start + - ud->soc_data->rchan_oes_offset; - irq_res.desc[i].num = rm_res->desc[j].num; + if (rm_res->desc[j].num) { + irq_res.desc[i].start = rm_res->desc[j].start + + ud->soc_data->oes.udma_rchan; + irq_res.desc[i].num = rm_res->desc[j].num; + } + if (rm_res->desc[j].num_sec) { + irq_res.desc[i].start_sec = rm_res->desc[j].start_sec + + ud->soc_data->oes.udma_rchan; + irq_res.desc[i].num_sec = rm_res->desc[j].num_sec; + } } ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res); kfree(irq_res.desc); @@ -3310,15 +4589,344 @@ bitmap_clear(ud->rflow_gp_map, ud->rchan_cnt, ud->rflow_cnt - ud->rchan_cnt); } else { + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->rflow_gp_map, + &rm_res->desc[i], "gp-rflow"); + } + + return 0; +} + +static int bcdma_setup_resources(struct udma_dev *ud) +{ + int ret, i, j; + struct device *dev = ud->dev; + struct ti_sci_resource *rm_res, irq_res; + struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + const struct udma_oes_offsets *oes = &ud->soc_data->oes; + u32 cap; + + /* Set up the throughput level start indexes */ + cap = udma_read(ud->mmrs[MMR_GCFG], 0x2c); + if (BCDMA_CAP3_UBCHAN_CNT(cap)) { + ud->bchan_tpl.levels = 3; + ud->bchan_tpl.start_idx[1] = BCDMA_CAP3_UBCHAN_CNT(cap); + ud->bchan_tpl.start_idx[0] = BCDMA_CAP3_HBCHAN_CNT(cap); + } else if (BCDMA_CAP3_HBCHAN_CNT(cap)) { + ud->bchan_tpl.levels = 2; + ud->bchan_tpl.start_idx[0] = BCDMA_CAP3_HBCHAN_CNT(cap); + } else { + ud->bchan_tpl.levels = 1; + } + + cap = udma_read(ud->mmrs[MMR_GCFG], 0x30); + if (BCDMA_CAP4_URCHAN_CNT(cap)) { + ud->rchan_tpl.levels = 3; + ud->rchan_tpl.start_idx[1] = BCDMA_CAP4_URCHAN_CNT(cap); + ud->rchan_tpl.start_idx[0] = BCDMA_CAP4_HRCHAN_CNT(cap); + } else if (BCDMA_CAP4_HRCHAN_CNT(cap)) { + ud->rchan_tpl.levels = 2; + ud->rchan_tpl.start_idx[0] = BCDMA_CAP4_HRCHAN_CNT(cap); + } else { + ud->rchan_tpl.levels = 1; + } + + if (BCDMA_CAP4_UTCHAN_CNT(cap)) { + ud->tchan_tpl.levels = 3; + ud->tchan_tpl.start_idx[1] = BCDMA_CAP4_UTCHAN_CNT(cap); + ud->tchan_tpl.start_idx[0] = BCDMA_CAP4_HTCHAN_CNT(cap); + } else if (BCDMA_CAP4_HTCHAN_CNT(cap)) { + ud->tchan_tpl.levels = 2; + ud->tchan_tpl.start_idx[0] = BCDMA_CAP4_HTCHAN_CNT(cap); + } else { + ud->tchan_tpl.levels = 1; + } + + ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt), + sizeof(unsigned long), GFP_KERNEL); + ud->bchans = devm_kcalloc(dev, ud->bchan_cnt, sizeof(*ud->bchans), + GFP_KERNEL); + ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), + sizeof(unsigned long), GFP_KERNEL); + ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans), + GFP_KERNEL); + ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt), + sizeof(unsigned long), GFP_KERNEL); + ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans), + GFP_KERNEL); + /* BCDMA do not really have flows, but the driver expect it */ + ud->rflow_in_use = devm_kcalloc(dev, BITS_TO_LONGS(ud->rchan_cnt), + sizeof(unsigned long), + GFP_KERNEL); + ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows), + GFP_KERNEL); + + if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map || + !ud->rflow_in_use || !ud->bchans || !ud->tchans || !ud->rchans || + !ud->rflows) + return -ENOMEM; + + /* Get resource ranges from tisci */ + for (i = 0; i < RM_RANGE_LAST; i++) { + if (i == RM_RANGE_RFLOW || i == RM_RANGE_TFLOW) + continue; + if (i == RM_RANGE_BCHAN && ud->bchan_cnt == 0) + continue; + if (i == RM_RANGE_TCHAN && ud->tchan_cnt == 0) + continue; + if (i == RM_RANGE_RCHAN && ud->rchan_cnt == 0) + continue; + + tisci_rm->rm_ranges[i] = + devm_ti_sci_get_of_resource(tisci_rm->tisci, dev, + tisci_rm->tisci_dev_id, + (char *)range_names[i]); + } + + irq_res.sets = 0; + + /* bchan ranges */ + if (ud->bchan_cnt) { + rm_res = tisci_rm->rm_ranges[RM_RANGE_BCHAN]; + if (IS_ERR(rm_res)) { + bitmap_zero(ud->bchan_map, ud->bchan_cnt); + } else { + bitmap_fill(ud->bchan_map, ud->bchan_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->bchan_map, + &rm_res->desc[i], + "bchan"); + } + irq_res.sets += rm_res->sets; + } + + /* tchan ranges */ + if (ud->tchan_cnt) { + rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN]; + if (IS_ERR(rm_res)) { + bitmap_zero(ud->tchan_map, ud->tchan_cnt); + } else { + bitmap_fill(ud->tchan_map, ud->tchan_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->tchan_map, + &rm_res->desc[i], + "tchan"); + } + irq_res.sets += rm_res->sets * 2; + } + + /* rchan ranges */ + if (ud->rchan_cnt) { + rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN]; + if (IS_ERR(rm_res)) { + bitmap_zero(ud->rchan_map, ud->rchan_cnt); + } else { + bitmap_fill(ud->rchan_map, ud->rchan_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->rchan_map, + &rm_res->desc[i], + "rchan"); + } + irq_res.sets += rm_res->sets * 2; + } + + irq_res.desc = kcalloc(irq_res.sets, sizeof(*irq_res.desc), GFP_KERNEL); + if (ud->bchan_cnt) { + rm_res = tisci_rm->rm_ranges[RM_RANGE_BCHAN]; for (i = 0; i < rm_res->sets; i++) { - rm_desc = &rm_res->desc[i]; - bitmap_clear(ud->rflow_gp_map, rm_desc->start, - rm_desc->num); - dev_dbg(dev, "ti-sci-res: rflow: %d:%d\n", - rm_desc->start, rm_desc->num); + irq_res.desc[i].start = rm_res->desc[i].start + + oes->bcdma_bchan_ring; + irq_res.desc[i].num = rm_res->desc[i].num; + } + } + if (ud->tchan_cnt) { + rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN]; + for (j = 0; j < rm_res->sets; j++, i += 2) { + irq_res.desc[i].start = rm_res->desc[j].start + + oes->bcdma_tchan_data; + irq_res.desc[i].num = rm_res->desc[j].num; + + irq_res.desc[i + 1].start = rm_res->desc[j].start + + oes->bcdma_tchan_ring; + irq_res.desc[i + 1].num = rm_res->desc[j].num; + } + } + if (ud->rchan_cnt) { + rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN]; + for (j = 0; j < rm_res->sets; j++, i += 2) { + irq_res.desc[i].start = rm_res->desc[j].start + + oes->bcdma_rchan_data; + irq_res.desc[i].num = rm_res->desc[j].num; + + irq_res.desc[i + 1].start = rm_res->desc[j].start + + oes->bcdma_rchan_ring; + irq_res.desc[i + 1].num = rm_res->desc[j].num; } } + ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res); + kfree(irq_res.desc); + if (ret) { + dev_err(ud->dev, "Failed to allocate MSI interrupts\n"); + return ret; + } + + return 0; +} + +static int pktdma_setup_resources(struct udma_dev *ud) +{ + int ret, i, j; + struct device *dev = ud->dev; + struct ti_sci_resource *rm_res, irq_res; + struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + const struct udma_oes_offsets *oes = &ud->soc_data->oes; + u32 cap3; + + /* Set up the throughput level start indexes */ + cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c); + if (UDMA_CAP3_UCHAN_CNT(cap3)) { + ud->tchan_tpl.levels = 3; + ud->tchan_tpl.start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3); + ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); + } else if (UDMA_CAP3_HCHAN_CNT(cap3)) { + ud->tchan_tpl.levels = 2; + ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); + } else { + ud->tchan_tpl.levels = 1; + } + + ud->rchan_tpl.levels = ud->tchan_tpl.levels; + ud->rchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0]; + ud->rchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1]; + + ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), + sizeof(unsigned long), GFP_KERNEL); + ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans), + GFP_KERNEL); + ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt), + sizeof(unsigned long), GFP_KERNEL); + ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans), + GFP_KERNEL); + ud->rflow_in_use = devm_kcalloc(dev, BITS_TO_LONGS(ud->rflow_cnt), + sizeof(unsigned long), + GFP_KERNEL); + ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows), + GFP_KERNEL); + ud->tflow_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tflow_cnt), + sizeof(unsigned long), GFP_KERNEL); + + if (!ud->tchan_map || !ud->rchan_map || !ud->tflow_map || !ud->tchans || + !ud->rchans || !ud->rflows || !ud->rflow_in_use) + return -ENOMEM; + + /* Get resource ranges from tisci */ + for (i = 0; i < RM_RANGE_LAST; i++) { + if (i == RM_RANGE_BCHAN) + continue; + + tisci_rm->rm_ranges[i] = + devm_ti_sci_get_of_resource(tisci_rm->tisci, dev, + tisci_rm->tisci_dev_id, + (char *)range_names[i]); + } + + /* tchan ranges */ + rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN]; + if (IS_ERR(rm_res)) { + bitmap_zero(ud->tchan_map, ud->tchan_cnt); + } else { + bitmap_fill(ud->tchan_map, ud->tchan_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->tchan_map, + &rm_res->desc[i], "tchan"); + } + + /* rchan ranges */ + rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN]; + if (IS_ERR(rm_res)) { + bitmap_zero(ud->rchan_map, ud->rchan_cnt); + } else { + bitmap_fill(ud->rchan_map, ud->rchan_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->rchan_map, + &rm_res->desc[i], "rchan"); + } + + /* rflow ranges */ + rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW]; + if (IS_ERR(rm_res)) { + /* all rflows are assigned exclusively to Linux */ + bitmap_zero(ud->rflow_in_use, ud->rflow_cnt); + } else { + bitmap_fill(ud->rflow_in_use, ud->rflow_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->rflow_in_use, + &rm_res->desc[i], "rflow"); + } + irq_res.sets = rm_res->sets; + + /* tflow ranges */ + rm_res = tisci_rm->rm_ranges[RM_RANGE_TFLOW]; + if (IS_ERR(rm_res)) { + /* all tflows are assigned exclusively to Linux */ + bitmap_zero(ud->tflow_map, ud->tflow_cnt); + } else { + bitmap_fill(ud->tflow_map, ud->tflow_cnt); + for (i = 0; i < rm_res->sets; i++) + udma_mark_resource_ranges(ud, ud->tflow_map, + &rm_res->desc[i], "tflow"); + } + irq_res.sets += rm_res->sets; + + irq_res.desc = kcalloc(irq_res.sets, sizeof(*irq_res.desc), GFP_KERNEL); + rm_res = tisci_rm->rm_ranges[RM_RANGE_TFLOW]; + for (i = 0; i < rm_res->sets; i++) { + irq_res.desc[i].start = rm_res->desc[i].start + + oes->pktdma_tchan_flow; + irq_res.desc[i].num = rm_res->desc[i].num; + } + rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW]; + for (j = 0; j < rm_res->sets; j++, i++) { + irq_res.desc[i].start = rm_res->desc[j].start + + oes->pktdma_rchan_flow; + irq_res.desc[i].num = rm_res->desc[j].num; + } + ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res); + kfree(irq_res.desc); + if (ret) { + dev_err(ud->dev, "Failed to allocate MSI interrupts\n"); + return ret; + } + + return 0; +} + +static int setup_resources(struct udma_dev *ud) +{ + struct device *dev = ud->dev; + int ch_count, ret; + + switch (ud->match_data->type) { + case DMA_TYPE_UDMA: + ret = udma_setup_resources(ud); + break; + case DMA_TYPE_BCDMA: + ret = bcdma_setup_resources(ud); + break; + case DMA_TYPE_PKTDMA: + ret = pktdma_setup_resources(ud); + break; + default: + return -EINVAL; + } + + if (ret) + return ret; + + ch_count = ud->bchan_cnt + ud->tchan_cnt + ud->rchan_cnt; + if (ud->bchan_cnt) + ch_count -= bitmap_weight(ud->bchan_map, ud->bchan_cnt); ch_count -= bitmap_weight(ud->tchan_map, ud->tchan_cnt); ch_count -= bitmap_weight(ud->rchan_map, ud->rchan_cnt); if (!ch_count) @@ -3329,12 +4937,40 @@ if (!ud->channels) return -ENOMEM; - dev_info(dev, "Channels: %d (tchan: %u, rchan: %u, gp-rflow: %u)\n", - ch_count, - ud->tchan_cnt - bitmap_weight(ud->tchan_map, ud->tchan_cnt), - ud->rchan_cnt - bitmap_weight(ud->rchan_map, ud->rchan_cnt), - ud->rflow_cnt - bitmap_weight(ud->rflow_gp_map, - ud->rflow_cnt)); + switch (ud->match_data->type) { + case DMA_TYPE_UDMA: + dev_info(dev, + "Channels: %d (tchan: %u, rchan: %u, gp-rflow: %u)\n", + ch_count, + ud->tchan_cnt - bitmap_weight(ud->tchan_map, + ud->tchan_cnt), + ud->rchan_cnt - bitmap_weight(ud->rchan_map, + ud->rchan_cnt), + ud->rflow_cnt - bitmap_weight(ud->rflow_gp_map, + ud->rflow_cnt)); + break; + case DMA_TYPE_BCDMA: + dev_info(dev, + "Channels: %d (bchan: %u, tchan: %u, rchan: %u)\n", + ch_count, + ud->bchan_cnt - bitmap_weight(ud->bchan_map, + ud->bchan_cnt), + ud->tchan_cnt - bitmap_weight(ud->tchan_map, + ud->tchan_cnt), + ud->rchan_cnt - bitmap_weight(ud->rchan_map, + ud->rchan_cnt)); + break; + case DMA_TYPE_PKTDMA: + dev_info(dev, + "Channels: %d (tchan: %u, rchan: %u)\n", + ch_count, + ud->tchan_cnt - bitmap_weight(ud->tchan_map, + ud->tchan_cnt), + ud->rchan_cnt - bitmap_weight(ud->rchan_map, + ud->rchan_cnt)); + default: + break; + } return ch_count; } @@ -3443,20 +5079,33 @@ seq_printf(s, " %-13s| %s", dma_chan_name(chan), chan->dbg_client_name ?: "in-use"); - seq_printf(s, " (%s, ", dmaengine_get_direction_text(uc->config.dir)); + if (ucc->tr_trigger_type) + seq_puts(s, " (triggered, "); + else + seq_printf(s, " (%s, ", + dmaengine_get_direction_text(uc->config.dir)); switch (uc->config.dir) { case DMA_MEM_TO_MEM: + if (uc->ud->match_data->type == DMA_TYPE_BCDMA) { + seq_printf(s, "bchan%d)\n", uc->bchan->id); + return; + } + seq_printf(s, "chan%d pair [0x%04x -> 0x%04x], ", uc->tchan->id, ucc->src_thread, ucc->dst_thread); break; case DMA_DEV_TO_MEM: seq_printf(s, "rchan%d [0x%04x -> 0x%04x], ", uc->rchan->id, ucc->src_thread, ucc->dst_thread); + if (uc->ud->match_data->type == DMA_TYPE_PKTDMA) + seq_printf(s, "rflow%d, ", uc->rflow->id); break; case DMA_MEM_TO_DEV: seq_printf(s, "tchan%d [0x%04x -> 0x%04x], ", uc->tchan->id, ucc->src_thread, ucc->dst_thread); + if (uc->ud->match_data->type == DMA_TYPE_PKTDMA) + seq_printf(s, "tflow%d, ", uc->tchan->tflow_id); break; default: seq_printf(s, ")\n"); @@ -3494,6 +5143,34 @@ } #endif /* CONFIG_DEBUG_FS */ +static enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud) +{ + const struct udma_match_data *match_data = ud->match_data; + u8 tpl; + + if (!match_data->enable_memcpy_support) + return DMAENGINE_ALIGN_8_BYTES; + + /* Get the highest TPL level the device supports for memcpy */ + if (ud->bchan_cnt) + tpl = udma_get_chan_tpl_index(&ud->bchan_tpl, 0); + else if (ud->tchan_cnt) + tpl = udma_get_chan_tpl_index(&ud->tchan_tpl, 0); + else + return DMAENGINE_ALIGN_8_BYTES; + + switch (match_data->burst_size[tpl]) { + case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES: + return DMAENGINE_ALIGN_256_BYTES; + case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES: + return DMAENGINE_ALIGN_128_BYTES; + case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES: + fallthrough; + default: + return DMAENGINE_ALIGN_64_BYTES; + } +} + #define TI_UDMAC_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ @@ -3518,6 +5195,25 @@ if (!ud) return -ENOMEM; + match = of_match_node(udma_of_match, dev->of_node); + if (!match) + match = of_match_node(bcdma_of_match, dev->of_node); + if (!match) { + match = of_match_node(pktdma_of_match, dev->of_node); + if (!match) { + dev_err(dev, "No compatible match found\n"); + return -ENODEV; + } + } + ud->match_data = match->data; + + soc = soc_device_match(k3_soc_devices); + if (!soc) { + dev_err(dev, "No compatible SoC found\n"); + return -ENODEV; + } + ud->soc_data = soc->data; + ret = udma_get_mmrs(pdev, ud); if (ret) return ret; @@ -3541,16 +5237,44 @@ return ret; } - ret = of_property_read_u32(dev->of_node, "ti,udma-atype", &ud->atype); - if (!ret && ud->atype > 2) { - dev_err(dev, "Invalid atype: %u\n", ud->atype); - return -EINVAL; + if (ud->match_data->type == DMA_TYPE_UDMA) { + ret = of_property_read_u32(dev->of_node, "ti,udma-atype", + &ud->atype); + if (!ret && ud->atype > 2) { + dev_err(dev, "Invalid atype: %u\n", ud->atype); + return -EINVAL; + } + } else { + ret = of_property_read_u32(dev->of_node, "ti,asel", + &ud->asel); + if (!ret && ud->asel > 15) { + dev_err(dev, "Invalid asel: %u\n", ud->asel); + return -EINVAL; + } } ud->tisci_rm.tisci_udmap_ops = &ud->tisci_rm.tisci->ops.rm_udmap_ops; ud->tisci_rm.tisci_psil_ops = &ud->tisci_rm.tisci->ops.rm_psil_ops; - ud->ringacc = of_k3_ringacc_get_by_phandle(dev->of_node, "ti,ringacc"); + if (ud->match_data->type == DMA_TYPE_UDMA) { + ud->ringacc = of_k3_ringacc_get_by_phandle(dev->of_node, "ti,ringacc"); + } else { + struct k3_ringacc_init_data ring_init_data; + + ring_init_data.tisci = ud->tisci_rm.tisci; + ring_init_data.tisci_dev_id = ud->tisci_rm.tisci_dev_id; + if (ud->match_data->type == DMA_TYPE_BCDMA) { + ring_init_data.num_rings = ud->bchan_cnt + + ud->tchan_cnt + + ud->rchan_cnt; + } else { + ring_init_data.num_rings = ud->rflow_cnt + + ud->tflow_cnt; + } + + ud->ringacc = k3_ringacc_dmarings_init(pdev, &ring_init_data); + } + if (IS_ERR(ud->ringacc)) return PTR_ERR(ud->ringacc); @@ -3561,27 +5285,15 @@ return -EPROBE_DEFER; } - match = of_match_node(udma_of_match, dev->of_node); - if (!match) { - dev_err(dev, "No compatible match found\n"); - return -ENODEV; - } - ud->match_data = match->data; - - soc = soc_device_match(k3_soc_devices); - if (!soc) { - dev_err(dev, "No compatible SoC found\n"); - return -ENODEV; - } - ud->soc_data = soc->data; - dma_cap_set(DMA_SLAVE, ud->ddev.cap_mask); - dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask); + /* cyclic operation is not supported via PKTDMA */ + if (ud->match_data->type != DMA_TYPE_PKTDMA) { + dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask); + ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic; + } - ud->ddev.device_alloc_chan_resources = udma_alloc_chan_resources; ud->ddev.device_config = udma_slave_config; ud->ddev.device_prep_slave_sg = udma_prep_slave_sg; - ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic; ud->ddev.device_issue_pending = udma_issue_pending; ud->ddev.device_tx_status = udma_tx_status; ud->ddev.device_pause = udma_pause; @@ -3592,15 +5304,33 @@ ud->ddev.dbg_summary_show = udma_dbg_summary_show; #endif + switch (ud->match_data->type) { + case DMA_TYPE_UDMA: + ud->ddev.device_alloc_chan_resources = + udma_alloc_chan_resources; + break; + case DMA_TYPE_BCDMA: + ud->ddev.device_alloc_chan_resources = + bcdma_alloc_chan_resources; + ud->ddev.device_router_config = bcdma_router_config; + break; + case DMA_TYPE_PKTDMA: + ud->ddev.device_alloc_chan_resources = + pktdma_alloc_chan_resources; + break; + default: + return -EINVAL; + } ud->ddev.device_free_chan_resources = udma_free_chan_resources; + ud->ddev.src_addr_widths = TI_UDMAC_BUSWIDTHS; ud->ddev.dst_addr_widths = TI_UDMAC_BUSWIDTHS; ud->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); ud->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - ud->ddev.copy_align = DMAENGINE_ALIGN_8_BYTES; ud->ddev.desc_metadata_modes = DESC_METADATA_CLIENT | DESC_METADATA_ENGINE; - if (ud->match_data->enable_memcpy_support) { + if (ud->match_data->enable_memcpy_support && + !(ud->match_data->type == DMA_TYPE_BCDMA && ud->bchan_cnt == 0)) { dma_cap_set(DMA_MEMCPY, ud->ddev.cap_mask); ud->ddev.device_prep_dma_memcpy = udma_prep_dma_memcpy; ud->ddev.directions |= BIT(DMA_MEM_TO_MEM); @@ -3613,7 +5343,7 @@ INIT_LIST_HEAD(&ud->ddev.channels); INIT_LIST_HEAD(&ud->desc_to_purge); - ch_count = udma_setup_resources(ud); + ch_count = setup_resources(ud); if (ch_count <= 0) return ch_count; @@ -3628,6 +5358,13 @@ if (ret) return ret; + for (i = 0; i < ud->bchan_cnt; i++) { + struct udma_bchan *bchan = &ud->bchans[i]; + + bchan->id = i; + bchan->reg_rt = ud->mmrs[MMR_BCHANRT] + i * 0x1000; + } + for (i = 0; i < ud->tchan_cnt; i++) { struct udma_tchan *tchan = &ud->tchans[i]; @@ -3654,9 +5391,12 @@ uc->ud = ud; uc->vc.desc_free = udma_desc_free; uc->id = i; + uc->bchan = NULL; uc->tchan = NULL; uc->rchan = NULL; uc->config.remote_thread_id = -1; + uc->config.mapped_channel_id = -1; + uc->config.default_flow_id = -1; uc->config.dir = DMA_MEM_TO_MEM; uc->name = devm_kasprintf(dev, GFP_KERNEL, "%s chan%d", dev_name(dev), i); @@ -3668,6 +5408,9 @@ INIT_DELAYED_WORK(&uc->tx_drain.work, udma_check_tx_completion); } + /* Configure the copy_align to the maximum burst size the device supports */ + ud->ddev.copy_align = udma_get_copy_align(ud); + ret = dma_async_device_register(&ud->ddev); if (ret) { dev_err(dev, "failed to register slave DMA engine: %d\n", ret); @@ -3695,5 +5438,25 @@ }; builtin_platform_driver(udma_driver); +static struct platform_driver bcdma_driver = { + .driver = { + .name = "ti-bcdma", + .of_match_table = bcdma_of_match, + .suppress_bind_attrs = true, + }, + .probe = udma_probe, +}; +builtin_platform_driver(bcdma_driver); + +static struct platform_driver pktdma_driver = { + .driver = { + .name = "ti-pktdma", + .of_match_table = pktdma_of_match, + .suppress_bind_attrs = true, + }, + .probe = udma_probe, +}; +builtin_platform_driver(pktdma_driver); + /* Private interfaces to UDMA */ #include "k3-udma-private.c" diff -Naur --no-dereference a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c --- a/drivers/dma/ti/k3-udma-glue.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-udma-glue.c 2022-01-06 12:45:53.810318090 -0500 @@ -22,6 +22,7 @@ struct k3_udma_glue_common { struct device *dev; + struct device chan_dev; struct udma_dev *udmax; const struct udma_tisci_rm *tisci_rm; struct k3_ringacc *ringacc; @@ -32,7 +33,8 @@ bool epib; u32 psdata_size; u32 swdata_size; - u32 atype; + u32 atype_asel; + struct psil_endpoint_config *ep_config; }; struct k3_udma_glue_tx_channel { @@ -53,6 +55,8 @@ bool tx_filt_einfo; bool tx_filt_pswords; bool tx_supr_tdpkt; + + int udma_tflow_id; }; struct k3_udma_glue_rx_flow { @@ -81,20 +85,26 @@ u32 flows_ready; }; +static void k3_udma_chan_dev_release(struct device *dev) +{ + /* The struct containing the device is devm managed */ +} + +static struct class k3_udma_glue_devclass = { + .name = "k3_udma_glue_chan", + .dev_release = k3_udma_chan_dev_release, +}; + #define K3_UDMAX_TDOWN_TIMEOUT_US 1000 static int of_k3_udma_glue_parse(struct device_node *udmax_np, struct k3_udma_glue_common *common) { - common->ringacc = of_k3_ringacc_get_by_phandle(udmax_np, - "ti,ringacc"); - if (IS_ERR(common->ringacc)) - return PTR_ERR(common->ringacc); - common->udmax = of_xudma_dev_get(udmax_np, NULL); if (IS_ERR(common->udmax)) return PTR_ERR(common->udmax); + common->ringacc = xudma_get_ringacc(common->udmax); common->tisci_rm = xudma_dev_get_tisci_rm(common->udmax); return 0; @@ -104,7 +114,6 @@ const char *name, struct k3_udma_glue_common *common, bool tx_chn) { - struct psil_endpoint_config *ep_config; struct of_phandle_args dma_spec; u32 thread_id; int ret = 0; @@ -121,15 +130,26 @@ &dma_spec)) return -ENOENT; + ret = of_k3_udma_glue_parse(dma_spec.np, common); + if (ret) + goto out_put_spec; + thread_id = dma_spec.args[0]; if (dma_spec.args_count == 2) { - if (dma_spec.args[1] > 2) { + if (dma_spec.args[1] > 2 && !xudma_is_pktdma(common->udmax)) { dev_err(common->dev, "Invalid channel atype: %u\n", dma_spec.args[1]); ret = -EINVAL; goto out_put_spec; } - common->atype = dma_spec.args[1]; + if (dma_spec.args[1] > 15 && xudma_is_pktdma(common->udmax)) { + dev_err(common->dev, "Invalid channel asel: %u\n", + dma_spec.args[1]); + ret = -EINVAL; + goto out_put_spec; + } + + common->atype_asel = dma_spec.args[1]; } if (tx_chn && !(thread_id & K3_PSIL_DST_THREAD_ID_OFFSET)) { @@ -143,25 +163,23 @@ } /* get psil endpoint config */ - ep_config = psil_get_ep_config(thread_id); - if (IS_ERR(ep_config)) { + common->ep_config = psil_get_ep_config(thread_id); + if (IS_ERR(common->ep_config)) { dev_err(common->dev, "No configuration for psi-l thread 0x%04x\n", thread_id); - ret = PTR_ERR(ep_config); + ret = PTR_ERR(common->ep_config); goto out_put_spec; } - common->epib = ep_config->needs_epib; - common->psdata_size = ep_config->psd_size; + common->epib = common->ep_config->needs_epib; + common->psdata_size = common->ep_config->psd_size; if (tx_chn) common->dst_thread = thread_id; else common->src_thread = thread_id; - ret = of_k3_udma_glue_parse(dma_spec.np, common); - out_put_spec: of_node_put(dma_spec.np); return ret; @@ -227,7 +245,7 @@ req.tx_supr_tdpkt = 1; req.tx_fetch_size = tx_chn->common.hdesc_size >> 2; req.txcq_qnum = k3_ringacc_get_ring_id(tx_chn->ringtxcq); - req.tx_atype = tx_chn->common.atype; + req.tx_atype = tx_chn->common.atype_asel; return tisci_rm->tisci_udmap_ops->tx_ch_cfg(tisci_rm->tisci, &req); } @@ -259,8 +277,14 @@ tx_chn->common.psdata_size, tx_chn->common.swdata_size); + if (xudma_is_pktdma(tx_chn->common.udmax)) + tx_chn->udma_tchan_id = tx_chn->common.ep_config->mapped_channel_id; + else + tx_chn->udma_tchan_id = -1; + /* request and cfg UDMAP TX channel */ - tx_chn->udma_tchanx = xudma_tchan_get(tx_chn->common.udmax, -1); + tx_chn->udma_tchanx = xudma_tchan_get(tx_chn->common.udmax, + tx_chn->udma_tchan_id); if (IS_ERR(tx_chn->udma_tchanx)) { ret = PTR_ERR(tx_chn->udma_tchanx); dev_err(dev, "UDMAX tchanx get err %d\n", ret); @@ -268,11 +292,34 @@ } tx_chn->udma_tchan_id = xudma_tchan_get_id(tx_chn->udma_tchanx); + if (xudma_is_pktdma(tx_chn->common.udmax)) { + tx_chn->common.chan_dev.class = &k3_udma_glue_devclass; + tx_chn->common.chan_dev.parent = xudma_get_device(tx_chn->common.udmax); + dev_set_name(&tx_chn->common.chan_dev, "tchan%d-0x%04x", + tx_chn->udma_tchan_id, tx_chn->common.dst_thread); + ret = device_register(&tx_chn->common.chan_dev); + if (ret) { + dev_err(dev, "Channel Device registration failed %d\n", ret); + tx_chn->common.chan_dev.parent = NULL; + goto err; + } + + /* prepare the channel device as coherent */ + tx_chn->common.chan_dev.dma_coherent = true; + dma_coerce_mask_and_coherent(&tx_chn->common.chan_dev, + DMA_BIT_MASK(48)); + } + atomic_set(&tx_chn->free_pkts, cfg->txcq_cfg.size); + if (xudma_is_pktdma(tx_chn->common.udmax)) + tx_chn->udma_tflow_id = tx_chn->common.ep_config->default_flow_id; + else + tx_chn->udma_tflow_id = tx_chn->udma_tchan_id; + /* request and cfg rings */ ret = k3_ringacc_request_rings_pair(tx_chn->common.ringacc, - tx_chn->udma_tchan_id, -1, + tx_chn->udma_tflow_id, -1, &tx_chn->ringtx, &tx_chn->ringtxcq); if (ret) { @@ -280,6 +327,16 @@ goto err; } + /* Set the dma_dev for the rings to be configured */ + cfg->tx_cfg.dma_dev = k3_udma_glue_tx_get_dma_device(tx_chn); + cfg->txcq_cfg.dma_dev = cfg->tx_cfg.dma_dev; + + /* Set the ASEL value for DMA rings of PKTDMA */ + if (xudma_is_pktdma(tx_chn->common.udmax)) { + cfg->tx_cfg.asel = tx_chn->common.atype_asel; + cfg->txcq_cfg.asel = tx_chn->common.atype_asel; + } + ret = k3_ringacc_ring_cfg(tx_chn->ringtx, &cfg->tx_cfg); if (ret) { dev_err(dev, "Failed to cfg ringtx %d\n", ret); @@ -303,19 +360,6 @@ goto err; } - ret = xudma_navss_psil_pair(tx_chn->common.udmax, - tx_chn->common.src_thread, - tx_chn->common.dst_thread); - if (ret) { - dev_err(dev, "PSI-L request err %d\n", ret); - goto err; - } - - tx_chn->psil_paired = true; - - /* reset TX RT registers */ - k3_udma_glue_disable_tx_chn(tx_chn); - k3_udma_glue_dump_tx_chn(tx_chn); return tx_chn; @@ -344,6 +388,11 @@ if (tx_chn->ringtx) k3_ringacc_ring_free(tx_chn->ringtx); + + if (tx_chn->common.chan_dev.parent) { + device_unregister(&tx_chn->common.chan_dev); + tx_chn->common.chan_dev.parent = NULL; + } } EXPORT_SYMBOL_GPL(k3_udma_glue_release_tx_chn); @@ -378,6 +427,18 @@ int k3_udma_glue_enable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn) { + int ret; + + ret = xudma_navss_psil_pair(tx_chn->common.udmax, + tx_chn->common.src_thread, + tx_chn->common.dst_thread); + if (ret) { + dev_err(tx_chn->common.dev, "PSI-L request err %d\n", ret); + return ret; + } + + tx_chn->psil_paired = true; + xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE); @@ -398,6 +459,13 @@ xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn dis2"); + + if (tx_chn->psil_paired) { + xudma_navss_psil_unpair(tx_chn->common.udmax, + tx_chn->common.src_thread, + tx_chn->common.dst_thread); + tx_chn->psil_paired = false; + } } EXPORT_SYMBOL_GPL(k3_udma_glue_disable_tx_chn); @@ -437,13 +505,10 @@ void *data, void (*cleanup)(void *data, dma_addr_t desc_dma)) { + struct device *dev = tx_chn->common.dev; dma_addr_t desc_dma; int occ_tx, i, ret; - /* reset TXCQ as it is not input for udma - expected to be empty */ - if (tx_chn->ringtxcq) - k3_ringacc_ring_reset(tx_chn->ringtxcq); - /* * TXQ reset need to be special way as it is input for udma and its * state cached by udma, so: @@ -452,17 +517,20 @@ * 3) reset TXQ in a special way */ occ_tx = k3_ringacc_ring_get_occ(tx_chn->ringtx); - dev_dbg(tx_chn->common.dev, "TX reset occ_tx %u\n", occ_tx); + dev_dbg(dev, "TX reset occ_tx %u\n", occ_tx); for (i = 0; i < occ_tx; i++) { ret = k3_ringacc_ring_pop(tx_chn->ringtx, &desc_dma); if (ret) { - dev_err(tx_chn->common.dev, "TX reset pop %d\n", ret); + if (ret != -ENODATA) + dev_err(dev, "TX reset pop %d\n", ret); break; } cleanup(data, desc_dma); } + /* reset TXCQ as it is not input for udma - expected to be empty */ + k3_ringacc_ring_reset(tx_chn->ringtxcq); k3_ringacc_ring_reset_dma(tx_chn->ringtx, occ_tx); } EXPORT_SYMBOL_GPL(k3_udma_glue_reset_tx_chn); @@ -481,12 +549,50 @@ int k3_udma_glue_tx_get_irq(struct k3_udma_glue_tx_channel *tx_chn) { - tx_chn->virq = k3_ringacc_get_ring_irq_num(tx_chn->ringtxcq); + if (xudma_is_pktdma(tx_chn->common.udmax)) { + tx_chn->virq = xudma_pktdma_tflow_get_irq(tx_chn->common.udmax, + tx_chn->udma_tflow_id); + } else { + tx_chn->virq = k3_ringacc_get_ring_irq_num(tx_chn->ringtxcq); + } return tx_chn->virq; } EXPORT_SYMBOL_GPL(k3_udma_glue_tx_get_irq); +struct device * + k3_udma_glue_tx_get_dma_device(struct k3_udma_glue_tx_channel *tx_chn) +{ + if (xudma_is_pktdma(tx_chn->common.udmax) && + (tx_chn->common.atype_asel == 14 || tx_chn->common.atype_asel == 15)) + return &tx_chn->common.chan_dev; + + return xudma_get_device(tx_chn->common.udmax); +} +EXPORT_SYMBOL_GPL(k3_udma_glue_tx_get_dma_device); + +void k3_udma_glue_tx_dma_to_cppi5_addr(struct k3_udma_glue_tx_channel *tx_chn, + dma_addr_t *addr) +{ + if (!xudma_is_pktdma(tx_chn->common.udmax) || + !tx_chn->common.atype_asel) + return; + + *addr |= (u64)tx_chn->common.atype_asel << K3_ADDRESS_ASEL_SHIFT; +} +EXPORT_SYMBOL_GPL(k3_udma_glue_tx_dma_to_cppi5_addr); + +void k3_udma_glue_tx_cppi5_to_dma_addr(struct k3_udma_glue_tx_channel *tx_chn, + dma_addr_t *addr) +{ + if (!xudma_is_pktdma(tx_chn->common.udmax) || + !tx_chn->common.atype_asel) + return; + + *addr &= (u64)GENMASK(K3_ADDRESS_ASEL_SHIFT - 1, 0); +} +EXPORT_SYMBOL_GPL(k3_udma_glue_tx_cppi5_to_dma_addr); + static int k3_udma_glue_cfg_rx_chn(struct k3_udma_glue_rx_channel *rx_chn) { const struct udma_tisci_rm *tisci_rm = rx_chn->common.tisci_rm; @@ -498,8 +604,6 @@ req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID | TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID | TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | - TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID | - TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID | TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID; req.nav_id = tisci_rm->tisci_dev_id; @@ -511,13 +615,16 @@ * req.rxcq_qnum = k3_ringacc_get_ring_id(rx_chn->flows[0].ringrx); */ req.rxcq_qnum = 0xFFFF; - if (rx_chn->flow_num && rx_chn->flow_id_base != rx_chn->udma_rchan_id) { + if (!xudma_is_pktdma(rx_chn->common.udmax) && rx_chn->flow_num && + rx_chn->flow_id_base != rx_chn->udma_rchan_id) { /* Default flow + extra ones */ + req.valid_params |= TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID | + TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID; req.flowid_start = rx_chn->flow_id_base; req.flowid_cnt = rx_chn->flow_num; } req.rx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR; - req.rx_atype = rx_chn->common.atype; + req.rx_atype = rx_chn->common.atype_asel; ret = tisci_rm->tisci_udmap_ops->rx_ch_cfg(tisci_rm->tisci, &req); if (ret) @@ -571,10 +678,18 @@ goto err_rflow_put; } + if (xudma_is_pktdma(rx_chn->common.udmax)) { + rx_ringfdq_id = flow->udma_rflow_id + + xudma_get_rflow_ring_offset(rx_chn->common.udmax); + rx_ring_id = 0; + } else { + rx_ring_id = flow_cfg->ring_rxq_id; + rx_ringfdq_id = flow_cfg->ring_rxfdq0_id; + } + /* request and cfg rings */ ret = k3_ringacc_request_rings_pair(rx_chn->common.ringacc, - flow_cfg->ring_rxfdq0_id, - flow_cfg->ring_rxq_id, + rx_ringfdq_id, rx_ring_id, &flow->ringrxfdq, &flow->ringrx); if (ret) { @@ -582,6 +697,16 @@ goto err_rflow_put; } + /* Set the dma_dev for the rings to be configured */ + flow_cfg->rx_cfg.dma_dev = k3_udma_glue_rx_get_dma_device(rx_chn); + flow_cfg->rxfdq_cfg.dma_dev = flow_cfg->rx_cfg.dma_dev; + + /* Set the ASEL value for DMA rings of PKTDMA */ + if (xudma_is_pktdma(rx_chn->common.udmax)) { + flow_cfg->rx_cfg.asel = rx_chn->common.atype_asel; + flow_cfg->rxfdq_cfg.asel = rx_chn->common.atype_asel; + } + ret = k3_ringacc_ring_cfg(flow->ringrx, &flow_cfg->rx_cfg); if (ret) { dev_err(dev, "Failed to cfg ringrx %d\n", ret); @@ -740,6 +865,7 @@ struct k3_udma_glue_rx_channel_cfg *cfg) { struct k3_udma_glue_rx_channel *rx_chn; + struct psil_endpoint_config *ep_cfg; int ret, i; if (cfg->flow_id_num <= 0) @@ -767,8 +893,16 @@ rx_chn->common.psdata_size, rx_chn->common.swdata_size); + ep_cfg = rx_chn->common.ep_config; + + if (xudma_is_pktdma(rx_chn->common.udmax)) + rx_chn->udma_rchan_id = ep_cfg->mapped_channel_id; + else + rx_chn->udma_rchan_id = -1; + /* request and cfg UDMAP RX channel */ - rx_chn->udma_rchanx = xudma_rchan_get(rx_chn->common.udmax, -1); + rx_chn->udma_rchanx = xudma_rchan_get(rx_chn->common.udmax, + rx_chn->udma_rchan_id); if (IS_ERR(rx_chn->udma_rchanx)) { ret = PTR_ERR(rx_chn->udma_rchanx); dev_err(dev, "UDMAX rchanx get err %d\n", ret); @@ -776,12 +910,48 @@ } rx_chn->udma_rchan_id = xudma_rchan_get_id(rx_chn->udma_rchanx); - rx_chn->flow_num = cfg->flow_id_num; - rx_chn->flow_id_base = cfg->flow_id_base; + if (xudma_is_pktdma(rx_chn->common.udmax)) { + rx_chn->common.chan_dev.class = &k3_udma_glue_devclass; + rx_chn->common.chan_dev.parent = xudma_get_device(rx_chn->common.udmax); + dev_set_name(&rx_chn->common.chan_dev, "rchan%d-0x%04x", + rx_chn->udma_rchan_id, rx_chn->common.src_thread); + ret = device_register(&rx_chn->common.chan_dev); + if (ret) { + dev_err(dev, "Channel Device registration failed %d\n", ret); + rx_chn->common.chan_dev.parent = NULL; + goto err; + } - /* Use RX channel id as flow id: target dev can't generate flow_id */ - if (cfg->flow_id_use_rxchan_id) - rx_chn->flow_id_base = rx_chn->udma_rchan_id; + /* prepare the channel device as coherent */ + rx_chn->common.chan_dev.dma_coherent = true; + dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev, + DMA_BIT_MASK(48)); + } + + if (xudma_is_pktdma(rx_chn->common.udmax)) { + int flow_start = cfg->flow_id_base; + int flow_end; + + if (flow_start == -1) + flow_start = ep_cfg->flow_start; + + flow_end = flow_start + cfg->flow_id_num - 1; + if (flow_start < ep_cfg->flow_start || + flow_end > (ep_cfg->flow_start + ep_cfg->flow_num - 1)) { + dev_err(dev, "Invalid flow range requested\n"); + ret = -EINVAL; + goto err; + } + rx_chn->flow_id_base = flow_start; + } else { + rx_chn->flow_id_base = cfg->flow_id_base; + + /* Use RX channel id as flow id: target dev can't generate flow_id */ + if (cfg->flow_id_use_rxchan_id) + rx_chn->flow_id_base = rx_chn->udma_rchan_id; + } + + rx_chn->flow_num = cfg->flow_id_num; rx_chn->flows = devm_kcalloc(dev, rx_chn->flow_num, sizeof(*rx_chn->flows), GFP_KERNEL); @@ -815,19 +985,6 @@ goto err; } - ret = xudma_navss_psil_pair(rx_chn->common.udmax, - rx_chn->common.src_thread, - rx_chn->common.dst_thread); - if (ret) { - dev_err(dev, "PSI-L request err %d\n", ret); - goto err; - } - - rx_chn->psil_paired = true; - - /* reset RX RT registers */ - k3_udma_glue_disable_rx_chn(rx_chn); - k3_udma_glue_dump_rx_chn(rx_chn); return rx_chn; @@ -884,6 +1041,25 @@ goto err; } + if (xudma_is_pktdma(rx_chn->common.udmax)) { + rx_chn->common.chan_dev.class = &k3_udma_glue_devclass; + rx_chn->common.chan_dev.parent = xudma_get_device(rx_chn->common.udmax); + dev_set_name(&rx_chn->common.chan_dev, "rchan_remote-0x%04x", + rx_chn->common.src_thread); + + ret = device_register(&rx_chn->common.chan_dev); + if (ret) { + dev_err(dev, "Channel Device registration failed %d\n", ret); + rx_chn->common.chan_dev.parent = NULL; + goto err; + } + + /* prepare the channel device as coherent */ + rx_chn->common.chan_dev.dma_coherent = true; + dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev, + DMA_BIT_MASK(48)); + } + ret = k3_udma_glue_allocate_rx_flows(rx_chn, cfg); if (ret) goto err; @@ -936,6 +1112,11 @@ if (!IS_ERR_OR_NULL(rx_chn->udma_rchanx)) xudma_rchan_put(rx_chn->common.udmax, rx_chn->udma_rchanx); + + if (rx_chn->common.chan_dev.parent) { + device_unregister(&rx_chn->common.chan_dev); + rx_chn->common.chan_dev.parent = NULL; + } } EXPORT_SYMBOL_GPL(k3_udma_glue_release_rx_chn); @@ -1052,12 +1233,24 @@ int k3_udma_glue_enable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn) { + int ret; + if (rx_chn->remote) return -EINVAL; if (rx_chn->flows_ready < rx_chn->flow_num) return -EINVAL; + ret = xudma_navss_psil_pair(rx_chn->common.udmax, + rx_chn->common.src_thread, + rx_chn->common.dst_thread); + if (ret) { + dev_err(rx_chn->common.dev, "PSI-L request err %d\n", ret); + return ret; + } + + rx_chn->psil_paired = true; + xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); @@ -1078,6 +1271,13 @@ xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG, 0); k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt dis2"); + + if (rx_chn->psil_paired) { + xudma_navss_psil_unpair(rx_chn->common.udmax, + rx_chn->common.src_thread, + rx_chn->common.dst_thread); + rx_chn->psil_paired = false; + } } EXPORT_SYMBOL_GPL(k3_udma_glue_disable_rx_chn); @@ -1128,12 +1328,10 @@ /* reset RXCQ as it is not input for udma - expected to be empty */ occ_rx = k3_ringacc_ring_get_occ(flow->ringrx); dev_dbg(dev, "RX reset flow %u occ_rx %u\n", flow_num, occ_rx); - if (flow->ringrx) - k3_ringacc_ring_reset(flow->ringrx); /* Skip RX FDQ in case one FDQ is used for the set of flows */ if (skip_fdq) - return; + goto do_reset; /* * RX FDQ reset need to be special way as it is input for udma and its @@ -1148,13 +1346,17 @@ for (i = 0; i < occ_rx; i++) { ret = k3_ringacc_ring_pop(flow->ringrxfdq, &desc_dma); if (ret) { - dev_err(dev, "RX reset pop %d\n", ret); + if (ret != -ENODATA) + dev_err(dev, "RX reset pop %d\n", ret); break; } cleanup(data, desc_dma); } k3_ringacc_ring_reset_dma(flow->ringrxfdq, occ_rx); + +do_reset: + k3_ringacc_ring_reset(flow->ringrx); } EXPORT_SYMBOL_GPL(k3_udma_glue_reset_rx_chn); @@ -1184,8 +1386,52 @@ flow = &rx_chn->flows[flow_num]; - flow->virq = k3_ringacc_get_ring_irq_num(flow->ringrx); + if (xudma_is_pktdma(rx_chn->common.udmax)) { + flow->virq = xudma_pktdma_rflow_get_irq(rx_chn->common.udmax, + flow->udma_rflow_id); + } else { + flow->virq = k3_ringacc_get_ring_irq_num(flow->ringrx); + } return flow->virq; } EXPORT_SYMBOL_GPL(k3_udma_glue_rx_get_irq); + +struct device * + k3_udma_glue_rx_get_dma_device(struct k3_udma_glue_rx_channel *rx_chn) +{ + if (xudma_is_pktdma(rx_chn->common.udmax) && + (rx_chn->common.atype_asel == 14 || rx_chn->common.atype_asel == 15)) + return &rx_chn->common.chan_dev; + + return xudma_get_device(rx_chn->common.udmax); +} +EXPORT_SYMBOL_GPL(k3_udma_glue_rx_get_dma_device); + +void k3_udma_glue_rx_dma_to_cppi5_addr(struct k3_udma_glue_rx_channel *rx_chn, + dma_addr_t *addr) +{ + if (!xudma_is_pktdma(rx_chn->common.udmax) || + !rx_chn->common.atype_asel) + return; + + *addr |= (u64)rx_chn->common.atype_asel << K3_ADDRESS_ASEL_SHIFT; +} +EXPORT_SYMBOL_GPL(k3_udma_glue_rx_dma_to_cppi5_addr); + +void k3_udma_glue_rx_cppi5_to_dma_addr(struct k3_udma_glue_rx_channel *rx_chn, + dma_addr_t *addr) +{ + if (!xudma_is_pktdma(rx_chn->common.udmax) || + !rx_chn->common.atype_asel) + return; + + *addr &= (u64)GENMASK(K3_ADDRESS_ASEL_SHIFT - 1, 0); +} +EXPORT_SYMBOL_GPL(k3_udma_glue_rx_cppi5_to_dma_addr); + +static int __init k3_udma_glue_class_init(void) +{ + return class_register(&k3_udma_glue_devclass); +} +arch_initcall(k3_udma_glue_class_init); diff -Naur --no-dereference a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h --- a/drivers/dma/ti/k3-udma.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-udma.h 2022-01-06 12:45:53.810318090 -0500 @@ -18,7 +18,7 @@ #define UDMA_RX_FLOW_ID_FW_OES_REG 0x80 #define UDMA_RX_FLOW_ID_FW_STATUS_REG 0x88 -/* TCHANRT/RCHANRT registers */ +/* BCHANRT/TCHANRT/RCHANRT registers */ #define UDMA_CHAN_RT_CTL_REG 0x0 #define UDMA_CHAN_RT_SWTRIG_REG 0x8 #define UDMA_CHAN_RT_STDATA_REG 0x80 @@ -45,6 +45,18 @@ #define UDMA_CAP3_HCHAN_CNT(val) (((val) >> 14) & 0x1ff) #define UDMA_CAP3_UCHAN_CNT(val) (((val) >> 23) & 0x1ff) +#define BCDMA_CAP2_BCHAN_CNT(val) ((val) & 0x1ff) +#define BCDMA_CAP2_TCHAN_CNT(val) (((val) >> 9) & 0x1ff) +#define BCDMA_CAP2_RCHAN_CNT(val) (((val) >> 18) & 0x1ff) +#define BCDMA_CAP3_HBCHAN_CNT(val) (((val) >> 14) & 0x1ff) +#define BCDMA_CAP3_UBCHAN_CNT(val) (((val) >> 23) & 0x1ff) +#define BCDMA_CAP4_HRCHAN_CNT(val) ((val) & 0xff) +#define BCDMA_CAP4_URCHAN_CNT(val) (((val) >> 8) & 0xff) +#define BCDMA_CAP4_HTCHAN_CNT(val) (((val) >> 16) & 0xff) +#define BCDMA_CAP4_UTCHAN_CNT(val) (((val) >> 24) & 0xff) + +#define PKTDMA_CAP4_TFLOW_CNT(val) ((val) & 0x3fff) + /* UDMA_CHAN_RT_CTL_REG */ #define UDMA_CHAN_RT_CTL_EN BIT(31) #define UDMA_CHAN_RT_CTL_TDOWN BIT(30) @@ -82,15 +94,20 @@ */ #define PDMA_STATIC_TR_Z(x, mask) ((x) & (mask)) +/* Address Space Select */ +#define K3_ADDRESS_ASEL_SHIFT 48 + struct udma_dev; struct udma_tchan; struct udma_rchan; struct udma_rflow; enum udma_rm_range { - RM_RANGE_TCHAN = 0, + RM_RANGE_BCHAN = 0, + RM_RANGE_TCHAN, RM_RANGE_RCHAN, RM_RANGE_RFLOW, + RM_RANGE_TFLOW, RM_RANGE_LAST, }; @@ -112,6 +129,8 @@ u32 dst_thread); struct udma_dev *of_xudma_dev_get(struct device_node *np, const char *property); +struct device *xudma_get_device(struct udma_dev *ud); +struct k3_ringacc *xudma_get_ringacc(struct udma_dev *ud); void xudma_dev_put(struct udma_dev *ud); u32 xudma_dev_get_psil_base(struct udma_dev *ud); struct udma_tisci_rm *xudma_dev_get_tisci_rm(struct udma_dev *ud); @@ -136,5 +155,10 @@ u32 xudma_rchanrt_read(struct udma_rchan *rchan, int reg); void xudma_rchanrt_write(struct udma_rchan *rchan, int reg, u32 val); bool xudma_rflow_is_gp(struct udma_dev *ud, int id); +int xudma_get_rflow_ring_offset(struct udma_dev *ud); + +int xudma_is_pktdma(struct udma_dev *ud); +int xudma_pktdma_tflow_get_irq(struct udma_dev *ud, int udma_tflow_id); +int xudma_pktdma_rflow_get_irq(struct udma_dev *ud, int udma_rflow_id); #endif /* K3_UDMA_H_ */ diff -Naur --no-dereference a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c --- a/drivers/dma/ti/k3-udma-private.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/k3-udma-private.c 2022-01-06 12:45:53.810318090 -0500 @@ -50,6 +50,18 @@ } EXPORT_SYMBOL(of_xudma_dev_get); +struct device *xudma_get_device(struct udma_dev *ud) +{ + return ud->dev; +} +EXPORT_SYMBOL(xudma_get_device); + +struct k3_ringacc *xudma_get_ringacc(struct udma_dev *ud) +{ + return ud->ringacc; +} +EXPORT_SYMBOL(xudma_get_ringacc); + u32 xudma_dev_get_psil_base(struct udma_dev *ud) { return ud->psil_base; @@ -76,6 +88,9 @@ bool xudma_rflow_is_gp(struct udma_dev *ud, int id) { + if (!ud->rflow_gp_map) + return false; + return !test_bit(id, ud->rflow_gp_map); } EXPORT_SYMBOL(xudma_rflow_is_gp); @@ -107,6 +122,12 @@ } EXPORT_SYMBOL(xudma_rflow_put); +int xudma_get_rflow_ring_offset(struct udma_dev *ud) +{ + return ud->tflow_cnt; +} +EXPORT_SYMBOL(xudma_get_rflow_ring_offset); + #define XUDMA_GET_RESOURCE_ID(res) \ int xudma_##res##_get_id(struct udma_##res *p) \ { \ @@ -136,3 +157,27 @@ EXPORT_SYMBOL(xudma_##res##rt_write) XUDMA_RT_IO_FUNCTIONS(tchan); XUDMA_RT_IO_FUNCTIONS(rchan); + +int xudma_is_pktdma(struct udma_dev *ud) +{ + return ud->match_data->type == DMA_TYPE_PKTDMA; +} +EXPORT_SYMBOL(xudma_is_pktdma); + +int xudma_pktdma_tflow_get_irq(struct udma_dev *ud, int udma_tflow_id) +{ + const struct udma_oes_offsets *oes = &ud->soc_data->oes; + + return ti_sci_inta_msi_get_virq(ud->dev, udma_tflow_id + + oes->pktdma_tchan_flow); +} +EXPORT_SYMBOL(xudma_pktdma_tflow_get_irq); + +int xudma_pktdma_rflow_get_irq(struct udma_dev *ud, int udma_rflow_id) +{ + const struct udma_oes_offsets *oes = &ud->soc_data->oes; + + return ti_sci_inta_msi_get_virq(ud->dev, udma_rflow_id + + oes->pktdma_rchan_flow); +} +EXPORT_SYMBOL(xudma_pktdma_rflow_get_irq); diff -Naur --no-dereference a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile --- a/drivers/dma/ti/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma/ti/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -7,5 +7,7 @@ obj-$(CONFIG_TI_K3_PSIL) += k3-psil.o \ k3-psil-am654.o \ k3-psil-j721e.o \ - k3-psil-j7200.o + k3-psil-j7200.o \ + k3-psil-am64.o \ + k3-psil-j721s2.o obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o diff -Naur --no-dereference a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c --- a/drivers/dma-buf/dma-heap.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma-buf/dma-heap.c 2022-01-06 12:45:53.810318090 -0500 @@ -295,4 +295,4 @@ return 0; } -subsys_initcall(dma_heap_init); +core_initcall(dma_heap_init); diff -Naur --no-dereference a/drivers/dma-buf/heaps/carveout-heap.c b/drivers/dma-buf/heaps/carveout-heap.c --- a/drivers/dma-buf/heaps/carveout-heap.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/dma-buf/heaps/carveout-heap.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Carveout DMA-Heap userspace exporter + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct carveout_dma_heap { + struct dma_heap *heap; + struct gen_pool *pool; +}; + +struct carveout_dma_heap_buffer { + struct gen_pool *pool; + struct list_head attachments; + struct mutex attachments_lock; + struct mutex vmap_lock; + int vmap_cnt; + unsigned long len; + void *vaddr; + phys_addr_t paddr; +}; + +struct dma_heap_attachment { + struct device *dev; + struct sg_table *table; + struct list_head list; +}; + +static int dma_heap_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct carveout_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a; + struct sg_table *table; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(a); + return -ENOMEM; + } + if (sg_alloc_table(table, 1, GFP_KERNEL)) { + kfree(a); + return -ENOMEM; + } + sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(buffer->paddr)), buffer->len, 0); + + a->table = table; + a->dev = attachment->dev; + INIT_LIST_HEAD(&a->list); + + attachment->priv = a; + + mutex_lock(&buffer->attachments_lock); + list_add(&a->list, &buffer->attachments); + mutex_unlock(&buffer->attachments_lock); + + return 0; +} + +static void dma_heap_detatch(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct carveout_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a = attachment->priv; + + mutex_lock(&buffer->attachments_lock); + list_del(&a->list); + mutex_unlock(&buffer->attachments_lock); + + sg_free_table(a->table); + kfree(a->table); + kfree(a); +} + +static struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct dma_heap_attachment *a = attachment->priv; + struct sg_table *table = a->table; + + if (!dma_map_sg_attrs(attachment->dev, table->sgl, table->nents, + direction, DMA_ATTR_SKIP_CPU_SYNC)) + return ERR_PTR(-ENOMEM); + + return table; +} + +static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + dma_unmap_sg_attrs(attachment->dev, table->sgl, table->nents, + direction, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void dma_heap_dma_buf_release(struct dma_buf *dmabuf) +{ + struct carveout_dma_heap_buffer *buffer = dmabuf->priv; + + if (buffer->vmap_cnt > 0) { + WARN(1, "%s: buffer still mapped in the kernel\n", __func__); + memunmap(buffer->vaddr); + } + + gen_pool_free(buffer->pool, buffer->paddr, buffer->len); + kfree(buffer); +} + +static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct carveout_dma_heap_buffer *buffer = dmabuf->priv; + int ret; + + /* Carveouts are not cached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret = vm_iomap_memory(vma, buffer->paddr, buffer->len); + if (ret) + pr_err("Could not map buffer to userspace\n"); + + return ret; +} + +static void *dma_heap_vmap(struct dma_buf *dmabuf) +{ + struct carveout_dma_heap_buffer *buffer = dmabuf->priv; + void *vaddr; + + mutex_lock(&buffer->vmap_lock); + + if (buffer->vmap_cnt) { + buffer->vmap_cnt++; + vaddr = buffer->vaddr; + goto exit; + } + vaddr = memremap(buffer->paddr, buffer->len, MEMREMAP_WC); + if (!vaddr) { + pr_err("Could not memremap buffer\n"); + goto exit; + } + if (IS_ERR(vaddr)) + goto exit; + buffer->vaddr = vaddr; + buffer->vmap_cnt++; + +exit: + mutex_unlock(&buffer->vmap_lock); + return vaddr; +} + +static void dma_heap_vunmap(struct dma_buf *dmabuf, void *vaddr) +{ + struct carveout_dma_heap_buffer *buffer = dmabuf->priv; + + mutex_lock(&buffer->vmap_lock); + if (!--buffer->vmap_cnt) { + memunmap(buffer->vaddr); + buffer->vaddr = NULL; + } + mutex_unlock(&buffer->vmap_lock); +} + +const struct dma_buf_ops carveout_dma_heap_buf_ops = { + .attach = dma_heap_attach, + .detach = dma_heap_detatch, + .map_dma_buf = dma_heap_map_dma_buf, + .unmap_dma_buf = dma_heap_unmap_dma_buf, + .release = dma_heap_dma_buf_release, + .mmap = dma_heap_mmap, + .vmap = dma_heap_vmap, + .vunmap = dma_heap_vunmap, +}; + +static int carveout_dma_heap_allocate(struct dma_heap *heap, + unsigned long len, + unsigned long fd_flags, + unsigned long heap_flags) +{ + struct carveout_dma_heap *carveout_dma_heap = dma_heap_get_drvdata(heap); + struct carveout_dma_heap_buffer *buffer; + + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dmabuf; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + buffer->pool = carveout_dma_heap->pool; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->attachments_lock); + mutex_init(&buffer->vmap_lock); + buffer->len = len; + + buffer->paddr = gen_pool_alloc(buffer->pool, buffer->len); + if (!buffer->paddr) { + ret = -ENOMEM; + goto free_buffer; + } + + /* create the dmabuf */ + exp_info.ops = &carveout_dma_heap_buf_ops; + exp_info.size = buffer->len; + exp_info.flags = fd_flags; + exp_info.priv = buffer; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto free_pool; + } + + ret = dma_buf_fd(dmabuf, fd_flags); + if (ret < 0) { + dma_buf_put(dmabuf); + /* just return, as put will call release and that will free */ + return ret; + } + + return ret; + +free_pool: + gen_pool_free(buffer->pool, buffer->paddr, buffer->len); +free_buffer: + kfree(buffer); + + return ret; +} + +static struct dma_heap_ops carveout_dma_heap_ops = { + .allocate = carveout_dma_heap_allocate, +}; + +int carveout_dma_heap_export(phys_addr_t base, size_t size, const char *name) +{ + struct carveout_dma_heap *carveout_dma_heap; + struct dma_heap_export_info exp_info; + int ret; + + carveout_dma_heap = kzalloc(sizeof(*carveout_dma_heap), GFP_KERNEL); + if (!carveout_dma_heap) + return -ENOMEM; + + carveout_dma_heap->pool = gen_pool_create(PAGE_SHIFT, NUMA_NO_NODE); + if (IS_ERR(carveout_dma_heap->pool)) { + pr_err("Carveout Heap: Could not create memory pool\n"); + ret = PTR_ERR(carveout_dma_heap->pool); + goto free_carveout_dma_heap; + } + ret = gen_pool_add(carveout_dma_heap->pool, base, size, NUMA_NO_NODE); + if (ret) { + pr_err("Carveout Heap: Could not add memory to pool\n"); + goto free_pool; + } + + exp_info.name = name; + exp_info.ops = &carveout_dma_heap_ops; + exp_info.priv = carveout_dma_heap; + carveout_dma_heap->heap = dma_heap_add(&exp_info); + if (IS_ERR(carveout_dma_heap->heap)) { + pr_err("Carveout Heap: Could not add DMA-Heap\n"); + ret = PTR_ERR(carveout_dma_heap->heap); + goto free_pool; + } + + pr_info("Carveout Heap: Exported %zu MiB at %pa\n", size / SZ_1M, &base); + + return 0; + +free_pool: + gen_pool_destroy(carveout_dma_heap->pool); +free_carveout_dma_heap: + kfree(carveout_dma_heap); + return ret; +} + +#ifdef CONFIG_OF_RESERVED_MEM +#include +#include +#include + +#define MAX_HEAP_AREAS 7 +struct reserved_mem heap_areas[MAX_HEAP_AREAS]; +size_t heap_area_count; + +static int __init carveout_dma_heap_init_areas(void) +{ + int i; + + for (i = 0; i < heap_area_count; i++) { + struct reserved_mem *rmem = &heap_areas[i]; + int ret = carveout_dma_heap_export(rmem->base, rmem->size, rmem->name); + + if (ret) { + pr_err("Carveout Heap: could not export as DMA-Heap\n"); + return ret; + } + } + + return 0; +} +fs_initcall(carveout_dma_heap_init_areas); + +static int __init rmem_dma_heap_carveout_setup(struct reserved_mem *rmem) +{ + phys_addr_t align = PAGE_SIZE; + phys_addr_t mask = align - 1; + unsigned long node = rmem->fdt_node; + + if (!of_get_flat_dt_prop(node, "no-map", NULL)) { + pr_err("Carveout Heap: regions without no-map are not yet supported\n"); + return -EINVAL; + } + + if ((rmem->base & mask) || (rmem->size & mask)) { + pr_err("Carveout Heap: incorrect alignment of region\n"); + return -EINVAL; + } + + /* Sanity check */ + if (heap_area_count == ARRAY_SIZE(heap_areas)) { + pr_err("Not enough slots for DMA-Heap reserved regions!\n"); + return -ENOSPC; + } + + /* + * Each reserved area must be initialized later, when more kernel + * subsystems (like slab allocator) are available. + */ + heap_areas[heap_area_count] = *rmem; + heap_area_count++; + + return 0; +} +RESERVEDMEM_OF_DECLARE(dma_heap_carveout, "dma-heap-carveout", rmem_dma_heap_carveout_setup); + +#endif diff -Naur --no-dereference a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig --- a/drivers/dma-buf/heaps/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma-buf/heaps/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -12,3 +12,11 @@ Choose this option to enable dma-buf CMA heap. This heap is backed by the Contiguous Memory Allocator (CMA). If your system has these regions, you should say Y here. + +config DMABUF_HEAPS_CARVEOUT + bool "DMA-BUF Carveout Heap" + depends on DMABUF_HEAPS + help + Choose this option to enable dma-buf Carveout heap. This heap is + backed by the a carved-out of memory. If your system has these + regions, you should say Y here. diff -Naur --no-dereference a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile --- a/drivers/dma-buf/heaps/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/dma-buf/heaps/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -2,3 +2,4 @@ obj-y += heap-helpers.o obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o +obj-$(CONFIG_DMABUF_HEAPS_CARVEOUT) += carveout-heap.o diff -Naur --no-dereference a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c --- a/drivers/firmware/ti_sci.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/firmware/ti_sci.c 2022-01-06 12:45:53.810318090 -0500 @@ -1674,6 +1674,7 @@ return ret; } req = (struct ti_sci_msg_req_reboot *)xfer->xfer_buf; + req->domain = 0; ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -1703,14 +1704,14 @@ * @subtype: Resource assignment subtype that is being requested * from the given device. * @s_host: Host processor ID to which the resources are allocated - * @range_start: Start index of the resource range - * @range_num: Number of resources in the range + * @desc: Pointer to ti_sci_resource_desc to be updated with the + * resource range start index and number of resources * * Return: 0 if all went fine, else return appropriate error. */ static int ti_sci_get_resource_range(const struct ti_sci_handle *handle, u32 dev_id, u8 subtype, u8 s_host, - u16 *range_start, u16 *range_num) + struct ti_sci_resource_desc *desc) { struct ti_sci_msg_resp_get_resource_range *resp; struct ti_sci_msg_req_get_resource_range *req; @@ -1721,7 +1722,7 @@ if (IS_ERR(handle)) return PTR_ERR(handle); - if (!handle) + if (!handle || !desc) return -EINVAL; info = handle_to_ti_sci_info(handle); @@ -1751,11 +1752,14 @@ if (!ti_sci_is_response_ack(resp)) { ret = -ENODEV; - } else if (!resp->range_start && !resp->range_num) { + } else if (!resp->range_num && !resp->range_num_sec) { + /* Neither of the two resource range is valid */ ret = -ENODEV; } else { - *range_start = resp->range_start; - *range_num = resp->range_num; + desc->start = resp->range_start; + desc->num = resp->range_num; + desc->start_sec = resp->range_start_sec; + desc->num_sec = resp->range_num_sec; }; fail: @@ -1771,18 +1775,18 @@ * @dev_id: TISCI device ID. * @subtype: Resource assignment subtype that is being requested * from the given device. - * @range_start: Start index of the resource range - * @range_num: Number of resources in the range + * @desc: Pointer to ti_sci_resource_desc to be updated with the + * resource range start index and number of resources * * Return: 0 if all went fine, else return appropriate error. */ static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle, u32 dev_id, u8 subtype, - u16 *range_start, u16 *range_num) + struct ti_sci_resource_desc *desc) { return ti_sci_get_resource_range(handle, dev_id, subtype, TI_SCI_IRQ_SECONDARY_HOST_INVALID, - range_start, range_num); + desc); } /** @@ -1793,18 +1797,17 @@ * @subtype: Resource assignment subtype that is being requested * from the given device. * @s_host: Host processor ID to which the resources are allocated - * @range_start: Start index of the resource range - * @range_num: Number of resources in the range + * @desc: Pointer to ti_sci_resource_desc to be updated with the + * resource range start index and number of resources * * Return: 0 if all went fine, else return appropriate error. */ static int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle, u32 dev_id, u8 subtype, u8 s_host, - u16 *range_start, u16 *range_num) + struct ti_sci_resource_desc *desc) { - return ti_sci_get_resource_range(handle, dev_id, subtype, s_host, - range_start, range_num); + return ti_sci_get_resource_range(handle, dev_id, subtype, s_host, desc); } /** @@ -2047,28 +2050,17 @@ } /** - * ti_sci_cmd_ring_config() - configure RA ring - * @handle: Pointer to TI SCI handle. - * @valid_params: Bitfield defining validity of ring configuration - * parameters - * @nav_id: Device ID of Navigator Subsystem from which the ring is - * allocated - * @index: Ring index - * @addr_lo: The ring base address lo 32 bits - * @addr_hi: The ring base address hi 32 bits - * @count: Number of ring elements - * @mode: The mode of the ring - * @size: The ring element size. - * @order_id: Specifies the ring's bus order ID + * ti_sci_cmd_rm_ring_cfg() - Configure a NAVSS ring + * @handle: Pointer to TI SCI handle. + * @params: Pointer to ti_sci_msg_rm_ring_cfg ring config structure * * Return: 0 if all went well, else returns appropriate error value. * - * See @ti_sci_msg_rm_ring_cfg_req for more info. + * See @ti_sci_msg_rm_ring_cfg and @ti_sci_msg_rm_ring_cfg_req for + * more info. */ -static int ti_sci_cmd_ring_config(const struct ti_sci_handle *handle, - u32 valid_params, u16 nav_id, u16 index, - u32 addr_lo, u32 addr_hi, u32 count, - u8 mode, u8 size, u8 order_id) +static int ti_sci_cmd_rm_ring_cfg(const struct ti_sci_handle *handle, + const struct ti_sci_msg_rm_ring_cfg *params) { struct ti_sci_msg_rm_ring_cfg_req *req; struct ti_sci_msg_hdr *resp; @@ -2092,15 +2084,17 @@ return ret; } req = (struct ti_sci_msg_rm_ring_cfg_req *)xfer->xfer_buf; - req->valid_params = valid_params; - req->nav_id = nav_id; - req->index = index; - req->addr_lo = addr_lo; - req->addr_hi = addr_hi; - req->count = count; - req->mode = mode; - req->size = size; - req->order_id = order_id; + req->valid_params = params->valid_params; + req->nav_id = params->nav_id; + req->index = params->index; + req->addr_lo = params->addr_lo; + req->addr_hi = params->addr_hi; + req->count = params->count; + req->mode = params->mode; + req->size = params->size; + req->order_id = params->order_id; + req->virtid = params->virtid; + req->asel = params->asel; ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -2109,90 +2103,11 @@ } resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; - ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; - -fail: - ti_sci_put_one_xfer(&info->minfo, xfer); - dev_dbg(dev, "RM_RA:config ring %u ret:%d\n", index, ret); - return ret; -} - -/** - * ti_sci_cmd_ring_get_config() - get RA ring configuration - * @handle: Pointer to TI SCI handle. - * @nav_id: Device ID of Navigator Subsystem from which the ring is - * allocated - * @index: Ring index - * @addr_lo: Returns ring's base address lo 32 bits - * @addr_hi: Returns ring's base address hi 32 bits - * @count: Returns number of ring elements - * @mode: Returns mode of the ring - * @size: Returns ring element size - * @order_id: Returns ring's bus order ID - * - * Return: 0 if all went well, else returns appropriate error value. - * - * See @ti_sci_msg_rm_ring_get_cfg_req for more info. - */ -static int ti_sci_cmd_ring_get_config(const struct ti_sci_handle *handle, - u32 nav_id, u32 index, u8 *mode, - u32 *addr_lo, u32 *addr_hi, - u32 *count, u8 *size, u8 *order_id) -{ - struct ti_sci_msg_rm_ring_get_cfg_resp *resp; - struct ti_sci_msg_rm_ring_get_cfg_req *req; - struct ti_sci_xfer *xfer; - struct ti_sci_info *info; - struct device *dev; - int ret = 0; - - if (IS_ERR_OR_NULL(handle)) - return -EINVAL; - - info = handle_to_ti_sci_info(handle); - dev = info->dev; - - xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_RM_RING_GET_CFG, - TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, - sizeof(*req), sizeof(*resp)); - if (IS_ERR(xfer)) { - ret = PTR_ERR(xfer); - dev_err(dev, - "RM_RA:Message get config failed(%d)\n", ret); - return ret; - } - req = (struct ti_sci_msg_rm_ring_get_cfg_req *)xfer->xfer_buf; - req->nav_id = nav_id; - req->index = index; - - ret = ti_sci_do_xfer(info, xfer); - if (ret) { - dev_err(dev, "RM_RA:Mbox get config send fail %d\n", ret); - goto fail; - } - - resp = (struct ti_sci_msg_rm_ring_get_cfg_resp *)xfer->xfer_buf; - - if (!ti_sci_is_response_ack(resp)) { - ret = -ENODEV; - } else { - if (mode) - *mode = resp->mode; - if (addr_lo) - *addr_lo = resp->addr_lo; - if (addr_hi) - *addr_hi = resp->addr_hi; - if (count) - *count = resp->count; - if (size) - *size = resp->size; - if (order_id) - *order_id = resp->order_id; - }; + ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL; fail: ti_sci_put_one_xfer(&info->minfo, xfer); - dev_dbg(dev, "RM_RA:get config ring %u ret:%d\n", index, ret); + dev_dbg(dev, "RM_RA:config ring %u ret:%d\n", params->index, ret); return ret; } @@ -2362,6 +2277,8 @@ req->fdepth = params->fdepth; req->tx_sched_priority = params->tx_sched_priority; req->tx_burst_size = params->tx_burst_size; + req->tx_tdtype = params->tx_tdtype; + req->extended_ch_type = params->extended_ch_type; ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -2921,8 +2838,7 @@ iops->free_irq = ti_sci_cmd_free_irq; iops->free_event_map = ti_sci_cmd_free_event_map; - rops->config = ti_sci_cmd_ring_config; - rops->get_config = ti_sci_cmd_ring_get_config; + rops->set_cfg = ti_sci_cmd_rm_ring_cfg; psilops->pair = ti_sci_cmd_rm_psil_pair; psilops->unpair = ti_sci_cmd_rm_psil_unpair; @@ -3157,12 +3073,18 @@ raw_spin_lock_irqsave(&res->lock, flags); for (set = 0; set < res->sets; set++) { - free_bit = find_first_zero_bit(res->desc[set].res_map, - res->desc[set].num); - if (free_bit != res->desc[set].num) { - set_bit(free_bit, res->desc[set].res_map); + struct ti_sci_resource_desc *desc = &res->desc[set]; + int res_count = desc->num + desc->num_sec; + + free_bit = find_first_zero_bit(desc->res_map, res_count); + if (free_bit != res_count) { + set_bit(free_bit, desc->res_map); raw_spin_unlock_irqrestore(&res->lock, flags); - return res->desc[set].start + free_bit; + + if (desc->num && free_bit < desc->num) + return desc->start + free_bit; + else + return desc->start_sec + free_bit; } } raw_spin_unlock_irqrestore(&res->lock, flags); @@ -3183,10 +3105,14 @@ raw_spin_lock_irqsave(&res->lock, flags); for (set = 0; set < res->sets; set++) { - if (res->desc[set].start <= id && - (res->desc[set].num + res->desc[set].start) > id) - clear_bit(id - res->desc[set].start, - res->desc[set].res_map); + struct ti_sci_resource_desc *desc = &res->desc[set]; + + if (desc->num && desc->start <= id && + (desc->start + desc->num) > id) + clear_bit(id - desc->start, desc->res_map); + else if (desc->num_sec && desc->start_sec <= id && + (desc->start_sec + desc->num_sec) > id) + clear_bit(id - desc->start_sec, desc->res_map); } raw_spin_unlock_irqrestore(&res->lock, flags); } @@ -3203,7 +3129,7 @@ u32 set, count = 0; for (set = 0; set < res->sets; set++) - count += res->desc[set].num; + count += res->desc[set].num + res->desc[set].num_sec; return count; } @@ -3227,7 +3153,7 @@ { struct ti_sci_resource *res; bool valid_set = false; - int i, ret; + int i, ret, res_count; res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); if (!res) @@ -3242,23 +3168,23 @@ for (i = 0; i < res->sets; i++) { ret = handle->ops.rm_core_ops.get_range(handle, dev_id, sub_types[i], - &res->desc[i].start, - &res->desc[i].num); + &res->desc[i]); if (ret) { dev_dbg(dev, "dev = %d subtype %d not allocated for this host\n", dev_id, sub_types[i]); - res->desc[i].start = 0; - res->desc[i].num = 0; + memset(&res->desc[i], 0, sizeof(res->desc[i])); continue; } - dev_dbg(dev, "dev = %d, subtype = %d, start = %d, num = %d\n", + dev_dbg(dev, "dev/sub_type: %d/%d, start/num: %d/%d | %d/%d\n", dev_id, sub_types[i], res->desc[i].start, - res->desc[i].num); + res->desc[i].num, res->desc[i].start_sec, + res->desc[i].num_sec); valid_set = true; + res_count = res->desc[i].num + res->desc[i].num_sec; res->desc[i].res_map = - devm_kzalloc(dev, BITS_TO_LONGS(res->desc[i].num) * + devm_kzalloc(dev, BITS_TO_LONGS(res_count) * sizeof(*res->desc[i].res_map), GFP_KERNEL); if (!res->desc[i].res_map) return ERR_PTR(-ENOMEM); diff -Naur --no-dereference a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h --- a/drivers/firmware/ti_sci.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/firmware/ti_sci.h 2022-01-06 12:45:53.810318090 -0500 @@ -49,7 +49,6 @@ #define TI_SCI_MSG_RM_RING_RECONFIG 0x1102 #define TI_SCI_MSG_RM_RING_RESET 0x1103 #define TI_SCI_MSG_RM_RING_CFG 0x1110 -#define TI_SCI_MSG_RM_RING_GET_CFG 0x1111 /* PSI-L requests */ #define TI_SCI_MSG_RM_PSIL_PAIR 0x1280 @@ -125,12 +124,14 @@ /** * struct ti_sci_msg_req_reboot - Reboot the SoC * @hdr: Generic Header + * @domain: Domain to be reset, 0 for full SoC reboot * * Request type is TI_SCI_MSG_SYS_RESET, responded with a generic * ACK/NACK message. */ struct ti_sci_msg_req_reboot { struct ti_sci_msg_hdr hdr; + u8 domain; } __packed; /** @@ -574,8 +575,10 @@ /** * struct ti_sci_msg_resp_get_resource_range - Response to resource get range. * @hdr: Generic Header - * @range_start: Start index of the resource range. - * @range_num: Number of resources in the range. + * @range_start: Start index of the first resource range. + * @range_num: Number of resources in the first range. + * @range_start_sec: Start index of the second resource range. + * @range_num_sec: Number of resources in the second range. * * Response to request TI_SCI_MSG_GET_RESOURCE_RANGE. */ @@ -583,6 +586,8 @@ struct ti_sci_msg_hdr hdr; u16 range_start; u16 range_num; + u16 range_start_sec; + u16 range_num_sec; } __packed; /** @@ -656,6 +661,8 @@ * 3 - Valid bit for @tisci_msg_rm_ring_cfg_req mode * 4 - Valid bit for @tisci_msg_rm_ring_cfg_req size * 5 - Valid bit for @tisci_msg_rm_ring_cfg_req order_id + * 6 - Valid bit for @tisci_msg_rm_ring_cfg_req virtid + * 7 - Valid bit for @tisci_msg_rm_ring_cfg_req ASEL * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated * @index: ring index to be configured. * @addr_lo: 32 LSBs of ring base address to be programmed into the ring's @@ -669,6 +676,9 @@ * the formula (log2(size_bytes) - 2), where size_bytes cannot be * greater than 256. * @order_id: Specifies the ring's bus order ID. + * @virtid: Ring virt ID value + * @asel: Ring ASEL (address select) value to be set into the ASEL field of the + * ring's RING_BA_HI register. */ struct ti_sci_msg_rm_ring_cfg_req { struct ti_sci_msg_hdr hdr; @@ -681,49 +691,8 @@ u8 mode; u8 size; u8 order_id; -} __packed; - -/** - * struct ti_sci_msg_rm_ring_get_cfg_req - Get RA ring's configuration - * - * Gets the configuration of the non-real-time register fields of a ring. The - * host, or a supervisor of the host, who owns the ring must be the requesting - * host. The values of the non-real-time registers are returned in - * @ti_sci_msg_rm_ring_get_cfg_resp. - * - * @hdr: Generic Header - * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated - * @index: ring index. - */ -struct ti_sci_msg_rm_ring_get_cfg_req { - struct ti_sci_msg_hdr hdr; - u16 nav_id; - u16 index; -} __packed; - -/** - * struct ti_sci_msg_rm_ring_get_cfg_resp - Ring get configuration response - * - * Response received by host processor after RM has handled - * @ti_sci_msg_rm_ring_get_cfg_req. The response contains the ring's - * non-real-time register values. - * - * @hdr: Generic Header - * @addr_lo: Ring 32 LSBs of base address - * @addr_hi: Ring 16 MSBs of base address. - * @count: Ring number of elements. - * @mode: Ring mode. - * @size: encoded Ring element size - * @order_id: ing order ID. - */ -struct ti_sci_msg_rm_ring_get_cfg_resp { - struct ti_sci_msg_hdr hdr; - u32 addr_lo; - u32 addr_hi; - u32 count; - u8 mode; - u8 size; - u8 order_id; + u16 virtid; + u8 asel; } __packed; /** @@ -910,6 +879,8 @@ * 12 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_credit_count * 13 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::fdepth * 14 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_burst_size + * 15 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_tdtype + * 16 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::extended_ch_type * * @nav_id: SoC device ID of Navigator Subsystem where tx channel is located * @@ -973,6 +944,15 @@ * * @tx_burst_size: UDMAP transmit channel burst size configuration to be * programmed into the tx_burst_size field of the TCHAN_TCFG register. + * + * @tx_tdtype: UDMAP transmit channel teardown type configuration to be + * programmed into the tdtype field of the TCHAN_TCFG register: + * 0 - Return immediately + * 1 - Wait for completion message from remote peer + * + * @extended_ch_type: Valid for BCDMA. + * 0 - the channel is split tx channel (tchan) + * 1 - the channel is block copy channel (bchan) */ struct ti_sci_msg_rm_udmap_tx_ch_cfg_req { struct ti_sci_msg_hdr hdr; @@ -994,6 +974,8 @@ u16 fdepth; u8 tx_sched_priority; u8 tx_burst_size; + u8 tx_tdtype; + u8 extended_ch_type; } __packed; /** diff -Naur --no-dereference a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c --- a/drivers/gpio/gpiolib.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpio/gpiolib.c 2022-01-06 12:45:53.810318090 -0500 @@ -784,9 +784,11 @@ ida_free(&gpio_ida, gdev->id); err_free_gdev: /* failures here can mean systems won't boot... */ - pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, - gdev->base, gdev->base + gdev->ngpio - 1, - gc->label ? : "generic", ret); + if (ret != -EPROBE_DEFER) { + pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, + gdev->base, gdev->base + gdev->ngpio - 1, + gc->label ? : "generic", ret); + } kfree(gdev); return ret; } @@ -2000,10 +2002,10 @@ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { desc_set_label(desc, label ? : "?"); ret = 0; - } else { - kfree_const(label); - ret = -EBUSY; - goto done; +// } else { +// kfree_const(label); +// ret = -EBUSY; +// goto done; } if (gc->request) { diff -Naur --no-dereference a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c --- a/drivers/gpio/gpiolib-sysfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpio/gpiolib-sysfs.c 2022-01-06 12:45:53.810318090 -0500 @@ -38,10 +38,10 @@ /* * /sys/class/gpio/gpioN... only for GPIOs that are exported * /direction - * * MAY BE OMITTED if kernel won't allow direction changes * * is read/write as "in" or "out" * * may also be written as "high" or "low", initializing * output value as specified ("out" implies "low") + * * read-only if kernel won't allow direction changes * /value * * always readable, subject to hardware behavior * * may be writable, as zero/nonzero @@ -54,6 +54,8 @@ * * is read/write as zero/nonzero * * also affects existing and subsequent "falling" and "rising" * /edge configuration + * /label + * * descriptor label */ static ssize_t direction_show(struct device *dev, @@ -84,7 +86,9 @@ mutex_lock(&data->mutex); - if (sysfs_streq(buf, "high")) + if (!data->direction_can_change) + status = -EPERM; + else if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) status = gpiod_direction_output_raw(desc, 0); @@ -363,6 +367,23 @@ } static DEVICE_ATTR_RW(active_low); +static ssize_t label_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; + ssize_t status; + + mutex_lock(&data->mutex); + + status = sprintf(buf, "%s\n", desc->label); + + mutex_unlock(&data->mutex); + + return status; +} +static DEVICE_ATTR_RO(label); + static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -374,12 +395,15 @@ if (attr == &dev_attr_direction.attr) { if (!show_direction) - mode = 0; + mode &= 0444; } else if (attr == &dev_attr_edge.attr) { if (gpiod_to_irq(desc) < 0) mode = 0; if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) mode = 0; + } else if (attr == &dev_attr_value.attr) { + if (!show_direction && !test_bit(FLAG_IS_OUT, &desc->flags)) + mode &= 0444; } return mode; @@ -390,6 +414,7 @@ &dev_attr_edge.attr, &dev_attr_value.attr, &dev_attr_active_low.attr, + &dev_attr_label.attr, NULL, }; @@ -403,6 +428,10 @@ NULL }; +/* bwlegh, a second device in the same file... get out of my namespace! */ +#define dev_attr_label dev_attr_chip_label +#define label_show chip_label_show + /* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) diff -Naur --no-dereference a/drivers/gpio/gpio-of-helper.c b/drivers/gpio/gpio-of-helper.c --- a/drivers/gpio/gpio-of-helper.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpio/gpio-of-helper.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,422 @@ +/* + * GPIO OF based helper + * + * A simple DT based driver to provide access to GPIO functionality + * to user-space via sysfs. + * + * Copyright (C) 2013 Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* fwd decl. */ +struct gpio_of_helper_info; + +enum gpio_type { + GPIO_TYPE_INPUT = 0, + GPIO_TYPE_OUTPUT = 1, +}; + +struct gpio_of_entry { + int id; + struct gpio_of_helper_info *info; + struct device_node *node; + enum gpio_type type; + int gpio; + int irq; + const char *name; + atomic64_t counter; + unsigned int count_flags; +#define COUNT_RISING_EDGE (1 << 0) +#define COUNT_FALLING_EDGE (1 << 1) +}; + +struct gpio_of_helper_info { + struct platform_device *pdev; + struct idr idr; +}; + +static const struct of_device_id gpio_of_helper_of_match[] = { + { + .compatible = "gpio-of-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_of_helper_of_match); + +static ssize_t gpio_of_helper_show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_of_helper_info *info = platform_get_drvdata(pdev); + struct gpio_of_entry *entry; + char *p, *e; + int id, n; + + p = buf; + e = p + PAGE_SIZE; + n = 0; + idr_for_each_entry(&info->idr, entry, id) { + switch (entry->type) { + case GPIO_TYPE_INPUT: + n = snprintf(p, e - p, "%2d %-24s %3d %-3s %llu\n", + entry->id, entry->name, entry->gpio, "IN", + (unsigned long long) + atomic64_read(&entry->counter)); + break; + case GPIO_TYPE_OUTPUT: + n = snprintf(p, e - p, "%2d %-24s %3d %-3s\n", + entry->id, entry->name, entry->gpio, "OUT"); + break; + } + p += n; + } + + return p - buf; +} + +static DEVICE_ATTR(status, S_IRUGO, + gpio_of_helper_show_status, NULL); + +static irqreturn_t gpio_of_helper_handler(int irq, void *ptr) +{ + struct gpio_of_entry *entry = ptr; + + /* caution - low speed interfaces only! */ + atomic64_inc(&entry->counter); + + return IRQ_HANDLED; +} + +static struct gpio_of_entry * +gpio_of_entry_create(struct gpio_of_helper_info *info, + struct device_node *node) +{ + struct platform_device *pdev = info->pdev; + struct device *dev = &pdev->dev; + struct gpio_of_entry *entry; + int err, gpio, irq; + unsigned int req_flags, count_flags, irq_flags; + enum gpio_type type; + enum of_gpio_flags gpio_flags; + const char *name; + + /* get the type of the node first */ + if (of_property_read_bool(node, "input")) + type = GPIO_TYPE_INPUT; + else if (of_property_read_bool(node, "output") + || of_property_read_bool(node, "init-low") + || of_property_read_bool(node, "init-high")) + type = GPIO_TYPE_OUTPUT; + else { + dev_err(dev, "Not valid gpio node type\n"); + err = -EINVAL; + goto err_bad_node; + } + + /* get the name */ + if (of_property_read_string(node, "line-name", &name)) + if (of_property_read_string(node, "gpio-name", &name)) + name = node->name; + + err = of_get_named_gpio_flags(node, "gpio", 0, &gpio_flags); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "Failed to get gpio property of '%s'\n", name); + goto err_bad_node; + } + gpio = err; + + req_flags = 0; + count_flags = 0; + + /* set the request flags */ + switch (type) { + case GPIO_TYPE_INPUT: + req_flags = GPIOF_DIR_IN | GPIOF_EXPORT; + if (of_property_read_bool(node, "count-falling-edge")) + count_flags |= COUNT_FALLING_EDGE; + if (of_property_read_bool(node, "count-rising-edge")) + count_flags |= COUNT_RISING_EDGE; + break; + case GPIO_TYPE_OUTPUT: + req_flags = GPIOF_DIR_OUT | GPIOF_EXPORT; + if (of_property_read_bool(node, "init-high")) + req_flags |= GPIOF_OUT_INIT_HIGH; + else if (of_property_read_bool(node, "init-low")) + req_flags |= GPIOF_OUT_INIT_LOW; + break; + } + if (of_property_read_bool(node, "dir-changeable")) + req_flags |= GPIOF_EXPORT_CHANGEABLE; + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + req_flags |= GPIOF_ACTIVE_LOW; + if (gpio_flags & OF_GPIO_SINGLE_ENDED) { + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + req_flags |= GPIOF_OPEN_DRAIN; + else + req_flags |= GPIOF_OPEN_SOURCE; + } + + /* request the gpio */ + err = devm_gpio_request_one(dev, gpio, req_flags, name); + if (err != 0) { + dev_err(dev, "Failed to request gpio '%s'\n", name); + goto err_bad_node; + } + + irq = -1; + irq_flags = 0; + + /* counter mode requested - need an interrupt */ + if (count_flags != 0) { + irq = gpio_to_irq(gpio); + if (IS_ERR_VALUE(irq)) { + dev_err(dev, "Failed to request gpio '%s'\n", name); + goto err_bad_node; + } + + if (count_flags & COUNT_RISING_EDGE) + irq_flags |= IRQF_TRIGGER_RISING; + if (count_flags & COUNT_FALLING_EDGE) + irq_flags |= IRQF_TRIGGER_FALLING; + } + + idr_preload(GFP_KERNEL); + + entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + dev_err(dev, "Failed to allocate gpio entry of '%s'\n", name); + err = -ENOMEM; + goto err_no_mem; + } + + entry->id = -1; + entry->info = info; + entry->node = of_node_get(node); /* get node reference */ + entry->type = type; + entry->gpio = gpio; + entry->irq = irq; + entry->name = name; + + /* interrupt enable is last thing done */ + if (irq >= 0) { + atomic64_set(&entry->counter, 0); + entry->count_flags = count_flags; + err = devm_request_irq(dev, irq, gpio_of_helper_handler, + irq_flags, name, entry); + if (err != 0) { + dev_err(dev, "Failed to request irq of '%s'\n", name); + goto err_no_irq; + } + } + + err = idr_alloc(&info->idr, entry, 0, 0, GFP_NOWAIT); + if (err >= 0) + entry->id = err; + + idr_preload_end(); + + if (err < 0) { + dev_err(dev, "Failed to idr_get_new of '%s'\n", name); + goto err_fail_idr; + } + + dev_info(dev, "Allocated GPIO id=%d name='%s'\n", entry->id, name); + + return entry; + +err_fail_idr: + /* nothing to do */ +err_no_irq: + /* release node ref */ + of_node_put(node); + /* nothing else needs to be done, devres handles it */ +err_no_mem: +err_bad_node: + return ERR_PTR(err); +} + +static int gpio_of_entry_destroy(struct gpio_of_entry *entry) +{ + struct gpio_of_helper_info *info = entry->info; + struct platform_device *pdev = info->pdev; + struct device *dev = &pdev->dev; + + dev_dbg(dev, "Destroying GPIO id=%d\n", entry->id); + + /* remove from the IDR */ + idr_remove(&info->idr, entry->id); + + /* remove node ref */ + of_node_put(entry->node); + + /* free gpio */ + devm_gpio_free(dev, entry->gpio); + + /* gree irq */ + if (entry->irq >= 0) + devm_free_irq(dev, entry->irq, entry); + + /* and free */ + devm_kfree(dev, entry); + + return 0; +} + +static int gpio_of_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_of_helper_info *info; + struct gpio_of_entry *entry; + struct device_node *pnode = pdev->dev.of_node; + struct device_node *cnode; + struct pinctrl *pinctrl; + int err; + + /* we only support OF */ + if (pnode == NULL) { + dev_err(&pdev->dev, "No platform of_node!\n"); + return -ENODEV; + } + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + /* special handling for probe defer */ + if (PTR_ERR(pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + } + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&pdev->dev, "Failed to allocate info\n"); + err = -ENOMEM; + goto err_no_mem; + } + platform_set_drvdata(pdev, info); + info->pdev = pdev; + + idr_init(&info->idr); + + err = device_create_file(dev, &dev_attr_status); + if (err != 0) { + dev_err(dev, "Failed to create status sysfs attribute\n"); + goto err_no_sysfs; + } + + for_each_child_of_node(pnode, cnode) { + if (!of_device_is_available(cnode)) + continue; + + entry = gpio_of_entry_create(info, cnode); + if (IS_ERR_OR_NULL(entry)) { + dev_err(dev, "Failed to create gpio entry\n"); + err = PTR_ERR(entry); + goto err_fail_entry; + } + } + + dev_info(&pdev->dev, "ready\n"); + + return 0; +err_fail_entry: + device_remove_file(&pdev->dev, &dev_attr_status); +err_no_sysfs: +err_no_mem: + return err; +} + +static int gpio_of_helper_remove(struct platform_device *pdev) +{ + struct gpio_of_helper_info *info = platform_get_drvdata(pdev); + struct gpio_of_entry *entry; + int id; + + dev_info(&pdev->dev, "removing\n"); + + device_remove_file(&pdev->dev, &dev_attr_status); + + id = 0; + idr_for_each_entry(&info->idr, entry, id) { + /* destroy each and every one */ + gpio_of_entry_destroy(entry); + } + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_of_helper_runtime_suspend(struct device *dev) +{ + /* place holder */ + return 0; +} + +static int gpio_of_helper_runtime_resume(struct device *dev) +{ + /* place holder */ + return 0; +} + +static struct dev_pm_ops gpio_of_helper_pm_ops = { + SET_RUNTIME_PM_OPS(gpio_of_helper_runtime_suspend, + gpio_of_helper_runtime_resume, NULL) +}; +#define GPIO_OF_HELPER_PM_OPS (&gpio_of_helper_pm_ops) +#else +#define GPIO_OF_HELPER_PM_OPS NULL +#endif /* CONFIG_PM */ + +struct platform_driver gpio_of_helper_driver = { + .probe = gpio_of_helper_probe, + .remove = gpio_of_helper_remove, + .driver = { + .name = "gpio-of-helper", + .owner = THIS_MODULE, + .pm = GPIO_OF_HELPER_PM_OPS, + .of_match_table = gpio_of_helper_of_match, + }, +}; + +module_platform_driver(gpio_of_helper_driver); + +MODULE_AUTHOR("Pantelis Antoniou "); +MODULE_DESCRIPTION("GPIO OF Helper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-of-helper"); diff -Naur --no-dereference a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c --- a/drivers/gpio/gpio-omap.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpio/gpio-omap.c 2022-01-06 12:45:53.810318090 -0500 @@ -1050,11 +1050,8 @@ irq->first = irq_base; ret = gpiochip_add_data(&bank->chip, bank); - if (ret) { - dev_err(bank->chip.parent, - "Could not register gpio chip %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(bank->chip.parent, ret, "Could not register gpio chip\n"); ret = devm_request_irq(bank->chip.parent, bank->irq, omap_gpio_irq_handler, diff -Naur --no-dereference a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig --- a/drivers/gpio/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpio/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -94,6 +94,20 @@ If unsure, say Y. +config GPIO_OF_HELPER + bool "GPIO OF helper device (EXPERIMENTAL)" + depends on OF_GPIO + help + Say Y here to add an GPIO OF helper driver + + Allows you specify a GPIO helper based on OF + which allows simple export of GPIO functionality + in user-space. + + Features include, value set/get, direction control, + interrupt/value change poll support, event counting + and others. + config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate diff -Naur --no-dereference a/drivers/gpio/Makefile b/drivers/gpio/Makefile --- a/drivers/gpio/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpio/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +obj-$(CONFIG_GPIO_OF_HELPER) += gpio-of-helper.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o diff -Naur --no-dereference a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c --- a/drivers/gpu/drm/bridge/display-connector.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/bridge/display-connector.c 2022-01-06 12:45:53.810318090 -0500 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,8 @@ struct gpio_desc *hpd_gpio; int hpd_irq; + + struct regulator *dp_pwr; }; static inline struct display_connector * @@ -172,11 +175,12 @@ of_property_read_string(pdev->dev.of_node, "label", &label); /* - * Get the HPD GPIO for DVI and HDMI connectors. If the GPIO can provide + * Get the HPD GPIO for DVI, HDMI and DP connectors. If the GPIO can provide * edge interrupts, register an interrupt handler. */ if (type == DRM_MODE_CONNECTOR_DVII || - type == DRM_MODE_CONNECTOR_HDMIA) { + type == DRM_MODE_CONNECTOR_HDMIA || + type == DRM_MODE_CONNECTOR_DisplayPort) { conn->hpd_gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN); if (IS_ERR(conn->hpd_gpio)) { @@ -223,6 +227,38 @@ } } + /* Get the DP PWR for DP connector. */ + if (type == DRM_MODE_CONNECTOR_DisplayPort) { + int ret; + + conn->dp_pwr = devm_regulator_get_optional(&pdev->dev, "dp-pwr"); + + if (IS_ERR(conn->dp_pwr)) { + ret = PTR_ERR(conn->dp_pwr); + + switch (ret) { + case -ENODEV: + conn->dp_pwr = NULL; + break; + + case -EPROBE_DEFER: + return -EPROBE_DEFER; + + default: + dev_err(&pdev->dev, "failed to get DP PWR regulator: %d\n", ret); + return ret; + } + } + + if (conn->dp_pwr) { + ret = regulator_enable(conn->dp_pwr); + if (ret) { + dev_err(&pdev->dev, "failed to enable DP PWR regulator: %d\n", ret); + return ret; + } + } + } + conn->bridge.funcs = &display_connector_bridge_funcs; conn->bridge.of_node = pdev->dev.of_node; @@ -251,6 +287,9 @@ { struct display_connector *conn = platform_get_drvdata(pdev); + if (conn->dp_pwr) + regulator_disable(conn->dp_pwr); + drm_bridge_remove(&conn->bridge); if (!IS_ERR(conn->bridge.ddc)) @@ -275,6 +314,9 @@ }, { .compatible = "vga-connector", .data = (void *)DRM_MODE_CONNECTOR_VGA, + }, { + .compatible = "dp-connector", + .data = (void *)DRM_MODE_CONNECTOR_DisplayPort, }, {}, }; diff -Naur --no-dereference a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c --- a/drivers/gpu/drm/drm_atomic_helper.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/drm_atomic_helper.c 2022-01-06 12:45:53.810318090 -0500 @@ -3160,6 +3160,7 @@ struct drm_connector_list_iter conn_iter; struct drm_plane *plane; struct drm_crtc *crtc; + struct drm_private_obj *privobj; int err = 0; state = drm_atomic_state_alloc(dev); @@ -3189,6 +3190,16 @@ } } + drm_for_each_privobj(privobj, dev) { + struct drm_private_state *priv_state; + + priv_state = drm_atomic_get_private_obj_state(state, privobj); + if (IS_ERR(priv_state)) { + err = PTR_ERR(priv_state); + goto free; + } + } + drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(conn, &conn_iter) { struct drm_connector_state *conn_state; @@ -3296,12 +3307,17 @@ struct drm_connector_state *new_conn_state; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; + struct drm_private_obj *privobj; + struct drm_private_state *new_priv_state; state->acquire_ctx = ctx; for_each_new_plane_in_state(state, plane, new_plane_state, i) state->planes[i].old_state = plane->state; + for_each_new_private_obj_in_state(state, privobj, new_priv_state, i) + state->private_objs[i].old_state = privobj->state; + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) state->crtcs[i].old_state = crtc->state; diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c --- a/drivers/gpu/drm/omapdrm/dss/dispc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c 2022-01-06 12:45:53.810318090 -0500 @@ -92,6 +92,8 @@ u8 mgr_height_start; u16 mgr_width_max; u16 mgr_height_max; + u16 ovl_width_max; + u16 ovl_height_max; unsigned long max_lcd_pclk; unsigned long max_tv_pclk; unsigned int max_downscale; @@ -892,32 +894,91 @@ #undef CVAL } -static void dispc_setup_color_conv_coef(struct dispc_device *dispc) -{ - int i; - int num_ovl = dispc_get_num_ovls(dispc); +/* YUV -> RGB, ITU-R BT.601, full range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_full = { + 256, 0, 358, /* ry, rcb, rcr |1.000 0.000 1.402|*/ + 256, -88, -182, /* gy, gcb, gcr |1.000 -0.344 -0.714|*/ + 256, 452, 0, /* by, bcb, bcr |1.000 1.772 0.000|*/ + true, /* full range */ +}; - /* YUV -> RGB, ITU-R BT.601, limited range */ - const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_lim = { - 298, 0, 409, /* ry, rcb, rcr */ - 298, -100, -208, /* gy, gcb, gcr */ - 298, 516, 0, /* by, bcb, bcr */ - false, /* limited range */ - }; +/* YUV -> RGB, ITU-R BT.601, limited range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_lim = { + 298, 0, 409, /* ry, rcb, rcr |1.164 0.000 1.596|*/ + 298, -100, -208, /* gy, gcb, gcr |1.164 -0.392 -0.813|*/ + 298, 516, 0, /* by, bcb, bcr |1.164 2.017 0.000|*/ + false, /* limited range */ +}; - /* RGB -> YUV, ITU-R BT.601, limited range */ - const struct csc_coef_rgb2yuv coefs_rgb2yuv_bt601_lim = { - 66, 129, 25, /* yr, yg, yb */ - -38, -74, 112, /* cbr, cbg, cbb */ - 112, -94, -18, /* crr, crg, crb */ - false, /* limited range */ - }; +/* YUV -> RGB, ITU-R BT.709, full range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt709_full = { + 256, 0, 402, /* ry, rcb, rcr |1.000 0.000 1.570|*/ + 256, -48, -120, /* gy, gcb, gcr |1.000 -0.187 -0.467|*/ + 256, 475, 0, /* by, bcb, bcr |1.000 1.856 0.000|*/ + true, /* full range */ +}; - for (i = 1; i < num_ovl; i++) - dispc_ovl_write_color_conv_coef(dispc, i, &coefs_yuv2rgb_bt601_lim); +/* YUV -> RGB, ITU-R BT.709, limited range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt709_lim = { + 298, 0, 459, /* ry, rcb, rcr |1.164 0.000 1.793|*/ + 298, -55, -136, /* gy, gcb, gcr |1.164 -0.213 -0.533|*/ + 298, 541, 0, /* by, bcb, bcr |1.164 2.112 0.000|*/ + false, /* limited range */ +}; - if (dispc->feat->has_writeback) - dispc_wb_write_color_conv_coef(dispc, &coefs_rgb2yuv_bt601_lim); +/* RGB -> YUV, ITU-R BT.601, limited range */ +static const struct csc_coef_rgb2yuv coefs_rgb2yuv_bt601_lim = { + 66, 129, 25, /* yr, yg, yb | 0.257 0.504 0.098|*/ + -38, -74, 112, /* cbr, cbg, cbb |-0.148 -0.291 0.439|*/ + 112, -94, -18, /* crr, crg, crb | 0.439 -0.368 -0.071|*/ + false, /* limited range */ +}; + +/* RGB -> YUV, ITU-R BT.601, full range */ +static const struct csc_coef_rgb2yuv coefs_rgb2yuv_bt601_full = { + 77, 150, 29, /* yr, yg, yb | 0.299 0.587 0.114|*/ + -43, -85, 128, /* cbr, cbg, cbb |-0.173 -0.339 0.511|*/ + 128, -107, -21, /* crr, crg, crb | 0.511 -0.428 -0.083|*/ + true, /* full range */ +}; + +/* RGB -> YUV, ITU-R BT.709, limited range */ +static const struct csc_coef_rgb2yuv coefs_rgb2yuv_bt701_lim = { + 47, 157, 16, /* yr, yg, yb | 0.1826 0.6142 0.0620|*/ + -26, -87, 112, /* cbr, cbg, cbb |-0.1006 -0.3386 0.4392|*/ + 112, -102, -10, /* crr, crg, crb | 0.4392 -0.3989 -0.0403|*/ + false, /* limited range */ +}; + +static int dispc_ovl_set_csc(struct dispc_device *dispc, + enum omap_plane_id plane, + enum drm_color_encoding color_encoding, + enum drm_color_range color_range) +{ + const struct csc_coef_yuv2rgb *csc; + + switch (color_encoding) { + case DRM_COLOR_YCBCR_BT601: + if (color_range == DRM_COLOR_YCBCR_FULL_RANGE) + csc = &coefs_yuv2rgb_bt601_full; + else + csc = &coefs_yuv2rgb_bt601_lim; + break; + case DRM_COLOR_YCBCR_BT709: + if (color_range == DRM_COLOR_YCBCR_FULL_RANGE) + csc = &coefs_yuv2rgb_bt709_full; + else + csc = &coefs_yuv2rgb_bt709_lim; + break; + default: + DSSERR("Unsupported CSC mode %d for plane %d\n", + color_encoding, plane); + return -EINVAL; + } + + dispc_ovl_write_color_conv_coef(dispc, plane, csc); + + return 0; } static void dispc_ovl_set_ba0(struct dispc_device *dispc, @@ -2475,6 +2536,12 @@ return 0; } +static enum omap_overlay_caps dispc_ovl_get_caps(struct dispc_device *dispc, + enum omap_plane_id plane) +{ + return dispc->feat->overlay_caps[plane]; +} + #define DIV_FRAC(dividend, divisor) \ ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100)) @@ -2587,6 +2654,13 @@ return 0; } +static void dispc_ovl_get_max_size(struct dispc_device *dispc, + u16 *width, u16 *height) +{ + *width = dispc->feat->ovl_width_max; + *height = dispc->feat->ovl_height_max; +} + static int dispc_ovl_setup_common(struct dispc_device *dispc, enum omap_plane_id plane, enum omap_overlay_caps caps, @@ -2598,7 +2672,9 @@ u8 pre_mult_alpha, u8 global_alpha, enum omap_dss_rotation_type rotation_type, bool replication, const struct videomode *vm, - bool mem_to_mem) + bool mem_to_mem, + enum drm_color_encoding color_encoding, + enum drm_color_range color_range) { bool five_taps = true; bool fieldmode = false; @@ -2747,6 +2823,9 @@ fieldmode, fourcc, rotation); dispc_ovl_set_output_size(dispc, plane, out_width, out_height); dispc_ovl_set_vid_color_conv(dispc, plane, cconv); + + if (plane != OMAP_DSS_WB) + dispc_ovl_set_csc(dispc, plane, color_encoding, color_range); } dispc_ovl_set_rotation_attrs(dispc, plane, rotation, rotation_type, @@ -2783,7 +2862,8 @@ oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height, oi->fourcc, oi->rotation, oi->zorder, oi->pre_mult_alpha, oi->global_alpha, - oi->rotation_type, replication, vm, mem_to_mem); + oi->rotation_type, replication, vm, mem_to_mem, + oi->color_encoding, oi->color_range); return r; } @@ -2816,7 +2896,8 @@ wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width, wi->height, wi->fourcc, wi->rotation, zorder, wi->pre_mult_alpha, global_alpha, wi->rotation_type, - replication, vm, mem_to_mem); + replication, vm, mem_to_mem, DRM_COLOR_YCBCR_BT601, + DRM_COLOR_YCBCR_LIMITED_RANGE); if (r) return r; @@ -2976,7 +3057,7 @@ info->trans_key); dispc_mgr_enable_trans_key(dispc, channel, info->trans_enabled); dispc_mgr_enable_alpha_fixed_zorder(dispc, channel, - info->partial_alpha_enabled); + info->alpha_blender_enabled); if (dispc_has_feature(dispc, FEAT_CPR)) { dispc_mgr_enable_cpr(dispc, channel, info->cpr_enable); dispc_mgr_set_cpr_coef(dispc, channel, &info->cpr_coefs); @@ -3927,7 +4008,8 @@ dispc->feat->has_gamma_table) REG_FLD_MOD(dispc, DISPC_CONFIG, 1, 9, 9); - dispc_setup_color_conv_coef(dispc); + if (dispc->feat->has_writeback) + dispc_wb_write_color_conv_coef(dispc, &coefs_rgb2yuv_bt601_full); dispc_set_loadmode(dispc, OMAP_DSS_LOAD_FRAME_ONLY); @@ -4223,6 +4305,8 @@ .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 66500000, .max_downscale = 2, /* @@ -4261,6 +4345,8 @@ .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 173000000, .max_tv_pclk = 59000000, .max_downscale = 4, @@ -4296,6 +4382,8 @@ .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 173000000, .max_tv_pclk = 59000000, .max_downscale = 4, @@ -4331,6 +4419,8 @@ .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 173000000, .max_tv_pclk = 59000000, .max_downscale = 4, @@ -4366,6 +4456,8 @@ .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 173000000, .max_tv_pclk = 59000000, .max_downscale = 4, @@ -4401,6 +4493,8 @@ .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 170000000, .max_tv_pclk = 185625000, .max_downscale = 4, @@ -4440,8 +4534,10 @@ .mgr_height_start = 27, .mgr_width_max = 4096, .mgr_height_max = 4096, + .ovl_width_max = 2048, + .ovl_height_max = 4096, .max_lcd_pclk = 170000000, - .max_tv_pclk = 186000000, + .max_tv_pclk = 192000000, .max_downscale = 4, .max_line_width = 2048, .min_pcd = 1, @@ -4571,7 +4667,7 @@ .mgri = { .default_color = 0, .trans_enabled = false, - .partial_alpha_enabled = false, + .alpha_blender_enabled = false, .cpr_enable = false, }, .lcd_conf = { @@ -4714,6 +4810,9 @@ .ovl_enable = dispc_ovl_enable, .ovl_setup = dispc_ovl_setup, .ovl_get_color_modes = dispc_ovl_get_color_modes, + .ovl_color_mode_supported = dispc_ovl_color_mode_supported, + .ovl_get_caps = dispc_ovl_get_caps, + .ovl_get_max_size = dispc_ovl_get_max_size, .wb_get_framedone_irq = dispc_wb_get_framedone_irq, .wb_setup = dispc_wb_setup, diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h 2022-01-06 12:45:53.810318090 -0500 @@ -14,6 +14,7 @@ #include #include #include +#include #define DISPC_IRQ_FRAMEDONE (1 << 0) #define DISPC_IRQ_VSYNC (1 << 1) @@ -243,6 +244,9 @@ u8 global_alpha; u8 pre_mult_alpha; u8 zorder; + + enum drm_color_encoding color_encoding; + enum drm_color_range color_range; }; struct omap_overlay_manager_info { @@ -252,7 +256,7 @@ u32 trans_key; bool trans_enabled; - bool partial_alpha_enabled; + bool alpha_blender_enabled; bool cpr_enable; struct omap_dss_cpr_coefs cpr_coefs; @@ -579,6 +583,12 @@ const u32 *(*ovl_get_color_modes)(struct dispc_device *dispc, enum omap_plane_id plane); + bool (*ovl_color_mode_supported)(struct dispc_device *dispc, + enum omap_plane_id plane, u32 fourcc); + enum omap_overlay_caps (*ovl_get_caps)(struct dispc_device *dispc, + enum omap_plane_id plane); + void (*ovl_get_max_size)(struct dispc_device *dispc, + u16 *width, u16 *height); u32 (*wb_get_framedone_irq)(struct dispc_device *dispc); int (*wb_setup)(struct dispc_device *dispc, diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig --- a/drivers/gpu/drm/omapdrm/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -11,6 +11,17 @@ if DRM_OMAP +config DRM_OMAP_WB + bool "Enable writeback support for OMAP DRM driver" + depends on DRM_OMAP + depends on (VIDEO_V4L2 = y) || (VIDEO_V4L2 = m && DRM_OMAP = m) + depends on VIDEO_DEV && HAS_DMA + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + default n + help + Select this to enable memory-to-memory/capture writeback support. + source "drivers/gpu/drm/omapdrm/dss/Kconfig" source "drivers/gpu/drm/omapdrm/displays/Kconfig" diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile --- a/drivers/gpu/drm/omapdrm/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -12,6 +12,7 @@ omap_debugfs.o \ omap_crtc.o \ omap_plane.o \ + omap_overlay.o \ omap_encoder.o \ omap_connector.o \ omap_fb.o \ @@ -22,4 +23,6 @@ omapdrm-$(CONFIG_DRM_FBDEV_EMULATION) += omap_fbdev.o +omapdrm-$(CONFIG_DRM_OMAP_WB) += omap_wb.o omap_wb_cap.o omap_wb_m2m.o + obj-$(CONFIG_DRM_OMAP) += omapdrm.o diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c --- a/drivers/gpu/drm/omapdrm/omap_crtc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c 2022-01-06 12:45:53.810318090 -0500 @@ -24,6 +24,11 @@ unsigned int rotation; unsigned int zpos; bool manually_updated; + + u32 default_color; + unsigned int trans_key_mode; + unsigned int trans_key; + bool alpha_blender_enabled; }; #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) @@ -391,18 +396,72 @@ } } +static s16 omap_crtc_S31_32_to_s2_8(s64 coef) +{ + uint64_t sign_bit = 1ULL << 63; + uint64_t cbits = (uint64_t) coef; + s16 ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x1FF); + + if (cbits & sign_bit) + ret = -ret; + + return ret; +} + +static void omap_crtc_cpr_coefs_from_ctm(const struct drm_color_ctm *ctm, + struct omap_dss_cpr_coefs *cpr) +{ + cpr->rr = omap_crtc_S31_32_to_s2_8(ctm->matrix[0]); + cpr->rg = omap_crtc_S31_32_to_s2_8(ctm->matrix[1]); + cpr->rb = omap_crtc_S31_32_to_s2_8(ctm->matrix[2]); + cpr->gr = omap_crtc_S31_32_to_s2_8(ctm->matrix[3]); + cpr->gg = omap_crtc_S31_32_to_s2_8(ctm->matrix[4]); + cpr->gb = omap_crtc_S31_32_to_s2_8(ctm->matrix[5]); + cpr->br = omap_crtc_S31_32_to_s2_8(ctm->matrix[6]); + cpr->bg = omap_crtc_S31_32_to_s2_8(ctm->matrix[7]); + cpr->bb = omap_crtc_S31_32_to_s2_8(ctm->matrix[8]); +} + static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) { struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_overlay_manager_info info; + const struct omap_crtc_state *omap_state = + to_omap_crtc_state(crtc->state); memset(&info, 0, sizeof(info)); - info.default_color = 0x000000; - info.trans_enabled = false; - info.partial_alpha_enabled = false; - info.cpr_enable = false; + info.default_color = omap_state->default_color; + + info.trans_key = omap_state->trans_key; + + switch (omap_state->trans_key_mode) { + case 0: + default: + info.trans_enabled = false; + break; + case 1: + info.trans_enabled = true; + info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + break; + case 2: + info.trans_enabled = true; + info.trans_key_type = OMAP_DSS_COLOR_KEY_VID_SRC; + break; + } + + info.alpha_blender_enabled = omap_state->alpha_blender_enabled; + + if (crtc->state->ctm) { + struct drm_color_ctm *ctm = + (struct drm_color_ctm *) crtc->state->ctm->data; + + info.cpr_enable = true; + omap_crtc_cpr_coefs_from_ctm(ctm, &info.cpr_coefs); + } else { + info.cpr_enable = false; + } priv->dispc_ops->mgr_setup(priv->dispc, omap_crtc->channel, &info); } @@ -571,6 +630,7 @@ static int omap_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { + const struct omap_crtc_state *omap_state = to_omap_crtc_state(state); struct drm_plane_state *pri_state; if (state->color_mgmt_changed && state->gamma_lut) { @@ -581,6 +641,25 @@ return -EINVAL; } + if (omap_state->trans_key_mode) { + struct drm_plane *plane; + struct drm_plane_state *plane_state; + u32 zpos_mask = 0; + + drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) { + plane_state = drm_atomic_get_plane_state(state->state, + plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + if (zpos_mask & BIT(plane_state->zpos)) + return -EINVAL; + + zpos_mask |= BIT(plane_state->zpos); + plane_state->normalized_zpos = plane_state->zpos; + } + } + pri_state = drm_atomic_get_new_plane_state(state->state, crtc->primary); if (pri_state) { struct omap_crtc_state *omap_crtc_state = @@ -657,6 +736,7 @@ { struct omap_drm_private *priv = crtc->dev->dev_private; struct drm_plane_state *plane_state; + struct omap_crtc_state *omap_state = to_omap_crtc_state(state); /* * Delegate property set to the primary plane. Get the plane state and @@ -672,6 +752,14 @@ plane_state->rotation = val; else if (property == priv->zorder_prop) plane_state->zpos = val; + else if (property == priv->background_color_prop) + omap_state->default_color = val; + else if (property == priv->trans_key_mode_prop) + omap_state->trans_key_mode = val; + else if (property == priv->trans_key_prop) + omap_state->trans_key = val; + else if (property == priv->alpha_blender_prop) + omap_state->alpha_blender_enabled = !!val; else return -EINVAL; @@ -690,12 +778,28 @@ *val = omap_state->rotation; else if (property == priv->zorder_prop) *val = omap_state->zpos; + else if (property == priv->background_color_prop) + *val = omap_state->default_color; + else if (property == priv->trans_key_mode_prop) + *val = omap_state->trans_key_mode; + else if (property == priv->trans_key_prop) + *val = omap_state->trans_key; + else if (property == priv->alpha_blender_prop) + *val = omap_state->alpha_blender_enabled; else return -EINVAL; return 0; } +int omap_crtc_atomic_get_trans_key_mode(struct drm_crtc *crtc, + const struct drm_crtc_state *state) +{ + struct omap_crtc_state *omap_state = to_omap_crtc_state(state); + + return omap_state->trans_key_mode; +} + static void omap_crtc_reset(struct drm_crtc *crtc) { struct omap_crtc_state *state; @@ -730,6 +834,12 @@ state->rotation = current_state->rotation; state->manually_updated = current_state->manually_updated; + state->default_color = current_state->default_color; + + state->trans_key_mode = current_state->trans_key_mode; + state->trans_key = current_state->trans_key; + state->alpha_blender_enabled = current_state->alpha_blender_enabled; + return &state->base; } @@ -778,6 +888,18 @@ dss_uninstall_mgr_ops(priv->dss); } +static void omap_crtc_install_properties(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_object *obj = &crtc->base; + struct omap_drm_private *priv = dev->dev_private; + + drm_object_attach_property(obj, priv->background_color_prop, 0); + drm_object_attach_property(obj, priv->trans_key_mode_prop, 0); + drm_object_attach_property(obj, priv->trans_key_prop, 0); + drm_object_attach_property(obj, priv->alpha_blender_prop, 0); +} + /* initialize crtc */ struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct omap_drm_pipeline *pipe, @@ -839,10 +961,11 @@ if (priv->dispc_ops->mgr_gamma_size(priv->dispc, channel)) { unsigned int gamma_lut_size = 256; - drm_crtc_enable_color_mgmt(crtc, 0, false, gamma_lut_size); + drm_crtc_enable_color_mgmt(crtc, 0, true, gamma_lut_size); drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); } + omap_crtc_install_properties(crtc); omap_plane_install_properties(crtc->primary, &crtc->base); return crtc; diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_crtc.h b/drivers/gpu/drm/omapdrm/omap_crtc.h --- a/drivers/gpu/drm/omapdrm/omap_crtc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_crtc.h 2022-01-06 12:45:53.810318090 -0500 @@ -32,5 +32,8 @@ void omap_crtc_vblank_irq(struct drm_crtc *crtc); void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus); void omap_crtc_flush(struct drm_crtc *crtc); +int omap_crtc_atomic_get_trans_key_mode(struct drm_crtc *crtc, + const struct drm_crtc_state *state); + #endif /* __OMAPDRM_CRTC_H__ */ diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c --- a/drivers/gpu/drm/omapdrm/omap_drv.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_drv.c 2022-01-06 12:45:53.810318090 -0500 @@ -116,6 +116,98 @@ priv->dispc_ops->runtime_put(priv->dispc); } +static int drm_atomic_state_normalized_zpos_cmp(const void *a, const void *b) +{ + const struct drm_plane_state *sa = *(struct drm_plane_state **)a; + const struct drm_plane_state *sb = *(struct drm_plane_state **)b; + + if (sa->normalized_zpos != sb->normalized_zpos) + return sa->normalized_zpos - sb->normalized_zpos; + else + return sa->plane->base.id - sb->plane->base.id; +} + +static int omap_atomic_update_normalize_zpos(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_state, *new_state; + struct drm_plane *plane; + int c, i, n, inc; + int total_planes = dev->mode_config.num_total_plane; + struct drm_plane_state **states; + int ret = 0; + + states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL); + if (!states) + return -ENOMEM; + + for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, c) { + if (old_state->plane_mask == new_state->plane_mask && + !new_state->zpos_changed) + continue; + + if (omap_crtc_atomic_get_trans_key_mode(crtc, new_state)) + continue; + + /* Reset plane increment and index value for every crtc */ + n = 0; + + /* + * Normalization process might create new states for planes + * which normalized_zpos has to be recalculated. + */ + drm_for_each_plane_mask(plane, dev, new_state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(new_state->state, + plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto done; + } + states[n++] = plane_state; + } + + sort(states, n, sizeof(*states), + drm_atomic_state_normalized_zpos_cmp, NULL); + + for (i = 0, inc = 0; i < n; i++) { + plane = states[i]->plane; + + states[i]->normalized_zpos = i + inc; + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] updated normalized zpos value %d\n", + plane->base.id, plane->name, + states[i]->normalized_zpos); + + if (is_omap_plane_dual_overlay(states[i])) + inc++; + } + new_state->zpos_changed = true; + } + +done: + kfree(states); + return ret; +} + +static int omap_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check(dev, state); + if (ret) + return ret; + + if (dev->mode_config.normalize_zpos) { + ret = omap_atomic_update_normalize_zpos(dev, state); + if (ret) + return ret; + } + + return 0; +} + static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = { .atomic_commit_tail = omap_atomic_commit_tail, }; @@ -123,10 +215,86 @@ static const struct drm_mode_config_funcs omap_mode_config_funcs = { .fb_create = omap_framebuffer_create, .output_poll_changed = drm_fb_helper_output_poll_changed, - .atomic_check = drm_atomic_helper_check, + .atomic_check = omap_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; +/* Global/shared object state funcs */ + +/* + * This is a helper that returns the private state currently in operation. + * Note that this would return the "old_state" if called in the atomic check + * path, and the "new_state" after the atomic swap has been done. + */ +struct omap_global_state * +omap_get_existing_global_state(struct omap_drm_private *priv) +{ + return to_omap_global_state(priv->glob_obj.state); +} + +/* + * This acquires the modeset lock set aside for global state, creates + * a new duplicated private object state. + */ +struct omap_global_state *__must_check +omap_get_global_state(struct drm_atomic_state *s) +{ + struct omap_drm_private *priv = s->dev->dev_private; + struct drm_private_state *priv_state; + + priv_state = drm_atomic_get_private_obj_state(s, &priv->glob_obj); + if (IS_ERR(priv_state)) + return ERR_CAST(priv_state); + + return to_omap_global_state(priv_state); +} + +static struct drm_private_state * +omap_global_duplicate_state(struct drm_private_obj *obj) +{ + struct omap_global_state *state; + + state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); + + return &state->base; +} + +static void omap_global_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + struct omap_global_state *omap_state = to_omap_global_state(state); + + kfree(omap_state); +} + +static const struct drm_private_state_funcs omap_global_state_funcs = { + .atomic_duplicate_state = omap_global_duplicate_state, + .atomic_destroy_state = omap_global_destroy_state, +}; + +static int omap_global_obj_init(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_global_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + drm_atomic_private_obj_init(dev, &priv->glob_obj, &state->base, + &omap_global_state_funcs); + return 0; +} + +static void omap_global_obj_fini(struct omap_drm_private *priv) +{ + drm_atomic_private_obj_fini(&priv->glob_obj); +} + static void omap_disconnect_pipelines(struct drm_device *ddev) { struct omap_drm_private *priv = ddev->dev_private; @@ -194,11 +362,40 @@ struct omap_drm_private *priv = dev->dev_private; unsigned int num_planes = priv->dispc_ops->get_num_ovls(priv->dispc); + static const struct drm_prop_enum_list trans_key_mode_list[] = { + { 0, "disable"}, + { 1, "gfx-dst"}, + { 2, "vid-src"}, + }; + priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0, num_planes - 1); if (!priv->zorder_prop) return -ENOMEM; + /* crtc properties */ + + priv->background_color_prop = drm_property_create_range(dev, 0, + "background", 0, 0xffffff); + if (!priv->background_color_prop) + return -ENOMEM; + + priv->trans_key_mode_prop = drm_property_create_enum(dev, 0, + "trans-key-mode", + trans_key_mode_list, ARRAY_SIZE(trans_key_mode_list)); + if (!priv->trans_key_mode_prop) + return -ENOMEM; + + priv->trans_key_prop = drm_property_create_range(dev, 0, "trans-key", + 0, 0xffffff); + if (!priv->trans_key_prop) + return -ENOMEM; + + priv->alpha_blender_prop = drm_property_create_bool(dev, 0, + "alpha_blender"); + if (!priv->alpha_blender_prop) + return -ENOMEM; + return 0; } @@ -237,8 +434,6 @@ if (!omapdss_stack_is_ready()) return -EPROBE_DEFER; - drm_mode_config_init(dev); - ret = omap_modeset_init_properties(dev); if (ret < 0) return ret; @@ -611,10 +806,20 @@ omap_gem_init(ddev); + drm_mode_config_init(ddev); + + ret = omap_global_obj_init(ddev); + if (ret) + goto err_gem_deinit; + + ret = omap_hwoverlays_init(priv); + if (ret) + goto err_free_priv_obj; + ret = omap_modeset_init(ddev); if (ret) { dev_err(priv->dev, "omap_modeset_init failed: ret=%d\n", ret); - goto err_gem_deinit; + goto err_free_overlays; } /* Initialize vblank handling, start with all CRTCs disabled. */ @@ -629,6 +834,14 @@ drm_kms_helper_poll_init(ddev); omap_modeset_enable_external_hpd(ddev); + if (priv->dispc_ops->has_writeback(priv->dispc)) { + ret = omap_wb_init(ddev); + if (ret) + dev_warn(priv->dev, "failed to initialize writeback\n"); + else + priv->wb_initialized = true; + } + /* * Register the DRM device with the core and the connectors with * sysfs. @@ -640,13 +853,22 @@ return 0; err_cleanup_helpers: + if (priv->wb_initialized) + omap_wb_cleanup(ddev); + omap_modeset_disable_external_hpd(ddev); + drm_kms_helper_poll_fini(ddev); omap_fbdev_fini(ddev); err_cleanup_modeset: omap_modeset_fini(ddev); +err_free_overlays: + omap_hwoverlays_destroy(priv); +err_free_priv_obj: + omap_global_obj_fini(priv); err_gem_deinit: + drm_mode_config_cleanup(ddev); omap_gem_deinit(ddev); destroy_workqueue(priv->wq); omap_disconnect_pipelines(ddev); @@ -663,6 +885,9 @@ drm_dev_unregister(ddev); + if (priv->wb_initialized) + omap_wb_cleanup(ddev); + omap_modeset_disable_external_hpd(ddev); drm_kms_helper_poll_fini(ddev); @@ -671,6 +896,9 @@ drm_atomic_helper_shutdown(ddev); omap_modeset_fini(ddev); + omap_hwoverlays_destroy(priv); + omap_global_obj_fini(priv); + drm_mode_config_cleanup(ddev); omap_gem_deinit(ddev); destroy_workqueue(priv->wq); diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h --- a/drivers/gpu/drm/omapdrm/omap_drv.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_drv.h 2022-01-06 12:45:53.810318090 -0500 @@ -13,6 +13,7 @@ #include "dss/omapdss.h" +#include #include #include @@ -24,6 +25,7 @@ #include "omap_gem.h" #include "omap_irq.h" #include "omap_plane.h" +#include "omap_overlay.h" #define DBG(fmt, ...) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) /* verbose debug */ @@ -40,6 +42,20 @@ unsigned int alias_id; }; +/* + * Global private object state for tracking resources that are shared across + * multiple kms objects (planes/crtcs/etc). + */ +#define to_omap_global_state(x) container_of(x, struct omap_global_state, base) +struct omap_global_state { + struct drm_private_state base; + + struct drm_atomic_state *state; + + /* global atomic state of assignment between overlays and planes */ + struct drm_plane *hwoverlay_to_plane[8]; +}; + struct omap_drm_private { struct drm_device *ddev; struct device *dev; @@ -56,6 +72,16 @@ unsigned int num_planes; struct drm_plane *planes[8]; + unsigned int num_ovls; + struct omap_hw_overlay *overlays[8]; + + /* + * Global private object state, Do not access directly, use + * omap_global_get_state() + */ + struct drm_modeset_lock glob_obj_lock; + struct drm_private_obj glob_obj; + struct drm_fb_helper *fbdev; struct workqueue_struct *wq; @@ -72,6 +98,12 @@ /* properties: */ struct drm_property *zorder_prop; + /* crtc properties */ + struct drm_property *background_color_prop; + struct drm_property *trans_key_mode_prop; + struct drm_property *trans_key_prop; + struct drm_property *alpha_blender_prop; + /* irq handling: */ spinlock_t wait_lock; /* protects the wait_list */ struct list_head wait_list; /* list of omap_irq_wait */ @@ -79,9 +111,36 @@ /* memory bandwidth limit if it is needed on the platform */ unsigned int max_bandwidth; + + void *wb_private; /* Write-back private data */ + bool wb_initialized; }; void omap_debugfs_init(struct drm_minor *minor); +struct omap_global_state *__must_check +omap_get_global_state(struct drm_atomic_state *s); +struct omap_global_state * +omap_get_existing_global_state(struct omap_drm_private *priv); + +#if IS_ENABLED(CONFIG_DRM_OMAP_WB) + +#define OMAP_WB_IRQ_MASK (DISPC_IRQ_FRAMEDONEWB | \ + DISPC_IRQ_WBBUFFEROVERFLOW | \ + DISPC_IRQ_WBUNCOMPLETEERROR) + +int omap_wb_init(struct drm_device *drmdev); +void omap_wb_cleanup(struct drm_device *drmdev); +void omap_wb_irq(void *priv, u32 irqstatus); + +#else + +#define OMAP_WB_IRQ_MASK (0) + +static inline int omap_wb_init(struct drm_device *drmdev) { return 0; } +static inline void omap_wb_cleanup(struct drm_device *drmdev) { } +static inline void omap_wb_irq(void *priv, u32 irqstatus) { } + +#endif #endif /* __OMAPDRM_DRV_H__ */ diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c --- a/drivers/gpu/drm/omapdrm/omap_fb.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_fb.c 2022-01-06 12:45:53.810318090 -0500 @@ -131,7 +131,9 @@ /* update ovl info for scanout, handles cases of multi-planar fb's, etc. */ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct drm_plane_state *state, struct omap_overlay_info *info) + struct drm_plane_state *state, + struct omap_overlay_info *info, + struct omap_overlay_info *r_info) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); const struct drm_format_info *format = omap_fb->format; @@ -218,6 +220,35 @@ } else { info->p_uv_addr = 0; } + + if (r_info) { + info->width /= 2; + info->out_width /= 2; + + *r_info = *info; + + if (fb->format->is_yuv) { + if (info->width & 1) { + info->width++; + r_info->width--; + } + + if (info->out_width & 1) { + info->out_width++; + r_info->out_width--; + } + } + + r_info->pos_x = info->pos_x + info->out_width; + + r_info->paddr = get_linear_addr(fb, format, 0, + x + info->width, y); + if (fb->format->format == DRM_FORMAT_NV12) { + r_info->p_uv_addr = + get_linear_addr(fb, format, 1, + x + info->width, y); + } + } } /* pin, prepare for scanout: */ diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_fb.h b/drivers/gpu/drm/omapdrm/omap_fb.h --- a/drivers/gpu/drm/omapdrm/omap_fb.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_fb.h 2022-01-06 12:45:53.810318090 -0500 @@ -26,7 +26,9 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb); void omap_framebuffer_unpin(struct drm_framebuffer *fb); void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct drm_plane_state *state, struct omap_overlay_info *info); + struct drm_plane_state *state, + struct omap_overlay_info *info, + struct omap_overlay_info *r_info); bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c --- a/drivers/gpu/drm/omapdrm/omap_irq.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_irq.c 2022-01-06 12:45:53.810318090 -0500 @@ -237,6 +237,7 @@ omap_irq_ocp_error_handler(dev, irqstatus); omap_irq_fifo_underflow(priv, irqstatus); + omap_wb_irq(priv->wb_private, irqstatus); spin_lock_irqsave(&priv->wait_lock, flags); list_for_each_entry_safe(wait, n, &priv->wait_list, node) { @@ -285,6 +286,9 @@ for (i = 0; i < num_mgrs; ++i) priv->irq_mask |= priv->dispc_ops->mgr_get_sync_lost_irq(priv->dispc, i); + if (priv->dispc_ops->has_writeback(priv->dispc)) + priv->irq_mask |= OMAP_WB_IRQ_MASK; + priv->dispc_ops->runtime_get(priv->dispc); priv->dispc_ops->clear_irqstatus(priv->dispc, 0xffffffff); priv->dispc_ops->runtime_put(priv->dispc); diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_overlay.c b/drivers/gpu/drm/omapdrm/omap_overlay.c --- a/drivers/gpu/drm/omapdrm/omap_overlay.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_overlay.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot, + */ + +#include +#include +#include + +#include "omap_dmm_tiler.h" +#include "omap_drv.h" + +/* + * overlay funcs + */ +static const char * const overlay_id_to_name[] = { + [OMAP_DSS_GFX] = "gfx", + [OMAP_DSS_VIDEO1] = "vid1", + [OMAP_DSS_VIDEO2] = "vid2", + [OMAP_DSS_VIDEO3] = "vid3", +}; + +static struct omap_hw_overlay * +omap_plane_find_free_overlay(struct drm_device *dev, + struct drm_plane *hwoverlay_to_plane[], + u32 caps, u32 fourcc, u32 crtc_mask) +{ + struct omap_drm_private *priv = dev->dev_private; + const struct dispc_ops *ops = priv->dispc_ops; + int i; + + DBG("caps: %x fourcc: %x crtc: %x", caps, fourcc, crtc_mask); + + for (i = 0; i < priv->num_ovls; i++) { + struct omap_hw_overlay *cur = priv->overlays[i]; + + DBG("%d: id: %d cur->caps: %x cur->crtc: %x", + cur->idx, cur->overlay_id, cur->caps, cur->possible_crtcs); + + /* skip if already in-use */ + if (hwoverlay_to_plane[cur->idx]) + continue; + + /* check if allowed on crtc */ + if (!(cur->possible_crtcs & crtc_mask)) + continue; + + /* skip if doesn't support some required caps: */ + if (caps & ~cur->caps) + continue; + + /* check supported format */ + if (!ops->ovl_color_mode_supported(priv->dispc, + cur->overlay_id, + fourcc)) + continue; + + return cur; + } + + DBG("no match"); + return NULL; +} + +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, + u32 caps, u32 fourcc, u32 crtc_mask, + struct omap_hw_overlay **overlay, + struct omap_hw_overlay **r_overlay) +{ + struct omap_drm_private *priv = s->dev->dev_private; + struct omap_global_state *new_global_state, *old_global_state; + struct drm_plane **overlay_map; + struct omap_hw_overlay *ovl, *r_ovl; + u32 save_possible_crtcs; + + new_global_state = omap_get_global_state(s); + if (IS_ERR(new_global_state)) + return PTR_ERR(new_global_state); + + /* + * grab old_state after omap_get_global_state(), + * since now we hold lock: + */ + old_global_state = omap_get_existing_global_state(priv); + DBG("new_global_state: %p old_global_state: %p", + new_global_state, old_global_state); + + overlay_map = new_global_state->hwoverlay_to_plane; + + if (!*overlay) { + ovl = omap_plane_find_free_overlay(s->dev, overlay_map, + caps, fourcc, crtc_mask); + if (!ovl) + return -ENOMEM; + + /* in case we need to backtrack */ + save_possible_crtcs = ovl->possible_crtcs; + + ovl->possible_crtcs = crtc_mask; + overlay_map[ovl->idx] = plane; + *overlay = ovl; + + if (r_overlay) { + r_ovl = omap_plane_find_free_overlay(s->dev, + overlay_map, + caps, fourcc, + crtc_mask); + if (!r_ovl) { + ovl->possible_crtcs = save_possible_crtcs; + overlay_map[ovl->idx] = NULL; + *overlay = NULL; + return -ENOMEM; + } + + r_ovl->possible_crtcs = crtc_mask; + overlay_map[r_ovl->idx] = plane; + *r_overlay = r_ovl; + } + + DBG("%s: assign to plane %s caps %x on crtc %x", + (*overlay)->name, plane->name, caps, crtc_mask); + + if (r_overlay) { + DBG("%s: assign to right of plane %s caps %x on crtc %x", + (*r_overlay)->name, plane->name, caps, crtc_mask); + } + } + + return 0; +} + +void omap_overlay_release(struct drm_atomic_state *s, + struct drm_plane *plane, + struct omap_hw_overlay *overlay) +{ + struct omap_global_state *state = omap_get_global_state(s); + struct drm_plane **overlay_map = state->hwoverlay_to_plane; + + if (!overlay) + return; + + if (WARN_ON(!overlay_map[overlay->idx])) + return; + /* + * Check that the overlay we are releasing is actually + * assigned to the plane we are trying to release it from. + */ + if (overlay_map[overlay->idx] == plane) { + DBG("%s: release from plane %s", overlay->name, plane->name); + + overlay_map[overlay->idx] = NULL; + } +} + +void omap_overlay_disable(struct drm_atomic_state *s, + struct drm_plane *plane, + struct omap_hw_overlay *overlay) +{ + struct omap_drm_private *priv = s->dev->dev_private; + struct drm_plane **overlay_map; + struct omap_global_state *old_state; + + old_state = omap_get_existing_global_state(priv); + overlay_map = old_state->hwoverlay_to_plane; + + if (!overlay) + return; + + /* + * Check that the overlay we are trying to disable has not + * been re-assigned to another plane already + */ + if (!overlay_map[overlay->idx]) { + DBG("%s: on %s disabled", overlay->name, plane->name); + + /* disable the overlay */ + priv->dispc_ops->ovl_enable(priv->dispc, + overlay->overlay_id, false); + + /* + * Since we are disabling this overlay in this + * atomic cycle we can reset the available crtcs + * it can be used on + */ + overlay->possible_crtcs = (1 << priv->num_pipes) - 1; + } + + /* + * Otherwise the overlay is still in use so leave it alone + */ +} + +int omap_overlay_assign_wb(struct omap_drm_private *priv, + struct drm_plane *plane, + u32 caps, u32 fourcc, u32 crtc_mask, + struct omap_hw_overlay **overlay) +{ + struct omap_global_state *old_global_state; + struct drm_plane **overlay_map; + struct omap_hw_overlay *ovl; + + /* + * As there is no state here we can't really grab the global obj lock. + * This might cause issue! + */ + old_global_state = omap_get_existing_global_state(priv); + DBG("old_global_state: %p", old_global_state); + + overlay_map = old_global_state->hwoverlay_to_plane; + + if (!*overlay) { + ovl = omap_plane_find_free_overlay(plane->dev, overlay_map, + caps, fourcc, crtc_mask); + if (!ovl) + return -ENOMEM; + + overlay_map[ovl->idx] = plane; + *overlay = ovl; + + DBG("%s: assign to WB plane %s for caps %x", + (*overlay)->name, plane->name, caps); + } + + return 0; +} + +void omap_overlay_release_wb(struct omap_drm_private *priv, + struct drm_plane *plane, + struct omap_hw_overlay *overlay) +{ + struct omap_global_state *old_global_state; + struct drm_plane **overlay_map; + + if (!overlay) + return; + + /* + * As there is no state here we can't really grab the global obj lock. + * This might cause issue! + */ + old_global_state = omap_get_existing_global_state(priv); + DBG("old_global_state: %p", old_global_state); + + overlay_map = old_global_state->hwoverlay_to_plane; + + if (WARN_ON(!overlay_map[overlay->idx])) + return; + /* + * Check that the overlay we are releasing is actually + * assigned to the plane we are trying to release it from. + */ + if (overlay_map[overlay->idx] == plane) { + DBG("%s: release from WB plane %s", overlay->name, plane->name); + + /* + * As this might get called without having done any other + * actual h/w access make sure the module is enabled before + * trying to access it. + */ + priv->dispc_ops->runtime_get(priv->dispc); + priv->dispc_ops->ovl_enable(priv->dispc, overlay->overlay_id, + false); + priv->dispc_ops->runtime_put(priv->dispc); + overlay->possible_crtcs = (1 << priv->num_pipes) - 1; + overlay_map[overlay->idx] = NULL; + } +} + +static void omap_overlay_destroy(struct omap_hw_overlay *overlay) +{ + kfree(overlay); +} + +static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id, + enum omap_overlay_caps caps) +{ + struct omap_hw_overlay *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + + overlay->name = overlay_id_to_name[overlay_id]; + overlay->overlay_id = overlay_id; + overlay->caps = caps; + /* + * When this is called priv->num_crtcs is not known yet. + * Use a safe mask value to start with, it will get updated to the + * proper value after the first use. + */ + overlay->possible_crtcs = 0xff; + + return overlay; +} + +int omap_hwoverlays_init(struct omap_drm_private *priv) +{ + static const enum omap_plane_id hw_plane_ids[] = { + OMAP_DSS_GFX, OMAP_DSS_VIDEO1, + OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3, + }; + u32 num_overlays = priv->dispc_ops->get_num_ovls(priv->dispc); + enum omap_overlay_caps caps; + int i, ret; + + for (i = 0; i < num_overlays; i++) { + struct omap_hw_overlay *overlay; + + caps = priv->dispc_ops->ovl_get_caps(priv->dispc, hw_plane_ids[i]); + overlay = omap_overlay_init(hw_plane_ids[i], caps); + if (IS_ERR(overlay)) { + ret = PTR_ERR(overlay); + dev_err(priv->dev, "failed to construct overlay for %s (%d)\n", + overlay_id_to_name[i], ret); + return ret; + } + overlay->idx = priv->num_ovls; + priv->overlays[priv->num_ovls++] = overlay; + } + + return 0; +} + +void omap_hwoverlays_destroy(struct omap_drm_private *priv) +{ + int i; + + for (i = 0; i < priv->num_ovls; i++) { + omap_overlay_destroy(priv->overlays[i]); + priv->overlays[i] = NULL; + } +} diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_overlay.h b/drivers/gpu/drm/omapdrm/omap_overlay.h --- a/drivers/gpu/drm/omapdrm/omap_overlay.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_overlay.h 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot, + */ + +#ifndef __OMAPDRM_OVERLAY_H__ +#define __OMAPDRM_OVERLAY_H__ + +#include + +enum drm_plane_type; + +struct drm_device; +struct drm_mode_object; +struct drm_plane; + +/* Used to associate a HW overlay/plane to a plane */ +struct omap_hw_overlay { + int idx; + + const char *name; + enum omap_plane_id overlay_id; + + enum omap_overlay_caps caps; + u32 possible_crtcs; +}; + +int omap_hwoverlays_init(struct omap_drm_private *priv); +void omap_hwoverlays_destroy(struct omap_drm_private *priv); +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, + u32 caps, u32 fourcc, u32 crtc_mask, + struct omap_hw_overlay **overlay, + struct omap_hw_overlay **r_overlay); +void omap_overlay_release(struct drm_atomic_state *s, + struct drm_plane *plane, + struct omap_hw_overlay *overlay); +void omap_overlay_disable(struct drm_atomic_state *s, + struct drm_plane *plane, + struct omap_hw_overlay *overlay); +int omap_overlay_assign_wb(struct omap_drm_private *priv, + struct drm_plane *plane, + u32 caps, u32 fourcc, u32 crtc_mask, + struct omap_hw_overlay **overlay); +void omap_overlay_release_wb(struct omap_drm_private *priv, + struct drm_plane *plane, + struct omap_hw_overlay *overlay); + +#endif /* __OMAPDRM_OVERLAY_H__ */ diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c --- a/drivers/gpu/drm/omapdrm/omap_plane.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_plane.c 2022-01-06 12:45:53.810318090 -0500 @@ -7,6 +7,7 @@ #include #include #include +#include #include "omap_dmm_tiler.h" #include "omap_drv.h" @@ -15,14 +16,37 @@ * plane funcs */ +#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) + +struct omap_plane_state { + /* Must be first. */ + struct drm_plane_state base; + + struct omap_hw_overlay *overlay; + struct omap_hw_overlay *r_overlay; /* right overlay */ +}; + #define to_omap_plane(x) container_of(x, struct omap_plane, base) struct omap_plane { struct drm_plane base; enum omap_plane_id id; const char *name; + + /* + * WB has no notion of atomic state we need to keep + * a reference to the allocated overlay here. + */ + struct omap_hw_overlay *reserved_wb_overlay; }; +bool is_omap_plane_dual_overlay(struct drm_plane_state *state) +{ + struct omap_plane_state *omap_state = to_omap_plane_state(state); + + return !!omap_state->r_overlay; +} + static int omap_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { @@ -45,9 +69,32 @@ struct omap_drm_private *priv = plane->dev->dev_private; struct omap_plane *omap_plane = to_omap_plane(plane); struct drm_plane_state *state = plane->state; - struct omap_overlay_info info; + struct omap_plane_state *new_omap_state; + struct omap_plane_state *old_omap_state; + struct omap_overlay_info info, r_info; + enum omap_plane_id ovl_id, r_ovl_id; int ret; + bool dual_ovl; + + new_omap_state = to_omap_plane_state(state); + old_omap_state = to_omap_plane_state(old_state); + + dual_ovl = is_omap_plane_dual_overlay(state); + + /* Cleanup previously held overlay if needed */ + omap_overlay_disable(old_state->state, plane, old_omap_state->overlay); + omap_overlay_disable(old_state->state, plane, + old_omap_state->r_overlay); + if (!new_omap_state->overlay) { + DBG("[PLANE:%d:%s] overlay_id: ??? (%p)", plane->base.id, plane->name, + new_omap_state->overlay); + return; + } + + ovl_id = new_omap_state->overlay->overlay_id; + DBG("[PLANE:%d:%s] overlay_id: %d", plane->base.id, plane->name, + ovl_id); DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); memset(&info, 0, sizeof(info)); @@ -59,75 +106,273 @@ info.pre_mult_alpha = 1; else info.pre_mult_alpha = 0; + info.color_encoding = state->color_encoding; + info.color_range = state->color_range; + + r_info = info; /* update scanout: */ - omap_framebuffer_update_scanout(state->fb, state, &info); + omap_framebuffer_update_scanout(state->fb, state, &info, + dual_ovl ? &r_info : NULL); - DBG("%dx%d -> %dx%d (%d)", info.width, info.height, - info.out_width, info.out_height, - info.screen_width); + DBG("%s: %dx%d -> %dx%d (%d)", + new_omap_state->overlay->name, info.width, info.height, + info.out_width, info.out_height, info.screen_width); DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, - &info.paddr, &info.p_uv_addr); + &info.paddr, &info.p_uv_addr); + + if (dual_ovl) { + r_ovl_id = new_omap_state->r_overlay->overlay_id; + /* + * If the current plane uses 2 hw planes the very next + * zorder is used by the r_overlay so we just use the + * main overlay zorder + 1 + */ + r_info.zorder = info.zorder + 1; + + DBG("%s: %dx%d -> %dx%d (%d)", + new_omap_state->r_overlay->name, + r_info.width, r_info.height, + r_info.out_width, r_info.out_height, r_info.screen_width); + DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y, + &r_info.paddr, &r_info.p_uv_addr); + } /* and finally, update omapdss: */ - ret = priv->dispc_ops->ovl_setup(priv->dispc, omap_plane->id, &info, + ret = priv->dispc_ops->ovl_setup(priv->dispc, ovl_id, &info, omap_crtc_timings(state->crtc), false, omap_crtc_channel(state->crtc)); if (ret) { - dev_err(plane->dev->dev, "Failed to setup plane %s\n", + dev_err(plane->dev->dev, "Failed to setup plane1 %s\n", omap_plane->name); - priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, false); + priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, false); return; } - priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, true); + priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, true); + + if (dual_ovl) { + ret = priv->dispc_ops->ovl_setup(priv->dispc, r_ovl_id, &r_info, + omap_crtc_timings(state->crtc), false, + omap_crtc_channel(state->crtc)); + if (ret) { + dev_err(plane->dev->dev, "Failed to setup plane2 %s\n", + omap_plane->name); + priv->dispc_ops->ovl_enable(priv->dispc, r_ovl_id, false); + priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, false); + return; + } + + priv->dispc_ops->ovl_enable(priv->dispc, r_ovl_id, true); + } } static void omap_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_plane_state *state = plane->state; + struct omap_plane_state *new_omap_state; + struct omap_plane_state *old_omap_state; + + new_omap_state = to_omap_plane_state(state); + old_omap_state = to_omap_plane_state(old_state); + + if (!old_omap_state->overlay) + return; plane->state->rotation = DRM_MODE_ROTATE_0; plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY - ? 0 : omap_plane->id; + ? 0 : old_omap_state->overlay->overlay_id; - priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, false); + omap_overlay_disable(old_state->state, plane, old_omap_state->overlay); + new_omap_state->overlay = NULL; + if (is_omap_plane_dual_overlay(old_state)) { + omap_overlay_disable(old_state->state, plane, + old_omap_state->r_overlay); + new_omap_state->r_overlay = NULL; + } } +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) static int omap_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { + struct omap_drm_private *priv = plane->dev->dev_private; + struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; + u16 width, height; + u32 width_fp, height_fp; + struct drm_plane_state *old_state = plane->state; + struct omap_plane_state *omap_state = to_omap_plane_state(state); + struct omap_global_state *omap_overlay_global_state; + u32 crtc_mask; + u32 fourcc; + u32 caps = 0; + bool new_hw_overlay = false; + bool new_r_hw_overlay = false; + bool is_fourcc_yuv = false; + int min_scale, max_scale; + int ret; - if (!state->fb) - return 0; + if (omap_plane->reserved_wb_overlay) + return -EBUSY; + + omap_overlay_global_state = omap_get_global_state(state->state); + if (IS_ERR(omap_overlay_global_state)) + return PTR_ERR(omap_overlay_global_state); + DBG("%s: omap_overlay_global_state: %p", plane->name, + omap_overlay_global_state); + + priv->dispc_ops->ovl_get_max_size(priv->dispc, &width, &height); + width_fp = width << 16; + height_fp = height << 16; - /* crtc should only be NULL when disabling (i.e., !state->fb) */ - if (WARN_ON(!state->crtc)) + crtc = state->crtc ? state->crtc : plane->state->crtc; + if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); /* we should have a crtc state if the plane is attached to a crtc */ if (WARN_ON(!crtc_state)) return 0; - if (!crtc_state->enable) - return 0; - - if (state->crtc_x < 0 || state->crtc_y < 0) + /* Make sure dimensions are within bounds. */ + if (state->src_h > height_fp || state->crtc_h > height) return -EINVAL; - if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) - return -EINVAL; + if (state->fb) + is_fourcc_yuv = state->fb->format->is_yuv; - if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) - return -EINVAL; + if (state->src_w > width_fp || state->crtc_w > width) { + /* + * We cannot have dual plane/overlay and trans_key_mode + * enabled concurrently, hence rejecting this configuration + */ + if (omap_crtc_atomic_get_trans_key_mode(crtc, crtc_state)) + return -EINVAL; + + if (is_fourcc_yuv && + (((state->src_w >> 16) / 2 & 1) || + state->crtc_w / 2 & 1)) { + /* + * When calculating the split overlay width + * and it yield an odd value we will need to adjust + * the indivual width +/- 1. So make sure it fits + */ + if (state->src_w <= ((2 * width - 1) << 16) && + state->crtc_w <= (2 * width - 1)) + new_r_hw_overlay = true; + else + return -EINVAL; + } else { + if (state->src_w <= (2 * width_fp) && + state->crtc_w <= (2 * width)) + new_r_hw_overlay = true; + else + return -EINVAL; + } + } - if (state->rotation != DRM_MODE_ROTATE_0 && - !omap_framebuffer_supports_rotation(state->fb)) - return -EINVAL; + /* + * Note: these are just sanity checks to filter out totally bad scaling + * factors. The real limits must be calculated case by case, and + * unfortunately we currently do those checks only at the commit + * phase in dispc. + */ + min_scale = FRAC_16_16(1, 8); + max_scale = FRAC_16_16(8, 1); + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + min_scale, max_scale, + true, true); + if (ret) + return ret; + + DBG("%s: check (%d -> %d)", plane->name, + old_state->visible, state->visible); + + if (state->visible) { + if (state->rotation != DRM_MODE_ROTATE_0 && + !omap_framebuffer_supports_rotation(state->fb)) + return -EINVAL; + + if ((state->src_w >> 16) != state->crtc_w || + (state->src_h >> 16) != state->crtc_h) + caps |= OMAP_DSS_OVL_CAP_SCALE; + + fourcc = state->fb->format->format; + crtc_mask = drm_crtc_mask(state->crtc); + + /* + * (re)allocate hw overlay if we don't have one or + * there is a caps mismatch + */ + if (!omap_state->overlay || + (caps & ~omap_state->overlay->caps)) { + new_hw_overlay = true; + } else { + /* check if allowed on crtc */ + if (!(omap_state->overlay->possible_crtcs & crtc_mask)) + new_hw_overlay = true; + + /* check supported format */ + if (!priv->dispc_ops->ovl_color_mode_supported(priv->dispc, + omap_state->overlay->overlay_id, + fourcc)) + new_hw_overlay = true; + } + /* + * check if we need two overlays and only have 1 or + * if we had 2 overlays but will only need 1 + */ + if ((new_r_hw_overlay && !omap_state->r_overlay) || + (!new_r_hw_overlay && omap_state->r_overlay)) + new_hw_overlay = true; + + if (new_hw_overlay) { + struct omap_hw_overlay *old_ovl = + omap_state->overlay; + struct omap_hw_overlay *old_r_ovl = + omap_state->r_overlay; + struct omap_hw_overlay *new_ovl = NULL; + struct omap_hw_overlay *new_r_ovl = NULL; + + omap_overlay_release(state->state, plane, old_ovl); + omap_overlay_release(state->state, plane, old_r_ovl); + + ret = omap_overlay_assign(state->state, plane, caps, + fourcc, crtc_mask, &new_ovl, + new_r_hw_overlay ? + &new_r_ovl : NULL); + if (ret) { + DBG("%s: failed to assign hw_overlay(s)!", + plane->name); + omap_state->overlay = NULL; + omap_state->r_overlay = NULL; + return ret; + } + + omap_state->overlay = new_ovl; + if (new_r_hw_overlay) + omap_state->r_overlay = new_r_ovl; + else + omap_state->r_overlay = NULL; + } + } else { + omap_overlay_release(state->state, plane, omap_state->overlay); + omap_overlay_release(state->state, plane, + omap_state->r_overlay); + omap_state->overlay = NULL; + omap_state->r_overlay = NULL; + } + + if (omap_state->overlay) + DBG("plane: %s overlay_id: %d", plane->name, + omap_state->overlay->overlay_id); + if (omap_state->r_overlay) + DBG("plane: %s r_overlay_id: %d", plane->name, + omap_state->r_overlay->overlay_id); return 0; } @@ -178,17 +423,73 @@ static void omap_plane_reset(struct drm_plane *plane) { struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_plane_state *omap_state; - drm_atomic_helper_plane_reset(plane); - if (!plane->state) + if (plane->state) + drm_atomic_helper_plane_destroy_state(plane, plane->state); + + omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); + if (!omap_state) return; + __drm_atomic_helper_plane_reset(plane, &omap_state->base); + /* * Set the zpos default depending on whether we are a primary or overlay * plane. */ plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; + plane->state->color_encoding = DRM_COLOR_YCBCR_BT601; + plane->state->color_range = DRM_COLOR_YCBCR_FULL_RANGE; +} + +static struct drm_plane_state * +omap_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct omap_plane_state *state; + struct omap_plane_state *copy; + + if (WARN_ON(!plane->state)) + return NULL; + + state = to_omap_plane_state(plane->state); + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + if (!copy) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, ©->base); + + return ©->base; +} + +static void omap_plane_atomic_print_state(struct drm_printer *p, + const struct drm_plane_state *state) +{ + struct omap_plane_state *omap_state = to_omap_plane_state(state); + + drm_printf(p, "\toverlay=%s\n", omap_state->overlay ? + omap_state->overlay->name : "(null)"); + if (omap_state->overlay) { + drm_printf(p, "\t\tidx=%d\n", omap_state->overlay->idx); + drm_printf(p, "\t\toverlay_id=%d\n", + omap_state->overlay->overlay_id); + drm_printf(p, "\t\tcaps=0x%x\n", omap_state->overlay->caps); + drm_printf(p, "\t\tpossible_crtcs=0x%x\n", + omap_state->overlay->possible_crtcs); + } + + drm_printf(p, "\tr_overlay=%s\n", omap_state->r_overlay ? + omap_state->r_overlay->name : + "(null)"); + if (omap_state->r_overlay) { + drm_printf(p, "\t\tidx=%d\n", omap_state->r_overlay->idx); + drm_printf(p, "\t\toverlay_id=%d\n", + omap_state->r_overlay->overlay_id); + drm_printf(p, "\t\tcaps=0x%x\n", omap_state->r_overlay->caps); + drm_printf(p, "\t\tpossible_crtcs=0x%x\n", + omap_state->r_overlay->possible_crtcs); + } } static int omap_plane_atomic_set_property(struct drm_plane *plane, @@ -226,12 +527,30 @@ .disable_plane = drm_atomic_helper_disable_plane, .reset = omap_plane_reset, .destroy = omap_plane_destroy, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_duplicate_state = omap_plane_atomic_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .atomic_set_property = omap_plane_atomic_set_property, .atomic_get_property = omap_plane_atomic_get_property, + .atomic_print_state = omap_plane_atomic_print_state, }; +static bool omap_plane_supports_yuv(struct drm_plane *plane) +{ + struct omap_drm_private *priv = plane->dev->dev_private; + struct omap_plane *omap_plane = to_omap_plane(plane); + const u32 *formats = + priv->dispc_ops->ovl_get_color_modes(priv->dispc, omap_plane->id); + int i; + + for (i = 0; formats[i]; i++) + if (formats[i] == DRM_FORMAT_YUYV || + formats[i] == DRM_FORMAT_UYVY || + formats[i] == DRM_FORMAT_NV12) + return true; + + return false; +} + static const char *plane_id_to_name[] = { [OMAP_DSS_GFX] = "gfx", [OMAP_DSS_VIDEO1] = "vid1", @@ -239,13 +558,6 @@ [OMAP_DSS_VIDEO3] = "vid3", }; -static const enum omap_plane_id plane_idx_to_id[] = { - OMAP_DSS_GFX, - OMAP_DSS_VIDEO1, - OMAP_DSS_VIDEO2, - OMAP_DSS_VIDEO3, -}; - /* initialize plane */ struct drm_plane *omap_plane_init(struct drm_device *dev, int idx, enum drm_plane_type type, @@ -255,27 +567,28 @@ unsigned int num_planes = priv->dispc_ops->get_num_ovls(priv->dispc); struct drm_plane *plane; struct omap_plane *omap_plane; - enum omap_plane_id id; int ret; u32 nformats; const u32 *formats; - if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) + if (WARN_ON(idx >= num_planes)) return ERR_PTR(-EINVAL); - id = plane_idx_to_id[idx]; - - DBG("%s: type=%d", plane_id_to_name[id], type); - omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); if (!omap_plane) return ERR_PTR(-ENOMEM); - formats = priv->dispc_ops->ovl_get_color_modes(priv->dispc, id); + omap_plane->id = idx; + omap_plane->name = plane_id_to_name[idx]; + + DBG("%s: type=%d", omap_plane->name, type); + DBG(" omap_plane->id: %d", omap_plane->id); + DBG(" crtc_mask: 0x%04x", possible_crtcs); + + formats = priv->dispc_ops->ovl_get_color_modes(priv->dispc, + omap_plane->id); for (nformats = 0; formats[nformats]; ++nformats) ; - omap_plane->id = id; - omap_plane->name = plane_id_to_name[id]; plane = &omap_plane->base; @@ -293,12 +606,85 @@ drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) | BIT(DRM_MODE_BLEND_COVERAGE)); + if (omap_plane_supports_yuv(plane)) + drm_plane_create_color_properties(plane, + BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709), + BIT(DRM_COLOR_YCBCR_FULL_RANGE) | + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), + DRM_COLOR_YCBCR_BT601, + DRM_COLOR_YCBCR_FULL_RANGE); + return plane; error: dev_err(dev->dev, "%s(): could not create plane: %s\n", - __func__, plane_id_to_name[id]); + __func__, omap_plane->name); kfree(omap_plane); return NULL; } + +enum omap_plane_id omap_plane_id_wb(struct drm_plane *plane) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + + return omap_plane->reserved_wb_overlay->overlay_id; +} + +struct drm_plane *omap_plane_reserve_wb(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + int i, ret; + + /* + * Look from the last plane to the first to lessen chances of the + * display side trying to use the same plane as writeback. + */ + for (i = priv->num_planes - 1; i >= 0; --i) { + struct drm_plane *plane = priv->planes[i]; + struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_hw_overlay *new_ovl = NULL; + u32 crtc_mask = (1 << priv->num_pipes) - 1; + u32 fourcc = DRM_FORMAT_YUYV; + u32 caps = OMAP_DSS_OVL_CAP_SCALE; + + if (plane->state->crtc || plane->state->fb) + continue; + + if (omap_plane->reserved_wb_overlay) + continue; + + ret = omap_overlay_assign_wb(priv, plane, caps, fourcc, + crtc_mask, &new_ovl); + if (ret) { + DBG("%s: failed to assign hw_overlay for wb!", + plane->name); + return NULL; + } + + omap_plane->reserved_wb_overlay = new_ovl; + + return plane; + } + + return NULL; +} + +void omap_plane_release_wb(struct drm_plane *plane) +{ + struct omap_drm_private *priv = plane->dev->dev_private; + struct omap_plane *omap_plane; + + /* + * This is also called on module unload at which point plane might + * not be set. In that case just return as there is nothing to do. + */ + if (!plane) + return; + + omap_plane = to_omap_plane(plane); + + omap_overlay_release_wb(priv, plane, omap_plane->reserved_wb_overlay); + omap_plane->reserved_wb_overlay = NULL; +} diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_plane.h b/drivers/gpu/drm/omapdrm/omap_plane.h --- a/drivers/gpu/drm/omapdrm/omap_plane.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_plane.h 2022-01-06 12:45:53.810318090 -0500 @@ -22,5 +22,10 @@ u32 possible_crtcs); void omap_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj); +bool is_omap_plane_dual_overlay(struct drm_plane_state *state); + +enum omap_plane_id omap_plane_id_wb(struct drm_plane *plane); +struct drm_plane *omap_plane_reserve_wb(struct drm_device *dev); +void omap_plane_release_wb(struct drm_plane *plane); #endif /* __OMAPDRM_PLANE_H__ */ diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_wb.c b/drivers/gpu/drm/omapdrm/omap_wb.c --- a/drivers/gpu/drm/omapdrm/omap_wb.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_wb.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot + */ + +#include +#include + +#include "omap_wb.h" + +unsigned int wbdebug; +module_param(wbdebug, uint, 0644); +MODULE_PARM_DESC(wbdebug, "activates debug info"); + +struct wb_fmt wb_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .coplanar = 0, + .depth = {8, 4}, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .coplanar = 1, + .depth = {8, 4}, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .coplanar = 0, + .depth = {16, 0}, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .coplanar = 0, + .depth = {16, 0}, + }, + { + /* "XR24", DRM_FORMAT_XRGB8888 */ + .fourcc = V4L2_PIX_FMT_XBGR32, + .coplanar = 0, + .depth = {32, 0}, + }, +}; + +unsigned int num_wb_formats = ARRAY_SIZE(wb_formats); + +/* find our format description corresponding to the passed v4l2_format */ +struct wb_fmt *find_format(struct v4l2_format *f) +{ + struct wb_fmt *fmt; + unsigned int k; + + for (k = 0; k < num_wb_formats; k++) { + fmt = &wb_formats[k]; + if (fmt->fourcc == f->fmt.pix_mp.pixelformat) + return fmt; + } + + return NULL; +} + +int omap_wb_fourcc_v4l2_to_drm(u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12M: + return DRM_FORMAT_NV12; + case V4L2_PIX_FMT_YUYV: + return DRM_FORMAT_YUYV; + case V4L2_PIX_FMT_UYVY: + return DRM_FORMAT_UYVY; + case V4L2_PIX_FMT_XBGR32: + return DRM_FORMAT_XRGB8888; + default: + WARN(1, "WB: unsupported fourcc\n"); + return 0; + } +} + +void omap_wb_irq(void *priv, u32 irqstatus) +{ + struct wb_dev *dev = (struct wb_dev *)priv; + const u32 mask = OMAP_WB_IRQ_MASK | + DISPC_IRQ_VSYNC | + DISPC_IRQ_VSYNC2 | + DISPC_IRQ_VSYNC3 | + DISPC_IRQ_EVSYNC_EVEN | + DISPC_IRQ_EVSYNC_ODD; + + if (!dev) + return; + + irqstatus &= mask; + if (!irqstatus) + return; + + if (!atomic_read(&dev->irq_enabled)) + return; + + switch (dev->mode) { + case OMAP_WB_NOT_CONFIGURED: + break; + case OMAP_WB_MEM2MEM_OVL: + wbm2m_irq(dev->m2m, irqstatus); + break; + case OMAP_WB_MEM2MEM_MGR: + /* To be added */ + break; + case OMAP_WB_CAPTURE_MGR: + wbcap_irq(dev->cap, irqstatus); + break; + default: + WARN_ONCE(1, "WB: unknown WB mode: 0x%x\n", dev->mode); + break; + } +} + +/* + * The initial setup of this device instance. Note that the initial state of + * the driver should be complete. So the initial format, standard, timings + * and video input should all be initialized to some reasonable value. + */ +int omap_wb_init(struct drm_device *drmdev) +{ + struct omap_drm_private *priv = drmdev->dev_private; + struct wb_dev *dev; + int ret = 0; + + /* Allocate a new instance */ + dev = devm_kzalloc(drmdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->drm_dev = drmdev; + + /* set pseudo v4l2 device name so we can use v4l2_printk */ + strlcpy(dev->v4l2_dev.name, WB_MODULE_NAME, + sizeof(dev->v4l2_dev.name)); + + priv->wb_private = dev; + + mutex_init(&dev->lock); + + atomic_set(&dev->irq_enabled, 0); + + dev->mode = OMAP_WB_NOT_CONFIGURED; + + ret = wbcap_init(dev); + if (ret) { + log_err(dev, "Failed to initialize wb capture\n"); + goto error; + } + + ret = wbm2m_init(dev); + if (ret) { + log_err(dev, "Failed to initialize wb m2m\n"); + goto free_cap; + } + + log_dbg(dev, "WB loaded\n"); + return 0; + +free_cap: + wbcap_cleanup(dev); +error: + return ret; +} + +void omap_wb_cleanup(struct drm_device *drmdev) +{ + struct omap_drm_private *priv = drmdev->dev_private; + struct wb_dev *dev = priv->wb_private; + + log_dbg(dev, "Cleanup WB\n"); + + wbcap_cleanup(dev); + wbm2m_cleanup(dev); +} diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_wb_cap.c b/drivers/gpu/drm/omapdrm/omap_wb_cap.c --- a/drivers/gpu/drm/omapdrm/omap_wb_cap.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_wb_cap.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,1045 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot + */ + +#include +#include +#include + +#include + +#include "omap_wb.h" + +static int omap_channel_to_wb_channel(int oc) +{ + switch (oc) { + case OMAP_DSS_CHANNEL_LCD: + return DSS_WB_LCD1_MGR; + case OMAP_DSS_CHANNEL_DIGIT: + return DSS_WB_TV_MGR; + case OMAP_DSS_CHANNEL_LCD2: + return DSS_WB_LCD2_MGR; + case OMAP_DSS_CHANNEL_LCD3: + return DSS_WB_LCD3_MGR; + default: + return DSS_WB_LCD1_MGR; + } +} + +static char *omap_channel_to_name(int oc) +{ + switch (oc) { + case OMAP_DSS_CHANNEL_LCD: + return "LCD1"; + case OMAP_DSS_CHANNEL_DIGIT: + return "DIGIT/TV"; + case OMAP_DSS_CHANNEL_LCD2: + return "LCD2"; + case OMAP_DSS_CHANNEL_LCD3: + return "LCD3"; + default: + return "LCD1"; + } +} + +/* driver info for each of the supported input overlay/mgr */ +struct wb_input { + char name[64]; + u32 wb_channel; + u32 omap_channel; + u32 crtc_index; +}; + +static struct wb_input wb_inputs[8]; +static int num_wb_input; + +static bool is_input_active(struct wbcap_dev *wbcap) +{ + struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private; + u32 oc = wb_inputs[wbcap->input].omap_channel; + + return priv->dispc_ops->mgr_is_enabled(priv->dispc, oc); +} + +static bool is_input_enabled(struct wbcap_dev *wbcap) +{ + struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private; + struct drm_crtc *crtc; + struct wb_input *input; + + input = &wb_inputs[wbcap->input]; + crtc = priv->pipes[input->crtc_index].crtc; + + return crtc->enabled; +} + +static void build_input_table(struct wbcap_dev *wbcap) +{ + struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private; + struct drm_crtc *crtc; + struct wb_input *input; + int i; + + for (i = 0; i < priv->num_pipes; i++) { + crtc = priv->pipes[i].crtc; + input = &wb_inputs[i]; + + input->crtc_index = i; + input->omap_channel = omap_crtc_channel(crtc); + input->wb_channel = + omap_channel_to_wb_channel(input->omap_channel); + snprintf(input->name, sizeof(input->name), "CRTC#%d - %s", + i, omap_channel_to_name(input->omap_channel)); + + log_dbg(wbcap, "Input# %d, name:'%s' omap_channel:%d wb_channel:%d\n", + i, input->name, input->omap_channel, input->wb_channel); + } + num_wb_input = i; +} + +static struct wb_q_data *get_q_data(struct wbcap_dev *dev, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &dev->q_data[Q_DATA_DST]; + default: + return NULL; + } + return NULL; +} + +static bool wb_cap_setup(struct wbcap_dev *dev, + enum dss_writeback_channel wb_channel, + const struct omap_dss_writeback_info *wb_info) +{ + struct omap_drm_private *priv = dev->dev->drm_dev->dev_private; + struct drm_crtc *crtc; + struct videomode *ct; + int r; + + crtc = priv->pipes[wb_inputs[dev->input].crtc_index].crtc; + ct = omap_crtc_timings(crtc); + + /* configure wb */ + r = priv->dispc_ops->wb_setup(priv->dispc, wb_info, false, ct, wb_channel); + if (r) + return false; + + if (is_input_active(dev)) { + priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, true); + priv->dispc_ops->wb_go(priv->dispc); + } else { + log_err(dev, "CHANNEL %u not enabled, skip WB GO\n", + wb_inputs[dev->input].omap_channel); + } + + return true; +} + +static bool is_input_irq_vsync_set(struct wbcap_dev *dev, u32 irqstatus) +{ + struct omap_drm_private *priv = dev->dev->drm_dev->dev_private; + u32 oc = wb_inputs[dev->input].omap_channel; + + if (irqstatus & priv->dispc_ops->mgr_get_vsync_irq(priv->dispc, oc)) + return true; + return false; +} + +static int wbcap_schedule_next_buffer(struct wbcap_dev *dev) +{ + struct wb_buffer *buf; + unsigned long addr_y = 0; + unsigned long addr_uv = 0; + struct wb_q_data *q_data; + int num_planes; + bool ok; + struct omap_dss_writeback_info wb_info = { 0 }; + struct v4l2_pix_format_mplane *pix; + unsigned long flags; + + if (!is_input_active(dev)) { + dev->next_frm = NULL; + return 0; + } + + spin_lock_irqsave(&dev->qlock, flags); + if (list_empty(&dev->buf_list)) { + dev->next_frm = NULL; + spin_unlock_irqrestore(&dev->qlock, flags); + return 0; + } + + buf = list_entry(dev->buf_list.next, struct wb_buffer, list); + dev->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&dev->qlock, flags); + + q_data = get_q_data(dev, buf->vb.vb2_buf.type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + num_planes = pix->num_planes; + + addr_y = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + if (num_planes == 2) + addr_uv = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1); + else if (pix->pixelformat == V4L2_PIX_FMT_NV12) + addr_uv = addr_y + (pix->plane_fmt[0].bytesperline * + pix->height); + + /* fill WB DSS info */ + wb_info.paddr = (u32)addr_y; + wb_info.p_uv_addr = (u32)addr_uv; + wb_info.buf_width = pix->plane_fmt[0].bytesperline / + (q_data->fmt->depth[LUMA_PLANE] / 8); + + wb_info.width = pix->width; + wb_info.height = pix->height; + wb_info.fourcc = omap_wb_fourcc_v4l2_to_drm(pix->pixelformat); + wb_info.pre_mult_alpha = 1; + + wb_info.rotation = DRM_MODE_ROTATE_0; + wb_info.rotation_type = OMAP_DSS_ROT_NONE; + + ok = wb_cap_setup(dev, + wb_inputs[dev->input].wb_channel, + &wb_info); + if (!ok) + return -EINVAL; + + return 0; +} + +static void wbcap_process_buffer_complete(struct wbcap_dev *dev) +{ + dev->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); + dev->cur_frm->vb.field = dev->field; + dev->cur_frm->vb.sequence = dev->sequence++; + + vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); + dev->cur_frm = dev->next_frm; +} + +static enum hrtimer_restart wbcap_wbgo_timer(struct hrtimer *timer) +{ + struct wbcap_dev *dev = container_of(timer, + struct wbcap_dev, wbgo_timer); + struct omap_drm_private *priv = dev->dev->drm_dev->dev_private; + + if (priv->dispc_ops->wb_go_busy(priv->dispc)) + log_err(dev, "WARNING, WB BUSY at hrtimer, state %u\n", + dev->state); + + switch (dev->state) { + case WB_STATE_NONE: + break; + + case WB_STATE_FIRST_FRAME: + dev->cur_frm = dev->next_frm; + wbcap_schedule_next_buffer(dev); + dev->state = WB_STATE_CAPTURING; + break; + + case WB_STATE_CAPTURING: + if (dev->cur_frm && dev->next_frm) { + /* + * We have cur_frm that was just captured, and next_frm + * to which the HW will start capturing. + * This means cur_frm is now released from DSS HW. + */ + wbcap_process_buffer_complete(dev); + dev->next_frm = NULL; + } else { + /* + * We have cur_frm which has a captured frame, + * but we don't have next_frm. + * This means cur_frm is will still be used by + * DSS for capture + */ + } + + if (dev->stopping) { + /* XXX should we set WB GO? */ + priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, + false); + dev->state = WB_STATE_STOPPING; + } else { + wbcap_schedule_next_buffer(dev); + } + break; + + case WB_STATE_STOPPING: + if (dev->cur_frm) + wbcap_process_buffer_complete(dev); + + dev->state = WB_STATE_STOPPED; + atomic_dec(&dev->dev->irq_enabled); + dev->stopping = false; + wake_up(&dev->event); + break; + + case WB_STATE_STOPPED: + log_err(dev, "ERROR: timer triggered in the stopped state. This shouldn't happen\n"); + break; + } + + return HRTIMER_NORESTART; +} + +static void wbcap_handle_vsync(struct wbcap_dev *dev) +{ + /* + * In writeback capture mode, the GO bit doesn't get reset + * at the manager's VSYNC interrupt. It takes an extra + * 'WBDELAYCOUNTER' time after VSYNC when the writeback + * FIFOs are flushed and the shadow registers are taken in. + * There isn't any DSS interrupt to notify this point in time. + * The correct solution is to set a timer far enough that it + * should cover the period defined by WBDELAYCOUNTER. + * The max value allowed in WBDELAYCOUNTER is 255 which + * correspond to 255 lines. So waiting anywhere from 1/4 to + * 1/2 a frame (i.e. 2ms at 60 to 120 fps) should be safe + * enough. + */ + + hrtimer_start_range_ns(&dev->wbgo_timer, ms_to_ktime(3), 1000000, + HRTIMER_MODE_REL); +} + +void wbcap_irq(struct wbcap_dev *dev, u32 irqstatus) +{ + if (irqstatus & DISPC_IRQ_FRAMEDONEWB) + log_dbg(dev, "WB: FRAMEDONE\n"); + + if (irqstatus & DISPC_IRQ_WBBUFFEROVERFLOW) + log_err(dev, "WB: UNDERFLOW\n"); + + if (irqstatus & DISPC_IRQ_WBUNCOMPLETEERROR) + log_err(dev, "WB: WBUNCOMPLETEERROR\n"); + + if (is_input_irq_vsync_set(dev, irqstatus)) { + if (dev->field != V4L2_FIELD_NONE) { + if (irqstatus & DISPC_IRQ_EVSYNC_EVEN) + dev->field = V4L2_FIELD_BOTTOM; + else if (irqstatus & DISPC_IRQ_EVSYNC_ODD) + dev->field = V4L2_FIELD_TOP; + } + wbcap_handle_vsync(dev); + } +} + +/* + * Setup the constraints of the queue: besides setting the number of planes + * per buffer and the size and allocation context of each plane, it also + * checks if sufficient buffers have been allocated. Usually 3 is a good + * minimum number: many DMA engines need a minimum of 2 buffers in the + * queue and you need to have another available for userspace processing. + */ +static int queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + int i; + struct wbcap_dev *wbcap = vb2_get_drv_priv(vq); + struct wb_q_data *q_data; + + q_data = get_q_data(wbcap, vq->type); + + if (!q_data) + return -EINVAL; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = q_data->format.fmt.pix_mp.num_planes; + + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->format.fmt.pix_mp.plane_fmt[i].sizeimage; + + log_dbg(wbcap, "get %d buffer(s) of size %d\n", *nbuffers, + sizes[LUMA_PLANE]); + if (*nplanes == 2) + log_dbg(wbcap, " and %d\n", sizes[CHROMA_PLANE]); + + return 0; +} + +/* + * Prepare the buffer for queueing to the DMA engine: check and set the + * payload size. + */ +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct wbcap_dev *wbcap = vb2_get_drv_priv(vb->vb2_queue); + struct wb_q_data *q_data; + struct v4l2_pix_format_mplane *mp; + int i, num_planes; + + q_data = get_q_data(wbcap, vb->vb2_queue->type); + if (!q_data) + return -EINVAL; + num_planes = q_data->format.fmt.pix_mp.num_planes; + + for (i = 0; i < num_planes; i++) { + mp = &q_data->format.fmt.pix_mp; + if (vb2_plane_size(vb, i) < mp->plane_fmt[i].sizeimage) { + log_err(wbcap, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, i), + (long)mp->plane_fmt[i].sizeimage); + return -EINVAL; + } + vb2_set_plane_payload(vb, i, mp->plane_fmt[i].sizeimage); + } + + return 0; +} + +/* + * Queue this buffer to the DMA engine. + */ +static void buffer_queue(struct vb2_buffer *vb) +{ + struct wbcap_dev *wbcap = vb2_get_drv_priv(vb->vb2_queue); + struct wb_buffer *buf = to_wb_buffer(vb); + unsigned long flags; + + spin_lock_irqsave(&wbcap->qlock, flags); + list_add_tail(&buf->list, &wbcap->buf_list); + + spin_unlock_irqrestore(&wbcap->qlock, flags); +} + +static void return_all_buffers(struct wbcap_dev *wbcap, + enum vb2_buffer_state state) +{ + struct wb_buffer *buf, *node; + unsigned long flags; + + spin_lock_irqsave(&wbcap->qlock, flags); + list_for_each_entry_safe(buf, node, &wbcap->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } + + if (wbcap->cur_frm) { + vb2_buffer_done(&wbcap->cur_frm->vb.vb2_buf, state); + wbcap->cur_frm = NULL; + } + + if (wbcap->next_frm) { + vb2_buffer_done(&wbcap->next_frm->vb.vb2_buf, state); + wbcap->next_frm = NULL; + } + + spin_unlock_irqrestore(&wbcap->qlock, flags); +} + +/* + * Start streaming. First check if the minimum number of buffers have been + * queued. If not, then return -ENOBUFS and the vb2 framework will call + * this function again the next time a buffer has been queued until enough + * buffers are available to actually start the DMA engine. + */ +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct wbcap_dev *wbcap = vb2_get_drv_priv(vq); + struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private; + struct drm_crtc *crtc; + int ret; + struct wb_q_data *q_data; + + priv->dispc_ops->runtime_get(priv->dispc); + + wbcap->sequence = 0; + q_data = get_q_data(wbcap, wbcap->queue.type); + if (!q_data) { + log_err(wbcap, "ERROR: getting q_data failed\n"); + return_all_buffers(wbcap, VB2_BUF_STATE_QUEUED); + priv->dispc_ops->runtime_put(priv->dispc); + return -EINVAL; + } + + if (q_data->format.fmt.pix_mp.field == V4L2_FIELD_ALTERNATE) + wbcap->field = V4L2_FIELD_TOP; + else + wbcap->field = V4L2_FIELD_NONE; + + log_dbg(wbcap, "Input (%s) is %s : %s\n", + wb_inputs[wbcap->input].name, + is_input_enabled(wbcap) ? "enabled" : "disabled", + is_input_active(wbcap) ? "active" : "inactive"); + + if (!is_input_active(wbcap)) { + log_err(wbcap, "ERROR: Selected input (%s) is not active, bailing out\n", + wb_inputs[wbcap->input].name); + return_all_buffers(wbcap, VB2_BUF_STATE_QUEUED); + priv->dispc_ops->runtime_put(priv->dispc); + return -EINVAL; + } + + /* Enable vsync irq on the input crtc */ + crtc = priv->pipes[wb_inputs[wbcap->input].crtc_index].crtc; + ret = drm_crtc_vblank_get(crtc); + WARN_ON(ret != 0); + + if (wbcap_schedule_next_buffer(wbcap)) { + return_all_buffers(wbcap, VB2_BUF_STATE_QUEUED); + priv->dispc_ops->runtime_put(priv->dispc); + return -EINVAL; + } + + wbcap->state = WB_STATE_FIRST_FRAME; + atomic_inc(&wbcap->dev->irq_enabled); + return 0; +} + +/* + * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued + * and passed on to the vb2 framework marked as STATE_ERROR. + */ +static void stop_streaming(struct vb2_queue *vq) +{ + struct wbcap_dev *wbcap = vb2_get_drv_priv(vq); + struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private; + struct drm_crtc *crtc; + int ret; + + log_dbg(wbcap, "Stopping WB\n"); + log_dbg(wbcap, "current state: %d\n", wbcap->state); + + wbcap->stopping = true; + ret = wait_event_timeout(wbcap->event, + !wbcap->stopping, + msecs_to_jiffies(250)); + + log_dbg(wbcap, "Returning VB2 buffers\n"); + + if (priv->dispc_ops->wb_go_busy(priv->dispc)) + log_err(wbcap, "WARNING, WB BUSY when stopping\n"); + + /* Release all active buffers */ + return_all_buffers(wbcap, VB2_BUF_STATE_ERROR); + + /* Disable vsync irq on the input crtc */ + crtc = priv->pipes[wb_inputs[wbcap->input].crtc_index].crtc; + drm_crtc_vblank_put(crtc); + + priv->dispc_ops->runtime_put(priv->dispc); +} + +/* + * The vb2 queue ops. Note that since q->lock is set we can use the standard + * vb2_ops_wait_prepare/finish helper functions. If q->lock would be NULL, + * then this driver would have to provide these ops. + */ +static struct vb2_ops wbcap_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* + * Required ioctl querycap. Note that the version field is prefilled with + * the version of the kernel. + */ +static int wbcap_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct wbcap_dev *wbcap = video_drvdata(file); + + strscpy(cap->driver, WBCAP_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, WBCAP_MODULE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + wbcap->v4l2_dev.name); + return 0; +} + +/* + * Helper function to check and correct struct v4l2_pix_format. It's used + * not only in VIDIOC_TRY/S_FMT, but also elsewhere if changes to the SDTV + * standard, HDTV timings or the video input would require updating the + * current format. + */ +static int wbcap_fill_pix_format(struct wbcap_dev *wbcap, + struct v4l2_format *f) +{ + struct wb_fmt *fmt = find_format(f); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt; + unsigned int w_align; + int i, depth, depth_bytes; + + if (!fmt) { + log_dbg(wbcap, "Fourcc format (0x%08x) invalid.\n", + pix->pixelformat); + fmt = &wb_formats[1]; + } + + /* we only allow V4L2_FIELD_NONE or V4L2_FIELD_ALTERNATE */ + if (pix->field != V4L2_FIELD_NONE && + pix->field != V4L2_FIELD_ALTERNATE) + pix->field = V4L2_FIELD_NONE; + + depth = fmt->depth[LUMA_PLANE]; + + /* + * The line stride needs to be even is even. + * Special case is with YUV422 interleaved format an even number + * of pixels is required also. + */ + depth_bytes = depth >> 3; + + w_align = 0; + if ((depth_bytes == 3) || (depth_bytes == 1)) + w_align = 1; + else if ((depth_bytes == 2) && + (fmt->fourcc == V4L2_PIX_FMT_YUYV || + fmt->fourcc == V4L2_PIX_FMT_UYVY)) + w_align = 1; + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, + &pix->height, MIN_H, MAX_H, H_ALIGN, + S_ALIGN); + pix->num_planes = fmt->coplanar ? 2 : 1; + pix->pixelformat = fmt->fourcc; + + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + memset(pix->reserved, 0, sizeof(pix->reserved)); + for (i = 0; i < pix->num_planes; i++) { + plane_fmt = &pix->plane_fmt[i]; + depth = fmt->depth[i]; + + if (i == LUMA_PLANE) + plane_fmt->bytesperline = pix->width * depth / 8; + else + plane_fmt->bytesperline = pix->width; + + plane_fmt->sizeimage = (pix->height * pix->width * + depth) / 8; + + if (fmt->fourcc == V4L2_PIX_FMT_NV12) { + /* + * Since we are using a single plane buffer + * we need to adjust the reported sizeimage + * to include the colocated UV part. + */ + plane_fmt->sizeimage += (pix->height / 2 * + plane_fmt->bytesperline); + } + + memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); + } + + return 0; +} + +static int wbcap_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct wbcap_dev *wbcap = video_drvdata(file); + struct omap_drm_private *drmpriv = wbcap->dev->drm_dev->dev_private; + struct drm_crtc *crtc; + struct videomode *ct; + + log_dbg(wbcap, "requested fourcc:%4.4s size: %dx%d\n", + (char *)&f->fmt.pix_mp.pixelformat, + f->fmt.pix_mp.width, f->fmt.pix_mp.height); + + /* + * Scaling currently does not work properly for Capture mode. + * So we are temporarily forcing the frame size to be the + * same as the source crtc for now. + */ + crtc = drmpriv->pipes[wb_inputs[wbcap->input].crtc_index].crtc; + ct = omap_crtc_timings(crtc); + + f->fmt.pix.width = ct->hactive; + f->fmt.pix.height = ct->vactive; + + if (ct->flags & DISPLAY_FLAGS_INTERLACED) { + f->fmt.pix.height /= 2; + f->fmt.pix_mp.field = V4L2_FIELD_ALTERNATE; + } + + log_dbg(wbcap, "replied fourcc:%4.4s size: %dx%d\n", + (char *)&f->fmt.pix_mp.pixelformat, + f->fmt.pix_mp.width, f->fmt.pix_mp.height); + + return wbcap_fill_pix_format(wbcap, f); +} + +static int wbcap_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct wbcap_dev *wbcap = video_drvdata(file); + int ret; + struct wb_q_data *q_data; + + log_dbg(wbcap, "type:%d\n", f->type); + + ret = wbcap_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + q_data = get_q_data(wbcap, f->type); + if (!q_data) + return -EINVAL; + + /* + * It is not allowed to change the format while buffers for use with + * streaming have already been allocated. + */ + if (vb2_is_busy(&wbcap->queue)) + return -EBUSY; + + q_data->format = *f; + q_data->fmt = find_format(f); + + log_dbg(wbcap, "Setting format for type %d, %dx%d, fmt: %4.4s bpl_y %d", + f->type, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + (char *)&f->fmt.pix_mp.pixelformat, + f->fmt.pix_mp.plane_fmt[LUMA_PLANE].bytesperline); + if (f->fmt.pix_mp.num_planes == 2) + log_dbg(wbcap, " bpl_uv %d\n", + f->fmt.pix_mp.plane_fmt[CHROMA_PLANE].bytesperline); + + return 0; +} + +static int wbcap_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct wbcap_dev *wbcap = video_drvdata(file); + struct wb_q_data *q_data; + + log_dbg(wbcap, "type:%d\n", f->type); + + q_data = get_q_data(wbcap, f->type); + if (!q_data) + return -EINVAL; + + *f = q_data->format; + return 0; +} + +static int wbcap_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= num_wb_formats) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f->pixelformat = wb_formats[f->index].fourcc; + return 0; +} + +static int wbcap_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index >= num_wb_input) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + strlcpy(i->name, wb_inputs[i->index].name, sizeof(i->name)); + return 0; +} + +static int wbcap_s_input(struct file *file, void *priv, unsigned int i) +{ + struct wbcap_dev *wbcap = video_drvdata(file); + struct wb_q_data *q_data; + + log_dbg(wbcap, "%d\n", i); + + q_data = get_q_data(wbcap, wbcap->queue.type); + if (!q_data) + return -EINVAL; + + if (i >= num_wb_input) + return -EINVAL; + + /* + * Changing the input implies a format change, which is not allowed + * while buffers for use with streaming have already been allocated. + */ + if (vb2_is_busy(&wbcap->queue)) + return -EBUSY; + + wbcap->input = i; + + /* Update the internal format to match the selected input */ + wbcap_try_fmt_vid_cap(file, priv, &q_data->format); + return 0; +} + +static int wbcap_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct wbcap_dev *wbcap = video_drvdata(file); + + log_dbg(wbcap, "%d\n", wbcap->input); + + *i = wbcap->input; + return 0; +} + +/* + * File operations + */ +static int wbcap_open(struct file *file) +{ + struct wbcap_dev *dev = video_drvdata(file); + int ret; + + log_dbg(dev, "enter\n"); + + if (mutex_lock_interruptible(&dev->dev->lock)) { + ret = -ERESTARTSYS; + goto unlock; + } + + if ((dev->dev->mode != OMAP_WB_NOT_CONFIGURED) && + (dev->dev->mode != OMAP_WB_CAPTURE_MGR)) { + /* WB is already open for other modes */ + ret = -EBUSY; + goto unlock; + } + + ret = v4l2_fh_open(file); + if (ret) { + log_err(dev, "v4l2_fh_open failed\n"); + goto unlock; + } + + if (v4l2_fh_is_singular_file(file)) + dev->dev->mode = OMAP_WB_CAPTURE_MGR; + +unlock: + mutex_unlock(&dev->dev->lock); + return ret; +} + +static int wbcap_release(struct file *file) +{ + struct wbcap_dev *dev = video_drvdata(file); + bool fh_singular; + int ret; + + log_dbg(dev, "releasing\n"); + + mutex_lock(&dev->dev->lock); + + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + dev->dev->mode = OMAP_WB_NOT_CONFIGURED; + + mutex_unlock(&dev->dev->lock); + + return ret; +} + +/* + * The set of all supported ioctls. Note that all the streaming ioctls + * use the vb2 helper functions that take care of all the locking and + * that also do ownership tracking (i.e. only the filehandle that requested + * the buffers can call the streaming ioctls, all other filehandles will + * receive -EBUSY if they attempt to call the same streaming ioctls). + * + * The last three ioctls also use standard helper functions: these implement + * standard behavior for drivers with controls. + */ +static const struct v4l2_ioctl_ops wbcap_ioctl_ops = { + .vidioc_querycap = wbcap_querycap, + .vidioc_try_fmt_vid_cap_mplane = wbcap_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = wbcap_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = wbcap_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = wbcap_enum_fmt_vid_cap, + + .vidioc_enum_input = wbcap_enum_input, + .vidioc_g_input = wbcap_g_input, + .vidioc_s_input = wbcap_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * The set of file operations. Note that all these ops are standard core + * helper functions. + */ +static const struct v4l2_file_operations wbcap_fops = { + .owner = THIS_MODULE, + .open = wbcap_open, + .release = wbcap_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +/* + * The initial setup of this device instance. Note that the initial state of + * the driver should be complete. So the initial format, standard, timings + * and video input should all be initialized to some reasonable value. + */ +int wbcap_init(struct wb_dev *dev) +{ + struct wbcap_dev *wbcap; + struct video_device *vdev; + struct vb2_queue *q; + struct wb_q_data *q_data; + int ret; + + if (!dev) + return -ENOMEM; + + /* Allocate a new instance */ + wbcap = devm_kzalloc(dev->drm_dev->dev, sizeof(*wbcap), GFP_KERNEL); + if (!wbcap) + return -ENOMEM; + + dev->cap = wbcap; + wbcap->dev = dev; + + /* Fill in the initial format-related settings */ + q_data = &wbcap->q_data[Q_DATA_DST]; + q_data->fmt = &wb_formats[1]; + q_data->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q_data->format.fmt.pix_mp.pixelformat = q_data->fmt->fourcc; + q_data->format.fmt.pix_mp.width = 1920; + q_data->format.fmt.pix_mp.height = 1080; + wbcap_fill_pix_format(wbcap, &q_data->format); + + /* Initialize the top-level structure */ + strlcpy(wbcap->v4l2_dev.name, WBCAP_MODULE_NAME, + sizeof(wbcap->v4l2_dev.name)); + ret = v4l2_device_register(dev->drm_dev->dev, &wbcap->v4l2_dev); + if (ret) + return ret; + + /* + * This lock is now created by the main level. + * We might need one per sub structure in the future + * + * mutex_init(&dev->lock); + */ + + /* Initialize the vb2 queue */ + q = &wbcap->queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = wbcap; + q->buf_struct_size = sizeof(struct wb_buffer); + q->ops = &wbcap_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->dev = wbcap->v4l2_dev.dev; + + /* + * Assume that this DMA engine needs to have at least two buffers + * available before it can be started. The start_streaming() op + * won't be called until at least this many buffers are queued up. + */ + q->min_buffers_needed = 2; + /* + * The serialization lock for the streaming ioctls. This is the same + * as the main serialization lock, but if some of the non-streaming + * ioctls could take a long time to execute, then you might want to + * have a different lock here to prevent VIDIOC_DQBUF from being + * blocked while waiting for another action to finish. This is + * generally not needed for PCI devices, but USB devices usually do + * want a separate lock here. + */ + q->lock = &dev->lock; + /* + * Since this driver can only do 32-bit DMA we must make sure that + * the vb2 core will allocate the buffers in 32-bit DMA memory. + */ + q->gfp_flags = GFP_DMA32; + ret = vb2_queue_init(q); + if (ret) + goto free_hdl; + + INIT_LIST_HEAD(&wbcap->buf_list); + spin_lock_init(&wbcap->qlock); + + /* Initialize the video_device structure */ + vdev = &wbcap->vdev; + strlcpy(vdev->name, WBCAP_MODULE_NAME, sizeof(vdev->name)); + /* + * There is nothing to clean up, so release is set to an empty release + * function. The release callback must be non-NULL. + */ + vdev->release = video_device_release_empty; + vdev->fops = &wbcap_fops, + vdev->ioctl_ops = &wbcap_ioctl_ops, + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + /* + * The main serialization lock. All ioctls are serialized by this + * lock. Exception: if q->lock is set, then the streaming ioctls + * are serialized by that separate lock. + */ + vdev->lock = &dev->lock; + vdev->queue = q; + vdev->v4l2_dev = &wbcap->v4l2_dev; + video_set_drvdata(vdev, wbcap); + + hrtimer_init(&wbcap->wbgo_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + wbcap->wbgo_timer.function = wbcap_wbgo_timer; + + init_waitqueue_head(&wbcap->event); + wbcap->stopping = false; + + build_input_table(wbcap); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, 11); + if (ret) + goto free_hdl; + + log_dbg(wbcap, "Device registered as %s\n", + video_device_node_name(vdev)); + return 0; + +free_hdl: + v4l2_device_unregister(&wbcap->v4l2_dev); + return ret; +} + +void wbcap_cleanup(struct wb_dev *dev) +{ + log_dbg(dev, "Cleanup WB Capture\n"); + + video_unregister_device(&dev->cap->vdev); + v4l2_device_unregister(&dev->cap->v4l2_dev); +} diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_wb.h b/drivers/gpu/drm/omapdrm/omap_wb.h --- a/drivers/gpu/drm/omapdrm/omap_wb.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_wb.h 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot + */ + +#ifndef __OMAP_WB_H__ +#define __OMAP_WB_H__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dss/omapdss.h" +#include "omap_drv.h" + +#define WB_MODULE_NAME "omapwb" +#define WBM2M_MODULE_NAME "omapwb-m2m" +#define WBCAP_MODULE_NAME "omapwb-cap" + +extern unsigned int wbdebug; + +#define log_dbg(dev, fmt, arg...) \ + v4l2_dbg(1, wbdebug, &dev->v4l2_dev, "%s: " fmt, \ + __func__, ## arg) +#define log_err(dev, fmt, arg...) \ + v4l2_err(&dev->v4l2_dev, fmt, ## arg) +#define log_info(dev, fmt, arg...) \ + v4l2_info(&dev->v4l2_dev, fmt, ## arg) + +/* minimum and maximum frame sizes */ +#define MIN_W 32 +#define MIN_H 32 +#define MAX_W 2048 +#define MAX_H 2048 + +/* required alignments */ +#define S_ALIGN 0 /* multiple of 1 */ +#define H_ALIGN 0 /* multiple of 2 */ + +/* used as plane indices */ +#define MAX_PLANES 2 +#define LUMA_PLANE 0 +#define CHROMA_PLANE 1 + +enum omap_wb_mode { + OMAP_WB_NOT_CONFIGURED = 0, + /* mem2mem from single ovl to wb */ + OMAP_WB_MEM2MEM_OVL = 1, + /* mem2mem from N overlays via single mgr to wb */ + OMAP_WB_MEM2MEM_MGR = 2, + /* capture from single mgr to wb */ + OMAP_WB_CAPTURE_MGR = 3 +}; + +enum wb_state { + WB_STATE_NONE = 0, + WB_STATE_FIRST_FRAME, + WB_STATE_CAPTURING, + WB_STATE_STOPPING, + WB_STATE_STOPPED, +}; + +/* driver info for each of the supported video formats */ +struct wb_fmt { + u32 fourcc; /* standard format identifier */ + u8 coplanar; /* set for unpacked Luma and Chroma */ + u8 depth[MAX_PLANES]; /* Bits per pixel per plane*/ +}; + +extern struct wb_fmt wb_formats[]; +extern unsigned int num_wb_formats; + +struct wb_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +/* + * per-queue, driver-specific private data. + * MEM-2-MEM: Source: V4L2_BUF_TYPE_VIDEO_OUTPUT* + * Destination: V4L2_BUF_TYPE_VIDEO_CAPTURE* + * CAPTURE: Destination: V4L2_BUF_TYPE_VIDEO_CAPTURE* only + */ +struct wb_q_data { + /* format info */ + struct v4l2_format format; + /* crop/compose rectangle */ + struct v4l2_rect c_rect; + /* format info */ + struct wb_fmt *fmt; +}; + +enum { + Q_DATA_SRC = 0, + Q_DATA_DST = 1, +}; + +/* find our format description corresponding to the passed v4l2_format */ +struct wb_fmt *find_format(struct v4l2_format *f); + +struct wb_dev { + struct v4l2_device v4l2_dev; + struct drm_device *drm_dev; + + atomic_t irq_enabled; + + /* v4l2_ioctl mutex */ + struct mutex lock; + + enum omap_wb_mode mode; + struct wbcap_dev *cap; + struct wbm2m_dev *m2m; +}; + +/* + * there is one wbcap_dev structure in the driver. + */ +struct wbcap_dev { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_fh fh; + struct wb_dev *dev; + struct v4l2_ctrl_handler hdl; + + /* dst queue data */ + struct wb_q_data q_data[2]; + + unsigned int input; + + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + + spinlock_t qlock; + struct list_head buf_list; + + /* Current v4l2_buffer */ + struct wb_buffer *cur_frm; + /* Next v4l2_buffer */ + struct wb_buffer *next_frm; + + unsigned int field; + unsigned int sequence; + + bool stopping; + wait_queue_head_t event; + + enum wb_state state; + + /* timer used to wait for wb go bit to be cleared */ + struct hrtimer wbgo_timer; +}; + +/* + * there is one wbm2m_dev structure in the driver. + */ +struct wbm2m_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct v4l2_m2m_dev *m2m_dev; + struct wb_dev *dev; + struct drm_plane *plane; + + /* v4l2 buffers lock */ + spinlock_t lock; + + struct vb2_alloc_ctx *alloc_ctx; +}; + +/* + * There is one wbm2m_ctx structure for each m2m context. + */ +struct wbm2m_ctx { + struct v4l2_fh fh; + struct wbm2m_dev *dev; + struct v4l2_ctrl_handler hdl; + + /* current frame seq */ + unsigned int sequence; + /* abort after next irq */ + unsigned int aborting; + + /* src & dst queue data */ + struct wb_q_data q_data[2]; +}; + +static inline struct wb_buffer *to_wb_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct wb_buffer, vb.vb2_buf); +} + +int omap_wb_fourcc_v4l2_to_drm(u32 fourcc); + +void wbm2m_irq(struct wbm2m_dev *dev, uint32_t irqstatus); +int wbm2m_init(struct wb_dev *dev); +void wbm2m_cleanup(struct wb_dev *dev); + +void wbcap_irq(struct wbcap_dev *dev, u32 irqstatus); +int wbcap_init(struct wb_dev *dev); +void wbcap_cleanup(struct wb_dev *dev); + +#endif /* __OMAP_WB_H__ */ diff -Naur --no-dereference a/drivers/gpu/drm/omapdrm/omap_wb_m2m.c b/drivers/gpu/drm/omapdrm/omap_wb_m2m.c --- a/drivers/gpu/drm/omapdrm/omap_wb_m2m.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/gpu/drm/omapdrm/omap_wb_m2m.c 2022-01-06 12:45:53.810318090 -0500 @@ -0,0 +1,1198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Benoit Parrot + * + * Based on the virtual v4l2-mem2mem example device + */ + +#include +#include +#include +#include + +#include "omap_wb.h" + +MODULE_DESCRIPTION("TI OMAP WB M2M driver"); +MODULE_AUTHOR("Benoit Parrot "); +MODULE_LICENSE("GPL v2"); + +/* + * M2M devices get 2 queues. + * Return the queue given the type. + */ +static struct wb_q_data *get_q_data(struct wbm2m_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &ctx->q_data[Q_DATA_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &ctx->q_data[Q_DATA_DST]; + default: + return NULL; + } + return NULL; +} + +static bool wbm2m_convert(struct wbm2m_dev *dev, enum omap_plane_id src_plane, + const struct omap_overlay_info *src_info, + const struct omap_dss_writeback_info *wb_info) +{ + struct omap_drm_private *priv = dev->dev->drm_dev->dev_private; + enum dss_writeback_channel wb_channel; + struct videomode t = { 0 }; + int r; + + t.hactive = src_info->out_width; + t.vactive = src_info->out_height; + + /* configure input */ + + r = priv->dispc_ops->ovl_setup(priv->dispc, src_plane, src_info, &t, + true, OMAP_DSS_CHANNEL_WB); + if (r) + return false; + + priv->dispc_ops->ovl_enable(priv->dispc, src_plane, true); + + /* configure output */ + + switch (src_plane) { + case OMAP_DSS_GFX: + wb_channel = DSS_WB_OVL0; break; + case OMAP_DSS_VIDEO1: + wb_channel = DSS_WB_OVL1; break; + case OMAP_DSS_VIDEO2: + wb_channel = DSS_WB_OVL2; break; + case OMAP_DSS_VIDEO3: + wb_channel = DSS_WB_OVL3; break; + default: + /* + * if src_plane is not valid it should have been flagged + * during the ovl_setup() step above. Let's set a default + * at any rate. + */ + wb_channel = DSS_WB_OVL3; break; + } + + r = priv->dispc_ops->wb_setup(priv->dispc, wb_info, true, &t, + wb_channel); + if (r) { + priv->dispc_ops->ovl_enable(priv->dispc, src_plane, false); + return false; + } + + priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, true); + + return true; +} + +/* + * mem2mem callbacks + */ + +/** + * job_ready() - check whether an instance is ready to be scheduled to run + */ +static int job_ready(void *priv) +{ + struct wbm2m_ctx *ctx = priv; + + /* + * This check is needed as this might be called directly from driver + * When called by m2m framework, this will always satisy, but when + * called from wbm2m_irq, this might fail. + * (src stream with zero buffers) + */ + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) <= 0 || + v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) <= 0) + return 0; + + return 1; +} + +static void job_abort(void *priv) +{ + struct wbm2m_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; + + log_dbg(ctx->dev, "Aborting transaction\n"); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); +} + +/* device_run() - prepares and starts the device + * + * This function is only called when both the source and destination + * buffers are in place. + */ +static void device_run(void *priv) +{ + struct wbm2m_ctx *ctx = priv; + struct wbm2m_dev *dev = ctx->dev; + struct wb_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + struct wb_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + struct vb2_buffer *s_vb, *d_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + dma_addr_t src_dma_addr[2] = {0, 0}; + dma_addr_t dst_dma_addr[2] = {0, 0}; + struct omap_overlay_info src_info = { 0 }; + struct omap_dss_writeback_info wb_info = { 0 }; + struct v4l2_pix_format_mplane *spix, *dpix; + struct v4l2_rect *srect, *drect; + u32 stride, depth; + bool ok; + + src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + if (!src_vb) { + log_err(dev, "getting next source buffer failed\n"); + return; + } + + s_vb = &src_vb->vb2_buf; + if (!s_vb) { + log_err(dev, "getting next src vb2_buf addr failed\n"); + return; + } + + dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + if (!dst_vb) { + log_err(dev, "getting next dest buffer failed\n"); + return; + } + + d_vb = &dst_vb->vb2_buf; + if (!d_vb) { + log_err(dev, "getting next dest vb2_buf addr failed\n"); + return; + } + + srect = &s_q_data->c_rect; + spix = &s_q_data->format.fmt.pix_mp; + src_dma_addr[0] = vb2_dma_contig_plane_dma_addr(s_vb, 0); + if (spix->num_planes == 2) + src_dma_addr[1] = vb2_dma_contig_plane_dma_addr(s_vb, 1); + else if (spix->pixelformat == V4L2_PIX_FMT_NV12) + src_dma_addr[1] = src_dma_addr[0] + + (spix->plane_fmt[0].bytesperline * spix->height); + if (!src_dma_addr[0]) { + log_err(dev, + "acquiring source buffer(%d) dma_addr failed\n", + s_vb->index); + return; + } + + drect = &d_q_data->c_rect; + dpix = &d_q_data->format.fmt.pix_mp; + dst_dma_addr[0] = vb2_dma_contig_plane_dma_addr(d_vb, 0); + if (dpix->num_planes == 2) + dst_dma_addr[1] = vb2_dma_contig_plane_dma_addr(d_vb, 1); + else if (dpix->pixelformat == V4L2_PIX_FMT_NV12) + dst_dma_addr[1] = dst_dma_addr[0] + + (dpix->plane_fmt[0].bytesperline * dpix->height); + if (!dst_dma_addr[0]) { + log_err(dev, + "acquiring destination buffer(%d) dma_addr failed\n", + d_vb->index); + return; + } + + /* fill source DSS info */ + src_info.paddr = (u32)src_dma_addr[0]; + src_info.p_uv_addr = (u32)src_dma_addr[1]; + + /* update addr based on cropping window */ + stride = spix->plane_fmt[0].bytesperline; + depth = s_q_data->fmt->depth[0]; + src_info.paddr += srect->top * stride + (srect->left * depth / 8); + + if (src_info.p_uv_addr) { + u32 top = srect->top; + + if (spix->pixelformat == V4L2_PIX_FMT_NV12 || + spix->pixelformat == V4L2_PIX_FMT_NV12M) { + top >>= 1; + depth = 8; + } + src_info.p_uv_addr += top * stride + (srect->left * depth / 8); + } + + src_info.screen_width = spix->plane_fmt[0].bytesperline / + (s_q_data->fmt->depth[0] / 8); + + src_info.width = srect->width; + src_info.height = srect->height; + + src_info.pos_x = 0; + src_info.pos_y = 0; + src_info.out_width = srect->width; + src_info.out_height = srect->height; + + src_info.fourcc = omap_wb_fourcc_v4l2_to_drm(spix->pixelformat); + src_info.global_alpha = 0xff; + + src_info.rotation = DRM_MODE_ROTATE_0; + src_info.rotation_type = OMAP_DSS_ROT_NONE; + + log_dbg(dev, "SRC: ctx %pa buf_index %d %dx%d, sw %d\n", + &ctx, s_vb->index, + src_info.width, src_info.height, src_info.screen_width); + + /* fill WB DSS info */ + wb_info.paddr = (u32)dst_dma_addr[0]; + wb_info.p_uv_addr = (u32)dst_dma_addr[1]; + + wb_info.buf_width = dpix->plane_fmt[0].bytesperline / + (d_q_data->fmt->depth[0] / 8); + + /* update addr based on compose window */ + stride = dpix->plane_fmt[0].bytesperline; + depth = d_q_data->fmt->depth[0]; + wb_info.paddr += drect->top * stride + (drect->left * depth / 8); + + if (wb_info.p_uv_addr) { + u32 top = drect->top; + + if (dpix->pixelformat == V4L2_PIX_FMT_NV12 || + dpix->pixelformat == V4L2_PIX_FMT_NV12M) { + top >>= 1; + depth = 8; + } + wb_info.p_uv_addr += top * stride + (drect->left * depth / 8); + } + + wb_info.width = drect->width; + wb_info.height = drect->height; + wb_info.fourcc = omap_wb_fourcc_v4l2_to_drm(dpix->pixelformat); + wb_info.pre_mult_alpha = 1; + + wb_info.rotation = DRM_MODE_ROTATE_0; + wb_info.rotation_type = OMAP_DSS_ROT_NONE; + + log_dbg(dev, "DST: ctx %pa buf_index %d %dx%d, sw %d\n", + &ctx, d_vb->index, + wb_info.width, wb_info.height, wb_info.buf_width); + + ok = wbm2m_convert(dev, omap_plane_id_wb(dev->plane), &src_info, + &wb_info); + if (!ok) { + log_err(dev, + "Conversion setup failed, check source and destination parameters\n" + ); + log_err(dev, "\tSRC: %dx%d, fmt: %4.4s sw %d\n", + src_info.width, src_info.height, + (char *)&spix->pixelformat, + src_info.screen_width); + log_err(dev, "\tDST: %dx%d, fmt: %4.4s sw %d\n", + wb_info.width, wb_info.height, + (char *)&dpix->pixelformat, + wb_info.buf_width); + return; + } +} + +void wbm2m_irq(struct wbm2m_dev *wbm2m, u32 irqstatus) +{ + struct wbm2m_ctx *ctx; + struct vb2_v4l2_buffer *s_vb, *d_vb; + unsigned long flags; + + if (irqstatus & DISPC_IRQ_WBBUFFEROVERFLOW) + log_err(wbm2m, "WB: UNDERFLOW\n"); + + if (irqstatus & DISPC_IRQ_WBUNCOMPLETEERROR) + log_err(wbm2m, "WB: DISPC_IRQ_WBUNCOMPLETEERROR\n"); + + /* If DISPC_IRQ_FRAMEDONEWB is not set then we are done */ + if (!(irqstatus & DISPC_IRQ_FRAMEDONEWB)) + goto handled; + + log_dbg(wbm2m, "WB: FRAMEDONE\n"); + + ctx = v4l2_m2m_get_curr_priv(wbm2m->m2m_dev); + if (!ctx) { + log_err(wbm2m, "instance released before end of transaction\n"); + goto handled; + } + + log_dbg(ctx->dev, "ctx %pa\n", &ctx); + + s_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + d_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!s_vb || !d_vb) { + log_err(wbm2m, "source or dest vb pointer is NULL!!"); + goto handled; + } + + d_vb->flags = s_vb->flags; + + d_vb->vb2_buf.timestamp = s_vb->vb2_buf.timestamp; + if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE) + d_vb->timecode = s_vb->timecode; + + d_vb->sequence = ctx->sequence; + s_vb->sequence = ctx->sequence; + log_dbg(wbm2m, "ctx %pa sequence %d\n", + &ctx, ctx->sequence); + + d_vb->field = V4L2_FIELD_NONE; + ctx->sequence++; + + spin_lock_irqsave(&wbm2m->lock, flags); + + v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE); + + spin_unlock_irqrestore(&wbm2m->lock, flags); + + v4l2_m2m_job_finish(wbm2m->m2m_dev, ctx->fh.m2m_ctx); +handled: + return; +} + +/* + * video ioctls + */ +static int wbm2m_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct wbm2m_ctx *ctx = file->private_data; + + strscpy(cap->driver, WBM2M_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, WBM2M_MODULE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + ctx->dev->v4l2_dev.name); + return 0; +} + +static int wbm2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= num_wb_formats) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f->pixelformat = wb_formats[f->index].fourcc; + return 0; +} + +static int wbm2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct wbm2m_ctx *ctx = file->private_data; + struct vb2_queue *vq; + struct wb_q_data *q_data; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + *f = q_data->format; + + if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + struct wb_q_data *s_q_data; + + /* get colorspace from the source queue */ + s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + f->fmt.pix_mp.colorspace = + s_q_data->format.fmt.pix_mp.colorspace; + } + + log_dbg(ctx->dev, "ctx %pa type %d, %dx%d, fmt: %4.4s bpl_y %d", + &ctx, f->type, pix->width, pix->height, + (char *)&pix->pixelformat, + pix->plane_fmt[LUMA_PLANE].bytesperline); + if (pix->num_planes == 2) + log_dbg(ctx->dev, " bpl_uv %d\n", + pix->plane_fmt[CHROMA_PLANE].bytesperline); + + return 0; +} + +static int wbm2m_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct wbm2m_ctx *ctx = file->private_data; + struct wb_fmt *fmt = find_format(f); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt; + unsigned int w_align; + int i, depth, depth_bytes; + + if (!fmt) { + log_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n", + pix->pixelformat); + fmt = &wb_formats[1]; + } + + /* we only allow V4L2_FIELD_NONE */ + if (pix->field != V4L2_FIELD_NONE) + pix->field = V4L2_FIELD_NONE; + + depth = fmt->depth[LUMA_PLANE]; + + /* + * The line stride needs to be even is even. + * Special case is with YUV422 interleaved format an even number + * of pixels is required also. + */ + depth_bytes = depth >> 3; + + w_align = 0; + if ((depth_bytes == 3) || (depth_bytes == 1)) + w_align = 1; + else if ((depth_bytes == 2) && + (fmt->fourcc == V4L2_PIX_FMT_YUYV || + fmt->fourcc == V4L2_PIX_FMT_UYVY)) + w_align = 1; + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, + &pix->height, MIN_H, MAX_H, H_ALIGN, + S_ALIGN); + pix->num_planes = fmt->coplanar ? 2 : 1; + pix->pixelformat = fmt->fourcc; + + /* Probably need something better here */ + if (!pix->colorspace) { + if (fmt->fourcc == V4L2_PIX_FMT_RGB24 || + fmt->fourcc == V4L2_PIX_FMT_BGR24 || + fmt->fourcc == V4L2_PIX_FMT_RGB32 || + fmt->fourcc == V4L2_PIX_FMT_BGR32) { + pix->colorspace = V4L2_COLORSPACE_SRGB; + } else { + if (pix->height > 1280) /* HD */ + pix->colorspace = V4L2_COLORSPACE_REC709; + else /* SD */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + } + } + + memset(pix->reserved, 0, sizeof(pix->reserved)); + for (i = 0; i < pix->num_planes; i++) { + plane_fmt = &pix->plane_fmt[i]; + depth = fmt->depth[i]; + + if (i == LUMA_PLANE) + plane_fmt->bytesperline = pix->width * depth / 8; + else + plane_fmt->bytesperline = pix->width; + + plane_fmt->sizeimage = (pix->height * pix->width * + depth) / 8; + + if (fmt->fourcc == V4L2_PIX_FMT_NV12) { + /* + * Since we are using a single plane buffer + * we need to adjust the reported sizeimage + * to include the colocated UV part. + */ + plane_fmt->sizeimage += (pix->height / 2 * + plane_fmt->bytesperline); + } + + memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); + } + + return 0; +} + +static int __wbm2m_s_fmt(struct wbm2m_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct wb_q_data *q_data; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + log_err(ctx->dev, "queue busy\n"); + return -EBUSY; + } + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + q_data->fmt = find_format(f); + q_data->format = *f; + + q_data->c_rect.left = 0; + q_data->c_rect.top = 0; + q_data->c_rect.width = pix->width; + q_data->c_rect.height = pix->height; + + log_dbg(ctx->dev, "ctx %pa type %d, %dx%d, fmt: %4.4s bpl_y %d", + &ctx, f->type, pix->width, pix->height, + (char *)&pix->pixelformat, + pix->plane_fmt[LUMA_PLANE].bytesperline); + if (pix->num_planes == 2) + log_dbg(ctx->dev, " bpl_uv %d\n", + pix->plane_fmt[CHROMA_PLANE].bytesperline); + + return 0; +} + +static int wbm2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + int ret; + struct wbm2m_ctx *ctx = file->private_data; + + ret = wbm2m_try_fmt(file, priv, f); + if (ret) + return ret; + + ret = __wbm2m_s_fmt(ctx, f); + if (ret) + return ret; + + ctx->sequence = 0; + + return 0; +} + +static int __wbm2m_try_selection(struct wbm2m_ctx *ctx, + struct v4l2_selection *s) +{ + struct wb_q_data *q_data; + struct v4l2_pix_format_mplane *pix; + unsigned int w_align; + int depth_bytes; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + /* + * COMPOSE target is only valid for capture buffer type, return + * error for output buffer type + */ + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP: + /* + * CROP target is only valid for output buffer type, return + * error for capture buffer type + */ + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + /* + * bound and default crop/compose targets are invalid targets to + * try/set + */ + default: + return -EINVAL; + } + + if (s->r.top < 0 || s->r.left < 0) { + log_err(ctx->dev, "negative values for top and left\n"); + s->r.top = 0; + s->r.left = 0; + } + + depth_bytes = q_data->fmt->depth[LUMA_PLANE] >> 3; + + w_align = 0; + if ((depth_bytes == 3) || (depth_bytes == 1)) + w_align = 1; + else if ((depth_bytes == 2) && + (pix->pixelformat == V4L2_PIX_FMT_YUYV || + pix->pixelformat == V4L2_PIX_FMT_UYVY)) + w_align = 1; + + v4l_bound_align_image(&s->r.width, MIN_W, pix->width, w_align, + &s->r.height, MIN_H, pix->height, + H_ALIGN, S_ALIGN); + + /* adjust left/top if cropping rectangle is out of bounds */ + if (s->r.left + s->r.width > pix->width) + s->r.left = pix->width - s->r.width; + if (s->r.top + s->r.height > pix->height) + s->r.top = pix->height - s->r.height; + + return 0; +} + +static int wbm2m_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct wbm2m_ctx *ctx = file->private_data; + struct wb_q_data *q_data; + struct v4l2_pix_format_mplane *pix; + bool use_c_rect = false; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + use_c_rect = true; + break; + case V4L2_SEL_TGT_CROP: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + use_c_rect = true; + break; + default: + return -EINVAL; + } + + if (use_c_rect) { + /* + * for CROP/COMPOSE target type, return c_rect params from the + * respective buffer type + */ + s->r = q_data->c_rect; + } else { + /* + * for DEFAULT/BOUNDS target type, return width and height from + * S_FMT of the respective buffer type + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = pix->width; + s->r.height = pix->height; + } + + return 0; +} + +static int wbm2m_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct wbm2m_ctx *ctx = file->private_data; + struct wb_q_data *q_data; + struct v4l2_selection sel = *s; + int ret; + + ret = __wbm2m_try_selection(ctx, &sel); + if (ret) + return ret; + + q_data = get_q_data(ctx, sel.type); + if (!q_data) + return -EINVAL; + + if ((q_data->c_rect.left == sel.r.left) && + (q_data->c_rect.top == sel.r.top) && + (q_data->c_rect.width == sel.r.width) && + (q_data->c_rect.height == sel.r.height)) { + log_dbg(ctx->dev, + "type: %d, requested crop/compose values are already set\n", + sel.type); + return 0; + } + + q_data->c_rect = sel.r; + + ctx->sequence = 0; + + return 0; +} + +static const struct v4l2_ioctl_ops wbm2m_ioctl_ops = { + .vidioc_querycap = wbm2m_querycap, + + .vidioc_enum_fmt_vid_cap = wbm2m_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = wbm2m_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = wbm2m_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = wbm2m_s_fmt, + + .vidioc_enum_fmt_vid_out = wbm2m_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = wbm2m_g_fmt, + .vidioc_try_fmt_vid_out_mplane = wbm2m_try_fmt, + .vidioc_s_fmt_vid_out_mplane = wbm2m_s_fmt, + + .vidioc_g_selection = wbm2m_g_selection, + .vidioc_s_selection = wbm2m_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Queue operations + */ +static int wbm2m_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + int i; + struct wbm2m_ctx *ctx = vb2_get_drv_priv(vq); + struct wb_q_data *q_data; + + q_data = get_q_data(ctx, vq->type); + if (!q_data) + return -EINVAL; + + *nplanes = q_data->format.fmt.pix_mp.num_planes; + + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->format.fmt.pix_mp.plane_fmt[i].sizeimage; + + log_dbg(ctx->dev, "get %d buffer(s) of size %d\n", *nbuffers, + sizes[LUMA_PLANE]); + if (*nplanes == 2) + log_dbg(ctx->dev, " and %d\n", sizes[CHROMA_PLANE]); + + return 0; +} + +static int wbm2m_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct wbm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct wb_q_data *q_data; + struct v4l2_pix_format_mplane *mp; + int i, num_planes; + + log_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (!q_data) + return -EINVAL; + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf->field = V4L2_FIELD_NONE; + + num_planes = q_data->format.fmt.pix_mp.num_planes; + + for (i = 0; i < num_planes; i++) { + mp = &q_data->format.fmt.pix_mp; + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (vb2_get_plane_payload(vb, i) < + mp->plane_fmt[i].sizeimage) { + log_dbg(ctx->dev, + "the payload is too small for plane plane (%lu < %lu)\n", + vb2_get_plane_payload(vb, i), + (long)mp->plane_fmt[i].sizeimage); + return -EINVAL; + } + } else { + if (vb2_plane_size(vb, i) < + mp->plane_fmt[i].sizeimage) { + log_dbg(ctx->dev, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, i), + (long)mp->plane_fmt[i].sizeimage); + return -EINVAL; + } + vb2_set_plane_payload(vb, i, + mp->plane_fmt[i].sizeimage); + } + } + + if (num_planes == 2) { + if (vb->planes[0].m.fd == + vb->planes[1].m.fd) { + /* + * So it appears we are in a single memory buffer + * with 2 plane case. Then we need to also set the + * data_offset properly + */ + vb->planes[1].data_offset = + vb2_get_plane_payload(vb, 0); + } + } + return 0; +} + +static void wbm2m_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct wbm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + log_dbg(ctx->dev, "queueing buffer: %s index %d\n", + V4L2_TYPE_IS_OUTPUT(vb->type) ? "OUTPUT" : "CAPTURE", + vb->index); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int wbm2m_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct wbm2m_ctx *ctx = vb2_get_drv_priv(q); + struct omap_drm_private *priv = ctx->dev->dev->drm_dev->dev_private; + + log_dbg(ctx->dev, "ctx %pa queue: %s\n", &ctx, + V4L2_TYPE_IS_OUTPUT(q->type) ? "OUTPUT" : "CAPTURE"); + + ctx->sequence = 0; + + priv->dispc_ops->runtime_get(priv->dispc); + atomic_inc(&ctx->dev->dev->irq_enabled); + + return 0; +} + +static void wbm2m_stop_streaming(struct vb2_queue *q) +{ + struct wbm2m_ctx *ctx = vb2_get_drv_priv(q); + struct omap_drm_private *priv = ctx->dev->dev->drm_dev->dev_private; + struct vb2_v4l2_buffer *vb; + unsigned long flags; + + log_dbg(ctx->dev, "ctx %pa queue: %s\n", &ctx, + V4L2_TYPE_IS_OUTPUT(q->type) ? "OUTPUT" : "CAPTURE"); + + atomic_dec(&ctx->dev->dev->irq_enabled); + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vb) + break; + log_dbg(ctx->dev, "returning from queue: buffer index %d\n", + vb->vb2_buf.index); + spin_lock_irqsave(&ctx->dev->lock, flags); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&ctx->dev->lock, flags); + } + + /* + * Cleanup the in-transit vb2 buffers that have been + * removed from their respective queue already but for + * which procecessing has not been completed yet. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + /* + * DRA7xx errata i829 (Reusing Pipe Connected To Writeback + * Pipeline On The Fly To An Active Panel) + */ + priv->dispc_ops->ovl_enable(priv->dispc, + omap_plane_id_wb(ctx->dev->plane), + false); + priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, true); + priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, false); + } + + priv->dispc_ops->runtime_put(priv->dispc); +} + +static struct vb2_ops wbm2m_qops = { + .queue_setup = wbm2m_queue_setup, + .buf_prepare = wbm2m_buf_prepare, + .buf_queue = wbm2m_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = wbm2m_start_streaming, + .stop_streaming = wbm2m_stop_streaming, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct wbm2m_ctx *ctx = priv; + struct wbm2m_dev *dev = ctx->dev; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &wbm2m_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &dev->dev->lock; + src_vq->min_buffers_needed = 1; + src_vq->dev = dev->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &wbm2m_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &dev->dev->lock; + dst_vq->min_buffers_needed = 1; + dst_vq->dev = dev->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +/* + * File operations + */ +static int wbm2m_open(struct file *file) +{ + struct wbm2m_dev *dev = video_drvdata(file); + struct wb_q_data *s_q_data; + struct wbm2m_ctx *ctx; + struct v4l2_pix_format_mplane *pix; + int ret; + + log_dbg(dev, "enter\n"); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = dev; + + if (mutex_lock_interruptible(&dev->dev->lock)) { + ret = -ERESTARTSYS; + goto free_ctx; + } + + if ((dev->dev->mode != OMAP_WB_NOT_CONFIGURED) && + (dev->dev->mode != OMAP_WB_MEM2MEM_OVL)) { + /* WB is already open for other modes */ + ret = -EBUSY; + goto unlock; + } + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = ctx; + + s_q_data = &ctx->q_data[Q_DATA_SRC]; + s_q_data->fmt = &wb_formats[1]; + pix = &s_q_data->format.fmt.pix_mp; + pix->pixelformat = s_q_data->fmt->fourcc; + s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + pix->width = 1920; + pix->height = 1080; + pix->plane_fmt[LUMA_PLANE].bytesperline = (pix->width * + s_q_data->fmt->depth[LUMA_PLANE]) >> 3; + pix->plane_fmt[LUMA_PLANE].sizeimage = + pix->plane_fmt[LUMA_PLANE].bytesperline * + pix->height; + pix->num_planes = s_q_data->fmt->coplanar ? 2 : 1; + pix->colorspace = V4L2_COLORSPACE_REC709; + pix->field = V4L2_FIELD_NONE; + s_q_data->c_rect.left = 0; + s_q_data->c_rect.top = 0; + s_q_data->c_rect.width = pix->width; + s_q_data->c_rect.height = pix->height; + + ctx->q_data[Q_DATA_DST] = *s_q_data; + ctx->q_data[Q_DATA_DST].format.type = + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + ctx->sequence = 0; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto exit_fh; + } + + v4l2_fh_add(&ctx->fh); + + if (v4l2_fh_is_singular_file(file)) { + log_dbg(dev, "first instance created\n"); + + drm_modeset_lock_all(dev->dev->drm_dev); + dev->plane = omap_plane_reserve_wb(dev->dev->drm_dev); + drm_modeset_unlock_all(dev->dev->drm_dev); + + if (!dev->plane) { + log_dbg(dev, "Could not reserve plane!\n"); + ret = -EBUSY; + goto free_fh; + } + + dev->dev->mode = OMAP_WB_MEM2MEM_OVL; + } + + log_dbg(dev, "created instance %pa, m2m_ctx: %pa\n", + &ctx, &ctx->fh.m2m_ctx); + + mutex_unlock(&dev->dev->lock); + + return 0; + +free_fh: + v4l2_fh_del(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +exit_fh: + v4l2_fh_exit(&ctx->fh); +unlock: + mutex_unlock(&dev->dev->lock); +free_ctx: + kfree(ctx); + return ret; +} + +static int wbm2m_release(struct file *file) +{ + struct wbm2m_dev *dev = video_drvdata(file); + struct wbm2m_ctx *ctx = file->private_data; + bool fh_singular; + + log_dbg(dev, "releasing instance %pa\n", &ctx); + + mutex_lock(&dev->dev->lock); + + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + kfree(ctx); + + if (fh_singular) { + log_dbg(dev, "last instance released\n"); + + drm_modeset_lock_all(dev->dev->drm_dev); + omap_plane_release_wb(dev->plane); + drm_modeset_unlock_all(dev->dev->drm_dev); + dev->dev->mode = OMAP_WB_NOT_CONFIGURED; + } + + mutex_unlock(&dev->dev->lock); + + return 0; +} + +static const struct v4l2_file_operations wbm2m_fops = { + .owner = THIS_MODULE, + .open = wbm2m_open, + .release = wbm2m_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static struct video_device wbm2m_videodev = { + .name = WBM2M_MODULE_NAME, + .fops = &wbm2m_fops, + .ioctl_ops = &wbm2m_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_ready = job_ready, + .job_abort = job_abort, +}; + +int wbm2m_init(struct wb_dev *dev) +{ + struct wbm2m_dev *wbm2m; + struct video_device *vfd; + int ret; + + if (!dev) + return -ENOMEM; + + /* Allocate a new instance */ + wbm2m = devm_kzalloc(dev->drm_dev->dev, sizeof(*wbm2m), GFP_KERNEL); + if (!wbm2m) + return -ENOMEM; + + dev->m2m = wbm2m; + wbm2m->dev = dev; + + spin_lock_init(&wbm2m->lock); + + snprintf(wbm2m->v4l2_dev.name, sizeof(wbm2m->v4l2_dev.name), + "%s", WBM2M_MODULE_NAME); + ret = v4l2_device_register(dev->drm_dev->dev, &wbm2m->v4l2_dev); + if (ret) + return ret; + + wbm2m->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(wbm2m->m2m_dev)) { + log_err(wbm2m, "Failed to init mem2mem device\n"); + ret = PTR_ERR(wbm2m->m2m_dev); + goto v4l2_dev_unreg; + } + + vfd = &wbm2m->vfd; + *vfd = wbm2m_videodev; + vfd->lock = &dev->lock; + vfd->v4l2_dev = &wbm2m->v4l2_dev; + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 10); + if (ret) { + log_err(wbm2m, "Failed to register video device\n"); + goto rel_m2m; + } + + video_set_drvdata(vfd, wbm2m); + snprintf(vfd->name, sizeof(vfd->name), "%s", wbm2m_videodev.name); + log_dbg(wbm2m, "Device registered as %s\n", + video_device_node_name(vfd)); + + return 0; + +rel_m2m: + v4l2_m2m_release(wbm2m->m2m_dev); +v4l2_dev_unreg: + v4l2_device_unregister(&wbm2m->v4l2_dev); + + return ret; +} + +void wbm2m_cleanup(struct wb_dev *dev) +{ + log_dbg(dev->m2m, "Cleanup WB M2M\n"); + + v4l2_m2m_release(dev->m2m->m2m_dev); + video_unregister_device(&dev->m2m->vfd); + v4l2_device_unregister(&dev->m2m->v4l2_dev); +} diff -Naur --no-dereference a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c --- a/drivers/gpu/drm/panel/panel-simple.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/panel/panel-simple.c 2022-01-06 12:45:53.810318090 -0500 @@ -3231,6 +3231,7 @@ static const struct panel_desc rocktech_rk101ii01d_ct = { .modes = &rocktech_rk101ii01d_ct_mode, + .bpc = 8, .num_modes = 1, .size = { .width = 217, diff -Naur --no-dereference a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c --- a/drivers/gpu/drm/tidss/tidss_plane.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/gpu/drm/tidss/tidss_plane.c 2022-01-06 12:45:53.810318090 -0500 @@ -10,6 +10,7 @@ #include #include #include +#include #include "tidss_crtc.h" #include "tidss_dispc.h" @@ -150,6 +151,7 @@ } static const struct drm_plane_helper_funcs tidss_plane_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, .atomic_check = tidss_plane_atomic_check, .atomic_update = tidss_plane_atomic_update, .atomic_disable = tidss_plane_atomic_disable, diff -Naur --no-dereference a/drivers/greybus/es2.c b/drivers/greybus/es2.c --- a/drivers/greybus/es2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/greybus/es2.c 2022-01-06 12:45:53.810318090 -0500 @@ -567,12 +567,9 @@ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, req, sizeof(*req), ES2_USB_CTRL_TIMEOUT); - if (ret != sizeof(*req)) { + if (ret < 0) { dev_err(&udev->dev, "failed to set cport flags for port %d\n", cport_id); - if (ret >= 0) - ret = -EIO; - goto out; } @@ -961,12 +958,10 @@ 0, 0, rpc->req, le16_to_cpu(rpc->req->size), ES2_USB_CTRL_TIMEOUT); - if (retval != le16_to_cpu(rpc->req->size)) { + if (retval < 0) { dev_err(&udev->dev, "failed to send ARPC request %d: %d\n", rpc->req->type, retval); - if (retval > 0) - retval = -EIO; return retval; } diff -Naur --no-dereference a/drivers/greybus/greybus_trace.h b/drivers/greybus/greybus_trace.h --- a/drivers/greybus/greybus_trace.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/greybus/greybus_trace.h 2022-01-06 12:45:53.810318090 -0500 @@ -40,7 +40,7 @@ __entry->result = message->header->result; ), - TP_printk("size=%hu operation_id=0x%04x type=0x%02x result=0x%02x", + TP_printk("size=%u operation_id=0x%04x type=0x%02x result=0x%02x", __entry->size, __entry->operation_id, __entry->type, __entry->result) ); @@ -317,7 +317,7 @@ __entry->mode_switch = intf->mode_switch; ), - TP_printk("intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d M=%d", + TP_printk("intf_id=%u device_id=%u module_id=%u D=%d J=%d A=%d E=%d M=%d", __entry->id, __entry->device_id, __entry->module_id, __entry->disconnected, __entry->ejected, __entry->active, __entry->enabled, __entry->mode_switch) @@ -391,7 +391,7 @@ __entry->disconnected = module->disconnected; ), - TP_printk("hd_bus_id=%d module_id=%hhu num_interfaces=%zu disconnected=%d", + TP_printk("hd_bus_id=%d module_id=%u num_interfaces=%zu disconnected=%d", __entry->hd_bus_id, __entry->module_id, __entry->num_interfaces, __entry->disconnected) ); diff -Naur --no-dereference a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c --- a/drivers/hwmon/sht3x.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/hwmon/sht3x.c 2022-01-06 12:45:53.810318090 -0500 @@ -738,9 +738,16 @@ {"sts3x", sts3x}, {} }; - MODULE_DEVICE_TABLE(i2c, sht3x_ids); +static const struct of_device_id shtc1_of_match[] = { + { .compatible = "sensirion,shtc1" }, + { .compatible = "sensirion,shtw1" }, + { .compatible = "sensirion,shtc3" }, + { } +}; +MODULE_DEVICE_TABLE(of, shtc1_of_match); + static struct i2c_driver sht3x_i2c_driver = { .driver.name = "sht3x", .probe_new = sht3x_probe, diff -Naur --no-dereference a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c --- a/drivers/hwspinlock/omap_hwspinlock.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/hwspinlock/omap_hwspinlock.c 2022-01-06 12:45:53.810318090 -0500 @@ -2,11 +2,12 @@ /* * OMAP hardware spinlock driver * - * Copyright (C) 2010-2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2021 Texas Instruments Incorporated - https://www.ti.com * * Contact: Simon Que * Hari Kanigeri * Ohad Ben-Cohen + * Suman Anna */ #include @@ -164,6 +165,7 @@ static const struct of_device_id omap_hwspinlock_of_match[] = { { .compatible = "ti,omap4-hwspinlock", }, + { .compatible = "ti,am64-hwspinlock", }, { .compatible = "ti,am654-hwspinlock", }, { /* end */ }, }; diff -Naur --no-dereference a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c --- a/drivers/input/misc/tps65218-pwrbutton.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/input/misc/tps65218-pwrbutton.c 2022-01-06 12:45:53.810318090 -0500 @@ -36,7 +36,7 @@ static const struct tps6521x_data tps65217_data = { .reg_status = TPS65217_REG_STATUS, .pb_mask = TPS65217_STATUS_PB, - .name = "tps65217_pwrbutton", + .name = "tps65217_pwr_but", }; static const struct tps6521x_data tps65218_data = { diff -Naur --no-dereference a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c --- a/drivers/input/touchscreen/ar1021_i2c.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/input/touchscreen/ar1021_i2c.c 2022-01-06 12:45:53.810318090 -0500 @@ -17,6 +17,7 @@ #define AR1021_MAX_X 4095 #define AR1021_MAX_Y 4095 +#define AR1021_MAX_PRESSURE 255 #define AR1021_CMD 0x55 @@ -26,8 +27,29 @@ struct i2c_client *client; struct input_dev *input; u8 data[AR1021_TOUCH_PKG_SIZE]; + bool invert_x; + bool invert_y; + bool swap_xy; }; +static bool ar1021_get_prop_u32(struct device *dev, + const char *property, + unsigned int default_value, + unsigned int *value) +{ + u32 val; + int error; + + error = device_property_read_u32(dev, property, &val); + if (error) { + *value = default_value; + return false; + } + + *value = val; + return true; +} + static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id) { struct ar1021_i2c *ar1021 = dev_id; @@ -49,9 +71,22 @@ x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f); y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f); - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); + if (ar1021->invert_x) + x = AR1021_MAX_X - x; + + if (ar1021->invert_y) + y = AR1021_MAX_Y - y; + + if (ar1021->swap_xy) { + input_report_abs(input, ABS_X, y); + input_report_abs(input, ABS_Y, x); + } else { + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + input_report_key(input, BTN_TOUCH, button); + input_report_abs(input, ABS_PRESSURE, AR1021_MAX_PRESSURE); input_sync(input); out: @@ -93,6 +128,8 @@ struct ar1021_i2c *ar1021; struct input_dev *input; int error; + unsigned int offset_x, offset_y; + bool data_present; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "i2c_check_functionality error\n"); @@ -116,10 +153,44 @@ input->open = ar1021_i2c_open; input->close = ar1021_i2c_close; + ar1021->invert_x = device_property_read_bool(&client->dev, "touchscreen-inverted-x"); + ar1021->invert_y = device_property_read_bool(&client->dev, "touchscreen-inverted-y"); + ar1021->swap_xy = device_property_read_bool(&client->dev, "touchscreen-swapped-x-y"); + + data_present = ar1021_get_prop_u32(&client->dev, + "touchscreen-offset-x", + 0, + &offset_x); + + if (data_present) + dev_info(&client->dev, "touchscreen-offset-x: %d\n", offset_x); + + data_present = ar1021_get_prop_u32(&client->dev, + "touchscreen-offset-y", + 0, + &offset_y); + + if (data_present) + dev_info(&client->dev, "touchscreen-offset-y: %d\n", offset_y); + __set_bit(INPUT_PROP_DIRECT, input->propbit); - input_set_capability(input, EV_KEY, BTN_TOUCH); - input_set_abs_params(input, ABS_X, 0, AR1021_MAX_X, 0, 0); - input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_Y, 0, 0); + //input_set_capability(input, EV_KEY, BTN_TOUCH); + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + if(ar1021->swap_xy) + { + input_set_abs_params(input, ABS_X, 0, AR1021_MAX_Y, 0, 0); + input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_X, 0, 0); + } + else + { + input_set_abs_params(input, ABS_X, offset_x, AR1021_MAX_X-offset_x, 0, 0); + input_set_abs_params(input, ABS_Y, offset_y, AR1021_MAX_Y-offset_y, 0, 0); + } + + input_set_abs_params(input, ABS_PRESSURE, 0, AR1021_MAX_PRESSURE, 0, 0); input_set_drvdata(input, ar1021); diff -Naur --no-dereference a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c --- a/drivers/input/touchscreen/goodix.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/input/touchscreen/goodix.c 2022-01-06 12:45:53.810318090 -0500 @@ -675,31 +675,30 @@ { int error; - /* begin select I2C slave addr */ - error = gpiod_direction_output(ts->gpiod_rst, 0); - if (error) - return error; - msleep(20); /* T2: > 10ms */ /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ +#ifdef ACPI_GPIO_SUPPORT error = goodix_irq_direction_output(ts, ts->client->addr == 0x14); if (error) return error; +#else + if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_ACPI_GPIO) + /* + * The IRQ pin triggers on a falling edge, so its gets marked + * as active-low, manually invert the value. + */ + gpiod_set_value_cansleep(ts->gpiod_int, ts->client->addr != 0x14); + else + gpiod_set_value_cansleep(ts->gpiod_int, ts->client->addr == 0x14); +#endif usleep_range(100, 2000); /* T3: > 100us */ - error = gpiod_direction_output(ts->gpiod_rst, 1); - if (error) - return error; + gpiod_set_value_cansleep(ts->gpiod_rst, 1); usleep_range(6000, 10000); /* T4: > 5ms */ - /* end select I2C slave addr */ - error = gpiod_direction_input(ts->gpiod_rst); - if (error) - return error; - error = goodix_int_sync(ts); if (error) return error; @@ -861,7 +860,8 @@ retry_get_irq_gpio: /* Get the interrupt GPIO pin number */ - gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, + GPIOD_OUT_LOW); if (IS_ERR(gpiod)) { error = PTR_ERR(gpiod); if (error != -EPROBE_DEFER) @@ -878,7 +878,8 @@ ts->gpiod_int = gpiod; /* Get the reset line GPIO pin number */ - gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, + GPIOD_OUT_LOW); if (IS_ERR(gpiod)) { error = PTR_ERR(gpiod); if (error != -EPROBE_DEFER) diff -Naur --no-dereference a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c --- a/drivers/input/touchscreen/ti_am335x_tsc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/input/touchscreen/ti_am335x_tsc.c 2022-01-06 12:45:53.810318090 -0500 @@ -34,6 +34,7 @@ #define ADCFSM_STEPID 0x10 #define SEQ_SETTLE 275 #define MAX_12BIT ((1 << 12) - 1) +#define PRESSURE_MAX 1000 #define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN) @@ -234,6 +235,7 @@ for (i = 0; i < creads; i++) { xvals[i] = titsc_readl(ts_dev, REG_FIFO0); xvals[i] &= 0xfff; + pr_debug("i %d xval %d yval %d z1 %d z2 %d\n", i, xvals[i], yvals[i], *z1, *z2); } /* @@ -312,13 +314,13 @@ * Resistance(touch) = x plate resistance * * x postion/4096 * ((z2 / z1) - 1) */ - z = z1 - z2; + z = z2 - z1; z *= x; z *= ts_dev->x_plate_resistance; - z /= z2; + z /= z1; z = (z + 2047) >> 12; - - if (z <= MAX_12BIT) { + pr_debug("x %d y %d z1 %d z2 %d z %d\n", x, y, z1, z2, z); + if (z <= PRESSURE_MAX) { input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, z); @@ -459,6 +461,7 @@ input_dev->name = "ti-tsc"; input_dev->dev.parent = &pdev->dev; + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); diff -Naur --no-dereference a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c --- a/drivers/irqchip/irq-pruss-intc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/irqchip/irq-pruss-intc.c 2022-01-06 12:45:53.810318090 -0500 @@ -70,6 +70,8 @@ #define MAX_PRU_SYS_EVENTS 160 #define MAX_PRU_CHANNELS 20 +#define MAX_PRU_INT_EVENTS 64 + /** * struct pruss_intc_map_record - keeps track of actual mapping state * @value: The currently mapped value (channel or host) @@ -85,10 +87,13 @@ * @num_system_events: number of input system events handled by the PRUSS INTC * @num_host_events: number of host events (which is equal to number of * channels) supported by the PRUSS INTC + * @quirky_events: bitmask of events that need quirky IRQ handling (limited to + * (internal sources only for now, so 64 bits suffice) */ struct pruss_intc_match_data { u8 num_system_events; u8 num_host_events; + u64 quirky_events; }; /** @@ -101,6 +106,7 @@ * @soc_config: cached PRUSS INTC IP configuration data * @dev: PRUSS INTC device pointer * @lock: mutex to serialize interrupts mapping + * @irqs_reserved: bit-mask of reserved host interrupts */ struct pruss_intc { struct pruss_intc_map_record event_channel[MAX_PRU_SYS_EVENTS]; @@ -111,6 +117,7 @@ const struct pruss_intc_match_data *soc_config; struct device *dev; struct mutex lock; /* PRUSS INTC lock */ + u8 irqs_reserved; }; /** @@ -178,6 +185,7 @@ static void pruss_intc_map(struct pruss_intc *intc, unsigned long hwirq) { struct device *dev = intc->dev; + bool enable_hwirq = false; u8 ch, host, reg_idx; u32 val; @@ -187,6 +195,9 @@ ch = intc->event_channel[hwirq].value; host = intc->channel_host[ch].value; + enable_hwirq = (host < FIRST_PRU_HOST_INT || + host >= FIRST_PRU_HOST_INT + MAX_NUM_HOST_IRQS || + intc->irqs_reserved & BIT(host - FIRST_PRU_HOST_INT)); pruss_intc_update_cmr(intc, hwirq, ch); @@ -194,8 +205,10 @@ val = BIT(hwirq % 32); /* clear and enable system event */ - pruss_intc_write_reg(intc, PRU_INTC_ESR(reg_idx), val); pruss_intc_write_reg(intc, PRU_INTC_SECR(reg_idx), val); + /* unmask only events going to various PRU and other cores by default */ + if (enable_hwirq) + pruss_intc_write_reg(intc, PRU_INTC_ESR(reg_idx), val); if (++intc->channel_host[ch].ref_count == 1) { pruss_intc_update_hmr(intc, ch, host); @@ -204,7 +217,8 @@ pruss_intc_write_reg(intc, PRU_INTC_HIEISR, host); } - dev_dbg(dev, "mapped system_event = %lu channel = %d host = %d", + dev_dbg(dev, "mapped%s system_event = %lu channel = %d host = %d", + enable_hwirq ? " and enabled" : "", hwirq, ch, host); mutex_unlock(&intc->lock); @@ -268,11 +282,14 @@ /* * configure polarity (SIPR register) to active high and - * type (SITR register) to level interrupt for all system events + * type (SITR register) to level interrupt for all system events, + * and disable and clear all the system events */ for (i = 0; i < num_event_type_regs; i++) { pruss_intc_write_reg(intc, PRU_INTC_SIPR(i), 0xffffffff); pruss_intc_write_reg(intc, PRU_INTC_SITR(i), 0); + pruss_intc_write_reg(intc, PRU_INTC_ECR(i), 0xffffffff); + pruss_intc_write_reg(intc, PRU_INTC_SECR(i), 0xffffffff); } /* clear all interrupt channel map registers, 4 events per register */ @@ -292,6 +309,10 @@ struct pruss_intc *intc = irq_data_get_irq_chip_data(data); unsigned int hwirq = data->hwirq; + if (hwirq < MAX_PRU_INT_EVENTS && + intc->soc_config->quirky_events & BIT_ULL(hwirq)) + return; + pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq); } @@ -308,6 +329,9 @@ struct pruss_intc *intc = irq_data_get_irq_chip_data(data); unsigned int hwirq = data->hwirq; + if (hwirq < MAX_PRU_INT_EVENTS && + intc->soc_config->quirky_events & BIT_ULL(hwirq)) + pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq); pruss_intc_write_reg(intc, PRU_INTC_EISR, hwirq); } @@ -361,6 +385,14 @@ return 0; } +static int pruss_intc_irq_irq_set_type(struct irq_data *data, unsigned int type) +{ + if (type != IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + + return 0; +} + static struct irq_chip pruss_irqchip = { .name = "pruss-intc", .irq_ack = pruss_intc_irq_ack, @@ -370,6 +402,7 @@ .irq_release_resources = pruss_intc_irq_relres, .irq_get_irqchip_state = pruss_intc_irq_get_irqchip_state, .irq_set_irqchip_state = pruss_intc_irq_set_irqchip_state, + .irq_set_type = pruss_intc_irq_irq_set_type, }; static int pruss_intc_validate_mapping(struct pruss_intc *intc, int event, @@ -524,7 +557,7 @@ struct pruss_intc *intc; struct pruss_host_irq_data *host_data; int i, irq, ret; - u8 max_system_events, irqs_reserved = 0; + u8 max_system_events; data = of_device_get_match_data(dev); if (!data) @@ -545,7 +578,7 @@ return PTR_ERR(intc->base); ret = of_property_read_u8(dev->of_node, "ti,irqs-reserved", - &irqs_reserved); + &intc->irqs_reserved); /* * The irqs-reserved is used only for some SoC's therefore not having @@ -564,7 +597,7 @@ return -ENOMEM; for (i = 0; i < MAX_NUM_HOST_IRQS; i++) { - if (irqs_reserved & BIT(i)) + if (intc->irqs_reserved & BIT(i)) continue; irq = platform_get_irq_byname(pdev, irq_names[i]); @@ -626,11 +659,13 @@ static const struct pruss_intc_match_data pruss_intc_data = { .num_system_events = 64, .num_host_events = 10, + .quirky_events = BIT_ULL(7), /* IEP capture/compare event */ }; static const struct pruss_intc_match_data icssg_intc_data = { .num_system_events = 160, .num_host_events = 20, + .quirky_events = BIT_ULL(7) | BIT_ULL(56), /* IEP{0,1} capture/compare events */ }; static const struct of_device_id pruss_intc_of_match[] = { diff -Naur --no-dereference a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig --- a/drivers/irqchip/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/irqchip/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -502,8 +502,9 @@ TI System Controller, say Y here. Otherwise, say N. config TI_PRUSS_INTC - tristate "TI PRU-ICSS Interrupt Controller" - depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX || SOC_DRA7XX || ARCH_KEYSTONE || ARCH_K3 + tristate + depends on TI_PRUSS + default TI_PRUSS select IRQ_DOMAIN help This enables support for the PRU-ICSS Local Interrupt Controller diff -Naur --no-dereference a/drivers/Kconfig b/drivers/Kconfig --- a/drivers/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -166,6 +166,8 @@ source "drivers/rpmsg/Kconfig" +source "drivers/rpmsg-kdrv/Kconfig" + source "drivers/soundwire/Kconfig" source "drivers/soc/Kconfig" diff -Naur --no-dereference a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c --- a/drivers/mailbox/omap-mailbox.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mailbox/omap-mailbox.c 2022-01-06 12:45:53.810318090 -0500 @@ -3,7 +3,7 @@ * OMAP mailbox driver * * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved. - * Copyright (C) 2013-2019 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2013-2021 Texas Instruments Incorporated - https://www.ti.com * * Contact: Hiroshi DOYU * Suman Anna @@ -664,6 +664,10 @@ .data = &omap4_data, }, { + .compatible = "ti,am64-mailbox", + .data = &omap4_data, + }, + { /* end */ }, }; diff -Naur --no-dereference a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/Makefile 2022-01-06 12:45:53.810318090 -0500 @@ -56,6 +56,9 @@ obj-y += tty/ obj-y += char/ +# put mmc early as many morden devices use emm/sd card as rootfs storage +obj-y += mmc/ + # iommu/ comes before gpu as gpu are using iommu controllers obj-y += iommu/ @@ -128,7 +131,6 @@ obj-$(CONFIG_PM_OPP) += opp/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ -obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ @@ -156,6 +158,7 @@ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-$(CONFIG_RPMSG) += rpmsg/ +obj-$(CONFIG_RPMSG_KDRV) += rpmsg-kdrv/ obj-$(CONFIG_SOUNDWIRE) += soundwire/ # Virtualization drivers diff -Naur --no-dereference a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c --- a/drivers/media/i2c/adv7170.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7170.c 2022-01-06 12:45:53.810318090 -0500 @@ -250,7 +250,7 @@ } static int adv7170_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(adv7170_codes)) @@ -261,7 +261,7 @@ } static int adv7170_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -284,7 +284,7 @@ } static int adv7170_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; diff -Naur --no-dereference a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c --- a/drivers/media/i2c/adv7175.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7175.c 2022-01-06 12:45:53.810318090 -0500 @@ -288,7 +288,7 @@ } static int adv7175_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(adv7175_codes)) @@ -299,7 +299,7 @@ } static int adv7175_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -322,7 +322,7 @@ } static int adv7175_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; diff -Naur --no-dereference a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c --- a/drivers/media/i2c/adv7180.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7180.c 2022-01-06 12:45:53.810318090 -0500 @@ -633,7 +633,7 @@ } static int adv7180_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -699,13 +699,13 @@ } static int adv7180_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7180_state *state = to_state(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); } else { adv7180_mbus_fmt(sd, &format->format); format->format.field = state->field; @@ -715,7 +715,7 @@ } static int adv7180_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7180_state *state = to_state(sd); @@ -742,7 +742,7 @@ adv7180_set_power(state, true); } } else { - framefmt = v4l2_subdev_get_try_format(sd, cfg, 0); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); *framefmt = format->format; } @@ -750,14 +750,14 @@ } static int adv7180_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { - .which = cfg ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, }; - return adv7180_set_pad_format(sd, cfg, &fmt); + return adv7180_set_pad_format(sd, sd_state, &fmt); } static int adv7180_get_mbus_config(struct v4l2_subdev *sd, diff -Naur --no-dereference a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c --- a/drivers/media/i2c/adv7183.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7183.c 2022-01-06 12:45:53.810318090 -0500 @@ -409,7 +409,7 @@ } static int adv7183_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index > 0) @@ -420,7 +420,7 @@ } static int adv7183_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7183 *decoder = to_adv7183(sd); @@ -443,12 +443,12 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) decoder->fmt = *fmt; else - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; return 0; } static int adv7183_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7183 *decoder = to_adv7183(sd); diff -Naur --no-dereference a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c --- a/drivers/media/i2c/adv748x/adv748x-afe.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv748x/adv748x-afe.c 2022-01-06 12:45:53.814318107 -0500 @@ -331,7 +331,7 @@ } static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -343,7 +343,7 @@ } static int adv748x_afe_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct adv748x_afe *afe = adv748x_sd_to_afe(sd); @@ -354,7 +354,8 @@ return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad); + mbusformat = v4l2_subdev_get_try_format(sd, sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_afe_fill_format(afe, &sdformat->format); @@ -365,7 +366,7 @@ } static int adv748x_afe_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct v4l2_mbus_framefmt *mbusformat; @@ -375,9 +376,9 @@ return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return adv748x_afe_get_format(sd, cfg, sdformat); + return adv748x_afe_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad); + mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff -Naur --no-dereference a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c --- a/drivers/media/i2c/adv748x/adv748x-csi2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c 2022-01-06 12:45:53.814318107 -0500 @@ -142,26 +142,26 @@ static struct v4l2_mbus_framefmt * adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, cfg, pad); + return v4l2_subdev_get_try_format(sd, sd_state, pad); return &tx->format; } static int adv748x_csi2_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); struct adv748x_state *state = tx->state; struct v4l2_mbus_framefmt *mbusformat; - mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad, + mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, sdformat->which); if (!mbusformat) return -EINVAL; @@ -176,7 +176,7 @@ } static int adv748x_csi2_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); @@ -184,7 +184,7 @@ struct v4l2_mbus_framefmt *mbusformat; int ret = 0; - mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad, + mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, sdformat->which); if (!mbusformat) return -EINVAL; @@ -194,7 +194,7 @@ if (sdformat->pad == ADV748X_CSI2_SOURCE) { const struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = adv748x_csi2_get_pad_format(sd, cfg, + sink_fmt = adv748x_csi2_get_pad_format(sd, sd_state, ADV748X_CSI2_SINK, sdformat->which); diff -Naur --no-dereference a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c 2022-01-06 12:45:53.814318107 -0500 @@ -409,7 +409,7 @@ } static int adv748x_hdmi_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -421,7 +421,7 @@ } static int adv748x_hdmi_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); @@ -431,7 +431,8 @@ return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad); + mbusformat = v4l2_subdev_get_try_format(sd, sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_hdmi_fill_format(hdmi, &sdformat->format); @@ -442,7 +443,7 @@ } static int adv748x_hdmi_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct v4l2_mbus_framefmt *mbusformat; @@ -451,9 +452,9 @@ return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return adv748x_hdmi_get_format(sd, cfg, sdformat); + return adv748x_hdmi_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad); + mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff -Naur --no-dereference a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c --- a/drivers/media/i2c/adv7511-v4l2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7511-v4l2.c 2022-01-06 12:45:53.814318107 -0500 @@ -1227,7 +1227,7 @@ } static int adv7511_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad != 0) @@ -1258,7 +1258,7 @@ } static int adv7511_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7511_state *state = get_adv7511_state(sd); @@ -1272,7 +1272,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); format->format.code = fmt->code; format->format.colorspace = fmt->colorspace; format->format.ycbcr_enc = fmt->ycbcr_enc; @@ -1290,7 +1290,7 @@ } static int adv7511_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7511_state *state = get_adv7511_state(sd); @@ -1327,7 +1327,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); fmt->code = format->format.code; fmt->colorspace = format->format.colorspace; fmt->ycbcr_enc = format->format.ycbcr_enc; diff -Naur --no-dereference a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c --- a/drivers/media/i2c/adv7604.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7604.c 2022-01-06 12:45:53.814318107 -0500 @@ -1819,7 +1819,7 @@ } static int adv76xx_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct adv76xx_state *state = to_state(sd); @@ -1899,7 +1899,7 @@ } static int adv76xx_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv76xx_state *state = to_state(sd); @@ -1912,7 +1912,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -1922,7 +1922,7 @@ } static int adv76xx_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct adv76xx_state *state = to_state(sd); @@ -1942,7 +1942,7 @@ } static int adv76xx_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv76xx_state *state = to_state(sd); @@ -1961,7 +1961,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff -Naur --no-dereference a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c --- a/drivers/media/i2c/adv7842.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/adv7842.c 2022-01-06 12:45:53.814318107 -0500 @@ -1995,7 +1995,7 @@ } static int adv7842_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(adv7842_formats)) @@ -2071,7 +2071,7 @@ } static int adv7842_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7842_state *state = to_state(sd); @@ -2099,7 +2099,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -2109,7 +2109,7 @@ } static int adv7842_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv7842_state *state = to_state(sd); @@ -2119,7 +2119,7 @@ return -EINVAL; if (state->mode == ADV7842_MODE_SDP) - return adv7842_get_format(sd, cfg, format); + return adv7842_get_format(sd, sd_state, format); info = adv7842_format_info(state, format->format.code); if (info == NULL) @@ -2131,7 +2131,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff -Naur --no-dereference a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c --- a/drivers/media/i2c/ak881x.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ak881x.c 2022-01-06 12:45:53.814318107 -0500 @@ -91,7 +91,7 @@ #endif static int ak881x_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -111,7 +111,7 @@ } static int ak881x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index) @@ -122,7 +122,7 @@ } static int ak881x_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff -Naur --no-dereference a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c --- a/drivers/media/i2c/cx25840/cx25840-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/cx25840/cx25840-core.c 2022-01-06 12:45:53.814318107 -0500 @@ -1746,7 +1746,7 @@ /* ----------------------------------------------------------------------- */ static int cx25840_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; diff -Naur --no-dereference a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c 2022-01-06 12:45:53.814318107 -0500 @@ -882,7 +882,7 @@ */ #define MAX_FMTS 4 static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct et8ek8_reglist **list = @@ -920,7 +920,7 @@ } static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct et8ek8_reglist **list = @@ -958,7 +958,7 @@ } static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct et8ek8_reglist **list = @@ -990,12 +990,13 @@ static struct v4l2_mbus_framefmt * __et8ek8_get_pad_format(struct et8ek8_sensor *sensor, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: @@ -1004,13 +1005,14 @@ } static int et8ek8_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); struct v4l2_mbus_framefmt *format; - format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which); + format = __et8ek8_get_pad_format(sensor, sd_state, fmt->pad, + fmt->which); if (!format) return -EINVAL; @@ -1020,14 +1022,15 @@ } static int et8ek8_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); struct v4l2_mbus_framefmt *format; struct et8ek8_reglist *reglist; - format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which); + format = __et8ek8_get_pad_format(sensor, sd_state, fmt->pad, + fmt->which); if (!format) return -EINVAL; @@ -1327,7 +1330,7 @@ struct et8ek8_reglist *reglist; reglist = et8ek8_reglist_find_type(&meta_reglist, ET8EK8_REGLIST_MODE); - format = __et8ek8_get_pad_format(sensor, fh->pad, 0, + format = __et8ek8_get_pad_format(sensor, fh->state, 0, V4L2_SUBDEV_FORMAT_TRY); et8ek8_reglist_to_mbus(reglist, format); diff -Naur --no-dereference a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c --- a/drivers/media/i2c/hi556.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/hi556.c 2022-01-06 12:45:53.814318107 -0500 @@ -878,7 +878,7 @@ } static int hi556_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct hi556 *hi556 = to_hi556(sd); @@ -893,7 +893,7 @@ mutex_lock(&hi556->mutex); hi556_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; } else { hi556->cur_mode = mode; __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); @@ -920,14 +920,15 @@ } static int hi556_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct hi556 *hi556 = to_hi556(sd); mutex_lock(&hi556->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, cfg, + fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, + sd_state, fmt->pad); else hi556_assign_pad_format(hi556->cur_mode, &fmt->format); @@ -938,7 +939,7 @@ } static int hi556_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -950,7 +951,7 @@ } static int hi556_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -973,7 +974,7 @@ mutex_lock(&hi556->mutex); hi556_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->pad, 0)); + v4l2_subdev_get_try_format(sd, fh->state, 0)); mutex_unlock(&hi556->mutex); return 0; diff -Naur --no-dereference a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c --- a/drivers/media/i2c/imx214.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx214.c 2022-01-06 12:45:53.814318107 -0500 @@ -474,7 +474,7 @@ } static int imx214_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -486,7 +486,7 @@ } static int imx214_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->code != IMX214_MBUS_CODE) @@ -534,13 +534,13 @@ static struct v4l2_mbus_framefmt * __imx214_get_pad_format(struct imx214 *imx214, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&imx214->sd, cfg, pad); + return v4l2_subdev_get_try_format(&imx214->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->fmt; default: @@ -549,13 +549,14 @@ } static int imx214_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct imx214 *imx214 = to_imx214(sd); mutex_lock(&imx214->mutex); - format->format = *__imx214_get_pad_format(imx214, cfg, format->pad, + format->format = *__imx214_get_pad_format(imx214, sd_state, + format->pad, format->which); mutex_unlock(&imx214->mutex); @@ -563,12 +564,13 @@ } static struct v4l2_rect * -__imx214_get_pad_crop(struct imx214 *imx214, struct v4l2_subdev_pad_config *cfg, +__imx214_get_pad_crop(struct imx214 *imx214, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx214->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&imx214->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->crop; default: @@ -577,7 +579,7 @@ } static int imx214_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct imx214 *imx214 = to_imx214(sd); @@ -587,7 +589,8 @@ mutex_lock(&imx214->mutex); - __crop = __imx214_get_pad_crop(imx214, cfg, format->pad, format->which); + __crop = __imx214_get_pad_crop(imx214, sd_state, format->pad, + format->which); mode = v4l2_find_nearest_size(imx214_modes, ARRAY_SIZE(imx214_modes), width, height, @@ -597,7 +600,7 @@ __crop->width = mode->width; __crop->height = mode->height; - __format = __imx214_get_pad_format(imx214, cfg, format->pad, + __format = __imx214_get_pad_format(imx214, sd_state, format->pad, format->which); __format->width = __crop->width; __format->height = __crop->height; @@ -617,7 +620,7 @@ } static int imx214_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct imx214 *imx214 = to_imx214(sd); @@ -626,22 +629,22 @@ return -EINVAL; mutex_lock(&imx214->mutex); - sel->r = *__imx214_get_pad_crop(imx214, cfg, sel->pad, + sel->r = *__imx214_get_pad_crop(imx214, sd_state, sel->pad, sel->which); mutex_unlock(&imx214->mutex); return 0; } static int imx214_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { }; - fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; fmt.format.width = imx214_modes[0].width; fmt.format.height = imx214_modes[0].height; - imx214_set_format(subdev, cfg, &fmt); + imx214_set_format(subdev, sd_state, &fmt); return 0; } @@ -810,7 +813,7 @@ } static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { const struct imx214_mode *mode; diff -Naur --no-dereference a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c --- a/drivers/media/i2c/imx219.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx219.c 2022-01-06 12:45:53.814318107 -0500 @@ -686,7 +686,7 @@ { struct imx219 *imx219 = to_imx219(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); struct v4l2_rect *try_crop; mutex_lock(&imx219->mutex); @@ -699,7 +699,7 @@ try_fmt->field = V4L2_FIELD_NONE; /* Initialize try_crop rectangle. */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); + try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); try_crop->top = IMX219_PIXEL_ARRAY_TOP; try_crop->left = IMX219_PIXEL_ARRAY_LEFT; try_crop->width = IMX219_PIXEL_ARRAY_WIDTH; @@ -800,7 +800,7 @@ }; static int imx219_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx219 *imx219 = to_imx219(sd); @@ -814,7 +814,7 @@ } static int imx219_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct imx219 *imx219 = to_imx219(sd); @@ -854,12 +854,13 @@ } static int __imx219_get_pad_format(struct imx219 *imx219, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad); + v4l2_subdev_get_try_format(&imx219->sd, sd_state, + fmt->pad); /* update the code which could change due to vflip or hflip: */ try_fmt->code = imx219_get_format_code(imx219, try_fmt->code); fmt->format = *try_fmt; @@ -873,21 +874,21 @@ } static int imx219_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx219 *imx219 = to_imx219(sd); int ret; mutex_lock(&imx219->mutex); - ret = __imx219_get_pad_format(imx219, cfg, fmt); + ret = __imx219_get_pad_format(imx219, sd_state, fmt); mutex_unlock(&imx219->mutex); return ret; } static int imx219_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx219 *imx219 = to_imx219(sd); @@ -913,7 +914,7 @@ fmt->format.width, fmt->format.height); imx219_update_pad_format(imx219, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; } else if (imx219->mode != mode || imx219->fmt.code != fmt->format.code) { @@ -970,12 +971,13 @@ } static const struct v4l2_rect * -__imx219_get_pad_crop(struct imx219 *imx219, struct v4l2_subdev_pad_config *cfg, +__imx219_get_pad_crop(struct imx219 *imx219, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx219->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&imx219->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx219->mode->crop; } @@ -984,7 +986,7 @@ } static int imx219_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { switch (sel->target) { @@ -992,7 +994,7 @@ struct imx219 *imx219 = to_imx219(sd); mutex_lock(&imx219->mutex); - sel->r = *__imx219_get_pad_crop(imx219, cfg, sel->pad, + sel->r = *__imx219_get_pad_crop(imx219, sd_state, sel->pad, sel->which); mutex_unlock(&imx219->mutex); diff -Naur --no-dereference a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c --- a/drivers/media/i2c/imx258.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx258.c 2022-01-06 12:45:53.814318107 -0500 @@ -695,7 +695,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; @@ -789,7 +789,7 @@ }; static int imx258_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /* Only one bayer order(GRBG) is supported */ @@ -802,7 +802,7 @@ } static int imx258_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -829,11 +829,12 @@ } static int __imx258_get_pad_format(struct imx258 *imx258, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg, + fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, + sd_state, fmt->pad); else imx258_update_pad_format(imx258->cur_mode, fmt); @@ -842,21 +843,21 @@ } static int imx258_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx258 *imx258 = to_imx258(sd); int ret; mutex_lock(&imx258->mutex); - ret = __imx258_get_pad_format(imx258, cfg, fmt); + ret = __imx258_get_pad_format(imx258, sd_state, fmt); mutex_unlock(&imx258->mutex); return ret; } static int imx258_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx258 *imx258 = to_imx258(sd); @@ -878,7 +879,7 @@ fmt->format.width, fmt->format.height); imx258_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; } else { imx258->cur_mode = mode; diff -Naur --no-dereference a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c --- a/drivers/media/i2c/imx274.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx274.c 2022-01-06 12:45:53.814318107 -0500 @@ -870,7 +870,7 @@ * available (when called from set_fmt) */ static int __imx274_change_compose(struct stimx274 *imx274, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, u32 which, u32 *width, u32 *height, @@ -884,8 +884,8 @@ int best_goodness = INT_MIN; if (which == V4L2_SUBDEV_FORMAT_TRY) { - cur_crop = &cfg->try_crop; - tgt_fmt = &cfg->try_fmt; + cur_crop = &sd_state->pads->try_crop; + tgt_fmt = &sd_state->pads->try_fmt; } else { cur_crop = &imx274->crop; tgt_fmt = &imx274->format; @@ -933,7 +933,7 @@ * Return: 0 on success */ static int imx274_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct stimx274 *imx274 = to_imx274(sd); @@ -955,7 +955,7 @@ * Return: 0 on success */ static int imx274_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -964,7 +964,7 @@ mutex_lock(&imx274->lock); - err = __imx274_change_compose(imx274, cfg, format->which, + err = __imx274_change_compose(imx274, sd_state, format->which, &fmt->width, &fmt->height, 0); if (err) @@ -977,7 +977,7 @@ */ fmt->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; else imx274->format = *fmt; @@ -988,7 +988,7 @@ } static int imx274_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct stimx274 *imx274 = to_imx274(sd); @@ -1008,8 +1008,8 @@ } if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - src_crop = &cfg->try_crop; - src_fmt = &cfg->try_fmt; + src_crop = &sd_state->pads->try_crop; + src_fmt = &sd_state->pads->try_fmt; } else { src_crop = &imx274->crop; src_fmt = &imx274->format; @@ -1043,7 +1043,7 @@ } static int imx274_set_selection_crop(struct stimx274 *imx274, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct v4l2_rect *tgt_crop; @@ -1080,7 +1080,7 @@ sel->r = new_crop; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - tgt_crop = &cfg->try_crop; + tgt_crop = &sd_state->pads->try_crop; else tgt_crop = &imx274->crop; @@ -1094,7 +1094,7 @@ /* if crop size changed then reset the output image size */ if (size_changed) - __imx274_change_compose(imx274, cfg, sel->which, + __imx274_change_compose(imx274, sd_state, sel->which, &new_crop.width, &new_crop.height, sel->flags); @@ -1104,7 +1104,7 @@ } static int imx274_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct stimx274 *imx274 = to_imx274(sd); @@ -1113,13 +1113,13 @@ return -EINVAL; if (sel->target == V4L2_SEL_TGT_CROP) - return imx274_set_selection_crop(imx274, cfg, sel); + return imx274_set_selection_crop(imx274, sd_state, sel); if (sel->target == V4L2_SEL_TGT_COMPOSE) { int err; mutex_lock(&imx274->lock); - err = __imx274_change_compose(imx274, cfg, sel->which, + err = __imx274_change_compose(imx274, sd_state, sel->which, &sel->r.width, &sel->r.height, sel->flags); mutex_unlock(&imx274->lock); diff -Naur --no-dereference a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c --- a/drivers/media/i2c/imx290.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx290.c 2022-01-06 12:45:53.814318107 -0500 @@ -516,7 +516,7 @@ }; static int imx290_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(imx290_formats)) @@ -528,7 +528,7 @@ } static int imx290_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { const struct imx290 *imx290 = to_imx290(sd); @@ -550,7 +550,7 @@ } static int imx290_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx290 *imx290 = to_imx290(sd); @@ -559,7 +559,7 @@ mutex_lock(&imx290->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - framefmt = v4l2_subdev_get_try_format(&imx290->sd, cfg, + framefmt = v4l2_subdev_get_try_format(&imx290->sd, sd_state, fmt->pad); else framefmt = &imx290->current_format; @@ -596,8 +596,8 @@ } static int imx290_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { struct imx290 *imx290 = to_imx290(sd); const struct imx290_mode *mode; @@ -624,7 +624,7 @@ fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + format = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); } else { format = &imx290->current_format; imx290->current_mode = mode; @@ -646,15 +646,15 @@ } static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { 0 }; - fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; fmt.format.width = 1920; fmt.format.height = 1080; - imx290_set_fmt(subdev, cfg, &fmt); + imx290_set_fmt(subdev, sd_state, &fmt); return 0; } diff -Naur --no-dereference a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c --- a/drivers/media/i2c/imx319.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx319.c 2022-01-06 12:45:53.814318107 -0500 @@ -1860,7 +1860,7 @@ { struct imx319 *imx319 = to_imx319(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); mutex_lock(&imx319->mutex); @@ -1947,7 +1947,7 @@ }; static int imx319_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx319 *imx319 = to_imx319(sd); @@ -1963,7 +1963,7 @@ } static int imx319_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct imx319 *imx319 = to_imx319(sd); @@ -1997,14 +1997,14 @@ } static int imx319_do_get_pad_format(struct imx319 *imx319, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; struct v4l2_subdev *sd = &imx319->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *framefmt; } else { imx319_update_pad_format(imx319, imx319->cur_mode, fmt); @@ -2014,14 +2014,14 @@ } static int imx319_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx319 *imx319 = to_imx319(sd); int ret; mutex_lock(&imx319->mutex); - ret = imx319_do_get_pad_format(imx319, cfg, fmt); + ret = imx319_do_get_pad_format(imx319, sd_state, fmt); mutex_unlock(&imx319->mutex); return ret; @@ -2029,7 +2029,7 @@ static int imx319_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx319 *imx319 = to_imx319(sd); @@ -2055,7 +2055,7 @@ fmt->format.width, fmt->format.height); imx319_update_pad_format(imx319, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; } else { imx319->cur_mode = mode; diff -Naur --no-dereference a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c --- a/drivers/media/i2c/imx355.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/imx355.c 2022-01-06 12:45:53.814318107 -0500 @@ -1161,7 +1161,7 @@ { struct imx355 *imx355 = to_imx355(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); mutex_lock(&imx355->mutex); @@ -1248,7 +1248,7 @@ }; static int imx355_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx355 *imx355 = to_imx355(sd); @@ -1264,7 +1264,7 @@ } static int imx355_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct imx355 *imx355 = to_imx355(sd); @@ -1298,14 +1298,14 @@ } static int imx355_do_get_pad_format(struct imx355 *imx355, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; struct v4l2_subdev *sd = &imx355->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *framefmt; } else { imx355_update_pad_format(imx355, imx355->cur_mode, fmt); @@ -1315,14 +1315,14 @@ } static int imx355_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx355 *imx355 = to_imx355(sd); int ret; mutex_lock(&imx355->mutex); - ret = imx355_do_get_pad_format(imx355, cfg, fmt); + ret = imx355_do_get_pad_format(imx355, sd_state, fmt); mutex_unlock(&imx355->mutex); return ret; @@ -1330,7 +1330,7 @@ static int imx355_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx355 *imx355 = to_imx355(sd); @@ -1356,7 +1356,7 @@ fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; } else { imx355->cur_mode = mode; diff -Naur --no-dereference a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig --- a/drivers/media/i2c/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/Kconfig 2022-01-06 12:45:53.810318090 -0500 @@ -886,7 +886,7 @@ config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" - depends on OF + depends on OF && PM depends on GPIOLIB && VIDEO_V4L2 && I2C select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff -Naur --no-dereference a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c --- a/drivers/media/i2c/m5mols/m5mols_core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/m5mols/m5mols_core.c 2022-01-06 12:45:53.814318107 -0500 @@ -539,17 +539,19 @@ } static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which, enum m5mols_restype type) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return cfg ? v4l2_subdev_get_try_format(&info->sd, cfg, 0) : NULL; + return sd_state ? v4l2_subdev_get_try_format(&info->sd, + sd_state, 0) : NULL; return &info->ffmt[type]; } -static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int m5mols_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); @@ -558,7 +560,7 @@ mutex_lock(&info->lock); - format = __find_format(info, cfg, fmt->which, info->res_type); + format = __find_format(info, sd_state, fmt->which, info->res_type); if (format) fmt->format = *format; else @@ -568,7 +570,8 @@ return ret; } -static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int m5mols_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); @@ -582,7 +585,7 @@ if (ret < 0) return ret; - sfmt = __find_format(info, cfg, fmt->which, type); + sfmt = __find_format(info, sd_state, fmt->which, type); if (!sfmt) return 0; @@ -648,7 +651,7 @@ static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (!code || code->index >= SIZE_DEFAULT_FFMT) @@ -909,7 +912,9 @@ */ static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, + fh->state, + 0); *format = m5mols_default_ffmt[0]; return 0; diff -Naur --no-dereference a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c --- a/drivers/media/i2c/max9286.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/max9286.c 2022-01-06 12:45:53.814318107 -0500 @@ -681,7 +681,7 @@ } static int max9286_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index > 0) @@ -694,12 +694,12 @@ static struct v4l2_mbus_framefmt * max9286_get_pad_format(struct max9286_priv *priv, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); + return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &priv->fmt[pad]; default: @@ -708,7 +708,7 @@ } static int max9286_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct max9286_priv *priv = sd_to_max9286(sd); @@ -729,7 +729,8 @@ break; } - cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which); + cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad, + format->which); if (!cfg_fmt) return -EINVAL; @@ -741,7 +742,7 @@ } static int max9286_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct max9286_priv *priv = sd_to_max9286(sd); @@ -757,7 +758,7 @@ if (pad == MAX9286_SRC_PAD) pad = __ffs(priv->bound_sources); - cfg_fmt = max9286_get_pad_format(priv, cfg, pad, format->which); + cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which); if (!cfg_fmt) return -EINVAL; @@ -801,7 +802,7 @@ unsigned int i; for (i = 0; i < MAX9286_N_SINKS; i++) { - format = v4l2_subdev_get_try_format(subdev, fh->pad, i); + format = v4l2_subdev_get_try_format(subdev, fh->state, i); max9286_init_format(format); } diff -Naur --no-dereference a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c --- a/drivers/media/i2c/ml86v7667.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ml86v7667.c 2022-01-06 12:45:53.814318107 -0500 @@ -188,7 +188,7 @@ } static int ml86v7667_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index > 0) @@ -200,7 +200,7 @@ } static int ml86v7667_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ml86v7667_priv *priv = to_ml86v7667(sd); diff -Naur --no-dereference a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c --- a/drivers/media/i2c/mt9m001.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9m001.c 2022-01-06 12:45:53.814318107 -0500 @@ -253,7 +253,7 @@ } static int mt9m001_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -294,7 +294,7 @@ } static int mt9m001_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -319,7 +319,7 @@ } static int mt9m001_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -330,7 +330,7 @@ return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mf; return 0; } @@ -376,7 +376,7 @@ } static int mt9m001_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -410,7 +410,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } @@ -656,12 +656,12 @@ }; static int mt9m001_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, cfg, 0); + v4l2_subdev_get_try_format(sd, sd_state, 0); try_fmt->width = MT9M001_MAX_WIDTH; try_fmt->height = MT9M001_MAX_HEIGHT; @@ -676,7 +676,7 @@ } static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff -Naur --no-dereference a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c --- a/drivers/media/i2c/mt9m032.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9m032.c 2022-01-06 12:45:53.814318107 -0500 @@ -304,7 +304,7 @@ */ static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -315,7 +315,7 @@ } static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index != 0 || fse->code != MEDIA_BUS_FMT_Y8_1X8) @@ -338,12 +338,13 @@ * Returns a pointer the current active or fh relative try crop rect */ static struct v4l2_rect * -__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_pad_config *cfg, +__mt9m032_get_pad_crop(struct mt9m032 *sensor, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&sensor->subdev, cfg, 0); + return v4l2_subdev_get_try_crop(&sensor->subdev, sd_state, 0); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->crop; default: @@ -360,12 +361,14 @@ * Returns a pointer the current active or fh relative try format */ static struct v4l2_mbus_framefmt * -__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_pad_config *cfg, +__mt9m032_get_pad_format(struct mt9m032 *sensor, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&sensor->subdev, cfg, 0); + return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, + 0); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: @@ -374,20 +377,20 @@ } static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct mt9m032 *sensor = to_mt9m032(subdev); mutex_lock(&sensor->lock); - fmt->format = *__mt9m032_get_pad_format(sensor, cfg, fmt->which); + fmt->format = *__mt9m032_get_pad_format(sensor, sd_state, fmt->which); mutex_unlock(&sensor->lock); return 0; } static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct mt9m032 *sensor = to_mt9m032(subdev); @@ -401,7 +404,7 @@ } /* Scaling is not supported, the format is thus fixed. */ - fmt->format = *__mt9m032_get_pad_format(sensor, cfg, fmt->which); + fmt->format = *__mt9m032_get_pad_format(sensor, sd_state, fmt->which); ret = 0; done: @@ -410,7 +413,7 @@ } static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); @@ -419,14 +422,14 @@ return -EINVAL; mutex_lock(&sensor->lock); - sel->r = *__mt9m032_get_pad_crop(sensor, cfg, sel->which); + sel->r = *__mt9m032_get_pad_crop(sensor, sd_state, sel->which); mutex_unlock(&sensor->lock); return 0; } static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); @@ -462,13 +465,14 @@ rect.height = min_t(unsigned int, rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9m032_get_pad_crop(sensor, cfg, sel->which); + __crop = __mt9m032_get_pad_crop(sensor, sd_state, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - format = __mt9m032_get_pad_format(sensor, cfg, sel->which); + format = __mt9m032_get_pad_format(sensor, sd_state, + sel->which); format->width = rect.width; format->height = rect.height; } diff -Naur --no-dereference a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c --- a/drivers/media/i2c/mt9m111.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9m111.c 2022-01-06 12:45:53.814318107 -0500 @@ -449,7 +449,7 @@ } static int mt9m111_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -493,7 +493,7 @@ } static int mt9m111_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -518,7 +518,7 @@ } static int mt9m111_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -529,7 +529,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mf = v4l2_subdev_get_try_format(sd, cfg, format->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, format->pad); format->format = *mf; return 0; #else @@ -624,7 +624,7 @@ } static int mt9m111_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -678,7 +678,7 @@ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } @@ -1100,7 +1100,7 @@ } static int mt9m111_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(mt9m111_colour_fmts)) @@ -1119,11 +1119,11 @@ } static int mt9m111_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, cfg, 0); + v4l2_subdev_get_try_format(sd, sd_state, 0); format->width = MT9M111_MAX_WIDTH; format->height = MT9M111_MAX_HEIGHT; diff -Naur --no-dereference a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c --- a/drivers/media/i2c/mt9p031.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9p031.c 2022-01-06 12:45:53.814318107 -0500 @@ -497,7 +497,7 @@ } static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -510,7 +510,7 @@ } static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -528,12 +528,14 @@ } static struct v4l2_mbus_framefmt * -__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, +__mt9p031_get_pad_format(struct mt9p031 *mt9p031, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9p031->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->format; default: @@ -542,12 +544,14 @@ } static struct v4l2_rect * -__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, - unsigned int pad, u32 which) +__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9p031->subdev, cfg, pad); + return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->crop; default: @@ -556,18 +560,18 @@ } static int mt9p031_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - fmt->format = *__mt9p031_get_pad_format(mt9p031, cfg, fmt->pad, + fmt->format = *__mt9p031_get_pad_format(mt9p031, sd_state, fmt->pad, fmt->which); return 0; } static int mt9p031_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -578,7 +582,7 @@ unsigned int hratio; unsigned int vratio; - __crop = __mt9p031_get_pad_crop(mt9p031, cfg, format->pad, + __crop = __mt9p031_get_pad_crop(mt9p031, sd_state, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -594,7 +598,7 @@ hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); - __format = __mt9p031_get_pad_format(mt9p031, cfg, format->pad, + __format = __mt9p031_get_pad_format(mt9p031, sd_state, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -605,7 +609,7 @@ } static int mt9p031_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -613,12 +617,13 @@ if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); + sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad, + sel->which); return 0; } static int mt9p031_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -648,13 +653,15 @@ rect.height = min_t(unsigned int, rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); + __crop = __mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad, + sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9p031_get_pad_format(mt9p031, cfg, sel->pad, + __format = __mt9p031_get_pad_format(mt9p031, sd_state, + sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -969,13 +976,13 @@ struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); crop->left = MT9P031_COLUMN_START_DEF; crop->top = MT9P031_ROW_START_DEF; crop->width = MT9P031_WINDOW_WIDTH_DEF; crop->height = MT9P031_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); + format = v4l2_subdev_get_try_format(subdev, fh->state, 0); if (mt9p031->model == MT9P031_MODEL_MONOCHROME) format->code = MEDIA_BUS_FMT_Y12_1X12; diff -Naur --no-dereference a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c --- a/drivers/media/i2c/mt9t001.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9t001.c 2022-01-06 12:45:53.814318107 -0500 @@ -252,12 +252,14 @@ */ static struct v4l2_mbus_framefmt * -__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_pad_config *cfg, +__mt9t001_get_pad_format(struct mt9t001 *mt9t001, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9t001->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&mt9t001->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9t001->format; default: @@ -266,12 +268,14 @@ } static struct v4l2_rect * -__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_pad_config *cfg, +__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9t001->subdev, cfg, pad); + return v4l2_subdev_get_try_crop(&mt9t001->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9t001->crop; default: @@ -335,7 +339,7 @@ } static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -346,7 +350,7 @@ } static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -361,18 +365,19 @@ } static int mt9t001_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); - format->format = *__mt9t001_get_pad_format(mt9t001, cfg, format->pad, + format->format = *__mt9t001_get_pad_format(mt9t001, sd_state, + format->pad, format->which); return 0; } static int mt9t001_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); @@ -383,7 +388,7 @@ unsigned int hratio; unsigned int vratio; - __crop = __mt9t001_get_pad_crop(mt9t001, cfg, format->pad, + __crop = __mt9t001_get_pad_crop(mt9t001, sd_state, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -399,7 +404,7 @@ hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); - __format = __mt9t001_get_pad_format(mt9t001, cfg, format->pad, + __format = __mt9t001_get_pad_format(mt9t001, sd_state, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -410,7 +415,7 @@ } static int mt9t001_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); @@ -418,12 +423,13 @@ if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9t001_get_pad_crop(mt9t001, cfg, sel->pad, sel->which); + sel->r = *__mt9t001_get_pad_crop(mt9t001, sd_state, sel->pad, + sel->which); return 0; } static int mt9t001_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); @@ -455,13 +461,15 @@ rect.height = min_t(unsigned int, rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9t001_get_pad_crop(mt9t001, cfg, sel->pad, sel->which); + __crop = __mt9t001_get_pad_crop(mt9t001, sd_state, sel->pad, + sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9t001_get_pad_format(mt9t001, cfg, sel->pad, + __format = __mt9t001_get_pad_format(mt9t001, sd_state, + sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -798,13 +806,13 @@ struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); crop->left = MT9T001_COLUMN_START_DEF; crop->top = MT9T001_ROW_START_DEF; crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); + format = v4l2_subdev_get_try_format(subdev, fh->state, 0); format->code = MEDIA_BUS_FMT_SGRBG10_1X10; format->width = MT9T001_WINDOW_WIDTH_DEF + 1; format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; diff -Naur --no-dereference a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c --- a/drivers/media/i2c/mt9t112.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9t112.c 2022-01-06 12:45:53.814318107 -0500 @@ -872,8 +872,8 @@ } static int mt9t112_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); @@ -897,7 +897,7 @@ } static int mt9t112_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -912,7 +912,7 @@ } static int mt9t112_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -953,7 +953,7 @@ } static int mt9t112_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -982,13 +982,13 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9t112_s_fmt(sd, mf); - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff -Naur --no-dereference a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c --- a/drivers/media/i2c/mt9v011.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9v011.c 2022-01-06 12:45:53.814318107 -0500 @@ -327,7 +327,7 @@ } static int mt9v011_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index > 0) @@ -338,7 +338,7 @@ } static int mt9v011_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -358,7 +358,7 @@ set_res(sd); } else { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; } return 0; diff -Naur --no-dereference a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c --- a/drivers/media/i2c/mt9v032.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9v032.c 2022-01-06 12:45:53.814318107 -0500 @@ -349,12 +349,14 @@ */ static struct v4l2_mbus_framefmt * -__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9v032->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&mt9v032->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->format; default: @@ -363,12 +365,14 @@ } static struct v4l2_rect * -__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9v032->subdev, cfg, pad); + return v4l2_subdev_get_try_crop(&mt9v032->subdev, sd_state, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->crop; default: @@ -425,7 +429,7 @@ } static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -438,7 +442,7 @@ } static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -457,12 +461,13 @@ } static int mt9v032_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); - format->format = *__mt9v032_get_pad_format(mt9v032, cfg, format->pad, + format->format = *__mt9v032_get_pad_format(mt9v032, sd_state, + format->pad, format->which); return 0; } @@ -492,7 +497,7 @@ } static int mt9v032_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -503,7 +508,7 @@ unsigned int hratio; unsigned int vratio; - __crop = __mt9v032_get_pad_crop(mt9v032, cfg, format->pad, + __crop = __mt9v032_get_pad_crop(mt9v032, sd_state, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -519,7 +524,7 @@ hratio = mt9v032_calc_ratio(__crop->width, width); vratio = mt9v032_calc_ratio(__crop->height, height); - __format = __mt9v032_get_pad_format(mt9v032, cfg, format->pad, + __format = __mt9v032_get_pad_format(mt9v032, sd_state, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -536,7 +541,7 @@ } static int mt9v032_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -544,12 +549,13 @@ if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); + sel->r = *__mt9v032_get_pad_crop(mt9v032, sd_state, sel->pad, + sel->which); return 0; } static int mt9v032_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -581,13 +587,15 @@ rect.height = min_t(unsigned int, rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); + __crop = __mt9v032_get_pad_crop(mt9v032, sd_state, sel->pad, + sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9v032_get_pad_format(mt9v032, cfg, sel->pad, + __format = __mt9v032_get_pad_format(mt9v032, sd_state, + sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -922,13 +930,13 @@ struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); crop->left = MT9V032_COLUMN_START_DEF; crop->top = MT9V032_ROW_START_DEF; crop->width = MT9V032_WINDOW_WIDTH_DEF; crop->height = MT9V032_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); + format = v4l2_subdev_get_try_format(subdev, fh->state, 0); if (mt9v032->model->color) format->code = MEDIA_BUS_FMT_SGRBG10_1X10; diff -Naur --no-dereference a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c --- a/drivers/media/i2c/mt9v111.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/mt9v111.c 2022-01-06 12:45:53.814318107 -0500 @@ -791,14 +791,14 @@ static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( struct mt9v111_dev *mt9v111, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: #if IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) - return v4l2_subdev_get_try_format(&mt9v111->sd, cfg, pad); + return v4l2_subdev_get_try_format(&mt9v111->sd, sd_state, pad); #else return &cfg->try_fmt; #endif @@ -810,7 +810,7 @@ } static int mt9v111_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index > ARRAY_SIZE(mt9v111_formats) - 1) @@ -822,7 +822,7 @@ } static int mt9v111_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { unsigned int i; @@ -845,7 +845,7 @@ } static int mt9v111_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->pad || fse->index >= ARRAY_SIZE(mt9v111_frame_sizes)) @@ -860,7 +860,7 @@ } static int mt9v111_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); @@ -869,7 +869,8 @@ return -EINVAL; mutex_lock(&mt9v111->stream_mutex); - format->format = *__mt9v111_get_pad_format(mt9v111, cfg, format->pad, + format->format = *__mt9v111_get_pad_format(mt9v111, sd_state, + format->pad, format->which); mutex_unlock(&mt9v111->stream_mutex); @@ -877,7 +878,7 @@ } static int mt9v111_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); @@ -925,7 +926,7 @@ new_fmt.height = mt9v111_frame_sizes[idx].height; /* Update the device (or pad) format if it has changed. */ - __fmt = __mt9v111_get_pad_format(mt9v111, cfg, format->pad, + __fmt = __mt9v111_get_pad_format(mt9v111, sd_state, format->pad, format->which); /* Format hasn't changed, stop here. */ @@ -954,9 +955,9 @@ } static int mt9v111_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { - cfg->try_fmt = mt9v111_def_fmt; + sd_state->pads->try_fmt = mt9v111_def_fmt; return 0; } diff -Naur --no-dereference a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c --- a/drivers/media/i2c/noon010pc30.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/noon010pc30.c 2022-01-06 12:45:53.814318107 -0500 @@ -488,7 +488,7 @@ } static int noon010_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(noon010_formats)) @@ -499,15 +499,15 @@ } static int noon010_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct noon010_info *info = to_noon010(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (cfg) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + if (sd_state) { + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); fmt->format = *mf; } return 0; @@ -539,7 +539,8 @@ return &noon010_formats[i]; } -static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int noon010_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct noon010_info *info = to_noon010(sd); @@ -554,8 +555,8 @@ fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (cfg) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + if (sd_state) { + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); *mf = fmt->format; } return 0; @@ -637,7 +638,9 @@ static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, + fh->state, + 0); mf->width = noon010_sizes[0].width; mf->height = noon010_sizes[0].height; diff -Naur --no-dereference a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c --- a/drivers/media/i2c/ov13858.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov13858.c 2022-01-06 12:45:53.814318107 -0500 @@ -1150,7 +1150,7 @@ { struct ov13858 *ov13858 = to_ov13858(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->pad, + fh->state, 0); mutex_lock(&ov13858->mutex); @@ -1275,7 +1275,7 @@ }; static int ov13858_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /* Only one bayer order(GRBG) is supported */ @@ -1288,7 +1288,7 @@ } static int ov13858_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -1315,14 +1315,14 @@ } static int ov13858_do_get_pad_format(struct ov13858 *ov13858, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; struct v4l2_subdev *sd = &ov13858->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13858_update_pad_format(ov13858->cur_mode, fmt); @@ -1332,14 +1332,14 @@ } static int ov13858_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov13858 *ov13858 = to_ov13858(sd); int ret; mutex_lock(&ov13858->mutex); - ret = ov13858_do_get_pad_format(ov13858, cfg, fmt); + ret = ov13858_do_get_pad_format(ov13858, sd_state, fmt); mutex_unlock(&ov13858->mutex); return ret; @@ -1347,7 +1347,7 @@ static int ov13858_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov13858 *ov13858 = to_ov13858(sd); @@ -1371,7 +1371,7 @@ fmt->format.width, fmt->format.height); ov13858_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13858->cur_mode = mode; diff -Naur --no-dereference a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c --- a/drivers/media/i2c/ov2640.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov2640.c 2022-01-06 12:45:53.814318107 -0500 @@ -913,7 +913,7 @@ } static int ov2640_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -925,7 +925,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mf; return 0; #else @@ -946,7 +946,7 @@ } static int ov2640_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -996,7 +996,7 @@ /* select format */ priv->cfmt_code = mf->code; } else { - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; } out: mutex_unlock(&priv->lock); @@ -1005,11 +1005,11 @@ } static int ov2640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, cfg, 0); + v4l2_subdev_get_try_format(sd, sd_state, 0); const struct ov2640_win_size *win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); @@ -1026,7 +1026,7 @@ } static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(ov2640_codes)) @@ -1037,7 +1037,7 @@ } static int ov2640_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) diff -Naur --no-dereference a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c --- a/drivers/media/i2c/ov2659.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov2659.c 2022-01-06 12:45:53.814318107 -0500 @@ -980,7 +980,7 @@ */ static int ov2659_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -996,7 +996,7 @@ } static int ov2659_enum_frame_sizes(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1022,7 +1022,7 @@ } static int ov2659_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1034,7 +1034,7 @@ #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); mutex_lock(&ov2659->lock); fmt->format = *mf; mutex_unlock(&ov2659->lock); @@ -1084,7 +1084,7 @@ } static int ov2659_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1114,7 +1114,7 @@ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; #endif } else { @@ -1313,7 +1313,7 @@ { struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); dev_dbg(&client->dev, "%s:\n", __func__); diff -Naur --no-dereference a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c --- a/drivers/media/i2c/ov2680.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov2680.c 2022-01-06 12:45:53.814318107 -0500 @@ -645,7 +645,7 @@ } static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct ov2680_dev *sensor = to_ov2680_dev(sd); @@ -659,7 +659,7 @@ } static int ov2680_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov2680_dev *sensor = to_ov2680_dev(sd); @@ -673,7 +673,8 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, + format->pad); #else ret = -EINVAL; #endif @@ -690,7 +691,7 @@ } static int ov2680_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov2680_dev *sensor = to_ov2680_dev(sd); @@ -721,7 +722,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *try_fmt; #endif goto unlock; @@ -743,22 +744,22 @@ } static int ov2680_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { - .which = cfg ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, .format = { .width = 800, .height = 600, } }; - return ov2680_set_fmt(sd, cfg, &fmt); + return ov2680_set_fmt(sd, sd_state, &fmt); } static int ov2680_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; @@ -775,7 +776,7 @@ } static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct v4l2_fract tpf; diff -Naur --no-dereference a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c --- a/drivers/media/i2c/ov2685.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov2685.c 2022-01-06 12:45:53.814318107 -0500 @@ -328,7 +328,7 @@ } static int ov2685_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov2685 *ov2685 = to_ov2685(sd); @@ -341,7 +341,7 @@ } static int ov2685_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov2685 *ov2685 = to_ov2685(sd); @@ -353,7 +353,7 @@ } static int ov2685_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(supported_modes)) @@ -365,7 +365,7 @@ } static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; @@ -494,7 +494,7 @@ mutex_lock(&ov2685->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); + try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); /* Initialize try_fmt */ ov2685_fill_fmt(&supported_modes[0], try_fmt); diff -Naur --no-dereference a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c --- a/drivers/media/i2c/ov2740.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov2740.c 2022-01-06 12:45:53.814318107 -0500 @@ -710,7 +710,7 @@ } static int ov2740_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov2740 *ov2740 = to_ov2740(sd); @@ -725,7 +725,7 @@ mutex_lock(&ov2740->mutex); ov2740_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; } else { ov2740->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov2740->link_freq, mode->link_freq_index); @@ -750,14 +750,15 @@ } static int ov2740_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov2740 *ov2740 = to_ov2740(sd); mutex_lock(&ov2740->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov2740->sd, cfg, + fmt->format = *v4l2_subdev_get_try_format(&ov2740->sd, + sd_state, fmt->pad); else ov2740_update_pad_format(ov2740->cur_mode, &fmt->format); @@ -768,7 +769,7 @@ } static int ov2740_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -780,7 +781,7 @@ } static int ov2740_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -803,7 +804,7 @@ mutex_lock(&ov2740->mutex); ov2740_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->pad, 0)); + v4l2_subdev_get_try_format(sd, fh->state, 0)); mutex_unlock(&ov2740->mutex); return 0; diff -Naur --no-dereference a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c --- a/drivers/media/i2c/ov5640.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov5640.c 2022-01-06 12:45:53.814318107 -0500 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -237,8 +238,6 @@ /* lock to protect all members below */ struct mutex lock; - int power_count; - struct v4l2_mbus_framefmt fmt; bool pending_fmt_change; @@ -1253,6 +1252,14 @@ on ? 0x00 : 0x0f); } +static int ov5640_set_stream(struct ov5640_dev *sensor, bool on) +{ + if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) + return ov5640_set_stream_mipi(sensor, on); + else + return ov5640_set_stream_dvp(sensor, on); +} + static int ov5640_get_sysclk(struct ov5640_dev *sensor) { /* calculate sysclk */ @@ -2131,37 +2138,6 @@ /* --------------- Subdev Operations --------------- */ -static int ov5640_s_power(struct v4l2_subdev *sd, int on) -{ - struct ov5640_dev *sensor = to_ov5640_dev(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - /* - * If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (sensor->power_count == !on) { - ret = ov5640_set_power(sensor, !!on); - if (ret) - goto out; - } - - /* Update the power count. */ - sensor->power_count += on ? 1 : -1; - WARN_ON(sensor->power_count < 0); -out: - mutex_unlock(&sensor->lock); - - if (on && !ret && sensor->power_count == 1) { - /* restore controls */ - ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - } - - return ret; -} - static int ov5640_try_frame_interval(struct ov5640_dev *sensor, struct v4l2_fract *fi, u32 width, u32 height) @@ -2203,7 +2179,7 @@ } static int ov5640_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov5640_dev *sensor = to_ov5640_dev(sd); @@ -2215,7 +2191,7 @@ mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, + fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, format->pad); else fmt = &sensor->fmt; @@ -2261,7 +2237,7 @@ } static int ov5640_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov5640_dev *sensor = to_ov5640_dev(sd); @@ -2286,7 +2262,7 @@ goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); else fmt = &sensor->fmt; @@ -2657,6 +2633,7 @@ { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct ov5640_dev *sensor = to_ov5640_dev(sd); + struct device *dev = &sensor->i2c_client->dev; int ret; /* v4l2_ctrl_lock() locks our own mutex */ @@ -2666,7 +2643,7 @@ * not apply any controls to H/W at this time. Instead * the controls will be restored right after power-up. */ - if (sensor->power_count == 0) + if (pm_runtime_suspended(dev)) return 0; switch (ctrl->id) { @@ -2794,7 +2771,7 @@ } static int ov5640_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->pad != 0) @@ -2814,7 +2791,7 @@ static int ov5640_enum_frame_interval( struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct ov5640_dev *sensor = to_ov5640_dev(sd); @@ -2900,7 +2877,7 @@ } static int ov5640_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad != 0) @@ -2915,39 +2892,57 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) { struct ov5640_dev *sensor = to_ov5640_dev(sd); + struct device *dev = &sensor->i2c_client->dev; int ret = 0; mutex_lock(&sensor->lock); - if (sensor->streaming == !enable) { - if (enable && sensor->pending_mode_change) { + if (sensor->streaming == enable) { + mutex_unlock(&sensor->lock); + return 0; + } + + if (enable) { + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err; + + if (sensor->pending_mode_change) { ret = ov5640_set_mode(sensor); if (ret) - goto out; + goto put_pm; } - if (enable && sensor->pending_fmt_change) { + if (sensor->pending_fmt_change) { ret = ov5640_set_framefmt(sensor, &sensor->fmt); if (ret) - goto out; + goto put_pm; sensor->pending_fmt_change = false; } - if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) - ret = ov5640_set_stream_mipi(sensor, enable); - else - ret = ov5640_set_stream_dvp(sensor, enable); + ret = ov5640_set_stream(sensor, true); + if (ret) + goto put_pm; + } else { + ret = ov5640_set_stream(sensor, false); + if (ret) + goto err; - if (!ret) - sensor->streaming = enable; + pm_runtime_put(dev); } -out: + + sensor->streaming = enable; + mutex_unlock(&sensor->lock); + return 0; + +put_pm: + pm_runtime_put(dev); +err: mutex_unlock(&sensor->lock); return ret; } static const struct v4l2_subdev_core_ops ov5640_core_ops = { - .s_power = ov5640_s_power, .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, @@ -3013,6 +3008,29 @@ return ret; } +static int ov5640_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov5640_dev *sensor = to_ov5640_dev(subdev); + + return ov5640_set_power(sensor, false); +} + +static int ov5640_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov5640_dev *sensor = to_ov5640_dev(subdev); + int ret = 0; + + ret = ov5640_set_power(sensor, true); + if (ret) + return ret; + + return __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); +} + static int ov5640_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -3138,13 +3156,17 @@ if (ret) goto entity_cleanup; + pm_runtime_enable(dev); + pm_runtime_set_suspended(dev); + ret = v4l2_async_register_subdev_sensor_common(&sensor->sd); if (ret) - goto free_ctrls; + goto pm_disable; return 0; -free_ctrls: +pm_disable: + pm_runtime_disable(dev); v4l2_ctrl_handler_free(&sensor->ctrls.handler); entity_cleanup: media_entity_cleanup(&sensor->sd.entity); @@ -3154,17 +3176,23 @@ static int ov5640_remove(struct i2c_client *client) { + struct device *dev = &client->dev; struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov5640_dev *sensor = to_ov5640_dev(sd); v4l2_async_unregister_subdev(&sensor->sd); media_entity_cleanup(&sensor->sd.entity); + pm_runtime_disable(dev); v4l2_ctrl_handler_free(&sensor->ctrls.handler); mutex_destroy(&sensor->lock); return 0; } +static const struct dev_pm_ops ov5640_pm_ops = { + SET_RUNTIME_PM_OPS(ov5640_suspend, ov5640_resume, NULL) +}; + static const struct i2c_device_id ov5640_id[] = { {"ov5640", 0}, {}, @@ -3181,6 +3209,7 @@ .driver = { .name = "ov5640", .of_match_table = ov5640_dt_ids, + .pm = &ov5640_pm_ops, }, .id_table = ov5640_id, .probe_new = ov5640_probe, diff -Naur --no-dereference a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c --- a/drivers/media/i2c/ov5645.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov5645.c 2022-01-06 12:45:53.814318107 -0500 @@ -837,7 +837,7 @@ }; static int ov5645_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -849,7 +849,7 @@ } static int ov5645_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->code != MEDIA_BUS_FMT_UYVY8_2X8) @@ -868,13 +868,13 @@ static struct v4l2_mbus_framefmt * __ov5645_get_pad_format(struct ov5645 *ov5645, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5645->sd, cfg, pad); + return v4l2_subdev_get_try_format(&ov5645->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->fmt; default: @@ -883,23 +883,25 @@ } static int ov5645_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov5645 *ov5645 = to_ov5645(sd); - format->format = *__ov5645_get_pad_format(ov5645, cfg, format->pad, + format->format = *__ov5645_get_pad_format(ov5645, sd_state, + format->pad, format->which); return 0; } static struct v4l2_rect * -__ov5645_get_pad_crop(struct ov5645 *ov5645, struct v4l2_subdev_pad_config *cfg, +__ov5645_get_pad_crop(struct ov5645 *ov5645, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5645->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&ov5645->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->crop; default: @@ -908,7 +910,7 @@ } static int ov5645_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov5645 *ov5645 = to_ov5645(sd); @@ -917,8 +919,8 @@ const struct ov5645_mode_info *new_mode; int ret; - __crop = __ov5645_get_pad_crop(ov5645, cfg, format->pad, - format->which); + __crop = __ov5645_get_pad_crop(ov5645, sd_state, format->pad, + format->which); new_mode = v4l2_find_nearest_size(ov5645_mode_info_data, ARRAY_SIZE(ov5645_mode_info_data), @@ -942,8 +944,8 @@ ov5645->current_mode = new_mode; } - __format = __ov5645_get_pad_format(ov5645, cfg, format->pad, - format->which); + __format = __ov5645_get_pad_format(ov5645, sd_state, format->pad, + format->which); __format->width = __crop->width; __format->height = __crop->height; __format->code = MEDIA_BUS_FMT_UYVY8_2X8; @@ -956,21 +958,21 @@ } static int ov5645_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { 0 }; - fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; fmt.format.width = 1920; fmt.format.height = 1080; - ov5645_set_format(subdev, cfg, &fmt); + ov5645_set_format(subdev, sd_state, &fmt); return 0; } static int ov5645_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct ov5645 *ov5645 = to_ov5645(sd); @@ -978,7 +980,7 @@ if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__ov5645_get_pad_crop(ov5645, cfg, sel->pad, + sel->r = *__ov5645_get_pad_crop(ov5645, sd_state, sel->pad, sel->which); return 0; } diff -Naur --no-dereference a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c --- a/drivers/media/i2c/ov5647.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov5647.c 2022-01-06 12:45:53.814318107 -0500 @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * A V4L2 driver for OmniVision OV5647 cameras. * @@ -8,119 +9,316 @@ * Copyright (C) 2006-7 Jonathan Corbet * * Copyright (C) 2016, Synopsys, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include +#include #include #include #include #include #include +#include #include #include +#include #include +#include #include #include #include -#define SENSOR_NAME "ov5647" +/* + * From the datasheet, "20ms after PWDN goes low or 20ms after RESETB goes + * high if reset is inserted after PWDN goes high, host can access sensor's + * SCCB to initialize sensor." + */ +#define PWDN_ACTIVE_DELAY_MS 20 #define MIPI_CTRL00_CLOCK_LANE_GATE BIT(5) +#define MIPI_CTRL00_LINE_SYNC_ENABLE BIT(4) #define MIPI_CTRL00_BUS_IDLE BIT(2) #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) #define OV5647_SW_STANDBY 0x0100 #define OV5647_SW_RESET 0x0103 -#define OV5647_REG_CHIPID_H 0x300A -#define OV5647_REG_CHIPID_L 0x300B -#define OV5640_REG_PAD_OUT 0x300D +#define OV5647_REG_CHIPID_H 0x300a +#define OV5647_REG_CHIPID_L 0x300b +#define OV5640_REG_PAD_OUT 0x300d +#define OV5647_REG_EXP_HI 0x3500 +#define OV5647_REG_EXP_MID 0x3501 +#define OV5647_REG_EXP_LO 0x3502 +#define OV5647_REG_AEC_AGC 0x3503 +#define OV5647_REG_GAIN_HI 0x350a +#define OV5647_REG_GAIN_LO 0x350b +#define OV5647_REG_VTS_HI 0x380e +#define OV5647_REG_VTS_LO 0x380f #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 +#define OV5647_REG_AWB 0x5001 #define REG_TERM 0xfffe #define VAL_TERM 0xfe #define REG_DLY 0xffff -#define OV5647_ROW_START 0x01 -#define OV5647_ROW_START_MIN 0 -#define OV5647_ROW_START_MAX 2004 -#define OV5647_ROW_START_DEF 54 - -#define OV5647_COLUMN_START 0x02 -#define OV5647_COLUMN_START_MIN 0 -#define OV5647_COLUMN_START_MAX 2750 -#define OV5647_COLUMN_START_DEF 16 - -#define OV5647_WINDOW_HEIGHT 0x03 -#define OV5647_WINDOW_HEIGHT_MIN 2 -#define OV5647_WINDOW_HEIGHT_MAX 2006 -#define OV5647_WINDOW_HEIGHT_DEF 1944 - -#define OV5647_WINDOW_WIDTH 0x04 -#define OV5647_WINDOW_WIDTH_MIN 2 -#define OV5647_WINDOW_WIDTH_MAX 2752 -#define OV5647_WINDOW_WIDTH_DEF 2592 +/* OV5647 native and active pixel array size */ +#define OV5647_NATIVE_WIDTH 2624U +#define OV5647_NATIVE_HEIGHT 1956U + +#define OV5647_PIXEL_ARRAY_LEFT 16U +#define OV5647_PIXEL_ARRAY_TOP 16U +#define OV5647_PIXEL_ARRAY_WIDTH 2592U +#define OV5647_PIXEL_ARRAY_HEIGHT 1944U + +#define OV5647_VBLANK_MIN 4 +#define OV5647_VTS_MAX 32767 + +#define OV5647_EXPOSURE_MIN 4 +#define OV5647_EXPOSURE_STEP 1 +#define OV5647_EXPOSURE_DEFAULT 1000 +#define OV5647_EXPOSURE_MAX 65535 struct regval_list { u16 addr; u8 data; }; +struct ov5647_mode { + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + u64 pixel_rate; + int hts; + int vts; + const struct regval_list *reg_list; + unsigned int num_regs; +}; + struct ov5647 { struct v4l2_subdev sd; struct media_pad pad; struct mutex lock; - struct v4l2_mbus_framefmt format; - unsigned int width; - unsigned int height; - int power_count; struct clk *xclk; + struct gpio_desc *pwdn; + bool clock_ncont; + struct v4l2_ctrl_handler ctrls; + const struct ov5647_mode *mode; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + bool streaming; }; -static inline struct ov5647 *to_state(struct v4l2_subdev *sd) +static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) { return container_of(sd, struct ov5647, sd); } -static struct regval_list sensor_oe_disable_regs[] = { +static const struct regval_list sensor_oe_disable_regs[] = { {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x00}, }; -static struct regval_list sensor_oe_enable_regs[] = { +static const struct regval_list sensor_oe_enable_regs[] = { {0x3000, 0x0f}, {0x3001, 0xff}, {0x3002, 0xe4}, }; -static struct regval_list ov5647_640x480[] = { +static struct regval_list ov5647_2592x1944_10bpp[] = { {0x0100, 0x00}, {0x0103, 0x01}, - {0x3034, 0x08}, + {0x3034, 0x1a}, {0x3035, 0x21}, - {0x3036, 0x46}, + {0x3036, 0x69}, + {0x303c, 0x11}, + {0x3106, 0xf5}, + {0x3821, 0x06}, + {0x3820, 0x00}, + {0x3827, 0xec}, + {0x370c, 0x03}, + {0x3612, 0x5b}, + {0x3618, 0x04}, + {0x5000, 0x06}, + {0x5002, 0x41}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x301c, 0xf8}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x0b}, + {0x380d, 0x1c}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3811, 0x10}, + {0x3813, 0x06}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x28}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x08}, + {0x3a0e, 0x06}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x04}, + {0x4000, 0x09}, + {0x4837, 0x19}, + {0x4800, 0x24}, + {0x3503, 0x03}, + {0x0100, 0x01}, +}; + +static struct regval_list ov5647_1080p30_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3034, 0x1a}, + {0x3035, 0x21}, + {0x3036, 0x62}, + {0x303c, 0x11}, + {0x3106, 0xf5}, + {0x3821, 0x06}, + {0x3820, 0x00}, + {0x3827, 0xec}, + {0x370c, 0x03}, + {0x3612, 0x5b}, + {0x3618, 0x04}, + {0x5000, 0x06}, + {0x5002, 0x41}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x301c, 0xf8}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x09}, + {0x380d, 0x70}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x3800, 0x01}, + {0x3801, 0x5c}, + {0x3802, 0x01}, + {0x3803, 0xb2}, + {0x3804, 0x08}, + {0x3805, 0xe3}, + {0x3806, 0x05}, + {0x3807, 0xf1}, + {0x3811, 0x04}, + {0x3813, 0x02}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x4b}, + {0x3a0a, 0x01}, + {0x3a0b, 0x13}, + {0x3a0d, 0x04}, + {0x3a0e, 0x03}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x04}, + {0x4000, 0x09}, + {0x4837, 0x19}, + {0x4800, 0x34}, + {0x3503, 0x03}, + {0x0100, 0x01}, +}; + +static struct regval_list ov5647_2x2binned_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3034, 0x1a}, + {0x3035, 0x21}, + {0x3036, 0x62}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x07}, - {0x3820, 0x41}, {0x3827, 0xec}, - {0x370c, 0x0f}, + {0x370c, 0x03}, {0x3612, 0x59}, {0x3618, 0x00}, {0x5000, 0x06}, - {0x5001, 0x01}, {0x5002, 0x41}, {0x5003, 0x08}, {0x5a00, 0x08}, @@ -136,32 +334,115 @@ {0x3a19, 0xf8}, {0x3c01, 0x80}, {0x3b07, 0x0c}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3808, 0x05}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, {0x380c, 0x07}, {0x380d, 0x68}, - {0x380e, 0x03}, - {0x380f, 0xd8}, + {0x3811, 0x0c}, + {0x3813, 0x06}, {0x3814, 0x31}, {0x3815, 0x31}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x28}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x08}, + {0x3a0e, 0x06}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x04}, + {0x4000, 0x09}, + {0x4837, 0x16}, + {0x4800, 0x24}, + {0x3503, 0x03}, + {0x3820, 0x41}, + {0x3821, 0x07}, + {0x350a, 0x00}, + {0x350b, 0x10}, + {0x3500, 0x00}, + {0x3501, 0x1a}, + {0x3502, 0xf0}, + {0x3212, 0xa0}, + {0x0100, 0x01}, +}; + +static struct regval_list ov5647_640x480_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3035, 0x11}, + {0x3036, 0x46}, + {0x303c, 0x11}, + {0x3821, 0x07}, + {0x3820, 0x41}, + {0x370c, 0x03}, + {0x3612, 0x59}, + {0x3618, 0x00}, + {0x5000, 0x06}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0xff}, + {0x3001, 0xff}, + {0x3002, 0xff}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x07}, + {0x380d, 0x3c}, + {0x3814, 0x35}, + {0x3815, 0x35}, {0x3708, 0x64}, {0x3709, 0x52}, {0x3808, 0x02}, {0x3809, 0x80}, {0x380a, 0x01}, - {0x380b, 0xE0}, - {0x3801, 0x00}, + {0x380b, 0xe0}, + {0x3800, 0x00}, + {0x3801, 0x10}, {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, - {0x3805, 0x3f}, + {0x3805, 0x2f}, {0x3806, 0x07}, - {0x3807, 0xa1}, - {0x3811, 0x08}, - {0x3813, 0x02}, + {0x3807, 0x9f}, {0x3630, 0x2e}, {0x3632, 0xe2}, {0x3633, 0x23}, {0x3634, 0x44}, - {0x3636, 0x06}, {0x3620, 0x64}, {0x3621, 0xe0}, {0x3600, 0x37}, @@ -176,11 +457,11 @@ {0x3f06, 0x10}, {0x3f01, 0x0a}, {0x3a08, 0x01}, - {0x3a09, 0x27}, + {0x3a09, 0x2e}, {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0d, 0x04}, - {0x3a0e, 0x03}, + {0x3a0b, 0xfb}, + {0x3a0d, 0x02}, + {0x3a0e, 0x01}, {0x3a0f, 0x58}, {0x3a10, 0x50}, {0x3a1b, 0x58}, @@ -190,31 +471,152 @@ {0x4001, 0x02}, {0x4004, 0x02}, {0x4000, 0x09}, - {0x4837, 0x24}, - {0x4050, 0x6e}, - {0x4051, 0x8f}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3017, 0xe0}, + {0x301c, 0xfc}, + {0x3636, 0x06}, + {0x3016, 0x08}, + {0x3827, 0xec}, + {0x3018, 0x44}, + {0x3035, 0x21}, + {0x3106, 0xf5}, + {0x3034, 0x1a}, + {0x301c, 0xf8}, + {0x4800, 0x34}, + {0x3503, 0x03}, {0x0100, 0x01}, }; -static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) +static const struct ov5647_mode ov5647_modes[] = { + /* 2592x1944 full resolution full FOV 10-bit mode. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 2592, + .height = 1944 + }, + .crop = { + .left = OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 2592, + .height = 1944 + }, + .pixel_rate = 87500000, + .hts = 2844, + .vts = 0x7b0, + .reg_list = ov5647_2592x1944_10bpp, + .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bpp) + }, + /* 1080p30 10-bit mode. Full resolution centre-cropped down to 1080p. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 1920, + .height = 1080 + }, + .crop = { + .left = 348 + OV5647_PIXEL_ARRAY_LEFT, + .top = 434 + OV5647_PIXEL_ARRAY_TOP, + .width = 1928, + .height = 1080, + }, + .pixel_rate = 81666700, + .hts = 2416, + .vts = 0x450, + .reg_list = ov5647_1080p30_10bpp, + .num_regs = ARRAY_SIZE(ov5647_1080p30_10bpp) + }, + /* 2x2 binned full FOV 10-bit mode. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 1296, + .height = 972 + }, + .crop = { + .left = OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 2592, + .height = 1944, + }, + .pixel_rate = 81666700, + .hts = 1896, + .vts = 0x59b, + .reg_list = ov5647_2x2binned_10bpp, + .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bpp) + }, + /* 10-bit VGA full FOV 60fps. 2x2 binned and subsampled down to VGA. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 640, + .height = 480 + }, + .crop = { + .left = 16 + OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 2560, + .height = 1920, + }, + .pixel_rate = 55000000, + .hts = 1852, + .vts = 0x1f8, + .reg_list = ov5647_640x480_10bpp, + .num_regs = ARRAY_SIZE(ov5647_640x480_10bpp) + }, +}; + +/* Default sensor mode is 2x2 binned 640x480 SBGGR10_1X10. */ +#define OV5647_DEFAULT_MODE (&ov5647_modes[3]) +#define OV5647_DEFAULT_FORMAT (ov5647_modes[3].format) + +static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) { + unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; + + ret = i2c_master_send(client, data, 4); + if (ret < 0) { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret; + } + + return 0; +} + +static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) +{ unsigned char data[3] = { reg >> 8, reg & 0xff, val}; struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; ret = i2c_master_send(client, data, 3); - if (ret < 0) + if (ret < 0) { dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", __func__, reg); + return ret; + } - return ret; + return 0; } static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) { - int ret; unsigned char data_w[2] = { reg >> 8, reg & 0xff }; struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; ret = i2c_master_send(client, data_w, 2); if (ret < 0) { @@ -224,15 +626,17 @@ } ret = i2c_master_recv(client, val, 1); - if (ret < 0) + if (ret < 0) { dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", __func__, reg); + return ret; + } - return ret; + return 0; } static int ov5647_write_array(struct v4l2_subdev *sd, - struct regval_list *regs, int array_size) + const struct regval_list *regs, int array_size) { int i, ret; @@ -255,161 +659,174 @@ return ret; channel_id &= ~(3 << 6); - return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6)); + + return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, + channel_id | (channel << 6)); } -static int ov5647_stream_on(struct v4l2_subdev *sd) +static int ov5647_set_mode(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = to_sensor(sd); + u8 resetval, rdval; int ret; - ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_BUS_IDLE); + ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); if (ret < 0) return ret; - ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00); - if (ret < 0) + ret = ov5647_write_array(sd, sensor->mode->reg_list, + sensor->mode->num_regs); + if (ret < 0) { + dev_err(&client->dev, "write sensor default regs error\n"); return ret; + } - return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00); -} - -static int ov5647_stream_off(struct v4l2_subdev *sd) -{ - int ret; - - ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_CLOCK_LANE_GATE - | MIPI_CTRL00_BUS_IDLE | MIPI_CTRL00_CLOCK_LANE_DISABLE); + ret = ov5647_set_virtual_channel(sd, 0); if (ret < 0) return ret; - ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f); + ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); if (ret < 0) return ret; - return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); + if (!(resetval & 0x01)) { + dev_err(&client->dev, "Device was in SW standby"); + ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); + if (ret < 0) + return ret; + } + + return 0; } -static int set_sw_standby(struct v4l2_subdev *sd, bool standby) +static int ov5647_stream_on(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = to_sensor(sd); + u8 val = MIPI_CTRL00_BUS_IDLE; int ret; - u8 rdval; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); - if (ret < 0) + ret = ov5647_set_mode(sd); + if (ret) { + dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret); return ret; + } - if (standby) - rdval &= ~0x01; - else - rdval |= 0x01; - - return ov5647_write(sd, OV5647_SW_STANDBY, rdval); -} + /* Apply customized values from user when stream starts. */ + ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler); + if (ret) + return ret; -static int __sensor_init(struct v4l2_subdev *sd) -{ - int ret; - u8 resetval, rdval; - struct i2c_client *client = v4l2_get_subdevdata(sd); + if (sensor->clock_ncont) + val |= MIPI_CTRL00_CLOCK_LANE_GATE | + MIPI_CTRL00_LINE_SYNC_ENABLE; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val); if (ret < 0) return ret; - ret = ov5647_write_array(sd, ov5647_640x480, - ARRAY_SIZE(ov5647_640x480)); - if (ret < 0) { - dev_err(&client->dev, "write sensor default regs error\n"); + ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00); + if (ret < 0) return ret; - } - ret = ov5647_set_virtual_channel(sd, 0); + return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00); +} + +static int ov5647_stream_off(struct v4l2_subdev *sd) +{ + int ret; + + ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, + MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE | + MIPI_CTRL00_CLOCK_LANE_DISABLE); if (ret < 0) return ret; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); + ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f); if (ret < 0) return ret; - if (!(resetval & 0x01)) { - dev_err(&client->dev, "Device was in SW standby"); - ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); - if (ret < 0) - return ret; - } - - /* - * stream off to make the clock lane into LP-11 state. - */ - return ov5647_stream_off(sd); + return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); } -static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) +static int ov5647_power_on(struct device *dev) { - int ret = 0; - struct ov5647 *ov5647 = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = dev_get_drvdata(dev); + int ret; - mutex_lock(&ov5647->lock); + dev_dbg(dev, "OV5647 power on\n"); - if (on && !ov5647->power_count) { - dev_dbg(&client->dev, "OV5647 power on\n"); + if (sensor->pwdn) { + gpiod_set_value_cansleep(sensor->pwdn, 0); + msleep(PWDN_ACTIVE_DELAY_MS); + } - ret = clk_prepare_enable(ov5647->xclk); - if (ret < 0) { - dev_err(&client->dev, "clk prepare enable failed\n"); - goto out; - } + ret = clk_prepare_enable(sensor->xclk); + if (ret < 0) { + dev_err(dev, "clk prepare enable failed\n"); + goto error_pwdn; + } - ret = ov5647_write_array(sd, sensor_oe_enable_regs, - ARRAY_SIZE(sensor_oe_enable_regs)); - if (ret < 0) { - clk_disable_unprepare(ov5647->xclk); - dev_err(&client->dev, - "write sensor_oe_enable_regs error\n"); - goto out; - } + ret = ov5647_write_array(&sensor->sd, sensor_oe_enable_regs, + ARRAY_SIZE(sensor_oe_enable_regs)); + if (ret < 0) { + dev_err(dev, "write sensor_oe_enable_regs error\n"); + goto error_clk_disable; + } - ret = __sensor_init(sd); - if (ret < 0) { - clk_disable_unprepare(ov5647->xclk); - dev_err(&client->dev, - "Camera not available, check Power\n"); - goto out; - } - } else if (!on && ov5647->power_count == 1) { - dev_dbg(&client->dev, "OV5647 power off\n"); + /* Stream off to coax lanes into LP-11 state. */ + ret = ov5647_stream_off(&sensor->sd); + if (ret < 0) { + dev_err(dev, "camera not available, check power\n"); + goto error_clk_disable; + } - ret = ov5647_write_array(sd, sensor_oe_disable_regs, - ARRAY_SIZE(sensor_oe_disable_regs)); + return 0; - if (ret < 0) - dev_dbg(&client->dev, "disable oe failed\n"); +error_clk_disable: + clk_disable_unprepare(sensor->xclk); +error_pwdn: + gpiod_set_value_cansleep(sensor->pwdn, 1); - ret = set_sw_standby(sd, true); + return ret; +} - if (ret < 0) - dev_dbg(&client->dev, "soft stby failed\n"); +static int ov5647_power_off(struct device *dev) +{ + struct ov5647 *sensor = dev_get_drvdata(dev); + u8 rdval; + int ret; - clk_disable_unprepare(ov5647->xclk); - } + dev_dbg(dev, "OV5647 power off\n"); - /* Update the power count. */ - ov5647->power_count += on ? 1 : -1; - WARN_ON(ov5647->power_count < 0); + ret = ov5647_write_array(&sensor->sd, sensor_oe_disable_regs, + ARRAY_SIZE(sensor_oe_disable_regs)); + if (ret < 0) + dev_dbg(dev, "disable oe failed\n"); -out: - mutex_unlock(&ov5647->lock); + /* Enter software standby */ + ret = ov5647_read(&sensor->sd, OV5647_SW_STANDBY, &rdval); + if (ret < 0) + dev_dbg(dev, "software standby failed\n"); - return ret; + rdval &= ~0x01; + ret = ov5647_write(&sensor->sd, OV5647_SW_STANDBY, rdval); + if (ret < 0) + dev_dbg(dev, "software standby failed\n"); + + clk_disable_unprepare(sensor->xclk); + gpiod_set_value_cansleep(sensor->pwdn, 1); + + return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG static int ov5647_sensor_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + struct v4l2_dbg_register *reg) { - u8 val; int ret; + u8 val; ret = ov5647_read(sd, reg->reg & 0xff, &val); if (ret < 0) @@ -422,29 +839,79 @@ } static int ov5647_sensor_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff); } #endif -/* - * Subdev core operations registration - */ +/* Subdev core operations registration */ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { - .s_power = ov5647_sensor_power, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov5647_sensor_get_register, .s_register = ov5647_sensor_set_register, #endif }; +static const struct v4l2_rect * +__ov5647_get_pad_crop(struct ov5647 *ov5647, + struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov5647->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov5647->mode->crop; + } + + return NULL; +} + static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) { - if (enable) - return ov5647_stream_on(sd); - else - return ov5647_stream_off(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = to_sensor(sd); + int ret; + + mutex_lock(&sensor->lock); + if (sensor->streaming == enable) { + mutex_unlock(&sensor->lock); + return 0; + } + + if (enable) { + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto error_unlock; + + ret = ov5647_stream_on(sd); + if (ret < 0) { + dev_err(&client->dev, "stream start failed: %d\n", ret); + goto error_pm; + } + } else { + ret = ov5647_stream_off(sd); + if (ret < 0) { + dev_err(&client->dev, "stream stop failed: %d\n", ret); + goto error_pm; + } + pm_runtime_put(&client->dev); + } + + sensor->streaming = enable; + mutex_unlock(&sensor->lock); + + return 0; + +error_pm: + pm_runtime_put(&client->dev); +error_unlock: + mutex_unlock(&sensor->lock); + + return ret; } static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { @@ -452,19 +919,150 @@ }; static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SBGGR8_1X8; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; return 0; } +static int ov5647_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct v4l2_mbus_framefmt *fmt; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10 || + fse->index >= ARRAY_SIZE(ov5647_modes)) + return -EINVAL; + + fmt = &ov5647_modes[fse->index].format; + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + + return 0; +} + +static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + const struct v4l2_mbus_framefmt *sensor_format; + struct ov5647 *sensor = to_sensor(sd); + + mutex_lock(&sensor->lock); + switch (format->which) { + case V4L2_SUBDEV_FORMAT_TRY: + sensor_format = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + break; + default: + sensor_format = &sensor->mode->format; + break; + } + + *fmt = *sensor_format; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct ov5647 *sensor = to_sensor(sd); + const struct ov5647_mode *mode; + + mode = v4l2_find_nearest_size(ov5647_modes, ARRAY_SIZE(ov5647_modes), + format.width, format.height, + fmt->width, fmt->height); + + /* Update the sensor mode and apply at it at streamon time. */ + mutex_lock(&sensor->lock); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = mode->format; + } else { + int exposure_max, exposure_def; + int hblank, vblank; + + sensor->mode = mode; + __v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate, + mode->pixel_rate, 1, mode->pixel_rate); + + hblank = mode->hts - mode->format.width; + __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1, + hblank); + + vblank = mode->vts - mode->format.height; + __v4l2_ctrl_modify_range(sensor->vblank, OV5647_VBLANK_MIN, + OV5647_VTS_MAX - mode->format.height, + 1, vblank); + __v4l2_ctrl_s_ctrl(sensor->vblank, vblank); + + exposure_max = mode->vts - 4; + exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, sensor->exposure->step, + exposure_def); + } + *fmt = mode->format; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov5647_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct ov5647 *sensor = to_sensor(sd); + + mutex_lock(&sensor->lock); + sel->r = *__ov5647_get_pad_crop(sensor, sd_state, sel->pad, + sel->which); + mutex_unlock(&sensor->lock); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV5647_NATIVE_WIDTH; + sel->r.height = OV5647_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = OV5647_PIXEL_ARRAY_TOP; + sel->r.left = OV5647_PIXEL_ARRAY_LEFT; + sel->r.width = OV5647_PIXEL_ARRAY_WIDTH; + sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { - .enum_mbus_code = ov5647_enum_mbus_code, + .enum_mbus_code = ov5647_enum_mbus_code, + .enum_frame_size = ov5647_enum_frame_size, + .set_fmt = ov5647_set_pad_fmt, + .get_fmt = ov5647_get_pad_fmt, + .get_selection = ov5647_get_selection, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { @@ -475,9 +1073,9 @@ static int ov5647_detect(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 read; int ret; - struct i2c_client *client = v4l2_get_subdevdata(sd); ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); if (ret < 0) @@ -507,21 +1105,16 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); struct v4l2_rect *crop = - v4l2_subdev_get_try_crop(sd, fh->pad, 0); + v4l2_subdev_get_try_crop(sd, fh->state, 0); + + crop->left = OV5647_PIXEL_ARRAY_LEFT; + crop->top = OV5647_PIXEL_ARRAY_TOP; + crop->width = OV5647_PIXEL_ARRAY_WIDTH; + crop->height = OV5647_PIXEL_ARRAY_HEIGHT; - crop->left = OV5647_COLUMN_START_DEF; - crop->top = OV5647_ROW_START_DEF; - crop->width = OV5647_WINDOW_WIDTH_DEF; - crop->height = OV5647_WINDOW_HEIGHT_DEF; - - format->code = MEDIA_BUS_FMT_SBGGR8_1X8; - - format->width = OV5647_WINDOW_WIDTH_DEF; - format->height = OV5647_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + *format = OV5647_DEFAULT_FORMAT; return 0; } @@ -530,11 +1123,220 @@ .open = ov5647_open, }; -static int ov5647_parse_dt(struct device_node *np) +static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val) { - struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; - struct device_node *ep; + return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0); +} + +static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val) +{ + int ret; + u8 reg; + + /* Non-zero turns on AGC by clearing bit 1.*/ + ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_AEC_AGC, val ? reg & ~BIT(1) + : reg | BIT(1)); +} + +static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val) +{ + int ret; + u8 reg; + + /* + * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by + * clearing bit 0. + */ + ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_AEC_AGC, + val == V4L2_EXPOSURE_MANUAL ? reg | BIT(0) + : reg & ~BIT(0)); +} + +static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val) +{ + int ret; + + /* 10 bits of gain, 2 in the high register. */ + ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff); +} + +static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val) +{ + int ret; + /* + * Sensor has 20 bits, but the bottom 4 bits are fractions of a line + * which we leave as zero (and don't receive in "val"). + */ + ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf); + if (ret) + return ret; + + ret = ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4); +} + +static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5647 *sensor = container_of(ctrl->handler, + struct ov5647, ctrls); + struct v4l2_subdev *sd = &sensor->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + + /* v4l2_ctrl_lock() locks our own mutex */ + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = sensor->mode->format.height + ctrl->val - 4; + exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, sensor->exposure->step, + exposure_def); + } + + /* + * If the device is not powered up do not apply any controls + * to H/W at this time. Instead the controls will be restored + * at s_stream(1) time. + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = ov5647_s_auto_white_balance(sd, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + ret = ov5647_s_autogain(sd, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = ov5647_s_exposure_auto(sd, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov5647_s_analogue_gain(sd, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = ov5647_s_exposure(sd, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = ov5647_write16(sd, OV5647_REG_VTS_HI, + sensor->mode->format.height + ctrl->val); + break; + + /* Read-only, but we adjust it based on mode. */ + case V4L2_CID_PIXEL_RATE: + case V4L2_CID_HBLANK: + /* Read-only, but we adjust it based on mode. */ + break; + + default: + dev_info(&client->dev, + "Control (id:0x%x, val:0x%x) not supported\n", + ctrl->id, ctrl->val); + return -EINVAL; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { + .s_ctrl = ov5647_s_ctrl, +}; + +static int ov5647_init_controls(struct ov5647 *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + int hblank, exposure_max, exposure_def; + + v4l2_ctrl_handler_init(&sensor->ctrls, 8); + + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 0); + + v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, + 0, V4L2_EXPOSURE_MANUAL); + + exposure_max = sensor->mode->vts - 4; + exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); + sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_EXPOSURE, + OV5647_EXPOSURE_MIN, + exposure_max, OV5647_EXPOSURE_STEP, + exposure_def); + + /* min: 16 = 1.0x; max (10 bits); default: 32 = 2.0x. */ + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32); + + /* By default, PIXEL_RATE is read only, but it does change per mode */ + sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_PIXEL_RATE, + sensor->mode->pixel_rate, + sensor->mode->pixel_rate, 1, + sensor->mode->pixel_rate); + + /* By default, HBLANK is read only, but it does change per mode. */ + hblank = sensor->mode->hts - sensor->mode->format.width; + sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, 1, + hblank); + + sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_VBLANK, OV5647_VBLANK_MIN, + OV5647_VTS_MAX - + sensor->mode->format.height, 1, + sensor->mode->vts - + sensor->mode->format.height); + + if (sensor->ctrls.error) + goto handler_free; + + sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->sd.ctrl_handler = &sensor->ctrls; + + return 0; + +handler_free: + dev_err(&client->dev, "%s Controls initialization failed (%d)\n", + __func__, sensor->ctrls.error); + v4l2_ctrl_handler_free(&sensor->ctrls); + + return sensor->ctrls.error; +} + +static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device_node *ep; int ret; ep = of_graph_get_next_endpoint(np, NULL); @@ -542,33 +1344,39 @@ return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); + if (ret) + goto out; + sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + +out: of_node_put(ep); + return ret; } static int ov5647_probe(struct i2c_client *client) { + struct device_node *np = client->dev.of_node; struct device *dev = &client->dev; struct ov5647 *sensor; - int ret; struct v4l2_subdev *sd; - struct device_node *np = client->dev.of_node; u32 xclk_freq; + int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; if (IS_ENABLED(CONFIG_OF) && np) { - ret = ov5647_parse_dt(np); + ret = ov5647_parse_dt(sensor, np); if (ret) { dev_err(dev, "DT parsing error: %d\n", ret); return ret; } } - /* get system clock (xclk) */ sensor->xclk = devm_clk_get(dev, NULL); if (IS_ERR(sensor->xclk)) { dev_err(dev, "could not get xclk"); @@ -581,52 +1389,87 @@ return -EINVAL; } + /* Request the power down GPIO asserted. */ + sensor->pwdn = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->pwdn)) { + dev_err(dev, "Failed to get 'pwdn' gpio\n"); + return -EINVAL; + } + mutex_init(&sensor->lock); + sensor->mode = OV5647_DEFAULT_MODE; + + ret = ov5647_init_controls(sensor); + if (ret) + goto mutex_destroy; + sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); - sensor->sd.internal_ops = &ov5647_subdev_internal_ops; - sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &ov5647_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) - goto mutex_remove; + goto ctrl_handler_free; + + ret = ov5647_power_on(dev); + if (ret) + goto entity_cleanup; ret = ov5647_detect(sd); if (ret < 0) - goto error; + goto power_off; ret = v4l2_async_register_subdev(sd); if (ret < 0) - goto error; + goto power_off; + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); + return 0; -error: + +power_off: + ov5647_power_off(dev); +entity_cleanup: media_entity_cleanup(&sd->entity); -mutex_remove: +ctrl_handler_free: + v4l2_ctrl_handler_free(&sensor->ctrls); +mutex_destroy: mutex_destroy(&sensor->lock); + return ret; } static int ov5647_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov5647 *ov5647 = to_state(sd); + struct ov5647 *sensor = to_sensor(sd); - v4l2_async_unregister_subdev(&ov5647->sd); - media_entity_cleanup(&ov5647->sd.entity); + v4l2_async_unregister_subdev(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls); v4l2_device_unregister_subdev(sd); - mutex_destroy(&ov5647->lock); + pm_runtime_disable(&client->dev); + mutex_destroy(&sensor->lock); return 0; } +static const struct dev_pm_ops ov5647_pm_ops = { + SET_RUNTIME_PM_OPS(ov5647_power_off, ov5647_power_on, NULL) +}; + static const struct i2c_device_id ov5647_id[] = { { "ov5647", 0 }, - { } + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov5647_id); @@ -641,7 +1484,8 @@ static struct i2c_driver ov5647_driver = { .driver = { .of_match_table = of_match_ptr(ov5647_of_match), - .name = SENSOR_NAME, + .name = "ov5647", + .pm = &ov5647_pm_ops, }, .probe_new = ov5647_probe, .remove = ov5647_remove, diff -Naur --no-dereference a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c --- a/drivers/media/i2c/ov5670.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov5670.c 2022-01-06 12:45:53.814318107 -0500 @@ -1937,7 +1937,7 @@ { struct ov5670 *ov5670 = to_ov5670(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); mutex_lock(&ov5670->mutex); @@ -2153,7 +2153,7 @@ } static int ov5670_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /* Only one bayer order GRBG is supported */ @@ -2166,7 +2166,7 @@ } static int ov5670_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -2193,11 +2193,12 @@ } static int ov5670_do_get_pad_format(struct ov5670 *ov5670, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5670->sd, cfg, + fmt->format = *v4l2_subdev_get_try_format(&ov5670->sd, + sd_state, fmt->pad); else ov5670_update_pad_format(ov5670->cur_mode, fmt); @@ -2206,21 +2207,21 @@ } static int ov5670_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov5670 *ov5670 = to_ov5670(sd); int ret; mutex_lock(&ov5670->mutex); - ret = ov5670_do_get_pad_format(ov5670, cfg, fmt); + ret = ov5670_do_get_pad_format(ov5670, sd_state, fmt); mutex_unlock(&ov5670->mutex); return ret; } static int ov5670_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov5670 *ov5670 = to_ov5670(sd); @@ -2238,7 +2239,7 @@ fmt->format.width, fmt->format.height); ov5670_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; } else { ov5670->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); diff -Naur --no-dereference a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c --- a/drivers/media/i2c/ov5675.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov5675.c 2022-01-06 12:45:53.814318107 -0500 @@ -926,7 +926,7 @@ } static int ov5675_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov5675 *ov5675 = to_ov5675(sd); @@ -941,7 +941,7 @@ mutex_lock(&ov5675->mutex); ov5675_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; } else { ov5675->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5675->link_freq, mode->link_freq_index); @@ -967,14 +967,15 @@ } static int ov5675_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov5675 *ov5675 = to_ov5675(sd); mutex_lock(&ov5675->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5675->sd, cfg, + fmt->format = *v4l2_subdev_get_try_format(&ov5675->sd, + sd_state, fmt->pad); else ov5675_update_pad_format(ov5675->cur_mode, &fmt->format); @@ -985,7 +986,7 @@ } static int ov5675_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -997,7 +998,7 @@ } static int ov5675_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -1020,7 +1021,7 @@ mutex_lock(&ov5675->mutex); ov5675_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->pad, 0)); + v4l2_subdev_get_try_format(sd, fh->state, 0)); mutex_unlock(&ov5675->mutex); return 0; diff -Naur --no-dereference a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c --- a/drivers/media/i2c/ov5695.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov5695.c 2022-01-06 12:45:53.814318107 -0500 @@ -806,7 +806,7 @@ } static int ov5695_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov5695 *ov5695 = to_ov5695(sd); @@ -822,7 +822,7 @@ fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; #endif } else { ov5695->cur_mode = mode; @@ -841,7 +841,7 @@ } static int ov5695_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov5695 *ov5695 = to_ov5695(sd); @@ -850,7 +850,8 @@ mutex_lock(&ov5695->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); #else mutex_unlock(&ov5695->mutex); return -EINVAL; @@ -867,7 +868,7 @@ } static int ov5695_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -878,7 +879,7 @@ } static int ov5695_enum_frame_sizes(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -1056,7 +1057,7 @@ { struct ov5695 *ov5695 = to_ov5695(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); const struct ov5695_mode *def_mode = &supported_modes[0]; mutex_lock(&ov5695->mutex); diff -Naur --no-dereference a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c --- a/drivers/media/i2c/ov6650.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov6650.c 2022-01-06 12:45:53.814318107 -0500 @@ -467,7 +467,7 @@ } static int ov6650_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -492,7 +492,7 @@ } static int ov6650_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -535,7 +535,7 @@ } static int ov6650_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -550,9 +550,9 @@ /* update media bus format code and frame size */ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf->width = cfg->try_fmt.width; - mf->height = cfg->try_fmt.height; - mf->code = cfg->try_fmt.code; + mf->width = sd_state->pads->try_fmt.width; + mf->height = sd_state->pads->try_fmt.height; + mf->code = sd_state->pads->try_fmt.code; } else { mf->width = priv->rect.width >> priv->half_scale; @@ -668,7 +668,7 @@ } static int ov6650_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -701,15 +701,15 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { /* store media bus format code and frame size in pad config */ - cfg->try_fmt.width = mf->width; - cfg->try_fmt.height = mf->height; - cfg->try_fmt.code = mf->code; + sd_state->pads->try_fmt.width = mf->width; + sd_state->pads->try_fmt.height = mf->height; + sd_state->pads->try_fmt.code = mf->code; /* return default mbus frame format updated with pad config */ *mf = ov6650_def_fmt; - mf->width = cfg->try_fmt.width; - mf->height = cfg->try_fmt.height; - mf->code = cfg->try_fmt.code; + mf->width = sd_state->pads->try_fmt.width; + mf->height = sd_state->pads->try_fmt.height; + mf->code = sd_state->pads->try_fmt.code; } else { /* apply new media bus format code and frame size */ @@ -728,7 +728,7 @@ } static int ov6650_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(ov6650_codes)) diff -Naur --no-dereference a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c --- a/drivers/media/i2c/ov7251.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov7251.c 2022-01-06 12:45:53.814318107 -0500 @@ -898,7 +898,7 @@ }; static int ov7251_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -910,7 +910,7 @@ } static int ov7251_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->code != MEDIA_BUS_FMT_Y10_1X10) @@ -928,7 +928,7 @@ } static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { unsigned int index = fie->index; @@ -950,13 +950,13 @@ static struct v4l2_mbus_framefmt * __ov7251_get_pad_format(struct ov7251 *ov7251, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov7251->sd, cfg, pad); + return v4l2_subdev_get_try_format(&ov7251->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->fmt; default: @@ -965,13 +965,14 @@ } static int ov7251_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7251 *ov7251 = to_ov7251(sd); mutex_lock(&ov7251->lock); - format->format = *__ov7251_get_pad_format(ov7251, cfg, format->pad, + format->format = *__ov7251_get_pad_format(ov7251, sd_state, + format->pad, format->which); mutex_unlock(&ov7251->lock); @@ -979,12 +980,13 @@ } static struct v4l2_rect * -__ov7251_get_pad_crop(struct ov7251 *ov7251, struct v4l2_subdev_pad_config *cfg, +__ov7251_get_pad_crop(struct ov7251 *ov7251, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov7251->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&ov7251->sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->crop; default: @@ -1027,7 +1029,7 @@ } static int ov7251_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7251 *ov7251 = to_ov7251(sd); @@ -1038,7 +1040,8 @@ mutex_lock(&ov7251->lock); - __crop = __ov7251_get_pad_crop(ov7251, cfg, format->pad, format->which); + __crop = __ov7251_get_pad_crop(ov7251, sd_state, format->pad, + format->which); new_mode = v4l2_find_nearest_size(ov7251_mode_info_data, ARRAY_SIZE(ov7251_mode_info_data), @@ -1077,7 +1080,7 @@ ov7251->current_mode = new_mode; } - __format = __ov7251_get_pad_format(ov7251, cfg, format->pad, + __format = __ov7251_get_pad_format(ov7251, sd_state, format->pad, format->which); __format->width = __crop->width; __format->height = __crop->height; @@ -1098,24 +1101,24 @@ } static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { - .which = cfg ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, .format = { .width = 640, .height = 480 } }; - ov7251_set_format(subdev, cfg, &fmt); + ov7251_set_format(subdev, sd_state, &fmt); return 0; } static int ov7251_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct ov7251 *ov7251 = to_ov7251(sd); @@ -1124,7 +1127,7 @@ return -EINVAL; mutex_lock(&ov7251->lock); - sel->r = *__ov7251_get_pad_crop(ov7251, cfg, sel->pad, + sel->r = *__ov7251_get_pad_crop(ov7251, sd_state, sel->pad, sel->which); mutex_unlock(&ov7251->lock); diff -Naur --no-dereference a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c --- a/drivers/media/i2c/ov7670.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov7670.c 2022-01-06 12:45:53.814318107 -0500 @@ -946,7 +946,7 @@ static int ov7670_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= N_OV7670_FMTS) @@ -1091,7 +1091,7 @@ * Set a format. */ static int ov7670_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7670_info *info = to_state(sd); @@ -1108,7 +1108,8 @@ if (ret) return ret; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, + format->pad); *mbus_fmt = format->format; #endif return 0; @@ -1130,7 +1131,7 @@ } static int ov7670_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7670_info *info = to_state(sd); @@ -1140,7 +1141,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mbus_fmt; return 0; #else @@ -1188,7 +1189,7 @@ static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; static int ov7670_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct ov7670_info *info = to_state(sd); @@ -1227,7 +1228,7 @@ * Frame size enumeration */ static int ov7670_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct ov7670_info *info = to_state(sd); @@ -1708,7 +1709,7 @@ static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); ov7670_get_default_format(sd, format); diff -Naur --no-dereference a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c --- a/drivers/media/i2c/ov772x.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov772x.c 2022-01-06 12:45:53.814318107 -0500 @@ -1136,7 +1136,7 @@ } static int ov772x_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct ov772x_priv *priv = to_ov772x(sd); @@ -1158,7 +1158,7 @@ } static int ov772x_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -1177,7 +1177,7 @@ } static int ov772x_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov772x_priv *priv = to_ov772x(sd); @@ -1201,7 +1201,7 @@ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } @@ -1299,7 +1299,7 @@ }; static int ov772x_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals)) @@ -1317,7 +1317,7 @@ } static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) diff -Naur --no-dereference a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c --- a/drivers/media/i2c/ov7740.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov7740.c 2022-01-06 12:45:53.814318107 -0500 @@ -709,7 +709,7 @@ #define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats) static int ov7740_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= N_OV7740_FMTS) @@ -721,7 +721,7 @@ } static int ov7740_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { if (fie->pad) @@ -740,7 +740,7 @@ } static int ov7740_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->pad) @@ -803,7 +803,7 @@ } static int ov7740_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); @@ -825,7 +825,8 @@ if (ret) goto error; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, + format->pad); *mbus_fmt = format->format; #endif mutex_unlock(&ov7740->mutex); @@ -848,7 +849,7 @@ } static int ov7740_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); @@ -860,7 +861,7 @@ mutex_lock(&ov7740->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mbus_fmt; ret = 0; #else @@ -905,7 +906,7 @@ { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); mutex_lock(&ov7740->mutex); ov7740_get_default_format(sd, format); diff -Naur --no-dereference a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c --- a/drivers/media/i2c/ov8856.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov8856.c 2022-01-06 12:45:53.814318107 -0500 @@ -1457,7 +1457,7 @@ } static int ov8856_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov8856 *ov8856 = to_ov8856(sd); @@ -1472,7 +1472,7 @@ mutex_lock(&ov8856->mutex); ov8856_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; } else { ov8856->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); @@ -1498,14 +1498,15 @@ } static int ov8856_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov8856 *ov8856 = to_ov8856(sd); mutex_lock(&ov8856->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, cfg, + fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, + sd_state, fmt->pad); else ov8856_update_pad_format(ov8856->cur_mode, &fmt->format); @@ -1516,7 +1517,7 @@ } static int ov8856_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /* Only one bayer order GRBG is supported */ @@ -1529,7 +1530,7 @@ } static int ov8856_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -1552,7 +1553,7 @@ mutex_lock(&ov8856->mutex); ov8856_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->pad, 0)); + v4l2_subdev_get_try_format(sd, fh->state, 0)); mutex_unlock(&ov8856->mutex); return 0; diff -Naur --no-dereference a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c --- a/drivers/media/i2c/ov9640.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov9640.c 2022-01-06 12:45:53.814318107 -0500 @@ -519,7 +519,7 @@ } static int ov9640_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -547,13 +547,13 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ov9640_s_fmt(sd, mf); - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) @@ -565,7 +565,7 @@ } static int ov9640_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) diff -Naur --no-dereference a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c --- a/drivers/media/i2c/ov9650.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/ov9650.c 2022-01-06 12:45:53.814318107 -0500 @@ -1070,7 +1070,7 @@ } static int ov965x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(ov965x_formats)) @@ -1081,7 +1081,7 @@ } static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int i = ARRAY_SIZE(ov965x_formats); @@ -1167,14 +1167,14 @@ } static int ov965x_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ov965x *ov965x = to_ov965x(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); fmt->format = *mf; return 0; } @@ -1212,7 +1212,7 @@ } static int ov965x_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { unsigned int index = ARRAY_SIZE(ov965x_formats); @@ -1234,8 +1234,9 @@ mutex_lock(&ov965x->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (cfg) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + if (sd_state) { + mf = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); *mf = fmt->format; } } else { @@ -1364,7 +1365,7 @@ static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + v4l2_subdev_get_try_format(sd, fh->state, 0); ov965x_get_default_format(mf); return 0; diff -Naur --no-dereference a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c --- a/drivers/media/i2c/rdacm20.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/rdacm20.c 2022-01-06 12:45:53.814318107 -0500 @@ -403,7 +403,7 @@ } static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index > 0) @@ -415,7 +415,7 @@ } static int rdacm20_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; diff -Naur --no-dereference a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c --- a/drivers/media/i2c/rj54n1cb0c.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/rj54n1cb0c.c 2022-01-06 12:45:53.814318107 -0500 @@ -488,7 +488,7 @@ } static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) @@ -541,7 +541,7 @@ s32 *out_w, s32 *out_h); static int rj54n1_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -578,7 +578,7 @@ } static int rj54n1_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -603,7 +603,7 @@ } static int rj54n1_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -973,7 +973,7 @@ } static int rj54n1_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -1009,7 +1009,7 @@ &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } diff -Naur --no-dereference a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c 2022-01-06 12:45:53.814318107 -0500 @@ -817,7 +817,7 @@ } static void s5c73m3_oif_try_format(struct s5c73m3 *state, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { @@ -844,8 +844,8 @@ *fs = state->oif_pix_size[RES_ISP]; else *fs = s5c73m3_find_frame_size( - v4l2_subdev_get_try_format(sd, cfg, - OIF_ISP_PAD), + v4l2_subdev_get_try_format(sd, sd_state, + OIF_ISP_PAD), RES_ISP); break; } @@ -854,7 +854,7 @@ } static void s5c73m3_try_format(struct s5c73m3 *state, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { @@ -946,7 +946,7 @@ } static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); @@ -984,7 +984,7 @@ } static int s5c73m3_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); @@ -992,7 +992,8 @@ u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); return 0; } @@ -1018,7 +1019,7 @@ } static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); @@ -1026,7 +1027,8 @@ u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); return 0; } @@ -1056,7 +1058,7 @@ } static int s5c73m3_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { const struct s5c73m3_frame_size *frame_size = NULL; @@ -1066,10 +1068,10 @@ mutex_lock(&state->lock); - s5c73m3_try_format(state, cfg, fmt, &frame_size); + s5c73m3_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; } else { switch (fmt->pad) { @@ -1095,7 +1097,7 @@ } static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { const struct s5c73m3_frame_size *frame_size = NULL; @@ -1105,13 +1107,14 @@ mutex_lock(&state->lock); - s5c73m3_oif_try_format(state, cfg, fmt, &frame_size); + s5c73m3_oif_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; if (fmt->pad == OIF_ISP_PAD) { - mf = v4l2_subdev_get_try_format(sd, cfg, OIF_SOURCE_PAD); + mf = v4l2_subdev_get_try_format(sd, sd_state, + OIF_SOURCE_PAD); mf->width = fmt->format.width; mf->height = fmt->format.height; } @@ -1183,7 +1186,7 @@ } static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { static const int codes[] = { @@ -1199,7 +1202,7 @@ } static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { int ret; @@ -1214,7 +1217,7 @@ } static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int idx; @@ -1241,7 +1244,7 @@ } static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); @@ -1259,7 +1262,7 @@ if (fse->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, cfg, + mf = v4l2_subdev_get_try_format(sd, sd_state, OIF_ISP_PAD); w = mf->width; @@ -1315,11 +1318,11 @@ { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->pad, S5C73M3_ISP_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->pad, S5C73M3_JPEG_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); @@ -1330,15 +1333,15 @@ { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_ISP_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_JPEG_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_SOURCE_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_SOURCE_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); return 0; diff -Naur --no-dereference a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c --- a/drivers/media/i2c/s5k4ecgx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/s5k4ecgx.c 2022-01-06 12:45:53.814318107 -0500 @@ -525,7 +525,7 @@ } static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k4ecgx_formats)) @@ -535,15 +535,16 @@ return 0; } -static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { struct s5k4ecgx *priv = to_s5k4ecgx(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (cfg) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + if (sd_state) { + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); fmt->format = *mf; } return 0; @@ -575,7 +576,8 @@ return &s5k4ecgx_formats[i]; } -static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5k4ecgx *priv = to_s5k4ecgx(sd); @@ -590,8 +592,8 @@ fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (cfg) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + if (sd_state) { + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); *mf = fmt->format; } return 0; @@ -686,7 +688,9 @@ */ static int s5k4ecgx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, + fh->state, + 0); mf->width = s5k4ecgx_prev_sizes[0].size.width; mf->height = s5k4ecgx_prev_sizes[0].size.height; diff -Naur --no-dereference a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c --- a/drivers/media/i2c/s5k5baf.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/s5k5baf.c 2022-01-06 12:45:53.814318107 -0500 @@ -1180,7 +1180,7 @@ * V4L2 subdev pad level and video operations */ static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || @@ -1199,7 +1199,7 @@ } static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad == PAD_CIS) { @@ -1217,7 +1217,7 @@ } static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int i; @@ -1274,15 +1274,16 @@ return pixfmt; } -static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int s5k5baf_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { struct s5k5baf *state = to_s5k5baf(sd); const struct s5k5baf_pixfmt *pixfmt; struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1304,8 +1305,9 @@ return 0; } -static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int s5k5baf_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; struct s5k5baf *state = to_s5k5baf(sd); @@ -1315,7 +1317,7 @@ mf->field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = *mf; + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = *mf; return 0; } @@ -1367,7 +1369,7 @@ } static int s5k5baf_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { enum selection_rect rtype; @@ -1387,9 +1389,11 @@ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { if (rtype == R_COMPOSE) - sel->r = *v4l2_subdev_get_try_compose(sd, cfg, sel->pad); + sel->r = *v4l2_subdev_get_try_compose(sd, sd_state, + sel->pad); else - sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, + sel->pad); return 0; } @@ -1458,7 +1462,7 @@ } static int s5k5baf_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { static enum selection_rect rtype; @@ -1479,9 +1483,12 @@ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { rects = (struct v4l2_rect * []) { &s5k5baf_cis_rect, - v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS), - v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS), - v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT) + v4l2_subdev_get_try_crop(sd, sd_state, + PAD_CIS), + v4l2_subdev_get_try_compose(sd, sd_state, + PAD_CIS), + v4l2_subdev_get_try_crop(sd, sd_state, + PAD_OUT) }; s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); return 0; @@ -1699,22 +1706,22 @@ { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_CIS); + mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_CIS); s5k5baf_try_cis_format(mf); if (s5k5baf_is_cis_subdev(sd)) return 0; - mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_OUT); + mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_OUT); mf->colorspace = s5k5baf_formats[0].colorspace; mf->code = s5k5baf_formats[0].code; mf->width = s5k5baf_cis_rect.width; mf->height = s5k5baf_cis_rect.height; mf->field = V4L2_FIELD_NONE; - *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_compose(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_OUT) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_crop(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_compose(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_crop(sd, fh->state, PAD_OUT) = s5k5baf_cis_rect; return 0; } diff -Naur --no-dereference a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c --- a/drivers/media/i2c/s5k6a3.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/s5k6a3.c 2022-01-06 12:45:53.814318107 -0500 @@ -99,7 +99,7 @@ } static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k6a3_formats)) @@ -123,17 +123,18 @@ } static struct v4l2_mbus_framefmt *__s5k6a3_get_format( - struct s5k6a3 *sensor, struct v4l2_subdev_pad_config *cfg, + struct s5k6a3 *sensor, struct v4l2_subdev_state *sd_state, u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return cfg ? v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad) : NULL; + return sd_state ? v4l2_subdev_get_try_format(&sensor->subdev, + sd_state, pad) : NULL; return &sensor->format; } static int s5k6a3_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); @@ -141,7 +142,7 @@ s5k6a3_try_format(&fmt->format); - mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); + mf = __s5k6a3_get_format(sensor, sd_state, fmt->pad, fmt->which); if (mf) { mutex_lock(&sensor->lock); *mf = fmt->format; @@ -151,13 +152,13 @@ } static int s5k6a3_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); struct v4l2_mbus_framefmt *mf; - mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); + mf = __s5k6a3_get_format(sensor, sd_state, fmt->pad, fmt->which); mutex_lock(&sensor->lock); fmt->format = *mf; @@ -173,7 +174,9 @@ static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, + fh->state, + 0); *format = s5k6a3_formats[0]; format->width = S5K6A3_DEFAULT_WIDTH; diff -Naur --no-dereference a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c --- a/drivers/media/i2c/s5k6aa.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/s5k6aa.c 2022-01-06 12:45:53.814318107 -0500 @@ -997,7 +997,7 @@ * V4L2 subdev pad level and video operations */ static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1024,7 +1024,7 @@ } static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k6aa_formats)) @@ -1035,7 +1035,7 @@ } static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int i = ARRAY_SIZE(s5k6aa_formats); @@ -1057,14 +1057,15 @@ } static struct v4l2_rect * -__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_pad_config *cfg, +__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_ACTIVE) return &s5k6aa->ccd_rect; WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY); - return v4l2_subdev_get_try_crop(&s5k6aa->sd, cfg, 0); + return v4l2_subdev_get_try_crop(&s5k6aa->sd, sd_state, 0); } static void s5k6aa_try_format(struct s5k6aa *s5k6aa, @@ -1088,7 +1089,8 @@ mf->field = V4L2_FIELD_NONE; } -static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int s5k6aa_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1097,7 +1099,7 @@ memset(fmt->reserved, 0, sizeof(fmt->reserved)); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); fmt->format = *mf; return 0; } @@ -1109,7 +1111,8 @@ return 0; } -static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int s5k6aa_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1122,8 +1125,8 @@ s5k6aa_try_format(s5k6aa, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); - crop = v4l2_subdev_get_try_crop(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); } else { if (s5k6aa->streaming) { ret = -EBUSY; @@ -1163,7 +1166,7 @@ } static int s5k6aa_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1175,7 +1178,7 @@ memset(sel->reserved, 0, sizeof(sel->reserved)); mutex_lock(&s5k6aa->lock); - rect = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which); + rect = __s5k6aa_get_crop_rect(s5k6aa, sd_state, sel->which); sel->r = *rect; mutex_unlock(&s5k6aa->lock); @@ -1186,7 +1189,7 @@ } static int s5k6aa_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1198,13 +1201,13 @@ return -EINVAL; mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, sd_state, sel->which); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { mf = &s5k6aa->preset->mbus_fmt; s5k6aa->apply_crop = 1; } else { - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); } v4l_bound_align_image(&sel->r.width, mf->width, S5K6AA_WIN_WIDTH_MAX, 1, @@ -1425,8 +1428,10 @@ */ static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, + fh->state, + 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); format->colorspace = s5k6aa_formats[0].colorspace; format->code = s5k6aa_formats[0].code; diff -Naur --no-dereference a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c --- a/drivers/media/i2c/saa6752hs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/saa6752hs.c 2022-01-06 12:45:53.814318107 -0500 @@ -543,7 +543,7 @@ } static int saa6752hs_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *f = &format->format; @@ -563,7 +563,7 @@ } static int saa6752hs_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *f = &format->format; @@ -595,7 +595,7 @@ f->colorspace = V4L2_COLORSPACE_SMPTE170M; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *f; + sd_state->pads->try_fmt = *f; return 0; } diff -Naur --no-dereference a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c --- a/drivers/media/i2c/saa7115.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/saa7115.c 2022-01-06 12:45:53.814318107 -0500 @@ -1167,7 +1167,7 @@ } static int saa711x_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; diff -Naur --no-dereference a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c --- a/drivers/media/i2c/saa717x.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/saa717x.c 2022-01-06 12:45:53.814318107 -0500 @@ -980,7 +980,7 @@ #endif static int saa717x_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; diff -Naur --no-dereference a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c --- a/drivers/media/i2c/smiapp/smiapp-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/smiapp/smiapp-core.c 2022-01-06 12:45:53.814318107 -0500 @@ -1573,7 +1573,7 @@ } static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(subdev); @@ -1627,13 +1627,13 @@ } static int __smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(subdev, cfg, + fmt->format = *v4l2_subdev_get_try_format(subdev, sd_state, fmt->pad); } else { struct v4l2_rect *r; @@ -1653,21 +1653,21 @@ } static int smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); int rval; mutex_lock(&sensor->mutex); - rval = __smiapp_get_format(subdev, cfg, fmt); + rval = __smiapp_get_format(subdev, sd_state, fmt); mutex_unlock(&sensor->mutex); return rval; } static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_rect **crops, struct v4l2_rect **comps, int which) { @@ -1683,12 +1683,14 @@ } else { if (crops) { for (i = 0; i < subdev->entity.num_pads; i++) { - crops[i] = v4l2_subdev_get_try_crop(subdev, cfg, i); + crops[i] = v4l2_subdev_get_try_crop(subdev, + sd_state, + i); BUG_ON(!crops[i]); } } if (comps) { - *comps = v4l2_subdev_get_try_compose(subdev, cfg, + *comps = v4l2_subdev_get_try_compose(subdev, sd_state, SMIAPP_PAD_SINK); BUG_ON(!*comps); } @@ -1697,14 +1699,14 @@ /* Changes require propagation only on sink pad. */ static void smiapp_propagate(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, int which, + struct v4l2_subdev_state *sd_state, int which, int target) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - smiapp_get_crop_compose(subdev, cfg, crops, &comp, which); + smiapp_get_crop_compose(subdev, sd_state, crops, &comp, which); switch (target) { case V4L2_SEL_TGT_CROP: @@ -1745,7 +1747,7 @@ } static int smiapp_set_format_source(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -1756,7 +1758,7 @@ unsigned int i; int rval; - rval = __smiapp_get_format(subdev, cfg, fmt); + rval = __smiapp_get_format(subdev, sd_state, fmt); if (rval) return rval; @@ -1798,7 +1800,7 @@ } static int smiapp_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -1810,7 +1812,7 @@ if (fmt->pad == ssd->source_pad) { int rval; - rval = smiapp_set_format_source(subdev, cfg, fmt); + rval = smiapp_set_format_source(subdev, sd_state, fmt); mutex_unlock(&sensor->mutex); @@ -1832,7 +1834,7 @@ SMIA_LIM(sensor, MIN_Y_OUTPUT_SIZE), SMIA_LIM(sensor, MAX_Y_OUTPUT_SIZE)); - smiapp_get_crop_compose(subdev, cfg, crops, NULL, fmt->which); + smiapp_get_crop_compose(subdev, sd_state, crops, NULL, fmt->which); crops[ssd->sink_pad]->left = 0; crops[ssd->sink_pad]->top = 0; @@ -1840,7 +1842,7 @@ crops[ssd->sink_pad]->height = fmt->format.height; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; - smiapp_propagate(subdev, cfg, fmt->which, + smiapp_propagate(subdev, sd_state, fmt->which, V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1893,7 +1895,7 @@ } static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel, struct v4l2_rect **crops, struct v4l2_rect *comp) @@ -1941,7 +1943,7 @@ * result. */ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel, struct v4l2_rect **crops, struct v4l2_rect *comp) @@ -2057,25 +2059,25 @@ } /* We're only called on source pads. This function sets scaling. */ static int smiapp_set_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which); + smiapp_get_crop_compose(subdev, sd_state, crops, &comp, sel->which); sel->r.top = 0; sel->r.left = 0; if (ssd == sensor->binner) - smiapp_set_compose_binner(subdev, cfg, sel, crops, comp); + smiapp_set_compose_binner(subdev, sd_state, sel, crops, comp); else - smiapp_set_compose_scaler(subdev, cfg, sel, crops, comp); + smiapp_set_compose_scaler(subdev, sd_state, sel, crops, comp); *comp = sel->r; - smiapp_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_COMPOSE); + smiapp_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return smiapp_pll_blanking_update(sensor); @@ -2127,7 +2129,7 @@ } static int smiapp_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2135,7 +2137,7 @@ struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; struct v4l2_rect _r; - smiapp_get_crop_compose(subdev, cfg, crops, NULL, sel->which); + smiapp_get_crop_compose(subdev, sd_state, crops, NULL, sel->which); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { if (sel->pad == ssd->sink_pad) @@ -2146,14 +2148,18 @@ if (sel->pad == ssd->sink_pad) { _r.left = 0; _r.top = 0; - _r.width = v4l2_subdev_get_try_format(subdev, cfg, sel->pad) + _r.width = v4l2_subdev_get_try_format(subdev, + sd_state, + sel->pad) ->width; - _r.height = v4l2_subdev_get_try_format(subdev, cfg, sel->pad) + _r.height = v4l2_subdev_get_try_format(subdev, + sd_state, + sel->pad) ->height; src_size = &_r; } else { src_size = v4l2_subdev_get_try_compose( - subdev, cfg, ssd->sink_pad); + subdev, sd_state, ssd->sink_pad); } } @@ -2171,7 +2177,7 @@ *crops[sel->pad] = sel->r; if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) - smiapp_propagate(subdev, cfg, sel->which, + smiapp_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_CROP); return 0; @@ -2187,7 +2193,7 @@ } static int __smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2200,13 +2206,14 @@ if (ret) return ret; - smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which); + smiapp_get_crop_compose(subdev, sd_state, crops, &comp, sel->which); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { sink_fmt = ssd->sink_fmt; } else { struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(subdev, cfg, ssd->sink_pad); + v4l2_subdev_get_try_format(subdev, sd_state, + ssd->sink_pad); sink_fmt.left = 0; sink_fmt.top = 0; @@ -2237,20 +2244,20 @@ } static int smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); int rval; mutex_lock(&sensor->mutex); - rval = __smiapp_get_selection(subdev, cfg, sel); + rval = __smiapp_get_selection(subdev, sd_state, sel); mutex_unlock(&sensor->mutex); return rval; } static int smiapp_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2276,10 +2283,10 @@ switch (sel->target) { case V4L2_SEL_TGT_CROP: - ret = smiapp_set_crop(subdev, cfg, sel); + ret = smiapp_set_crop(subdev, sd_state, sel); break; case V4L2_SEL_TGT_COMPOSE: - ret = smiapp_set_compose(subdev, cfg, sel); + ret = smiapp_set_compose(subdev, sd_state, sel); break; default: ret = -EINVAL; @@ -2635,9 +2642,9 @@ for (i = 0; i < ssd->npads; i++) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, i); + v4l2_subdev_get_try_format(sd, fh->state, i); struct v4l2_rect *try_crop = - v4l2_subdev_get_try_crop(sd, fh->pad, i); + v4l2_subdev_get_try_crop(sd, fh->state, i); struct v4l2_rect *try_comp; smiapp_get_native_size(ssd, try_crop); @@ -2650,7 +2657,7 @@ if (ssd != sensor->pixel_array) continue; - try_comp = v4l2_subdev_get_try_compose(sd, fh->pad, i); + try_comp = v4l2_subdev_get_try_compose(sd, fh->state, i); *try_comp = *try_crop; } diff -Naur --no-dereference a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c --- a/drivers/media/i2c/sr030pc30.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/sr030pc30.c 2022-01-06 12:45:53.814318107 -0500 @@ -468,7 +468,7 @@ } static int sr030pc30_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (!code || code->pad || @@ -480,7 +480,7 @@ } static int sr030pc30_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf; @@ -525,7 +525,7 @@ /* Return nearest media bus frame format. */ static int sr030pc30_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct sr030pc30_info *info = sd ? to_sr030pc30(sd) : NULL; @@ -541,7 +541,7 @@ fmt = try_fmt(sd, mf); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } diff -Naur --no-dereference a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c --- a/drivers/media/i2c/st-mipid02.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/st-mipid02.c 2022-01-06 12:45:53.814318107 -0500 @@ -644,7 +644,7 @@ } static int mipid02_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct mipid02_dev *bridge = to_mipid02_dev(sd); @@ -671,7 +671,7 @@ } static int mipid02_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mbus_fmt = &format->format; @@ -688,7 +688,8 @@ return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&bridge->sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(&bridge->sd, sd_state, + format->pad); else fmt = &bridge->fmt; @@ -705,7 +706,7 @@ } static void mipid02_set_fmt_source(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mipid02_dev *bridge = to_mipid02_dev(sd); @@ -719,11 +720,11 @@ if (format->which != V4L2_SUBDEV_FORMAT_TRY) return; - *v4l2_subdev_get_try_format(sd, cfg, format->pad) = format->format; + *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = format->format; } static void mipid02_set_fmt_sink(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mipid02_dev *bridge = to_mipid02_dev(sd); @@ -732,7 +733,7 @@ format->format.code = get_fmt_code(format->format.code); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); else fmt = &bridge->fmt; @@ -740,7 +741,7 @@ } static int mipid02_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct mipid02_dev *bridge = to_mipid02_dev(sd); @@ -763,9 +764,9 @@ } if (format->pad == MIPID02_SOURCE) - mipid02_set_fmt_source(sd, cfg, format); + mipid02_set_fmt_source(sd, sd_state, format); else - mipid02_set_fmt_sink(sd, cfg, format); + mipid02_set_fmt_sink(sd, sd_state, format); error: mutex_unlock(&bridge->lock); diff -Naur --no-dereference a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c --- a/drivers/media/i2c/tc358743.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/tc358743.c 2022-01-06 12:45:53.814318107 -0500 @@ -1649,7 +1649,7 @@ /* --------------- PAD OPS --------------- */ static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { switch (code->index) { @@ -1666,7 +1666,7 @@ } static int tc358743_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct tc358743_state *state = to_state(sd); @@ -1702,13 +1702,13 @@ } static int tc358743_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct tc358743_state *state = to_state(sd); u32 code = format->format.code; /* is overwritten by get_fmt */ - int ret = tc358743_get_fmt(sd, cfg, format); + int ret = tc358743_get_fmt(sd, sd_state, format); format->format.code = code; diff -Naur --no-dereference a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c --- a/drivers/media/i2c/tda1997x.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/tda1997x.c 2022-01-06 12:45:53.814318107 -0500 @@ -1719,19 +1719,19 @@ */ static int tda1997x_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct tda1997x_state *state = to_state(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); mf->code = state->mbus_codes[0]; return 0; } static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct tda1997x_state *state = to_state(sd); @@ -1763,7 +1763,7 @@ } static int tda1997x_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct tda1997x_state *state = to_state(sd); @@ -1776,7 +1776,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); format->format.code = fmt->code; } else format->format.code = state->mbus_code; @@ -1785,7 +1785,7 @@ } static int tda1997x_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct tda1997x_state *state = to_state(sd); @@ -1810,7 +1810,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); *fmt = format->format; } else { int ret = tda1997x_setup_format(state, format->format.code); diff -Naur --no-dereference a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c --- a/drivers/media/i2c/tvp514x.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/tvp514x.c 2022-01-06 12:45:53.814318107 -0500 @@ -859,7 +859,7 @@ * Enumertaes mbus codes supported */ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { u32 pad = code->pad; @@ -886,7 +886,7 @@ * Retrieves pad format which is active or tried based on requirement */ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct tvp514x_decoder *decoder = to_decoder(sd); @@ -918,7 +918,7 @@ * Set pad format for the output pad */ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct tvp514x_decoder *decoder = to_decoder(sd); diff -Naur --no-dereference a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c --- a/drivers/media/i2c/tvp5150.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/tvp5150.c 2022-01-06 12:45:53.814318107 -0500 @@ -1027,7 +1027,7 @@ static struct v4l2_rect * tvp5150_get_pad_crop(struct tvp5150 *decoder, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { @@ -1035,7 +1035,7 @@ return &decoder->rect; case V4L2_SUBDEV_FORMAT_TRY: #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - return v4l2_subdev_get_try_crop(&decoder->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&decoder->sd, sd_state, pad); #else return ERR_PTR(-EINVAL); #endif @@ -1045,7 +1045,7 @@ } static int tvp5150_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *f; @@ -1104,7 +1104,7 @@ } static int tvp5150_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct tvp5150 *decoder = to_tvp5150(sd); @@ -1138,7 +1138,7 @@ sel->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - crop = tvp5150_get_pad_crop(decoder, cfg, sel->pad, sel->which); + crop = tvp5150_get_pad_crop(decoder, sd_state, sel->pad, sel->which); if (IS_ERR(crop)) return PTR_ERR(crop); @@ -1156,7 +1156,7 @@ } static int tvp5150_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); @@ -1180,7 +1180,7 @@ sel->r.height = TVP5150_V_MAX_OTHERS; return 0; case V4L2_SEL_TGT_CROP: - crop = tvp5150_get_pad_crop(decoder, cfg, sel->pad, + crop = tvp5150_get_pad_crop(decoder, sd_state, sel->pad, sel->which); if (IS_ERR(crop)) return PTR_ERR(crop); @@ -1208,7 +1208,7 @@ V4L2 subdev pad ops ****************************************************************************/ static int tvp5150_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; @@ -1229,7 +1229,7 @@ } static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index) @@ -1240,7 +1240,7 @@ } static int tvp5150_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct tvp5150 *decoder = to_tvp5150(sd); diff -Naur --no-dereference a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c --- a/drivers/media/i2c/tvp7002.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/tvp7002.c 2022-01-06 12:45:53.814318107 -0500 @@ -797,7 +797,8 @@ * Enumerate supported digital video formats for pad. */ static int -tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +tvp7002_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /* Check requested format index is within range */ @@ -818,7 +819,8 @@ * get video format for pad. */ static int -tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +tvp7002_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct tvp7002 *tvp7002 = to_tvp7002(sd); @@ -841,10 +843,11 @@ * set video format for pad. */ static int -tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +tvp7002_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - return tvp7002_get_pad_format(sd, cfg, fmt); + return tvp7002_get_pad_format(sd, sd_state, fmt); } /* V4L2 core operation handlers */ diff -Naur --no-dereference a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c --- a/drivers/media/i2c/tw9910.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/tw9910.c 2022-01-06 12:45:53.814318107 -0500 @@ -720,7 +720,7 @@ } static int tw9910_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -746,7 +746,7 @@ } static int tw9910_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -797,7 +797,7 @@ } static int tw9910_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; @@ -829,7 +829,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return tw9910_s_fmt(sd, mf); - cfg->try_fmt = *mf; + sd_state->pads->try_fmt = *mf; return 0; } @@ -886,7 +886,7 @@ }; static int tw9910_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index) diff -Naur --no-dereference a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c --- a/drivers/media/i2c/vs6624.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/i2c/vs6624.c 2022-01-06 12:45:53.814318107 -0500 @@ -546,7 +546,7 @@ } static int vs6624_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(vs6624_formats)) @@ -557,7 +557,7 @@ } static int vs6624_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -587,7 +587,7 @@ fmt->colorspace = vs6624_formats[index].colorspace; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; return 0; } @@ -637,7 +637,7 @@ } static int vs6624_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct vs6624 *sensor = to_vs6624(sd); diff -Naur --no-dereference a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c --- a/drivers/media/mc/mc-device.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/mc/mc-device.c 2022-01-06 12:45:53.814318107 -0500 @@ -581,7 +581,7 @@ struct media_device *mdev = entity->graph_obj.mdev; struct media_link *link, *tmp; struct media_interface *intf; - unsigned int i; + struct media_pad *iter; ida_free(&mdev->entity_internal_idx, entity->internal_idx); @@ -597,8 +597,8 @@ __media_entity_remove_links(entity); /* Remove all pads that belong to this entity */ - for (i = 0; i < entity->num_pads; i++) - media_gobj_destroy(&entity->pads[i].graph_obj); + media_entity_for_each_pad(entity, iter) + media_gobj_destroy(&iter->graph_obj); /* Remove the entity */ media_gobj_destroy(&entity->graph_obj); @@ -617,7 +617,7 @@ struct media_entity *entity) { struct media_entity_notify *notify, *next; - unsigned int i; + struct media_pad *iter; int ret; if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN || @@ -646,9 +646,8 @@ media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj); /* Initialize objects at the pads */ - for (i = 0; i < entity->num_pads; i++) - media_gobj_create(mdev, MEDIA_GRAPH_PAD, - &entity->pads[i].graph_obj); + media_entity_for_each_pad(entity, iter) + media_gobj_create(mdev, MEDIA_GRAPH_PAD, &iter->graph_obj); /* invoke entity_notify callbacks */ list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) diff -Naur --no-dereference a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c --- a/drivers/media/mc/mc-entity.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/mc/mc-entity.c 2022-01-06 12:45:53.814318107 -0500 @@ -198,7 +198,8 @@ struct media_pad *pads) { struct media_device *mdev = entity->graph_obj.mdev; - unsigned int i; + struct media_pad *iter; + unsigned int i = 0; if (num_pads >= MEDIA_ENTITY_MAX_PADS) return -E2BIG; @@ -209,12 +210,12 @@ if (mdev) mutex_lock(&mdev->graph_mutex); - for (i = 0; i < num_pads; i++) { - pads[i].entity = entity; - pads[i].index = i; + media_entity_for_each_pad(entity, iter) { + iter->entity = entity; + iter->index = i++; if (mdev) media_gobj_create(mdev, MEDIA_GRAPH_PAD, - &entity->pads[i].graph_obj); + &iter->graph_obj); } if (mdev) @@ -228,40 +229,63 @@ * Graph traversal */ -static struct media_entity * -media_entity_other(struct media_entity *entity, struct media_link *link) +bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) { - if (link->source->entity == entity) - return link->sink->entity; - else - return link->source->entity; + if (pad0 >= entity->num_pads || pad1 >= entity->num_pads) + return false; + + if (pad0 == pad1) + return true; + + if (!entity->ops || !entity->ops->has_route) + return true; + + if (entity->pads[pad1].index < entity->pads[pad0].index) + swap(pad0, pad1); + + return entity->ops->has_route(entity, pad0, pad1); +} +EXPORT_SYMBOL_GPL(media_entity_has_route); + +struct media_pad *__media_entity_next_routed_pad(struct media_pad *root, + struct media_pad *iter) +{ + struct media_entity *entity = root->entity; + + for (; iter < &entity->pads[entity->num_pads]; iter++) { + if (media_entity_has_route(entity, root->index, iter->index)) + return iter; + } + + return NULL; } +EXPORT_SYMBOL_GPL(__media_entity_next_routed_pad); /* push an entity to traversal stack */ -static void stack_push(struct media_graph *graph, - struct media_entity *entity) +static void stack_push(struct media_graph *graph, struct media_pad *pad) { if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { WARN_ON(1); return; } graph->top++; - graph->stack[graph->top].link = entity->links.next; - graph->stack[graph->top].entity = entity; + graph->stack[graph->top].link = pad->entity->links.next; + graph->stack[graph->top].pad = pad; } -static struct media_entity *stack_pop(struct media_graph *graph) +static struct media_pad *stack_pop(struct media_graph *graph) { - struct media_entity *entity; + struct media_pad *pad; - entity = graph->stack[graph->top].entity; + pad = graph->stack[graph->top].pad; graph->top--; - return entity; + return pad; } #define link_top(en) ((en)->stack[(en)->top].link) -#define stack_top(en) ((en)->stack[(en)->top].entity) +#define stack_top(en) ((en)->stack[(en)->top].pad) /** * media_graph_walk_init - Allocate resources for graph walk @@ -291,60 +315,81 @@ } EXPORT_SYMBOL_GPL(media_graph_walk_cleanup); -void media_graph_walk_start(struct media_graph *graph, - struct media_entity *entity) +void media_graph_walk_start(struct media_graph *graph, struct media_pad *pad) { media_entity_enum_zero(&graph->ent_enum); - media_entity_enum_set(&graph->ent_enum, entity); + media_entity_enum_set(&graph->ent_enum, pad->entity); graph->top = 0; - graph->stack[graph->top].entity = NULL; - stack_push(graph, entity); - dev_dbg(entity->graph_obj.mdev->dev, - "begin graph walk at '%s'\n", entity->name); + graph->stack[graph->top].pad = NULL; + stack_push(graph, pad); + dev_dbg(pad->graph_obj.mdev->dev, + "begin graph walk at '%s':%u\n", pad->entity->name, pad->index); } EXPORT_SYMBOL_GPL(media_graph_walk_start); static void media_graph_walk_iter(struct media_graph *graph) { - struct media_entity *entity = stack_top(graph); + struct media_pad *pad = stack_top(graph); struct media_link *link; - struct media_entity *next; + struct media_pad *remote; + struct media_pad *local; link = list_entry(link_top(graph), typeof(*link), list); /* The link is not enabled so we do not follow. */ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { link_top(graph) = link_top(graph)->next; - dev_dbg(entity->graph_obj.mdev->dev, + dev_dbg(pad->graph_obj.mdev->dev, "walk: skipping disabled link '%s':%u -> '%s':%u\n", link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index); return; } - /* Get the entity in the other end of the link . */ - next = media_entity_other(entity, link); + /* + * Get the local pad, the remote pad and the entity at the other + * end of the link. + */ + if (link->source->entity == pad->entity) { + remote = link->sink; + local = link->source; + } else { + remote = link->source; + local = link->sink; + } + + /* + * Are the local pad and the pad we came from connected + * internally in the entity ? + */ + if (!media_entity_has_route(pad->entity, pad->index, local->index)) { + link_top(graph) = link_top(graph)->next; + dev_dbg(pad->graph_obj.mdev->dev, + "walk: skipping \"%s\":%u -> %u (no route)\n", + pad->entity->name, pad->index, local->index); + return; + } /* Has the entity already been visited? */ - if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { + if (media_entity_enum_test_and_set(&graph->ent_enum, remote->entity)) { link_top(graph) = link_top(graph)->next; - dev_dbg(entity->graph_obj.mdev->dev, + dev_dbg(pad->graph_obj.mdev->dev, "walk: skipping entity '%s' (already seen)\n", - next->name); + remote->entity->name); return; } /* Push the new entity to stack and start over. */ link_top(graph) = link_top(graph)->next; - stack_push(graph, next); - dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n", - next->name); + stack_push(graph, remote); + dev_dbg(remote->graph_obj.mdev->dev, "walk: pushing '%s':%u on stack\n", + remote->entity->name, remote->index); } -struct media_entity *media_graph_walk_next(struct media_graph *graph) +struct media_pad *media_graph_walk_next(struct media_graph *graph) { - struct media_entity *entity; + struct media_pad *pad; if (stack_top(graph) == NULL) return NULL; @@ -354,14 +399,14 @@ * top of the stack until no more entities on the level can be * found. */ - while (link_top(graph) != &stack_top(graph)->links) + while (link_top(graph) != &stack_top(graph)->entity->links) media_graph_walk_iter(graph); - entity = stack_pop(graph); - dev_dbg(entity->graph_obj.mdev->dev, - "walk: returning entity '%s'\n", entity->name); + pad = stack_pop(graph); + dev_dbg(pad->graph_obj.mdev->dev, + "walk: returning pad '%s':%u\n", pad->entity->name, pad->index); - return entity; + return pad; } EXPORT_SYMBOL_GPL(media_graph_walk_next); @@ -404,12 +449,12 @@ * Pipeline management */ -__must_check int __media_pipeline_start(struct media_entity *entity, +__must_check int __media_pipeline_start(struct media_pad *pad, struct media_pipeline *pipe) { - struct media_device *mdev = entity->graph_obj.mdev; + struct media_device *mdev = pad->graph_obj.mdev; struct media_graph *graph = &pipe->graph; - struct media_entity *entity_err = entity; + struct media_pad *pad_err = pad; struct media_link *link; int ret; @@ -419,26 +464,32 @@ goto error_graph_walk_start; } - media_graph_walk_start(&pipe->graph, entity); + media_graph_walk_start(&pipe->graph, pad); + + while ((pad = media_graph_walk_next(graph))) { + struct media_entity *entity = pad->entity; + bool skip_validation = pad->pipe != NULL; + struct media_pad *iter; - while ((entity = media_graph_walk_next(graph))) { DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); - entity->stream_count++; - - if (entity->pipe && entity->pipe != pipe) { - pr_err("Pipe active for %s. Can't start for %s\n", - entity->name, - entity_err->name); - ret = -EBUSY; - goto error; + media_entity_for_each_routed_pad(pad, iter) { + if (iter->pipe && iter->pipe != pipe) { + pr_err("Pipe active for %s. Can't start for %s\n", + entity->name, iter->entity->name); + ret = -EBUSY; + } else { + iter->pipe = pipe; + } + iter->stream_count++; } - entity->pipe = pipe; + if (ret) + goto error; - /* Already streaming --- no need to check. */ - if (entity->stream_count > 1) + /* Already part of the pipeline, skip validation. */ + if (skip_validation) continue; if (!entity->ops || !entity->ops->link_validate) @@ -448,26 +499,32 @@ bitmap_fill(has_no_links, entity->num_pads); list_for_each_entry(link, &entity->links, list) { - struct media_pad *pad = link->sink->entity == entity - ? link->sink : link->source; + struct media_pad *other_pad = + link->sink->entity == entity ? + link->sink : link->source; + + /* Ignore pads to which there is no route. */ + if (!media_entity_has_route(entity, pad->index, + other_pad->index)) + continue; /* Mark that a pad is connected by a link. */ - bitmap_clear(has_no_links, pad->index, 1); + bitmap_clear(has_no_links, other_pad->index, 1); /* * Pads that either do not need to connect or * are connected through an enabled link are * fine. */ - if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) || + if (!(other_pad->flags & MEDIA_PAD_FL_MUST_CONNECT) || link->flags & MEDIA_LNK_FL_ENABLED) - bitmap_set(active, pad->index, 1); + bitmap_set(active, other_pad->index, 1); /* * Link validation will only take place for * sink ends of the link that are enabled. */ - if (link->sink != pad || + if (link->sink != other_pad || !(link->flags & MEDIA_LNK_FL_ENABLED)) continue; @@ -503,21 +560,25 @@ * Link validation on graph failed. We revert what we did and * return the error. */ - media_graph_walk_start(graph, entity_err); + media_graph_walk_start(graph, pad_err); - while ((entity_err = media_graph_walk_next(graph))) { - /* Sanity check for negative stream_count */ - if (!WARN_ON_ONCE(entity_err->stream_count <= 0)) { - entity_err->stream_count--; - if (entity_err->stream_count == 0) - entity_err->pipe = NULL; + while ((pad_err = media_graph_walk_next(graph))) { + struct media_pad *iter; + + media_entity_for_each_routed_pad(pad_err, iter) { + /* Sanity check for negative stream_count */ + if (!WARN_ON_ONCE(iter->stream_count <= 0)) { + --iter->stream_count; + if (iter->stream_count == 0) + iter->pipe = NULL; + } } /* * We haven't increased stream_count further than this * so we quit here. */ - if (entity_err == entity) + if (pad_err->entity == pad->entity) break; } @@ -529,23 +590,23 @@ } EXPORT_SYMBOL_GPL(__media_pipeline_start); -__must_check int media_pipeline_start(struct media_entity *entity, +__must_check int media_pipeline_start(struct media_pad *pad, struct media_pipeline *pipe) { - struct media_device *mdev = entity->graph_obj.mdev; + struct media_device *mdev = pad->graph_obj.mdev; int ret; mutex_lock(&mdev->graph_mutex); - ret = __media_pipeline_start(entity, pipe); + ret = __media_pipeline_start(pad, pipe); mutex_unlock(&mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_pipeline_start); -void __media_pipeline_stop(struct media_entity *entity) +void __media_pipeline_stop(struct media_pad *pad) { - struct media_graph *graph = &entity->pipe->graph; - struct media_pipeline *pipe = entity->pipe; + struct media_pipeline *pipe = pad->pipe; + struct media_graph *graph = &pipe->graph; /* * If the following check fails, the driver has performed an @@ -554,14 +615,18 @@ if (WARN_ON(!pipe)) return; - media_graph_walk_start(graph, entity); + media_graph_walk_start(graph, pad); + + while ((pad = media_graph_walk_next(graph))) { + struct media_pad *iter; - while ((entity = media_graph_walk_next(graph))) { - /* Sanity check for negative stream_count */ - if (!WARN_ON_ONCE(entity->stream_count <= 0)) { - entity->stream_count--; - if (entity->stream_count == 0) - entity->pipe = NULL; + media_entity_for_each_routed_pad(pad, iter) { + /* Sanity check for negative stream_count */ + if (!WARN_ON_ONCE(iter->stream_count <= 0)) { + iter->stream_count--; + if (iter->stream_count == 0) + iter->pipe = NULL; + } } } @@ -571,12 +636,12 @@ } EXPORT_SYMBOL_GPL(__media_pipeline_stop); -void media_pipeline_stop(struct media_entity *entity) +void media_pipeline_stop(struct media_pad *pad) { - struct media_device *mdev = entity->graph_obj.mdev; + struct media_device *mdev = pad->graph_obj.mdev; mutex_lock(&mdev->graph_mutex); - __media_pipeline_stop(entity); + __media_pipeline_stop(pad); mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_pipeline_stop); @@ -831,7 +896,7 @@ { const u32 mask = MEDIA_LNK_FL_ENABLED; struct media_device *mdev; - struct media_entity *source, *sink; + struct media_pad *source, *sink; int ret = -EBUSY; if (link == NULL) @@ -847,8 +912,8 @@ if (link->flags == flags) return 0; - source = link->source->entity; - sink = link->sink->entity; + source = link->source; + sink = link->sink; if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) && (source->stream_count || sink->stream_count)) diff -Naur --no-dereference a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c --- a/drivers/media/pci/cx18/cx18-av-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/pci/cx18/cx18-av-core.c 2022-01-06 12:45:53.814318107 -0500 @@ -930,7 +930,7 @@ } static int cx18_av_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; diff -Naur --no-dereference a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c 2022-01-06 12:45:53.814318107 -0500 @@ -33,6 +33,7 @@ u32 mbus_code; u32 fourcc; u8 mipicode; + u8 bpp; }; /* @@ -46,18 +47,22 @@ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SGRBG10, .mipicode = 0x2b, + .bpp = 10, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SGBRG10, .mipicode = 0x2b, + .bpp = 10, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SBGGR10, .mipicode = 0x2b, + .bpp = 10, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SRGGB10, .mipicode = 0x2b, + .bpp = 10, }, }; @@ -288,35 +293,20 @@ /* Calculate the the delay value for termination enable of clock lane HS Rx */ static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q, - struct cio2_csi2_timing *timing) + struct cio2_csi2_timing *timing, + unsigned int bpp, unsigned int lanes) { struct device *dev = &cio2->pci_dev->dev; - struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; - struct v4l2_ctrl *link_freq; s64 freq; - int r; if (!q->sensor) return -ENODEV; - link_freq = v4l2_ctrl_find(q->sensor->ctrl_handler, V4L2_CID_LINK_FREQ); - if (!link_freq) { - dev_err(dev, "failed to find LINK_FREQ\n"); - return -EPIPE; - } - - qm.index = v4l2_ctrl_g_ctrl(link_freq); - r = v4l2_querymenu(q->sensor->ctrl_handler, &qm); - if (r) { - dev_err(dev, "failed to get menu item\n"); - return r; - } - - if (!qm.value) { - dev_err(dev, "error invalid link_freq\n"); - return -EINVAL; + freq = v4l2_get_link_freq(q->sensor->ctrl_handler, bpp, lanes); + if (freq < 0) { + dev_err(dev, "error %lld, invalid link_freq\n", freq); + return freq; } - freq = qm.value; timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A, CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B, @@ -364,7 +354,7 @@ lanes = q->csi2.lanes; - r = cio2_csi2_calc_timing(cio2, q, &timing); + r = cio2_csi2_calc_timing(cio2, q, &timing, fmt->bpp, lanes); if (r) return r; @@ -1211,11 +1201,11 @@ }; /* Initialize try_fmt */ - format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SINK); + format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SINK); *format = fmt_default; /* same as sink */ - format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SOURCE); + format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SOURCE); *format = fmt_default; return 0; @@ -1229,7 +1219,7 @@ * return -EINVAL or zero on success */ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); @@ -1237,7 +1227,8 @@ mutex_lock(&q->subdev_lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); else fmt->format = q->subdev_fmt; @@ -1254,7 +1245,7 @@ * return -EINVAL or zero on success */ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); @@ -1267,10 +1258,10 @@ * source always propagates from sink */ if (fmt->pad == CIO2_PAD_SOURCE) - return cio2_subdev_get_fmt(sd, cfg, fmt); + return cio2_subdev_get_fmt(sd, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mbus = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mbus = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); else mbus = &q->subdev_fmt; @@ -1296,7 +1287,7 @@ } static int cio2_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(formats)) diff -Naur --no-dereference a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c --- a/drivers/media/pci/saa7134/saa7134-empress.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/pci/saa7134/saa7134-empress.c 2022-01-06 12:45:53.814318107 -0500 @@ -138,12 +138,15 @@ { struct saa7134_dev *dev = video_drvdata(file); struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - saa_call_all(dev, pad, set_fmt, &pad_cfg, &format); + saa_call_all(dev, pad, set_fmt, &pad_state, &format); v4l2_fill_pix_format(&f->fmt.pix, &format.format); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; diff -Naur --no-dereference a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c --- a/drivers/media/platform/atmel/atmel-isc-base.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/atmel/atmel-isc-base.c 2022-01-06 12:45:53.814318107 -0500 @@ -1224,7 +1224,7 @@ } static void isc_try_fse(struct isc_device *isc, - struct v4l2_subdev_pad_config *pad_cfg) + struct v4l2_subdev_state *sd_state) { int ret; struct v4l2_subdev_frame_size_enum fse = {}; @@ -1240,17 +1240,17 @@ fse.which = V4L2_SUBDEV_FORMAT_TRY; ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - pad_cfg, &fse); + sd_state, &fse); /* * Attempt to obtain format size from subdev. If not available, * just use the maximum ISC can receive. */ if (ret) { - pad_cfg->try_crop.width = ISC_MAX_SUPPORT_WIDTH; - pad_cfg->try_crop.height = ISC_MAX_SUPPORT_HEIGHT; + sd_state->pads->try_crop.width = ISC_MAX_SUPPORT_WIDTH; + sd_state->pads->try_crop.height = ISC_MAX_SUPPORT_HEIGHT; } else { - pad_cfg->try_crop.width = fse.max_width; - pad_cfg->try_crop.height = fse.max_height; + sd_state->pads->try_crop.width = fse.max_width; + sd_state->pads->try_crop.height = fse.max_height; } } @@ -1261,6 +1261,9 @@ struct isc_format *sd_fmt = NULL, *direct_fmt = NULL; struct v4l2_pix_format *pixfmt = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg = {}; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1358,11 +1361,11 @@ goto isc_try_fmt_err; /* Obtain frame sizes if possible to have crop requirements ready */ - isc_try_fse(isc, &pad_cfg); + isc_try_fse(isc, &pad_state); v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code); ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, - &pad_cfg, &format); + &pad_state, &format); if (ret < 0) goto isc_try_fmt_subdev_err; diff -Naur --no-dereference a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c --- a/drivers/media/platform/atmel/atmel-isi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/atmel/atmel-isi.c 2022-01-06 12:45:53.814318107 -0500 @@ -556,7 +556,7 @@ } static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, - struct v4l2_subdev_pad_config *pad_cfg) + struct v4l2_subdev_state *sd_state) { int ret; struct v4l2_subdev_frame_size_enum fse = { @@ -565,17 +565,17 @@ }; ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, - pad_cfg, &fse); + sd_state, &fse); /* * Attempt to obtain format size from subdev. If not available, * just use the maximum ISI can receive. */ if (ret) { - pad_cfg->try_crop.width = MAX_SUPPORT_WIDTH; - pad_cfg->try_crop.height = MAX_SUPPORT_HEIGHT; + sd_state->pads->try_crop.width = MAX_SUPPORT_WIDTH; + sd_state->pads->try_crop.height = MAX_SUPPORT_HEIGHT; } else { - pad_cfg->try_crop.width = fse.max_width; - pad_cfg->try_crop.height = fse.max_height; + sd_state->pads->try_crop.width = fse.max_width; + sd_state->pads->try_crop.height = fse.max_height; } } @@ -585,6 +585,9 @@ const struct isi_format *isi_fmt; struct v4l2_pix_format *pixfmt = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg = {}; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -602,10 +605,10 @@ v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code); - isi_try_fse(isi, isi_fmt, &pad_cfg); + isi_try_fse(isi, isi_fmt, &pad_state); ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, - &pad_cfg, &format); + &pad_state, &format); if (ret < 0) return ret; diff -Naur --no-dereference a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c --- a/drivers/media/platform/cadence/cdns-csi2rx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/cadence/cdns-csi2rx.c 2022-01-06 12:45:53.814318107 -0500 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -30,14 +31,25 @@ #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) +#define CSI2RX_DPHY_LANE_CTRL_REG 0x40 +#define CSI2RX_DPHY_CL_RST BIT(16) +#define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) +#define CSI2RX_DPHY_CL_EN BIT(4) +#define CSI2RX_DPHY_DL_EN(i) BIT(i) + #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) +#define CSI2RX_STREAM_CTRL_SOFT_RST BIT(4) +#define CSI2RX_STREAM_CTRL_STOP BIT(1) #define CSI2RX_STREAM_CTRL_START BIT(0) +#define CSI2RX_STREAM_STATUS_REG(n) (CSI2RX_STREAM_BASE(n) + 0x004) +#define CSI2RX_STREAM_STATUS_RDY BIT(31) + #define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008) -#define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31) #define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16) +#define CSI2RX_STREAM_DATA_CFG_VC_ALL 0 #define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c) #define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8) @@ -54,6 +66,11 @@ CSI2RX_PAD_MAX, }; +struct csi2rx_fmt { + u32 code; + u8 bpp; +}; + struct csi2rx_priv { struct device *dev; unsigned int count; @@ -86,6 +103,102 @@ int source_pad; }; +static const struct csi2rx_fmt formats[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, +}; + +static u8 csi2rx_get_bpp(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return formats[i].bpp; + } + + return 0; +} + +static int csi2rx_get_frame_desc_from_source(struct csi2rx_priv *csi2rx, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *remote_pad; + + remote_pad = media_entity_remote_pad(&csi2rx->pads[CSI2RX_PAD_SINK]); + if (!remote_pad) { + dev_err(csi2rx->dev, "No remote pad found for sink\n"); + return -ENODEV; + } + + return v4l2_subdev_call(csi2rx->source_subdev, pad, get_frame_desc, + remote_pad->index, fd); +} + +static s64 csi2rx_get_link_freq(struct csi2rx_priv *csi2rx) +{ + struct v4l2_mbus_frame_desc fd; + bool has_fd = true; + int ret; + u8 bpp; + + /* First check if the source is sending a multiplexed stream. */ + ret = csi2rx_get_frame_desc_from_source(csi2rx, &fd); + if (ret == -ENOIOCTLCMD) + /* + * Assume not multiplexed if source can't send frame descriptor. + */ + has_fd = false; + else if (ret) + return ret; + + if (has_fd && fd.num_entries > 1) { + /* + * With multistream input we don't have bpp, and cannot use + * V4L2_CID_PIXEL_RATE. Passing 0 as bpp causes + * v4l2_get_link_freq() to return an error if it falls back to + * V4L2_CID_PIXEL_RATE. + */ + bpp = 0; + } else if (has_fd && fd.num_entries == 1) { + bpp = csi2rx_get_bpp(fd.entry[0].pixelcode); + if (!bpp) + return -EINVAL; + } else { + struct v4l2_subdev_format sd_fmt; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + sd_fmt.stream = 0; + + ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, + NULL, &sd_fmt); + if (ret) + return ret; + + bpp = csi2rx_get_bpp(sd_fmt.format.code); + if (!bpp) + return -EINVAL; + } + + return v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler, bpp, + 2 * csi2rx->num_lanes); +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { @@ -94,12 +207,70 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) { + int i; + writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT, csi2rx->base + CSI2RX_SOFT_RESET_REG); udelay(10); writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); + + /* Reset individual streams. */ + for (i = 0; i < csi2rx->max_streams; i++) { + writel(CSI2RX_STREAM_CTRL_SOFT_RST, + csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + usleep_range(10, 20); + writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + } +} + +static int csi2rx_configure_external_dphy(struct csi2rx_priv *csi2rx) +{ + union phy_configure_opts opts = { }; + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; + s64 link_freq; + int ret; + bool got_pm = true; + + link_freq = csi2rx_get_link_freq(csi2rx); + if (link_freq < 0) + return link_freq; + + /* link_freq already takes bpp and num_lanes into account. */ + ret = phy_mipi_dphy_get_default_config(link_freq, 1, 1, cfg); + if (ret) + return ret; + + cfg->lanes = csi2rx->num_lanes; + + ret = phy_pm_runtime_get_sync(csi2rx->dphy); + if (ret == -ENOTSUPP) + got_pm = false; + else if (ret) + return ret; + + ret = phy_set_mode_ext(csi2rx->dphy, PHY_MODE_MIPI_DPHY, + PHY_MIPI_DPHY_SUBMODE_RX); + if (ret) + goto out; + + ret = phy_power_on(csi2rx->dphy); + if (ret) + goto out; + + ret = phy_configure(csi2rx->dphy, &opts); + if (ret) { + /* Can't do anything if it fails. Ignore the return value. */ + phy_power_off(csi2rx->dphy); + goto out; + } + +out: + if (got_pm) + phy_pm_runtime_put(csi2rx->dphy); + + return ret; } static int csi2rx_start(struct csi2rx_priv *csi2rx) @@ -140,6 +311,17 @@ if (ret) goto err_disable_pclk; + /* Enable DPHY clk and data lanes. */ + if (csi2rx->dphy) { + reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; + for (i = 0; i < csi2rx->num_lanes; i++) { + reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); + reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); + } + + writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + } + /* * Create a static mapping between the CSI virtual channels * and the output stream. @@ -158,8 +340,8 @@ writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); - writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT | - CSI2RX_STREAM_DATA_CFG_VC_SELECT(i), + /* Let all virtual channels through. */ + writel(CSI2RX_STREAM_DATA_CFG_VC_ALL, csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); writel(CSI2RX_STREAM_CTRL_START, @@ -170,10 +352,21 @@ if (ret) goto err_disable_pixclk; + if (csi2rx->dphy) { + ret = csi2rx_configure_external_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_sysclk; + } + } + clk_disable_unprepare(csi2rx->p_clk); return 0; +err_disable_sysclk: + clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: for (; i > 0; i--) clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); @@ -187,12 +380,23 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) { unsigned int i; + u32 val; + int ret; clk_prepare_enable(csi2rx->p_clk); clk_disable_unprepare(csi2rx->sys_clk); for (i = 0; i < csi2rx->max_streams; i++) { - writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + writel(CSI2RX_STREAM_CTRL_STOP, + csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + + ret = readl_relaxed_poll_timeout(csi2rx->base + + CSI2RX_STREAM_STATUS_REG(i), + val, + (val & CSI2RX_STREAM_STATUS_RDY), + 10, 10000); + if (ret) + dev_warn(csi2rx->dev, "Failed to stop stream%d\n", i); clk_disable_unprepare(csi2rx->pixel_clk[i]); } @@ -201,6 +405,13 @@ if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); + + if (csi2rx->dphy) { + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + if (phy_power_off(csi2rx->dphy)) + dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); + } } static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) @@ -237,12 +448,25 @@ return ret; } +static int csi2rx_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + return csi2rx_get_frame_desc_from_source(csi2rx, fd); +} + static const struct v4l2_subdev_video_ops csi2rx_video_ops = { .s_stream = csi2rx_s_stream, }; +static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { + .get_frame_desc = csi2rx_get_frame_desc, +}; + static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .video = &csi2rx_video_ops, + .pad = &csi2rx_pad_ops, }; static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, @@ -307,15 +531,6 @@ return PTR_ERR(csi2rx->dphy); } - /* - * FIXME: Once we'll have external D-PHY support, the check - * will need to be removed. - */ - if (csi2rx->dphy) { - dev_err(&pdev->dev, "External D-PHY not supported yet\n"); - return -EINVAL; - } - clk_prepare_enable(csi2rx->p_clk); dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG); clk_disable_unprepare(csi2rx->p_clk); @@ -340,7 +555,7 @@ * FIXME: Once we'll have internal D-PHY support, the check * will need to be removed. */ - if (csi2rx->has_internal_dphy) { + if (!csi2rx->dphy && csi2rx->has_internal_dphy) { dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); return -EINVAL; } @@ -450,6 +665,7 @@ csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; + csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, csi2rx->pads); @@ -463,11 +679,13 @@ dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, + csi2rx->dphy ? "external" : csi2rx->has_internal_dphy ? "internal" : "no"); return 0; err_cleanup: + v4l2_async_notifier_unregister(&csi2rx->notifier); v4l2_async_notifier_cleanup(&csi2rx->notifier); err_free_priv: kfree(csi2rx); @@ -478,6 +696,8 @@ { struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); + v4l2_async_notifier_unregister(&csi2rx->notifier); + v4l2_async_notifier_cleanup(&csi2rx->notifier); v4l2_async_unregister_subdev(&csi2rx->subdev); kfree(csi2rx); diff -Naur --no-dereference a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c --- a/drivers/media/platform/cadence/cdns-csi2tx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/cadence/cdns-csi2tx.c 2022-01-06 12:45:53.814318107 -0500 @@ -156,7 +156,7 @@ } static int csi2tx_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= ARRAY_SIZE(csi2tx_formats)) @@ -169,20 +169,20 @@ static struct v4l2_mbus_framefmt * __csi2tx_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(subdev, cfg, + return v4l2_subdev_get_try_format(subdev, sd_state, fmt->pad); return &csi2tx->pad_fmts[fmt->pad]; } static int csi2tx_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { const struct v4l2_mbus_framefmt *format; @@ -191,7 +191,7 @@ if (fmt->pad == CSI2TX_PAD_SOURCE) return -EINVAL; - format = __csi2tx_get_pad_format(subdev, cfg, fmt); + format = __csi2tx_get_pad_format(subdev, sd_state, fmt); if (!format) return -EINVAL; @@ -201,7 +201,7 @@ } static int csi2tx_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { const struct v4l2_mbus_framefmt *src_format = &fmt->format; @@ -214,7 +214,7 @@ if (!csi2tx_get_fmt_from_mbus(fmt->format.code)) src_format = &fmt_default; - dst_format = __csi2tx_get_pad_format(subdev, cfg, fmt); + dst_format = __csi2tx_get_pad_format(subdev, sd_state, fmt); if (!dst_format) return -EINVAL; diff -Naur --no-dereference a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c --- a/drivers/media/platform/exynos4-is/fimc-capture.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/exynos4-is/fimc-capture.c 2022-01-06 12:45:53.814318107 -0500 @@ -524,7 +524,7 @@ mutex_lock(&fimc->lock); if (close && vc->streaming) { - media_pipeline_stop(&vc->ve.vdev.entity); + media_pipeline_stop(vc->ve.vdev.entity.pads); vc->streaming = false; } @@ -1184,7 +1184,7 @@ if (fimc_capture_active(fimc)) return -EBUSY; - ret = media_pipeline_start(entity, &vc->ve.pipe->mp); + ret = media_pipeline_start(entity->pads, &vc->ve.pipe->mp); if (ret < 0) return ret; @@ -1218,7 +1218,7 @@ } err_p_stop: - media_pipeline_stop(entity); + media_pipeline_stop(entity->pads); return ret; } @@ -1234,7 +1234,7 @@ return ret; if (vc->streaming) { - media_pipeline_stop(&vc->ve.vdev.entity); + media_pipeline_stop(vc->ve.vdev.entity.pads); vc->streaming = false; } @@ -1454,7 +1454,7 @@ } static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct fimc_fmt *fmt; @@ -1467,7 +1467,7 @@ } static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1476,7 +1476,7 @@ struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1508,7 +1508,7 @@ } static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1531,7 +1531,7 @@ mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; return 0; } @@ -1574,7 +1574,7 @@ } static int fimc_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1601,10 +1601,10 @@ return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); f = &ctx->d_frame; break; default: @@ -1630,7 +1630,7 @@ } static int fimc_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1648,10 +1648,10 @@ switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); f = &ctx->d_frame; break; default: diff -Naur --no-dereference a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c --- a/drivers/media/platform/exynos4-is/fimc-isp.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/exynos4-is/fimc-isp.c 2022-01-06 12:45:53.814318107 -0500 @@ -106,7 +106,7 @@ }; static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { const struct fimc_fmt *fmt; @@ -119,14 +119,14 @@ } static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *mf = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); return 0; } @@ -156,7 +156,7 @@ } static void __isp_subdev_try_format(struct fimc_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; @@ -172,8 +172,9 @@ mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(&isp->subdev, cfg, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_get_try_format(&isp->subdev, + sd_state, + FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -191,7 +192,7 @@ } static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); @@ -203,10 +204,10 @@ __func__, fmt->pad, mf->code, mf->width, mf->height); mutex_lock(&isp->subdev_lock); - __isp_subdev_try_format(isp, cfg, fmt); + __isp_subdev_try_format(isp, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -217,20 +218,23 @@ for (pad = FIMC_ISP_SD_PAD_SRC_FIFO; pad < FIMC_ISP_SD_PADS_NUM; pad++) { format.pad = pad; - __isp_subdev_try_format(isp, cfg, &format); - mf = v4l2_subdev_get_try_format(sd, cfg, pad); + __isp_subdev_try_format(isp, sd_state, + &format); + mf = v4l2_subdev_get_try_format(sd, sd_state, + pad); *mf = format.format; } } } else { - if (sd->entity.stream_count == 0) { + if (sd->entity.pads->stream_count == 0) { if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { struct v4l2_subdev_format format = *fmt; isp->sink_fmt = *mf; format.pad = FIMC_ISP_SD_PAD_SRC_DMA; - __isp_subdev_try_format(isp, cfg, &format); + __isp_subdev_try_format(isp, sd_state, + &format); isp->src_fmt = format.format; __is_set_frame_size(is, &isp->src_fmt); @@ -370,15 +374,18 @@ .field = V4L2_FIELD_NONE, }; - format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_get_try_format(sd, fh->state, + FIMC_ISP_SD_PAD_SINK); *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_get_try_format(sd, fh->state, + FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_get_try_format(sd, fh->state, + FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff -Naur --no-dereference a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c 2022-01-06 12:45:53.814318107 -0500 @@ -312,7 +312,7 @@ is_singular_file = v4l2_fh_is_singular_file(file); if (is_singular_file && ivc->streaming) { - media_pipeline_stop(entity); + media_pipeline_stop(entity->pads); ivc->streaming = 0; } @@ -493,7 +493,7 @@ struct media_entity *me = &ve->vdev.entity; int ret; - ret = media_pipeline_start(me, &ve->pipe->mp); + ret = media_pipeline_start(me->pads, &ve->pipe->mp); if (ret < 0) return ret; @@ -508,7 +508,7 @@ isp->video_capture.streaming = 1; return 0; p_stop: - media_pipeline_stop(me); + media_pipeline_stop(me->pads); return ret; } @@ -523,7 +523,7 @@ if (ret < 0) return ret; - media_pipeline_stop(&video->ve.vdev.entity); + media_pipeline_stop(video->ve.vdev.entity.pads); video->streaming = 0; return 0; } diff -Naur --no-dereference a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c --- a/drivers/media/platform/exynos4-is/fimc-lite.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/exynos4-is/fimc-lite.c 2022-01-06 12:45:53.814318107 -0500 @@ -516,7 +516,7 @@ if (v4l2_fh_is_singular_file(file) && atomic_read(&fimc->out_path) == FIMC_IO_DMA) { if (fimc->streaming) { - media_pipeline_stop(entity); + media_pipeline_stop(entity->pads); fimc->streaming = false; } fimc_lite_stop_capture(fimc, false); @@ -550,7 +550,7 @@ */ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct flite_drvdata *dd = fimc->dd; @@ -574,14 +574,16 @@ struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, cfg, - FLITE_SD_PAD_SINK); + sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, + sd_state, + FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(&fimc->subdev, cfg, - FLITE_SD_PAD_SINK); + rect = v4l2_subdev_get_try_crop(&fimc->subdev, + sd_state, + FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; mf->colorspace = sink->fmt->colorspace; @@ -820,7 +822,7 @@ if (fimc_lite_active(fimc)) return -EBUSY; - ret = media_pipeline_start(entity, &fimc->ve.pipe->mp); + ret = media_pipeline_start(entity->pads, &fimc->ve.pipe->mp); if (ret < 0) return ret; @@ -837,7 +839,7 @@ } err_p_stop: - media_pipeline_stop(entity); + media_pipeline_stop(entity->pads); return 0; } @@ -851,7 +853,7 @@ if (ret < 0) return ret; - media_pipeline_stop(&fimc->ve.vdev.entity); + media_pipeline_stop(fimc->ve.vdev.entity.pads); fimc->streaming = false; return 0; } @@ -1002,7 +1004,7 @@ }; static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { const struct fimc_fmt *fmt; @@ -1016,16 +1018,16 @@ static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, unsigned int pad) + struct v4l2_subdev_state *sd_state, unsigned int pad) { if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(sd, cfg, pad); + return v4l2_subdev_get_try_format(sd, sd_state, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1033,7 +1035,7 @@ struct flite_frame *f = &fimc->inp_frame; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(sd, sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1056,7 +1058,7 @@ } static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1071,24 +1073,25 @@ mutex_lock(&fimc->lock); if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && - sd->entity.stream_count > 0) || + sd->entity.pads->stream_count > 0) || (atomic_read(&fimc->out_path) == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) { mutex_unlock(&fimc->lock); return -EBUSY; } - ffmt = fimc_lite_subdev_try_fmt(fimc, cfg, fmt); + ffmt = fimc_lite_subdev_try_fmt(fimc, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *src_fmt; - mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(sd, sd_state, fmt->pad); *mf = fmt->format; if (fmt->pad == FLITE_SD_PAD_SINK) { unsigned int pad = FLITE_SD_PAD_SOURCE_DMA; - src_fmt = __fimc_lite_subdev_get_try_fmt(sd, cfg, pad); + src_fmt = __fimc_lite_subdev_get_try_fmt(sd, sd_state, + pad); *src_fmt = *mf; } @@ -1116,7 +1119,7 @@ } static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1128,7 +1131,7 @@ return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); return 0; } @@ -1151,7 +1154,7 @@ } static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1165,7 +1168,7 @@ fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; + *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff -Naur --no-dereference a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c --- a/drivers/media/platform/exynos4-is/media-dev.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/exynos4-is/media-dev.c 2022-01-06 12:45:53.814318107 -0500 @@ -1160,7 +1160,7 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, struct media_graph *graph) { - struct media_entity *entity_err = entity; + struct media_pad *pad, *pad_err = entity->pads; int ret; /* @@ -1169,13 +1169,13 @@ * through active links. This is needed as we cannot power on/off the * subdevs in random order. */ - media_graph_walk_start(graph, entity); + media_graph_walk_start(graph, pad_err); - while ((entity = media_graph_walk_next(graph))) { - if (!is_media_entity_v4l2_video_device(entity)) + while ((pad = media_graph_walk_next(graph))) { + if (!is_media_entity_v4l2_video_device(pad->entity)) continue; - ret = __fimc_md_modify_pipeline(entity, enable); + ret = __fimc_md_modify_pipeline(pad->entity, enable); if (ret < 0) goto err; @@ -1184,15 +1184,15 @@ return 0; err: - media_graph_walk_start(graph, entity_err); + media_graph_walk_start(graph, pad_err); - while ((entity_err = media_graph_walk_next(graph))) { - if (!is_media_entity_v4l2_video_device(entity_err)) + while ((pad_err = media_graph_walk_next(graph))) { + if (!is_media_entity_v4l2_video_device(pad_err->entity)) continue; - __fimc_md_modify_pipeline(entity_err, !enable); + __fimc_md_modify_pipeline(pad_err->entity, !enable); - if (entity_err == entity) + if (pad_err == pad) break; } diff -Naur --no-dereference a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c --- a/drivers/media/platform/exynos4-is/mipi-csis.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/exynos4-is/mipi-csis.c 2022-01-06 12:45:53.814318107 -0500 @@ -537,7 +537,7 @@ } static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5pcsis_formats)) @@ -565,23 +565,25 @@ } static struct v4l2_mbus_framefmt *__s5pcsis_get_format( - struct csis_state *state, struct v4l2_subdev_pad_config *cfg, + struct csis_state *state, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return cfg ? v4l2_subdev_get_try_format(&state->sd, cfg, 0) : NULL; + return sd_state ? v4l2_subdev_get_try_format(&state->sd, + sd_state, 0) : NULL; return &state->format; } -static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int s5pcsis_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csis_state *state = sd_to_csis_state(sd); struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, cfg, fmt->which); + mf = __s5pcsis_get_format(state, sd_state, fmt->which); if (fmt->pad == CSIS_PAD_SOURCE) { if (mf) { @@ -602,13 +604,14 @@ return 0; } -static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int s5pcsis_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csis_state *state = sd_to_csis_state(sd); struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, cfg, fmt->which); + mf = __s5pcsis_get_format(state, sd_state, fmt->which); if (!mf) return -EINVAL; diff -Naur --no-dereference a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig --- a/drivers/media/platform/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/Kconfig 2022-01-06 12:45:53.814318107 -0500 @@ -168,6 +168,32 @@ In TI Technical Reference Manual this module is referred as Camera Interface Subsystem (CAMSS). +if VIDEO_TI_CAL + +config VIDEO_TI_CAL_MC + bool "Media Controller centric mode by default" + default n + help + Enables Media Controller centric mode by default. + + If set, CAL driver will start in Media Controller mode by + default. Note that this behavior can be overridden via + module parameter 'mc_api'. + +endif # VIDEO_TI_CAL + +config VIDEO_TI_J721E_CSI2RX + tristate "TI J721E CSI2RX wrapper layer driver" + depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_SUPPORT && MEDIA_CONTROLLER + depends on PHY_CADENCE_DPHY && VIDEO_CADENCE_CSI2RX + depends on ARCH_K3 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Support for TI CSI2RX wrapper layer. This just enables the wrapper driver. + The Cadence CSI2RX bridge driver needs to be enabled separately. + endif # V4L_PLATFORM_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS @@ -495,6 +521,30 @@ help Enable debug messages on VPE driver. +config VIDEO_IMG_VXD_DEC + tristate "IMG VXD DEC (Video Decoder) driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_CORE + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG + select V4L2_MEM2MEM_DEV + help + This is an IMG VXD DEC V4L2 driver that adds support for the + Imagination D5520 (Video Decoder) hardware. + The module name when built is vxd-dec. + +config VIDEO_IMG_VXE_ENC + tristate "IMG VXE ENC (Video Encoder) driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_CORE + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG + select V4L2_MEM2MEM_DEV + help + This is an IMG VXE ENC V4L2 driver that adds support for the + Imagination VXE384 (Video Encoder) hardware. + The module name when built is vxe-enc. + config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on VIDEO_DEV && VIDEO_V4L2 diff -Naur --no-dereference a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile --- a/drivers/media/platform/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/Makefile 2022-01-06 12:45:53.814318107 -0500 @@ -14,7 +14,7 @@ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o -obj-y += ti-vpe/ +obj-y += ti/ obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda/ @@ -80,3 +80,6 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-y += sunxi/ + +obj-$(CONFIG_VIDEO_IMG_VXD_DEC) += vxe-vxd/ +obj-$(CONFIG_VIDEO_IMG_VXE_ENC) += vxe-vxd/ diff -Naur --no-dereference a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c --- a/drivers/media/platform/marvell-ccic/mcam-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/marvell-ccic/mcam-core.c 2022-01-06 12:45:53.814318107 -0500 @@ -1350,6 +1350,9 @@ struct mcam_format_struct *f; struct v4l2_pix_format *pix = &fmt->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1358,7 +1361,7 @@ f = mcam_find_format(pix->pixelformat); pix->pixelformat = f->pixelformat; v4l2_fill_mbus_format(&format.format, pix, f->mbus_code); - ret = sensor_call(cam, pad, set_fmt, &pad_cfg, &format); + ret = sensor_call(cam, pad, set_fmt, &pad_state, &format); v4l2_fill_pix_format(pix, &format.format); pix->bytesperline = pix->width * f->bpp; switch (f->pixelformat) { diff -Naur --no-dereference a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c --- a/drivers/media/platform/omap3isp/isp.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/isp.c 2022-01-06 12:45:53.814318107 -0500 @@ -930,7 +930,7 @@ struct isp_pipeline *pipe; struct media_pad *pad; - if (!me->pipe) + if (!me->pads->pipe) return 0; pipe = to_isp_pipeline(me); if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) diff -Naur --no-dereference a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c --- a/drivers/media/platform/omap3isp/ispccdc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/ispccdc.c 2022-01-06 12:45:53.814318107 -0500 @@ -29,7 +29,8 @@ #define CCDC_MIN_HEIGHT 32 static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, +__ccdc_get_format(struct isp_ccdc_device *ccdc, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which); static const unsigned int ccdc_fmts[] = { @@ -1937,21 +1938,25 @@ } static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, +__ccdc_get_format(struct isp_ccdc_device *ccdc, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccdc->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&ccdc->subdev, sd_state, + pad); else return &ccdc->formats[pad]; } static struct v4l2_rect * -__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, +__ccdc_get_crop(struct isp_ccdc_device *ccdc, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&ccdc->subdev, cfg, CCDC_PAD_SOURCE_OF); + return v4l2_subdev_get_try_crop(&ccdc->subdev, sd_state, + CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } @@ -1964,7 +1969,8 @@ * @fmt: Format */ static void -ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, +ccdc_try_format(struct isp_ccdc_device *ccdc, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -2000,7 +2006,8 @@ case CCDC_PAD_SOURCE_OF: pixelcode = fmt->code; field = fmt->field; - *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK, + which); /* In SYNC mode the bridge converts YUV formats from 2X8 to * 1X16. In BT.656 no such conversion occurs. As we don't know @@ -2025,7 +2032,7 @@ } /* Hardcode the output size to the crop rectangle size. */ - crop = __ccdc_get_crop(ccdc, cfg, which); + crop = __ccdc_get_crop(ccdc, sd_state, which); fmt->width = crop->width; fmt->height = crop->height; @@ -2042,7 +2049,8 @@ break; case CCDC_PAD_SOURCE_VP: - *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK, + which); /* The video port interface truncates the data to 10 bits. */ info = omap3isp_video_format_info(fmt->code); @@ -2119,7 +2127,7 @@ * return -EINVAL or zero on success */ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2134,7 +2142,7 @@ break; case CCDC_PAD_SOURCE_OF: - format = __ccdc_get_format(ccdc, cfg, code->pad, + format = __ccdc_get_format(ccdc, sd_state, code->pad, code->which); if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 || @@ -2165,7 +2173,7 @@ if (code->index != 0) return -EINVAL; - format = __ccdc_get_format(ccdc, cfg, code->pad, + format = __ccdc_get_format(ccdc, sd_state, code->pad, code->which); /* A pixel code equal to 0 means that the video port doesn't @@ -2185,7 +2193,7 @@ } static int ccdc_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2197,7 +2205,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which); + ccdc_try_format(ccdc, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -2207,7 +2215,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which); + ccdc_try_format(ccdc, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -2225,7 +2233,8 @@ * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int ccdc_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2241,12 +2250,13 @@ sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK, + sel->which); ccdc_try_crop(ccdc, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); + sel->r = *__ccdc_get_crop(ccdc, sd_state, sel->which); break; default: @@ -2267,7 +2277,8 @@ * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int ccdc_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2286,17 +2297,19 @@ * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); + sel->r = *__ccdc_get_crop(ccdc, sd_state, sel->which); return 0; } - format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); - *__ccdc_get_crop(ccdc, cfg, sel->which) = sel->r; + *__ccdc_get_crop(ccdc, sd_state, sel->which) = sel->r; /* Update the source format. */ - format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, sel->which); - ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, sel->which); + format = __ccdc_get_format(ccdc, sd_state, CCDC_PAD_SOURCE_OF, + sel->which); + ccdc_try_format(ccdc, sd_state, CCDC_PAD_SOURCE_OF, format, + sel->which); return 0; } @@ -2310,13 +2323,14 @@ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int ccdc_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2333,24 +2347,25 @@ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int ccdc_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccdc_try_format(ccdc, cfg, fmt->pad, &fmt->format, fmt->which); + ccdc_try_format(ccdc, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __ccdc_get_crop(ccdc, cfg, fmt->which); + crop = __ccdc_get_crop(ccdc, sd_state, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2359,16 +2374,16 @@ ccdc_try_crop(ccdc, &fmt->format, crop); /* Update the source formats. */ - format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, + format = __ccdc_get_format(ccdc, sd_state, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, + ccdc_try_format(ccdc, sd_state, CCDC_PAD_SOURCE_OF, format, fmt->which); - format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, + format = __ccdc_get_format(ccdc, sd_state, CCDC_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, format, + ccdc_try_format(ccdc, sd_state, CCDC_PAD_SOURCE_VP, format, fmt->which); } @@ -2455,7 +2470,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccdc_set_format(sd, fh ? fh->pad : NULL, &format); + ccdc_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c --- a/drivers/media/platform/omap3isp/ispccp2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/ispccp2.c 2022-01-06 12:45:53.814318107 -0500 @@ -618,11 +618,13 @@ * return format structure or NULL on error */ static struct v4l2_mbus_framefmt * -__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_pad_config *cfg, +__ccp2_get_format(struct isp_ccp2_device *ccp2, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccp2->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&ccp2->subdev, sd_state, + pad); else return &ccp2->formats[pad]; } @@ -636,7 +638,8 @@ * @which : wanted subdev format */ static void ccp2_try_format(struct isp_ccp2_device *ccp2, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -670,7 +673,8 @@ * When CCP2 write to memory feature will be added this * should be changed properly. */ - format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, which); + format = __ccp2_get_format(ccp2, sd_state, CCP2_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; break; @@ -688,7 +692,7 @@ * return -EINVAL or zero on success */ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); @@ -703,7 +707,7 @@ if (code->index != 0) return -EINVAL; - format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, + format = __ccp2_get_format(ccp2, sd_state, CCP2_PAD_SINK, code->which); code->code = format->code; } @@ -712,7 +716,7 @@ } static int ccp2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); @@ -724,7 +728,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - ccp2_try_format(ccp2, cfg, fse->pad, &format, fse->which); + ccp2_try_format(ccp2, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -734,7 +738,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - ccp2_try_format(ccp2, cfg, fse->pad, &format, fse->which); + ccp2_try_format(ccp2, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -748,13 +752,14 @@ * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int ccp2_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which); + format = __ccp2_get_format(ccp2, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -769,25 +774,27 @@ * @fmt : pointer to v4l2 subdev format structure * returns zero */ -static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int ccp2_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which); + format = __ccp2_get_format(ccp2, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccp2_try_format(ccp2, cfg, fmt->pad, &fmt->format, fmt->which); + ccp2_try_format(ccp2, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCP2_PAD_SINK) { - format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SOURCE, + format = __ccp2_get_format(ccp2, sd_state, CCP2_PAD_SOURCE, fmt->which); *format = fmt->format; - ccp2_try_format(ccp2, cfg, CCP2_PAD_SOURCE, format, fmt->which); + ccp2_try_format(ccp2, sd_state, CCP2_PAD_SOURCE, format, + fmt->which); } return 0; @@ -812,7 +819,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccp2_set_format(sd, fh ? fh->pad : NULL, &format); + ccp2_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c --- a/drivers/media/platform/omap3isp/ispcsi2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/ispcsi2.c 2022-01-06 12:45:53.814318107 -0500 @@ -827,17 +827,20 @@ */ static struct v4l2_mbus_framefmt * -__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, +__csi2_get_format(struct isp_csi2_device *csi2, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, + pad); else return &csi2->formats[pad]; } static void -csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, +csi2_try_format(struct isp_csi2_device *csi2, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -867,7 +870,8 @@ * compression. */ pixelcode = fmt->code; - format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which); + format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); /* @@ -893,7 +897,7 @@ * return -EINVAL or zero on success */ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -906,7 +910,7 @@ code->code = csi2_input_fmts[code->index]; } else { - format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, + format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SINK, code->which); switch (code->index) { case 0: @@ -930,7 +934,7 @@ } static int csi2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -942,7 +946,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - csi2_try_format(csi2, cfg, fse->pad, &format, fse->which); + csi2_try_format(csi2, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -952,7 +956,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - csi2_try_format(csi2, cfg, fse->pad, &format, fse->which); + csi2_try_format(csi2, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -966,13 +970,14 @@ * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int csi2_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -987,25 +992,27 @@ * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int csi2_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which); + csi2_try_format(csi2, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CSI2_PAD_SINK) { - format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE, + format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SOURCE, fmt->which); *format = fmt->format; - csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which); + csi2_try_format(csi2, sd_state, CSI2_PAD_SOURCE, format, + fmt->which); } return 0; @@ -1030,7 +1037,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - csi2_set_format(sd, fh ? fh->pad : NULL, &format); + csi2_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c --- a/drivers/media/platform/omap3isp/isppreview.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/isppreview.c 2022-01-06 12:45:53.814318107 -0500 @@ -1679,21 +1679,25 @@ } static struct v4l2_mbus_framefmt * -__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg, +__preview_get_format(struct isp_prev_device *prev, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&prev->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&prev->subdev, sd_state, + pad); else return &prev->formats[pad]; } static struct v4l2_rect * -__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg, +__preview_get_crop(struct isp_prev_device *prev, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&prev->subdev, cfg, PREV_PAD_SINK); + return v4l2_subdev_get_try_crop(&prev->subdev, sd_state, + PREV_PAD_SINK); else return &prev->crop; } @@ -1729,7 +1733,8 @@ * engine limits and the format and crop rectangles on other pads. */ static void preview_try_format(struct isp_prev_device *prev, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1770,7 +1775,8 @@ case PREV_PAD_SOURCE: pixelcode = fmt->code; - *fmt = *__preview_get_format(prev, cfg, PREV_PAD_SINK, which); + *fmt = *__preview_get_format(prev, sd_state, PREV_PAD_SINK, + which); switch (pixelcode) { case MEDIA_BUS_FMT_YUYV8_1X16: @@ -1788,7 +1794,7 @@ * is not supported yet, hardcode the output size to the crop * rectangle size. */ - crop = __preview_get_crop(prev, cfg, which); + crop = __preview_get_crop(prev, sd_state, which); fmt->width = crop->width; fmt->height = crop->height; @@ -1862,7 +1868,7 @@ * return -EINVAL or zero on success */ static int preview_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -1886,7 +1892,7 @@ } static int preview_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1898,7 +1904,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - preview_try_format(prev, cfg, fse->pad, &format, fse->which); + preview_try_format(prev, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -1908,7 +1914,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - preview_try_format(prev, cfg, fse->pad, &format, fse->which); + preview_try_format(prev, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -1926,7 +1932,7 @@ * Return 0 on success or a negative error code otherwise. */ static int preview_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1942,13 +1948,13 @@ sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __preview_get_format(prev, cfg, PREV_PAD_SINK, + format = __preview_get_format(prev, sd_state, PREV_PAD_SINK, sel->which); preview_try_crop(prev, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__preview_get_crop(prev, cfg, sel->which); + sel->r = *__preview_get_crop(prev, sd_state, sel->which); break; default: @@ -1969,7 +1975,7 @@ * Return 0 on success or a negative error code otherwise. */ static int preview_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1988,17 +1994,20 @@ * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__preview_get_crop(prev, cfg, sel->which); + sel->r = *__preview_get_crop(prev, sd_state, sel->which); return 0; } - format = __preview_get_format(prev, cfg, PREV_PAD_SINK, sel->which); + format = __preview_get_format(prev, sd_state, PREV_PAD_SINK, + sel->which); preview_try_crop(prev, format, &sel->r); - *__preview_get_crop(prev, cfg, sel->which) = sel->r; + *__preview_get_crop(prev, sd_state, sel->which) = sel->r; /* Update the source format. */ - format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, sel->which); - preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, sel->which); + format = __preview_get_format(prev, sd_state, PREV_PAD_SOURCE, + sel->which); + preview_try_format(prev, sd_state, PREV_PAD_SOURCE, format, + sel->which); return 0; } @@ -2010,13 +2019,14 @@ * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int preview_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __preview_get_format(prev, cfg, fmt->pad, fmt->which); + format = __preview_get_format(prev, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2031,24 +2041,25 @@ * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int preview_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __preview_get_format(prev, cfg, fmt->pad, fmt->which); + format = __preview_get_format(prev, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - preview_try_format(prev, cfg, fmt->pad, &fmt->format, fmt->which); + preview_try_format(prev, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == PREV_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __preview_get_crop(prev, cfg, fmt->which); + crop = __preview_get_crop(prev, sd_state, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2057,9 +2068,9 @@ preview_try_crop(prev, &fmt->format, crop); /* Update the source format. */ - format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, + format = __preview_get_format(prev, sd_state, PREV_PAD_SOURCE, fmt->which); - preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, + preview_try_format(prev, sd_state, PREV_PAD_SOURCE, format, fmt->which); } @@ -2086,7 +2097,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - preview_set_format(sd, fh ? fh->pad : NULL, &format); + preview_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c --- a/drivers/media/platform/omap3isp/ispresizer.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/ispresizer.c 2022-01-06 12:45:53.814318107 -0500 @@ -114,11 +114,12 @@ * return zero */ static struct v4l2_mbus_framefmt * -__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg, +__resizer_get_format(struct isp_res_device *res, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&res->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&res->subdev, sd_state, pad); else return &res->formats[pad]; } @@ -130,11 +131,13 @@ * @which : wanted subdev crop rectangle */ static struct v4l2_rect * -__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg, +__resizer_get_crop(struct isp_res_device *res, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&res->subdev, cfg, RESZ_PAD_SINK); + return v4l2_subdev_get_try_crop(&res->subdev, sd_state, + RESZ_PAD_SINK); else return &res->crop.request; } @@ -1220,7 +1223,7 @@ * Return 0 on success or a negative error code otherwise. */ static int resizer_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1231,9 +1234,9 @@ if (sel->pad != RESZ_PAD_SINK) return -EINVAL; - format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK, + format_sink = __resizer_get_format(res, sd_state, RESZ_PAD_SINK, sel->which); - format_source = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE, + format_source = __resizer_get_format(res, sd_state, RESZ_PAD_SOURCE, sel->which); switch (sel->target) { @@ -1248,7 +1251,7 @@ break; case V4L2_SEL_TGT_CROP: - sel->r = *__resizer_get_crop(res, cfg, sel->which); + sel->r = *__resizer_get_crop(res, sd_state, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1273,7 +1276,7 @@ * Return 0 on success or a negative error code otherwise. */ static int resizer_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1287,9 +1290,9 @@ sel->pad != RESZ_PAD_SINK) return -EINVAL; - format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK, + format_sink = __resizer_get_format(res, sd_state, RESZ_PAD_SINK, sel->which); - format_source = *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, + format_source = *__resizer_get_format(res, sd_state, RESZ_PAD_SOURCE, sel->which); dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", @@ -1307,7 +1310,7 @@ * stored the mangled rectangle. */ resizer_try_crop(format_sink, &format_source, &sel->r); - *__resizer_get_crop(res, cfg, sel->which) = sel->r; + *__resizer_get_crop(res, sd_state, sel->which) = sel->r; resizer_calc_ratios(res, &sel->r, &format_source, &ratio); dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", @@ -1317,7 +1320,8 @@ format_source.width, format_source.height); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) = + *__resizer_get_format(res, sd_state, RESZ_PAD_SOURCE, + sel->which) = format_source; return 0; } @@ -1328,7 +1332,7 @@ */ spin_lock_irqsave(&res->lock, flags); - *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) = + *__resizer_get_format(res, sd_state, RESZ_PAD_SOURCE, sel->which) = format_source; res->ratio = ratio; @@ -1371,7 +1375,8 @@ * @which : wanted subdev format */ static void resizer_try_format(struct isp_res_device *res, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1392,10 +1397,11 @@ break; case RESZ_PAD_SOURCE: - format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, which); + format = __resizer_get_format(res, sd_state, RESZ_PAD_SINK, + which); fmt->code = format->code; - crop = *__resizer_get_crop(res, cfg, which); + crop = *__resizer_get_crop(res, sd_state, which); resizer_calc_ratios(res, &crop, fmt, &ratio); break; } @@ -1412,7 +1418,7 @@ * return -EINVAL or zero on success */ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1427,7 +1433,7 @@ if (code->index != 0) return -EINVAL; - format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, + format = __resizer_get_format(res, sd_state, RESZ_PAD_SINK, code->which); code->code = format->code; } @@ -1436,7 +1442,7 @@ } static int resizer_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1448,7 +1454,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(res, cfg, fse->pad, &format, fse->which); + resizer_try_format(res, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -1458,7 +1464,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(res, cfg, fse->pad, &format, fse->which); + resizer_try_format(res, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -1472,13 +1478,14 @@ * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int resizer_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(res, cfg, fmt->pad, fmt->which); + format = __resizer_get_format(res, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1493,33 +1500,34 @@ * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, +static int resizer_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __resizer_get_format(res, cfg, fmt->pad, fmt->which); + format = __resizer_get_format(res, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - resizer_try_format(res, cfg, fmt->pad, &fmt->format, fmt->which); + resizer_try_format(res, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->pad == RESZ_PAD_SINK) { /* reset crop rectangle */ - crop = __resizer_get_crop(res, cfg, fmt->which); + crop = __resizer_get_crop(res, sd_state, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; crop->height = fmt->format.height; /* Propagate the format from sink to source */ - format = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE, + format = __resizer_get_format(res, sd_state, RESZ_PAD_SOURCE, fmt->which); *format = fmt->format; - resizer_try_format(res, cfg, RESZ_PAD_SOURCE, format, + resizer_try_format(res, sd_state, RESZ_PAD_SOURCE, format, fmt->which); } @@ -1570,7 +1578,7 @@ format.format.code = MEDIA_BUS_FMT_YUYV8_1X16; format.format.width = 4096; format.format.height = 4096; - resizer_set_format(sd, fh ? fh->pad : NULL, &format); + resizer_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c --- a/drivers/media/platform/omap3isp/ispvideo.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/ispvideo.c 2022-01-06 12:45:53.814318107 -0500 @@ -222,8 +222,8 @@ struct isp_pipeline *pipe) { struct media_graph graph; - struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->graph_obj.mdev; + struct media_pad *pad = video->video.entity.pads; + struct media_device *mdev = video->video.entity.graph_obj.mdev; struct isp_video *far_end = NULL; int ret; @@ -234,23 +234,24 @@ return ret; } - media_graph_walk_start(&graph, entity); + media_graph_walk_start(&graph, pad); - while ((entity = media_graph_walk_next(&graph))) { + while ((pad = media_graph_walk_next(&graph))) { struct isp_video *__video; - media_entity_enum_set(&pipe->ent_enum, entity); + media_entity_enum_set(&pipe->ent_enum, pad->entity); if (far_end != NULL) continue; - if (entity == &video->video.entity) + if (pad == video->video.entity.pads) continue; - if (!is_media_entity_v4l2_video_device(entity)) + if (!is_media_entity_v4l2_video_device(pad->entity)) continue; - __video = to_isp_video(media_entity_to_video_device(entity)); + __video = to_isp_video(media_entity_to_video_device( + pad->entity)); if (__video->type != video->type) far_end = __video; } @@ -1093,7 +1094,7 @@ /* Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ - pipe = video->video.entity.pipe + pipe = video->video.entity.pads->pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev); @@ -1104,7 +1105,7 @@ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; - ret = media_pipeline_start(&video->video.entity, &pipe->pipe); + ret = media_pipeline_start(video->video.entity.pads, &pipe->pipe); if (ret < 0) goto err_pipeline_start; @@ -1161,7 +1162,7 @@ return 0; err_check_format: - media_pipeline_stop(&video->video.entity); + media_pipeline_stop(video->video.entity.pads); err_pipeline_start: /* TODO: Implement PM QoS */ /* The DMA queue must be emptied here, otherwise CCDC interrupts that @@ -1228,7 +1229,7 @@ video->error = false; /* TODO: Implement PM QoS */ - media_pipeline_stop(&video->video.entity); + media_pipeline_stop(video->video.entity.pads); media_entity_enum_cleanup(&pipe->ent_enum); diff -Naur --no-dereference a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h --- a/drivers/media/platform/omap3isp/ispvideo.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/omap3isp/ispvideo.h 2022-01-06 12:45:53.814318107 -0500 @@ -100,7 +100,7 @@ }; #define to_isp_pipeline(__e) \ - container_of((__e)->pipe, struct isp_pipeline, pipe) + container_of((__e)->pads->pipe, struct isp_pipeline, pipe) static inline int isp_pipeline_ready(struct isp_pipeline *pipe) { diff -Naur --no-dereference a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c --- a/drivers/media/platform/pxa_camera.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/pxa_camera.c 2022-01-06 12:45:53.814318107 -0500 @@ -1796,6 +1796,9 @@ const struct pxa_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1820,7 +1823,7 @@ pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0); v4l2_fill_mbus_format(mf, pix, xlate->code); - ret = sensor_call(pcdev, pad, set_fmt, &pad_cfg, &format); + ret = sensor_call(pcdev, pad, set_fmt, &pad_state, &format); if (ret < 0) return ret; diff -Naur --no-dereference a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c --- a/drivers/media/platform/qcom/camss/camss-csid.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/qcom/camss/camss-csid.c 2022-01-06 12:45:53.814318107 -0500 @@ -762,12 +762,13 @@ */ static struct v4l2_mbus_framefmt * __csid_get_format(struct csid_device *csid, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&csid->subdev, sd_state, + pad); return &csid->fmt[pad]; } @@ -781,7 +782,7 @@ * @which: wanted subdev format */ static void csid_try_format(struct csid_device *csid, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) @@ -814,7 +815,7 @@ /* keep pad formats in sync */ u32 code = fmt->code; - *fmt = *__csid_get_format(csid, cfg, + *fmt = *__csid_get_format(csid, sd_state, MSM_CSID_PAD_SINK, which); fmt->code = csid_src_pad_code(csid, fmt->code, 0, code); } else { @@ -848,7 +849,7 @@ * return -EINVAL or zero on success */ static int csid_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct csid_device *csid = v4l2_get_subdevdata(sd); @@ -862,7 +863,7 @@ if (csid->testgen_mode->cur.val == 0) { struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = __csid_get_format(csid, cfg, + sink_fmt = __csid_get_format(csid, sd_state, MSM_CSID_PAD_SINK, code->which); @@ -889,7 +890,7 @@ * return -EINVAL or zero on success */ static int csid_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct csid_device *csid = v4l2_get_subdevdata(sd); @@ -901,7 +902,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - csid_try_format(csid, cfg, fse->pad, &format, fse->which); + csid_try_format(csid, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -911,7 +912,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - csid_try_format(csid, cfg, fse->pad, &format, fse->which); + csid_try_format(csid, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -927,13 +928,13 @@ * Return -EINVAL or zero on success */ static int csid_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csid_device *csid = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); + format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -951,26 +952,26 @@ * Return -EINVAL or zero on success */ static int csid_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csid_device *csid = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); + format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which); + csid_try_format(csid, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == MSM_CSID_PAD_SINK) { - format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC, + format = __csid_get_format(csid, sd_state, MSM_CSID_PAD_SRC, fmt->which); *format = fmt->format; - csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format, + csid_try_format(csid, sd_state, MSM_CSID_PAD_SRC, format, fmt->which); } @@ -999,7 +1000,7 @@ } }; - return csid_set_format(sd, fh ? fh->pad : NULL, &format); + return csid_set_format(sd, fh ? fh->state : NULL, &format); } static const char * const csid_test_pattern_menu[] = { diff -Naur --no-dereference a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c --- a/drivers/media/platform/qcom/camss/camss-csiphy.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c 2022-01-06 12:45:53.814318107 -0500 @@ -318,12 +318,13 @@ */ static struct v4l2_mbus_framefmt * __csiphy_get_format(struct csiphy_device *csiphy, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&csiphy->subdev, sd_state, + pad); return &csiphy->fmt[pad]; } @@ -337,7 +338,7 @@ * @which: wanted subdev format */ static void csiphy_try_format(struct csiphy_device *csiphy, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) @@ -367,7 +368,8 @@ case MSM_CSIPHY_PAD_SRC: /* Set and return a format same as sink pad */ - *fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK, + *fmt = *__csiphy_get_format(csiphy, sd_state, + MSM_CSID_PAD_SINK, which); break; @@ -382,7 +384,7 @@ * return -EINVAL or zero on success */ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); @@ -397,7 +399,8 @@ if (code->index > 0) return -EINVAL; - format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK, + format = __csiphy_get_format(csiphy, sd_state, + MSM_CSIPHY_PAD_SINK, code->which); code->code = format->code; @@ -414,7 +417,7 @@ * return -EINVAL or zero on success */ static int csiphy_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); @@ -426,7 +429,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); + csiphy_try_format(csiphy, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -436,7 +439,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); + csiphy_try_format(csiphy, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -452,13 +455,13 @@ * Return -EINVAL or zero on success */ static int csiphy_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); + format = __csiphy_get_format(csiphy, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -476,26 +479,29 @@ * Return -EINVAL or zero on success */ static int csiphy_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); + format = __csiphy_get_format(csiphy, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which); + csiphy_try_format(csiphy, sd_state, fmt->pad, &fmt->format, + fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == MSM_CSIPHY_PAD_SINK) { - format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, + format = __csiphy_get_format(csiphy, sd_state, + MSM_CSIPHY_PAD_SRC, fmt->which); *format = fmt->format; - csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format, + csiphy_try_format(csiphy, sd_state, MSM_CSIPHY_PAD_SRC, + format, fmt->which); } @@ -525,7 +531,7 @@ } }; - return csiphy_set_format(sd, fh ? fh->pad : NULL, &format); + return csiphy_set_format(sd, fh ? fh->state : NULL, &format); } /* diff -Naur --no-dereference a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c --- a/drivers/media/platform/qcom/camss/camss-ispif.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/qcom/camss/camss-ispif.c 2022-01-06 12:45:53.814318107 -0500 @@ -844,12 +844,13 @@ */ static struct v4l2_mbus_framefmt * __ispif_get_format(struct ispif_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&line->subdev, sd_state, + pad); return &line->fmt[pad]; } @@ -863,7 +864,7 @@ * @which: wanted subdev format */ static void ispif_try_format(struct ispif_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) @@ -893,7 +894,7 @@ case MSM_ISPIF_PAD_SRC: /* Set and return a format same as sink pad */ - *fmt = *__ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, + *fmt = *__ispif_get_format(line, sd_state, MSM_ISPIF_PAD_SINK, which); break; @@ -910,7 +911,7 @@ * return -EINVAL or zero on success */ static int ispif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct ispif_line *line = v4l2_get_subdevdata(sd); @@ -925,7 +926,8 @@ if (code->index > 0) return -EINVAL; - format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, + format = __ispif_get_format(line, sd_state, + MSM_ISPIF_PAD_SINK, code->which); code->code = format->code; @@ -942,7 +944,7 @@ * return -EINVAL or zero on success */ static int ispif_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct ispif_line *line = v4l2_get_subdevdata(sd); @@ -954,7 +956,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - ispif_try_format(line, cfg, fse->pad, &format, fse->which); + ispif_try_format(line, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -964,7 +966,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - ispif_try_format(line, cfg, fse->pad, &format, fse->which); + ispif_try_format(line, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -980,13 +982,13 @@ * Return -EINVAL or zero on success */ static int ispif_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ispif_line *line = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); + format = __ispif_get_format(line, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1004,26 +1006,26 @@ * Return -EINVAL or zero on success */ static int ispif_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct ispif_line *line = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); + format = __ispif_get_format(line, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ispif_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); + ispif_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == MSM_ISPIF_PAD_SINK) { - format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SRC, + format = __ispif_get_format(line, sd_state, MSM_ISPIF_PAD_SRC, fmt->which); *format = fmt->format; - ispif_try_format(line, cfg, MSM_ISPIF_PAD_SRC, format, + ispif_try_format(line, sd_state, MSM_ISPIF_PAD_SRC, format, fmt->which); } @@ -1052,7 +1054,7 @@ } }; - return ispif_set_format(sd, fh ? fh->pad : NULL, &format); + return ispif_set_format(sd, fh ? fh->state : NULL, &format); } /* diff -Naur --no-dereference a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c --- a/drivers/media/platform/qcom/camss/camss-vfe.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/qcom/camss/camss-vfe.c 2022-01-06 12:45:53.814318107 -0500 @@ -1466,12 +1466,13 @@ */ static struct v4l2_mbus_framefmt * __vfe_get_format(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&line->subdev, sd_state, + pad); return &line->fmt[pad]; } @@ -1486,11 +1487,11 @@ */ static struct v4l2_rect * __vfe_get_compose(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&line->subdev, cfg, + return v4l2_subdev_get_try_compose(&line->subdev, sd_state, MSM_VFE_PAD_SINK); return &line->compose; @@ -1506,11 +1507,11 @@ */ static struct v4l2_rect * __vfe_get_crop(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&line->subdev, cfg, + return v4l2_subdev_get_try_crop(&line->subdev, sd_state, MSM_VFE_PAD_SRC); return &line->crop; @@ -1525,7 +1526,7 @@ * @which: wanted subdev format */ static void vfe_try_format(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) @@ -1557,14 +1558,15 @@ /* Set and return a format same as sink pad */ code = fmt->code; - *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); + *fmt = *__vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, + which); fmt->code = vfe_src_pad_code(line, fmt->code, 0, code); if (line->id == VFE_LINE_PIX) { struct v4l2_rect *rect; - rect = __vfe_get_crop(line, cfg, which); + rect = __vfe_get_crop(line, sd_state, which); fmt->width = rect->width; fmt->height = rect->height; @@ -1584,13 +1586,13 @@ * @which: wanted subdev format */ static void vfe_try_compose(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_rect *rect, enum v4l2_subdev_format_whence which) { struct v4l2_mbus_framefmt *fmt; - fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); + fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, which); if (rect->width > fmt->width) rect->width = fmt->width; @@ -1623,13 +1625,13 @@ * @which: wanted subdev format */ static void vfe_try_crop(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_rect *rect, enum v4l2_subdev_format_whence which) { struct v4l2_rect *compose; - compose = __vfe_get_compose(line, cfg, which); + compose = __vfe_get_compose(line, sd_state, which); if (rect->width > compose->width) rect->width = compose->width; @@ -1667,7 +1669,7 @@ * return -EINVAL or zero on success */ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct vfe_line *line = v4l2_get_subdevdata(sd); @@ -1680,7 +1682,7 @@ } else { struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, + sink_fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, code->which); code->code = vfe_src_pad_code(line, sink_fmt->code, @@ -1701,7 +1703,7 @@ * Return -EINVAL or zero on success */ static int vfe_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct vfe_line *line = v4l2_get_subdevdata(sd); @@ -1713,7 +1715,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - vfe_try_format(line, cfg, fse->pad, &format, fse->which); + vfe_try_format(line, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -1723,7 +1725,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - vfe_try_format(line, cfg, fse->pad, &format, fse->which); + vfe_try_format(line, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -1739,13 +1741,13 @@ * Return -EINVAL or zero on success */ static int vfe_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vfe_line *line = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); + format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1755,7 +1757,7 @@ } static int vfe_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel); /* @@ -1767,17 +1769,17 @@ * Return -EINVAL or zero on success */ static int vfe_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vfe_line *line = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); + format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); + vfe_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->pad == MSM_VFE_PAD_SINK) { @@ -1785,11 +1787,11 @@ int ret; /* Propagate the format from sink to source */ - format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC, + format = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SRC, fmt->which); *format = fmt->format; - vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format, + vfe_try_format(line, sd_state, MSM_VFE_PAD_SRC, format, fmt->which); if (line->id != VFE_LINE_PIX) @@ -1801,7 +1803,7 @@ sel.target = V4L2_SEL_TGT_COMPOSE; sel.r.width = fmt->format.width; sel.r.height = fmt->format.height; - ret = vfe_set_selection(sd, cfg, &sel); + ret = vfe_set_selection(sd, sd_state, &sel); if (ret < 0) return ret; } @@ -1818,7 +1820,7 @@ * Return -EINVAL or zero on success */ static int vfe_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vfe_line *line = v4l2_get_subdevdata(sd); @@ -1834,7 +1836,7 @@ case V4L2_SEL_TGT_COMPOSE_BOUNDS: fmt.pad = sel->pad; fmt.which = sel->which; - ret = vfe_get_format(sd, cfg, &fmt); + ret = vfe_get_format(sd, sd_state, &fmt); if (ret < 0) return ret; @@ -1844,7 +1846,7 @@ sel->r.height = fmt.format.height; break; case V4L2_SEL_TGT_COMPOSE: - rect = __vfe_get_compose(line, cfg, sel->which); + rect = __vfe_get_compose(line, sd_state, sel->which); if (rect == NULL) return -EINVAL; @@ -1856,7 +1858,7 @@ else if (sel->pad == MSM_VFE_PAD_SRC) switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - rect = __vfe_get_compose(line, cfg, sel->which); + rect = __vfe_get_compose(line, sd_state, sel->which); if (rect == NULL) return -EINVAL; @@ -1866,7 +1868,7 @@ sel->r.height = rect->height; break; case V4L2_SEL_TGT_CROP: - rect = __vfe_get_crop(line, cfg, sel->which); + rect = __vfe_get_crop(line, sd_state, sel->which); if (rect == NULL) return -EINVAL; @@ -1888,7 +1890,7 @@ * Return -EINVAL or zero on success */ static int vfe_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vfe_line *line = v4l2_get_subdevdata(sd); @@ -1902,11 +1904,11 @@ sel->pad == MSM_VFE_PAD_SINK) { struct v4l2_subdev_selection crop = { 0 }; - rect = __vfe_get_compose(line, cfg, sel->which); + rect = __vfe_get_compose(line, sd_state, sel->which); if (rect == NULL) return -EINVAL; - vfe_try_compose(line, cfg, &sel->r, sel->which); + vfe_try_compose(line, sd_state, &sel->r, sel->which); *rect = sel->r; /* Reset source crop selection */ @@ -1914,28 +1916,28 @@ crop.pad = MSM_VFE_PAD_SRC; crop.target = V4L2_SEL_TGT_CROP; crop.r = *rect; - ret = vfe_set_selection(sd, cfg, &crop); + ret = vfe_set_selection(sd, sd_state, &crop); } else if (sel->target == V4L2_SEL_TGT_CROP && sel->pad == MSM_VFE_PAD_SRC) { struct v4l2_subdev_format fmt = { 0 }; - rect = __vfe_get_crop(line, cfg, sel->which); + rect = __vfe_get_crop(line, sd_state, sel->which); if (rect == NULL) return -EINVAL; - vfe_try_crop(line, cfg, &sel->r, sel->which); + vfe_try_crop(line, sd_state, &sel->r, sel->which); *rect = sel->r; /* Reset source pad format width and height */ fmt.which = sel->which; fmt.pad = MSM_VFE_PAD_SRC; - ret = vfe_get_format(sd, cfg, &fmt); + ret = vfe_get_format(sd, sd_state, &fmt); if (ret < 0) return ret; fmt.format.width = rect->width; fmt.format.height = rect->height; - ret = vfe_set_format(sd, cfg, &fmt); + ret = vfe_set_format(sd, sd_state, &fmt); } else { ret = -EINVAL; } @@ -1965,7 +1967,7 @@ } }; - return vfe_set_format(sd, fh ? fh->pad : NULL, &format); + return vfe_set_format(sd, fh ? fh->state : NULL, &format); } /* diff -Naur --no-dereference a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c --- a/drivers/media/platform/qcom/camss/camss-video.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/qcom/camss/camss-video.c 2022-01-06 12:45:53.814318107 -0500 @@ -442,7 +442,7 @@ struct v4l2_subdev *subdev; int ret; - ret = media_pipeline_start(&vdev->entity, &video->pipe); + ret = media_pipeline_start(vdev->entity.pads, &video->pipe); if (ret < 0) return ret; @@ -471,7 +471,7 @@ return 0; error: - media_pipeline_stop(&vdev->entity); + media_pipeline_stop(vdev->entity.pads); video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); @@ -502,7 +502,7 @@ v4l2_subdev_call(subdev, video, s_stream, 0); } - media_pipeline_stop(&vdev->entity); + media_pipeline_stop(vdev->entity.pads); video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); } diff -Naur --no-dereference a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c --- a/drivers/media/platform/rcar-vin/rcar-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/rcar-vin/rcar-core.c 2022-01-06 12:45:53.814318107 -0500 @@ -132,13 +132,17 @@ return 0; /* - * Don't allow link changes if any entity in the graph is - * streaming, modifying the CHSEL register fields can disrupt - * running streams. + * Don't allow link changes if any stream in the graph is active as + * modifying the CHSEL register fields can disrupt running streams. */ - media_device_for_each_entity(entity, &group->mdev) - if (entity->stream_count) - return -EBUSY; + media_device_for_each_entity(entity, &group->mdev) { + struct media_pad *iter; + + media_entity_for_each_pad(entity, iter) { + if (iter->stream_count) + return -EBUSY; + } + } mutex_lock(&group->lock); diff -Naur --no-dereference a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c --- a/drivers/media/platform/rcar-vin/rcar-csi2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c 2022-01-06 12:45:53.814318107 -0500 @@ -710,7 +710,7 @@ } static int rcsi2_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct rcar_csi2 *priv = sd_to_csi2(sd); @@ -722,7 +722,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { priv->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, cfg, 0); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); *framefmt = format->format; } @@ -730,7 +730,7 @@ } static int rcsi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct rcar_csi2 *priv = sd_to_csi2(sd); @@ -738,7 +738,7 @@ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = priv->mf; else - format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); return 0; } diff -Naur --no-dereference a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c --- a/drivers/media/platform/rcar-vin/rcar-dma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/rcar-vin/rcar-dma.c 2022-01-06 12:45:53.814318107 -0500 @@ -1235,7 +1235,7 @@ sd = media_entity_to_v4l2_subdev(pad->entity); if (!on) { - media_pipeline_stop(&vin->vdev.entity); + media_pipeline_stop(vin->vdev.entity.pads); return v4l2_subdev_call(sd, video, s_stream, 0); } @@ -1251,8 +1251,8 @@ */ mdev = vin->vdev.entity.graph_obj.mdev; mutex_lock(&mdev->graph_mutex); - pipe = sd->entity.pipe ? sd->entity.pipe : &vin->vdev.pipe; - ret = __media_pipeline_start(&vin->vdev.entity, pipe); + pipe = sd->entity.pads->pipe ? sd->entity.pads->pipe : &vin->vdev.pipe; + ret = __media_pipeline_start(vin->vdev.entity.pads, pipe); mutex_unlock(&mdev->graph_mutex); if (ret) return ret; @@ -1261,7 +1261,7 @@ if (ret == -ENOIOCTLCMD) ret = 0; if (ret) - media_pipeline_stop(&vin->vdev.entity); + media_pipeline_stop(vin->vdev.entity.pads); return ret; } diff -Naur --no-dereference a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c 2022-01-06 12:45:53.814318107 -0500 @@ -243,7 +243,7 @@ struct v4l2_rect *src_rect) { struct v4l2_subdev *sd = vin_to_source(vin); - struct v4l2_subdev_pad_config *pad_cfg; + struct v4l2_subdev_state *sd_state; struct v4l2_subdev_format format = { .which = which, .pad = vin->parallel->source_pad, @@ -252,8 +252,8 @@ u32 width, height; int ret; - pad_cfg = v4l2_subdev_alloc_pad_config(sd); - if (pad_cfg == NULL) + sd_state = v4l2_subdev_alloc_state(sd); + if (sd_state == NULL) return -ENOMEM; if (!rvin_format_from_pixel(vin, pix->pixelformat)) @@ -266,7 +266,7 @@ width = pix->width; height = pix->height; - ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); + ret = v4l2_subdev_call(sd, pad, set_fmt, sd_state, &format); if (ret < 0 && ret != -ENOIOCTLCMD) goto done; ret = 0; @@ -288,7 +288,7 @@ rvin_format_align(vin, pix); done: - v4l2_subdev_free_pad_config(pad_cfg); + v4l2_subdev_free_state(sd_state); return ret; } diff -Naur --no-dereference a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c --- a/drivers/media/platform/renesas-ceu.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/renesas-ceu.c 2022-01-06 12:45:53.814318107 -0500 @@ -794,6 +794,9 @@ struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; const struct ceu_fmt *ceu_fmt; u32 mbus_code_old; u32 mbus_code; @@ -850,13 +853,13 @@ * time. */ sd_format.format.code = mbus_code; - ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format); + ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_state, &sd_format); if (ret) { if (ret == -EINVAL) { /* fallback */ sd_format.format.code = mbus_code_old; ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, - &pad_cfg, &sd_format); + &pad_state, &sd_format); } if (ret) diff -Naur --no-dereference a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c --- a/drivers/media/platform/s3c-camif/camif-capture.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/s3c-camif/camif-capture.c 2022-01-06 12:45:53.814318107 -0500 @@ -848,13 +848,13 @@ if (s3c_vp_active(vp)) return 0; - ret = media_pipeline_start(sensor, camif->m_pipeline); + ret = media_pipeline_start(sensor->pads, camif->m_pipeline); if (ret < 0) return ret; ret = camif_pipeline_validate(camif); if (ret < 0) { - media_pipeline_stop(sensor); + media_pipeline_stop(sensor->pads); return ret; } @@ -878,7 +878,7 @@ ret = vb2_streamoff(&vp->vb_queue, type); if (ret == 0) - media_pipeline_stop(&camif->sensor.sd->entity); + media_pipeline_stop(camif->sensor.sd->entity.pads); return ret; } @@ -1199,7 +1199,7 @@ */ static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(camif_mbus_formats)) @@ -1210,14 +1210,14 @@ } static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct camif_dev *camif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1278,7 +1278,7 @@ } static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1306,7 +1306,7 @@ __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1345,7 +1345,7 @@ } static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1358,7 +1358,7 @@ return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); return 0; } @@ -1432,7 +1432,7 @@ } static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1446,7 +1446,7 @@ __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; + *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff -Naur --no-dereference a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c --- a/drivers/media/platform/stm32/stm32-dcmi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/stm32/stm32-dcmi.c 2022-01-06 12:45:53.814318107 -0500 @@ -607,7 +607,7 @@ } static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, - struct v4l2_subdev_pad_config *pad_cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct media_entity *entity = &dcmi->entity.source->entity; @@ -649,7 +649,7 @@ format->format.width, format->format.height); fmt.pad = pad->index; - ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &fmt); + ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt); if (ret < 0) { dev_err(dcmi->dev, "%s: Failed to set format 0x%x %ux%u on \"%s\":%d pad (%d)\n", __func__, format->format.code, @@ -737,7 +737,7 @@ goto err_pm_put; } - ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline); + ret = media_pipeline_start(dcmi->vdev->entity.pads, &dcmi->pipeline); if (ret < 0) { dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n", __func__, ret); @@ -834,7 +834,7 @@ dcmi_pipeline_stop(dcmi); err_media_pipeline_stop: - media_pipeline_stop(&dcmi->vdev->entity); + media_pipeline_stop(dcmi->vdev->entity.pads); err_pm_put: pm_runtime_put(dcmi->dev); @@ -860,7 +860,7 @@ dcmi_pipeline_stop(dcmi); - media_pipeline_stop(&dcmi->vdev->entity); + media_pipeline_stop(dcmi->vdev->entity.pads); spin_lock_irq(&dcmi->irqlock); @@ -967,6 +967,9 @@ struct dcmi_framesize sd_fsize; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1002,7 +1005,7 @@ v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, - &pad_cfg, &format); + &pad_state, &format); if (ret < 0) return ret; @@ -1151,6 +1154,9 @@ .which = V4L2_SUBDEV_FORMAT_TRY, }; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; int ret; sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); @@ -1164,7 +1170,7 @@ v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, - &pad_cfg, &format); + &pad_state, &format); if (ret < 0) return ret; diff -Naur --no-dereference a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c 2022-01-06 12:45:53.814318107 -0500 @@ -266,7 +266,7 @@ goto err_clear_dma_queue; } - ret = media_pipeline_start(&csi->vdev.entity, &csi->vdev.pipe); + ret = media_pipeline_start(csi->vdev.entity.pads, &csi->vdev.pipe); if (ret < 0) goto err_free_scratch_buffer; @@ -330,7 +330,7 @@ sun4i_csi_capture_stop(csi); err_disable_pipeline: - media_pipeline_stop(&csi->vdev.entity); + media_pipeline_stop(csi->vdev.entity.pads); err_free_scratch_buffer: dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr, @@ -359,7 +359,7 @@ return_all_buffers(csi, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&csi->qlock, flags); - media_pipeline_stop(&csi->vdev.entity); + media_pipeline_stop(csi->vdev.entity.pads); dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr, csi->scratch.paddr); diff -Naur --no-dereference a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c 2022-01-06 12:45:53.814318107 -0500 @@ -273,25 +273,26 @@ }; static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(subdev, cfg, CSI_SUBDEV_SINK); + fmt = v4l2_subdev_get_try_format(subdev, sd_state, CSI_SUBDEV_SINK); *fmt = sun4i_csi_pad_fmt_default; return 0; } static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct sun4i_csi *csi = container_of(subdev, struct sun4i_csi, subdev); struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, cfg, fmt->pad); + subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, + fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -301,14 +302,15 @@ } static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct sun4i_csi *csi = container_of(subdev, struct sun4i_csi, subdev); struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, cfg, fmt->pad); + subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, + fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -327,7 +329,7 @@ static int sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *mbus) { if (mbus->index >= ARRAY_SIZE(sun4i_csi_formats)) diff -Naur --no-dereference a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c 2022-01-06 12:45:53.814318107 -0500 @@ -141,7 +141,7 @@ video->sequence = 0; - ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe); + ret = media_pipeline_start(video->vdev.entity.pads, &video->vdev.pipe); if (ret < 0) goto clear_dma_queue; @@ -207,7 +207,7 @@ stop_csi_stream: sun6i_csi_set_stream(video->csi, false); stop_media_pipeline: - media_pipeline_stop(&video->vdev.entity); + media_pipeline_stop(video->vdev.entity.pads); clear_dma_queue: spin_lock_irqsave(&video->dma_queue_lock, flags); list_for_each_entry(buf, &video->dma_queue, list) @@ -231,7 +231,7 @@ sun6i_csi_set_stream(video->csi, false); - media_pipeline_stop(&video->vdev.entity); + media_pipeline_stop(video->vdev.entity.pads); /* Release all active buffers */ spin_lock_irqsave(&video->dma_queue_lock, flags); diff -Naur --no-dereference a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c --- a/drivers/media/platform/ti/cal/cal.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/cal/cal.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - Driver + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "cal.h" +#include "cal_regs.h" + +MODULE_DESCRIPTION("TI CAL driver"); +MODULE_AUTHOR("Benoit Parrot, "); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1.0"); + +int cal_video_nr = -1; +module_param_named(video_nr, cal_video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); + +unsigned int cal_debug; +module_param_named(debug, cal_debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#ifdef CONFIG_VIDEO_TI_CAL_MC +#define CAL_MC_API_DEFAULT 1 +#else +#define CAL_MC_API_DEFAULT 0 +#endif + +bool cal_mc_api = CAL_MC_API_DEFAULT; +module_param_named(mc_api, cal_mc_api, bool, 0444); +MODULE_PARM_DESC(mc_api, "activates the MC API"); + +/* ------------------------------------------------------------------ + * Format Handling + * ------------------------------------------------------------------ + */ + +const struct cal_format_info cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, +}; + +const unsigned int cal_num_formats = ARRAY_SIZE(cal_formats); + +const struct cal_format_info *cal_format_by_fourcc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { + if (cal_formats[i].fourcc == fourcc) + return &cal_formats[i]; + } + + return NULL; +} + +const struct cal_format_info *cal_format_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { + if (cal_formats[i].code == code) + return &cal_formats[i]; + } + + return NULL; +} + +/* ------------------------------------------------------------------ + * Platform Data + * ------------------------------------------------------------------ + */ + +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { + { + .fields = { + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, + }, + .num_lanes = 4, + }, + { + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, + }, + .num_lanes = 2, + }, +}; + +static const struct cal_data dra72x_cal_data = { + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), +}; + +static const struct cal_data dra72x_es1_cal_data = { + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), + .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, +}; + +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { + { + .fields = { + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, + }, + .num_lanes = 5, + }, + { + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, + }, + .num_lanes = 3, + }, +}; + +static const struct cal_data dra76x_cal_data = { + .camerarx = dra76x_cal_csi_phy, + .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), +}; + +static const struct cal_camerarx_data am654_cal_csi_phy[] = { + { + .fields = { + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, + }, + .num_lanes = 5, + }, +}; + +static const struct cal_data am654_cal_data = { + .camerarx = am654_cal_csi_phy, + .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), +}; + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +void cal_quickdump_regs(struct cal_dev *cal) +{ + unsigned int i; + + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->base, + resource_size(cal->res), false); + + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + struct cal_camerarx *phy = cal->phy[i]; + + cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, + &phy->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)phy->base, + resource_size(phy->res), + false); + } +} + +/* ------------------------------------------------------------------ + * Context Management + * ------------------------------------------------------------------ + */ + +#define CAL_MAX_PIX_PROC 4 + +static int cal_reserve_pix_proc(struct cal_dev *cal) +{ + unsigned long ret; + + spin_lock(&cal->v4l2_dev.lock); + + ret = find_first_zero_bit(&cal->reserved_pix_proc_mask, CAL_MAX_PIX_PROC); + + if (ret == CAL_MAX_PIX_PROC) { + spin_unlock(&cal->v4l2_dev.lock); + return -ENOSPC; + } + + cal->reserved_pix_proc_mask |= BIT(ret); + + spin_unlock(&cal->v4l2_dev.lock); + + return ret; +} + +static void cal_release_pix_proc(struct cal_dev *cal, unsigned int pix_proc_num) +{ + spin_lock(&cal->v4l2_dev.lock); + + cal->reserved_pix_proc_mask &= ~BIT(pix_proc_num); + + spin_unlock(&cal->v4l2_dev.lock); +} + +static void cal_ctx_csi2_config(struct cal_ctx *ctx) +{ + u32 val; + + val = cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx)); + cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); + /* + * DT type: MIPI CSI-2 Specs + * 0x1: All - DT filter is disabled + * 0x24: RGB888 1 pixel = 3 bytes + * 0x2B: RAW10 4 pixels = 5 bytes + * 0x2A: RAW8 1 pixel = 1 byte + * 0x1E: YUV422 2 pixels = 4 bytes + */ + cal_set_field(&val, ctx->datatype, CAL_CSI2_CTX_DT_MASK); + cal_set_field(&val, ctx->vc, CAL_CSI2_CTX_VC_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); + cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); + cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, + CAL_CSI2_CTX_PACK_MODE_MASK); + cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX(%u, %u) = 0x%08x\n", + ctx->phy->instance, ctx->csi2_ctx, + cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx))); +} + +static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) +{ + u32 val, extract, pack; + + switch (ctx->fmtinfo->bpp) { + case 8: + extract = CAL_PIX_PROC_EXTRACT_B8; + pack = CAL_PIX_PROC_PACK_B8; + break; + case 10: + extract = CAL_PIX_PROC_EXTRACT_B10_MIPI; + pack = CAL_PIX_PROC_PACK_B16; + break; + case 12: + extract = CAL_PIX_PROC_EXTRACT_B12_MIPI; + pack = CAL_PIX_PROC_PACK_B16; + break; + case 16: + extract = CAL_PIX_PROC_EXTRACT_B16_LE; + pack = CAL_PIX_PROC_PACK_B16; + break; + default: + /* + * If you see this warning then it means that you added + * some new entry in the cal_formats[] array with a different + * bit per pixel values then the one supported below. + * Either add support for the new bpp value below or adjust + * the new entry to use one of the value below. + * + * Instead of failing here just use 8 bpp as a default. + */ + dev_warn_once(ctx->cal->dev, + "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", + __FILE__, __LINE__, __func__, ctx->fmtinfo->bpp); + extract = CAL_PIX_PROC_EXTRACT_B8; + pack = CAL_PIX_PROC_PACK_B8; + break; + } + + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc)); + cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); + cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); + cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); + cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); + cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%u) = 0x%08x\n", ctx->pix_proc, + cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc))); +} + +static void cal_ctx_wr_dma_config(struct cal_ctx *ctx) +{ + unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline; + u32 val; + + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); + cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, + CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, + CAL_WR_DMA_CTRL_DTAG_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, + CAL_WR_DMA_CTRL_PATTERN_MASK); + cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->dma_ctx, + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx))); + + cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx), + stride / 16, CAL_WR_DMA_OFST_MASK); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->dma_ctx, + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx))); + + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx)); + /* 64 bit word means no skipping */ + cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); + /* + * The XSIZE field is expressed in 64-bit units and prevents overflows + * in case of synchronization issues by limiting the number of bytes + * written per line. + */ + cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->dma_ctx, + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx))); +} + +void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) +{ + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->dma_ctx), addr); +} + +static void cal_ctx_wr_dma_enable(struct cal_ctx *ctx) +{ + u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); + + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); +} + +static void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) +{ + u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); + + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); +} + +static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) +{ + bool stopped; + + spin_lock_irq(&ctx->dma.lock); + stopped = ctx->dma.state == CAL_DMA_STOPPED; + spin_unlock_irq(&ctx->dma.lock); + + return stopped; +} + +static int +cal_get_remote_frame_desc_entry(struct cal_camerarx *phy, u32 stream, + struct v4l2_mbus_frame_desc_entry *entry) +{ + struct v4l2_mbus_frame_desc fd; + unsigned int i; + int ret; + + ret = cal_camerarx_get_remote_frame_desc(phy, &fd); + if (ret) { + if (ret != -ENOIOCTLCMD) + dev_err(phy->cal->dev, + "Failed to get remote frame desc: %d\n", ret); + return ret; + } + + for (i = 0; i < fd.num_entries; i++) { + if (stream == fd.entry[i].stream) { + *entry = fd.entry[i]; + return 0; + } + } + + return -ENODEV; +} + +int cal_ctx_prepare(struct cal_ctx *ctx) +{ + struct v4l2_mbus_frame_desc_entry entry; + int ret; + + ret = cal_get_remote_frame_desc_entry(ctx->phy, ctx->stream, &entry); + + if (ret == -ENOIOCTLCMD) { + ctx->vc = 0; + ctx->datatype = CAL_CSI2_CTX_DT_ANY; + } else if (!ret) { + ctx_dbg(2, ctx, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", + entry.stream, + entry.length, + entry.bus.csi2.vc, + entry.bus.csi2.dt); + + ctx->vc = entry.bus.csi2.vc; + ctx->datatype = entry.bus.csi2.dt; + } else { + ctx_err(ctx, "Failed to get remote frame desc: %d\n", ret); + return ret; + } + + ctx->use_pix_proc = !ctx->fmtinfo->meta; + + if (ctx->use_pix_proc) { + ret = cal_reserve_pix_proc(ctx->cal); + if (ret < 0) { + ctx_err(ctx, "Failed to reserve pix proc: %d\n", ret); + return ret; + } + + ctx->pix_proc = ret; + } + + return 0; +} + +void cal_ctx_unprepare(struct cal_ctx *ctx) +{ + if (ctx->use_pix_proc) + cal_release_pix_proc(ctx->cal, ctx->pix_proc); +} + +void cal_ctx_start(struct cal_ctx *ctx) +{ + ctx->dma.state = CAL_DMA_RUNNING; + + /* Configure the CSI-2, pixel processing and write DMA contexts. */ + cal_ctx_csi2_config(ctx); + if (ctx->use_pix_proc) + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx); + + /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), + CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), + CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx)); + + cal_ctx_wr_dma_enable(ctx); +} + +void cal_ctx_stop(struct cal_ctx *ctx) +{ + long timeout; + + /* + * Request DMA stop and wait until it completes. If completion times + * out, forcefully disable the DMA. + */ + spin_lock_irq(&ctx->dma.lock); + ctx->dma.state = CAL_DMA_STOP_REQUESTED; + spin_unlock_irq(&ctx->dma.lock); + + timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), + msecs_to_jiffies(500)); + if (!timeout) { + ctx_err(ctx, "failed to disable dma cleanly\n"); + cal_ctx_wr_dma_disable(ctx); + } + + /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), + CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), + CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx)); + + ctx->dma.state = CAL_DMA_STOPPED; + + /* Disable CSI2 context */ + cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), 0); + + /* Disable pix proc */ + if (ctx->use_pix_proc) + cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), 0); +} + +/* ------------------------------------------------------------------ + * IRQ Handling + * ------------------------------------------------------------------ + */ + +static inline void cal_irq_wdma_start(struct cal_ctx *ctx) +{ + spin_lock(&ctx->dma.lock); + + if (ctx->dma.state == CAL_DMA_STOP_REQUESTED) { + /* + * If a stop is requested, disable the write DMA context + * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed, + * the current frame will complete and the DMA will then stop. + */ + cal_ctx_wr_dma_disable(ctx); + ctx->dma.state = CAL_DMA_STOP_PENDING; + } else if (!list_empty(&ctx->dma.queue) && !ctx->dma.pending) { + /* + * Otherwise, if a new buffer is available, queue it to the + * hardware. + */ + struct cal_buffer *buf; + dma_addr_t addr; + + buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, + list); + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + cal_ctx_set_dma_addr(ctx, addr); + + ctx->dma.pending = buf; + list_del(&buf->list); + } + + spin_unlock(&ctx->dma.lock); +} + +static inline void cal_irq_wdma_end(struct cal_ctx *ctx) +{ + struct cal_buffer *buf = NULL; + u32 frame_num; + + frame_num = cal_read(ctx->cal, CAL_CSI2_STATUS(ctx->phy->instance, + ctx->csi2_ctx)) & 0xffff; + + spin_lock(&ctx->dma.lock); + + /* If the DMA context was stopping, it is now stopped. */ + if (ctx->dma.state == CAL_DMA_STOP_PENDING) { + ctx->dma.state = CAL_DMA_STOPPED; + wake_up(&ctx->dma.wait); + } + + /* If a new buffer was queued, complete the current buffer. */ + if (ctx->dma.pending) { + buf = ctx->dma.active; + ctx->dma.active = ctx->dma.pending; + ctx->dma.pending = NULL; + } + + spin_unlock(&ctx->dma.lock); + + if (buf) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.field = ctx->v_fmt.fmt.pix.field; + buf->vb.sequence = frame_num; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } +} + +static irqreturn_t cal_irq(int irq_cal, void *data) +{ + struct cal_dev *cal = data; + u32 status; + + status = cal_read(cal, CAL_HL_IRQSTATUS(0)); + if (status) { + unsigned int i; + + cal_write(cal, CAL_HL_IRQSTATUS(0), status); + + if (status & CAL_HL_IRQ_OCPO_ERR_MASK) + dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); + + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + if (status & CAL_HL_IRQ_CIO_MASK(i)) { + u32 cio_stat = cal_read(cal, + CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); + + dev_err_ratelimited(cal->dev, + "CIO%u error: %#08x\n", i, cio_stat); + + cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + cio_stat); + } + + if (status & CAL_HL_IRQ_VC_MASK(i)) { + u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i)); + + dev_err_ratelimited(cal->dev, + "CIO%u VC error: %#08x\n", + i, vc_stat); + + cal_write(cal, CAL_CSI2_VC_IRQSTATUS(i), vc_stat); + } + } + } + + /* Check which DMA just finished */ + status = cal_read(cal, CAL_HL_IRQSTATUS(1)); + if (status) { + unsigned int i; + + /* Clear Interrupt status */ + cal_write(cal, CAL_HL_IRQSTATUS(1), status); + + for (i = 0; i < cal->num_contexts; ++i) { + if (status & CAL_HL_IRQ_WDMA_END_MASK(i)) + cal_irq_wdma_end(cal->ctx[i]); + } + } + + /* Check which DMA just started */ + status = cal_read(cal, CAL_HL_IRQSTATUS(2)); + if (status) { + unsigned int i; + + /* Clear Interrupt status */ + cal_write(cal, CAL_HL_IRQSTATUS(2), status); + + for (i = 0; i < cal->num_contexts; ++i) { + if (status & CAL_HL_IRQ_WDMA_START_MASK(i)) + cal_irq_wdma_start(cal->ctx[i]); + } + } + + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------------ + * Asynchronous V4L2 subdev binding + * ------------------------------------------------------------------ + */ + +struct cal_v4l2_async_subdev { + struct v4l2_async_subdev asd; /* Must be first */ + struct cal_camerarx *phy; +}; + +static inline struct cal_v4l2_async_subdev * +to_cal_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct cal_v4l2_async_subdev, asd); +} + +static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct cal_camerarx *phy = to_cal_asd(asd)->phy; + int pad; + int ret; + + if (phy->source) { + phy_info(phy, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + phy->source = subdev; + phy_dbg(1, phy, "Using source %s for capture\n", subdev->name); + + pad = media_entity_get_fwnode_pad(&subdev->entity, + of_fwnode_handle(phy->source_ep_node), + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + phy_err(phy, "Source %s has no connected source pad\n", + subdev->name); + return pad; + } + + ret = media_create_pad_link(&subdev->entity, pad, + &phy->subdev.entity, CAL_CAMERARX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + phy_err(phy, "Failed to create media link for source %s\n", + subdev->name); + return ret; + } + + return 0; +} + +static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); + unsigned int i; + int ret = 0; + + for (i = 0; i < cal->num_contexts; ++i) { + ret = cal_ctx_v4l2_register(cal->ctx[i]); + if (ret) + return ret; + } + + if (cal_mc_api) + ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev); + + return ret; +} + +static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { + .bound = cal_async_notifier_bound, + .complete = cal_async_notifier_complete, +}; + +static int cal_async_notifier_register(struct cal_dev *cal) +{ + unsigned int i; + int ret; + + v4l2_async_notifier_init(&cal->notifier); + cal->notifier.ops = &cal_async_notifier_ops; + + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + struct cal_camerarx *phy = cal->phy[i]; + struct cal_v4l2_async_subdev *casd; + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode; + + if (!phy->source_node) + continue; + + fwnode = of_fwnode_handle(phy->source_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + sizeof(*casd)); + if (IS_ERR(asd)) { + phy_err(phy, "Failed to add subdev to notifier\n"); + ret = PTR_ERR(asd); + goto error; + } + + casd = to_cal_asd(asd); + casd->phy = phy; + } + + ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); + if (ret) { + cal_err(cal, "Error registering async notifier\n"); + goto error; + } + + return 0; + +error: + v4l2_async_notifier_cleanup(&cal->notifier); + return ret; +} + +static void cal_async_notifier_unregister(struct cal_dev *cal) +{ + v4l2_async_notifier_unregister(&cal->notifier); + v4l2_async_notifier_cleanup(&cal->notifier); +} + +/* ------------------------------------------------------------------ + * Media and V4L2 device handling + * ------------------------------------------------------------------ + */ + +/* + * Register user-facing devices. To be called at the end of the probe function + * when all resources are initialized and ready. + */ +static int cal_media_register(struct cal_dev *cal) +{ + int ret; + + ret = media_device_register(&cal->mdev); + if (ret) { + cal_err(cal, "Failed to register media device\n"); + return ret; + } + + /* + * Register the async notifier. This may trigger registration of the + * V4L2 video devices if all subdevs are ready. + */ + ret = cal_async_notifier_register(cal); + if (ret) { + media_device_unregister(&cal->mdev); + return ret; + } + + return 0; +} + +/* + * Unregister the user-facing devices, but don't free memory yet. To be called + * at the beginning of the remove function, to disallow access from userspace. + */ +static void cal_media_unregister(struct cal_dev *cal) +{ + unsigned int i; + + /* Unregister all the V4L2 video devices. */ + for (i = 0; i < cal->num_contexts; i++) + cal_ctx_v4l2_unregister(cal->ctx[i]); + + cal_async_notifier_unregister(cal); + media_device_unregister(&cal->mdev); +} + +/* + * Initialize the in-kernel objects. To be called at the beginning of the probe + * function, before the V4L2 device is used by the driver. + */ +static int cal_media_init(struct cal_dev *cal) +{ + struct media_device *mdev = &cal->mdev; + int ret; + + mdev->dev = cal->dev; + mdev->hw_revision = cal->revision; + strscpy(mdev->model, "CAL", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + media_device_init(mdev); + + /* + * Initialize the V4L2 device (despite the function name, this performs + * initialization, not registration). + */ + cal->v4l2_dev.mdev = mdev; + ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); + return ret; + } + + vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); + + return 0; +} + +/* + * Cleanup the in-kernel objects, freeing memory. To be called at the very end + * of the remove sequence, when nothing (including userspace) can access the + * objects anymore. + */ +static void cal_media_cleanup(struct cal_dev *cal) +{ + v4l2_device_unregister(&cal->v4l2_dev); + media_device_cleanup(&cal->mdev); + + vb2_dma_contig_clear_max_seg_size(cal->dev); +} + +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + +static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) +{ + struct cal_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + ctx->cal = cal; + ctx->dma_ctx = inst; + ctx->csi2_ctx = inst; + ctx->cport = inst; + ctx->stream = 0; + + ret = cal_ctx_v4l2_init(ctx); + if (ret) + return NULL; + + return ctx; +} + +static void cal_ctx_destroy(struct cal_ctx *ctx) +{ + cal_ctx_v4l2_cleanup(ctx); + + kfree(ctx); +} + +static const struct of_device_id cal_of_match[] = { + { + .compatible = "ti,dra72-cal", + .data = (void *)&dra72x_cal_data, + }, + { + .compatible = "ti,dra72-pre-es2-cal", + .data = (void *)&dra72x_es1_cal_data, + }, + { + .compatible = "ti,dra76-cal", + .data = (void *)&dra76x_cal_data, + }, + { + .compatible = "ti,am654-cal", + .data = (void *)&am654_cal_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cal_of_match); + +/* Get hardware revision and info. */ + +#define CAL_HL_HWINFO_VALUE 0xa3c90469 + +static void cal_get_hwinfo(struct cal_dev *cal) +{ + u32 hwinfo; + + cal->revision = cal_read(cal, CAL_HL_REVISION); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { + case CAL_HL_REVISION_SCHEME_H08: + cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), + cal->revision); + break; + + case CAL_HL_REVISION_SCHEME_LEGACY: + default: + cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", + cal->revision); + break; + } + + hwinfo = cal_read(cal, CAL_HL_HWINFO); + if (hwinfo != CAL_HL_HWINFO_VALUE) + cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", + hwinfo, CAL_HL_HWINFO_VALUE); +} + +static int cal_init_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; + struct regmap_config config = { }; + struct regmap *syscon; + struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } + + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(cal->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + syscon = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(syscon)) { + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); + } + + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = 0; + + return 0; +} + +static int cal_probe(struct platform_device *pdev) +{ + struct cal_dev *cal; + bool connected = false; + unsigned int i; + int ret; + int irq; + + cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); + if (!cal) + return -ENOMEM; + + cal->data = of_device_get_match_data(&pdev->dev); + if (!cal->data) { + dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); + return -ENODEV; + } + + cal->dev = &pdev->dev; + platform_set_drvdata(pdev, cal); + + /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ + cal->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(cal->fclk)) { + dev_err(&pdev->dev, "cannot get CAL fclk\n"); + return PTR_ERR(cal->fclk); + } + + ret = cal_init_camerarx_regmap(cal); + if (ret < 0) + return ret; + + cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "cal_top"); + cal->base = devm_ioremap_resource(&pdev->dev, cal->res); + if (IS_ERR(cal->base)) + return PTR_ERR(cal->base); + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + cal->res->name, &cal->res->start, &cal->res->end); + + irq = platform_get_irq(pdev, 0); + cal_dbg(1, cal, "got irq# %d\n", irq); + ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, + cal); + if (ret) + return ret; + + /* Read the revision and hardware info to verify hardware access. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) + goto error_pm_runtime; + + cal_get_hwinfo(cal); + pm_runtime_put_sync(&pdev->dev); + + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) + goto error_pm_runtime; + + /* Create CAMERARX PHYs. */ + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + cal->phy[i] = cal_camerarx_create(cal, i); + if (IS_ERR(cal->phy[i])) { + ret = PTR_ERR(cal->phy[i]); + cal->phy[i] = NULL; + goto error_camerarx; + } + + if (cal->phy[i]->source_node) + connected = true; + } + + if (!connected) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); + ret = -ENODEV; + goto error_camerarx; + } + + /* Create contexts. */ + if (!cal_mc_api) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + if (!cal->phy[i]->source_node) + continue; + + cal->ctx[i] = cal_ctx_create(cal, i); + if (!cal->ctx[i]) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } + + cal->ctx[i]->phy = cal->phy[i]; + + cal->num_contexts++; + } + } else { + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + cal->ctx[i] = cal_ctx_create(cal, i); + if (!cal->ctx[i]) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } + + cal->num_contexts++; + } + } + + /* Register the media device. */ + ret = cal_media_register(cal); + if (ret) + goto error_context; + + return 0; + +error_context: + for (i = 0; i < cal->num_contexts; i++) + cal_ctx_destroy(cal->ctx[i]); + +error_camerarx: + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_destroy(cal->phy[i]); + + cal_media_cleanup(cal); + +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int cal_remove(struct platform_device *pdev) +{ + struct cal_dev *cal = platform_get_drvdata(pdev); + unsigned int i; + int ret; + + cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); + + ret = pm_runtime_resume_and_get(&pdev->dev); + + cal_media_unregister(cal); + + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_disable(cal->phy[i]); + + for (i = 0; i < cal->num_contexts; i++) + cal_ctx_destroy(cal->ctx[i]); + + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_destroy(cal->phy[i]); + + cal_media_cleanup(cal); + + if (ret >= 0) + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int cal_runtime_resume(struct device *dev) +{ + struct cal_dev *cal = dev_get_drvdata(dev); + unsigned int i; + u32 val; + + if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + /* + * Apply errata on both port everytime we (re-)enable + * the clock + */ + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_i913_errata(cal->phy[i]); + } + + /* + * Enable global interrupts that are not related to a particular + * CAMERARAX or context. + */ + cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + + val = cal_read(cal, CAL_CTRL); + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, + CAL_CTRL_BURSTSIZE_MASK); + cal_set_field(&val, 0xf, CAL_CTRL_TAGCNT_MASK); + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + cal_set_field(&val, 0xff, CAL_CTRL_MFLAGL_MASK); + cal_set_field(&val, 0xff, CAL_CTRL_MFLAGH_MASK); + cal_write(cal, CAL_CTRL, val); + cal_dbg(3, cal, "CAL_CTRL = 0x%08x\n", cal_read(cal, CAL_CTRL)); + + return 0; +} + +static const struct dev_pm_ops cal_pm_ops = { + .runtime_resume = cal_runtime_resume, +}; + +static struct platform_driver cal_pdrv = { + .probe = cal_probe, + .remove = cal_remove, + .driver = { + .name = CAL_MODULE_NAME, + .pm = &cal_pm_ops, + .of_match_table = cal_of_match, + }, +}; + +module_platform_driver(cal_pdrv); diff -Naur --no-dereference a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c --- a/drivers/media/platform/ti/cal/cal-camerarx.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/cal/cal-camerarx.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - CAMERARX + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cal.h" +#include "cal_regs.h" + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) +{ + return ioread32(phy->base + offset); +} + +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) +{ + iowrite32(val, phy->base + offset); +} + +/* ------------------------------------------------------------------ + * CAMERARX Management + * ------------------------------------------------------------------ + */ + +static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + u32 bpp; + s64 freq; + + /* + * With multistream input we don't have bpp, and cannot use + * V4L2_CID_PIXEL_RATE. Passing 0 as bpp causes v4l2_get_link_freq() + * to return an error if it falls back to V4L2_CID_PIXEL_RATE. + */ + + if (phy->stream_configs.num_configs == 0) + return -EINVAL; + + if (phy->stream_configs.num_configs > 2) { + bpp = 0; + } else { + const struct cal_format_info *fmtinfo; + struct v4l2_mbus_framefmt *fmt; + + /* The first format is for the sink */ + fmt = &phy->stream_configs.configs[0].fmt; + + fmtinfo = cal_format_by_code(fmt->code); + if (!fmtinfo) + return -EINVAL; + + bpp = fmtinfo->bpp; + } + + freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes); + if (freq < 0) { + phy_err(phy, "failed to get link freq for subdev '%s'\n", + phy->source->name); + return freq; + } + + phy_dbg(3, phy, "Source Link Freq: %llu\n", freq); + + return freq; +} + +static void cal_camerarx_lane_config(struct cal_camerarx *phy) +{ + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + int lane; + + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); +} + +static void cal_camerarx_enable(struct cal_camerarx *phy) +{ + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; + + regmap_field_write(phy->fields[F_CAMMODE], 0); + /* Always enable all lanes at the phy control level */ + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); + /* F_CSI_MODE is not present on every architecture */ + if (phy->fields[F_CSI_MODE]) + regmap_field_write(phy->fields[F_CSI_MODE], 1); + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); +} + +void cal_camerarx_disable(struct cal_camerarx *phy) +{ + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); +} + +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +static void cal_camerarx_config(struct cal_camerarx *phy, s64 link_freq) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + + /* DPHY timing configuration */ + + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = div_s64(20 * link_freq, 1000 * 1000 * 1000); + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = div_s64(105 * link_freq, 1000 * 1000 * 1000) + 4; + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); + + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + cal_set_field(®1, TCLK_MISS, + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); +} + +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) +{ + u32 target_state; + unsigned int i; + + target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; + + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); + + for (i = 0; i < 10; i++) { + u32 current_state; + + current_state = cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); + + if (current_state == target_state) + break; + + usleep_range(1000, 1100); + } + + if (i == 10) + phy_err(phy, "Failed to power %s complexio\n", + enable ? "up" : "down"); +} + +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); +} + +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + phy_err(phy, "Timeout waiting for stop state\n"); +} + +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + const u32 vc_err_mask = + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(0) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(1) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(2) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(3) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(0) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(1) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(2) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(3); + + /* Enable CIO & VC error IRQs. */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance) | + CAL_HL_IRQ_VC_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), + vc_err_mask); +} + +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + /* Disable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance) | + CAL_HL_IRQ_VC_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), 0); +} + +static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_ECC_EN_MASK); + + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static int cal_camerarx_start(struct cal_camerarx *phy) +{ + s64 link_freq; + u32 sscounter; + u32 val; + int ret; + + if (phy->enable_count > 0) { + phy->enable_count++; + return 0; + } + + link_freq = cal_camerarx_get_ext_link_freq(phy); + if (link_freq < 0) + return link_freq; + + ret = v4l2_subdev_call(phy->source, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { + phy_err(phy, "power on failed in subdev\n"); + return ret; + } + + cal_camerarx_enable_irqs(phy); + + /* + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / + * DRA80xM TRMs have a a slightly simplified sequence. + */ + + /* + * 1. Configure all CSI-2 low level protocol registers to be ready to + * receive signals/data from the CSI-2 PHY. + * + * i.-v. Configure the lanes position and polarity. + */ + cal_camerarx_lane_config(phy); + + /* + * vi.-vii. Configure D-PHY mode, enable the required lanes and + * enable the CAMERARX clock. + */ + cal_camerarx_enable(phy); + + /* + * 2. CSI PHY and link initialization sequence. + * + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion + * at this point, as it requires the external source to send the + * CSI-2 HS clock. + */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + + /* Dummy read to allow SCP reset to complete. */ + camerarx_read(phy, CAL_CSI2_PHY_REG0); + + /* Program the PHY timing parameters. */ + cal_camerarx_config(phy, link_freq); + + /* + * b. Assert the FORCERXMODE signal. + * + * The stop-state-counter is based on fclk cycles, and we always use + * the x16 and x4 settings, so stop-state-timeout = + * fclk-cycle * 16 * 4 * counter. + * + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we + * calculate a timeout that's 100us (rounding up). + */ + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); + + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + cal_set_field(&val, sscounter, + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* Assert the FORCERXMODE signal. */ + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* + * c. Connect pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* + * d. Power up the CSI-2 PHY. + * e. Check whether the state status reaches the ON state. + */ + cal_camerarx_power(phy, true); + + /* + * Start the source to enable the CSI-2 HS clock. We can now wait for + * CSI-2 PHY reset to complete. + */ + ret = v4l2_subdev_call(phy->source, video, s_stream, 1); + if (ret) { + v4l2_subdev_call(phy->source, core, s_power, 0); + cal_camerarx_disable_irqs(phy); + phy_err(phy, "stream on failed in subdev\n"); + return ret; + } + + cal_camerarx_wait_reset(phy); + + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); + + /* + * g. Disable pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* Finally, enable the PHY Protocol Interface (PPI). */ + cal_camerarx_ppi_enable(phy); + + phy->enable_count++; + + return 0; +} + +static void cal_camerarx_stop(struct cal_camerarx *phy) +{ + int ret; + + if (--phy->enable_count > 0) + return; + + cal_camerarx_ppi_disable(phy); + + cal_camerarx_disable_irqs(phy); + + cal_camerarx_power(phy, false); + + /* Assert Complex IO Reset */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + + /* Disable the phy */ + cal_camerarx_disable(phy); + + if (v4l2_subdev_call(phy->source, video, s_stream, 0)) + phy_err(phy, "stream off failed in subdev\n"); + + ret = v4l2_subdev_call(phy->source, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + phy_err(phy, "power off failed in subdev\n"); +} + +/* + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +void cal_camerarx_i913_errata(struct cal_camerarx *phy) +{ + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); + + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); +} + +static int cal_camerarx_regmap_init(struct cal_dev *cal, + struct cal_camerarx *phy) +{ + const struct cal_camerarx_data *phy_data; + unsigned int i; + + if (!cal->data) + return -EINVAL; + + phy_data = &cal->data->camerarx[phy->instance]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = cal->syscon_camerrx_offset, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + phy->fields[i] = devm_regmap_field_alloc(cal->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { + cal_err(cal, "Unable to allocate regmap fields\n"); + return PTR_ERR(phy->fields[i]); + } + } + + return 0; +} + +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + struct device_node *ep_node; + unsigned int i; + int ret; + + /* + * Find the endpoint node for the port corresponding to the PHY + * instance, and parse its CSI-2-related properties. + */ + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, + phy->instance, 0); + if (!ep_node) { + /* + * The endpoint is not mandatory, not all PHY instances need to + * be connected in DT. + */ + phy_dbg(3, phy, "Port has no endpoint\n"); + return 0; + } + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + phy_err(phy, "Failed to parse endpoint\n"); + goto done; + } + + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; + + if (lane > 4) { + phy_err(phy, "Invalid position %u for data lane %u\n", + lane, i); + ret = -EINVAL; + goto done; + } + + data_lanes[i*2] = '0' + lane; + data_lanes[i*2+1] = ' '; + } + + data_lanes[i*2-1] = '\0'; + + phy_dbg(3, phy, + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", + endpoint->bus.mipi_csi2.clock_lane, data_lanes, + endpoint->bus.mipi_csi2.flags); + + /* Retrieve the connected device and store it for later use. */ + phy->source_ep_node = of_graph_get_remote_endpoint(ep_node); + phy->source_node = of_graph_get_port_parent(phy->source_ep_node); + if (!phy->source_node) { + phy_dbg(3, phy, "Can't get remote parent\n"); + of_node_put(phy->source_ep_node); + ret = -EINVAL; + goto done; + } + + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->source_node); + +done: + of_node_put(ep_node); + return ret; +} + +int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *pad; + int ret; + + if (!phy->source) + return -ENODEV; + + pad = media_entity_remote_pad(&phy->pads[CAL_CAMERARX_PAD_SINK]); + if (!pad) + return -ENODEV; + + ret = v4l2_subdev_call(phy->source, pad, get_frame_desc, pad->index, + fd); + if (ret) + return ret; + + if (fd->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(phy->cal->dev, "Frame desc do not describe CSI-2 link"); + return -EINVAL; + } + + return 0; +} + +/* ------------------------------------------------------------------ + * V4L2 Subdev Operations + * ------------------------------------------------------------------ + */ + +static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cal_camerarx, subdev); +} + +struct cal_camerarx * +cal_camerarx_get_phy_from_entity(struct media_entity *entity) +{ + struct v4l2_subdev *sd; + + sd = media_entity_to_v4l2_subdev(entity); + if (!sd) + return NULL; + + return to_cal_camerarx(sd); +} + +static struct v4l2_subdev_krouting * +cal_camerarx_get_routing_table(struct cal_camerarx *phy, + struct v4l2_subdev_state *cfg, u32 which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &phy->routing; + else + return &cfg->routing; +} + +static struct v4l2_subdev_stream_configs * +cal_camerarx_get_stream_configs(struct cal_camerarx *phy, + struct v4l2_subdev_state *cfg, u32 which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &phy->stream_configs; + else + return &cfg->stream_configs; +} + +struct v4l2_mbus_framefmt * +cal_camerarx_get_stream_format(struct cal_camerarx *phy, + struct v4l2_subdev_state *cfg, + unsigned int pad, u32 stream, u32 which) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + stream_configs = cal_camerarx_get_stream_configs(phy, cfg, which); + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].fmt; + } + + return NULL; +} + +static int cal_camerarx_find_opposite_end(struct v4l2_subdev_krouting *routing, + u32 pad, u32 stream, u32 *other_pad, + u32 *other_stream) +{ + unsigned int i; + + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + + if (cal_rx_pad_is_source(pad)) { + if (route->source_pad == pad && + route->source_stream == stream) { + *other_pad = route->sink_pad; + *other_stream = route->sink_stream; + return 0; + } + } else { + if (route->sink_pad == pad && + route->sink_stream == stream) { + *other_pad = route->source_pad; + *other_stream = route->source_stream; + return 0; + } + } + } + + return -EINVAL; +} + +static struct v4l2_mbus_framefmt * +cal_camerarx_get_opposite_stream_format(struct cal_camerarx *phy, + struct v4l2_subdev_state *cfg, + u32 pad, u32 stream, u32 which) +{ + struct v4l2_subdev_krouting *routing; + u32 other_pad, other_stream; + int ret; + + routing = cal_camerarx_get_routing_table(phy, cfg, which); + + ret = cal_camerarx_find_opposite_end(routing, pad, stream, &other_pad, + &other_stream); + if (ret) + return NULL; + + return cal_camerarx_get_stream_format(phy, cfg, other_pad, + other_stream, which); +} + +static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + int r = 0; + + mutex_lock(&phy->mutex); + + if (enable) + r = cal_camerarx_start(phy); + else + cal_camerarx_stop(phy); + + mutex_unlock(&phy->mutex); + + return r; +} + +static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + int r = 0; + + mutex_lock(&phy->mutex); + + /* No transcoding, source and sink codes must match. */ + if (cal_rx_pad_is_source(code->pad)) { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) { + r = -EINVAL; + goto out; + } + + fmt = cal_camerarx_get_opposite_stream_format(phy, sd_state, + code->pad, code->stream, + code->which); + + if (!fmt) { + r = -EINVAL; + goto out; + } + + code->code = fmt->code; + } else { + if (code->index >= cal_num_formats) { + r = -EINVAL; + goto out; + } + + code->code = cal_formats[code->index].code; + } + +out: + mutex_unlock(&phy->mutex); + + return r; +} + +static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + const struct cal_format_info *fmtinfo; + int r = 0; + + if (fse->index > 0) + return -EINVAL; + + mutex_lock(&phy->mutex); + + /* No transcoding, source and sink formats must match. */ + if (cal_rx_pad_is_source(fse->pad)) { + struct v4l2_mbus_framefmt *fmt; + + fmt = cal_camerarx_get_opposite_stream_format( + phy, sd_state, fse->pad, fse->stream, fse->which); + + if (!fmt) { + r = -EINVAL; + goto out; + } + + if (fse->code != fmt->code) { + r = -EINVAL; + goto out; + } + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + } else { + fmtinfo = cal_format_by_code(fse->code); + if (!fmtinfo) { + r = -EINVAL; + goto out; + } + + fse->min_width = + CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->max_width = + CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->min_height = CAL_MIN_HEIGHT_LINES; + fse->max_height = CAL_MAX_HEIGHT_LINES; + } + +out: + mutex_unlock(&phy->mutex); + + return r; +} + +static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + struct v4l2_mbus_framefmt *fmt; + + mutex_lock(&phy->mutex); + + fmt = cal_camerarx_get_stream_format(phy, sd_state, format->pad, + format->stream, format->which); + + if (!fmt) { + mutex_unlock(&phy->mutex); + return -EINVAL; + } + + format->format = *fmt; + + mutex_unlock(&phy->mutex); + + return 0; +} + +static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + const struct cal_format_info *fmtinfo; + struct v4l2_mbus_framefmt *fmt; + unsigned int bpp; + int ret = 0; + + /* No transcoding, source and sink formats must match. */ + if (cal_rx_pad_is_source(format->pad)) + return cal_camerarx_sd_get_fmt(sd, sd_state, format); + + /* + * Default to the first format if the requested media bus code isn't + * supported. + */ + fmtinfo = cal_format_by_code(format->format.code); + if (!fmtinfo) + fmtinfo = &cal_formats[0]; + + /* Clamp the size, update the code. The colorspace is accepted as-is. */ + bpp = ALIGN(fmtinfo->bpp, 8); + + format->format.width = clamp_t(unsigned int, format->format.width, + CAL_MIN_WIDTH_BYTES * 8 / bpp, + CAL_MAX_WIDTH_BYTES * 8 / bpp); + format->format.height = clamp_t(unsigned int, format->format.height, + CAL_MIN_HEIGHT_LINES, + CAL_MAX_HEIGHT_LINES); + format->format.code = fmtinfo->code; + format->format.field = V4L2_FIELD_NONE; + + /* Store the format and propagate it to the source pad. */ + + mutex_lock(&phy->mutex); + + fmt = cal_camerarx_get_stream_format(phy, sd_state, format->pad, + format->stream, format->which); + if (!fmt) { + ret = -EINVAL; + goto out; + } + + *fmt = format->format; + + fmt = cal_camerarx_get_opposite_stream_format(phy, sd_state, format->pad, + format->stream, + format->which); + if (!fmt) { + ret = -EINVAL; + goto out; + } + + *fmt = format->format; + +out: + mutex_unlock(&phy->mutex); + + return ret; +} + +static int cal_camerarx_sd_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *cfg, + struct v4l2_subdev_krouting *routing) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + struct v4l2_subdev_krouting *src; + + src = cal_camerarx_get_routing_table(phy, cfg, routing->which); + + return v4l2_subdev_cpy_routing(routing, src); +} + +static void cal_camerarx_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_state *cfg, + u32 which) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + stream_configs = cal_camerarx_get_stream_configs(phy, cfg, which); + + for (i = 0; i < stream_configs->num_configs; ++i) + stream_configs->configs[i].fmt = format; +} + +static int cal_camerarx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *cfg, + struct v4l2_subdev_krouting *routing) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + int ret; + struct v4l2_subdev_krouting *dst; + struct v4l2_subdev_stream_configs *stream_configs; + + dst = cal_camerarx_get_routing_table(phy, cfg, routing->which); + stream_configs = + cal_camerarx_get_stream_configs(phy, cfg, routing->which); + + ret = v4l2_subdev_dup_routing(dst, routing); + if (ret) + return ret; + + ret = v4l2_init_stream_configs(stream_configs, dst); + if (ret) + return ret; + + /* Initialize stream formats */ + cal_camerarx_init_formats(sd, cfg, routing->which); + + return 0; +} + +static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + u32 which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + + struct v4l2_subdev_route routes[] = { { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = 1, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .which = which, + .num_routes = 1, + .routes = routes, + }; + + /* Initialize routing to single route to the fist source pad */ + return cal_camerarx_sd_set_routing(sd, sd_state, &routing); +} + +static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { + .s_stream = cal_camerarx_sd_s_stream, +}; + +static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { + .init_cfg = cal_camerarx_sd_init_cfg, + .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, + .enum_frame_size = cal_camerarx_sd_enum_frame_size, + .get_fmt = cal_camerarx_sd_get_fmt, + .set_fmt = cal_camerarx_sd_set_fmt, + .get_routing = cal_camerarx_sd_get_routing, + .set_routing = cal_camerarx_sd_set_routing, +}; + +static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { + .video = &cal_camerarx_video_ops, + .pad = &cal_camerarx_pad_ops, +}; + +static bool cal_camerarx_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct cal_camerarx *phy = to_cal_camerarx(sd); + + return v4l2_subdev_has_route(&phy->routing, pad0, pad1); +} + +static struct media_entity_operations cal_camerarx_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_route = cal_camerarx_has_route, +}; + +/* ------------------------------------------------------------------ + * Create and Destroy + * ------------------------------------------------------------------ + */ + +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct cal_camerarx *phy; + struct v4l2_subdev *sd; + int ret; + unsigned int i; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->cal = cal; + phy->instance = instance; + + mutex_init(&phy->mutex); + + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (instance == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + phy->base = devm_ioremap_resource(cal->dev, phy->res); + if (IS_ERR(phy->base)) { + cal_err(cal, "failed to ioremap\n"); + ret = PTR_ERR(phy->base); + goto error; + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + phy->res->name, &phy->res->start, &phy->res->end); + + ret = cal_camerarx_regmap_init(cal, phy); + if (ret) + goto error; + + ret = cal_camerarx_parse_dt(phy); + if (ret) + goto error; + + /* Initialize the V4L2 subdev and media entity. */ + sd = &phy->subdev; + v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_MULTIPLEXED; + snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); + sd->dev = cal->dev; + + phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (i = CAL_CAMERARX_PAD_FIRST_SOURCE; i < CAL_CAMERARX_NUM_PADS; ++i) + phy->pads[i].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.ops = &cal_camerarx_media_ops; + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), + phy->pads); + if (ret) + goto error; + + ret = cal_camerarx_sd_init_cfg(sd, NULL); + if (ret) + goto error; + + ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); + if (ret) + goto error; + + return phy; + +error: + v4l2_subdev_free_routing(&phy->routing); + v4l2_uninit_stream_configs(&phy->stream_configs); + media_entity_cleanup(&phy->subdev.entity); + kfree(phy); + return ERR_PTR(ret); +} + +void cal_camerarx_destroy(struct cal_camerarx *phy) +{ + if (!phy) + return; + + v4l2_device_unregister_subdev(&phy->subdev); + v4l2_subdev_free_routing(&phy->routing); + v4l2_uninit_stream_configs(&phy->stream_configs); + media_entity_cleanup(&phy->subdev.entity); + of_node_put(phy->source_ep_node); + of_node_put(phy->source_node); + kfree(phy); +} diff -Naur --no-dereference a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h --- a/drivers/media/platform/ti/cal/cal.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/cal/cal.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,349 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TI Camera Access Layer (CAL) + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ +#ifndef __TI_CAL_H__ +#define __TI_CAL_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAL_MODULE_NAME "cal" +#define CAL_MAX_NUM_CONTEXT 8 +#define CAL_NUM_CSI2_PORTS 2 + +/* + * The width is limited by the size of the CAL_WR_DMA_XSIZE_j.XSIZE field, + * expressed in multiples of 64 bits. The height is limited by the size of the + * CAL_CSI2_CTXi_j.CTXi_LINES and CAL_WR_DMA_CTRL_j.YSIZE fields, expressed in + * lines. + */ +#define CAL_MIN_WIDTH_BYTES 16 +#define CAL_MAX_WIDTH_BYTES (8192 * 8) +#define CAL_MIN_HEIGHT_LINES 1 +#define CAL_MAX_HEIGHT_LINES 16383 + +#define CAL_CAMERARX_PAD_SINK 0 +#define CAL_CAMERARX_PAD_FIRST_SOURCE 1 +#define CAL_CAMERARX_NUM_SOURCE_PADS 8 +#define CAL_CAMERARX_NUM_PADS (1 + CAL_CAMERARX_NUM_SOURCE_PADS) + +static inline bool cal_rx_pad_is_sink(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad == 0; +} + +static inline bool cal_rx_pad_is_source(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad >= CAL_CAMERARX_PAD_FIRST_SOURCE && + pad <= CAL_CAMERARX_NUM_SOURCE_PADS; +} + +struct device; +struct device_node; +struct resource; +struct regmap; +struct regmap_fied; + +/* CTRL_CORE_CAMERRX_CONTROL register field id */ +enum cal_camerarx_field { + F_CTRLCLKEN, + F_CAMMODE, + F_LANEENABLE, + F_CSI_MODE, + F_MAX_FIELDS, +}; + +enum cal_dma_state { + CAL_DMA_RUNNING, + CAL_DMA_STOP_REQUESTED, + CAL_DMA_STOP_PENDING, + CAL_DMA_STOPPED, +}; + +struct cal_format_info { + u32 fourcc; + u32 code; + /* Bits per pixel */ + u8 bpp; + bool meta; +}; + +/* buffer for one video frame */ +struct cal_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +/** + * struct cal_dmaqueue - Queue of DMA buffers + */ +struct cal_dmaqueue { + /** + * @lock: Protects all fields in the cal_dmaqueue. + */ + spinlock_t lock; + + /** + * @queue: Buffers queued to the driver and waiting for DMA processing. + * Buffers are added to the list by the vb2 .buffer_queue() operation, + * and move to @pending when they are scheduled for the next frame. + */ + struct list_head queue; + /** + * @pending: Buffer provided to the hardware to DMA the next frame. + * Will move to @active at the end of the current frame. + */ + struct cal_buffer *pending; + /** + * @active: Buffer being DMA'ed to for the current frame. Will be + * retired and given back to vb2 at the end of the current frame if + * a @pending buffer has been scheduled to replace it. + */ + struct cal_buffer *active; + + /** @state: State of the DMA engine. */ + enum cal_dma_state state; + /** @wait: Wait queue to signal a @state transition to CAL_DMA_STOPPED. */ + struct wait_queue_head wait; +}; + +struct cal_camerarx_data { + struct { + unsigned int lsb; + unsigned int msb; + } fields[F_MAX_FIELDS]; + unsigned int num_lanes; +}; + +struct cal_data { + const struct cal_camerarx_data *camerarx; + unsigned int num_csi2_phy; + unsigned int flags; +}; + +/* + * The Camera Adaptation Layer (CAL) module is paired with one or more complex + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and + * DMA contexts. + * + * The cal_dev structure represents the whole subsystem, including the CAL and + * the CAMERARX instances. Instances of struct cal_dev are named cal through the + * driver. + * + * The cal_camerarx structure represents one CAMERARX instance. Instances of + * cal_camerarx are named phy through the driver. + * + * The cal_ctx structure represents the combination of one CSI-2 context, one + * processing context and one DMA context. Instance of struct cal_ctx are named + * ctx through the driver. + */ + +struct cal_camerarx { + void __iomem *base; + struct resource *res; + struct regmap_field *fields[F_MAX_FIELDS]; + + struct cal_dev *cal; + unsigned int instance; + + struct v4l2_fwnode_endpoint endpoint; + struct device_node *source_ep_node; + struct device_node *source_node; + struct v4l2_subdev *source; + struct media_pipeline pipe; + + struct v4l2_subdev subdev; + struct media_pad pads[CAL_CAMERARX_NUM_PADS]; + + /* mutex for camerarx ops */ + struct mutex mutex; + + unsigned int enable_count; + + struct v4l2_subdev_krouting routing; + struct v4l2_subdev_stream_configs stream_configs; +}; + +struct cal_dev { + struct clk *fclk; + int irq; + void __iomem *base; + struct resource *res; + struct device *dev; + + const struct cal_data *data; + u32 revision; + + /* Control Module handle */ + struct regmap *syscon_camerrx; + u32 syscon_camerrx_offset; + + /* Camera Core Module handle */ + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; + + u32 num_contexts; + struct cal_ctx *ctx[CAL_MAX_NUM_CONTEXT]; + + struct media_device mdev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + + unsigned long reserved_pix_proc_mask; +}; + +/* + * There is one cal_ctx structure for each camera core context. + */ +struct cal_ctx { + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; + struct media_pad pad; + + struct cal_dev *cal; + struct cal_camerarx *phy; + + /* v4l2_ioctl mutex */ + struct mutex mutex; + + struct cal_dmaqueue dma; + + /* video capture */ + const struct cal_format_info *fmtinfo; + /* Used to store current pixel format */ + struct v4l2_format v_fmt; + + /* Current subdev enumerated format (legacy) */ + const struct cal_format_info **active_fmt; + unsigned int num_active_fmt; + + struct vb2_queue vb_vidq; + u8 dma_ctx; + u8 cport; + u8 csi2_ctx; + u8 pix_proc; + u8 vc; + u8 datatype; + u32 stream; + + bool use_pix_proc; +}; + +extern unsigned int cal_debug; +extern int cal_video_nr; +extern bool cal_mc_api; + +#define cal_dbg(level, cal, fmt, arg...) \ + do { \ + if (cal_debug >= (level)) \ + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ + } while (0) +#define cal_info(cal, fmt, arg...) \ + dev_info((cal)->dev, fmt, ##arg) +#define cal_err(cal, fmt, arg...) \ + dev_err((cal)->dev, fmt, ##arg) + +#define ctx_dbg(level, ctx, fmt, arg...) \ + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg) +#define ctx_info(ctx, fmt, arg...) \ + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg) +#define ctx_err(ctx, fmt, arg...) \ + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg) + +#define phy_dbg(level, phy, fmt, arg...) \ + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_info(phy, fmt, arg...) \ + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_err(phy, fmt, arg...) \ + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) + +static inline u32 cal_read(struct cal_dev *cal, u32 offset) +{ + return ioread32(cal->base + offset); +} + +static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) +{ + iowrite32(val, cal->base + offset); +} + +static __always_inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) +{ + return FIELD_GET(mask, cal_read(cal, offset)); +} + +static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value, + u32 mask) +{ + u32 val = cal_read(cal, offset); + + val &= ~mask; + val |= (value << __ffs(mask)) & mask; + cal_write(cal, offset, val); +} + +static inline void cal_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +extern const struct cal_format_info cal_formats[]; +extern const unsigned int cal_num_formats; +const struct cal_format_info *cal_format_by_fourcc(u32 fourcc); +const struct cal_format_info *cal_format_by_code(u32 code); + +void cal_quickdump_regs(struct cal_dev *cal); + +int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, + struct v4l2_mbus_frame_desc *fd); +struct cal_camerarx *cal_camerarx_get_phy_from_entity(struct media_entity *entity); +void cal_camerarx_disable(struct cal_camerarx *phy); +void cal_camerarx_i913_errata(struct cal_camerarx *phy); +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance); +void cal_camerarx_destroy(struct cal_camerarx *phy); + +int cal_ctx_prepare(struct cal_ctx *ctx); +void cal_ctx_unprepare(struct cal_ctx *ctx); +void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr); +void cal_ctx_start(struct cal_ctx *ctx); +void cal_ctx_stop(struct cal_ctx *ctx); + +int cal_ctx_v4l2_register(struct cal_ctx *ctx); +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); +int cal_ctx_v4l2_init(struct cal_ctx *ctx); +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); + +struct v4l2_mbus_framefmt * +cal_camerarx_get_stream_format(struct cal_camerarx *phy, + struct v4l2_subdev_state *state, + unsigned int pad, u32 stream, u32 which); + +#endif /* __TI_CAL_H__ */ diff -Naur --no-dereference a/drivers/media/platform/ti/cal/cal_regs.h b/drivers/media/platform/ti/cal/cal_regs.h --- a/drivers/media/platform/ti/cal/cal_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/cal/cal_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,463 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TI CAL camera interface driver + * + * Copyright (c) 2015 Texas Instruments Inc. + * + * Benoit Parrot, + */ + +#ifndef __TI_CAL_REGS_H +#define __TI_CAL_REGS_H + +/* + * struct cal_dev.flags possibilities + * + * DRA72_CAL_PRE_ES2_LDO_DISABLE: + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +#define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0) + +/* CAL register offsets */ + +#define CAL_HL_REVISION 0x0000 +#define CAL_HL_HWINFO 0x0004 +#define CAL_HL_SYSCONFIG 0x0010 +#define CAL_HL_IRQ_EOI 0x001c +#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U) +#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U) +#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) +#define CAL_CTRL 0x100 +#define CAL_CTRL1 0x104 +#define CAL_LINE_NUMBER_EVT 0x108 +#define CAL_VPORT_CTRL1 0x120 +#define CAL_VPORT_CTRL2 0x124 +#define CAL_BYS_CTRL1 0x130 +#define CAL_BYS_CTRL2 0x134 +#define CAL_RD_DMA_CTRL 0x140 +#define CAL_RD_DMA_PIX_ADDR 0x144 +#define CAL_RD_DMA_PIX_OFST 0x148 +#define CAL_RD_DMA_XSIZE 0x14c +#define CAL_RD_DMA_YSIZE 0x150 +#define CAL_RD_DMA_INIT_ADDR 0x154 +#define CAL_RD_DMA_INIT_OFST 0x168 +#define CAL_RD_DMA_CTRL2 0x16c +#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U) +#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U) +#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U) +#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U) +#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U) +#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U) +#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) +#define CAL_CSI2_CTX(phy, csi2_ctx) (0x330U + (phy) * 0x80U + (csi2_ctx) * 4) +#define CAL_CSI2_STATUS(phy, csi2_ctx) (0x350U + (phy) * 0x80U + (csi2_ctx) * 4) + +/* CAL CSI2 PHY register offsets */ +#define CAL_CSI2_PHY_REG0 0x000 +#define CAL_CSI2_PHY_REG1 0x004 +#define CAL_CSI2_PHY_REG2 0x008 +#define CAL_CSI2_PHY_REG10 0x028 + +/* CAL Control Module Core Camerrx Control register offsets */ +#define CM_CTRL_CORE_CAMERRX_CONTROL 0x000 + +/********************************************************************* +* Field Definition Macros +*********************************************************************/ + +#define CAL_HL_REVISION_MINOR_MASK GENMASK(5, 0) +#define CAL_HL_REVISION_CUSTOM_MASK GENMASK(7, 6) +#define CAL_HL_REVISION_MAJOR_MASK GENMASK(10, 8) +#define CAL_HL_REVISION_RTL_MASK GENMASK(15, 11) +#define CAL_HL_REVISION_FUNC_MASK GENMASK(27, 16) +#define CAL_HL_REVISION_SCHEME_MASK GENMASK(31, 30) +#define CAL_HL_REVISION_SCHEME_H08 1 +#define CAL_HL_REVISION_SCHEME_LEGACY 0 + +#define CAL_HL_HWINFO_WFIFO_MASK GENMASK(3, 0) +#define CAL_HL_HWINFO_RFIFO_MASK GENMASK(7, 4) +#define CAL_HL_HWINFO_PCTX_MASK GENMASK(12, 8) +#define CAL_HL_HWINFO_WCTX_MASK GENMASK(18, 13) +#define CAL_HL_HWINFO_VFIFO_MASK GENMASK(22, 19) +#define CAL_HL_HWINFO_NCPORT_MASK GENMASK(27, 23) +#define CAL_HL_HWINFO_NPPI_CTXS0_MASK GENMASK(29, 28) +#define CAL_HL_HWINFO_NPPI_CTXS1_MASK GENMASK(31, 30) +#define CAL_HL_HWINFO_NPPI_CONTEXTS_ZERO 0 +#define CAL_HL_HWINFO_NPPI_CONTEXTS_FOUR 1 +#define CAL_HL_HWINFO_NPPI_CONTEXTS_EIGHT 2 +#define CAL_HL_HWINFO_NPPI_CONTEXTS_RESERVED 3 + +#define CAL_HL_SYSCONFIG_SOFTRESET_MASK BIT(0) +#define CAL_HL_SYSCONFIG_SOFTRESET_DONE 0x0 +#define CAL_HL_SYSCONFIG_SOFTRESET_PENDING 0x1 +#define CAL_HL_SYSCONFIG_SOFTRESET_NOACTION 0x0 +#define CAL_HL_SYSCONFIG_SOFTRESET_RESET 0x1 +#define CAL_HL_SYSCONFIG_IDLE_MASK GENMASK(3, 2) +#define CAL_HL_SYSCONFIG_IDLEMODE_FORCE 0 +#define CAL_HL_SYSCONFIG_IDLEMODE_NO 1 +#define CAL_HL_SYSCONFIG_IDLEMODE_SMART1 2 +#define CAL_HL_SYSCONFIG_IDLEMODE_SMART2 3 + +#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK BIT(0) +#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 +#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 + +#define CAL_HL_IRQ_WDMA_END_MASK(m) BIT(m) +#define CAL_HL_IRQ_WDMA_START_MASK(m) BIT(m) + +#define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) + +#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8) +#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8) + +#define CAL_PIX_PROC_EN_MASK BIT(0) +#define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) +#define CAL_PIX_PROC_EXTRACT_B6 0x0 +#define CAL_PIX_PROC_EXTRACT_B7 0x1 +#define CAL_PIX_PROC_EXTRACT_B8 0x2 +#define CAL_PIX_PROC_EXTRACT_B10 0x3 +#define CAL_PIX_PROC_EXTRACT_B10_MIPI 0x4 +#define CAL_PIX_PROC_EXTRACT_B12 0x5 +#define CAL_PIX_PROC_EXTRACT_B12_MIPI 0x6 +#define CAL_PIX_PROC_EXTRACT_B14 0x7 +#define CAL_PIX_PROC_EXTRACT_B14_MIPI 0x8 +#define CAL_PIX_PROC_EXTRACT_B16_BE 0x9 +#define CAL_PIX_PROC_EXTRACT_B16_LE 0xa +#define CAL_PIX_PROC_DPCMD_MASK GENMASK(9, 5) +#define CAL_PIX_PROC_DPCMD_BYPASS 0x0 +#define CAL_PIX_PROC_DPCMD_DPCM_10_8_1 0x2 +#define CAL_PIX_PROC_DPCMD_DPCM_12_8_1 0x8 +#define CAL_PIX_PROC_DPCMD_DPCM_10_7_1 0x4 +#define CAL_PIX_PROC_DPCMD_DPCM_10_7_2 0x5 +#define CAL_PIX_PROC_DPCMD_DPCM_10_6_1 0x6 +#define CAL_PIX_PROC_DPCMD_DPCM_10_6_2 0x7 +#define CAL_PIX_PROC_DPCMD_DPCM_12_7_1 0xa +#define CAL_PIX_PROC_DPCMD_DPCM_12_6_1 0xc +#define CAL_PIX_PROC_DPCMD_DPCM_14_10 0xe +#define CAL_PIX_PROC_DPCMD_DPCM_14_8_1 0x10 +#define CAL_PIX_PROC_DPCMD_DPCM_16_12_1 0x12 +#define CAL_PIX_PROC_DPCMD_DPCM_16_10_1 0x14 +#define CAL_PIX_PROC_DPCMD_DPCM_16_8_1 0x16 +#define CAL_PIX_PROC_DPCME_MASK GENMASK(15, 11) +#define CAL_PIX_PROC_DPCME_BYPASS 0x0 +#define CAL_PIX_PROC_DPCME_DPCM_10_8_1 0x2 +#define CAL_PIX_PROC_DPCME_DPCM_12_8_1 0x8 +#define CAL_PIX_PROC_DPCME_DPCM_14_10 0xe +#define CAL_PIX_PROC_DPCME_DPCM_14_8_1 0x10 +#define CAL_PIX_PROC_DPCME_DPCM_16_12_1 0x12 +#define CAL_PIX_PROC_DPCME_DPCM_16_10_1 0x14 +#define CAL_PIX_PROC_DPCME_DPCM_16_8_1 0x16 +#define CAL_PIX_PROC_PACK_MASK GENMASK(18, 16) +#define CAL_PIX_PROC_PACK_B8 0x0 +#define CAL_PIX_PROC_PACK_B10_MIPI 0x2 +#define CAL_PIX_PROC_PACK_B12 0x3 +#define CAL_PIX_PROC_PACK_B12_MIPI 0x4 +#define CAL_PIX_PROC_PACK_B16 0x5 +#define CAL_PIX_PROC_PACK_ARGB 0x6 +#define CAL_PIX_PROC_CPORT_MASK GENMASK(23, 19) + +#define CAL_CTRL_POSTED_WRITES_MASK BIT(0) +#define CAL_CTRL_POSTED_WRITES_NONPOSTED 0 +#define CAL_CTRL_POSTED_WRITES 1 +#define CAL_CTRL_TAGCNT_MASK GENMASK(4, 1) +#define CAL_CTRL_BURSTSIZE_MASK GENMASK(6, 5) +#define CAL_CTRL_BURSTSIZE_BURST16 0x0 +#define CAL_CTRL_BURSTSIZE_BURST32 0x1 +#define CAL_CTRL_BURSTSIZE_BURST64 0x2 +#define CAL_CTRL_BURSTSIZE_BURST128 0x3 +#define CAL_CTRL_LL_FORCE_STATE_MASK GENMASK(12, 7) +#define CAL_CTRL_MFLAGL_MASK GENMASK(20, 13) +#define CAL_CTRL_PWRSCPCLK_MASK BIT(21) +#define CAL_CTRL_PWRSCPCLK_AUTO 0 +#define CAL_CTRL_PWRSCPCLK_FORCE 1 +#define CAL_CTRL_RD_DMA_STALL_MASK BIT(22) +#define CAL_CTRL_MFLAGH_MASK GENMASK(31, 24) + +#define CAL_CTRL1_PPI_GROUPING_MASK GENMASK(1, 0) +#define CAL_CTRL1_PPI_GROUPING_DISABLED 0 +#define CAL_CTRL1_PPI_GROUPING_RESERVED 1 +#define CAL_CTRL1_PPI_GROUPING_0 2 +#define CAL_CTRL1_PPI_GROUPING_1 3 +#define CAL_CTRL1_INTERLEAVE01_MASK GENMASK(3, 2) +#define CAL_CTRL1_INTERLEAVE01_DISABLED 0 +#define CAL_CTRL1_INTERLEAVE01_PIX1 1 +#define CAL_CTRL1_INTERLEAVE01_PIX4 2 +#define CAL_CTRL1_INTERLEAVE01_RESERVED 3 +#define CAL_CTRL1_INTERLEAVE23_MASK GENMASK(5, 4) +#define CAL_CTRL1_INTERLEAVE23_DISABLED 0 +#define CAL_CTRL1_INTERLEAVE23_PIX1 1 +#define CAL_CTRL1_INTERLEAVE23_PIX4 2 +#define CAL_CTRL1_INTERLEAVE23_RESERVED 3 + +#define CAL_LINE_NUMBER_EVT_CPORT_MASK GENMASK(4, 0) +#define CAL_LINE_NUMBER_EVT_MASK GENMASK(29, 16) + +#define CAL_VPORT_CTRL1_PCLK_MASK GENMASK(16, 0) +#define CAL_VPORT_CTRL1_XBLK_MASK GENMASK(24, 17) +#define CAL_VPORT_CTRL1_YBLK_MASK GENMASK(30, 25) +#define CAL_VPORT_CTRL1_WIDTH_MASK BIT(31) +#define CAL_VPORT_CTRL1_WIDTH_ONE 0 +#define CAL_VPORT_CTRL1_WIDTH_TWO 1 + +#define CAL_VPORT_CTRL2_CPORT_MASK GENMASK(4, 0) +#define CAL_VPORT_CTRL2_FREERUNNING_MASK BIT(15) +#define CAL_VPORT_CTRL2_FREERUNNING_GATED 0 +#define CAL_VPORT_CTRL2_FREERUNNING_FREE 1 +#define CAL_VPORT_CTRL2_FS_RESETS_MASK BIT(16) +#define CAL_VPORT_CTRL2_FS_RESETS_NO 0 +#define CAL_VPORT_CTRL2_FS_RESETS_YES 1 +#define CAL_VPORT_CTRL2_FSM_RESET_MASK BIT(17) +#define CAL_VPORT_CTRL2_FSM_RESET_NOEFFECT 0 +#define CAL_VPORT_CTRL2_FSM_RESET 1 +#define CAL_VPORT_CTRL2_RDY_THR_MASK GENMASK(31, 18) + +#define CAL_BYS_CTRL1_PCLK_MASK GENMASK(16, 0) +#define CAL_BYS_CTRL1_XBLK_MASK GENMASK(24, 17) +#define CAL_BYS_CTRL1_YBLK_MASK GENMASK(30, 25) +#define CAL_BYS_CTRL1_BYSINEN_MASK BIT(31) + +#define CAL_BYS_CTRL2_CPORTIN_MASK GENMASK(4, 0) +#define CAL_BYS_CTRL2_CPORTOUT_MASK GENMASK(9, 5) +#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK BIT(10) +#define CAL_BYS_CTRL2_DUPLICATEDDATA_NO 0 +#define CAL_BYS_CTRL2_DUPLICATEDDATA_YES 1 +#define CAL_BYS_CTRL2_FREERUNNING_MASK BIT(11) +#define CAL_BYS_CTRL2_FREERUNNING_NO 0 +#define CAL_BYS_CTRL2_FREERUNNING_YES 1 + +#define CAL_RD_DMA_CTRL_GO_MASK BIT(0) +#define CAL_RD_DMA_CTRL_GO_DIS 0 +#define CAL_RD_DMA_CTRL_GO_EN 1 +#define CAL_RD_DMA_CTRL_GO_IDLE 0 +#define CAL_RD_DMA_CTRL_GO_BUSY 1 +#define CAL_RD_DMA_CTRL_INIT_MASK BIT(1) +#define CAL_RD_DMA_CTRL_BW_LIMITER_MASK GENMASK(10, 2) +#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_MASK GENMASK(14, 11) +#define CAL_RD_DMA_CTRL_PCLK_MASK GENMASK(31, 15) + +#define CAL_RD_DMA_PIX_ADDR_MASK GENMASK(31, 3) + +#define CAL_RD_DMA_PIX_OFST_MASK GENMASK(31, 4) + +#define CAL_RD_DMA_XSIZE_MASK GENMASK(31, 19) + +#define CAL_RD_DMA_YSIZE_MASK GENMASK(29, 16) + +#define CAL_RD_DMA_INIT_ADDR_MASK GENMASK(31, 3) + +#define CAL_RD_DMA_INIT_OFST_MASK GENMASK(31, 3) + +#define CAL_RD_DMA_CTRL2_CIRC_MODE_MASK GENMASK(2, 0) +#define CAL_RD_DMA_CTRL2_CIRC_MODE_DIS 0 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_ONE 1 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_FOUR 2 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTEEN 3 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTYFOUR 4 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_RESERVED 5 +#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK BIT(3) +#define CAL_RD_DMA_CTRL2_PATTERN_MASK GENMASK(5, 4) +#define CAL_RD_DMA_CTRL2_PATTERN_LINEAR 0 +#define CAL_RD_DMA_CTRL2_PATTERN_YUV420 1 +#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP2 2 +#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP4 3 +#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK BIT(6) +#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_FREERUNNING 0 +#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_WAITFORBYSOUT 1 +#define CAL_RD_DMA_CTRL2_CIRC_SIZE_MASK GENMASK(29, 16) + +#define CAL_WR_DMA_CTRL_MODE_MASK GENMASK(2, 0) +#define CAL_WR_DMA_CTRL_MODE_DIS 0 +#define CAL_WR_DMA_CTRL_MODE_SHD 1 +#define CAL_WR_DMA_CTRL_MODE_CNT 2 +#define CAL_WR_DMA_CTRL_MODE_CNT_INIT 3 +#define CAL_WR_DMA_CTRL_MODE_CONST 4 +#define CAL_WR_DMA_CTRL_MODE_RESERVED 5 +#define CAL_WR_DMA_CTRL_PATTERN_MASK GENMASK(4, 3) +#define CAL_WR_DMA_CTRL_PATTERN_LINEAR 0 +#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP2 2 +#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP4 3 +#define CAL_WR_DMA_CTRL_PATTERN_RESERVED 1 +#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK BIT(5) +#define CAL_WR_DMA_CTRL_DTAG_MASK GENMASK(8, 6) +#define CAL_WR_DMA_CTRL_DTAG_ATT_HDR 0 +#define CAL_WR_DMA_CTRL_DTAG_ATT_DAT 1 +#define CAL_WR_DMA_CTRL_DTAG 2 +#define CAL_WR_DMA_CTRL_DTAG_PIX_HDR 3 +#define CAL_WR_DMA_CTRL_DTAG_PIX_DAT 4 +#define CAL_WR_DMA_CTRL_DTAG_D5 5 +#define CAL_WR_DMA_CTRL_DTAG_D6 6 +#define CAL_WR_DMA_CTRL_DTAG_D7 7 +#define CAL_WR_DMA_CTRL_CPORT_MASK GENMASK(13, 9) +#define CAL_WR_DMA_CTRL_STALL_RD_MASK BIT(14) +#define CAL_WR_DMA_CTRL_YSIZE_MASK GENMASK(31, 18) + +#define CAL_WR_DMA_ADDR_MASK GENMASK(31, 4) + +#define CAL_WR_DMA_OFST_MASK GENMASK(18, 4) +#define CAL_WR_DMA_OFST_CIRC_MODE_MASK GENMASK(23, 22) +#define CAL_WR_DMA_OFST_CIRC_MODE_ONE 1 +#define CAL_WR_DMA_OFST_CIRC_MODE_FOUR 2 +#define CAL_WR_DMA_OFST_CIRC_MODE_SIXTYFOUR 3 +#define CAL_WR_DMA_OFST_CIRC_MODE_DISABLED 0 +#define CAL_WR_DMA_OFST_CIRC_SIZE_MASK GENMASK(31, 24) + +#define CAL_WR_DMA_XSIZE_XSKIP_MASK GENMASK(15, 3) +#define CAL_WR_DMA_XSIZE_MASK GENMASK(31, 19) + +#define CAL_CSI2_PPI_CTRL_IF_EN_MASK BIT(0) +#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK BIT(2) +#define CAL_CSI2_PPI_CTRL_FRAME_MASK BIT(3) +#define CAL_CSI2_PPI_CTRL_FRAME_IMMEDIATE 0 +#define CAL_CSI2_PPI_CTRL_FRAME 1 + +#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK GENMASK(2, 0) +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_5 5 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_4 4 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_3 3 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_2 2 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_1 1 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_NOT_USED 0 +#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK BIT(3) +#define CAL_CSI2_COMPLEXIO_CFG_POL_PLUSMINUS 0 +#define CAL_CSI2_COMPLEXIO_CFG_POL_MINUSPLUS 1 +#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_MASK GENMASK(6, 4) +#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK BIT(7) +#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_MASK GENMASK(10, 8) +#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK BIT(11) +#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_MASK GENMASK(14, 12) +#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK BIT(15) +#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_MASK GENMASK(18, 16) +#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK BIT(19) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK BIT(24) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK GENMASK(26, 25) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF 0 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON 1 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ULP 2 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK GENMASK(28, 27) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF 0 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON 1 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ULP 2 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK BIT(29) +#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED 1 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING 0 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK BIT(30) +#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL 0 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL 1 + +#define CAL_CSI2_SHORT_PACKET_MASK GENMASK(23, 0) + +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS1_MASK BIT(0) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS2_MASK BIT(1) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS3_MASK BIT(2) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS4_MASK BIT(3) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS5_MASK BIT(4) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1_MASK BIT(5) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2_MASK BIT(6) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3_MASK BIT(7) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4_MASK BIT(8) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5_MASK BIT(9) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC1_MASK BIT(10) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC2_MASK BIT(11) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC3_MASK BIT(12) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC4_MASK BIT(13) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC5_MASK BIT(14) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL1_MASK BIT(15) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL2_MASK BIT(16) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL3_MASK BIT(17) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL4_MASK BIT(18) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL5_MASK BIT(19) +#define CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK GENMASK(19, 0) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM1_MASK BIT(20) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM2_MASK BIT(21) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM3_MASK BIT(22) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM4_MASK BIT(23) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM5_MASK BIT(24) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER_MASK BIT(25) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT_MASK BIT(26) +#define CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK BIT(27) +#define CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK BIT(28) +#define CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK BIT(30) + +#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK GENMASK(12, 0) +#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK BIT(13) +#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT(14) +#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT(15) + +#define CAL_CSI2_VC_IRQ_FS_IRQ_MASK(n) BIT(0 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_FE_IRQ_MASK(n) BIT(1 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_LS_IRQ_MASK(n) BIT(2 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_LE_IRQ_MASK(n) BIT(3 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_CS_IRQ_MASK(n) BIT(4 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(n) BIT(5 + ((n) * 8)) + +#define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0) +#define CAL_CSI2_CTX_DT_DISABLED 0 +#define CAL_CSI2_CTX_DT_ANY 1 +#define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6) +#define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8) +#define CAL_CSI2_CTX_ATT_MASK BIT(13) +#define CAL_CSI2_CTX_ATT_PIX 0 +#define CAL_CSI2_CTX_ATT 1 +#define CAL_CSI2_CTX_PACK_MODE_MASK BIT(14) +#define CAL_CSI2_CTX_PACK_MODE_LINE 0 +#define CAL_CSI2_CTX_PACK_MODE_FRAME 1 +#define CAL_CSI2_CTX_LINES_MASK GENMASK(29, 16) + +#define CAL_CSI2_STATUS_FRAME_MASK GENMASK(15, 0) + +#define CAL_CSI2_PHY_REG0_THS_SETTLE_MASK GENMASK(7, 0) +#define CAL_CSI2_PHY_REG0_THS_TERM_MASK GENMASK(15, 8) +#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK BIT(24) +#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE 1 +#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_ENABLE 0 + +#define CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK GENMASK(7, 0) +#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK GENMASK(9, 8) +#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK GENMASK(17, 10) +#define CAL_CSI2_PHY_REG1_TCLK_TERM_MASK GENMASK(24, 18) +#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_MASK BIT(25) +#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_ERROR 1 +#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_SUCCESS 0 +#define CAL_CSI2_PHY_REG1_RESET_DONE_STATUS_MASK GENMASK(29, 28) + +#define CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK BIT(6) + +#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK GENMASK(23, 0) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK GENMASK(25, 24) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK GENMASK(27, 26) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK GENMASK(29, 28) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK GENMASK(31, 30) + +#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK BIT(0) +#define CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK GENMASK(2, 1) +#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK GENMASK(4, 3) +#define CM_CAMERRX_CTRL_CSI1_MODE_MASK BIT(5) +#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK BIT(10) +#define CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK GENMASK(12, 11) +#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK GENMASK(16, 13) +#define CM_CAMERRX_CTRL_CSI0_MODE_MASK BIT(17) + +#endif diff -Naur --no-dereference a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c --- a/drivers/media/platform/ti/cal/cal-video.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/cal/cal-video.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - Video Device + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cal.h" + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +/* ------------------------------------------------------------------ + * V4L2 Common IOCTLs + * ------------------------------------------------------------------ + */ + +static int cal_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cal_ctx *ctx = video_drvdata(file); + + strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(ctx->cal->dev)); + return 0; +} + +static int cal_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + + *f = ctx->v_fmt; + + return 0; +} + +/* ------------------------------------------------------------------ + * V4L2 Video Node Centric IOCTLs + * ------------------------------------------------------------------ + */ + +static const struct cal_format_info *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_format_info *fmtinfo; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmtinfo = ctx->active_fmt[k]; + if (fmtinfo->fourcc == pixelformat) + return fmtinfo; + } + + return NULL; +} + +static const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_format_info *fmtinfo; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmtinfo = ctx->active_fmt[k]; + if (fmtinfo->code == code) + return fmtinfo; + } + + return NULL; +} + +static int cal_legacy_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_format_info *fmtinfo; + + if (f->index >= ctx->num_active_fmt) + return -EINVAL; + + fmtinfo = ctx->active_fmt[f->index]; + + f->pixelformat = fmtinfo->fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; +} + +static int __subdev_get_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + *fmt = *mbus_fmt; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int __subdev_set_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + *mbus_fmt = *fmt; + + ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static void cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_format_info *fmtinfo, + struct v4l2_format *f) +{ + u32 bpl, max_width; + + /* + * Maximum width is bound by the DMA max width in bytes. + * We need to recalculate the actual maxi width depending on the + * number of bytes per pixels required. + */ + max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmtinfo->bpp, 8) >> 3); + v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, + &f->fmt.pix.height, 32, CAL_MAX_HEIGHT_LINES, + 0, 0); + + bpl = (f->fmt.pix.width * ALIGN(fmtinfo->bpp, 8)) >> 3; + f->fmt.pix.bytesperline = ALIGN(bpl, 16); + + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", + __func__, fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); +} + +static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_format_info *fmtinfo; + struct v4l2_subdev_frame_size_enum fse; + int ret, found; + + fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmtinfo) { + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmtinfo = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmtinfo->fourcc; + } + + f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + fse.pad = 0; + fse.code = fmtinfo->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, + NULL, &fse); + if (ret) + break; + + if ((f->fmt.pix.width == fse.max_width) && + (f->fmt.pix.height == fse.max_height)) { + found = true; + break; + } else if ((f->fmt.pix.width >= fse.min_width) && + (f->fmt.pix.width <= fse.max_width) && + (f->fmt.pix.height >= fse.min_height) && + (f->fmt.pix.height <= fse.max_height)) { + found = true; + break; + } + } + + if (!found) { + /* use existing values as default */ + f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; + f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; + } + + /* + * Use current colorspace for now, it will get + * updated properly during s_fmt + */ + f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; + cal_calc_format_size(ctx, fmtinfo, f); + return 0; +} + +static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + struct vb2_queue *q = &ctx->vb_vidq; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CAL_CAMERARX_PAD_SINK, + }; + const struct cal_format_info *fmtinfo; + int ret; + + if (vb2_is_busy(q)) { + ctx_dbg(3, ctx, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = cal_legacy_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + + v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmtinfo->code); + + ret = __subdev_set_format(ctx, &sd_fmt.format); + if (ret) + return ret; + + /* Just double check nothing has gone wrong */ + if (sd_fmt.format.code != fmtinfo->code) { + ctx_dbg(3, ctx, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &sd_fmt.format); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc; + ctx->v_fmt.fmt.pix.field = sd_fmt.format.field; + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + + v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt); + + ctx->fmtinfo = fmtinfo; + *f = ctx->v_fmt; + + return 0; +} + +static int cal_legacy_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_format_info *fmtinfo; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + /* check for valid format */ + fmtinfo = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmtinfo) { + ctx_dbg(3, ctx, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fse.index = fsize->index; + fse.pad = 0; + fse.code = fmtinfo->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, + &fse); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int cal_legacy_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index > 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + sprintf(inp->name, "Camera %u", inp->index); + return 0; +} + +static int cal_legacy_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cal_legacy_s_input(struct file *file, void *priv, unsigned int i) +{ + return i > 0 ? -EINVAL : 0; +} + +/* timeperframe is arbitrary and continuous */ +static int cal_legacy_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_format_info *fmtinfo; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmtinfo = find_format_by_pix(ctx, fival->pixel_format); + if (!fmtinfo) + return -EINVAL; + + fie.code = fmtinfo->code; + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static int cal_legacy_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct cal_ctx *ctx = video_drvdata(file); + + return v4l2_g_parm_cap(video_devdata(file), ctx->phy->source, a); +} + +static int cal_legacy_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct cal_ctx *ctx = video_drvdata(file); + + return v4l2_s_parm_cap(video_devdata(file), ctx->phy->source, a); +} + +static const struct v4l2_ioctl_ops cal_ioctl_legacy_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_legacy_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_legacy_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_legacy_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_legacy_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cal_legacy_enum_input, + .vidioc_g_input = cal_legacy_g_input, + .vidioc_s_input = cal_legacy_s_input, + .vidioc_enum_frameintervals = cal_legacy_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_parm = cal_legacy_g_parm, + .vidioc_s_parm = cal_legacy_s_parm, +}; + +/* ------------------------------------------------------------------ + * V4L2 Media Controller Centric IOCTLs + * ------------------------------------------------------------------ + */ + +static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int i; + unsigned int idx; + + if (f->index >= cal_num_formats) + return -EINVAL; + + idx = 0; + + for (i = 0; i < cal_num_formats; ++i) { + if (f->mbus_code && cal_formats[i].code != f->mbus_code) + continue; + + if (idx == f->index) { + f->pixelformat = cal_formats[i].fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; + } + + idx++; + } + + return -EINVAL; +} + +static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f, + const struct cal_format_info **info) +{ + struct v4l2_pix_format *format = &f->fmt.pix; + const struct cal_format_info *fmtinfo; + unsigned int bpp; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = cal_format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmtinfo) + fmtinfo = &cal_formats[0]; + + /* + * Clamp the size, update the pixel format. The field and colorspace are + * accepted as-is, except for V4L2_FIELD_ANY that is turned into + * V4L2_FIELD_NONE. + */ + bpp = ALIGN(fmtinfo->bpp, 8); + + format->width = clamp_t(unsigned int, format->width, + CAL_MIN_WIDTH_BYTES * 8 / bpp, + CAL_MAX_WIDTH_BYTES * 8 / bpp); + format->height = clamp_t(unsigned int, format->height, + CAL_MIN_HEIGHT_LINES, CAL_MAX_HEIGHT_LINES); + format->pixelformat = fmtinfo->fourcc; + + if (format->field == V4L2_FIELD_ANY) + format->field = V4L2_FIELD_NONE; + + /* + * Calculate the number of bytes per line and the image size. The + * hardware stores the stride as a number of 16 bytes words, in a + * signed 15-bit value. Only 14 bits are thus usable. + */ + format->bytesperline = ALIGN(clamp(format->bytesperline, + format->width * bpp / 8, + ((1U << 14) - 1) * 16), 16); + + format->sizeimage = format->height * format->bytesperline; + + format->colorspace = ctx->v_fmt.fmt.pix.colorspace; + + if (info) + *info = fmtinfo; + + ctx_dbg(3, ctx, "%s: %s %ux%u (bytesperline %u sizeimage %u)\n", + __func__, fourcc_to_str(format->pixelformat), + format->width, format->height, + format->bytesperline, format->sizeimage); +} + +static int cal_mc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + + cal_mc_try_fmt(ctx, f, NULL); + return 0; +} + +static int cal_mc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_format_info *fmtinfo; + + if (vb2_is_busy(&ctx->vb_vidq)) { + ctx_dbg(3, ctx, "%s device busy\n", __func__); + return -EBUSY; + } + + cal_mc_try_fmt(ctx, f, &fmtinfo); + + ctx->v_fmt = *f; + ctx->fmtinfo = fmtinfo; + + return 0; +} + +static int cal_mc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_format_info *fmtinfo; + unsigned int bpp; + + if (fsize->index > 0) + return -EINVAL; + + fmtinfo = cal_format_by_fourcc(fsize->pixel_format); + if (!fmtinfo) { + ctx_dbg(3, ctx, "Invalid pixel format 0x%08x\n", + fsize->pixel_format); + return -EINVAL; + } + + bpp = ALIGN(fmtinfo->bpp, 8); + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = CAL_MIN_WIDTH_BYTES * 8 / bpp; + fsize->stepwise.max_width = CAL_MAX_WIDTH_BYTES * 8 / bpp; + fsize->stepwise.step_width = 64 / bpp; + fsize->stepwise.min_height = CAL_MIN_HEIGHT_LINES; + fsize->stepwise.max_height = CAL_MAX_HEIGHT_LINES; + fsize->stepwise.step_height = 1; + + return 0; +} + +static const struct v4l2_ioctl_ops cal_ioctl_mc_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_mc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_mc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_mc_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_mc_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +/* ------------------------------------------------------------------ + * videobuf2 Common Operations + * ------------------------------------------------------------------ + */ + +static int cal_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); + + return 0; +} + +static int cal_buffer_prepare(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + unsigned long size; + + size = ctx->v_fmt.fmt.pix.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + ctx_err(ctx, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + return 0; +} + +static void cal_buffer_queue(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + unsigned long flags; + + /* recheck locking */ + spin_lock_irqsave(&ctx->dma.lock, flags); + list_add_tail(&buf->list, &ctx->dma.queue); + spin_unlock_irqrestore(&ctx->dma.lock, flags); +} + +static void cal_release_buffers(struct cal_ctx *ctx, + enum vb2_buffer_state state) +{ + struct cal_buffer *buf, *tmp; + + /* Release all queued buffers. */ + spin_lock_irq(&ctx->dma.lock); + + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (ctx->dma.pending) { + vb2_buffer_done(&ctx->dma.pending->vb.vb2_buf, state); + ctx->dma.pending = NULL; + } + + if (ctx->dma.active) { + vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state); + ctx->dma.active = NULL; + } + + spin_unlock_irq(&ctx->dma.lock); +} + +/* ------------------------------------------------------------------ + * videobuf2 Operations + * ------------------------------------------------------------------ + */ + +static int cal_video_check_format(struct cal_ctx *ctx) +{ + const struct v4l2_mbus_framefmt *format; + struct media_pad *remote_pad; + + remote_pad = media_entity_remote_pad(&ctx->pad); + if (!remote_pad) + return -ENODEV; + + format = cal_camerarx_get_stream_format(ctx->phy, NULL, + remote_pad->index, 0, + V4L2_SUBDEV_FORMAT_ACTIVE); + if (!format) + return -EINVAL; + + if (ctx->fmtinfo->code != format->code || + ctx->v_fmt.fmt.pix.height != format->height || + ctx->v_fmt.fmt.pix.width != format->width || + ctx->v_fmt.fmt.pix.field != format->field) + return -EPIPE; + + return 0; +} + +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_buffer *buf; + dma_addr_t addr; + int ret; + + if (cal_mc_api) { + struct v4l2_subdev_route *route = NULL; + struct media_pad *remote_pad; + unsigned int i; + + /* Find the PHY connected to this video device */ + + remote_pad = media_entity_remote_pad(&ctx->pad); + if (!remote_pad) { + ctx_err(ctx, "Context not connected\n"); + ret = -ENODEV; + goto error_release_buffers; + } + + ctx->phy = cal_camerarx_get_phy_from_entity(remote_pad->entity); + + /* Find the stream */ + + for (i = 0; i < ctx->phy->routing.num_routes; ++i) { + struct v4l2_subdev_route *r = + &ctx->phy->routing.routes[i]; + + if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (r->source_pad != remote_pad->index) + continue; + + route = r; + + break; + } + + if (!route) { + ctx_err(ctx, "Failed to find route\n"); + ret = -ENODEV; + goto error_release_buffers; + } + + ctx->stream = route->sink_stream; + } + + ret = media_pipeline_start(ctx->vdev.entity.pads, &ctx->phy->pipe); + if (ret < 0) { + ctx_err(ctx, "Failed to start media pipeline: %d\n", ret); + goto error_release_buffers; + } + + /* + * Verify that the currently configured format matches the output of + * the connected CAMERARX. + */ + ret = cal_video_check_format(ctx); + if (ret < 0) { + ctx_dbg(3, ctx, + "Format mismatch between CAMERARX and video node\n"); + goto error_pipeline; + } + + ret = cal_ctx_prepare(ctx); + if (ret) { + ctx_err(ctx, "Failed to prepare context: %d\n", ret); + goto error_pipeline; + } + + spin_lock_irq(&ctx->dma.lock); + buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); + ctx->dma.active = buf; + list_del(&buf->list); + spin_unlock_irq(&ctx->dma.lock); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + + ret = pm_runtime_resume_and_get(ctx->cal->dev); + if (ret < 0) + goto error_pipeline; + + cal_ctx_set_dma_addr(ctx, addr); + cal_ctx_start(ctx); + + ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); + if (ret) + goto error_stop; + + if (cal_debug >= 4) + cal_quickdump_regs(ctx->cal); + + return 0; + +error_stop: + cal_ctx_stop(ctx); + pm_runtime_put_sync(ctx->cal->dev); + cal_ctx_unprepare(ctx); + +error_pipeline: + media_pipeline_stop(ctx->vdev.entity.pads); +error_release_buffers: + cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void cal_stop_streaming(struct vb2_queue *vq) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + + cal_ctx_stop(ctx); + + v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); + + pm_runtime_put_sync(ctx->cal->dev); + + cal_ctx_unprepare(ctx); + + cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); + + media_pipeline_stop(ctx->vdev.entity.pads); + + if (cal_mc_api) + ctx->phy = NULL; +} + +static const struct vb2_ops cal_video_qops = { + .queue_setup = cal_queue_setup, + .buf_prepare = cal_buffer_prepare, + .buf_queue = cal_buffer_queue, + .start_streaming = cal_start_streaming, + .stop_streaming = cal_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ + * V4L2 Initialization and Registration + * ------------------------------------------------------------------ + */ + +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + struct v4l2_mbus_framefmt mbus_fmt; + const struct cal_format_info *fmtinfo; + unsigned int i, j, k; + int ret = 0; + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats, + sizeof(*ctx->active_fmt), GFP_KERNEL); + ctx->num_active_fmt = 0; + + for (j = 0, i = 0; ; ++j) { + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret == -EINVAL) + break; + + if (ret) { + ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n", + ctx->phy->source->name, ret); + return ret; + } + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %u\n", + ctx->phy->source->name, mbus_code.code, j); + + for (k = 0; k < cal_num_formats; k++) { + fmtinfo = &cal_formats[k]; + + if (mbus_code.code == fmtinfo->code) { + ctx->active_fmt[i] = fmtinfo; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %u\n", + fourcc_to_str(fmtinfo->fourcc), + fmtinfo->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + ctx->phy->source->name); + return -EINVAL; + } + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmtinfo = find_format_by_code(ctx, mbus_fmt.code); + if (!fmtinfo) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc; + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + ctx->fmtinfo = fmtinfo; + + return 0; +} + +static int cal_ctx_v4l2_init_mc_format(struct cal_ctx *ctx) +{ + const struct cal_format_info *fmtinfo; + struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; + + fmtinfo = cal_format_by_code(MEDIA_BUS_FMT_UYVY8_2X8); + if (!fmtinfo) + return -EINVAL; + + pix_fmt->width = 640; + pix_fmt->height = 480; + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + pix_fmt->pixelformat = fmtinfo->fourcc; + + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* Save current format */ + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + ctx->fmtinfo = fmtinfo; + + return 0; +} + +int cal_ctx_v4l2_register(struct cal_ctx *ctx) +{ + struct video_device *vfd = &ctx->vdev; + int ret; + + if (!cal_mc_api) { + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) { + ctx_err(ctx, "Failed to init formats: %d\n", ret); + return ret; + } + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->source->ctrl_handler, + NULL, true); + if (ret < 0) { + ctx_err(ctx, "Failed to add source ctrl handler\n"); + return ret; + } + } else { + ret = cal_ctx_v4l2_init_mc_format(ctx); + if (ret) { + ctx_err(ctx, "Failed to init format: %d\n", ret); + return ret; + } + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + if (cal_mc_api) { + u16 phy_idx; + u16 pad_idx; + + /* Create links from all video nodes to all PHYs */ + + for (phy_idx = 0; phy_idx < ctx->cal->data->num_csi2_phy; ++phy_idx) { + for (pad_idx = 1; pad_idx < CAL_CAMERARX_NUM_PADS; ++pad_idx) { + /* + * Enable only links from video0 to PHY0 pad 1, and + * video1 to PHY1 pad 1. + */ + bool enable = (ctx->dma_ctx == 0 && + phy_idx == 0 && pad_idx == 1) || + (ctx->dma_ctx == 1 && + phy_idx == 1 && pad_idx == 1); + + ret = media_create_pad_link( + &ctx->cal->phy[phy_idx]->subdev.entity, + pad_idx, &vfd->entity, 0, + enable ? MEDIA_LNK_FL_ENABLED : 0); + if (ret) { + ctx_err(ctx, + "Failed to create media link for context %u\n", + ctx->dma_ctx); + video_unregister_device(vfd); + return ret; + } + } + } + } else { + ret = media_create_pad_link( + &ctx->phy->subdev.entity, CAL_CAMERARX_PAD_FIRST_SOURCE, + &vfd->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret) { + ctx_err(ctx, + "Failed to create media link for context %u\n", + ctx->dma_ctx); + video_unregister_device(vfd); + return ret; + } + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + + video_unregister_device(&ctx->vdev); +} + +int cal_ctx_v4l2_init(struct cal_ctx *ctx) +{ + struct video_device *vfd = &ctx->vdev; + struct vb2_queue *q = &ctx->vb_vidq; + int ret; + + INIT_LIST_HEAD(&ctx->dma.queue); + spin_lock_init(&ctx->dma.lock); + mutex_init(&ctx->mutex); + init_waitqueue_head(&ctx->dma.wait); + + /* Initialize the vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + q->dev = ctx->cal->dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* Initialize the video device and media entity. */ + vfd->fops = &cal_fops; + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING + | (cal_mc_api ? V4L2_CAP_IO_MC : 0); + vfd->v4l2_dev = &ctx->cal->v4l2_dev; + vfd->queue = q; + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->dma_ctx); + vfd->release = video_device_release_empty; + vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_legacy_ops; + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); + if (ret < 0) + return ret; + + if (!cal_mc_api) { + /* Initialize the control handler. */ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret < 0) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + goto error; + } + + vfd->ctrl_handler = hdl; + } + + return 0; + +error: + media_entity_cleanup(&vfd->entity); + return ret; +} + +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) +{ + if (!cal_mc_api) + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + media_entity_cleanup(&ctx->vdev.entity); +} diff -Naur --no-dereference a/drivers/media/platform/ti/cal/Makefile b/drivers/media/platform/ti/cal/Makefile --- a/drivers/media/platform/ti/cal/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/cal/Makefile 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o +ti-cal-y := cal.o cal-camerarx.o cal-video.o diff -Naur --no-dereference a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI CSI2 RX driver. + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: Pratyush Yadav + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TI_CSI2RX_MODULE_NAME "j721e-csi2rx" + +#define SHIM_CNTL 0x10 +#define SHIM_CNTL_PIX_RST BIT(0) + +#define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20)) +#define SHIM_DMACNTX_EN BIT(31) +#define SHIM_DMACNTX_YUV422 GENMASK(27, 26) +#define SHIM_DMACNTX_VC GENMASK(9, 6) +#define SHIM_DMACNTX_FMT GENMASK(5, 0) +#define SHIM_DMACNTX_UYVY 0 +#define SHIM_DMACNTX_VYUY 1 +#define SHIM_DMACNTX_YUYV 2 +#define SHIM_DMACNTX_YVYU 3 + +#define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20)) +#define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0) +#define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 15) + +#define CSI_DF_YUV420 0x18 +#define CSI_DF_YUV422 0x1e +#define CSI_DF_RGB444 0x20 +#define CSI_DF_RGB888 0x24 + +#define PSIL_WORD_SIZE_BYTES 16 +#define TI_CSI2RX_NUM_CTX 16 + +/* + * There are no hard limits on the width or height. The DMA engine can handle + * all sizes. The max width and height are arbitrary numbers for this driver. + * Use 16M * 16M as the arbitrary limit. It is large enough that it is unlikely + * the limit will be hit in practice. + */ +#define MAX_WIDTH_BYTES SZ_16M +#define MAX_HEIGHT_BYTES SZ_16M + +#define TI_CSI2RX_PAD_SINK 0 +#define TI_CSI2RX_PAD_FIRST_SOURCE 1 +#define TI_CSI2RX_NUM_SOURCE_PADS TI_CSI2RX_NUM_CTX +#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS) + +#define DRAIN_TIMEOUT_MS 50 + +struct ti_csi2rx_fmt { + u32 fourcc; /* Four character code. */ + u32 code; /* Mbus code. */ + enum v4l2_colorspace colorspace; + u32 csi_df; /* CSI Data format. */ + u8 bpp; /* Bits per pixel. */ +}; + +struct ti_csi2rx_buffer { + /* Common v4l2 buffer. Must be first. */ + struct vb2_v4l2_buffer vb; + struct list_head list; + struct ti_csi2rx_ctx *ctx; +}; + +enum ti_csi2rx_dma_state { + TI_CSI2RX_DMA_STOPPED, /* Streaming not started yet. */ + TI_CSI2RX_DMA_IDLE, /* Streaming but no pending DMA operation. */ + TI_CSI2RX_DMA_ACTIVE, /* Streaming and pending DMA operation. */ +}; + +struct ti_csi2rx_dma { + /* Protects all fields in this struct. */ + spinlock_t lock; + struct dma_chan *chan; + /* Buffers queued to the driver, waiting to be processed by DMA. */ + struct list_head queue; + enum ti_csi2rx_dma_state state; + /* + * Current buffer being processed by DMA. NULL if no buffer is being + * processed. + */ + struct ti_csi2rx_buffer *curr; +}; + +struct ti_csi2rx_dev; + +struct ti_csi2rx_ctx { + struct ti_csi2rx_dev *csi; + struct video_device vdev; + struct vb2_queue vidq; + struct mutex mutex; /* To serialize ioctls. */ + struct v4l2_format v_fmt; + struct ti_csi2rx_dma dma; + struct media_pad pad; + u32 sequence; + u32 idx; + u32 vc; + u32 stream; +}; + +struct ti_csi2rx_dev { + struct device *dev; + void __iomem *shim; + /* To serialize core subdev ioctls. */ + struct mutex mutex; + unsigned int enable_count; + struct v4l2_async_notifier notifier; + struct media_device mdev; + struct media_pipeline pipe; + struct media_pad pads[TI_CSI2RX_NUM_PADS]; + struct v4l2_device v4l2_dev; + struct v4l2_subdev *source; + struct v4l2_subdev subdev; + struct v4l2_subdev_krouting routing; + struct v4l2_subdev_stream_configs stream_configs; + struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; +}; + +static const struct ti_csi2rx_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_YUV422, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_YUV422, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_YUV422, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_YUV422, + .bpp = 16, + }, + + /* More formats can be supported but they are not listed for now. */ +}; + +static const unsigned int num_formats = ARRAY_SIZE(formats); + +/* Forward declaration needed by ti_csi2rx_dma_callback. */ +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, + struct ti_csi2rx_buffer *buf); + +static const struct ti_csi2rx_fmt *find_format_by_pix(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < num_formats; i++) { + if (formats[i].fourcc == pixelformat) + return &formats[i]; + } + + return NULL; +} + +static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt, + struct v4l2_format *v4l2_fmt) +{ + struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix; + u32 bpl; + + v4l2_fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pix->pixelformat = csi_fmt->fourcc; + pix->colorspace = csi_fmt->colorspace; + pix->sizeimage = pix->height * pix->width * (csi_fmt->bpp / 8); + + bpl = (pix->width * ALIGN(csi_fmt->bpp, 8)) >> 3; + pix->bytesperline = ALIGN(bpl, 16); +} + +static int ti_csi2rx_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct ti_csi2rx_ctx *ctx = video_drvdata(file); + + strscpy(cap->driver, TI_CSI2RX_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, TI_CSI2RX_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(ctx->csi->dev)); + + return 0; +} + +static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= num_formats) + return -EINVAL; + + memset(f->reserved, 0, sizeof(f->reserved)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *prov, + struct v4l2_format *f) +{ + struct ti_csi2rx_ctx *ctx = video_drvdata(file); + + *f = ctx->v_fmt; + + return 0; +} + +static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct ti_csi2rx_fmt *fmt; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmt = find_format_by_pix(f->fmt.pix.pixelformat); + if (!fmt) + fmt = &formats[0]; + + if (f->fmt.pix.field == V4L2_FIELD_ANY) + f->fmt.pix.field = V4L2_FIELD_NONE; + + if (f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + ti_csi2rx_fill_fmt(fmt, f); + + return 0; +} + +static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct ti_csi2rx_ctx *ctx = video_drvdata(file); + struct vb2_queue *q = &ctx->vidq; + int ret; + + if (vb2_is_busy(q)) + return -EBUSY; + + ret = ti_csi2rx_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + ctx->v_fmt = *f; + + return 0; +} + +static int ti_csi2rx_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct ti_csi2rx_fmt *fmt; + unsigned int pixels_in_word; + u8 bpp; + + fmt = find_format_by_pix(fsize->pixel_format); + if (!fmt) + return -EINVAL; + + bpp = ALIGN(fmt->bpp, 8); + + /* + * Number of pixels in one PSI-L word. The transfer happens in multiples + * of PSI-L word sizes. + */ + pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / bpp; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = pixels_in_word; + fsize->stepwise.max_width = rounddown(MAX_WIDTH_BYTES, pixels_in_word); + fsize->stepwise.step_width = pixels_in_word; + fsize->stepwise.min_height = 1; + fsize->stepwise.max_height = MAX_HEIGHT_BYTES; + fsize->stepwise.step_height = 1; + + return 0; +} + +static const struct v4l2_ioctl_ops csi_ioctl_ops = { + .vidioc_querycap = ti_csi2rx_querycap, + .vidioc_enum_fmt_vid_cap = ti_csi2rx_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = ti_csi2rx_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = ti_csi2rx_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = ti_csi2rx_s_fmt_vid_cap, + .vidioc_enum_framesizes = ti_csi2rx_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct v4l2_file_operations csi_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int ti_csi2rx_video_register(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct video_device *vdev = &ctx->vdev; + int ret; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + ret = media_create_pad_link(&csi->subdev.entity, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + &vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + video_unregister_device(vdev); + return ret; + } + + return 0; +} + +static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); + + /* Should register only one source. */ + WARN_ON(csi->source); + + csi->source = subdev; + + return 0; +} + +static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); + int ret, i, src_pad; + + src_pad = media_entity_get_fwnode_pad(&csi->source->entity, + csi->source->fwnode, + MEDIA_PAD_FL_SOURCE); + if (src_pad < 0) { + dev_err(csi->dev, "Couldn't find source pad for subdev\n"); + return src_pad; + } + + ret = media_create_pad_link(&csi->source->entity, src_pad, + &csi->subdev.entity, TI_CSI2RX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + ret = ti_csi2rx_video_register(&csi->ctx[i]); + if (ret) + return ret; + } + + return v4l2_device_register_subdev_nodes(&csi->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations csi_async_notifier_ops = { + .bound = csi_async_notifier_bound, + .complete = csi_async_notifier_complete, +}; + +static int ti_csi2rx_init_subdev(struct ti_csi2rx_dev *csi) +{ + struct fwnode_handle *fwnode; + struct v4l2_async_subdev *asd; + struct device_node *node; + int ret; + + node = of_get_child_by_name(csi->dev->of_node, "csi-bridge"); + if (!node) + return -EINVAL; + + fwnode = of_fwnode_handle(node); + if (!fwnode) { + of_node_put(node); + return -EINVAL; + } + + v4l2_async_notifier_init(&csi->notifier); + csi->notifier.ops = &csi_async_notifier_ops; + + asd = v4l2_async_notifier_add_fwnode_subdev(&csi->notifier, fwnode, + sizeof(struct v4l2_async_subdev)); + of_node_put(node); + if (IS_ERR(asd)) { + v4l2_async_notifier_cleanup(&csi->notifier); + return PTR_ERR(asd); + } + + ret = v4l2_async_notifier_register(&csi->v4l2_dev, &csi->notifier); + if (ret) { + v4l2_async_notifier_cleanup(&csi->notifier); + return ret; + } + + return 0; +} + +static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + const struct ti_csi2rx_fmt *fmt; + unsigned int reg; + + fmt = find_format_by_pix(ctx->v_fmt.fmt.pix.pixelformat); + if (!fmt) { + dev_err(csi->dev, "Unknown format\n"); + return; + } + + reg = SHIM_DMACNTX_EN; + reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_df); + + /* + * Using the values from the documentation gives incorrect ordering for + * the luma and chroma components. In practice, the "reverse" format + * gives the correct image. So for example, if the image is in UYVY, the + * reverse would be YVYU. + */ + switch (fmt->fourcc) { + case V4L2_PIX_FMT_UYVY: + reg |= FIELD_PREP(SHIM_DMACNTX_YUV422, + SHIM_DMACNTX_YVYU); + break; + case V4L2_PIX_FMT_VYUY: + reg |= FIELD_PREP(SHIM_DMACNTX_YUV422, + SHIM_DMACNTX_YUYV); + break; + case V4L2_PIX_FMT_YUYV: + reg |= FIELD_PREP(SHIM_DMACNTX_YUV422, + SHIM_DMACNTX_VYUY); + break; + case V4L2_PIX_FMT_YVYU: + reg |= FIELD_PREP(SHIM_DMACNTX_YUV422, + SHIM_DMACNTX_UYVY); + break; + default: + /* Ignore if not YUV 4:2:2 */ + break; + } + + reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc); + + writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); + + reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) | + FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 1); + writel(reg, csi->shim + SHIM_PSI_CFG0(ctx->idx)); +} + +static void ti_csi2rx_drain_callback(void *param) +{ + struct completion *drain_complete = param; + + complete(drain_complete); +} + +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *csi) +{ + struct dma_async_tx_descriptor *desc; + struct device *dev = csi->dma.chan->device->dev; + struct completion drain_complete; + void *buf; + size_t len = csi->v_fmt.fmt.pix.sizeimage; + dma_addr_t addr; + dma_cookie_t cookie; + int ret; + + init_completion(&drain_complete); + + buf = dma_alloc_coherent(dev, len, &addr, GFP_KERNEL | GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + ret = -EIO; + goto out; + } + + desc->callback = ti_csi2rx_drain_callback; + desc->callback_param = &drain_complete; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) + goto out; + + dma_async_issue_pending(csi->dma.chan); + + if (!wait_for_completion_timeout(&drain_complete, + msecs_to_jiffies(DRAIN_TIMEOUT_MS))) { + dmaengine_terminate_sync(csi->dma.chan); + ret = -ETIMEDOUT; + goto out; + } +out: + dma_free_coherent(dev, len, buf, addr); + return ret; +} + +static void ti_csi2rx_dma_callback(void *param) +{ + struct ti_csi2rx_buffer *buf = param; + struct ti_csi2rx_ctx *ctx = buf->ctx; + struct ti_csi2rx_dma *dma = &ctx->dma; + unsigned long flags = 0; + + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = ctx->sequence++; + + spin_lock_irqsave(&dma->lock, flags); + + WARN_ON(dma->curr != buf); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + /* If there are more buffers to process then start their transfer. */ + dma->curr = NULL; + while (!list_empty(&dma->queue)) { + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + list_del(&buf->list); + + if (ti_csi2rx_start_dma(ctx, buf)) { + dev_err(ctx->csi->dev, + "Failed to queue the next buffer for DMA\n"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } else { + dma->curr = buf; + break; + } + } + + if (!dma->curr) + dma->state = TI_CSI2RX_DMA_IDLE; + + spin_unlock_irqrestore(&dma->lock, flags); +} + +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, + struct ti_csi2rx_buffer *buf) +{ + unsigned long addr; + struct dma_async_tx_descriptor *desc; + size_t len = ctx->v_fmt.fmt.pix.sizeimage; + dma_cookie_t cookie; + int ret = 0; + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + desc = dmaengine_prep_slave_single(ctx->dma.chan, addr, len, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EIO; + + desc->callback = ti_csi2rx_dma_callback; + desc->callback_param = buf; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) + return ret; + + dma_async_issue_pending(ctx->dma.chan); + + return 0; +} + +static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(q); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) +{ + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = ctx->v_fmt.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(ctx->csi->dev, "Data will not fit into plane\n"); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + return 0; +} + +static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) +{ + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct ti_csi2rx_buffer *buf; + struct ti_csi2rx_dma *dma = &ctx->dma; + bool restart_dma = false; + unsigned long flags = 0; + int ret; + + buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf); + buf->ctx = ctx; + + spin_lock_irqsave(&dma->lock, flags); + /* + * Usually the DMA callback takes care of queueing the pending buffers. + * But if DMA has stalled due to lack of buffers, restart it now. + */ + if (dma->state == TI_CSI2RX_DMA_IDLE) { + /* + * Do not restart DMA with the lock held because + * ti_csi2rx_drain_dma() might block when allocating a buffer. + * There won't be a race on queueing DMA anyway since the + * callback is not being fired. + */ + restart_dma = true; + dma->curr = buf; + dma->state = TI_CSI2RX_DMA_ACTIVE; + } else { + list_add_tail(&buf->list, &dma->queue); + } + spin_unlock_irqrestore(&dma->lock, flags); + + if (restart_dma) { + /* + * Once frames start dropping, some data gets stuck in the DMA + * pipeline somewhere. So the first DMA transfer after frame + * drops gives a partial frame. This is obviously not useful to + * the application and will only confuse it. Issue a DMA + * transaction to drain that up. + */ + ret = ti_csi2rx_drain_dma(ctx); + if (ret) + dev_warn(ctx->csi->dev, + "Failed to drain DMA. Next frame might be bogus\n"); + + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret); + spin_lock_irqsave(&dma->lock, flags); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dma->curr = NULL; + dma->state = TI_CSI2RX_DMA_IDLE; + spin_unlock_irqrestore(&dma->lock, flags); + } + } +} + +static int ti_csi2rx_get_vc(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct v4l2_mbus_frame_desc fd; + struct media_pad *pad; + int ret, i; + + pad = media_entity_remote_pad(&csi->pads[TI_CSI2RX_PAD_SINK]); + if (!pad) + return -ENODEV; + + ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index, + &fd); + if (ret) + return ret; + + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (i = 0; i < fd.num_entries; i++) { + if (ctx->stream == fd.entry[i].stream) + return fd.entry[i].bus.csi2.vc; + } + + return -ENODEV; +} + +static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_dma *dma = &ctx->dma; + struct ti_csi2rx_buffer *buf, *tmp; + struct v4l2_subdev_route *route = NULL; + struct media_pad *remote_pad; + unsigned long flags = 0; + int ret = 0, i; + + spin_lock_irqsave(&dma->lock, flags); + if (list_empty(&dma->queue)) + ret = -EIO; + spin_unlock_irqrestore(&dma->lock, flags); + if (ret) + return ret; + + ret = media_pipeline_start(ctx->vdev.entity.pads, &csi->pipe); + if (ret) + goto err; + + remote_pad = media_entity_remote_pad(&ctx->pad); + if (!remote_pad) { + ret = -ENODEV; + goto err_pipeline; + } + + /* Find the stream to process. */ + for (i = 0; i < csi->routing.num_routes; i++) { + struct v4l2_subdev_route *r = &csi->routing.routes[i]; + + if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (r->source_pad != remote_pad->index) + continue; + + route = r; + break; + } + + if (!route) { + ret = -ENODEV; + goto err_pipeline; + } + + ctx->stream = route->sink_stream; + + ret = ti_csi2rx_get_vc(ctx); + if (ret == -ENOIOCTLCMD) + ctx->vc = 0; + else if (ret < 0) + goto err_pipeline; + else + ctx->vc = ret; + + ti_csi2rx_setup_shim(ctx); + + ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 1); + if (ret) + goto err_pipeline; + + ctx->sequence = 0; + + spin_lock_irqsave(&dma->lock, flags); + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + list_del(&buf->list); + dma->state = TI_CSI2RX_DMA_ACTIVE; + + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(csi->dev, "Failed to start DMA: %d\n", ret); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + spin_unlock_irqrestore(&dma->lock, flags); + goto err_stream; + } + + dma->curr = buf; + spin_unlock_irqrestore(&dma->lock, flags); + + return 0; + +err_stream: + v4l2_subdev_call(&csi->subdev, video, s_stream, 0); +err_pipeline: + media_pipeline_stop(ctx->vdev.entity.pads); +err: + spin_lock_irqsave(&dma->lock, flags); + list_for_each_entry_safe(buf, tmp, &dma->queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + ctx->dma.state = TI_CSI2RX_DMA_STOPPED; + spin_unlock_irqrestore(&dma->lock, flags); + + return ret; +} + +static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) +{ + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_buffer *buf = NULL, *tmp; + struct ti_csi2rx_dma *dma = &ctx->dma; + unsigned long flags = 0; + enum ti_csi2rx_dma_state state; + int ret; + + media_pipeline_stop(ctx->vdev.entity.pads); + + ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 0); + if (ret) + dev_err(csi->dev, "Failed to stop subdev stream\n"); + + ret = dmaengine_terminate_sync(ctx->dma.chan); + if (ret) + dev_err(csi->dev, "Failed to stop DMA\n"); + + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + spin_lock_irqsave(&dma->lock, flags); + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + if (dma->curr) + vb2_buffer_done(&dma->curr->vb.vb2_buf, VB2_BUF_STATE_ERROR); + + state = dma->state; + + dma->curr = NULL; + dma->state = TI_CSI2RX_DMA_STOPPED; + spin_unlock_irqrestore(&dma->lock, flags); + + /* + * TODO: For some reason the first frame is wrong if we don't toggle + * the pixel reset. But at the same time, drain does not work either. + * Figure this one out. + */ + if (state == TI_CSI2RX_DMA_IDLE) { + ret = ti_csi2rx_drain_dma(ctx); + if (ret) + dev_warn(csi->dev, + "Failed to drain DMA. Next frame might be bogus\n"); + } +} + +static const struct vb2_ops csi_vb2_qops = { + .queue_setup = ti_csi2rx_queue_setup, + .buf_prepare = ti_csi2rx_buffer_prepare, + .buf_queue = ti_csi2rx_buffer_queue, + .start_streaming = ti_csi2rx_start_streaming, + .stop_streaming = ti_csi2rx_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ti_csi2rx_dev, subdev); +} + +static struct v4l2_subdev_krouting * +ti_csi2rx_get_routing_table(struct ti_csi2rx_dev *csi, + struct v4l2_subdev_state *state, u32 which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &csi->routing; + else + return &state->routing; +} + +static struct v4l2_subdev_stream_configs * +ti_csi2rx_get_stream_configs(struct ti_csi2rx_dev *csi, + struct v4l2_subdev_state *state, u32 which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &csi->stream_configs; + else + return &state->stream_configs; +} + +static int ti_csi2rx_sd_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct v4l2_subdev_krouting *src; + + src = ti_csi2rx_get_routing_table(csi, state, routing->which); + + return v4l2_subdev_cpy_routing(routing, src); +} + +static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct v4l2_subdev_krouting *dst; + struct v4l2_subdev_stream_configs *stream_configs; + int ret; + + dst = ti_csi2rx_get_routing_table(csi, state, routing->which); + stream_configs = ti_csi2rx_get_stream_configs(csi, state, + routing->which); + + ret = v4l2_subdev_dup_routing(dst, routing); + if (ret) + return ret; + + ret = v4l2_init_stream_configs(stream_configs, dst); + if (ret) + return ret; + + return 0; +} + +static int ti_csi2rx_sd_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + u32 which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + + struct v4l2_subdev_route routes[] = { { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .which = which, + .num_routes = 1, + .routes = routes, + }; + + /* Initialize routing to single route to the fist source pad */ + return ti_csi2rx_sd_set_routing(sd, sd_state, &routing); +} + +static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + int ret = 0; + + mutex_lock(&csi->mutex); + + if (enable) { + if (csi->enable_count > 0) { + csi->enable_count++; + goto out; + } + + ret = v4l2_subdev_call(csi->source, video, s_stream, 1); + if (ret) + goto out; + + csi->enable_count++; + } else { + if (csi->enable_count == 0) { + ret = -EINVAL; + goto out; + } + + if (--csi->enable_count > 0) + goto out; + + ret = v4l2_subdev_call(csi->source, video, s_stream, 0); + } + +out: + mutex_unlock(&csi->mutex); + return ret; +} + +static const struct v4l2_subdev_video_ops ti_csi2rx_subdev_video_ops = { + .s_stream = ti_csi2rx_sd_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { + .init_cfg = ti_csi2rx_sd_init_cfg, + .get_routing = ti_csi2rx_sd_get_routing, + .set_routing = ti_csi2rx_sd_set_routing, +}; + +static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = { + .video = &ti_csi2rx_subdev_video_ops, + .pad = &ti_csi2rx_subdev_pad_ops, +}; + +static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_ctx *ctx) +{ + dma_release_channel(ctx->dma.chan); +} + +static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) +{ + media_device_unregister(&csi->mdev); + v4l2_device_unregister(&csi->v4l2_dev); + media_device_cleanup(&csi->mdev); +} + +static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi) +{ + v4l2_async_notifier_unregister(&csi->notifier); + v4l2_async_notifier_cleanup(&csi->notifier); +} + +static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_ctx *ctx) +{ + vb2_queue_release(&ctx->vidq); +} + +static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx) +{ + ti_csi2rx_cleanup_dma(ctx); + ti_csi2rx_cleanup_vb2q(ctx); + + video_unregister_device(&ctx->vdev); + + mutex_destroy(&ctx->mutex); +} + +static int ti_csi2rx_init_vb2q(struct ti_csi2rx_ctx *ctx) +{ + struct vb2_queue *q = &ctx->vidq; + int ret; + + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct ti_csi2rx_buffer); + q->ops = &csi_vb2_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->dev = dmaengine_get_dma_device(ctx->dma.chan); + q->lock = &ctx->mutex; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + ctx->vdev.queue = q; + + return 0; +} + +static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) +{ + struct dma_slave_config cfg; + char name[32]; + int ret; + + INIT_LIST_HEAD(&ctx->dma.queue); + spin_lock_init(&ctx->dma.lock); + + ctx->dma.state = TI_CSI2RX_DMA_STOPPED; + + snprintf(name, sizeof(name), "rx%u", ctx->idx); + ctx->dma.chan = dma_request_chan(ctx->csi->dev, name); + if (IS_ERR(ctx->dma.chan)) + return PTR_ERR(ctx->dma.chan); + + memset(&cfg, 0, sizeof(cfg)); + + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES; + + ret = dmaengine_slave_config(ctx->dma.chan, &cfg); + if (ret) + return ret; + + return 0; +} + +static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) +{ + struct media_device *mdev = &csi->mdev; + struct v4l2_subdev *sd = &csi->subdev; + int ret, i; + + mdev->dev = csi->dev; + mdev->hw_revision = 1; + strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + + media_device_init(mdev); + + csi->v4l2_dev.mdev = mdev; + + ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); + if (ret) + goto cleanup_media; + + ret = media_device_register(mdev); + if (ret) + goto unregister_v4l2; + + v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); + sd->dev = csi->dev; + + csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (i = TI_CSI2RX_PAD_FIRST_SOURCE; i < TI_CSI2RX_NUM_PADS; i++) + csi->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads), + csi->pads); + if (ret) + goto unregister_media; + + ret = ti_csi2rx_sd_init_cfg(sd, NULL); + if (ret) + goto unregister_media; + + ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd); + if (ret) + goto unregister_media; + + return 0; + +unregister_media: + media_device_unregister(mdev); +unregister_v4l2: + v4l2_device_unregister(&csi->v4l2_dev); +cleanup_media: + media_device_cleanup(mdev); + + return ret; +} + +static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct video_device *vdev = &ctx->vdev; + const struct ti_csi2rx_fmt *fmt; + struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; + int ret; + + mutex_init(&ctx->mutex); + + fmt = find_format_by_pix(V4L2_PIX_FMT_UYVY); + if (!fmt) + return -EINVAL; + + pix_fmt->width = 640; + pix_fmt->height = 480; + + ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&ctx->vdev.entity, 1, &ctx->pad); + if (ret) + return ret; + + snprintf(vdev->name, sizeof(vdev->name), "%s context %u", + dev_name(csi->dev), ctx->idx); + vdev->v4l2_dev = &csi->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->fops = &csi_fops; + vdev->ioctl_ops = &csi_ioctl_ops; + vdev->release = video_device_release_empty; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->lock = &ctx->mutex; + + video_set_drvdata(vdev, ctx); + + ret = ti_csi2rx_init_dma(ctx); + if (ret) + return ret; + + ret = ti_csi2rx_init_vb2q(ctx); + if (ret) + goto cleanup_dma; + + return 0; + +cleanup_dma: + ti_csi2rx_cleanup_dma(ctx); + return ret; +} + +static int ti_csi2rx_probe(struct platform_device *pdev) +{ + struct ti_csi2rx_dev *csi; + struct resource *res; + int ret, i; + unsigned int reg; + + csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); + if (!csi) + return -ENOMEM; + + csi->dev = &pdev->dev; + platform_set_drvdata(pdev, csi); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + csi->shim = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(csi->shim)) + return PTR_ERR(csi->shim); + + mutex_init(&csi->mutex); + + ret = ti_csi2rx_v4l2_init(csi); + if (ret) + return ret; + + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + csi->ctx[i].idx = i; + csi->ctx[i].csi = csi; + ret = ti_csi2rx_init_ctx(&csi->ctx[i]); + if (ret) + goto cleanup_ctx; + } + + ret = ti_csi2rx_init_subdev(csi); + if (ret) + goto cleanup_ctx; + + ret = of_platform_populate(csi->dev->of_node, NULL, NULL, csi->dev); + if (ret) { + dev_err(csi->dev, "Failed to create children: %d\n", ret); + goto cleanup_subdev; + } + + /* De-assert the pixel interface reset. */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + + return 0; + +cleanup_subdev: + ti_csi2rx_cleanup_subdev(csi); +cleanup_ctx: + + i--; + for (; i >= 0; i--) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); + + ti_csi2rx_cleanup_v4l2(csi); + return ret; +} + +static int ti_csi2rx_remove(struct platform_device *pdev) +{ + struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + if (vb2_is_busy(&csi->ctx[i].vidq)) + return -EBUSY; + } + + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); + + ti_csi2rx_cleanup_subdev(csi); + ti_csi2rx_cleanup_v4l2(csi); + + /* Assert the pixel reset. */ + writel(0, csi->shim + SHIM_CNTL); + + mutex_destroy(&csi->mutex); + + return 0; +} + +static const struct of_device_id ti_csi2rx_of_match[] = { + { .compatible = "ti,j721e-csi2rx", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ti_csi2rx_of_match); + +static struct platform_driver ti_csi2rx_pdrv = { + .probe = ti_csi2rx_probe, + .remove = ti_csi2rx_remove, + .driver = { + .name = TI_CSI2RX_MODULE_NAME, + .of_match_table = ti_csi2rx_of_match, + }, +}; + +module_platform_driver(ti_csi2rx_pdrv); + +MODULE_DESCRIPTION("TI J721E CSI2 RX Driver"); +MODULE_AUTHOR("Pratyush Yadav "); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); diff -Naur --no-dereference a/drivers/media/platform/ti/j721e-csi2rx/Makefile b/drivers/media/platform/ti/j721e-csi2rx/Makefile --- a/drivers/media/platform/ti/j721e-csi2rx/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/j721e-csi2rx/Makefile 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_TI_J721E_CSI2RX) += j721e-csi2rx.o diff -Naur --no-dereference a/drivers/media/platform/ti/Makefile b/drivers/media/platform/ti/Makefile --- a/drivers/media/platform/ti/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/Makefile 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-y += cal/ +obj-y += vpe/ +obj-y += j721e-csi2rx/ diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/csc.c b/drivers/media/platform/ti/vpe/csc.c --- a/drivers/media/platform/ti/vpe/csc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/csc.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Color space converter library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "csc.h" + +/* + * 12 coefficients in the order: + * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2 + */ +struct quantization { + u16 coeff[12]; +}; + +struct colorspace { + struct quantization limited; + struct quantization full; +}; + +struct encoding_direction { + struct colorspace r601; + struct colorspace r709; +}; + +struct csc_coeffs { + struct encoding_direction y2r; + struct encoding_direction r2y; +}; + +/* default colorspace coefficients */ +static struct csc_coeffs csc_coeffs = { + .y2r = { + .r601 = { + .limited = { + { /* SDTV */ + 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, + 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, + } + }, + .full = { + { /* SDTV */ + 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, + 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, + } + }, + }, + .r709 = { + .limited = { + { /* HDTV */ + 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, + 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, + } + }, + .full = { + { /* HDTV */ + 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, + 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, + } + }, + }, + }, + .r2y = { + .r601 = { + .limited = { + { /* SDTV */ + 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, + 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, + } + }, + .full = { + { /* SDTV */ + 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, + 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, + } + }, + }, + .r709 = { + .limited = { + { /* HDTV */ + 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, + 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, + } + }, + .full = { + { /* HDTV */ + 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2, + 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200, + } + }, + }, + }, + +}; + +void csc_dump_regs(struct csc_data *csc) +{ + struct device *dev = &csc->pdev->dev; + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \ + ioread32(csc->base + CSC_##r)) + + dev_dbg(dev, "CSC Registers @ %pa:\n", &csc->res->start); + + DUMPREG(CSC00); + DUMPREG(CSC01); + DUMPREG(CSC02); + DUMPREG(CSC03); + DUMPREG(CSC04); + DUMPREG(CSC05); + +#undef DUMPREG +} +EXPORT_SYMBOL(csc_dump_regs); + +void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5) +{ + *csc_reg5 |= CSC_BYPASS; +} +EXPORT_SYMBOL(csc_set_coeff_bypass); + +/* + * set the color space converter coefficient shadow register values + */ +void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, + struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt) +{ + u32 *csc_reg5 = csc_reg0 + 5; + u32 *shadow_csc = csc_reg0; + u16 *coeff, *end_coeff; + const struct v4l2_pix_format *pix; + const struct v4l2_pix_format_mplane *mp; + const struct v4l2_format_info *src_finfo, *dst_finfo; + enum v4l2_ycbcr_encoding src_ycbcr_enc, dst_ycbcr_enc; + enum v4l2_quantization src_quantization, dst_quantization; + u32 src_pixelformat, dst_pixelformat; + + if (V4L2_TYPE_IS_MULTIPLANAR(src_fmt->type)) { + mp = &src_fmt->fmt.pix_mp; + src_pixelformat = mp->pixelformat; + src_ycbcr_enc = mp->ycbcr_enc; + src_quantization = mp->quantization; + } else { + pix = &src_fmt->fmt.pix; + src_pixelformat = pix->pixelformat; + src_ycbcr_enc = pix->ycbcr_enc; + src_quantization = pix->quantization; + } + + if (V4L2_TYPE_IS_MULTIPLANAR(dst_fmt->type)) { + mp = &dst_fmt->fmt.pix_mp; + dst_pixelformat = mp->pixelformat; + dst_ycbcr_enc = mp->ycbcr_enc; + dst_quantization = mp->quantization; + } else { + pix = &dst_fmt->fmt.pix; + dst_pixelformat = pix->pixelformat; + dst_ycbcr_enc = pix->ycbcr_enc; + dst_quantization = pix->quantization; + } + + src_finfo = v4l2_format_info(src_pixelformat); + dst_finfo = v4l2_format_info(dst_pixelformat); + + if (v4l2_is_format_yuv(src_finfo) && + v4l2_is_format_rgb(dst_finfo)) { + /* Y2R */ + + /* + * These are not the standard default values but are + * set this way for historical compatibility + */ + if (src_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + src_ycbcr_enc = V4L2_YCBCR_ENC_601; + + if (src_quantization == V4L2_QUANTIZATION_DEFAULT) + src_quantization = V4L2_QUANTIZATION_FULL_RANGE; + + if (src_ycbcr_enc == V4L2_YCBCR_ENC_601) { + if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.y2r.r601.full.coeff; + else + coeff = csc_coeffs.y2r.r601.limited.coeff; + } else if (src_ycbcr_enc == V4L2_YCBCR_ENC_709) { + if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.y2r.r709.full.coeff; + else + coeff = csc_coeffs.y2r.r709.limited.coeff; + } else { + /* Should never reach this, but it keeps gcc happy */ + coeff = csc_coeffs.y2r.r601.full.coeff; + } + } else if (v4l2_is_format_rgb(src_finfo) && + v4l2_is_format_yuv(dst_finfo)) { + /* R2Y */ + + /* + * These are not the standard default values but are + * set this way for historical compatibility + */ + if (dst_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + dst_ycbcr_enc = V4L2_YCBCR_ENC_601; + + if (dst_quantization == V4L2_QUANTIZATION_DEFAULT) + dst_quantization = V4L2_QUANTIZATION_FULL_RANGE; + + if (dst_ycbcr_enc == V4L2_YCBCR_ENC_601) { + if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.r2y.r601.full.coeff; + else + coeff = csc_coeffs.r2y.r601.limited.coeff; + } else if (dst_ycbcr_enc == V4L2_YCBCR_ENC_709) { + if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.r2y.r709.full.coeff; + else + coeff = csc_coeffs.r2y.r709.limited.coeff; + } else { + /* Should never reach this, but it keeps gcc happy */ + coeff = csc_coeffs.r2y.r601.full.coeff; + } + } else { + *csc_reg5 |= CSC_BYPASS; + return; + } + + end_coeff = coeff + 12; + + for (; coeff < end_coeff; coeff += 2) + *shadow_csc++ = (*(coeff + 1) << 16) | *coeff; +} +EXPORT_SYMBOL(csc_set_coeff); + +struct csc_data *csc_create(struct platform_device *pdev, const char *res_name) +{ + struct csc_data *csc; + + dev_dbg(&pdev->dev, "csc_create\n"); + + csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL); + if (!csc) { + dev_err(&pdev->dev, "couldn't alloc csc_data\n"); + return ERR_PTR(-ENOMEM); + } + + csc->pdev = pdev; + + csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + res_name); + if (csc->res == NULL) { + dev_err(&pdev->dev, "missing '%s' platform resources data\n", + res_name); + return ERR_PTR(-ENODEV); + } + + csc->base = devm_ioremap_resource(&pdev->dev, csc->res); + if (IS_ERR(csc->base)) { + dev_err(&pdev->dev, "failed to ioremap\n"); + return ERR_CAST(csc->base); + } + + return csc; +} +EXPORT_SYMBOL(csc_create); + +MODULE_DESCRIPTION("TI VIP/VPE Color Space Converter"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/csc.h b/drivers/media/platform/ti/vpe/csc.h --- a/drivers/media/platform/ti/vpe/csc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/csc.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ +#ifndef TI_CSC_H +#define TI_CSC_H + +/* VPE color space converter regs */ +#define CSC_CSC00 0x00 +#define CSC_A0_MASK 0x1fff +#define CSC_A0_SHIFT 0 +#define CSC_B0_MASK 0x1fff +#define CSC_B0_SHIFT 16 + +#define CSC_CSC01 0x04 +#define CSC_C0_MASK 0x1fff +#define CSC_C0_SHIFT 0 +#define CSC_A1_MASK 0x1fff +#define CSC_A1_SHIFT 16 + +#define CSC_CSC02 0x08 +#define CSC_B1_MASK 0x1fff +#define CSC_B1_SHIFT 0 +#define CSC_C1_MASK 0x1fff +#define CSC_C1_SHIFT 16 + +#define CSC_CSC03 0x0c +#define CSC_A2_MASK 0x1fff +#define CSC_A2_SHIFT 0 +#define CSC_B2_MASK 0x1fff +#define CSC_B2_SHIFT 16 + +#define CSC_CSC04 0x10 +#define CSC_C2_MASK 0x1fff +#define CSC_C2_SHIFT 0 +#define CSC_D0_MASK 0x0fff +#define CSC_D0_SHIFT 16 + +#define CSC_CSC05 0x14 +#define CSC_D1_MASK 0x0fff +#define CSC_D1_SHIFT 0 +#define CSC_D2_MASK 0x0fff +#define CSC_D2_SHIFT 16 + +#define CSC_BYPASS (1 << 28) + +struct csc_data { + void __iomem *base; + struct resource *res; + + struct platform_device *pdev; +}; + +void csc_dump_regs(struct csc_data *csc); +void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5); +void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, + struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt); + +struct csc_data *csc_create(struct platform_device *pdev, const char *res_name); + +#endif diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/Makefile b/drivers/media/platform/ti/vpe/Makefile --- a/drivers/media/platform/ti/vpe/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/Makefile 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o +obj-$(CONFIG_VIDEO_TI_VPDMA) += ti-vpdma.o +obj-$(CONFIG_VIDEO_TI_SC) += ti-sc.o +obj-$(CONFIG_VIDEO_TI_CSC) += ti-csc.o + +ti-vpe-y := vpe.o +ti-vpdma-y := vpdma.o +ti-sc-y := sc.o +ti-csc-y := csc.o + +ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/sc.c b/drivers/media/platform/ti/vpe/sc.c --- a/drivers/media/platform/ti/vpe/sc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/sc.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Scaler library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#include +#include +#include +#include +#include + +#include "sc.h" +#include "sc_coeff.h" + +void sc_dump_regs(struct sc_data *sc) +{ + struct device *dev = &sc->pdev->dev; + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \ + ioread32(sc->base + CFG_##r)) + + dev_dbg(dev, "SC Registers @ %pa:\n", &sc->res->start); + + DUMPREG(SC0); + DUMPREG(SC1); + DUMPREG(SC2); + DUMPREG(SC3); + DUMPREG(SC4); + DUMPREG(SC5); + DUMPREG(SC6); + DUMPREG(SC8); + DUMPREG(SC9); + DUMPREG(SC10); + DUMPREG(SC11); + DUMPREG(SC12); + DUMPREG(SC13); + DUMPREG(SC17); + DUMPREG(SC18); + DUMPREG(SC19); + DUMPREG(SC20); + DUMPREG(SC21); + DUMPREG(SC22); + DUMPREG(SC23); + DUMPREG(SC24); + DUMPREG(SC25); + +#undef DUMPREG +} +EXPORT_SYMBOL(sc_dump_regs); + +/* + * set the horizontal scaler coefficients according to the ratio of output to + * input widths, after accounting for up to two levels of decimation + */ +void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, + unsigned int dst_w) +{ + int sixteenths; + int idx; + int i, j; + u16 *coeff_h = addr; + const u16 *cp; + + if (dst_w > src_w) { + idx = HS_UP_SCALE; + } else { + if ((dst_w << 1) < src_w) + dst_w <<= 1; /* first level decimation */ + if ((dst_w << 1) < src_w) + dst_w <<= 1; /* second level decimation */ + + if (dst_w == src_w) { + idx = HS_LE_16_16_SCALE; + } else { + sixteenths = (dst_w << 4) / src_w; + if (sixteenths < 8) + sixteenths = 8; + idx = HS_LT_9_16_SCALE + sixteenths - 8; + } + } + + cp = scaler_hs_coeffs[idx]; + + for (i = 0; i < SC_NUM_PHASES * 2; i++) { + for (j = 0; j < SC_H_NUM_TAPS; j++) + *coeff_h++ = *cp++; + /* + * for each phase, the scaler expects space for 8 coefficients + * in it's memory. For the horizontal scaler, we copy the first + * 7 coefficients and skip the last slot to move to the next + * row to hold coefficients for the next phase + */ + coeff_h += SC_NUM_TAPS_MEM_ALIGN - SC_H_NUM_TAPS; + } + + sc->load_coeff_h = true; +} +EXPORT_SYMBOL(sc_set_hs_coeffs); + +/* + * set the vertical scaler coefficients according to the ratio of output to + * input heights + */ +void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, + unsigned int dst_h) +{ + int sixteenths; + int idx; + int i, j; + u16 *coeff_v = addr; + const u16 *cp; + + if (dst_h > src_h) { + idx = VS_UP_SCALE; + } else if (dst_h == src_h) { + idx = VS_1_TO_1_SCALE; + } else { + sixteenths = (dst_h << 4) / src_h; + if (sixteenths < 8) + sixteenths = 8; + idx = VS_LT_9_16_SCALE + sixteenths - 8; + } + + cp = scaler_vs_coeffs[idx]; + + for (i = 0; i < SC_NUM_PHASES * 2; i++) { + for (j = 0; j < SC_V_NUM_TAPS; j++) + *coeff_v++ = *cp++; + /* + * for the vertical scaler, we copy the first 5 coefficients and + * skip the last 3 slots to move to the next row to hold + * coefficients for the next phase + */ + coeff_v += SC_NUM_TAPS_MEM_ALIGN - SC_V_NUM_TAPS; + } + + sc->load_coeff_v = true; +} +EXPORT_SYMBOL(sc_set_vs_coeffs); + +void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, + u32 *sc_reg17, unsigned int src_w, unsigned int src_h, + unsigned int dst_w, unsigned int dst_h) +{ + struct device *dev = &sc->pdev->dev; + u32 val; + int dcm_x, dcm_shift; + bool use_rav; + unsigned long lltmp; + u32 lin_acc_inc, lin_acc_inc_u; + u32 col_acc_offset; + u16 factor = 0; + int row_acc_init_rav = 0, row_acc_init_rav_b = 0; + u32 row_acc_inc = 0, row_acc_offset = 0, row_acc_offset_b = 0; + /* + * location of SC register in payload memory with respect to the first + * register in the mmr address data block + */ + u32 *sc_reg9 = sc_reg8 + 1; + u32 *sc_reg12 = sc_reg8 + 4; + u32 *sc_reg13 = sc_reg8 + 5; + u32 *sc_reg24 = sc_reg17 + 7; + + val = sc_reg0[0]; + + /* clear all the features(they may get enabled elsewhere later) */ + val &= ~(CFG_SELFGEN_FID | CFG_TRIM | CFG_ENABLE_SIN2_VER_INTP | + CFG_INTERLACE_I | CFG_DCM_4X | CFG_DCM_2X | CFG_AUTO_HS | + CFG_ENABLE_EV | CFG_USE_RAV | CFG_INVT_FID | CFG_SC_BYPASS | + CFG_INTERLACE_O | CFG_Y_PK_EN | CFG_HP_BYPASS | CFG_LINEAR); + + if (src_w == dst_w && src_h == dst_h) { + val |= CFG_SC_BYPASS; + sc_reg0[0] = val; + return; + } + + /* we only support linear scaling for now */ + val |= CFG_LINEAR; + + /* configure horizontal scaler */ + + /* enable 2X or 4X decimation */ + dcm_x = src_w / dst_w; + if (dcm_x > 4) { + val |= CFG_DCM_4X; + dcm_shift = 2; + } else if (dcm_x > 2) { + val |= CFG_DCM_2X; + dcm_shift = 1; + } else { + dcm_shift = 0; + } + + lltmp = dst_w - 1; + lin_acc_inc = div64_u64(((u64)(src_w >> dcm_shift) - 1) << 24, lltmp); + lin_acc_inc_u = 0; + col_acc_offset = 0; + + dev_dbg(dev, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n", + src_w, dst_w, dcm_shift == 2 ? "4x" : + (dcm_shift == 1 ? "2x" : "none"), lin_acc_inc); + + /* configure vertical scaler */ + + /* use RAV for vertical scaler if vertical downscaling is > 4x */ + if (dst_h < (src_h >> 2)) { + use_rav = true; + val |= CFG_USE_RAV; + } else { + use_rav = false; + } + + if (use_rav) { + /* use RAV */ + factor = (u16) ((dst_h << 10) / src_h); + + row_acc_init_rav = factor + ((1 + factor) >> 1); + if (row_acc_init_rav >= 1024) + row_acc_init_rav -= 1024; + + row_acc_init_rav_b = row_acc_init_rav + + (1 + (row_acc_init_rav >> 1)) - + (1024 >> 1); + + if (row_acc_init_rav_b < 0) { + row_acc_init_rav_b += row_acc_init_rav; + row_acc_init_rav *= 2; + } + + dev_dbg(dev, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n", + src_h, dst_h, factor, row_acc_init_rav, + row_acc_init_rav_b); + } else { + /* use polyphase */ + row_acc_inc = ((src_h - 1) << 16) / (dst_h - 1); + row_acc_offset = 0; + row_acc_offset_b = 0; + + dev_dbg(dev, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n", + src_h, dst_h, row_acc_inc); + } + + + sc_reg0[0] = val; + sc_reg0[1] = row_acc_inc; + sc_reg0[2] = row_acc_offset; + sc_reg0[3] = row_acc_offset_b; + + sc_reg0[4] = ((lin_acc_inc_u & CFG_LIN_ACC_INC_U_MASK) << + CFG_LIN_ACC_INC_U_SHIFT) | (dst_w << CFG_TAR_W_SHIFT) | + (dst_h << CFG_TAR_H_SHIFT); + + sc_reg0[5] = (src_w << CFG_SRC_W_SHIFT) | (src_h << CFG_SRC_H_SHIFT); + + sc_reg0[6] = (row_acc_init_rav_b << CFG_ROW_ACC_INIT_RAV_B_SHIFT) | + (row_acc_init_rav << CFG_ROW_ACC_INIT_RAV_SHIFT); + + *sc_reg9 = lin_acc_inc; + + *sc_reg12 = col_acc_offset << CFG_COL_ACC_OFFSET_SHIFT; + + *sc_reg13 = factor; + + *sc_reg24 = (src_w << CFG_ORG_W_SHIFT) | (src_h << CFG_ORG_H_SHIFT); +} +EXPORT_SYMBOL(sc_config_scaler); + +struct sc_data *sc_create(struct platform_device *pdev, const char *res_name) +{ + struct sc_data *sc; + + dev_dbg(&pdev->dev, "sc_create\n"); + + sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL); + if (!sc) { + dev_err(&pdev->dev, "couldn't alloc sc_data\n"); + return ERR_PTR(-ENOMEM); + } + + sc->pdev = pdev; + + sc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); + if (!sc->res) { + dev_err(&pdev->dev, "missing '%s' platform resources data\n", + res_name); + return ERR_PTR(-ENODEV); + } + + sc->base = devm_ioremap_resource(&pdev->dev, sc->res); + if (IS_ERR(sc->base)) { + dev_err(&pdev->dev, "failed to ioremap\n"); + return ERR_CAST(sc->base); + } + + return sc; +} +EXPORT_SYMBOL(sc_create); + +MODULE_DESCRIPTION("TI VIP/VPE Scaler"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/sc_coeff.h b/drivers/media/platform/ti/vpe/sc_coeff.h --- a/drivers/media/platform/ti/vpe/sc_coeff.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/sc_coeff.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1339 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * VPE SC coefs + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#ifndef __TI_SC_COEFF_H +#define __TI_SC_COEFF_H + +/* horizontal scaler coefficients */ +enum { + HS_UP_SCALE = 0, + HS_LT_9_16_SCALE, + HS_LT_10_16_SCALE, + HS_LT_11_16_SCALE, + HS_LT_12_16_SCALE, + HS_LT_13_16_SCALE, + HS_LT_14_16_SCALE, + HS_LT_15_16_SCALE, + HS_LE_16_16_SCALE, +}; + +static const u16 scaler_hs_coeffs[13][SC_NUM_PHASES * 2 * SC_H_NUM_TAPS] = { + [HS_UP_SCALE] = { + /* Luma */ + 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, + 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, + 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, + 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, + 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, + 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, + 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, + 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, + 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, + 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, + 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, + 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, + 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, + 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, + 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, + 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, + 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, + 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, + 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, + 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, + 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, + 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, + 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, + 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, + 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, + 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, + 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, + 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, + 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, + 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, + 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, + 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, + /* Chroma */ + 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, + 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, + 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, + 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, + 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, + 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, + 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, + 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, + 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, + 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, + 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, + 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, + 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, + 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, + 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, + 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, + 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, + 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, + 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, + 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, + 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, + 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, + 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, + 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, + 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, + 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, + 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, + 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, + 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, + 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, + 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, + 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, + }, + [HS_LT_9_16_SCALE] = { + /* Luma */ + 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, + 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, + 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, + 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, + 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, + 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, + 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, + 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, + 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, + 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, + 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, + 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, + 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, + 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, + 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, + 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, + 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, + 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, + 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, + 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, + 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, + 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, + 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, + 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, + 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, + 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, + 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, + 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, + 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, + 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, + 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, + 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, + /* Chroma */ + 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, + 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, + 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, + 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, + 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, + 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, + 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, + 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, + 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, + 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, + 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, + 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, + 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, + 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, + 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, + 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, + 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, + 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, + 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, + 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, + 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, + 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, + 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, + 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, + 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, + 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, + 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, + 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, + 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, + 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, + 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, + 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, + }, + [HS_LT_10_16_SCALE] = { + /* Luma */ + 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, + 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, + 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, + 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, + 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, + 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, + 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, + 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, + 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, + 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, + 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, + 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, + 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, + 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, + 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, + 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, + 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, + 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, + 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, + 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, + 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, + 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, + 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, + 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, + 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, + 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, + 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, + 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, + 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, + 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, + 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, + 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, + /* Chroma */ + 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, + 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, + 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, + 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, + 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, + 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, + 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, + 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, + 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, + 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, + 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, + 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, + 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, + 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, + 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, + 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, + 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, + 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, + 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, + 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, + 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, + 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, + 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, + 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, + 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, + 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, + 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, + 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, + 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, + 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, + 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, + 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, + }, + [HS_LT_11_16_SCALE] = { + /* Luma */ + 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, + 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, + 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, + 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, + 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, + 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, + 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, + 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, + 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, + 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, + 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, + 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, + 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, + 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, + 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, + 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, + 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, + 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, + 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, + 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, + 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, + 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, + 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, + 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, + 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, + 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, + 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, + 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, + 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, + 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, + 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, + 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, + /* Chroma */ + 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, + 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, + 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, + 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, + 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, + 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, + 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, + 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, + 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, + 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, + 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, + 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, + 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, + 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, + 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, + 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, + 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, + 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, + 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, + 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, + 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, + 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, + 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, + 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, + 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, + 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, + 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, + 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, + 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, + 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, + 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, + 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, + }, + [HS_LT_12_16_SCALE] = { + /* Luma */ + 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, + 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, + 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, + 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, + 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, + 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, + 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, + 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, + 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, + 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, + 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, + 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, + 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, + 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, + 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, + 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, + 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, + 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, + 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, + 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, + 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, + 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, + 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, + 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, + 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, + 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, + 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, + 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, + 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, + 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, + 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, + 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, + /* Chroma */ + 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, + 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, + 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, + 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, + 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, + 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, + 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, + 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, + 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, + 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, + 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, + 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, + 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, + 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, + 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, + 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, + 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, + 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, + 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, + 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, + 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, + 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, + 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, + 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, + 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, + 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, + 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, + 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, + 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, + 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, + 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, + 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, + }, + [HS_LT_13_16_SCALE] = { + /* Luma */ + 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, + 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, + 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, + 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, + 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, + 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, + 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, + 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, + 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, + 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, + 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, + 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, + 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, + 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, + 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, + 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, + 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, + 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, + 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, + 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, + 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, + 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, + 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, + 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, + 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, + 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, + 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, + 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, + 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, + 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, + 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, + 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, + /* Chroma */ + 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, + 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, + 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, + 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, + 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, + 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, + 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, + 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, + 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, + 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, + 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, + 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, + 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, + 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, + 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, + 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, + 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, + 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, + 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, + 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, + 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, + 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, + 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, + 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, + 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, + 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, + 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, + 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, + 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, + 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, + 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, + 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, + }, + [HS_LT_14_16_SCALE] = { + /* Luma */ + 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, + 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, + 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, + 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, + 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, + 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, + 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, + 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, + 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, + 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, + 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, + 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, + 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, + 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, + 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, + 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, + 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, + 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, + 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, + 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, + 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, + 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, + 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, + 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, + 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, + 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, + 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, + 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, + 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, + 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, + 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, + 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, + /* Chroma */ + 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, + 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, + 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, + 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, + 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, + 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, + 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, + 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, + 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, + 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, + 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, + 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, + 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, + 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, + 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, + 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, + 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, + 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, + 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, + 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, + 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, + 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, + 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, + 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, + 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, + 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, + 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, + 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, + 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, + 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, + 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, + 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, + }, + [HS_LT_15_16_SCALE] = { + /* Luma */ + 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, + 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, + 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, + 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, + 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, + 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, + 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, + 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, + 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, + 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, + 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, + 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, + 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, + 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, + 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, + 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, + 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, + 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, + 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, + 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, + 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, + 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, + 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, + 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, + 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, + 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, + 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, + 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, + 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, + 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, + 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, + 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, + /* Chroma */ + 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, + 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, + 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, + 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, + 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, + 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, + 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, + 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, + 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, + 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, + 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, + 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, + 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, + 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, + 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, + 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, + 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, + 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, + 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, + 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, + 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, + 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, + 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, + 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, + 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, + 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, + 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, + 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, + 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, + 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, + 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, + 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, + }, + [HS_LE_16_16_SCALE] = { + /* Luma */ + 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, + 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, + 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, + 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, + 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, + 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, + 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, + 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, + 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, + 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, + 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, + 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, + 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, + 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, + 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, + 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, + 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, + 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, + 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, + 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, + 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, + 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, + 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, + 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, + 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, + 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, + 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, + 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, + 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, + 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, + 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, + 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, + /* Chroma */ + 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, + 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, + 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, + 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, + 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, + 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, + 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, + 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, + 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, + 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, + 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, + 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, + 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, + 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, + 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, + 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, + 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, + 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, + 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, + 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, + 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, + 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, + 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, + 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, + 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, + 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, + 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, + 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, + 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, + 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, + 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, + 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, + }, +}; + +/* vertical scaler coefficients */ +enum { + VS_UP_SCALE = 0, + VS_LT_9_16_SCALE, + VS_LT_10_16_SCALE, + VS_LT_11_16_SCALE, + VS_LT_12_16_SCALE, + VS_LT_13_16_SCALE, + VS_LT_14_16_SCALE, + VS_LT_15_16_SCALE, + VS_LT_16_16_SCALE, + VS_1_TO_1_SCALE, +}; + +static const u16 scaler_vs_coeffs[15][SC_NUM_PHASES * 2 * SC_V_NUM_TAPS] = { + [VS_UP_SCALE] = { + /* Luma */ + 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + /* Chroma */ + 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + }, + [VS_LT_9_16_SCALE] = { + /* Luma */ + 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, + 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, + 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, + 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, + 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, + 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, + 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, + 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, + 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, + 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, + 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, + 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, + 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, + 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, + 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, + 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, + 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, + 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, + 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, + 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, + 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, + 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, + 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, + 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, + 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, + 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, + 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, + 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, + 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, + 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, + 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, + 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, + /* Chroma */ + 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, + 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, + 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, + 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, + 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, + 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, + 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, + 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, + 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, + 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, + 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, + 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, + 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, + 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, + 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, + 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, + 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, + 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, + 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, + 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, + 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, + 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, + 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, + 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, + 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, + 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, + 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, + 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, + 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, + 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, + 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, + 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, + }, + [VS_LT_10_16_SCALE] = { + /* Luma */ + 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, + 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, + 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, + 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, + 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, + 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, + 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, + 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, + 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, + 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, + 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, + 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, + 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, + 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, + 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, + 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, + 0x0094, 0x036C, 0x036C, 0x0094, 0x0000, + 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, + 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, + 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, + 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, + 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, + 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, + 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, + 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, + 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, + 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, + 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, + 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, + 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, + 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, + 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, + /* Chroma */ + 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, + 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, + 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, + 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, + 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, + 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, + 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, + 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, + 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, + 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, + 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, + 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, + 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, + 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, + 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, + 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, + 0x0094, 0x036C, 0x036C, 0x0094, 0x0000, + 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, + 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, + 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, + 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, + 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, + 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, + 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, + 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, + 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, + 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, + 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, + 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, + 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, + 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, + 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, + }, + [VS_LT_11_16_SCALE] = { + /* Luma */ + 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, + 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, + 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, + 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, + 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, + 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, + 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, + 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, + 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, + 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, + 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, + 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, + 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, + 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, + 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, + 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, + 0x0069, 0x0397, 0x0397, 0x0069, 0x0000, + 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, + 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, + 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, + 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, + 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, + 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, + 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, + 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, + 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, + 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, + 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, + 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, + 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, + 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, + 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, + /* Chroma */ + 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, + 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, + 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, + 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, + 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, + 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, + 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, + 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, + 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, + 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, + 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, + 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, + 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, + 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, + 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, + 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, + 0x0069, 0x0397, 0x0397, 0x0069, 0x0000, + 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, + 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, + 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, + 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, + 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, + 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, + 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, + 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, + 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, + 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, + 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, + 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, + 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, + 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, + 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, + }, + [VS_LT_12_16_SCALE] = { + /* Luma */ + 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, + 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, + 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, + 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, + 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, + 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, + 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, + 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, + 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, + 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, + 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, + 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, + 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, + 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, + 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, + 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, + 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, + 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, + 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, + 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, + 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, + 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, + 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, + 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, + 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, + 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, + 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, + 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, + 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, + 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, + 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, + 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, + /* Chroma */ + 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, + 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, + 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, + 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, + 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, + 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, + 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, + 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, + 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, + 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, + 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, + 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, + 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, + 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, + 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, + 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, + 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, + 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, + 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, + 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, + 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, + 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, + 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, + 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, + 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, + 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, + 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, + 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, + 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, + 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, + 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, + 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, + }, + [VS_LT_13_16_SCALE] = { + /* Luma */ + 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, + 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, + 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, + 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, + 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, + 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, + 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, + 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, + 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, + 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, + 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, + 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, + 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, + 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, + 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, + 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, + 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, + 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, + 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, + 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, + 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, + 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, + 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, + 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, + 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, + 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, + 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, + 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, + 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, + 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, + 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, + 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, + /* Chroma */ + 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, + 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, + 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, + 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, + 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, + 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, + 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, + 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, + 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, + 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, + 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, + 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, + 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, + 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, + 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, + 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, + 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, + 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, + 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, + 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, + 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, + 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, + 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, + 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, + 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, + 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, + 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, + 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, + 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, + 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, + 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, + 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, + }, + [VS_LT_14_16_SCALE] = { + /* Luma */ + 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, + 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, + 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, + 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, + 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, + 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, + 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, + 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, + 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, + 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, + 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, + 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, + 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, + 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, + 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, + 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, + 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, + 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, + 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, + 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, + 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, + 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, + 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, + 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, + 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, + 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, + 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, + 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, + 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, + 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, + 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, + 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, + /* Chroma */ + 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, + 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, + 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, + 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, + 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, + 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, + 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, + 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, + 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, + 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, + 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, + 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, + 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, + 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, + 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, + 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, + 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, + 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, + 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, + 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, + 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, + 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, + 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, + 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, + 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, + 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, + 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, + 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, + 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, + 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, + 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, + 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, + }, + [VS_LT_15_16_SCALE] = { + /* Luma */ + 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, + 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, + 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, + 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, + 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, + 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, + 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, + 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, + 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, + 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, + 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, + 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, + 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, + 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, + 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, + 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, + 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, + 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, + 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, + 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, + 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, + 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, + 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, + 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, + 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, + 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, + 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, + 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, + 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, + 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, + 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, + 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, + /* Chroma */ + 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, + 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, + 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, + 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, + 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, + 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, + 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, + 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, + 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, + 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, + 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, + 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, + 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, + 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, + 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, + 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, + 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, + 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, + 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, + 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, + 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, + 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, + 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, + 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, + 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, + 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, + 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, + 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, + 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, + 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, + 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, + 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, + }, + [VS_LT_16_16_SCALE] = { + /* Luma */ + 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, + 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, + 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, + 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, + 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, + 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, + 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, + 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, + 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, + 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, + 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, + 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, + 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, + 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, + 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, + 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, + 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, + 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, + 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, + 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, + 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, + 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, + 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, + 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, + 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, + 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, + 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, + 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, + 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, + 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, + 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, + 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, + /* Chroma */ + 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, + 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, + 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, + 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, + 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, + 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, + 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, + 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, + 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, + 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, + 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, + 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, + 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, + 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, + 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, + 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, + 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, + 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, + 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, + 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, + 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, + 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, + 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, + 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, + 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, + 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, + 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, + 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, + 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, + 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, + 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, + 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, + }, + [VS_1_TO_1_SCALE] = { + /* Luma */ + 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + /* Chroma */ + 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + }, +}; +#endif diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/sc.h b/drivers/media/platform/ti/vpe/sc.h --- a/drivers/media/platform/ti/vpe/sc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/sc.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ +#ifndef TI_SC_H +#define TI_SC_H + +/* Scaler regs */ +#define CFG_SC0 0x0 +#define CFG_INTERLACE_O (1 << 0) +#define CFG_LINEAR (1 << 1) +#define CFG_SC_BYPASS (1 << 2) +#define CFG_INVT_FID (1 << 3) +#define CFG_USE_RAV (1 << 4) +#define CFG_ENABLE_EV (1 << 5) +#define CFG_AUTO_HS (1 << 6) +#define CFG_DCM_2X (1 << 7) +#define CFG_DCM_4X (1 << 8) +#define CFG_HP_BYPASS (1 << 9) +#define CFG_INTERLACE_I (1 << 10) +#define CFG_ENABLE_SIN2_VER_INTP (1 << 11) +#define CFG_Y_PK_EN (1 << 14) +#define CFG_TRIM (1 << 15) +#define CFG_SELFGEN_FID (1 << 16) + +#define CFG_SC1 0x4 +#define CFG_ROW_ACC_INC_MASK 0x07ffffff +#define CFG_ROW_ACC_INC_SHIFT 0 + +#define CFG_SC2 0x08 +#define CFG_ROW_ACC_OFFSET_MASK 0x0fffffff +#define CFG_ROW_ACC_OFFSET_SHIFT 0 + +#define CFG_SC3 0x0c +#define CFG_ROW_ACC_OFFSET_B_MASK 0x0fffffff +#define CFG_ROW_ACC_OFFSET_B_SHIFT 0 + +#define CFG_SC4 0x10 +#define CFG_TAR_H_MASK 0x07ff +#define CFG_TAR_H_SHIFT 0 +#define CFG_TAR_W_MASK 0x07ff +#define CFG_TAR_W_SHIFT 12 +#define CFG_LIN_ACC_INC_U_MASK 0x07 +#define CFG_LIN_ACC_INC_U_SHIFT 24 +#define CFG_NLIN_ACC_INIT_U_MASK 0x07 +#define CFG_NLIN_ACC_INIT_U_SHIFT 28 + +#define CFG_SC5 0x14 +#define CFG_SRC_H_MASK 0x07ff +#define CFG_SRC_H_SHIFT 0 +#define CFG_SRC_W_MASK 0x07ff +#define CFG_SRC_W_SHIFT 12 +#define CFG_NLIN_ACC_INC_U_MASK 0x07 +#define CFG_NLIN_ACC_INC_U_SHIFT 24 + +#define CFG_SC6 0x18 +#define CFG_ROW_ACC_INIT_RAV_MASK 0x03ff +#define CFG_ROW_ACC_INIT_RAV_SHIFT 0 +#define CFG_ROW_ACC_INIT_RAV_B_MASK 0x03ff +#define CFG_ROW_ACC_INIT_RAV_B_SHIFT 10 + +#define CFG_SC8 0x20 +#define CFG_NLIN_LEFT_MASK 0x07ff +#define CFG_NLIN_LEFT_SHIFT 0 +#define CFG_NLIN_RIGHT_MASK 0x07ff +#define CFG_NLIN_RIGHT_SHIFT 12 + +#define CFG_SC9 0x24 +#define CFG_LIN_ACC_INC CFG_SC9 + +#define CFG_SC10 0x28 +#define CFG_NLIN_ACC_INIT CFG_SC10 + +#define CFG_SC11 0x2c +#define CFG_NLIN_ACC_INC CFG_SC11 + +#define CFG_SC12 0x30 +#define CFG_COL_ACC_OFFSET_MASK 0x01ffffff +#define CFG_COL_ACC_OFFSET_SHIFT 0 + +#define CFG_SC13 0x34 +#define CFG_SC_FACTOR_RAV_MASK 0xff +#define CFG_SC_FACTOR_RAV_SHIFT 0 +#define CFG_CHROMA_INTP_THR_MASK 0x03ff +#define CFG_CHROMA_INTP_THR_SHIFT 12 +#define CFG_DELTA_CHROMA_THR_MASK 0x0f +#define CFG_DELTA_CHROMA_THR_SHIFT 24 + +#define CFG_SC17 0x44 +#define CFG_EV_THR_MASK 0x03ff +#define CFG_EV_THR_SHIFT 12 +#define CFG_DELTA_LUMA_THR_MASK 0x0f +#define CFG_DELTA_LUMA_THR_SHIFT 24 +#define CFG_DELTA_EV_THR_MASK 0x0f +#define CFG_DELTA_EV_THR_SHIFT 28 + +#define CFG_SC18 0x48 +#define CFG_HS_FACTOR_MASK 0x03ff +#define CFG_HS_FACTOR_SHIFT 0 +#define CFG_CONF_DEFAULT_MASK 0x01ff +#define CFG_CONF_DEFAULT_SHIFT 16 + +#define CFG_SC19 0x4c +#define CFG_HPF_COEFF0_MASK 0xff +#define CFG_HPF_COEFF0_SHIFT 0 +#define CFG_HPF_COEFF1_MASK 0xff +#define CFG_HPF_COEFF1_SHIFT 8 +#define CFG_HPF_COEFF2_MASK 0xff +#define CFG_HPF_COEFF2_SHIFT 16 +#define CFG_HPF_COEFF3_MASK 0xff +#define CFG_HPF_COEFF3_SHIFT 23 + +#define CFG_SC20 0x50 +#define CFG_HPF_COEFF4_MASK 0xff +#define CFG_HPF_COEFF4_SHIFT 0 +#define CFG_HPF_COEFF5_MASK 0xff +#define CFG_HPF_COEFF5_SHIFT 8 +#define CFG_HPF_NORM_SHIFT_MASK 0x07 +#define CFG_HPF_NORM_SHIFT_SHIFT 16 +#define CFG_NL_LIMIT_MASK 0x1ff +#define CFG_NL_LIMIT_SHIFT 20 + +#define CFG_SC21 0x54 +#define CFG_NL_LO_THR_MASK 0x01ff +#define CFG_NL_LO_THR_SHIFT 0 +#define CFG_NL_LO_SLOPE_MASK 0xff +#define CFG_NL_LO_SLOPE_SHIFT 16 + +#define CFG_SC22 0x58 +#define CFG_NL_HI_THR_MASK 0x01ff +#define CFG_NL_HI_THR_SHIFT 0 +#define CFG_NL_HI_SLOPE_SH_MASK 0x07 +#define CFG_NL_HI_SLOPE_SH_SHIFT 16 + +#define CFG_SC23 0x5c +#define CFG_GRADIENT_THR_MASK 0x07ff +#define CFG_GRADIENT_THR_SHIFT 0 +#define CFG_GRADIENT_THR_RANGE_MASK 0x0f +#define CFG_GRADIENT_THR_RANGE_SHIFT 12 +#define CFG_MIN_GY_THR_MASK 0xff +#define CFG_MIN_GY_THR_SHIFT 16 +#define CFG_MIN_GY_THR_RANGE_MASK 0x0f +#define CFG_MIN_GY_THR_RANGE_SHIFT 28 + +#define CFG_SC24 0x60 +#define CFG_ORG_H_MASK 0x07ff +#define CFG_ORG_H_SHIFT 0 +#define CFG_ORG_W_MASK 0x07ff +#define CFG_ORG_W_SHIFT 16 + +#define CFG_SC25 0x64 +#define CFG_OFF_H_MASK 0x07ff +#define CFG_OFF_H_SHIFT 0 +#define CFG_OFF_W_MASK 0x07ff +#define CFG_OFF_W_SHIFT 16 + +/* number of phases supported by the polyphase scalers */ +#define SC_NUM_PHASES 32 + +/* number of taps used by horizontal polyphase scaler */ +#define SC_H_NUM_TAPS 7 + +/* number of taps used by vertical polyphase scaler */ +#define SC_V_NUM_TAPS 5 + +/* number of taps expected by the scaler in it's coefficient memory */ +#define SC_NUM_TAPS_MEM_ALIGN 8 + +/* Maximum frame width the scaler can handle (in pixels) */ +#define SC_MAX_PIXEL_WIDTH 2047 + +/* Maximum frame height the scaler can handle (in lines) */ +#define SC_MAX_PIXEL_HEIGHT 2047 + +/* + * coefficient memory size in bytes: + * num phases x num sets(luma and chroma) x num taps(aligned) x coeff size + */ +#define SC_COEF_SRAM_SIZE (SC_NUM_PHASES * 2 * SC_NUM_TAPS_MEM_ALIGN * 2) + +struct sc_data { + void __iomem *base; + struct resource *res; + + dma_addr_t loaded_coeff_h; /* loaded h coeffs in SC */ + dma_addr_t loaded_coeff_v; /* loaded v coeffs in SC */ + + bool load_coeff_h; /* have new h SC coeffs */ + bool load_coeff_v; /* have new v SC coeffs */ + + struct platform_device *pdev; +}; + +void sc_dump_regs(struct sc_data *sc); +void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, + unsigned int dst_w); +void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, + unsigned int dst_h); +void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, + u32 *sc_reg17, unsigned int src_w, unsigned int src_h, + unsigned int dst_w, unsigned int dst_h); +struct sc_data *sc_create(struct platform_device *pdev, const char *res_name); + +#endif diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c --- a/drivers/media/platform/ti/vpe/vpdma.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/vpdma.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VPDMA helper library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vpdma.h" +#include "vpdma_priv.h" + +#define VPDMA_FIRMWARE "vpdma-1b8.bin" + +const struct vpdma_data_format vpdma_yuv_fmts[] = { + [VPDMA_DATA_FMT_Y444] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_Y444, + .depth = 8, + }, + [VPDMA_DATA_FMT_Y422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_Y422, + .depth = 8, + }, + [VPDMA_DATA_FMT_Y420] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_Y420, + .depth = 8, + }, + [VPDMA_DATA_FMT_C444] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_C444, + .depth = 8, + }, + [VPDMA_DATA_FMT_C422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_C422, + .depth = 8, + }, + [VPDMA_DATA_FMT_C420] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_C420, + .depth = 4, + }, + [VPDMA_DATA_FMT_CB420] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_CB420, + .depth = 4, + }, + [VPDMA_DATA_FMT_YCR422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_YCR422, + .depth = 16, + }, + [VPDMA_DATA_FMT_YC444] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_YC444, + .depth = 24, + }, + [VPDMA_DATA_FMT_CRY422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_CRY422, + .depth = 16, + }, + [VPDMA_DATA_FMT_CBY422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_CBY422, + .depth = 16, + }, + [VPDMA_DATA_FMT_YCB422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_YCB422, + .depth = 16, + }, +}; +EXPORT_SYMBOL(vpdma_yuv_fmts); + +const struct vpdma_data_format vpdma_rgb_fmts[] = { + [VPDMA_DATA_FMT_RGB565] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_RGB16_565, + .depth = 16, + }, + [VPDMA_DATA_FMT_ARGB16_1555] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ARGB_1555, + .depth = 16, + }, + [VPDMA_DATA_FMT_ARGB16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ARGB_4444, + .depth = 16, + }, + [VPDMA_DATA_FMT_RGBA16_5551] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_RGBA_5551, + .depth = 16, + }, + [VPDMA_DATA_FMT_RGBA16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_RGBA_4444, + .depth = 16, + }, + [VPDMA_DATA_FMT_ARGB24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ARGB24_6666, + .depth = 24, + }, + [VPDMA_DATA_FMT_RGB24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_RGB24_888, + .depth = 24, + }, + [VPDMA_DATA_FMT_ARGB32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ARGB32_8888, + .depth = 32, + }, + [VPDMA_DATA_FMT_RGBA24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_RGBA24_6666, + .depth = 24, + }, + [VPDMA_DATA_FMT_RGBA32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_RGBA32_8888, + .depth = 32, + }, + [VPDMA_DATA_FMT_BGR565] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_BGR16_565, + .depth = 16, + }, + [VPDMA_DATA_FMT_ABGR16_1555] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ABGR_1555, + .depth = 16, + }, + [VPDMA_DATA_FMT_ABGR16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ABGR_4444, + .depth = 16, + }, + [VPDMA_DATA_FMT_BGRA16_5551] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_BGRA_5551, + .depth = 16, + }, + [VPDMA_DATA_FMT_BGRA16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_BGRA_4444, + .depth = 16, + }, + [VPDMA_DATA_FMT_ABGR24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ABGR24_6666, + .depth = 24, + }, + [VPDMA_DATA_FMT_BGR24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_BGR24_888, + .depth = 24, + }, + [VPDMA_DATA_FMT_ABGR32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_ABGR32_8888, + .depth = 32, + }, + [VPDMA_DATA_FMT_BGRA24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_BGRA24_6666, + .depth = 24, + }, + [VPDMA_DATA_FMT_BGRA32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, + .data_type = DATA_TYPE_BGRA32_8888, + .depth = 32, + }, +}; +EXPORT_SYMBOL(vpdma_rgb_fmts); + +/* + * To handle RAW format we are re-using the CBY422 + * vpdma data type so that we use the vpdma to re-order + * the incoming bytes, as the parser assumes that the + * first byte presented on the bus is the MSB of a 2 + * bytes value. + * RAW8 handles from 1 to 8 bits + * RAW16 handles from 9 to 16 bits + */ +const struct vpdma_data_format vpdma_raw_fmts[] = { + [VPDMA_DATA_FMT_RAW8] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_CBY422, + .depth = 8, + }, + [VPDMA_DATA_FMT_RAW16] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_CBY422, + .depth = 16, + }, +}; +EXPORT_SYMBOL(vpdma_raw_fmts); + +const struct vpdma_data_format vpdma_misc_fmts[] = { + [VPDMA_DATA_FMT_MV] = { + .type = VPDMA_DATA_FMT_TYPE_MISC, + .data_type = DATA_TYPE_MV, + .depth = 4, + }, +}; +EXPORT_SYMBOL(vpdma_misc_fmts); + +struct vpdma_channel_info { + int num; /* VPDMA channel number */ + int cstat_offset; /* client CSTAT register offset */ +}; + +static const struct vpdma_channel_info chan_info[] = { + [VPE_CHAN_LUMA1_IN] = { + .num = VPE_CHAN_NUM_LUMA1_IN, + .cstat_offset = VPDMA_DEI_LUMA1_CSTAT, + }, + [VPE_CHAN_CHROMA1_IN] = { + .num = VPE_CHAN_NUM_CHROMA1_IN, + .cstat_offset = VPDMA_DEI_CHROMA1_CSTAT, + }, + [VPE_CHAN_LUMA2_IN] = { + .num = VPE_CHAN_NUM_LUMA2_IN, + .cstat_offset = VPDMA_DEI_LUMA2_CSTAT, + }, + [VPE_CHAN_CHROMA2_IN] = { + .num = VPE_CHAN_NUM_CHROMA2_IN, + .cstat_offset = VPDMA_DEI_CHROMA2_CSTAT, + }, + [VPE_CHAN_LUMA3_IN] = { + .num = VPE_CHAN_NUM_LUMA3_IN, + .cstat_offset = VPDMA_DEI_LUMA3_CSTAT, + }, + [VPE_CHAN_CHROMA3_IN] = { + .num = VPE_CHAN_NUM_CHROMA3_IN, + .cstat_offset = VPDMA_DEI_CHROMA3_CSTAT, + }, + [VPE_CHAN_MV_IN] = { + .num = VPE_CHAN_NUM_MV_IN, + .cstat_offset = VPDMA_DEI_MV_IN_CSTAT, + }, + [VPE_CHAN_MV_OUT] = { + .num = VPE_CHAN_NUM_MV_OUT, + .cstat_offset = VPDMA_DEI_MV_OUT_CSTAT, + }, + [VPE_CHAN_LUMA_OUT] = { + .num = VPE_CHAN_NUM_LUMA_OUT, + .cstat_offset = VPDMA_VIP_UP_Y_CSTAT, + }, + [VPE_CHAN_CHROMA_OUT] = { + .num = VPE_CHAN_NUM_CHROMA_OUT, + .cstat_offset = VPDMA_VIP_UP_UV_CSTAT, + }, + [VPE_CHAN_RGB_OUT] = { + .num = VPE_CHAN_NUM_RGB_OUT, + .cstat_offset = VPDMA_VIP_UP_Y_CSTAT, + }, +}; + +static u32 read_reg(struct vpdma_data *vpdma, int offset) +{ + return ioread32(vpdma->base + offset); +} + +static void write_reg(struct vpdma_data *vpdma, int offset, u32 value) +{ + iowrite32(value, vpdma->base + offset); +} + +static int read_field_reg(struct vpdma_data *vpdma, int offset, + u32 mask, int shift) +{ + return (read_reg(vpdma, offset) & (mask << shift)) >> shift; +} + +static void write_field_reg(struct vpdma_data *vpdma, int offset, u32 field, + u32 mask, int shift) +{ + u32 val = read_reg(vpdma, offset); + + val &= ~(mask << shift); + val |= (field & mask) << shift; + + write_reg(vpdma, offset, val); +} + +void vpdma_dump_regs(struct vpdma_data *vpdma) +{ + struct device *dev = &vpdma->pdev->dev; + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(vpdma, VPDMA_##r)) + + dev_dbg(dev, "VPDMA Registers:\n"); + + DUMPREG(PID); + DUMPREG(LIST_ADDR); + DUMPREG(LIST_ATTR); + DUMPREG(LIST_STAT_SYNC); + DUMPREG(BG_RGB); + DUMPREG(BG_YUV); + DUMPREG(SETUP); + DUMPREG(MAX_SIZE1); + DUMPREG(MAX_SIZE2); + DUMPREG(MAX_SIZE3); + + /* + * dumping registers of only group0 and group3, because VPE channels + * lie within group0 and group3 registers + */ + DUMPREG(INT_CHAN_STAT(0)); + DUMPREG(INT_CHAN_MASK(0)); + DUMPREG(INT_CHAN_STAT(3)); + DUMPREG(INT_CHAN_MASK(3)); + DUMPREG(INT_CLIENT0_STAT); + DUMPREG(INT_CLIENT0_MASK); + DUMPREG(INT_CLIENT1_STAT); + DUMPREG(INT_CLIENT1_MASK); + DUMPREG(INT_LIST0_STAT); + DUMPREG(INT_LIST0_MASK); + + /* + * these are registers specific to VPE clients, we can make this + * function dump client registers specific to VPE or VIP based on + * who is using it + */ + DUMPREG(DEI_CHROMA1_CSTAT); + DUMPREG(DEI_LUMA1_CSTAT); + DUMPREG(DEI_CHROMA2_CSTAT); + DUMPREG(DEI_LUMA2_CSTAT); + DUMPREG(DEI_CHROMA3_CSTAT); + DUMPREG(DEI_LUMA3_CSTAT); + DUMPREG(DEI_MV_IN_CSTAT); + DUMPREG(DEI_MV_OUT_CSTAT); + DUMPREG(VIP_UP_Y_CSTAT); + DUMPREG(VIP_UP_UV_CSTAT); + DUMPREG(VPI_CTL_CSTAT); +} +EXPORT_SYMBOL(vpdma_dump_regs); + +/* + * Allocate a DMA buffer + */ +int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size) +{ + buf->size = size; + buf->mapped = false; + buf->addr = kzalloc(size, GFP_KERNEL); + if (!buf->addr) + return -ENOMEM; + + WARN_ON(((unsigned long)buf->addr & VPDMA_DESC_ALIGN) != 0); + + return 0; +} +EXPORT_SYMBOL(vpdma_alloc_desc_buf); + +void vpdma_free_desc_buf(struct vpdma_buf *buf) +{ + WARN_ON(buf->mapped); + kfree(buf->addr); + buf->addr = NULL; + buf->size = 0; +} +EXPORT_SYMBOL(vpdma_free_desc_buf); + +/* + * map descriptor/payload DMA buffer, enabling DMA access + */ +int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf) +{ + struct device *dev = &vpdma->pdev->dev; + + WARN_ON(buf->mapped); + buf->dma_addr = dma_map_single(dev, buf->addr, buf->size, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, buf->dma_addr)) { + dev_err(dev, "failed to map buffer\n"); + return -EINVAL; + } + + buf->mapped = true; + + return 0; +} +EXPORT_SYMBOL(vpdma_map_desc_buf); + +/* + * unmap descriptor/payload DMA buffer, disabling DMA access and + * allowing the main processor to access the data + */ +void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf) +{ + struct device *dev = &vpdma->pdev->dev; + + if (buf->mapped) + dma_unmap_single(dev, buf->dma_addr, buf->size, + DMA_BIDIRECTIONAL); + + buf->mapped = false; +} +EXPORT_SYMBOL(vpdma_unmap_desc_buf); + +/* + * Cleanup all pending descriptors of a list + * First, stop the current list being processed. + * If the VPDMA was busy, this step makes vpdma to accept post lists. + * To cleanup the internal FSM, post abort list descriptor for all the + * channels from @channels array of size @size. + */ +int vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num, + int *channels, int size) +{ + struct vpdma_desc_list abort_list; + int i, ret, timeout = 500; + + write_reg(vpdma, VPDMA_LIST_ATTR, + (list_num << VPDMA_LIST_NUM_SHFT) | + (1 << VPDMA_LIST_STOP_SHFT)); + + if (size <= 0 || !channels) + return 0; + + ret = vpdma_create_desc_list(&abort_list, + size * sizeof(struct vpdma_dtd), VPDMA_LIST_TYPE_NORMAL); + if (ret) + return ret; + + for (i = 0; i < size; i++) + vpdma_add_abort_channel_ctd(&abort_list, channels[i]); + + ret = vpdma_map_desc_buf(vpdma, &abort_list.buf); + if (ret) + goto free_desc; + ret = vpdma_submit_descs(vpdma, &abort_list, list_num); + if (ret) + goto unmap_desc; + + while (vpdma_list_busy(vpdma, list_num) && --timeout) + ; + + if (timeout == 0) { + dev_err(&vpdma->pdev->dev, "Timed out cleaning up VPDMA list\n"); + ret = -EBUSY; + } + +unmap_desc: + vpdma_unmap_desc_buf(vpdma, &abort_list.buf); +free_desc: + vpdma_free_desc_buf(&abort_list.buf); + + return ret; +} +EXPORT_SYMBOL(vpdma_list_cleanup); + +/* + * create a descriptor list, the user of this list will append configuration, + * control and data descriptors to this list, this list will be submitted to + * VPDMA. VPDMA's list parser will go through each descriptor and perform the + * required DMA operations + */ +int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type) +{ + int r; + + r = vpdma_alloc_desc_buf(&list->buf, size); + if (r) + return r; + + list->next = list->buf.addr; + + list->type = type; + + return 0; +} +EXPORT_SYMBOL(vpdma_create_desc_list); + +/* + * once a descriptor list is parsed by VPDMA, we reset the list by emptying it, + * to allow new descriptors to be added to the list. + */ +void vpdma_reset_desc_list(struct vpdma_desc_list *list) +{ + list->next = list->buf.addr; +} +EXPORT_SYMBOL(vpdma_reset_desc_list); + +/* + * free the buffer allocated for the VPDMA descriptor list, this should be + * called when the user doesn't want to use VPDMA any more. + */ +void vpdma_free_desc_list(struct vpdma_desc_list *list) +{ + vpdma_free_desc_buf(&list->buf); + + list->next = NULL; +} +EXPORT_SYMBOL(vpdma_free_desc_list); + +bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num) +{ + return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16); +} +EXPORT_SYMBOL(vpdma_list_busy); + +/* + * submit a list of DMA descriptors to the VPE VPDMA, do not wait for completion + */ +int vpdma_submit_descs(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, int list_num) +{ + int list_size; + unsigned long flags; + + if (vpdma_list_busy(vpdma, list_num)) + return -EBUSY; + + /* 16-byte granularity */ + list_size = (list->next - list->buf.addr) >> 4; + + spin_lock_irqsave(&vpdma->lock, flags); + write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr); + + write_reg(vpdma, VPDMA_LIST_ATTR, + (list_num << VPDMA_LIST_NUM_SHFT) | + (list->type << VPDMA_LIST_TYPE_SHFT) | + list_size); + spin_unlock_irqrestore(&vpdma->lock, flags); + + return 0; +} +EXPORT_SYMBOL(vpdma_submit_descs); + +static void dump_dtd(struct vpdma_dtd *dtd); + +void vpdma_update_dma_addr(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, dma_addr_t dma_addr, + void *write_dtd, int drop, int idx) +{ + struct vpdma_dtd *dtd = list->buf.addr; + dma_addr_t write_desc_addr; + int offset; + + dtd += idx; + vpdma_unmap_desc_buf(vpdma, &list->buf); + + dtd->start_addr = dma_addr; + + /* Calculate write address from the offset of write_dtd from start + * of the list->buf + */ + offset = (void *)write_dtd - list->buf.addr; + write_desc_addr = list->buf.dma_addr + offset; + + if (drop) + dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, + 1, 1, 0); + else + dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, + 1, 0, 0); + + vpdma_map_desc_buf(vpdma, &list->buf); + + dump_dtd(dtd); +} +EXPORT_SYMBOL(vpdma_update_dma_addr); + +void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, + u32 width, u32 height) +{ + if (reg_addr != VPDMA_MAX_SIZE1 && reg_addr != VPDMA_MAX_SIZE2 && + reg_addr != VPDMA_MAX_SIZE3) + reg_addr = VPDMA_MAX_SIZE1; + + write_field_reg(vpdma, reg_addr, width - 1, + VPDMA_MAX_SIZE_WIDTH_MASK, VPDMA_MAX_SIZE_WIDTH_SHFT); + + write_field_reg(vpdma, reg_addr, height - 1, + VPDMA_MAX_SIZE_HEIGHT_MASK, VPDMA_MAX_SIZE_HEIGHT_SHFT); + +} +EXPORT_SYMBOL(vpdma_set_max_size); + +static void dump_cfd(struct vpdma_cfd *cfd) +{ + int class; + + class = cfd_get_class(cfd); + + pr_debug("config descriptor of payload class: %s\n", + class == CFD_CLS_BLOCK ? "simple block" : + "address data block"); + + if (class == CFD_CLS_BLOCK) + pr_debug("word0: dst_addr_offset = 0x%08x\n", + cfd->dest_addr_offset); + + if (class == CFD_CLS_BLOCK) + pr_debug("word1: num_data_wrds = %d\n", cfd->block_len); + + pr_debug("word2: payload_addr = 0x%08x\n", cfd->payload_addr); + + pr_debug("word3: pkt_type = %d, direct = %d, class = %d, dest = %d, payload_len = %d\n", + cfd_get_pkt_type(cfd), + cfd_get_direct(cfd), class, cfd_get_dest(cfd), + cfd_get_payload_len(cfd)); +} + +/* + * append a configuration descriptor to the given descriptor list, where the + * payload is in the form of a simple data block specified in the descriptor + * header, this is used to upload scaler coefficients to the scaler module + */ +void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client, + struct vpdma_buf *blk, u32 dest_offset) +{ + struct vpdma_cfd *cfd; + int len = blk->size; + + WARN_ON(blk->dma_addr & VPDMA_DESC_ALIGN); + + cfd = list->next; + WARN_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size)); + + cfd->dest_addr_offset = dest_offset; + cfd->block_len = len; + cfd->payload_addr = (u32) blk->dma_addr; + cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_BLOCK, + client, len >> 4); + + list->next = cfd + 1; + + dump_cfd(cfd); +} +EXPORT_SYMBOL(vpdma_add_cfd_block); + +/* + * append a configuration descriptor to the given descriptor list, where the + * payload is in the address data block format, this is used to a configure a + * discontiguous set of MMRs + */ +void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, + struct vpdma_buf *adb) +{ + struct vpdma_cfd *cfd; + unsigned int len = adb->size; + + WARN_ON(len & VPDMA_ADB_SIZE_ALIGN); + WARN_ON(adb->dma_addr & VPDMA_DESC_ALIGN); + + cfd = list->next; + BUG_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size)); + + cfd->w0 = 0; + cfd->w1 = 0; + cfd->payload_addr = (u32) adb->dma_addr; + cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_ADB, + client, len >> 4); + + list->next = cfd + 1; + + dump_cfd(cfd); +}; +EXPORT_SYMBOL(vpdma_add_cfd_adb); + +/* + * control descriptor format change based on what type of control descriptor it + * is, we only use 'sync on channel' control descriptors for now, so assume it's + * that + */ +static void dump_ctd(struct vpdma_ctd *ctd) +{ + pr_debug("control descriptor\n"); + + pr_debug("word3: pkt_type = %d, source = %d, ctl_type = %d\n", + ctd_get_pkt_type(ctd), ctd_get_source(ctd), ctd_get_ctl(ctd)); +} + +/* + * append a 'sync on channel' type control descriptor to the given descriptor + * list, this descriptor stalls the VPDMA list till the time DMA is completed + * on the specified channel + */ +void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, + enum vpdma_channel chan) +{ + struct vpdma_ctd *ctd; + + ctd = list->next; + WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size)); + + ctd->w0 = 0; + ctd->w1 = 0; + ctd->w2 = 0; + ctd->type_source_ctl = ctd_type_source_ctl(chan_info[chan].num, + CTD_TYPE_SYNC_ON_CHANNEL); + + list->next = ctd + 1; + + dump_ctd(ctd); +} +EXPORT_SYMBOL(vpdma_add_sync_on_channel_ctd); + +/* + * append an 'abort_channel' type control descriptor to the given descriptor + * list, this descriptor aborts any DMA transaction happening using the + * specified channel + */ +void vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list, + int chan_num) +{ + struct vpdma_ctd *ctd; + + ctd = list->next; + WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size)); + + ctd->w0 = 0; + ctd->w1 = 0; + ctd->w2 = 0; + ctd->type_source_ctl = ctd_type_source_ctl(chan_num, + CTD_TYPE_ABORT_CHANNEL); + + list->next = ctd + 1; + + dump_ctd(ctd); +} +EXPORT_SYMBOL(vpdma_add_abort_channel_ctd); + +static void dump_dtd(struct vpdma_dtd *dtd) +{ + int dir, chan; + + dir = dtd_get_dir(dtd); + chan = dtd_get_chan(dtd); + + pr_debug("%s data transfer descriptor for channel %d\n", + dir == DTD_DIR_OUT ? "outbound" : "inbound", chan); + + pr_debug("word0: data_type = %d, notify = %d, field = %d, 1D = %d, even_ln_skp = %d, odd_ln_skp = %d, line_stride = %d\n", + dtd_get_data_type(dtd), dtd_get_notify(dtd), dtd_get_field(dtd), + dtd_get_1d(dtd), dtd_get_even_line_skip(dtd), + dtd_get_odd_line_skip(dtd), dtd_get_line_stride(dtd)); + + if (dir == DTD_DIR_IN) + pr_debug("word1: line_length = %d, xfer_height = %d\n", + dtd_get_line_length(dtd), dtd_get_xfer_height(dtd)); + + pr_debug("word2: start_addr = %x\n", dtd->start_addr); + + pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, pri = %d, next_chan = %d\n", + dtd_get_pkt_type(dtd), + dtd_get_mode(dtd), dir, chan, dtd_get_priority(dtd), + dtd_get_next_chan(dtd)); + + if (dir == DTD_DIR_IN) + pr_debug("word4: frame_width = %d, frame_height = %d\n", + dtd_get_frame_width(dtd), dtd_get_frame_height(dtd)); + else + pr_debug("word4: desc_write_addr = 0x%08x, write_desc = %d, drp_data = %d, use_desc_reg = %d\n", + dtd_get_desc_write_addr(dtd), dtd_get_write_desc(dtd), + dtd_get_drop_data(dtd), dtd_get_use_desc(dtd)); + + if (dir == DTD_DIR_IN) + pr_debug("word5: hor_start = %d, ver_start = %d\n", + dtd_get_h_start(dtd), dtd_get_v_start(dtd)); + else + pr_debug("word5: max_width %d, max_height %d\n", + dtd_get_max_width(dtd), dtd_get_max_height(dtd)); + + pr_debug("word6: client specific attr0 = 0x%08x\n", dtd->client_attr0); + pr_debug("word7: client specific attr1 = 0x%08x\n", dtd->client_attr1); +} + +/* + * append an outbound data transfer descriptor to the given descriptor list, + * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel + * + * @list: vpdma desc list to which we add this descriptor + * @width: width of the image in pixels in memory + * @c_rect: compose params of output image + * @fmt: vpdma data format of the buffer + * dma_addr: dma address as seen by VPDMA + * max_width: enum for maximum width of data transfer + * max_height: enum for maximum height of data transfer + * chan: VPDMA channel + * flags: VPDMA flags to configure some descriptor fields + */ +void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, + int stride, const struct v4l2_rect *c_rect, + const struct vpdma_data_format *fmt, dma_addr_t dma_addr, + int max_w, int max_h, enum vpdma_channel chan, u32 flags) +{ + vpdma_rawchan_add_out_dtd(list, width, stride, c_rect, fmt, dma_addr, + max_w, max_h, chan_info[chan].num, flags); +} +EXPORT_SYMBOL(vpdma_add_out_dtd); + +void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width, + int stride, const struct v4l2_rect *c_rect, + const struct vpdma_data_format *fmt, dma_addr_t dma_addr, + int max_w, int max_h, int raw_vpdma_chan, u32 flags) +{ + int priority = 0; + int field = 0; + int notify = 1; + int channel, next_chan; + struct v4l2_rect rect = *c_rect; + int depth = fmt->depth; + struct vpdma_dtd *dtd; + + channel = next_chan = raw_vpdma_chan; + + if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && + (fmt->data_type == DATA_TYPE_C420 || + fmt->data_type == DATA_TYPE_CB420)) { + rect.height >>= 1; + rect.top >>= 1; + depth = 8; + } + + dma_addr += rect.top * stride + (rect.left * depth >> 3); + + dtd = list->next; + WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); + + dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type, + notify, + field, + !!(flags & VPDMA_DATA_FRAME_1D), + !!(flags & VPDMA_DATA_EVEN_LINE_SKIP), + !!(flags & VPDMA_DATA_ODD_LINE_SKIP), + stride); + dtd->w1 = 0; + dtd->start_addr = (u32) dma_addr; + dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), + DTD_DIR_OUT, channel, priority, next_chan); + dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0); + dtd->max_width_height = dtd_max_width_height(max_w, max_h); + dtd->client_attr0 = 0; + dtd->client_attr1 = 0; + + list->next = dtd + 1; + + dump_dtd(dtd); +} +EXPORT_SYMBOL(vpdma_rawchan_add_out_dtd); + +/* + * append an inbound data transfer descriptor to the given descriptor list, + * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel + * + * @list: vpdma desc list to which we add this descriptor + * @width: width of the image in pixels in memory(not the cropped width) + * @c_rect: crop params of input image + * @fmt: vpdma data format of the buffer + * dma_addr: dma address as seen by VPDMA + * chan: VPDMA channel + * field: top or bottom field info of the input image + * flags: VPDMA flags to configure some descriptor fields + * frame_width/height: the complete width/height of the image presented to the + * client (this makes sense when multiple channels are + * connected to the same client, forming a larger frame) + * start_h, start_v: position where the given channel starts providing pixel + * data to the client (makes sense when multiple channels + * contribute to the client) + */ +void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, + int stride, const struct v4l2_rect *c_rect, + const struct vpdma_data_format *fmt, dma_addr_t dma_addr, + enum vpdma_channel chan, int field, u32 flags, int frame_width, + int frame_height, int start_h, int start_v) +{ + int priority = 0; + int notify = 1; + int depth = fmt->depth; + int channel, next_chan; + struct v4l2_rect rect = *c_rect; + struct vpdma_dtd *dtd; + + channel = next_chan = chan_info[chan].num; + + if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && + (fmt->data_type == DATA_TYPE_C420 || + fmt->data_type == DATA_TYPE_CB420)) { + rect.height >>= 1; + rect.top >>= 1; + depth = 8; + } + + dma_addr += rect.top * stride + (rect.left * depth >> 3); + + dtd = list->next; + WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); + + dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type, + notify, + field, + !!(flags & VPDMA_DATA_FRAME_1D), + !!(flags & VPDMA_DATA_EVEN_LINE_SKIP), + !!(flags & VPDMA_DATA_ODD_LINE_SKIP), + stride); + + dtd->xfer_length_height = dtd_xfer_length_height(rect.width, + rect.height); + dtd->start_addr = (u32) dma_addr; + dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), + DTD_DIR_IN, channel, priority, next_chan); + dtd->frame_width_height = dtd_frame_width_height(frame_width, + frame_height); + dtd->start_h_v = dtd_start_h_v(start_h, start_v); + dtd->client_attr0 = 0; + dtd->client_attr1 = 0; + + list->next = dtd + 1; + + dump_dtd(dtd); +} +EXPORT_SYMBOL(vpdma_add_in_dtd); + +int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv) +{ + int i, list_num = -1; + unsigned long flags; + + spin_lock_irqsave(&vpdma->lock, flags); + for (i = 0; i < VPDMA_MAX_NUM_LIST && + vpdma->hwlist_used[i] == true; i++) + ; + + if (i < VPDMA_MAX_NUM_LIST) { + list_num = i; + vpdma->hwlist_used[i] = true; + vpdma->hwlist_priv[i] = priv; + } + spin_unlock_irqrestore(&vpdma->lock, flags); + + return list_num; +} +EXPORT_SYMBOL(vpdma_hwlist_alloc); + +void *vpdma_hwlist_get_priv(struct vpdma_data *vpdma, int list_num) +{ + if (!vpdma || list_num >= VPDMA_MAX_NUM_LIST) + return NULL; + + return vpdma->hwlist_priv[list_num]; +} +EXPORT_SYMBOL(vpdma_hwlist_get_priv); + +void *vpdma_hwlist_release(struct vpdma_data *vpdma, int list_num) +{ + void *priv; + unsigned long flags; + + spin_lock_irqsave(&vpdma->lock, flags); + vpdma->hwlist_used[list_num] = false; + priv = vpdma->hwlist_priv; + spin_unlock_irqrestore(&vpdma->lock, flags); + + return priv; +} +EXPORT_SYMBOL(vpdma_hwlist_release); + +/* set or clear the mask for list complete interrupt */ +void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int irq_num, + int list_num, bool enable) +{ + u32 reg_addr = VPDMA_INT_LIST0_MASK + VPDMA_INTX_OFFSET * irq_num; + u32 val; + + val = read_reg(vpdma, reg_addr); + if (enable) + val |= (1 << (list_num * 2)); + else + val &= ~(1 << (list_num * 2)); + write_reg(vpdma, reg_addr, val); +} +EXPORT_SYMBOL(vpdma_enable_list_complete_irq); + +/* get the LIST_STAT register */ +unsigned int vpdma_get_list_stat(struct vpdma_data *vpdma, int irq_num) +{ + u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num; + + return read_reg(vpdma, reg_addr); +} +EXPORT_SYMBOL(vpdma_get_list_stat); + +/* get the LIST_MASK register */ +unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num) +{ + u32 reg_addr = VPDMA_INT_LIST0_MASK + VPDMA_INTX_OFFSET * irq_num; + + return read_reg(vpdma, reg_addr); +} +EXPORT_SYMBOL(vpdma_get_list_mask); + +/* clear previously occurred list interrupts in the LIST_STAT register */ +void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num, + int list_num) +{ + u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num; + + write_reg(vpdma, reg_addr, 3 << (list_num * 2)); +} +EXPORT_SYMBOL(vpdma_clear_list_stat); + +void vpdma_set_bg_color(struct vpdma_data *vpdma, + struct vpdma_data_format *fmt, u32 color) +{ + if (fmt->type == VPDMA_DATA_FMT_TYPE_RGB) + write_reg(vpdma, VPDMA_BG_RGB, color); + else if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV) + write_reg(vpdma, VPDMA_BG_YUV, color); +} +EXPORT_SYMBOL(vpdma_set_bg_color); + +/* + * configures the output mode of the line buffer for the given client, the + * line buffer content can either be mirrored(each line repeated twice) or + * passed to the client as is + */ +void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode, + enum vpdma_channel chan) +{ + int client_cstat = chan_info[chan].cstat_offset; + + write_field_reg(vpdma, client_cstat, line_mode, + VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT); +} +EXPORT_SYMBOL(vpdma_set_line_mode); + +/* + * configures the event which should trigger VPDMA transfer for the given + * client + */ +void vpdma_set_frame_start_event(struct vpdma_data *vpdma, + enum vpdma_frame_start_event fs_event, + enum vpdma_channel chan) +{ + int client_cstat = chan_info[chan].cstat_offset; + + write_field_reg(vpdma, client_cstat, fs_event, + VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT); +} +EXPORT_SYMBOL(vpdma_set_frame_start_event); + +static void vpdma_firmware_cb(const struct firmware *f, void *context) +{ + struct vpdma_data *vpdma = context; + struct vpdma_buf fw_dma_buf; + int i, r; + + dev_dbg(&vpdma->pdev->dev, "firmware callback\n"); + + if (!f || !f->data) { + dev_err(&vpdma->pdev->dev, "couldn't get firmware\n"); + return; + } + + /* already initialized */ + if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, + VPDMA_LIST_RDY_SHFT)) { + vpdma->cb(vpdma->pdev); + return; + } + + r = vpdma_alloc_desc_buf(&fw_dma_buf, f->size); + if (r) { + dev_err(&vpdma->pdev->dev, + "failed to allocate dma buffer for firmware\n"); + goto rel_fw; + } + + memcpy(fw_dma_buf.addr, f->data, f->size); + + vpdma_map_desc_buf(vpdma, &fw_dma_buf); + + write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr); + + for (i = 0; i < 100; i++) { /* max 1 second */ + msleep_interruptible(10); + + if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, + VPDMA_LIST_RDY_SHFT)) + break; + } + + if (i == 100) { + dev_err(&vpdma->pdev->dev, "firmware upload failed\n"); + goto free_buf; + } + + vpdma->cb(vpdma->pdev); + +free_buf: + vpdma_unmap_desc_buf(vpdma, &fw_dma_buf); + + vpdma_free_desc_buf(&fw_dma_buf); +rel_fw: + release_firmware(f); +} + +static int vpdma_load_firmware(struct vpdma_data *vpdma) +{ + int r; + struct device *dev = &vpdma->pdev->dev; + + r = request_firmware_nowait(THIS_MODULE, 1, + (const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma, + vpdma_firmware_cb); + if (r) { + dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE); + return r; + } else { + dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE); + } + + return 0; +} + +int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma, + void (*cb)(struct platform_device *pdev)) +{ + struct resource *res; + int r; + + dev_dbg(&pdev->dev, "vpdma_create\n"); + + vpdma->pdev = pdev; + vpdma->cb = cb; + spin_lock_init(&vpdma->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma"); + if (res == NULL) { + dev_err(&pdev->dev, "missing platform resources data\n"); + return -ENODEV; + } + + vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!vpdma->base) { + dev_err(&pdev->dev, "failed to ioremap\n"); + return -ENOMEM; + } + + r = vpdma_load_firmware(vpdma); + if (r) { + pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE); + return r; + } + + return 0; +} +EXPORT_SYMBOL(vpdma_create); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_FIRMWARE(VPDMA_FIRMWARE); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h --- a/drivers/media/platform/ti/vpe/vpdma.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/vpdma.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#ifndef __TI_VPDMA_H_ +#define __TI_VPDMA_H_ + +#define VPDMA_MAX_NUM_LIST 8 +/* + * A vpdma_buf tracks the size, DMA address and mapping status of each + * driver DMA area. + */ +struct vpdma_buf { + void *addr; + dma_addr_t dma_addr; + size_t size; + bool mapped; +}; + +struct vpdma_desc_list { + struct vpdma_buf buf; + void *next; + int type; +}; + +struct vpdma_data { + void __iomem *base; + + struct platform_device *pdev; + + spinlock_t lock; + bool hwlist_used[VPDMA_MAX_NUM_LIST]; + void *hwlist_priv[VPDMA_MAX_NUM_LIST]; + /* callback to VPE driver when the firmware is loaded */ + void (*cb)(struct platform_device *pdev); +}; + +enum vpdma_data_format_type { + VPDMA_DATA_FMT_TYPE_YUV, + VPDMA_DATA_FMT_TYPE_RGB, + VPDMA_DATA_FMT_TYPE_MISC, +}; + +struct vpdma_data_format { + enum vpdma_data_format_type type; + int data_type; + u8 depth; +}; + +#define VPDMA_DESC_ALIGN 16 /* 16-byte descriptor alignment */ +#define VPDMA_STRIDE_ALIGN 16 /* + * line stride of source and dest + * buffers should be 16 byte aligned + */ +#define VPDMA_MAX_STRIDE 65520 /* Max line stride 16 byte aligned */ +#define VPDMA_DTD_DESC_SIZE 32 /* 8 words */ +#define VPDMA_CFD_CTD_DESC_SIZE 16 /* 4 words */ + +#define VPDMA_LIST_TYPE_NORMAL 0 +#define VPDMA_LIST_TYPE_SELF_MODIFYING 1 +#define VPDMA_LIST_TYPE_DOORBELL 2 + +enum vpdma_yuv_formats { + VPDMA_DATA_FMT_Y444 = 0, + VPDMA_DATA_FMT_Y422, + VPDMA_DATA_FMT_Y420, + VPDMA_DATA_FMT_C444, + VPDMA_DATA_FMT_C422, + VPDMA_DATA_FMT_C420, + VPDMA_DATA_FMT_CB420, + VPDMA_DATA_FMT_YCR422, + VPDMA_DATA_FMT_YC444, + VPDMA_DATA_FMT_CRY422, + VPDMA_DATA_FMT_CBY422, + VPDMA_DATA_FMT_YCB422, +}; + +enum vpdma_rgb_formats { + VPDMA_DATA_FMT_RGB565 = 0, + VPDMA_DATA_FMT_ARGB16_1555, + VPDMA_DATA_FMT_ARGB16, + VPDMA_DATA_FMT_RGBA16_5551, + VPDMA_DATA_FMT_RGBA16, + VPDMA_DATA_FMT_ARGB24, + VPDMA_DATA_FMT_RGB24, + VPDMA_DATA_FMT_ARGB32, + VPDMA_DATA_FMT_RGBA24, + VPDMA_DATA_FMT_RGBA32, + VPDMA_DATA_FMT_BGR565, + VPDMA_DATA_FMT_ABGR16_1555, + VPDMA_DATA_FMT_ABGR16, + VPDMA_DATA_FMT_BGRA16_5551, + VPDMA_DATA_FMT_BGRA16, + VPDMA_DATA_FMT_ABGR24, + VPDMA_DATA_FMT_BGR24, + VPDMA_DATA_FMT_ABGR32, + VPDMA_DATA_FMT_BGRA24, + VPDMA_DATA_FMT_BGRA32, +}; + +enum vpdma_raw_formats { + VPDMA_DATA_FMT_RAW8 = 0, + VPDMA_DATA_FMT_RAW16, +}; + +enum vpdma_misc_formats { + VPDMA_DATA_FMT_MV = 0, +}; + +extern const struct vpdma_data_format vpdma_yuv_fmts[]; +extern const struct vpdma_data_format vpdma_rgb_fmts[]; +extern const struct vpdma_data_format vpdma_raw_fmts[]; +extern const struct vpdma_data_format vpdma_misc_fmts[]; + +enum vpdma_frame_start_event { + VPDMA_FSEVENT_HDMI_FID = 0, + VPDMA_FSEVENT_DVO2_FID, + VPDMA_FSEVENT_HDCOMP_FID, + VPDMA_FSEVENT_SD_FID, + VPDMA_FSEVENT_LM_FID0, + VPDMA_FSEVENT_LM_FID1, + VPDMA_FSEVENT_LM_FID2, + VPDMA_FSEVENT_CHANNEL_ACTIVE, +}; + +/* max width configurations */ +enum vpdma_max_width { + MAX_OUT_WIDTH_UNLIMITED = 0, + MAX_OUT_WIDTH_REG1, + MAX_OUT_WIDTH_REG2, + MAX_OUT_WIDTH_REG3, + MAX_OUT_WIDTH_352, + MAX_OUT_WIDTH_768, + MAX_OUT_WIDTH_1280, + MAX_OUT_WIDTH_1920, +}; + +/* max height configurations */ +enum vpdma_max_height { + MAX_OUT_HEIGHT_UNLIMITED = 0, + MAX_OUT_HEIGHT_REG1, + MAX_OUT_HEIGHT_REG2, + MAX_OUT_HEIGHT_REG3, + MAX_OUT_HEIGHT_288, + MAX_OUT_HEIGHT_576, + MAX_OUT_HEIGHT_720, + MAX_OUT_HEIGHT_1080, +}; + +/* + * VPDMA channel numbers + */ +enum vpdma_channel { + VPE_CHAN_LUMA1_IN, + VPE_CHAN_CHROMA1_IN, + VPE_CHAN_LUMA2_IN, + VPE_CHAN_CHROMA2_IN, + VPE_CHAN_LUMA3_IN, + VPE_CHAN_CHROMA3_IN, + VPE_CHAN_MV_IN, + VPE_CHAN_MV_OUT, + VPE_CHAN_LUMA_OUT, + VPE_CHAN_CHROMA_OUT, + VPE_CHAN_RGB_OUT, +}; + +#define VIP_CHAN_VIP2_OFFSET 70 +#define VIP_CHAN_MULT_PORTB_OFFSET 16 +#define VIP_CHAN_YUV_PORTB_OFFSET 2 +#define VIP_CHAN_RGB_PORTB_OFFSET 1 + +#define VPDMA_MAX_CHANNELS 256 + +/* flags for VPDMA data descriptors */ +#define VPDMA_DATA_ODD_LINE_SKIP (1 << 0) +#define VPDMA_DATA_EVEN_LINE_SKIP (1 << 1) +#define VPDMA_DATA_FRAME_1D (1 << 2) +#define VPDMA_DATA_MODE_TILED (1 << 3) + +/* + * client identifiers used for configuration descriptors + */ +#define CFD_MMR_CLIENT 0 +#define CFD_SC_CLIENT 4 + +/* Address data block header format */ +struct vpdma_adb_hdr { + u32 offset; + u32 nwords; + u32 reserved0; + u32 reserved1; +}; + +/* helpers for creating ADB headers for config descriptors MMRs as client */ +#define ADB_ADDR(dma_buf, str, fld) ((dma_buf)->addr + offsetof(str, fld)) +#define MMR_ADB_ADDR(buf, str, fld) ADB_ADDR(&(buf), struct str, fld) + +#define VPDMA_SET_MMR_ADB_HDR(buf, str, hdr, regs, offset_a) \ + do { \ + struct vpdma_adb_hdr *h; \ + struct str *adb = NULL; \ + h = MMR_ADB_ADDR(buf, str, hdr); \ + h->offset = (offset_a); \ + h->nwords = sizeof(adb->regs) >> 2; \ + } while (0) + +/* vpdma descriptor buffer allocation and management */ +int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size); +void vpdma_free_desc_buf(struct vpdma_buf *buf); +int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf); +void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf); + +/* vpdma descriptor list funcs */ +int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type); +void vpdma_reset_desc_list(struct vpdma_desc_list *list); +void vpdma_free_desc_list(struct vpdma_desc_list *list); +int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list, + int list_num); +bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num); +void vpdma_update_dma_addr(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, dma_addr_t dma_addr, + void *write_dtd, int drop, int idx); + +/* VPDMA hardware list funcs */ +int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv); +void *vpdma_hwlist_get_priv(struct vpdma_data *vpdma, int list_num); +void *vpdma_hwlist_release(struct vpdma_data *vpdma, int list_num); + +/* helpers for creating vpdma descriptors */ +void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client, + struct vpdma_buf *blk, u32 dest_offset); +void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, + struct vpdma_buf *adb); +void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, + enum vpdma_channel chan); +void vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list, + int chan_num); +void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, + int stride, const struct v4l2_rect *c_rect, + const struct vpdma_data_format *fmt, dma_addr_t dma_addr, + int max_w, int max_h, enum vpdma_channel chan, u32 flags); +void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width, + int stride, const struct v4l2_rect *c_rect, + const struct vpdma_data_format *fmt, dma_addr_t dma_addr, + int max_w, int max_h, int raw_vpdma_chan, u32 flags); + +void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, + int stride, const struct v4l2_rect *c_rect, + const struct vpdma_data_format *fmt, dma_addr_t dma_addr, + enum vpdma_channel chan, int field, u32 flags, int frame_width, + int frame_height, int start_h, int start_v); +int vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num, + int *channels, int size); + +/* vpdma list interrupt management */ +void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int irq_num, + int list_num, bool enable); +void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num, + int list_num); +unsigned int vpdma_get_list_stat(struct vpdma_data *vpdma, int irq_num); +unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num); + +/* vpdma client configuration */ +void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode, + enum vpdma_channel chan); +void vpdma_set_frame_start_event(struct vpdma_data *vpdma, + enum vpdma_frame_start_event fs_event, enum vpdma_channel chan); +void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, + u32 width, u32 height); + +void vpdma_set_bg_color(struct vpdma_data *vpdma, + struct vpdma_data_format *fmt, u32 color); +void vpdma_dump_regs(struct vpdma_data *vpdma); + +/* initialize vpdma, passed with VPE's platform device pointer */ +int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma, + void (*cb)(struct platform_device *pdev)); + +#endif diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/vpdma_priv.h b/drivers/media/platform/ti/vpe/vpdma_priv.h --- a/drivers/media/platform/ti/vpe/vpdma_priv.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/vpdma_priv.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,639 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#ifndef _TI_VPDMA_PRIV_H_ +#define _TI_VPDMA_PRIV_H_ + +/* + * VPDMA Register offsets + */ + +/* Top level */ +#define VPDMA_PID 0x00 +#define VPDMA_LIST_ADDR 0x04 +#define VPDMA_LIST_ATTR 0x08 +#define VPDMA_LIST_STAT_SYNC 0x0c +#define VPDMA_BG_RGB 0x18 +#define VPDMA_BG_YUV 0x1c +#define VPDMA_SETUP 0x30 +#define VPDMA_MAX_SIZE1 0x34 +#define VPDMA_MAX_SIZE2 0x38 +#define VPDMA_MAX_SIZE3 0x3c +#define VPDMA_MAX_SIZE_WIDTH_MASK 0xffff +#define VPDMA_MAX_SIZE_WIDTH_SHFT 16 +#define VPDMA_MAX_SIZE_HEIGHT_MASK 0xffff +#define VPDMA_MAX_SIZE_HEIGHT_SHFT 0 + +/* Interrupts */ +#define VPDMA_INT_CHAN_STAT(grp) (0x40 + grp * 8) +#define VPDMA_INT_CHAN_MASK(grp) (VPDMA_INT_CHAN_STAT(grp) + 4) +#define VPDMA_INT_CLIENT0_STAT 0x78 +#define VPDMA_INT_CLIENT0_MASK 0x7c +#define VPDMA_INT_CLIENT1_STAT 0x80 +#define VPDMA_INT_CLIENT1_MASK 0x84 +#define VPDMA_INT_LIST0_STAT 0x88 +#define VPDMA_INT_LIST0_MASK 0x8c + +#define VPDMA_INTX_OFFSET 0x50 + +#define VPDMA_PERFMON(i) (0x200 + i * 4) + +/* VIP/VPE client registers */ +#define VPDMA_DEI_CHROMA1_CSTAT 0x0300 +#define VPDMA_DEI_LUMA1_CSTAT 0x0304 +#define VPDMA_DEI_LUMA2_CSTAT 0x0308 +#define VPDMA_DEI_CHROMA2_CSTAT 0x030c +#define VPDMA_DEI_LUMA3_CSTAT 0x0310 +#define VPDMA_DEI_CHROMA3_CSTAT 0x0314 +#define VPDMA_DEI_MV_IN_CSTAT 0x0330 +#define VPDMA_DEI_MV_OUT_CSTAT 0x033c +#define VPDMA_VIP_LO_Y_CSTAT 0x0388 +#define VPDMA_VIP_LO_UV_CSTAT 0x038c +#define VPDMA_VIP_UP_Y_CSTAT 0x0390 +#define VPDMA_VIP_UP_UV_CSTAT 0x0394 +#define VPDMA_VPI_CTL_CSTAT 0x03d0 + +/* Reg field info for VPDMA_CLIENT_CSTAT registers */ +#define VPDMA_CSTAT_LINE_MODE_MASK 0x03 +#define VPDMA_CSTAT_LINE_MODE_SHIFT 8 +#define VPDMA_CSTAT_FRAME_START_MASK 0xf +#define VPDMA_CSTAT_FRAME_START_SHIFT 10 + +#define VPDMA_LIST_NUM_MASK 0x07 +#define VPDMA_LIST_NUM_SHFT 24 +#define VPDMA_LIST_STOP_SHFT 20 +#define VPDMA_LIST_RDY_MASK 0x01 +#define VPDMA_LIST_RDY_SHFT 19 +#define VPDMA_LIST_TYPE_MASK 0x03 +#define VPDMA_LIST_TYPE_SHFT 16 +#define VPDMA_LIST_SIZE_MASK 0xffff + +/* + * The YUV data type definition below are taken from + * both the TRM and i839 Errata information. + * Use the correct data type considering byte + * reordering of components. + * + * Also since the single use of "C" in the 422 case + * to mean "Cr" (i.e. V component). It was decided + * to explicitly label them CR to remove any confusion. + * Bear in mind that the type label refer to the memory + * packed order (LSB - MSB). + */ +#define DATA_TYPE_Y444 0x0 +#define DATA_TYPE_Y422 0x1 +#define DATA_TYPE_Y420 0x2 +#define DATA_TYPE_C444 0x4 +#define DATA_TYPE_C422 0x5 +#define DATA_TYPE_C420 0x6 +#define DATA_TYPE_CB420 0x16 +#define DATA_TYPE_YC444 0x8 +#define DATA_TYPE_YCB422 0x7 +#define DATA_TYPE_YCR422 0x17 +#define DATA_TYPE_CBY422 0x27 +#define DATA_TYPE_CRY422 0x37 + +/* + * The RGB data type definition below are defined + * to follow Errata i819. + * The initial values were taken from: + * VPDMA_data_type_mapping_v0.2vayu_c.pdf + * But some of the ARGB definition appeared to be wrong + * in the document also. As they would yield RGBA instead. + * They have been corrected based on experimentation. + */ +#define DATA_TYPE_RGB16_565 0x10 +#define DATA_TYPE_ARGB_1555 0x13 +#define DATA_TYPE_ARGB_4444 0x14 +#define DATA_TYPE_RGBA_5551 0x11 +#define DATA_TYPE_RGBA_4444 0x12 +#define DATA_TYPE_ARGB24_6666 0x18 +#define DATA_TYPE_RGB24_888 0x16 +#define DATA_TYPE_ARGB32_8888 0x17 +#define DATA_TYPE_RGBA24_6666 0x15 +#define DATA_TYPE_RGBA32_8888 0x19 +#define DATA_TYPE_BGR16_565 0x0 +#define DATA_TYPE_ABGR_1555 0x3 +#define DATA_TYPE_ABGR_4444 0x4 +#define DATA_TYPE_BGRA_5551 0x1 +#define DATA_TYPE_BGRA_4444 0x2 +#define DATA_TYPE_ABGR24_6666 0x8 +#define DATA_TYPE_BGR24_888 0x6 +#define DATA_TYPE_ABGR32_8888 0x7 +#define DATA_TYPE_BGRA24_6666 0x5 +#define DATA_TYPE_BGRA32_8888 0x9 + +#define DATA_TYPE_MV 0x3 + +/* VPDMA channel numbers, some are common between VIP/VPE and appear twice */ +#define VPE_CHAN_NUM_LUMA1_IN 0 +#define VPE_CHAN_NUM_CHROMA1_IN 1 +#define VPE_CHAN_NUM_LUMA2_IN 2 +#define VPE_CHAN_NUM_CHROMA2_IN 3 +#define VPE_CHAN_NUM_LUMA3_IN 4 +#define VPE_CHAN_NUM_CHROMA3_IN 5 +#define VPE_CHAN_NUM_MV_IN 12 +#define VPE_CHAN_NUM_MV_OUT 15 +#define VIP1_CHAN_NUM_MULT_PORT_A_SRC0 38 +#define VIP1_CHAN_NUM_MULT_ANC_A_SRC0 70 +#define VPE_CHAN_NUM_LUMA_OUT 102 +#define VPE_CHAN_NUM_CHROMA_OUT 103 +#define VIP1_CHAN_NUM_PORT_A_LUMA 102 +#define VIP1_CHAN_NUM_PORT_A_CHROMA 103 +#define VPE_CHAN_NUM_RGB_OUT 106 +#define VIP1_CHAN_NUM_PORT_A_RGB 106 +#define VIP1_CHAN_NUM_PORT_B_RGB 107 +/* + * a VPDMA address data block payload for a configuration descriptor needs to + * have each sub block length as a multiple of 16 bytes. Therefore, the overall + * size of the payload also needs to be a multiple of 16 bytes. The sub block + * lengths should be ensured to be aligned by the VPDMA user. + */ +#define VPDMA_ADB_SIZE_ALIGN 0x0f + +/* + * data transfer descriptor + */ +struct vpdma_dtd { + u32 type_ctl_stride; + union { + u32 xfer_length_height; + u32 w1; + }; + u32 start_addr; + u32 pkt_ctl; + union { + u32 frame_width_height; /* inbound */ + u32 desc_write_addr; /* outbound */ + }; + union { + u32 start_h_v; /* inbound */ + u32 max_width_height; /* outbound */ + }; + u32 client_attr0; + u32 client_attr1; +}; + +/* Data Transfer Descriptor specifics */ +#define DTD_NO_NOTIFY 0 +#define DTD_NOTIFY 1 + +#define DTD_PKT_TYPE 0xa +#define DTD_DIR_IN 0 +#define DTD_DIR_OUT 1 + +/* type_ctl_stride */ +#define DTD_DATA_TYPE_MASK 0x3f +#define DTD_DATA_TYPE_SHFT 26 +#define DTD_NOTIFY_MASK 0x01 +#define DTD_NOTIFY_SHFT 25 +#define DTD_FIELD_MASK 0x01 +#define DTD_FIELD_SHFT 24 +#define DTD_1D_MASK 0x01 +#define DTD_1D_SHFT 23 +#define DTD_EVEN_LINE_SKIP_MASK 0x01 +#define DTD_EVEN_LINE_SKIP_SHFT 20 +#define DTD_ODD_LINE_SKIP_MASK 0x01 +#define DTD_ODD_LINE_SKIP_SHFT 16 +#define DTD_LINE_STRIDE_MASK 0xffff +#define DTD_LINE_STRIDE_SHFT 0 + +/* xfer_length_height */ +#define DTD_LINE_LENGTH_MASK 0xffff +#define DTD_LINE_LENGTH_SHFT 16 +#define DTD_XFER_HEIGHT_MASK 0xffff +#define DTD_XFER_HEIGHT_SHFT 0 + +/* pkt_ctl */ +#define DTD_PKT_TYPE_MASK 0x1f +#define DTD_PKT_TYPE_SHFT 27 +#define DTD_MODE_MASK 0x01 +#define DTD_MODE_SHFT 26 +#define DTD_DIR_MASK 0x01 +#define DTD_DIR_SHFT 25 +#define DTD_CHAN_MASK 0x01ff +#define DTD_CHAN_SHFT 16 +#define DTD_PRI_MASK 0x0f +#define DTD_PRI_SHFT 9 +#define DTD_NEXT_CHAN_MASK 0x01ff +#define DTD_NEXT_CHAN_SHFT 0 + +/* frame_width_height */ +#define DTD_FRAME_WIDTH_MASK 0xffff +#define DTD_FRAME_WIDTH_SHFT 16 +#define DTD_FRAME_HEIGHT_MASK 0xffff +#define DTD_FRAME_HEIGHT_SHFT 0 + +/* start_h_v */ +#define DTD_H_START_MASK 0xffff +#define DTD_H_START_SHFT 16 +#define DTD_V_START_MASK 0xffff +#define DTD_V_START_SHFT 0 + +#define DTD_DESC_START_MASK 0xffffffe0 +#define DTD_DESC_START_SHIFT 5 +#define DTD_WRITE_DESC_MASK 0x01 +#define DTD_WRITE_DESC_SHIFT 2 +#define DTD_DROP_DATA_MASK 0x01 +#define DTD_DROP_DATA_SHIFT 1 +#define DTD_USE_DESC_MASK 0x01 +#define DTD_USE_DESC_SHIFT 0 + +/* max_width_height */ +#define DTD_MAX_WIDTH_MASK 0x07 +#define DTD_MAX_WIDTH_SHFT 4 +#define DTD_MAX_HEIGHT_MASK 0x07 +#define DTD_MAX_HEIGHT_SHFT 0 + +static inline u32 dtd_type_ctl_stride(int type, bool notify, int field, + bool one_d, bool even_line_skip, bool odd_line_skip, + int line_stride) +{ + return (type << DTD_DATA_TYPE_SHFT) | (notify << DTD_NOTIFY_SHFT) | + (field << DTD_FIELD_SHFT) | (one_d << DTD_1D_SHFT) | + (even_line_skip << DTD_EVEN_LINE_SKIP_SHFT) | + (odd_line_skip << DTD_ODD_LINE_SKIP_SHFT) | + line_stride; +} + +static inline u32 dtd_xfer_length_height(int line_length, int xfer_height) +{ + return (line_length << DTD_LINE_LENGTH_SHFT) | xfer_height; +} + +static inline u32 dtd_pkt_ctl(bool mode, bool dir, int chan, int pri, + int next_chan) +{ + return (DTD_PKT_TYPE << DTD_PKT_TYPE_SHFT) | (mode << DTD_MODE_SHFT) | + (dir << DTD_DIR_SHFT) | (chan << DTD_CHAN_SHFT) | + (pri << DTD_PRI_SHFT) | next_chan; +} + +static inline u32 dtd_frame_width_height(int width, int height) +{ + return (width << DTD_FRAME_WIDTH_SHFT) | height; +} + +static inline u32 dtd_desc_write_addr(unsigned int addr, bool write_desc, + bool drop_data, bool use_desc) +{ + return (addr & DTD_DESC_START_MASK) | + (write_desc << DTD_WRITE_DESC_SHIFT) | + (drop_data << DTD_DROP_DATA_SHIFT) | + use_desc; +} + +static inline u32 dtd_start_h_v(int h_start, int v_start) +{ + return (h_start << DTD_H_START_SHFT) | v_start; +} + +static inline u32 dtd_max_width_height(int max_width, int max_height) +{ + return (max_width << DTD_MAX_WIDTH_SHFT) | max_height; +} + +static inline int dtd_get_data_type(struct vpdma_dtd *dtd) +{ + return dtd->type_ctl_stride >> DTD_DATA_TYPE_SHFT; +} + +static inline bool dtd_get_notify(struct vpdma_dtd *dtd) +{ + return (dtd->type_ctl_stride >> DTD_NOTIFY_SHFT) & DTD_NOTIFY_MASK; +} + +static inline int dtd_get_field(struct vpdma_dtd *dtd) +{ + return (dtd->type_ctl_stride >> DTD_FIELD_SHFT) & DTD_FIELD_MASK; +} + +static inline bool dtd_get_1d(struct vpdma_dtd *dtd) +{ + return (dtd->type_ctl_stride >> DTD_1D_SHFT) & DTD_1D_MASK; +} + +static inline bool dtd_get_even_line_skip(struct vpdma_dtd *dtd) +{ + return (dtd->type_ctl_stride >> DTD_EVEN_LINE_SKIP_SHFT) + & DTD_EVEN_LINE_SKIP_MASK; +} + +static inline bool dtd_get_odd_line_skip(struct vpdma_dtd *dtd) +{ + return (dtd->type_ctl_stride >> DTD_ODD_LINE_SKIP_SHFT) + & DTD_ODD_LINE_SKIP_MASK; +} + +static inline int dtd_get_line_stride(struct vpdma_dtd *dtd) +{ + return dtd->type_ctl_stride & DTD_LINE_STRIDE_MASK; +} + +static inline int dtd_get_line_length(struct vpdma_dtd *dtd) +{ + return dtd->xfer_length_height >> DTD_LINE_LENGTH_SHFT; +} + +static inline int dtd_get_xfer_height(struct vpdma_dtd *dtd) +{ + return dtd->xfer_length_height & DTD_XFER_HEIGHT_MASK; +} + +static inline int dtd_get_pkt_type(struct vpdma_dtd *dtd) +{ + return dtd->pkt_ctl >> DTD_PKT_TYPE_SHFT; +} + +static inline bool dtd_get_mode(struct vpdma_dtd *dtd) +{ + return (dtd->pkt_ctl >> DTD_MODE_SHFT) & DTD_MODE_MASK; +} + +static inline bool dtd_get_dir(struct vpdma_dtd *dtd) +{ + return (dtd->pkt_ctl >> DTD_DIR_SHFT) & DTD_DIR_MASK; +} + +static inline int dtd_get_chan(struct vpdma_dtd *dtd) +{ + return (dtd->pkt_ctl >> DTD_CHAN_SHFT) & DTD_CHAN_MASK; +} + +static inline int dtd_get_priority(struct vpdma_dtd *dtd) +{ + return (dtd->pkt_ctl >> DTD_PRI_SHFT) & DTD_PRI_MASK; +} + +static inline int dtd_get_next_chan(struct vpdma_dtd *dtd) +{ + return (dtd->pkt_ctl >> DTD_NEXT_CHAN_SHFT) & DTD_NEXT_CHAN_MASK; +} + +static inline int dtd_get_frame_width(struct vpdma_dtd *dtd) +{ + return dtd->frame_width_height >> DTD_FRAME_WIDTH_SHFT; +} + +static inline int dtd_get_frame_height(struct vpdma_dtd *dtd) +{ + return dtd->frame_width_height & DTD_FRAME_HEIGHT_MASK; +} + +static inline int dtd_get_desc_write_addr(struct vpdma_dtd *dtd) +{ + return dtd->desc_write_addr & DTD_DESC_START_MASK; +} + +static inline bool dtd_get_write_desc(struct vpdma_dtd *dtd) +{ + return (dtd->desc_write_addr >> DTD_WRITE_DESC_SHIFT) & + DTD_WRITE_DESC_MASK; +} + +static inline bool dtd_get_drop_data(struct vpdma_dtd *dtd) +{ + return (dtd->desc_write_addr >> DTD_DROP_DATA_SHIFT) & + DTD_DROP_DATA_MASK; +} + +static inline bool dtd_get_use_desc(struct vpdma_dtd *dtd) +{ + return dtd->desc_write_addr & DTD_USE_DESC_MASK; +} + +static inline int dtd_get_h_start(struct vpdma_dtd *dtd) +{ + return dtd->start_h_v >> DTD_H_START_SHFT; +} + +static inline int dtd_get_v_start(struct vpdma_dtd *dtd) +{ + return dtd->start_h_v & DTD_V_START_MASK; +} + +static inline int dtd_get_max_width(struct vpdma_dtd *dtd) +{ + return (dtd->max_width_height >> DTD_MAX_WIDTH_SHFT) & + DTD_MAX_WIDTH_MASK; +} + +static inline int dtd_get_max_height(struct vpdma_dtd *dtd) +{ + return (dtd->max_width_height >> DTD_MAX_HEIGHT_SHFT) & + DTD_MAX_HEIGHT_MASK; +} + +/* + * configuration descriptor + */ +struct vpdma_cfd { + union { + u32 dest_addr_offset; + u32 w0; + }; + union { + u32 block_len; /* in words */ + u32 w1; + }; + u32 payload_addr; + u32 ctl_payload_len; /* in words */ +}; + +/* Configuration descriptor specifics */ + +#define CFD_PKT_TYPE 0xb + +#define CFD_DIRECT 1 +#define CFD_INDIRECT 0 +#define CFD_CLS_ADB 0 +#define CFD_CLS_BLOCK 1 + +/* block_len */ +#define CFD__BLOCK_LEN_MASK 0xffff +#define CFD__BLOCK_LEN_SHFT 0 + +/* ctl_payload_len */ +#define CFD_PKT_TYPE_MASK 0x1f +#define CFD_PKT_TYPE_SHFT 27 +#define CFD_DIRECT_MASK 0x01 +#define CFD_DIRECT_SHFT 26 +#define CFD_CLASS_MASK 0x03 +#define CFD_CLASS_SHFT 24 +#define CFD_DEST_MASK 0xff +#define CFD_DEST_SHFT 16 +#define CFD_PAYLOAD_LEN_MASK 0xffff +#define CFD_PAYLOAD_LEN_SHFT 0 + +static inline u32 cfd_pkt_payload_len(bool direct, int cls, int dest, + int payload_len) +{ + return (CFD_PKT_TYPE << CFD_PKT_TYPE_SHFT) | + (direct << CFD_DIRECT_SHFT) | + (cls << CFD_CLASS_SHFT) | + (dest << CFD_DEST_SHFT) | + payload_len; +} + +static inline int cfd_get_pkt_type(struct vpdma_cfd *cfd) +{ + return cfd->ctl_payload_len >> CFD_PKT_TYPE_SHFT; +} + +static inline bool cfd_get_direct(struct vpdma_cfd *cfd) +{ + return (cfd->ctl_payload_len >> CFD_DIRECT_SHFT) & CFD_DIRECT_MASK; +} + +static inline bool cfd_get_class(struct vpdma_cfd *cfd) +{ + return (cfd->ctl_payload_len >> CFD_CLASS_SHFT) & CFD_CLASS_MASK; +} + +static inline int cfd_get_dest(struct vpdma_cfd *cfd) +{ + return (cfd->ctl_payload_len >> CFD_DEST_SHFT) & CFD_DEST_MASK; +} + +static inline int cfd_get_payload_len(struct vpdma_cfd *cfd) +{ + return cfd->ctl_payload_len & CFD_PAYLOAD_LEN_MASK; +} + +/* + * control descriptor + */ +struct vpdma_ctd { + union { + u32 timer_value; + u32 list_addr; + u32 w0; + }; + union { + u32 pixel_line_count; + u32 list_size; + u32 w1; + }; + union { + u32 event; + u32 fid_ctl; + u32 w2; + }; + u32 type_source_ctl; +}; + +/* control descriptor types */ +#define CTD_TYPE_SYNC_ON_CLIENT 0 +#define CTD_TYPE_SYNC_ON_LIST 1 +#define CTD_TYPE_SYNC_ON_EXT 2 +#define CTD_TYPE_SYNC_ON_LM_TIMER 3 +#define CTD_TYPE_SYNC_ON_CHANNEL 4 +#define CTD_TYPE_CHNG_CLIENT_IRQ 5 +#define CTD_TYPE_SEND_IRQ 6 +#define CTD_TYPE_RELOAD_LIST 7 +#define CTD_TYPE_ABORT_CHANNEL 8 + +#define CTD_PKT_TYPE 0xc + +/* timer_value */ +#define CTD_TIMER_VALUE_MASK 0xffff +#define CTD_TIMER_VALUE_SHFT 0 + +/* pixel_line_count */ +#define CTD_PIXEL_COUNT_MASK 0xffff +#define CTD_PIXEL_COUNT_SHFT 16 +#define CTD_LINE_COUNT_MASK 0xffff +#define CTD_LINE_COUNT_SHFT 0 + +/* list_size */ +#define CTD_LIST_SIZE_MASK 0xffff +#define CTD_LIST_SIZE_SHFT 0 + +/* event */ +#define CTD_EVENT_MASK 0x0f +#define CTD_EVENT_SHFT 0 + +/* fid_ctl */ +#define CTD_FID2_MASK 0x03 +#define CTD_FID2_SHFT 4 +#define CTD_FID1_MASK 0x03 +#define CTD_FID1_SHFT 2 +#define CTD_FID0_MASK 0x03 +#define CTD_FID0_SHFT 0 + +/* type_source_ctl */ +#define CTD_PKT_TYPE_MASK 0x1f +#define CTD_PKT_TYPE_SHFT 27 +#define CTD_SOURCE_MASK 0xff +#define CTD_SOURCE_SHFT 16 +#define CTD_CONTROL_MASK 0x0f +#define CTD_CONTROL_SHFT 0 + +static inline u32 ctd_pixel_line_count(int pixel_count, int line_count) +{ + return (pixel_count << CTD_PIXEL_COUNT_SHFT) | line_count; +} + +static inline u32 ctd_set_fid_ctl(int fid0, int fid1, int fid2) +{ + return (fid2 << CTD_FID2_SHFT) | (fid1 << CTD_FID1_SHFT) | fid0; +} + +static inline u32 ctd_type_source_ctl(int source, int control) +{ + return (CTD_PKT_TYPE << CTD_PKT_TYPE_SHFT) | + (source << CTD_SOURCE_SHFT) | control; +} + +static inline u32 ctd_get_pixel_count(struct vpdma_ctd *ctd) +{ + return ctd->pixel_line_count >> CTD_PIXEL_COUNT_SHFT; +} + +static inline int ctd_get_line_count(struct vpdma_ctd *ctd) +{ + return ctd->pixel_line_count & CTD_LINE_COUNT_MASK; +} + +static inline int ctd_get_event(struct vpdma_ctd *ctd) +{ + return ctd->event & CTD_EVENT_MASK; +} + +static inline int ctd_get_fid2_ctl(struct vpdma_ctd *ctd) +{ + return (ctd->fid_ctl >> CTD_FID2_SHFT) & CTD_FID2_MASK; +} + +static inline int ctd_get_fid1_ctl(struct vpdma_ctd *ctd) +{ + return (ctd->fid_ctl >> CTD_FID1_SHFT) & CTD_FID1_MASK; +} + +static inline int ctd_get_fid0_ctl(struct vpdma_ctd *ctd) +{ + return ctd->fid_ctl & CTD_FID2_MASK; +} + +static inline int ctd_get_pkt_type(struct vpdma_ctd *ctd) +{ + return ctd->type_source_ctl >> CTD_PKT_TYPE_SHFT; +} + +static inline int ctd_get_source(struct vpdma_ctd *ctd) +{ + return (ctd->type_source_ctl >> CTD_SOURCE_SHFT) & CTD_SOURCE_MASK; +} + +static inline int ctd_get_ctl(struct vpdma_ctd *ctd) +{ + return ctd->type_source_ctl & CTD_CONTROL_MASK; +} + +#endif diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c --- a/drivers/media/platform/ti/vpe/vpe.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/vpe.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,2667 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI VPE mem2mem driver, based on the virtual v4l2-mem2mem example driver + * + * Copyright (c) 2013 Texas Instruments Inc. + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * Based on the virtual v4l2-mem2mem example device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vpdma.h" +#include "vpdma_priv.h" +#include "vpe_regs.h" +#include "sc.h" +#include "csc.h" + +#define VPE_MODULE_NAME "vpe" + +/* minimum and maximum frame sizes */ +#define MIN_W 32 +#define MIN_H 32 +#define MAX_W 2048 +#define MAX_H 2048 + +/* required alignments */ +#define S_ALIGN 0 /* multiple of 1 */ +#define H_ALIGN 1 /* multiple of 2 */ + +/* flags that indicate a format can be used for capture/output */ +#define VPE_FMT_TYPE_CAPTURE (1 << 0) +#define VPE_FMT_TYPE_OUTPUT (1 << 1) + +/* used as plane indices */ +#define VPE_MAX_PLANES 2 +#define VPE_LUMA 0 +#define VPE_CHROMA 1 + +/* per m2m context info */ +#define VPE_MAX_SRC_BUFS 3 /* need 3 src fields to de-interlace */ + +#define VPE_DEF_BUFS_PER_JOB 1 /* default one buffer per batch job */ + +/* + * each VPE context can need up to 3 config descriptors, 7 input descriptors, + * 3 output descriptors, and 10 control descriptors + */ +#define VPE_DESC_LIST_SIZE (10 * VPDMA_DTD_DESC_SIZE + \ + 13 * VPDMA_CFD_CTD_DESC_SIZE) + +#define vpe_dbg(vpedev, fmt, arg...) \ + dev_dbg((vpedev)->v4l2_dev.dev, fmt, ##arg) +#define vpe_err(vpedev, fmt, arg...) \ + dev_err((vpedev)->v4l2_dev.dev, fmt, ##arg) + +struct vpe_us_coeffs { + unsigned short anchor_fid0_c0; + unsigned short anchor_fid0_c1; + unsigned short anchor_fid0_c2; + unsigned short anchor_fid0_c3; + unsigned short interp_fid0_c0; + unsigned short interp_fid0_c1; + unsigned short interp_fid0_c2; + unsigned short interp_fid0_c3; + unsigned short anchor_fid1_c0; + unsigned short anchor_fid1_c1; + unsigned short anchor_fid1_c2; + unsigned short anchor_fid1_c3; + unsigned short interp_fid1_c0; + unsigned short interp_fid1_c1; + unsigned short interp_fid1_c2; + unsigned short interp_fid1_c3; +}; + +/* + * Default upsampler coefficients + */ +static const struct vpe_us_coeffs us_coeffs[] = { + { + /* Coefficients for progressive input */ + 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, + 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, + }, + { + /* Coefficients for Top Field Interlaced input */ + 0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3, + /* Coefficients for Bottom Field Interlaced input */ + 0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9, + }, +}; + +/* + * the following registers are for configuring some of the parameters of the + * motion and edge detection blocks inside DEI, these generally remain the same, + * these could be passed later via userspace if some one needs to tweak these. + */ +struct vpe_dei_regs { + unsigned long mdt_spacial_freq_thr_reg; /* VPE_DEI_REG2 */ + unsigned long edi_config_reg; /* VPE_DEI_REG3 */ + unsigned long edi_lut_reg0; /* VPE_DEI_REG4 */ + unsigned long edi_lut_reg1; /* VPE_DEI_REG5 */ + unsigned long edi_lut_reg2; /* VPE_DEI_REG6 */ + unsigned long edi_lut_reg3; /* VPE_DEI_REG7 */ +}; + +/* + * default expert DEI register values, unlikely to be modified. + */ +static const struct vpe_dei_regs dei_regs = { + .mdt_spacial_freq_thr_reg = 0x020C0804u, + .edi_config_reg = 0x0118100Cu, + .edi_lut_reg0 = 0x08040200u, + .edi_lut_reg1 = 0x1010100Cu, + .edi_lut_reg2 = 0x10101010u, + .edi_lut_reg3 = 0x10101010u, +}; + +/* + * The port_data structure contains per-port data. + */ +struct vpe_port_data { + enum vpdma_channel channel; /* VPDMA channel */ + u8 vb_index; /* input frame f, f-1, f-2 index */ + u8 vb_part; /* plane index for co-panar formats */ +}; + +/* + * Define indices into the port_data tables + */ +#define VPE_PORT_LUMA1_IN 0 +#define VPE_PORT_CHROMA1_IN 1 +#define VPE_PORT_LUMA2_IN 2 +#define VPE_PORT_CHROMA2_IN 3 +#define VPE_PORT_LUMA3_IN 4 +#define VPE_PORT_CHROMA3_IN 5 +#define VPE_PORT_MV_IN 6 +#define VPE_PORT_MV_OUT 7 +#define VPE_PORT_LUMA_OUT 8 +#define VPE_PORT_CHROMA_OUT 9 +#define VPE_PORT_RGB_OUT 10 + +static const struct vpe_port_data port_data[11] = { + [VPE_PORT_LUMA1_IN] = { + .channel = VPE_CHAN_LUMA1_IN, + .vb_index = 0, + .vb_part = VPE_LUMA, + }, + [VPE_PORT_CHROMA1_IN] = { + .channel = VPE_CHAN_CHROMA1_IN, + .vb_index = 0, + .vb_part = VPE_CHROMA, + }, + [VPE_PORT_LUMA2_IN] = { + .channel = VPE_CHAN_LUMA2_IN, + .vb_index = 1, + .vb_part = VPE_LUMA, + }, + [VPE_PORT_CHROMA2_IN] = { + .channel = VPE_CHAN_CHROMA2_IN, + .vb_index = 1, + .vb_part = VPE_CHROMA, + }, + [VPE_PORT_LUMA3_IN] = { + .channel = VPE_CHAN_LUMA3_IN, + .vb_index = 2, + .vb_part = VPE_LUMA, + }, + [VPE_PORT_CHROMA3_IN] = { + .channel = VPE_CHAN_CHROMA3_IN, + .vb_index = 2, + .vb_part = VPE_CHROMA, + }, + [VPE_PORT_MV_IN] = { + .channel = VPE_CHAN_MV_IN, + }, + [VPE_PORT_MV_OUT] = { + .channel = VPE_CHAN_MV_OUT, + }, + [VPE_PORT_LUMA_OUT] = { + .channel = VPE_CHAN_LUMA_OUT, + .vb_part = VPE_LUMA, + }, + [VPE_PORT_CHROMA_OUT] = { + .channel = VPE_CHAN_CHROMA_OUT, + .vb_part = VPE_CHROMA, + }, + [VPE_PORT_RGB_OUT] = { + .channel = VPE_CHAN_RGB_OUT, + .vb_part = VPE_LUMA, + }, +}; + + +/* driver info for each of the supported video formats */ +struct vpe_fmt { + u32 fourcc; /* standard format identifier */ + u8 types; /* CAPTURE and/or OUTPUT */ + u8 coplanar; /* set for unpacked Luma and Chroma */ + /* vpdma format info for each plane */ + struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES]; +}; + +static struct vpe_fmt vpe_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, + .coplanar = 1, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444], + &vpdma_yuv_fmts[VPDMA_DATA_FMT_C444], + }, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, + .coplanar = 1, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], + &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], + }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, + .coplanar = 1, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], + &vpdma_yuv_fmts[VPDMA_DATA_FMT_CB420], + }, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, + .coplanar = 0, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422], + }, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, + .coplanar = 0, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], + }, + }, + { + .fourcc = V4L2_PIX_FMT_BGR32, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB565], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGBA16_5551], + }, + }, +}; + +/* + * per-queue, driver-specific private data. + * there is one source queue and one destination queue for each m2m context. + */ +struct vpe_q_data { + /* current v4l2 format info */ + struct v4l2_format format; + unsigned int flags; + struct v4l2_rect c_rect; /* crop/compose rectangle */ + struct vpe_fmt *fmt; /* format info */ +}; + +/* vpe_q_data flag bits */ +#define Q_DATA_FRAME_1D BIT(0) +#define Q_DATA_MODE_TILED BIT(1) +#define Q_DATA_INTERLACED_ALTERNATE BIT(2) +#define Q_DATA_INTERLACED_SEQ_TB BIT(3) +#define Q_DATA_INTERLACED_SEQ_BT BIT(4) + +#define Q_IS_SEQ_XX (Q_DATA_INTERLACED_SEQ_TB | \ + Q_DATA_INTERLACED_SEQ_BT) + +#define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \ + Q_DATA_INTERLACED_SEQ_TB | \ + Q_DATA_INTERLACED_SEQ_BT) + +enum { + Q_DATA_SRC = 0, + Q_DATA_DST = 1, +}; + +/* find our format description corresponding to the passed v4l2_format */ +static struct vpe_fmt *__find_format(u32 fourcc) +{ + struct vpe_fmt *fmt; + unsigned int k; + + for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) { + fmt = &vpe_formats[k]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static struct vpe_fmt *find_format(struct v4l2_format *f) +{ + return __find_format(f->fmt.pix.pixelformat); +} + +/* + * there is one vpe_dev structure in the driver, it is shared by + * all instances. + */ +struct vpe_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct v4l2_m2m_dev *m2m_dev; + + atomic_t num_instances; /* count of driver instances */ + dma_addr_t loaded_mmrs; /* shadow mmrs in device */ + struct mutex dev_mutex; + spinlock_t lock; + + int irq; + void __iomem *base; + struct resource *res; + + struct vpdma_data vpdma_data; + struct vpdma_data *vpdma; /* vpdma data handle */ + struct sc_data *sc; /* scaler data handle */ + struct csc_data *csc; /* csc data handle */ +}; + +/* + * There is one vpe_ctx structure for each m2m context. + */ +struct vpe_ctx { + struct v4l2_fh fh; + struct vpe_dev *dev; + struct v4l2_ctrl_handler hdl; + + unsigned int field; /* current field */ + unsigned int sequence; /* current frame/field seq */ + unsigned int aborting; /* abort after next irq */ + + unsigned int bufs_per_job; /* input buffers per batch */ + unsigned int bufs_completed; /* bufs done in this batch */ + + struct vpe_q_data q_data[2]; /* src & dst queue data */ + struct vb2_v4l2_buffer *src_vbs[VPE_MAX_SRC_BUFS]; + struct vb2_v4l2_buffer *dst_vb; + + dma_addr_t mv_buf_dma[2]; /* dma addrs of motion vector in/out bufs */ + void *mv_buf[2]; /* virtual addrs of motion vector bufs */ + size_t mv_buf_size; /* current motion vector buffer size */ + struct vpdma_buf mmr_adb; /* shadow reg addr/data block */ + struct vpdma_buf sc_coeff_h; /* h coeff buffer */ + struct vpdma_buf sc_coeff_v; /* v coeff buffer */ + struct vpdma_desc_list desc_list; /* DMA descriptor list */ + + bool deinterlacing; /* using de-interlacer */ + bool load_mmrs; /* have new shadow reg values */ + + unsigned int src_mv_buf_selector; +}; + + +/* + * M2M devices get 2 queues. + * Return the queue given the type. + */ +static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &ctx->q_data[Q_DATA_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &ctx->q_data[Q_DATA_DST]; + default: + return NULL; + } + return NULL; +} + +static u32 read_reg(struct vpe_dev *dev, int offset) +{ + return ioread32(dev->base + offset); +} + +static void write_reg(struct vpe_dev *dev, int offset, u32 value) +{ + iowrite32(value, dev->base + offset); +} + +/* register field read/write helpers */ +static int get_field(u32 value, u32 mask, int shift) +{ + return (value & (mask << shift)) >> shift; +} + +static int read_field_reg(struct vpe_dev *dev, int offset, u32 mask, int shift) +{ + return get_field(read_reg(dev, offset), mask, shift); +} + +static void write_field(u32 *valp, u32 field, u32 mask, int shift) +{ + u32 val = *valp; + + val &= ~(mask << shift); + val |= (field & mask) << shift; + *valp = val; +} + +static void write_field_reg(struct vpe_dev *dev, int offset, u32 field, + u32 mask, int shift) +{ + u32 val = read_reg(dev, offset); + + write_field(&val, field, mask, shift); + + write_reg(dev, offset, val); +} + +/* + * DMA address/data block for the shadow registers + */ +struct vpe_mmr_adb { + struct vpdma_adb_hdr out_fmt_hdr; + u32 out_fmt_reg[1]; + u32 out_fmt_pad[3]; + struct vpdma_adb_hdr us1_hdr; + u32 us1_regs[8]; + struct vpdma_adb_hdr us2_hdr; + u32 us2_regs[8]; + struct vpdma_adb_hdr us3_hdr; + u32 us3_regs[8]; + struct vpdma_adb_hdr dei_hdr; + u32 dei_regs[8]; + struct vpdma_adb_hdr sc_hdr0; + u32 sc_regs0[7]; + u32 sc_pad0[1]; + struct vpdma_adb_hdr sc_hdr8; + u32 sc_regs8[6]; + u32 sc_pad8[2]; + struct vpdma_adb_hdr sc_hdr17; + u32 sc_regs17[9]; + u32 sc_pad17[3]; + struct vpdma_adb_hdr csc_hdr; + u32 csc_regs[6]; + u32 csc_pad[2]; +}; + +#define GET_OFFSET_TOP(ctx, obj, reg) \ + ((obj)->res->start - ctx->dev->res->start + reg) + +#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \ + VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a) +/* + * Set the headers for all of the address/data block structures. + */ +static void init_adb_hdrs(struct vpe_ctx *ctx) +{ + VPE_SET_MMR_ADB_HDR(ctx, out_fmt_hdr, out_fmt_reg, VPE_CLK_FORMAT_SELECT); + VPE_SET_MMR_ADB_HDR(ctx, us1_hdr, us1_regs, VPE_US1_R0); + VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0); + VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0); + VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8)); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17)); + VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, + GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); +}; + +/* + * Allocate or re-allocate the motion vector DMA buffers + * There are two buffers, one for input and one for output. + * However, the roles are reversed after each field is processed. + * In other words, after each field is processed, the previous + * output (dst) MV buffer becomes the new input (src) MV buffer. + */ +static int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size) +{ + struct device *dev = ctx->dev->v4l2_dev.dev; + + if (ctx->mv_buf_size == size) + return 0; + + if (ctx->mv_buf[0]) + dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0], + ctx->mv_buf_dma[0]); + + if (ctx->mv_buf[1]) + dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1], + ctx->mv_buf_dma[1]); + + if (size == 0) + return 0; + + ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0], + GFP_KERNEL); + if (!ctx->mv_buf[0]) { + vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); + return -ENOMEM; + } + + ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1], + GFP_KERNEL); + if (!ctx->mv_buf[1]) { + vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); + dma_free_coherent(dev, size, ctx->mv_buf[0], + ctx->mv_buf_dma[0]); + + return -ENOMEM; + } + + ctx->mv_buf_size = size; + ctx->src_mv_buf_selector = 0; + + return 0; +} + +static void free_mv_buffers(struct vpe_ctx *ctx) +{ + realloc_mv_buffers(ctx, 0); +} + +/* + * While de-interlacing, we keep the two most recent input buffers + * around. This function frees those two buffers when we have + * finished processing the current stream. + */ +static void free_vbs(struct vpe_ctx *ctx) +{ + struct vpe_dev *dev = ctx->dev; + unsigned long flags; + + if (ctx->src_vbs[2] == NULL) + return; + + spin_lock_irqsave(&dev->lock, flags); + if (ctx->src_vbs[2]) { + v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE); + if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) + v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE); + ctx->src_vbs[2] = NULL; + ctx->src_vbs[1] = NULL; + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* + * Enable or disable the VPE clocks + */ +static void vpe_set_clock_enable(struct vpe_dev *dev, bool on) +{ + u32 val = 0; + + if (on) + val = VPE_DATA_PATH_CLK_ENABLE | VPE_VPEDMA_CLK_ENABLE; + write_reg(dev, VPE_CLK_ENABLE, val); +} + +static void vpe_top_reset(struct vpe_dev *dev) +{ + + write_field_reg(dev, VPE_CLK_RESET, 1, VPE_DATA_PATH_CLK_RESET_MASK, + VPE_DATA_PATH_CLK_RESET_SHIFT); + + usleep_range(100, 150); + + write_field_reg(dev, VPE_CLK_RESET, 0, VPE_DATA_PATH_CLK_RESET_MASK, + VPE_DATA_PATH_CLK_RESET_SHIFT); +} + +static void vpe_top_vpdma_reset(struct vpe_dev *dev) +{ + write_field_reg(dev, VPE_CLK_RESET, 1, VPE_VPDMA_CLK_RESET_MASK, + VPE_VPDMA_CLK_RESET_SHIFT); + + usleep_range(100, 150); + + write_field_reg(dev, VPE_CLK_RESET, 0, VPE_VPDMA_CLK_RESET_MASK, + VPE_VPDMA_CLK_RESET_SHIFT); +} + +/* + * Load the correct of upsampler coefficients into the shadow MMRs + */ +static void set_us_coefficients(struct vpe_ctx *ctx) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + u32 *us1_reg = &mmr_adb->us1_regs[0]; + u32 *us2_reg = &mmr_adb->us2_regs[0]; + u32 *us3_reg = &mmr_adb->us3_regs[0]; + const unsigned short *cp, *end_cp; + + cp = &us_coeffs[0].anchor_fid0_c0; + + if (s_q_data->flags & Q_IS_INTERLACED) /* interlaced */ + cp += sizeof(us_coeffs[0]) / sizeof(*cp); + + end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp); + + while (cp < end_cp) { + write_field(us1_reg, *cp++, VPE_US_C0_MASK, VPE_US_C0_SHIFT); + write_field(us1_reg, *cp++, VPE_US_C1_MASK, VPE_US_C1_SHIFT); + *us2_reg++ = *us1_reg; + *us3_reg++ = *us1_reg++; + } + ctx->load_mmrs = true; +} + +/* + * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs. + */ +static void set_cfg_modes(struct vpe_ctx *ctx) +{ + struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + u32 *us1_reg0 = &mmr_adb->us1_regs[0]; + u32 *us2_reg0 = &mmr_adb->us2_regs[0]; + u32 *us3_reg0 = &mmr_adb->us3_regs[0]; + int cfg_mode = 1; + + /* + * Cfg Mode 0: YUV420 source, enable upsampler, DEI is de-interlacing. + * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing. + */ + + if (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21) + cfg_mode = 0; + + write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); + write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); + write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); + + ctx->load_mmrs = true; +} + +static void set_line_modes(struct vpe_ctx *ctx) +{ + struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; + int line_mode = 1; + + if (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21) + line_mode = 0; /* double lines to line buffer */ + + /* regs for now */ + vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN); + vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN); + vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN); + + /* frame start for input luma */ + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_LUMA1_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_LUMA2_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_LUMA3_IN); + + /* frame start for input chroma */ + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_CHROMA1_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_CHROMA2_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_CHROMA3_IN); + + /* frame start for MV in client */ + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_MV_IN); +} + +/* + * Set the shadow registers that are modified when the source + * format changes. + */ +static void set_src_registers(struct vpe_ctx *ctx) +{ + set_us_coefficients(ctx); +} + +/* + * Set the shadow registers that are modified when the destination + * format changes. + */ +static void set_dst_registers(struct vpe_ctx *ctx) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; + const struct v4l2_format_info *finfo; + u32 val = 0; + + finfo = v4l2_format_info(fmt->fourcc); + if (v4l2_is_format_rgb(finfo)) { + val |= VPE_RGB_OUT_SELECT; + vpdma_set_bg_color(ctx->dev->vpdma, + (struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff); + } else if (fmt->fourcc == V4L2_PIX_FMT_NV16) + val |= VPE_COLOR_SEPARATE_422; + + /* + * the source of CHR_DS and CSC is always the scaler, irrespective of + * whether it's used or not + */ + val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER; + + if (fmt->fourcc != V4L2_PIX_FMT_NV12 && + fmt->fourcc != V4L2_PIX_FMT_NV21) + val |= VPE_DS_BYPASS; + + mmr_adb->out_fmt_reg[0] = val; + + ctx->load_mmrs = true; +} + +/* + * Set the de-interlacer shadow register values + */ +static void set_dei_regs(struct vpe_ctx *ctx) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + unsigned int src_h = s_q_data->c_rect.height; + unsigned int src_w = s_q_data->c_rect.width; + u32 *dei_mmr0 = &mmr_adb->dei_regs[0]; + bool deinterlace = true; + u32 val = 0; + + /* + * according to TRM, we should set DEI in progressive bypass mode when + * the input content is progressive, however, DEI is bypassed correctly + * for both progressive and interlace content in interlace bypass mode. + * It has been recommended not to use progressive bypass mode. + */ + if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) { + deinterlace = false; + val = VPE_DEI_INTERLACE_BYPASS; + } + + src_h = deinterlace ? src_h * 2 : src_h; + + val |= (src_h << VPE_DEI_HEIGHT_SHIFT) | + (src_w << VPE_DEI_WIDTH_SHIFT) | + VPE_DEI_FIELD_FLUSH; + + *dei_mmr0 = val; + + ctx->load_mmrs = true; +} + +static void set_dei_shadow_registers(struct vpe_ctx *ctx) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + u32 *dei_mmr = &mmr_adb->dei_regs[0]; + const struct vpe_dei_regs *cur = &dei_regs; + + dei_mmr[2] = cur->mdt_spacial_freq_thr_reg; + dei_mmr[3] = cur->edi_config_reg; + dei_mmr[4] = cur->edi_lut_reg0; + dei_mmr[5] = cur->edi_lut_reg1; + dei_mmr[6] = cur->edi_lut_reg2; + dei_mmr[7] = cur->edi_lut_reg3; + + ctx->load_mmrs = true; +} + +static void config_edi_input_mode(struct vpe_ctx *ctx, int mode) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + u32 *edi_config_reg = &mmr_adb->dei_regs[3]; + + if (mode & 0x2) + write_field(edi_config_reg, 1, 1, 2); /* EDI_ENABLE_3D */ + + if (mode & 0x3) + write_field(edi_config_reg, 1, 1, 3); /* EDI_CHROMA_3D */ + + write_field(edi_config_reg, mode, VPE_EDI_INP_MODE_MASK, + VPE_EDI_INP_MODE_SHIFT); + + ctx->load_mmrs = true; +} + +/* + * Set the shadow registers whose values are modified when either the + * source or destination format is changed. + */ +static int set_srcdst_params(struct vpe_ctx *ctx) +{ + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + unsigned int src_w = s_q_data->c_rect.width; + unsigned int src_h = s_q_data->c_rect.height; + unsigned int dst_w = d_q_data->c_rect.width; + unsigned int dst_h = d_q_data->c_rect.height; + struct v4l2_pix_format_mplane *spix; + size_t mv_buf_size; + int ret; + + ctx->sequence = 0; + ctx->field = V4L2_FIELD_TOP; + spix = &s_q_data->format.fmt.pix_mp; + + if ((s_q_data->flags & Q_IS_INTERLACED) && + !(d_q_data->flags & Q_IS_INTERLACED)) { + int bytes_per_line; + const struct vpdma_data_format *mv = + &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + + /* + * we make sure that the source image has a 16 byte aligned + * stride, we need to do the same for the motion vector buffer + * by aligning it's stride to the next 16 byte boundary. this + * extra space will not be used by the de-interlacer, but will + * ensure that vpdma operates correctly + */ + bytes_per_line = ALIGN((spix->width * mv->depth) >> 3, + VPDMA_STRIDE_ALIGN); + mv_buf_size = bytes_per_line * spix->height; + + ctx->deinterlacing = true; + src_h <<= 1; + } else { + ctx->deinterlacing = false; + mv_buf_size = 0; + } + + free_vbs(ctx); + ctx->src_vbs[2] = ctx->src_vbs[1] = ctx->src_vbs[0] = NULL; + + ret = realloc_mv_buffers(ctx, mv_buf_size); + if (ret) + return ret; + + set_cfg_modes(ctx); + set_dei_regs(ctx); + + csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], + &s_q_data->format, &d_q_data->format); + + sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); + sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); + + sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0], + &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], + src_w, src_h, dst_w, dst_h); + + return 0; +} + +/* + * mem2mem callbacks + */ + +/* + * job_ready() - check whether an instance is ready to be scheduled to run + */ +static int job_ready(void *priv) +{ + struct vpe_ctx *ctx = priv; + + /* + * This check is needed as this might be called directly from driver + * When called by m2m framework, this will always satisfy, but when + * called from vpe_irq, this might fail. (src stream with zero buffers) + */ + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) <= 0 || + v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) <= 0) + return 0; + + return 1; +} + +static void job_abort(void *priv) +{ + struct vpe_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +static void vpe_dump_regs(struct vpe_dev *dev) +{ +#define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r)) + + vpe_dbg(dev, "VPE Registers:\n"); + + DUMPREG(PID); + DUMPREG(SYSCONFIG); + DUMPREG(INT0_STATUS0_RAW); + DUMPREG(INT0_STATUS0); + DUMPREG(INT0_ENABLE0); + DUMPREG(INT0_STATUS1_RAW); + DUMPREG(INT0_STATUS1); + DUMPREG(INT0_ENABLE1); + DUMPREG(CLK_ENABLE); + DUMPREG(CLK_RESET); + DUMPREG(CLK_FORMAT_SELECT); + DUMPREG(CLK_RANGE_MAP); + DUMPREG(US1_R0); + DUMPREG(US1_R1); + DUMPREG(US1_R2); + DUMPREG(US1_R3); + DUMPREG(US1_R4); + DUMPREG(US1_R5); + DUMPREG(US1_R6); + DUMPREG(US1_R7); + DUMPREG(US2_R0); + DUMPREG(US2_R1); + DUMPREG(US2_R2); + DUMPREG(US2_R3); + DUMPREG(US2_R4); + DUMPREG(US2_R5); + DUMPREG(US2_R6); + DUMPREG(US2_R7); + DUMPREG(US3_R0); + DUMPREG(US3_R1); + DUMPREG(US3_R2); + DUMPREG(US3_R3); + DUMPREG(US3_R4); + DUMPREG(US3_R5); + DUMPREG(US3_R6); + DUMPREG(US3_R7); + DUMPREG(DEI_FRAME_SIZE); + DUMPREG(MDT_BYPASS); + DUMPREG(MDT_SF_THRESHOLD); + DUMPREG(EDI_CONFIG); + DUMPREG(DEI_EDI_LUT_R0); + DUMPREG(DEI_EDI_LUT_R1); + DUMPREG(DEI_EDI_LUT_R2); + DUMPREG(DEI_EDI_LUT_R3); + DUMPREG(DEI_FMD_WINDOW_R0); + DUMPREG(DEI_FMD_WINDOW_R1); + DUMPREG(DEI_FMD_CONTROL_R0); + DUMPREG(DEI_FMD_CONTROL_R1); + DUMPREG(DEI_FMD_STATUS_R0); + DUMPREG(DEI_FMD_STATUS_R1); + DUMPREG(DEI_FMD_STATUS_R2); +#undef DUMPREG + + sc_dump_regs(dev->sc); + csc_dump_regs(dev->csc); +} + +static void add_out_dtd(struct vpe_ctx *ctx, int port) +{ + struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; + const struct vpe_port_data *p_data = &port_data[port]; + struct vb2_buffer *vb = &ctx->dst_vb->vb2_buf; + struct vpe_fmt *fmt = q_data->fmt; + const struct vpdma_data_format *vpdma_fmt; + int mv_buf_selector = !ctx->src_mv_buf_selector; + struct v4l2_pix_format_mplane *pix; + dma_addr_t dma_addr; + u32 flags = 0; + u32 offset = 0; + u32 stride; + + if (port == VPE_PORT_MV_OUT) { + vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + dma_addr = ctx->mv_buf_dma[mv_buf_selector]; + q_data = &ctx->q_data[Q_DATA_SRC]; + pix = &q_data->format.fmt.pix_mp; + stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, + VPDMA_STRIDE_ALIGN); + } else { + /* to incorporate interleaved formats */ + int plane = fmt->coplanar ? p_data->vb_part : 0; + + pix = &q_data->format.fmt.pix_mp; + vpdma_fmt = fmt->vpdma_fmt[plane]; + /* + * If we are using a single plane buffer and + * we need to set a separate vpdma chroma channel. + */ + if (pix->num_planes == 1 && plane) { + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Compute required offset */ + offset = pix->plane_fmt[0].bytesperline * pix->height; + } else { + dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); + /* Use address as is, no offset */ + offset = 0; + } + if (!dma_addr) { + vpe_err(ctx->dev, + "acquiring output buffer(%d) dma_addr failed\n", + port); + return; + } + /* Apply the offset */ + dma_addr += offset; + stride = pix->plane_fmt[VPE_LUMA].bytesperline; + } + + if (q_data->flags & Q_DATA_FRAME_1D) + flags |= VPDMA_DATA_FRAME_1D; + if (q_data->flags & Q_DATA_MODE_TILED) + flags |= VPDMA_DATA_MODE_TILED; + + vpdma_set_max_size(ctx->dev->vpdma, VPDMA_MAX_SIZE1, + MAX_W, MAX_H); + + vpdma_add_out_dtd(&ctx->desc_list, pix->width, + stride, &q_data->c_rect, + vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1, + MAX_OUT_HEIGHT_REG1, p_data->channel, flags); +} + +static void add_in_dtd(struct vpe_ctx *ctx, int port) +{ + struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; + const struct vpe_port_data *p_data = &port_data[port]; + struct vb2_buffer *vb = &ctx->src_vbs[p_data->vb_index]->vb2_buf; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpe_fmt *fmt = q_data->fmt; + struct v4l2_pix_format_mplane *pix; + const struct vpdma_data_format *vpdma_fmt; + int mv_buf_selector = ctx->src_mv_buf_selector; + int field = vbuf->field == V4L2_FIELD_BOTTOM; + int frame_width, frame_height; + dma_addr_t dma_addr; + u32 flags = 0; + u32 offset = 0; + u32 stride; + + pix = &q_data->format.fmt.pix_mp; + if (port == VPE_PORT_MV_IN) { + vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + dma_addr = ctx->mv_buf_dma[mv_buf_selector]; + stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, + VPDMA_STRIDE_ALIGN); + } else { + /* to incorporate interleaved formats */ + int plane = fmt->coplanar ? p_data->vb_part : 0; + + vpdma_fmt = fmt->vpdma_fmt[plane]; + /* + * If we are using a single plane buffer and + * we need to set a separate vpdma chroma channel. + */ + if (pix->num_planes == 1 && plane) { + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Compute required offset */ + offset = pix->plane_fmt[0].bytesperline * pix->height; + } else { + dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); + /* Use address as is, no offset */ + offset = 0; + } + if (!dma_addr) { + vpe_err(ctx->dev, + "acquiring output buffer(%d) dma_addr failed\n", + port); + return; + } + /* Apply the offset */ + dma_addr += offset; + stride = pix->plane_fmt[VPE_LUMA].bytesperline; + + /* + * field used in VPDMA desc = 0 (top) / 1 (bottom) + * Use top or bottom field from same vb alternately + * For each de-interlacing operation, f,f-1,f-2 should be one + * of TBT or BTB + */ + if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB || + q_data->flags & Q_DATA_INTERLACED_SEQ_BT) { + /* Select initial value based on format */ + if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT) + field = 1; + else + field = 0; + + /* Toggle for each vb_index and each operation */ + field = (field + p_data->vb_index + ctx->sequence) % 2; + + if (field) { + int height = pix->height / 2; + int bpp; + + if (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21) + bpp = 1; + else + bpp = vpdma_fmt->depth >> 3; + + if (plane) + height /= 2; + + dma_addr += pix->width * height * bpp; + } + } + } + + if (q_data->flags & Q_DATA_FRAME_1D) + flags |= VPDMA_DATA_FRAME_1D; + if (q_data->flags & Q_DATA_MODE_TILED) + flags |= VPDMA_DATA_MODE_TILED; + + frame_width = q_data->c_rect.width; + frame_height = q_data->c_rect.height; + + if (p_data->vb_part && (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21)) + frame_height /= 2; + + vpdma_add_in_dtd(&ctx->desc_list, pix->width, stride, + &q_data->c_rect, vpdma_fmt, dma_addr, + p_data->channel, field, flags, frame_width, + frame_height, 0, 0); +} + +/* + * Enable the expected IRQ sources + */ +static void enable_irqs(struct vpe_ctx *ctx) +{ + write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE); + write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT | + VPE_DS1_UV_ERROR_INT); + + vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, true); +} + +static void disable_irqs(struct vpe_ctx *ctx) +{ + write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff); + write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff); + + vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, false); +} + +/* device_run() - prepares and starts the device + * + * This function is only called when both the source and destination + * buffers are in place. + */ +static void device_run(void *priv) +{ + struct vpe_ctx *ctx = priv; + struct sc_data *sc = ctx->dev->sc; + struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + const struct v4l2_format_info *d_finfo; + + d_finfo = v4l2_format_info(d_q_data->fmt->fourcc); + + if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX && + ctx->sequence % 2 == 0) { + /* When using SEQ_XX type buffers, each buffer has two fields + * each buffer has two fields (top & bottom) + * Removing one buffer is actually getting two fields + * Alternate between two operations:- + * Even : consume one field but DO NOT REMOVE from queue + * Odd : consume other field and REMOVE from queue + */ + ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + WARN_ON(ctx->src_vbs[0] == NULL); + } else { + ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + WARN_ON(ctx->src_vbs[0] == NULL); + } + + ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + WARN_ON(ctx->dst_vb == NULL); + + if (ctx->deinterlacing) { + + if (ctx->src_vbs[2] == NULL) { + ctx->src_vbs[2] = ctx->src_vbs[0]; + WARN_ON(ctx->src_vbs[2] == NULL); + ctx->src_vbs[1] = ctx->src_vbs[0]; + WARN_ON(ctx->src_vbs[1] == NULL); + } + + /* + * we have output the first 2 frames through line average, we + * now switch to EDI de-interlacer + */ + if (ctx->sequence == 2) + config_edi_input_mode(ctx, 0x3); /* EDI (Y + UV) */ + } + + /* config descriptors */ + if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb); + vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb); + + set_line_modes(ctx); + + ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr; + ctx->load_mmrs = false; + } + + if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr || + sc->load_coeff_h) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h); + vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, + &ctx->sc_coeff_h, 0); + + sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr; + sc->load_coeff_h = false; + } + + if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr || + sc->load_coeff_v) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v); + vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, + &ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); + + sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr; + sc->load_coeff_v = false; + } + + /* output data descriptors */ + if (ctx->deinterlacing) + add_out_dtd(ctx, VPE_PORT_MV_OUT); + + if (v4l2_is_format_rgb(d_finfo)) { + add_out_dtd(ctx, VPE_PORT_RGB_OUT); + } else { + add_out_dtd(ctx, VPE_PORT_LUMA_OUT); + if (d_q_data->fmt->coplanar) + add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); + } + + /* input data descriptors */ + if (ctx->deinterlacing) { + add_in_dtd(ctx, VPE_PORT_LUMA3_IN); + add_in_dtd(ctx, VPE_PORT_CHROMA3_IN); + + add_in_dtd(ctx, VPE_PORT_LUMA2_IN); + add_in_dtd(ctx, VPE_PORT_CHROMA2_IN); + } + + add_in_dtd(ctx, VPE_PORT_LUMA1_IN); + add_in_dtd(ctx, VPE_PORT_CHROMA1_IN); + + if (ctx->deinterlacing) + add_in_dtd(ctx, VPE_PORT_MV_IN); + + /* sync on channel control descriptors for input ports */ + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN); + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN); + + if (ctx->deinterlacing) { + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_LUMA2_IN); + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_CHROMA2_IN); + + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_LUMA3_IN); + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_CHROMA3_IN); + + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN); + } + + /* sync on channel control descriptors for output ports */ + if (v4l2_is_format_rgb(d_finfo)) { + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_RGB_OUT); + } else { + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_LUMA_OUT); + if (d_q_data->fmt->coplanar) + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_CHROMA_OUT); + } + + if (ctx->deinterlacing) + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT); + + enable_irqs(ctx); + + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf); + vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list, 0); +} + +static void dei_error(struct vpe_ctx *ctx) +{ + dev_warn(ctx->dev->v4l2_dev.dev, + "received DEI error interrupt\n"); +} + +static void ds1_uv_error(struct vpe_ctx *ctx) +{ + dev_warn(ctx->dev->v4l2_dev.dev, + "received downsampler error interrupt\n"); +} + +static irqreturn_t vpe_irq(int irq_vpe, void *data) +{ + struct vpe_dev *dev = (struct vpe_dev *)data; + struct vpe_ctx *ctx; + struct vpe_q_data *d_q_data; + struct vb2_v4l2_buffer *s_vb, *d_vb; + unsigned long flags; + u32 irqst0, irqst1; + bool list_complete = false; + + irqst0 = read_reg(dev, VPE_INT0_STATUS0); + if (irqst0) { + write_reg(dev, VPE_INT0_STATUS0_CLR, irqst0); + vpe_dbg(dev, "INT0_STATUS0 = 0x%08x\n", irqst0); + } + + irqst1 = read_reg(dev, VPE_INT0_STATUS1); + if (irqst1) { + write_reg(dev, VPE_INT0_STATUS1_CLR, irqst1); + vpe_dbg(dev, "INT0_STATUS1 = 0x%08x\n", irqst1); + } + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + vpe_err(dev, "instance released before end of transaction\n"); + goto handled; + } + + if (irqst1) { + if (irqst1 & VPE_DEI_ERROR_INT) { + irqst1 &= ~VPE_DEI_ERROR_INT; + dei_error(ctx); + } + if (irqst1 & VPE_DS1_UV_ERROR_INT) { + irqst1 &= ~VPE_DS1_UV_ERROR_INT; + ds1_uv_error(ctx); + } + } + + if (irqst0) { + if (irqst0 & VPE_INT0_LIST0_COMPLETE) + vpdma_clear_list_stat(ctx->dev->vpdma, 0, 0); + + irqst0 &= ~(VPE_INT0_LIST0_COMPLETE); + list_complete = true; + } + + if (irqst0 | irqst1) { + dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n", + irqst0, irqst1); + } + + /* + * Setup next operation only when list complete IRQ occurs + * otherwise, skip the following code + */ + if (!list_complete) + goto handled; + + disable_irqs(ctx); + + vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); + + vpdma_reset_desc_list(&ctx->desc_list); + + /* the previous dst mv buffer becomes the next src mv buffer */ + ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector; + + s_vb = ctx->src_vbs[0]; + d_vb = ctx->dst_vb; + + d_vb->flags = s_vb->flags; + d_vb->vb2_buf.timestamp = s_vb->vb2_buf.timestamp; + + if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE) + d_vb->timecode = s_vb->timecode; + + d_vb->sequence = ctx->sequence; + s_vb->sequence = ctx->sequence; + + d_q_data = &ctx->q_data[Q_DATA_DST]; + if (d_q_data->flags & Q_IS_INTERLACED) { + d_vb->field = ctx->field; + if (ctx->field == V4L2_FIELD_BOTTOM) { + ctx->sequence++; + ctx->field = V4L2_FIELD_TOP; + } else { + WARN_ON(ctx->field != V4L2_FIELD_TOP); + ctx->field = V4L2_FIELD_BOTTOM; + } + } else { + d_vb->field = V4L2_FIELD_NONE; + ctx->sequence++; + } + + if (ctx->deinterlacing) { + /* + * Allow source buffer to be dequeued only if it won't be used + * in the next iteration. All vbs are initialized to first + * buffer and we are shifting buffers every iteration, for the + * first two iterations, no buffer will be dequeued. + * This ensures that driver will keep (n-2)th (n-1)th and (n)th + * field when deinterlacing is enabled + */ + if (ctx->src_vbs[2] != ctx->src_vbs[1]) + s_vb = ctx->src_vbs[2]; + else + s_vb = NULL; + } + + spin_lock_irqsave(&dev->lock, flags); + + if (s_vb) + v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE); + + v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE); + + spin_unlock_irqrestore(&dev->lock, flags); + + if (ctx->deinterlacing) { + ctx->src_vbs[2] = ctx->src_vbs[1]; + ctx->src_vbs[1] = ctx->src_vbs[0]; + } + + /* + * Since the vb2_buf_done has already been called fir therse + * buffer we can now NULL them out so that we won't try + * to clean out stray pointer later on. + */ + ctx->src_vbs[0] = NULL; + ctx->dst_vb = NULL; + + if (ctx->aborting) + goto finished; + + ctx->bufs_completed++; + if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) { + device_run(ctx); + goto handled; + } + +finished: + vpe_dbg(ctx->dev, "finishing transaction\n"); + ctx->bufs_completed = 0; + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); +handled: + return IRQ_HANDLED; +} + +/* + * video ioctls + */ +static int vpe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + VPE_MODULE_NAME); + return 0; +} + +static int __enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ + int i, index; + struct vpe_fmt *fmt = NULL; + + index = 0; + for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) { + if (vpe_formats[i].types & type) { + if (index == f->index) { + fmt = &vpe_formats[i]; + break; + } + index++; + } + } + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static int vpe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (V4L2_TYPE_IS_OUTPUT(f->type)) + return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT); + + return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE); +} + +static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct vpe_ctx *ctx = file->private_data; + struct vb2_queue *vq; + struct vpe_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + *f = q_data->format; + + if (V4L2_TYPE_IS_CAPTURE(f->type)) { + struct vpe_q_data *s_q_data; + struct v4l2_pix_format_mplane *spix; + + /* get colorimetry from the source queue */ + s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + spix = &s_q_data->format.fmt.pix_mp; + + pix->colorspace = spix->colorspace; + pix->xfer_func = spix->xfer_func; + pix->ycbcr_enc = spix->ycbcr_enc; + pix->quantization = spix->quantization; + } + + return 0; +} + +static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, + struct vpe_fmt *fmt, int type) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt; + unsigned int w_align; + int i, depth, depth_bytes, height; + unsigned int stride = 0; + const struct v4l2_format_info *finfo; + + if (!fmt || !(fmt->types & type)) { + vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n", + pix->pixelformat); + fmt = __find_format(V4L2_PIX_FMT_YUYV); + } + + if (pix->field != V4L2_FIELD_NONE && + pix->field != V4L2_FIELD_ALTERNATE && + pix->field != V4L2_FIELD_SEQ_TB && + pix->field != V4L2_FIELD_SEQ_BT) + pix->field = V4L2_FIELD_NONE; + + depth = fmt->vpdma_fmt[VPE_LUMA]->depth; + + /* + * the line stride should 16 byte aligned for VPDMA to work, based on + * the bytes per pixel, figure out how much the width should be aligned + * to make sure line stride is 16 byte aligned + */ + depth_bytes = depth >> 3; + + if (depth_bytes == 3) { + /* + * if bpp is 3(as in some RGB formats), the pixel width doesn't + * really help in ensuring line stride is 16 byte aligned + */ + w_align = 4; + } else { + /* + * for the remainder bpp(4, 2 and 1), the pixel width alignment + * can ensure a line stride alignment of 16 bytes. For example, + * if bpp is 2, then the line stride can be 16 byte aligned if + * the width is 8 byte aligned + */ + + /* + * HACK: using order_base_2() here causes lots of asm output + * errors with smatch, on i386: + * ./arch/x86/include/asm/bitops.h:457:22: + * warning: asm output is not an lvalue + * Perhaps some gcc optimization is doing the wrong thing + * there. + * Let's get rid of them by doing the calculus on two steps + */ + w_align = roundup_pow_of_two(VPDMA_DESC_ALIGN / depth_bytes); + w_align = ilog2(w_align); + } + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, + &pix->height, MIN_H, MAX_H, H_ALIGN, + S_ALIGN); + + if (!pix->num_planes || pix->num_planes > 2) + pix->num_planes = fmt->coplanar ? 2 : 1; + else if (pix->num_planes > 1 && !fmt->coplanar) + pix->num_planes = 1; + + pix->pixelformat = fmt->fourcc; + finfo = v4l2_format_info(fmt->fourcc); + + /* + * For the actual image parameters, we need to consider the field + * height of the image for SEQ_XX buffers. + */ + if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT) + height = pix->height / 2; + else + height = pix->height; + + if (!pix->colorspace) { + if (v4l2_is_format_rgb(finfo)) { + pix->colorspace = V4L2_COLORSPACE_SRGB; + } else { + if (height > 1280) /* HD */ + pix->colorspace = V4L2_COLORSPACE_REC709; + else /* SD */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + } + } + + memset(pix->reserved, 0, sizeof(pix->reserved)); + for (i = 0; i < pix->num_planes; i++) { + plane_fmt = &pix->plane_fmt[i]; + depth = fmt->vpdma_fmt[i]->depth; + + stride = (pix->width * fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; + if (stride > plane_fmt->bytesperline) + plane_fmt->bytesperline = stride; + + plane_fmt->bytesperline = clamp_t(u32, plane_fmt->bytesperline, + stride, + VPDMA_MAX_STRIDE); + + plane_fmt->bytesperline = ALIGN(plane_fmt->bytesperline, + VPDMA_STRIDE_ALIGN); + + if (i == VPE_LUMA) { + plane_fmt->sizeimage = pix->height * + plane_fmt->bytesperline; + + if (pix->num_planes == 1 && fmt->coplanar) + plane_fmt->sizeimage += pix->height * + plane_fmt->bytesperline * + fmt->vpdma_fmt[VPE_CHROMA]->depth >> 3; + + } else { /* i == VIP_CHROMA */ + plane_fmt->sizeimage = (pix->height * + plane_fmt->bytesperline * + depth) >> 3; + } + memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); + } + + return 0; +} + +static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vpe_ctx *ctx = file->private_data; + struct vpe_fmt *fmt = find_format(f); + + if (V4L2_TYPE_IS_OUTPUT(f->type)) + return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT); + else + return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE); +} + +static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct v4l2_pix_format_mplane *qpix; + struct vpe_q_data *q_data; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + vpe_err(ctx->dev, "queue busy\n"); + return -EBUSY; + } + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + qpix = &q_data->format.fmt.pix_mp; + q_data->fmt = find_format(f); + q_data->format = *f; + + q_data->c_rect.left = 0; + q_data->c_rect.top = 0; + q_data->c_rect.width = pix->width; + q_data->c_rect.height = pix->height; + + if (qpix->field == V4L2_FIELD_ALTERNATE) + q_data->flags |= Q_DATA_INTERLACED_ALTERNATE; + else if (qpix->field == V4L2_FIELD_SEQ_TB) + q_data->flags |= Q_DATA_INTERLACED_SEQ_TB; + else if (qpix->field == V4L2_FIELD_SEQ_BT) + q_data->flags |= Q_DATA_INTERLACED_SEQ_BT; + else + q_data->flags &= ~Q_IS_INTERLACED; + + /* the crop height is halved for the case of SEQ_XX buffers */ + if (q_data->flags & Q_IS_SEQ_XX) + q_data->c_rect.height /= 2; + + vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d", + f->type, pix->width, pix->height, pix->pixelformat, + pix->plane_fmt[0].bytesperline); + if (pix->num_planes == 2) + vpe_dbg(ctx->dev, " bpl_uv %d\n", + pix->plane_fmt[1].bytesperline); + + return 0; +} + +static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + int ret; + struct vpe_ctx *ctx = file->private_data; + + ret = vpe_try_fmt(file, priv, f); + if (ret) + return ret; + + ret = __vpe_s_fmt(ctx, f); + if (ret) + return ret; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) + set_src_registers(ctx); + else + set_dst_registers(ctx); + + return set_srcdst_params(ctx); +} + +static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) +{ + struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; + int height; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + /* + * COMPOSE target is only valid for capture buffer type, return + * error for output buffer type + */ + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP: + /* + * CROP target is only valid for output buffer type, return + * error for capture buffer type + */ + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + /* + * bound and default crop/compose targets are invalid targets to + * try/set + */ + default: + return -EINVAL; + } + + /* + * For SEQ_XX buffers, crop height should be less than the height of + * the field height, not the buffer height + */ + if (q_data->flags & Q_IS_SEQ_XX) + height = pix->height / 2; + else + height = pix->height; + + if (s->r.top < 0 || s->r.left < 0) { + vpe_err(ctx->dev, "negative values for top and left\n"); + s->r.top = s->r.left = 0; + } + + v4l_bound_align_image(&s->r.width, MIN_W, pix->width, 1, + &s->r.height, MIN_H, height, H_ALIGN, S_ALIGN); + + /* adjust left/top if cropping rectangle is out of bounds */ + if (s->r.left + s->r.width > pix->width) + s->r.left = pix->width - s->r.width; + if (s->r.top + s->r.height > pix->height) + s->r.top = pix->height - s->r.height; + + return 0; +} + +static int vpe_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vpe_ctx *ctx = file->private_data; + struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; + bool use_c_rect = false; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + use_c_rect = true; + break; + case V4L2_SEL_TGT_CROP: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + use_c_rect = true; + break; + default: + return -EINVAL; + } + + if (use_c_rect) { + /* + * for CROP/COMPOSE target type, return c_rect params from the + * respective buffer type + */ + s->r = q_data->c_rect; + } else { + /* + * for DEFAULT/BOUNDS target type, return width and height from + * S_FMT of the respective buffer type + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = pix->width; + s->r.height = pix->height; + } + + return 0; +} + + +static int vpe_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vpe_ctx *ctx = file->private_data; + struct vpe_q_data *q_data; + struct v4l2_selection sel = *s; + int ret; + + ret = __vpe_try_selection(ctx, &sel); + if (ret) + return ret; + + q_data = get_q_data(ctx, sel.type); + if (!q_data) + return -EINVAL; + + if ((q_data->c_rect.left == sel.r.left) && + (q_data->c_rect.top == sel.r.top) && + (q_data->c_rect.width == sel.r.width) && + (q_data->c_rect.height == sel.r.height)) { + vpe_dbg(ctx->dev, + "requested crop/compose values are already set\n"); + return 0; + } + + q_data->c_rect = sel.r; + + return set_srcdst_params(ctx); +} + +/* + * defines number of buffers/frames a context can process with VPE before + * switching to a different context. default value is 1 buffer per context + */ +#define V4L2_CID_VPE_BUFS_PER_JOB (V4L2_CID_USER_TI_VPE_BASE + 0) + +static int vpe_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpe_ctx *ctx = + container_of(ctrl->handler, struct vpe_ctx, hdl); + + switch (ctrl->id) { + case V4L2_CID_VPE_BUFS_PER_JOB: + ctx->bufs_per_job = ctrl->val; + break; + + default: + vpe_err(ctx->dev, "Invalid control\n"); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops vpe_ctrl_ops = { + .s_ctrl = vpe_s_ctrl, +}; + +static const struct v4l2_ioctl_ops vpe_ioctl_ops = { + .vidioc_querycap = vpe_querycap, + + .vidioc_enum_fmt_vid_cap = vpe_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt, + + .vidioc_enum_fmt_vid_out = vpe_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt, + .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, + .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, + + .vidioc_g_selection = vpe_g_selection, + .vidioc_s_selection = vpe_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Queue operations + */ +static int vpe_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + int i; + struct vpe_ctx *ctx = vb2_get_drv_priv(vq); + struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; + + q_data = get_q_data(ctx, vq->type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + *nplanes = pix->num_planes; + + for (i = 0; i < *nplanes; i++) + sizes[i] = pix->plane_fmt[i].sizeimage; + + vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, + sizes[VPE_LUMA]); + if (*nplanes == 2) + vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]); + + return 0; +} + +static int vpe_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; + int i; + + vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (!q_data) + return -EINVAL; + + pix = &q_data->format.fmt.pix_mp; + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!(q_data->flags & Q_IS_INTERLACED)) { + vbuf->field = V4L2_FIELD_NONE; + } else { + if (vbuf->field != V4L2_FIELD_TOP && + vbuf->field != V4L2_FIELD_BOTTOM && + vbuf->field != V4L2_FIELD_SEQ_TB && + vbuf->field != V4L2_FIELD_SEQ_BT) + return -EINVAL; + } + } + + for (i = 0; i < pix->num_planes; i++) { + if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { + vpe_err(ctx->dev, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, i), + (long)pix->plane_fmt[i].sizeimage); + return -EINVAL; + } + } + + for (i = 0; i < pix->num_planes; i++) + vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); + + return 0; +} + +static void vpe_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int check_srcdst_sizes(struct vpe_ctx *ctx) +{ + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + unsigned int src_w = s_q_data->c_rect.width; + unsigned int src_h = s_q_data->c_rect.height; + unsigned int dst_w = d_q_data->c_rect.width; + unsigned int dst_h = d_q_data->c_rect.height; + + if (src_w == dst_w && src_h == dst_h) + return 0; + + if (src_h <= SC_MAX_PIXEL_HEIGHT && + src_w <= SC_MAX_PIXEL_WIDTH && + dst_h <= SC_MAX_PIXEL_HEIGHT && + dst_w <= SC_MAX_PIXEL_WIDTH) + return 0; + + return -1; +} + +static void vpe_return_all_buffers(struct vpe_ctx *ctx, struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vb; + unsigned long flags; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vb) + break; + spin_lock_irqsave(&ctx->dev->lock, flags); + v4l2_m2m_buf_done(vb, state); + spin_unlock_irqrestore(&ctx->dev->lock, flags); + } + + /* + * Cleanup the in-transit vb2 buffers that have been + * removed from their respective queue already but for + * which procecessing has not been completed yet. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + spin_lock_irqsave(&ctx->dev->lock, flags); + + if (ctx->src_vbs[2]) + v4l2_m2m_buf_done(ctx->src_vbs[2], state); + + if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) + v4l2_m2m_buf_done(ctx->src_vbs[1], state); + + if (ctx->src_vbs[0] && + (ctx->src_vbs[0] != ctx->src_vbs[1]) && + (ctx->src_vbs[0] != ctx->src_vbs[2])) + v4l2_m2m_buf_done(ctx->src_vbs[0], state); + + ctx->src_vbs[2] = NULL; + ctx->src_vbs[1] = NULL; + ctx->src_vbs[0] = NULL; + + spin_unlock_irqrestore(&ctx->dev->lock, flags); + } else { + if (ctx->dst_vb) { + spin_lock_irqsave(&ctx->dev->lock, flags); + + v4l2_m2m_buf_done(ctx->dst_vb, state); + ctx->dst_vb = NULL; + spin_unlock_irqrestore(&ctx->dev->lock, flags); + } + } +} + +static int vpe_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpe_ctx *ctx = vb2_get_drv_priv(q); + + /* Check any of the size exceed maximum scaling sizes */ + if (check_srcdst_sizes(ctx)) { + vpe_err(ctx->dev, + "Conversion setup failed, check source and destination parameters\n" + ); + vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_QUEUED); + return -EINVAL; + } + + if (ctx->deinterlacing) + config_edi_input_mode(ctx, 0x0); + + if (ctx->sequence != 0) + set_srcdst_params(ctx); + + return 0; +} + +static void vpe_stop_streaming(struct vb2_queue *q) +{ + struct vpe_ctx *ctx = vb2_get_drv_priv(q); + + vpe_dump_regs(ctx->dev); + vpdma_dump_regs(ctx->dev->vpdma); + + vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops vpe_qops = { + .queue_setup = vpe_queue_setup, + .buf_prepare = vpe_buf_prepare, + .buf_queue = vpe_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vpe_start_streaming, + .stop_streaming = vpe_stop_streaming, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct vpe_ctx *ctx = priv; + struct vpe_dev *dev = ctx->dev; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &vpe_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &dev->dev_mutex; + src_vq->dev = dev->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &vpe_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &dev->dev_mutex; + dst_vq->dev = dev->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static const struct v4l2_ctrl_config vpe_bufs_per_job = { + .ops = &vpe_ctrl_ops, + .id = V4L2_CID_VPE_BUFS_PER_JOB, + .name = "Buffers Per Transaction", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = VPE_DEF_BUFS_PER_JOB, + .min = 1, + .max = VIDEO_MAX_FRAME, + .step = 1, +}; + +/* + * File operations + */ +static int vpe_open(struct file *file) +{ + struct vpe_dev *dev = video_drvdata(file); + struct vpe_q_data *s_q_data; + struct v4l2_ctrl_handler *hdl; + struct vpe_ctx *ctx; + struct v4l2_pix_format_mplane *pix; + int ret; + + vpe_dbg(dev, "vpe_open\n"); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = dev; + + if (mutex_lock_interruptible(&dev->dev_mutex)) { + ret = -ERESTARTSYS; + goto free_ctx; + } + + ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE, + VPDMA_LIST_TYPE_NORMAL); + if (ret != 0) + goto unlock; + + ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb)); + if (ret != 0) + goto free_desc_list; + + ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_mmr_adb; + + ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_sc_h; + + init_adb_hdrs(ctx); + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = ctx; + + hdl = &ctx->hdl; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL); + if (hdl->error) { + ret = hdl->error; + goto exit_fh; + } + ctx->fh.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + + s_q_data = &ctx->q_data[Q_DATA_SRC]; + pix = &s_q_data->format.fmt.pix_mp; + s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV); + pix->pixelformat = s_q_data->fmt->fourcc; + s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + pix->width = 1920; + pix->height = 1080; + pix->num_planes = 1; + pix->plane_fmt[VPE_LUMA].bytesperline = (pix->width * + s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; + pix->plane_fmt[VPE_LUMA].sizeimage = + pix->plane_fmt[VPE_LUMA].bytesperline * + pix->height; + pix->colorspace = V4L2_COLORSPACE_REC709; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + pix->field = V4L2_FIELD_NONE; + s_q_data->c_rect.left = 0; + s_q_data->c_rect.top = 0; + s_q_data->c_rect.width = pix->width; + s_q_data->c_rect.height = pix->height; + s_q_data->flags = 0; + + ctx->q_data[Q_DATA_DST] = *s_q_data; + ctx->q_data[Q_DATA_DST].format.type = + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + set_dei_shadow_registers(ctx); + set_src_registers(ctx); + set_dst_registers(ctx); + ret = set_srcdst_params(ctx); + if (ret) + goto exit_fh; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto exit_fh; + } + + v4l2_fh_add(&ctx->fh); + + /* + * for now, just report the creation of the first instance, we can later + * optimize the driver to enable or disable clocks when the first + * instance is created or the last instance released + */ + if (atomic_inc_return(&dev->num_instances) == 1) + vpe_dbg(dev, "first instance created\n"); + + ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB; + + ctx->load_mmrs = true; + + vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n", + ctx, ctx->fh.m2m_ctx); + + mutex_unlock(&dev->dev_mutex); + + return 0; +exit_fh: + v4l2_ctrl_handler_free(hdl); + v4l2_fh_exit(&ctx->fh); + vpdma_free_desc_buf(&ctx->sc_coeff_v); +free_sc_h: + vpdma_free_desc_buf(&ctx->sc_coeff_h); +free_mmr_adb: + vpdma_free_desc_buf(&ctx->mmr_adb); +free_desc_list: + vpdma_free_desc_list(&ctx->desc_list); +unlock: + mutex_unlock(&dev->dev_mutex); +free_ctx: + kfree(ctx); + return ret; +} + +static int vpe_release(struct file *file) +{ + struct vpe_dev *dev = video_drvdata(file); + struct vpe_ctx *ctx = file->private_data; + + vpe_dbg(dev, "releasing instance %p\n", ctx); + + mutex_lock(&dev->dev_mutex); + free_mv_buffers(ctx); + + vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); + + vpdma_free_desc_list(&ctx->desc_list); + vpdma_free_desc_buf(&ctx->mmr_adb); + + vpdma_free_desc_buf(&ctx->sc_coeff_v); + vpdma_free_desc_buf(&ctx->sc_coeff_h); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->hdl); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + kfree(ctx); + + /* + * for now, just report the release of the last instance, we can later + * optimize the driver to enable or disable clocks when the first + * instance is created or the last instance released + */ + if (atomic_dec_return(&dev->num_instances) == 0) + vpe_dbg(dev, "last instance released\n"); + + mutex_unlock(&dev->dev_mutex); + + return 0; +} + +static const struct v4l2_file_operations vpe_fops = { + .owner = THIS_MODULE, + .open = vpe_open, + .release = vpe_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device vpe_videodev = { + .name = VPE_MODULE_NAME, + .fops = &vpe_fops, + .ioctl_ops = &vpe_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_ready = job_ready, + .job_abort = job_abort, +}; + +static int vpe_runtime_get(struct platform_device *pdev) +{ + int r; + + dev_dbg(&pdev->dev, "vpe_runtime_get\n"); + + r = pm_runtime_resume_and_get(&pdev->dev); + WARN_ON(r < 0); + return r; +} + +static void vpe_runtime_put(struct platform_device *pdev) +{ + + int r; + + dev_dbg(&pdev->dev, "vpe_runtime_put\n"); + + r = pm_runtime_put_sync(&pdev->dev); + WARN_ON(r < 0 && r != -ENOSYS); +} + +static void vpe_fw_cb(struct platform_device *pdev) +{ + struct vpe_dev *dev = platform_get_drvdata(pdev); + struct video_device *vfd; + int ret; + + vfd = &dev->vfd; + *vfd = vpe_videodev; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + vpe_err(dev, "Failed to register video device\n"); + + vpe_set_clock_enable(dev, 0); + vpe_runtime_put(pdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(dev->m2m_dev); + v4l2_device_unregister(&dev->v4l2_dev); + + return; + } + + video_set_drvdata(vfd, dev); + dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", + vfd->num); +} + +static int vpe_probe(struct platform_device *pdev) +{ + struct vpe_dev *dev; + int ret, irq, func; + + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, + "32-bit consistent DMA enable failed\n"); + return ret; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + atomic_set(&dev->num_instances, 0); + mutex_init(&dev->dev_mutex); + + dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "vpe_top"); + if (!dev->res) { + dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); + return -ENODEV; + } + + /* + * HACK: we get resource info from device tree in the form of a list of + * VPE sub blocks, the driver currently uses only the base of vpe_top + * for register access, the driver should be changed later to access + * registers based on the sub block base addresses + */ + dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); + if (!dev->base) { + ret = -ENOMEM; + goto v4l2_dev_unreg; + } + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME, + dev); + if (ret) + goto v4l2_dev_unreg; + + platform_set_drvdata(pdev, dev); + + dev->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + vpe_err(dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(dev->m2m_dev); + goto v4l2_dev_unreg; + } + + pm_runtime_enable(&pdev->dev); + + ret = vpe_runtime_get(pdev); + if (ret < 0) + goto rel_m2m; + + /* Perform clk enable followed by reset */ + vpe_set_clock_enable(dev, 1); + + vpe_top_reset(dev); + + func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK, + VPE_PID_FUNC_SHIFT); + vpe_dbg(dev, "VPE PID function %x\n", func); + + vpe_top_vpdma_reset(dev); + + dev->sc = sc_create(pdev, "sc"); + if (IS_ERR(dev->sc)) { + ret = PTR_ERR(dev->sc); + goto runtime_put; + } + + dev->csc = csc_create(pdev, "csc"); + if (IS_ERR(dev->csc)) { + ret = PTR_ERR(dev->csc); + goto runtime_put; + } + + dev->vpdma = &dev->vpdma_data; + ret = vpdma_create(pdev, dev->vpdma, vpe_fw_cb); + if (ret) + goto runtime_put; + + return 0; + +runtime_put: + vpe_runtime_put(pdev); +rel_m2m: + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(dev->m2m_dev); +v4l2_dev_unreg: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int vpe_remove(struct platform_device *pdev) +{ + struct vpe_dev *dev = platform_get_drvdata(pdev); + + v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME); + + v4l2_m2m_release(dev->m2m_dev); + video_unregister_device(&dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + vpe_set_clock_enable(dev, 0); + vpe_runtime_put(pdev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id vpe_of_match[] = { + { + .compatible = "ti,dra7-vpe", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, vpe_of_match); +#endif + +static struct platform_driver vpe_pdrv = { + .probe = vpe_probe, + .remove = vpe_remove, + .driver = { + .name = VPE_MODULE_NAME, + .of_match_table = of_match_ptr(vpe_of_match), + }, +}; + +module_platform_driver(vpe_pdrv); + +MODULE_DESCRIPTION("TI VPE driver"); +MODULE_AUTHOR("Dale Farnsworth, "); +MODULE_LICENSE("GPL"); diff -Naur --no-dereference a/drivers/media/platform/ti/vpe/vpe_regs.h b/drivers/media/platform/ti/vpe/vpe_regs.h --- a/drivers/media/platform/ti/vpe/vpe_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/ti/vpe/vpe_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + */ + +#ifndef __TI_VPE_REGS_H +#define __TI_VPE_REGS_H + +/* VPE register offsets and field selectors */ + +/* VPE top level regs */ +#define VPE_PID 0x0000 +#define VPE_PID_MINOR_MASK 0x3f +#define VPE_PID_MINOR_SHIFT 0 +#define VPE_PID_CUSTOM_MASK 0x03 +#define VPE_PID_CUSTOM_SHIFT 6 +#define VPE_PID_MAJOR_MASK 0x07 +#define VPE_PID_MAJOR_SHIFT 8 +#define VPE_PID_RTL_MASK 0x1f +#define VPE_PID_RTL_SHIFT 11 +#define VPE_PID_FUNC_MASK 0xfff +#define VPE_PID_FUNC_SHIFT 16 +#define VPE_PID_SCHEME_MASK 0x03 +#define VPE_PID_SCHEME_SHIFT 30 + +#define VPE_SYSCONFIG 0x0010 +#define VPE_SYSCONFIG_IDLE_MASK 0x03 +#define VPE_SYSCONFIG_IDLE_SHIFT 2 +#define VPE_SYSCONFIG_STANDBY_MASK 0x03 +#define VPE_SYSCONFIG_STANDBY_SHIFT 4 +#define VPE_FORCE_IDLE_MODE 0 +#define VPE_NO_IDLE_MODE 1 +#define VPE_SMART_IDLE_MODE 2 +#define VPE_SMART_IDLE_WAKEUP_MODE 3 +#define VPE_FORCE_STANDBY_MODE 0 +#define VPE_NO_STANDBY_MODE 1 +#define VPE_SMART_STANDBY_MODE 2 +#define VPE_SMART_STANDBY_WAKEUP_MODE 3 + +#define VPE_INT0_STATUS0_RAW_SET 0x0020 +#define VPE_INT0_STATUS0_RAW VPE_INT0_STATUS0_RAW_SET +#define VPE_INT0_STATUS0_CLR 0x0028 +#define VPE_INT0_STATUS0 VPE_INT0_STATUS0_CLR +#define VPE_INT0_ENABLE0_SET 0x0030 +#define VPE_INT0_ENABLE0 VPE_INT0_ENABLE0_SET +#define VPE_INT0_ENABLE0_CLR 0x0038 +#define VPE_INT0_LIST0_COMPLETE BIT(0) +#define VPE_INT0_LIST0_NOTIFY BIT(1) +#define VPE_INT0_LIST1_COMPLETE BIT(2) +#define VPE_INT0_LIST1_NOTIFY BIT(3) +#define VPE_INT0_LIST2_COMPLETE BIT(4) +#define VPE_INT0_LIST2_NOTIFY BIT(5) +#define VPE_INT0_LIST3_COMPLETE BIT(6) +#define VPE_INT0_LIST3_NOTIFY BIT(7) +#define VPE_INT0_LIST4_COMPLETE BIT(8) +#define VPE_INT0_LIST4_NOTIFY BIT(9) +#define VPE_INT0_LIST5_COMPLETE BIT(10) +#define VPE_INT0_LIST5_NOTIFY BIT(11) +#define VPE_INT0_LIST6_COMPLETE BIT(12) +#define VPE_INT0_LIST6_NOTIFY BIT(13) +#define VPE_INT0_LIST7_COMPLETE BIT(14) +#define VPE_INT0_LIST7_NOTIFY BIT(15) +#define VPE_INT0_DESCRIPTOR BIT(16) +#define VPE_DEI_FMD_INT BIT(18) + +#define VPE_INT0_STATUS1_RAW_SET 0x0024 +#define VPE_INT0_STATUS1_RAW VPE_INT0_STATUS1_RAW_SET +#define VPE_INT0_STATUS1_CLR 0x002c +#define VPE_INT0_STATUS1 VPE_INT0_STATUS1_CLR +#define VPE_INT0_ENABLE1_SET 0x0034 +#define VPE_INT0_ENABLE1 VPE_INT0_ENABLE1_SET +#define VPE_INT0_ENABLE1_CLR 0x003c +#define VPE_INT0_CHANNEL_GROUP0 BIT(0) +#define VPE_INT0_CHANNEL_GROUP1 BIT(1) +#define VPE_INT0_CHANNEL_GROUP2 BIT(2) +#define VPE_INT0_CHANNEL_GROUP3 BIT(3) +#define VPE_INT0_CHANNEL_GROUP4 BIT(4) +#define VPE_INT0_CHANNEL_GROUP5 BIT(5) +#define VPE_INT0_CLIENT BIT(7) +#define VPE_DEI_ERROR_INT BIT(16) +#define VPE_DS1_UV_ERROR_INT BIT(22) + +#define VPE_INTC_EOI 0x00a0 + +#define VPE_CLK_ENABLE 0x0100 +#define VPE_VPEDMA_CLK_ENABLE BIT(0) +#define VPE_DATA_PATH_CLK_ENABLE BIT(1) + +#define VPE_CLK_RESET 0x0104 +#define VPE_VPDMA_CLK_RESET_MASK 0x1 +#define VPE_VPDMA_CLK_RESET_SHIFT 0 +#define VPE_DATA_PATH_CLK_RESET_MASK 0x1 +#define VPE_DATA_PATH_CLK_RESET_SHIFT 1 +#define VPE_MAIN_RESET_MASK 0x1 +#define VPE_MAIN_RESET_SHIFT 31 + +#define VPE_CLK_FORMAT_SELECT 0x010c +#define VPE_CSC_SRC_SELECT_MASK 0x03 +#define VPE_CSC_SRC_SELECT_SHIFT 0 +#define VPE_RGB_OUT_SELECT BIT(8) +#define VPE_DS_SRC_SELECT_MASK 0x07 +#define VPE_DS_SRC_SELECT_SHIFT 9 +#define VPE_DS_BYPASS BIT(16) +#define VPE_COLOR_SEPARATE_422 BIT(18) + +#define VPE_DS_SRC_DEI_SCALER (5 << VPE_DS_SRC_SELECT_SHIFT) +#define VPE_CSC_SRC_DEI_SCALER (3 << VPE_CSC_SRC_SELECT_SHIFT) + +#define VPE_CLK_RANGE_MAP 0x011c +#define VPE_RANGE_RANGE_MAP_Y_MASK 0x07 +#define VPE_RANGE_RANGE_MAP_Y_SHIFT 0 +#define VPE_RANGE_RANGE_MAP_UV_MASK 0x07 +#define VPE_RANGE_RANGE_MAP_UV_SHIFT 3 +#define VPE_RANGE_MAP_ON BIT(6) +#define VPE_RANGE_REDUCTION_ON BIT(28) + +/* VPE chrominance upsampler regs */ +#define VPE_US1_R0 0x0304 +#define VPE_US2_R0 0x0404 +#define VPE_US3_R0 0x0504 +#define VPE_US_C1_MASK 0x3fff +#define VPE_US_C1_SHIFT 2 +#define VPE_US_C0_MASK 0x3fff +#define VPE_US_C0_SHIFT 18 +#define VPE_US_MODE_MASK 0x03 +#define VPE_US_MODE_SHIFT 16 +#define VPE_ANCHOR_FID0_C1_MASK 0x3fff +#define VPE_ANCHOR_FID0_C1_SHIFT 2 +#define VPE_ANCHOR_FID0_C0_MASK 0x3fff +#define VPE_ANCHOR_FID0_C0_SHIFT 18 + +#define VPE_US1_R1 0x0308 +#define VPE_US2_R1 0x0408 +#define VPE_US3_R1 0x0508 +#define VPE_ANCHOR_FID0_C3_MASK 0x3fff +#define VPE_ANCHOR_FID0_C3_SHIFT 2 +#define VPE_ANCHOR_FID0_C2_MASK 0x3fff +#define VPE_ANCHOR_FID0_C2_SHIFT 18 + +#define VPE_US1_R2 0x030c +#define VPE_US2_R2 0x040c +#define VPE_US3_R2 0x050c +#define VPE_INTERP_FID0_C1_MASK 0x3fff +#define VPE_INTERP_FID0_C1_SHIFT 2 +#define VPE_INTERP_FID0_C0_MASK 0x3fff +#define VPE_INTERP_FID0_C0_SHIFT 18 + +#define VPE_US1_R3 0x0310 +#define VPE_US2_R3 0x0410 +#define VPE_US3_R3 0x0510 +#define VPE_INTERP_FID0_C3_MASK 0x3fff +#define VPE_INTERP_FID0_C3_SHIFT 2 +#define VPE_INTERP_FID0_C2_MASK 0x3fff +#define VPE_INTERP_FID0_C2_SHIFT 18 + +#define VPE_US1_R4 0x0314 +#define VPE_US2_R4 0x0414 +#define VPE_US3_R4 0x0514 +#define VPE_ANCHOR_FID1_C1_MASK 0x3fff +#define VPE_ANCHOR_FID1_C1_SHIFT 2 +#define VPE_ANCHOR_FID1_C0_MASK 0x3fff +#define VPE_ANCHOR_FID1_C0_SHIFT 18 + +#define VPE_US1_R5 0x0318 +#define VPE_US2_R5 0x0418 +#define VPE_US3_R5 0x0518 +#define VPE_ANCHOR_FID1_C3_MASK 0x3fff +#define VPE_ANCHOR_FID1_C3_SHIFT 2 +#define VPE_ANCHOR_FID1_C2_MASK 0x3fff +#define VPE_ANCHOR_FID1_C2_SHIFT 18 + +#define VPE_US1_R6 0x031c +#define VPE_US2_R6 0x041c +#define VPE_US3_R6 0x051c +#define VPE_INTERP_FID1_C1_MASK 0x3fff +#define VPE_INTERP_FID1_C1_SHIFT 2 +#define VPE_INTERP_FID1_C0_MASK 0x3fff +#define VPE_INTERP_FID1_C0_SHIFT 18 + +#define VPE_US1_R7 0x0320 +#define VPE_US2_R7 0x0420 +#define VPE_US3_R7 0x0520 +#define VPE_INTERP_FID0_C3_MASK 0x3fff +#define VPE_INTERP_FID0_C3_SHIFT 2 +#define VPE_INTERP_FID0_C2_MASK 0x3fff +#define VPE_INTERP_FID0_C2_SHIFT 18 + +/* VPE de-interlacer regs */ +#define VPE_DEI_FRAME_SIZE 0x0600 +#define VPE_DEI_WIDTH_MASK 0x07ff +#define VPE_DEI_WIDTH_SHIFT 0 +#define VPE_DEI_HEIGHT_MASK 0x07ff +#define VPE_DEI_HEIGHT_SHIFT 16 +#define VPE_DEI_INTERLACE_BYPASS BIT(29) +#define VPE_DEI_FIELD_FLUSH BIT(30) +#define VPE_DEI_PROGRESSIVE BIT(31) + +#define VPE_MDT_BYPASS 0x0604 +#define VPE_MDT_TEMPMAX_BYPASS BIT(0) +#define VPE_MDT_SPATMAX_BYPASS BIT(1) + +#define VPE_MDT_SF_THRESHOLD 0x0608 +#define VPE_MDT_SF_SC_THR1_MASK 0xff +#define VPE_MDT_SF_SC_THR1_SHIFT 0 +#define VPE_MDT_SF_SC_THR2_MASK 0xff +#define VPE_MDT_SF_SC_THR2_SHIFT 0 +#define VPE_MDT_SF_SC_THR3_MASK 0xff +#define VPE_MDT_SF_SC_THR3_SHIFT 0 + +#define VPE_EDI_CONFIG 0x060c +#define VPE_EDI_INP_MODE_MASK 0x03 +#define VPE_EDI_INP_MODE_SHIFT 0 +#define VPE_EDI_ENABLE_3D BIT(2) +#define VPE_EDI_ENABLE_CHROMA_3D BIT(3) +#define VPE_EDI_CHROMA3D_COR_THR_MASK 0xff +#define VPE_EDI_CHROMA3D_COR_THR_SHIFT 8 +#define VPE_EDI_DIR_COR_LOWER_THR_MASK 0xff +#define VPE_EDI_DIR_COR_LOWER_THR_SHIFT 16 +#define VPE_EDI_COR_SCALE_FACTOR_MASK 0xff +#define VPE_EDI_COR_SCALE_FACTOR_SHIFT 23 + +#define VPE_DEI_EDI_LUT_R0 0x0610 +#define VPE_EDI_LUT0_MASK 0x1f +#define VPE_EDI_LUT0_SHIFT 0 +#define VPE_EDI_LUT1_MASK 0x1f +#define VPE_EDI_LUT1_SHIFT 8 +#define VPE_EDI_LUT2_MASK 0x1f +#define VPE_EDI_LUT2_SHIFT 16 +#define VPE_EDI_LUT3_MASK 0x1f +#define VPE_EDI_LUT3_SHIFT 24 + +#define VPE_DEI_EDI_LUT_R1 0x0614 +#define VPE_EDI_LUT0_MASK 0x1f +#define VPE_EDI_LUT0_SHIFT 0 +#define VPE_EDI_LUT1_MASK 0x1f +#define VPE_EDI_LUT1_SHIFT 8 +#define VPE_EDI_LUT2_MASK 0x1f +#define VPE_EDI_LUT2_SHIFT 16 +#define VPE_EDI_LUT3_MASK 0x1f +#define VPE_EDI_LUT3_SHIFT 24 + +#define VPE_DEI_EDI_LUT_R2 0x0618 +#define VPE_EDI_LUT4_MASK 0x1f +#define VPE_EDI_LUT4_SHIFT 0 +#define VPE_EDI_LUT5_MASK 0x1f +#define VPE_EDI_LUT5_SHIFT 8 +#define VPE_EDI_LUT6_MASK 0x1f +#define VPE_EDI_LUT6_SHIFT 16 +#define VPE_EDI_LUT7_MASK 0x1f +#define VPE_EDI_LUT7_SHIFT 24 + +#define VPE_DEI_EDI_LUT_R3 0x061c +#define VPE_EDI_LUT8_MASK 0x1f +#define VPE_EDI_LUT8_SHIFT 0 +#define VPE_EDI_LUT9_MASK 0x1f +#define VPE_EDI_LUT9_SHIFT 8 +#define VPE_EDI_LUT10_MASK 0x1f +#define VPE_EDI_LUT10_SHIFT 16 +#define VPE_EDI_LUT11_MASK 0x1f +#define VPE_EDI_LUT11_SHIFT 24 + +#define VPE_DEI_FMD_WINDOW_R0 0x0620 +#define VPE_FMD_WINDOW_MINX_MASK 0x07ff +#define VPE_FMD_WINDOW_MINX_SHIFT 0 +#define VPE_FMD_WINDOW_MAXX_MASK 0x07ff +#define VPE_FMD_WINDOW_MAXX_SHIFT 16 +#define VPE_FMD_WINDOW_ENABLE BIT(31) + +#define VPE_DEI_FMD_WINDOW_R1 0x0624 +#define VPE_FMD_WINDOW_MINY_MASK 0x07ff +#define VPE_FMD_WINDOW_MINY_SHIFT 0 +#define VPE_FMD_WINDOW_MAXY_MASK 0x07ff +#define VPE_FMD_WINDOW_MAXY_SHIFT 16 + +#define VPE_DEI_FMD_CONTROL_R0 0x0628 +#define VPE_FMD_ENABLE BIT(0) +#define VPE_FMD_LOCK BIT(1) +#define VPE_FMD_JAM_DIR BIT(2) +#define VPE_FMD_BED_ENABLE BIT(3) +#define VPE_FMD_CAF_FIELD_THR_MASK 0xff +#define VPE_FMD_CAF_FIELD_THR_SHIFT 16 +#define VPE_FMD_CAF_LINE_THR_MASK 0xff +#define VPE_FMD_CAF_LINE_THR_SHIFT 24 + +#define VPE_DEI_FMD_CONTROL_R1 0x062c +#define VPE_FMD_CAF_THR_MASK 0x000fffff +#define VPE_FMD_CAF_THR_SHIFT 0 + +#define VPE_DEI_FMD_STATUS_R0 0x0630 +#define VPE_FMD_CAF_MASK 0x000fffff +#define VPE_FMD_CAF_SHIFT 0 +#define VPE_FMD_RESET BIT(24) + +#define VPE_DEI_FMD_STATUS_R1 0x0634 +#define VPE_FMD_FIELD_DIFF_MASK 0x0fffffff +#define VPE_FMD_FIELD_DIFF_SHIFT 0 + +#define VPE_DEI_FMD_STATUS_R2 0x0638 +#define VPE_FMD_FRAME_DIFF_MASK 0x000fffff +#define VPE_FMD_FRAME_DIFF_SHIFT 0 + +#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c --- a/drivers/media/platform/ti-vpe/cal.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/cal.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,920 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * TI Camera Access Layer (CAL) - Driver - * - * Copyright (c) 2015-2020 Texas Instruments Inc. - * - * Authors: - * Benoit Parrot - * Laurent Pinchart - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "cal.h" -#include "cal_regs.h" - -MODULE_DESCRIPTION("TI CAL driver"); -MODULE_AUTHOR("Benoit Parrot, "); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.1.0"); - -int cal_video_nr = -1; -module_param_named(video_nr, cal_video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); - -unsigned int cal_debug; -module_param_named(debug, cal_debug, uint, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - -/* ------------------------------------------------------------------ - * Platform Data - * ------------------------------------------------------------------ - */ - -static const struct cal_camerarx_data dra72x_cal_camerarx[] = { - { - .fields = { - [F_CTRLCLKEN] = { 10, 10 }, - [F_CAMMODE] = { 11, 12 }, - [F_LANEENABLE] = { 13, 16 }, - [F_CSI_MODE] = { 17, 17 }, - }, - .num_lanes = 4, - }, - { - .fields = { - [F_CTRLCLKEN] = { 0, 0 }, - [F_CAMMODE] = { 1, 2 }, - [F_LANEENABLE] = { 3, 4 }, - [F_CSI_MODE] = { 5, 5 }, - }, - .num_lanes = 2, - }, -}; - -static const struct cal_data dra72x_cal_data = { - .camerarx = dra72x_cal_camerarx, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), -}; - -static const struct cal_data dra72x_es1_cal_data = { - .camerarx = dra72x_cal_camerarx, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), - .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, -}; - -static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { - { - .fields = { - [F_CTRLCLKEN] = { 8, 8 }, - [F_CAMMODE] = { 9, 10 }, - [F_CSI_MODE] = { 11, 11 }, - [F_LANEENABLE] = { 27, 31 }, - }, - .num_lanes = 5, - }, - { - .fields = { - [F_CTRLCLKEN] = { 0, 0 }, - [F_CAMMODE] = { 1, 2 }, - [F_CSI_MODE] = { 3, 3 }, - [F_LANEENABLE] = { 24, 26 }, - }, - .num_lanes = 3, - }, -}; - -static const struct cal_data dra76x_cal_data = { - .camerarx = dra76x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), -}; - -static const struct cal_camerarx_data am654_cal_csi_phy[] = { - { - .fields = { - [F_CTRLCLKEN] = { 15, 15 }, - [F_CAMMODE] = { 24, 25 }, - [F_LANEENABLE] = { 0, 4 }, - }, - .num_lanes = 5, - }, -}; - -static const struct cal_data am654_cal_data = { - .camerarx = am654_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), -}; - -/* ------------------------------------------------------------------ - * I/O Register Accessors - * ------------------------------------------------------------------ - */ - -void cal_quickdump_regs(struct cal_dev *cal) -{ - unsigned int i; - - cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->base, - resource_size(cal->res), false); - - for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { - struct cal_camerarx *phy = cal->phy[i]; - - if (!phy) - continue; - - cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, - &phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)phy->base, - resource_size(phy->res), - false); - } -} - -/* ------------------------------------------------------------------ - * Context Management - * ------------------------------------------------------------------ - */ - -void cal_ctx_csi2_config(struct cal_ctx *ctx) -{ - u32 val; - - val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); - cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); - /* - * DT type: MIPI CSI-2 Specs - * 0x1: All - DT filter is disabled - * 0x24: RGB888 1 pixel = 3 bytes - * 0x2B: RAW10 4 pixels = 5 bytes - * 0x2A: RAW8 1 pixel = 1 byte - * 0x1E: YUV422 2 pixels = 4 bytes - */ - cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); - cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); - cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); - cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, - CAL_CSI2_CTX_PACK_MODE_MASK); - cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); - ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); -} - -void cal_ctx_pix_proc_config(struct cal_ctx *ctx) -{ - u32 val, extract, pack; - - switch (ctx->fmt->bpp) { - case 8: - extract = CAL_PIX_PROC_EXTRACT_B8; - pack = CAL_PIX_PROC_PACK_B8; - break; - case 10: - extract = CAL_PIX_PROC_EXTRACT_B10_MIPI; - pack = CAL_PIX_PROC_PACK_B16; - break; - case 12: - extract = CAL_PIX_PROC_EXTRACT_B12_MIPI; - pack = CAL_PIX_PROC_PACK_B16; - break; - case 16: - extract = CAL_PIX_PROC_EXTRACT_B16_LE; - pack = CAL_PIX_PROC_PACK_B16; - break; - default: - /* - * If you see this warning then it means that you added - * some new entry in the cal_formats[] array with a different - * bit per pixel values then the one supported below. - * Either add support for the new bpp value below or adjust - * the new entry to use one of the value below. - * - * Instead of failing here just use 8 bpp as a default. - */ - dev_warn_once(ctx->cal->dev, - "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", - __FILE__, __LINE__, __func__, ctx->fmt->bpp); - extract = CAL_PIX_PROC_EXTRACT_B8; - pack = CAL_PIX_PROC_PACK_B8; - break; - } - - val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); - cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); - cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); - cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); - cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); - cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); - ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); -} - -void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, - unsigned int height) -{ - u32 val; - - val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); - cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); - cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); - cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, - CAL_WR_DMA_CTRL_DTAG_MASK); - cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, - CAL_WR_DMA_CTRL_MODE_MASK); - cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, - CAL_WR_DMA_CTRL_PATTERN_MASK); - cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); - - /* - * width/16 not sure but giving it a whirl. - * zero does not work right - */ - cal_write_field(ctx->cal, - CAL_WR_DMA_OFST(ctx->index), - (width / 16), - CAL_WR_DMA_OFST_MASK); - ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - - val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); - /* 64 bit word means no skipping */ - cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); - /* - * (width*8)/64 this should be size of an entire line - * in 64bit word but 0 means all data until the end - * is detected automagically - */ - cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); - - val = cal_read(ctx->cal, CAL_CTRL); - cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, - CAL_CTRL_BURSTSIZE_MASK); - cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); - cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, - CAL_CTRL_POSTED_WRITES_MASK); - cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); - cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - cal_write(ctx->cal, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); -} - -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) -{ - cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); -} - -/* ------------------------------------------------------------------ - * IRQ Handling - * ------------------------------------------------------------------ - */ - -static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) -{ - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf; - unsigned long addr; - - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->next_frm = buf; - list_del(&buf->list); - - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_ctx_wr_dma_addr(ctx, addr); -} - -static inline void cal_process_buffer_complete(struct cal_ctx *ctx) -{ - ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); - ctx->cur_frm->vb.field = ctx->m_fmt.field; - ctx->cur_frm->vb.sequence = ctx->sequence++; - - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); - ctx->cur_frm = ctx->next_frm; -} - -static irqreturn_t cal_irq(int irq_cal, void *data) -{ - struct cal_dev *cal = data; - struct cal_ctx *ctx; - struct cal_dmaqueue *dma_q; - u32 status; - - status = cal_read(cal, CAL_HL_IRQSTATUS(0)); - if (status) { - unsigned int i; - - cal_write(cal, CAL_HL_IRQSTATUS(0), status); - - if (status & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); - - for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { - if (status & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = cal_read(cal, - CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - - dev_err_ratelimited(cal->dev, - "CIO%u error: %#08x\n", i, cio_stat); - - cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), - cio_stat); - } - } - } - - /* Check which DMA just finished */ - status = cal_read(cal, CAL_HL_IRQSTATUS(1)); - if (status) { - unsigned int i; - - /* Clear Interrupt status */ - cal_write(cal, CAL_HL_IRQSTATUS(1), status); - - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) { - ctx = cal->ctx[i]; - - spin_lock(&ctx->slock); - ctx->dma_act = false; - - if (ctx->cur_frm != ctx->next_frm) - cal_process_buffer_complete(ctx); - - spin_unlock(&ctx->slock); - } - } - } - - /* Check which DMA just started */ - status = cal_read(cal, CAL_HL_IRQSTATUS(2)); - if (status) { - unsigned int i; - - /* Clear Interrupt status */ - cal_write(cal, CAL_HL_IRQSTATUS(2), status); - - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) { - ctx = cal->ctx[i]; - dma_q = &ctx->vidq; - - spin_lock(&ctx->slock); - ctx->dma_act = true; - if (!list_empty(&dma_q->active) && - ctx->cur_frm == ctx->next_frm) - cal_schedule_next_buffer(ctx); - spin_unlock(&ctx->slock); - } - } - } - - return IRQ_HANDLED; -} - -/* ------------------------------------------------------------------ - * Asynchronous V4L2 subdev binding - * ------------------------------------------------------------------ - */ - -struct cal_v4l2_async_subdev { - struct v4l2_async_subdev asd; /* Must be first */ - struct cal_camerarx *phy; -}; - -static inline struct cal_v4l2_async_subdev * -to_cal_asd(struct v4l2_async_subdev *asd) -{ - return container_of(asd, struct cal_v4l2_async_subdev, asd); -} - -static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) -{ - struct cal_camerarx *phy = to_cal_asd(asd)->phy; - - if (phy->sensor) { - phy_info(phy, "Rejecting subdev %s (Already set!!)", - subdev->name); - return 0; - } - - phy->sensor = subdev; - phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); - - return 0; -} - -static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) -{ - struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (cal->ctx[i]) - cal_ctx_v4l2_register(cal->ctx[i]); - } - - return 0; -} - -static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { - .bound = cal_async_notifier_bound, - .complete = cal_async_notifier_complete, -}; - -static int cal_async_notifier_register(struct cal_dev *cal) -{ - unsigned int i; - int ret; - - v4l2_async_notifier_init(&cal->notifier); - cal->notifier.ops = &cal_async_notifier_ops; - - for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { - struct cal_camerarx *phy = cal->phy[i]; - struct cal_v4l2_async_subdev *casd; - struct v4l2_async_subdev *asd; - struct fwnode_handle *fwnode; - - if (!phy || !phy->sensor_node) - continue; - - fwnode = of_fwnode_handle(phy->sensor_node); - asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, - fwnode, - sizeof(*casd)); - if (IS_ERR(asd)) { - phy_err(phy, "Failed to add subdev to notifier\n"); - ret = PTR_ERR(asd); - goto error; - } - - casd = to_cal_asd(asd); - casd->phy = phy; - } - - ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); - if (ret) { - cal_err(cal, "Error registering async notifier\n"); - goto error; - } - - return 0; - -error: - v4l2_async_notifier_cleanup(&cal->notifier); - return ret; -} - -static void cal_async_notifier_unregister(struct cal_dev *cal) -{ - v4l2_async_notifier_unregister(&cal->notifier); - v4l2_async_notifier_cleanup(&cal->notifier); -} - -/* ------------------------------------------------------------------ - * Media and V4L2 device handling - * ------------------------------------------------------------------ - */ - -/* - * Register user-facing devices. To be called at the end of the probe function - * when all resources are initialized and ready. - */ -static int cal_media_register(struct cal_dev *cal) -{ - int ret; - - ret = media_device_register(&cal->mdev); - if (ret) { - cal_err(cal, "Failed to register media device\n"); - return ret; - } - - /* - * Register the async notifier. This may trigger registration of the - * V4L2 video devices if all subdevs are ready. - */ - ret = cal_async_notifier_register(cal); - if (ret) { - media_device_unregister(&cal->mdev); - return ret; - } - - return 0; -} - -/* - * Unregister the user-facing devices, but don't free memory yet. To be called - * at the beginning of the remove function, to disallow access from userspace. - */ -static void cal_media_unregister(struct cal_dev *cal) -{ - unsigned int i; - - /* Unregister all the V4L2 video devices. */ - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_unregister(cal->ctx[i]); - } - - cal_async_notifier_unregister(cal); - media_device_unregister(&cal->mdev); -} - -/* - * Initialize the in-kernel objects. To be called at the beginning of the probe - * function, before the V4L2 device is used by the driver. - */ -static int cal_media_init(struct cal_dev *cal) -{ - struct media_device *mdev = &cal->mdev; - int ret; - - mdev->dev = cal->dev; - mdev->hw_revision = cal->revision; - strscpy(mdev->model, "CAL", sizeof(mdev->model)); - snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", - dev_name(mdev->dev)); - media_device_init(mdev); - - /* - * Initialize the V4L2 device (despite the function name, this performs - * initialization, not registration). - */ - cal->v4l2_dev.mdev = mdev; - ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); - if (ret) { - cal_err(cal, "Failed to register V4L2 device\n"); - return ret; - } - - vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); - - return 0; -} - -/* - * Cleanup the in-kernel objects, freeing memory. To be called at the very end - * of the remove sequence, when nothing (including userspace) can access the - * objects anymore. - */ -static void cal_media_cleanup(struct cal_dev *cal) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_cleanup(cal->ctx[i]); - } - - v4l2_device_unregister(&cal->v4l2_dev); - media_device_cleanup(&cal->mdev); - - vb2_dma_contig_clear_max_seg_size(cal->dev); -} - -/* ------------------------------------------------------------------ - * Initialization and module stuff - * ------------------------------------------------------------------ - */ - -static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) -{ - struct cal_ctx *ctx; - int ret; - - ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return NULL; - - ctx->cal = cal; - ctx->phy = cal->phy[inst]; - ctx->index = inst; - ctx->cport = inst; - - ret = cal_ctx_v4l2_init(ctx); - if (ret) - return NULL; - - return ctx; -} - -static const struct of_device_id cal_of_match[] = { - { - .compatible = "ti,dra72-cal", - .data = (void *)&dra72x_cal_data, - }, - { - .compatible = "ti,dra72-pre-es2-cal", - .data = (void *)&dra72x_es1_cal_data, - }, - { - .compatible = "ti,dra76-cal", - .data = (void *)&dra76x_cal_data, - }, - { - .compatible = "ti,am654-cal", - .data = (void *)&am654_cal_data, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, cal_of_match); - -/* Get hardware revision and info. */ - -#define CAL_HL_HWINFO_VALUE 0xa3c90469 - -static void cal_get_hwinfo(struct cal_dev *cal) -{ - u32 hwinfo; - - cal->revision = cal_read(cal, CAL_HL_REVISION); - switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { - case CAL_HL_REVISION_SCHEME_H08: - cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", - FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), - FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), - FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), - cal->revision); - break; - - case CAL_HL_REVISION_SCHEME_LEGACY: - default: - cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", - cal->revision); - break; - } - - hwinfo = cal_read(cal, CAL_HL_HWINFO); - if (hwinfo != CAL_HL_HWINFO_VALUE) - cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", - hwinfo, CAL_HL_HWINFO_VALUE); -} - -static int cal_init_camerarx_regmap(struct cal_dev *cal) -{ - struct platform_device *pdev = to_platform_device(cal->dev); - struct device_node *np = cal->dev->of_node; - struct regmap_config config = { }; - struct regmap *syscon; - struct resource *res; - unsigned int offset; - void __iomem *base; - - syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", - 1, &offset); - if (!IS_ERR(syscon)) { - cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = offset; - return 0; - } - - dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", - PTR_ERR(syscon)); - - /* - * Backward DTS compatibility. If syscon entry is not present then - * check if the camerrx_control resource is present. - */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(cal->dev, res); - if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap camerrx_control\n"); - return PTR_ERR(base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - config.reg_bits = 32; - config.reg_stride = 4; - config.val_bits = 32; - config.max_register = resource_size(res) - 4; - - syscon = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(syscon)) { - pr_err("regmap init failed\n"); - return PTR_ERR(syscon); - } - - /* - * In this case the base already point to the direct CM register so no - * need for an offset. - */ - cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = 0; - - return 0; -} - -static int cal_probe(struct platform_device *pdev) -{ - struct cal_dev *cal; - struct cal_ctx *ctx; - bool connected = false; - unsigned int i; - int ret; - int irq; - - cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); - if (!cal) - return -ENOMEM; - - cal->data = of_device_get_match_data(&pdev->dev); - if (!cal->data) { - dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); - return -ENODEV; - } - - cal->dev = &pdev->dev; - platform_set_drvdata(pdev, cal); - - /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ - cal->fclk = devm_clk_get(&pdev->dev, "fck"); - if (IS_ERR(cal->fclk)) { - dev_err(&pdev->dev, "cannot get CAL fclk\n"); - return PTR_ERR(cal->fclk); - } - - ret = cal_init_camerarx_regmap(cal); - if (ret < 0) - return ret; - - cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "cal_top"); - cal->base = devm_ioremap_resource(&pdev->dev, cal->res); - if (IS_ERR(cal->base)) - return PTR_ERR(cal->base); - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - cal->res->name, &cal->res->start, &cal->res->end); - - irq = platform_get_irq(pdev, 0); - cal_dbg(1, cal, "got irq# %d\n", irq); - ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, - cal); - if (ret) - return ret; - - /* Read the revision and hardware info to verify hardware access. */ - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); - if (ret) - goto error_pm_runtime; - - cal_get_hwinfo(cal); - pm_runtime_put_sync(&pdev->dev); - - /* Create CAMERARX PHYs. */ - for (i = 0; i < cal->data->num_csi2_phy; ++i) { - cal->phy[i] = cal_camerarx_create(cal, i); - if (IS_ERR(cal->phy[i])) { - ret = PTR_ERR(cal->phy[i]); - cal->phy[i] = NULL; - goto error_camerarx; - } - - if (cal->phy[i]->sensor_node) - connected = true; - } - - if (!connected) { - cal_err(cal, "Neither port is configured, no point in staying up\n"); - ret = -ENODEV; - goto error_camerarx; - } - - /* Initialize the media device. */ - ret = cal_media_init(cal); - if (ret < 0) - goto error_camerarx; - - /* Create contexts. */ - for (i = 0; i < cal->data->num_csi2_phy; ++i) { - if (!cal->phy[i]->sensor_node) - continue; - - cal->ctx[i] = cal_ctx_create(cal, i); - if (!cal->ctx[i]) { - cal_err(cal, "Failed to create context %u\n", i); - ret = -ENODEV; - goto error_context; - } - } - - /* Register the media device. */ - ret = cal_media_register(cal); - if (ret) - goto error_context; - - return 0; - -error_context: - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - ctx = cal->ctx[i]; - if (ctx) - cal_ctx_v4l2_cleanup(ctx); - } - - cal_media_cleanup(cal); - -error_camerarx: - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) - cal_camerarx_destroy(cal->phy[i]); - -error_pm_runtime: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static int cal_remove(struct platform_device *pdev) -{ - struct cal_dev *cal = platform_get_drvdata(pdev); - unsigned int i; - - cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); - - pm_runtime_get_sync(&pdev->dev); - - cal_media_unregister(cal); - - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { - if (cal->phy[i]) - cal_camerarx_disable(cal->phy[i]); - } - - cal_media_cleanup(cal); - - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) - cal_camerarx_destroy(cal->phy[i]); - - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static int cal_runtime_resume(struct device *dev) -{ - struct cal_dev *cal = dev_get_drvdata(dev); - - if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { - /* - * Apply errata on both port everytime we (re-)enable - * the clock - */ - cal_camerarx_i913_errata(cal->phy[0]); - cal_camerarx_i913_errata(cal->phy[1]); - } - - return 0; -} - -static const struct dev_pm_ops cal_pm_ops = { - .runtime_resume = cal_runtime_resume, -}; - -static struct platform_driver cal_pdrv = { - .probe = cal_probe, - .remove = cal_remove, - .driver = { - .name = CAL_MODULE_NAME, - .pm = &cal_pm_ops, - .of_match_table = cal_of_match, - }, -}; - -module_platform_driver(cal_pdrv); diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c --- a/drivers/media/platform/ti-vpe/cal-camerarx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,649 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * TI Camera Access Layer (CAL) - CAMERARX - * - * Copyright (c) 2015-2020 Texas Instruments Inc. - * - * Authors: - * Benoit Parrot - * Laurent Pinchart - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "cal.h" -#include "cal_regs.h" - -/* ------------------------------------------------------------------ - * I/O Register Accessors - * ------------------------------------------------------------------ - */ - -static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) -{ - return ioread32(phy->base + offset); -} - -static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) -{ - iowrite32(val, phy->base + offset); -} - -/* ------------------------------------------------------------------ - * CAMERARX Management - * ------------------------------------------------------------------ - */ - -static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - s64 rate; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); - - return rate; -} - -static void cal_camerarx_lane_config(struct cal_camerarx *phy) -{ - u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - int lane; - - cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } - - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - phy->instance, val); -} - -static void cal_camerarx_enable(struct cal_camerarx *phy) -{ - u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; - - regmap_field_write(phy->fields[F_CAMMODE], 0); - /* Always enable all lanes at the phy control level */ - regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); - /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); -} - -void cal_camerarx_disable(struct cal_camerarx *phy) -{ - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); -} - -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, - const struct cal_fmt *fmt) -{ - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - - /* - * CSI-2 is DDR and we only count used lanes. - * - * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * fmt->bpp; - */ - csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, - 2 * num_lanes * 1000); - - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); - cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); - - reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); - cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - cal_set_field(®1, TCLK_MISS, - CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); -} - -static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) -{ - u32 target_state; - unsigned int i; - - target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - - for (i = 0; i < 10; i++) { - u32 current_state; - - current_state = cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); - - if (current_state == target_state) - break; - - usleep_range(1000, 1100); - } - - if (i == 10) - phy_err(phy, "Failed to power %s complexio\n", - enable ? "up" : "down"); -} - -static void cal_camerarx_wait_reset(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - phy_err(phy, "Timeout waiting for Complex IO reset done\n"); -} - -static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (cal_read_field(phy->cal, - CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - phy_err(phy, "Timeout waiting for stop state\n"); -} - -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) -{ - s64 external_rate; - u32 sscounter; - u32 val; - int ret; - - external_rate = cal_camerarx_get_external_rate(phy); - if (external_rate < 0) - return external_rate; - - ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - phy_err(phy, "power on failed in subdev\n"); - return ret; - } - - /* - * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / - * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / - * DRA80xM TRMs have a a slightly simplified sequence. - */ - - /* - * 1. Configure all CSI-2 low level protocol registers to be ready to - * receive signals/data from the CSI-2 PHY. - * - * i.-v. Configure the lanes position and polarity. - */ - cal_camerarx_lane_config(phy); - - /* - * vi.-vii. Configure D-PHY mode, enable the required lanes and - * enable the CAMERARX clock. - */ - cal_camerarx_enable(phy); - - /* - * 2. CSI PHY and link initialization sequence. - * - * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion - * at this point, as it requires the external sensor to send the - * CSI-2 HS clock. - */ - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); - - /* Dummy read to allow SCP reset to complete. */ - camerarx_read(phy, CAL_CSI2_PHY_REG0); - - /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate, fmt); - - /* - * b. Assert the FORCERXMODE signal. - * - * The stop-state-counter is based on fclk cycles, and we always use - * the x16 and x4 settings, so stop-state-timeout = - * fclk-cycle * 16 * 4 * counter. - * - * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we - * calculate a timeout that's 100us (rounding up). - */ - sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - - val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); - cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - cal_set_field(&val, sscounter, - CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - - /* Assert the FORCERXMODE signal. */ - cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - - /* - * c. Connect pull-down on CSI-2 PHY link (using pad control). - * - * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not - * implemented. - */ - - /* - * d. Power up the CSI-2 PHY. - * e. Check whether the state status reaches the ON state. - */ - cal_camerarx_power(phy, true); - - /* - * Start the sensor to enable the CSI-2 HS clock. We can now wait for - * CSI-2 PHY reset to complete. - */ - ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); - if (ret) { - v4l2_subdev_call(phy->sensor, core, s_power, 0); - phy_err(phy, "stream on failed in subdev\n"); - return ret; - } - - cal_camerarx_wait_reset(phy); - - /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", - phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); - - /* - * g. Disable pull-down on CSI-2 PHY link (using pad control). - * - * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not - * implemented. - */ - - return 0; -} - -void cal_camerarx_stop(struct cal_camerarx *phy) -{ - unsigned int i; - int ret; - - cal_camerarx_power(phy, false); - - /* Assert Complex IO Reset */ - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, - (i >= 10) ? "(timeout)" : ""); - - /* Disable the phy */ - cal_camerarx_disable(phy); - - if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) - phy_err(phy, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - phy_err(phy, "power off failed in subdev\n"); -} - -/* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 - */ -void cal_camerarx_i913_errata(struct cal_camerarx *phy) -{ - u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); - - cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); -} - -/* - * Enable the expected IRQ sources - */ -void cal_camerarx_enable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); - - /* Always enable OCPO error */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} - -void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - /* Disable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); -} - -void cal_camerarx_ppi_enable(struct cal_camerarx *phy) -{ - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -void cal_camerarx_ppi_disable(struct cal_camerarx *phy) -{ - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy) -{ - const struct cal_camerarx_data *phy_data; - unsigned int i; - - if (!cal->data) - return -EINVAL; - - phy_data = &cal->data->camerarx[phy->instance]; - - for (i = 0; i < F_MAX_FIELDS; i++) { - struct reg_field field = { - .reg = cal->syscon_camerrx_offset, - .lsb = phy_data->fields[i].lsb, - .msb = phy_data->fields[i].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[i] = devm_regmap_field_alloc(cal->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->fields[i])) { - cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[i]); - } - } - - return 0; -} - -static int cal_camerarx_parse_dt(struct cal_camerarx *phy) -{ - struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct device_node *ep_node; - char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; - unsigned int i; - int ret; - - /* - * Find the endpoint node for the port corresponding to the PHY - * instance, and parse its CSI-2-related properties. - */ - ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, - phy->instance, 0); - if (!ep_node) { - /* - * The endpoint is not mandatory, not all PHY instances need to - * be connected in DT. - */ - phy_dbg(3, phy, "Port has no endpoint\n"); - return 0; - } - - endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - if (ret < 0) { - phy_err(phy, "Failed to parse endpoint\n"); - goto done; - } - - for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { - unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; - - if (lane > 4) { - phy_err(phy, "Invalid position %u for data lane %u\n", - lane, i); - ret = -EINVAL; - goto done; - } - - data_lanes[i*2] = '0' + lane; - data_lanes[i*2+1] = ' '; - } - - data_lanes[i*2-1] = '\0'; - - phy_dbg(3, phy, - "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", - endpoint->bus.mipi_csi2.clock_lane, data_lanes, - endpoint->bus.mipi_csi2.flags); - - /* Retrieve the connected device and store it for later use. */ - phy->sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!phy->sensor_node) { - phy_dbg(3, phy, "Can't get remote parent\n"); - ret = -EINVAL; - goto done; - } - - phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); - -done: - of_node_put(ep_node); - return ret; -} - -struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, - unsigned int instance) -{ - struct platform_device *pdev = to_platform_device(cal->dev); - struct cal_camerarx *phy; - int ret; - - phy = kzalloc(sizeof(*phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->cal = cal; - phy->instance = instance; - - phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (instance == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - phy->base = devm_ioremap_resource(cal->dev, phy->res); - if (IS_ERR(phy->base)) { - cal_err(cal, "failed to ioremap\n"); - ret = PTR_ERR(phy->base); - goto error; - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - phy->res->name, &phy->res->start, &phy->res->end); - - ret = cal_camerarx_regmap_init(cal, phy); - if (ret) - goto error; - - ret = cal_camerarx_parse_dt(phy); - if (ret) - goto error; - - return phy; - -error: - kfree(phy); - return ERR_PTR(ret); -} - -void cal_camerarx_destroy(struct cal_camerarx *phy) -{ - if (!phy) - return; - - of_node_put(phy->sensor_node); - kfree(phy); -} diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h --- a/drivers/media/platform/ti-vpe/cal.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/cal.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,267 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * TI Camera Access Layer (CAL) - * - * Copyright (c) 2015-2020 Texas Instruments Inc. - * - * Authors: - * Benoit Parrot - * Laurent Pinchart - */ -#ifndef __TI_CAL_H__ -#define __TI_CAL_H__ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define CAL_MODULE_NAME "cal" -#define CAL_NUM_CONTEXT 2 -#define CAL_NUM_CSI2_PORTS 2 - -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 - -struct device; -struct device_node; -struct resource; -struct regmap; -struct regmap_fied; -struct v4l2_subdev; - -/* CTRL_CORE_CAMERRX_CONTROL register field id */ -enum cal_camerarx_field { - F_CTRLCLKEN, - F_CAMMODE, - F_LANEENABLE, - F_CSI_MODE, - F_MAX_FIELDS, -}; - -struct cal_fmt { - u32 fourcc; - u32 code; - /* Bits per pixel */ - u8 bpp; -}; - -/* buffer for one video frame */ -struct cal_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct cal_dmaqueue { - struct list_head active; -}; - -struct cal_camerarx_data { - struct { - unsigned int lsb; - unsigned int msb; - } fields[F_MAX_FIELDS]; - unsigned int num_lanes; -}; - -struct cal_data { - const struct cal_camerarx_data *camerarx; - unsigned int num_csi2_phy; - unsigned int flags; -}; - -/* - * The Camera Adaptation Layer (CAL) module is paired with one or more complex - * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and - * DMA contexts. - * - * The cal_dev structure represents the whole subsystem, including the CAL and - * the CAMERARX instances. Instances of struct cal_dev are named cal through the - * driver. - * - * The cal_camerarx structure represents one CAMERARX instance. Instances of - * cal_camerarx are named phy through the driver. - * - * The cal_ctx structure represents the combination of one CSI-2 context, one - * processing context and one DMA context. Instance of struct cal_ctx are named - * ctx through the driver. - */ - -struct cal_camerarx { - void __iomem *base; - struct resource *res; - struct device *dev; - struct regmap_field *fields[F_MAX_FIELDS]; - - struct cal_dev *cal; - unsigned int instance; - - struct v4l2_fwnode_endpoint endpoint; - struct device_node *sensor_node; - struct v4l2_subdev *sensor; -}; - -struct cal_dev { - struct clk *fclk; - int irq; - void __iomem *base; - struct resource *res; - struct device *dev; - - const struct cal_data *data; - u32 revision; - - /* Control Module handle */ - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; - - /* Camera Core Module handle */ - struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; - - struct cal_ctx *ctx[CAL_NUM_CONTEXT]; - - struct media_device mdev; - struct v4l2_device v4l2_dev; - struct v4l2_async_notifier notifier; -}; - -/* - * There is one cal_ctx structure for each camera core context. - */ -struct cal_ctx { - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; - struct media_pad pad; - - struct cal_dev *cal; - struct cal_camerarx *phy; - - /* v4l2_ioctl mutex */ - struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - - struct cal_dmaqueue vidq; - - /* video capture */ - const struct cal_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - - /* Current subdev enumerated format */ - const struct cal_fmt **active_fmt; - unsigned int num_active_fmt; - - unsigned int sequence; - struct vb2_queue vb_vidq; - unsigned int index; - unsigned int cport; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - bool dma_act; -}; - -extern unsigned int cal_debug; -extern int cal_video_nr; - -#define cal_dbg(level, cal, fmt, arg...) \ - do { \ - if (cal_debug >= (level)) \ - dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ - } while (0) -#define cal_info(cal, fmt, arg...) \ - dev_info((cal)->dev, fmt, ##arg) -#define cal_err(cal, fmt, arg...) \ - dev_err((cal)->dev, fmt, ##arg) - -#define ctx_dbg(level, ctx, fmt, arg...) \ - cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) -#define ctx_info(ctx, fmt, arg...) \ - cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) -#define ctx_err(ctx, fmt, arg...) \ - cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) - -#define phy_dbg(level, phy, fmt, arg...) \ - cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define phy_info(phy, fmt, arg...) \ - cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define phy_err(phy, fmt, arg...) \ - cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) - -static inline u32 cal_read(struct cal_dev *cal, u32 offset) -{ - return ioread32(cal->base + offset); -} - -static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) -{ - iowrite32(val, cal->base + offset); -} - -static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) -{ - return FIELD_GET(mask, cal_read(cal, offset)); -} - -static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value, - u32 mask) -{ - u32 val = cal_read(cal, offset); - - val &= ~mask; - val |= (value << __ffs(mask)) & mask; - cal_write(cal, offset, val); -} - -static inline void cal_set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - -void cal_quickdump_regs(struct cal_dev *cal); - -void cal_camerarx_disable(struct cal_camerarx *phy); -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); -void cal_camerarx_stop(struct cal_camerarx *phy); -void cal_camerarx_enable_irqs(struct cal_camerarx *phy); -void cal_camerarx_disable_irqs(struct cal_camerarx *phy); -void cal_camerarx_ppi_enable(struct cal_camerarx *phy); -void cal_camerarx_ppi_disable(struct cal_camerarx *phy); -void cal_camerarx_i913_errata(struct cal_camerarx *phy); -struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, - unsigned int instance); -void cal_camerarx_destroy(struct cal_camerarx *phy); - -void cal_ctx_csi2_config(struct cal_ctx *ctx); -void cal_ctx_pix_proc_config(struct cal_ctx *ctx); -void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, - unsigned int height); -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); - -int cal_ctx_v4l2_register(struct cal_ctx *ctx); -void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); -int cal_ctx_v4l2_init(struct cal_ctx *ctx); -void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); - -#endif /* __TI_CAL_H__ */ diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h --- a/drivers/media/platform/ti-vpe/cal_regs.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/cal_regs.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,492 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * TI CAL camera interface driver - * - * Copyright (c) 2015 Texas Instruments Inc. - * - * Benoit Parrot, - */ - -#ifndef __TI_CAL_REGS_H -#define __TI_CAL_REGS_H - -/* - * struct cal_dev.flags possibilities - * - * DRA72_CAL_PRE_ES2_LDO_DISABLE: - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 - */ -#define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0) - -/* CAL register offsets */ - -#define CAL_HL_REVISION 0x0000 -#define CAL_HL_HWINFO 0x0004 -#define CAL_HL_SYSCONFIG 0x0010 -#define CAL_HL_IRQ_EOI 0x001c -#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U) -#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U) -#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U) -#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U) -#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) -#define CAL_CTRL 0x100 -#define CAL_CTRL1 0x104 -#define CAL_LINE_NUMBER_EVT 0x108 -#define CAL_VPORT_CTRL1 0x120 -#define CAL_VPORT_CTRL2 0x124 -#define CAL_BYS_CTRL1 0x130 -#define CAL_BYS_CTRL2 0x134 -#define CAL_RD_DMA_CTRL 0x140 -#define CAL_RD_DMA_PIX_ADDR 0x144 -#define CAL_RD_DMA_PIX_OFST 0x148 -#define CAL_RD_DMA_XSIZE 0x14c -#define CAL_RD_DMA_YSIZE 0x150 -#define CAL_RD_DMA_INIT_ADDR 0x154 -#define CAL_RD_DMA_INIT_OFST 0x168 -#define CAL_RD_DMA_CTRL2 0x16c -#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U) -#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U) -#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U) -#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U) -#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U) -#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U) -#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U) -#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U) -#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U) -#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) -#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) -#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) -#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U) -#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U) -#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U) -#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U) -#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U) -#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U) -#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U) -#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U) -#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U) -#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U) -#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U) -#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U) -#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U) -#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U) -#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U) -#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U) - -/* CAL CSI2 PHY register offsets */ -#define CAL_CSI2_PHY_REG0 0x000 -#define CAL_CSI2_PHY_REG1 0x004 -#define CAL_CSI2_PHY_REG2 0x008 -#define CAL_CSI2_PHY_REG10 0x028 - -/* CAL Control Module Core Camerrx Control register offsets */ -#define CM_CTRL_CORE_CAMERRX_CONTROL 0x000 - -/********************************************************************* -* Field Definition Macros -*********************************************************************/ - -#define CAL_HL_REVISION_MINOR_MASK GENMASK(5, 0) -#define CAL_HL_REVISION_CUSTOM_MASK GENMASK(7, 6) -#define CAL_HL_REVISION_MAJOR_MASK GENMASK(10, 8) -#define CAL_HL_REVISION_RTL_MASK GENMASK(15, 11) -#define CAL_HL_REVISION_FUNC_MASK GENMASK(27, 16) -#define CAL_HL_REVISION_SCHEME_MASK GENMASK(31, 30) -#define CAL_HL_REVISION_SCHEME_H08 1 -#define CAL_HL_REVISION_SCHEME_LEGACY 0 - -#define CAL_HL_HWINFO_WFIFO_MASK GENMASK(3, 0) -#define CAL_HL_HWINFO_RFIFO_MASK GENMASK(7, 4) -#define CAL_HL_HWINFO_PCTX_MASK GENMASK(12, 8) -#define CAL_HL_HWINFO_WCTX_MASK GENMASK(18, 13) -#define CAL_HL_HWINFO_VFIFO_MASK GENMASK(22, 19) -#define CAL_HL_HWINFO_NCPORT_MASK GENMASK(27, 23) -#define CAL_HL_HWINFO_NPPI_CTXS0_MASK GENMASK(29, 28) -#define CAL_HL_HWINFO_NPPI_CTXS1_MASK GENMASK(31, 30) -#define CAL_HL_HWINFO_NPPI_CONTEXTS_ZERO 0 -#define CAL_HL_HWINFO_NPPI_CONTEXTS_FOUR 1 -#define CAL_HL_HWINFO_NPPI_CONTEXTS_EIGHT 2 -#define CAL_HL_HWINFO_NPPI_CONTEXTS_RESERVED 3 - -#define CAL_HL_SYSCONFIG_SOFTRESET_MASK BIT(0) -#define CAL_HL_SYSCONFIG_SOFTRESET_DONE 0x0 -#define CAL_HL_SYSCONFIG_SOFTRESET_PENDING 0x1 -#define CAL_HL_SYSCONFIG_SOFTRESET_NOACTION 0x0 -#define CAL_HL_SYSCONFIG_SOFTRESET_RESET 0x1 -#define CAL_HL_SYSCONFIG_IDLE_MASK GENMASK(3, 2) -#define CAL_HL_SYSCONFIG_IDLEMODE_FORCE 0 -#define CAL_HL_SYSCONFIG_IDLEMODE_NO 1 -#define CAL_HL_SYSCONFIG_IDLEMODE_SMART1 2 -#define CAL_HL_SYSCONFIG_IDLEMODE_SMART2 3 - -#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK BIT(0) -#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 -#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 - -#define CAL_HL_IRQ_MASK(m) BIT(m) - -#define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) - -#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8) -#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8) - -#define CAL_PIX_PROC_EN_MASK BIT(0) -#define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) -#define CAL_PIX_PROC_EXTRACT_B6 0x0 -#define CAL_PIX_PROC_EXTRACT_B7 0x1 -#define CAL_PIX_PROC_EXTRACT_B8 0x2 -#define CAL_PIX_PROC_EXTRACT_B10 0x3 -#define CAL_PIX_PROC_EXTRACT_B10_MIPI 0x4 -#define CAL_PIX_PROC_EXTRACT_B12 0x5 -#define CAL_PIX_PROC_EXTRACT_B12_MIPI 0x6 -#define CAL_PIX_PROC_EXTRACT_B14 0x7 -#define CAL_PIX_PROC_EXTRACT_B14_MIPI 0x8 -#define CAL_PIX_PROC_EXTRACT_B16_BE 0x9 -#define CAL_PIX_PROC_EXTRACT_B16_LE 0xa -#define CAL_PIX_PROC_DPCMD_MASK GENMASK(9, 5) -#define CAL_PIX_PROC_DPCMD_BYPASS 0x0 -#define CAL_PIX_PROC_DPCMD_DPCM_10_8_1 0x2 -#define CAL_PIX_PROC_DPCMD_DPCM_12_8_1 0x8 -#define CAL_PIX_PROC_DPCMD_DPCM_10_7_1 0x4 -#define CAL_PIX_PROC_DPCMD_DPCM_10_7_2 0x5 -#define CAL_PIX_PROC_DPCMD_DPCM_10_6_1 0x6 -#define CAL_PIX_PROC_DPCMD_DPCM_10_6_2 0x7 -#define CAL_PIX_PROC_DPCMD_DPCM_12_7_1 0xa -#define CAL_PIX_PROC_DPCMD_DPCM_12_6_1 0xc -#define CAL_PIX_PROC_DPCMD_DPCM_14_10 0xe -#define CAL_PIX_PROC_DPCMD_DPCM_14_8_1 0x10 -#define CAL_PIX_PROC_DPCMD_DPCM_16_12_1 0x12 -#define CAL_PIX_PROC_DPCMD_DPCM_16_10_1 0x14 -#define CAL_PIX_PROC_DPCMD_DPCM_16_8_1 0x16 -#define CAL_PIX_PROC_DPCME_MASK GENMASK(15, 11) -#define CAL_PIX_PROC_DPCME_BYPASS 0x0 -#define CAL_PIX_PROC_DPCME_DPCM_10_8_1 0x2 -#define CAL_PIX_PROC_DPCME_DPCM_12_8_1 0x8 -#define CAL_PIX_PROC_DPCME_DPCM_14_10 0xe -#define CAL_PIX_PROC_DPCME_DPCM_14_8_1 0x10 -#define CAL_PIX_PROC_DPCME_DPCM_16_12_1 0x12 -#define CAL_PIX_PROC_DPCME_DPCM_16_10_1 0x14 -#define CAL_PIX_PROC_DPCME_DPCM_16_8_1 0x16 -#define CAL_PIX_PROC_PACK_MASK GENMASK(18, 16) -#define CAL_PIX_PROC_PACK_B8 0x0 -#define CAL_PIX_PROC_PACK_B10_MIPI 0x2 -#define CAL_PIX_PROC_PACK_B12 0x3 -#define CAL_PIX_PROC_PACK_B12_MIPI 0x4 -#define CAL_PIX_PROC_PACK_B16 0x5 -#define CAL_PIX_PROC_PACK_ARGB 0x6 -#define CAL_PIX_PROC_CPORT_MASK GENMASK(23, 19) - -#define CAL_CTRL_POSTED_WRITES_MASK BIT(0) -#define CAL_CTRL_POSTED_WRITES_NONPOSTED 0 -#define CAL_CTRL_POSTED_WRITES 1 -#define CAL_CTRL_TAGCNT_MASK GENMASK(4, 1) -#define CAL_CTRL_BURSTSIZE_MASK GENMASK(6, 5) -#define CAL_CTRL_BURSTSIZE_BURST16 0x0 -#define CAL_CTRL_BURSTSIZE_BURST32 0x1 -#define CAL_CTRL_BURSTSIZE_BURST64 0x2 -#define CAL_CTRL_BURSTSIZE_BURST128 0x3 -#define CAL_CTRL_LL_FORCE_STATE_MASK GENMASK(12, 7) -#define CAL_CTRL_MFLAGL_MASK GENMASK(20, 13) -#define CAL_CTRL_PWRSCPCLK_MASK BIT(21) -#define CAL_CTRL_PWRSCPCLK_AUTO 0 -#define CAL_CTRL_PWRSCPCLK_FORCE 1 -#define CAL_CTRL_RD_DMA_STALL_MASK BIT(22) -#define CAL_CTRL_MFLAGH_MASK GENMASK(31, 24) - -#define CAL_CTRL1_PPI_GROUPING_MASK GENMASK(1, 0) -#define CAL_CTRL1_PPI_GROUPING_DISABLED 0 -#define CAL_CTRL1_PPI_GROUPING_RESERVED 1 -#define CAL_CTRL1_PPI_GROUPING_0 2 -#define CAL_CTRL1_PPI_GROUPING_1 3 -#define CAL_CTRL1_INTERLEAVE01_MASK GENMASK(3, 2) -#define CAL_CTRL1_INTERLEAVE01_DISABLED 0 -#define CAL_CTRL1_INTERLEAVE01_PIX1 1 -#define CAL_CTRL1_INTERLEAVE01_PIX4 2 -#define CAL_CTRL1_INTERLEAVE01_RESERVED 3 -#define CAL_CTRL1_INTERLEAVE23_MASK GENMASK(5, 4) -#define CAL_CTRL1_INTERLEAVE23_DISABLED 0 -#define CAL_CTRL1_INTERLEAVE23_PIX1 1 -#define CAL_CTRL1_INTERLEAVE23_PIX4 2 -#define CAL_CTRL1_INTERLEAVE23_RESERVED 3 - -#define CAL_LINE_NUMBER_EVT_CPORT_MASK GENMASK(4, 0) -#define CAL_LINE_NUMBER_EVT_MASK GENMASK(29, 16) - -#define CAL_VPORT_CTRL1_PCLK_MASK GENMASK(16, 0) -#define CAL_VPORT_CTRL1_XBLK_MASK GENMASK(24, 17) -#define CAL_VPORT_CTRL1_YBLK_MASK GENMASK(30, 25) -#define CAL_VPORT_CTRL1_WIDTH_MASK BIT(31) -#define CAL_VPORT_CTRL1_WIDTH_ONE 0 -#define CAL_VPORT_CTRL1_WIDTH_TWO 1 - -#define CAL_VPORT_CTRL2_CPORT_MASK GENMASK(4, 0) -#define CAL_VPORT_CTRL2_FREERUNNING_MASK BIT(15) -#define CAL_VPORT_CTRL2_FREERUNNING_GATED 0 -#define CAL_VPORT_CTRL2_FREERUNNING_FREE 1 -#define CAL_VPORT_CTRL2_FS_RESETS_MASK BIT(16) -#define CAL_VPORT_CTRL2_FS_RESETS_NO 0 -#define CAL_VPORT_CTRL2_FS_RESETS_YES 1 -#define CAL_VPORT_CTRL2_FSM_RESET_MASK BIT(17) -#define CAL_VPORT_CTRL2_FSM_RESET_NOEFFECT 0 -#define CAL_VPORT_CTRL2_FSM_RESET 1 -#define CAL_VPORT_CTRL2_RDY_THR_MASK GENMASK(31, 18) - -#define CAL_BYS_CTRL1_PCLK_MASK GENMASK(16, 0) -#define CAL_BYS_CTRL1_XBLK_MASK GENMASK(24, 17) -#define CAL_BYS_CTRL1_YBLK_MASK GENMASK(30, 25) -#define CAL_BYS_CTRL1_BYSINEN_MASK BIT(31) - -#define CAL_BYS_CTRL2_CPORTIN_MASK GENMASK(4, 0) -#define CAL_BYS_CTRL2_CPORTOUT_MASK GENMASK(9, 5) -#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK BIT(10) -#define CAL_BYS_CTRL2_DUPLICATEDDATA_NO 0 -#define CAL_BYS_CTRL2_DUPLICATEDDATA_YES 1 -#define CAL_BYS_CTRL2_FREERUNNING_MASK BIT(11) -#define CAL_BYS_CTRL2_FREERUNNING_NO 0 -#define CAL_BYS_CTRL2_FREERUNNING_YES 1 - -#define CAL_RD_DMA_CTRL_GO_MASK BIT(0) -#define CAL_RD_DMA_CTRL_GO_DIS 0 -#define CAL_RD_DMA_CTRL_GO_EN 1 -#define CAL_RD_DMA_CTRL_GO_IDLE 0 -#define CAL_RD_DMA_CTRL_GO_BUSY 1 -#define CAL_RD_DMA_CTRL_INIT_MASK BIT(1) -#define CAL_RD_DMA_CTRL_BW_LIMITER_MASK GENMASK(10, 2) -#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_MASK GENMASK(14, 11) -#define CAL_RD_DMA_CTRL_PCLK_MASK GENMASK(31, 15) - -#define CAL_RD_DMA_PIX_ADDR_MASK GENMASK(31, 3) - -#define CAL_RD_DMA_PIX_OFST_MASK GENMASK(31, 4) - -#define CAL_RD_DMA_XSIZE_MASK GENMASK(31, 19) - -#define CAL_RD_DMA_YSIZE_MASK GENMASK(29, 16) - -#define CAL_RD_DMA_INIT_ADDR_MASK GENMASK(31, 3) - -#define CAL_RD_DMA_INIT_OFST_MASK GENMASK(31, 3) - -#define CAL_RD_DMA_CTRL2_CIRC_MODE_MASK GENMASK(2, 0) -#define CAL_RD_DMA_CTRL2_CIRC_MODE_DIS 0 -#define CAL_RD_DMA_CTRL2_CIRC_MODE_ONE 1 -#define CAL_RD_DMA_CTRL2_CIRC_MODE_FOUR 2 -#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTEEN 3 -#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTYFOUR 4 -#define CAL_RD_DMA_CTRL2_CIRC_MODE_RESERVED 5 -#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK BIT(3) -#define CAL_RD_DMA_CTRL2_PATTERN_MASK GENMASK(5, 4) -#define CAL_RD_DMA_CTRL2_PATTERN_LINEAR 0 -#define CAL_RD_DMA_CTRL2_PATTERN_YUV420 1 -#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP2 2 -#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP4 3 -#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK BIT(6) -#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_FREERUNNING 0 -#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_WAITFORBYSOUT 1 -#define CAL_RD_DMA_CTRL2_CIRC_SIZE_MASK GENMASK(29, 16) - -#define CAL_WR_DMA_CTRL_MODE_MASK GENMASK(2, 0) -#define CAL_WR_DMA_CTRL_MODE_DIS 0 -#define CAL_WR_DMA_CTRL_MODE_SHD 1 -#define CAL_WR_DMA_CTRL_MODE_CNT 2 -#define CAL_WR_DMA_CTRL_MODE_CNT_INIT 3 -#define CAL_WR_DMA_CTRL_MODE_CONST 4 -#define CAL_WR_DMA_CTRL_MODE_RESERVED 5 -#define CAL_WR_DMA_CTRL_PATTERN_MASK GENMASK(4, 3) -#define CAL_WR_DMA_CTRL_PATTERN_LINEAR 0 -#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP2 2 -#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP4 3 -#define CAL_WR_DMA_CTRL_PATTERN_RESERVED 1 -#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK BIT(5) -#define CAL_WR_DMA_CTRL_DTAG_MASK GENMASK(8, 6) -#define CAL_WR_DMA_CTRL_DTAG_ATT_HDR 0 -#define CAL_WR_DMA_CTRL_DTAG_ATT_DAT 1 -#define CAL_WR_DMA_CTRL_DTAG 2 -#define CAL_WR_DMA_CTRL_DTAG_PIX_HDR 3 -#define CAL_WR_DMA_CTRL_DTAG_PIX_DAT 4 -#define CAL_WR_DMA_CTRL_DTAG_D5 5 -#define CAL_WR_DMA_CTRL_DTAG_D6 6 -#define CAL_WR_DMA_CTRL_DTAG_D7 7 -#define CAL_WR_DMA_CTRL_CPORT_MASK GENMASK(13, 9) -#define CAL_WR_DMA_CTRL_STALL_RD_MASK BIT(14) -#define CAL_WR_DMA_CTRL_YSIZE_MASK GENMASK(31, 18) - -#define CAL_WR_DMA_ADDR_MASK GENMASK(31, 4) - -#define CAL_WR_DMA_OFST_MASK GENMASK(18, 4) -#define CAL_WR_DMA_OFST_CIRC_MODE_MASK GENMASK(23, 22) -#define CAL_WR_DMA_OFST_CIRC_MODE_ONE 1 -#define CAL_WR_DMA_OFST_CIRC_MODE_FOUR 2 -#define CAL_WR_DMA_OFST_CIRC_MODE_SIXTYFOUR 3 -#define CAL_WR_DMA_OFST_CIRC_MODE_DISABLED 0 -#define CAL_WR_DMA_OFST_CIRC_SIZE_MASK GENMASK(31, 24) - -#define CAL_WR_DMA_XSIZE_XSKIP_MASK GENMASK(15, 3) -#define CAL_WR_DMA_XSIZE_MASK GENMASK(31, 19) - -#define CAL_CSI2_PPI_CTRL_IF_EN_MASK BIT(0) -#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK BIT(2) -#define CAL_CSI2_PPI_CTRL_FRAME_MASK BIT(3) -#define CAL_CSI2_PPI_CTRL_FRAME_IMMEDIATE 0 -#define CAL_CSI2_PPI_CTRL_FRAME 1 - -#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK GENMASK(2, 0) -#define CAL_CSI2_COMPLEXIO_CFG_POSITION_5 5 -#define CAL_CSI2_COMPLEXIO_CFG_POSITION_4 4 -#define CAL_CSI2_COMPLEXIO_CFG_POSITION_3 3 -#define CAL_CSI2_COMPLEXIO_CFG_POSITION_2 2 -#define CAL_CSI2_COMPLEXIO_CFG_POSITION_1 1 -#define CAL_CSI2_COMPLEXIO_CFG_POSITION_NOT_USED 0 -#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK BIT(3) -#define CAL_CSI2_COMPLEXIO_CFG_POL_PLUSMINUS 0 -#define CAL_CSI2_COMPLEXIO_CFG_POL_MINUSPLUS 1 -#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_MASK GENMASK(6, 4) -#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK BIT(7) -#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_MASK GENMASK(10, 8) -#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK BIT(11) -#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_MASK GENMASK(14, 12) -#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK BIT(15) -#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_MASK GENMASK(18, 16) -#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK BIT(19) -#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK BIT(24) -#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK GENMASK(26, 25) -#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF 0 -#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON 1 -#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ULP 2 -#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK GENMASK(28, 27) -#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF 0 -#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON 1 -#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ULP 2 -#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK BIT(29) -#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED 1 -#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING 0 -#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK BIT(30) -#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL 0 -#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL 1 - -#define CAL_CSI2_SHORT_PACKET_MASK GENMASK(23, 0) - -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS1_MASK BIT(0) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS2_MASK BIT(1) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS3_MASK BIT(2) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS4_MASK BIT(3) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS5_MASK BIT(4) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1_MASK BIT(5) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2_MASK BIT(6) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3_MASK BIT(7) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4_MASK BIT(8) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5_MASK BIT(9) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC1_MASK BIT(10) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC2_MASK BIT(11) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC3_MASK BIT(12) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC4_MASK BIT(13) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC5_MASK BIT(14) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL1_MASK BIT(15) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL2_MASK BIT(16) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL3_MASK BIT(17) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL4_MASK BIT(18) -#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL5_MASK BIT(19) -#define CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK GENMASK(19, 0) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM1_MASK BIT(20) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM2_MASK BIT(21) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM3_MASK BIT(22) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM4_MASK BIT(23) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM5_MASK BIT(24) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER_MASK BIT(25) -#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT_MASK BIT(26) -#define CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK BIT(27) -#define CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK BIT(28) -#define CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK BIT(30) - -#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK GENMASK(12, 0) -#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK BIT(13) -#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT(14) -#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT(15) - -#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT(0) -#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT(1) -#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT(2) -#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT(3) -#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT(4) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT(5) -#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT(8) -#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT(9) -#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT(10) -#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT(11) -#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT(12) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT(13) -#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT(16) -#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT(17) -#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT(18) -#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT(19) -#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT(20) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT(21) -#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT(24) -#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT(25) -#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT(26) -#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT(27) -#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT(28) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT(29) - -#define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0) -#define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6) -#define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8) -#define CAL_CSI2_CTX_ATT_MASK BIT(13) -#define CAL_CSI2_CTX_ATT_PIX 0 -#define CAL_CSI2_CTX_ATT 1 -#define CAL_CSI2_CTX_PACK_MODE_MASK BIT(14) -#define CAL_CSI2_CTX_PACK_MODE_LINE 0 -#define CAL_CSI2_CTX_PACK_MODE_FRAME 1 -#define CAL_CSI2_CTX_LINES_MASK GENMASK(29, 16) - -#define CAL_CSI2_STATUS_FRAME_MASK GENMASK(15, 0) - -#define CAL_CSI2_PHY_REG0_THS_SETTLE_MASK GENMASK(7, 0) -#define CAL_CSI2_PHY_REG0_THS_TERM_MASK GENMASK(15, 8) -#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK BIT(24) -#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE 1 -#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_ENABLE 0 - -#define CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK GENMASK(7, 0) -#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK GENMASK(9, 8) -#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK GENMASK(17, 10) -#define CAL_CSI2_PHY_REG1_TCLK_TERM_MASK GENMASK(24, 18) -#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_MASK BIT(25) -#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_ERROR 1 -#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_SUCCESS 0 -#define CAL_CSI2_PHY_REG1_RESET_DONE_STATUS_MASK GENMASK(29, 28) - -#define CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK BIT(6) - -#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK GENMASK(23, 0) -#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK GENMASK(25, 24) -#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK GENMASK(27, 26) -#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK GENMASK(29, 28) -#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK GENMASK(31, 30) - -#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK BIT(0) -#define CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK GENMASK(2, 1) -#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK GENMASK(4, 3) -#define CM_CAMERRX_CTRL_CSI1_MODE_MASK BIT(5) -#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK BIT(10) -#define CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK GENMASK(12, 11) -#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK GENMASK(16, 13) -#define CM_CAMERRX_CTRL_CSI0_MODE_MASK BIT(17) - -#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c --- a/drivers/media/platform/ti-vpe/cal-video.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/cal-video.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,886 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * TI Camera Access Layer (CAL) - Video Device - * - * Copyright (c) 2015-2020 Texas Instruments Inc. - * - * Authors: - * Benoit Parrot - * Laurent Pinchart - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cal.h" - -/* ------------------------------------------------------------------ - * Format Handling - * ------------------------------------------------------------------ - */ - -static const struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - -/* Print Four-character-code (FOURCC) */ -static char *fourcc_to_str(u32 fmt) -{ - static char code[5]; - - code[0] = (unsigned char)(fmt & 0xff); - code[1] = (unsigned char)((fmt >> 8) & 0xff); - code[2] = (unsigned char)((fmt >> 16) & 0xff); - code[3] = (unsigned char)((fmt >> 24) & 0xff); - code[4] = '\0'; - - return code; -} - -/* ------------------------------------------------------------------ - * V4L2 Video IOCTLs - * ------------------------------------------------------------------ - */ - -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } - - return NULL; -} - -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } - - return NULL; -} - -static int cal_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cal_ctx *ctx = video_drvdata(file); - - strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(ctx->cal->dev)); - return 0; -} - -static int cal_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - - if (f->index >= ctx->num_active_fmt) - return -EINVAL; - - fmt = ctx->active_fmt[f->index]; - - f->pixelformat = fmt->fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; -} - -static int __subdev_get_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - *fmt = *mbus_fmt; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - *mbus_fmt = *fmt; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, - struct v4l2_format *f) -{ - u32 bpl, max_width; - - if (!fmt) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - - /* - * Maximum width is bound by the DMA max width in bytes. - * We need to recalculate the actual maxi width depending on the - * number of bytes per pixels required. - */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); - v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); - - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; - f->fmt.pix.bytesperline = ALIGN(bpl, 16); - - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - - ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", - __func__, fourcc_to_str(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int cal_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *f = ctx->v_fmt; - - return 0; -} - -static int cal_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret, found; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { - ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", - f->fmt.pix.pixelformat); - - /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; - - /* check for/find a valid width/height */ - ret = 0; - found = false; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, - NULL, &fse); - if (ret) - break; - - if ((f->fmt.pix.width == fse.max_width) && - (f->fmt.pix.height == fse.max_height)) { - found = true; - break; - } else if ((f->fmt.pix.width >= fse.min_width) && - (f->fmt.pix.width <= fse.max_width) && - (f->fmt.pix.height >= fse.min_height) && - (f->fmt.pix.height <= fse.max_height)) { - found = true; - break; - } - } - - if (!found) { - /* use existing values as default */ - f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; - f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; - } - - /* - * Use current colorspace for now, it will get - * updated properly during s_fmt - */ - f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); -} - -static int cal_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - struct vb2_queue *q = &ctx->vb_vidq; - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (vb2_is_busy(q)) { - ctx_dbg(3, ctx, "%s device busy\n", __func__); - return -EBUSY; - } - - ret = cal_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(ctx, &mbus_fmt); - if (ret) - return ret; - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - ctx_dbg(3, ctx, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - *f = ctx->v_fmt; - - return 0; -} - -static int cal_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; - - /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { - ctx_dbg(3, ctx, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; - } - - fse.index = fsize->index; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, - &fse); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int cal_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index > 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - sprintf(inp->name, "Camera %u", inp->index); - return 0; -} - -static int cal_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int cal_s_input(struct file *file, void *priv, unsigned int i) -{ - return i > 0 ? -EINVAL : 0; -} - -/* timeperframe is arbitrary and continuous */ -static int cal_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; - - return 0; -} - -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* ------------------------------------------------------------------ - * videobuf2 Operations - * ------------------------------------------------------------------ - */ - -static int cal_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); - - return 0; -} - -static int cal_buffer_prepare(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - unsigned long size; - - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - - size = ctx->v_fmt.fmt.pix.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - ctx_err(ctx, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); - return 0; -} - -static void cal_buffer_queue(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags; - - /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); -} - -static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long addr; - unsigned long flags; - int ret; - - spin_lock_irqsave(&ctx->slock, flags); - if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; - } - - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); - - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); - ctx->sequence = 0; - - pm_runtime_get_sync(ctx->cal->dev); - - cal_ctx_csi2_config(ctx); - cal_ctx_pix_proc_config(ctx); - cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); - - cal_camerarx_enable_irqs(ctx->phy); - - ret = cal_camerarx_start(ctx->phy, ctx->fmt); - if (ret) - goto err; - - cal_ctx_wr_dma_addr(ctx, addr); - cal_camerarx_ppi_enable(ctx->phy); - - if (cal_debug >= 4) - cal_quickdump_regs(ctx->cal); - - return 0; - -err: - spin_lock_irqsave(&ctx->slock, flags); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&ctx->slock, flags); - return ret; -} - -static void cal_stop_streaming(struct vb2_queue *vq) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long timeout; - unsigned long flags; - bool dma_act; - - cal_camerarx_ppi_disable(ctx->phy); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); - - cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_stop(ctx->phy); - - /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); - - pm_runtime_put_sync(ctx->cal->dev); -} - -static const struct vb2_ops cal_video_qops = { - .queue_setup = cal_queue_setup, - .buf_prepare = cal_buffer_prepare, - .buf_queue = cal_buffer_queue, - .start_streaming = cal_start_streaming, - .stop_streaming = cal_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/* ------------------------------------------------------------------ - * V4L2 Initialization and Registration - * ------------------------------------------------------------------ - */ - -static const struct video_device cal_videodev = { - .name = CAL_MODULE_NAME, - .fops = &cal_fops, - .ioctl_ops = &cal_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, -}; - -static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) -{ - struct v4l2_subdev_mbus_code_enum mbus_code; - struct v4l2_mbus_framefmt mbus_fmt; - const struct cal_fmt *fmt; - unsigned int i, j, k; - int ret = 0; - - /* Enumerate sub device formats and enable all matching local formats */ - ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), - sizeof(*ctx->active_fmt), GFP_KERNEL); - ctx->num_active_fmt = 0; - - for (j = 0, i = 0; ret != -EINVAL; ++j) { - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %u\n", - ctx->phy->sensor->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - const struct cal_fmt *fmt = &cal_formats[k]; - - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } - } - - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - ctx->phy->sensor->name); - return -EINVAL; - } - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; - - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; - } - - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - - return 0; -} - -int cal_ctx_v4l2_register(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - int ret; - - ret = cal_ctx_v4l2_init_formats(ctx); - if (ret) - return ret; - - ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, - true); - if (ret < 0) { - ctx_err(ctx, "Failed to add sensor ctrl handler\n"); - return ret; - } - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); - if (ret < 0) { - ctx_err(ctx, "Failed to register video device\n"); - return ret; - } - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - -void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) -{ - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - - video_unregister_device(&ctx->vdev); -} - -int cal_ctx_v4l2_init(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - struct vb2_queue *q = &ctx->vb_vidq; - int ret; - - INIT_LIST_HEAD(&ctx->vidq.active); - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* Initialize the vb2 queue. */ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->cal->dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* Initialize the video device and media entity. */ - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->cal->v4l2_dev; - vfd->queue = q; - snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ctx->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); - if (ret < 0) - return ret; - - /* Initialize the control handler. */ - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret < 0) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto error; - } - - vfd->ctrl_handler = hdl; - - return 0; - -error: - media_entity_cleanup(&vfd->entity); - return ret; -} - -void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) -{ - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - media_entity_cleanup(&ctx->vdev.entity); -} diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c --- a/drivers/media/platform/ti-vpe/csc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/csc.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,281 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Color space converter library - * - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "csc.h" - -/* - * 12 coefficients in the order: - * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2 - */ -struct quantization { - u16 coeff[12]; -}; - -struct colorspace { - struct quantization limited; - struct quantization full; -}; - -struct encoding_direction { - struct colorspace r601; - struct colorspace r709; -}; - -struct csc_coeffs { - struct encoding_direction y2r; - struct encoding_direction r2y; -}; - -/* default colorspace coefficients */ -static struct csc_coeffs csc_coeffs = { - .y2r = { - .r601 = { - .limited = { - { /* SDTV */ - 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, - 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, - } - }, - .full = { - { /* SDTV */ - 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, - 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, - } - }, - }, - .r709 = { - .limited = { - { /* HDTV */ - 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, - 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, - } - }, - .full = { - { /* HDTV */ - 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, - 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, - } - }, - }, - }, - .r2y = { - .r601 = { - .limited = { - { /* SDTV */ - 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, - 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, - } - }, - .full = { - { /* SDTV */ - 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, - 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, - } - }, - }, - .r709 = { - .limited = { - { /* HDTV */ - 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, - 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, - } - }, - .full = { - { /* HDTV */ - 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2, - 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200, - } - }, - }, - }, - -}; - -void csc_dump_regs(struct csc_data *csc) -{ - struct device *dev = &csc->pdev->dev; - -#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \ - ioread32(csc->base + CSC_##r)) - - dev_dbg(dev, "CSC Registers @ %pa:\n", &csc->res->start); - - DUMPREG(CSC00); - DUMPREG(CSC01); - DUMPREG(CSC02); - DUMPREG(CSC03); - DUMPREG(CSC04); - DUMPREG(CSC05); - -#undef DUMPREG -} -EXPORT_SYMBOL(csc_dump_regs); - -void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5) -{ - *csc_reg5 |= CSC_BYPASS; -} -EXPORT_SYMBOL(csc_set_coeff_bypass); - -/* - * set the color space converter coefficient shadow register values - */ -void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, - struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt) -{ - u32 *csc_reg5 = csc_reg0 + 5; - u32 *shadow_csc = csc_reg0; - u16 *coeff, *end_coeff; - const struct v4l2_pix_format *pix; - const struct v4l2_pix_format_mplane *mp; - const struct v4l2_format_info *src_finfo, *dst_finfo; - enum v4l2_ycbcr_encoding src_ycbcr_enc, dst_ycbcr_enc; - enum v4l2_quantization src_quantization, dst_quantization; - u32 src_pixelformat, dst_pixelformat; - - if (V4L2_TYPE_IS_MULTIPLANAR(src_fmt->type)) { - mp = &src_fmt->fmt.pix_mp; - src_pixelformat = mp->pixelformat; - src_ycbcr_enc = mp->ycbcr_enc; - src_quantization = mp->quantization; - } else { - pix = &src_fmt->fmt.pix; - src_pixelformat = pix->pixelformat; - src_ycbcr_enc = pix->ycbcr_enc; - src_quantization = pix->quantization; - } - - if (V4L2_TYPE_IS_MULTIPLANAR(dst_fmt->type)) { - mp = &dst_fmt->fmt.pix_mp; - dst_pixelformat = mp->pixelformat; - dst_ycbcr_enc = mp->ycbcr_enc; - dst_quantization = mp->quantization; - } else { - pix = &dst_fmt->fmt.pix; - dst_pixelformat = pix->pixelformat; - dst_ycbcr_enc = pix->ycbcr_enc; - dst_quantization = pix->quantization; - } - - src_finfo = v4l2_format_info(src_pixelformat); - dst_finfo = v4l2_format_info(dst_pixelformat); - - if (v4l2_is_format_yuv(src_finfo) && - v4l2_is_format_rgb(dst_finfo)) { - /* Y2R */ - - /* - * These are not the standard default values but are - * set this way for historical compatibility - */ - if (src_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) - src_ycbcr_enc = V4L2_YCBCR_ENC_601; - - if (src_quantization == V4L2_QUANTIZATION_DEFAULT) - src_quantization = V4L2_QUANTIZATION_FULL_RANGE; - - if (src_ycbcr_enc == V4L2_YCBCR_ENC_601) { - if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) - coeff = csc_coeffs.y2r.r601.full.coeff; - else - coeff = csc_coeffs.y2r.r601.limited.coeff; - } else if (src_ycbcr_enc == V4L2_YCBCR_ENC_709) { - if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) - coeff = csc_coeffs.y2r.r709.full.coeff; - else - coeff = csc_coeffs.y2r.r709.limited.coeff; - } else { - /* Should never reach this, but it keeps gcc happy */ - coeff = csc_coeffs.y2r.r601.full.coeff; - } - } else if (v4l2_is_format_rgb(src_finfo) && - v4l2_is_format_yuv(dst_finfo)) { - /* R2Y */ - - /* - * These are not the standard default values but are - * set this way for historical compatibility - */ - if (dst_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) - dst_ycbcr_enc = V4L2_YCBCR_ENC_601; - - if (dst_quantization == V4L2_QUANTIZATION_DEFAULT) - dst_quantization = V4L2_QUANTIZATION_FULL_RANGE; - - if (dst_ycbcr_enc == V4L2_YCBCR_ENC_601) { - if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) - coeff = csc_coeffs.r2y.r601.full.coeff; - else - coeff = csc_coeffs.r2y.r601.limited.coeff; - } else if (dst_ycbcr_enc == V4L2_YCBCR_ENC_709) { - if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) - coeff = csc_coeffs.r2y.r709.full.coeff; - else - coeff = csc_coeffs.r2y.r709.limited.coeff; - } else { - /* Should never reach this, but it keeps gcc happy */ - coeff = csc_coeffs.r2y.r601.full.coeff; - } - } else { - *csc_reg5 |= CSC_BYPASS; - return; - } - - end_coeff = coeff + 12; - - for (; coeff < end_coeff; coeff += 2) - *shadow_csc++ = (*(coeff + 1) << 16) | *coeff; -} -EXPORT_SYMBOL(csc_set_coeff); - -struct csc_data *csc_create(struct platform_device *pdev, const char *res_name) -{ - struct csc_data *csc; - - dev_dbg(&pdev->dev, "csc_create\n"); - - csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL); - if (!csc) { - dev_err(&pdev->dev, "couldn't alloc csc_data\n"); - return ERR_PTR(-ENOMEM); - } - - csc->pdev = pdev; - - csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - res_name); - if (csc->res == NULL) { - dev_err(&pdev->dev, "missing '%s' platform resources data\n", - res_name); - return ERR_PTR(-ENODEV); - } - - csc->base = devm_ioremap_resource(&pdev->dev, csc->res); - if (IS_ERR(csc->base)) { - dev_err(&pdev->dev, "failed to ioremap\n"); - return ERR_CAST(csc->base); - } - - return csc; -} -EXPORT_SYMBOL(csc_create); - -MODULE_DESCRIPTION("TI VIP/VPE Color Space Converter"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h --- a/drivers/media/platform/ti-vpe/csc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/csc.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ -#ifndef TI_CSC_H -#define TI_CSC_H - -/* VPE color space converter regs */ -#define CSC_CSC00 0x00 -#define CSC_A0_MASK 0x1fff -#define CSC_A0_SHIFT 0 -#define CSC_B0_MASK 0x1fff -#define CSC_B0_SHIFT 16 - -#define CSC_CSC01 0x04 -#define CSC_C0_MASK 0x1fff -#define CSC_C0_SHIFT 0 -#define CSC_A1_MASK 0x1fff -#define CSC_A1_SHIFT 16 - -#define CSC_CSC02 0x08 -#define CSC_B1_MASK 0x1fff -#define CSC_B1_SHIFT 0 -#define CSC_C1_MASK 0x1fff -#define CSC_C1_SHIFT 16 - -#define CSC_CSC03 0x0c -#define CSC_A2_MASK 0x1fff -#define CSC_A2_SHIFT 0 -#define CSC_B2_MASK 0x1fff -#define CSC_B2_SHIFT 16 - -#define CSC_CSC04 0x10 -#define CSC_C2_MASK 0x1fff -#define CSC_C2_SHIFT 0 -#define CSC_D0_MASK 0x0fff -#define CSC_D0_SHIFT 16 - -#define CSC_CSC05 0x14 -#define CSC_D1_MASK 0x0fff -#define CSC_D1_SHIFT 0 -#define CSC_D2_MASK 0x0fff -#define CSC_D2_SHIFT 16 - -#define CSC_BYPASS (1 << 28) - -struct csc_data { - void __iomem *base; - struct resource *res; - - struct platform_device *pdev; -}; - -void csc_dump_regs(struct csc_data *csc); -void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5); -void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, - struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt); - -struct csc_data *csc_create(struct platform_device *pdev, const char *res_name); - -#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile --- a/drivers/media/platform/ti-vpe/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/Makefile 1969-12-31 19:00:00.000000000 -0500 @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o -obj-$(CONFIG_VIDEO_TI_VPDMA) += ti-vpdma.o -obj-$(CONFIG_VIDEO_TI_SC) += ti-sc.o -obj-$(CONFIG_VIDEO_TI_CSC) += ti-csc.o - -ti-vpe-y := vpe.o -ti-vpdma-y := vpdma.o -ti-sc-y := sc.o -ti-csc-y := csc.o - -ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG - -obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o - -ti-cal-y := cal.o cal-camerarx.o cal-video.o diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c --- a/drivers/media/platform/ti-vpe/sc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/sc.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,308 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Scaler library - * - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#include -#include -#include -#include -#include - -#include "sc.h" -#include "sc_coeff.h" - -void sc_dump_regs(struct sc_data *sc) -{ - struct device *dev = &sc->pdev->dev; - -#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \ - ioread32(sc->base + CFG_##r)) - - dev_dbg(dev, "SC Registers @ %pa:\n", &sc->res->start); - - DUMPREG(SC0); - DUMPREG(SC1); - DUMPREG(SC2); - DUMPREG(SC3); - DUMPREG(SC4); - DUMPREG(SC5); - DUMPREG(SC6); - DUMPREG(SC8); - DUMPREG(SC9); - DUMPREG(SC10); - DUMPREG(SC11); - DUMPREG(SC12); - DUMPREG(SC13); - DUMPREG(SC17); - DUMPREG(SC18); - DUMPREG(SC19); - DUMPREG(SC20); - DUMPREG(SC21); - DUMPREG(SC22); - DUMPREG(SC23); - DUMPREG(SC24); - DUMPREG(SC25); - -#undef DUMPREG -} -EXPORT_SYMBOL(sc_dump_regs); - -/* - * set the horizontal scaler coefficients according to the ratio of output to - * input widths, after accounting for up to two levels of decimation - */ -void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, - unsigned int dst_w) -{ - int sixteenths; - int idx; - int i, j; - u16 *coeff_h = addr; - const u16 *cp; - - if (dst_w > src_w) { - idx = HS_UP_SCALE; - } else { - if ((dst_w << 1) < src_w) - dst_w <<= 1; /* first level decimation */ - if ((dst_w << 1) < src_w) - dst_w <<= 1; /* second level decimation */ - - if (dst_w == src_w) { - idx = HS_LE_16_16_SCALE; - } else { - sixteenths = (dst_w << 4) / src_w; - if (sixteenths < 8) - sixteenths = 8; - idx = HS_LT_9_16_SCALE + sixteenths - 8; - } - } - - cp = scaler_hs_coeffs[idx]; - - for (i = 0; i < SC_NUM_PHASES * 2; i++) { - for (j = 0; j < SC_H_NUM_TAPS; j++) - *coeff_h++ = *cp++; - /* - * for each phase, the scaler expects space for 8 coefficients - * in it's memory. For the horizontal scaler, we copy the first - * 7 coefficients and skip the last slot to move to the next - * row to hold coefficients for the next phase - */ - coeff_h += SC_NUM_TAPS_MEM_ALIGN - SC_H_NUM_TAPS; - } - - sc->load_coeff_h = true; -} -EXPORT_SYMBOL(sc_set_hs_coeffs); - -/* - * set the vertical scaler coefficients according to the ratio of output to - * input heights - */ -void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, - unsigned int dst_h) -{ - int sixteenths; - int idx; - int i, j; - u16 *coeff_v = addr; - const u16 *cp; - - if (dst_h > src_h) { - idx = VS_UP_SCALE; - } else if (dst_h == src_h) { - idx = VS_1_TO_1_SCALE; - } else { - sixteenths = (dst_h << 4) / src_h; - if (sixteenths < 8) - sixteenths = 8; - idx = VS_LT_9_16_SCALE + sixteenths - 8; - } - - cp = scaler_vs_coeffs[idx]; - - for (i = 0; i < SC_NUM_PHASES * 2; i++) { - for (j = 0; j < SC_V_NUM_TAPS; j++) - *coeff_v++ = *cp++; - /* - * for the vertical scaler, we copy the first 5 coefficients and - * skip the last 3 slots to move to the next row to hold - * coefficients for the next phase - */ - coeff_v += SC_NUM_TAPS_MEM_ALIGN - SC_V_NUM_TAPS; - } - - sc->load_coeff_v = true; -} -EXPORT_SYMBOL(sc_set_vs_coeffs); - -void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, - u32 *sc_reg17, unsigned int src_w, unsigned int src_h, - unsigned int dst_w, unsigned int dst_h) -{ - struct device *dev = &sc->pdev->dev; - u32 val; - int dcm_x, dcm_shift; - bool use_rav; - unsigned long lltmp; - u32 lin_acc_inc, lin_acc_inc_u; - u32 col_acc_offset; - u16 factor = 0; - int row_acc_init_rav = 0, row_acc_init_rav_b = 0; - u32 row_acc_inc = 0, row_acc_offset = 0, row_acc_offset_b = 0; - /* - * location of SC register in payload memory with respect to the first - * register in the mmr address data block - */ - u32 *sc_reg9 = sc_reg8 + 1; - u32 *sc_reg12 = sc_reg8 + 4; - u32 *sc_reg13 = sc_reg8 + 5; - u32 *sc_reg24 = sc_reg17 + 7; - - val = sc_reg0[0]; - - /* clear all the features(they may get enabled elsewhere later) */ - val &= ~(CFG_SELFGEN_FID | CFG_TRIM | CFG_ENABLE_SIN2_VER_INTP | - CFG_INTERLACE_I | CFG_DCM_4X | CFG_DCM_2X | CFG_AUTO_HS | - CFG_ENABLE_EV | CFG_USE_RAV | CFG_INVT_FID | CFG_SC_BYPASS | - CFG_INTERLACE_O | CFG_Y_PK_EN | CFG_HP_BYPASS | CFG_LINEAR); - - if (src_w == dst_w && src_h == dst_h) { - val |= CFG_SC_BYPASS; - sc_reg0[0] = val; - return; - } - - /* we only support linear scaling for now */ - val |= CFG_LINEAR; - - /* configure horizontal scaler */ - - /* enable 2X or 4X decimation */ - dcm_x = src_w / dst_w; - if (dcm_x > 4) { - val |= CFG_DCM_4X; - dcm_shift = 2; - } else if (dcm_x > 2) { - val |= CFG_DCM_2X; - dcm_shift = 1; - } else { - dcm_shift = 0; - } - - lltmp = dst_w - 1; - lin_acc_inc = div64_u64(((u64)(src_w >> dcm_shift) - 1) << 24, lltmp); - lin_acc_inc_u = 0; - col_acc_offset = 0; - - dev_dbg(dev, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n", - src_w, dst_w, dcm_shift == 2 ? "4x" : - (dcm_shift == 1 ? "2x" : "none"), lin_acc_inc); - - /* configure vertical scaler */ - - /* use RAV for vertical scaler if vertical downscaling is > 4x */ - if (dst_h < (src_h >> 2)) { - use_rav = true; - val |= CFG_USE_RAV; - } else { - use_rav = false; - } - - if (use_rav) { - /* use RAV */ - factor = (u16) ((dst_h << 10) / src_h); - - row_acc_init_rav = factor + ((1 + factor) >> 1); - if (row_acc_init_rav >= 1024) - row_acc_init_rav -= 1024; - - row_acc_init_rav_b = row_acc_init_rav + - (1 + (row_acc_init_rav >> 1)) - - (1024 >> 1); - - if (row_acc_init_rav_b < 0) { - row_acc_init_rav_b += row_acc_init_rav; - row_acc_init_rav *= 2; - } - - dev_dbg(dev, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n", - src_h, dst_h, factor, row_acc_init_rav, - row_acc_init_rav_b); - } else { - /* use polyphase */ - row_acc_inc = ((src_h - 1) << 16) / (dst_h - 1); - row_acc_offset = 0; - row_acc_offset_b = 0; - - dev_dbg(dev, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n", - src_h, dst_h, row_acc_inc); - } - - - sc_reg0[0] = val; - sc_reg0[1] = row_acc_inc; - sc_reg0[2] = row_acc_offset; - sc_reg0[3] = row_acc_offset_b; - - sc_reg0[4] = ((lin_acc_inc_u & CFG_LIN_ACC_INC_U_MASK) << - CFG_LIN_ACC_INC_U_SHIFT) | (dst_w << CFG_TAR_W_SHIFT) | - (dst_h << CFG_TAR_H_SHIFT); - - sc_reg0[5] = (src_w << CFG_SRC_W_SHIFT) | (src_h << CFG_SRC_H_SHIFT); - - sc_reg0[6] = (row_acc_init_rav_b << CFG_ROW_ACC_INIT_RAV_B_SHIFT) | - (row_acc_init_rav << CFG_ROW_ACC_INIT_RAV_SHIFT); - - *sc_reg9 = lin_acc_inc; - - *sc_reg12 = col_acc_offset << CFG_COL_ACC_OFFSET_SHIFT; - - *sc_reg13 = factor; - - *sc_reg24 = (src_w << CFG_ORG_W_SHIFT) | (src_h << CFG_ORG_H_SHIFT); -} -EXPORT_SYMBOL(sc_config_scaler); - -struct sc_data *sc_create(struct platform_device *pdev, const char *res_name) -{ - struct sc_data *sc; - - dev_dbg(&pdev->dev, "sc_create\n"); - - sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL); - if (!sc) { - dev_err(&pdev->dev, "couldn't alloc sc_data\n"); - return ERR_PTR(-ENOMEM); - } - - sc->pdev = pdev; - - sc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); - if (!sc->res) { - dev_err(&pdev->dev, "missing '%s' platform resources data\n", - res_name); - return ERR_PTR(-ENODEV); - } - - sc->base = devm_ioremap_resource(&pdev->dev, sc->res); - if (IS_ERR(sc->base)) { - dev_err(&pdev->dev, "failed to ioremap\n"); - return ERR_CAST(sc->base); - } - - return sc; -} -EXPORT_SYMBOL(sc_create); - -MODULE_DESCRIPTION("TI VIP/VPE Scaler"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/sc_coeff.h b/drivers/media/platform/ti-vpe/sc_coeff.h --- a/drivers/media/platform/ti-vpe/sc_coeff.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/sc_coeff.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,1339 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * VPE SC coefs - * - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#ifndef __TI_SC_COEFF_H -#define __TI_SC_COEFF_H - -/* horizontal scaler coefficients */ -enum { - HS_UP_SCALE = 0, - HS_LT_9_16_SCALE, - HS_LT_10_16_SCALE, - HS_LT_11_16_SCALE, - HS_LT_12_16_SCALE, - HS_LT_13_16_SCALE, - HS_LT_14_16_SCALE, - HS_LT_15_16_SCALE, - HS_LE_16_16_SCALE, -}; - -static const u16 scaler_hs_coeffs[13][SC_NUM_PHASES * 2 * SC_H_NUM_TAPS] = { - [HS_UP_SCALE] = { - /* Luma */ - 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, - 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, - 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, - 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, - 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, - 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, - 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, - 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, - 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, - 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, - 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, - 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, - 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, - 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, - 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, - 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, - 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, - 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, - 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, - 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, - 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, - 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, - 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, - 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, - 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, - 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, - 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, - 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, - 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, - 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, - 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, - 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, - /* Chroma */ - 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, - 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, - 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, - 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, - 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, - 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, - 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, - 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, - 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, - 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, - 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, - 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, - 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, - 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, - 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, - 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, - 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, - 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, - 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, - 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, - 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, - 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, - 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, - 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, - 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, - 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, - 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, - 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, - 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, - 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, - 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, - 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, - }, - [HS_LT_9_16_SCALE] = { - /* Luma */ - 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, - 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, - 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, - 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, - 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, - 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, - 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, - 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, - 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, - 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, - 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, - 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, - 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, - 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, - 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, - 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, - 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, - 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, - 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, - 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, - 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, - 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, - 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, - 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, - 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, - 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, - 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, - 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, - 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, - 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, - 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, - 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, - /* Chroma */ - 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, - 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, - 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, - 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, - 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, - 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, - 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, - 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, - 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, - 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, - 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, - 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, - 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, - 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, - 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, - 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, - 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, - 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, - 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, - 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, - 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, - 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, - 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, - 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, - 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, - 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, - 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, - 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, - 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, - 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, - 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, - 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, - }, - [HS_LT_10_16_SCALE] = { - /* Luma */ - 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, - 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, - 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, - 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, - 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, - 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, - 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, - 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, - 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, - 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, - 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, - 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, - 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, - 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, - 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, - 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, - 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, - 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, - 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, - 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, - 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, - 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, - 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, - 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, - 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, - 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, - 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, - 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, - 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, - 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, - 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, - 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, - /* Chroma */ - 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, - 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, - 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, - 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, - 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, - 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, - 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, - 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, - 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, - 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, - 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, - 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, - 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, - 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, - 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, - 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, - 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, - 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, - 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, - 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, - 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, - 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, - 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, - 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, - 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, - 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, - 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, - 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, - 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, - 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, - 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, - 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, - }, - [HS_LT_11_16_SCALE] = { - /* Luma */ - 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, - 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, - 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, - 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, - 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, - 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, - 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, - 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, - 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, - 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, - 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, - 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, - 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, - 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, - 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, - 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, - 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, - 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, - 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, - 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, - 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, - 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, - 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, - 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, - 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, - 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, - 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, - 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, - 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, - 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, - 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, - 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, - /* Chroma */ - 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, - 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, - 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, - 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, - 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, - 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, - 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, - 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, - 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, - 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, - 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, - 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, - 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, - 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, - 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, - 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, - 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, - 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, - 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, - 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, - 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, - 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, - 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, - 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, - 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, - 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, - 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, - 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, - 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, - 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, - 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, - 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, - }, - [HS_LT_12_16_SCALE] = { - /* Luma */ - 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, - 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, - 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, - 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, - 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, - 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, - 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, - 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, - 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, - 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, - 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, - 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, - 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, - 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, - 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, - 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, - 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, - 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, - 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, - 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, - 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, - 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, - 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, - 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, - 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, - 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, - 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, - 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, - 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, - 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, - 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, - 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, - /* Chroma */ - 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, - 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, - 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, - 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, - 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, - 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, - 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, - 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, - 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, - 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, - 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, - 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, - 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, - 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, - 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, - 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, - 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, - 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, - 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, - 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, - 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, - 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, - 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, - 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, - 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, - 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, - 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, - 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, - 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, - 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, - 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, - 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, - }, - [HS_LT_13_16_SCALE] = { - /* Luma */ - 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, - 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, - 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, - 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, - 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, - 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, - 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, - 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, - 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, - 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, - 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, - 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, - 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, - 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, - 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, - 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, - 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, - 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, - 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, - 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, - 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, - 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, - 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, - 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, - 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, - 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, - 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, - 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, - 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, - 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, - 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, - 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, - /* Chroma */ - 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, - 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, - 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, - 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, - 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, - 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, - 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, - 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, - 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, - 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, - 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, - 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, - 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, - 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, - 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, - 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, - 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, - 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, - 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, - 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, - 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, - 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, - 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, - 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, - 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, - 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, - 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, - 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, - 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, - 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, - 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, - 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, - }, - [HS_LT_14_16_SCALE] = { - /* Luma */ - 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, - 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, - 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, - 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, - 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, - 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, - 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, - 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, - 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, - 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, - 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, - 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, - 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, - 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, - 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, - 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, - 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, - 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, - 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, - 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, - 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, - 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, - 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, - 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, - 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, - 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, - 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, - 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, - 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, - 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, - 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, - 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, - /* Chroma */ - 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, - 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, - 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, - 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, - 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, - 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, - 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, - 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, - 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, - 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, - 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, - 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, - 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, - 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, - 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, - 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, - 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, - 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, - 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, - 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, - 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, - 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, - 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, - 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, - 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, - 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, - 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, - 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, - 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, - 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, - 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, - 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, - }, - [HS_LT_15_16_SCALE] = { - /* Luma */ - 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, - 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, - 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, - 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, - 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, - 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, - 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, - 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, - 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, - 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, - 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, - 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, - 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, - 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, - 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, - 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, - 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, - 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, - 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, - 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, - 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, - 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, - 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, - 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, - 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, - 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, - 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, - 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, - 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, - 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, - 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, - 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, - /* Chroma */ - 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, - 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, - 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, - 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, - 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, - 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, - 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, - 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, - 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, - 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, - 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, - 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, - 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, - 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, - 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, - 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, - 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, - 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, - 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, - 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, - 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, - 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, - 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, - 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, - 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, - 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, - 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, - 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, - 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, - 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, - 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, - 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, - }, - [HS_LE_16_16_SCALE] = { - /* Luma */ - 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, - 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, - 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, - 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, - 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, - 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, - 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, - 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, - 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, - 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, - 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, - 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, - 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, - 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, - 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, - 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, - 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, - 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, - 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, - 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, - 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, - 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, - 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, - 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, - 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, - 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, - 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, - 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, - 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, - 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, - 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, - 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, - /* Chroma */ - 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, - 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, - 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, - 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, - 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, - 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, - 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, - 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, - 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, - 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, - 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, - 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, - 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, - 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, - 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, - 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, - 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, - 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, - 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, - 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, - 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, - 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, - 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, - 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, - 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, - 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, - 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, - 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, - 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, - 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, - 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, - 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, - }, -}; - -/* vertical scaler coefficients */ -enum { - VS_UP_SCALE = 0, - VS_LT_9_16_SCALE, - VS_LT_10_16_SCALE, - VS_LT_11_16_SCALE, - VS_LT_12_16_SCALE, - VS_LT_13_16_SCALE, - VS_LT_14_16_SCALE, - VS_LT_15_16_SCALE, - VS_LT_16_16_SCALE, - VS_1_TO_1_SCALE, -}; - -static const u16 scaler_vs_coeffs[15][SC_NUM_PHASES * 2 * SC_V_NUM_TAPS] = { - [VS_UP_SCALE] = { - /* Luma */ - 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, - 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, - 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, - 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, - 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, - 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, - 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, - 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, - 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, - 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, - 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, - 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, - 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, - 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, - 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, - 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, - 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, - 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, - 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, - 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, - 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, - 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, - 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, - 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, - 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, - 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, - 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, - 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, - 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, - 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, - 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, - 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, - /* Chroma */ - 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, - 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, - 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, - 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, - 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, - 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, - 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, - 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, - 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, - 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, - 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, - 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, - 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, - 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, - 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, - 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, - 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, - 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, - 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, - 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, - 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, - 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, - 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, - 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, - 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, - 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, - 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, - 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, - 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, - 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, - 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, - 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, - }, - [VS_LT_9_16_SCALE] = { - /* Luma */ - 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, - 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, - 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, - 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, - 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, - 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, - 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, - 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, - 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, - 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, - 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, - 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, - 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, - 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, - 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, - 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, - 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, - 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, - 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, - 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, - 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, - 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, - 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, - 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, - 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, - 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, - 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, - 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, - 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, - 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, - 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, - 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, - /* Chroma */ - 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, - 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, - 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, - 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, - 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, - 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, - 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, - 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, - 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, - 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, - 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, - 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, - 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, - 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, - 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, - 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, - 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, - 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, - 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, - 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, - 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, - 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, - 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, - 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, - 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, - 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, - 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, - 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, - 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, - 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, - 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, - 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, - }, - [VS_LT_10_16_SCALE] = { - /* Luma */ - 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, - 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, - 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, - 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, - 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, - 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, - 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, - 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, - 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, - 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, - 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, - 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, - 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, - 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, - 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, - 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, - 0x0094, 0x036C, 0x036C, 0x0094, 0x0000, - 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, - 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, - 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, - 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, - 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, - 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, - 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, - 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, - 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, - 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, - 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, - 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, - 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, - 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, - 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, - /* Chroma */ - 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, - 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, - 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, - 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, - 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, - 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, - 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, - 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, - 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, - 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, - 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, - 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, - 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, - 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, - 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, - 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, - 0x0094, 0x036C, 0x036C, 0x0094, 0x0000, - 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, - 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, - 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, - 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, - 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, - 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, - 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, - 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, - 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, - 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, - 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, - 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, - 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, - 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, - 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, - }, - [VS_LT_11_16_SCALE] = { - /* Luma */ - 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, - 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, - 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, - 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, - 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, - 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, - 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, - 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, - 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, - 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, - 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, - 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, - 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, - 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, - 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, - 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, - 0x0069, 0x0397, 0x0397, 0x0069, 0x0000, - 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, - 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, - 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, - 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, - 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, - 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, - 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, - 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, - 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, - 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, - 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, - 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, - 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, - 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, - 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, - /* Chroma */ - 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, - 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, - 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, - 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, - 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, - 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, - 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, - 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, - 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, - 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, - 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, - 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, - 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, - 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, - 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, - 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, - 0x0069, 0x0397, 0x0397, 0x0069, 0x0000, - 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, - 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, - 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, - 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, - 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, - 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, - 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, - 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, - 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, - 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, - 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, - 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, - 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, - 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, - 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, - }, - [VS_LT_12_16_SCALE] = { - /* Luma */ - 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, - 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, - 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, - 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, - 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, - 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, - 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, - 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, - 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, - 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, - 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, - 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, - 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, - 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, - 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, - 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, - 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, - 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, - 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, - 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, - 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, - 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, - 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, - 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, - 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, - 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, - 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, - 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, - 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, - 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, - 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, - 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, - /* Chroma */ - 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, - 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, - 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, - 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, - 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, - 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, - 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, - 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, - 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, - 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, - 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, - 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, - 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, - 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, - 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, - 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, - 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, - 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, - 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, - 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, - 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, - 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, - 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, - 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, - 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, - 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, - 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, - 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, - 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, - 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, - 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, - 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, - }, - [VS_LT_13_16_SCALE] = { - /* Luma */ - 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, - 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, - 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, - 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, - 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, - 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, - 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, - 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, - 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, - 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, - 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, - 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, - 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, - 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, - 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, - 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, - 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, - 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, - 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, - 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, - 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, - 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, - 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, - 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, - 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, - 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, - 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, - 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, - 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, - 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, - 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, - 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, - /* Chroma */ - 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, - 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, - 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, - 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, - 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, - 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, - 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, - 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, - 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, - 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, - 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, - 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, - 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, - 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, - 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, - 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, - 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, - 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, - 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, - 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, - 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, - 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, - 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, - 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, - 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, - 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, - 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, - 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, - 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, - 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, - 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, - 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, - }, - [VS_LT_14_16_SCALE] = { - /* Luma */ - 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, - 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, - 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, - 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, - 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, - 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, - 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, - 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, - 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, - 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, - 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, - 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, - 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, - 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, - 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, - 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, - 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, - 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, - 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, - 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, - 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, - 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, - 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, - 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, - 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, - 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, - 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, - 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, - 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, - 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, - 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, - 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, - /* Chroma */ - 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, - 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, - 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, - 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, - 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, - 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, - 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, - 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, - 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, - 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, - 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, - 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, - 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, - 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, - 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, - 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, - 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, - 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, - 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, - 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, - 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, - 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, - 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, - 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, - 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, - 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, - 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, - 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, - 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, - 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, - 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, - 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, - }, - [VS_LT_15_16_SCALE] = { - /* Luma */ - 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, - 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, - 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, - 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, - 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, - 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, - 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, - 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, - 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, - 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, - 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, - 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, - 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, - 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, - 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, - 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, - 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, - 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, - 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, - 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, - 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, - 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, - 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, - 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, - 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, - 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, - 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, - 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, - 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, - 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, - 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, - 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, - /* Chroma */ - 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, - 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, - 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, - 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, - 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, - 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, - 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, - 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, - 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, - 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, - 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, - 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, - 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, - 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, - 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, - 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, - 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, - 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, - 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, - 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, - 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, - 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, - 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, - 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, - 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, - 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, - 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, - 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, - 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, - 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, - 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, - 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, - }, - [VS_LT_16_16_SCALE] = { - /* Luma */ - 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, - 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, - 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, - 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, - 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, - 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, - 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, - 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, - 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, - 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, - 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, - 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, - 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, - 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, - 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, - 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, - 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, - 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, - 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, - 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, - 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, - 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, - 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, - 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, - 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, - 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, - 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, - 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, - 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, - 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, - 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, - 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, - /* Chroma */ - 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, - 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, - 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, - 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, - 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, - 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, - 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, - 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, - 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, - 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, - 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, - 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, - 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, - 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, - 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, - 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, - 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, - 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, - 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, - 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, - 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, - 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, - 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, - 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, - 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, - 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, - 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, - 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, - 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, - 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, - 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, - 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, - }, - [VS_1_TO_1_SCALE] = { - /* Luma */ - 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, - 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, - 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, - 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, - 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, - 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, - 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, - 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, - 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, - 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, - 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, - 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, - 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, - 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, - 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, - 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, - 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, - 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, - 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, - 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, - 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, - 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, - 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, - 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, - 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, - 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, - 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, - 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, - 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, - 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, - 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, - 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, - /* Chroma */ - 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, - 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, - 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, - 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, - 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, - 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, - 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, - 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, - 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, - 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, - 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, - 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, - 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, - 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, - 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, - 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, - 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, - 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, - 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, - 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, - 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, - 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, - 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, - 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, - 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, - 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, - 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, - 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, - 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, - 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, - 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, - 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, - }, -}; -#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti-vpe/sc.h --- a/drivers/media/platform/ti-vpe/sc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/sc.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ -#ifndef TI_SC_H -#define TI_SC_H - -/* Scaler regs */ -#define CFG_SC0 0x0 -#define CFG_INTERLACE_O (1 << 0) -#define CFG_LINEAR (1 << 1) -#define CFG_SC_BYPASS (1 << 2) -#define CFG_INVT_FID (1 << 3) -#define CFG_USE_RAV (1 << 4) -#define CFG_ENABLE_EV (1 << 5) -#define CFG_AUTO_HS (1 << 6) -#define CFG_DCM_2X (1 << 7) -#define CFG_DCM_4X (1 << 8) -#define CFG_HP_BYPASS (1 << 9) -#define CFG_INTERLACE_I (1 << 10) -#define CFG_ENABLE_SIN2_VER_INTP (1 << 11) -#define CFG_Y_PK_EN (1 << 14) -#define CFG_TRIM (1 << 15) -#define CFG_SELFGEN_FID (1 << 16) - -#define CFG_SC1 0x4 -#define CFG_ROW_ACC_INC_MASK 0x07ffffff -#define CFG_ROW_ACC_INC_SHIFT 0 - -#define CFG_SC2 0x08 -#define CFG_ROW_ACC_OFFSET_MASK 0x0fffffff -#define CFG_ROW_ACC_OFFSET_SHIFT 0 - -#define CFG_SC3 0x0c -#define CFG_ROW_ACC_OFFSET_B_MASK 0x0fffffff -#define CFG_ROW_ACC_OFFSET_B_SHIFT 0 - -#define CFG_SC4 0x10 -#define CFG_TAR_H_MASK 0x07ff -#define CFG_TAR_H_SHIFT 0 -#define CFG_TAR_W_MASK 0x07ff -#define CFG_TAR_W_SHIFT 12 -#define CFG_LIN_ACC_INC_U_MASK 0x07 -#define CFG_LIN_ACC_INC_U_SHIFT 24 -#define CFG_NLIN_ACC_INIT_U_MASK 0x07 -#define CFG_NLIN_ACC_INIT_U_SHIFT 28 - -#define CFG_SC5 0x14 -#define CFG_SRC_H_MASK 0x07ff -#define CFG_SRC_H_SHIFT 0 -#define CFG_SRC_W_MASK 0x07ff -#define CFG_SRC_W_SHIFT 12 -#define CFG_NLIN_ACC_INC_U_MASK 0x07 -#define CFG_NLIN_ACC_INC_U_SHIFT 24 - -#define CFG_SC6 0x18 -#define CFG_ROW_ACC_INIT_RAV_MASK 0x03ff -#define CFG_ROW_ACC_INIT_RAV_SHIFT 0 -#define CFG_ROW_ACC_INIT_RAV_B_MASK 0x03ff -#define CFG_ROW_ACC_INIT_RAV_B_SHIFT 10 - -#define CFG_SC8 0x20 -#define CFG_NLIN_LEFT_MASK 0x07ff -#define CFG_NLIN_LEFT_SHIFT 0 -#define CFG_NLIN_RIGHT_MASK 0x07ff -#define CFG_NLIN_RIGHT_SHIFT 12 - -#define CFG_SC9 0x24 -#define CFG_LIN_ACC_INC CFG_SC9 - -#define CFG_SC10 0x28 -#define CFG_NLIN_ACC_INIT CFG_SC10 - -#define CFG_SC11 0x2c -#define CFG_NLIN_ACC_INC CFG_SC11 - -#define CFG_SC12 0x30 -#define CFG_COL_ACC_OFFSET_MASK 0x01ffffff -#define CFG_COL_ACC_OFFSET_SHIFT 0 - -#define CFG_SC13 0x34 -#define CFG_SC_FACTOR_RAV_MASK 0xff -#define CFG_SC_FACTOR_RAV_SHIFT 0 -#define CFG_CHROMA_INTP_THR_MASK 0x03ff -#define CFG_CHROMA_INTP_THR_SHIFT 12 -#define CFG_DELTA_CHROMA_THR_MASK 0x0f -#define CFG_DELTA_CHROMA_THR_SHIFT 24 - -#define CFG_SC17 0x44 -#define CFG_EV_THR_MASK 0x03ff -#define CFG_EV_THR_SHIFT 12 -#define CFG_DELTA_LUMA_THR_MASK 0x0f -#define CFG_DELTA_LUMA_THR_SHIFT 24 -#define CFG_DELTA_EV_THR_MASK 0x0f -#define CFG_DELTA_EV_THR_SHIFT 28 - -#define CFG_SC18 0x48 -#define CFG_HS_FACTOR_MASK 0x03ff -#define CFG_HS_FACTOR_SHIFT 0 -#define CFG_CONF_DEFAULT_MASK 0x01ff -#define CFG_CONF_DEFAULT_SHIFT 16 - -#define CFG_SC19 0x4c -#define CFG_HPF_COEFF0_MASK 0xff -#define CFG_HPF_COEFF0_SHIFT 0 -#define CFG_HPF_COEFF1_MASK 0xff -#define CFG_HPF_COEFF1_SHIFT 8 -#define CFG_HPF_COEFF2_MASK 0xff -#define CFG_HPF_COEFF2_SHIFT 16 -#define CFG_HPF_COEFF3_MASK 0xff -#define CFG_HPF_COEFF3_SHIFT 23 - -#define CFG_SC20 0x50 -#define CFG_HPF_COEFF4_MASK 0xff -#define CFG_HPF_COEFF4_SHIFT 0 -#define CFG_HPF_COEFF5_MASK 0xff -#define CFG_HPF_COEFF5_SHIFT 8 -#define CFG_HPF_NORM_SHIFT_MASK 0x07 -#define CFG_HPF_NORM_SHIFT_SHIFT 16 -#define CFG_NL_LIMIT_MASK 0x1ff -#define CFG_NL_LIMIT_SHIFT 20 - -#define CFG_SC21 0x54 -#define CFG_NL_LO_THR_MASK 0x01ff -#define CFG_NL_LO_THR_SHIFT 0 -#define CFG_NL_LO_SLOPE_MASK 0xff -#define CFG_NL_LO_SLOPE_SHIFT 16 - -#define CFG_SC22 0x58 -#define CFG_NL_HI_THR_MASK 0x01ff -#define CFG_NL_HI_THR_SHIFT 0 -#define CFG_NL_HI_SLOPE_SH_MASK 0x07 -#define CFG_NL_HI_SLOPE_SH_SHIFT 16 - -#define CFG_SC23 0x5c -#define CFG_GRADIENT_THR_MASK 0x07ff -#define CFG_GRADIENT_THR_SHIFT 0 -#define CFG_GRADIENT_THR_RANGE_MASK 0x0f -#define CFG_GRADIENT_THR_RANGE_SHIFT 12 -#define CFG_MIN_GY_THR_MASK 0xff -#define CFG_MIN_GY_THR_SHIFT 16 -#define CFG_MIN_GY_THR_RANGE_MASK 0x0f -#define CFG_MIN_GY_THR_RANGE_SHIFT 28 - -#define CFG_SC24 0x60 -#define CFG_ORG_H_MASK 0x07ff -#define CFG_ORG_H_SHIFT 0 -#define CFG_ORG_W_MASK 0x07ff -#define CFG_ORG_W_SHIFT 16 - -#define CFG_SC25 0x64 -#define CFG_OFF_H_MASK 0x07ff -#define CFG_OFF_H_SHIFT 0 -#define CFG_OFF_W_MASK 0x07ff -#define CFG_OFF_W_SHIFT 16 - -/* number of phases supported by the polyphase scalers */ -#define SC_NUM_PHASES 32 - -/* number of taps used by horizontal polyphase scaler */ -#define SC_H_NUM_TAPS 7 - -/* number of taps used by vertical polyphase scaler */ -#define SC_V_NUM_TAPS 5 - -/* number of taps expected by the scaler in it's coefficient memory */ -#define SC_NUM_TAPS_MEM_ALIGN 8 - -/* Maximum frame width the scaler can handle (in pixels) */ -#define SC_MAX_PIXEL_WIDTH 2047 - -/* Maximum frame height the scaler can handle (in lines) */ -#define SC_MAX_PIXEL_HEIGHT 2047 - -/* - * coefficient memory size in bytes: - * num phases x num sets(luma and chroma) x num taps(aligned) x coeff size - */ -#define SC_COEF_SRAM_SIZE (SC_NUM_PHASES * 2 * SC_NUM_TAPS_MEM_ALIGN * 2) - -struct sc_data { - void __iomem *base; - struct resource *res; - - dma_addr_t loaded_coeff_h; /* loaded h coeffs in SC */ - dma_addr_t loaded_coeff_v; /* loaded v coeffs in SC */ - - bool load_coeff_h; /* have new h SC coeffs */ - bool load_coeff_v; /* have new v SC coeffs */ - - struct platform_device *pdev; -}; - -void sc_dump_regs(struct sc_data *sc); -void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, - unsigned int dst_w); -void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, - unsigned int dst_h); -void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, - u32 *sc_reg17, unsigned int src_w, unsigned int src_h, - unsigned int dst_w, unsigned int dst_h); -struct sc_data *sc_create(struct platform_device *pdev, const char *res_name); - -#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c --- a/drivers/media/platform/ti-vpe/vpdma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/vpdma.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,1177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * VPDMA helper library - * - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vpdma.h" -#include "vpdma_priv.h" - -#define VPDMA_FIRMWARE "vpdma-1b8.bin" - -const struct vpdma_data_format vpdma_yuv_fmts[] = { - [VPDMA_DATA_FMT_Y444] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_Y444, - .depth = 8, - }, - [VPDMA_DATA_FMT_Y422] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_Y422, - .depth = 8, - }, - [VPDMA_DATA_FMT_Y420] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_Y420, - .depth = 8, - }, - [VPDMA_DATA_FMT_C444] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_C444, - .depth = 8, - }, - [VPDMA_DATA_FMT_C422] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_C422, - .depth = 8, - }, - [VPDMA_DATA_FMT_C420] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_C420, - .depth = 4, - }, - [VPDMA_DATA_FMT_CB420] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_CB420, - .depth = 4, - }, - [VPDMA_DATA_FMT_YCR422] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_YCR422, - .depth = 16, - }, - [VPDMA_DATA_FMT_YC444] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_YC444, - .depth = 24, - }, - [VPDMA_DATA_FMT_CRY422] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_CRY422, - .depth = 16, - }, - [VPDMA_DATA_FMT_CBY422] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_CBY422, - .depth = 16, - }, - [VPDMA_DATA_FMT_YCB422] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_YCB422, - .depth = 16, - }, -}; -EXPORT_SYMBOL(vpdma_yuv_fmts); - -const struct vpdma_data_format vpdma_rgb_fmts[] = { - [VPDMA_DATA_FMT_RGB565] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_RGB16_565, - .depth = 16, - }, - [VPDMA_DATA_FMT_ARGB16_1555] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ARGB_1555, - .depth = 16, - }, - [VPDMA_DATA_FMT_ARGB16] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ARGB_4444, - .depth = 16, - }, - [VPDMA_DATA_FMT_RGBA16_5551] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_RGBA_5551, - .depth = 16, - }, - [VPDMA_DATA_FMT_RGBA16] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_RGBA_4444, - .depth = 16, - }, - [VPDMA_DATA_FMT_ARGB24] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ARGB24_6666, - .depth = 24, - }, - [VPDMA_DATA_FMT_RGB24] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_RGB24_888, - .depth = 24, - }, - [VPDMA_DATA_FMT_ARGB32] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ARGB32_8888, - .depth = 32, - }, - [VPDMA_DATA_FMT_RGBA24] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_RGBA24_6666, - .depth = 24, - }, - [VPDMA_DATA_FMT_RGBA32] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_RGBA32_8888, - .depth = 32, - }, - [VPDMA_DATA_FMT_BGR565] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_BGR16_565, - .depth = 16, - }, - [VPDMA_DATA_FMT_ABGR16_1555] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ABGR_1555, - .depth = 16, - }, - [VPDMA_DATA_FMT_ABGR16] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ABGR_4444, - .depth = 16, - }, - [VPDMA_DATA_FMT_BGRA16_5551] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_BGRA_5551, - .depth = 16, - }, - [VPDMA_DATA_FMT_BGRA16] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_BGRA_4444, - .depth = 16, - }, - [VPDMA_DATA_FMT_ABGR24] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ABGR24_6666, - .depth = 24, - }, - [VPDMA_DATA_FMT_BGR24] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_BGR24_888, - .depth = 24, - }, - [VPDMA_DATA_FMT_ABGR32] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_ABGR32_8888, - .depth = 32, - }, - [VPDMA_DATA_FMT_BGRA24] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_BGRA24_6666, - .depth = 24, - }, - [VPDMA_DATA_FMT_BGRA32] = { - .type = VPDMA_DATA_FMT_TYPE_RGB, - .data_type = DATA_TYPE_BGRA32_8888, - .depth = 32, - }, -}; -EXPORT_SYMBOL(vpdma_rgb_fmts); - -/* - * To handle RAW format we are re-using the CBY422 - * vpdma data type so that we use the vpdma to re-order - * the incoming bytes, as the parser assumes that the - * first byte presented on the bus is the MSB of a 2 - * bytes value. - * RAW8 handles from 1 to 8 bits - * RAW16 handles from 9 to 16 bits - */ -const struct vpdma_data_format vpdma_raw_fmts[] = { - [VPDMA_DATA_FMT_RAW8] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_CBY422, - .depth = 8, - }, - [VPDMA_DATA_FMT_RAW16] = { - .type = VPDMA_DATA_FMT_TYPE_YUV, - .data_type = DATA_TYPE_CBY422, - .depth = 16, - }, -}; -EXPORT_SYMBOL(vpdma_raw_fmts); - -const struct vpdma_data_format vpdma_misc_fmts[] = { - [VPDMA_DATA_FMT_MV] = { - .type = VPDMA_DATA_FMT_TYPE_MISC, - .data_type = DATA_TYPE_MV, - .depth = 4, - }, -}; -EXPORT_SYMBOL(vpdma_misc_fmts); - -struct vpdma_channel_info { - int num; /* VPDMA channel number */ - int cstat_offset; /* client CSTAT register offset */ -}; - -static const struct vpdma_channel_info chan_info[] = { - [VPE_CHAN_LUMA1_IN] = { - .num = VPE_CHAN_NUM_LUMA1_IN, - .cstat_offset = VPDMA_DEI_LUMA1_CSTAT, - }, - [VPE_CHAN_CHROMA1_IN] = { - .num = VPE_CHAN_NUM_CHROMA1_IN, - .cstat_offset = VPDMA_DEI_CHROMA1_CSTAT, - }, - [VPE_CHAN_LUMA2_IN] = { - .num = VPE_CHAN_NUM_LUMA2_IN, - .cstat_offset = VPDMA_DEI_LUMA2_CSTAT, - }, - [VPE_CHAN_CHROMA2_IN] = { - .num = VPE_CHAN_NUM_CHROMA2_IN, - .cstat_offset = VPDMA_DEI_CHROMA2_CSTAT, - }, - [VPE_CHAN_LUMA3_IN] = { - .num = VPE_CHAN_NUM_LUMA3_IN, - .cstat_offset = VPDMA_DEI_LUMA3_CSTAT, - }, - [VPE_CHAN_CHROMA3_IN] = { - .num = VPE_CHAN_NUM_CHROMA3_IN, - .cstat_offset = VPDMA_DEI_CHROMA3_CSTAT, - }, - [VPE_CHAN_MV_IN] = { - .num = VPE_CHAN_NUM_MV_IN, - .cstat_offset = VPDMA_DEI_MV_IN_CSTAT, - }, - [VPE_CHAN_MV_OUT] = { - .num = VPE_CHAN_NUM_MV_OUT, - .cstat_offset = VPDMA_DEI_MV_OUT_CSTAT, - }, - [VPE_CHAN_LUMA_OUT] = { - .num = VPE_CHAN_NUM_LUMA_OUT, - .cstat_offset = VPDMA_VIP_UP_Y_CSTAT, - }, - [VPE_CHAN_CHROMA_OUT] = { - .num = VPE_CHAN_NUM_CHROMA_OUT, - .cstat_offset = VPDMA_VIP_UP_UV_CSTAT, - }, - [VPE_CHAN_RGB_OUT] = { - .num = VPE_CHAN_NUM_RGB_OUT, - .cstat_offset = VPDMA_VIP_UP_Y_CSTAT, - }, -}; - -static u32 read_reg(struct vpdma_data *vpdma, int offset) -{ - return ioread32(vpdma->base + offset); -} - -static void write_reg(struct vpdma_data *vpdma, int offset, u32 value) -{ - iowrite32(value, vpdma->base + offset); -} - -static int read_field_reg(struct vpdma_data *vpdma, int offset, - u32 mask, int shift) -{ - return (read_reg(vpdma, offset) & (mask << shift)) >> shift; -} - -static void write_field_reg(struct vpdma_data *vpdma, int offset, u32 field, - u32 mask, int shift) -{ - u32 val = read_reg(vpdma, offset); - - val &= ~(mask << shift); - val |= (field & mask) << shift; - - write_reg(vpdma, offset, val); -} - -void vpdma_dump_regs(struct vpdma_data *vpdma) -{ - struct device *dev = &vpdma->pdev->dev; - -#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(vpdma, VPDMA_##r)) - - dev_dbg(dev, "VPDMA Registers:\n"); - - DUMPREG(PID); - DUMPREG(LIST_ADDR); - DUMPREG(LIST_ATTR); - DUMPREG(LIST_STAT_SYNC); - DUMPREG(BG_RGB); - DUMPREG(BG_YUV); - DUMPREG(SETUP); - DUMPREG(MAX_SIZE1); - DUMPREG(MAX_SIZE2); - DUMPREG(MAX_SIZE3); - - /* - * dumping registers of only group0 and group3, because VPE channels - * lie within group0 and group3 registers - */ - DUMPREG(INT_CHAN_STAT(0)); - DUMPREG(INT_CHAN_MASK(0)); - DUMPREG(INT_CHAN_STAT(3)); - DUMPREG(INT_CHAN_MASK(3)); - DUMPREG(INT_CLIENT0_STAT); - DUMPREG(INT_CLIENT0_MASK); - DUMPREG(INT_CLIENT1_STAT); - DUMPREG(INT_CLIENT1_MASK); - DUMPREG(INT_LIST0_STAT); - DUMPREG(INT_LIST0_MASK); - - /* - * these are registers specific to VPE clients, we can make this - * function dump client registers specific to VPE or VIP based on - * who is using it - */ - DUMPREG(DEI_CHROMA1_CSTAT); - DUMPREG(DEI_LUMA1_CSTAT); - DUMPREG(DEI_CHROMA2_CSTAT); - DUMPREG(DEI_LUMA2_CSTAT); - DUMPREG(DEI_CHROMA3_CSTAT); - DUMPREG(DEI_LUMA3_CSTAT); - DUMPREG(DEI_MV_IN_CSTAT); - DUMPREG(DEI_MV_OUT_CSTAT); - DUMPREG(VIP_UP_Y_CSTAT); - DUMPREG(VIP_UP_UV_CSTAT); - DUMPREG(VPI_CTL_CSTAT); -} -EXPORT_SYMBOL(vpdma_dump_regs); - -/* - * Allocate a DMA buffer - */ -int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size) -{ - buf->size = size; - buf->mapped = false; - buf->addr = kzalloc(size, GFP_KERNEL); - if (!buf->addr) - return -ENOMEM; - - WARN_ON(((unsigned long)buf->addr & VPDMA_DESC_ALIGN) != 0); - - return 0; -} -EXPORT_SYMBOL(vpdma_alloc_desc_buf); - -void vpdma_free_desc_buf(struct vpdma_buf *buf) -{ - WARN_ON(buf->mapped); - kfree(buf->addr); - buf->addr = NULL; - buf->size = 0; -} -EXPORT_SYMBOL(vpdma_free_desc_buf); - -/* - * map descriptor/payload DMA buffer, enabling DMA access - */ -int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf) -{ - struct device *dev = &vpdma->pdev->dev; - - WARN_ON(buf->mapped); - buf->dma_addr = dma_map_single(dev, buf->addr, buf->size, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(dev, buf->dma_addr)) { - dev_err(dev, "failed to map buffer\n"); - return -EINVAL; - } - - buf->mapped = true; - - return 0; -} -EXPORT_SYMBOL(vpdma_map_desc_buf); - -/* - * unmap descriptor/payload DMA buffer, disabling DMA access and - * allowing the main processor to access the data - */ -void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf) -{ - struct device *dev = &vpdma->pdev->dev; - - if (buf->mapped) - dma_unmap_single(dev, buf->dma_addr, buf->size, - DMA_BIDIRECTIONAL); - - buf->mapped = false; -} -EXPORT_SYMBOL(vpdma_unmap_desc_buf); - -/* - * Cleanup all pending descriptors of a list - * First, stop the current list being processed. - * If the VPDMA was busy, this step makes vpdma to accept post lists. - * To cleanup the internal FSM, post abort list descriptor for all the - * channels from @channels array of size @size. - */ -int vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num, - int *channels, int size) -{ - struct vpdma_desc_list abort_list; - int i, ret, timeout = 500; - - write_reg(vpdma, VPDMA_LIST_ATTR, - (list_num << VPDMA_LIST_NUM_SHFT) | - (1 << VPDMA_LIST_STOP_SHFT)); - - if (size <= 0 || !channels) - return 0; - - ret = vpdma_create_desc_list(&abort_list, - size * sizeof(struct vpdma_dtd), VPDMA_LIST_TYPE_NORMAL); - if (ret) - return ret; - - for (i = 0; i < size; i++) - vpdma_add_abort_channel_ctd(&abort_list, channels[i]); - - ret = vpdma_map_desc_buf(vpdma, &abort_list.buf); - if (ret) - goto free_desc; - ret = vpdma_submit_descs(vpdma, &abort_list, list_num); - if (ret) - goto unmap_desc; - - while (vpdma_list_busy(vpdma, list_num) && --timeout) - ; - - if (timeout == 0) { - dev_err(&vpdma->pdev->dev, "Timed out cleaning up VPDMA list\n"); - ret = -EBUSY; - } - -unmap_desc: - vpdma_unmap_desc_buf(vpdma, &abort_list.buf); -free_desc: - vpdma_free_desc_buf(&abort_list.buf); - - return ret; -} -EXPORT_SYMBOL(vpdma_list_cleanup); - -/* - * create a descriptor list, the user of this list will append configuration, - * control and data descriptors to this list, this list will be submitted to - * VPDMA. VPDMA's list parser will go through each descriptor and perform the - * required DMA operations - */ -int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type) -{ - int r; - - r = vpdma_alloc_desc_buf(&list->buf, size); - if (r) - return r; - - list->next = list->buf.addr; - - list->type = type; - - return 0; -} -EXPORT_SYMBOL(vpdma_create_desc_list); - -/* - * once a descriptor list is parsed by VPDMA, we reset the list by emptying it, - * to allow new descriptors to be added to the list. - */ -void vpdma_reset_desc_list(struct vpdma_desc_list *list) -{ - list->next = list->buf.addr; -} -EXPORT_SYMBOL(vpdma_reset_desc_list); - -/* - * free the buffer allocated for the VPDMA descriptor list, this should be - * called when the user doesn't want to use VPDMA any more. - */ -void vpdma_free_desc_list(struct vpdma_desc_list *list) -{ - vpdma_free_desc_buf(&list->buf); - - list->next = NULL; -} -EXPORT_SYMBOL(vpdma_free_desc_list); - -bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num) -{ - return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16); -} -EXPORT_SYMBOL(vpdma_list_busy); - -/* - * submit a list of DMA descriptors to the VPE VPDMA, do not wait for completion - */ -int vpdma_submit_descs(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, int list_num) -{ - int list_size; - unsigned long flags; - - if (vpdma_list_busy(vpdma, list_num)) - return -EBUSY; - - /* 16-byte granularity */ - list_size = (list->next - list->buf.addr) >> 4; - - spin_lock_irqsave(&vpdma->lock, flags); - write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr); - - write_reg(vpdma, VPDMA_LIST_ATTR, - (list_num << VPDMA_LIST_NUM_SHFT) | - (list->type << VPDMA_LIST_TYPE_SHFT) | - list_size); - spin_unlock_irqrestore(&vpdma->lock, flags); - - return 0; -} -EXPORT_SYMBOL(vpdma_submit_descs); - -static void dump_dtd(struct vpdma_dtd *dtd); - -void vpdma_update_dma_addr(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, dma_addr_t dma_addr, - void *write_dtd, int drop, int idx) -{ - struct vpdma_dtd *dtd = list->buf.addr; - dma_addr_t write_desc_addr; - int offset; - - dtd += idx; - vpdma_unmap_desc_buf(vpdma, &list->buf); - - dtd->start_addr = dma_addr; - - /* Calculate write address from the offset of write_dtd from start - * of the list->buf - */ - offset = (void *)write_dtd - list->buf.addr; - write_desc_addr = list->buf.dma_addr + offset; - - if (drop) - dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, - 1, 1, 0); - else - dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, - 1, 0, 0); - - vpdma_map_desc_buf(vpdma, &list->buf); - - dump_dtd(dtd); -} -EXPORT_SYMBOL(vpdma_update_dma_addr); - -void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, - u32 width, u32 height) -{ - if (reg_addr != VPDMA_MAX_SIZE1 && reg_addr != VPDMA_MAX_SIZE2 && - reg_addr != VPDMA_MAX_SIZE3) - reg_addr = VPDMA_MAX_SIZE1; - - write_field_reg(vpdma, reg_addr, width - 1, - VPDMA_MAX_SIZE_WIDTH_MASK, VPDMA_MAX_SIZE_WIDTH_SHFT); - - write_field_reg(vpdma, reg_addr, height - 1, - VPDMA_MAX_SIZE_HEIGHT_MASK, VPDMA_MAX_SIZE_HEIGHT_SHFT); - -} -EXPORT_SYMBOL(vpdma_set_max_size); - -static void dump_cfd(struct vpdma_cfd *cfd) -{ - int class; - - class = cfd_get_class(cfd); - - pr_debug("config descriptor of payload class: %s\n", - class == CFD_CLS_BLOCK ? "simple block" : - "address data block"); - - if (class == CFD_CLS_BLOCK) - pr_debug("word0: dst_addr_offset = 0x%08x\n", - cfd->dest_addr_offset); - - if (class == CFD_CLS_BLOCK) - pr_debug("word1: num_data_wrds = %d\n", cfd->block_len); - - pr_debug("word2: payload_addr = 0x%08x\n", cfd->payload_addr); - - pr_debug("word3: pkt_type = %d, direct = %d, class = %d, dest = %d, payload_len = %d\n", - cfd_get_pkt_type(cfd), - cfd_get_direct(cfd), class, cfd_get_dest(cfd), - cfd_get_payload_len(cfd)); -} - -/* - * append a configuration descriptor to the given descriptor list, where the - * payload is in the form of a simple data block specified in the descriptor - * header, this is used to upload scaler coefficients to the scaler module - */ -void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client, - struct vpdma_buf *blk, u32 dest_offset) -{ - struct vpdma_cfd *cfd; - int len = blk->size; - - WARN_ON(blk->dma_addr & VPDMA_DESC_ALIGN); - - cfd = list->next; - WARN_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size)); - - cfd->dest_addr_offset = dest_offset; - cfd->block_len = len; - cfd->payload_addr = (u32) blk->dma_addr; - cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_BLOCK, - client, len >> 4); - - list->next = cfd + 1; - - dump_cfd(cfd); -} -EXPORT_SYMBOL(vpdma_add_cfd_block); - -/* - * append a configuration descriptor to the given descriptor list, where the - * payload is in the address data block format, this is used to a configure a - * discontiguous set of MMRs - */ -void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, - struct vpdma_buf *adb) -{ - struct vpdma_cfd *cfd; - unsigned int len = adb->size; - - WARN_ON(len & VPDMA_ADB_SIZE_ALIGN); - WARN_ON(adb->dma_addr & VPDMA_DESC_ALIGN); - - cfd = list->next; - BUG_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size)); - - cfd->w0 = 0; - cfd->w1 = 0; - cfd->payload_addr = (u32) adb->dma_addr; - cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_ADB, - client, len >> 4); - - list->next = cfd + 1; - - dump_cfd(cfd); -}; -EXPORT_SYMBOL(vpdma_add_cfd_adb); - -/* - * control descriptor format change based on what type of control descriptor it - * is, we only use 'sync on channel' control descriptors for now, so assume it's - * that - */ -static void dump_ctd(struct vpdma_ctd *ctd) -{ - pr_debug("control descriptor\n"); - - pr_debug("word3: pkt_type = %d, source = %d, ctl_type = %d\n", - ctd_get_pkt_type(ctd), ctd_get_source(ctd), ctd_get_ctl(ctd)); -} - -/* - * append a 'sync on channel' type control descriptor to the given descriptor - * list, this descriptor stalls the VPDMA list till the time DMA is completed - * on the specified channel - */ -void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, - enum vpdma_channel chan) -{ - struct vpdma_ctd *ctd; - - ctd = list->next; - WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size)); - - ctd->w0 = 0; - ctd->w1 = 0; - ctd->w2 = 0; - ctd->type_source_ctl = ctd_type_source_ctl(chan_info[chan].num, - CTD_TYPE_SYNC_ON_CHANNEL); - - list->next = ctd + 1; - - dump_ctd(ctd); -} -EXPORT_SYMBOL(vpdma_add_sync_on_channel_ctd); - -/* - * append an 'abort_channel' type control descriptor to the given descriptor - * list, this descriptor aborts any DMA transaction happening using the - * specified channel - */ -void vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list, - int chan_num) -{ - struct vpdma_ctd *ctd; - - ctd = list->next; - WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size)); - - ctd->w0 = 0; - ctd->w1 = 0; - ctd->w2 = 0; - ctd->type_source_ctl = ctd_type_source_ctl(chan_num, - CTD_TYPE_ABORT_CHANNEL); - - list->next = ctd + 1; - - dump_ctd(ctd); -} -EXPORT_SYMBOL(vpdma_add_abort_channel_ctd); - -static void dump_dtd(struct vpdma_dtd *dtd) -{ - int dir, chan; - - dir = dtd_get_dir(dtd); - chan = dtd_get_chan(dtd); - - pr_debug("%s data transfer descriptor for channel %d\n", - dir == DTD_DIR_OUT ? "outbound" : "inbound", chan); - - pr_debug("word0: data_type = %d, notify = %d, field = %d, 1D = %d, even_ln_skp = %d, odd_ln_skp = %d, line_stride = %d\n", - dtd_get_data_type(dtd), dtd_get_notify(dtd), dtd_get_field(dtd), - dtd_get_1d(dtd), dtd_get_even_line_skip(dtd), - dtd_get_odd_line_skip(dtd), dtd_get_line_stride(dtd)); - - if (dir == DTD_DIR_IN) - pr_debug("word1: line_length = %d, xfer_height = %d\n", - dtd_get_line_length(dtd), dtd_get_xfer_height(dtd)); - - pr_debug("word2: start_addr = %x\n", dtd->start_addr); - - pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, pri = %d, next_chan = %d\n", - dtd_get_pkt_type(dtd), - dtd_get_mode(dtd), dir, chan, dtd_get_priority(dtd), - dtd_get_next_chan(dtd)); - - if (dir == DTD_DIR_IN) - pr_debug("word4: frame_width = %d, frame_height = %d\n", - dtd_get_frame_width(dtd), dtd_get_frame_height(dtd)); - else - pr_debug("word4: desc_write_addr = 0x%08x, write_desc = %d, drp_data = %d, use_desc_reg = %d\n", - dtd_get_desc_write_addr(dtd), dtd_get_write_desc(dtd), - dtd_get_drop_data(dtd), dtd_get_use_desc(dtd)); - - if (dir == DTD_DIR_IN) - pr_debug("word5: hor_start = %d, ver_start = %d\n", - dtd_get_h_start(dtd), dtd_get_v_start(dtd)); - else - pr_debug("word5: max_width %d, max_height %d\n", - dtd_get_max_width(dtd), dtd_get_max_height(dtd)); - - pr_debug("word6: client specific attr0 = 0x%08x\n", dtd->client_attr0); - pr_debug("word7: client specific attr1 = 0x%08x\n", dtd->client_attr1); -} - -/* - * append an outbound data transfer descriptor to the given descriptor list, - * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel - * - * @list: vpdma desc list to which we add this descriptor - * @width: width of the image in pixels in memory - * @c_rect: compose params of output image - * @fmt: vpdma data format of the buffer - * dma_addr: dma address as seen by VPDMA - * max_width: enum for maximum width of data transfer - * max_height: enum for maximum height of data transfer - * chan: VPDMA channel - * flags: VPDMA flags to configure some descriptor fields - */ -void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, - int stride, const struct v4l2_rect *c_rect, - const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - int max_w, int max_h, enum vpdma_channel chan, u32 flags) -{ - vpdma_rawchan_add_out_dtd(list, width, stride, c_rect, fmt, dma_addr, - max_w, max_h, chan_info[chan].num, flags); -} -EXPORT_SYMBOL(vpdma_add_out_dtd); - -void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width, - int stride, const struct v4l2_rect *c_rect, - const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - int max_w, int max_h, int raw_vpdma_chan, u32 flags) -{ - int priority = 0; - int field = 0; - int notify = 1; - int channel, next_chan; - struct v4l2_rect rect = *c_rect; - int depth = fmt->depth; - struct vpdma_dtd *dtd; - - channel = next_chan = raw_vpdma_chan; - - if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && - (fmt->data_type == DATA_TYPE_C420 || - fmt->data_type == DATA_TYPE_CB420)) { - rect.height >>= 1; - rect.top >>= 1; - depth = 8; - } - - dma_addr += rect.top * stride + (rect.left * depth >> 3); - - dtd = list->next; - WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); - - dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type, - notify, - field, - !!(flags & VPDMA_DATA_FRAME_1D), - !!(flags & VPDMA_DATA_EVEN_LINE_SKIP), - !!(flags & VPDMA_DATA_ODD_LINE_SKIP), - stride); - dtd->w1 = 0; - dtd->start_addr = (u32) dma_addr; - dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), - DTD_DIR_OUT, channel, priority, next_chan); - dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0); - dtd->max_width_height = dtd_max_width_height(max_w, max_h); - dtd->client_attr0 = 0; - dtd->client_attr1 = 0; - - list->next = dtd + 1; - - dump_dtd(dtd); -} -EXPORT_SYMBOL(vpdma_rawchan_add_out_dtd); - -/* - * append an inbound data transfer descriptor to the given descriptor list, - * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel - * - * @list: vpdma desc list to which we add this descriptor - * @width: width of the image in pixels in memory(not the cropped width) - * @c_rect: crop params of input image - * @fmt: vpdma data format of the buffer - * dma_addr: dma address as seen by VPDMA - * chan: VPDMA channel - * field: top or bottom field info of the input image - * flags: VPDMA flags to configure some descriptor fields - * frame_width/height: the complete width/height of the image presented to the - * client (this makes sense when multiple channels are - * connected to the same client, forming a larger frame) - * start_h, start_v: position where the given channel starts providing pixel - * data to the client (makes sense when multiple channels - * contribute to the client) - */ -void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, - int stride, const struct v4l2_rect *c_rect, - const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - enum vpdma_channel chan, int field, u32 flags, int frame_width, - int frame_height, int start_h, int start_v) -{ - int priority = 0; - int notify = 1; - int depth = fmt->depth; - int channel, next_chan; - struct v4l2_rect rect = *c_rect; - struct vpdma_dtd *dtd; - - channel = next_chan = chan_info[chan].num; - - if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && - (fmt->data_type == DATA_TYPE_C420 || - fmt->data_type == DATA_TYPE_CB420)) { - rect.height >>= 1; - rect.top >>= 1; - depth = 8; - } - - dma_addr += rect.top * stride + (rect.left * depth >> 3); - - dtd = list->next; - WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); - - dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type, - notify, - field, - !!(flags & VPDMA_DATA_FRAME_1D), - !!(flags & VPDMA_DATA_EVEN_LINE_SKIP), - !!(flags & VPDMA_DATA_ODD_LINE_SKIP), - stride); - - dtd->xfer_length_height = dtd_xfer_length_height(rect.width, - rect.height); - dtd->start_addr = (u32) dma_addr; - dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), - DTD_DIR_IN, channel, priority, next_chan); - dtd->frame_width_height = dtd_frame_width_height(frame_width, - frame_height); - dtd->start_h_v = dtd_start_h_v(start_h, start_v); - dtd->client_attr0 = 0; - dtd->client_attr1 = 0; - - list->next = dtd + 1; - - dump_dtd(dtd); -} -EXPORT_SYMBOL(vpdma_add_in_dtd); - -int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv) -{ - int i, list_num = -1; - unsigned long flags; - - spin_lock_irqsave(&vpdma->lock, flags); - for (i = 0; i < VPDMA_MAX_NUM_LIST && - vpdma->hwlist_used[i] == true; i++) - ; - - if (i < VPDMA_MAX_NUM_LIST) { - list_num = i; - vpdma->hwlist_used[i] = true; - vpdma->hwlist_priv[i] = priv; - } - spin_unlock_irqrestore(&vpdma->lock, flags); - - return list_num; -} -EXPORT_SYMBOL(vpdma_hwlist_alloc); - -void *vpdma_hwlist_get_priv(struct vpdma_data *vpdma, int list_num) -{ - if (!vpdma || list_num >= VPDMA_MAX_NUM_LIST) - return NULL; - - return vpdma->hwlist_priv[list_num]; -} -EXPORT_SYMBOL(vpdma_hwlist_get_priv); - -void *vpdma_hwlist_release(struct vpdma_data *vpdma, int list_num) -{ - void *priv; - unsigned long flags; - - spin_lock_irqsave(&vpdma->lock, flags); - vpdma->hwlist_used[list_num] = false; - priv = vpdma->hwlist_priv; - spin_unlock_irqrestore(&vpdma->lock, flags); - - return priv; -} -EXPORT_SYMBOL(vpdma_hwlist_release); - -/* set or clear the mask for list complete interrupt */ -void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int irq_num, - int list_num, bool enable) -{ - u32 reg_addr = VPDMA_INT_LIST0_MASK + VPDMA_INTX_OFFSET * irq_num; - u32 val; - - val = read_reg(vpdma, reg_addr); - if (enable) - val |= (1 << (list_num * 2)); - else - val &= ~(1 << (list_num * 2)); - write_reg(vpdma, reg_addr, val); -} -EXPORT_SYMBOL(vpdma_enable_list_complete_irq); - -/* get the LIST_STAT register */ -unsigned int vpdma_get_list_stat(struct vpdma_data *vpdma, int irq_num) -{ - u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num; - - return read_reg(vpdma, reg_addr); -} -EXPORT_SYMBOL(vpdma_get_list_stat); - -/* get the LIST_MASK register */ -unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num) -{ - u32 reg_addr = VPDMA_INT_LIST0_MASK + VPDMA_INTX_OFFSET * irq_num; - - return read_reg(vpdma, reg_addr); -} -EXPORT_SYMBOL(vpdma_get_list_mask); - -/* clear previously occurred list interrupts in the LIST_STAT register */ -void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num, - int list_num) -{ - u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num; - - write_reg(vpdma, reg_addr, 3 << (list_num * 2)); -} -EXPORT_SYMBOL(vpdma_clear_list_stat); - -void vpdma_set_bg_color(struct vpdma_data *vpdma, - struct vpdma_data_format *fmt, u32 color) -{ - if (fmt->type == VPDMA_DATA_FMT_TYPE_RGB) - write_reg(vpdma, VPDMA_BG_RGB, color); - else if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV) - write_reg(vpdma, VPDMA_BG_YUV, color); -} -EXPORT_SYMBOL(vpdma_set_bg_color); - -/* - * configures the output mode of the line buffer for the given client, the - * line buffer content can either be mirrored(each line repeated twice) or - * passed to the client as is - */ -void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode, - enum vpdma_channel chan) -{ - int client_cstat = chan_info[chan].cstat_offset; - - write_field_reg(vpdma, client_cstat, line_mode, - VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT); -} -EXPORT_SYMBOL(vpdma_set_line_mode); - -/* - * configures the event which should trigger VPDMA transfer for the given - * client - */ -void vpdma_set_frame_start_event(struct vpdma_data *vpdma, - enum vpdma_frame_start_event fs_event, - enum vpdma_channel chan) -{ - int client_cstat = chan_info[chan].cstat_offset; - - write_field_reg(vpdma, client_cstat, fs_event, - VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT); -} -EXPORT_SYMBOL(vpdma_set_frame_start_event); - -static void vpdma_firmware_cb(const struct firmware *f, void *context) -{ - struct vpdma_data *vpdma = context; - struct vpdma_buf fw_dma_buf; - int i, r; - - dev_dbg(&vpdma->pdev->dev, "firmware callback\n"); - - if (!f || !f->data) { - dev_err(&vpdma->pdev->dev, "couldn't get firmware\n"); - return; - } - - /* already initialized */ - if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, - VPDMA_LIST_RDY_SHFT)) { - vpdma->cb(vpdma->pdev); - return; - } - - r = vpdma_alloc_desc_buf(&fw_dma_buf, f->size); - if (r) { - dev_err(&vpdma->pdev->dev, - "failed to allocate dma buffer for firmware\n"); - goto rel_fw; - } - - memcpy(fw_dma_buf.addr, f->data, f->size); - - vpdma_map_desc_buf(vpdma, &fw_dma_buf); - - write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr); - - for (i = 0; i < 100; i++) { /* max 1 second */ - msleep_interruptible(10); - - if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, - VPDMA_LIST_RDY_SHFT)) - break; - } - - if (i == 100) { - dev_err(&vpdma->pdev->dev, "firmware upload failed\n"); - goto free_buf; - } - - vpdma->cb(vpdma->pdev); - -free_buf: - vpdma_unmap_desc_buf(vpdma, &fw_dma_buf); - - vpdma_free_desc_buf(&fw_dma_buf); -rel_fw: - release_firmware(f); -} - -static int vpdma_load_firmware(struct vpdma_data *vpdma) -{ - int r; - struct device *dev = &vpdma->pdev->dev; - - r = request_firmware_nowait(THIS_MODULE, 1, - (const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma, - vpdma_firmware_cb); - if (r) { - dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE); - return r; - } else { - dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE); - } - - return 0; -} - -int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma, - void (*cb)(struct platform_device *pdev)) -{ - struct resource *res; - int r; - - dev_dbg(&pdev->dev, "vpdma_create\n"); - - vpdma->pdev = pdev; - vpdma->cb = cb; - spin_lock_init(&vpdma->lock); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma"); - if (res == NULL) { - dev_err(&pdev->dev, "missing platform resources data\n"); - return -ENODEV; - } - - vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!vpdma->base) { - dev_err(&pdev->dev, "failed to ioremap\n"); - return -ENOMEM; - } - - r = vpdma_load_firmware(vpdma); - if (r) { - pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE); - return r; - } - - return 0; -} -EXPORT_SYMBOL(vpdma_create); - -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_FIRMWARE(VPDMA_FIRMWARE); -MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h --- a/drivers/media/platform/ti-vpe/vpdma.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/vpdma.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,284 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#ifndef __TI_VPDMA_H_ -#define __TI_VPDMA_H_ - -#define VPDMA_MAX_NUM_LIST 8 -/* - * A vpdma_buf tracks the size, DMA address and mapping status of each - * driver DMA area. - */ -struct vpdma_buf { - void *addr; - dma_addr_t dma_addr; - size_t size; - bool mapped; -}; - -struct vpdma_desc_list { - struct vpdma_buf buf; - void *next; - int type; -}; - -struct vpdma_data { - void __iomem *base; - - struct platform_device *pdev; - - spinlock_t lock; - bool hwlist_used[VPDMA_MAX_NUM_LIST]; - void *hwlist_priv[VPDMA_MAX_NUM_LIST]; - /* callback to VPE driver when the firmware is loaded */ - void (*cb)(struct platform_device *pdev); -}; - -enum vpdma_data_format_type { - VPDMA_DATA_FMT_TYPE_YUV, - VPDMA_DATA_FMT_TYPE_RGB, - VPDMA_DATA_FMT_TYPE_MISC, -}; - -struct vpdma_data_format { - enum vpdma_data_format_type type; - int data_type; - u8 depth; -}; - -#define VPDMA_DESC_ALIGN 16 /* 16-byte descriptor alignment */ -#define VPDMA_STRIDE_ALIGN 16 /* - * line stride of source and dest - * buffers should be 16 byte aligned - */ -#define VPDMA_MAX_STRIDE 65520 /* Max line stride 16 byte aligned */ -#define VPDMA_DTD_DESC_SIZE 32 /* 8 words */ -#define VPDMA_CFD_CTD_DESC_SIZE 16 /* 4 words */ - -#define VPDMA_LIST_TYPE_NORMAL 0 -#define VPDMA_LIST_TYPE_SELF_MODIFYING 1 -#define VPDMA_LIST_TYPE_DOORBELL 2 - -enum vpdma_yuv_formats { - VPDMA_DATA_FMT_Y444 = 0, - VPDMA_DATA_FMT_Y422, - VPDMA_DATA_FMT_Y420, - VPDMA_DATA_FMT_C444, - VPDMA_DATA_FMT_C422, - VPDMA_DATA_FMT_C420, - VPDMA_DATA_FMT_CB420, - VPDMA_DATA_FMT_YCR422, - VPDMA_DATA_FMT_YC444, - VPDMA_DATA_FMT_CRY422, - VPDMA_DATA_FMT_CBY422, - VPDMA_DATA_FMT_YCB422, -}; - -enum vpdma_rgb_formats { - VPDMA_DATA_FMT_RGB565 = 0, - VPDMA_DATA_FMT_ARGB16_1555, - VPDMA_DATA_FMT_ARGB16, - VPDMA_DATA_FMT_RGBA16_5551, - VPDMA_DATA_FMT_RGBA16, - VPDMA_DATA_FMT_ARGB24, - VPDMA_DATA_FMT_RGB24, - VPDMA_DATA_FMT_ARGB32, - VPDMA_DATA_FMT_RGBA24, - VPDMA_DATA_FMT_RGBA32, - VPDMA_DATA_FMT_BGR565, - VPDMA_DATA_FMT_ABGR16_1555, - VPDMA_DATA_FMT_ABGR16, - VPDMA_DATA_FMT_BGRA16_5551, - VPDMA_DATA_FMT_BGRA16, - VPDMA_DATA_FMT_ABGR24, - VPDMA_DATA_FMT_BGR24, - VPDMA_DATA_FMT_ABGR32, - VPDMA_DATA_FMT_BGRA24, - VPDMA_DATA_FMT_BGRA32, -}; - -enum vpdma_raw_formats { - VPDMA_DATA_FMT_RAW8 = 0, - VPDMA_DATA_FMT_RAW16, -}; - -enum vpdma_misc_formats { - VPDMA_DATA_FMT_MV = 0, -}; - -extern const struct vpdma_data_format vpdma_yuv_fmts[]; -extern const struct vpdma_data_format vpdma_rgb_fmts[]; -extern const struct vpdma_data_format vpdma_raw_fmts[]; -extern const struct vpdma_data_format vpdma_misc_fmts[]; - -enum vpdma_frame_start_event { - VPDMA_FSEVENT_HDMI_FID = 0, - VPDMA_FSEVENT_DVO2_FID, - VPDMA_FSEVENT_HDCOMP_FID, - VPDMA_FSEVENT_SD_FID, - VPDMA_FSEVENT_LM_FID0, - VPDMA_FSEVENT_LM_FID1, - VPDMA_FSEVENT_LM_FID2, - VPDMA_FSEVENT_CHANNEL_ACTIVE, -}; - -/* max width configurations */ -enum vpdma_max_width { - MAX_OUT_WIDTH_UNLIMITED = 0, - MAX_OUT_WIDTH_REG1, - MAX_OUT_WIDTH_REG2, - MAX_OUT_WIDTH_REG3, - MAX_OUT_WIDTH_352, - MAX_OUT_WIDTH_768, - MAX_OUT_WIDTH_1280, - MAX_OUT_WIDTH_1920, -}; - -/* max height configurations */ -enum vpdma_max_height { - MAX_OUT_HEIGHT_UNLIMITED = 0, - MAX_OUT_HEIGHT_REG1, - MAX_OUT_HEIGHT_REG2, - MAX_OUT_HEIGHT_REG3, - MAX_OUT_HEIGHT_288, - MAX_OUT_HEIGHT_576, - MAX_OUT_HEIGHT_720, - MAX_OUT_HEIGHT_1080, -}; - -/* - * VPDMA channel numbers - */ -enum vpdma_channel { - VPE_CHAN_LUMA1_IN, - VPE_CHAN_CHROMA1_IN, - VPE_CHAN_LUMA2_IN, - VPE_CHAN_CHROMA2_IN, - VPE_CHAN_LUMA3_IN, - VPE_CHAN_CHROMA3_IN, - VPE_CHAN_MV_IN, - VPE_CHAN_MV_OUT, - VPE_CHAN_LUMA_OUT, - VPE_CHAN_CHROMA_OUT, - VPE_CHAN_RGB_OUT, -}; - -#define VIP_CHAN_VIP2_OFFSET 70 -#define VIP_CHAN_MULT_PORTB_OFFSET 16 -#define VIP_CHAN_YUV_PORTB_OFFSET 2 -#define VIP_CHAN_RGB_PORTB_OFFSET 1 - -#define VPDMA_MAX_CHANNELS 256 - -/* flags for VPDMA data descriptors */ -#define VPDMA_DATA_ODD_LINE_SKIP (1 << 0) -#define VPDMA_DATA_EVEN_LINE_SKIP (1 << 1) -#define VPDMA_DATA_FRAME_1D (1 << 2) -#define VPDMA_DATA_MODE_TILED (1 << 3) - -/* - * client identifiers used for configuration descriptors - */ -#define CFD_MMR_CLIENT 0 -#define CFD_SC_CLIENT 4 - -/* Address data block header format */ -struct vpdma_adb_hdr { - u32 offset; - u32 nwords; - u32 reserved0; - u32 reserved1; -}; - -/* helpers for creating ADB headers for config descriptors MMRs as client */ -#define ADB_ADDR(dma_buf, str, fld) ((dma_buf)->addr + offsetof(str, fld)) -#define MMR_ADB_ADDR(buf, str, fld) ADB_ADDR(&(buf), struct str, fld) - -#define VPDMA_SET_MMR_ADB_HDR(buf, str, hdr, regs, offset_a) \ - do { \ - struct vpdma_adb_hdr *h; \ - struct str *adb = NULL; \ - h = MMR_ADB_ADDR(buf, str, hdr); \ - h->offset = (offset_a); \ - h->nwords = sizeof(adb->regs) >> 2; \ - } while (0) - -/* vpdma descriptor buffer allocation and management */ -int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size); -void vpdma_free_desc_buf(struct vpdma_buf *buf); -int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf); -void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf); - -/* vpdma descriptor list funcs */ -int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type); -void vpdma_reset_desc_list(struct vpdma_desc_list *list); -void vpdma_free_desc_list(struct vpdma_desc_list *list); -int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list, - int list_num); -bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num); -void vpdma_update_dma_addr(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, dma_addr_t dma_addr, - void *write_dtd, int drop, int idx); - -/* VPDMA hardware list funcs */ -int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv); -void *vpdma_hwlist_get_priv(struct vpdma_data *vpdma, int list_num); -void *vpdma_hwlist_release(struct vpdma_data *vpdma, int list_num); - -/* helpers for creating vpdma descriptors */ -void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client, - struct vpdma_buf *blk, u32 dest_offset); -void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, - struct vpdma_buf *adb); -void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, - enum vpdma_channel chan); -void vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list, - int chan_num); -void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, - int stride, const struct v4l2_rect *c_rect, - const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - int max_w, int max_h, enum vpdma_channel chan, u32 flags); -void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width, - int stride, const struct v4l2_rect *c_rect, - const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - int max_w, int max_h, int raw_vpdma_chan, u32 flags); - -void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, - int stride, const struct v4l2_rect *c_rect, - const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - enum vpdma_channel chan, int field, u32 flags, int frame_width, - int frame_height, int start_h, int start_v); -int vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num, - int *channels, int size); - -/* vpdma list interrupt management */ -void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int irq_num, - int list_num, bool enable); -void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num, - int list_num); -unsigned int vpdma_get_list_stat(struct vpdma_data *vpdma, int irq_num); -unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num); - -/* vpdma client configuration */ -void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode, - enum vpdma_channel chan); -void vpdma_set_frame_start_event(struct vpdma_data *vpdma, - enum vpdma_frame_start_event fs_event, enum vpdma_channel chan); -void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, - u32 width, u32 height); - -void vpdma_set_bg_color(struct vpdma_data *vpdma, - struct vpdma_data_format *fmt, u32 color); -void vpdma_dump_regs(struct vpdma_data *vpdma); - -/* initialize vpdma, passed with VPE's platform device pointer */ -int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma, - void (*cb)(struct platform_device *pdev)); - -#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h --- a/drivers/media/platform/ti-vpe/vpdma_priv.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/vpdma_priv.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,639 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#ifndef _TI_VPDMA_PRIV_H_ -#define _TI_VPDMA_PRIV_H_ - -/* - * VPDMA Register offsets - */ - -/* Top level */ -#define VPDMA_PID 0x00 -#define VPDMA_LIST_ADDR 0x04 -#define VPDMA_LIST_ATTR 0x08 -#define VPDMA_LIST_STAT_SYNC 0x0c -#define VPDMA_BG_RGB 0x18 -#define VPDMA_BG_YUV 0x1c -#define VPDMA_SETUP 0x30 -#define VPDMA_MAX_SIZE1 0x34 -#define VPDMA_MAX_SIZE2 0x38 -#define VPDMA_MAX_SIZE3 0x3c -#define VPDMA_MAX_SIZE_WIDTH_MASK 0xffff -#define VPDMA_MAX_SIZE_WIDTH_SHFT 16 -#define VPDMA_MAX_SIZE_HEIGHT_MASK 0xffff -#define VPDMA_MAX_SIZE_HEIGHT_SHFT 0 - -/* Interrupts */ -#define VPDMA_INT_CHAN_STAT(grp) (0x40 + grp * 8) -#define VPDMA_INT_CHAN_MASK(grp) (VPDMA_INT_CHAN_STAT(grp) + 4) -#define VPDMA_INT_CLIENT0_STAT 0x78 -#define VPDMA_INT_CLIENT0_MASK 0x7c -#define VPDMA_INT_CLIENT1_STAT 0x80 -#define VPDMA_INT_CLIENT1_MASK 0x84 -#define VPDMA_INT_LIST0_STAT 0x88 -#define VPDMA_INT_LIST0_MASK 0x8c - -#define VPDMA_INTX_OFFSET 0x50 - -#define VPDMA_PERFMON(i) (0x200 + i * 4) - -/* VIP/VPE client registers */ -#define VPDMA_DEI_CHROMA1_CSTAT 0x0300 -#define VPDMA_DEI_LUMA1_CSTAT 0x0304 -#define VPDMA_DEI_LUMA2_CSTAT 0x0308 -#define VPDMA_DEI_CHROMA2_CSTAT 0x030c -#define VPDMA_DEI_LUMA3_CSTAT 0x0310 -#define VPDMA_DEI_CHROMA3_CSTAT 0x0314 -#define VPDMA_DEI_MV_IN_CSTAT 0x0330 -#define VPDMA_DEI_MV_OUT_CSTAT 0x033c -#define VPDMA_VIP_LO_Y_CSTAT 0x0388 -#define VPDMA_VIP_LO_UV_CSTAT 0x038c -#define VPDMA_VIP_UP_Y_CSTAT 0x0390 -#define VPDMA_VIP_UP_UV_CSTAT 0x0394 -#define VPDMA_VPI_CTL_CSTAT 0x03d0 - -/* Reg field info for VPDMA_CLIENT_CSTAT registers */ -#define VPDMA_CSTAT_LINE_MODE_MASK 0x03 -#define VPDMA_CSTAT_LINE_MODE_SHIFT 8 -#define VPDMA_CSTAT_FRAME_START_MASK 0xf -#define VPDMA_CSTAT_FRAME_START_SHIFT 10 - -#define VPDMA_LIST_NUM_MASK 0x07 -#define VPDMA_LIST_NUM_SHFT 24 -#define VPDMA_LIST_STOP_SHFT 20 -#define VPDMA_LIST_RDY_MASK 0x01 -#define VPDMA_LIST_RDY_SHFT 19 -#define VPDMA_LIST_TYPE_MASK 0x03 -#define VPDMA_LIST_TYPE_SHFT 16 -#define VPDMA_LIST_SIZE_MASK 0xffff - -/* - * The YUV data type definition below are taken from - * both the TRM and i839 Errata information. - * Use the correct data type considering byte - * reordering of components. - * - * Also since the single use of "C" in the 422 case - * to mean "Cr" (i.e. V component). It was decided - * to explicitly label them CR to remove any confusion. - * Bear in mind that the type label refer to the memory - * packed order (LSB - MSB). - */ -#define DATA_TYPE_Y444 0x0 -#define DATA_TYPE_Y422 0x1 -#define DATA_TYPE_Y420 0x2 -#define DATA_TYPE_C444 0x4 -#define DATA_TYPE_C422 0x5 -#define DATA_TYPE_C420 0x6 -#define DATA_TYPE_CB420 0x16 -#define DATA_TYPE_YC444 0x8 -#define DATA_TYPE_YCB422 0x7 -#define DATA_TYPE_YCR422 0x17 -#define DATA_TYPE_CBY422 0x27 -#define DATA_TYPE_CRY422 0x37 - -/* - * The RGB data type definition below are defined - * to follow Errata i819. - * The initial values were taken from: - * VPDMA_data_type_mapping_v0.2vayu_c.pdf - * But some of the ARGB definition appeared to be wrong - * in the document also. As they would yield RGBA instead. - * They have been corrected based on experimentation. - */ -#define DATA_TYPE_RGB16_565 0x10 -#define DATA_TYPE_ARGB_1555 0x13 -#define DATA_TYPE_ARGB_4444 0x14 -#define DATA_TYPE_RGBA_5551 0x11 -#define DATA_TYPE_RGBA_4444 0x12 -#define DATA_TYPE_ARGB24_6666 0x18 -#define DATA_TYPE_RGB24_888 0x16 -#define DATA_TYPE_ARGB32_8888 0x17 -#define DATA_TYPE_RGBA24_6666 0x15 -#define DATA_TYPE_RGBA32_8888 0x19 -#define DATA_TYPE_BGR16_565 0x0 -#define DATA_TYPE_ABGR_1555 0x3 -#define DATA_TYPE_ABGR_4444 0x4 -#define DATA_TYPE_BGRA_5551 0x1 -#define DATA_TYPE_BGRA_4444 0x2 -#define DATA_TYPE_ABGR24_6666 0x8 -#define DATA_TYPE_BGR24_888 0x6 -#define DATA_TYPE_ABGR32_8888 0x7 -#define DATA_TYPE_BGRA24_6666 0x5 -#define DATA_TYPE_BGRA32_8888 0x9 - -#define DATA_TYPE_MV 0x3 - -/* VPDMA channel numbers, some are common between VIP/VPE and appear twice */ -#define VPE_CHAN_NUM_LUMA1_IN 0 -#define VPE_CHAN_NUM_CHROMA1_IN 1 -#define VPE_CHAN_NUM_LUMA2_IN 2 -#define VPE_CHAN_NUM_CHROMA2_IN 3 -#define VPE_CHAN_NUM_LUMA3_IN 4 -#define VPE_CHAN_NUM_CHROMA3_IN 5 -#define VPE_CHAN_NUM_MV_IN 12 -#define VPE_CHAN_NUM_MV_OUT 15 -#define VIP1_CHAN_NUM_MULT_PORT_A_SRC0 38 -#define VIP1_CHAN_NUM_MULT_ANC_A_SRC0 70 -#define VPE_CHAN_NUM_LUMA_OUT 102 -#define VPE_CHAN_NUM_CHROMA_OUT 103 -#define VIP1_CHAN_NUM_PORT_A_LUMA 102 -#define VIP1_CHAN_NUM_PORT_A_CHROMA 103 -#define VPE_CHAN_NUM_RGB_OUT 106 -#define VIP1_CHAN_NUM_PORT_A_RGB 106 -#define VIP1_CHAN_NUM_PORT_B_RGB 107 -/* - * a VPDMA address data block payload for a configuration descriptor needs to - * have each sub block length as a multiple of 16 bytes. Therefore, the overall - * size of the payload also needs to be a multiple of 16 bytes. The sub block - * lengths should be ensured to be aligned by the VPDMA user. - */ -#define VPDMA_ADB_SIZE_ALIGN 0x0f - -/* - * data transfer descriptor - */ -struct vpdma_dtd { - u32 type_ctl_stride; - union { - u32 xfer_length_height; - u32 w1; - }; - u32 start_addr; - u32 pkt_ctl; - union { - u32 frame_width_height; /* inbound */ - u32 desc_write_addr; /* outbound */ - }; - union { - u32 start_h_v; /* inbound */ - u32 max_width_height; /* outbound */ - }; - u32 client_attr0; - u32 client_attr1; -}; - -/* Data Transfer Descriptor specifics */ -#define DTD_NO_NOTIFY 0 -#define DTD_NOTIFY 1 - -#define DTD_PKT_TYPE 0xa -#define DTD_DIR_IN 0 -#define DTD_DIR_OUT 1 - -/* type_ctl_stride */ -#define DTD_DATA_TYPE_MASK 0x3f -#define DTD_DATA_TYPE_SHFT 26 -#define DTD_NOTIFY_MASK 0x01 -#define DTD_NOTIFY_SHFT 25 -#define DTD_FIELD_MASK 0x01 -#define DTD_FIELD_SHFT 24 -#define DTD_1D_MASK 0x01 -#define DTD_1D_SHFT 23 -#define DTD_EVEN_LINE_SKIP_MASK 0x01 -#define DTD_EVEN_LINE_SKIP_SHFT 20 -#define DTD_ODD_LINE_SKIP_MASK 0x01 -#define DTD_ODD_LINE_SKIP_SHFT 16 -#define DTD_LINE_STRIDE_MASK 0xffff -#define DTD_LINE_STRIDE_SHFT 0 - -/* xfer_length_height */ -#define DTD_LINE_LENGTH_MASK 0xffff -#define DTD_LINE_LENGTH_SHFT 16 -#define DTD_XFER_HEIGHT_MASK 0xffff -#define DTD_XFER_HEIGHT_SHFT 0 - -/* pkt_ctl */ -#define DTD_PKT_TYPE_MASK 0x1f -#define DTD_PKT_TYPE_SHFT 27 -#define DTD_MODE_MASK 0x01 -#define DTD_MODE_SHFT 26 -#define DTD_DIR_MASK 0x01 -#define DTD_DIR_SHFT 25 -#define DTD_CHAN_MASK 0x01ff -#define DTD_CHAN_SHFT 16 -#define DTD_PRI_MASK 0x0f -#define DTD_PRI_SHFT 9 -#define DTD_NEXT_CHAN_MASK 0x01ff -#define DTD_NEXT_CHAN_SHFT 0 - -/* frame_width_height */ -#define DTD_FRAME_WIDTH_MASK 0xffff -#define DTD_FRAME_WIDTH_SHFT 16 -#define DTD_FRAME_HEIGHT_MASK 0xffff -#define DTD_FRAME_HEIGHT_SHFT 0 - -/* start_h_v */ -#define DTD_H_START_MASK 0xffff -#define DTD_H_START_SHFT 16 -#define DTD_V_START_MASK 0xffff -#define DTD_V_START_SHFT 0 - -#define DTD_DESC_START_MASK 0xffffffe0 -#define DTD_DESC_START_SHIFT 5 -#define DTD_WRITE_DESC_MASK 0x01 -#define DTD_WRITE_DESC_SHIFT 2 -#define DTD_DROP_DATA_MASK 0x01 -#define DTD_DROP_DATA_SHIFT 1 -#define DTD_USE_DESC_MASK 0x01 -#define DTD_USE_DESC_SHIFT 0 - -/* max_width_height */ -#define DTD_MAX_WIDTH_MASK 0x07 -#define DTD_MAX_WIDTH_SHFT 4 -#define DTD_MAX_HEIGHT_MASK 0x07 -#define DTD_MAX_HEIGHT_SHFT 0 - -static inline u32 dtd_type_ctl_stride(int type, bool notify, int field, - bool one_d, bool even_line_skip, bool odd_line_skip, - int line_stride) -{ - return (type << DTD_DATA_TYPE_SHFT) | (notify << DTD_NOTIFY_SHFT) | - (field << DTD_FIELD_SHFT) | (one_d << DTD_1D_SHFT) | - (even_line_skip << DTD_EVEN_LINE_SKIP_SHFT) | - (odd_line_skip << DTD_ODD_LINE_SKIP_SHFT) | - line_stride; -} - -static inline u32 dtd_xfer_length_height(int line_length, int xfer_height) -{ - return (line_length << DTD_LINE_LENGTH_SHFT) | xfer_height; -} - -static inline u32 dtd_pkt_ctl(bool mode, bool dir, int chan, int pri, - int next_chan) -{ - return (DTD_PKT_TYPE << DTD_PKT_TYPE_SHFT) | (mode << DTD_MODE_SHFT) | - (dir << DTD_DIR_SHFT) | (chan << DTD_CHAN_SHFT) | - (pri << DTD_PRI_SHFT) | next_chan; -} - -static inline u32 dtd_frame_width_height(int width, int height) -{ - return (width << DTD_FRAME_WIDTH_SHFT) | height; -} - -static inline u32 dtd_desc_write_addr(unsigned int addr, bool write_desc, - bool drop_data, bool use_desc) -{ - return (addr & DTD_DESC_START_MASK) | - (write_desc << DTD_WRITE_DESC_SHIFT) | - (drop_data << DTD_DROP_DATA_SHIFT) | - use_desc; -} - -static inline u32 dtd_start_h_v(int h_start, int v_start) -{ - return (h_start << DTD_H_START_SHFT) | v_start; -} - -static inline u32 dtd_max_width_height(int max_width, int max_height) -{ - return (max_width << DTD_MAX_WIDTH_SHFT) | max_height; -} - -static inline int dtd_get_data_type(struct vpdma_dtd *dtd) -{ - return dtd->type_ctl_stride >> DTD_DATA_TYPE_SHFT; -} - -static inline bool dtd_get_notify(struct vpdma_dtd *dtd) -{ - return (dtd->type_ctl_stride >> DTD_NOTIFY_SHFT) & DTD_NOTIFY_MASK; -} - -static inline int dtd_get_field(struct vpdma_dtd *dtd) -{ - return (dtd->type_ctl_stride >> DTD_FIELD_SHFT) & DTD_FIELD_MASK; -} - -static inline bool dtd_get_1d(struct vpdma_dtd *dtd) -{ - return (dtd->type_ctl_stride >> DTD_1D_SHFT) & DTD_1D_MASK; -} - -static inline bool dtd_get_even_line_skip(struct vpdma_dtd *dtd) -{ - return (dtd->type_ctl_stride >> DTD_EVEN_LINE_SKIP_SHFT) - & DTD_EVEN_LINE_SKIP_MASK; -} - -static inline bool dtd_get_odd_line_skip(struct vpdma_dtd *dtd) -{ - return (dtd->type_ctl_stride >> DTD_ODD_LINE_SKIP_SHFT) - & DTD_ODD_LINE_SKIP_MASK; -} - -static inline int dtd_get_line_stride(struct vpdma_dtd *dtd) -{ - return dtd->type_ctl_stride & DTD_LINE_STRIDE_MASK; -} - -static inline int dtd_get_line_length(struct vpdma_dtd *dtd) -{ - return dtd->xfer_length_height >> DTD_LINE_LENGTH_SHFT; -} - -static inline int dtd_get_xfer_height(struct vpdma_dtd *dtd) -{ - return dtd->xfer_length_height & DTD_XFER_HEIGHT_MASK; -} - -static inline int dtd_get_pkt_type(struct vpdma_dtd *dtd) -{ - return dtd->pkt_ctl >> DTD_PKT_TYPE_SHFT; -} - -static inline bool dtd_get_mode(struct vpdma_dtd *dtd) -{ - return (dtd->pkt_ctl >> DTD_MODE_SHFT) & DTD_MODE_MASK; -} - -static inline bool dtd_get_dir(struct vpdma_dtd *dtd) -{ - return (dtd->pkt_ctl >> DTD_DIR_SHFT) & DTD_DIR_MASK; -} - -static inline int dtd_get_chan(struct vpdma_dtd *dtd) -{ - return (dtd->pkt_ctl >> DTD_CHAN_SHFT) & DTD_CHAN_MASK; -} - -static inline int dtd_get_priority(struct vpdma_dtd *dtd) -{ - return (dtd->pkt_ctl >> DTD_PRI_SHFT) & DTD_PRI_MASK; -} - -static inline int dtd_get_next_chan(struct vpdma_dtd *dtd) -{ - return (dtd->pkt_ctl >> DTD_NEXT_CHAN_SHFT) & DTD_NEXT_CHAN_MASK; -} - -static inline int dtd_get_frame_width(struct vpdma_dtd *dtd) -{ - return dtd->frame_width_height >> DTD_FRAME_WIDTH_SHFT; -} - -static inline int dtd_get_frame_height(struct vpdma_dtd *dtd) -{ - return dtd->frame_width_height & DTD_FRAME_HEIGHT_MASK; -} - -static inline int dtd_get_desc_write_addr(struct vpdma_dtd *dtd) -{ - return dtd->desc_write_addr & DTD_DESC_START_MASK; -} - -static inline bool dtd_get_write_desc(struct vpdma_dtd *dtd) -{ - return (dtd->desc_write_addr >> DTD_WRITE_DESC_SHIFT) & - DTD_WRITE_DESC_MASK; -} - -static inline bool dtd_get_drop_data(struct vpdma_dtd *dtd) -{ - return (dtd->desc_write_addr >> DTD_DROP_DATA_SHIFT) & - DTD_DROP_DATA_MASK; -} - -static inline bool dtd_get_use_desc(struct vpdma_dtd *dtd) -{ - return dtd->desc_write_addr & DTD_USE_DESC_MASK; -} - -static inline int dtd_get_h_start(struct vpdma_dtd *dtd) -{ - return dtd->start_h_v >> DTD_H_START_SHFT; -} - -static inline int dtd_get_v_start(struct vpdma_dtd *dtd) -{ - return dtd->start_h_v & DTD_V_START_MASK; -} - -static inline int dtd_get_max_width(struct vpdma_dtd *dtd) -{ - return (dtd->max_width_height >> DTD_MAX_WIDTH_SHFT) & - DTD_MAX_WIDTH_MASK; -} - -static inline int dtd_get_max_height(struct vpdma_dtd *dtd) -{ - return (dtd->max_width_height >> DTD_MAX_HEIGHT_SHFT) & - DTD_MAX_HEIGHT_MASK; -} - -/* - * configuration descriptor - */ -struct vpdma_cfd { - union { - u32 dest_addr_offset; - u32 w0; - }; - union { - u32 block_len; /* in words */ - u32 w1; - }; - u32 payload_addr; - u32 ctl_payload_len; /* in words */ -}; - -/* Configuration descriptor specifics */ - -#define CFD_PKT_TYPE 0xb - -#define CFD_DIRECT 1 -#define CFD_INDIRECT 0 -#define CFD_CLS_ADB 0 -#define CFD_CLS_BLOCK 1 - -/* block_len */ -#define CFD__BLOCK_LEN_MASK 0xffff -#define CFD__BLOCK_LEN_SHFT 0 - -/* ctl_payload_len */ -#define CFD_PKT_TYPE_MASK 0x1f -#define CFD_PKT_TYPE_SHFT 27 -#define CFD_DIRECT_MASK 0x01 -#define CFD_DIRECT_SHFT 26 -#define CFD_CLASS_MASK 0x03 -#define CFD_CLASS_SHFT 24 -#define CFD_DEST_MASK 0xff -#define CFD_DEST_SHFT 16 -#define CFD_PAYLOAD_LEN_MASK 0xffff -#define CFD_PAYLOAD_LEN_SHFT 0 - -static inline u32 cfd_pkt_payload_len(bool direct, int cls, int dest, - int payload_len) -{ - return (CFD_PKT_TYPE << CFD_PKT_TYPE_SHFT) | - (direct << CFD_DIRECT_SHFT) | - (cls << CFD_CLASS_SHFT) | - (dest << CFD_DEST_SHFT) | - payload_len; -} - -static inline int cfd_get_pkt_type(struct vpdma_cfd *cfd) -{ - return cfd->ctl_payload_len >> CFD_PKT_TYPE_SHFT; -} - -static inline bool cfd_get_direct(struct vpdma_cfd *cfd) -{ - return (cfd->ctl_payload_len >> CFD_DIRECT_SHFT) & CFD_DIRECT_MASK; -} - -static inline bool cfd_get_class(struct vpdma_cfd *cfd) -{ - return (cfd->ctl_payload_len >> CFD_CLASS_SHFT) & CFD_CLASS_MASK; -} - -static inline int cfd_get_dest(struct vpdma_cfd *cfd) -{ - return (cfd->ctl_payload_len >> CFD_DEST_SHFT) & CFD_DEST_MASK; -} - -static inline int cfd_get_payload_len(struct vpdma_cfd *cfd) -{ - return cfd->ctl_payload_len & CFD_PAYLOAD_LEN_MASK; -} - -/* - * control descriptor - */ -struct vpdma_ctd { - union { - u32 timer_value; - u32 list_addr; - u32 w0; - }; - union { - u32 pixel_line_count; - u32 list_size; - u32 w1; - }; - union { - u32 event; - u32 fid_ctl; - u32 w2; - }; - u32 type_source_ctl; -}; - -/* control descriptor types */ -#define CTD_TYPE_SYNC_ON_CLIENT 0 -#define CTD_TYPE_SYNC_ON_LIST 1 -#define CTD_TYPE_SYNC_ON_EXT 2 -#define CTD_TYPE_SYNC_ON_LM_TIMER 3 -#define CTD_TYPE_SYNC_ON_CHANNEL 4 -#define CTD_TYPE_CHNG_CLIENT_IRQ 5 -#define CTD_TYPE_SEND_IRQ 6 -#define CTD_TYPE_RELOAD_LIST 7 -#define CTD_TYPE_ABORT_CHANNEL 8 - -#define CTD_PKT_TYPE 0xc - -/* timer_value */ -#define CTD_TIMER_VALUE_MASK 0xffff -#define CTD_TIMER_VALUE_SHFT 0 - -/* pixel_line_count */ -#define CTD_PIXEL_COUNT_MASK 0xffff -#define CTD_PIXEL_COUNT_SHFT 16 -#define CTD_LINE_COUNT_MASK 0xffff -#define CTD_LINE_COUNT_SHFT 0 - -/* list_size */ -#define CTD_LIST_SIZE_MASK 0xffff -#define CTD_LIST_SIZE_SHFT 0 - -/* event */ -#define CTD_EVENT_MASK 0x0f -#define CTD_EVENT_SHFT 0 - -/* fid_ctl */ -#define CTD_FID2_MASK 0x03 -#define CTD_FID2_SHFT 4 -#define CTD_FID1_MASK 0x03 -#define CTD_FID1_SHFT 2 -#define CTD_FID0_MASK 0x03 -#define CTD_FID0_SHFT 0 - -/* type_source_ctl */ -#define CTD_PKT_TYPE_MASK 0x1f -#define CTD_PKT_TYPE_SHFT 27 -#define CTD_SOURCE_MASK 0xff -#define CTD_SOURCE_SHFT 16 -#define CTD_CONTROL_MASK 0x0f -#define CTD_CONTROL_SHFT 0 - -static inline u32 ctd_pixel_line_count(int pixel_count, int line_count) -{ - return (pixel_count << CTD_PIXEL_COUNT_SHFT) | line_count; -} - -static inline u32 ctd_set_fid_ctl(int fid0, int fid1, int fid2) -{ - return (fid2 << CTD_FID2_SHFT) | (fid1 << CTD_FID1_SHFT) | fid0; -} - -static inline u32 ctd_type_source_ctl(int source, int control) -{ - return (CTD_PKT_TYPE << CTD_PKT_TYPE_SHFT) | - (source << CTD_SOURCE_SHFT) | control; -} - -static inline u32 ctd_get_pixel_count(struct vpdma_ctd *ctd) -{ - return ctd->pixel_line_count >> CTD_PIXEL_COUNT_SHFT; -} - -static inline int ctd_get_line_count(struct vpdma_ctd *ctd) -{ - return ctd->pixel_line_count & CTD_LINE_COUNT_MASK; -} - -static inline int ctd_get_event(struct vpdma_ctd *ctd) -{ - return ctd->event & CTD_EVENT_MASK; -} - -static inline int ctd_get_fid2_ctl(struct vpdma_ctd *ctd) -{ - return (ctd->fid_ctl >> CTD_FID2_SHFT) & CTD_FID2_MASK; -} - -static inline int ctd_get_fid1_ctl(struct vpdma_ctd *ctd) -{ - return (ctd->fid_ctl >> CTD_FID1_SHFT) & CTD_FID1_MASK; -} - -static inline int ctd_get_fid0_ctl(struct vpdma_ctd *ctd) -{ - return ctd->fid_ctl & CTD_FID2_MASK; -} - -static inline int ctd_get_pkt_type(struct vpdma_ctd *ctd) -{ - return ctd->type_source_ctl >> CTD_PKT_TYPE_SHFT; -} - -static inline int ctd_get_source(struct vpdma_ctd *ctd) -{ - return (ctd->type_source_ctl >> CTD_SOURCE_SHFT) & CTD_SOURCE_MASK; -} - -static inline int ctd_get_ctl(struct vpdma_ctd *ctd) -{ - return ctd->type_source_ctl & CTD_CONTROL_MASK; -} - -#endif diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c --- a/drivers/media/platform/ti-vpe/vpe.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/vpe.c 1969-12-31 19:00:00.000000000 -0500 @@ -1,2669 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * TI VPE mem2mem driver, based on the virtual v4l2-mem2mem example driver - * - * Copyright (c) 2013 Texas Instruments Inc. - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - * - * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. - * Pawel Osciak, - * Marek Szyprowski, - * - * Based on the virtual v4l2-mem2mem example device - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vpdma.h" -#include "vpdma_priv.h" -#include "vpe_regs.h" -#include "sc.h" -#include "csc.h" - -#define VPE_MODULE_NAME "vpe" - -/* minimum and maximum frame sizes */ -#define MIN_W 32 -#define MIN_H 32 -#define MAX_W 2048 -#define MAX_H 2048 - -/* required alignments */ -#define S_ALIGN 0 /* multiple of 1 */ -#define H_ALIGN 1 /* multiple of 2 */ - -/* flags that indicate a format can be used for capture/output */ -#define VPE_FMT_TYPE_CAPTURE (1 << 0) -#define VPE_FMT_TYPE_OUTPUT (1 << 1) - -/* used as plane indices */ -#define VPE_MAX_PLANES 2 -#define VPE_LUMA 0 -#define VPE_CHROMA 1 - -/* per m2m context info */ -#define VPE_MAX_SRC_BUFS 3 /* need 3 src fields to de-interlace */ - -#define VPE_DEF_BUFS_PER_JOB 1 /* default one buffer per batch job */ - -/* - * each VPE context can need up to 3 config descriptors, 7 input descriptors, - * 3 output descriptors, and 10 control descriptors - */ -#define VPE_DESC_LIST_SIZE (10 * VPDMA_DTD_DESC_SIZE + \ - 13 * VPDMA_CFD_CTD_DESC_SIZE) - -#define vpe_dbg(vpedev, fmt, arg...) \ - dev_dbg((vpedev)->v4l2_dev.dev, fmt, ##arg) -#define vpe_err(vpedev, fmt, arg...) \ - dev_err((vpedev)->v4l2_dev.dev, fmt, ##arg) - -struct vpe_us_coeffs { - unsigned short anchor_fid0_c0; - unsigned short anchor_fid0_c1; - unsigned short anchor_fid0_c2; - unsigned short anchor_fid0_c3; - unsigned short interp_fid0_c0; - unsigned short interp_fid0_c1; - unsigned short interp_fid0_c2; - unsigned short interp_fid0_c3; - unsigned short anchor_fid1_c0; - unsigned short anchor_fid1_c1; - unsigned short anchor_fid1_c2; - unsigned short anchor_fid1_c3; - unsigned short interp_fid1_c0; - unsigned short interp_fid1_c1; - unsigned short interp_fid1_c2; - unsigned short interp_fid1_c3; -}; - -/* - * Default upsampler coefficients - */ -static const struct vpe_us_coeffs us_coeffs[] = { - { - /* Coefficients for progressive input */ - 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, - 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, - }, - { - /* Coefficients for Top Field Interlaced input */ - 0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3, - /* Coefficients for Bottom Field Interlaced input */ - 0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9, - }, -}; - -/* - * the following registers are for configuring some of the parameters of the - * motion and edge detection blocks inside DEI, these generally remain the same, - * these could be passed later via userspace if some one needs to tweak these. - */ -struct vpe_dei_regs { - unsigned long mdt_spacial_freq_thr_reg; /* VPE_DEI_REG2 */ - unsigned long edi_config_reg; /* VPE_DEI_REG3 */ - unsigned long edi_lut_reg0; /* VPE_DEI_REG4 */ - unsigned long edi_lut_reg1; /* VPE_DEI_REG5 */ - unsigned long edi_lut_reg2; /* VPE_DEI_REG6 */ - unsigned long edi_lut_reg3; /* VPE_DEI_REG7 */ -}; - -/* - * default expert DEI register values, unlikely to be modified. - */ -static const struct vpe_dei_regs dei_regs = { - .mdt_spacial_freq_thr_reg = 0x020C0804u, - .edi_config_reg = 0x0118100Cu, - .edi_lut_reg0 = 0x08040200u, - .edi_lut_reg1 = 0x1010100Cu, - .edi_lut_reg2 = 0x10101010u, - .edi_lut_reg3 = 0x10101010u, -}; - -/* - * The port_data structure contains per-port data. - */ -struct vpe_port_data { - enum vpdma_channel channel; /* VPDMA channel */ - u8 vb_index; /* input frame f, f-1, f-2 index */ - u8 vb_part; /* plane index for co-panar formats */ -}; - -/* - * Define indices into the port_data tables - */ -#define VPE_PORT_LUMA1_IN 0 -#define VPE_PORT_CHROMA1_IN 1 -#define VPE_PORT_LUMA2_IN 2 -#define VPE_PORT_CHROMA2_IN 3 -#define VPE_PORT_LUMA3_IN 4 -#define VPE_PORT_CHROMA3_IN 5 -#define VPE_PORT_MV_IN 6 -#define VPE_PORT_MV_OUT 7 -#define VPE_PORT_LUMA_OUT 8 -#define VPE_PORT_CHROMA_OUT 9 -#define VPE_PORT_RGB_OUT 10 - -static const struct vpe_port_data port_data[11] = { - [VPE_PORT_LUMA1_IN] = { - .channel = VPE_CHAN_LUMA1_IN, - .vb_index = 0, - .vb_part = VPE_LUMA, - }, - [VPE_PORT_CHROMA1_IN] = { - .channel = VPE_CHAN_CHROMA1_IN, - .vb_index = 0, - .vb_part = VPE_CHROMA, - }, - [VPE_PORT_LUMA2_IN] = { - .channel = VPE_CHAN_LUMA2_IN, - .vb_index = 1, - .vb_part = VPE_LUMA, - }, - [VPE_PORT_CHROMA2_IN] = { - .channel = VPE_CHAN_CHROMA2_IN, - .vb_index = 1, - .vb_part = VPE_CHROMA, - }, - [VPE_PORT_LUMA3_IN] = { - .channel = VPE_CHAN_LUMA3_IN, - .vb_index = 2, - .vb_part = VPE_LUMA, - }, - [VPE_PORT_CHROMA3_IN] = { - .channel = VPE_CHAN_CHROMA3_IN, - .vb_index = 2, - .vb_part = VPE_CHROMA, - }, - [VPE_PORT_MV_IN] = { - .channel = VPE_CHAN_MV_IN, - }, - [VPE_PORT_MV_OUT] = { - .channel = VPE_CHAN_MV_OUT, - }, - [VPE_PORT_LUMA_OUT] = { - .channel = VPE_CHAN_LUMA_OUT, - .vb_part = VPE_LUMA, - }, - [VPE_PORT_CHROMA_OUT] = { - .channel = VPE_CHAN_CHROMA_OUT, - .vb_part = VPE_CHROMA, - }, - [VPE_PORT_RGB_OUT] = { - .channel = VPE_CHAN_RGB_OUT, - .vb_part = VPE_LUMA, - }, -}; - - -/* driver info for each of the supported video formats */ -struct vpe_fmt { - u32 fourcc; /* standard format identifier */ - u8 types; /* CAPTURE and/or OUTPUT */ - u8 coplanar; /* set for unpacked Luma and Chroma */ - /* vpdma format info for each plane */ - struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES]; -}; - -static struct vpe_fmt vpe_formats[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, - .coplanar = 1, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444], - &vpdma_yuv_fmts[VPDMA_DATA_FMT_C444], - }, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, - .coplanar = 1, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], - &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], - }, - }, - { - .fourcc = V4L2_PIX_FMT_NV21, - .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, - .coplanar = 1, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], - &vpdma_yuv_fmts[VPDMA_DATA_FMT_CB420], - }, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, - .coplanar = 0, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422], - }, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, - .coplanar = 0, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422], - }, - }, - { - .fourcc = V4L2_PIX_FMT_RGB24, - .types = VPE_FMT_TYPE_CAPTURE, - .coplanar = 0, - .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], - }, - }, - { - .fourcc = V4L2_PIX_FMT_RGB32, - .types = VPE_FMT_TYPE_CAPTURE, - .coplanar = 0, - .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], - }, - }, - { - .fourcc = V4L2_PIX_FMT_BGR24, - .types = VPE_FMT_TYPE_CAPTURE, - .coplanar = 0, - .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], - }, - }, - { - .fourcc = V4L2_PIX_FMT_BGR32, - .types = VPE_FMT_TYPE_CAPTURE, - .coplanar = 0, - .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], - }, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .types = VPE_FMT_TYPE_CAPTURE, - .coplanar = 0, - .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB565], - }, - }, - { - .fourcc = V4L2_PIX_FMT_RGB555, - .types = VPE_FMT_TYPE_CAPTURE, - .coplanar = 0, - .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGBA16_5551], - }, - }, -}; - -/* - * per-queue, driver-specific private data. - * there is one source queue and one destination queue for each m2m context. - */ -struct vpe_q_data { - /* current v4l2 format info */ - struct v4l2_format format; - unsigned int flags; - struct v4l2_rect c_rect; /* crop/compose rectangle */ - struct vpe_fmt *fmt; /* format info */ -}; - -/* vpe_q_data flag bits */ -#define Q_DATA_FRAME_1D BIT(0) -#define Q_DATA_MODE_TILED BIT(1) -#define Q_DATA_INTERLACED_ALTERNATE BIT(2) -#define Q_DATA_INTERLACED_SEQ_TB BIT(3) -#define Q_DATA_INTERLACED_SEQ_BT BIT(4) - -#define Q_IS_SEQ_XX (Q_DATA_INTERLACED_SEQ_TB | \ - Q_DATA_INTERLACED_SEQ_BT) - -#define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \ - Q_DATA_INTERLACED_SEQ_TB | \ - Q_DATA_INTERLACED_SEQ_BT) - -enum { - Q_DATA_SRC = 0, - Q_DATA_DST = 1, -}; - -/* find our format description corresponding to the passed v4l2_format */ -static struct vpe_fmt *__find_format(u32 fourcc) -{ - struct vpe_fmt *fmt; - unsigned int k; - - for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) { - fmt = &vpe_formats[k]; - if (fmt->fourcc == fourcc) - return fmt; - } - - return NULL; -} - -static struct vpe_fmt *find_format(struct v4l2_format *f) -{ - return __find_format(f->fmt.pix.pixelformat); -} - -/* - * there is one vpe_dev structure in the driver, it is shared by - * all instances. - */ -struct vpe_dev { - struct v4l2_device v4l2_dev; - struct video_device vfd; - struct v4l2_m2m_dev *m2m_dev; - - atomic_t num_instances; /* count of driver instances */ - dma_addr_t loaded_mmrs; /* shadow mmrs in device */ - struct mutex dev_mutex; - spinlock_t lock; - - int irq; - void __iomem *base; - struct resource *res; - - struct vpdma_data vpdma_data; - struct vpdma_data *vpdma; /* vpdma data handle */ - struct sc_data *sc; /* scaler data handle */ - struct csc_data *csc; /* csc data handle */ -}; - -/* - * There is one vpe_ctx structure for each m2m context. - */ -struct vpe_ctx { - struct v4l2_fh fh; - struct vpe_dev *dev; - struct v4l2_ctrl_handler hdl; - - unsigned int field; /* current field */ - unsigned int sequence; /* current frame/field seq */ - unsigned int aborting; /* abort after next irq */ - - unsigned int bufs_per_job; /* input buffers per batch */ - unsigned int bufs_completed; /* bufs done in this batch */ - - struct vpe_q_data q_data[2]; /* src & dst queue data */ - struct vb2_v4l2_buffer *src_vbs[VPE_MAX_SRC_BUFS]; - struct vb2_v4l2_buffer *dst_vb; - - dma_addr_t mv_buf_dma[2]; /* dma addrs of motion vector in/out bufs */ - void *mv_buf[2]; /* virtual addrs of motion vector bufs */ - size_t mv_buf_size; /* current motion vector buffer size */ - struct vpdma_buf mmr_adb; /* shadow reg addr/data block */ - struct vpdma_buf sc_coeff_h; /* h coeff buffer */ - struct vpdma_buf sc_coeff_v; /* v coeff buffer */ - struct vpdma_desc_list desc_list; /* DMA descriptor list */ - - bool deinterlacing; /* using de-interlacer */ - bool load_mmrs; /* have new shadow reg values */ - - unsigned int src_mv_buf_selector; -}; - - -/* - * M2M devices get 2 queues. - * Return the queue given the type. - */ -static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, - enum v4l2_buf_type type) -{ - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return &ctx->q_data[Q_DATA_SRC]; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &ctx->q_data[Q_DATA_DST]; - default: - return NULL; - } - return NULL; -} - -static u32 read_reg(struct vpe_dev *dev, int offset) -{ - return ioread32(dev->base + offset); -} - -static void write_reg(struct vpe_dev *dev, int offset, u32 value) -{ - iowrite32(value, dev->base + offset); -} - -/* register field read/write helpers */ -static int get_field(u32 value, u32 mask, int shift) -{ - return (value & (mask << shift)) >> shift; -} - -static int read_field_reg(struct vpe_dev *dev, int offset, u32 mask, int shift) -{ - return get_field(read_reg(dev, offset), mask, shift); -} - -static void write_field(u32 *valp, u32 field, u32 mask, int shift) -{ - u32 val = *valp; - - val &= ~(mask << shift); - val |= (field & mask) << shift; - *valp = val; -} - -static void write_field_reg(struct vpe_dev *dev, int offset, u32 field, - u32 mask, int shift) -{ - u32 val = read_reg(dev, offset); - - write_field(&val, field, mask, shift); - - write_reg(dev, offset, val); -} - -/* - * DMA address/data block for the shadow registers - */ -struct vpe_mmr_adb { - struct vpdma_adb_hdr out_fmt_hdr; - u32 out_fmt_reg[1]; - u32 out_fmt_pad[3]; - struct vpdma_adb_hdr us1_hdr; - u32 us1_regs[8]; - struct vpdma_adb_hdr us2_hdr; - u32 us2_regs[8]; - struct vpdma_adb_hdr us3_hdr; - u32 us3_regs[8]; - struct vpdma_adb_hdr dei_hdr; - u32 dei_regs[8]; - struct vpdma_adb_hdr sc_hdr0; - u32 sc_regs0[7]; - u32 sc_pad0[1]; - struct vpdma_adb_hdr sc_hdr8; - u32 sc_regs8[6]; - u32 sc_pad8[2]; - struct vpdma_adb_hdr sc_hdr17; - u32 sc_regs17[9]; - u32 sc_pad17[3]; - struct vpdma_adb_hdr csc_hdr; - u32 csc_regs[6]; - u32 csc_pad[2]; -}; - -#define GET_OFFSET_TOP(ctx, obj, reg) \ - ((obj)->res->start - ctx->dev->res->start + reg) - -#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \ - VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a) -/* - * Set the headers for all of the address/data block structures. - */ -static void init_adb_hdrs(struct vpe_ctx *ctx) -{ - VPE_SET_MMR_ADB_HDR(ctx, out_fmt_hdr, out_fmt_reg, VPE_CLK_FORMAT_SELECT); - VPE_SET_MMR_ADB_HDR(ctx, us1_hdr, us1_regs, VPE_US1_R0); - VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0); - VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0); - VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE); - VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0, - GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); - VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8, - GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8)); - VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17, - GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17)); - VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, - GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); -}; - -/* - * Allocate or re-allocate the motion vector DMA buffers - * There are two buffers, one for input and one for output. - * However, the roles are reversed after each field is processed. - * In other words, after each field is processed, the previous - * output (dst) MV buffer becomes the new input (src) MV buffer. - */ -static int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size) -{ - struct device *dev = ctx->dev->v4l2_dev.dev; - - if (ctx->mv_buf_size == size) - return 0; - - if (ctx->mv_buf[0]) - dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0], - ctx->mv_buf_dma[0]); - - if (ctx->mv_buf[1]) - dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1], - ctx->mv_buf_dma[1]); - - if (size == 0) - return 0; - - ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0], - GFP_KERNEL); - if (!ctx->mv_buf[0]) { - vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); - return -ENOMEM; - } - - ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1], - GFP_KERNEL); - if (!ctx->mv_buf[1]) { - vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); - dma_free_coherent(dev, size, ctx->mv_buf[0], - ctx->mv_buf_dma[0]); - - return -ENOMEM; - } - - ctx->mv_buf_size = size; - ctx->src_mv_buf_selector = 0; - - return 0; -} - -static void free_mv_buffers(struct vpe_ctx *ctx) -{ - realloc_mv_buffers(ctx, 0); -} - -/* - * While de-interlacing, we keep the two most recent input buffers - * around. This function frees those two buffers when we have - * finished processing the current stream. - */ -static void free_vbs(struct vpe_ctx *ctx) -{ - struct vpe_dev *dev = ctx->dev; - unsigned long flags; - - if (ctx->src_vbs[2] == NULL) - return; - - spin_lock_irqsave(&dev->lock, flags); - if (ctx->src_vbs[2]) { - v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE); - if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) - v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE); - ctx->src_vbs[2] = NULL; - ctx->src_vbs[1] = NULL; - } - spin_unlock_irqrestore(&dev->lock, flags); -} - -/* - * Enable or disable the VPE clocks - */ -static void vpe_set_clock_enable(struct vpe_dev *dev, bool on) -{ - u32 val = 0; - - if (on) - val = VPE_DATA_PATH_CLK_ENABLE | VPE_VPEDMA_CLK_ENABLE; - write_reg(dev, VPE_CLK_ENABLE, val); -} - -static void vpe_top_reset(struct vpe_dev *dev) -{ - - write_field_reg(dev, VPE_CLK_RESET, 1, VPE_DATA_PATH_CLK_RESET_MASK, - VPE_DATA_PATH_CLK_RESET_SHIFT); - - usleep_range(100, 150); - - write_field_reg(dev, VPE_CLK_RESET, 0, VPE_DATA_PATH_CLK_RESET_MASK, - VPE_DATA_PATH_CLK_RESET_SHIFT); -} - -static void vpe_top_vpdma_reset(struct vpe_dev *dev) -{ - write_field_reg(dev, VPE_CLK_RESET, 1, VPE_VPDMA_CLK_RESET_MASK, - VPE_VPDMA_CLK_RESET_SHIFT); - - usleep_range(100, 150); - - write_field_reg(dev, VPE_CLK_RESET, 0, VPE_VPDMA_CLK_RESET_MASK, - VPE_VPDMA_CLK_RESET_SHIFT); -} - -/* - * Load the correct of upsampler coefficients into the shadow MMRs - */ -static void set_us_coefficients(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - u32 *us1_reg = &mmr_adb->us1_regs[0]; - u32 *us2_reg = &mmr_adb->us2_regs[0]; - u32 *us3_reg = &mmr_adb->us3_regs[0]; - const unsigned short *cp, *end_cp; - - cp = &us_coeffs[0].anchor_fid0_c0; - - if (s_q_data->flags & Q_IS_INTERLACED) /* interlaced */ - cp += sizeof(us_coeffs[0]) / sizeof(*cp); - - end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp); - - while (cp < end_cp) { - write_field(us1_reg, *cp++, VPE_US_C0_MASK, VPE_US_C0_SHIFT); - write_field(us1_reg, *cp++, VPE_US_C1_MASK, VPE_US_C1_SHIFT); - *us2_reg++ = *us1_reg; - *us3_reg++ = *us1_reg++; - } - ctx->load_mmrs = true; -} - -/* - * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs. - */ -static void set_cfg_modes(struct vpe_ctx *ctx) -{ - struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *us1_reg0 = &mmr_adb->us1_regs[0]; - u32 *us2_reg0 = &mmr_adb->us2_regs[0]; - u32 *us3_reg0 = &mmr_adb->us3_regs[0]; - int cfg_mode = 1; - - /* - * Cfg Mode 0: YUV420 source, enable upsampler, DEI is de-interlacing. - * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing. - */ - - if (fmt->fourcc == V4L2_PIX_FMT_NV12 || - fmt->fourcc == V4L2_PIX_FMT_NV21) - cfg_mode = 0; - - write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); - write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); - write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); - - ctx->load_mmrs = true; -} - -static void set_line_modes(struct vpe_ctx *ctx) -{ - struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; - int line_mode = 1; - - if (fmt->fourcc == V4L2_PIX_FMT_NV12 || - fmt->fourcc == V4L2_PIX_FMT_NV21) - line_mode = 0; /* double lines to line buffer */ - - /* regs for now */ - vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN); - vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN); - vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN); - - /* frame start for input luma */ - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_LUMA1_IN); - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_LUMA2_IN); - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_LUMA3_IN); - - /* frame start for input chroma */ - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_CHROMA1_IN); - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_CHROMA2_IN); - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_CHROMA3_IN); - - /* frame start for MV in client */ - vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, - VPE_CHAN_MV_IN); -} - -/* - * Set the shadow registers that are modified when the source - * format changes. - */ -static void set_src_registers(struct vpe_ctx *ctx) -{ - set_us_coefficients(ctx); -} - -/* - * Set the shadow registers that are modified when the destination - * format changes. - */ -static void set_dst_registers(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; - const struct v4l2_format_info *finfo; - u32 val = 0; - - finfo = v4l2_format_info(fmt->fourcc); - if (v4l2_is_format_rgb(finfo)) { - val |= VPE_RGB_OUT_SELECT; - vpdma_set_bg_color(ctx->dev->vpdma, - (struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff); - } else if (fmt->fourcc == V4L2_PIX_FMT_NV16) - val |= VPE_COLOR_SEPARATE_422; - - /* - * the source of CHR_DS and CSC is always the scaler, irrespective of - * whether it's used or not - */ - val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER; - - if (fmt->fourcc != V4L2_PIX_FMT_NV12 && - fmt->fourcc != V4L2_PIX_FMT_NV21) - val |= VPE_DS_BYPASS; - - mmr_adb->out_fmt_reg[0] = val; - - ctx->load_mmrs = true; -} - -/* - * Set the de-interlacer shadow register values - */ -static void set_dei_regs(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - unsigned int src_h = s_q_data->c_rect.height; - unsigned int src_w = s_q_data->c_rect.width; - u32 *dei_mmr0 = &mmr_adb->dei_regs[0]; - bool deinterlace = true; - u32 val = 0; - - /* - * according to TRM, we should set DEI in progressive bypass mode when - * the input content is progressive, however, DEI is bypassed correctly - * for both progressive and interlace content in interlace bypass mode. - * It has been recommended not to use progressive bypass mode. - */ - if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) { - deinterlace = false; - val = VPE_DEI_INTERLACE_BYPASS; - } - - src_h = deinterlace ? src_h * 2 : src_h; - - val |= (src_h << VPE_DEI_HEIGHT_SHIFT) | - (src_w << VPE_DEI_WIDTH_SHIFT) | - VPE_DEI_FIELD_FLUSH; - - *dei_mmr0 = val; - - ctx->load_mmrs = true; -} - -static void set_dei_shadow_registers(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *dei_mmr = &mmr_adb->dei_regs[0]; - const struct vpe_dei_regs *cur = &dei_regs; - - dei_mmr[2] = cur->mdt_spacial_freq_thr_reg; - dei_mmr[3] = cur->edi_config_reg; - dei_mmr[4] = cur->edi_lut_reg0; - dei_mmr[5] = cur->edi_lut_reg1; - dei_mmr[6] = cur->edi_lut_reg2; - dei_mmr[7] = cur->edi_lut_reg3; - - ctx->load_mmrs = true; -} - -static void config_edi_input_mode(struct vpe_ctx *ctx, int mode) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *edi_config_reg = &mmr_adb->dei_regs[3]; - - if (mode & 0x2) - write_field(edi_config_reg, 1, 1, 2); /* EDI_ENABLE_3D */ - - if (mode & 0x3) - write_field(edi_config_reg, 1, 1, 3); /* EDI_CHROMA_3D */ - - write_field(edi_config_reg, mode, VPE_EDI_INP_MODE_MASK, - VPE_EDI_INP_MODE_SHIFT); - - ctx->load_mmrs = true; -} - -/* - * Set the shadow registers whose values are modified when either the - * source or destination format is changed. - */ -static int set_srcdst_params(struct vpe_ctx *ctx) -{ - struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - unsigned int src_w = s_q_data->c_rect.width; - unsigned int src_h = s_q_data->c_rect.height; - unsigned int dst_w = d_q_data->c_rect.width; - unsigned int dst_h = d_q_data->c_rect.height; - struct v4l2_pix_format_mplane *spix; - size_t mv_buf_size; - int ret; - - ctx->sequence = 0; - ctx->field = V4L2_FIELD_TOP; - spix = &s_q_data->format.fmt.pix_mp; - - if ((s_q_data->flags & Q_IS_INTERLACED) && - !(d_q_data->flags & Q_IS_INTERLACED)) { - int bytes_per_line; - const struct vpdma_data_format *mv = - &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; - - /* - * we make sure that the source image has a 16 byte aligned - * stride, we need to do the same for the motion vector buffer - * by aligning it's stride to the next 16 byte boundary. this - * extra space will not be used by the de-interlacer, but will - * ensure that vpdma operates correctly - */ - bytes_per_line = ALIGN((spix->width * mv->depth) >> 3, - VPDMA_STRIDE_ALIGN); - mv_buf_size = bytes_per_line * spix->height; - - ctx->deinterlacing = true; - src_h <<= 1; - } else { - ctx->deinterlacing = false; - mv_buf_size = 0; - } - - free_vbs(ctx); - ctx->src_vbs[2] = ctx->src_vbs[1] = ctx->src_vbs[0] = NULL; - - ret = realloc_mv_buffers(ctx, mv_buf_size); - if (ret) - return ret; - - set_cfg_modes(ctx); - set_dei_regs(ctx); - - csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], - &s_q_data->format, &d_q_data->format); - - sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); - sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); - - sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0], - &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], - src_w, src_h, dst_w, dst_h); - - return 0; -} - -/* - * mem2mem callbacks - */ - -/* - * job_ready() - check whether an instance is ready to be scheduled to run - */ -static int job_ready(void *priv) -{ - struct vpe_ctx *ctx = priv; - - /* - * This check is needed as this might be called directly from driver - * When called by m2m framework, this will always satisfy, but when - * called from vpe_irq, this might fail. (src stream with zero buffers) - */ - if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) <= 0 || - v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) <= 0) - return 0; - - return 1; -} - -static void job_abort(void *priv) -{ - struct vpe_ctx *ctx = priv; - - /* Will cancel the transaction in the next interrupt handler */ - ctx->aborting = 1; -} - -static void vpe_dump_regs(struct vpe_dev *dev) -{ -#define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r)) - - vpe_dbg(dev, "VPE Registers:\n"); - - DUMPREG(PID); - DUMPREG(SYSCONFIG); - DUMPREG(INT0_STATUS0_RAW); - DUMPREG(INT0_STATUS0); - DUMPREG(INT0_ENABLE0); - DUMPREG(INT0_STATUS1_RAW); - DUMPREG(INT0_STATUS1); - DUMPREG(INT0_ENABLE1); - DUMPREG(CLK_ENABLE); - DUMPREG(CLK_RESET); - DUMPREG(CLK_FORMAT_SELECT); - DUMPREG(CLK_RANGE_MAP); - DUMPREG(US1_R0); - DUMPREG(US1_R1); - DUMPREG(US1_R2); - DUMPREG(US1_R3); - DUMPREG(US1_R4); - DUMPREG(US1_R5); - DUMPREG(US1_R6); - DUMPREG(US1_R7); - DUMPREG(US2_R0); - DUMPREG(US2_R1); - DUMPREG(US2_R2); - DUMPREG(US2_R3); - DUMPREG(US2_R4); - DUMPREG(US2_R5); - DUMPREG(US2_R6); - DUMPREG(US2_R7); - DUMPREG(US3_R0); - DUMPREG(US3_R1); - DUMPREG(US3_R2); - DUMPREG(US3_R3); - DUMPREG(US3_R4); - DUMPREG(US3_R5); - DUMPREG(US3_R6); - DUMPREG(US3_R7); - DUMPREG(DEI_FRAME_SIZE); - DUMPREG(MDT_BYPASS); - DUMPREG(MDT_SF_THRESHOLD); - DUMPREG(EDI_CONFIG); - DUMPREG(DEI_EDI_LUT_R0); - DUMPREG(DEI_EDI_LUT_R1); - DUMPREG(DEI_EDI_LUT_R2); - DUMPREG(DEI_EDI_LUT_R3); - DUMPREG(DEI_FMD_WINDOW_R0); - DUMPREG(DEI_FMD_WINDOW_R1); - DUMPREG(DEI_FMD_CONTROL_R0); - DUMPREG(DEI_FMD_CONTROL_R1); - DUMPREG(DEI_FMD_STATUS_R0); - DUMPREG(DEI_FMD_STATUS_R1); - DUMPREG(DEI_FMD_STATUS_R2); -#undef DUMPREG - - sc_dump_regs(dev->sc); - csc_dump_regs(dev->csc); -} - -static void add_out_dtd(struct vpe_ctx *ctx, int port) -{ - struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; - const struct vpe_port_data *p_data = &port_data[port]; - struct vb2_buffer *vb = &ctx->dst_vb->vb2_buf; - struct vpe_fmt *fmt = q_data->fmt; - const struct vpdma_data_format *vpdma_fmt; - int mv_buf_selector = !ctx->src_mv_buf_selector; - struct v4l2_pix_format_mplane *pix; - dma_addr_t dma_addr; - u32 flags = 0; - u32 offset = 0; - u32 stride; - - if (port == VPE_PORT_MV_OUT) { - vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; - dma_addr = ctx->mv_buf_dma[mv_buf_selector]; - q_data = &ctx->q_data[Q_DATA_SRC]; - pix = &q_data->format.fmt.pix_mp; - stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, - VPDMA_STRIDE_ALIGN); - } else { - /* to incorporate interleaved formats */ - int plane = fmt->coplanar ? p_data->vb_part : 0; - - pix = &q_data->format.fmt.pix_mp; - vpdma_fmt = fmt->vpdma_fmt[plane]; - /* - * If we are using a single plane buffer and - * we need to set a separate vpdma chroma channel. - */ - if (pix->num_planes == 1 && plane) { - dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); - /* Compute required offset */ - offset = pix->plane_fmt[0].bytesperline * pix->height; - } else { - dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); - /* Use address as is, no offset */ - offset = 0; - } - if (!dma_addr) { - vpe_err(ctx->dev, - "acquiring output buffer(%d) dma_addr failed\n", - port); - return; - } - /* Apply the offset */ - dma_addr += offset; - stride = pix->plane_fmt[VPE_LUMA].bytesperline; - } - - if (q_data->flags & Q_DATA_FRAME_1D) - flags |= VPDMA_DATA_FRAME_1D; - if (q_data->flags & Q_DATA_MODE_TILED) - flags |= VPDMA_DATA_MODE_TILED; - - vpdma_set_max_size(ctx->dev->vpdma, VPDMA_MAX_SIZE1, - MAX_W, MAX_H); - - vpdma_add_out_dtd(&ctx->desc_list, pix->width, - stride, &q_data->c_rect, - vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1, - MAX_OUT_HEIGHT_REG1, p_data->channel, flags); -} - -static void add_in_dtd(struct vpe_ctx *ctx, int port) -{ - struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; - const struct vpe_port_data *p_data = &port_data[port]; - struct vb2_buffer *vb = &ctx->src_vbs[p_data->vb_index]->vb2_buf; - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct vpe_fmt *fmt = q_data->fmt; - struct v4l2_pix_format_mplane *pix; - const struct vpdma_data_format *vpdma_fmt; - int mv_buf_selector = ctx->src_mv_buf_selector; - int field = vbuf->field == V4L2_FIELD_BOTTOM; - int frame_width, frame_height; - dma_addr_t dma_addr; - u32 flags = 0; - u32 offset = 0; - u32 stride; - - pix = &q_data->format.fmt.pix_mp; - if (port == VPE_PORT_MV_IN) { - vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; - dma_addr = ctx->mv_buf_dma[mv_buf_selector]; - stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, - VPDMA_STRIDE_ALIGN); - } else { - /* to incorporate interleaved formats */ - int plane = fmt->coplanar ? p_data->vb_part : 0; - - vpdma_fmt = fmt->vpdma_fmt[plane]; - /* - * If we are using a single plane buffer and - * we need to set a separate vpdma chroma channel. - */ - if (pix->num_planes == 1 && plane) { - dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); - /* Compute required offset */ - offset = pix->plane_fmt[0].bytesperline * pix->height; - } else { - dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); - /* Use address as is, no offset */ - offset = 0; - } - if (!dma_addr) { - vpe_err(ctx->dev, - "acquiring output buffer(%d) dma_addr failed\n", - port); - return; - } - /* Apply the offset */ - dma_addr += offset; - stride = pix->plane_fmt[VPE_LUMA].bytesperline; - - /* - * field used in VPDMA desc = 0 (top) / 1 (bottom) - * Use top or bottom field from same vb alternately - * For each de-interlacing operation, f,f-1,f-2 should be one - * of TBT or BTB - */ - if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB || - q_data->flags & Q_DATA_INTERLACED_SEQ_BT) { - /* Select initial value based on format */ - if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT) - field = 1; - else - field = 0; - - /* Toggle for each vb_index and each operation */ - field = (field + p_data->vb_index + ctx->sequence) % 2; - - if (field) { - int height = pix->height / 2; - int bpp; - - if (fmt->fourcc == V4L2_PIX_FMT_NV12 || - fmt->fourcc == V4L2_PIX_FMT_NV21) - bpp = 1; - else - bpp = vpdma_fmt->depth >> 3; - - if (plane) - height /= 2; - - dma_addr += pix->width * height * bpp; - } - } - } - - if (q_data->flags & Q_DATA_FRAME_1D) - flags |= VPDMA_DATA_FRAME_1D; - if (q_data->flags & Q_DATA_MODE_TILED) - flags |= VPDMA_DATA_MODE_TILED; - - frame_width = q_data->c_rect.width; - frame_height = q_data->c_rect.height; - - if (p_data->vb_part && (fmt->fourcc == V4L2_PIX_FMT_NV12 || - fmt->fourcc == V4L2_PIX_FMT_NV21)) - frame_height /= 2; - - vpdma_add_in_dtd(&ctx->desc_list, pix->width, stride, - &q_data->c_rect, vpdma_fmt, dma_addr, - p_data->channel, field, flags, frame_width, - frame_height, 0, 0); -} - -/* - * Enable the expected IRQ sources - */ -static void enable_irqs(struct vpe_ctx *ctx) -{ - write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE); - write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT | - VPE_DS1_UV_ERROR_INT); - - vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, true); -} - -static void disable_irqs(struct vpe_ctx *ctx) -{ - write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff); - write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff); - - vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, false); -} - -/* device_run() - prepares and starts the device - * - * This function is only called when both the source and destination - * buffers are in place. - */ -static void device_run(void *priv) -{ - struct vpe_ctx *ctx = priv; - struct sc_data *sc = ctx->dev->sc; - struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; - struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - const struct v4l2_format_info *d_finfo; - - d_finfo = v4l2_format_info(d_q_data->fmt->fourcc); - - if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX && - ctx->sequence % 2 == 0) { - /* When using SEQ_XX type buffers, each buffer has two fields - * each buffer has two fields (top & bottom) - * Removing one buffer is actually getting two fields - * Alternate between two operations:- - * Even : consume one field but DO NOT REMOVE from queue - * Odd : consume other field and REMOVE from queue - */ - ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - WARN_ON(ctx->src_vbs[0] == NULL); - } else { - ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - WARN_ON(ctx->src_vbs[0] == NULL); - } - - ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - WARN_ON(ctx->dst_vb == NULL); - - if (ctx->deinterlacing) { - - if (ctx->src_vbs[2] == NULL) { - ctx->src_vbs[2] = ctx->src_vbs[0]; - WARN_ON(ctx->src_vbs[2] == NULL); - ctx->src_vbs[1] = ctx->src_vbs[0]; - WARN_ON(ctx->src_vbs[1] == NULL); - } - - /* - * we have output the first 2 frames through line average, we - * now switch to EDI de-interlacer - */ - if (ctx->sequence == 2) - config_edi_input_mode(ctx, 0x3); /* EDI (Y + UV) */ - } - - /* config descriptors */ - if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) { - vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb); - vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb); - - set_line_modes(ctx); - - ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr; - ctx->load_mmrs = false; - } - - if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr || - sc->load_coeff_h) { - vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h); - vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, - &ctx->sc_coeff_h, 0); - - sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr; - sc->load_coeff_h = false; - } - - if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr || - sc->load_coeff_v) { - vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v); - vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, - &ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); - - sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr; - sc->load_coeff_v = false; - } - - /* output data descriptors */ - if (ctx->deinterlacing) - add_out_dtd(ctx, VPE_PORT_MV_OUT); - - if (v4l2_is_format_rgb(d_finfo)) { - add_out_dtd(ctx, VPE_PORT_RGB_OUT); - } else { - add_out_dtd(ctx, VPE_PORT_LUMA_OUT); - if (d_q_data->fmt->coplanar) - add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); - } - - /* input data descriptors */ - if (ctx->deinterlacing) { - add_in_dtd(ctx, VPE_PORT_LUMA3_IN); - add_in_dtd(ctx, VPE_PORT_CHROMA3_IN); - - add_in_dtd(ctx, VPE_PORT_LUMA2_IN); - add_in_dtd(ctx, VPE_PORT_CHROMA2_IN); - } - - add_in_dtd(ctx, VPE_PORT_LUMA1_IN); - add_in_dtd(ctx, VPE_PORT_CHROMA1_IN); - - if (ctx->deinterlacing) - add_in_dtd(ctx, VPE_PORT_MV_IN); - - /* sync on channel control descriptors for input ports */ - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN); - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN); - - if (ctx->deinterlacing) { - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_LUMA2_IN); - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_CHROMA2_IN); - - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_LUMA3_IN); - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_CHROMA3_IN); - - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN); - } - - /* sync on channel control descriptors for output ports */ - if (v4l2_is_format_rgb(d_finfo)) { - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_RGB_OUT); - } else { - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_LUMA_OUT); - if (d_q_data->fmt->coplanar) - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, - VPE_CHAN_CHROMA_OUT); - } - - if (ctx->deinterlacing) - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT); - - enable_irqs(ctx); - - vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf); - vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list, 0); -} - -static void dei_error(struct vpe_ctx *ctx) -{ - dev_warn(ctx->dev->v4l2_dev.dev, - "received DEI error interrupt\n"); -} - -static void ds1_uv_error(struct vpe_ctx *ctx) -{ - dev_warn(ctx->dev->v4l2_dev.dev, - "received downsampler error interrupt\n"); -} - -static irqreturn_t vpe_irq(int irq_vpe, void *data) -{ - struct vpe_dev *dev = (struct vpe_dev *)data; - struct vpe_ctx *ctx; - struct vpe_q_data *d_q_data; - struct vb2_v4l2_buffer *s_vb, *d_vb; - unsigned long flags; - u32 irqst0, irqst1; - bool list_complete = false; - - irqst0 = read_reg(dev, VPE_INT0_STATUS0); - if (irqst0) { - write_reg(dev, VPE_INT0_STATUS0_CLR, irqst0); - vpe_dbg(dev, "INT0_STATUS0 = 0x%08x\n", irqst0); - } - - irqst1 = read_reg(dev, VPE_INT0_STATUS1); - if (irqst1) { - write_reg(dev, VPE_INT0_STATUS1_CLR, irqst1); - vpe_dbg(dev, "INT0_STATUS1 = 0x%08x\n", irqst1); - } - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (!ctx) { - vpe_err(dev, "instance released before end of transaction\n"); - goto handled; - } - - if (irqst1) { - if (irqst1 & VPE_DEI_ERROR_INT) { - irqst1 &= ~VPE_DEI_ERROR_INT; - dei_error(ctx); - } - if (irqst1 & VPE_DS1_UV_ERROR_INT) { - irqst1 &= ~VPE_DS1_UV_ERROR_INT; - ds1_uv_error(ctx); - } - } - - if (irqst0) { - if (irqst0 & VPE_INT0_LIST0_COMPLETE) - vpdma_clear_list_stat(ctx->dev->vpdma, 0, 0); - - irqst0 &= ~(VPE_INT0_LIST0_COMPLETE); - list_complete = true; - } - - if (irqst0 | irqst1) { - dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n", - irqst0, irqst1); - } - - /* - * Setup next operation only when list complete IRQ occurs - * otherwise, skip the following code - */ - if (!list_complete) - goto handled; - - disable_irqs(ctx); - - vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); - vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); - vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); - vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); - - vpdma_reset_desc_list(&ctx->desc_list); - - /* the previous dst mv buffer becomes the next src mv buffer */ - ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector; - - s_vb = ctx->src_vbs[0]; - d_vb = ctx->dst_vb; - - d_vb->flags = s_vb->flags; - d_vb->vb2_buf.timestamp = s_vb->vb2_buf.timestamp; - - if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE) - d_vb->timecode = s_vb->timecode; - - d_vb->sequence = ctx->sequence; - s_vb->sequence = ctx->sequence; - - d_q_data = &ctx->q_data[Q_DATA_DST]; - if (d_q_data->flags & Q_IS_INTERLACED) { - d_vb->field = ctx->field; - if (ctx->field == V4L2_FIELD_BOTTOM) { - ctx->sequence++; - ctx->field = V4L2_FIELD_TOP; - } else { - WARN_ON(ctx->field != V4L2_FIELD_TOP); - ctx->field = V4L2_FIELD_BOTTOM; - } - } else { - d_vb->field = V4L2_FIELD_NONE; - ctx->sequence++; - } - - if (ctx->deinterlacing) { - /* - * Allow source buffer to be dequeued only if it won't be used - * in the next iteration. All vbs are initialized to first - * buffer and we are shifting buffers every iteration, for the - * first two iterations, no buffer will be dequeued. - * This ensures that driver will keep (n-2)th (n-1)th and (n)th - * field when deinterlacing is enabled - */ - if (ctx->src_vbs[2] != ctx->src_vbs[1]) - s_vb = ctx->src_vbs[2]; - else - s_vb = NULL; - } - - spin_lock_irqsave(&dev->lock, flags); - - if (s_vb) - v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE); - - v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE); - - spin_unlock_irqrestore(&dev->lock, flags); - - if (ctx->deinterlacing) { - ctx->src_vbs[2] = ctx->src_vbs[1]; - ctx->src_vbs[1] = ctx->src_vbs[0]; - } - - /* - * Since the vb2_buf_done has already been called fir therse - * buffer we can now NULL them out so that we won't try - * to clean out stray pointer later on. - */ - ctx->src_vbs[0] = NULL; - ctx->dst_vb = NULL; - - if (ctx->aborting) - goto finished; - - ctx->bufs_completed++; - if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) { - device_run(ctx); - goto handled; - } - -finished: - vpe_dbg(ctx->dev, "finishing transaction\n"); - ctx->bufs_completed = 0; - v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); -handled: - return IRQ_HANDLED; -} - -/* - * video ioctls - */ -static int vpe_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - VPE_MODULE_NAME); - return 0; -} - -static int __enum_fmt(struct v4l2_fmtdesc *f, u32 type) -{ - int i, index; - struct vpe_fmt *fmt = NULL; - - index = 0; - for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) { - if (vpe_formats[i].types & type) { - if (index == f->index) { - fmt = &vpe_formats[i]; - break; - } - index++; - } - } - - if (!fmt) - return -EINVAL; - - f->pixelformat = fmt->fourcc; - return 0; -} - -static int vpe_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (V4L2_TYPE_IS_OUTPUT(f->type)) - return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT); - - return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE); -} - -static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct vpe_ctx *ctx = file->private_data; - struct vb2_queue *vq; - struct vpe_q_data *q_data; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - *f = q_data->format; - - if (V4L2_TYPE_IS_CAPTURE(f->type)) { - struct vpe_q_data *s_q_data; - struct v4l2_pix_format_mplane *spix; - - /* get colorimetry from the source queue */ - s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - spix = &s_q_data->format.fmt.pix_mp; - - pix->colorspace = spix->colorspace; - pix->xfer_func = spix->xfer_func; - pix->ycbcr_enc = spix->ycbcr_enc; - pix->quantization = spix->quantization; - } - - return 0; -} - -static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, - struct vpe_fmt *fmt, int type) -{ - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct v4l2_plane_pix_format *plane_fmt; - unsigned int w_align; - int i, depth, depth_bytes, height; - unsigned int stride = 0; - const struct v4l2_format_info *finfo; - - if (!fmt || !(fmt->types & type)) { - vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n", - pix->pixelformat); - fmt = __find_format(V4L2_PIX_FMT_YUYV); - } - - if (pix->field != V4L2_FIELD_NONE && - pix->field != V4L2_FIELD_ALTERNATE && - pix->field != V4L2_FIELD_SEQ_TB && - pix->field != V4L2_FIELD_SEQ_BT) - pix->field = V4L2_FIELD_NONE; - - depth = fmt->vpdma_fmt[VPE_LUMA]->depth; - - /* - * the line stride should 16 byte aligned for VPDMA to work, based on - * the bytes per pixel, figure out how much the width should be aligned - * to make sure line stride is 16 byte aligned - */ - depth_bytes = depth >> 3; - - if (depth_bytes == 3) { - /* - * if bpp is 3(as in some RGB formats), the pixel width doesn't - * really help in ensuring line stride is 16 byte aligned - */ - w_align = 4; - } else { - /* - * for the remainder bpp(4, 2 and 1), the pixel width alignment - * can ensure a line stride alignment of 16 bytes. For example, - * if bpp is 2, then the line stride can be 16 byte aligned if - * the width is 8 byte aligned - */ - - /* - * HACK: using order_base_2() here causes lots of asm output - * errors with smatch, on i386: - * ./arch/x86/include/asm/bitops.h:457:22: - * warning: asm output is not an lvalue - * Perhaps some gcc optimization is doing the wrong thing - * there. - * Let's get rid of them by doing the calculus on two steps - */ - w_align = roundup_pow_of_two(VPDMA_DESC_ALIGN / depth_bytes); - w_align = ilog2(w_align); - } - - v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, - &pix->height, MIN_H, MAX_H, H_ALIGN, - S_ALIGN); - - if (!pix->num_planes || pix->num_planes > 2) - pix->num_planes = fmt->coplanar ? 2 : 1; - else if (pix->num_planes > 1 && !fmt->coplanar) - pix->num_planes = 1; - - pix->pixelformat = fmt->fourcc; - finfo = v4l2_format_info(fmt->fourcc); - - /* - * For the actual image parameters, we need to consider the field - * height of the image for SEQ_XX buffers. - */ - if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT) - height = pix->height / 2; - else - height = pix->height; - - if (!pix->colorspace) { - if (v4l2_is_format_rgb(finfo)) { - pix->colorspace = V4L2_COLORSPACE_SRGB; - } else { - if (height > 1280) /* HD */ - pix->colorspace = V4L2_COLORSPACE_REC709; - else /* SD */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - } - } - - memset(pix->reserved, 0, sizeof(pix->reserved)); - for (i = 0; i < pix->num_planes; i++) { - plane_fmt = &pix->plane_fmt[i]; - depth = fmt->vpdma_fmt[i]->depth; - - stride = (pix->width * fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; - if (stride > plane_fmt->bytesperline) - plane_fmt->bytesperline = stride; - - plane_fmt->bytesperline = clamp_t(u32, plane_fmt->bytesperline, - stride, - VPDMA_MAX_STRIDE); - - plane_fmt->bytesperline = ALIGN(plane_fmt->bytesperline, - VPDMA_STRIDE_ALIGN); - - if (i == VPE_LUMA) { - plane_fmt->sizeimage = pix->height * - plane_fmt->bytesperline; - - if (pix->num_planes == 1 && fmt->coplanar) - plane_fmt->sizeimage += pix->height * - plane_fmt->bytesperline * - fmt->vpdma_fmt[VPE_CHROMA]->depth >> 3; - - } else { /* i == VIP_CHROMA */ - plane_fmt->sizeimage = (pix->height * - plane_fmt->bytesperline * - depth) >> 3; - } - memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); - } - - return 0; -} - -static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct vpe_ctx *ctx = file->private_data; - struct vpe_fmt *fmt = find_format(f); - - if (V4L2_TYPE_IS_OUTPUT(f->type)) - return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT); - else - return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE); -} - -static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct v4l2_pix_format_mplane *qpix; - struct vpe_q_data *q_data; - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - if (vb2_is_busy(vq)) { - vpe_err(ctx->dev, "queue busy\n"); - return -EBUSY; - } - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - qpix = &q_data->format.fmt.pix_mp; - q_data->fmt = find_format(f); - q_data->format = *f; - - q_data->c_rect.left = 0; - q_data->c_rect.top = 0; - q_data->c_rect.width = pix->width; - q_data->c_rect.height = pix->height; - - if (qpix->field == V4L2_FIELD_ALTERNATE) - q_data->flags |= Q_DATA_INTERLACED_ALTERNATE; - else if (qpix->field == V4L2_FIELD_SEQ_TB) - q_data->flags |= Q_DATA_INTERLACED_SEQ_TB; - else if (qpix->field == V4L2_FIELD_SEQ_BT) - q_data->flags |= Q_DATA_INTERLACED_SEQ_BT; - else - q_data->flags &= ~Q_IS_INTERLACED; - - /* the crop height is halved for the case of SEQ_XX buffers */ - if (q_data->flags & Q_IS_SEQ_XX) - q_data->c_rect.height /= 2; - - vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d", - f->type, pix->width, pix->height, pix->pixelformat, - pix->plane_fmt[0].bytesperline); - if (pix->num_planes == 2) - vpe_dbg(ctx->dev, " bpl_uv %d\n", - pix->plane_fmt[1].bytesperline); - - return 0; -} - -static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - int ret; - struct vpe_ctx *ctx = file->private_data; - - ret = vpe_try_fmt(file, priv, f); - if (ret) - return ret; - - ret = __vpe_s_fmt(ctx, f); - if (ret) - return ret; - - if (V4L2_TYPE_IS_OUTPUT(f->type)) - set_src_registers(ctx); - else - set_dst_registers(ctx); - - return set_srcdst_params(ctx); -} - -static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) -{ - struct vpe_q_data *q_data; - struct v4l2_pix_format_mplane *pix; - int height; - - if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) - return -EINVAL; - - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - pix = &q_data->format.fmt.pix_mp; - - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE: - /* - * COMPOSE target is only valid for capture buffer type, return - * error for output buffer type - */ - if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - break; - case V4L2_SEL_TGT_CROP: - /* - * CROP target is only valid for output buffer type, return - * error for capture buffer type - */ - if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - break; - /* - * bound and default crop/compose targets are invalid targets to - * try/set - */ - default: - return -EINVAL; - } - - /* - * For SEQ_XX buffers, crop height should be less than the height of - * the field height, not the buffer height - */ - if (q_data->flags & Q_IS_SEQ_XX) - height = pix->height / 2; - else - height = pix->height; - - if (s->r.top < 0 || s->r.left < 0) { - vpe_err(ctx->dev, "negative values for top and left\n"); - s->r.top = s->r.left = 0; - } - - v4l_bound_align_image(&s->r.width, MIN_W, pix->width, 1, - &s->r.height, MIN_H, height, H_ALIGN, S_ALIGN); - - /* adjust left/top if cropping rectangle is out of bounds */ - if (s->r.left + s->r.width > pix->width) - s->r.left = pix->width - s->r.width; - if (s->r.top + s->r.height > pix->height) - s->r.top = pix->height - s->r.height; - - return 0; -} - -static int vpe_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct vpe_ctx *ctx = file->private_data; - struct vpe_q_data *q_data; - struct v4l2_pix_format_mplane *pix; - bool use_c_rect = false; - - if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) - return -EINVAL; - - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - pix = &q_data->format.fmt.pix_mp; - - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - break; - case V4L2_SEL_TGT_COMPOSE: - if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - use_c_rect = true; - break; - case V4L2_SEL_TGT_CROP: - if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - use_c_rect = true; - break; - default: - return -EINVAL; - } - - if (use_c_rect) { - /* - * for CROP/COMPOSE target type, return c_rect params from the - * respective buffer type - */ - s->r = q_data->c_rect; - } else { - /* - * for DEFAULT/BOUNDS target type, return width and height from - * S_FMT of the respective buffer type - */ - s->r.left = 0; - s->r.top = 0; - s->r.width = pix->width; - s->r.height = pix->height; - } - - return 0; -} - - -static int vpe_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct vpe_ctx *ctx = file->private_data; - struct vpe_q_data *q_data; - struct v4l2_selection sel = *s; - int ret; - - ret = __vpe_try_selection(ctx, &sel); - if (ret) - return ret; - - q_data = get_q_data(ctx, sel.type); - if (!q_data) - return -EINVAL; - - if ((q_data->c_rect.left == sel.r.left) && - (q_data->c_rect.top == sel.r.top) && - (q_data->c_rect.width == sel.r.width) && - (q_data->c_rect.height == sel.r.height)) { - vpe_dbg(ctx->dev, - "requested crop/compose values are already set\n"); - return 0; - } - - q_data->c_rect = sel.r; - - return set_srcdst_params(ctx); -} - -/* - * defines number of buffers/frames a context can process with VPE before - * switching to a different context. default value is 1 buffer per context - */ -#define V4L2_CID_VPE_BUFS_PER_JOB (V4L2_CID_USER_TI_VPE_BASE + 0) - -static int vpe_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vpe_ctx *ctx = - container_of(ctrl->handler, struct vpe_ctx, hdl); - - switch (ctrl->id) { - case V4L2_CID_VPE_BUFS_PER_JOB: - ctx->bufs_per_job = ctrl->val; - break; - - default: - vpe_err(ctx->dev, "Invalid control\n"); - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops vpe_ctrl_ops = { - .s_ctrl = vpe_s_ctrl, -}; - -static const struct v4l2_ioctl_ops vpe_ioctl_ops = { - .vidioc_querycap = vpe_querycap, - - .vidioc_enum_fmt_vid_cap = vpe_enum_fmt, - .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt, - .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt, - .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt, - - .vidioc_enum_fmt_vid_out = vpe_enum_fmt, - .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt, - .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, - .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, - - .vidioc_g_selection = vpe_g_selection, - .vidioc_s_selection = vpe_s_selection, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* - * Queue operations - */ -static int vpe_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - int i; - struct vpe_ctx *ctx = vb2_get_drv_priv(vq); - struct vpe_q_data *q_data; - struct v4l2_pix_format_mplane *pix; - - q_data = get_q_data(ctx, vq->type); - if (!q_data) - return -EINVAL; - - pix = &q_data->format.fmt.pix_mp; - *nplanes = pix->num_planes; - - for (i = 0; i < *nplanes; i++) - sizes[i] = pix->plane_fmt[i].sizeimage; - - vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, - sizes[VPE_LUMA]); - if (*nplanes == 2) - vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]); - - return 0; -} - -static int vpe_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vpe_q_data *q_data; - struct v4l2_pix_format_mplane *pix; - int i; - - vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); - - q_data = get_q_data(ctx, vb->vb2_queue->type); - if (!q_data) - return -EINVAL; - - pix = &q_data->format.fmt.pix_mp; - - if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - if (!(q_data->flags & Q_IS_INTERLACED)) { - vbuf->field = V4L2_FIELD_NONE; - } else { - if (vbuf->field != V4L2_FIELD_TOP && - vbuf->field != V4L2_FIELD_BOTTOM && - vbuf->field != V4L2_FIELD_SEQ_TB && - vbuf->field != V4L2_FIELD_SEQ_BT) - return -EINVAL; - } - } - - for (i = 0; i < pix->num_planes; i++) { - if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { - vpe_err(ctx->dev, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, i), - (long)pix->plane_fmt[i].sizeimage); - return -EINVAL; - } - } - - for (i = 0; i < pix->num_planes; i++) - vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); - - return 0; -} - -static void vpe_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); -} - -static int check_srcdst_sizes(struct vpe_ctx *ctx) -{ - struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; - unsigned int src_w = s_q_data->c_rect.width; - unsigned int src_h = s_q_data->c_rect.height; - unsigned int dst_w = d_q_data->c_rect.width; - unsigned int dst_h = d_q_data->c_rect.height; - - if (src_w == dst_w && src_h == dst_h) - return 0; - - if (src_h <= SC_MAX_PIXEL_HEIGHT && - src_w <= SC_MAX_PIXEL_WIDTH && - dst_h <= SC_MAX_PIXEL_HEIGHT && - dst_w <= SC_MAX_PIXEL_WIDTH) - return 0; - - return -1; -} - -static void vpe_return_all_buffers(struct vpe_ctx *ctx, struct vb2_queue *q, - enum vb2_buffer_state state) -{ - struct vb2_v4l2_buffer *vb; - unsigned long flags; - - for (;;) { - if (V4L2_TYPE_IS_OUTPUT(q->type)) - vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - else - vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (!vb) - break; - spin_lock_irqsave(&ctx->dev->lock, flags); - v4l2_m2m_buf_done(vb, state); - spin_unlock_irqrestore(&ctx->dev->lock, flags); - } - - /* - * Cleanup the in-transit vb2 buffers that have been - * removed from their respective queue already but for - * which procecessing has not been completed yet. - */ - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - spin_lock_irqsave(&ctx->dev->lock, flags); - - if (ctx->src_vbs[2]) - v4l2_m2m_buf_done(ctx->src_vbs[2], state); - - if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) - v4l2_m2m_buf_done(ctx->src_vbs[1], state); - - if (ctx->src_vbs[0] && - (ctx->src_vbs[0] != ctx->src_vbs[1]) && - (ctx->src_vbs[0] != ctx->src_vbs[2])) - v4l2_m2m_buf_done(ctx->src_vbs[0], state); - - ctx->src_vbs[2] = NULL; - ctx->src_vbs[1] = NULL; - ctx->src_vbs[0] = NULL; - - spin_unlock_irqrestore(&ctx->dev->lock, flags); - } else { - if (ctx->dst_vb) { - spin_lock_irqsave(&ctx->dev->lock, flags); - - v4l2_m2m_buf_done(ctx->dst_vb, state); - ctx->dst_vb = NULL; - spin_unlock_irqrestore(&ctx->dev->lock, flags); - } - } -} - -static int vpe_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct vpe_ctx *ctx = vb2_get_drv_priv(q); - - /* Check any of the size exceed maximum scaling sizes */ - if (check_srcdst_sizes(ctx)) { - vpe_err(ctx->dev, - "Conversion setup failed, check source and destination parameters\n" - ); - vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_QUEUED); - return -EINVAL; - } - - if (ctx->deinterlacing) - config_edi_input_mode(ctx, 0x0); - - if (ctx->sequence != 0) - set_srcdst_params(ctx); - - return 0; -} - -static void vpe_stop_streaming(struct vb2_queue *q) -{ - struct vpe_ctx *ctx = vb2_get_drv_priv(q); - - vpe_dump_regs(ctx->dev); - vpdma_dump_regs(ctx->dev->vpdma); - - vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_ERROR); -} - -static const struct vb2_ops vpe_qops = { - .queue_setup = vpe_queue_setup, - .buf_prepare = vpe_buf_prepare, - .buf_queue = vpe_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = vpe_start_streaming, - .stop_streaming = vpe_stop_streaming, -}; - -static int queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct vpe_ctx *ctx = priv; - struct vpe_dev *dev = ctx->dev; - int ret; - - memset(src_vq, 0, sizeof(*src_vq)); - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_DMABUF; - src_vq->drv_priv = ctx; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->ops = &vpe_qops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &dev->dev_mutex; - src_vq->dev = dev->v4l2_dev.dev; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - memset(dst_vq, 0, sizeof(*dst_vq)); - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; - dst_vq->drv_priv = ctx; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->ops = &vpe_qops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &dev->dev_mutex; - dst_vq->dev = dev->v4l2_dev.dev; - - return vb2_queue_init(dst_vq); -} - -static const struct v4l2_ctrl_config vpe_bufs_per_job = { - .ops = &vpe_ctrl_ops, - .id = V4L2_CID_VPE_BUFS_PER_JOB, - .name = "Buffers Per Transaction", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = VPE_DEF_BUFS_PER_JOB, - .min = 1, - .max = VIDEO_MAX_FRAME, - .step = 1, -}; - -/* - * File operations - */ -static int vpe_open(struct file *file) -{ - struct vpe_dev *dev = video_drvdata(file); - struct vpe_q_data *s_q_data; - struct v4l2_ctrl_handler *hdl; - struct vpe_ctx *ctx; - struct v4l2_pix_format_mplane *pix; - int ret; - - vpe_dbg(dev, "vpe_open\n"); - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = dev; - - if (mutex_lock_interruptible(&dev->dev_mutex)) { - ret = -ERESTARTSYS; - goto free_ctx; - } - - ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE, - VPDMA_LIST_TYPE_NORMAL); - if (ret != 0) - goto unlock; - - ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb)); - if (ret != 0) - goto free_desc_list; - - ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE); - if (ret != 0) - goto free_mmr_adb; - - ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE); - if (ret != 0) - goto free_sc_h; - - init_adb_hdrs(ctx); - - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = ctx; - - hdl = &ctx->hdl; - v4l2_ctrl_handler_init(hdl, 1); - v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL); - if (hdl->error) { - ret = hdl->error; - goto exit_fh; - } - ctx->fh.ctrl_handler = hdl; - v4l2_ctrl_handler_setup(hdl); - - s_q_data = &ctx->q_data[Q_DATA_SRC]; - pix = &s_q_data->format.fmt.pix_mp; - s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV); - pix->pixelformat = s_q_data->fmt->fourcc; - s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - pix->width = 1920; - pix->height = 1080; - pix->num_planes = 1; - pix->plane_fmt[VPE_LUMA].bytesperline = (pix->width * - s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; - pix->plane_fmt[VPE_LUMA].sizeimage = - pix->plane_fmt[VPE_LUMA].bytesperline * - pix->height; - pix->colorspace = V4L2_COLORSPACE_REC709; - pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; - pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - pix->quantization = V4L2_QUANTIZATION_DEFAULT; - pix->field = V4L2_FIELD_NONE; - s_q_data->c_rect.left = 0; - s_q_data->c_rect.top = 0; - s_q_data->c_rect.width = pix->width; - s_q_data->c_rect.height = pix->height; - s_q_data->flags = 0; - - ctx->q_data[Q_DATA_DST] = *s_q_data; - ctx->q_data[Q_DATA_DST].format.type = - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - - set_dei_shadow_registers(ctx); - set_src_registers(ctx); - set_dst_registers(ctx); - ret = set_srcdst_params(ctx); - if (ret) - goto exit_fh; - - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); - - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - goto exit_fh; - } - - v4l2_fh_add(&ctx->fh); - - /* - * for now, just report the creation of the first instance, we can later - * optimize the driver to enable or disable clocks when the first - * instance is created or the last instance released - */ - if (atomic_inc_return(&dev->num_instances) == 1) - vpe_dbg(dev, "first instance created\n"); - - ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB; - - ctx->load_mmrs = true; - - vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n", - ctx, ctx->fh.m2m_ctx); - - mutex_unlock(&dev->dev_mutex); - - return 0; -exit_fh: - v4l2_ctrl_handler_free(hdl); - v4l2_fh_exit(&ctx->fh); - vpdma_free_desc_buf(&ctx->sc_coeff_v); -free_sc_h: - vpdma_free_desc_buf(&ctx->sc_coeff_h); -free_mmr_adb: - vpdma_free_desc_buf(&ctx->mmr_adb); -free_desc_list: - vpdma_free_desc_list(&ctx->desc_list); -unlock: - mutex_unlock(&dev->dev_mutex); -free_ctx: - kfree(ctx); - return ret; -} - -static int vpe_release(struct file *file) -{ - struct vpe_dev *dev = video_drvdata(file); - struct vpe_ctx *ctx = file->private_data; - - vpe_dbg(dev, "releasing instance %p\n", ctx); - - mutex_lock(&dev->dev_mutex); - free_mv_buffers(ctx); - - vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); - vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); - vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); - vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); - - vpdma_free_desc_list(&ctx->desc_list); - vpdma_free_desc_buf(&ctx->mmr_adb); - - vpdma_free_desc_buf(&ctx->sc_coeff_v); - vpdma_free_desc_buf(&ctx->sc_coeff_h); - - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - v4l2_ctrl_handler_free(&ctx->hdl); - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - - kfree(ctx); - - /* - * for now, just report the release of the last instance, we can later - * optimize the driver to enable or disable clocks when the first - * instance is created or the last instance released - */ - if (atomic_dec_return(&dev->num_instances) == 0) - vpe_dbg(dev, "last instance released\n"); - - mutex_unlock(&dev->dev_mutex); - - return 0; -} - -static const struct v4l2_file_operations vpe_fops = { - .owner = THIS_MODULE, - .open = vpe_open, - .release = vpe_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static const struct video_device vpe_videodev = { - .name = VPE_MODULE_NAME, - .fops = &vpe_fops, - .ioctl_ops = &vpe_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .vfl_dir = VFL_DIR_M2M, - .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, -}; - -static const struct v4l2_m2m_ops m2m_ops = { - .device_run = device_run, - .job_ready = job_ready, - .job_abort = job_abort, -}; - -static int vpe_runtime_get(struct platform_device *pdev) -{ - int r; - - dev_dbg(&pdev->dev, "vpe_runtime_get\n"); - - r = pm_runtime_get_sync(&pdev->dev); - WARN_ON(r < 0); - if (r) - pm_runtime_put_noidle(&pdev->dev); - return r < 0 ? r : 0; -} - -static void vpe_runtime_put(struct platform_device *pdev) -{ - - int r; - - dev_dbg(&pdev->dev, "vpe_runtime_put\n"); - - r = pm_runtime_put_sync(&pdev->dev); - WARN_ON(r < 0 && r != -ENOSYS); -} - -static void vpe_fw_cb(struct platform_device *pdev) -{ - struct vpe_dev *dev = platform_get_drvdata(pdev); - struct video_device *vfd; - int ret; - - vfd = &dev->vfd; - *vfd = vpe_videodev; - vfd->lock = &dev->dev_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (ret) { - vpe_err(dev, "Failed to register video device\n"); - - vpe_set_clock_enable(dev, 0); - vpe_runtime_put(pdev); - pm_runtime_disable(&pdev->dev); - v4l2_m2m_release(dev->m2m_dev); - v4l2_device_unregister(&dev->v4l2_dev); - - return; - } - - video_set_drvdata(vfd, dev); - dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", - vfd->num); -} - -static int vpe_probe(struct platform_device *pdev) -{ - struct vpe_dev *dev; - int ret, irq, func; - - ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(&pdev->dev, - "32-bit consistent DMA enable failed\n"); - return ret; - } - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - spin_lock_init(&dev->lock); - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - atomic_set(&dev->num_instances, 0); - mutex_init(&dev->dev_mutex); - - dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "vpe_top"); - if (!dev->res) { - dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); - return -ENODEV; - } - - /* - * HACK: we get resource info from device tree in the form of a list of - * VPE sub blocks, the driver currently uses only the base of vpe_top - * for register access, the driver should be changed later to access - * registers based on the sub block base addresses - */ - dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); - if (!dev->base) { - ret = -ENOMEM; - goto v4l2_dev_unreg; - } - - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME, - dev); - if (ret) - goto v4l2_dev_unreg; - - platform_set_drvdata(pdev, dev); - - dev->m2m_dev = v4l2_m2m_init(&m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - vpe_err(dev, "Failed to init mem2mem device\n"); - ret = PTR_ERR(dev->m2m_dev); - goto v4l2_dev_unreg; - } - - pm_runtime_enable(&pdev->dev); - - ret = vpe_runtime_get(pdev); - if (ret) - goto rel_m2m; - - /* Perform clk enable followed by reset */ - vpe_set_clock_enable(dev, 1); - - vpe_top_reset(dev); - - func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK, - VPE_PID_FUNC_SHIFT); - vpe_dbg(dev, "VPE PID function %x\n", func); - - vpe_top_vpdma_reset(dev); - - dev->sc = sc_create(pdev, "sc"); - if (IS_ERR(dev->sc)) { - ret = PTR_ERR(dev->sc); - goto runtime_put; - } - - dev->csc = csc_create(pdev, "csc"); - if (IS_ERR(dev->csc)) { - ret = PTR_ERR(dev->csc); - goto runtime_put; - } - - dev->vpdma = &dev->vpdma_data; - ret = vpdma_create(pdev, dev->vpdma, vpe_fw_cb); - if (ret) - goto runtime_put; - - return 0; - -runtime_put: - vpe_runtime_put(pdev); -rel_m2m: - pm_runtime_disable(&pdev->dev); - v4l2_m2m_release(dev->m2m_dev); -v4l2_dev_unreg: - v4l2_device_unregister(&dev->v4l2_dev); - - return ret; -} - -static int vpe_remove(struct platform_device *pdev) -{ - struct vpe_dev *dev = platform_get_drvdata(pdev); - - v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME); - - v4l2_m2m_release(dev->m2m_dev); - video_unregister_device(&dev->vfd); - v4l2_device_unregister(&dev->v4l2_dev); - - vpe_set_clock_enable(dev, 0); - vpe_runtime_put(pdev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -#if defined(CONFIG_OF) -static const struct of_device_id vpe_of_match[] = { - { - .compatible = "ti,dra7-vpe", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, vpe_of_match); -#endif - -static struct platform_driver vpe_pdrv = { - .probe = vpe_probe, - .remove = vpe_remove, - .driver = { - .name = VPE_MODULE_NAME, - .of_match_table = of_match_ptr(vpe_of_match), - }, -}; - -module_platform_driver(vpe_pdrv); - -MODULE_DESCRIPTION("TI VPE driver"); -MODULE_AUTHOR("Dale Farnsworth, "); -MODULE_LICENSE("GPL"); diff -Naur --no-dereference a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h --- a/drivers/media/platform/ti-vpe/vpe_regs.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/ti-vpe/vpe_regs.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,306 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013 Texas Instruments Inc. - * - * David Griego, - * Dale Farnsworth, - * Archit Taneja, - */ - -#ifndef __TI_VPE_REGS_H -#define __TI_VPE_REGS_H - -/* VPE register offsets and field selectors */ - -/* VPE top level regs */ -#define VPE_PID 0x0000 -#define VPE_PID_MINOR_MASK 0x3f -#define VPE_PID_MINOR_SHIFT 0 -#define VPE_PID_CUSTOM_MASK 0x03 -#define VPE_PID_CUSTOM_SHIFT 6 -#define VPE_PID_MAJOR_MASK 0x07 -#define VPE_PID_MAJOR_SHIFT 8 -#define VPE_PID_RTL_MASK 0x1f -#define VPE_PID_RTL_SHIFT 11 -#define VPE_PID_FUNC_MASK 0xfff -#define VPE_PID_FUNC_SHIFT 16 -#define VPE_PID_SCHEME_MASK 0x03 -#define VPE_PID_SCHEME_SHIFT 30 - -#define VPE_SYSCONFIG 0x0010 -#define VPE_SYSCONFIG_IDLE_MASK 0x03 -#define VPE_SYSCONFIG_IDLE_SHIFT 2 -#define VPE_SYSCONFIG_STANDBY_MASK 0x03 -#define VPE_SYSCONFIG_STANDBY_SHIFT 4 -#define VPE_FORCE_IDLE_MODE 0 -#define VPE_NO_IDLE_MODE 1 -#define VPE_SMART_IDLE_MODE 2 -#define VPE_SMART_IDLE_WAKEUP_MODE 3 -#define VPE_FORCE_STANDBY_MODE 0 -#define VPE_NO_STANDBY_MODE 1 -#define VPE_SMART_STANDBY_MODE 2 -#define VPE_SMART_STANDBY_WAKEUP_MODE 3 - -#define VPE_INT0_STATUS0_RAW_SET 0x0020 -#define VPE_INT0_STATUS0_RAW VPE_INT0_STATUS0_RAW_SET -#define VPE_INT0_STATUS0_CLR 0x0028 -#define VPE_INT0_STATUS0 VPE_INT0_STATUS0_CLR -#define VPE_INT0_ENABLE0_SET 0x0030 -#define VPE_INT0_ENABLE0 VPE_INT0_ENABLE0_SET -#define VPE_INT0_ENABLE0_CLR 0x0038 -#define VPE_INT0_LIST0_COMPLETE BIT(0) -#define VPE_INT0_LIST0_NOTIFY BIT(1) -#define VPE_INT0_LIST1_COMPLETE BIT(2) -#define VPE_INT0_LIST1_NOTIFY BIT(3) -#define VPE_INT0_LIST2_COMPLETE BIT(4) -#define VPE_INT0_LIST2_NOTIFY BIT(5) -#define VPE_INT0_LIST3_COMPLETE BIT(6) -#define VPE_INT0_LIST3_NOTIFY BIT(7) -#define VPE_INT0_LIST4_COMPLETE BIT(8) -#define VPE_INT0_LIST4_NOTIFY BIT(9) -#define VPE_INT0_LIST5_COMPLETE BIT(10) -#define VPE_INT0_LIST5_NOTIFY BIT(11) -#define VPE_INT0_LIST6_COMPLETE BIT(12) -#define VPE_INT0_LIST6_NOTIFY BIT(13) -#define VPE_INT0_LIST7_COMPLETE BIT(14) -#define VPE_INT0_LIST7_NOTIFY BIT(15) -#define VPE_INT0_DESCRIPTOR BIT(16) -#define VPE_DEI_FMD_INT BIT(18) - -#define VPE_INT0_STATUS1_RAW_SET 0x0024 -#define VPE_INT0_STATUS1_RAW VPE_INT0_STATUS1_RAW_SET -#define VPE_INT0_STATUS1_CLR 0x002c -#define VPE_INT0_STATUS1 VPE_INT0_STATUS1_CLR -#define VPE_INT0_ENABLE1_SET 0x0034 -#define VPE_INT0_ENABLE1 VPE_INT0_ENABLE1_SET -#define VPE_INT0_ENABLE1_CLR 0x003c -#define VPE_INT0_CHANNEL_GROUP0 BIT(0) -#define VPE_INT0_CHANNEL_GROUP1 BIT(1) -#define VPE_INT0_CHANNEL_GROUP2 BIT(2) -#define VPE_INT0_CHANNEL_GROUP3 BIT(3) -#define VPE_INT0_CHANNEL_GROUP4 BIT(4) -#define VPE_INT0_CHANNEL_GROUP5 BIT(5) -#define VPE_INT0_CLIENT BIT(7) -#define VPE_DEI_ERROR_INT BIT(16) -#define VPE_DS1_UV_ERROR_INT BIT(22) - -#define VPE_INTC_EOI 0x00a0 - -#define VPE_CLK_ENABLE 0x0100 -#define VPE_VPEDMA_CLK_ENABLE BIT(0) -#define VPE_DATA_PATH_CLK_ENABLE BIT(1) - -#define VPE_CLK_RESET 0x0104 -#define VPE_VPDMA_CLK_RESET_MASK 0x1 -#define VPE_VPDMA_CLK_RESET_SHIFT 0 -#define VPE_DATA_PATH_CLK_RESET_MASK 0x1 -#define VPE_DATA_PATH_CLK_RESET_SHIFT 1 -#define VPE_MAIN_RESET_MASK 0x1 -#define VPE_MAIN_RESET_SHIFT 31 - -#define VPE_CLK_FORMAT_SELECT 0x010c -#define VPE_CSC_SRC_SELECT_MASK 0x03 -#define VPE_CSC_SRC_SELECT_SHIFT 0 -#define VPE_RGB_OUT_SELECT BIT(8) -#define VPE_DS_SRC_SELECT_MASK 0x07 -#define VPE_DS_SRC_SELECT_SHIFT 9 -#define VPE_DS_BYPASS BIT(16) -#define VPE_COLOR_SEPARATE_422 BIT(18) - -#define VPE_DS_SRC_DEI_SCALER (5 << VPE_DS_SRC_SELECT_SHIFT) -#define VPE_CSC_SRC_DEI_SCALER (3 << VPE_CSC_SRC_SELECT_SHIFT) - -#define VPE_CLK_RANGE_MAP 0x011c -#define VPE_RANGE_RANGE_MAP_Y_MASK 0x07 -#define VPE_RANGE_RANGE_MAP_Y_SHIFT 0 -#define VPE_RANGE_RANGE_MAP_UV_MASK 0x07 -#define VPE_RANGE_RANGE_MAP_UV_SHIFT 3 -#define VPE_RANGE_MAP_ON BIT(6) -#define VPE_RANGE_REDUCTION_ON BIT(28) - -/* VPE chrominance upsampler regs */ -#define VPE_US1_R0 0x0304 -#define VPE_US2_R0 0x0404 -#define VPE_US3_R0 0x0504 -#define VPE_US_C1_MASK 0x3fff -#define VPE_US_C1_SHIFT 2 -#define VPE_US_C0_MASK 0x3fff -#define VPE_US_C0_SHIFT 18 -#define VPE_US_MODE_MASK 0x03 -#define VPE_US_MODE_SHIFT 16 -#define VPE_ANCHOR_FID0_C1_MASK 0x3fff -#define VPE_ANCHOR_FID0_C1_SHIFT 2 -#define VPE_ANCHOR_FID0_C0_MASK 0x3fff -#define VPE_ANCHOR_FID0_C0_SHIFT 18 - -#define VPE_US1_R1 0x0308 -#define VPE_US2_R1 0x0408 -#define VPE_US3_R1 0x0508 -#define VPE_ANCHOR_FID0_C3_MASK 0x3fff -#define VPE_ANCHOR_FID0_C3_SHIFT 2 -#define VPE_ANCHOR_FID0_C2_MASK 0x3fff -#define VPE_ANCHOR_FID0_C2_SHIFT 18 - -#define VPE_US1_R2 0x030c -#define VPE_US2_R2 0x040c -#define VPE_US3_R2 0x050c -#define VPE_INTERP_FID0_C1_MASK 0x3fff -#define VPE_INTERP_FID0_C1_SHIFT 2 -#define VPE_INTERP_FID0_C0_MASK 0x3fff -#define VPE_INTERP_FID0_C0_SHIFT 18 - -#define VPE_US1_R3 0x0310 -#define VPE_US2_R3 0x0410 -#define VPE_US3_R3 0x0510 -#define VPE_INTERP_FID0_C3_MASK 0x3fff -#define VPE_INTERP_FID0_C3_SHIFT 2 -#define VPE_INTERP_FID0_C2_MASK 0x3fff -#define VPE_INTERP_FID0_C2_SHIFT 18 - -#define VPE_US1_R4 0x0314 -#define VPE_US2_R4 0x0414 -#define VPE_US3_R4 0x0514 -#define VPE_ANCHOR_FID1_C1_MASK 0x3fff -#define VPE_ANCHOR_FID1_C1_SHIFT 2 -#define VPE_ANCHOR_FID1_C0_MASK 0x3fff -#define VPE_ANCHOR_FID1_C0_SHIFT 18 - -#define VPE_US1_R5 0x0318 -#define VPE_US2_R5 0x0418 -#define VPE_US3_R5 0x0518 -#define VPE_ANCHOR_FID1_C3_MASK 0x3fff -#define VPE_ANCHOR_FID1_C3_SHIFT 2 -#define VPE_ANCHOR_FID1_C2_MASK 0x3fff -#define VPE_ANCHOR_FID1_C2_SHIFT 18 - -#define VPE_US1_R6 0x031c -#define VPE_US2_R6 0x041c -#define VPE_US3_R6 0x051c -#define VPE_INTERP_FID1_C1_MASK 0x3fff -#define VPE_INTERP_FID1_C1_SHIFT 2 -#define VPE_INTERP_FID1_C0_MASK 0x3fff -#define VPE_INTERP_FID1_C0_SHIFT 18 - -#define VPE_US1_R7 0x0320 -#define VPE_US2_R7 0x0420 -#define VPE_US3_R7 0x0520 -#define VPE_INTERP_FID0_C3_MASK 0x3fff -#define VPE_INTERP_FID0_C3_SHIFT 2 -#define VPE_INTERP_FID0_C2_MASK 0x3fff -#define VPE_INTERP_FID0_C2_SHIFT 18 - -/* VPE de-interlacer regs */ -#define VPE_DEI_FRAME_SIZE 0x0600 -#define VPE_DEI_WIDTH_MASK 0x07ff -#define VPE_DEI_WIDTH_SHIFT 0 -#define VPE_DEI_HEIGHT_MASK 0x07ff -#define VPE_DEI_HEIGHT_SHIFT 16 -#define VPE_DEI_INTERLACE_BYPASS BIT(29) -#define VPE_DEI_FIELD_FLUSH BIT(30) -#define VPE_DEI_PROGRESSIVE BIT(31) - -#define VPE_MDT_BYPASS 0x0604 -#define VPE_MDT_TEMPMAX_BYPASS BIT(0) -#define VPE_MDT_SPATMAX_BYPASS BIT(1) - -#define VPE_MDT_SF_THRESHOLD 0x0608 -#define VPE_MDT_SF_SC_THR1_MASK 0xff -#define VPE_MDT_SF_SC_THR1_SHIFT 0 -#define VPE_MDT_SF_SC_THR2_MASK 0xff -#define VPE_MDT_SF_SC_THR2_SHIFT 0 -#define VPE_MDT_SF_SC_THR3_MASK 0xff -#define VPE_MDT_SF_SC_THR3_SHIFT 0 - -#define VPE_EDI_CONFIG 0x060c -#define VPE_EDI_INP_MODE_MASK 0x03 -#define VPE_EDI_INP_MODE_SHIFT 0 -#define VPE_EDI_ENABLE_3D BIT(2) -#define VPE_EDI_ENABLE_CHROMA_3D BIT(3) -#define VPE_EDI_CHROMA3D_COR_THR_MASK 0xff -#define VPE_EDI_CHROMA3D_COR_THR_SHIFT 8 -#define VPE_EDI_DIR_COR_LOWER_THR_MASK 0xff -#define VPE_EDI_DIR_COR_LOWER_THR_SHIFT 16 -#define VPE_EDI_COR_SCALE_FACTOR_MASK 0xff -#define VPE_EDI_COR_SCALE_FACTOR_SHIFT 23 - -#define VPE_DEI_EDI_LUT_R0 0x0610 -#define VPE_EDI_LUT0_MASK 0x1f -#define VPE_EDI_LUT0_SHIFT 0 -#define VPE_EDI_LUT1_MASK 0x1f -#define VPE_EDI_LUT1_SHIFT 8 -#define VPE_EDI_LUT2_MASK 0x1f -#define VPE_EDI_LUT2_SHIFT 16 -#define VPE_EDI_LUT3_MASK 0x1f -#define VPE_EDI_LUT3_SHIFT 24 - -#define VPE_DEI_EDI_LUT_R1 0x0614 -#define VPE_EDI_LUT0_MASK 0x1f -#define VPE_EDI_LUT0_SHIFT 0 -#define VPE_EDI_LUT1_MASK 0x1f -#define VPE_EDI_LUT1_SHIFT 8 -#define VPE_EDI_LUT2_MASK 0x1f -#define VPE_EDI_LUT2_SHIFT 16 -#define VPE_EDI_LUT3_MASK 0x1f -#define VPE_EDI_LUT3_SHIFT 24 - -#define VPE_DEI_EDI_LUT_R2 0x0618 -#define VPE_EDI_LUT4_MASK 0x1f -#define VPE_EDI_LUT4_SHIFT 0 -#define VPE_EDI_LUT5_MASK 0x1f -#define VPE_EDI_LUT5_SHIFT 8 -#define VPE_EDI_LUT6_MASK 0x1f -#define VPE_EDI_LUT6_SHIFT 16 -#define VPE_EDI_LUT7_MASK 0x1f -#define VPE_EDI_LUT7_SHIFT 24 - -#define VPE_DEI_EDI_LUT_R3 0x061c -#define VPE_EDI_LUT8_MASK 0x1f -#define VPE_EDI_LUT8_SHIFT 0 -#define VPE_EDI_LUT9_MASK 0x1f -#define VPE_EDI_LUT9_SHIFT 8 -#define VPE_EDI_LUT10_MASK 0x1f -#define VPE_EDI_LUT10_SHIFT 16 -#define VPE_EDI_LUT11_MASK 0x1f -#define VPE_EDI_LUT11_SHIFT 24 - -#define VPE_DEI_FMD_WINDOW_R0 0x0620 -#define VPE_FMD_WINDOW_MINX_MASK 0x07ff -#define VPE_FMD_WINDOW_MINX_SHIFT 0 -#define VPE_FMD_WINDOW_MAXX_MASK 0x07ff -#define VPE_FMD_WINDOW_MAXX_SHIFT 16 -#define VPE_FMD_WINDOW_ENABLE BIT(31) - -#define VPE_DEI_FMD_WINDOW_R1 0x0624 -#define VPE_FMD_WINDOW_MINY_MASK 0x07ff -#define VPE_FMD_WINDOW_MINY_SHIFT 0 -#define VPE_FMD_WINDOW_MAXY_MASK 0x07ff -#define VPE_FMD_WINDOW_MAXY_SHIFT 16 - -#define VPE_DEI_FMD_CONTROL_R0 0x0628 -#define VPE_FMD_ENABLE BIT(0) -#define VPE_FMD_LOCK BIT(1) -#define VPE_FMD_JAM_DIR BIT(2) -#define VPE_FMD_BED_ENABLE BIT(3) -#define VPE_FMD_CAF_FIELD_THR_MASK 0xff -#define VPE_FMD_CAF_FIELD_THR_SHIFT 16 -#define VPE_FMD_CAF_LINE_THR_MASK 0xff -#define VPE_FMD_CAF_LINE_THR_SHIFT 24 - -#define VPE_DEI_FMD_CONTROL_R1 0x062c -#define VPE_FMD_CAF_THR_MASK 0x000fffff -#define VPE_FMD_CAF_THR_SHIFT 0 - -#define VPE_DEI_FMD_STATUS_R0 0x0630 -#define VPE_FMD_CAF_MASK 0x000fffff -#define VPE_FMD_CAF_SHIFT 0 -#define VPE_FMD_RESET BIT(24) - -#define VPE_DEI_FMD_STATUS_R1 0x0634 -#define VPE_FMD_FIELD_DIFF_MASK 0x0fffffff -#define VPE_FMD_FIELD_DIFF_SHIFT 0 - -#define VPE_DEI_FMD_STATUS_R2 0x0638 -#define VPE_FMD_FRAME_DIFF_MASK 0x000fffff -#define VPE_FMD_FRAME_DIFF_SHIFT 0 - -#endif diff -Naur --no-dereference a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c --- a/drivers/media/platform/via-camera.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/via-camera.c 2022-01-06 12:45:53.818318123 -0500 @@ -844,6 +844,9 @@ { int ret; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -852,7 +855,7 @@ upix->pixelformat = f->pixelformat; viacam_fmt_pre(upix, spix); v4l2_fill_mbus_format(&format.format, spix, f->mbus_code); - ret = sensor_call(cam, pad, set_fmt, &pad_cfg, &format); + ret = sensor_call(cam, pad, set_fmt, &pad_state, &format); v4l2_fill_pix_format(spix, &format.format); viacam_fmt_post(upix, spix); return ret; diff -Naur --no-dereference a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c --- a/drivers/media/platform/video-mux.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/video-mux.c 2022-01-06 12:45:53.818318123 -0500 @@ -140,14 +140,14 @@ static struct v4l2_mbus_framefmt * __video_mux_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(sd, cfg, pad); + return v4l2_subdev_get_try_format(sd, sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &vmux->format_mbus[pad]; default: @@ -156,14 +156,15 @@ } static int video_mux_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); mutex_lock(&vmux->lock); - sdformat->format = *__video_mux_get_pad_format(sd, cfg, sdformat->pad, + sdformat->format = *__video_mux_get_pad_format(sd, sd_state, + sdformat->pad, sdformat->which); mutex_unlock(&vmux->lock); @@ -172,7 +173,7 @@ } static int video_mux_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); @@ -180,12 +181,13 @@ struct media_pad *pad = &vmux->pads[sdformat->pad]; u16 source_pad = sd->entity.num_pads - 1; - mbusformat = __video_mux_get_pad_format(sd, cfg, sdformat->pad, - sdformat->which); + mbusformat = __video_mux_get_pad_format(sd, sd_state, sdformat->pad, + sdformat->which); if (!mbusformat) return -EINVAL; - source_mbusformat = __video_mux_get_pad_format(sd, cfg, source_pad, + source_mbusformat = __video_mux_get_pad_format(sd, sd_state, + source_pad, sdformat->which); if (!source_mbusformat) return -EINVAL; @@ -310,7 +312,7 @@ } static int video_mux_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); struct v4l2_mbus_framefmt *mbusformat; @@ -319,7 +321,7 @@ mutex_lock(&vmux->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_try_format(sd, cfg, i); + mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i); *mbusformat = video_mux_format_mbus_default; } diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_brx.c b/drivers/media/platform/vsp1/vsp1_brx.c --- a/drivers/media/platform/vsp1/vsp1_brx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_brx.c 2022-01-06 12:45:53.818318123 -0500 @@ -65,7 +65,7 @@ */ static int brx_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -73,12 +73,12 @@ MEDIA_BUS_FMT_AYUV8_1X32, }; - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, ARRAY_SIZE(codes)); } static int brx_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index) @@ -97,14 +97,14 @@ } static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad); + return v4l2_subdev_get_try_compose(&brx->entity.subdev, sd_state, pad); } static void brx_try_format(struct vsp1_brx *brx, - struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; @@ -119,7 +119,7 @@ default: /* The BRx can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&brx->entity, config, + format = vsp1_entity_get_pad_format(&brx->entity, sd_state, BRX_PAD_SINK(0)); fmt->code = format->code; break; @@ -132,17 +132,18 @@ } static int brx_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&brx->entity, sd_state, + fmt->which); if (!config) { ret = -EINVAL; goto done; @@ -181,11 +182,11 @@ } static int brx_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; if (sel->pad == brx->entity.source_pad) return -EINVAL; @@ -199,7 +200,7 @@ return 0; case V4L2_SEL_TGT_COMPOSE: - config = vsp1_entity_get_pad_config(&brx->entity, cfg, + config = vsp1_entity_get_pad_config(&brx->entity, sd_state, sel->which); if (!config) return -EINVAL; @@ -215,11 +216,11 @@ } static int brx_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; int ret = 0; @@ -232,7 +233,8 @@ mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&brx->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c --- a/drivers/media/platform/vsp1/vsp1_clu.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_clu.c 2022-01-06 12:45:53.818318123 -0500 @@ -123,27 +123,28 @@ }; static int clu_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, clu_codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, clu_codes, ARRAY_SIZE(clu_codes)); } static int clu_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, CLU_MIN_SIZE, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + CLU_MIN_SIZE, CLU_MIN_SIZE, CLU_MAX_SIZE, CLU_MAX_SIZE); } static int clu_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - return vsp1_subdev_set_pad_format(subdev, cfg, fmt, clu_codes, + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, clu_codes, ARRAY_SIZE(clu_codes), CLU_MIN_SIZE, CLU_MIN_SIZE, CLU_MAX_SIZE, CLU_MAX_SIZE); diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c --- a/drivers/media/platform/vsp1/vsp1_entity.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_entity.c 2022-01-06 12:45:53.818318123 -0500 @@ -114,9 +114,9 @@ * and simply returned when requested. The ACTIVE configuration comes from the * entity structure. */ -struct v4l2_subdev_pad_config * +struct v4l2_subdev_state * vsp1_entity_get_pad_config(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { switch (which) { @@ -124,7 +124,7 @@ return entity->config; case V4L2_SUBDEV_FORMAT_TRY: default: - return cfg; + return sd_state; } } @@ -139,10 +139,10 @@ */ struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&entity->subdev, sd_state, pad); } /** @@ -158,14 +158,16 @@ */ struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, unsigned int target) { switch (target) { case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); + return v4l2_subdev_get_try_compose(&entity->subdev, sd_state, + pad); case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(&entity->subdev, cfg, pad); + return v4l2_subdev_get_try_crop(&entity->subdev, sd_state, + pad); default: return NULL; } @@ -180,7 +182,7 @@ * function can be used as a handler for the subdev pad::init_cfg operation. */ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format format; unsigned int pad; @@ -189,10 +191,10 @@ memset(&format, 0, sizeof(format)); format.pad = pad; - format.which = cfg ? V4L2_SUBDEV_FORMAT_TRY + format.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format); + v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); } return 0; @@ -208,13 +210,13 @@ * a direct drop-in for the operation handler. */ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; - config = vsp1_entity_get_pad_config(entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(entity, sd_state, fmt->which); if (!config) return -EINVAL; @@ -239,7 +241,7 @@ * the sink pad. */ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code, const unsigned int *codes, unsigned int ncodes) { @@ -251,7 +253,7 @@ code->code = codes[code->index]; } else { - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; /* @@ -261,7 +263,8 @@ if (code->index) return -EINVAL; - config = vsp1_entity_get_pad_config(entity, cfg, code->which); + config = vsp1_entity_get_pad_config(entity, sd_state, + code->which); if (!config) return -EINVAL; @@ -290,17 +293,17 @@ * source pad size identical to the sink pad. */ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse, unsigned int min_width, unsigned int min_height, unsigned int max_width, unsigned int max_height) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(entity, cfg, fse->which); + config = vsp1_entity_get_pad_config(entity, sd_state, fse->which); if (!config) return -EINVAL; @@ -353,14 +356,14 @@ * source pad. */ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt, const unsigned int *codes, unsigned int ncodes, unsigned int min_width, unsigned int min_height, unsigned int max_width, unsigned int max_height) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; unsigned int i; @@ -368,7 +371,7 @@ mutex_lock(&entity->lock); - config = vsp1_entity_get_pad_config(entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(entity, sd_state, fmt->which); if (!config) { ret = -EINVAL; goto done; @@ -672,7 +675,7 @@ * Allocate the pad configuration to store formats and selection * rectangles. */ - entity->config = v4l2_subdev_alloc_pad_config(&entity->subdev); + entity->config = v4l2_subdev_alloc_state(&entity->subdev); if (entity->config == NULL) { media_entity_cleanup(&entity->subdev.entity); return -ENOMEM; @@ -687,6 +690,6 @@ entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); - v4l2_subdev_free_pad_config(entity->config); + v4l2_subdev_free_state(entity->config); media_entity_cleanup(&entity->subdev.entity); } diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h --- a/drivers/media/platform/vsp1/vsp1_entity.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_entity.h 2022-01-06 12:45:53.818318123 -0500 @@ -115,7 +115,7 @@ unsigned int sink_pad; struct v4l2_subdev subdev; - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct mutex lock; /* Protects the pad config */ }; @@ -136,20 +136,20 @@ const struct media_pad *local, const struct media_pad *remote, u32 flags); -struct v4l2_subdev_pad_config * +struct v4l2_subdev_state * vsp1_entity_get_pad_config(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which); struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad); struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, unsigned int target); int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg); + struct v4l2_subdev_state *sd_state); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, @@ -173,20 +173,20 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad); int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt); int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt, const unsigned int *codes, unsigned int ncodes, unsigned int min_width, unsigned int min_height, unsigned int max_width, unsigned int max_height); int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code, const unsigned int *codes, unsigned int ncodes); int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse, unsigned int min_w, unsigned int min_h, unsigned int max_w, unsigned int max_h); diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c --- a/drivers/media/platform/vsp1/vsp1_histo.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_histo.c 2022-01-06 12:45:53.818318123 -0500 @@ -170,7 +170,7 @@ */ static int histo_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct vsp1_histogram *histo = subdev_to_histo(subdev); @@ -180,28 +180,30 @@ return 0; } - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, histo->formats, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, + histo->formats, histo->num_formats); } static int histo_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->pad != HISTO_PAD_SINK) return -EINVAL; - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, HISTO_MIN_SIZE, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + HISTO_MIN_SIZE, HISTO_MIN_SIZE, HISTO_MAX_SIZE, HISTO_MAX_SIZE); } static int histo_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; int ret = 0; @@ -211,7 +213,8 @@ mutex_lock(&histo->entity.lock); - config = vsp1_entity_get_pad_config(&histo->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&histo->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; @@ -256,15 +259,15 @@ } static int histo_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *config, - struct v4l2_subdev_selection *sel) + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&histo->entity, config, + format = vsp1_entity_get_pad_format(&histo->entity, sd_state, HISTO_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -274,11 +277,11 @@ format->height - sel->r.top); /* Set the crop rectangle and reset the compose rectangle. */ - selection = vsp1_entity_get_pad_selection(&histo->entity, config, + selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, sel->pad, V4L2_SEL_TGT_CROP); *selection = sel->r; - selection = vsp1_entity_get_pad_selection(&histo->entity, config, + selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, sel->pad, V4L2_SEL_TGT_COMPOSE); *selection = sel->r; @@ -287,7 +290,7 @@ } static int histo_set_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); @@ -303,7 +306,8 @@ sel->r.left = 0; sel->r.top = 0; - crop = vsp1_entity_get_pad_selection(&histo->entity, config, sel->pad, + crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state, + sel->pad, V4L2_SEL_TGT_CROP); /* @@ -329,7 +333,7 @@ ratio = 1 << (crop->height * 2 / sel->r.height / 3); sel->r.height = crop->height / ratio; - compose = vsp1_entity_get_pad_selection(&histo->entity, config, + compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state, sel->pad, V4L2_SEL_TGT_COMPOSE); *compose = sel->r; @@ -338,11 +342,11 @@ } static int histo_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; int ret; if (sel->pad != HISTO_PAD_SINK) @@ -350,7 +354,8 @@ mutex_lock(&histo->entity.lock); - config = vsp1_entity_get_pad_config(&histo->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&histo->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; @@ -369,7 +374,7 @@ } static int histo_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { if (fmt->pad == HISTO_PAD_SOURCE) { @@ -381,19 +386,19 @@ return 0; } - return vsp1_subdev_get_pad_format(subdev, cfg, fmt); + return vsp1_subdev_get_pad_format(subdev, sd_state, fmt); } static int histo_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_histogram *histo = subdev_to_histo(subdev); if (fmt->pad != HISTO_PAD_SINK) - return histo_get_format(subdev, cfg, fmt); + return histo_get_format(subdev, sd_state, fmt); - return vsp1_subdev_set_pad_format(subdev, cfg, fmt, + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, histo->formats, histo->num_formats, HISTO_MIN_SIZE, HISTO_MIN_SIZE, HISTO_MAX_SIZE, HISTO_MAX_SIZE); diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c --- a/drivers/media/platform/vsp1/vsp1_hsit.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_hsit.c 2022-01-06 12:45:53.818318123 -0500 @@ -34,7 +34,7 @@ */ static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct vsp1_hsit *hsit = to_hsit(subdev); @@ -52,26 +52,28 @@ } static int hsit_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, HSIT_MIN_SIZE, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + HSIT_MIN_SIZE, HSIT_MIN_SIZE, HSIT_MAX_SIZE, HSIT_MAX_SIZE); } static int hsit_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&hsit->entity.lock); - config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&hsit->entity, sd_state, + fmt->which); if (!config) { ret = -EINVAL; goto done; diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c --- a/drivers/media/platform/vsp1/vsp1_lif.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_lif.c 2022-01-06 12:45:53.818318123 -0500 @@ -40,27 +40,28 @@ }; static int lif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lif_codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lif_codes, ARRAY_SIZE(lif_codes)); } static int lif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LIF_MIN_SIZE, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + LIF_MIN_SIZE, LIF_MIN_SIZE, LIF_MAX_SIZE, LIF_MAX_SIZE); } static int lif_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lif_codes, + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lif_codes, ARRAY_SIZE(lif_codes), LIF_MIN_SIZE, LIF_MIN_SIZE, LIF_MAX_SIZE, LIF_MAX_SIZE); diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c --- a/drivers/media/platform/vsp1/vsp1_lut.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_lut.c 2022-01-06 12:45:53.818318123 -0500 @@ -99,27 +99,28 @@ }; static int lut_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lut_codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lut_codes, ARRAY_SIZE(lut_codes)); } static int lut_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LUT_MIN_SIZE, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + LUT_MIN_SIZE, LUT_MIN_SIZE, LUT_MAX_SIZE, LUT_MAX_SIZE); } static int lut_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lut_codes, + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lut_codes, ARRAY_SIZE(lut_codes), LUT_MIN_SIZE, LUT_MIN_SIZE, LUT_MAX_SIZE, LUT_MAX_SIZE); diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c --- a/drivers/media/platform/vsp1/vsp1_rwpf.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c 2022-01-06 12:45:53.818318123 -0500 @@ -17,9 +17,9 @@ #define RWPF_MIN_HEIGHT 1 struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_pad_config *config) + struct v4l2_subdev_state *sd_state) { - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config, + return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state, RWPF_PAD_SINK); } @@ -28,7 +28,7 @@ */ static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -46,28 +46,30 @@ } static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, RWPF_MIN_WIDTH, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + RWPF_MIN_WIDTH, RWPF_MIN_HEIGHT, rwpf->max_width, rwpf->max_height); } static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, + fmt->which); if (!config) { ret = -EINVAL; goto done; @@ -128,11 +130,11 @@ } static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; @@ -145,7 +147,8 @@ mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; @@ -176,11 +179,11 @@ } static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; int ret = 0; @@ -197,7 +200,8 @@ mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h --- a/drivers/media/platform/vsp1/vsp1_rwpf.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h 2022-01-06 12:45:53.818318123 -0500 @@ -84,6 +84,6 @@ extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops; struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_pad_config *config); + struct v4l2_subdev_state *sd_state); #endif /* __VSP1_RWPF_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c --- a/drivers/media/platform/vsp1/vsp1_sru.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_sru.c 2022-01-06 12:45:53.818318123 -0500 @@ -106,7 +106,7 @@ */ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -114,20 +114,21 @@ MEDIA_BUS_FMT_AYUV8_1X32, }; - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, ARRAY_SIZE(codes)); } static int sru_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which); + config = vsp1_entity_get_pad_config(&sru->entity, sd_state, + fse->which); if (!config) return -EINVAL; @@ -164,7 +165,7 @@ } static void sru_try_format(struct vsp1_sru *sru, - struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; @@ -184,7 +185,7 @@ case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, config, + format = vsp1_entity_get_pad_format(&sru->entity, sd_state, SRU_PAD_SINK); fmt->code = format->code; @@ -216,17 +217,18 @@ } static int sru_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&sru->entity.lock); - config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&sru->entity, sd_state, + fmt->which); if (!config) { ret = -EINVAL; goto done; diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c --- a/drivers/media/platform/vsp1/vsp1_uds.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_uds.c 2022-01-06 12:45:53.818318123 -0500 @@ -111,7 +111,7 @@ */ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -119,20 +119,21 @@ MEDIA_BUS_FMT_AYUV8_1X32, }; - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, ARRAY_SIZE(codes)); } static int uds_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(&uds->entity, cfg, fse->which); + config = vsp1_entity_get_pad_config(&uds->entity, sd_state, + fse->which); if (!config) return -EINVAL; @@ -164,7 +165,7 @@ } static void uds_try_format(struct vsp1_uds *uds, - struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; @@ -184,7 +185,7 @@ case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, config, + format = vsp1_entity_get_pad_format(&uds->entity, sd_state, UDS_PAD_SINK); fmt->code = format->code; @@ -200,17 +201,18 @@ } static int uds_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&uds->entity.lock); - config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&uds->entity, sd_state, + fmt->which); if (!config) { ret = -EINVAL; goto done; diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/vsp1/vsp1_uif.c --- a/drivers/media/platform/vsp1/vsp1_uif.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_uif.c 2022-01-06 12:45:53.818318123 -0500 @@ -54,38 +54,39 @@ }; static int uif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, uif_codes, + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, uif_codes, ARRAY_SIZE(uif_codes)); } static int uif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - return vsp1_subdev_enum_frame_size(subdev, cfg, fse, UIF_MIN_SIZE, + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + UIF_MIN_SIZE, UIF_MIN_SIZE, UIF_MAX_SIZE, UIF_MAX_SIZE); } static int uif_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - return vsp1_subdev_set_pad_format(subdev, cfg, fmt, uif_codes, + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, uif_codes, ARRAY_SIZE(uif_codes), UIF_MIN_SIZE, UIF_MIN_SIZE, UIF_MAX_SIZE, UIF_MAX_SIZE); } static int uif_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_uif *uif = to_uif(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; int ret = 0; @@ -94,7 +95,8 @@ mutex_lock(&uif->entity.lock); - config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&uif->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; @@ -127,11 +129,11 @@ } static int uif_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vsp1_uif *uif = to_uif(subdev); - struct v4l2_subdev_pad_config *config; + struct v4l2_subdev_state *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; int ret = 0; @@ -142,7 +144,8 @@ mutex_lock(&uif->entity.lock); - config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&uif->entity, sd_state, + sel->which); if (!config) { ret = -EINVAL; goto done; diff -Naur --no-dereference a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c --- a/drivers/media/platform/vsp1/vsp1_video.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/vsp1/vsp1_video.c 2022-01-06 12:45:53.818318123 -0500 @@ -559,8 +559,8 @@ struct vsp1_video *video) { struct media_graph graph; - struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->graph_obj.mdev; + struct media_pad *pad = video->video.entity.pads; + struct media_device *mdev = video->video.entity.graph_obj.mdev; unsigned int i; int ret; @@ -569,17 +569,17 @@ if (ret) return ret; - media_graph_walk_start(&graph, entity); + media_graph_walk_start(&graph, pad); - while ((entity = media_graph_walk_next(&graph))) { + while ((pad = media_graph_walk_next(&graph))) { struct v4l2_subdev *subdev; struct vsp1_rwpf *rwpf; struct vsp1_entity *e; - if (!is_media_entity_v4l2_subdev(entity)) + if (!is_media_entity_v4l2_subdev(pad->entity)) continue; - subdev = media_entity_to_v4l2_subdev(entity); + subdev = media_entity_to_v4l2_subdev(pad->entity); e = to_vsp1_entity(subdev); list_add_tail(&e->list_pipe, &pipe->entities); e->pipe = pipe; @@ -927,7 +927,7 @@ } mutex_unlock(&pipe->lock); - media_pipeline_stop(&video->video.entity); + media_pipeline_stop(video->video.entity.pads); vsp1_video_release_buffers(video); vsp1_video_pipeline_put(pipe); } @@ -1048,7 +1048,7 @@ return PTR_ERR(pipe); } - ret = __media_pipeline_start(&video->video.entity, &pipe->pipe); + ret = __media_pipeline_start(video->video.entity.pads, &pipe->pipe); if (ret < 0) { mutex_unlock(&mdev->graph_mutex); goto err_pipe; @@ -1072,7 +1072,7 @@ return 0; err_stop: - media_pipeline_stop(&video->video.entity); + media_pipeline_stop(video->video.entity.pads); err_pipe: vsp1_video_pipeline_put(pipe); return ret; diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/addr_alloc.c b/drivers/media/platform/vxe-vxd/common/addr_alloc.c --- a/drivers/media/platform/vxe-vxd/common/addr_alloc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/addr_alloc.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Address allocation APIs - used to manage address allocation + * with a number of predefined regions. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "addr_alloc.h" +#include "hash.h" +#include "img_errors.h" + +/* Global context. */ +static struct addr_context global_ctx = {0}; +/* Sub-system initialized. */ +static int global_initialized; +/* Count of contexts. */ +static unsigned int num_ctx; +/* Global mutex */ +static struct mutex *global_lock; + +/** + * addr_initialise - addr_initialise + */ + +int addr_initialise(void) +{ + unsigned int result = IMG_ERROR_ALREADY_INITIALISED; + + /* If we are not initialized */ + if (!global_initialized) + result = addr_cx_initialise(&global_ctx); + return result; +} + +int addr_cx_initialise(struct addr_context * const context) +{ + unsigned int result = IMG_ERROR_FATAL; + + if (!context) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!global_initialized) { + /* Initialise context */ + memset(context, 0x00, sizeof(struct addr_context)); + + /* If no mutex associated with this resource */ + if (!global_lock) { + /* Create one */ + + global_lock = kzalloc(sizeof(*global_lock), GFP_KERNEL); + if (!global_lock) + return -ENOMEM; + + mutex_init(global_lock); + } + + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + /* Initialise the hash functions. */ + result = vid_hash_initialise(); + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + + /* Initialise the arena functions */ + result = vid_ra_initialise(); + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + result = vid_hash_finalise(); + return IMG_ERROR_UNEXPECTED_STATE; + } + + /* We are now initialized */ + global_initialized = TRUE; + result = IMG_SUCCESS; + } else { + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + } + + num_ctx++; + mutex_unlock(global_lock); + + return result; +} + +int addr_deinitialise(void) +{ + return addr_cx_deinitialise(&global_ctx); +} + +int addr_cx_deinitialise(struct addr_context * const context) +{ + struct addr_region *tmp_region = NULL; + unsigned int result = IMG_ERROR_FATAL; + + if (!context) + return IMG_ERROR_INVALID_PARAMETERS; + + if (global_initialized) { + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + tmp_region = context->regions; + + /* Delete all arena structure */ + if (context->default_region) + result = vid_ra_delete(context->default_region->arena); + + while (tmp_region) { + result = vid_ra_delete(tmp_region->arena); + tmp_region = tmp_region->nxt_region; + } + + if (num_ctx != 0) + num_ctx--; + + result = IMG_SUCCESS; + if (num_ctx == 0) { + /* Free off resources */ + result = vid_hash_finalise(); + result = vid_ra_deinit(); + global_initialized = FALSE; + + mutex_unlock(global_lock); + mutex_destroy(global_lock); + kfree(global_lock); + global_lock = NULL; + } else { + mutex_unlock(global_lock); + } + } + + return result; +} + +int addr_define_mem_region(struct addr_region * const region) +{ + return addr_cx_define_mem_region(&global_ctx, region); +} + +int addr_cx_define_mem_region(struct addr_context * const context, + struct addr_region * const region) +{ + struct addr_region *tmp_region = NULL; + unsigned int result = IMG_SUCCESS; + + if (!context || !region) + return IMG_ERROR_INVALID_PARAMETERS; + + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + tmp_region = context->regions; + + /* Ensure the link to the next is NULL */ + region->nxt_region = NULL; + + /* If this is the default memory region */ + if (!region->name) { + /* Should not previously have been defined */ + if (context->default_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + + context->default_region = region; + context->no_regions++; + + /* + * Create an arena for memory allocation + * name of resource arena for debug + * start of resource + * size of resource + * allocation quantum + * import allocator + * import deallocator + * import handle + */ + result = vid_ra_create("memory", + region->base_addr, + region->size, + 1, + NULL, + NULL, + NULL, + ®ion->arena); + + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + } else { + /* + * Run down the list of existing named regions + * to check if there is a region with this name + */ + while (tmp_region && + (strcmp(region->name, tmp_region->name) != 0) && + tmp_region->nxt_region) { + tmp_region = tmp_region->nxt_region; + } + + /* If we have items in the list */ + if (tmp_region) { + /* + * Check we didn't stop because the name + * clashes with one already defined. + */ + + if (strcmp(region->name, tmp_region->name) == 0 || + tmp_region->nxt_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + + /* Add to end of list */ + tmp_region->nxt_region = region; + } else { + /* Add to head of list */ + context->regions = region; + } + + context->no_regions++; + + /* + * Create an arena for memory allocation + * name of resource arena for debug + * start of resource + * size of resource + * allocation quantum + * import allocator + * import deallocator + * import handle + */ + result = vid_ra_create(region->name, + region->base_addr, + region->size, + 1, + NULL, + NULL, + NULL, + ®ion->arena); + + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + } + + mutex_unlock(global_lock); + + /* Check the arean was created OK */ + if (!region->arena) + return IMG_ERROR_UNEXPECTED_STATE; + + return result; +} + +int addr_malloc(const unsigned char * const name, + unsigned long long size, + unsigned long long * const base_adr) +{ + return addr_cx_malloc(&global_ctx, name, size, base_adr); +} + +int addr_cx_malloc(struct addr_context * const context, + const unsigned char * const name, + unsigned long long size, + unsigned long long * const base_adr) +{ + unsigned int result = IMG_ERROR_FATAL; + struct addr_region *tmp_region = NULL; + + if (!context || !base_adr || !name) + return IMG_ERROR_INVALID_PARAMETERS; + + *(base_adr) = (unsigned long long)-1LL; + + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + tmp_region = context->regions; + + /* + * Run down the list of existing named + * regions to locate this + */ + while (tmp_region && (strcmp(name, tmp_region->name) != 0) && (tmp_region->nxt_region)) + tmp_region = tmp_region->nxt_region; + + /* If there was no match. */ + if (!tmp_region || (strcmp(name, tmp_region->name) != 0)) { + /* Use the default */ + if (!context->default_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + + tmp_region = context->default_region; + } + + if (!tmp_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + + /* Allocate size + guard band */ + result = vid_ra_alloc(tmp_region->arena, + size + tmp_region->guard_band, + NULL, + NULL, + SEQUENTIAL_ALLOCATION, + 1, + base_adr); + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + return IMG_ERROR_OUT_OF_MEMORY; + } + + mutex_unlock(global_lock); + + return result; +} + +int addr_cx_malloc_res(struct addr_context * const context, + const unsigned char * const name, + unsigned long long size, + unsigned long long * const base_adr) +{ + unsigned int result = IMG_ERROR_FATAL; + struct addr_region *tmp_region = NULL; + + if (!context || !base_adr || !name) + return IMG_ERROR_INVALID_PARAMETERS; + + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + tmp_region = context->regions; + /* If the allocation is for the default region */ + /* + * Run down the list of existing named + * regions to locate this + */ + while (tmp_region && (strcmp(name, tmp_region->name) != 0) && (tmp_region->nxt_region)) + tmp_region = tmp_region->nxt_region; + + /* If there was no match. */ + if (!tmp_region || (strcmp(name, tmp_region->name) != 0)) { + /* Use the default */ + if (!context->default_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + tmp_region = context->default_region; + } + if (!tmp_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + /* Allocate size + guard band */ + result = vid_ra_alloc(tmp_region->arena, size + tmp_region->guard_band, + NULL, NULL, SEQUENTIAL_ALLOCATION, 1, base_adr); + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + return IMG_ERROR_OUT_OF_MEMORY; + } + mutex_unlock(global_lock); + + return result; +} + +int addr_cx_malloc_align_res(struct addr_context * const context, + const unsigned char * const name, + unsigned long long size, + unsigned long long alignment, + unsigned long long * const base_adr) +{ + unsigned int result; + struct addr_region *tmp_region = NULL; + + if (!context || !base_adr || !name) + return IMG_ERROR_INVALID_PARAMETERS; + + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + tmp_region = context->regions; + + /* + * Run down the list of existing named + * regions to locate this + */ + while (tmp_region && + (strcmp(name, tmp_region->name) != 0) && + (tmp_region->nxt_region)) { + tmp_region = tmp_region->nxt_region; + } + /* If there was no match. */ + if (!tmp_region || + (strcmp(name, tmp_region->name) != 0)) { + /* Use the default */ + if (!context->default_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + + tmp_region = context->default_region; + } + + if (!tmp_region) { + mutex_unlock(global_lock); + return IMG_ERROR_UNEXPECTED_STATE; + } + /* Allocate size + guard band */ + result = vid_ra_alloc(tmp_region->arena, + size + tmp_region->guard_band, + NULL, + NULL, + SEQUENTIAL_ALLOCATION, + alignment, + base_adr); + if (result != IMG_SUCCESS) { + mutex_unlock(global_lock); + return IMG_ERROR_OUT_OF_MEMORY; + } + + mutex_unlock(global_lock); + + return result; +} + +int addr_free(const unsigned char * const name, unsigned long long addr) +{ + return addr_cx_free(&global_ctx, name, addr); +} + +int addr_cx_free(struct addr_context * const context, + const unsigned char * const name, + unsigned long long addr) +{ + struct addr_region *tmp_region; + unsigned int result; + + if (!context) + return IMG_ERROR_INVALID_PARAMETERS; + + tmp_region = context->regions; + + mutex_lock_nested(global_lock, SUBCLASS_ADDR_ALLOC); + + /* If the allocation is for the default region */ + if (!name) { + if (!context->default_region) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + tmp_region = context->default_region; + } else { + /* + * Run down the list of existing named + * regions to locate this + */ + while (tmp_region && + (strcmp(name, tmp_region->name) != 0) && + tmp_region->nxt_region) { + tmp_region = tmp_region->nxt_region; + } + + /* If there was no match */ + if (!tmp_region || (strcmp(name, tmp_region->name) != 0)) { + /* Use the default */ + if (!context->default_region) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + tmp_region = context->default_region; + } + } + + /* Free the address */ + result = vid_ra_free(tmp_region->arena, addr); + +error: + mutex_unlock(global_lock); + return result; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/addr_alloc.h b/drivers/media/platform/vxe-vxd/common/addr_alloc.h --- a/drivers/media/platform/vxe-vxd/common/addr_alloc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/addr_alloc.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Address allocation management API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ +#ifndef __ADDR_ALLOC_H__ +#define __ADDR_ALLOC_H__ + +#include +#include "ra.h" + +/* Defines whether sequential or random allocation is used */ +enum { + SEQUENTIAL_ALLOCATION, + RANDOM_ALLOCATION, + RANDOM_FORCE32BITS = 0x7FFFFFFFU +}; + +/** + * struct addr_region - Memory region structure + *@name: A pointer to a sring containing the name of the region. + * NULL for the default memory region. + *@base_addr: The base address of the memory region. + *@size: The size of the memory region. + *@guard_band: The size of any guard band to be used. + * Guard bands can be useful in separating block allocations + * and allows the caller to detect erroneous accesses + * into these areas. + *@nxt_region:Used internally by the ADDR API.A pointer used to point + * to the next memory region. + *@arena: Used internally by the ADDR API. A to a structure used to + * maintain and perform address allocation. + * + * This structure contains information about the memory region. + */ +struct addr_region { + unsigned char *name; + unsigned long long base_addr; + unsigned long long size; + unsigned int guard_band; + struct addr_region *nxt_region; + void *arena; +}; + +/* + * This structure contains the context for allocation. + *@regions: Pointer the first region in the list. + *@default_region: Pointer the default region. + *@no_regions: Number of regions currently available (including default) + */ +struct addr_context { + struct addr_region *regions; + struct addr_region *default_region; + unsigned int no_regions; +}; + +/* + * @Function ADDR_Initialise + * @Description + * This function is used to initialise the address alocation sub-system. + * NOTE: This function may be called multiple times. The initialisation only + * happens the first time it is called. + * @Return IMG_SUCCESS or an error code. + */ +int addr_initialise(void); + +/* + * @Function addr_deinitialise + * @Description + * This function is used to de-initialise the address alocation sub-system. + * @Return IMG_SUCCESS or an error code. + */ +int addr_deinitialise(void); + +/* + * @Function addr_define_mem_region + * @Description + * This function is used define a memory region. + * NOTE: The region structure MUST be defined in static memory as this + * is retained and used by the ADDR sub-system. + * NOTE: Invalid parameters are trapped by asserts. + * @Input region: A pointer to a region structure. + * @Return IMG_RESULT : IMG_SUCCESS or an error code. + */ +int addr_define_mem_region(struct addr_region * const region); + +/* + * @Function addr_malloc + * @Description + * This function is used allocate space within a memory region. + * NOTE: Allocation failures or invalid parameters are trapped by asserts. + * @Input name: Is a pointer the name of the memory region. + * NULL can be used to allocate space from the + * default memory region. + * @Input size: The size (in bytes) of the allocation. + * @Output base_adr : The address of the allocated space. + * @Return IMG_SUCCESS or an error code. + */ +int addr_malloc(const unsigned char *const name, + unsigned long long size, + unsigned long long *const base_adr); + +/* + * @Function addr_free + * @Description + * This function is used free a previously allocate space within + * a memory region. + * NOTE: Invalid parameters are trapped by asserts. + * @Input name: Is a pointer to the name of the memory region. + * NULL is used to free space from the default memory region. + *@Input addr: The address allocated. + *@Return IMG_SUCCESS or an error code. + */ +int addr_free(const unsigned char * const name, unsigned long long addr); + +/* + * @Function addr_cx_initialise + * @Description + * This function is used to initialise the address allocation sub-system with + * an external context structure. + * NOTE: This function should be call only once for the context. + * @Input context : Pointer to context structure. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_initialise(struct addr_context * const context); + +/* + * @Function addr_cx_deinitialise + * @Description + * This function is used to de-initialise the address allocation + * sub-system with an external context structure. + * @Input context : Pointer to context structure. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_deinitialise(struct addr_context * const context); + +/* + * @Function addr_cx_define_mem_region + * @Description + * This function is used define a memory region with an external + * context structure. + * NOTE: The region structure MUST be defined in static memory as this + * is retained and used by the ADDR sub-system. + * NOTE: Invalid parameters are trapped by asserts. + * @Input context : Pointer to context structure. + * @Input region : A pointer to a region structure. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_define_mem_region(struct addr_context *const context, + struct addr_region *const region); + +/* + * @Function addr_cx_malloc + * @Description + * This function is used allocate space within a memory region with + * an external context structure. + * NOTE: Allocation failures or invalid parameters are trapped by asserts. + * @Input context : Pointer to context structure. + * @Input name : Is a pointer the name of the memory region. + * NULL can be used to allocate space from the + * default memory region. + * @Input size : The size (in bytes) of the allocation. + * @Output base_adr : The address of the allocated space. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_malloc(struct addr_context * const context, + const unsigned char *const name, + unsigned long long size, + unsigned long long *const base_adr); + +/* + * @Function addr_cx_malloc_res + * @Description + * This function is used allocate space within a memory region with + * an external context structure. + * NOTE: Allocation failures are returned in IMG_RESULT, however invalid + * parameters are trapped by asserts. + * @Input context : Pointer to context structure. + * @Input name : Is a pointer the name of the memory region. + * NULL can be used to allocate space from the + * default memory region. + * @Input size : The size (in bytes) of the allocation. + * @Input base_adr : Pointer to the address of the allocated space. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_malloc_res(struct addr_context *const context, + const unsigned char *const name, + unsigned long long size, + unsigned long long * const base_adr); + +/* + * @Function addr_cx_malloc1_res + * @Description + * This function is used allocate space within a memory region with + * an external context structure. + * NOTE: Allocation failures are returned in IMG_RESULT, however invalid + * parameters are trapped by asserts. + * @Input context : Pointer to context structure. + * @Input name : Is a pointer the name of the memory region. + * NULL can be used to allocate space from the + * default memory region. + * @Input size : The size (in bytes) of the allocation. + * @Input alignment : The required byte alignment (1, 2, 4, 8, 16 etc). + * @Input base_adr : Pointer to the address of the allocated space. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_malloc_align_res(struct addr_context *const context, + const unsigned char *const name, + unsigned long long size, + unsigned long long alignment, + unsigned long long *const base_adr); + +/* + * @Function addr_cx_free + * @Description + * This function is used free a previously allocate space within a memory region + * with an external context structure. + * NOTE: Invalid parameters are trapped by asserts. + * @Input context : Pointer to context structure. + * @Input name : Is a pointer the name of the memory region. + * NULL is used to free space from the + * default memory region. + * @Input addr : The address allocated. + * @Return IMG_SUCCESS or an error code. + */ +int addr_cx_free(struct addr_context *const context, + const unsigned char *const name, + unsigned long long addr); + +#endif /* __ADDR_ALLOC_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/dq.c b/drivers/media/platform/vxe-vxd/common/dq.c --- a/drivers/media/platform/vxe-vxd/common/dq.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/dq.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Utility module for doubly linked queues. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include + +#include "dq.h" +#include "img_errors.h" + +void dq_init(struct dq_linkage_t *queue) +{ + queue->fwd = (struct dq_linkage_t *)queue; + queue->back = (struct dq_linkage_t *)queue; +} + +void dq_addhead(struct dq_linkage_t *queue, void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return; + + ((struct dq_linkage_t *)item)->back = (struct dq_linkage_t *)queue; + ((struct dq_linkage_t *)item)->fwd = + ((struct dq_linkage_t *)queue)->fwd; + ((struct dq_linkage_t *)queue)->fwd->back = (struct dq_linkage_t *)item; + ((struct dq_linkage_t *)queue)->fwd = (struct dq_linkage_t *)item; +} + +void dq_addtail(struct dq_linkage_t *queue, void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return; + + ((struct dq_linkage_t *)item)->fwd = (struct dq_linkage_t *)queue; + ((struct dq_linkage_t *)item)->back = + ((struct dq_linkage_t *)queue)->back; + ((struct dq_linkage_t *)queue)->back->fwd = (struct dq_linkage_t *)item; + ((struct dq_linkage_t *)queue)->back = (struct dq_linkage_t *)item; +} + +int dq_empty(struct dq_linkage_t *queue) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return 1; + + return ((queue)->fwd == (struct dq_linkage_t *)(queue)); +} + +void *dq_first(struct dq_linkage_t *queue) +{ + struct dq_linkage_t *temp = queue->fwd; + + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return NULL; + + return temp == (struct dq_linkage_t *)queue ? NULL : temp; +} + +void *dq_last(struct dq_linkage_t *queue) +{ + struct dq_linkage_t *temp = queue->back; + + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return NULL; + + return temp == (struct dq_linkage_t *)queue ? NULL : temp; +} + +void *dq_next(void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)item)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)item)->fwd); + + if (!((struct dq_linkage_t *)item)->back || + !((struct dq_linkage_t *)item)->fwd) + return NULL; + + return ((struct dq_linkage_t *)item)->fwd; +} + +void *dq_previous(void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)item)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)item)->fwd); + + if (!((struct dq_linkage_t *)item)->back || + !((struct dq_linkage_t *)item)->fwd) + return NULL; + + return ((struct dq_linkage_t *)item)->back; +} + +void dq_remove(void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)item)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)item)->fwd); + + if (!((struct dq_linkage_t *)item)->back || + !((struct dq_linkage_t *)item)->fwd) + return; + + ((struct dq_linkage_t *)item)->fwd->back = + ((struct dq_linkage_t *)item)->back; + ((struct dq_linkage_t *)item)->back->fwd = + ((struct dq_linkage_t *)item)->fwd; + + /* make item linkages safe for "orphan" removes */ + ((struct dq_linkage_t *)item)->fwd = item; + ((struct dq_linkage_t *)item)->back = item; +} + +void *dq_removehead(struct dq_linkage_t *queue) +{ + struct dq_linkage_t *temp; + + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return NULL; + + if ((queue)->fwd == (struct dq_linkage_t *)(queue)) + return NULL; + + temp = ((struct dq_linkage_t *)queue)->fwd; + temp->fwd->back = temp->back; + temp->back->fwd = temp->fwd; + + /* make item linkages safe for "orphan" removes */ + temp->fwd = temp; + temp->back = temp; + return temp; +} + +void *dq_removetail(struct dq_linkage_t *queue) +{ + struct dq_linkage_t *temp; + + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)queue)->fwd); + + if (!((struct dq_linkage_t *)queue)->back || + !((struct dq_linkage_t *)queue)->fwd) + return NULL; + + if ((queue)->fwd == (struct dq_linkage_t *)(queue)) + return NULL; + + temp = ((struct dq_linkage_t *)queue)->back; + temp->fwd->back = temp->back; + temp->back->fwd = temp->fwd; + + /* make item linkages safe for "orphan" removes */ + temp->fwd = temp; + temp->back = temp; + + return temp; +} + +void dq_addbefore(void *successor, void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)successor)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)successor)->fwd); + + if (!((struct dq_linkage_t *)successor)->back || + !((struct dq_linkage_t *)successor)->fwd) + return; + + ((struct dq_linkage_t *)item)->fwd = (struct dq_linkage_t *)successor; + ((struct dq_linkage_t *)item)->back = + ((struct dq_linkage_t *)successor)->back; + ((struct dq_linkage_t *)item)->back->fwd = (struct dq_linkage_t *)item; + ((struct dq_linkage_t *)successor)->back = (struct dq_linkage_t *)item; +} + +void dq_addafter(void *predecessor, void *item) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)predecessor)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)predecessor)->fwd); + + if (!((struct dq_linkage_t *)predecessor)->back || + !((struct dq_linkage_t *)predecessor)->fwd) + return; + + ((struct dq_linkage_t *)item)->fwd = + ((struct dq_linkage_t *)predecessor)->fwd; + ((struct dq_linkage_t *)item)->back = + (struct dq_linkage_t *)predecessor; + ((struct dq_linkage_t *)item)->fwd->back = (struct dq_linkage_t *)item; + ((struct dq_linkage_t *)predecessor)->fwd = (struct dq_linkage_t *)item; +} + +void dq_move(struct dq_linkage_t *from, struct dq_linkage_t *to) +{ + IMG_DBG_ASSERT(((struct dq_linkage_t *)from)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)from)->fwd); + IMG_DBG_ASSERT(((struct dq_linkage_t *)to)->back); + IMG_DBG_ASSERT(((struct dq_linkage_t *)to)->fwd); + + if (!((struct dq_linkage_t *)from)->back || + !((struct dq_linkage_t *)from)->fwd || + !((struct dq_linkage_t *)to)->back || + !((struct dq_linkage_t *)to)->fwd) + return; + + if ((from)->fwd == (struct dq_linkage_t *)(from)) { + dq_init(to); + } else { + *to = *from; + to->fwd->back = (struct dq_linkage_t *)to; + to->back->fwd = (struct dq_linkage_t *)to; + dq_init(from); + } +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/dq.h b/drivers/media/platform/vxe-vxd/common/dq.h --- a/drivers/media/platform/vxe-vxd/common/dq.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/dq.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Utility module for doubly linked queues. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + */ +#ifndef DQ_H +#define DQ_H + +/* dq structure */ +struct dq_linkage_t { + struct dq_linkage_t *fwd; + struct dq_linkage_t *back; +}; + +/* Function Prototypes */ +void dq_addafter(void *predecessor, void *item); +void dq_addbefore(void *successor, void *item); +void dq_addhead(struct dq_linkage_t *queue, void *item); +void dq_addtail(struct dq_linkage_t *queue, void *item); +int dq_empty(struct dq_linkage_t *queue); +void *dq_first(struct dq_linkage_t *queue); +void *dq_last(struct dq_linkage_t *queue); +void dq_init(struct dq_linkage_t *queue); +void dq_move(struct dq_linkage_t *from, struct dq_linkage_t *to); +void *dq_next(void *item); +void *dq_previous(void *item); +void dq_remove(void *item); +void *dq_removehead(struct dq_linkage_t *queue); +void *dq_removetail(struct dq_linkage_t *queue); + +#endif /* #define DQ_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/hash.c b/drivers/media/platform/vxe-vxd/common/hash.c --- a/drivers/media/platform/vxe-vxd/common/hash.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/hash.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Self scaling hash tables. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "hash.h" +#include "img_errors.h" +#include "pool.h" + +/* pool of struct hash objects */ +static struct pool *global_hashpool; + +/* pool of struct bucket objects */ +static struct pool *global_bucketpool; + +static int global_initialized; + +/* Each entry in a hash table is placed into a bucket */ +struct bucket { + struct bucket *next; + unsigned long long key; + unsigned long long value; +}; + +struct hash { + struct bucket **table; + unsigned int size; + unsigned int count; + unsigned int minimum_size; +}; + +/** + * hash_func - Hash function intended for hashing addresses. + * @vale : The key to hash. + * @size : The size of the hash table + */ +static unsigned int hash_func(unsigned long long vale, + unsigned int size) +{ + unsigned int hash = (unsigned int)(vale); + + hash += (hash << 12); + hash ^= (hash >> 22); + hash += (hash << 4); + hash ^= (hash >> 9); + hash += (hash << 10); + hash ^= (hash >> 2); + hash += (hash << 7); + hash ^= (hash >> 12); + hash &= (size - 1); + return hash; +} + +/* + * @Function hash_chain_insert + * @Description + * Hash function intended for hashing addresses. + * @Input bucket : The bucket + * @Input table : The hash table + * @Input size : The size of the hash table + * @Return IMG_SUCCESS or an error code. + */ +static int hash_chain_insert(struct bucket *bucket, + struct bucket **table, + unsigned int size) +{ + unsigned int idx; + unsigned int result = IMG_ERROR_FATAL; + + if (!bucket || !table || !size) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + idx = hash_func(bucket->key, size); + + if (idx < size) { + result = IMG_SUCCESS; + bucket->next = table[idx]; + table[idx] = bucket; + } + + return result; +} + +/* + * @Function hash_rehash + * @Description + * Iterate over every entry in an old hash table and rehash into the new table. + * @Input old_table : The old hash table + * @Input old_size : The size of the old hash table + * @Input new_table : The new hash table + * @Input new_sz : The size of the new hash table + * @Return IMG_SUCCESS or an error code. + */ +static int hash_rehash(struct bucket **old_table, + unsigned int old_size, + struct bucket **new_table, + unsigned int new_sz) +{ + unsigned int idx; + unsigned int result = IMG_ERROR_FATAL; + + if (!old_table || !new_table) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + for (idx = 0; idx < old_size; idx++) { + struct bucket *bucket; + struct bucket *nex_bucket; + + bucket = old_table[idx]; + while (bucket) { + nex_bucket = bucket->next; + result = hash_chain_insert(bucket, new_table, new_sz); + if (result != IMG_SUCCESS) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + bucket = nex_bucket; + } + } + result = IMG_SUCCESS; + + return result; +} + +/* + * @Function hash_resize + * @Description + * Attempt to resize a hash table, failure to allocate a new larger hash table + * is not considered a hard failure. We simply continue and allow the table to + * fill up, the effect is to allow hash chains to become longer. + * @Input hash_arg : Pointer to the hash table + * @Input new_sz : The size of the new hash table + * @Return IMG_SUCCESS or an error code. + */ +static int hash_resize(struct hash *hash_arg, + unsigned int new_sz) +{ + unsigned int malloc_sz = 0; + unsigned int result = IMG_ERROR_FATAL; + unsigned int idx; + + if (!hash_arg) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + if (new_sz != hash_arg->size) { + struct bucket **new_bkt_table; + + malloc_sz = (sizeof(struct bucket *) * new_sz); + new_bkt_table = kmalloc(malloc_sz, GFP_KERNEL); + + if (!new_bkt_table) { + result = IMG_ERROR_MALLOC_FAILED; + return result; + } + + for (idx = 0; idx < new_sz; idx++) + new_bkt_table[idx] = NULL; + + result = hash_rehash(hash_arg->table, + hash_arg->size, + new_bkt_table, + new_sz); + + if (result != IMG_SUCCESS) { + kfree(new_bkt_table); + new_bkt_table = NULL; + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + + kfree(hash_arg->table); + hash_arg->table = new_bkt_table; + hash_arg->size = new_sz; + } + result = IMG_SUCCESS; + + return result; +} + +static unsigned int private_max(unsigned int a, unsigned int b) +{ + unsigned int ret = (a > b) ? a : b; + return ret; +} + +/* + * @Function vid_hash_initialise + * @Description + * To initialise the hash module. + * @Input None + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_initialise(void) +{ + unsigned int result = IMG_ERROR_ALREADY_COMPLETE; + + if (!global_initialized) { + if (global_hashpool || global_bucketpool) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + + result = pool_create("img-hash", + sizeof(struct hash), + &global_hashpool); + + if (result != IMG_SUCCESS) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + + result = pool_create("img-sBucket", + sizeof(struct bucket), + &global_bucketpool); + if (result != IMG_SUCCESS) { + if (global_bucketpool) { + result = pool_delete(global_bucketpool); + global_bucketpool = NULL; + } + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + global_initialized = true; + result = IMG_SUCCESS; + } + return result; +} + +/* + * @Function vid_hash_finalise + * @Description + * To finalise the hash module. All allocated hash tables should + * be deleted before calling this function. + * @Input None + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_finalise(void) +{ + unsigned int result = IMG_ERROR_FATAL; + + if (global_initialized) { + if (global_hashpool) { + result = pool_delete(global_hashpool); + if (result != IMG_SUCCESS) + return result; + + global_hashpool = NULL; + } + + if (global_bucketpool) { + result = pool_delete(global_bucketpool); + if (result != IMG_SUCCESS) + return result; + + global_bucketpool = NULL; + } + global_initialized = false; + result = IMG_SUCCESS; + } + + return result; +} + +/* + * @Function vid_hash_create + * @Description + * Create a self scaling hash table. + * @Input initial_size : Initial and minimum size of the hash table. + * @Output hash_arg : Will countin the hash table handle or NULL. + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_create(unsigned int initial_size, + struct hash ** const hash_arg) +{ + unsigned int idx; + unsigned int tbl_sz = 0; + unsigned int result = IMG_ERROR_FATAL; + struct hash *local_hash = NULL; + + if (!hash_arg) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + if (global_initialized) { + pool_alloc(global_hashpool, ((void **)&local_hash)); + if (!local_hash) { + result = IMG_ERROR_UNEXPECTED_STATE; + *hash_arg = NULL; + return result; + } + + local_hash->count = 0; + local_hash->size = initial_size; + local_hash->minimum_size = initial_size; + + tbl_sz = (sizeof(struct bucket *) * local_hash->size); + local_hash->table = kmalloc(tbl_sz, GFP_KERNEL); + if (!local_hash->table) { + result = pool_free(global_hashpool, local_hash); + if (result != IMG_SUCCESS) + result = IMG_ERROR_UNEXPECTED_STATE; + result |= IMG_ERROR_MALLOC_FAILED; + *hash_arg = NULL; + return result; + } + + for (idx = 0; idx < local_hash->size; idx++) + local_hash->table[idx] = NULL; + + *hash_arg = local_hash; + result = IMG_SUCCESS; + } + return result; +} + +/* + * @Function vid_hash_delete + * @Description + * To delete a hash table, all entries in the table should be + * removed before calling this function. + * @Input hash_arg : Hash table pointer + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_delete(struct hash * const hash_arg) +{ + unsigned int result = IMG_ERROR_FATAL; + + if (!hash_arg) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + if (global_initialized) { + if (hash_arg->count != 0) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + + kfree(hash_arg->table); + hash_arg->table = NULL; + + result = pool_free(global_hashpool, hash_arg); + if (result != IMG_SUCCESS) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + } + return result; +} + +/* + * @Function vid_hash_insert + * @Description + * To insert a key value pair into a hash table. + * @Input hash_arg : Hash table pointer + * @Input key : Key value + * @Input value : The value associated with the key. + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_insert(struct hash * const hash_arg, + unsigned long long key, + unsigned long long value) +{ + struct bucket *ps_bucket = NULL; + unsigned int result = IMG_ERROR_FATAL; + + if (!hash_arg) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + if (global_initialized) { + result = pool_alloc(global_bucketpool, ((void **)&ps_bucket)); + if (result != IMG_SUCCESS || !ps_bucket) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + ps_bucket->next = NULL; + ps_bucket->key = key; + ps_bucket->value = value; + + result = hash_chain_insert(ps_bucket, + hash_arg->table, + hash_arg->size); + + if (result != IMG_SUCCESS) { + pool_free(global_bucketpool, ((void **)&ps_bucket)); + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + + hash_arg->count++; + + /* check if we need to think about re-balancing */ + if ((hash_arg->count << 1) > hash_arg->size) { + result = hash_resize(hash_arg, (hash_arg->size << 1)); + if (result != IMG_SUCCESS) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + } + result = IMG_SUCCESS; + } + return result; +} + +/* + * @Function vid_hash_remove + * @Description + * To remove a key value pair from a hash table + * @Input hash_arg : Hash table pointer + * @Input key : Key value + * @Input ret_result : 0 if the key is missing or the value + * associated with the key. + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_remove(struct hash * const hash_arg, + unsigned long long key, + unsigned long * const ret_result) +{ + unsigned int idx; + unsigned int tmp1 = 0; + unsigned int tmp2 = 0; + unsigned int result = IMG_ERROR_FATAL; + struct bucket **bucket = NULL; + + if (!hash_arg) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + idx = hash_func(key, hash_arg->size); + + for (bucket = &hash_arg->table[idx]; (*bucket) != NULL; + bucket = &((*bucket)->next)) { + if ((*bucket)->key == key) { + struct bucket *ps_bucket = (*bucket); + + unsigned long long value = ps_bucket->value; + + *bucket = ps_bucket->next; + result = pool_free(global_bucketpool, ps_bucket); + + hash_arg->count--; + + /* check if we need to think about re-balencing */ + if (hash_arg->size > (hash_arg->count << 2) && + hash_arg->size > hash_arg->minimum_size) { + tmp1 = (hash_arg->size >> 1); + tmp2 = hash_arg->minimum_size; + result = hash_resize(hash_arg, + private_max(tmp1, tmp2)); + } + *ret_result = value; + result = IMG_SUCCESS; + break; + } + } + return result; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/hash.h b/drivers/media/platform/vxe-vxd/common/hash.h --- a/drivers/media/platform/vxe-vxd/common/hash.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/hash.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Self scaling hash tables. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ +#ifndef _HASH_H_ +#define _HASH_H_ + +#include +struct hash; + +/** + * vid_hash_initialise - VID_HASH_Initialise + * @Input None + * + * To initialise the hash module. + */ +int vid_hash_initialise(void); + +/* + * @Function VID_HASH_Finalise + * @Description + * To finalise the hash module. All allocated hash tables should + * be deleted before calling this function. + * @Input None + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_finalise(void); + +/* + * @Function VID_HASH_Create + * @Description + * Create a self scaling hash table. + * @Input initial_size : Initial and minimum size of the hash table. + * @Output hash : Hash table handle or NULL. + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_create(unsigned int initial_size, + struct hash ** const hash_hndl); + +/* + * @Function VID_HASH_Delete + * @Description + * To delete a hash table, all entries in the table should be + * removed before calling this function. + * @Input hash : Hash table pointer + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_delete(struct hash * const ps_hash); + +/* + * @Function VID_HASH_Insert + * @Description + * To insert a key value pair into a hash table. + * @Input ps_hash : Hash table pointer + * @Input key : Key value + * @Input value : The value associated with the key. + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_insert(struct hash * const ps_hash, + unsigned long long key, + unsigned long long value); + +/* + * @Function VID_HASH_Remove + * @Description + * To remove a key value pair from a hash table + * @Input ps_hash : Hash table pointer + * @Input key : Key value + * @Input result : 0 if the key is missing or the value + * associated with the key. + * @Return IMG_SUCCESS or an error code. + */ +int vid_hash_remove(struct hash * const ps_hash, + unsigned long long key, + unsigned long * const result); + +#endif /* _HASH_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/idgen_api.c b/drivers/media/platform/vxe-vxd/common/idgen_api.c --- a/drivers/media/platform/vxe-vxd/common/idgen_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/idgen_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ID generation manager API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include + +#include "idgen_api.h" +#include "lst.h" + +/* + * This structure contains ID context. + */ +struct idgen_context { + /* List of handle block structures */ + struct lst_t hdlblklst; + /* Max ID - set by IDGEN_CreateContext(). */ + unsigned int maxid; + /* + * The number of handle per block. In case of + * incrementing ids, size of the Hash table. + */ + unsigned int blksize; + /* Next free slot. */ + unsigned int freeslot; + /* Max slot+1 for which we have allocated blocks. */ + unsigned int maxslotplus1; + /* Incrementing ID's */ + /* API needed to return incrementing IDs */ + int incids; + /* Latest ID given back */ + unsigned int latestincnumb; + /* Array of list to hold IDGEN_sHdlId */ + struct lst_t *incidlist; +}; + +/* + * This structure represents internal representation of an Incrementing ID. + */ +struct idgen_id { + void **link; /* to be part of single linked list */ + /* Incrementing ID returned */ + unsigned int incid; + void *hid; +}; + +/* + * Structure contains the ID context. + */ +struct idgen_hdblk { + void **link; /* to be part of single linked list */ + /* Array of handles in this block. */ + void *ahhandles[1]; +}; + +/* + * A hashing function could go here. Currently just makes a circular list of + * max number of concurrent Ids (idgen_context->blksize) in the system. + */ +static unsigned int idgen_func(struct idgen_context *idcontext, unsigned int id) +{ + return ((id - 1) % idcontext->blksize); +} + +int idgen_createcontext(unsigned int maxid, unsigned int blksize, + int incid, void **idgenhandle) +{ + struct idgen_context *idcontext; + + /* Create context structure */ + idcontext = kzalloc(sizeof(*idcontext), GFP_KERNEL); + if (!idcontext) + return IMG_ERROR_OUT_OF_MEMORY; + + /* InitIalise the context */ + lst_init(&idcontext->hdlblklst); + idcontext->maxid = maxid; + idcontext->blksize = blksize; + + /* If we need incrementing Ids */ + idcontext->incids = incid; + idcontext->latestincnumb = 0; + idcontext->incidlist = NULL; + if (idcontext->incids) { + unsigned int i = 0; + /* Initialise the hash table of lists of length ui32BlkSize */ + idcontext->incidlist = kzalloc((sizeof(*idcontext->incidlist) * + idcontext->blksize), GFP_KERNEL); + if (!idcontext->incidlist) { + kfree(idcontext); + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* Initialise all the lists in the hash table */ + for (i = 0; i < idcontext->blksize; i++) + lst_init(&idcontext->incidlist[i]); + } + + /* Return context structure as handle */ + *idgenhandle = idcontext; + + return IMG_SUCCESS; +} + +int idgen_destroycontext(void *idgenhandle) +{ + struct idgen_context *idcontext = (struct idgen_context *)idgenhandle; + struct idgen_hdblk *hdblk; + + if (!idcontext) + return IMG_ERROR_INVALID_PARAMETERS; + + /* If incrementing Ids, free the List of Incrementing Ids */ + if (idcontext->incids) { + struct idgen_id *id; + unsigned int i = 0; + + for (i = 0; i < idcontext->blksize; i++) { + id = lst_removehead(&idcontext->incidlist[i]); + while (id) { + kfree(id); + id = lst_removehead(&idcontext->incidlist[i]); + } + } + kfree(idcontext->incidlist); + } + + /* Remove and free all handle blocks */ + hdblk = (struct idgen_hdblk *)lst_removehead(&idcontext->hdlblklst); + while (hdblk) { + kfree(hdblk); + hdblk = (struct idgen_hdblk *) + lst_removehead(&idcontext->hdlblklst); + } + + /* Free context structure */ + kfree(idcontext); + + return IMG_SUCCESS; +} + +static int idgen_findnextfreeslot(void *idgenhandle, unsigned int prevfreeslot) +{ + struct idgen_context *idcontext = (struct idgen_context *)idgenhandle; + struct idgen_hdblk *hdblk; + unsigned int freslotblk; + unsigned int freeslot; + + if (!idcontext) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Find the block containing the current free slot */ + freeslot = prevfreeslot; + freslotblk = prevfreeslot; + hdblk = (struct idgen_hdblk *)lst_first(&idcontext->hdlblklst); + if (!hdblk) + return IMG_ERROR_FATAL; + + while (freslotblk >= idcontext->blksize) { + freslotblk -= idcontext->blksize; + hdblk = (struct idgen_hdblk *)lst_next(hdblk); + } + + /* Locate the next free slot */ + while (hdblk) { + while (freslotblk < idcontext->blksize) { + if (!hdblk->ahhandles[freslotblk]) { + /* Found */ + idcontext->freeslot = freeslot; + return IMG_SUCCESS; + } + freeslot++; + freslotblk++; + } + freslotblk = 0; + hdblk = (struct idgen_hdblk *)lst_next(hdblk); + } + + /* Beyond the last block */ + idcontext->freeslot = freeslot; + return IMG_SUCCESS; +} + +/* + * This function returns ID structure ( + */ +static struct idgen_id *idgen_getid(struct lst_t *idlist, unsigned int id) +{ + struct idgen_id *idstruct; + + idstruct = lst_first(idlist); + while (idstruct) { + if (idstruct->incid == id) + break; + + idstruct = lst_next(idstruct); + } + return idstruct; +} + +/* + * This function does IDGEN allocation. + */ +int idgen_allocid(void *idgenhandle, void *handle, unsigned int *id) +{ + struct idgen_context *idcontext = (struct idgen_context *)idgenhandle; + struct idgen_hdblk *hdblk; + unsigned int size = 0; + unsigned int freeslot = 0; + unsigned int result = 0; + + if (!idcontext || !handle) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!idcontext->incids) { + /* If the free slot is >= to the max id */ + if (idcontext->freeslot >= idcontext->maxid) { + result = IMG_ERROR_INVALID_ID; + goto error; + } + + /* If all of the allocated Ids have been used */ + if (idcontext->freeslot >= idcontext->maxslotplus1) { + /* Allocate a stream context */ + size = sizeof(*hdblk) + (sizeof(void *) * + (idcontext->blksize - 1)); + hdblk = kzalloc(size, GFP_KERNEL); + if (!hdblk) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + + lst_add(&idcontext->hdlblklst, hdblk); + idcontext->maxslotplus1 += idcontext->blksize; + } + + /* Find the block containing the next free slot */ + freeslot = idcontext->freeslot; + hdblk = (struct idgen_hdblk *)lst_first(&idcontext->hdlblklst); + if (!hdblk) { + result = IMG_ERROR_FATAL; + goto error; + } + while (freeslot >= idcontext->blksize) { + freeslot -= idcontext->blksize; + hdblk = (struct idgen_hdblk *)lst_next(hdblk); + if (!hdblk) { + result = IMG_ERROR_FATAL; + goto error; + } + } + + /* Put handle in the next free slot */ + hdblk->ahhandles[freeslot] = handle; + + *id = idcontext->freeslot + 1; + + /* Find a new free slot */ + result = idgen_findnextfreeslot(idcontext, idcontext->freeslot); + if (result != 0) + goto error; + /* + * If incrementing IDs, just add the ID node to the correct hash table + * list. + */ + } else { + struct idgen_id *psid; + unsigned int currentincnum, funcid; + /* + * If incrementing IDs, increment the id for returning back,and + * save the ID node in the list of ids, indexed by hash function + * (idgen_func). We might want to use a better hashing function + */ + currentincnum = (idcontext->latestincnumb + 1) % + idcontext->maxid; + + /* Increment the id. Wraps if greater then Max Id */ + if (currentincnum == 0) + currentincnum++; + + idcontext->latestincnumb = currentincnum; + + result = IMG_ERROR_INVALID_ID; + do { + /* Add to list in the correct hash table entry */ + funcid = idgen_func(idcontext, idcontext->latestincnumb); + if (idgen_getid(&idcontext->incidlist[funcid], + idcontext->latestincnumb) == NULL) { + psid = kmalloc(sizeof(*psid), GFP_KERNEL); + if (!psid) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + + psid->incid = idcontext->latestincnumb; + psid->hid = handle; + + funcid = idgen_func(idcontext, + idcontext->latestincnumb); + lst_add(&idcontext->incidlist[funcid], + psid); + + result = IMG_SUCCESS; + } else { + idcontext->latestincnumb = + (idcontext->latestincnumb + 1) % + idcontext->maxid; + if (idcontext->latestincnumb == 0) { + /* Do not want to have zero as pic id */ + idcontext->latestincnumb++; + } + /* + * We have reached a point where we have wrapped + * allowed Ids (MaxId) and we want to overwrite + * ID still not released + */ + if (idcontext->latestincnumb == currentincnum) + goto error; + } + } while (result != IMG_SUCCESS); + + *id = psid->incid; + } + return IMG_SUCCESS; +error: + return result; +} + +int idgen_freeid(void *idgenhandle, unsigned int id) +{ + struct idgen_context *idcontext = (struct idgen_context *)idgenhandle; + struct idgen_hdblk *hdblk; + unsigned int origslot; + unsigned int slot; + + if (idcontext->incids) { + /* + * Find the slot in the correct hash table entry, and + * remove the ID. + */ + struct idgen_id *psid; + + psid = idgen_getid(&idcontext->incidlist + [idgen_func(idcontext, id)], id); + if (psid) { + lst_remove(&idcontext->incidlist + [idgen_func(idcontext, id)], psid); + kfree(psid); + } else { + return IMG_ERROR_INVALID_ID; + } + } else { + /* If not incrementing id */ + slot = id - 1; + origslot = slot; + + if (slot >= idcontext->maxslotplus1) + return IMG_ERROR_INVALID_ID; + + /* Find the block containing the id */ + hdblk = (struct idgen_hdblk *)lst_first(&idcontext->hdlblklst); + if (!hdblk) + return IMG_ERROR_FATAL; + + while (slot >= idcontext->blksize) { + slot -= idcontext->blksize; + hdblk = (struct idgen_hdblk *)lst_next(hdblk); + if (!hdblk) + return IMG_ERROR_FATAL; + } + + /* Slot should be occupied */ + if (!hdblk->ahhandles[slot]) + return IMG_ERROR_INVALID_ID; + + /* Free slot */ + hdblk->ahhandles[slot] = NULL; + + /* If this slot is before the previous free slot */ + if ((origslot) < idcontext->freeslot) + idcontext->freeslot = origslot; + } + return IMG_SUCCESS; +} + +int idgen_gethandle(void *idgenhandle, unsigned int id, void **handle) +{ + struct idgen_context *idcontext = (struct idgen_context *)idgenhandle; + struct idgen_hdblk *hdblk; + unsigned int slot; + + if (!idcontext) + return IMG_ERROR_INVALID_PARAMETERS; + + if (idcontext->incids) { + /* + * Find the slot in the correct hash table entry, and return + * the handles. + */ + struct idgen_id *psid; + + psid = idgen_getid(&idcontext->incidlist + [idgen_func(idcontext, id)], id); + if (psid) + *handle = psid->hid; + + else + return IMG_ERROR_INVALID_ID; + } else { + /* If not incrementing IDs */ + slot = id - 1; + if (slot >= idcontext->maxslotplus1) + return IMG_ERROR_INVALID_ID; + + /* Find the block containing the id */ + hdblk = (struct idgen_hdblk *)lst_first(&idcontext->hdlblklst); + if (!hdblk) + return IMG_ERROR_INVALID_PARAMETERS; + + while (slot >= idcontext->blksize) { + slot -= idcontext->blksize; + hdblk = (struct idgen_hdblk *)lst_next(hdblk); + if (!hdblk) + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Slot should be occupied */ + if (!hdblk->ahhandles[slot]) + return IMG_ERROR_INVALID_ID; + + /* Return the handle */ + *handle = hdblk->ahhandles[slot]; + } + + return IMG_SUCCESS; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/idgen_api.h b/drivers/media/platform/vxe-vxd/common/idgen_api.h --- a/drivers/media/platform/vxe-vxd/common/idgen_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/idgen_api.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ID generation manager API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ +#ifndef __IDGENAPI_H__ +#define __IDGENAPI_H__ + +#include + +#include "img_errors.h" + +/* + * This function is used to create Id generation context. + * NOTE: Should only be called once to setup the context structure. + * NOTE: The client is responsible for providing thread/process safe locks on + * the context structure to maintain coherence. + */ +int idgen_createcontext(unsigned int maxid, unsigned int blksize, + int incid, void **idgenhandle); + +/* + * This function is used to destroy an Id generation context. This function + * discards any handle blocks associated with the context. + * NOTE: The client is responsible for providing thread/process safe locks on + * the context structure to maintain coherence. + */ +int idgen_destroycontext(void *idgenhandle); + +/* + * This function is used to associate a handle with an Id. + * NOTE: The client is responsible for providing thread/process safe locks on + * the context structure to maintain coherency. + */ +int idgen_allocid(void *idgenhandle, void *handle, unsigned int *id); + +/* + * This function is used to free an Id. + * NOTE: The client is responsible for providing thread/process safe locks on + * the context structure to maintain coherency. + */ +int idgen_freeid(void *idgenhandle, unsigned int id); + +/* + * This function is used to get the handle associated with an Id. + * NOTE: The client is responsible for providing thread/process safe locks on + * the context structure to maintain coherency. + */ +int idgen_gethandle(void *idgenhandle, unsigned int id, void **handle); +#endif /* __IDGENAPI_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/img_errors.h b/drivers/media/platform/vxe-vxd/common/img_errors.h --- a/drivers/media/platform/vxe-vxd/common/img_errors.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/img_errors.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Error codes. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ +#ifndef __IMG_ERRORS__ +#define __IMG_ERRORS__ + +#include +#include +#include +#include + +#define IMG_DBG_ASSERT(expected) ({WARN_ON(!(expected)); 0; }) + +/* @brief Success */ +#define IMG_SUCCESS (0) +/* @brief Timeout */ +#define IMG_ERROR_TIMEOUT (1) +/* @brief memory allocation failed */ +#define IMG_ERROR_MALLOC_FAILED (2) +/* @brief Unspecified fatal error */ +#define IMG_ERROR_FATAL (3) +/* @brief Memory allocation failed */ +#define IMG_ERROR_OUT_OF_MEMORY (4) +/* @brief Device is not found */ +#define IMG_ERROR_DEVICE_NOT_FOUND (5) +/* @brief Device is not available/in use */ +#define IMG_ERROR_DEVICE_UNAVAILABLE (6) +/* @brief Generic/unspecified failure */ +#define IMG_ERROR_GENERIC_FAILURE (7) +/* @brief Operation was interrupted - retry */ +#define IMG_ERROR_INTERRUPTED (8) +/* @brief Invalid id */ +#define IMG_ERROR_INVALID_ID (9) +/* @brief A signature value was found to be incorrect */ +#define IMG_ERROR_SIGNATURE_INCORRECT (10) +/* @brief The provided parameters were inconsistent/incorrect */ +#define IMG_ERROR_INVALID_PARAMETERS (11) +/* @brief A list/pool has run dry */ +#define IMG_ERROR_STORAGE_TYPE_EMPTY (12) +/* @brief A list is full */ +#define IMG_ERROR_STORAGE_TYPE_FULL (13) +/* @brief Something has already occurred which the code thinks has not */ +#define IMG_ERROR_ALREADY_COMPLETE (14) +/* @brief A state machine is in an unexpected/illegal state */ +#define IMG_ERROR_UNEXPECTED_STATE (15) +/* @brief A required resource could not be created/locked */ +#define IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE (16) +/* + * @brief An attempt to access a structure/resource was + * made before it was initialised + */ +#define IMG_ERROR_NOT_INITIALISED (17) +/* + * @brief An attempt to initialise a structure/resource + * was made when it has already been initialised + */ +#define IMG_ERROR_ALREADY_INITIALISED (18) +/* @brief A provided value exceeded stated bounds */ +#define IMG_ERROR_VALUE_OUT_OF_RANGE (19) +/* @brief The operation has been cancelled */ +#define IMG_ERROR_CANCELLED (20) +/* @brief A specified minimum has not been met */ +#define IMG_ERROR_MINIMUM_LIMIT_NOT_MET (21) +/* @brief The requested feature or mode is not supported */ +#define IMG_ERROR_NOT_SUPPORTED (22) +/* @brief A device or process was idle */ +#define IMG_ERROR_IDLE (23) +/* @brief A device or process was busy */ +#define IMG_ERROR_BUSY (24) +/* @brief The device or resource has been disabled */ +#define IMG_ERROR_DISABLED (25) +/* @brief The requested operation is not permitted at this time */ +#define IMG_ERROR_OPERATION_PROHIBITED (26) +/* @brief The entry read from the MMU page directory is invalid */ +#define IMG_ERROR_MMU_PAGE_DIRECTORY_FAULT (27) +/* @brief The entry read from an MMU page table is invalid */ +#define IMG_ERROR_MMU_PAGE_TABLE_FAULT (28) +/* @brief The entry read from an MMU page catalogue is invalid */ +#define IMG_ERROR_MMU_PAGE_CATALOGUE_FAULT (29) +/* @brief Memory can not be freed as it is still been used */ +#define IMG_ERROR_MEMORY_IN_USE (30) +/* @brief A mismatch has unexpectedly occurred in data */ +#define IMG_ERROR_TEST_MISMATCH (31) + +#define IMG_ERROR_INVALID_CONTEXT (32) + +#define IMG_ERROR_RETRY (33) +#define IMG_ERROR_UNDEFINED (34) +#define IMG_ERROR_INVALID_SIZE (35) +#define IMG_ERROR_SURFACE_LOCKED (36) + +/* Mutex subclasses */ +#define SUBCLASS_BASE 0 +#define SUBCLASS_VXD_V4L2 1 +#define SUBCLASS_VXE_V4L2 1 +#define SUBCLASS_BSPP 1 +#define SUBCLASS_ADDR_ALLOC 7 +#define SUBCLASS_IMGMEM 6 +#define SUBCLASS_RMAN 1 +#define SUBCLASS_TALMMU 5 +#define SUBCLASS_VXD_CORE 2 +#define SUBCLASS_POOL 3 +#define SUBCLASS_POOL_RES 5 +#define SUBCLASS_TOPAZ_API 2 +#define SUBCLASS_TOPAZDD_TX 4 +#define SUBCLASS_TOPAZDD 3 + +#endif /* __IMG_ERRORS__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/img_mem.h b/drivers/media/platform/vxe-vxd/common/img_mem.h --- a/drivers/media/platform/vxe-vxd/common/img_mem.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/img_mem.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Typedefs for memory pool and attributes + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#ifndef __IMG_MEM__ +#define __IMG_MEM__ + +/* + * This type defines the memory attributes. + * @0x00000001: Memory to be allocated as cached + * @0x00000002: Memory to be allocated as uncached + * @0x00000004: Memory to be allocated as write-combined + * (or equivalent buffered/burst writes mechanism) + * @0x00001000: Memory can be read only by the core + * @0x00002000: Memory can be written only by the core + * @0x00010000: Memory should be readable by the cpu + * @0x00020000: Memory should be writable by the cpu + */ +enum sys_emem_attrib { + SYS_MEMATTRIB_CACHED = 0x00000001, + SYS_MEMATTRIB_UNCACHED = 0x00000002, + SYS_MEMATTRIB_WRITECOMBINE = 0x00000004, + SYS_MEMATTRIB_SECURE = 0x00000010, + SYS_MEMATTRIB_INPUT = 0x00000100, + SYS_MEMATTRIB_OUTPUT = 0x00000200, + SYS_MEMATTRIB_INTERNAL = 0x00000400, + SYS_MEMATTRIB_CORE_READ_ONLY = 0x00001000, + SYS_MEMATTRIB_CORE_WRITE_ONLY = 0x00002000, + SYS_MEMATTRIB_CPU_READ = 0x00010000, + SYS_MEMATTRIB_CPU_WRITE = 0x00020000, + SYS_MEMATTRIB_FORCE32BITS = 0x7FFFFFFFU +}; + +#endif /* __IMG_MEM__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/img_mem_man.c b/drivers/media/platform/vxe-vxd/common/img_mem_man.c --- a/drivers/media/platform/vxe-vxd/common/img_mem_man.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/img_mem_man.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC Memory Manager + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imgmmu.h" +#include "img_mem_man.h" +#include "img_errors.h" + +#define VXD_MMU_SHIFT 8 /* assume 40-bit MMU */ +/* heaps ids (global) */ +#define MIN_HEAP 1 +#define MAX_HEAP 16 + +/* + * struct dev_mem_man - the device memory management + * @heaps: idr list of heap for the device memory manager + * @mem_ctxs: contains lists of mem_ctx + * @mutex: mutex for this device + */ +struct mem_man { + void *dev; + struct idr *heaps; + struct list_head mem_ctxs; + struct mutex *mutex; /* mutex for this device */ +}; + +static struct mem_man mem_man_data = {0}; + +/** + * struct mmu_page - the mmu page information for the buffer + * @buffer: buffer pointer for the particular mmu_page + * @page_cfg: mmu page configuration of physical and virtual addr + * @addr_shift: address shifting information + */ +struct mmu_page { + struct buffer *buffer; + struct mmu_page_cfg page_cfg; + unsigned int addr_shift; +}; + +static void _img_mem_free(struct buffer *buffer); +static void _img_mmu_unmap(struct mmu_ctx_mapping *mapping); +static void _img_mmu_ctx_destroy(struct mmu_ctx *ctx); + +#if defined(DEBUG_DECODER_DRIVER) +static unsigned char *get_heap_name(enum heap_type type) +{ + switch (type) { + case MEM_HEAP_TYPE_UNIFIED: + return "unified"; + default: + return "unknown"; + } +} +#endif + +int img_mem_add_heap(const struct heap_config *heap_cfg, int *heap_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct heap *heap; + int (*init_fn)(const struct heap_config *heap_cfg, struct heap *heap); + int ret; + + switch (heap_cfg->type) { + case MEM_HEAP_TYPE_UNIFIED: + init_fn = img_mem_unified_init; + break; + default: + dev_err(mem_man->dev, "%s: heap type %d unknown\n", __func__, + heap_cfg->type); + return -EINVAL; + } + + heap = kmalloc(sizeof(*heap), GFP_KERNEL); + if (!heap) + return -ENOMEM; + + ret = mutex_lock_interruptible_nested(mem_man->mutex, SUBCLASS_IMGMEM); + if (ret) + goto lock_failed; + + ret = idr_alloc(mem_man->heaps, heap, MIN_HEAP, MAX_HEAP, GFP_KERNEL); + if (ret < 0) { + dev_err(mem_man->dev, "%s: idr_alloc failed\n", __func__); + goto alloc_id_failed; + } + + heap->id = ret; + heap->type = heap_cfg->type; + heap->options = heap_cfg->options; + heap->to_dev_addr = heap_cfg->to_dev_addr; + heap->priv = NULL; + + ret = init_fn(heap_cfg, heap); + if (ret) { + dev_err(mem_man->dev, "%s: heap init failed\n", __func__); + goto heap_init_failed; + } + + *heap_id = heap->id; + mutex_unlock(mem_man->mutex); + +#ifdef DEBUG_DECODER_DRIVER + dev_info(mem_man->dev, "%s created heap %d type %d (%s)\n", + __func__, *heap_id, heap_cfg->type, get_heap_name(heap->type)); +#endif + return 0; + +heap_init_failed: + idr_remove(mem_man->heaps, heap->id); +alloc_id_failed: + mutex_unlock(mem_man->mutex); +lock_failed: + kfree(heap); + return ret; +} + +static void _img_mem_del_heap(struct heap *heap) +{ + struct mem_man *mem_man = &mem_man_data; + + if (heap->ops->destroy) + heap->ops->destroy(heap); + + idr_remove(mem_man->heaps, heap->id); +} + +void img_mem_del_heap(int heap_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct heap *heap; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + heap = idr_find(mem_man->heaps, heap_id); + if (!heap) { + dev_warn(mem_man->dev, "%s heap %d not found!\n", __func__, + heap_id); + mutex_unlock(mem_man->mutex); + return; + } + + _img_mem_del_heap(heap); + + mutex_unlock(mem_man->mutex); + + kfree(heap); +} + +int img_mem_create_ctx(struct mem_ctx **new_ctx) +{ + struct mem_man *mem_man = &mem_man_data; + struct mem_ctx *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->buffers = kzalloc(sizeof(*ctx->buffers), GFP_KERNEL); + if (!ctx->buffers) + return -ENOMEM; + idr_init(ctx->buffers); + + INIT_LIST_HEAD(&ctx->mmu_ctxs); + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + list_add(&ctx->mem_man_entry, &mem_man->mem_ctxs); + mutex_unlock(mem_man->mutex); + + *new_ctx = ctx; + return 0; +} + +static void _img_mem_destroy_ctx(struct mem_ctx *ctx) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + int buff_id; + + /* free derelict mmu contexts */ + while (!list_empty(&ctx->mmu_ctxs)) { + struct mmu_ctx *mc; + + mc = list_first_entry(&ctx->mmu_ctxs, + struct mmu_ctx, mem_ctx_entry); + dev_warn(mem_man->dev, "%s: found derelict mmu context %p\n", + __func__, mc); + _img_mmu_ctx_destroy(mc); + kfree(mc); + } + + /* free derelict buffers */ + buff_id = MEM_MAN_MIN_BUFFER; + buffer = idr_get_next(ctx->buffers, &buff_id); + while (buffer) { + dev_warn(mem_man->dev, "%s: found derelict buffer %d\n", + __func__, buff_id); + if (buffer->heap) + _img_mem_free(buffer); + else + idr_remove(ctx->buffers, buffer->id); + kfree(buffer); + buff_id = MEM_MAN_MIN_BUFFER; + buffer = idr_get_next(ctx->buffers, &buff_id); + } + + idr_destroy(ctx->buffers); + kfree(ctx->buffers); + __list_del_entry(&ctx->mem_man_entry); +} + +void img_mem_destroy_ctx(struct mem_ctx *ctx) +{ + struct mem_man *mem_man = &mem_man_data; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + _img_mem_destroy_ctx(ctx); + mutex_unlock(mem_man->mutex); + + kfree(ctx); +} + +static int _img_mem_alloc(void *device, struct mem_ctx *ctx, + struct heap *heap, unsigned long size, + enum mem_attr attr, struct buffer **buffer_new) +{ + struct buffer *buffer; + int ret; + + if (size == 0) { + dev_err(device, "%s: buffer size is zero\n", __func__); + return -EINVAL; + } + + if (!heap->ops || !heap->ops->alloc) { + dev_err(device, "%s: no alloc function in heap %d!\n", + __func__, heap->id); + return -EINVAL; + } + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = idr_alloc(ctx->buffers, buffer, + MEM_MAN_MIN_BUFFER, MEM_MAN_MAX_BUFFER, GFP_KERNEL); + if (ret < 0) { + dev_err(device, "%s: idr_alloc failed\n", __func__); + goto idr_alloc_failed; + } + + buffer->id = ret; + buffer->request_size = size; + buffer->actual_size = ((size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; + buffer->device = device; + buffer->mem_ctx = ctx; + buffer->heap = heap; + INIT_LIST_HEAD(&buffer->mappings); + buffer->kptr = NULL; + buffer->priv = NULL; + + ret = heap->ops->alloc(device, heap, buffer->actual_size, attr, + buffer); + if (ret) { + dev_err(device, "%s: heap %d alloc failed\n", __func__, + heap->id); + goto heap_alloc_failed; + } + + *buffer_new = buffer; + + dev_dbg(device, "%s heap %p ctx %p created buffer %d (%p) actual_size %zu\n", + __func__, heap, ctx, buffer->id, buffer, buffer->actual_size); + return 0; + +heap_alloc_failed: + idr_remove(ctx->buffers, buffer->id); +idr_alloc_failed: + kfree(buffer); + return ret; +} + +int img_mem_alloc(void *device, struct mem_ctx *ctx, int heap_id, + unsigned long size, enum mem_attr attr, int *buf_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct heap *heap; + struct buffer *buffer; + int ret; + + dev_dbg(device, "%s heap %d ctx %p size %zu\n", __func__, heap_id, + ctx, size); + + ret = mutex_lock_interruptible_nested(mem_man->mutex, SUBCLASS_IMGMEM); + if (ret) + return ret; + + heap = idr_find(mem_man->heaps, heap_id); + if (!heap) { + dev_err(device, "%s: heap id %d not found\n", __func__, + heap_id); + mutex_unlock(mem_man->mutex); + return -EINVAL; + } + + ret = _img_mem_alloc(device, ctx, heap, size, attr, &buffer); + if (ret) { + mutex_unlock(mem_man->mutex); + return ret; + } + + *buf_id = buffer->id; + mutex_unlock(mem_man->mutex); + + dev_dbg(device, "%s heap %d ctx %p created buffer %d (%p) size %zu\n", + __func__, heap_id, ctx, *buf_id, buffer, size); + return ret; +} + +static int _img_mem_import(void *device, struct mem_ctx *ctx, + unsigned long size, enum mem_attr attr, struct buffer **buffer_new) +{ + struct buffer *buffer; + int ret; + + if (size == 0) { + dev_err(device, "%s: buffer size is zero\n", __func__); + return -EINVAL; + } + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = idr_alloc(ctx->buffers, buffer, + MEM_MAN_MIN_BUFFER, MEM_MAN_MAX_BUFFER, GFP_KERNEL); + if (ret < 0) { + dev_err(device, "%s: idr_alloc failed\n", __func__); + goto idr_alloc_failed; + } + + buffer->id = ret; + buffer->request_size = size; + buffer->actual_size = ((size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; + buffer->device = device; + buffer->mem_ctx = ctx; + buffer->heap = NULL; + INIT_LIST_HEAD(&buffer->mappings); + buffer->kptr = NULL; + buffer->priv = NULL; + + *buffer_new = buffer; + + dev_dbg(device, "%s ctx %p created buffer %d (%p) actual_size %zu\n", + __func__, ctx, buffer->id, buffer, buffer->actual_size); + return 0; + +idr_alloc_failed: + kfree(buffer); + return ret; +} + +int img_mem_import(void *device, struct mem_ctx *ctx, + unsigned long size, enum mem_attr attr, int *buf_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + int ret; + + dev_dbg(device, "%s ctx %p size %zu\n", __func__, ctx, size); + + ret = mutex_lock_interruptible_nested(mem_man->mutex, SUBCLASS_IMGMEM); + if (ret) + return ret; + + ret = _img_mem_import(device, ctx, size, attr, &buffer); + if (ret) { + mutex_unlock(mem_man->mutex); + return ret; + } + + *buf_id = buffer->id; + mutex_unlock(mem_man->mutex); + + dev_dbg(device, "%s ctx %p created buffer %d (%p) size %zu\n", + __func__, ctx, *buf_id, buffer, size); + return ret; +} + +static void _img_mem_free(struct buffer *buffer) +{ + void *dev = buffer->device; + struct heap *heap = buffer->heap; + struct mem_ctx *ctx = buffer->mem_ctx; + + if (!heap->ops || !heap->ops->free) { + dev_err(dev, "%s: no free function in heap %d!\n", + __func__, heap->id); + return; + } + + while (!list_empty(&buffer->mappings)) { + struct mmu_ctx_mapping *map; + + map = list_first_entry(&buffer->mappings, + struct mmu_ctx_mapping, buffer_entry); + dev_warn(dev, "%s: found mapping for buffer %d (size %zu)\n", + __func__, map->buffer->id, map->buffer->actual_size); + + _img_mmu_unmap(map); + + kfree(map); + } + + heap->ops->free(heap, buffer); + + idr_remove(ctx->buffers, buffer->id); +} + +void img_mem_free(struct mem_ctx *ctx, int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + buffer = idr_find(ctx->buffers, buff_id); + if (!buffer) { + dev_err(mem_man->dev, "%s: buffer id %d not found\n", + __func__, buff_id); + mutex_unlock(mem_man->mutex); + return; + } + + _img_mem_free(buffer); + + mutex_unlock(mem_man->mutex); + + kfree(buffer); +} + +void img_mem_free_bufid(struct mem_ctx *ctx, int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + buffer = idr_find(ctx->buffers, buff_id); + if (!buffer) { + dev_err(mem_man->dev, "%s: buffer id %d not found\n", + __func__, buff_id); + mutex_unlock(mem_man->mutex); + return; + } + + idr_remove(ctx->buffers, buffer->id); + + mutex_unlock(mem_man->mutex); + + kfree(buffer); +} + +static int _img_mem_map_km(struct buffer *buffer) +{ + void *dev = buffer->device; + struct heap *heap = buffer->heap; + + if (!heap->ops || !heap->ops->map_km) { + dev_err(dev, "%s: no map_km in heap %d!\n", __func__, heap->id); + return -EINVAL; + } + + return heap->ops->map_km(heap, buffer); +} + +int img_mem_map_km(struct mem_ctx *ctx, int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + int ret; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + buffer = idr_find(ctx->buffers, buff_id); + if (!buffer) { + dev_err(mem_man->dev, "%s: buffer id %d not found\n", + __func__, buff_id); + mutex_unlock(mem_man->mutex); + return -EINVAL; + } + + ret = _img_mem_map_km(buffer); + + mutex_unlock(mem_man->mutex); + + return ret; +} + +void *img_mem_get_kptr(struct mem_ctx *ctx, int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + void *kptr; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + buffer = idr_find(ctx->buffers, buff_id); + if (!buffer) { + dev_err(mem_man->dev, "%s: buffer id %d not found\n", __func__, + buff_id); + mutex_unlock(mem_man->mutex); + return NULL; + } + kptr = buffer->kptr; + mutex_unlock(mem_man->mutex); + return kptr; +} + +static void _img_mem_sync_cpu_to_device(struct buffer *buffer) +{ + struct heap *heap = buffer->heap; + + if (heap->ops && heap->ops->sync_cpu_to_dev) + heap->ops->sync_cpu_to_dev(heap, buffer); + + /* sync to device memory */ + mb(); +} + +int img_mem_sync_cpu_to_device(struct mem_ctx *ctx, int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + buffer = idr_find(ctx->buffers, buff_id); + if (!buffer) { + dev_err(mem_man->dev, "%s: buffer id %d not found\n", __func__, + buff_id); + mutex_unlock(mem_man->mutex); + return -EINVAL; + } + + _img_mem_sync_cpu_to_device(buffer); + + mutex_unlock(mem_man->mutex); + return 0; +} + +static void _img_mem_sync_device_to_cpu(struct buffer *buffer) +{ + struct heap *heap = buffer->heap; + + if (heap->ops && heap->ops->sync_dev_to_cpu) + heap->ops->sync_dev_to_cpu(heap, buffer); +} + +int img_mem_sync_device_to_cpu(struct mem_ctx *ctx, int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct buffer *buffer; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + buffer = idr_find(ctx->buffers, buff_id); + if (!buffer) { + dev_err(mem_man->dev, "%s: buffer id %d not found\n", __func__, + buff_id); + mutex_unlock(mem_man->mutex); + return -EINVAL; + } + + _img_mem_sync_device_to_cpu(buffer); + + mutex_unlock(mem_man->mutex); + return 0; +} + +static struct mmu_page_cfg *mmu_page_alloc(void *arg) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_ctx *mmu_ctx = arg; + struct mmu_page *page; + struct buffer *buffer; + struct heap *heap; + int ret; + + dev_dbg(mmu_ctx->device, "%s:%d arg %p\n", __func__, __LINE__, arg); + + WARN_ON(!mutex_is_locked(mem_man->mutex)); + + page = kzalloc(sizeof(*page), GFP_KERNEL); + if (!page) + return NULL; + + ret = _img_mem_alloc(mmu_ctx->device, mmu_ctx->mem_ctx, + mmu_ctx->heap, PAGE_SIZE, (enum mem_attr)0, &buffer); + if (ret) { + dev_err(mmu_ctx->device, "%s: img_mem_alloc failed (%d)\n", + __func__, ret); + goto free_page; + } + + ret = _img_mem_map_km(buffer); + if (ret) { + dev_err(mmu_ctx->device, "%s: img_mem_map_km failed (%d)\n", + __func__, ret); + goto free_buffer; + } + + page->addr_shift = mmu_ctx->mmu_config_addr_width - 32; + page->buffer = buffer; + page->page_cfg.cpu_virt_addr = (unsigned long)buffer->kptr; + + heap = buffer->heap; + if (heap->ops && heap->ops->get_sg_table) { + void *sgt; + + ret = heap->ops->get_sg_table(heap, buffer, &sgt); + if (ret) { + dev_err(mmu_ctx->device, + "%s: heap %d buffer %d no sg_table!\n", + __func__, heap->id, buffer->id); + ret = -EINVAL; + goto free_buffer; + } + page->page_cfg.phys_addr = sg_phys(img_mmu_get_sgl(sgt)); + } else { + dev_err(mmu_ctx->device, "%s: heap %d buffer %d no get_sg!\n", + __func__, heap->id, buffer->id); + ret = -EINVAL; + goto free_buffer; + } + + dev_dbg(mmu_ctx->device, "%s:%d virt addr %#lx\n", __func__, __LINE__, + page->page_cfg.cpu_virt_addr); + dev_dbg(mmu_ctx->device, "%s:%d phys addr %#llx\n", __func__, __LINE__, + page->page_cfg.phys_addr); + return &page->page_cfg; + +free_buffer: + _img_mem_free(buffer); + kfree(buffer); +free_page: + kfree(page); + return NULL; +} + +static void mmu_page_free(struct mmu_page_cfg *arg) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_page *page; + + page = container_of(arg, struct mmu_page, page_cfg); + + WARN_ON(!mutex_is_locked(mem_man->mutex)); + + _img_mem_free(page->buffer); + kfree(page->buffer); + kfree(page); +} + +static void mmu_page_write(struct mmu_page_cfg *page_cfg, + unsigned int offset, unsigned long long addr, + unsigned int flags) +{ + unsigned int *mem = (unsigned int *)page_cfg->cpu_virt_addr; + struct mmu_page *mmu_page; + struct heap *heap; + + mmu_page = container_of(page_cfg, struct mmu_page, page_cfg); + heap = mmu_page->buffer->heap; + + /* skip translation when flags are zero, assuming address is invalid */ + if (flags && heap->to_dev_addr) + addr = heap->to_dev_addr(&heap->options, addr); + addr >>= mmu_page->addr_shift; + + mem[offset] = addr | flags; +} + +static void mmu_update_page(struct mmu_page_cfg *arg) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_page *page; + + page = container_of(arg, struct mmu_page, page_cfg); + + WARN_ON(!mutex_is_locked(mem_man->mutex)); + + _img_mem_sync_cpu_to_device(page->buffer); +} + +int img_mmu_ctx_create(void *device, unsigned int mmu_config_addr_width, + struct mem_ctx *mem_ctx, int heap_id, + void (*callback_fn)(enum mmu_callback_type type, + int buff_id, void *data), + void *callback_data, struct mmu_ctx **mmu_ctx) +{ + struct mem_man *mem_man = &mem_man_data; + + static struct mmu_info mmu_functions = { + .pfn_page_alloc = mmu_page_alloc, + .pfn_page_free = mmu_page_free, + .pfn_page_write = mmu_page_write, + .pfn_page_update = mmu_update_page, + }; + struct mmu_ctx *ctx; + int ret; + + if (mmu_config_addr_width < 32) { + dev_err(device, + "%s: invalid addr_width (%d) must be >= 32 !\n", + __func__, mmu_config_addr_width); + return -EINVAL; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->device = device; + ctx->mem_ctx = mem_ctx; + ctx->mmu_config_addr_width = mmu_config_addr_width; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + ctx->heap = idr_find(mem_man->heaps, heap_id); + if (!ctx->heap) { + dev_err(device, "%s: invalid heap_id (%d)!\n", __func__, + heap_id); + mutex_unlock(mem_man->mutex); + kfree(ctx); + return -EINVAL; + } + + mmu_functions.alloc_ctx = ctx; + ctx->mmu_dir = mmu_create_directory(&mmu_functions); + if (IS_ERR_VALUE((unsigned long)ctx->mmu_dir)) { + ret = (long)(ctx->mmu_dir); + dev_err(device, "%s: directory create failed (%d)!\n", __func__, + ret); + ctx->mmu_dir = NULL; + mutex_unlock(mem_man->mutex); + kfree(ctx); + return ret; + } + + list_add(&ctx->mem_ctx_entry, &mem_ctx->mmu_ctxs); + INIT_LIST_HEAD(&ctx->mappings); + + ctx->callback_fn = callback_fn; + ctx->callback_data = callback_data; + + *mmu_ctx = ctx; + + mutex_unlock(mem_man->mutex); + + return 0; +} + +static void _img_mmu_ctx_destroy(struct mmu_ctx *ctx) +{ + struct mem_man *mem_man = &mem_man_data; + int ret; + + while (!list_empty(&ctx->mappings)) { + struct mmu_ctx_mapping *map; + + map = list_first_entry(&ctx->mappings, + struct mmu_ctx_mapping, mmu_ctx_entry); +#ifdef DEBUG_DECODER_DRIVER + dev_info(ctx->device, + "%s: found mapped buffer %d (size %zu)\n", + __func__, map->buffer->id, map->buffer->request_size); +#endif + + _img_mmu_unmap(map); + + kfree(map); + } + + ret = mmu_destroy_directory(ctx->mmu_dir); + if (ret) + dev_err(mem_man->dev, "mmu_destroy_directory failed (%d)!\n", + ret); + __list_del_entry(&ctx->mem_ctx_entry); +} + +void img_mmu_ctx_destroy(struct mmu_ctx *ctx) +{ + struct mem_man *mem_man = &mem_man_data; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + _img_mmu_ctx_destroy(ctx); + mutex_unlock(mem_man->mutex); + + kfree(ctx); +} + +int img_mmu_map_sg(struct mmu_ctx *mmu_ctx, struct mem_ctx *mem_ctx, + int buff_id, void *sgt, unsigned int virt_addr, + unsigned int map_flags) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_ctx_mapping *mapping; + struct mmu_heap_alloc heap_alloc; + struct buffer *buffer; + int ret = 0; + + dev_dbg(mmu_ctx->device, "%s sgt %p virt_addr %#x\n", __func__, + sgt, virt_addr); + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) + return -ENOMEM; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + buffer = idr_find(mem_ctx->buffers, buff_id); + if (!buffer) { + dev_err(mmu_ctx->device, "%s: buffer id %d not found\n", + __func__, buff_id); + ret = -EINVAL; + goto error; + } + dev_dbg(mmu_ctx->device, "%s buffer %d 0x%p size %zu virt_addr %#x\n", + __func__, buff_id, buffer, buffer->request_size, virt_addr); + + heap_alloc.virt_addr = virt_addr; + heap_alloc.alloc_size = buffer->actual_size; + + mapping->mmu_ctx = mmu_ctx; + mapping->buffer = buffer; + mapping->virt_addr = virt_addr; + + if (sgt) { + struct sg_table *sgt_new = sgt; + + mapping->map = mmu_directory_map_sg(mmu_ctx->mmu_dir, sgt_new->sgl, + &heap_alloc, map_flags); + if (IS_ERR_VALUE((unsigned long)mapping->map)) { + ret = (long)(mapping->map); + mapping->map = NULL; + } + } else { + dev_err(mmu_ctx->device, "%s: buffer %d no get_sg!\n", + __func__, buffer->id); + ret = -EINVAL; + goto error; + } + if (ret) { + dev_err(mmu_ctx->device, "mmu_directory_map_sg failed (%d)!\n", + ret); + goto error; + } + + list_add(&mapping->mmu_ctx_entry, &mmu_ctx->mappings); + list_add(&mapping->buffer_entry, &mapping->buffer->mappings); + + if (mmu_ctx->callback_fn) + mmu_ctx->callback_fn(MMU_CALLBACK_MAP, buffer->id, + mmu_ctx->callback_data); + + mutex_unlock(mem_man->mutex); + return 0; + +error: + mutex_unlock(mem_man->mutex); + kfree(mapping); + return ret; +} + +int img_mmu_map(struct mmu_ctx *mmu_ctx, struct mem_ctx *mem_ctx, + int buff_id, unsigned int virt_addr, unsigned int map_flags) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_ctx_mapping *mapping; + struct mmu_heap_alloc heap_alloc; + struct buffer *buffer; + struct heap *heap; + int ret; + + dev_dbg(mmu_ctx->device, "%s buffer %d virt_addr %#x\n", __func__, + buff_id, virt_addr); + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) + return -ENOMEM; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + buffer = idr_find(mem_ctx->buffers, buff_id); + if (!buffer) { + dev_err(mmu_ctx->device, "%s: buffer id %d not found\n", + __func__, buff_id); + ret = -EINVAL; + goto error; + } + dev_dbg(mmu_ctx->device, "%s buffer %d 0x%p size %zu virt_addr %#x\n", + __func__, buff_id, buffer, buffer->request_size, virt_addr); + + heap_alloc.virt_addr = virt_addr; + heap_alloc.alloc_size = buffer->actual_size; + + mapping->mmu_ctx = mmu_ctx; + mapping->buffer = buffer; + mapping->virt_addr = virt_addr; + + heap = buffer->heap; + if (heap->ops && heap->ops->get_sg_table) { + void *sgt; + + ret = heap->ops->get_sg_table(heap, buffer, &sgt); + if (ret) { + dev_err(mmu_ctx->device, + "%s: heap %d buffer %d no sg_table!\n", + __func__, heap->id, buffer->id); + goto error; + } + + mapping->map = mmu_directory_map_sg(mmu_ctx->mmu_dir, img_mmu_get_sgl(sgt), + &heap_alloc, map_flags); + if (IS_ERR_VALUE((unsigned long)mapping->map)) { + ret = (long)(mapping->map); + mapping->map = NULL; + } + } else { + dev_err(mmu_ctx->device, "%s: heap %d buffer %d no get_sg!\n", + __func__, heap->id, buffer->id); + ret = -EINVAL; + goto error; + } + if (ret) { + dev_err(mmu_ctx->device, "mmu_directory_map failed (%d)!\n", + ret); + goto error; + } + + list_add(&mapping->mmu_ctx_entry, &mmu_ctx->mappings); + list_add(&mapping->buffer_entry, &mapping->buffer->mappings); + + if (mmu_ctx->callback_fn) + mmu_ctx->callback_fn(MMU_CALLBACK_MAP, buffer->id, + mmu_ctx->callback_data); + + mutex_unlock(mem_man->mutex); + return 0; + +error: + mutex_unlock(mem_man->mutex); + kfree(mapping); + return ret; +} + +static void _img_mmu_unmap(struct mmu_ctx_mapping *mapping) +{ + struct mmu_ctx *ctx = mapping->mmu_ctx; + int res; + + dev_dbg(ctx->device, "%s:%d mapping %p buffer %d\n", __func__, + __LINE__, mapping, mapping->buffer->id); + + res = mmu_directory_unmap(mapping->map); + if (res) + dev_warn(ctx->device, "mmu_directory_unmap failed (%d)!\n", + res); + + __list_del_entry(&mapping->mmu_ctx_entry); + __list_del_entry(&mapping->buffer_entry); + + if (ctx->callback_fn) + ctx->callback_fn(MMU_CALLBACK_UNMAP, mapping->buffer->id, + ctx->callback_data); +} + +int img_mmu_unmap(struct mmu_ctx *mmu_ctx, struct mem_ctx *mem_ctx, + int buff_id) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_ctx_mapping *mapping; + struct list_head *lst; + + dev_dbg(mmu_ctx->device, "%s:%d buffer %d\n", __func__, __LINE__, + buff_id); + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + mapping = NULL; + list_for_each(lst, &mmu_ctx->mappings) { + struct mmu_ctx_mapping *m; + + m = list_entry(lst, struct mmu_ctx_mapping, mmu_ctx_entry); + if (m->buffer->id == buff_id) { + mapping = m; + break; + } + } + + if (!mapping) { + dev_err(mmu_ctx->device, "%s: buffer id %d not found\n", + __func__, buff_id); + mutex_unlock(mem_man->mutex); + return -EINVAL; + } + + _img_mmu_unmap(mapping); + + mutex_unlock(mem_man->mutex); + kfree(mapping); + return 0; +} + +int img_mmu_get_ptd(const struct mmu_ctx *ctx, unsigned int *ptd) +{ + struct mem_man *mem_man = &mem_man_data; + struct mmu_page_cfg *page_cfg; + unsigned long long addr; + + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + page_cfg = mmu_directory_get_page(ctx->mmu_dir); + if (!page_cfg) { + mutex_unlock(mem_man->mutex); + return -EINVAL; + } + + addr = page_cfg->phys_addr; + if (ctx->heap->to_dev_addr) + addr = ctx->heap->to_dev_addr(&ctx->heap->options, addr); + + mutex_unlock(mem_man->mutex); + + *ptd = (unsigned int)(addr >>= VXD_MMU_SHIFT); + + dev_dbg(ctx->device, "%s: addr %#llx ptd %#x\n", __func__, + page_cfg->phys_addr, *ptd); + return 0; +} + +int img_mmu_get_pagetable_entry(const struct mmu_ctx *ctx, unsigned long dev_virt_addr) +{ + if (!ctx) + return 0xFFFFFF; + + return mmu_directory_get_pagetable_entry(ctx->mmu_dir, dev_virt_addr); +} + +/* + * Initialisation + */ +int img_mem_init(void *dev) +{ + struct mem_man *mem_man = &mem_man_data; + + mem_man->dev = dev; + mem_man->heaps = kzalloc(sizeof(*mem_man->heaps), GFP_KERNEL); + if (!mem_man->heaps) + return -ENOMEM; + idr_init(mem_man->heaps); + INIT_LIST_HEAD(&mem_man->mem_ctxs); + mem_man->mutex = kzalloc(sizeof(*mem_man->mutex), GFP_KERNEL); + if (!mem_man->mutex) { + pr_err("Memory allocation failed for mutex\n"); + return -ENOMEM; + } + mutex_init(mem_man->mutex); + + return 0; +} + +void img_mem_exit(void) +{ + struct mem_man *mem_man = &mem_man_data; + struct heap *heap; + int heap_id; + + /* keeps mutex checks (WARN_ON) happy, this will never actually wait */ + mutex_lock_nested(mem_man->mutex, SUBCLASS_IMGMEM); + + while (!list_empty(&mem_man->mem_ctxs)) { + struct mem_ctx *mc; + + mc = list_first_entry(&mem_man->mem_ctxs, + struct mem_ctx, mem_man_entry); + dev_warn(mem_man->dev, "%s derelict memory context %p!\n", + __func__, mc); + _img_mem_destroy_ctx(mc); + kfree(mc); + } + + heap_id = MIN_HEAP; + heap = idr_get_next(mem_man->heaps, &heap_id); + while (heap) { + dev_warn(mem_man->dev, "%s derelict heap %d!\n", __func__, + heap_id); + _img_mem_del_heap(heap); + kfree(heap); + heap_id = MIN_HEAP; + heap = idr_get_next(mem_man->heaps, &heap_id); + } + idr_destroy(mem_man->heaps); + kfree(mem_man->heaps); + + mutex_unlock(mem_man->mutex); + + mutex_destroy(mem_man->mutex); + kfree(mem_man->mutex); + mem_man->mutex = NULL; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/img_mem_man.h b/drivers/media/platform/vxe-vxd/common/img_mem_man.h --- a/drivers/media/platform/vxe-vxd/common/img_mem_man.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/img_mem_man.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG DEC Memory Manager header file + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_DEC_MEM_MGR_H +#define _IMG_DEC_MEM_MGR_H + +#include + +/* buffer ids (per memory context) */ +#define MEM_MAN_MIN_BUFFER 1 +#define MEM_MAN_MAX_BUFFER 16384 + +enum mem_attr { + MEM_ATTR_CACHED = 0x00000001, + MEM_ATTR_UNCACHED = 0x00000002, + MEM_ATTR_WRITECOMBINE = 0x00000004, + MEM_ATTR_SECURE = 0x00000010, + MEM_ATTR_FORCE32BITS = 0x7FFFFFFFU +}; + +enum mmu_callback_type { + MMU_CALLBACK_MAP = 1, + MMU_CALLBACK_UNMAP, + MMU_CALLBACK_FORCE32BITS = 0x7FFFFFFFU +}; + +enum heap_type { + MEM_HEAP_TYPE_UNIFIED = 1, + MEM_HEAP_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +union heap_options { + struct { + long long gfp_type; /* pool and flags for buffer allocations */ + } unified; +}; + +/** + * struct heap_config - contains heap configuration structure + * @type: enumeration of heap_type + * @options: pool and flags for buffer allocations, eg GFP_KERNEL + * @to_dev_addr: function pointer for retrieving device addr + */ +struct heap_config { + enum heap_type type; + union heap_options options; + unsigned long long (*to_dev_addr)(union heap_options *opts, unsigned long long addr); +}; + +/* + * struct mmu_heap - typedef for mmu_heap + * @virt_addr_start: start of the device virtual address + * @alloc_atom: atom allocation in bytes + * @size: total size of the heap in bytes + */ +struct mmu_heap { + unsigned long virt_addr_start; + unsigned long alloc_atom; + unsigned long size; +}; + +/* + * struct mem_ctx - the memory context + * @buffers: idr list of buffers + * @mmu_ctxs: contains linked lists of struct mmu_ctx + * @mem_man_entry: the entry list for dev_mem_main:mem_ctxs linked list + */ +struct mem_ctx { + struct idr *buffers; + struct list_head mmu_ctxs; + struct list_head mem_man_entry; +}; + +/* + * struct mmu_ctx_mapping - the mmu context mapping information + * @mmu_ctx: pointer to the mmu_ctx to which this mmu mapping information + * belongs + * @buffer: pointer to the buffer which this mmu_ctx_mapping is for + * @map: pointer to the mmu_map which this mmu_ctx_mapping belongs + * @virt_addr: Virtual address + * @mmu_ctx_entry: the entry list for mmu_ctx:mapping linked list. + * @buffer_entry: the entry list for buffer:mappings linked list. + */ +struct mmu_ctx_mapping { + struct mmu_ctx *mmu_ctx; + struct buffer *buffer; + struct mmu_map *map; + unsigned int virt_addr; + struct list_head mmu_ctx_entry; + struct list_head buffer_entry; +}; + +/* + * struct mmu_ctx - the mmu context information - one per stream + * @device: pointer to the device + * @mmu_config_addr_width: the address width for the mmu config + * @mem_ctx: pointer to mem_ctx where this mmu_ctx belongs to + * @heap: pointer to struct heap to where this mem_ctx belongs to + * @mmu_dir: pointer to the mmu_directory this mmu_ctx belongs to + * @mappings: contains linked list of struct mmu_ctx_mapping + * @mem_ctx_entry: the entry list for mem_ctx:mmu_ctxs + * @callback_fn: pointer to function callback + * @callback_data: pointer to the callback data + */ +struct mmu_ctx { + void *device; + unsigned int mmu_config_addr_width; + struct mem_ctx *mem_ctx; + struct heap *heap; + struct mmu_directory *mmu_dir; + struct list_head mappings; + struct list_head mem_ctx_entry; + void (*callback_fn)(enum mmu_callback_type type, int buff_id, + void *data); + void *callback_data; +}; + +/* + * struct buffer - the mmu context information - one per stream + * @id: buffer identification + * @request_size: request size for the allocation + * @actual_size: size aligned with the PAGE_SIZE allocation + * @device: pointer to the device + * @mem_ctx: pointer to struct mem_ctx to where this buffer belongs to + * @heap: pointer to struct heap to where this buffer belongs to + * @mappings: contains linked lists of struct mmu_ctx_mapping + * @kptr: pointer to virtual mapping for the buffer object into kernel address + * space + * @priv: pointer to priv data used for scaterlist table info + */ +struct buffer { + int id; /* Generated in */ + unsigned long request_size; + unsigned long actual_size; + void *device; + struct mem_ctx *mem_ctx; + struct heap *heap; + struct list_head mappings; /* contains */ + void *kptr; + void *priv; +}; + +struct heap_ops { + int (*alloc)(void *device, struct heap *heap, + unsigned long size, enum mem_attr attr, + struct buffer *buffer); + void (*free)(struct heap *heap, struct buffer *buffer); + int (*map_km)(struct heap *heap, struct buffer *buffer); + int (*get_sg_table)(struct heap *heap, struct buffer *buffer, + void **sg_table); + void (*sync_cpu_to_dev)(struct heap *heap, struct buffer *buffer); + void (*sync_dev_to_cpu)(struct heap *heap, struct buffer *buffer); + void (*destroy)(struct heap *heap); +}; + +struct heap { + int id; /* Generated in */ + enum heap_type type; + struct heap_ops *ops; + union heap_options options; + unsigned long long (*to_dev_addr)(union heap_options *opts, unsigned long long addr); + void *priv; +}; + +int img_mem_init(void *dev); +void img_mem_exit(void); + +int img_mem_create_ctx(struct mem_ctx **new_ctx); +void img_mem_destroy_ctx(struct mem_ctx *ctx); + +int img_mem_import(void *device, struct mem_ctx *ctx, + unsigned long size, enum mem_attr attr, int *buf_id); + +int img_mem_alloc(void *device, struct mem_ctx *ctx, int heap_id, + unsigned long size, enum mem_attr attributes, int *buf_id); +void img_mem_free(struct mem_ctx *ctx, int buff_id); + +void img_mem_free_bufid(struct mem_ctx *ctx, int buf_id); + +int img_mem_map_km(struct mem_ctx *ctx, int buf_id); +void *img_mem_get_kptr(struct mem_ctx *ctx, int buff_id); + +int img_mem_sync_cpu_to_device(struct mem_ctx *ctx, int buf_id); +int img_mem_sync_device_to_cpu(struct mem_ctx *ctx, int buf_id); + +int img_mmu_ctx_create(void *device, unsigned int mmu_config_addr_width, + struct mem_ctx *mem_ctx, int heap_id, + void (*callback_fn)(enum mmu_callback_type type, + int buff_id, void *data), + void *callback_data, struct mmu_ctx **mmu_ctx); +void img_mmu_ctx_destroy(struct mmu_ctx *ctx); + +int img_mmu_map(struct mmu_ctx *mmu_ctx, struct mem_ctx *mem_ctx, + int buff_id, unsigned int virt_addr, unsigned int map_flags); +int img_mmu_map_sg(struct mmu_ctx *mmu_ctx, struct mem_ctx *mem_ctx, + int buff_id, void *sgt, unsigned int virt_addr, + unsigned int map_flags); +int img_mmu_unmap(struct mmu_ctx *mmu_ctx, struct mem_ctx *mem_ctx, + int buff_id); + +int img_mmu_get_ptd(const struct mmu_ctx *ctx, unsigned int *ptd); + +int img_mmu_get_pagetable_entry(const struct mmu_ctx *ctx, unsigned long dev_virt_addr); + +int img_mem_add_heap(const struct heap_config *heap_cfg, int *heap_id); +void img_mem_del_heap(int heap_id); + +/* Heap operation related function */ +int img_mem_unified_init(const struct heap_config *config, + struct heap *heap); + +/* page and sg list related functions */ +void img_mmu_get_pages(void **page_args, void *sgt_args); +unsigned int img_mmu_get_orig_nents(void *sgt_args); +void img_mmu_set_sgt_nents(void *sgt_args, int ret); +void img_mmu_set_sg_table(void **sg_table_args, void *buffer); +unsigned int img_mmu_get_sgl_length(void *sgl_args); +void *img_mmu_get_sgl(void *sgt_args); + +#endif /* _IMG_DEC_MEM_MGR */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/img_mem_unified.c b/drivers/media/platform/vxe-vxd/common/img_mem_unified.c --- a/drivers/media/platform/vxe-vxd/common/img_mem_unified.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/img_mem_unified.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC Memory Manager for unified memory + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "img_mem_man.h" + +void img_mmu_get_pages(void **page_args, void *sgt_args) +{ + struct page **pages = (struct page **)page_args; + struct sg_table *sgt = sgt_args; + struct scatterlist *sgl = sgt->sgl; + int i; + + i = 0; + while (sgl) { + pages[i++] = sg_page(sgl); + sgl = sg_next(sgl); + } +} + +unsigned int img_mmu_get_orig_nents(void *sgt_args) +{ + struct sg_table *sgt = sgt_args; + + return sgt->orig_nents; +} + +void img_mmu_set_sgt_nents(void *sgt_args, int ret) +{ + struct sg_table *sgt = sgt_args; + + sgt->nents = ret; +} + +void img_mmu_set_sg_table(void **sg_table_args, void *buffer) +{ + struct sg_table **sg_table = (struct sg_table **)sg_table_args; + + *sg_table = buffer; +} + +unsigned int img_mmu_get_sgl_length(void *sgl_args) +{ + struct scatterlist *sgl = (struct scatterlist *)sgl_args; + + return sgl->length; +} + +void *img_mmu_get_sgl(void *sgt_args) +{ + struct sg_table *sgt = sgt_args; + + return sgt->sgl; +} + +static int unified_alloc(void *device, struct heap *heap, + unsigned long size, enum mem_attr attr, + struct buffer *buffer) +{ + struct sg_table *sgt; + void *sgl; + int pages; + int ret; + + dev_dbg(device, "%s:%d buffer %d (0x%p)\n", __func__, __LINE__, + buffer->id, buffer); + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + + pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + + ret = sg_alloc_table(sgt, pages, GFP_KERNEL); + if (ret) + goto sg_alloc_table_failed; + + sgl = img_mmu_get_sgl(sgt); + while (sgl) { + void *page; + unsigned long long dma_addr; + + page = alloc_page(heap->options.unified.gfp_type); + if (!page) { + dev_err(device, "%s alloc_page failed!\n", __func__); + ret = -ENOMEM; + goto alloc_page_failed; + } + + /* + * dma_map_page() is probably going to fail if alloc flags are + * GFP_HIGHMEM, since it is not mapped to CPU. Hopefully, this + * will never happen because memory of this sort cannot be used + * for DMA anyway. To check if this is the case, build with + * debug, set trace_physical_pages=1 and check if page_address + * printed above is NULL + */ + dma_addr = dma_map_page(device, page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(device, dma_addr)) { + __free_page(page); + dev_err(device, "%s dma_map_page failed!\n", __func__); + ret = -EIO; + goto alloc_page_failed; + } + dma_unmap_page(device, dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + + sg_set_page(sgl, page, PAGE_SIZE, 0); + + sgl = sg_next(sgl); + } + + buffer->priv = sgt; + return 0; + +alloc_page_failed: + sgl = img_mmu_get_sgl(sgt); + while (sgl) { + void *page = sg_page(sgl); + + if (page) + __free_page(page); + + sgl = sg_next(sgl); + } + sg_free_table(sgt); +sg_alloc_table_failed: + kfree(sgt); + return ret; +} + +static void unified_free(struct heap *heap, struct buffer *buffer) +{ + void *dev = buffer->device; + void *sgt = buffer->priv; + void *sgl; + + dev_dbg(dev, "%s:%d buffer %d (0x%p)\n", __func__, __LINE__, + buffer->id, buffer); + + if (buffer->kptr) { + dev_dbg(dev, "%s vunmap 0x%p\n", __func__, buffer->kptr); + dma_unmap_sg(dev, img_mmu_get_sgl(sgt), img_mmu_get_orig_nents(sgt), + DMA_FROM_DEVICE); + vunmap(buffer->kptr); + } + + sgl = img_mmu_get_sgl(sgt); + while (sgl) { + __free_page(sg_page(sgl)); + sgl = sg_next(sgl); + } + sg_free_table(sgt); + kfree(sgt); +} + +static int unified_map_km(struct heap *heap, struct buffer *buffer) +{ + void *dev = buffer->device; + void *sgt = buffer->priv; + void *sgl = img_mmu_get_sgl(sgt); + unsigned int num_pages = sg_nents(sgl); + unsigned int orig_nents = img_mmu_get_orig_nents(sgt); + void **pages; + int ret; + pgprot_t prot; + + dev_dbg(dev, "%s:%d buffer %d (0x%p)\n", __func__, __LINE__, buffer->id, buffer); + + if (buffer->kptr) { + dev_warn(dev, "%s called for already mapped buffer %d\n", __func__, buffer->id); + return 0; + } + + pages = kmalloc_array(num_pages, sizeof(void *), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + img_mmu_get_pages(pages, sgt); + + prot = PAGE_KERNEL; + prot = pgprot_writecombine(prot); + buffer->kptr = vmap((struct page **)pages, num_pages, VM_MAP, prot); + kfree(pages); + if (!buffer->kptr) { + dev_err(dev, "%s vmap failed!\n", __func__); + return -EFAULT; + } + + ret = dma_map_sg(dev, sgl, orig_nents, DMA_FROM_DEVICE); + + if (ret <= 0) { + dev_err(dev, "%s dma_map_sg failed!\n", __func__); + vunmap(buffer->kptr); + return -EFAULT; + } + dev_dbg(dev, "%s:%d buffer %d orig_nents %d nents %d\n", __func__, + __LINE__, buffer->id, orig_nents, ret); + + img_mmu_set_sgt_nents(sgt, ret); + + dev_dbg(dev, "%s:%d buffer %d vmap to 0x%p\n", __func__, __LINE__, + buffer->id, buffer->kptr); + + return 0; +} + +static int unified_get_sg_table(struct heap *heap, struct buffer *buffer, void **sg_table) +{ + img_mmu_set_sg_table(sg_table, buffer->priv); + return 0; +} + +static void unified_sync_cpu_to_dev(struct heap *heap, struct buffer *buffer) +{ + void *dev = buffer->device; + void *sgt = buffer->priv; + + if (!buffer->kptr) + return; + + dev_dbg(dev, "%s:%d buffer %d (0x%p)\n", __func__, __LINE__, buffer->id, buffer); + + dma_sync_sg_for_device(dev, img_mmu_get_sgl(sgt), img_mmu_get_orig_nents(sgt), + DMA_TO_DEVICE); +} + +static void unified_sync_dev_to_cpu(struct heap *heap, struct buffer *buffer) +{ + void *dev = buffer->device; + void *sgt = buffer->priv; + + if (!buffer->kptr) + return; + + dev_dbg(dev, "%s:%d buffer %d (0x%p)\n", __func__, __LINE__, + buffer->id, buffer); + + dma_sync_sg_for_cpu(dev, img_mmu_get_sgl(sgt), img_mmu_get_orig_nents(sgt), + DMA_FROM_DEVICE); +} + +static void unified_heap_destroy(struct heap *heap) +{ +} + +static struct heap_ops unified_heap_ops = { + .alloc = unified_alloc, + .free = unified_free, + .map_km = unified_map_km, + .get_sg_table = unified_get_sg_table, + .sync_cpu_to_dev = unified_sync_cpu_to_dev, + .sync_dev_to_cpu = unified_sync_dev_to_cpu, + .destroy = unified_heap_destroy, +}; + +int img_mem_unified_init(const struct heap_config *heap_cfg, + struct heap *heap) +{ + heap->ops = &unified_heap_ops; + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/imgmmu.c b/drivers/media/platform/vxe-vxd/common/imgmmu.c --- a/drivers/media/platform/vxe-vxd/common/imgmmu.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/imgmmu.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC MMU function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include "img_mem_man.h" +#include "imgmmu.h" + +/** + * struct mmu_directory - the MMU directory information + * @dir_page: pointer to the mmu_page_cfg_table (physical table used) which + * this mmu_directory belongs to + * @dir_page_table: All the page table structures in a static array of pointers + * @mmu_info_cfg: Functions to use to manage pages allocation, liberation and + * writing + * @num_mapping: number of mapping using this directory + */ +struct mmu_directory { + struct mmu_page_cfg *dir_page; + struct mmu_page_cfg_table **dir_page_table; + struct mmu_info mmu_info_cfg; + unsigned int num_mapping; +}; + +/* + * struct mmu_map - the MMU mapping information + * @mmu_dir: pointer to the mmu_directory which this mmu_map belongs to + * @dev_virt_addr: device virtual address root associated with this mapping + * @used_flag: flag used when allocating + * @n_entries: number of entries mapped + */ +struct mmu_map { + struct mmu_directory *mmu_dir; + struct mmu_heap_alloc dev_virt_addr; + unsigned int used_flag; + unsigned int n_entries; +}; + +/* + * struct mmu_page_cfg_table - the MMU page table information. + * One page table of the directory. + * @mmu_dir: pointer to the mmu_directory which this mmu_page_cfg_table + * belongs to + * @page: page used to store this mapping in the MMU + * @valid_entries: number of valid entries in this page + */ +struct mmu_page_cfg_table { + struct mmu_directory *mmu_dir; + struct mmu_page_cfg *page; + unsigned int valid_entries; +}; + +/* + * mmu_pgt_destroy() - Destruction of a page table (does not follow the + * child pointer) + * @pgt: pointer to the MMU page table information + * + * Warning: Does not verify if pages are still valid or not + */ +static void mmu_pgt_destroy(struct mmu_page_cfg_table *pgt) +{ + if (!pgt->mmu_dir || + !pgt->mmu_dir->mmu_info_cfg.pfn_page_free || + !pgt->page) { + return; + } + + pr_debug("%s:%d Destroy page table (phys addr %llu)\n", + __func__, __LINE__, pgt->page->phys_addr); + + pgt->mmu_dir->mmu_info_cfg.pfn_page_free(pgt->page); + pgt->page = NULL; + + kfree(pgt); +} + +/* + * mmu_dir_entry() - Extact the directory index from a virtual address + * @vaddr: virtual address + */ +static inline unsigned int mmu_dir_entry(unsigned long vaddr) +{ + return (unsigned int)((vaddr & VIRT_DIR_IDX_MASK) >> MMU_DIR_SHIFT); +} + +/* + * mmu_pg_entry() - Extract the page table index from a virtual address + * @vaddr: virtual address + */ +static inline unsigned int mmu_pg_entry(unsigned long vaddr) +{ + return (unsigned int)((vaddr & VIRT_PAGE_TBL_MASK) >> MMU_PAGE_SHIFT); +} + +/* + * mmu_pg_wr() - Default function used when a mmu_info structure has an empty + * pfn_page_write pointer + * @mmu_page: pointer to the mmu_page to update + * @offset: offset into the directory + * @pa_to_write: physical address value to add to the entr + * @mmu_flag: mmu flag(s) to set + */ +static void mmu_pg_wr(struct mmu_page_cfg *mmu_page, unsigned int offset, + unsigned long long pa_to_write, unsigned int mmu_flag) +{ + unsigned int *dir_mem = NULL; + unsigned long long cur_pa = pa_to_write; + + if (!mmu_page) + return; + + dir_mem = (unsigned int *)mmu_page->cpu_virt_addr; + /* + * assumes that the MMU HW has the extra-bits enabled (this default + * function has no way of knowing) + */ + if ((MMU_PHYS_SIZE - MMU_VIRT_SIZE) > 0) + cur_pa >>= (MMU_PHYS_SIZE - MMU_VIRT_SIZE); + /* + * The MMU_PAGE_SHIFT bottom bits should be masked because page + * allocation. + * MMU_PAGE_SHIFT-(MMU_PHYS_SIZE-MMU_VIRT_SIZE) are used for + * flags so it's ok + */ + dir_mem[offset] = (unsigned int)cur_pa | (mmu_flag); +} + +/* + * mmu_page_cfg_table() - Create a page table + * @mmu_dir: pointer to the mmu_directory in which to create the new page table + * structure + * + * Return: A pointer to the new page table structure in case of success. + * (void *) in case of error + */ +static struct mmu_page_cfg_table *mmu_pgt_create(struct mmu_directory *mmu_dir) +{ + struct mmu_page_cfg_table *neo = NULL; + unsigned int i; + + if (!mmu_dir || !mmu_dir->mmu_info_cfg.pfn_page_alloc || + !mmu_dir->mmu_info_cfg.pfn_page_write) + return (void *)(-EINVAL); + + neo = kmalloc(sizeof(*neo), GFP_KERNEL); + if (!neo) + return (void *)(-ENOMEM); + + neo->mmu_dir = mmu_dir; + + neo->page = + mmu_dir->mmu_info_cfg.pfn_page_alloc(mmu_dir->mmu_info_cfg.alloc_ctx); + if (!neo->page) { + pr_err("%s:%d failed to allocate Page Table physical page\n", + __func__, __LINE__); + kfree(neo); + return (void *)(-ENOMEM); + } + pr_debug("%s:%d Create page table (phys addr 0x%llx CPU Virt 0x%lx)\n", + __func__, __LINE__, neo->page->phys_addr, + neo->page->cpu_virt_addr); + + /* invalidate all pages */ + for (i = 0; i < MMU_N_PAGE; i++) { + mmu_dir->mmu_info_cfg.pfn_page_write(neo->page, i, 0, + MMU_FLAG_INVALID); + } + + /* + * When non-UMA need to update the device memory after setting + * it to 0 + */ + if (mmu_dir->mmu_info_cfg.pfn_page_update) + mmu_dir->mmu_info_cfg.pfn_page_update(neo->page); + + return neo; +} + +/* + * mmu_create_directory - Create a directory entry based on a given directory + * configuration + * @mmu_info_ops: contains the functions to use to manage page table memory. + * Is copied and not modified. + * + * @warning Obviously creation of the directory allocates memory - do not call + * while interrupts are disabled + * + * @return The opaque handle to the mmu_directory object and result to 0 + * @return (void *) in case of an error and result has the value: + * @li -EINVAL if mmu_info configuration is NULL or does not + * contain function pointers + * @li -ENOMEM if an internal allocation failed + * @li -ENOMEM if the given mmu_pfn_page_alloc returned NULL + */ +struct mmu_directory *mmu_create_directory(const struct mmu_info *mmu_info_ops) +{ + struct mmu_directory *neo = NULL; + unsigned int i; + + /* + * invalid information in the directory config: + * - invalid page allocator and dealloc (page write can be NULL) + * - invalid virtual address representation + * - invalid page size + * - invalid MMU size + */ + if (!mmu_info_ops || !mmu_info_ops->pfn_page_alloc || !mmu_info_ops->pfn_page_free) { + pr_err("%s:%d invalid MMU configuration\n", __func__, __LINE__); + return (void *)(-EINVAL); + } + + neo = kzalloc(sizeof(*neo), GFP_KERNEL); + if (!neo) + return (void *)(-ENOMEM); + + neo->dir_page_table = kcalloc(MMU_N_TABLE, sizeof(struct mmu_page_cfg_table *), + GFP_KERNEL); + if (!neo->dir_page_table) { + kfree(neo); + return (void *)(-ENOMEM); + } + + memcpy(&neo->mmu_info_cfg, mmu_info_ops, sizeof(struct mmu_info)); + if (!mmu_info_ops->pfn_page_write) { + pr_debug("%s:%d using default MMU write\n", __func__, __LINE__); + /* use internal function */ + neo->mmu_info_cfg.pfn_page_write = &mmu_pg_wr; + } + + neo->dir_page = mmu_info_ops->pfn_page_alloc(mmu_info_ops->alloc_ctx); + if (!neo->dir_page) { + kfree(neo->dir_page_table); + kfree(neo); + return (void *)(-ENOMEM); + } + + pr_debug("%s:%d (phys page 0x%llx; CPU virt 0x%lx)\n", __func__, + __LINE__, neo->dir_page->phys_addr, + neo->dir_page->cpu_virt_addr); + /* now we have a valid mmu_directory structure */ + + /* invalidate all entries */ + for (i = 0; i < MMU_N_TABLE; i++) { + neo->mmu_info_cfg.pfn_page_write(neo->dir_page, i, 0, + MMU_FLAG_INVALID); + } + + /* when non-UMA need to update the device memory */ + if (neo->mmu_info_cfg.pfn_page_update) + neo->mmu_info_cfg.pfn_page_update(neo->dir_page); + + return neo; +} + +/* + * mmu_destroy_directory - Destroy the mmu_directory - assumes that the HW is + * not going to access the memory any-more + * @mmu_dir: pointer to the mmu directory to destroy + * + * Does not invalidate any memory because it assumes that everything is not + * used any-more + */ +int mmu_destroy_directory(struct mmu_directory *mmu_dir) +{ + unsigned int i; + + if (!mmu_dir) { + /* could be an assert */ + pr_err("%s:%d mmu_dir is NULL\n", __func__, __LINE__); + return -EINVAL; + } + + if (mmu_dir->num_mapping > 0) + /* mappings should have been destroyed! */ + pr_err("%s:%d directory still has %u mapping attached to it\n", + __func__, __LINE__, mmu_dir->num_mapping); + /* + * not exiting because clearing the page table map is more + * important than losing a few structures + */ + + if (!mmu_dir->mmu_info_cfg.pfn_page_free || !mmu_dir->dir_page_table) + return -EINVAL; + + pr_debug("%s:%d destroy MMU dir (phys page 0x%llx)\n", + __func__, __LINE__, mmu_dir->dir_page->phys_addr); + + /* first we destroy the directory entry */ + mmu_dir->mmu_info_cfg.pfn_page_free(mmu_dir->dir_page); + mmu_dir->dir_page = NULL; + + /* destroy every mapping that still exists */ + for (i = 0; i < MMU_N_TABLE; i++) { + if (mmu_dir->dir_page_table[i]) { + mmu_pgt_destroy(mmu_dir->dir_page_table[i]); + mmu_dir->dir_page_table[i] = NULL; + } + } + + kfree(mmu_dir->dir_page_table); + kfree(mmu_dir); + return 0; +} + +/* + * mmu_directory_get_page - Get access to the page table structure used in the + * directory (to be able to write it to registers) + * @mmu_dir: pointer to the mmu directory. asserts if mmu_dir is NULL + * + * @return the page table structure used + */ +struct mmu_page_cfg *mmu_directory_get_page(struct mmu_directory *mmu_dir) +{ + if (!mmu_dir) + return NULL; + + return mmu_dir->dir_page; +} + +static struct mmu_map *mmu_directory_map(struct mmu_directory *mmu_dir, + const struct mmu_heap_alloc *dev_va, + unsigned int ui_map_flags, + int (*phys_iter_next)(void *arg, + unsigned long long *next), + void *phys_iter_arg) +{ + unsigned int first_dir = 0; + unsigned int first_pg = 0; + unsigned int dir_off = 0; + unsigned int pg_off = 0; + unsigned int n_entries = 0; + unsigned int i; + unsigned int d; + const unsigned int duplicate = PAGE_SIZE / mmu_get_page_size(); + int res = 0; + struct mmu_map *neo = NULL; + struct mmu_page_cfg_table **dir_pgtbl = NULL; + + /* + * in non UMA updates on pages needs to be done - store index of + * directory entry pages to update + */ + unsigned int *to_update; + /* + * number of pages in to_update (will be at least 1 for the first_pg to + * update) + */ + unsigned int n_pgs_to_update = 0; + /* + * to know if we also need to update the directory page (creation of new + * page) + */ + unsigned char dir_modified = FALSE; + + if (!mmu_dir || !dev_va || duplicate < 1) + return (void *)(-EINVAL); + + dir_pgtbl = mmu_dir->dir_page_table; + + n_entries = dev_va->alloc_size / PAGE_SIZE; + if (dev_va->alloc_size % MMU_PAGE_SIZE != 0 || n_entries == 0) { + pr_err("%s:%d invalid allocation size\n", __func__, __LINE__); + return (void *)(-EINVAL); + } + + if ((ui_map_flags & MMU_FLAG_VALID) != 0) { + pr_err("%s:%d valid flag (0x%x) is set in the falgs 0x%x\n", + __func__, __LINE__, MMU_FLAG_VALID, ui_map_flags); + return (void *)(-EINVAL); + } + + /* + * has to be dynamically allocated because it is bigger than 1k (max + * stack in the kernel) + * MMU_N_TABLE is 1024 for 4096B pages, that's a 4k allocation (1 page) + * - if it gets bigger may IMG_BIGALLOC should be used + */ + to_update = kcalloc(MMU_N_TABLE, sizeof(unsigned int), GFP_KERNEL); + if (!to_update) + return (void *)(-ENOMEM); + + /* manage multiple page table mapping */ + + first_dir = mmu_dir_entry(dev_va->virt_addr); + first_pg = mmu_pg_entry(dev_va->virt_addr); + + if (first_dir >= MMU_N_TABLE || first_pg >= MMU_N_PAGE) { + kfree(to_update); + return (void *)(-EINVAL); + } + + /* verify that the pages that should be used are available */ + dir_off = first_dir; + pg_off = first_pg; + + /* + * loop over the number of entries given by CPU allocator but CPU page + * size can be > than MMU page size therefore it may need to "duplicate" + * entries by creating a fake physical address + */ + for (i = 0; i < n_entries * duplicate; i++) { + if (pg_off >= MMU_N_PAGE) { + dir_off++; /* move to next directory */ + if (dir_off >= MMU_N_TABLE) { + res = -EINVAL; + break; + } + pg_off = 0; /* using its first page */ + } + + /* + * if dir_pgtbl[dir_off] == NULL not yet + * allocated it means all entries are available + */ + if (dir_pgtbl[dir_off]) { + /* + * inside a pagetable - verify that the required offset + * is invalid + */ + struct mmu_page_cfg_table *tbl = dir_pgtbl[dir_off]; + unsigned int *page_mem = (unsigned int *)tbl->page->cpu_virt_addr; + + if ((page_mem[pg_off] & MMU_FLAG_VALID) != 0) { + pr_err("%s:%d one of the required page is currently in use\n", + __func__, __LINE__); + res = -EPERM; + break; + } + } + /* PageTable struct exists */ + pg_off++; + } /* for all needed entries */ + + /* it means one entry was not invalid or not enough page were given */ + if (res != 0) { + /* + * message already printed + * IMG_ERROR_MEMORY_IN_USE when an entry is not invalid + * IMG_ERROR_INVALID_PARAMETERS when not enough pages are given + * (or too much) + */ + kfree(to_update); + return (void *)(unsigned long)(res); + } + + neo = kmalloc(sizeof(*neo), GFP_KERNEL); + if (!neo) { + kfree(to_update); + return (void *)(-ENOMEM); + } + neo->mmu_dir = mmu_dir; + neo->dev_virt_addr = *dev_va; + memcpy(&neo->dev_virt_addr, dev_va, sizeof(struct mmu_heap_alloc)); + neo->used_flag = ui_map_flags; + + /* we now know that all pages are available */ + dir_off = first_dir; + pg_off = first_pg; + + to_update[n_pgs_to_update] = first_dir; + n_pgs_to_update++; + + for (i = 0; i < n_entries; i++) { + unsigned long long cur_phys_addr; + + if (phys_iter_next(phys_iter_arg, &cur_phys_addr) != 0) { + pr_err("%s:%d not enough entries in physical address array\n", + __func__, __LINE__); + kfree(neo); + kfree(to_update); + return (void *)(-EBUSY); + } + for (d = 0; d < duplicate; d++) { + if (pg_off >= MMU_N_PAGE) { + /* move to next directory */ + dir_off++; + /* using its first page */ + pg_off = 0; + + to_update[n_pgs_to_update] = dir_off; + n_pgs_to_update++; + } + + /* this page table object does not exists, create it */ + if (!dir_pgtbl[dir_off]) { + dir_pgtbl[dir_off] = mmu_pgt_create(mmu_dir); + if (IS_ERR_VALUE((unsigned long)dir_pgtbl[dir_off])) { + dir_pgtbl[dir_off] = NULL; + goto cleanup_fail; + } + /* + * make this page table valid + * should be dir_off + */ + mmu_dir->mmu_info_cfg.pfn_page_write(mmu_dir->dir_page, + dir_off, + dir_pgtbl[dir_off]->page->phys_addr, + MMU_FLAG_VALID); + dir_modified = TRUE; + } + + /* + * map this particular page in the page table + * use d*(MMU page size) to add additional entries from + * the given physical address with the correct offset + * for the MMU + */ + mmu_dir->mmu_info_cfg.pfn_page_write(dir_pgtbl[dir_off]->page, + pg_off, + cur_phys_addr + d * + mmu_get_page_size(), + neo->used_flag | + MMU_FLAG_VALID); + dir_pgtbl[dir_off]->valid_entries++; + + pg_off++; + } /* for duplicate */ + } /* for entries */ + + neo->n_entries = n_entries * duplicate; + /* one more mapping is related to this directory */ + mmu_dir->num_mapping++; + + /* if non UMA we need to update device memory */ + if (mmu_dir->mmu_info_cfg.pfn_page_update) { + while (n_pgs_to_update > 0) { + unsigned int idx = to_update[n_pgs_to_update - 1]; + struct mmu_page_cfg_table *tbl = dir_pgtbl[idx]; + + mmu_dir->mmu_info_cfg.pfn_page_update(tbl->page); + n_pgs_to_update--; + } + if (dir_modified) + mmu_dir->mmu_info_cfg.pfn_page_update(mmu_dir->dir_page); + } + + kfree(to_update); + return neo; + +cleanup_fail: + pr_err("%s:%d failed to create a non-existing page table\n", __func__, __LINE__); + + /* + * invalidate all already mapped pages - + * do not destroy the created pages + */ + while (i > 1) { + if (d == 0) { + i--; + d = duplicate; + } + d--; + + if (pg_off == 0) { + pg_off = MMU_N_PAGE; + if (!dir_off) + continue; + dir_off--; + } + + pg_off--; + + /* it should have been used before */ + if (!dir_pgtbl[dir_off]) + continue; + + mmu_dir->mmu_info_cfg.pfn_page_write(dir_pgtbl[dir_off]->page, + pg_off, 0, + MMU_FLAG_INVALID); + dir_pgtbl[dir_off]->valid_entries--; + } + + kfree(neo); + kfree(to_update); + return (void *)(-ENOMEM); +} + +/* + * with sg + */ +struct sg_phys_iter { + void *sgl; + unsigned int offset; +}; + +static int sg_phys_iter_next(void *arg, unsigned long long *next) +{ + struct sg_phys_iter *iter = arg; + + if (!iter->sgl) + return -ENOENT; + + *next = sg_phys(iter->sgl) + iter->offset; /* phys_addr to dma_addr? */ + iter->offset += PAGE_SIZE; + + if (iter->offset == img_mmu_get_sgl_length(iter->sgl)) { + iter->sgl = sg_next(iter->sgl); + iter->offset = 0; + } + + return 0; +} + +/* + * mmu_directory_map_sg - Create a page table mapping for a list of physical + * pages and device virtual address + * + * @mmu_dir: directory to use for the mapping + * @phys_page_sg: sorted array of physical addresses (ascending order). The + * number of elements is dev_va->alloc_size/MMU_PAGE_SIZE + * @note This array can potentially be big, the caller may need to use vmalloc + * if running the linux kernel (e.g. mapping a 1080p NV12 is 760 entries, 6080 + * Bytes - 2 CPU pages needed, fine with kmalloc; 4k NV12 is 3038 entries, + * 24304 Bytes - 6 CPU pages needed, kmalloc would try to find 8 contiguous + * pages which may be problematic if memory is fragmented) + * @dev_va: associated device virtual address. Given structure is copied + * @map_flag: flags to apply on the page (typically 0x2 for Write Only, + * 0x4 for Read Only) - the flag should not set bit 1 as 0x1 is the + * valid flag. + * + * @warning Mapping can cause memory allocation (missing pages) - do not call + * while interrupts are disabled + * + * @return The opaque handle to the mmu_map object and result to 0 + * @return (void *) in case of an error with the following values: + * @li -EINVAL if the allocation size is not a multiple of MMU_PAGE_SIZE, + * if the given list of page table is too long or not long enough for the + * mapping or if the give flags set the invalid bit + * @li -EPERM if the virtual memory is already mapped + * @li -ENOMEM if an internal allocation failed + * @li -ENOMEM if a page creation failed + */ +struct mmu_map *mmu_directory_map_sg(struct mmu_directory *mmu_dir, + void *phys_page_sg, + const struct mmu_heap_alloc *dev_va, + unsigned int map_flag) +{ + struct sg_phys_iter arg = { phys_page_sg }; + + return mmu_directory_map(mmu_dir, dev_va, map_flag, + sg_phys_iter_next, &arg); +} + +/* + * mmu_directory_unmap - Un-map the mapped pages (invalidate their entries) and + * destroy the mapping object + * @map: pointer to the pages to un-map + * + * This does not destroy the created Page Table (even if they are becoming + * un-used) and does not change the Directory valid bits. + * + * @return 0 + */ +int mmu_directory_unmap(struct mmu_map *map) +{ + unsigned int first_dir = 0; + unsigned int first_pg = 0; + unsigned int dir_offset = 0; + unsigned int pg_offset = 0; + unsigned int i; + struct mmu_directory *mmu_dir = NULL; + + /* + * in non UMA updates on pages needs to be done - store index of + * directory entry pages to update + */ + unsigned int *to_update; + unsigned int n_pgs_to_update = 0; + + if (!map || map->n_entries <= 0 || !map->mmu_dir) + return -EINVAL; + + mmu_dir = map->mmu_dir; + + /* + * has to be dynamically allocated because it is bigger than 1k (max + * stack in the kernel) + */ + to_update = kcalloc(MMU_N_TABLE, sizeof(unsigned int), GFP_KERNEL); + if (!to_update) + return -ENOMEM; + + first_dir = mmu_dir_entry(map->dev_virt_addr.virt_addr); + first_pg = mmu_pg_entry(map->dev_virt_addr.virt_addr); + + /* verify that the pages that should be used are available */ + dir_offset = first_dir; + pg_offset = first_pg; + + to_update[n_pgs_to_update] = first_dir; + n_pgs_to_update++; + + for (i = 0; i < map->n_entries; i++) { + if (pg_offset >= MMU_N_PAGE) { + /* move to next directory */ + dir_offset++; + /* using its first page */ + pg_offset = 0; + + to_update[n_pgs_to_update] = dir_offset; + n_pgs_to_update++; + } + + /* + * this page table object does not exist, something destroyed + * it while the mapping was supposed to use it + */ + if (mmu_dir->dir_page_table[dir_offset]) { + mmu_dir->mmu_info_cfg.pfn_page_write + (mmu_dir->dir_page_table[dir_offset]->page, + pg_offset, 0, + MMU_FLAG_INVALID); + mmu_dir->dir_page_table[dir_offset]->valid_entries--; + } + + pg_offset++; + } + + mmu_dir->num_mapping--; + + if (mmu_dir->mmu_info_cfg.pfn_page_update) + while (n_pgs_to_update > 0) { + unsigned int idx = to_update[n_pgs_to_update - 1]; + struct mmu_page_cfg_table *tbl = mmu_dir->dir_page_table[idx]; + + mmu_dir->mmu_info_cfg.pfn_page_update(tbl->page); + n_pgs_to_update--; + } + + /* mapping does not own the given virtual address */ + kfree(map); + kfree(to_update); + return 0; +} + +unsigned int mmu_directory_get_pagetable_entry(struct mmu_directory *mmu_dir, + unsigned long dev_virt_addr) +{ + unsigned int dir_entry = 0; + unsigned int table_entry = 0; + struct mmu_page_cfg_table *tbl; + struct mmu_page_cfg_table **dir_pgtbl = NULL; + unsigned int *page_mem; + + if (!mmu_dir) { + pr_err("mmu directory table is NULL\n"); + return 0xFFFFFF; + } + + dir_pgtbl = mmu_dir->dir_page_table; + + dir_entry = mmu_dir_entry(dev_virt_addr); + table_entry = mmu_pg_entry(dev_virt_addr); + + tbl = dir_pgtbl[dir_entry]; + if (!tbl) { + pr_err("page table entry is NULL\n"); + return 0xFFFFFF; + } + + page_mem = (unsigned int *)tbl->page->cpu_virt_addr; + +#if defined(DEBUG_DECODER_DRIVER) || defined(DEBUG_ENCODER_DRIVER) + pr_info("Page table value@dir_entry:table_entry[%d : %d] = %x\n", + dir_entry, table_entry, page_mem[table_entry]); +#endif + + return page_mem[table_entry]; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/imgmmu.h b/drivers/media/platform/vxe-vxd/common/imgmmu.h --- a/drivers/media/platform/vxe-vxd/common/imgmmu.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/imgmmu.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG DEC MMU Library + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef IMG_DEC_MMU_MMU_H +#define IMG_DEC_MMU_MMU_H + +#include + +#ifndef MMU_PHYS_SIZE +/* @brief MMU physical address size in bits */ +#define MMU_PHYS_SIZE 40 +#endif + +#ifndef MMU_VIRT_SIZE +/* @brief MMU virtual address size in bits */ +#define MMU_VIRT_SIZE 32 +#endif + +#ifndef MMU_PAGE_SIZE +/* @brief Page size in bytes */ +#define MMU_PAGE_SIZE 4096u +#define MMU_PAGE_SHIFT 12 +#define MMU_DIR_SHIFT 22 +#endif + +#if MMU_VIRT_SIZE == 32 +/* @brief max number of pagetable that can be stored in the directory entry */ +#define MMU_N_TABLE (MMU_PAGE_SIZE / 4u) +/* @brief max number of page mapping in the pagetable */ +#define MMU_N_PAGE (MMU_PAGE_SIZE / 4u) +#endif + +/* @brief Memory flag used to mark a page mapping as invalid */ +#define MMU_FLAG_VALID 0x1 +#define MMU_FLAG_INVALID 0x0 + +/* + * This type defines MMU variant. + */ +enum mmu_etype { + MMU_TYPE_NONE = 0, + MMU_TYPE_32BIT, + MMU_TYPE_36BIT, + MMU_TYPE_40BIT, + MMU_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* @brief Page offset mask in virtual address - bottom bits */ +static const unsigned long VIRT_PAGE_OFF_MASK = ((1 << MMU_PAGE_SHIFT) - 1); +/* @brief Page table index mask in virtual address - middle bits */ +static const unsigned long VIRT_PAGE_TBL_MASK = + (((1 << MMU_DIR_SHIFT) - 1) & ~(((1 << MMU_PAGE_SHIFT) - 1))); +/* @brief Directory index mask in virtual address - high bits */ +static const unsigned long VIRT_DIR_IDX_MASK = (~((1 << MMU_DIR_SHIFT) - 1)); + +/* + * struct mmu_heap_alloc - information about a virtual mem heap allocation + * @virt_addr: pointer to start of the allocation + * @alloc_size: size in bytes + */ +struct mmu_heap_alloc { + unsigned long virt_addr; + unsigned long alloc_size; +}; + +/* + * struct mmu_page_cfg - mmu_page configuration + * @phys_addr: physical address - unsigned long long is used to support extended physical + * address on 32bit system + * @cpu_virt_addr: CPU virtual address pointer + */ +struct mmu_page_cfg { + unsigned long long phys_addr; + unsigned long cpu_virt_addr; +}; + +/* + * typedef mmu_pfn_page_alloc - page table allocation function + * + * Pointer to a function implemented by the used allocator to create 1 + * page table (used for the MMU mapping - directory page and mapping page) + * + * Return: + * * A populated mmu_page_cfg structure with the result of the page alloc. + * * NULL if the allocation failed. + */ +typedef struct mmu_page_cfg *(*mmu_pfn_page_alloc) (void *); + +/* + * typedef mmu_pfn_page_free + * @arg1: pointer to the mmu_page_cfg that is allocated using mmu_pfn_page_alloc + * + * Pointer to a function to free the allocated page table used for MMU mapping. + * + * @return void + */ +typedef void (*mmu_pfn_page_free) (struct mmu_page_cfg *arg1); + +/* + * typedef mmu_pfn_page_update + * @arg1: pointer to the mmu_page_cfg that is allocated using mmu_pfn_page_alloc + * + * Pointer to a function to update Device memory on non Unified Memory + * + * @return void + */ +typedef void (*mmu_pfn_page_update) (struct mmu_page_cfg *arg1); + +/* + * typedef mmu_pfn_page_write + * @mmu_page: mmu_page mmu page configuration to be written + * @offset: offset in entries (32b word) + * @pa_to_write: pa_to_write physical address to write + * @flags: flags bottom part of the entry used as flags for the MMU (including + * valid flag) + * + * Pointer to a function to write to a device address + * + * @return void + */ +typedef void (*mmu_pfn_page_write) (struct mmu_page_cfg *mmu_page, + unsigned int offset, + unsigned long long pa_to_write, unsigned int flags); + +/* + * struct mmu_info + * @pfn_page_alloc: function pointer for allocating a physical page used in + * MMU mapping + * @alloc_ctx: allocation context handler + * @pfn_page_free: function pointer for freeing a physical page used in + * MMU mapping + * @pfn_page_write: function pointer to write a physical address onto a page. + * If NULL, then internal function is used. Internal function + * assumes that MMU_PHYS_SIZE is the MMU size. + * @pfn_page_update: function pointer to update a physical page on device if + * non UMA. + */ +struct mmu_info { + mmu_pfn_page_alloc pfn_page_alloc; + void *alloc_ctx; + mmu_pfn_page_free pfn_page_free; + mmu_pfn_page_write pfn_page_write; + mmu_pfn_page_update pfn_page_update; +}; + +/* + * mmu_get_page_size() - Access the compilation specified page size of the + * MMU (in Bytes) + */ +static inline unsigned long mmu_get_page_size(void) +{ + return MMU_PAGE_SIZE; +} + +struct mmu_directory *mmu_create_directory(const struct mmu_info *mmu_info_ops); +int mmu_destroy_directory(struct mmu_directory *mmu_dir); + +struct mmu_page_cfg *mmu_directory_get_page(struct mmu_directory *mmu_dir); + +struct mmu_map *mmu_directory_map_sg(struct mmu_directory *mmu_dir, + void *phys_page_sg, + const struct mmu_heap_alloc *dev_va, + unsigned int map_flag); +int mmu_directory_unmap(struct mmu_map *map); + +unsigned int mmu_directory_get_pagetable_entry(struct mmu_directory *mmu_dir, + unsigned long dev_virt_addr); + +#endif /* IMG_DEC_MMU_MMU_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/lst.c b/drivers/media/platform/vxe-vxd/common/lst.c --- a/drivers/media/platform/vxe-vxd/common/lst.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/lst.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * List processing primitives. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Author: + * Lakshmi Sankar + */ + +#include "lst.h" + +#ifndef NULL +#define NULL ((void *)0) +#endif + +void lst_add(struct lst_t *list, void *item) +{ + if (!list->first) { + list->first = item; + list->last = item; + } else { + *list->last = item; + list->last = item; + } + *((void **)item) = NULL; +} + +void lst_addhead(struct lst_t *list, void *item) +{ + if (!list->first) { + list->first = item; + list->last = item; + *((void **)item) = NULL; + } else { + *((void **)item) = list->first; + list->first = item; + } +} + +int lst_empty(struct lst_t *list) +{ + if (!list->first) + return 1; + else + return 0; +} + +void *lst_first(struct lst_t *list) +{ + return list->first; +} + +void lst_init(struct lst_t *list) +{ + list->first = NULL; + list->last = NULL; +} + +void *lst_last(struct lst_t *list) +{ + return list->last; +} + +void *lst_next(void *item) +{ + return *((void **)item); +} + +void *lst_removehead(struct lst_t *list) +{ + void **temp = list->first; + + if (temp) { + list->first = *temp; + if (!list->first) + list->last = NULL; + } + return temp; +} + +void *lst_remove(struct lst_t *list, void *item) +{ + void **p; + void **q; + + p = (void **)list; + q = *p; + while (q) { + if (q == item) { + *p = *q; + if (list->last == q) + list->last = p; + return item; + } + p = q; + q = *p; + } + + return NULL; +} + +int lst_check(struct lst_t *list, void *item) +{ + void **p; + void **q; + + p = (void **)list; + q = *p; + while (q) { + if (q == item) + return 1; + p = q; + q = *p; + } + + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/lst.h b/drivers/media/platform/vxe-vxd/common/lst.h --- a/drivers/media/platform/vxe-vxd/common/lst.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/lst.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * List processing primitives. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Author: + * Lakshmi Sankar + */ +#ifndef __LIST_H__ +#define __LIST_H__ + +#include + +struct lst_t { + void **first; + void **last; +}; + +void lst_add(struct lst_t *list, void *item); +void lst_addhead(struct lst_t *list, void *item); + +/** + * lst_empty- Is list empty? + * @list: pointer to list + */ +int lst_empty(struct lst_t *list); +void *lst_first(struct lst_t *list); +void lst_init(struct lst_t *list); +void *lst_last(struct lst_t *list); +void *lst_next(void *item); +void *lst_remove(struct lst_t *list, void *item); +void *lst_removehead(struct lst_t *list); +int lst_check(struct lst_t *list, void *item); + +#endif /* __LIST_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/pool_api.c b/drivers/media/platform/vxe-vxd/common/pool_api.c --- a/drivers/media/platform/vxe-vxd/common/pool_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/pool_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Resource pool manager API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idgen_api.h" +#include "lst.h" +#include "pool_api.h" +#include "img_errors.h" + +/* + * list can be modified by different instances. So please, + * make sure to acquire mutex lock before initializing the list. + */ +static struct mutex *shared_res_mutex_handle; + +/* + * Max resource ID's. + */ +#define POOL_IDGEN_MAX_ID (0xFFFFFFFF) +/* + * Size of blocks used for ID's. + */ +#define POOL_IDGEN_BLOCK_SIZE (50) + +/* + * Indicates if the pool API has been indialized or not. + * zero if not done. 1 if done. + */ +static int poolinitdone; + +/* list of resource pool */ +static struct lst_t poollist = {0}; + +/** + * struct poollist - Structure contains resource list information. + * @link: to be able to part of single linked list + * @pool_mutex: lock + * @freereslst: list of free resource structure + * @actvreslst: list of active resource structure + * @pfnfree: pool free callback function + * @idgenhandle: ID generator context handl + */ +struct poollist { + void **link; + struct mutex *pool_mutex; /* Mutex lock */ + struct lst_t freereslst; + struct lst_t actvreslst; + pfrecalbkpntr pfnfree; + void *idgenhandle; +}; + +/* + * This structure contains pool resource. + */ +struct poolres { + void **link; /* to be able to part of single linked list */ + /* Resource id */ + unsigned int resid; + /* Pointer to destructor function */ + pdestcallbkptr desfunc; + /* resource param */ + void *resparam; + /* size of resource param in bytes */ + unsigned int resparmsize; + /* pointer to resource pool list */ + struct poollist *respoollst; + /* 1 if this is a clone of the original resource */ + int isclone; + /* pointer to original resource */ + struct poolres *origres; + /* list of cloned resource structures. Only used on the original */ + struct lst_t clonereslst; + /* reference count. Only used on the original resource */ + unsigned int refcnt; + void *cb_handle; +}; + +/* + * This function initializes the list if not done earlier. + */ +int pool_init(void) +{ + /* Check if list already initialized */ + if (!poolinitdone) { + /* + * list can be modified by different instances. So please, + * make sure to acquire mutex lock before initializing the list. + */ + + shared_res_mutex_handle = kzalloc(sizeof(*shared_res_mutex_handle), GFP_KERNEL); + if (!shared_res_mutex_handle) + return -ENOMEM; + + mutex_init(shared_res_mutex_handle); + + /* initialize the list of pools */ + lst_init(&poollist); + /* Get initialized flag to true */ + poolinitdone = 1; + } + + return 0; +} + +/* + * This function de-initializes the list. + */ +void pool_deinit(void) +{ + struct poollist *respoollist; + + /* Check if list initialized */ + if (poolinitdone) { + /* destroy any active pools */ + respoollist = (struct poollist *)lst_first(&poollist); + while (respoollist) { + pool_destroy(respoollist); + respoollist = (struct poollist *)lst_first(&poollist); + } + + /* Destroy mutex */ + mutex_destroy(shared_res_mutex_handle); + kfree(shared_res_mutex_handle); + shared_res_mutex_handle = NULL; + + /* set initialized flag to 0 */ + poolinitdone = 0; + } +} + +/* + * This function creates pool. + */ +int pool_api_create(void **poolhndle) +{ + struct poollist *respoollist; + unsigned int result = 0; + + /* Allocate a pool structure */ + respoollist = kzalloc(sizeof(*respoollist), GFP_KERNEL); + if (!respoollist) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Initialize the pool info */ + lst_init(&respoollist->freereslst); + lst_init(&respoollist->actvreslst); + + /* Create mutex */ + respoollist->pool_mutex = kzalloc(sizeof(*respoollist->pool_mutex), GFP_KERNEL); + if (!respoollist->pool_mutex) { + result = ENOMEM; + goto error_create_context; + } + mutex_init(respoollist->pool_mutex); + + /* Create context for the Id generator */ + result = idgen_createcontext(POOL_IDGEN_MAX_ID, + POOL_IDGEN_BLOCK_SIZE, 0, + &respoollist->idgenhandle); + if (result != IMG_SUCCESS) + goto error_create_context; + + /* Disable interrupts */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_POOL_RES); + + /* Add to list of pools */ + lst_add(&poollist, respoollist); + + /* Enable interrupts */ + mutex_unlock(shared_res_mutex_handle); + + /* Return handle to pool */ + *poolhndle = respoollist; + + return IMG_SUCCESS; + + /* Error handling. */ +error_create_context: + kfree(respoollist); + + return result; +} + +/* + * This function destroys the pool. + */ +int pool_destroy(void *poolhndle) +{ + struct poollist *respoollist = poolhndle; + struct poolres *respool; + struct poolres *clonerespool; + unsigned int result = 0; + + if (!poolinitdone || !respoollist) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + /* Disable interrupts */ + /* + * We need to check if we really need to check disable, + * interrupts because before deleting we need to make sure the + * pool lst is not being used other process. As of now getting ipl + * global mutex + */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_POOL_RES); + + /* Remove the pool from the active list */ + lst_remove(&poollist, respoollist); + + /* Enable interrupts */ + mutex_unlock(shared_res_mutex_handle); + + /* Destroy any resources in the free list */ + respool = (struct poolres *)lst_removehead(&respoollist->freereslst); + while (respool) { + respool->desfunc(respool->resparam, respool->cb_handle); + kfree(respool); + respool = (struct poolres *) + lst_removehead(&respoollist->freereslst); + } + + /* Destroy any resources in the active list */ + respool = (struct poolres *)lst_removehead(&respoollist->actvreslst); + while (respool) { + clonerespool = (struct poolres *) + lst_removehead(&respool->clonereslst); + while (clonerespool) { + /* + * If we created a copy of the resources pvParam + * then free it. + * kfree(NULL) is safe and this check is probably not + * required + */ + kfree(clonerespool->resparam); + + kfree(clonerespool); + clonerespool = (struct poolres *) + lst_removehead(&respool->clonereslst); + } + + /* Call the resource destructor */ + respool->desfunc(respool->resparam, respool->cb_handle); + kfree(respool); + respool = (struct poolres *) + lst_removehead(&respoollist->actvreslst); + } + /* Destroy the context for the Id generator */ + if (respoollist->idgenhandle) + result = idgen_destroycontext(respoollist->idgenhandle); + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* Destroy mutex */ + mutex_destroy(respoollist->pool_mutex); + kfree(respoollist->pool_mutex); + respoollist->pool_mutex = NULL; + + /* Free the pool structure */ + kfree(respoollist); + + return IMG_SUCCESS; + +error_nolock: + return result; +} + +int pool_setfreecalbck(void *poolhndle, pfrecalbkpntr pfnfree) +{ + struct poollist *respoollist = poolhndle; + struct poolres *respool; + unsigned int result = 0; + + if (!poolinitdone || !respoollist) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + respoollist->pfnfree = pfnfree; + + /* If free callback set */ + if (respoollist->pfnfree) { + /* Move resources from free to active list */ + respool = (struct poolres *) + lst_removehead(&respoollist->freereslst); + while (respool) { + /* Add to active list */ + lst_add(&respoollist->actvreslst, respool); + respool->refcnt++; + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* Call the free callback */ + respoollist->pfnfree(respool->resid, respool->resparam); + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + /* Get next free resource */ + respool = (struct poolres *) + lst_removehead(&respoollist->freereslst); + } + } + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* Return IMG_SUCCESS */ + return IMG_SUCCESS; + +error_nolock: + return result; +} + +int pool_resreg(void *poolhndle, pdestcallbkptr fndestructor, + void *resparam, unsigned int resparamsize, + int balloc, unsigned int *residptr, + void **poolreshndle, void *cb_handle) +{ + struct poollist *respoollist = poolhndle; + struct poolres *respool; + unsigned int result = 0; + + if (!poolinitdone || !respoollist) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + /* Allocate a resource structure */ + respool = kzalloc(sizeof(*respool), GFP_KERNEL); + if (!respool) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Setup the resource */ + respool->desfunc = fndestructor; + respool->cb_handle = cb_handle; + respool->resparam = resparam; + respool->resparmsize = resparamsize; + respool->respoollst = respoollist; + lst_init(&respool->clonereslst); + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + /* Set resource id */ + result = idgen_allocid(respoollist->idgenhandle, + (void *)respool, &respool->resid); + if (result != IMG_SUCCESS) { + kfree(respool); + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + return result; + } + + /* If allocated or free callback not set */ + if (balloc || respoollist->pfnfree) { + /* Add to active list */ + lst_add(&respoollist->actvreslst, respool); + respool->refcnt++; + } else { + /* Add to free list */ + lst_add(&respoollist->freereslst, respool); + } + + /* Return the resource id */ + if (residptr) + *residptr = respool->resid; + + /* Return the handle to the resource */ + if (poolreshndle) + *poolreshndle = respool; + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* If free callback set */ + if (respoollist->pfnfree) { + /* Call the free callback */ + respoollist->pfnfree(respool->resid, respool->resparam); + } + + /* Return IMG_SUCCESS */ + return IMG_SUCCESS; + +error_nolock: + return result; +} + +int pool_resdestroy(void *poolreshndle, int bforce) +{ + struct poolres *respool = poolreshndle; + struct poollist *respoollist; + struct poolres *origrespool; + unsigned int result = 0; + + if (!poolinitdone || !respool) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + respoollist = respool->respoollst; + + /* If this is a clone */ + if (respool->isclone) { + /* Get access to the original */ + origrespool = respool->origres; + if (!origrespool) { + result = IMG_ERROR_UNEXPECTED_STATE; + goto error_nolock; + } + + if (origrespool->isclone) { + result = IMG_ERROR_UNEXPECTED_STATE; + goto error_nolock; + } + + /* Remove from the clone list */ + lst_remove(&origrespool->clonereslst, respool); + + /* Free resource id */ + result = idgen_freeid(respoollist->idgenhandle, + respool->resid); + if (result != IMG_SUCCESS) + return result; + + /* + * If we created a copy of the resources pvParam then free it + * kfree(NULL) is safe and this check is probably not required. + */ + kfree(respool->resparam); + + /* Free the clone resource structure */ + kfree(respool); + + /* Set resource to be "freed" to the original */ + respool = origrespool; + } + + /* If there are still outstanding references */ + if (!bforce && respool->refcnt != 0) { + /* + * We may need to mark the resource and destroy it when + * there are no outstanding references + */ + return IMG_SUCCESS; + } + + /* Has the resource outstanding references */ + if (respool->refcnt != 0) { + /* Remove the resource from the active list */ + lst_remove(&respoollist->actvreslst, respool); + } else { + /* Remove the resource from the free list */ + lst_remove(&respoollist->freereslst, respool); + } + + /* Free resource id */ + result = idgen_freeid(respoollist->idgenhandle, + respool->resid); + if (result != IMG_SUCCESS) + return result; + + /* Call the resource destructor */ + respool->desfunc(respool->resparam, respool->cb_handle); + kfree(respool); + + return IMG_SUCCESS; + +error_nolock: + return result; +} + +int pool_resalloc(void *poolhndle, void *poolreshndle) +{ + struct poollist *respoollist = poolhndle; + struct poolres *respool = poolreshndle; + unsigned int result = 0; + + if (!poolinitdone || !respoollist || !poolreshndle) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + /* Remove resource from free list */ + lst_remove(&respoollist->freereslst, respool); + + /* Add to active list */ + lst_add(&respoollist->actvreslst, respool); + respool->refcnt++; + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* Return IMG_SUCCESS */ + return IMG_SUCCESS; + +error_nolock: + return result; +} + +int pool_resfree(void *poolreshndle) +{ + struct poolres *respool = poolreshndle; + struct poollist *respoollist; + struct poolres *origrespool; + unsigned int result = 0; + + if (!poolinitdone || !respool) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + respoollist = respool->respoollst; + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + /* If this is a clone */ + if (respool->isclone) { + /* Get access to the original */ + origrespool = respool->origres; + if (!origrespool) { + mutex_unlock(respoollist->pool_mutex); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Remove from the clone list */ + lst_remove(&origrespool->clonereslst, respool); + + /* Free resource id */ + result = idgen_freeid(respoollist->idgenhandle, + respool->resid); + if (result != IMG_SUCCESS) { + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + return result; + } + + /* + * If we created a copy of the resources pvParam then free it + * kfree(NULL) is safe and this check is probably not required. + */ + kfree(respool->resparam); + + /* Free the clone resource structure */ + kfree(respool); + + /* Set resource to be "freed" to the original */ + respool = origrespool; + } + + /* Update the reference count */ + respool->refcnt--; + + /* If there are still outstanding references */ + if (respool->refcnt != 0) { + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + /* Return IMG_SUCCESS */ + return IMG_SUCCESS; + } + + /* Remove the resource from the active list */ + lst_remove(&respoollist->actvreslst, respool); + + /* If free callback set */ + if (respoollist->pfnfree) { + /* Add to active list */ + lst_add(&respoollist->actvreslst, respool); + respool->refcnt++; + } else { + /* Add to free list */ + lst_add(&respoollist->freereslst, respool); + } + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* If free callback set */ + if (respoollist->pfnfree) { + /* Call the free callback */ + respoollist->pfnfree(respool->resid, respool->resparam); + } + + /* Return IMG_SUCCESS */ + return IMG_SUCCESS; + +error_nolock: + return result; +} + +int pool_resclone(void *poolreshndle, void **clonereshndle, void **resparam) +{ + struct poolres *respool = poolreshndle; + struct poollist *respoollist; + struct poolres *origrespool = respool; + struct poolres *clonerespool; + unsigned int result = 0; + + if (!poolinitdone || !respool) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error_nolock; + } + + /* Allocate a resource structure */ + clonerespool = kzalloc(sizeof(*clonerespool), GFP_KERNEL); + if (!clonerespool) + return IMG_ERROR_OUT_OF_MEMORY; + + respoollist = respool->respoollst; + if (!respoollist) + return IMG_ERROR_FATAL; + + /* Lock the pool */ + mutex_lock_nested(respoollist->pool_mutex, SUBCLASS_POOL); + + /* Set resource id */ + result = idgen_allocid(respoollist->idgenhandle, + (void *)clonerespool, &clonerespool->resid); + if (result != IMG_SUCCESS) + goto error_alloc_id; + + /* If this is a clone, set the original */ + if (respool->isclone) + origrespool = respool->origres; + + /* Setup the cloned resource */ + clonerespool->isclone = 1; + clonerespool->respoollst = respoollist; + clonerespool->origres = origrespool; + + /* Add to clone list */ + lst_add(&origrespool->clonereslst, clonerespool); + origrespool->refcnt++; + + /* If ppvParam is not IMG_NULL */ + if (resparam) { + /* If the size of the original vParam is 0 */ + if (origrespool->resparmsize == 0) { + *resparam = NULL; + } else { + /* Allocate memory for a copy of the original vParam */ + /* + * kmemdup allocates memory of length + * origrespool->resparmsize and to resparam and copy + * origrespool->resparam to resparam of the allocated + * length + */ + *resparam = kmemdup(origrespool->resparam, + origrespool->resparmsize, + GFP_KERNEL); + if (!(*resparam)) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error_copy_param; + } + } + } + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + + /* Return the cloned resource */ + *clonereshndle = clonerespool; + + /* Return IMG_SUCCESS */ + return IMG_SUCCESS; + + /* Error handling. */ +error_copy_param: + lst_remove(&origrespool->clonereslst, clonerespool); + origrespool->refcnt--; +error_alloc_id: + kfree(clonerespool); + + /* Unlock the pool */ + mutex_unlock(respoollist->pool_mutex); + +error_nolock: + return result; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/pool_api.h b/drivers/media/platform/vxe-vxd/common/pool_api.h --- a/drivers/media/platform/vxe-vxd/common/pool_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/pool_api.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Resource pool manager API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#ifndef __POOLAPI_H__ +#define __POOLAPI_H__ + +#include "img_errors.h" +#include "lst.h" + +/* + * This is the prototype for "free" callback functions. This function + * is called when resources are returned to the pools list of free resources. + * NOTE: The "freed" resource is then allocated and passed to the callback + * function. + */ +typedef void (*pfrecalbkpntr)(unsigned int ui32resid, void *resparam); + +/* + * This is the prototype for "destructor" callback functions. This function + * is called when a resource registered with the resource pool manager is to + * be destroyed. + */ +typedef void (*pdestcallbkptr)(void *resparam, void *cb_handle); + +/* + * pool_init - This function is used to initializes the resource pool manager component + * and should be called at start-up. + */ +int pool_init(void); + +/* + * This function is used to deinitialises the resource pool manager component + * and would normally be called at shutdown. + */ +void pool_deinit(void); + +/* + * This function is used to create a resource pool into which resources can be + * placed. + */ +int pool_api_create(void **poolhndle); + +/* + * This function is used to destroy a resource pool. + * NOTE: Destroying a resource pool destroys all of the resources within the + * pool by calling the associated destructor function #POOL_pfnDestructor + * defined when the resource what registered using POOL_ResRegister(). + * + * NOTE: All of the pools resources must be in the pools free list - the + * allocated list must be empty. + */ +int pool_destroy(void *poolhndle); + +/* + * This function is used to set or remove a free callback function on a pool. + * The free callback function gets call for any resources already in the + * pools free list or for any resources that subsequently get freed. + * NOTE: The resource passed to the callback function has been allocated before + * the callback is made. + */ +int pool_setfreecalbck(void *poolhndle, pfrecalbkpntr pfnfree); + +/* + * This function is used to register a resource within a resource pool. The + * resource is added to the pools allocated or free list based on the value + * of bAlloc. + */ +int pool_resreg(void *poolhndle, pdestcallbkptr fndestructor, + void *resparam, unsigned int resparamsize, + int balloc, unsigned int *residptr, + void **poolreshndle, void *cb_handle); + +/* + * This function is used to destroy a resource. + */ +int pool_resdestroy(void *poolreshndle, int bforce); + +/* + * This function is used to get/allocate a resource from a pool. This moves + * the resource from the free to allocated list. + */ +int pool_resalloc(void *poolhndle, void *poolreshndle); + +/* + * This function is used to free a resource and return it to the pools lists of + * free resources. + * NOTE: The resources is only moved to the free list when all references to + * the resource have been freed. + */ +int pool_resfree(void *poolreshndle); + +/* + * This function is used to clone a resource - this creates an additional + * reference to the resource. + * NOTE: The resources is only moved to the free list when all references to + * the resource have been freed. + * NOTE: If this function is used to clone the resource's pvParam data then + * the clone of the data is freed when the clone of the resource is freed. + * The resource destructor is NOT used for this - simply an IMG_FREE. + */ +int pool_resclone(void *poolreshndle, void **clonereshndle, void **resparam); + +#endif /* __POOLAPI_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/pool.c b/drivers/media/platform/vxe-vxd/common/pool.c --- a/drivers/media/platform/vxe-vxd/common/pool.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/pool.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Object Pool Memory Allocator + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "img_errors.h" +#include "pool.h" + +#define BUFF_MAX_SIZE 4096 +#define BUFF_MAX_GROW 32 + +/* 64 bits */ +#define ALIGN_SIZE (sizeof(long long) - 1) + +struct pool { + unsigned char *name; + unsigned int size; + unsigned int grow; + struct buffer *buffers; + struct object *objects; +}; + +struct buffer { + struct buffer *next; +}; + +struct object { + struct object *next; +}; + +static inline unsigned char *strdup_cust(const unsigned char *str) +{ + unsigned char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); + + if (r) + strcpy(r, str); + return r; +} + +/* + * pool_create - Create an sObject pool + * @name: Name of sObject pool for diagnostic purposes + * @obj_size: size of each sObject in the pool in bytes + * @pool_hdnl: Will contain NULL or sObject pool handle + * + * This function Create an sObject pool + */ + +int pool_create(const unsigned char * const name, + unsigned int obj_size, + struct pool ** const pool_hdnl) +{ + struct pool *local_pool = NULL; + unsigned int result = IMG_ERROR_FATAL; + + if (!name || !pool_hdnl) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + local_pool = kmalloc((sizeof(*local_pool)), GFP_KERNEL); + if (!local_pool) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + local_pool->name = strdup_cust((unsigned char *)name); + local_pool->size = obj_size; + local_pool->buffers = NULL; + local_pool->objects = NULL; + local_pool->grow = + (BUFF_MAX_SIZE - sizeof(struct buffer)) / + (obj_size + ALIGN_SIZE); + + if (local_pool->grow == 0) + local_pool->grow = 1; + else if (local_pool->grow > BUFF_MAX_GROW) + local_pool->grow = BUFF_MAX_GROW; + + *pool_hdnl = local_pool; + result = IMG_SUCCESS; + + return result; +} + +/* + * @Function pool_delete + * @Description + * Delete an sObject pool. All psObjects allocated from the pool must + * be free'd with pool_free() before deleting the sObject pool. + * @Input pool : Object Pool pointer + * @Return IMG_SUCCESS or an error code. + */ +int pool_delete(struct pool * const pool_arg) +{ + struct buffer *local_buf = NULL; + unsigned int result = IMG_ERROR_FATAL; + + if (!pool_arg) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + local_buf = pool_arg->buffers; + while (local_buf) { + local_buf = local_buf->next; + kfree(pool_arg->buffers); + pool_arg->buffers = local_buf; + } + + kfree(pool_arg->name); + pool_arg->name = NULL; + + kfree(pool_arg); + result = IMG_SUCCESS; + + return result; +} + +/* + * @Function pool_alloc + * @Description + * Allocate an sObject from an sObject pool. + * @Input pool_arg : Object Pool + * @Output obj_hndl : Pointer containing the handle to the + * object created or IMG_NULL + * @Return IMG_SUCCESS or an error code. + */ +int pool_alloc(struct pool * const pool_arg, + void ** const obj_hndl) +{ + struct object *local_obj1 = NULL; + struct buffer *local_buf = NULL; + unsigned int idx = 0; + unsigned int sz = 0; + unsigned int result = IMG_ERROR_FATAL; + + if (!pool_arg || !obj_hndl) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + if (!pool_arg->objects) { + sz = (pool_arg->size + ALIGN_SIZE); + sz *= (pool_arg->grow + sizeof(struct buffer)); + local_buf = kmalloc(sz, GFP_KERNEL); + if (!local_buf) { + result = IMG_ERROR_MALLOC_FAILED; + return result; + } + + local_buf->next = pool_arg->buffers; + pool_arg->buffers = local_buf; + + for (idx = 0; idx < pool_arg->grow; idx++) { + struct object *local_obj2; + unsigned char *temp_ptr = NULL; + + local_obj2 = (struct object *)(((unsigned char *)(local_buf + 1)) + + (idx * (pool_arg->size + ALIGN_SIZE))); + + temp_ptr = (unsigned char *)local_obj2; + if ((unsigned long)temp_ptr & ALIGN_SIZE) { + temp_ptr += ((ALIGN_SIZE + 1) + - ((unsigned long)temp_ptr & ALIGN_SIZE)); + local_obj2 = (struct object *)temp_ptr; + } + + local_obj2->next = pool_arg->objects; + pool_arg->objects = local_obj2; + } + } + + if (!pool_arg->objects) { + result = IMG_ERROR_UNEXPECTED_STATE; + return result; + } + + local_obj1 = pool_arg->objects; + pool_arg->objects = local_obj1->next; + + *obj_hndl = (void *)(local_obj1); + result = IMG_SUCCESS; + + return result; +} + +/* + * @Function pool_free + * @Description + * Free an sObject previously allocated from an sObject pool. + * @Input pool_arg : Object Pool pointer. + * @Output h_object : Handle to the object to be freed. + * @Return IMG_SUCCESS or an error code. + */ +int pool_free(struct pool * const pool_arg, + void * const obj_hndl) +{ + struct object *object = NULL; + unsigned int result = IMG_ERROR_FATAL; + + if (!pool_arg || !obj_hndl) { + result = IMG_ERROR_INVALID_PARAMETERS; + return result; + } + + object = (struct object *)obj_hndl; + object->next = pool_arg->objects; + pool_arg->objects = object; + + result = IMG_SUCCESS; + + return result; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/pool.h b/drivers/media/platform/vxe-vxd/common/pool.h --- a/drivers/media/platform/vxe-vxd/common/pool.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/pool.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Object Pool Memory Allocator header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ +#ifndef _pool_h_ +#define _pool_h_ + +#include + +struct pool; + +/** + * pool_create - Create an sObject pool + * @name: Name of sObject pool for diagnostic purposes + * @obj_size: size of each sObject in the pool in bytes + * @pool: Will contain NULL or sObject pool handle + * + * Return IMG_SUCCESS or an error code. + */ +int pool_create(const unsigned char * const name, + unsigned int obj_size, + struct pool ** const pool); + +/* + * @Function pool_delete + * @Description + * Delete an sObject pool. All psObjects allocated from the pool must + * be free'd with pool_free() before deleting the sObject pool. + * @Input pool : Object Pool pointer + * @Return IMG_SUCCESS or an error code. + */ +int pool_delete(struct pool * const pool); + +/* + * @Function pool_alloc + * @Description + * Allocate an Object from an Object pool. + * @Input pool : Object Pool + * @Output obj_hdnl : Pointer containing the handle to the + * object created or IMG_NULL + * @Return IMG_SUCCESS or an error code. + */ +int pool_alloc(struct pool * const pool, + void ** const obj_hdnl); + +/* + * @Function pool_free + * @Description + * Free an sObject previously allocated from an sObject pool. + * @Input pool : Object Pool pointer. + * @Output obj_hdnl : Handle to the object to be freed. + * @Return IMG_SUCCESS or an error code. + */ +int pool_free(struct pool * const pool, + void * const obj_hdnl); + +#endif /* _pool_h_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/ra.c b/drivers/media/platform/vxe-vxd/common/ra.c --- a/drivers/media/platform/vxe-vxd/common/ra.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/ra.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,972 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implements generic resource allocation. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "hash.h" +#include "img_errors.h" +#include "pool.h" +#include "ra.h" + +static unsigned char global_init; + +/* pool of struct arena's */ +static struct pool *global_pool_arena; + +/* pool of struct boundary tag */ +static struct pool *global_pool_bt; + +/** + * ra_request_alloc_fail - ra_request_alloc_fail + * @import_hdnl : Callback handle. + * @requested_size : Requested allocation size. + * @ref : Pointer to user reference data. + * @alloc_flags : Allocation flags. + * @actual_size : Pointer to contain the actual allocated size. + * @base_addr : Allocation base(always 0,it is failing). + * + * Default callback allocator used if no callback is specified, always fails + * to allocate further resources to the arena. + */ +static int ra_request_alloc_fail(void *import_hdnl, + unsigned long long requested_size, + unsigned long long *actual_size, + void **ref, + unsigned int alloc_flags, + unsigned long long *base_addr) +{ + if (base_addr) + *base_addr = 0; + + return IMG_SUCCESS; +} + +/* + * @Function ra_log2 + * @Description + * Calculates the Log2(n) with n being a 64-bit value. + * + * @Input value : Input value. + * @Output None + * @Return result : Log2(ui64Value). + */ + +static unsigned int ra_log2(unsigned long long value) +{ + int res = 0; + + value >>= 1; + while (value > 0) { + value >>= 1; + res++; + } + return res; +} + +/* + * @Function ra_segment_list_insert_after + * @Description Insert a boundary tag into an arena segment list after a + * specified boundary tag. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_here_arg : The boundary tag before which psBTToInsert + * will be added . + * @Input bt_to_insert_arg : The boundary tag to insert. + * @Output None + * @Return None + */ +static void ra_segment_list_insert_after(struct arena *arena_arg, + struct btag *bt_here_arg, + struct btag *bt_to_insert_arg) +{ + bt_to_insert_arg->nxt_seg = bt_here_arg->nxt_seg; + bt_to_insert_arg->prv_seg = bt_here_arg; + + if (!bt_here_arg->nxt_seg) + arena_arg->tail_seg = bt_to_insert_arg; + else + bt_here_arg->nxt_seg->prv_seg = bt_to_insert_arg; + + bt_here_arg->nxt_seg = bt_to_insert_arg; +} + +/* + * @Function ra_segment_list_insert + * @Description + * Insert a boundary tag into an arena segment list at the appropriate point. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_to_insert_arg : The boundary tag to insert. + * @Output None + * @Return None + */ +static void ra_segment_list_insert(struct arena *arena_arg, + struct btag *bt_to_insert_arg) +{ + /* insert into the segment chain */ + if (!arena_arg->head_seg) { + arena_arg->head_seg = bt_to_insert_arg; + arena_arg->tail_seg = bt_to_insert_arg; + bt_to_insert_arg->nxt_seg = NULL; + bt_to_insert_arg->prv_seg = NULL; + } else { + struct btag *bt_scan = arena_arg->head_seg; + + while (bt_scan->nxt_seg && + bt_to_insert_arg->base >= + bt_scan->nxt_seg->base) { + bt_scan = bt_scan->nxt_seg; + } + ra_segment_list_insert_after(arena_arg, + bt_scan, + bt_to_insert_arg); + } +} + +/* + * @Function ra_SegmentListRemove + * @Description + * Insert a boundary tag into an arena segment list at the appropriate point. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_to_remove_arg : The boundary tag to insert. + * @Output None + * @Return None + */ +static void ra_segment_list_remove(struct arena *arena_arg, + struct btag *bt_to_remove_arg) +{ + if (!bt_to_remove_arg->prv_seg) + arena_arg->head_seg = bt_to_remove_arg->nxt_seg; + else + bt_to_remove_arg->prv_seg->nxt_seg = bt_to_remove_arg->nxt_seg; + + if (!bt_to_remove_arg->nxt_seg) + arena_arg->tail_seg = bt_to_remove_arg->prv_seg; + else + bt_to_remove_arg->nxt_seg->prv_seg = bt_to_remove_arg->prv_seg; +} + +/* + * @Function ra_segment_split + * @Description + * Split a segment into two, maintain the arena segment list. + * The boundary tag should not be in the free table. Neither the original or + * the new psBTNeighbour bounary tag will be in the free table. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_to_split_arg : The boundary tag to split. + * The required segment size of boundary tag after the split. + * @Output None + * @Return btag *: New boundary tag. + */ +static struct btag *ra_segment_split(struct arena *arena_arg, + struct btag *bt_to_split_arg, + unsigned long long size) +{ + struct btag *local_bt_neighbour = NULL; + int res = IMG_ERROR_FATAL; + + res = pool_alloc(global_pool_bt, ((void **)&local_bt_neighbour)); + if (res != IMG_SUCCESS) + return NULL; + + local_bt_neighbour->prv_seg = bt_to_split_arg; + local_bt_neighbour->nxt_seg = bt_to_split_arg->nxt_seg; + local_bt_neighbour->bt_type = RA_BOUNDARY_TAG_TYPE_FREE; + local_bt_neighbour->size = (bt_to_split_arg->size - size); + local_bt_neighbour->base = (bt_to_split_arg->base + size); + local_bt_neighbour->nxt_free = NULL; + local_bt_neighbour->prv_free = NULL; + local_bt_neighbour->ref = bt_to_split_arg->ref; + + if (!bt_to_split_arg->nxt_seg) + arena_arg->tail_seg = local_bt_neighbour; + else + bt_to_split_arg->nxt_seg->prv_seg = local_bt_neighbour; + + bt_to_split_arg->nxt_seg = local_bt_neighbour; + bt_to_split_arg->size = size; + + return local_bt_neighbour; +} + +/* + * @Function ra_free_list_insert + * @Description + * Insert a boundary tag into an arena free table. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_arg : The boundary tag to insert into an arena + * free table. + * @Output None + * @Return None + */ +static void ra_free_list_insert(struct arena *arena_arg, + struct btag *bt_arg) +{ + unsigned int index = ra_log2(bt_arg->size); + + bt_arg->bt_type = RA_BOUNDARY_TAG_TYPE_FREE; + if (index < FREE_TABLE_LIMIT) + bt_arg->nxt_free = arena_arg->head_free[index]; + else + bt_arg->nxt_free = NULL; + + bt_arg->prv_free = NULL; + + if (index < FREE_TABLE_LIMIT) { + if (arena_arg->head_free[index]) + arena_arg->head_free[index]->prv_free = bt_arg; + } + + if (index < FREE_TABLE_LIMIT) + arena_arg->head_free[index] = bt_arg; +} + +/* + * @Function ra_free_list_remove + * @Description + * Remove a boundary tag from an arena free table. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_arg : The boundary tag to remove from + * an arena free table. + * @Output None + * @Return None + */ +static void ra_free_list_remove(struct arena *arena_arg, + struct btag *bt_arg) +{ + unsigned int index = ra_log2(bt_arg->size); + + if (bt_arg->nxt_free) + bt_arg->nxt_free->prv_free = bt_arg->prv_free; + + if (!bt_arg->prv_free && index < FREE_TABLE_LIMIT) + arena_arg->head_free[index] = bt_arg->nxt_free; + else if (bt_arg->prv_free) + bt_arg->prv_free->nxt_free = bt_arg->nxt_free; +} + +/* + * @Function ra_build_span_marker + * @Description + * Construct a span marker boundary tag. + * @Input base : The base of the boundary tag. + * @Output None + * @Return btag * : New span marker boundary tag + */ +static struct btag *ra_build_span_marker(unsigned long long base) +{ + struct btag *local_bt = NULL; + int res = IMG_ERROR_FATAL; + + res = pool_alloc(global_pool_bt, ((void **)&local_bt)); + if (res != IMG_SUCCESS) + return NULL; + + local_bt->bt_type = RA_BOUNDARY_TAG_TYPE_SPAN; + local_bt->base = base; + local_bt->size = 0; + local_bt->nxt_seg = NULL; + local_bt->prv_seg = NULL; + local_bt->nxt_free = NULL; + local_bt->prv_free = NULL; + local_bt->ref = NULL; + + return local_bt; +} + +/* + * @Function ra_build_bt + * @Description + * Construct a boundary tag for a free segment. + * @Input ui64Base : The base of the resource segment. + * @Input ui64Size : The extent of the resource segment. + * @Output None + * @Return btag * : New boundary tag + */ +static struct btag *ra_build_bt(unsigned long long base, unsigned long long size) +{ + struct btag *local_bt = NULL; + int res = IMG_ERROR_FATAL; + + res = pool_alloc(global_pool_bt, ((void **)&local_bt)); + + if (res != IMG_SUCCESS) + return local_bt; + + local_bt->bt_type = RA_BOUNDARY_TAG_TYPE_FREE; + local_bt->base = base; + local_bt->size = size; + local_bt->nxt_seg = NULL; + local_bt->prv_seg = NULL; + local_bt->nxt_free = NULL; + local_bt->prv_free = NULL; + local_bt->ref = NULL; + + return local_bt; +} + +/* + * @Function ra_insert_resource + * @Description + * Add a free resource segment to an arena. + * @Input base : The base of the resource segment. + * @Input size : The size of the resource segment. + * @Output None + * @Return IMG_SUCCESS or an error code. + */ +static int ra_insert_resource(struct arena *arena_arg, + unsigned long long base, + unsigned long long size) +{ + struct btag *local_bt = NULL; + + local_bt = ra_build_bt(base, size); + if (!local_bt) + return IMG_ERROR_UNEXPECTED_STATE; + + ra_segment_list_insert(arena_arg, local_bt); + ra_free_list_insert(arena_arg, local_bt); + arena_arg->max_idx = ra_log2(size); + if (1ULL << arena_arg->max_idx < size) + arena_arg->max_idx++; + + return IMG_SUCCESS; +} + +/* + * @Function ra_insert_resource_span + * @Description + * Add a free resource span to an arena, complete with span markers. + * @Input arena_arg : Pointer to the input arena. + * @Input base : The base of the resource segment. + * @Input size : The size of the resource segment. + * @Output None + * @Return btag * : The boundary tag representing + * the free resource segment. + */ +static struct btag *ra_insert_resource_span(struct arena *arena_arg, + unsigned long long base, + unsigned long long size) +{ + struct btag *local_bt = NULL; + struct btag *local_bt_span_start = NULL; + struct btag *local_bt_span_end = NULL; + + local_bt_span_start = ra_build_span_marker(base); + if (!local_bt_span_start) + return NULL; + + local_bt_span_end = ra_build_span_marker(base + size); + if (!local_bt_span_end) { + pool_free(global_pool_bt, local_bt_span_start); + return NULL; + } + + local_bt = ra_build_bt(base, size); + if (!local_bt) { + pool_free(global_pool_bt, local_bt_span_end); + pool_free(global_pool_bt, local_bt_span_start); + return NULL; + } + + ra_segment_list_insert(arena_arg, local_bt_span_start); + ra_segment_list_insert_after(arena_arg, + local_bt_span_start, + local_bt); + ra_free_list_insert(arena_arg, local_bt); + ra_segment_list_insert_after(arena_arg, + local_bt, + local_bt_span_end); + + return local_bt; +} + +/* + * @Function ra_free_bt + * @Description + * Free a boundary tag taking care of the segment list and the + * boundary tag free table. + * @Input arena_arg : Pointer to the input arena. + * @Input bt_arg : The boundary tag to free. + * @Output None + * @Return None + */ +static void ra_free_bt(struct arena *arena_arg, + struct btag *bt_arg) +{ + struct btag *bt_neibr; + + /* try and coalesce with left bt_neibr */ + bt_neibr = bt_arg->prv_seg; + if (bt_neibr && + bt_neibr->bt_type == RA_BOUNDARY_TAG_TYPE_FREE && + bt_neibr->base + bt_neibr->size == bt_arg->base) { + ra_free_list_remove(arena_arg, bt_neibr); + ra_segment_list_remove(arena_arg, bt_neibr); + bt_arg->base = bt_neibr->base; + bt_arg->size += bt_neibr->size; + pool_free(global_pool_bt, bt_neibr); + } + + /* try to coalesce with right psBTNeighbour */ + bt_neibr = bt_arg->nxt_seg; + if (bt_neibr && + bt_neibr->bt_type == RA_BOUNDARY_TAG_TYPE_FREE && + bt_arg->base + bt_arg->size == bt_neibr->base) { + ra_free_list_remove(arena_arg, bt_neibr); + ra_segment_list_remove(arena_arg, bt_neibr); + bt_arg->size += bt_neibr->size; + pool_free(global_pool_bt, bt_neibr); + } + + if (bt_arg->nxt_seg && + bt_arg->nxt_seg->bt_type == RA_BOUNDARY_TAG_TYPE_SPAN && + bt_arg->prv_seg && bt_arg->prv_seg->bt_type == + RA_BOUNDARY_TAG_TYPE_SPAN) { + struct btag *ps_bt_nxt = bt_arg->nxt_seg; + struct btag *ps_bt_prev = bt_arg->prv_seg; + + ra_segment_list_remove(arena_arg, ps_bt_nxt); + ra_segment_list_remove(arena_arg, ps_bt_prev); + ra_segment_list_remove(arena_arg, bt_arg); + arena_arg->import_free_fxn(arena_arg->import_hdnl, + bt_arg->base, + bt_arg->ref); + pool_free(global_pool_bt, ps_bt_nxt); + pool_free(global_pool_bt, ps_bt_prev); + pool_free(global_pool_bt, bt_arg); + } else { + ra_free_list_insert(arena_arg, bt_arg); + } +} + +static int ra_check_btag(struct arena *arena_arg, + unsigned long long size_arg, + void **ref, + struct btag *bt_arg, + unsigned long long align_arg, + unsigned long long *base_arg, + unsigned int align_log2) +{ + unsigned long long local_align_base; + int res = IMG_ERROR_FATAL; + + while (bt_arg) { + if (align_arg > 1ULL) + local_align_base = ((bt_arg->base + align_arg - 1) + >> align_log2) << align_log2; + else + local_align_base = bt_arg->base; + + if ((bt_arg->base + bt_arg->size) >= + (local_align_base + size_arg)) { + ra_free_list_remove(arena_arg, bt_arg); + + /* + * with align_arg we might need to discard the front of + * this segment + */ + if (local_align_base > bt_arg->base) { + struct btag *btneighbor; + + btneighbor = ra_segment_split(arena_arg, + bt_arg, + (local_align_base - + bt_arg->base)); + /* + * Partition the buffer, create a new boundary + * tag + */ + if (!btneighbor) + return IMG_ERROR_UNEXPECTED_STATE; + + ra_free_list_insert(arena_arg, bt_arg); + bt_arg = btneighbor; + } + + /* + * The segment might be too big, if so, discard the back + * of the segment + */ + if (bt_arg->size > size_arg) { + struct btag *btneighbor; + + btneighbor = ra_segment_split(arena_arg, + bt_arg, + size_arg); + /* + * Partition the buffer, create a new boundary + * tag + */ + if (!btneighbor) + return IMG_ERROR_UNEXPECTED_STATE; + + ra_free_list_insert(arena_arg, btneighbor); + } + + bt_arg->bt_type = RA_BOUNDARY_TAG_TYPE_LIVE; + + res = vid_hash_insert(arena_arg->hash_tbl, + bt_arg->base, + (unsigned long)bt_arg); + if (res != IMG_SUCCESS) { + ra_free_bt(arena_arg, bt_arg); + *base_arg = 0; + return IMG_ERROR_UNEXPECTED_STATE; + } + + if (ref) + *ref = bt_arg->ref; + + *base_arg = bt_arg->base; + return IMG_SUCCESS; + } + bt_arg = bt_arg->nxt_free; + } + + return res; +} + +/* + * @Function ra_attempt_alloc_aligned + * @Description Attempt to allocate from an arena + * @Input arena_arg: Pointer to the input arena + * @Input size_arg: The requested allocation size + * @Input ref: The user references associated with the allocated + * segment + * @Input align_arg: Required alignment + * @Output base_arg: Allocated resource size + * @Return IMG_SUCCESS or an error code + */ +static int ra_attempt_alloc_aligned(struct arena *arena_arg, + unsigned long long size_arg, + void **ref, + unsigned long long align_arg, + unsigned long long *base_arg) +{ + unsigned int index; + unsigned int align_log2; + int res = IMG_ERROR_FATAL; + + if (!arena_arg || !base_arg) + return IMG_ERROR_INVALID_PARAMETERS; + + /* + * Take the log of the alignment to get number of bits to shift + * left/right for multiply/divide. Assumption made here is that + * alignment has to be a power of 2 value. Aserting otherwise. + */ + align_log2 = ra_log2(align_arg); + + /* + * Search for a near fit free boundary tag, start looking at the + * log2 free table for our required size and work on up the table. + */ + index = ra_log2(size_arg); + + /* + * If the Size required is exactly 2**n then use the n bucket, because + * we know that every free block in that bucket is larger than 2**n, + * otherwise start at then next bucket up. + */ + if (size_arg > (1ull << index)) + index++; + + while ((index < FREE_TABLE_LIMIT) && !arena_arg->head_free[index]) + index++; + + if (index >= FREE_TABLE_LIMIT) { + pr_err("requested allocation size doesn't fit in the arena. Increase MMU HEAP Size\n"); + return IMG_ERROR_OUT_OF_MEMORY; + } + + while (index < FREE_TABLE_LIMIT) { + if (arena_arg->head_free[index]) { + /* we have a cached free boundary tag */ + struct btag *local_bt = + arena_arg->head_free[index]; + + res = ra_check_btag(arena_arg, + size_arg, + ref, + local_bt, + align_arg, + base_arg, + align_log2); + if (res != IMG_SUCCESS) + return res; + } + index++; + } + + return IMG_SUCCESS; +} + +/* + * @Function vid_ra_init + * @Description Initializes the RA module. Must be called before any other + * ra API function + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_initialise(void) +{ + int res = IMG_ERROR_FATAL; + + if (!global_init) { + res = pool_create("img-arena", + sizeof(struct arena), + &global_pool_arena); + if (res != IMG_SUCCESS) + return IMG_ERROR_UNEXPECTED_STATE; + + res = pool_create("img-bt", + sizeof(struct btag), + &global_pool_bt); + if (res != IMG_SUCCESS) { + res = pool_delete(global_pool_arena); + global_pool_arena = NULL; + return IMG_ERROR_UNEXPECTED_STATE; + } + global_init = 1; + res = IMG_SUCCESS; + } + + return res; +} + +/* + * @Function vid_ra_deinit + * @Description Deinitializes the RA module + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_deinit(void) +{ + int res = IMG_ERROR_FATAL; + + if (global_init) { + if (global_pool_arena) { + res = pool_delete(global_pool_arena); + global_pool_arena = NULL; + } + if (global_pool_bt) { + res = pool_delete(global_pool_bt); + global_pool_bt = NULL; + } + global_init = 0; + res = IMG_SUCCESS; + } + return res; +} + +/* + * @Function vid_ra_create + * @Description Used to create a resource arena. + * @Input name: The name of the arena for diagnostic purposes + * @Input base_arg: The base of an initial resource span or 0 + * @Input size_arg: The size of an initial resource span or 0 + * @Input quantum: The arena allocation quantum + * @Input (*import_alloc_fxn): A resource allocation callback or NULL + * @Input (*import_free_fxn): A resource de-allocation callback or NULL + * @Input import_hdnl: Handle passed to alloc and free or NULL + * @Output arena_hndl: The handle for the arene being created, or NULL + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_create(const unsigned char * const name, + unsigned long long base_arg, + unsigned long long size_arg, + unsigned long quantum, + int (*import_alloc_fxn)(void * const import_hdnl, + unsigned long long req_sz, + unsigned long long * const actl_sz, + void ** const ref, + unsigned int alloc_flags, + unsigned long long * const base_arg), + int (*import_free_fxn)(void * const import_hdnl, + unsigned long long import_base, + void * const import_ref), + void *import_hdnl, + void **arena_hndl) +{ + struct arena *local_arena = NULL; + unsigned int idx = 0; + int res = IMG_ERROR_FATAL; + + if (!arena_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + *(arena_hndl) = NULL; + + if (global_init) { + res = pool_alloc(global_pool_arena, ((void **)&local_arena)); + if (!local_arena || res != IMG_SUCCESS) + return IMG_ERROR_UNEXPECTED_STATE; + + local_arena->name = NULL; + if (name) + local_arena->name = kstrdup((const signed char *)name, + GFP_KERNEL); + if (import_alloc_fxn) + local_arena->import_alloc_fxn = import_alloc_fxn; + else + local_arena->import_alloc_fxn = ra_request_alloc_fail; + + local_arena->import_free_fxn = import_free_fxn; + local_arena->import_hdnl = import_hdnl; + + for (idx = 0; idx < FREE_TABLE_LIMIT; idx++) + local_arena->head_free[idx] = NULL; + + local_arena->head_seg = NULL; + local_arena->tail_seg = NULL; + local_arena->quantum = quantum; + + res = vid_hash_create(MINIMUM_HASH_SIZE, + &local_arena->hash_tbl); + + if (!local_arena->hash_tbl) { + vid_hash_delete(local_arena->hash_tbl); + kfree(local_arena->name); + local_arena->name = NULL; + return IMG_ERROR_UNEXPECTED_STATE; + } + + //if (size_arg > (unsigned long long)0) { + if (size_arg > 0ULL) { + size_arg = (size_arg + quantum - 1) / quantum * quantum; + + res = ra_insert_resource(local_arena, + base_arg, + size_arg); + if (res != IMG_SUCCESS) { + vid_hash_delete(local_arena->hash_tbl); + pool_free(global_pool_arena, local_arena); + kfree(local_arena->name); + local_arena->name = NULL; + return IMG_ERROR_UNEXPECTED_STATE; + } + } + *(arena_hndl) = local_arena; + res = IMG_SUCCESS; + } + + return res; +} + +/* + * @Function vid_ra_delete + * @Description Used to delete a resource arena. All resources allocated from + * the arena must be freed before deleting the arena + * @Input arena_hndl: The handle to the arena to delete + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_delete(void * const arena_hndl) +{ + int res = IMG_ERROR_FATAL; + struct arena *local_arena = NULL; + unsigned int idx; + + if (!arena_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + if (global_init) { + local_arena = (struct arena *)arena_hndl; + kfree(local_arena->name); + local_arena->name = NULL; + for (idx = 0; idx < FREE_TABLE_LIMIT; idx++) + local_arena->head_free[idx] = NULL; + + while (local_arena->head_seg) { + struct btag *local_bt = local_arena->head_seg; + + ra_segment_list_remove(local_arena, local_bt); + } + res = vid_hash_delete(local_arena->hash_tbl); + if (res != IMG_SUCCESS) + return IMG_ERROR_UNEXPECTED_STATE; + + res = pool_free(global_pool_arena, local_arena); + if (res != IMG_SUCCESS) + return IMG_ERROR_UNEXPECTED_STATE; + } + + return res; +} + +/* + * @Function vid_ra_add + * @Description Used to add a resource span to an arena. The span must not + * overlap with any span previously added to the arena + * @Input base_arg: The base_arg of the span + * @Input size_arg: The size of the span + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_add(void * const arena_hndl, unsigned long long base_arg, unsigned long long size_arg) +{ + int res = IMG_ERROR_FATAL; + struct arena *local_arena = NULL; + + if (!arena_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + if (global_init) { + local_arena = (struct arena *)arena_hndl; + size_arg = (size_arg + local_arena->quantum - 1) / + local_arena->quantum * local_arena->quantum; + + res = ra_insert_resource(local_arena, base_arg, size_arg); + if (res != IMG_SUCCESS) + return IMG_ERROR_INVALID_PARAMETERS; + } + + return res; +} + +/* + * @Function vid_ra_alloc + * @Description Used to allocate resource from an arena + * @Input arena_hndl: The handle to the arena to create the resource + * @Input request_size: The requested size of resource segment + * @Input actl_size: The actualSize of resource segment + * @Input ref: The user reference associated with allocated resource + * span + * @Input alloc_flags: AllocationFlags influencing allocation policy + * @Input align_arg: The alignment constraint required for the allocated + * segment + * @Output base_args: The base of the allocated resource + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_alloc(void * const arena_hndl, + unsigned long long request_size, + unsigned long long * const actl_sz, + void ** const ref, + unsigned int alloc_flags, + unsigned long long alignarg, + unsigned long long * const basearg) +{ + int res = IMG_ERROR_FATAL; + struct arena *arn_ctx = NULL; + unsigned long long loc_size = request_size; + + if (!arena_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + if (global_init) { + arn_ctx = (struct arena *)arena_hndl; + loc_size = ((loc_size + arn_ctx->quantum - 1) / + arn_ctx->quantum) * arn_ctx->quantum; + + if (actl_sz) + *actl_sz = loc_size; + + /* + * If allocation failed then we might have an import source + * which can provide more resource, else we will have to fail + * the allocation to the caller. + */ + if (alloc_flags == RA_SEQUENTIAL_ALLOCATION) + res = ra_attempt_alloc_aligned(arn_ctx, + loc_size, + ref, + alignarg, + basearg); + + if (res != IMG_SUCCESS) { + void *import_ref = NULL; + unsigned long long import_base = 0ULL; + unsigned long long locimprt_reqsz = loc_size; + unsigned long long locimprt_actsz = 0ULL; + + res = arn_ctx->import_alloc_fxn(arn_ctx->import_hdnl, + locimprt_reqsz, + &locimprt_actsz, + &import_ref, + alloc_flags, + &import_base); + + if (res == IMG_SUCCESS) { + struct btag *local_bt = + ra_insert_resource_span(arn_ctx, + import_base, + locimprt_actsz); + + /* + * Successfully import more resource, create a + * span to represent it and retry the allocation + * attempt + */ + if (!local_bt) { + /* + * Insufficient resources to insert the + * newly acquired span, so free it back + */ + arn_ctx->import_free_fxn(arn_ctx->import_hdnl, + import_base, + import_ref); + return IMG_ERROR_UNEXPECTED_STATE; + } + local_bt->ref = import_ref; + if (alloc_flags == RA_SEQUENTIAL_ALLOCATION) { + res = ra_attempt_alloc_aligned(arn_ctx, + loc_size, + ref, + alignarg, + basearg); + } + } + } + } + + return res; +} + +/* + * @Function vid_ra_free + * @Description Used to free a resource segment + * @Input arena_hndl: The arena the segment was originally allocated from + * @Input base_arg: The base of the span + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_free(void * const arena_hndl, unsigned long long base_arg) +{ + int res = IMG_ERROR_FATAL; + struct arena *local_arena = NULL; + struct btag *local_bt = NULL; + unsigned long uip_res; + + if (!arena_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + if (global_init) { + local_arena = (struct arena *)arena_hndl; + + res = vid_hash_remove(local_arena->hash_tbl, + base_arg, + &uip_res); + if (res != IMG_SUCCESS) + return res; + local_bt = (struct btag *)uip_res; + + ra_free_bt(local_arena, local_bt); + } + + return res; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/ra.h b/drivers/media/platform/vxe-vxd/common/ra.h --- a/drivers/media/platform/vxe-vxd/common/ra.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/ra.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Implements generic resource allocation. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ +#ifndef _RA_H_ +#define _RA_H_ + +#define MINIMUM_HASH_SIZE (64) +#define FREE_TABLE_LIMIT (64) + +/* Defines whether sequential or random allocation is used */ +enum { + RA_SEQUENTIAL_ALLOCATION = 0, + RA_RANDOM_ALLOCATION, + RA_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Defines boundary tag type */ +enum eboundary_tag_type { + RA_BOUNDARY_TAG_TYPE_SPAN = 0, + RA_BOUNDARY_TAG_TYPE_FREE, + RA_BOUNDARY_TAG_TYPE_LIVE, + RA_BOUNDARY_TAG_TYPE_MAX, + RA_BOUNDARY_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * @Description + * Boundary tags, used to describe a resource segment + * + * @enum0: span markers + * @enum1: free resource segment + * @enum2: allocated resource segment + * @enum3: max + * @base,size: The base resource of this segment and extent of this segment + * @nxt_seg, prv_seg: doubly linked ordered list of all segments + * within the arena + * @nxt_free, prv_free: doubly linked un-ordered list of free segments + * @reference : a user reference associated with this span, user + * references are currently only provided in + * the callback mechanism + */ +struct btag { + unsigned int bt_type; + unsigned long long base; + unsigned long long size; + struct btag *nxt_seg; + struct btag *prv_seg; + struct btag *nxt_free; + struct btag *prv_free; + void *ref; +}; + +/* + * @Description + * resource allocation arena + * + * @name: arena for diagnostics output + * @quantum: allocations within this arena are quantum sized + * @max_idx: index of the last position in the psBTHeadFree table, + * with available free space + * @import_alloc_fxn: import interface, if provided + * @import_free_fxn: import interface, if provided + * @import_hdnl: import interface, if provided + * @head_free: head of list of free boundary tags for indexed by Log2 + * of the boundary tag size. Power-of-two table of free lists + * @head_seg, tail_seg : resource ordered segment list + * @ps_hash : segment address to boundary tag hash table + */ +struct arena { + unsigned char *name; + unsigned long quantum; + unsigned int max_idx; + int (*import_alloc_fxn)(void *import_hdnl, + unsigned long long requested_size, + unsigned long long *actual_size, + void **ref, + unsigned int alloc_flags, + unsigned long long *base_addr); + int (*import_free_fxn)(void *import_hdnl, + unsigned long long base, + void *ref); + void *import_hdnl; + struct btag *head_free[FREE_TABLE_LIMIT]; + struct btag *head_seg; + struct btag *tail_seg; + struct hash *hash_tbl; +}; + +/* + * @Function vid_ra_init + * @Description Initializes the RA module. Must be called before any other + * ra API function + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_initialise(void); + +/* + * @Function vid_ra_deinit + * @Description Deinitializes the RA module + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_deinit(void); + +/* + * @Function vid_ra_create + * @Description Used to create a resource arena. + * @Input name: The name of the arena for diagnostic purposes + * @Input base_arg: The base of an initial resource span or 0 + * @Input size_arg: The size of an initial resource span or 0 + * @Input quantum: The arena allocation quantum + * @Input (*import_alloc_fxn): A resource allocation callback or NULL + * @Input (*import_free_fxn): A resource de-allocation callback or NULL + * @Input import_hdnl: Handle passed to alloc and free or NULL + * @Output arena_hndl: The handle for the arene being created, or NULL + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_create(const unsigned char * const name, + unsigned long long base_arg, + unsigned long long size_arg, + unsigned long quantum, + int (*import_alloc_fxn)(void * const import_hdnl, + unsigned long long req_sz, + unsigned long long * const actl_sz, + void ** const ref, + unsigned int alloc_flags, + unsigned long long * const base_arg), + int (*import_free_fxn)(void * const import_hdnl, + unsigned long long import_base, + void * const import_ref), + void *import_hdnl, + void **arena_hndl); + +/* + * @Function vid_ra_delete + * @Description Used to delete a resource arena. All resources allocated from + * the arena must be freed before deleting the arena + * @Input arena_hndl: The handle to the arena to delete + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_delete(void * const arena_hndl); + +/* + * @Function vid_ra_add + * @Description Used to add a resource span to an arena. The span must not + * overlap with any span previously added to the arena + * @Input base_arg: The base_arg of the span + * @Input size_arg: The size of the span + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_add(void * const arena_hndl, unsigned long long base_arg, unsigned long long size_arg); + +/* + * @Function vid_ra_alloc + * @Description Used to allocate resource from an arena + * @Input arena_hndl: The handle to the arena to create the resource + * @Input request_size: The requested size of resource segment + * @Input actl_size: The actualSize of resource segment + * @Input ref: The user reference associated with allocated resource + * span + * @Input alloc_flags: AllocationFlags influencing allocation policy + * @Input align_arg: The alignment constraint required for the allocated + * segment + * @Output base_args: The base of the allocated resource + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_alloc(void * const arena_hndl, + unsigned long long request_size, + unsigned long long * const actl_sz, + void ** const ref, + unsigned int alloc_flags, + unsigned long long align_arg, + unsigned long long * const base_arg); + +/* + * @Function vid_ra_free + * @Description Used to free a resource segment + * @Input arena_hndl: The arena the segment was originally allocated from + * @Input base_arg: The base of the span + * @Return IMG_SUCCESS or an error code + * + */ +int vid_ra_free(void * const arena_hndl, unsigned long long base_arg); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/resource.c b/drivers/media/platform/vxe-vxd/common/resource.c --- a/drivers/media/platform/vxe-vxd/common/resource.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/resource.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD DEC Resource manager implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include +#include + +#include "dq.h" +#include "img_errors.h" +#include "lst.h" +#include "resource.h" + +struct resource_list_elem { + struct dq_linkage_t link; + void *item; + unsigned int id; + unsigned int *refcnt; +}; + +/* + * marks an item as used by incrementing the reference count + */ +int resource_item_use(unsigned int *refcnt) +{ + if (refcnt) + (*refcnt)++; + + return 0; +} + +/* + * returns an item by decrementing the reference count + */ +void resource_item_return(unsigned int *refcnt) +{ + if (refcnt && *refcnt > 0) + (*refcnt)--; +} + +/* + * releases an item by setting reference count to 1 (original owner) + */ +int resource_item_release(unsigned int *refcnt) +{ + if (refcnt) + *refcnt = 1; + + return 0; +} + +/* + * indicates whether an item is free to be used (no owners) + */ +int resource_item_isavailable(unsigned int *refcnt) +{ + if (refcnt) + return (*refcnt == 0) ? 1 : 0; + else + return 0; +} + +/* + * adds an item (and associated id) to a resource list + */ +int resource_list_add_img(struct lst_t *list, void *item, unsigned int id, unsigned int *refcnt) +{ + struct resource_list_elem *listelem = NULL; + int bfound = 0; + unsigned int result = 0; + + if (!list || !item) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* + * Decrement the reference count on the item + * to signal that the owner has relinquished it. + */ + resource_item_return(refcnt); + + /* + * Determine whether this buffer is already in the list. + */ + listelem = lst_first(list); + while (listelem) { + if (listelem->item == item) { + bfound = 1; + break; + } + + listelem = lst_next(listelem); + } + + if (!bfound) { + /* + * allocate the image buffer list element structure. + */ + listelem = kmalloc(sizeof(*(listelem)), GFP_KERNEL); + if (!listelem) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(listelem, 0, sizeof(*(listelem))); + + /* + * setup the list element. + */ + listelem->item = item; + listelem->id = id; + listelem->refcnt = refcnt; + + /* + * add the element to the list. + */ + lst_add(list, (void *)listelem); + } + + return 0; + +error: + return result; +} + +/* + * obtains pointer to item at head of resource list + */ +void *resource_list_pickhead(struct lst_t *list) +{ + struct resource_list_elem *listelem = NULL; + void *item = NULL; + + if (!list) + goto error; + /* + * peek the head item of the list. + */ + listelem = lst_first(list); + if (listelem) + item = listelem->item; + +error: + return item; +} + +/* + * removes item from resource list + */ +int resource_list_remove(struct lst_t *list, void *item) +{ + struct resource_list_elem *listelem = NULL; + unsigned int result = 0; + + if (!list || !item) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* + * find the specified item in the list. + */ + listelem = lst_first(list); + while (listelem) { + if (listelem->item == item) { + if (*listelem->refcnt != 0) + pr_warn("item remove from list still in use\n"); + + /* + * Remove the item from the list. + */ + lst_remove(list, listelem); + /* + * Free the stream unit queue element. + */ + kfree(listelem); + listelem = NULL; + return 0; + } + + listelem = lst_next(listelem); + } + +#if defined(DEBUG_DECODER_DRIVER) || defined(DEBUG_ENCODER_DRIVER) + pr_info("item could not be located to remove from RESOURCE list\n"); +#endif + + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + +error: + return result; +} + +/* + * resource_list_removehead - removes item at head of resource list + * @list: head of resource list + */ +void *resource_list_removehead(struct lst_t *list) +{ + struct resource_list_elem *listelem = NULL; + void *item = NULL; + + if (!list) + goto error; + + /* + * peek the head item of the list. + */ + listelem = lst_removehead(list); + if (listelem) { + item = listelem->item; + kfree(listelem); + listelem = NULL; + } + +error: + return item; +} + +/* + * removes next available item from resource list. + * item is freed if no longer used + */ +int resource_list_remove_nextavail(struct lst_t *list, + resource_pfn_freeitem fn_freeitem, + void *free_cb_param) +{ + struct resource_list_elem *listelem = NULL; + unsigned int result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + + if (!list) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* + * find the next unused item in the list. + */ + listelem = lst_first(list); + while (listelem) { + if (resource_item_isavailable(listelem->refcnt)) { + resource_item_return(listelem->refcnt); + + if (*listelem->refcnt == 0) { + if (fn_freeitem) + fn_freeitem(listelem->item, free_cb_param); + else + kfree(listelem->item); + + listelem->item = NULL; + } + + /* + * get the next element from the list. + */ + lst_remove(list, listelem); + + /* + * free the buffer list element. + */ + kfree(listelem); + listelem = NULL; + + result = 0; + break; + } + + listelem = lst_next(listelem); + } + + if (result == IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE) + pr_debug("failed to locate an available resource element to remove\n"); + +error: + return result; +} + +/* + * obtains pointer to an available item from the resource list + */ +void *resource_list_get_avail(struct lst_t *list) +{ + struct resource_list_elem *listelem = NULL; + void *item = NULL; + + if (!list) + goto error; + + /* + * find the next unused item in the list. + */ + listelem = lst_first(list); + while (listelem) { + if (resource_item_isavailable(listelem->refcnt)) { + resource_item_use(listelem->refcnt); + item = listelem->item; + break; + } + listelem = lst_next(listelem); + } + +error: + return item; +} + +/* + * signal duplicate use of specified item with resource list + */ +void *resource_list_reuseitem(struct lst_t *list, void *item) +{ + struct resource_list_elem *listelem = NULL; + void *ret_item = NULL; + + if (!list || !item) + goto error; + + /* + * find the specified item in the list. + */ + listelem = lst_first(list); + + while (listelem) { + if (listelem->item == item) { + resource_item_use(listelem->refcnt); + ret_item = item; + break; + } + + listelem = lst_next(listelem); + } + +error: + return ret_item; +} + +/* + * obtain pointer to item from resource list with id + */ +void *resource_list_getbyid(struct lst_t *list, unsigned int id) +{ + struct resource_list_elem *listelem = NULL; + void *item = NULL; + + if (!list) + goto error; + + /* + * find the next unused buffer in the list. + */ + listelem = lst_first(list); + while (listelem) { + if (listelem->id == id) { + resource_item_use(listelem->refcnt); + item = listelem->item; + break; + } + + listelem = lst_next(listelem); + } + +error: + return item; +} + +/* + * obtain the number of available (unused) items within list. + */ +int resource_list_getnumavail(struct lst_t *list) +{ + struct resource_list_elem *listelem = NULL; + unsigned int num_items = 0; + + if (!list) + goto error; + + /* + * find the next unused buffer in the list. + */ + listelem = lst_first(list); + while (listelem) { + if (resource_item_isavailable(listelem->refcnt)) + num_items++; + + listelem = lst_next(listelem); + } + +error: + return num_items; +} + +/* + * Obtain the number of items within list + */ +int resource_list_getnum(struct lst_t *list) +{ + struct resource_list_elem *listelem = NULL; + unsigned int num_items = 0; + + if (!list) + goto error; + + /* + * find the next unused buffer in the list. + */ + listelem = lst_first(list); + while (listelem) { + num_items++; + listelem = lst_next(listelem); + } + +error: + return num_items; +} + +/* + * replaces an item (of specified id) within a resource list + */ +int resource_list_replace(struct lst_t *list, void *item, unsigned int id, unsigned int *refcnt, + resource_pfn_freeitem fn_freeitem, + void *free_cb_param) +{ + struct resource_list_elem *listelem = NULL; + unsigned int result = 0; + + if (!list || !item) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* + * determine whether this sequence header is already in the list + */ + listelem = lst_first(list); + while (listelem) { + if (listelem->id == id) { + resource_item_return(listelem->refcnt); + if (*listelem->refcnt == 0) { + if (fn_freeitem) + fn_freeitem(listelem->item, + free_cb_param); + else + kfree(listelem->item); + listelem->item = NULL; + } + + lst_remove(list, listelem); + break; + } + + listelem = lst_next(listelem); + } + + if (!listelem) { + /* + * Allocate the sequence header list element structure. + */ + listelem = kmalloc(sizeof(*(listelem)), GFP_KERNEL); + if (!listelem) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(listelem, 0, sizeof(*(listelem))); + } + + /* + * setup the sequence header list element. + */ + resource_item_use(refcnt); + + listelem->item = item; + listelem->id = id; + listelem->refcnt = refcnt; + + /* + * Add the sequence header list element to the sequence header list. + */ + lst_add(list, (void *)listelem); + + return 0; + +error: + return result; +} + +/* + * removes all items from a resource list. + */ +int resource_list_empty(struct lst_t *list, unsigned int release_item, + resource_pfn_freeitem fn_freeitem, + void *free_cb_param) +{ + struct resource_list_elem *listelem = NULL; + unsigned int result = 0; + + if (!list) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* + * remove all the buffer list elements from the image buffer list + */ + listelem = lst_removehead(list); + while (listelem) { + if (release_item) { + resource_item_release(listelem->refcnt); + } else { + /* + * Return and free. + */ + resource_item_return(listelem->refcnt); + + if (!listelem->refcnt || *listelem->refcnt == 0) { + if (fn_freeitem) + fn_freeitem(listelem->item, + free_cb_param); + else + kfree(listelem->item); + listelem->item = NULL; + } + } + + /* + * free the buffer list element. + */ + kfree(listelem); + listelem = NULL; + + /* + * Get the next element from the list. + */ + listelem = lst_removehead(list); + } + + return 0; + +error: + return result; +} + +/* + * obtain the number of pictures within list + */ +int resource_getnumpict(struct lst_t *list) +{ + struct resource_list_elem *listelem = NULL; + unsigned int num_pict = 0; + + if (!list) + goto error; + + /* + * find the next unused buffer in the list. + */ + listelem = lst_first(list); + while (listelem) { + num_pict++; + listelem = lst_next(listelem); + } + +error: + return num_pict; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/resource.h b/drivers/media/platform/vxe-vxd/common/resource.h --- a/drivers/media/platform/vxe-vxd/common/resource.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/resource.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC SYSDEV and UI Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef _VXD_RESOURCE_H +#define _VXD_RESOURCE_H + +typedef int (*resource_pfn_freeitem)(void *item, void *free_cb_param); + +int resource_item_use(unsigned int *refcnt); + +void resource_item_return(unsigned int *refcnt); + +int resource_item_release(unsigned int *refcnt); + +int resource_item_isavailable(unsigned int *refcnt); + +int resource_list_add_img(struct lst_t *list, void *item, unsigned int id, unsigned int *refcnt); + +void *resource_list_pickhead(struct lst_t *list); + +int resource_list_remove(struct lst_t *list, void *item); + +/** + * resource_list_removehead - removes item at head of resource list + * @list: head of resource list + */ + +void *resource_list_removehead(struct lst_t *list); + +int resource_list_remove_nextavail(struct lst_t *list, + resource_pfn_freeitem fn_freeitem, + void *free_cb_param); + +void *resource_list_get_avail(struct lst_t *list); + +void *resource_list_reuseitem(struct lst_t *list, void *item); + +void *resource_list_getbyid(struct lst_t *list, unsigned int id); + +int resource_list_getnumavail(struct lst_t *list); + +int resource_list_getnum(struct lst_t *list); + +int resource_list_replace(struct lst_t *list, void *item, unsigned int id, unsigned int *refcnt, + resource_pfn_freeitem fn_freeitem, + void *free_cb_param); + +int resource_list_empty(struct lst_t *list, unsigned int release_item, + resource_pfn_freeitem fn_freeitem, + void *free_cb_param); + +int resource_getnumpict(struct lst_t *list); + +#endif /* _VXD_RESOURCE_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/rman_api.c b/drivers/media/platform/vxe-vxd/common/rman_api.c --- a/drivers/media/platform/vxe-vxd/common/rman_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/rman_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This component is used to track decoder resources, + * and share them across other components. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dq.h" +#include "idgen_api.h" +#include "rman_api.h" +#include "img_errors.h" + +/* + * The following macros are used to build/decompose the composite resource Id + * made up from the bucket index + 1 and the allocated resource Id. + */ +#define RMAN_CRESID_BUCKET_INDEX_BITS (8) +#define RMAN_CRESID_RES_ID_BITS (32 - RMAN_CRESID_BUCKET_INDEX_BITS) +#define RMAN_CRESID_MAX_RES_ID ((1 << RMAN_CRESID_RES_ID_BITS) - 1) +#define RMAN_CRESID_RES_ID_MASK (RMAN_CRESID_MAX_RES_ID) +#define RMAN_CRESID_BUCKET_SHIFT (RMAN_CRESID_RES_ID_BITS) +#define RMAN_CRESID_MAX_BUCKET_INDEX \ + ((1 << RMAN_CRESID_BUCKET_INDEX_BITS) - 1) + +#define RMAN_MAX_ID 4096 +#define RMAN_ID_BLOCKSIZE 256 + +/* global state variable */ +static unsigned char inited; +static struct rman_bucket *bucket_array[RMAN_CRESID_MAX_BUCKET_INDEX] = {0}; +static struct rman_bucket *global_res_bucket; +static struct rman_bucket *shared_res_bucket; +static struct mutex *shared_res_mutex_handle; +static struct mutex *global_mutex; + +/* + * This structure contains the bucket information. + */ +struct rman_bucket { + void **link; /* to be part of single linked list */ + struct dq_linkage_t res_list; + unsigned int bucket_idx; + void *id_gen; + unsigned int res_cnt; +}; + +/* + * This structure contains the resource details for a resource registered with + * the resource manager. + */ +struct rman_res { + struct dq_linkage_t link; /* to be part of double linked list */ + struct rman_bucket *bucket; + unsigned int type_id; + rman_fn_free fn_free; + void *param; + unsigned int res_id; + struct mutex *mutex_handle; /*resource mutex */ + unsigned char *res_name; + struct rman_res *shared_res; + unsigned int ref_cnt; +}; + +/* + * initialization + */ +int rman_initialise(void) +{ + unsigned int ret; + + if (!inited) { + shared_res_mutex_handle = kzalloc(sizeof(*shared_res_mutex_handle), GFP_KERNEL); + if (!shared_res_mutex_handle) + return IMG_ERROR_OUT_OF_MEMORY; + + mutex_init(shared_res_mutex_handle); + + /* Set initialised flag */ + inited = TRUE; + + /* Create the global resource bucket */ + ret = rman_create_bucket((void **)&global_res_bucket); + IMG_DBG_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Create the shared resource bucket */ + ret = rman_create_bucket((void **)&shared_res_bucket); + IMG_DBG_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + global_mutex = kzalloc(sizeof(*global_mutex), GFP_KERNEL); + if (!global_mutex) + return IMG_ERROR_OUT_OF_MEMORY; + + mutex_init(global_mutex); + } + return IMG_SUCCESS; +} + +/* + * deinitialization + */ +void rman_deinitialise(void) +{ + unsigned int i; + + if (inited) { + /* Destroy the golbal resource bucket */ + rman_destroy_bucket(global_res_bucket); + + /* Destroy the shared resource bucket */ + rman_destroy_bucket(shared_res_bucket); + + /* Make sure we destroy the mutex after destroying the bucket */ + mutex_destroy(global_mutex); + kfree(global_mutex); + global_mutex = NULL; + + /* Destroy mutex */ + mutex_destroy(shared_res_mutex_handle); + kfree(shared_res_mutex_handle); + shared_res_mutex_handle = NULL; + + /* Check all buckets destroyed */ + for (i = 0; i < RMAN_CRESID_MAX_BUCKET_INDEX; i++) + IMG_DBG_ASSERT(!bucket_array[i]); + + /* Reset initialised flag */ + inited = FALSE; + } +} + +int rman_create_bucket(void **res_bucket_handle) +{ + struct rman_bucket *bucket; + unsigned int i; + int ret; + + IMG_DBG_ASSERT(inited); + + /* Allocate a bucket structure */ + bucket = kzalloc(sizeof(*bucket), GFP_KERNEL); + IMG_DBG_ASSERT(bucket); + if (!bucket) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Initialise the resource list */ + dq_init(&bucket->res_list); + + /* Then start allocating resource ids at the first */ + ret = idgen_createcontext(RMAN_MAX_ID, RMAN_ID_BLOCKSIZE, FALSE, + &bucket->id_gen); + if (ret != IMG_SUCCESS) { + kfree(bucket); + IMG_DBG_ASSERT("failed to create IDGEN context" == NULL); + return ret; + } + + /* Locate free bucket index within the table */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + for (i = 0; i < RMAN_CRESID_MAX_BUCKET_INDEX; i++) { + if (!bucket_array[i]) + break; + } + if (i >= RMAN_CRESID_MAX_BUCKET_INDEX) { + mutex_unlock(shared_res_mutex_handle); + idgen_destroycontext(bucket->id_gen); + kfree(bucket); + IMG_DBG_ASSERT("No free buckets left" == NULL); + return IMG_ERROR_GENERIC_FAILURE; + } + + /* Allocate bucket index */ + bucket->bucket_idx = i; + bucket_array[i] = bucket; + + mutex_unlock(shared_res_mutex_handle); + + /* Return the bucket handle */ + *res_bucket_handle = bucket; + + return IMG_SUCCESS; +} + +void rman_destroy_bucket(void *res_bucket_handle) +{ + struct rman_bucket *bucket = (struct rman_bucket *)res_bucket_handle; + + IMG_DBG_ASSERT(inited); + + IMG_DBG_ASSERT(bucket); + if (!bucket) + return; + + IMG_DBG_ASSERT(bucket->bucket_idx < RMAN_CRESID_MAX_BUCKET_INDEX); + IMG_DBG_ASSERT(bucket_array[bucket->bucket_idx]); + + /* Free all resources from the bucket */ + rman_free_resources(res_bucket_handle, RMAN_TYPE_P1); + rman_free_resources(res_bucket_handle, RMAN_TYPE_P2); + rman_free_resources(res_bucket_handle, RMAN_TYPE_P3); + rman_free_resources(res_bucket_handle, RMAN_ALL_TYPES); + + /* free sticky resources last: other resources are dependent on them */ + rman_free_resources(res_bucket_handle, RMAN_STICKY); + /* Use proper locking around global buckets. */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + + /* Free from array of bucket pointers */ + bucket_array[bucket->bucket_idx] = NULL; + + mutex_unlock(shared_res_mutex_handle); + + /* Free the bucket itself */ + idgen_destroycontext(bucket->id_gen); + kfree(bucket); +} + +void *rman_get_global_bucket(void) +{ + IMG_DBG_ASSERT(inited); + IMG_DBG_ASSERT(global_res_bucket); + + /* Return the handle of the global resource bucket */ + return global_res_bucket; +} + +int rman_register_resource(void *res_bucket_handle, unsigned int type_id, + rman_fn_free fnfree, void *param, + void **res_handle, unsigned int *res_id) +{ + struct rman_bucket *bucket = (struct rman_bucket *)res_bucket_handle; + struct rman_res *res; + int ret; + + IMG_DBG_ASSERT(inited); + IMG_DBG_ASSERT(type_id != RMAN_ALL_TYPES); + + IMG_DBG_ASSERT(res_bucket_handle); + if (!res_bucket_handle) + return IMG_ERROR_GENERIC_FAILURE; + + /* Allocate a resource structure */ + res = kzalloc(sizeof(*res), GFP_KERNEL); + IMG_DBG_ASSERT(res); + if (!res) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Fill in the resource structure */ + res->bucket = bucket; + res->type_id = type_id; + res->fn_free = fnfree; + res->param = param; + + /* Allocate resource Id */ + mutex_lock_nested(global_mutex, SUBCLASS_RMAN); + ret = idgen_allocid(bucket->id_gen, res, &res->res_id); + mutex_unlock(global_mutex); + if (ret != IMG_SUCCESS) { + IMG_DBG_ASSERT("failed to allocate RMAN id" == NULL); + return ret; + } + IMG_DBG_ASSERT(res->res_id <= RMAN_CRESID_MAX_RES_ID); + + /* add this resource to the bucket */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + dq_addtail(&bucket->res_list, res); + + /* Update count of resources */ + bucket->res_cnt++; + mutex_unlock(shared_res_mutex_handle); + + /* If resource handle required */ + if (res_handle) + *res_handle = res; + + /* If resource id required */ + if (res_id) + *res_id = rman_get_resource_id(res); + + return IMG_SUCCESS; +} + +unsigned int rman_get_resource_id(void *res_handle) +{ + struct rman_res *res = res_handle; + unsigned int ext_res_id; + + IMG_DBG_ASSERT(res_handle); + if (!res_handle) + return 0; + + IMG_DBG_ASSERT(res->res_id <= RMAN_CRESID_MAX_RES_ID); + IMG_DBG_ASSERT(res->bucket->bucket_idx < RMAN_CRESID_MAX_BUCKET_INDEX); + if (res->bucket->bucket_idx >= RMAN_CRESID_MAX_BUCKET_INDEX) + return 0; + + ext_res_id = (((res->bucket->bucket_idx + 1) << + RMAN_CRESID_BUCKET_SHIFT) | res->res_id); + + return ext_res_id; +} + +static void *rman_getresource_int(void *res_bucket_handle, unsigned int res_id, + unsigned int type_id, void **res_handle) +{ + struct rman_bucket *bucket = (struct rman_bucket *)res_bucket_handle; + struct rman_res *res; + int ret; + + IMG_DBG_ASSERT(res_id <= RMAN_CRESID_MAX_RES_ID); + + /* Loop over the resources in this bucket till we find the required id */ + mutex_lock_nested(global_mutex, SUBCLASS_RMAN); + ret = idgen_gethandle(bucket->id_gen, res_id, (void **)&res); + mutex_unlock(global_mutex); + if (ret != IMG_SUCCESS) { + IMG_DBG_ASSERT("failed to get RMAN resource" == NULL); + return NULL; + } + + /* If the resource handle is required */ + if (res_handle) + *res_handle = res; /* Return it */ + + /* If the resource was not found */ + IMG_DBG_ASSERT(res); + IMG_DBG_ASSERT((void *)res != &bucket->res_list); + if (!res || ((void *)res == &bucket->res_list)) + return NULL; + + /* Cross check the type */ + IMG_DBG_ASSERT(type_id == res->type_id); + + /* Return the resource. */ + return res->param; +} + +int rman_get_resource(unsigned int res_id, unsigned int type_id, void **param, + void **res_handle) +{ + unsigned int bucket_idx = (res_id >> RMAN_CRESID_BUCKET_SHIFT) - 1; + unsigned int int_res_id = (res_id & RMAN_CRESID_RES_ID_MASK); + void *local_param; + + IMG_DBG_ASSERT(bucket_idx < RMAN_CRESID_MAX_BUCKET_INDEX); + if (bucket_idx >= RMAN_CRESID_MAX_BUCKET_INDEX) + return IMG_ERROR_INVALID_ID; /* Happens when bucket_idx == 0 */ + + IMG_DBG_ASSERT(bucket_array[bucket_idx]); + if (!bucket_array[bucket_idx]) + return IMG_ERROR_INVALID_ID; + + local_param = rman_getresource_int(bucket_array[bucket_idx], + int_res_id, type_id, + res_handle); + + /* If we didn't find the resource */ + if (!local_param) + return IMG_ERROR_INVALID_ID; + + /* Return the resource */ + if (param) + *param = local_param; + + return IMG_SUCCESS; +} + +int rman_get_named_resource(unsigned char *res_name, rman_fn_alloc fn_alloc, + void *alloc_info, void *res_bucket_handle, + unsigned int type_id, rman_fn_free fn_free, + void **param, void **res_handle, unsigned int *res_id) +{ + struct rman_bucket *bucket = res_bucket_handle; + struct rman_res *res; + unsigned int ret; + void *local_param; + unsigned char found = FALSE; + + IMG_DBG_ASSERT(inited); + + IMG_DBG_ASSERT(res_bucket_handle); + if (!res_bucket_handle) + return IMG_ERROR_GENERIC_FAILURE; + + /* Lock the shared resources */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + res = (struct rman_res *)dq_first(&bucket->res_list); + while (res && ((void *)res != &bucket->res_list)) { + /* If resource already in the shared list */ + if (res->res_name && (strcmp(res_name, + res->res_name) == 0)) { + IMG_DBG_ASSERT(res->fn_free == fn_free); + found = TRUE; + break; + } + + /* Move to next resource */ + res = (struct rman_res *)dq_next(res); + } + mutex_unlock(shared_res_mutex_handle); + + /* If the named resource was not found */ + if (!found) { + /* Allocate the resource */ + ret = fn_alloc(alloc_info, &local_param); + IMG_DBG_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Register the named resource */ + ret = rman_register_resource(res_bucket_handle, type_id, + fn_free, local_param, + (void **)&res, NULL); + IMG_DBG_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + res->res_name = res_name; + mutex_unlock(shared_res_mutex_handle); + } + + /* Return the pvParam value */ + *param = res->param; + + /* If resource handle required */ + if (res_handle) + *res_handle = res; + + /* If resource id required */ + if (res_id) + *res_id = rman_get_resource_id(res); + + /* Exit */ + return IMG_SUCCESS; +} + +static void rman_free_resource_int(struct rman_res *res) +{ + struct rman_bucket *bucket = res->bucket; + + /* Remove the resource from the active list */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + + /* Remove from list */ + dq_remove(res); + + /* Update count of resources */ + bucket->res_cnt--; + + mutex_unlock(shared_res_mutex_handle); + + /* If mutex associated with the resource */ + if (res->mutex_handle) { + /* Destroy mutex */ + mutex_destroy(res->mutex_handle); + kfree(res->mutex_handle); + res->mutex_handle = NULL; + } + + /* If this resource is not already shared */ + if (res->shared_res) { + /* Lock the shared resources */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + + /* Update the reference count */ + IMG_DBG_ASSERT(res->shared_res->ref_cnt != 0); + res->shared_res->ref_cnt--; + + /* If this is the last free for the shared resource */ + if (res->shared_res->ref_cnt == 0) + /* Free the shared resource */ + rman_free_resource_int(res->shared_res); + + /* UnLock the shared resources */ + mutex_unlock(shared_res_mutex_handle); + } else { + /* If there is a free callback function. */ + if (res->fn_free) + /* Call resource free callback */ + res->fn_free(res->param); + } + + /* If the resource has a name then free it */ + kfree(res->res_name); + + /* Free the resource ID. */ + mutex_lock_nested(global_mutex, SUBCLASS_RMAN); + idgen_freeid(bucket->id_gen, res->res_id); + mutex_unlock(global_mutex); + + /* Free a resource structure */ + kfree(res); +} + +void rman_free_resource(void *res_handle) +{ + struct rman_res *res; + + IMG_DBG_ASSERT(inited); + + IMG_DBG_ASSERT(res_handle); + if (!res_handle) + return; + + /* Get access to the resource structure */ + res = (struct rman_res *)res_handle; + + /* Free resource */ + rman_free_resource_int(res); +} + +void rman_lock_resource(void *res_handle) +{ + struct rman_res *res; + + IMG_DBG_ASSERT(inited); + + IMG_DBG_ASSERT(res_handle); + if (!res_handle) + return; + + /* Get access to the resource structure */ + res = (struct rman_res *)res_handle; + + /* If this is a shared resource */ + if (res->shared_res) + /* We need to lock/unlock the underlying shared resource */ + res = res->shared_res; + + /* If no mutex associated with this resource */ + if (!res->mutex_handle) { + /* Create one */ + + res->mutex_handle = kzalloc(sizeof(*res->mutex_handle), GFP_KERNEL); + if (!res->mutex_handle) + return; + + mutex_init(res->mutex_handle); + } + + /* lock it */ + mutex_lock(res->mutex_handle); +} + +void rman_unlock_resource(void *res_handle) +{ + struct rman_res *res; + + IMG_DBG_ASSERT(inited); + + IMG_DBG_ASSERT(res_handle); + if (!res_handle) + return; + + /* Get access to the resource structure */ + res = (struct rman_res *)res_handle; + + /* If this is a shared resource */ + if (res->shared_res) + /* We need to lock/unlock the underlying shared resource */ + res = res->shared_res; + + IMG_DBG_ASSERT(res->mutex_handle); + + /* Unlock mutex */ + mutex_unlock(res->mutex_handle); +} + +void rman_free_resources(void *res_bucket_handle, unsigned int type_id) +{ + struct rman_bucket *bucket = (struct rman_bucket *)res_bucket_handle; + struct rman_res *res; + + IMG_DBG_ASSERT(inited); + + IMG_DBG_ASSERT(res_bucket_handle); + if (!res_bucket_handle) + return; + + /* Scan the active list looking for the resources to be freed */ + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + res = (struct rman_res *)dq_first(&bucket->res_list); + while ((res) && ((void *)res != &bucket->res_list)) { + /* If this is resource is to be removed */ + if ((type_id == RMAN_ALL_TYPES && + res->type_id != RMAN_STICKY) || + res->type_id == type_id) { + /* Yes, remove it, Free current resource */ + mutex_unlock(shared_res_mutex_handle); + rman_free_resource_int(res); + mutex_lock_nested(shared_res_mutex_handle, SUBCLASS_RMAN); + + /* Restart from the beginning of the list */ + res = (struct rman_res *)dq_first(&bucket->res_list); + } else { + /* Move to next resource */ + res = (struct rman_res *)lst_next(res); + } + } + mutex_unlock(shared_res_mutex_handle); +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/rman_api.h b/drivers/media/platform/vxe-vxd/common/rman_api.h --- a/drivers/media/platform/vxe-vxd/common/rman_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/rman_api.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This component is used to track decoder resources, + * and share them across other components. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef __RMAN_API_H__ +#define __RMAN_API_H__ + +#include + +#include "img_errors.h" +#include "lst.h" + +#define RMAN_ALL_TYPES (0xFFFFFFFF) +#define RMAN_TYPE_P1 (0xFFFFFFFE) +#define RMAN_TYPE_P2 (0xFFFFFFFE) +#define RMAN_TYPE_P3 (0xFFFFFFFE) +#define RMAN_STICKY (0xFFFFFFFD) + +int rman_initialise(void); + +void rman_deinitialise(void); + +int rman_create_bucket(void **res_handle); + +void rman_destroy_bucket(void *res_handle); + +void *rman_get_global_bucket(void); + +typedef void (*rman_fn_free) (void *param); + +int rman_register_resource(void *res_handle, unsigned int type_id, rman_fn_free fn_free, + void *param, void **res_handle_ptr, + unsigned int *res_id); + +typedef int (*rman_fn_alloc) (void *alloc_info, void **param); + +int rman_get_named_resource(unsigned char *res_name, rman_fn_alloc fn_alloc, + void *alloc_info, void *res_bucket_handle, + unsigned int type_id, rman_fn_free fn_free, + void **param, void **res_handle, unsigned int *res_id); + +unsigned int rman_get_resource_id(void *res_handle); + +int rman_get_resource(unsigned int res_id, unsigned int type_id, void **param, + void **res_handle); + +void rman_free_resource(void *res_handle); + +void rman_lock_resource(void *res_handle); + +void rman_unlock_resource(void *res_hanle); + +void rman_free_resources(void *res_bucket_handle, unsigned int type_id); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/talmmu_api.c b/drivers/media/platform/vxe-vxd/common/talmmu_api.c --- a/drivers/media/platform/vxe-vxd/common/talmmu_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/talmmu_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TAL MMU Extensions. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ +#include +#include +#include +#include +#include +#include +#include + +#include "img_errors.h" +#include "lst.h" +#include "talmmu_api.h" + +static int global_init; +static struct lst_t gl_dmtmpl_lst = {0}; +static struct mutex *global_lock; + +static int talmmu_devmem_free(void *mem_hndl) +{ + struct talmmu_memory *mem = mem_hndl; + struct talmmu_devmem_heap *mem_heap; + + if (!mem_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + mem_heap = mem->devmem_heap; + + if (!mem->ext_dev_virtaddr) + addr_cx_free(&mem_heap->ctx, "", mem->dev_virtoffset); + + mutex_lock_nested(global_lock, SUBCLASS_TALMMU); + + lst_remove(&mem_heap->memory_list, mem); + + mutex_unlock(global_lock); + + kfree(mem); + + return IMG_SUCCESS; +} + +/* + * talmmu_devmem_heap_empty - talmmu_devmem_heap_empty + * @devmem_heap_hndl: device memory heap handle + * + * This function is used for emptying the device memory heap list + */ +int talmmu_devmem_heap_empty(void *devmem_heap_hndl) +{ + struct talmmu_devmem_heap *devmem_heap = devmem_heap_hndl; + + if (!devmem_heap) + return IMG_ERROR_INVALID_PARAMETERS; + + while (!lst_empty(&devmem_heap->memory_list)) + talmmu_devmem_free(lst_first(&devmem_heap->memory_list)); + + addr_cx_deinitialise(&devmem_heap->ctx); + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_devmem_heap_destroy + * + * @Description This function is used for freeing the device memory heap + * + * @Input + * + * @Output + * + * @Return IMG_SUCCESS or an error code + * + */ +static void talmmu_devmem_heap_destroy(void *devmem_heap_hndl) +{ + struct talmmu_devmem_heap *devmem_heap = devmem_heap_hndl; + + talmmu_devmem_heap_empty(devmem_heap_hndl); + kfree(devmem_heap); +} + +/* + * @Function talmmu_init + * + * @Description This function is used to initialize the TALMMU component. + * + * @Input None. + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_init(void) +{ + if (!global_init) { + /* If no mutex associated with this resource */ + if (!global_lock) { + /* Create one */ + global_lock = kzalloc(sizeof(*global_lock), GFP_KERNEL); + if (!global_lock) + return IMG_ERROR_OUT_OF_MEMORY; + + mutex_init(global_lock); + } + + lst_init(&gl_dmtmpl_lst); + global_init = 1; + } + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_deinit + * + * @Description This function is used to de-initialize the TALMMU component. + * + * @Input None. + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_deinit(void) +{ + struct talmmu_dm_tmpl *t; + + if (global_init) { + while (!lst_empty(&gl_dmtmpl_lst)) { + t = (struct talmmu_dm_tmpl *)lst_first(&gl_dmtmpl_lst); + talmmu_devmem_template_destroy((void *)t); + } + mutex_destroy(global_lock); + kfree(global_lock); + global_lock = NULL; + global_init = 0; + } + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_devmem_template_create + * + * @Description This function is used to create a device memory template + * + * @Input devmem_info: A pointer to a talmmu_devmem_info structure. + * + * @Output devmem_template_hndl: A pointer used to return the template + * handle + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_template_create(struct talmmu_devmem_info *devmem_info, + void **devmem_template_hndl) +{ + struct talmmu_dm_tmpl *devmem_template; + struct talmmu_dm_tmpl *tmp_devmem_template; + + if (!devmem_info) + return IMG_ERROR_INVALID_PARAMETERS; + + devmem_template = kzalloc(sizeof(*devmem_template), GFP_KERNEL); + if (!devmem_template) + return IMG_ERROR_OUT_OF_MEMORY; + + devmem_template->devmem_info = *devmem_info; + + lst_init(&devmem_template->devmem_ctx_list); + + mutex_lock_nested(global_lock, SUBCLASS_TALMMU); + + tmp_devmem_template = lst_first(&gl_dmtmpl_lst); + while (tmp_devmem_template) + tmp_devmem_template = lst_next(tmp_devmem_template); + + devmem_template->page_num_shift = 12; + devmem_template->byte_in_pagemask = 0xFFF; + devmem_template->heap_alignment = 0x400000; + devmem_template->pagetable_entries_perpage = + (devmem_template->devmem_info.page_size / sizeof(unsigned int)); + devmem_template->pagetable_num_shift = 10; + devmem_template->index_in_pagetable_mask = 0x3FF; + devmem_template->pagedir_num_shift = 22; + + lst_add(&gl_dmtmpl_lst, devmem_template); + + mutex_unlock(global_lock); + + *devmem_template_hndl = devmem_template; + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_devmem_template_destroy + * + * @Description This function is used to obtain the template from the list and + * destroy + * + * @Input devmem_tmplt_hndl: Device memory template handle + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_template_destroy(void *devmem_tmplt_hndl) +{ + struct talmmu_dm_tmpl *dm_tmpl = devmem_tmplt_hndl; + unsigned int i; + + if (!devmem_tmplt_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + while (!lst_empty(&dm_tmpl->devmem_ctx_list)) + talmmu_devmem_ctx_destroy(lst_first(&dm_tmpl->devmem_ctx_list)); + + for (i = 0; i < dm_tmpl->num_heaps; i++) + talmmu_devmem_heap_destroy(dm_tmpl->devmem_heap[i]); + + mutex_lock_nested(global_lock, SUBCLASS_TALMMU); + + lst_remove(&gl_dmtmpl_lst, dm_tmpl); + + mutex_unlock(global_lock); + + kfree(dm_tmpl); + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_create_heap + * + * @Description This function is used to create a device memory heap + * + * @Input + * + * @Output + * + * @Return IMG_SUCCESS or an error code + * + */ +static int talmmu_create_heap(void *devmem_tmplt_hndl, + struct talmmu_heap_info *heap_info_arg, + unsigned char isfull, + struct talmmu_devmem_heap **devmem_heap_arg) +{ + struct talmmu_dm_tmpl *devmem_template = devmem_tmplt_hndl; + struct talmmu_devmem_heap *devmem_heap; + + /* Allocating memory for device memory heap */ + devmem_heap = kzalloc(sizeof(*devmem_heap), GFP_KERNEL); + if (!devmem_heap) + return IMG_ERROR_OUT_OF_MEMORY; + + /* + * Update the device memory heap structure members + * Update the device memory template + */ + devmem_heap->devmem_template = devmem_template; + /* Update the device memory heap information */ + devmem_heap->heap_info = *heap_info_arg; + + /* Initialize the device memory heap list */ + lst_init(&devmem_heap->memory_list); + + /* If full structure required */ + if (isfull) { + addr_cx_initialise(&devmem_heap->ctx); + devmem_heap->regions.base_addr = 0; + devmem_heap->regions.size = devmem_heap->heap_info.size; + addr_cx_define_mem_region(&devmem_heap->ctx, + &devmem_heap->regions); + } + + *devmem_heap_arg = devmem_heap; + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_devmem_heap_add + * + * @Description This function is for creating and adding the heap to the + * device memory template + * + * @Input devmem_tmplt_hndl: device memory template handle + * + * @Input heap_info_arg: pointer to the heap info structure + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_heap_add(void *devmem_tmplt_hndl, + struct talmmu_heap_info *heap_info_arg) +{ + struct talmmu_dm_tmpl *devmem_template = devmem_tmplt_hndl; + struct talmmu_devmem_heap *devmem_heap; + unsigned int res; + + if (!devmem_tmplt_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!heap_info_arg) + return IMG_ERROR_INVALID_PARAMETERS; + + res = talmmu_create_heap(devmem_tmplt_hndl, + heap_info_arg, + 1, + &devmem_heap); + if (res != IMG_SUCCESS) + return res; + + devmem_template->devmem_heap[devmem_template->num_heaps] = devmem_heap; + devmem_template->num_heaps++; + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_devmem_ctx_create + * + * @Description This function is used to create a device memory context + * + * @Input devmem_tmplt_hndl: pointer to the device memory template handle + * + * @Input mmu_ctx_id: MMU context ID used with the TAL + * + * @Output devmem_ctx_hndl: pointer to the device memory context handle + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_ctx_create(void *devmem_tmplt_hndl, + unsigned int mmu_ctx_id, + void **devmem_ctx_hndl) +{ + struct talmmu_dm_tmpl *dm_tmpl = devmem_tmplt_hndl; + struct talmmu_devmem_ctx *dm_ctx; + struct talmmu_devmem_heap *dm_heap; + int i; + unsigned int res = IMG_SUCCESS; + + if (!devmem_tmplt_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Allocate memory for device memory context */ + dm_ctx = kzalloc((sizeof(struct talmmu_devmem_ctx)), GFP_KERNEL); + if (!dm_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + /* + * Update the device memory context structure members + * Update the device memory template + */ + dm_ctx->devmem_template = dm_tmpl; + /* Update MMU context ID */ + dm_ctx->mmu_ctx_id = mmu_ctx_id; + + /* Check for PTD Alignment */ + if (dm_tmpl->devmem_info.ptd_alignment == 0) + /* + * Make sure alignment is a multiple of page size. + * Set up PTD alignment to Page Size + */ + dm_tmpl->devmem_info.ptd_alignment = + dm_tmpl->devmem_info.page_size; + + /* Reference or create heaps for this context */ + for (i = 0; i < dm_tmpl->num_heaps; i++) { + dm_heap = dm_tmpl->devmem_heap[i]; + if (!dm_heap) + goto error_heap_create; + + switch (dm_heap->heap_info.heap_type) { + case TALMMU_HEAP_PERCONTEXT: + res = talmmu_create_heap(dm_tmpl, + &dm_heap->heap_info, + 1, + &dm_ctx->devmem_heap[i]); + if (res != IMG_SUCCESS) + goto error_heap_create; + break; + + default: + break; + } + + dm_ctx->num_heaps++; + } + + mutex_lock_nested(global_lock, SUBCLASS_TALMMU); + + /* Add the device memory context to the list */ + lst_add(&dm_tmpl->devmem_ctx_list, dm_ctx); + + dm_tmpl->num_ctxs++; + + mutex_unlock(global_lock); + + *devmem_ctx_hndl = dm_ctx; + + return IMG_SUCCESS; + +error_heap_create: + /* Destroy the device memory heaps which were already created */ + for (i--; i >= 0; i--) { + dm_heap = dm_ctx->devmem_heap[i]; + if (dm_heap->heap_info.heap_type == TALMMU_HEAP_PERCONTEXT) + talmmu_devmem_heap_destroy(dm_heap); + + dm_ctx->num_heaps--; + } + kfree(dm_ctx); + return res; +} + +/* + * @Function talmmu_devmem_ctx_destroy + * + * @Description This function is used to get the device memory context from + * the list and destroy + * + * @Input devmem_ctx_hndl: device memory context handle + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_ctx_destroy(void *devmem_ctx_hndl) +{ + struct talmmu_devmem_ctx *devmem_ctx = devmem_ctx_hndl; + struct talmmu_dm_tmpl *devmem_template; + struct talmmu_devmem_heap *devmem_heap; + unsigned int i; + + if (!devmem_ctx_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + devmem_template = devmem_ctx->devmem_template; + + for (i = 0; i < devmem_ctx->num_heaps; i++) { + devmem_heap = devmem_ctx->devmem_heap[i]; + if (!devmem_heap) + return IMG_ERROR_INVALID_PARAMETERS; + + talmmu_devmem_heap_destroy(devmem_heap); + } + + devmem_ctx->pagedir = NULL; + + mutex_lock_nested(global_lock, SUBCLASS_TALMMU); + + lst_remove(&devmem_template->devmem_ctx_list, devmem_ctx); + + devmem_ctx->devmem_template->num_ctxs--; + + mutex_unlock(global_lock); + + kfree(devmem_ctx); + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_get_heap_handle + * + * @Description This function is used to get the device memory heap handle + * + * @Input hid: heap id + * + * @Input devmem_ctx_hndl: device memory context handle + * + * @Output devmem_heap_hndl: pointer to the device memory heap handle + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_get_heap_handle(unsigned int hid, + void *devmem_ctx_hndl, + void **devmem_heap_hndl) +{ + struct talmmu_devmem_ctx *devmem_ctx = devmem_ctx_hndl; + unsigned int i; + + if (!devmem_ctx_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + for (i = 0; i < devmem_ctx->num_heaps; i++) { + /* + * Checking for requested heap id match and return the device + * memory heap handle + */ + if (devmem_ctx->devmem_heap[i]->heap_info.heap_id == hid) { + *devmem_heap_hndl = devmem_ctx->devmem_heap[i]; + return IMG_SUCCESS; + } + } + + return IMG_ERROR_GENERIC_FAILURE; +} + +/* + * @Function talmmu_devmem_heap_options + * + * @Description This function is used to set additional heap options + * + * @Input devmem_heap_hndl: Handle for heap + * + * @Input heap_opt_id: Heap options ID + * + * @Input heap_options: Heap options + * + * @Return IMG_SUCCESS or an error code + * + */ +void talmmu_devmem_heap_options(void *devmem_heap_hndl, + enum talmmu_heap_option_id heap_opt_id, + union talmmu_heap_options heap_options) +{ + struct talmmu_devmem_heap *dm_heap = devmem_heap_hndl; + + switch (heap_opt_id) { + case TALMMU_HEAP_OPT_ADD_GUARD_BAND: + dm_heap->guardband = heap_options.guardband_opt.guardband; + break; + default: + break; + } +} + +/* + * @Function talmmu_devmem_malloc_nonmap + * + * @Description + * + * @Input + * + * @Output + * + * @Return IMG_SUCCESS or an error code + * + */ +static int talmmu_devmem_alloc_nonmap(void *devmem_ctx_hndl, + void *devmem_heap_hndl, + unsigned int size, + unsigned int align, + unsigned int dev_virt_ofset, + unsigned char ext_dev_vaddr, + void **mem_hndl) +{ + struct talmmu_devmem_ctx *dm_ctx = devmem_ctx_hndl; + struct talmmu_dm_tmpl *dm_tmpl; + struct talmmu_devmem_heap *dm_heap = devmem_heap_hndl; + struct talmmu_memory *mem; + unsigned long long ui64_dev_offset = 0; + int res = IMG_SUCCESS; + + if (!dm_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!devmem_heap_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + dm_tmpl = dm_ctx->devmem_template; + + /* Allocate memory for memory structure */ + mem = kzalloc((sizeof(struct talmmu_memory)), GFP_KERNEL); + if (!mem) + return IMG_ERROR_OUT_OF_MEMORY; + + mem->devmem_heap = dm_heap; + mem->devmem_ctx = dm_ctx; + mem->ext_dev_virtaddr = ext_dev_vaddr; + + /* We always for to be at least page aligned */ + if (align >= dm_tmpl->devmem_info.page_size) + /* + * alignment is larger than page size - make sure alignment is + * a multiple of page size + */ + mem->alignment = align; + else + /* + * alignment is smaller than page size - make sure page size is + * a multiple of alignment. Now round up alignment to one page + */ + mem->alignment = dm_tmpl->devmem_info.page_size; + + /* Round size up to next multiple of physical pages */ + if ((size % dm_tmpl->devmem_info.page_size) != 0) + mem->size = ((size / dm_tmpl->devmem_info.page_size) + + 1) * dm_tmpl->devmem_info.page_size; + else + mem->size = size; + + /* If the device virtual address was externally defined */ + if (mem->ext_dev_virtaddr) { + res = IMG_ERROR_INVALID_PARAMETERS; + goto free_mem; + } + + res = addr_cx_malloc_align_res(&dm_heap->ctx, "", + (mem->size + dm_heap->guardband), + mem->alignment, + &ui64_dev_offset); + + mem->dev_virtoffset = (unsigned int)ui64_dev_offset; + if (res != IMG_SUCCESS) + /* + * If heap space is unavaliable return NULL, the caller must + * handle this condition + */ + goto free_virt; + + mutex_lock_nested(global_lock, SUBCLASS_TALMMU); + + /* + * Add memory allocation to the list for this heap... + * If the heap is empty... + */ + if (lst_empty(&dm_heap->memory_list)) + /* + * Save flag to indicate whether the device virtual address + * is allocated internally or externally... + */ + dm_heap->ext_dev_virtaddr = mem->ext_dev_virtaddr; + + /* + * Once we have started allocating in one way ensure that we continue + * to do this... + */ + lst_add(&dm_heap->memory_list, mem); + + mutex_unlock(global_lock); + + *mem_hndl = mem; + + return IMG_SUCCESS; + +free_virt: + addr_cx_free(&dm_heap->ctx, "", mem->dev_virtoffset); +free_mem: + kfree(mem); + + return res; +} + +/* + * @Function talmmu_devmem_addr_alloc + * + * @Description + * + * @Input + * + * @Output + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_addr_alloc(void *devmem_ctx_hndl, + void *devmem_heap_hndl, + unsigned int size, + unsigned int align, + void **mem_hndl) +{ + unsigned int res; + void *mem; + + res = talmmu_devmem_alloc_nonmap(devmem_ctx_hndl, + devmem_heap_hndl, + size, + align, + 0, + 0, + &mem); + if (res != IMG_SUCCESS) + return res; + + *mem_hndl = mem; + + return IMG_SUCCESS; +} + +/* + * @Function talmmu_devmem_addr_free + * + * @Description This function is used to free device memory allocated using + * talmmu_devmem_addr_alloc(). + * + * @Input mem_hndl : Handle for the memory object + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_devmem_addr_free(void *mem_hndl) +{ + unsigned int res; + + if (!mem_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* free device memory allocated by calling talmmu_devmem_free() */ + res = talmmu_devmem_free(mem_hndl); + + return res; +} + +/* + * @Function talmmu_get_dev_virt_addr + * + * @Description This function is use to obtain the device (virtual) memory + * address which may be required for as a device virtual address + * in some of the TAL image functions + * + * @Input mem_hndl : Handle for the memory object + * + * @Output dev_virt: A piointer used to return the device virtual address + * + * @Return IMG_SUCCESS or an error code + * + */ +int talmmu_get_dev_virt_addr(void *mem_hndl, + unsigned int *dev_virt) +{ + struct talmmu_memory *mem = mem_hndl; + struct talmmu_devmem_heap *devmem_heap; + + if (!mem_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + devmem_heap = mem->devmem_heap; + + /* + * Device virtual address is addition of the specific device virtual + * offset and the base device virtual address from the heap information + */ + *dev_virt = (devmem_heap->heap_info.basedev_virtaddr + + mem->dev_virtoffset); + + return IMG_SUCCESS; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/talmmu_api.h b/drivers/media/platform/vxe-vxd/common/talmmu_api.h --- a/drivers/media/platform/vxe-vxd/common/talmmu_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/talmmu_api.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TAL MMU Extensions. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ +#include "addr_alloc.h" +#include "ra.h" +#include "lst.h" + +#ifndef __TALMMU_API_H__ +#define __TALMMU_API_H__ + +#define TALMMU_MAX_DEVICE_HEAPS (32) +#define TALMMU_MAX_TEMPLATES (32) + +/* MMU type */ +enum talmmu_mmu_type { + /* 4kb pages and 32-bit address range */ + TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR = 0x1, + /* variable size pages and 32-bit address */ + TALMMU_MMUTYPE_VAR_PAGES_32BIT_ADDR, + /* 4kb pages and 36-bit address range */ + TALMMU_MMUTYPE_4K_PAGES_36BIT_ADDR, + /* 4kb pages and 40-bit address range */ + TALMMU_MMUTYPE_4K_PAGES_40BIT_ADDR, + /* variable size pages and 40-bit address range */ + TALMMU_MMUTYPE_VP_40BIT, + TALMMU_MMUTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Device flags */ +enum talmmu_dev_flags { + TALMMU_DEVFLAGS_NONE = 0x0, + TALMMU_DEVFLAGS_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Heap type */ +enum talmmu_heap_type { + TALMMU_HEAP_SHARED_EXPORTED, + TALMMU_HEAP_PERCONTEXT, + TALMMU_HEAP_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Heap flags */ +enum talmmu_eheapflags { + TALMMU_HEAPFLAGS_NONE = 0x0, + TALMMU_HEAPFLAGS_SET_CACHE_CONSISTENCY = 0x00000001, + TALMMU_HEAPFLAGS_128BYTE_INTERLEAVE = 0x00000002, + TALMMU_HEAPFLAGS_256BYTE_INTERLEAVE = 0x00000004, + TALMMU_HEAPFLAGS_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Contains the device memory information */ +struct talmmu_devmem_info { + /* device id */ + unsigned int device_id; + /* mmu type */ + enum talmmu_mmu_type mmu_type; + /* Device flags - bit flags that can be combined */ + enum talmmu_dev_flags dev_flags; + /* Name of the memory space for page directory allocations */ + unsigned char *pagedir_memspace_name; + /* Name of the memory space for page table allocations */ + unsigned char *pagetable_memspace_name; + /* Page size in bytes */ + unsigned int page_size; + /* PTD alignment, must be multiple of Page size */ + unsigned int ptd_alignment; +}; + +struct talmmu_heap_info { + /* heap id */ + unsigned int heap_id; + /* heap type */ + enum talmmu_heap_type heap_type; + /* heap flags - bit flags that can be combined */ + enum talmmu_eheapflags heap_flags; + /* Name of the memory space for memory allocations */ + unsigned char *memspace_name; + /* Base device virtual address */ + unsigned int basedev_virtaddr; + /* size in bytes */ + unsigned int size; +}; + +/* Device memory template information */ +struct talmmu_dm_tmpl { + /* list */ + struct lst_t list; + /* Copy of device memory info structure */ + struct talmmu_devmem_info devmem_info; + /* Memory space ID for PTD allocations */ + void *ptd_memspace_hndl; + /* Memory space ID for Page Table allocations */ + void *ptentry_memspace_hndl; + /* number of heaps */ + unsigned int num_heaps; + /* Array of heap pointers */ + struct talmmu_devmem_heap *devmem_heap[TALMMU_MAX_DEVICE_HEAPS]; + /* Number of active contexts */ + unsigned int num_ctxs; + /* List of device memory context created from this template */ + struct lst_t devmem_ctx_list; + /* Number of bits to shift right to obtain page number */ + unsigned int page_num_shift; + /* Mask to extract byte-within-page */ + unsigned int byte_in_pagemask; + /* Heap alignment */ + unsigned int heap_alignment; + /* Page table entries/page */ + unsigned int pagetable_entries_perpage; + /* Number of bits to shift right to obtain page table number */ + unsigned int pagetable_num_shift; + /* Mask to extract index-within-page-table */ + unsigned int index_in_pagetable_mask; + /* Number of bits to shift right to obtain page dir number */ + unsigned int pagedir_num_shift; +}; + +/* Device memory heap information */ +struct talmmu_devmem_heap { + /* list item */ + struct lst_t list; + /* Copy of the heap info structure */ + struct talmmu_heap_info heap_info; + /* Pointer to the device memory template */ + struct talmmu_dm_tmpl *devmem_template; + /* true if device virtual address offset allocated externally by user */ + unsigned int ext_dev_virtaddr; + /* list of memory allocations */ + struct lst_t memory_list; + /* Memory space ID for memory allocations */ + void *memspace_hndl; + /* Address context structure */ + struct addr_context ctx; + /* Regions structure */ + struct addr_region regions; + /* size of heap guard band */ + unsigned int guardband; +}; + +struct talmmu_devmem_ctx { + /* list item */ + struct lst_t list; + /* Pointer to device template */ + struct talmmu_dm_tmpl *devmem_template; + /* No. of heaps */ + unsigned int num_heaps; + /* Array of heap pointers */ + struct talmmu_devmem_heap *devmem_heap[TALMMU_MAX_DEVICE_HEAPS]; + /* The MMU context id */ + unsigned int mmu_ctx_id; + /* Pointer to the memory that represents Page directory */ + unsigned int *pagedir; +}; + +struct talmmu_memory { + /* list item */ + struct lst_t list; + /* Heap from which memory was allocated */ + struct talmmu_devmem_heap *devmem_heap; + /* Context through which memory was allocated */ + struct talmmu_devmem_ctx *devmem_ctx; + /* size */ + unsigned int size; + /* alignment */ + unsigned int alignment; + /* device virtual offset of allocation */ + unsigned int dev_virtoffset; + /* true if device virtual address offset allocated externally by user */ + unsigned int ext_dev_virtaddr; +}; + +/* This type defines the event types for the TALMMU callbacks */ +enum talmmu_event { + /* Function to flush the cache. */ + TALMMU_EVENT_FLUSH_CACHE, + /*! Function to write the page directory address to the device */ + TALMMU_EVENT_WRITE_PAGE_DIRECTORY_REF, + /* Placeholder*/ + TALMMU_NO_OF_EVENTS +}; + +enum talmmu_heap_option_id { + /* Add guard band to all mallocs */ + TALMMU_HEAP_OPT_ADD_GUARD_BAND, + TALMMU_HEAP_OPT_SET_MEM_ATTRIB, + TALMMU_HEAP_OPT_SET_MEM_POOL, + + /* Placeholder */ + TALMMU_NO_OF_OPTIONS, + TALMMU_NO_OF_FORCE32BITS = 0x7FFFFFFFU +}; + +struct talmmu_guardband_options { + unsigned int guardband; +}; + +union talmmu_heap_options { + /* Guardband parameters */ + struct talmmu_guardband_options guardband_opt; +}; + +int talmmu_init(void); +int talmmu_deinit(void); +int talmmu_devmem_template_create(struct talmmu_devmem_info *devmem_info, + void **devmem_template_hndl); +int talmmu_devmem_heap_add(void *devmem_tmplt_hndl, + struct talmmu_heap_info *heap_info_arg); +int talmmu_devmem_template_destroy(void *devmem_tmplt_hndl); +int talmmu_devmem_ctx_create(void *devmem_tmplt_hndl, + unsigned int mmu_ctx_id, + void **devmem_ctx_hndl); +int talmmu_devmem_ctx_destroy(void *devmem_ctx_hndl); +int talmmu_get_heap_handle(unsigned int hid, + void *devmem_ctx_hndl, + void **devmem_heap_hndl); +/** + * talmmu_devmem_heap_empty - talmmu_devmem_heap_empty + * @devmem_heap_hndl: device memory heap handle + * + * This function is used for emptying the device memory heap list + */ + +int talmmu_devmem_heap_empty(void *devmem_heap_hndl); +void talmmu_devmem_heap_options(void *devmem_heap_hndl, + enum talmmu_heap_option_id heap_opt_id, + union talmmu_heap_options heap_options); +int talmmu_devmem_addr_alloc(void *devmem_ctx_hndl, + void *devmem_heap_hndl, + unsigned int size, + unsigned int align, + void **mem_hndl); +int talmmu_devmem_addr_free(void *mem_hndl); +int talmmu_get_dev_virt_addr(void *mem_hndl, + unsigned int *dev_virt); + +#endif /* __TALMMU_API_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/vid_buf.h b/drivers/media/platform/vxe-vxd/common/vid_buf.h --- a/drivers/media/platform/vxe-vxd/common/vid_buf.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/vid_buf.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low-level VXD interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#ifndef _VID_BUF_H +#define _VID_BUF_H + +/* + * struct vidio_ddbufinfo - contains information about virtual address + * @buf_size: the size of the buffer (in bytes). + * @cpu_virt: the cpu virtual address (mapped into the local cpu mmu) + * @dev_virt: device virtual address (pages mapped into IMG H/W mmu) + * @hndl_memory: handle to device mmu mapping + * @buff_id: buffer id used in communication with interface + * @is_internal: true, if the buffer is allocated internally + * @ref_count: reference count (number of users) + * @kmstr_id: stream id + * @core_id: core id + */ +struct vidio_ddbufinfo { + unsigned int buf_size; + void *cpu_virt; + unsigned int dev_virt; + void *hndl_memory; + unsigned int buff_id; + unsigned int is_internal; + unsigned int ref_count; + unsigned int kmstr_id; + unsigned int core_id; +}; + +#endif /* _VID_BUF_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/work_queue.c b/drivers/media/platform/vxe-vxd/common/work_queue.c --- a/drivers/media/platform/vxe-vxd/common/work_queue.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/work_queue.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Work Queue Handling for Linux + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Prashanth Kumar Amai + */ + +#include +#include +#include + +#include "work_queue.h" + +/* Defining and initilizing mutex + */ +DEFINE_MUTEX(mutex); + +#define false 0 +#define true 1 + +struct node { + void **key; + struct node *next; +}; + +struct node *work_head; +struct node *delayed_work_head; + +void init_work(void **work_args, void *work_fn, uint8_t hwa_id) +{ + struct work_struct **work = (struct work_struct **)work_args; + //create a link + struct node *link = kmalloc(sizeof(*link), GFP_KERNEL); + + *work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!(*work)) { + pr_err("Memory allocation failed for work_queue\n"); + return; + } + INIT_WORK(*work, work_fn); + + link->key = (void **)work; + mutex_lock(&mutex); + //point it to old first node + link->next = work_head; + + //point first to new first node + work_head = link; + mutex_unlock(&mutex); +} + +void init_delayed_work(void **work_args, void *work_fn, uint8_t hwa_id) +{ + struct delayed_work **work = (struct delayed_work **)work_args; + //create a link + struct node *link = kmalloc(sizeof(*link), GFP_KERNEL); + + *work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!(*work)) { + pr_err("Memory allocation failed for delayed_work_queue\n"); + return; + } + INIT_DELAYED_WORK(*work, work_fn); + + link->key = (void **)work; + mutex_lock(&mutex); + //point it to old first node + link->next = delayed_work_head; + + //point first to new first node + delayed_work_head = link; + mutex_unlock(&mutex); +} + +/** + * get_work_buff - get_work_buff + * @key: key value + * @flag: flag + */ + +void *get_work_buff(void *key, signed char flag) +{ + struct node *data = NULL; + void *work_new = NULL; + struct node *temp = NULL; + struct node *previous = NULL; + struct work_struct **work = NULL; + + //start from the first link + mutex_lock(&mutex); + temp = work_head; + + //if list is empty + if (!work_head) { + mutex_unlock(&mutex); + return NULL; + } + + work = ((struct work_struct **)(temp->key)); + //navigate through list + while (*work != key) { + //if it is last node + if (!temp->next) { + mutex_unlock(&mutex); + return NULL; + } + //store reference to current link + previous = temp; + //move to next link + temp = temp->next; + work = ((struct work_struct **)(temp->key)); + } + + if (flag) { + //found a match, update the link + if (temp == work_head) { + //change first to point to next link + work_head = work_head->next; + } else { + //bypass the current link + previous->next = temp->next; + } + } + + mutex_unlock(&mutex); + //return temp; + data = temp; + if (data) { + work_new = data->key; + if (flag) + kfree(data); + } + return work_new; +} + +void *get_delayed_work_buff(void *key, signed char flag) +{ + struct node *data = NULL; + void *dwork_new = NULL; + struct node *temp = NULL; + struct node *previous = NULL; + struct delayed_work **dwork = NULL; + + if (flag) { + /* This Condition is true when kernel module is removed */ + return delayed_work_head; + } + //start from the first link + mutex_lock(&mutex); + temp = delayed_work_head; + + //if list is empty + if (!delayed_work_head) { + mutex_unlock(&mutex); + return NULL; + } + + dwork = ((struct delayed_work **)(temp->key)); + //navigate through list + while (&(*dwork)->work != key) { + //if it is last node + if (!temp->next) { + mutex_unlock(&mutex); + return NULL; + } + //store reference to current link + previous = temp; + //move to next link + temp = temp->next; + dwork = ((struct delayed_work **)(temp->key)); + } + + mutex_unlock(&mutex); + data = temp; + if (data) { + dwork_new = data->key; + if (flag) + kfree(data); + } + return dwork_new; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/common/work_queue.h b/drivers/media/platform/vxe-vxd/common/work_queue.h --- a/drivers/media/platform/vxe-vxd/common/work_queue.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/common/work_queue.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Work Queue Related Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Prashanth Kumar Amai + */ + +#ifndef WORKQUEUE_H_ +#define WORKQUEUE_H_ + +#include + +enum { + HWA_DECODER = 0, + HWA_ENCODER = 1, + HWA_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * init_work - This function provides the necessary initialization + * and saving given pointer(work_args) in linked list. + * @work_args: structure for the initialization + * @work_fn: work function pointer + * + * This function provides the necessary initialization + * and setting of the handler function (passed by the user). + */ +void init_work(void **work_args, void *work_fn, uint8_t hwa_id); + +/* + * init_delayed_work - This function provides the necessary initialization. + * and saving given pointer(work_args) in linked list. + * @work_args: structure for the initialization + * @work_fn: work function pointer + * + * This function provides the necessary initialization + * and setting of the handler function (passed by the user). + */ +void init_delayed_work(void **work_args, void *work_fn, uint8_t hwa_id); + +/* + * get_delayed_work_buff - This function return base address of given pointer + * @key: The given work struct pointer + * @flag: If TRUE, delete the node from the linked list. + * + * Return: Base address of the given input buffer. + */ +void *get_delayed_work_buff(void *key, signed char flag); + +/** + * get_work_buff - This function return base address of given pointer + * @key: The given work struct pointer + * @flag: If TRUE, delete the node from the linked list. + * + * Return: Base address of the given input buffer. + */ +void *get_work_buff(void *key, signed char flag); + +#endif /* WORKQUEUE_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/bspp.c b/drivers/media/platform/vxe-vxd/decoder/bspp.c --- a/drivers/media/platform/vxe-vxd/decoder/bspp.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/bspp.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,2480 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD Bitstream Buffer Pre-Parser + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "bspp.h" +#include "h264_secure_parser.h" +#include "hevc_secure_parser.h" +#ifdef HAS_JPEG +#include "jpeg_secure_parser.h" +#endif +#include "lst.h" +#include "swsr.h" +#include "vdecdd_defs.h" +#include "img_errors.h" + +#define BSPP_ERR_MSG_LENGTH 1024 + +/* + * This type defines the exception flag to catch the error if more catch block + * is required to catch different kind of error then more enum can be added + * @breif BSPP exception handler to catch the errors + */ +enum bspp_exception_handler { + /* BSPP parse exception handler */ + BSPP_EXCEPTION_HANDLER_NONE = 0x00, + /* Jump at exception (external use) */ + BSPP_EXCEPTION_HANDLER_JUMP, + BSPP_EXCEPTION_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This structure contains bitstream buffer information. + * @brief BSPP Bitstream Buffer Information + */ +struct bspp_bitstream_buffer { + void **lst_link; + struct bspp_ddbuf_info ddbuf_info; + unsigned int data_size; + unsigned int bufmap_id; + enum vdec_bstr_element_type bstr_element_type; + unsigned long long bytes_read; + void *pict_tag_param; +}; + +/* + * This structure contains shift-register state. + * @brief BSPP Shift-register State + */ +struct bspp_parse_ctx { + void *swsr_context; + enum swsr_exception exception; +}; + +/* + * This structure contains context for the current picture. + * @brief BSPP Picture Context + */ +struct bspp_pict_ctx { + struct bspp_sequence_hdr_info *sequ_hdr_info; + int closed_gop; + struct bspp_pict_hdr_info pict_hdr_info[VDEC_H264_MVC_MAX_VIEWS]; + struct bspp_sequence_hdr_info *ext_sequ_hdr_info; + int present; + int invalid; + int unsupported; + int finished; + unsigned int new_pict_signalled; +}; + +/* + * This structure contains resources allocated for the stream. + * @brief BSPP Stream Resource Allocations + */ +struct bspp_stream_alloc_data { + struct lst_t sequence_data_list[SEQUENCE_SLOTS]; + struct lst_t pps_data_list[PPS_SLOTS]; + struct lst_t available_sequence_list; + struct lst_t available_ppss_list; + struct lst_t raw_data_list_available; + struct lst_t raw_data_list_used; + struct lst_t vps_data_list[VPS_SLOTS]; + struct lst_t raw_sei_alloc_list; + struct lst_t available_vps_list; +}; + +struct bspp_raw_sei_alloc { + void **lst_link; + struct vdec_raw_bstr_data raw_sei_data; +}; + +/* + * This structure contains bitstream parsing state information for the current + * group of buffers. + * @brief BSPP Bitstream Parsing State Information + */ +struct bspp_grp_bstr_ctx { + enum vdec_vid_std vid_std; + int disable_mvc; + int delim_present; + void *swsr_context; + enum bspp_unit_type unit_type; + enum bspp_unit_type last_unit_type; + int not_pic_unit_yet; + int not_ext_pic_unit_yet; + unsigned int total_data_size; + unsigned int total_bytes_read; + struct lst_t buffer_chain; + struct lst_t in_flight_bufs; + struct lst_t *pre_pict_seg_list[3]; + struct lst_t *pict_seg_list[3]; + void **pict_tag_param_array[3]; + struct lst_t *segment_list; + void **pict_tag_param; + struct lst_t *free_segments; + unsigned int segment_offset; + int insert_start_code; + unsigned char start_code_suffix; + unsigned char current_view_idx; +}; + +/* + * This structure contains the stream context information. + * @brief BSPP Stream Context Information + */ +struct bspp_str_context { + enum vdec_vid_std vid_std; + int disable_mvc; + int full_scan; + int immediate_decode; + enum vdec_bstr_format bstr_format; + struct vdec_codec_config codec_config; + unsigned int user_str_id; + struct bspp_vid_std_features vid_std_features; + struct bspp_swsr_ctx swsr_ctx; + struct bspp_parser_callbacks parser_callbacks; + struct bspp_stream_alloc_data str_alloc; + unsigned int sequ_hdr_id; + unsigned char *sequ_hdr_info; + unsigned char *secure_sequence_info; + unsigned char *pps_info; + unsigned char *secure_pps_info; + unsigned char *raw_data; + struct bspp_grp_bstr_ctx grp_bstr_ctx; + struct bspp_parse_ctx parse_ctx; + struct bspp_inter_pict_data inter_pict_data; + struct lst_t decoded_pictures_list; + /* Mutex for secure access */ + struct mutex *bspp_mutex; + int intra_frame_closed_gop; + struct bspp_pict_ctx pict_ctx; + struct bspp_parse_state parse_state; +}; + +/* + * This structure contains the standard related parser functions. + * @brief BSPP Standard Related Functions + */ +struct bspp_parser_functions { + /* Pointer to standard-specific parser configuration function */ + bspp_cb_set_parser_config set_parser_config; + /* Pointer to standard-specific unit type determining function */ + bspp_cb_determine_unit_type determine_unit_type; +}; + +static struct bspp_parser_functions parser_fxns[VDEC_STD_MAX] = { + /* VDEC_STD_UNDEFINED */ + { NULL, NULL }, + /* VDEC_STD_MPEG2 */ + { NULL, NULL }, + /* VDEC_STD_MPEG4 */ + { NULL, NULL }, + /* VDEC_STD_H263 */ + { NULL, NULL }, + /* VDEC_STD_H264 */ + { bspp_h264_set_parser_config, bspp_h264_determine_unittype }, + /* VDEC_STD_VC1 */ + { NULL, NULL }, + /* VDEC_STD_AVS */ + { NULL, NULL }, + /* VDEC_STD_REAL */ + { NULL, NULL }, + /* VDEC_STD_JPEG */ +#ifdef HAS_JPEG + { bspp_jpeg_setparser_config, bspp_jpeg_determine_unit_type }, +#else + { NULL, NULL }, +#endif + /* VDEC_STD_VP6 */ + { NULL, NULL }, + /* VDEC_STD_VP8 */ + { NULL, NULL }, + /* VDEC_STD_SORENSON */ + { NULL, NULL }, + /* VDEC_STD_HEVC */ + { bspp_hevc_set_parser_config, bspp_hevc_determine_unittype }, +}; + +/* + * @Function bspp_get_pps_hdr + * @Description Obtains the most recent PPS header of a given Id. + */ +struct bspp_pps_info *bspp_get_pps_hdr(void *str_res_handle, unsigned int pps_id) +{ + struct bspp_stream_alloc_data *alloc_data = + (struct bspp_stream_alloc_data *)str_res_handle; + + if (pps_id >= PPS_SLOTS || !alloc_data) + return NULL; + + return lst_last(&alloc_data->pps_data_list[pps_id]); +} + +/* + * @Function bspp_get_sequ_hdr + * @Description Obtains the most recent sequence header of a given Id. + */ +struct bspp_sequence_hdr_info *bspp_get_sequ_hdr(void *str_res_handle, + unsigned int sequ_id) +{ + struct bspp_stream_alloc_data *alloc_data = + (struct bspp_stream_alloc_data *)str_res_handle; + if (sequ_id >= SEQUENCE_SLOTS || !alloc_data) + return NULL; + + return lst_last(&alloc_data->sequence_data_list[sequ_id]); +} + +/* + * @Function bspp_free_bitstream_elem + * @Description Frees a bitstream chain element. + */ +static void bspp_free_bitstream_elem(struct bspp_bitstream_buffer *bstr_buf) +{ + memset(bstr_buf, 0, sizeof(struct bspp_bitstream_buffer)); + + kfree(bstr_buf); +} + +/* + * @Function bspp_create_segment + * @Description Constructs a bitstream segment for the current unit and adds + * it to the list. + */ +static int bspp_create_segment(struct bspp_grp_bstr_ctx *grp_btsr_ctx, + struct bspp_bitstream_buffer *cur_buf) +{ + struct bspp_bitstr_seg *segment; + unsigned int result; + + /* + * Only create a segment when data (not in a previous segment) has been + * parsed from the buffer. + */ + if (cur_buf->bytes_read != grp_btsr_ctx->segment_offset) { + /* Allocate a software shift-register context structure */ + segment = lst_removehead(grp_btsr_ctx->free_segments); + if (!segment) { + result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + goto error; + } + memset(segment, 0, sizeof(struct bspp_bitstr_seg)); + + segment->bufmap_id = cur_buf->bufmap_id; + segment->data_size = (unsigned int)cur_buf->bytes_read + - grp_btsr_ctx->segment_offset; + segment->data_byte_offset = grp_btsr_ctx->segment_offset; + + if (cur_buf->bytes_read == cur_buf->data_size) { + /* This is the last segment in the buffer. */ + segment->bstr_seg_flag |= VDECDD_BSSEG_LASTINBUFF; + } + + /* + * Next segment will start part way through the buffer + * (current read position). + */ + grp_btsr_ctx->segment_offset = (unsigned int)cur_buf->bytes_read; + + if (grp_btsr_ctx->insert_start_code) { + segment->bstr_seg_flag |= VDECDD_BSSEG_INSERT_STARTCODE; + segment->start_code_suffix = grp_btsr_ctx->start_code_suffix; + grp_btsr_ctx->insert_start_code = 0; + } + + lst_add(grp_btsr_ctx->segment_list, segment); + + /* + * If multiple segments correspond to the same (picture) + * stream-unit, update it only the first time + */ + if (cur_buf->pict_tag_param && grp_btsr_ctx->pict_tag_param && + (grp_btsr_ctx->segment_list == + grp_btsr_ctx->pict_seg_list[0] || + grp_btsr_ctx->segment_list == + grp_btsr_ctx->pict_seg_list[1] || + grp_btsr_ctx->segment_list == + grp_btsr_ctx->pict_seg_list[2])) + *grp_btsr_ctx->pict_tag_param = cur_buf->pict_tag_param; + } + + return IMG_SUCCESS; +error: + return result; +} + +/* + * @Function bspp_DetermineUnitType + * + */ +static int bspp_determine_unit_type(enum vdec_vid_std vid_std, + unsigned char unit_type, + int disable_mvc, + enum bspp_unit_type *unit_type_enum) +{ + /* Determine the unit type from the NAL type. */ + if (vid_std < VDEC_STD_MAX && parser_fxns[vid_std].determine_unit_type) + parser_fxns[vid_std].determine_unit_type(unit_type, disable_mvc, unit_type_enum); + else + return IMG_ERROR_INVALID_PARAMETERS; + + return IMG_SUCCESS; +} + +/* + * @Function bspp_shift_reg_cb + * + */ +static void bspp_shift_reg_cb(enum swsr_cbevent event, + struct bspp_grp_bstr_ctx *grp_btsr_ctx, + unsigned char nal_type, + unsigned char **data_buffer, + unsigned long long *data_size) +{ + unsigned int result; + + switch (event) { + case SWSR_EVENT_INPUT_BUFFER_START: { + struct bspp_bitstream_buffer *next_buf; + + /* Take the next bitstream buffer for use in shift-register. */ + next_buf = lst_removehead(&grp_btsr_ctx->buffer_chain); + + if (next_buf && data_buffer && data_size) { + lst_add(&grp_btsr_ctx->in_flight_bufs, next_buf); + + *data_buffer = next_buf->ddbuf_info.cpu_virt_addr; + *data_size = next_buf->data_size; + + next_buf->bytes_read = 0; + } else { + goto error; + } + } + break; + case SWSR_EVENT_OUTPUT_BUFFER_END: { + struct bspp_bitstream_buffer *cur_buf; + + cur_buf = lst_removehead(&grp_btsr_ctx->in_flight_bufs); + + if (cur_buf) { + /* + * Indicate that the whole buffer content has been + * used. + */ + cur_buf->bytes_read = cur_buf->data_size; + grp_btsr_ctx->total_bytes_read += (unsigned int)cur_buf->bytes_read; + + /* + * Construct segment for current buffer and add to + * active list. + */ + result = bspp_create_segment(grp_btsr_ctx, cur_buf); + if (result != IMG_SUCCESS) + goto error; + + /* + * Next segment will start at the beginning of the next + * buffer. + */ + grp_btsr_ctx->segment_offset = 0; + + /* Destroy the bitstream element. */ + bspp_free_bitstream_elem(cur_buf); + } else { + goto error; + } + } + break; + + case SWSR_EVENT_DELIMITER_NAL_TYPE: + /* + * Initialise the unit type with the last (unclassified or + * unsupported types are not retained since they. + */ + grp_btsr_ctx->unit_type = grp_btsr_ctx->last_unit_type; + + /* + * Determine the unit type without consuming any data (start + * code) from shift-register. Segments are created automatically + * when a new buffer is requested by the shift-register so the + * unit type must be known in order to switch over the segment + * list. + */ + result = bspp_determine_unit_type(grp_btsr_ctx->vid_std, nal_type, + grp_btsr_ctx->disable_mvc, + &grp_btsr_ctx->unit_type); + + /* + * Only look to change bitstream segment list when the unit type + * is different and the current unit contains data that could be + * placed in a new list. + */ + if (grp_btsr_ctx->last_unit_type != grp_btsr_ctx->unit_type && + grp_btsr_ctx->unit_type != BSPP_UNIT_UNSUPPORTED && + grp_btsr_ctx->unit_type != BSPP_UNIT_UNCLASSIFIED) { + int prev_pict_data; + int curr_pict_data; + + prev_pict_data = (grp_btsr_ctx->last_unit_type == BSPP_UNIT_PICTURE || + grp_btsr_ctx->last_unit_type == + BSPP_UNIT_SKIP_PICTURE) ? 1 : 0; + + curr_pict_data = (grp_btsr_ctx->unit_type == BSPP_UNIT_PICTURE || + grp_btsr_ctx->unit_type == + BSPP_UNIT_SKIP_PICTURE) ? 1 : 0; + + /* + * When switching between picture and non-picture + * units. + */ + if ((prev_pict_data && !curr_pict_data) || + (!prev_pict_data && curr_pict_data)) { + /* + * Only delimit unit change when we're not the + * first unit and we're not already in the last + * segment list. + */ + if (grp_btsr_ctx->last_unit_type != BSPP_UNIT_NONE && + grp_btsr_ctx->segment_list != + grp_btsr_ctx->pict_seg_list[2]) { + struct bspp_bitstream_buffer *cur_buf = + lst_first(&grp_btsr_ctx->in_flight_bufs); + if (!cur_buf) + goto error; + + /* + * Update the offset within current buf. + */ + swsr_get_byte_offset_curbuf(grp_btsr_ctx->swsr_context, + &cur_buf->bytes_read); + + /* + * Create the last segment of the + * previous type (which may split a + * buffer into two). If the unit is + * exactly at the start of a buffer this + * will not create a zero-byte segment. + */ + result = bspp_create_segment(grp_btsr_ctx, cur_buf); + if (result != IMG_SUCCESS) + goto error; + } + + /* Point at the next segment list. */ + if (grp_btsr_ctx->segment_list + == grp_btsr_ctx->pre_pict_seg_list[0]) { + grp_btsr_ctx->segment_list = + grp_btsr_ctx->pict_seg_list[0]; + grp_btsr_ctx->pict_tag_param = + grp_btsr_ctx->pict_tag_param_array[0]; + } else if (grp_btsr_ctx->segment_list + == grp_btsr_ctx->pict_seg_list[0]) + grp_btsr_ctx->segment_list = + grp_btsr_ctx->pre_pict_seg_list[1]; + else if (grp_btsr_ctx->segment_list + == grp_btsr_ctx->pre_pict_seg_list[1]) { + grp_btsr_ctx->segment_list = + grp_btsr_ctx->pict_seg_list[1]; + grp_btsr_ctx->pict_tag_param = + grp_btsr_ctx->pict_tag_param_array[1]; + } else if (grp_btsr_ctx->segment_list + == grp_btsr_ctx->pict_seg_list[1]) + grp_btsr_ctx->segment_list = + grp_btsr_ctx->pre_pict_seg_list[2]; + else if (grp_btsr_ctx->segment_list + == grp_btsr_ctx->pre_pict_seg_list[2]) { + grp_btsr_ctx->segment_list = + grp_btsr_ctx->pict_seg_list[2]; + grp_btsr_ctx->pict_tag_param = + grp_btsr_ctx->pict_tag_param_array[2]; + } + } + + grp_btsr_ctx->last_unit_type = grp_btsr_ctx->unit_type; + } + break; + + default: + break; + } + +error: + return; +} + +/* + * @Function bspp_exception_handler + * + */ +static void bspp_exception_handler(enum swsr_exception exception, void *parse_ctx_handle) +{ + struct bspp_parse_ctx *parse_ctx = (struct bspp_parse_ctx *)parse_ctx_handle; + + /* Store the exception. */ + parse_ctx->exception = exception; + + switch (parse_ctx->exception) { + case SWSR_EXCEPT_NO_EXCEPTION: + break; + case SWSR_EXCEPT_ENCAPULATION_ERROR1: + break; + case SWSR_EXCEPT_ENCAPULATION_ERROR2: + break; + case SWSR_EXCEPT_ACCESS_INTO_SCP: + break; + case SWSR_EXCEPT_ACCESS_BEYOND_EOD: + break; + case SWSR_EXCEPT_EXPGOULOMB_ERROR: + break; + case SWSR_EXCEPT_WRONG_CODEWORD_ERROR: + break; + case SWSR_EXCEPT_NO_SCP: + break; + case SWSR_EXCEPT_INVALID_CONTEXT: + break; + + default: + break; + } + + /* Clear the exception. */ + swsr_check_exception(parse_ctx->swsr_context); +} + +/* + * @Function bspp_reset_sequence + * + */ +static void bspp_reset_sequence(struct bspp_str_context *str_ctx, + struct bspp_sequence_hdr_info *sequ_hdr_info) +{ + /* Temporarily store relevant sequence fields. */ + struct bspp_ddbuf_array_info aux_fw_sequence = sequ_hdr_info->fw_sequence; + void *aux_secure_sequence_info_hndl = sequ_hdr_info->secure_sequence_info; + + struct bspp_ddbuf_array_info *tmp = &sequ_hdr_info->fw_sequence; + + /* Reset all related structures. */ + memset(((unsigned char *)tmp->ddbuf_info.cpu_virt_addr + tmp->buf_offset), 0x00, + sequ_hdr_info->fw_sequence.buf_element_size); + + if (str_ctx->parser_callbacks.reset_data_cb) + str_ctx->parser_callbacks.reset_data_cb(BSPP_UNIT_SEQUENCE, + sequ_hdr_info->secure_sequence_info); + else + memset(aux_secure_sequence_info_hndl, 0, str_ctx->vid_std_features.seq_size); + + memset(sequ_hdr_info, 0, sizeof(*sequ_hdr_info)); + + /* Restore relevant sequence fields. */ + sequ_hdr_info->fw_sequence = aux_fw_sequence; + sequ_hdr_info->sequ_hdr_info.bufmap_id = aux_fw_sequence.ddbuf_info.bufmap_id; + sequ_hdr_info->sequ_hdr_info.buf_offset = aux_fw_sequence.buf_offset; + sequ_hdr_info->secure_sequence_info = aux_secure_sequence_info_hndl; +} + +/* + * @Function bspp_reset_pps + * + */ +static void bspp_reset_pps(struct bspp_str_context *str_ctx, + struct bspp_pps_info *pps_info) +{ + /* Temporarily store relevant PPS fields. */ + struct bspp_ddbuf_array_info aux_fw_pps = pps_info->fw_pps; + void *aux_secure_pps_info_hndl = pps_info->secure_pps_info; + struct bspp_ddbuf_array_info *tmp = &pps_info->fw_pps; + + /* Reset all related structures. */ + memset(((unsigned char *)tmp->ddbuf_info.cpu_virt_addr + tmp->buf_offset), 0x00, + pps_info->fw_pps.buf_element_size); + + /* Reset the parser specific data. */ + if (str_ctx->parser_callbacks.reset_data_cb) + str_ctx->parser_callbacks.reset_data_cb(BSPP_UNIT_PPS, pps_info->secure_pps_info); + + /* Reset the common data. */ + memset(pps_info, 0, sizeof(*pps_info)); + + /* Restore relevant PPS fields. */ + pps_info->fw_pps = aux_fw_pps; + pps_info->bufmap_id = aux_fw_pps.ddbuf_info.bufmap_id; + pps_info->buf_offset = aux_fw_pps.buf_offset; + pps_info->secure_pps_info = aux_secure_pps_info_hndl; +} + +/* + * @Function bspp_stream_submit_buffer + * + */ +int bspp_stream_submit_buffer(void *str_context_handle, + const struct bspp_ddbuf_info *ddbuf_info, + unsigned int bufmap_id, + unsigned int data_size, + void *pict_tag_param, + enum vdec_bstr_element_type bstr_element_type) +{ + struct bspp_str_context *str_ctx = (struct bspp_str_context *)str_context_handle; + struct bspp_bitstream_buffer *bstr_buf; + unsigned int result = IMG_SUCCESS; + + if (!str_context_handle) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + if (bstr_element_type == VDEC_BSTRELEMENT_UNDEFINED || + bstr_element_type >= VDEC_BSTRELEMENT_MAX) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* + * Check that the new bitstream buffer is compatible with those + * before. + */ + bstr_buf = lst_last(&str_ctx->grp_bstr_ctx.buffer_chain); + if (bstr_buf && bstr_buf->bstr_element_type != bstr_element_type) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + /* Allocate a bitstream buffer chain element structure */ + bstr_buf = kmalloc(sizeof(*bstr_buf), GFP_KERNEL); + if (!bstr_buf) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(bstr_buf, 0, sizeof(*bstr_buf)); + + /* Queue buffer in a chain since units might span buffers. */ + if (ddbuf_info) + bstr_buf->ddbuf_info = *ddbuf_info; + + bstr_buf->data_size = data_size; + bstr_buf->bstr_element_type = bstr_element_type; + bstr_buf->pict_tag_param = pict_tag_param; + bstr_buf->bufmap_id = bufmap_id; + lst_add(&str_ctx->grp_bstr_ctx.buffer_chain, bstr_buf); + + str_ctx->grp_bstr_ctx.total_data_size += data_size; + +error: + return result; +} + +/* + * @Function bspp_sequence_hdr_info + * + */ +static struct bspp_sequence_hdr_info *bspp_obtain_sequence_hdr(struct bspp_str_context *str_ctx) +{ + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + struct bspp_sequence_hdr_info *sequ_hdr_info; + + /* + * Obtain any partially filled sequence data else provide a new one + * (always new for H.264 and HEVC) + */ + sequ_hdr_info = lst_last(&str_alloc->sequence_data_list[BSPP_DEFAULT_SEQUENCE_ID]); + if (!sequ_hdr_info || sequ_hdr_info->ref_count > 0 || str_ctx->vid_std == VDEC_STD_H264 || + str_ctx->vid_std == VDEC_STD_HEVC) { + /* Get Sequence resource. */ + sequ_hdr_info = lst_removehead(&str_alloc->available_sequence_list); + if (sequ_hdr_info) { + bspp_reset_sequence(str_ctx, sequ_hdr_info); + sequ_hdr_info->sequ_hdr_info.sequ_hdr_id = BSPP_INVALID; + } + } + + return sequ_hdr_info; +} + +/* + * @Function bspp_submit_picture_decoded + * + */ +int bspp_submit_picture_decoded(void *str_context_handle, + struct bspp_picture_decoded *picture_decoded) +{ + struct bspp_picture_decoded *picture_decoded_elem; + struct bspp_str_context *str_ctx = (struct bspp_str_context *)str_context_handle; + + /* Validate input arguments. */ + if (!str_context_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + picture_decoded_elem = kmalloc(sizeof(*picture_decoded_elem), GFP_KERNEL); + if (!picture_decoded_elem) + return IMG_ERROR_MALLOC_FAILED; + + *picture_decoded_elem = *picture_decoded; + + /* Lock access to the list for adding a picture - HIGH PRIORITY */ + mutex_lock_nested(str_ctx->bspp_mutex, SUBCLASS_BSPP); + + lst_add(&str_ctx->decoded_pictures_list, picture_decoded_elem); + + /* Unlock access to the list for adding a picture - HIGH PRIORITY */ + mutex_unlock(str_ctx->bspp_mutex); + + return IMG_SUCCESS; +} + +/* + * @Function bspp_check_and_detach_pps_info + * + */ +static void bspp_check_and_detach_pps_info(struct bspp_stream_alloc_data *str_alloc, + unsigned int pps_id) +{ + if (pps_id != BSPP_INVALID) { + struct bspp_pps_info *pps_info = lst_first(&str_alloc->pps_data_list[pps_id]); + + if (!pps_info) /* Invalid id */ + return; + + pps_info->ref_count--; + /* If nothing references it any more */ + if (pps_info->ref_count == 0) { + struct bspp_pps_info *next_pps_info = lst_next(pps_info); + + /* + * If it is not the last sequence in the slot list + * remove it and return it to the pool-list + */ + if (next_pps_info) { + lst_remove(&str_alloc->pps_data_list[pps_id], pps_info); + lst_addhead(&str_alloc->available_ppss_list, pps_info); + } + } + } +} + +/* + * @Function bspp_picture_decoded + * + */ +static int bspp_picture_decoded(struct bspp_str_context *str_ctx, + struct bspp_picture_decoded *picture_decoded) +{ + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + + /* Manage Sequence */ + if (picture_decoded->sequ_hdr_id != BSPP_INVALID) { + struct bspp_sequence_hdr_info *seq = + lst_first(&str_alloc->sequence_data_list[picture_decoded->sequ_hdr_id]); + + if (!seq) + return IMG_ERROR_INVALID_ID; + + if (picture_decoded->not_decoded) { + /* Release sequence data. */ + if (str_ctx->parser_callbacks.release_data_cb) + str_ctx->parser_callbacks.release_data_cb((void *)str_alloc, + BSPP_UNIT_SEQUENCE, seq->secure_sequence_info); + } + + seq->ref_count--; + /* If nothing references it any more */ + if (seq->ref_count == 0) { + struct bspp_sequence_hdr_info *next_sequ_hdr_info = lst_next(seq); + + /* + * If it is not the last sequence in the slot list + * remove it and return it to the pool-list + */ + if (next_sequ_hdr_info) { + lst_remove(&str_alloc->sequence_data_list + [picture_decoded->sequ_hdr_id], seq); + /* Release sequence data. */ + if (str_ctx->parser_callbacks.release_data_cb) + str_ctx->parser_callbacks.release_data_cb((void *)str_alloc, + BSPP_UNIT_SEQUENCE, seq->secure_sequence_info); + + lst_addhead(&str_alloc->available_sequence_list, seq); + } + } + } + + /* + * Expect at least one valid PPS for H.264 and always invalid for all + * others + */ + bspp_check_and_detach_pps_info(str_alloc, picture_decoded->pps_id); + bspp_check_and_detach_pps_info(str_alloc, picture_decoded->second_pps_id); + + return IMG_SUCCESS; +} + +/* + * @Function bspp_service_pictures_decoded + * + */ +static int bspp_service_pictures_decoded(struct bspp_str_context *str_ctx) +{ + struct bspp_picture_decoded *picture_decoded; + + while (1) { + /* + * Lock access to the list for removing a picture - + * LOW PRIORITY + */ + mutex_lock_nested(str_ctx->bspp_mutex, SUBCLASS_BSPP); + + picture_decoded = lst_removehead(&str_ctx->decoded_pictures_list); + + /* + * Unlock access to the list for removing a picture - + * LOW PRIORITY + */ + mutex_unlock(str_ctx->bspp_mutex); + + if (!picture_decoded) + break; + + bspp_picture_decoded(str_ctx, picture_decoded); + kfree(picture_decoded); + } + + return IMG_SUCCESS; +} + +static void bspp_remove_unused_vps(struct bspp_str_context *str_ctx, unsigned int vps_id) +{ + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + struct bspp_vps_info *temp_vps_info = NULL; + struct bspp_vps_info *next_temp_vps_info = NULL; + + /* + * Check the whole Vps slot list for any unused Vpss + * BEFORE ADDING THE NEW ONE, if found remove them + */ + next_temp_vps_info = lst_first(&str_alloc->vps_data_list[vps_id]); + while (next_temp_vps_info) { + /* Set Temp, it is the one which we will potentially remove */ + temp_vps_info = next_temp_vps_info; + /* + * Set Next Temp, it is the one for the next iteration + * (we cannot ask for next after removing it) + */ + next_temp_vps_info = lst_next(temp_vps_info); + /* If it is not used remove it */ + if (temp_vps_info->ref_count == 0 && next_temp_vps_info) { + /* Return resource to the available pool */ + lst_remove(&str_alloc->vps_data_list[vps_id], temp_vps_info); + lst_addhead(&str_alloc->available_vps_list, temp_vps_info); + } + } +} + +static void bspp_remove_unused_pps(struct bspp_str_context *str_ctx, unsigned int pps_id) +{ + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + struct bspp_pps_info *temp_pps_info = NULL; + struct bspp_pps_info *next_temp_pps_info = NULL; + + /* + * Check the whole PPS slot list for any unused PPSs BEFORE ADDING + * THE NEW ONE, if found remove them + */ + next_temp_pps_info = lst_first(&str_alloc->pps_data_list[pps_id]); + while (next_temp_pps_info) { + /* Set Temp, it is the one which we will potentially remove */ + temp_pps_info = next_temp_pps_info; + /* + * Set Next Temp, it is the one for the next iteration + * (we cannot ask for next after removing it) + */ + next_temp_pps_info = lst_next(temp_pps_info); + /* If it is not used remove it */ + if (temp_pps_info->ref_count == 0 && next_temp_pps_info) { + /* Return resource to the available pool */ + lst_remove(&str_alloc->pps_data_list[pps_id], temp_pps_info); + lst_addhead(&str_alloc->available_ppss_list, temp_pps_info); + } + } +} + +static void bspp_remove_unused_sequence(struct bspp_str_context *str_ctx, unsigned int sps_id) +{ + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + struct bspp_sequence_hdr_info *seq = NULL; + struct bspp_sequence_hdr_info *next_seq = NULL; + + /* + * Check the whole sequence slot list for any unused sequences, + * if found remove them + */ + next_seq = lst_first(&str_alloc->sequence_data_list[sps_id]); + while (next_seq) { + /* Set Temp, it is the one which we will potentially remove */ + seq = next_seq; + /* + * Set Next Temp, it is the one for the next iteration (we + * cannot ask for next after removing it) + */ + next_seq = lst_next(seq); + + /* + * If the head is no longer used and there is something after, + * remove it + */ + if (seq->ref_count == 0 && next_seq) { + /* Return resource to the pool-list */ + lst_remove(&str_alloc->sequence_data_list[sps_id], seq); + if (str_ctx->parser_callbacks.release_data_cb) { + str_ctx->parser_callbacks.release_data_cb + ((void *)str_alloc, + BSPP_UNIT_SEQUENCE, + seq->secure_sequence_info); + } + lst_addhead(&str_alloc->available_sequence_list, seq); + } + } +} + +/* + * @Function bspp_return_or_store_sequence_hdr + * + */ +static int bspp_return_or_store_sequence_hdr(struct bspp_str_context *str_ctx, + enum bspp_error_type parse_error, + struct bspp_sequence_hdr_info *sequ_hdr_info) +{ + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + struct bspp_sequence_hdr_info *prev_sequ_hdr_info; + + if (((parse_error & BSPP_ERROR_UNRECOVERABLE) || (parse_error & BSPP_ERROR_UNSUPPORTED)) && + sequ_hdr_info->sequ_hdr_info.sequ_hdr_id != BSPP_INVALID) { + prev_sequ_hdr_info = + lst_last(&str_alloc->sequence_data_list + [sequ_hdr_info->sequ_hdr_info.sequ_hdr_id]); + + /* check if it's not the same pointer */ + if (prev_sequ_hdr_info && prev_sequ_hdr_info != sequ_hdr_info) { + /* + * Throw away corrupted sequence header if a previous "good" one exists. + */ + sequ_hdr_info->sequ_hdr_info.sequ_hdr_id = BSPP_INVALID; + } + } + + /* Store or return Sequence resource. */ + if (sequ_hdr_info->sequ_hdr_info.sequ_hdr_id != BSPP_INVALID) { + /* Only add when not already in list. */ + if (sequ_hdr_info != lst_last(&str_alloc->sequence_data_list + [sequ_hdr_info->sequ_hdr_info.sequ_hdr_id])) { + /* + * Add new sequence header (not already in list) to end + * of the slot-list. + */ + lst_add(&str_alloc->sequence_data_list + [sequ_hdr_info->sequ_hdr_info.sequ_hdr_id], sequ_hdr_info); + } + + bspp_remove_unused_sequence(str_ctx, sequ_hdr_info->sequ_hdr_info.sequ_hdr_id); + } else { + /* + * if unit was not a sequnce info, add resource to the + * pool-list + */ + lst_addhead(&str_alloc->available_sequence_list, sequ_hdr_info); + } + + return IMG_SUCCESS; +} + +/* + * @Function bspp_get_resource + * + */ +static int bspp_get_resource(struct bspp_str_context *str_ctx, + struct bspp_pict_hdr_info *pict_hdr_info, + struct bspp_unit_data *unit_data) +{ + int result = IMG_SUCCESS; + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + + switch (unit_data->unit_type) { + case BSPP_UNIT_VPS: + /* Get VPS resource (HEVC only). */ + if (unit_data->vid_std != VDEC_STD_HEVC) + break; + unit_data->out.vps_info = lst_removehead(&str_alloc->available_vps_list); + if (!unit_data->out.vps_info) { + result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } else { + unit_data->out.vps_info->vps_id = BSPP_INVALID; + unit_data->out.vps_info->ref_count = 0; + } + break; + case BSPP_UNIT_SEQUENCE: + unit_data->out.sequ_hdr_info = bspp_obtain_sequence_hdr(str_ctx); + if (!unit_data->out.sequ_hdr_info) + result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + + break; + + case BSPP_UNIT_PPS: + /* Get PPS resource (H.264 only). */ + unit_data->out.pps_info = lst_removehead(&str_alloc->available_ppss_list); + /* allocate and return extra resources */ + if (!unit_data->out.pps_info) { + result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } else { + bspp_reset_pps(str_ctx, unit_data->out.pps_info); + unit_data->out.pps_info->pps_id = BSPP_INVALID; + } + break; + + case BSPP_UNIT_PICTURE: + case BSPP_UNIT_SKIP_PICTURE: + unit_data->out.pict_hdr_info = pict_hdr_info; +#ifdef HAS_JPEG + if (unit_data->vid_std == VDEC_STD_JPEG) { + unit_data->impl_sequ_hdr_info = bspp_obtain_sequence_hdr(str_ctx); + if (!unit_data->impl_sequ_hdr_info) + result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } +#endif + break; + + default: + break; + } + + return result; +} + +/* + * @Function bspp_file_resource + * @Description Stores or returns all resources provided to parse unit. + */ +static int bspp_file_resource(struct bspp_str_context *str_ctx, struct bspp_unit_data *unit_data) +{ + unsigned int result = IMG_SUCCESS; + struct bspp_stream_alloc_data *str_alloc = &str_ctx->str_alloc; + + switch (unit_data->unit_type) { + case BSPP_UNIT_VPS: + /* Store or return VPS resource (HEVC only) */ + if (unit_data->vid_std != VDEC_STD_HEVC) + break; + + if (unit_data->out.vps_info->vps_id != BSPP_INVALID) { + lst_add(&str_alloc->vps_data_list[unit_data->out.vps_info->vps_id], + unit_data->out.vps_info); + + bspp_remove_unused_vps(str_ctx, unit_data->out.vps_info->vps_id); + } else { + lst_addhead(&str_alloc->available_vps_list, unit_data->out.vps_info); + } + break; + case BSPP_UNIT_SEQUENCE: + result = bspp_return_or_store_sequence_hdr(str_ctx, unit_data->parse_error, + unit_data->out.sequ_hdr_info); + VDEC_ASSERT(result == IMG_SUCCESS); + break; + + case BSPP_UNIT_PPS: + /* Store or return PPS resource (H.264 only). */ + if (unit_data->out.pps_info->pps_id != BSPP_INVALID) { + /* + * if unit was a PPS info, add resource to the slot-list + * AFTER REMOVING THE UNUSED ONES otherwise this will be + * removed along the rest unless special provision for + * last is made + */ + lst_add(&str_alloc->pps_data_list[unit_data->out.pps_info->pps_id], + unit_data->out.pps_info); + + bspp_remove_unused_pps(str_ctx, unit_data->out.pps_info->pps_id); + } else { + /* + * if unit was not a PPS info, add resource to the + * pool-list + */ + lst_addhead(&str_alloc->available_ppss_list, unit_data->out.pps_info); + } + break; + + case BSPP_UNIT_PICTURE: + case BSPP_UNIT_SKIP_PICTURE: +#ifdef HAS_JPEG + if (unit_data->vid_std == VDEC_STD_JPEG) { + result = bspp_return_or_store_sequence_hdr(str_ctx, + unit_data->parse_error, + unit_data->impl_sequ_hdr_info); + VDEC_ASSERT(result == IMG_SUCCESS); + } +#endif + break; + + default: + break; + } + + return result; +} + +/* + * @Function bspp_process_unit + * + */ +static int bspp_process_unit(struct bspp_str_context *str_ctx, + unsigned int size_delim_bits, + struct bspp_pict_ctx *pict_ctx, + struct bspp_parse_state *parse_state) +{ + struct bspp_unit_data unit_data; + unsigned long long unit_size = 0; /* Unit size (in bytes, size delimited only). */ + unsigned int result; + unsigned char vidx = str_ctx->grp_bstr_ctx.current_view_idx; + struct bspp_pict_hdr_info *curr_pict_hdr_info; + + /* + * during call to swsr_consume_delim(), above. + * Setup default unit data. + */ + memset(&unit_data, 0, sizeof(struct bspp_unit_data)); + + if (str_ctx->grp_bstr_ctx.delim_present) { + /* Consume delimiter and catch any exceptions. */ + /* + * Consume the bitstream unit delimiter (size or + * start code prefix). + * When size-delimited the unit size is also returned + * so that the next unit can be found. + */ + result = swsr_consume_delim(str_ctx->swsr_ctx.swsr_context, + str_ctx->swsr_ctx.emulation_prevention, + size_delim_bits, &unit_size); + if (result != IMG_SUCCESS) + goto error; + } + + unit_data.unit_type = str_ctx->grp_bstr_ctx.unit_type; + unit_data.vid_std = str_ctx->vid_std; + unit_data.delim_present = str_ctx->grp_bstr_ctx.delim_present; + unit_data.codec_config = &str_ctx->codec_config; + unit_data.parse_state = parse_state; + unit_data.pict_sequ_hdr_id = str_ctx->sequ_hdr_id; + unit_data.str_res_handle = &str_ctx->str_alloc; + unit_data.unit_data_size = str_ctx->grp_bstr_ctx.total_data_size; + unit_data.intra_frm_as_closed_gop = str_ctx->intra_frame_closed_gop; + + /* ponit to picture headers, check boundaries */ + curr_pict_hdr_info = vidx < VDEC_H264_MVC_MAX_VIEWS ? + &pict_ctx->pict_hdr_info[vidx] : NULL; + unit_data.parse_state->next_pict_hdr_info = + vidx + 1 < VDEC_H264_MVC_MAX_VIEWS ? + &pict_ctx->pict_hdr_info[vidx + 1] : NULL; + unit_data.parse_state->is_prefix = 0; + + /* Obtain output data containers. */ + result = bspp_get_resource(str_ctx, curr_pict_hdr_info, &unit_data); + if (result != IMG_SUCCESS) + return result; + + /* Process Unit and catch any exceptions. */ + /* + * Call the standard-specific function to parse the bitstream + * unit. + */ + result = str_ctx->parser_callbacks.parse_unit_cb(str_ctx->swsr_ctx.swsr_context, + &unit_data); + if (result != IMG_SUCCESS) { + pr_err("Failed to process unit, error = %d", unit_data.parse_error); + goto error; + } + + if (unit_data.parse_error != BSPP_ERROR_NONE) + pr_err("Issues found while processing unit, error = %d\n", unit_data.parse_error); + + /* Store or return resource used for parsing unit. */ + result = bspp_file_resource(str_ctx, &unit_data); + + if (!str_ctx->inter_pict_data.seen_closed_gop && + str_ctx->grp_bstr_ctx.unit_type == BSPP_UNIT_PICTURE && + unit_data.slice && + (unit_data.out.pict_hdr_info && + unit_data.out.pict_hdr_info->intra_coded) && + str_ctx->vid_std != VDEC_STD_H264) + unit_data.new_closed_gop = 1; + + if (unit_data.new_closed_gop) { + str_ctx->inter_pict_data.seen_closed_gop = 1; + str_ctx->inter_pict_data.new_closed_gop = 1; + } + + /* + * Post-process unit (use local context in case + * parse function tried to change the unit type. + */ + if (str_ctx->grp_bstr_ctx.unit_type == BSPP_UNIT_PICTURE || + str_ctx->grp_bstr_ctx.unit_type == BSPP_UNIT_SKIP_PICTURE) { + if (str_ctx->inter_pict_data.new_closed_gop) { + pict_ctx->closed_gop = 1; + str_ctx->inter_pict_data.new_closed_gop = 0; + } + + if (unit_data.ext_slice && str_ctx->grp_bstr_ctx.not_ext_pic_unit_yet && + unit_data.pict_sequ_hdr_id != BSPP_INVALID) { + unsigned int id = unit_data.pict_sequ_hdr_id; + + str_ctx->grp_bstr_ctx.not_ext_pic_unit_yet = 0; + pict_ctx->ext_sequ_hdr_info = + lst_last(&str_ctx->str_alloc.sequence_data_list[id]); + } + + if (unit_data.slice) { + if (!curr_pict_hdr_info) { + VDEC_ASSERT(0); + return -EINVAL; + } + if (str_ctx->grp_bstr_ctx.not_pic_unit_yet && + unit_data.pict_sequ_hdr_id != BSPP_INVALID) { + str_ctx->grp_bstr_ctx.not_pic_unit_yet = 0; + + /* + * depend upon the picture header being + * populated (in addition to slice data). + */ + pict_ctx->present = 1; + + /* + * Update the picture context from the last unit parsed. + * This context must be stored since a non-picture unit may follow. + * Obtain current instance of sequence data for given ID. + */ + if (!pict_ctx->sequ_hdr_info) { + unsigned int id = unit_data.pict_sequ_hdr_id; + + pict_ctx->sequ_hdr_info = + lst_last(&str_ctx->str_alloc.sequence_data_list[id]); + + /* Do the sequence flagging/reference-counting */ + pict_ctx->sequ_hdr_info->ref_count++; + } + + /* Override the field here. */ + if (str_ctx->swsr_ctx.sr_config.delim_type == SWSR_DELIM_NONE) { + if (str_ctx->grp_bstr_ctx.unit_type == + BSPP_UNIT_SKIP_PICTURE) { + /* VDECFW_SKIPPED_PICTURE; */ + curr_pict_hdr_info->parser_mode = + VDECFW_SKIPPED_PICTURE; + curr_pict_hdr_info->pic_data_size = 0; + } else { + /* VDECFW_SIZE_SIDEBAND; */ + curr_pict_hdr_info->parser_mode = + VDECFW_SIZE_SIDEBAND; + curr_pict_hdr_info->pic_data_size = + str_ctx->grp_bstr_ctx.total_data_size; + } + } else if (str_ctx->swsr_ctx.sr_config.delim_type == + SWSR_DELIM_SIZE) { + if (str_ctx->swsr_ctx.sr_config.delim_length <= 8) + /* VDECFW_SIZE_DELIMITED_1_ONLY; */ + curr_pict_hdr_info->parser_mode = + VDECFW_SIZE_DELIMITED_1_ONLY; + else if (str_ctx->swsr_ctx.sr_config.delim_length <= 16) + /* VDECFW_SIZE_DELIMITED_2_ONLY; */ + curr_pict_hdr_info->parser_mode = + VDECFW_SIZE_DELIMITED_2_ONLY; + else if (str_ctx->swsr_ctx.sr_config.delim_length <= 32) + /* VDECFW_SIZE_DELIMITED_4_ONLY; */ + curr_pict_hdr_info->parser_mode = + VDECFW_SIZE_DELIMITED_4_ONLY; + + curr_pict_hdr_info->pic_data_size += + ((unsigned int)unit_size + + (size_delim_bits / 8)); + } else if (str_ctx->swsr_ctx.sr_config.delim_type == SWSR_DELIM_SCP) + /* VDECFW_SCP_ONLY; */ + curr_pict_hdr_info->parser_mode = VDECFW_SCP_ONLY; + } + + /* + * for MVC, the Slice Extension should also have the + * same ParserMode as the Base view. + */ + if (unit_data.parse_state->next_pict_hdr_info) { + unit_data.parse_state->next_pict_hdr_info->parser_mode = + curr_pict_hdr_info->parser_mode; + } + + if (unit_data.parse_error & BSPP_ERROR_UNSUPPORTED) { + pict_ctx->invalid = 1; + pict_ctx->unsupported = 1; + } else if (!str_ctx->full_scan) { + /* + * Only parse up to and including the first + * valid video slice unless full scanning. + */ + pict_ctx->finished = 1; + } + } + } + + if (unit_data.extracted_all_data) { + enum swsr_found found; + + swsr_byte_align(str_ctx->swsr_ctx.swsr_context); + + found = swsr_check_delim_or_eod(str_ctx->swsr_ctx.swsr_context); + if (found != SWSR_FOUND_DELIM && found != SWSR_FOUND_EOD) { + /* + * Should already be at the next delimiter or EOD. + * Any bits left at the end of the unit could indicate + * corrupted syntax or erroneous parsing. + */ + } + } + + return IMG_SUCCESS; + +error: + if (unit_data.unit_type == BSPP_UNIT_PICTURE || + unit_data.unit_type == BSPP_UNIT_SKIP_PICTURE) + pict_ctx->invalid = 1; + + /* + * Tidy-up resources. + * Store or return resource used for parsing unit. + */ + bspp_file_resource(str_ctx, &unit_data); + + return result; +} + +/* + * @Function bspp_terminate_buffer + * + */ +static int bspp_terminate_buffer(struct bspp_grp_bstr_ctx *grp_btsr_ctx, + struct bspp_bitstream_buffer *buf) +{ + int result = -1; + + /* Indicate that all the data in buffer should be added to segment. */ + buf->bytes_read = buf->data_size; + + result = bspp_create_segment(grp_btsr_ctx, buf); + if (result != IMG_SUCCESS) + return result; + + /* Next segment will start at the beginning of the next buffer. */ + grp_btsr_ctx->segment_offset = 0; + + bspp_free_bitstream_elem(buf); + + return result; +} + +/* + * @Function bspp_jump_to_next_view + * + */ +static int bspp_jump_to_next_view(struct bspp_grp_bstr_ctx *grp_btsr_ctx, + struct bspp_preparsed_data *preparsed_data, + struct bspp_parse_state *parse_state) +{ + struct bspp_bitstream_buffer *cur_buf; + int result; + unsigned int i; + unsigned char vidx; + + if (!grp_btsr_ctx || !parse_state || !preparsed_data) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + vidx = grp_btsr_ctx->current_view_idx; + + if (vidx >= VDEC_H264_MVC_MAX_VIEWS) { + result = IMG_ERROR_NOT_SUPPORTED; + goto error; + } + + /* get current buffer */ + cur_buf = (struct bspp_bitstream_buffer *)lst_first(&grp_btsr_ctx->in_flight_bufs); + if (!cur_buf) { + result = IMG_ERROR_CANCELLED; + goto error; + } + + if (cur_buf->bufmap_id != parse_state->prev_buf_map_id) { + /* + * If we moved to the next buffer while parsing the slice + * header of the new view we have to reduce the size of + * the last segment up to the beginning of the new view slice + * and create a new segment from that point up to the end of + * the buffer. The new segment should belong to the new view. + * THIS ONLY WORKS IF THE SLICE HEADER DOES NOT SPAN MORE THAN + * TWO BUFFERS. If we want to support the case that the slice + * header of the new view spans multiple buffer we either have + * here remove all the segments up to the point were we find + * the buffer we are looking for, then adjust the size of this + * segment and then add the segments we removed to the next + * view list or we can implement a mechanism like the one that + * peeks for the NAL unit type and delimit the next view + * segment before parsing the first slice of the view. + */ + struct bspp_bitstr_seg *segment; + + segment = lst_last(grp_btsr_ctx->segment_list); + if (segment && segment->bufmap_id == parse_state->prev_buf_map_id) { + struct bspp_bitstream_buffer prev_buf; + + segment->data_size -= parse_state->prev_buf_data_size + - parse_state->prev_byte_offset_buf; + segment->bstr_seg_flag &= ~VDECDD_BSSEG_LASTINBUFF; + + /* + * Change the segmenOffset value with the value it + * would have if we had delemited the segment correctly + * beforehand. + */ + grp_btsr_ctx->segment_offset = parse_state->prev_byte_offset_buf; + + /* set lists of segments to new view... */ + for (i = 0; i < BSPP_MAX_PICTURES_PER_BUFFER; i++) { + grp_btsr_ctx->pre_pict_seg_list[i] = + &preparsed_data->ext_pictures_data[vidx].pre_pict_seg_list + [i]; + grp_btsr_ctx->pict_seg_list[i] = + &preparsed_data->ext_pictures_data[vidx].pict_seg_list[i]; + + lst_init(grp_btsr_ctx->pre_pict_seg_list[i]); + lst_init(grp_btsr_ctx->pict_seg_list[i]); + } + /* and current segment list */ + grp_btsr_ctx->segment_list = grp_btsr_ctx->pict_seg_list[0]; + + memset(&prev_buf, 0, sizeof(struct bspp_bitstream_buffer)); + prev_buf.bufmap_id = segment->bufmap_id; + prev_buf.data_size = parse_state->prev_buf_data_size; + prev_buf.bytes_read = prev_buf.data_size; + + /* Create the segment the first part of the next view */ + result = bspp_create_segment(grp_btsr_ctx, &prev_buf); + if (result != IMG_SUCCESS) + goto error; + } else { + result = IMG_ERROR_NOT_SUPPORTED; + goto error; + } + } else { + /* + * the data just parsed belongs to new view, so use previous byte + * offset + */ + cur_buf->bytes_read = parse_state->prev_byte_offset_buf; + + /* Create the segment for previous view */ + result = bspp_create_segment(grp_btsr_ctx, cur_buf); + if (result != IMG_SUCCESS) + goto error; + + /* set lists of segments to new view */ + for (i = 0; i < BSPP_MAX_PICTURES_PER_BUFFER; i++) { + grp_btsr_ctx->pre_pict_seg_list[i] = + &preparsed_data->ext_pictures_data[vidx].pre_pict_seg_list[i]; + grp_btsr_ctx->pict_seg_list[i] = + &preparsed_data->ext_pictures_data[vidx].pict_seg_list[i]; + + lst_init(grp_btsr_ctx->pre_pict_seg_list[i]); + lst_init(grp_btsr_ctx->pict_seg_list[i]); + } + /* and current segment list */ + grp_btsr_ctx->segment_list = grp_btsr_ctx->pict_seg_list[0]; + } + + /* update prefix flag */ + preparsed_data->ext_pictures_data[vidx].is_prefix = parse_state->is_prefix; + /* and view index */ + grp_btsr_ctx->current_view_idx++; + + /* set number of extended pictures */ + preparsed_data->num_ext_pictures = grp_btsr_ctx->current_view_idx; + +error: + return result; +} + +static void bspp_reset_pict_state(struct bspp_str_context *str_ctx, struct bspp_pict_ctx *pict_ctx, + struct bspp_parse_state *parse_state) +{ + memset(pict_ctx, 0, sizeof(struct bspp_pict_ctx)); + memset(parse_state, 0, sizeof(struct bspp_parse_state)); + + /* Setup group buffer processing state. */ + parse_state->inter_pict_ctx = &str_ctx->inter_pict_data; + parse_state->prev_bottom_pic_flag = (unsigned char)BSPP_INVALID; + parse_state->next_pic_is_new = 1; + parse_state->prev_frame_num = BSPP_INVALID; + parse_state->second_field_flag = 0; + parse_state->first_chunk = 1; +} + +/* + * @Function bspp_stream_preparse_buffers + * @Description Buffer list cannot be processed since units in this last buffer + * may not be complete. Must wait until a buffer is provided with end-of-picture + * signalled. When the buffer indicates that units won't span then we can + * process the bitstream buffer chain. + */ +int bspp_stream_preparse_buffers(void *str_context_handle, + const struct bspp_ddbuf_info *contig_buf_info, + unsigned int contig_buf_map_id, struct lst_t *segments, + struct bspp_preparsed_data *preparsed_data, + int end_of_pic) +{ + struct bspp_str_context *str_ctx = (struct bspp_str_context *)str_context_handle; + struct bspp_pict_ctx *pict_ctx = &str_ctx->pict_ctx; + struct bspp_parse_state *parse_state = &str_ctx->parse_state; + int i; + unsigned int unit_count = 0, num_arrays = 0; + unsigned int size_delim_bits = 0; + enum swsr_found found = SWSR_FOUND_NONE; + unsigned int result; + struct bspp_bitstr_seg *segment; + struct lst_t temp_list; + + /* + * since it is new picture, resetting the context status to + * beginning + */ + /* TODO: revisit this */ + pict_ctx->finished = 0; + pict_ctx->new_pict_signalled = 0; + + if (!str_context_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!segments || !preparsed_data) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Check that bitstream buffers have been registered. */ + if (!lst_last(&str_ctx->grp_bstr_ctx.buffer_chain)) + return IMG_ERROR_OPERATION_PROHIBITED; + + /* Initialise the output data. */ + memset(preparsed_data, 0, sizeof(struct bspp_preparsed_data)); + + if (!parse_state->initialised) { + bspp_reset_pict_state(str_ctx, pict_ctx, parse_state); + parse_state->initialised = 1; + } + + for (i = 0; i < 3; i++) { + lst_init(&preparsed_data->picture_data.pre_pict_seg_list[i]); + lst_init(&preparsed_data->picture_data.pict_seg_list[i]); + } + + /* Initialise parsing for this video standard. */ + if (str_ctx->parser_callbacks.initialise_parsing_cb && parse_state->first_chunk) + str_ctx->parser_callbacks.initialise_parsing_cb(parse_state); + + parse_state->first_chunk = 0; + + for (i = 0; i < VDEC_H264_MVC_MAX_VIEWS; i++) { + pict_ctx->pict_hdr_info[i].pict_aux_data.id = BSPP_INVALID; + pict_ctx->pict_hdr_info[i].second_pict_aux_data.id = BSPP_INVALID; + } + + /* Setup buffer group bitstream context. */ + str_ctx->grp_bstr_ctx.vid_std = str_ctx->vid_std; + str_ctx->grp_bstr_ctx.disable_mvc = str_ctx->disable_mvc; + str_ctx->grp_bstr_ctx.delim_present = 1; + str_ctx->grp_bstr_ctx.swsr_context = str_ctx->swsr_ctx.swsr_context; + str_ctx->grp_bstr_ctx.unit_type = BSPP_UNIT_NONE; + str_ctx->grp_bstr_ctx.last_unit_type = BSPP_UNIT_NONE; + str_ctx->grp_bstr_ctx.not_pic_unit_yet = 1; + str_ctx->grp_bstr_ctx.not_ext_pic_unit_yet = 1; + str_ctx->grp_bstr_ctx.total_bytes_read = 0; + str_ctx->grp_bstr_ctx.current_view_idx = 0; + + for (i = 0; i < 3; i++) { + str_ctx->grp_bstr_ctx.pre_pict_seg_list[i] = + &preparsed_data->picture_data.pre_pict_seg_list[i]; + str_ctx->grp_bstr_ctx.pict_seg_list[i] = + &preparsed_data->picture_data.pict_seg_list[i]; + str_ctx->grp_bstr_ctx.pict_tag_param_array[i] = + &preparsed_data->picture_data.pict_tag_param[i]; + } + str_ctx->grp_bstr_ctx.segment_list = str_ctx->grp_bstr_ctx.pre_pict_seg_list[0]; + str_ctx->grp_bstr_ctx.pict_tag_param = str_ctx->grp_bstr_ctx.pict_tag_param_array[0]; + str_ctx->grp_bstr_ctx.free_segments = segments; + str_ctx->grp_bstr_ctx.segment_offset = 0; + str_ctx->grp_bstr_ctx.insert_start_code = 0; + + /* + * Before processing the units service all the picture decoded events + * to free the resources1794 + */ + bspp_service_pictures_decoded(str_ctx); + + /* + * A picture currently being parsed is already decoded (may happen + * after dwr in low latency mode) and its recourses were freed. Skip + * the rest of the picture. + */ + if (pict_ctx->sequ_hdr_info && pict_ctx->sequ_hdr_info->ref_count == 0) { + pict_ctx->present = 0; + pict_ctx->finished = 1; + } + + /* + * For bitstreams without unit delimiters treat all the buffers as + * a single unit whose type is defined by the first buffer element. + */ + if (str_ctx->swsr_ctx.sr_config.delim_type == SWSR_DELIM_NONE) { + struct bspp_bitstream_buffer *cur_buf = + lst_first(&str_ctx->grp_bstr_ctx.buffer_chain); + + /* if there is no picture data we must be skipped. */ + if (!cur_buf || cur_buf->data_size == 0) { + str_ctx->grp_bstr_ctx.unit_type = BSPP_UNIT_SKIP_PICTURE; + } else if (cur_buf->bstr_element_type == VDEC_BSTRELEMENT_CODEC_CONFIG) { + str_ctx->grp_bstr_ctx.unit_type = BSPP_UNIT_SEQUENCE; + } else if (cur_buf->bstr_element_type == VDEC_BSTRELEMENT_PICTURE_DATA || + cur_buf->bstr_element_type == VDEC_BSTRELEMENT_UNSPECIFIED) { + str_ctx->grp_bstr_ctx.unit_type = BSPP_UNIT_PICTURE; + str_ctx->grp_bstr_ctx.segment_list = str_ctx->grp_bstr_ctx.pict_seg_list[0]; + } + + str_ctx->grp_bstr_ctx.delim_present = 0; + } + + /* + * Load the first section (buffer) of biststream into the software + * shift-register. BSPP maps "buffer" to "section" and allows for + * contiguous parsing of all buffers since unit boundaries are not + * known up-front. Unit parsing and segment creation is happening in a + * single pass. + */ + result = swsr_start_bitstream(str_ctx->swsr_ctx.swsr_context, + &str_ctx->swsr_ctx.sr_config, + str_ctx->grp_bstr_ctx.total_data_size, + str_ctx->swsr_ctx.emulation_prevention); + + /* Seek for next delimiter or end of data and catch any exceptions. */ + if (str_ctx->grp_bstr_ctx.delim_present) { + /* Locate the first bitstream unit. */ + found = swsr_seek_delim_or_eod(str_ctx->swsr_ctx.swsr_context); + } + + if (str_ctx->swsr_ctx.sr_config.delim_type == SWSR_DELIM_SIZE) { + struct bspp_bitstream_buffer *cur_buf = + lst_first(&str_ctx->grp_bstr_ctx.in_flight_bufs); + + if (cur_buf->bstr_element_type == VDEC_BSTRELEMENT_CODEC_CONFIG && + str_ctx->parser_callbacks.parse_codec_config_cb) { + /* Parse codec config header and catch any exceptions */ + str_ctx->parser_callbacks.parse_codec_config_cb + (str_ctx->swsr_ctx.swsr_context, + &unit_count, + &num_arrays, + &str_ctx->swsr_ctx.sr_config.delim_length, + &size_delim_bits); + } else { + size_delim_bits = str_ctx->swsr_ctx.sr_config.delim_length; + } + } + + /* Process all the bitstream units until the picture is located. */ + while (found != SWSR_FOUND_EOD && !pict_ctx->finished) { + struct bspp_bitstream_buffer *cur_buf = + lst_first(&str_ctx->grp_bstr_ctx.in_flight_bufs); + + if (!cur_buf) { + pr_err("%s: cur_buf pointer is NULL\n", __func__); + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + if (str_ctx->swsr_ctx.sr_config.delim_type == + SWSR_DELIM_SIZE && cur_buf->bstr_element_type == + VDEC_BSTRELEMENT_CODEC_CONFIG && + str_ctx->parser_callbacks.update_unit_counts_cb) { + /* + * Parse middle part of codec config header and catch + * any exceptions. + */ + str_ctx->parser_callbacks.update_unit_counts_cb + (str_ctx->swsr_ctx.swsr_context, + &unit_count, + &num_arrays); + } + + /* Process the next unit. */ + result = bspp_process_unit(str_ctx, size_delim_bits, pict_ctx, parse_state); + if (result == IMG_ERROR_NOT_SUPPORTED) + goto error; + + if (str_ctx->swsr_ctx.sr_config.delim_type != SWSR_DELIM_NONE) + str_ctx->grp_bstr_ctx.delim_present = 1; + + /* jump to the next view */ + if (parse_state->new_view) { + result = bspp_jump_to_next_view(&str_ctx->grp_bstr_ctx, + preparsed_data, + parse_state); + if (result != IMG_SUCCESS) + goto error; + + parse_state->new_view = 0; + } + + if (!pict_ctx->finished) { + /* + * Seek for next delimiter or end of data and catch any + * exceptions. + */ + /* Locate the next bitstream unit or end of data */ + found = swsr_seek_delim_or_eod(str_ctx->swsr_ctx.swsr_context); + + { + struct bspp_bitstream_buffer *buf; + /* Update the offset within current buffer. */ + swsr_get_byte_offset_curbuf(str_ctx->grp_bstr_ctx.swsr_context, + &parse_state->prev_byte_offset_buf); + buf = lst_first(&str_ctx->grp_bstr_ctx.in_flight_bufs); + if (buf) { + parse_state->prev_buf_map_id = buf->bufmap_id; + parse_state->prev_buf_data_size = buf->data_size; + } + } + } + } + + /* Finalize parsing for this video standard. */ + if (str_ctx->parser_callbacks.finalise_parsing_cb && end_of_pic) { + str_ctx->parser_callbacks.finalise_parsing_cb((void *)&str_ctx->str_alloc, + parse_state); + } + + /* + * Create segments for each buffer held by the software shift register + * (and not yet processed). + */ + while (lst_first(&str_ctx->grp_bstr_ctx.in_flight_bufs)) { + struct bspp_bitstream_buffer *buf = + lst_removehead(&str_ctx->grp_bstr_ctx.in_flight_bufs); + + result = bspp_terminate_buffer(&str_ctx->grp_bstr_ctx, buf); + } + + /* + * Create segments for each buffer not yet requested by the shift + * register. + */ + while (lst_first(&str_ctx->grp_bstr_ctx.buffer_chain)) { + struct bspp_bitstream_buffer *buf = + lst_removehead(&str_ctx->grp_bstr_ctx.buffer_chain); + + result = bspp_terminate_buffer(&str_ctx->grp_bstr_ctx, buf); + } + + /* + * Populate the parsed data information for picture only if one is + * present. The anonymous data has already been added to the + * appropriate segment list. + */ + if (pict_ctx->present && !pict_ctx->invalid) { + if (!pict_ctx->new_pict_signalled) { + /* + * Provide data about sequence used by picture. + * Signal "new sequence" if the sequence header is new + * or has changed. always switch seq when changing base + * and additional views + */ + if (pict_ctx->sequ_hdr_info) { + if (pict_ctx->sequ_hdr_info->sequ_hdr_info.sequ_hdr_id != + str_ctx->sequ_hdr_id || + pict_ctx->sequ_hdr_info->ref_count == 1 || + pict_ctx->ext_sequ_hdr_info || + pict_ctx->closed_gop) { + preparsed_data->new_sequence = 1; + preparsed_data->sequ_hdr_info = + pict_ctx->sequ_hdr_info->sequ_hdr_info; + } + } + + /* Signal "new subsequence" and its common header information. */ + if (pict_ctx->ext_sequ_hdr_info) { + preparsed_data->new_sub_sequence = 1; + preparsed_data->ext_sequ_hdr_info = + pict_ctx->ext_sequ_hdr_info->sequ_hdr_info; + + for (i = 0; i < VDEC_H264_MVC_MAX_VIEWS - 1; + i++) { + /* + * prefix is always the last one + * do not attach any header info to it + */ + if (preparsed_data->ext_pictures_data[i].is_prefix) + break; + + /* attach headers */ + preparsed_data->ext_pictures_data[i].sequ_hdr_id = + pict_ctx->ext_sequ_hdr_info->sequ_hdr_info.sequ_hdr_id; + pict_ctx->ext_sequ_hdr_info->ref_count++; + preparsed_data->ext_pictures_data[i].pict_hdr_info = + pict_ctx->pict_hdr_info[i + 1]; + } + + preparsed_data->ext_pictures_data + [0].pict_hdr_info.first_pic_of_sequence = + preparsed_data->new_sub_sequence; + + /* + * Update the base view common sequence info + * with the number of views that the stream has. + * Otherwise the number of views is inconsistent + * between base view sequence and dependent view + * sequences. Also base view sequence appears + * with one view and the driver calculates the + * wrong number of resources. + */ + preparsed_data->sequ_hdr_info.com_sequ_hdr_info.num_views = + preparsed_data->ext_sequ_hdr_info.com_sequ_hdr_info.num_views; + } + + /* Signal if this picture is the first in a closed GOP */ + if (pict_ctx->closed_gop) { + preparsed_data->closed_gop = 1; + preparsed_data->sequ_hdr_info.com_sequ_hdr_info.not_dpb_flush = + str_ctx->inter_pict_data.not_dpb_flush; + } + + /* + * Signal "new picture" and its common header + * information. + */ + preparsed_data->new_picture = 1; + if (pict_ctx->sequ_hdr_info) { + preparsed_data->picture_data.sequ_hdr_id = + pict_ctx->sequ_hdr_info->sequ_hdr_info.sequ_hdr_id; + } + preparsed_data->picture_data.pict_hdr_info = pict_ctx->pict_hdr_info[0]; + + preparsed_data->picture_data.pict_hdr_info.first_pic_of_sequence = + preparsed_data->new_sequence; + if (contig_buf_info) + preparsed_data->picture_data.pict_hdr_info.fragmented_data = 1; + else + preparsed_data->picture_data.pict_hdr_info.fragmented_data = 0; + + str_ctx->sequ_hdr_id = preparsed_data->picture_data.sequ_hdr_id; + + pict_ctx->new_pict_signalled = 1; + + /* + * aso/fmo supported only when a frame is submitted as + * a whole + */ + if (parse_state->discontinuous_mb && !end_of_pic) + result = IMG_ERROR_NOT_SUPPORTED; + } else { + preparsed_data->new_fragment = 1; + + if (parse_state->discontinuous_mb) + result = IMG_ERROR_NOT_SUPPORTED; + } + + lst_init(&temp_list); + + segment = lst_removehead(&preparsed_data->picture_data.pict_seg_list[0]); + while (segment) { + lst_add(&temp_list, segment); + segment = lst_removehead(&preparsed_data->picture_data.pict_seg_list[0]); + } + + segment = lst_removehead(&str_ctx->inter_pict_data.pic_prefix_seg); + while (segment) { + lst_add(&preparsed_data->picture_data.pict_seg_list[0], + segment); + segment = lst_removehead(&str_ctx->inter_pict_data.pic_prefix_seg); + } + + segment = lst_removehead(&temp_list); + while (segment) { + lst_add(&preparsed_data->picture_data.pict_seg_list[0], + segment); + segment = lst_removehead(&temp_list); + } + + for (i = 0; i < VDEC_H264_MVC_MAX_VIEWS; i++) { + unsigned int j; + struct bspp_picture_data *ext_pic_data = + &preparsed_data->ext_pictures_data[i]; + + if (preparsed_data->ext_pictures_data[i].is_prefix) { + for (j = 0; j < BSPP_MAX_PICTURES_PER_BUFFER; + j++) { + segment = lst_removehead(&ext_pic_data->pict_seg_list[j]); + while (segment) { + lst_add(&str_ctx->inter_pict_data.pic_prefix_seg, + segment); + segment = lst_removehead + (&ext_pic_data->pict_seg_list[j]); + } + } + preparsed_data->num_ext_pictures--; + break; + } + } + } else if (pict_ctx->present && pict_ctx->sequ_hdr_info) { + /* + * Reduce the reference count since this picture will not be + * decoded. + */ + pict_ctx->sequ_hdr_info->ref_count--; + /* Release sequence data. */ + if (str_ctx->parser_callbacks.release_data_cb) { + str_ctx->parser_callbacks.release_data_cb((void *)&str_ctx->str_alloc, + BSPP_UNIT_SEQUENCE, + pict_ctx->sequ_hdr_info->secure_sequence_info); + } + } + + /* Reset the group bitstream context */ + lst_init(&str_ctx->grp_bstr_ctx.buffer_chain); + memset(&str_ctx->grp_bstr_ctx, 0, sizeof(str_ctx->grp_bstr_ctx)); + + /* + * for now: return IMG_ERROR_NOT_SUPPORTED only if explicitly set by + * parser + */ + result = (result == IMG_ERROR_NOT_SUPPORTED) ? + IMG_ERROR_NOT_SUPPORTED : IMG_SUCCESS; + + if (end_of_pic) + parse_state->initialised = 0; + + return result; + +error: + /* Free the SWSR list of buffers */ + while (lst_first(&str_ctx->grp_bstr_ctx.in_flight_bufs)) + lst_removehead(&str_ctx->grp_bstr_ctx.in_flight_bufs); + + return result; +} + +/* + * @Function bspp_stream_destroy + * + */ +int bspp_stream_destroy(void *str_context_handle) +{ + struct bspp_str_context *str_ctx = (struct bspp_str_context *)str_context_handle; + unsigned int i; + unsigned int sps_id; + unsigned int pps_id; + struct bspp_sequence_hdr_info *sequ_hdr_info; + struct bspp_pps_info *pps_info; + unsigned int result; + + /* Validate input arguments. */ + if (!str_context_handle) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + swsr_deinitialise(str_ctx->swsr_ctx.swsr_context); + + /* + * Service all the picture decoded events and free any unused + * resources. + */ + bspp_service_pictures_decoded(str_ctx); + for (sps_id = 0; sps_id < SEQUENCE_SLOTS; sps_id++) + bspp_remove_unused_sequence(str_ctx, sps_id); + + if (str_ctx->vid_std_features.uses_pps) { + for (pps_id = 0; pps_id < PPS_SLOTS; pps_id++) + bspp_remove_unused_pps(str_ctx, pps_id); + } + + if (str_ctx->vid_std_features.uses_vps) { + struct bspp_vps_info *vps_info; + + for (i = 0; i < VPS_SLOTS; ++i) { + vps_info = lst_removehead(&str_ctx->str_alloc.vps_data_list[i]); + + if (vps_info) + lst_add(&str_ctx->str_alloc.available_vps_list, vps_info); + + /* + * when we are done with the stream we should have MAXIMUM 1 VPS + * per slot, so after removing this one we should have none + * In case of "decodenframes" this is not true because we send more + * pictures for decode than what we expect to receive back, which + * means that potentially additional sequences/PPS are in the list + */ + vps_info = lst_removehead(&str_ctx->str_alloc.vps_data_list[i]); + if (vps_info) { + do { + lst_add(&str_ctx->str_alloc.available_vps_list, vps_info); + vps_info = + lst_removehead(&str_ctx->str_alloc.vps_data_list[i]); + } while (vps_info); + } + VDEC_ASSERT(lst_empty(&str_ctx->str_alloc.vps_data_list[i])); + } + + vps_info = NULL; + for (i = 0; i < MAX_VPSS; ++i) { + VDEC_ASSERT(!lst_empty(&str_ctx->str_alloc.available_vps_list)); + vps_info = lst_removehead(&str_ctx->str_alloc.available_vps_list); + if (vps_info) { + kfree(vps_info->secure_vpsinfo); + kfree(vps_info); + } else { + VDEC_ASSERT(vps_info); + pr_err("vps still active at shutdown\n"); + } + } + VDEC_ASSERT(lst_empty(&str_ctx->str_alloc.available_vps_list)); + } + + /* Free the memory required for this stream. */ + for (i = 0; i < SEQUENCE_SLOTS; i++) { + sequ_hdr_info = lst_removehead(&str_ctx->str_alloc.sequence_data_list[i]); + if (sequ_hdr_info) { + if (str_ctx->parser_callbacks.release_data_cb) + str_ctx->parser_callbacks.release_data_cb + ((void *)&str_ctx->str_alloc, + BSPP_UNIT_SEQUENCE, + sequ_hdr_info->secure_sequence_info); + lst_add(&str_ctx->str_alloc.available_sequence_list, + sequ_hdr_info); + } + + /* + * when we are done with the stream we should have MAXIMUM 1 + * sequence per slot, so after removing this one we should have + * none In case of "decoded frames" this is not true because we + * send more pictures for decode than what we expect to receive + * back, which means that potentially additional sequences/PPS + * are in the list + */ + sequ_hdr_info = lst_removehead(&str_ctx->str_alloc.sequence_data_list[i]); + if (sequ_hdr_info) { + unsigned int count_extra_sequences = 0; + + do { + count_extra_sequences++; + if (str_ctx->parser_callbacks.release_data_cb) { + str_ctx->parser_callbacks.release_data_cb + ((void *)&str_ctx->str_alloc, + BSPP_UNIT_SEQUENCE, + sequ_hdr_info->secure_sequence_info); + } + lst_add(&str_ctx->str_alloc.available_sequence_list, + sequ_hdr_info); + sequ_hdr_info = + lst_removehead(&str_ctx->str_alloc.sequence_data_list[i]); + } while (sequ_hdr_info); + } + } + + if (str_ctx->vid_std_features.uses_pps) { + for (i = 0; i < PPS_SLOTS; i++) { + pps_info = lst_removehead(&str_ctx->str_alloc.pps_data_list[i]); + if (pps_info) + lst_add(&str_ctx->str_alloc.available_ppss_list, pps_info); + + /* + * when we are done with the stream we should have + * MAXIMUM 1 PPS per slot, so after removing this one + * we should have none + * In case of "decodedframes" this is not true because + * we send more pictures for decode than what we expect + * to receive back, which means that potentially + * additional sequences/PPS are in the list + */ + pps_info = lst_removehead(&str_ctx->str_alloc.pps_data_list[i]); + if (pps_info) { + unsigned int count_extra_ppss = 0; + + do { + count_extra_ppss++; + lst_add(&str_ctx->str_alloc.available_ppss_list, + pps_info); + pps_info = + lst_removehead(&str_ctx->str_alloc.pps_data_list[i]); + } while (pps_info); + } + } + } + + for (i = 0; i < MAX_SEQUENCES; i++) { + sequ_hdr_info = lst_removehead(&str_ctx->str_alloc.available_sequence_list); + if (sequ_hdr_info && str_ctx->parser_callbacks.destroy_data_cb) + str_ctx->parser_callbacks.destroy_data_cb + (BSPP_UNIT_SEQUENCE, sequ_hdr_info->secure_sequence_info); + } + + kfree(str_ctx->secure_sequence_info); + str_ctx->secure_sequence_info = NULL; + kfree(str_ctx->sequ_hdr_info); + str_ctx->sequ_hdr_info = NULL; + + if (str_ctx->vid_std_features.uses_pps) { + for (i = 0; i < MAX_PPSS; i++) { + pps_info = lst_removehead(&str_ctx->str_alloc.available_ppss_list); + if (pps_info && str_ctx->parser_callbacks.destroy_data_cb) + str_ctx->parser_callbacks.destroy_data_cb + (BSPP_UNIT_PPS, pps_info->secure_pps_info); + } + + kfree(str_ctx->secure_pps_info); + str_ctx->secure_pps_info = NULL; + kfree(str_ctx->pps_info); + str_ctx->pps_info = NULL; + } + + /* destroy mutex */ + mutex_destroy(str_ctx->bspp_mutex); + kfree(str_ctx->bspp_mutex); + str_ctx->bspp_mutex = NULL; + + kfree(str_ctx); + + return IMG_SUCCESS; +error: + return result; +} + +/* + * @Function bspp_set_codec_config + * + */ +int bspp_set_codec_config(const void *str_context_handle, + const struct vdec_codec_config *codec_config) +{ + struct bspp_str_context *str_ctx = (struct bspp_str_context *)str_context_handle; + unsigned int result = IMG_SUCCESS; + + /* Validate input arguments. */ + if (!str_context_handle || !codec_config) { + result = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + + switch (str_ctx->vid_std) { + default: + result = IMG_ERROR_NOT_SUPPORTED; + break; + } +error: + return result; +} + +/* + * @Function bspp_stream_create + * + */ +int bspp_stream_create(const struct vdec_str_configdata *str_config_data, + void **str_ctx_handle, + struct bspp_ddbuf_array_info fw_sequence[], + struct bspp_ddbuf_array_info fw_pps[]) +{ + struct bspp_str_context *str_ctx; + unsigned int result = IMG_SUCCESS; + unsigned int i; + struct bspp_sequence_hdr_info *sequ_hdr_info; + struct bspp_pps_info *pps_info; + struct bspp_parse_state *parse_state; + + /* Allocate a stream structure */ + str_ctx = kmalloc(sizeof(*str_ctx), GFP_KERNEL); + if (!str_ctx) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(str_ctx, 0, sizeof(*str_ctx)); + + /* Initialise the stream context structure. */ + str_ctx->sequ_hdr_id = BSPP_INVALID; + str_ctx->vid_std = str_config_data->vid_std; + str_ctx->bstr_format = str_config_data->bstr_format; + str_ctx->disable_mvc = str_config_data->disable_mvc; + str_ctx->full_scan = str_config_data->full_scan; + str_ctx->immediate_decode = str_config_data->immediate_decode; + str_ctx->intra_frame_closed_gop = str_config_data->intra_frame_closed_gop; + + parse_state = &str_ctx->parse_state; + + /* Setup group buffer processing state. */ + parse_state->inter_pict_ctx = &str_ctx->inter_pict_data; + parse_state->prev_bottom_pic_flag = (unsigned char)BSPP_INVALID; + parse_state->next_pic_is_new = 1; + parse_state->prev_frame_num = BSPP_INVALID; + parse_state->second_field_flag = 0; + + lst_init(&str_ctx->grp_bstr_ctx.buffer_chain); + + if (str_ctx->vid_std < VDEC_STD_MAX && parser_fxns[str_ctx->vid_std].set_parser_config) { + parser_fxns[str_ctx->vid_std].set_parser_config(str_ctx->bstr_format, + &str_ctx->vid_std_features, + &str_ctx->swsr_ctx, + &str_ctx->parser_callbacks, + &str_ctx->inter_pict_data); + } else { + result = IMG_ERROR_NOT_SUPPORTED; + goto error; + } + + /* Allocate the memory required for this stream for Sequence/PPS info */ + lst_init(&str_ctx->str_alloc.available_sequence_list); + + str_ctx->sequ_hdr_info = kmalloc((MAX_SEQUENCES * sizeof(struct bspp_sequence_hdr_info)), + GFP_KERNEL); + if (!str_ctx->sequ_hdr_info) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(str_ctx->sequ_hdr_info, 0x00, + (MAX_SEQUENCES * sizeof(struct bspp_sequence_hdr_info))); + + str_ctx->secure_sequence_info = + kmalloc((MAX_SEQUENCES * str_ctx->vid_std_features.seq_size), + GFP_KERNEL); + if (!str_ctx->secure_sequence_info) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(str_ctx->secure_sequence_info, 0x00, + (MAX_SEQUENCES * str_ctx->vid_std_features.seq_size)); + + sequ_hdr_info = (struct bspp_sequence_hdr_info *)(str_ctx->sequ_hdr_info); + for (i = 0; i < MAX_SEQUENCES; i++) { + /* Deal with the device memory for FW SPS data */ + sequ_hdr_info->fw_sequence = fw_sequence[i]; + sequ_hdr_info->sequ_hdr_info.bufmap_id = + fw_sequence[i].ddbuf_info.bufmap_id; + sequ_hdr_info->sequ_hdr_info.buf_offset = + fw_sequence[i].buf_offset; + sequ_hdr_info->secure_sequence_info = (void *)(str_ctx->secure_sequence_info + + (i * str_ctx->vid_std_features.seq_size)); + + lst_add(&str_ctx->str_alloc.available_sequence_list, + sequ_hdr_info); + sequ_hdr_info++; + } + + if (str_ctx->vid_std_features.uses_pps) { + lst_init(&str_ctx->str_alloc.available_ppss_list); + str_ctx->pps_info = kmalloc((MAX_PPSS * sizeof(struct bspp_pps_info)), GFP_KERNEL); + if (!str_ctx->pps_info) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(str_ctx->pps_info, 0x00, (MAX_PPSS * sizeof(struct bspp_pps_info))); + str_ctx->secure_pps_info = kmalloc((MAX_PPSS * str_ctx->vid_std_features.pps_size), + GFP_KERNEL); + if (!str_ctx->secure_pps_info) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(str_ctx->secure_pps_info, 0x00, + (MAX_PPSS * str_ctx->vid_std_features.pps_size)); + + pps_info = (struct bspp_pps_info *)(str_ctx->pps_info); + for (i = 0; i < MAX_PPSS; i++) { + /* Deal with the device memory for FW PPS data */ + pps_info->fw_pps = fw_pps[i]; + pps_info->bufmap_id = fw_pps[i].ddbuf_info.bufmap_id; + pps_info->buf_offset = fw_pps[i].buf_offset; + + /* + * We have no container for the PPS that passes down to the kernel, + * for this reason the h264 secure parser needs to populate that + * info into the picture header (Second)PictAuxData. + */ + pps_info->secure_pps_info = (void *)(str_ctx->secure_pps_info + (i * + str_ctx->vid_std_features.pps_size)); + + lst_add(&str_ctx->str_alloc.available_ppss_list, pps_info); + pps_info++; + } + + /* As only standards that use PPS also use VUI, initialise + * the appropriate data structures here. + * Initialise the list of raw bitstream data containers. + */ + lst_init(&str_ctx->str_alloc.raw_data_list_available); + lst_init(&str_ctx->str_alloc.raw_data_list_used); + } + + if (str_ctx->vid_std_features.uses_vps) { + struct bspp_vps_info *vps_info; + + lst_init(&str_ctx->str_alloc.available_vps_list); + for (i = 0; i < MAX_VPSS; ++i) { + vps_info = kmalloc(sizeof(*vps_info), GFP_KERNEL); + VDEC_ASSERT(vps_info); + if (!vps_info) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + + memset(vps_info, 0x00, sizeof(struct bspp_vps_info)); + /* + * for VPS we do not allocate device memory since (at least for now) + * there is no need to pass any data from VPS directly to FW + */ + /* Allocate memory for BSPP local VPS data structure. */ + vps_info->secure_vpsinfo = + kmalloc(str_ctx->vid_std_features.vps_size, GFP_KERNEL); + + VDEC_ASSERT(vps_info->secure_vpsinfo); + if (!vps_info->secure_vpsinfo) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + memset(vps_info->secure_vpsinfo, 0, str_ctx->vid_std_features.vps_size); + + lst_add(&str_ctx->str_alloc.available_vps_list, vps_info); + } + } + + /* ... and initialise the lists that will use this data */ + for (i = 0; i < SEQUENCE_SLOTS; i++) + lst_init(&str_ctx->str_alloc.sequence_data_list[i]); + + if (str_ctx->vid_std_features.uses_pps) + for (i = 0; i < PPS_SLOTS; i++) + lst_init(&str_ctx->str_alloc.pps_data_list[i]); + + str_ctx->bspp_mutex = kzalloc(sizeof(*str_ctx->bspp_mutex), GFP_KERNEL); + if (!str_ctx->bspp_mutex) { + result = -ENOMEM; + goto error; + } + mutex_init(str_ctx->bspp_mutex); + + /* Initialise the software shift-register */ + swsr_initialise(bspp_exception_handler, &str_ctx->parse_ctx, + (swsr_callback_fxn) bspp_shift_reg_cb, + &str_ctx->grp_bstr_ctx, + &str_ctx->swsr_ctx.swsr_context); + + /* Setup the parse context */ + str_ctx->parse_ctx.swsr_context = str_ctx->swsr_ctx.swsr_context; + + *str_ctx_handle = str_ctx; + + return IMG_SUCCESS; + +error: + if (str_ctx) { + kfree(str_ctx->sequ_hdr_info); + kfree(str_ctx->secure_sequence_info); + kfree(str_ctx->pps_info); + kfree(str_ctx->secure_pps_info); + kfree(str_ctx); + } + + return result; +} + +void bspp_freeraw_sei_datacontainer(const void *str_res, + struct vdec_raw_bstr_data *rawsei_datacontainer) +{ + struct bspp_raw_sei_alloc *rawsei_alloc = NULL; + + /* Check input params. */ + if (str_res && rawsei_datacontainer) { + struct bspp_stream_alloc_data *alloc_data = + (struct bspp_stream_alloc_data *)str_res; + + rawsei_alloc = container_of(rawsei_datacontainer, + struct bspp_raw_sei_alloc, + raw_sei_data); + memset(&rawsei_alloc->raw_sei_data, 0, sizeof(rawsei_alloc->raw_sei_data)); + lst_remove(&alloc_data->raw_sei_alloc_list, rawsei_alloc); + kfree(rawsei_alloc); + } +} + +void bspp_freeraw_sei_datalist(const void *str_res, struct vdec_raw_bstr_data *rawsei_datalist) +{ + /* Check input params. */ + if (rawsei_datalist && str_res) { + struct vdec_raw_bstr_data *sei_raw_datacurr = NULL; + + /* Start fromm the first element... */ + sei_raw_datacurr = rawsei_datalist; + /* Free all the linked raw SEI data containers. */ + while (sei_raw_datacurr) { + struct vdec_raw_bstr_data *seiraw_datanext = + sei_raw_datacurr->next; + bspp_freeraw_sei_datacontainer(str_res, sei_raw_datacurr); + sei_raw_datacurr = seiraw_datanext; + } + } +} + +void bspp_streamrelese_rawbstrdataplain(const void *str_res, const void *rawdata) +{ + struct bspp_stream_alloc_data *str_alloc = + (struct bspp_stream_alloc_data *)str_res; + struct bspp_raw_bitstream_data *rawbstrdata = + (struct bspp_raw_bitstream_data *)rawdata; + + if (rawbstrdata) { + /* Decrement the raw bitstream data reference count. */ + rawbstrdata->ref_count--; + /* If no entity is referencing the raw + * bitstream data any more + */ + if (rawbstrdata->ref_count == 0) { + /* ... free the raw bistream data buffer... */ + kfree(rawbstrdata->raw_bitstream_data.data); + memset(&rawbstrdata->raw_bitstream_data, 0, + sizeof(rawbstrdata->raw_bitstream_data)); + /* ...and return it to the list. */ + lst_remove(&str_alloc->raw_data_list_used, rawbstrdata); + lst_add(&str_alloc->raw_data_list_available, rawbstrdata); + } + } +} + +struct bspp_vps_info *bspp_get_vpshdr(void *str_res, unsigned int vps_id) +{ + struct bspp_stream_alloc_data *alloc_data = + (struct bspp_stream_alloc_data *)str_res; + + if (vps_id >= VPS_SLOTS || !alloc_data) + return NULL; + + return lst_last(&alloc_data->vps_data_list[vps_id]); +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/bspp.h b/drivers/media/platform/vxe-vxd/decoder/bspp.h --- a/drivers/media/platform/vxe-vxd/decoder/bspp.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/bspp.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,363 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Bitstream Buffer Pre-Parser + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ +#ifndef __BSPP_H__ +#define __BSPP_H__ + +#include + +#include "h264fw_data.h" +#include "lst.h" +#include "vdec_defs.h" + +/* + * There are up to 2 pictures in each buffer + * (plus trailing data for the next picture, e.g. PPS). + */ +#define BSPP_MAX_PICTURES_PER_BUFFER 3 + +#define BSPP_INVALID ((unsigned int)(-1)) + +/* + * This enables signalling of closed gop at every I frame. Add resilience to + * seeking functionality. + */ +#define I_FRAME_SIGNALS_CLOSED_GOP + +/* + * enum bspp_error_type - enumeration of parsing error , different error flag + * for different data unit + */ +enum bspp_error_type { + /* No Error in parsing. */ + BSPP_ERROR_NONE = (0), + /* Correction in VSH, Replaced VSH with faulty one */ + BSPP_ERROR_CORRECTION_VSH = (1 << 0), + /* + * Correction in parsed Value, clamp the value if it goes beyond + * the limit + */ + BSPP_ERROR_CORRECTION_VALIDVALUE = (1 << 1), + /* Error in Aux data (i.e. PPS in H.264) parsing */ + BSPP_ERROR_AUXDATA = (1 << 2), + /* Error in parsing, more data remains in VSH data unit after parsing */ + BSPP_ERROR_DATA_REMAINS = (1 << 3), + /* Error in parsing, parsed codeword is invalid */ + BSPP_ERROR_INVALID_VALUE = (1 << 4), + /* Error in parsing, parsing error */ + BSPP_ERROR_DECODE = (1 << 5), + /* reference frame is not available for decoding */ + BSPP_ERROR_NO_REF_FRAME = (1 << 6), + /* Non IDR frame loss detected */ + BSPP_ERROR_NONIDR_FRAME_LOSS = (1 << 7), + /* IDR frame loss detected */ + BSPP_ERROR_IDR_FRAME_LOSS = (1 << 8), + /* Error in parsing, insufficient data to complete parsing */ + BSPP_ERROR_INSUFFICIENT_DATA = (1 << 9), + /* Severe Error, Error indicates, no support for this picture data */ + BSPP_ERROR_UNSUPPORTED = (1 << 10), + /* Severe Error, Error in which could not be recovered */ + BSPP_ERROR_UNRECOVERABLE = (1 << 11), + /* Severe Error, to indicate that NAL Header is absent after SCP */ + BSPP_ERROR_NO_NALHEADER = (1 << 12), + BSPP_ERROR_NO_SEQUENCE_HDR = (1 << 13), + BSPP_ERROR_SIGNALED_IN_STREAM = (1 << 14), + BSPP_ERROR_UNKNOWN_DATAUNIT_DETECTED = (1 << 15), + BSPP_ERROR_NO_PPS = (1 << 16), + BSPP_ERROR_NO_VPS = (1 << 17), + BSPP_ERROR_OUT_OF_MEMORY = (1 << 18), + /* The shift value of the last error bit */ + BSPP_ERROR_MAX_SHIFT = 18, + BSPP_ERROR_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * struct bspp_ddbuf_info - Buffer info + * @buf_size: The size of the buffer (in bytes) + * @cpu_virt_addr: The CPU virtual address (mapped into the local cpu MMU) + * @mem_attrib: Memory attributes + * @bufmap_id: buffer mappind id + */ +struct bspp_ddbuf_info { + unsigned int buf_size; + void *cpu_virt_addr; + enum sys_emem_attrib mem_attrib; + unsigned int buf_id; + unsigned int bufmap_id; +}; + +/* + * struct bspp_ddbuf_array_info - Buffer array info + * @ddbuf_info: Buffer info (container) + * @buf_element_size: Size of each element + * @buf_offset: Offset for each element + */ +struct bspp_ddbuf_array_info { + struct bspp_ddbuf_info ddbuf_info; + unsigned int buf_element_size; + unsigned int buf_offset; +}; + +/** + * struct bspp_bitstr_seg - Bitstream segment + * @lst_padding: + * @data_size: Size of data + * @data_byte_offset: Offset for data + * @bstr_seg_flag: flag indicates the bitstream segment type + * @start_code_suffix: start code prefix + * @bufmap_id: Buffer map ID + */ +struct bspp_bitstr_seg { + void *lst_padding; + unsigned int data_size; + unsigned int data_byte_offset; + unsigned int bstr_seg_flag; + unsigned char start_code_suffix; + unsigned int bufmap_id; +}; + +/* + * struct bspp_pict_data - Picture Header Data Information + * @bufmap_id: Buffer ID to use inside kernel #VXDIO_sDdBufInfo + * @buf_offset: Buffer offset (for packed device buffers, e.g. PPS) + * @pic_data: Picture data + * @size: Size (in bytes) of data. + * @data_id: Data identifier. + */ +struct bspp_pict_data { + unsigned int bufmap_id; + unsigned int buf_offset; + void *pic_data; + unsigned int size; + unsigned int id; +}; + +/* + * struct bspp_pict_hdr_info - Picture Header Information + */ +struct bspp_pict_hdr_info { + /* + * Picture is entirely intra-coded and doesn't use any reference data. + * NOTE: should be IMG_FALSE if this cannot be determined. + */ + int intra_coded; + /* Picture might be referenced by subsequent pictures. */ + int ref; + /* Picture is a field as part of a frame. */ + int field; + /* Emulation prevention bytes are present in picture data. */ + int emulation_prevention; + /* Post Processing */ + int post_processing; + /* Macroblocks within the picture may not occur in raster-scan order */ + int discontinuous_mbs; + /* Flag to indicate data is span across mulitple buffer. */ + int fragmented_data; + /* SOS fields count value */ + unsigned char sos_count; + /* This picture is the first of the sequence or not */ + int first_pic_of_sequence; + + enum vdecfw_parsermode parser_mode; + /* Total size of picture data which is going to be submitted. */ + unsigned int pic_data_size; + /* Size of coded frame as specified in the bitstream. */ + struct vdec_pict_size coded_frame_size; + /* Display information for picture */ + struct vdec_pict_disp_info disp_info; + + /* Picture auxiliary data (e.g. H.264 SPS/PPS) */ + struct bspp_pict_data pict_aux_data; + /* Picture auxiliary data (e.g. H.264 SPS/PPS) for 2nd picture */ + struct bspp_pict_data second_pict_aux_data; + /* Slice group-map data. */ + struct bspp_pict_data pict_sgm_data; +#ifdef HAS_JPEG + /* JPEG specific picture header information.*/ + struct vdec_jpeg_pict_hdr_info jpeg_pict_hdr_info; +#endif + + struct h264_pict_hdr_info { + void *raw_vui_data; + void *raw_sei_data_list_first_field; + void *raw_sei_data_list_second_field; + unsigned char nal_ref_idc; + unsigned short frame_num; + } h264_pict_hdr_info; + + struct { /* HEVC specific frame information.*/ + int range_ext_present; + int is_full_range_ext; + void *raw_vui_data; + void *raw_sei_datalist_firstfield; + void *raw_sei_datalist_secondfield; + } hevc_pict_hdr_info; +}; + +/* + * struct bspp_sequ_hdr_info - Sequence header information + */ +struct bspp_sequ_hdr_info { + unsigned int sequ_hdr_id; + unsigned int ref_count; + struct vdec_comsequ_hdrinfo com_sequ_hdr_info; + unsigned int bufmap_id; + unsigned int buf_offset; +}; + +/* + * struct bspp_picture_data - Picture data + */ +struct bspp_picture_data { + /* Anonymous */ + /* + * Bitstream segments that contain other (non-picture) data before + * the picture in the buffer (elements of type #VDECDD_sBitStrSeg). + */ + struct lst_t pre_pict_seg_list[BSPP_MAX_PICTURES_PER_BUFFER]; + /* Picture */ + unsigned int sequ_hdr_id; + struct bspp_pict_hdr_info pict_hdr_info; + /* + * Bitstream segments that contain picture data, one for each field + * (if present in same group of buffers (elements of type + * #VDECDD_sBitStrSeg). + */ + struct lst_t pict_seg_list[BSPP_MAX_PICTURES_PER_BUFFER]; + void *pict_tag_param[BSPP_MAX_PICTURES_PER_BUFFER]; + int is_prefix; +}; + +/* + * struct bspp_preparsed_data - Pre-parsed buffer information + */ +struct bspp_preparsed_data { + /* Sequence */ + int new_sequence; + struct bspp_sequ_hdr_info sequ_hdr_info; + int sequence_end; + + /* Closed GOP */ + int closed_gop; + + /* Picture */ + int new_picture; + int new_fragment; + struct bspp_picture_data picture_data; + + /* Additional pictures (MVC extension) */ + int new_sub_sequence; + struct bspp_sequ_hdr_info ext_sequ_hdr_info; + /* non-base view pictures + picture prefix for next frame */ + struct bspp_picture_data ext_pictures_data[VDEC_H264_MVC_MAX_VIEWS]; + unsigned int num_ext_pictures; + + /* + * Additional information + * Flags word to indicate error in parsing/decoding - see + * #VDEC_eErrorType + */ + unsigned int error_flags; +}; + +/* + * struct bspp_picture_decoded - used to store picture-decoded information for + * resource handling (sequences/PPSs) + */ +struct bspp_picture_decoded { + void **lst_link; + unsigned int sequ_hdr_id; + unsigned int pps_id; + unsigned int second_pps_id; + int not_decoded; + struct vdec_raw_bstr_data *sei_raw_data_first_field; + struct vdec_raw_bstr_data *sei_raw_data_second_field; +}; + +/* + * @Function bspp_stream_create + * @Description Creates a stream context for which to pre-parse bitstream + * buffers. The following allocations will take place: + * - Local storage for high-level header parameters (secure) + * - Host memory for common sequence information (insecure) + * - Device memory for Sequence information (secure) + * - Device memory for PPS (secure, H.264 only) + * @Input vdec_str_configdata : config data corresponding to bitstream + * @Output str_context : A pointer used to return the stream context handle + * @Input fw_sequ: FW sequence data + * @Input fw_pps: FW pps data + * @Return This function returns either IMG_SUCCESS or an error code. + */ +int bspp_stream_create(const struct vdec_str_configdata *str_config_data, + void **str_context, + struct bspp_ddbuf_array_info fw_sequ[], + struct bspp_ddbuf_array_info fw_pps[]); + +/* + * @Function bspp_set_codec_config + * @Description This function is used to set the out-of-band codec config data. + * @Input str_context_handle : Stream context handle. + * @Input codec_config : Codec-config data + * @Return This function returns either IMG_SUCCESS or an error code. + */ +int bspp_set_codec_config(const void *str_context_handle, + const struct vdec_codec_config *codec_config); + +/* + * @Function bspp_stream_destroy + * @Description Destroys a stream context used to pre-parse bitstream buffers. + * @Input str_context_handle : Stream context handle. + * @Return This function returns either IMG_SUCCESS or an error code. + */ +int bspp_stream_destroy(void *str_context_handle); + +/* + * @Function bspp_submit_picture_decoded + */ +int bspp_submit_picture_decoded(void *str_context_handle, + struct bspp_picture_decoded *picture_decoded); + +/* + * @Function bspp_stream_submit_buffer + */ +int bspp_stream_submit_buffer(void *str_context_handle, + const struct bspp_ddbuf_info *ddbuf_info, + unsigned int bufmap_id, + unsigned int data_size, + void *pict_tag_param, + enum vdec_bstr_element_type bstr_element_type); + +/* + * @Function bspp_stream_preparse_buffers + * @Description Pre-parses bistream buffer and returns picture information in + * structure that also signals when the buffer is last in picture. + * @Input str_context_handle: Stream context handle. + * @Input contiguous_buf_info : Contiguous buffer information + * multiple segments that may be non contiguous in memory + * @Input contiguous_buf_map_id : Contiguous Buffer Map id + * @Input segments: Pointer to a list of segments (see #VDECDD_sBitStrSeg) + * @Output preparsed_data: Container to return picture information. Only + * provide when buffer is last in picture (see #bForceEop in + * function #VDEC_StreamSubmitBstrBuf) + * @Output eos_flag: flag indicates end of stream + * @Return int : This function returns either IMG_SUCCESS or an error code. + */ +int bspp_stream_preparse_buffers + (void *str_context_handle, + const struct bspp_ddbuf_info *contiguous_buf_info, + unsigned int contiguous_buf_map_id, + struct lst_t *segments, + struct bspp_preparsed_data *preparsed_data, + int eos_flag); + +#endif /* __BSPP_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/bspp_int.h b/drivers/media/platform/vxe-vxd/decoder/bspp_int.h --- a/drivers/media/platform/vxe-vxd/decoder/bspp_int.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/bspp_int.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,514 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Bitstream Buffer Pre-Parser Internal + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#ifndef __BSPP_INT_H__ +#define __BSPP_INT_H__ + +#include "bspp.h" +#include "swsr.h" + +#define VDEC_MB_DIMENSION (16) +#define MAX_COMPONENTS (4) + +#define print_value(a, ...) + +#define BSPP_DEFAULT_SEQUENCE_ID (0) + +enum bspp_unit_type { + BSPP_UNIT_NONE = 0, + /* Only relevant for HEVC. */ + BSPP_UNIT_VPS, + /* Only relevant for h.264 and HEVC */ + BSPP_UNIT_SEQUENCE, BSPP_UNIT_PPS, + /* + * !< Data from these units should be placed in non-picture bitstream + * segment lists. In conformant streams these units should not occur + * in-between the picture data. + */ + BSPP_UNIT_PICTURE, + BSPP_UNIT_SKIP_PICTURE, + BSPP_UNIT_NON_PICTURE, + BSPP_UNIT_UNCLASSIFIED, + /* Unit is unsupported, don't change segment list */ + BSPP_UNIT_UNSUPPORTED, + BSPP_UNIT_MAX, + BSPP_UNIT_FORCE32BITS = 0x7FFFFFFFU +}; + +struct bspp_raw_bitstream_data { + void **lst_link; + unsigned int ref_count; + struct vdec_raw_bstr_data raw_bitstream_data; +}; + +/* + * struct bspp_h264_inter_pict_ctx + * @Brief: This structure contains H264 state to be retained between pictures. + */ +struct bspp_h264_inter_pict_ctx { + /* + * The following get applied to every picture until updated + * (bitstream properties) + */ + int disable_vdmc_filt; + int b4x4transform_mb_unavailable; + /* + * The following get applied to the next picture only + * (picture properties) + */ + int repeat_first_field; + unsigned int max_frm_repeat; + /* + * Control variable to decide when to attach the SEI info + * (picture properties) to a picture + */ + int sei_info_attached_to_pic; + /* + * The following variable is an approximation because we cannot + * parse out-of-order, it takes value as described: + * 1) Initially it is BSPP_INVALID + * 2) The first SPS sets it to its SPSid + * 3) The last bspp_H264SeiBufferingPeriod sets it, and it is used + * for every SEI parsing until updated by another + * bspp_H264SeiBufferingPeriod message + */ + unsigned int active_sps_for_sei_parsing; + unsigned short current_view_id; + struct vdec_raw_bstr_data *sei_raw_data_list; +}; + +/* This structure contains HEVC state to be retained between pictures. */ +struct bspp_hevc_inter_pict_ctx { + /* Picture count in a sequence */ + unsigned int seq_pic_count; + struct { + /* There was EOS NAL detected and no new picture yet */ + unsigned eos_detected : 1; + /* This is first picture after EOS NAL */ + unsigned first_after_eos : 1; + }; + + /* control variable to decide when to attach the SEI info + * (picture properties) to a picture. + */ + unsigned char sei_info_attached_to_pic; + /* Raw SEI list to be attached to a picture. */ + struct vdec_raw_bstr_data *sei_rawdata_list; + /* Handle to a picture header field to attach the raw SEI list to. */ + void **hndl_pichdr_sei_rawdata_list; +}; + +/* + * struct bspp_inter_pict_data + * @Brief This structure contains state to be retained between pictures. + */ +struct bspp_inter_pict_data { + /* A closed GOP has occurred in the bitstream. */ + int seen_closed_gop; + /* Closed GOP has been signaled by a unit before the next picture */ + int new_closed_gop; + /* Indicates whether or not DPB flush is needed */ + int not_dpb_flush; + struct lst_t pic_prefix_seg; + union { + struct bspp_h264_inter_pict_ctx h264_ctx; + struct bspp_hevc_inter_pict_ctx hevc_ctx; + }; +}; + +/* + * struct bspp_parse_state + * @Brief This structure contains parse state + */ +struct bspp_parse_state { + struct bspp_inter_pict_data *inter_pict_ctx; + int initialised; + + /* Input/Output (H264 etc. state). */ + /* For SCP ASO detection we need to log 3 components */ + unsigned int prev_first_mb_in_slice[MAX_COMPONENTS]; + struct bspp_pict_hdr_info *next_pict_hdr_info; + unsigned char prev_bottom_pic_flag; + unsigned char second_field_flag; + unsigned char next_pic_is_new; + unsigned int prev_frame_num; + unsigned int prev_pps_id; + unsigned int prev_field_pic_flag; + unsigned int prev_nal_ref_idc; + unsigned int prev_pic_order_cnt_lsb; + int prev_delta_pic_order_cnt_bottom; + int prev_delta_pic_order_cnt[2]; + int prev_nal_unit_type; + int prev_idr_pic_id; + int discontinuous_mb; + /* Position in bitstream before parsing a unit */ + unsigned long long prev_byte_offset_buf; + unsigned int prev_buf_map_id; + unsigned int prev_buf_data_size; + /* + * !< Flags word to indicate error in parsing/decoding + * - see #VDEC_eErrorType. + */ + unsigned int error_flags; + /* Outputs. */ + int new_closed_gop; + unsigned char new_view; + unsigned char is_prefix; + int first_chunk; +}; + +/* + * struct bspp_pps_info + * @Brief Contains PPS information + */ +struct bspp_pps_info { + void **lst_link; + /* PPS Id. INSECURE MEMORY HOST */ + unsigned int pps_id; + /* Reference count for PPS. INSECURE MEMORY HOST */ + unsigned int ref_count; + struct bspp_ddbuf_array_info fw_pps; + /* Buffer ID to be used in Kernel */ + unsigned int bufmap_id; + /* Parsing Info. SECURE MEMORY HOST */ + void *secure_pps_info; + /* Buffer Offset to be used in kernel */ + unsigned int buf_offset; +}; + +/* + * struct bspp_sequence_hdr_info + * @Brief Contains SPS information + */ +struct bspp_sequence_hdr_info { + void **lst_link; + /* Reference count for sequence header */ + unsigned int ref_count; + struct bspp_sequ_hdr_info sequ_hdr_info; + struct bspp_ddbuf_array_info fw_sequence; + /* Parsing Info. SECURE MEMORY HOST */ + void *secure_sequence_info; +}; + +enum bspp_element_status { + BSPP_UNALLOCATED = 0, + BSPP_AVAILABLE, + BSPP_UNAVAILABLE, + BSPP_STATUSMAX, + BSPP_FORCE32BITS = 0x7FFFFFFFU +}; + +struct bspp_vps_info { + void **lst_link; + /* VPS Id INSECURE MEMORY HOST */ + unsigned int vps_id; + /* Reference count for video header. INSECURE MEMORY HOST */ + unsigned int ref_count; + /*!< Parsing Info. SECURE MEMORY HOST */ + void *secure_vpsinfo; +}; + +/* + * struct bspp_unit_data + * @Brief Contains bitstream unit data + */ +struct bspp_unit_data { + /* Input. */ + /* Indicates which output data to populate */ + enum bspp_unit_type unit_type; + /* Video Standard of unit to parse */ + enum vdec_vid_std vid_std; + /* Indicates whether delimiter is present for unit */ + int delim_present; + /* Codec configuration used by this stream */ + const struct vdec_codec_config *codec_config; + void *str_res_handle; + /* Needed for calculating the size of the last fragment */ + unsigned int unit_data_size; + /* Input/Output. */ + struct bspp_parse_state *parse_state; + /* Output */ + /* eVidStd == VDEC_STD_H263 && BSPP_UNIT_PICTURE. */ + struct bspp_sequence_hdr_info *impl_sequ_hdr_info; + /* Union of output data for each of the unit types. */ + union { + /* BSPP_UNIT_SEQUENCE. */ + struct bspp_sequence_hdr_info *sequ_hdr_info; + /* BSPP_UNIT_PPS. */ + struct bspp_pps_info *pps_info; + /* BSPP_UNIT_PICTURE. */ + struct bspp_pict_hdr_info *pict_hdr_info; + /* For Video Header (HEVC) */ + struct bspp_vps_info *vps_info; + } out; + + /* + * For picture it should give the SequenceHdrId, for anything + * else it should contain BSPP_INVALID. This value is pre-loaded + * with the sequence ID of the last picture. + */ + unsigned int pict_sequ_hdr_id; + /* State: output. */ + /* + * Picture unit (BSPP_UNIT_PICTURE) contains slice data. + * Picture header information must be populated once this unit has been + * parsed. + */ + int slice; + int ext_slice; /* Current slice belongs to non-base view (MVC only) */ + /* + * True if we meet a unit that signifies closed gop, different + * for each standard. + */ + int new_closed_gop; + /* True if the end of a sequence of pictures has been reached. */ + int sequence_end; + /* + * Extracted all data from unit whereby shift-register should now + * be at the next delimiter or end of data (when byte-aligned). + */ + int extracted_all_data; + /* Indicates the presence of any errors while processing this unit. */ + enum bspp_error_type parse_error; + /* To turn on/off considering I-Frames as ClosedGop boundaries. */ + int intra_frm_as_closed_gop; +}; + +/* + * struct bspp_swsr_ctx + * @brief BSPP Software Shift Register Context Information + */ +struct bspp_swsr_ctx { + /* + * Default configuration for the shift-register for this + * stream. The delimiter type may be adjusted for each unit + * where the buffer requires it. Information about how to + * process each unit will be passed down with the picture + * header information. + */ + struct swsr_config sr_config; + /* + * Emulation prevention scheme present in bitstream. This is + * sometimes not ascertained (e.g. VC-1) until the first + * bitstream buffer (often codec configuration) has been + * received. + */ + enum swsr_emprevent emulation_prevention; + /* Software shift-register context. */ + void *swsr_context; +}; + +/* + * struct bspp_vid_std_features + * @brief BSPP Video Standard Specific Features and Information + */ +struct bspp_vid_std_features { + /* The size of the sequence header structure for this video standard */ + unsigned long seq_size; + /* This video standard uses Picture Parameter Sets. */ + int uses_pps; + /* + * The size of the Picture Parameter Sets structure for + * this video standard. + */ + unsigned long pps_size; + /* This video standard uses Video Parameter Sets. */ + int uses_vps; + /* + * The size of the Video Parameter Sets structure for + * this video standard + */ + unsigned long vps_size; +}; + +/* + * @Function bspp_cb_parse_unit + * @Description Function prototype for the parse unit callback functions. + * @Input swsr_context_handle: A handle to software shift-register context + * @InOut unit_data: A pointer to unit data which includes input & output + * parameters as defined by structure. + * @Return int : This function returns either IMG_SUCCESS or an error code. + */ +typedef int (*bspp_cb_parse_unit)(void *swsr_context_handle, + struct bspp_unit_data *unit_data); + +/* + * @Function bspp_pfnReleaseData + * @Description This is a function prototype for the data releasing callback + * functions. + * @Input str_alloc_handle : A handle to stream related resources. + * @Input data_type : A type of data which is to be released. + * @Input data_handle : A handle for data which is to be released. + * @Return int : This function returns either IMG_SUCCESS or an error code. + */ +typedef int (*bspp_cb_release_data)(void *str_alloc_handle, + enum bspp_unit_type data_type, + void *data_handle); + +/* + * @Function bspp_cb_reset_data + * @Description This is a function prototype for the data resetting callback + * functions. + * @Input data_type : A type of data which is to be reset. + * @InOut data_handle : A handle for data which is to be reset. + * @Return int : This function returns either IMG_SUCCESS or an error code. + */ +typedef int (*bspp_cb_reset_data)(enum bspp_unit_type data_type, + void *data_handle); + +/* + * @Function bspp_cb_destroy_data + * @Description This is a function prototype for the data destruction callback + * functions. + * @Input data_type : A type of data which is to be destroyed. + * @InOut data_handle : A handle for data which is to be destroyed. + * @Return int : This function returns either IMG_SUCCESS or an error code. + */ +typedef int (*bspp_cb_destroy_data)(enum bspp_unit_type data_type, + void *data_handle); + +/* + * @Function bspp_cb_parse_codec_config + * @Description This is a function prototype for parsing codec config bitstream + * element for size delimited bitstreams. + * @Input swsr_context_handle: A handle to Shift Register processing + * current bitstream. + * @Output unit_count: A pointer to variable in which to return unit count. + * @Output unit_array_count: A pointer to variable in which to return unit + * array count. + * @Output delim_length: A pointer to variable in which to return NAL + * delimiter length in bits. + * @Output size_delim_length: A pointer to variable in which to return size + * delimiter length in bits. + * @Return None. + */ +typedef void (*bspp_cb_parse_codec_config)(void *swsr_context_handle, + unsigned int *unit_count, + unsigned int *unit_array_count, + unsigned int *delim_length, + unsigned int *size_delim_length); + +/* + * @Function bspp_cb_update_unit_counts + * @Description This is a function prototype for updating unit counts for size + * delimited bitstreams. + * @Input swsr_context_handle: A handle to Shift Register processing + * current bitstream. + * @InOut unit_count: A pointer to variable holding current unit count + * @InOut unit_array_count: A pointer to variable holding current unit + * array count. + * @Return None. + */ +typedef void (*bspp_cb_update_unit_counts)(void *swsr_context_handle, + unsigned int *unit_count, + unsigned int *unit_array_count); + +/* + * @Function bspp_cb_initialise_parsing + * @Description This prototype is for unit group parsing initialization. + * @InOut parse_state: The current unit group parsing state. + * @Return None. + */ +typedef void (*bspp_cb_initialise_parsing)(struct bspp_parse_state *prs_state); + +/* + * @Function bspp_cb_finalise_parsing + * @Description This is prototype is for unit group parsing finalization. + * @Input str_alloc_handle: A handle to stream related resources. + * @InOut parse_state: The current unit group parsing state. + * @Return None. + */ +typedef void (*bspp_cb_finalise_parsing)(void *str_alloc_handle, + struct bspp_parse_state *parse_state); + +/* + * struct bspp_parser_callbacks + * @brief BSPP Standard Related Parser Callback Functions + */ +struct bspp_parser_callbacks { + /* Pointer to standard-specific unit parsing callback function. */ + bspp_cb_parse_unit parse_unit_cb; + /* Pointer to standard-specific data releasing callback function. */ + bspp_cb_release_data release_data_cb; + /* Pointer to standard-specific data resetting callback function. */ + bspp_cb_reset_data reset_data_cb; + /* Pointer to standard-specific data destruction callback function. */ + bspp_cb_destroy_data destroy_data_cb; + /* Pointer to standard-specific codec config parsing callback function */ + bspp_cb_parse_codec_config parse_codec_config_cb; + /* Pointer to standard-specific unit count updating callback function */ + bspp_cb_update_unit_counts update_unit_counts_cb; + /* + * Pointer to standard-specific unit group parsing initialization + * function. + */ + bspp_cb_initialise_parsing initialise_parsing_cb; + /* + * Pointer to standard-specific unit group parsing finalization + * function + */ + bspp_cb_finalise_parsing finalise_parsing_cb; +}; + +/* + * @Function bspp_cb_set_parser_config + * @Description Prototype is for the setting parser config callback functions. + * @Input bstr_format: Input bitstream format. + * @Output vid_std_features: Features of video standard for this bitstream. + * @Output swsr_ctx: Software Shift Register settings for this bitstream. + * @Output parser_callbacks: Parser functions to be used for parsing this + * bitstream. + * @Output inter_pict_data: Inter-picture settings specific for this + * bitstream. + * @Return int : This function returns either IMG_SUCCESS or an error code. + */ +typedef int (*bspp_cb_set_parser_config)(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *vid_std_features, + struct bspp_swsr_ctx *swsr_ctx, + struct bspp_parser_callbacks *parser_callbacks, + struct bspp_inter_pict_data *inter_pict_data); + +/* + * @Function bspp_cb_determine_unit_type + * @Description This is a function prototype for determining the BSPP unit type + * based on the bitstream (video standard specific) unit type + * callback functions. + * @Input bitstream_unit_type: Bitstream (video standard specific) unit + * type. + * @Input disable_mvc: Skip MVC related units (relevant for standards + * that support it). + * @InOut bspp_unit_type *: Last BSPP unit type on input. Current BSPP + * unit type on output. + * @Return None. + */ +typedef void (*bspp_cb_determine_unit_type)(unsigned char bitstream_unit_type, + int disable_mvc, + enum bspp_unit_type *bspp_unit_type); + +struct bspp_pps_info *bspp_get_pps_hdr(void *str_res_handle, unsigned int pps_id); + +struct bspp_sequence_hdr_info *bspp_get_sequ_hdr(void *str_res_handle, + unsigned int sequ_id); + +struct bspp_vps_info *bspp_get_vpshdr(void *str_res, unsigned int vps_id); + +void bspp_streamrelese_rawbstrdataplain(const void *str_res, + const void *rawdata); + +void bspp_freeraw_sei_datacontainer(const void *str_res, + struct vdec_raw_bstr_data *rawsei_datacontainer); + +void bspp_freeraw_sei_datalist(const void *str_res, + struct vdec_raw_bstr_data *rawsei_datalist); + +#endif /* __BSPP_INT_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/core.c b/drivers/media/platform/vxe-vxd/decoder/core.c --- a/drivers/media/platform/vxe-vxd/decoder/core.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/core.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,3656 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD Decoder Core component function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include "core.h" +#include "decoder.h" +#include "img_errors.h" +#include "img_pixfmts.h" +#include "img_profiles_levels.h" +#include "lst.h" +#include "resource.h" +#include "rman_api.h" +#include "vdecdd_utils.h" +#include "vdec_mmu_wrapper.h" +#include "vxd_dec.h" + +#ifdef HAS_HEVC +#define SEQ_RES_NEEDED +#define GENC_BUFF_COUNT 4 +#endif + +/* + * This enum defines resource availability masks. + * @brief Resource Availability + */ +enum core_availability { + CORE_AVAIL_PICTBUF = (1 << 0), + CORE_AVAIL_PICTRES = (1 << 1), + CORE_AVAIL_CORE = (1 << 2), + CORE_AVAIL_MAX, + CORE_AVAIL_FORCE32BITS = 0x7FFFFFFFU +}; + +struct core_mbparam_alloc_info { + unsigned char alloc_mbparam_bufs; + unsigned int mbparam_size; + unsigned int overalloc_mbnum; +}; + +static struct core_mbparam_alloc_info mbparam_allocinfo[VDEC_STD_MAX - 1] = { + /* AllocFlag MBParamSize Overalloc */ + /* MPEG2 */ { TRUE, 0xc8, 0 }, + /* MPEG4 */ { TRUE, 0xc8, 0 }, + /* H263 */ { TRUE, 0xc8, 0 }, + /* H264 */ { TRUE, 0x80, 0 }, + /* VC1 */ { TRUE, 0x80, (4096 * 2) / 0x80 }, + /* AVS */ { TRUE, 0x80, 0 }, + /* REAL */ { TRUE, 0x80, 0 }, + /* JPEG */ { FALSE, 0x00, 0 }, + /* VP6 */ { TRUE, 0x80, 0 }, + /* VP8 */ { TRUE, 0x80, 0 }, + /* SORENSON */ { TRUE, 0xc8, 0 }, + /* HEVC */ { TRUE, 0x40, 0 }, +}; + +struct vxdio_mempool { + unsigned int mem_heap_id; + enum sys_emem_attrib mem_attrib; +}; + +static unsigned int global_avail_slots; +static unsigned char is_core_initialized; + +/* + * This structure contains the core Context. + * @brief core Context + */ +struct core_context { + struct vdecdd_dddev_context *dev_ctx; + /* List of stream context structures */ + struct lst_t core_str_ctx; + vxd_cb vxd_str_processed_cb; +}; + +/* Global Core Context */ +static struct core_context *global_core_ctx; + +/* + * This structure contains the picture buffer size info. + * @brief Picture Resource Info + */ +struct core_pict_bufsize_info { + unsigned int mbparams_bufsize; + +#ifdef HAS_HEVC + union { + struct hevc_bufsize_pict { + /* Size of GENC fragment buffer for HEVC */ + unsigned int genc_fragment_bufsize; + } hevc_bufsize_pict; + }; +#endif +}; + +/* + * This structure contains the sequence resource info. + * @brief Sequence Resource Info + */ +struct core_seq_resinfo { + union { +#ifdef HAS_HEVC + struct hevc_bufsize_seqres { + unsigned int genc_bufsize; /* Size of GEN buffers for HEVC */ + unsigned int intra_bufsize; /* Size of GEN buffers for HEVC */ + unsigned int aux_bufsize; /* Size of GEN buffers for HEVC */ + } hevc_bufsize_seqres; +#endif + +#ifndef SEQ_RES_NEEDED + unsigned int dummy; +#endif + }; +}; + +struct core_pict_resinfo { + unsigned int pict_res_num; + struct core_pict_bufsize_info size_info; + unsigned char is_valid; +}; + +/* + * This structure contains the standard specific part of plant context. + * @brief Standard Specific Context + */ +struct core_std_spec_context { + union { +#ifdef HAS_HEVC + struct hevc_ctx { + /* Counts genc buffer allocations */ + unsigned short genc_id_gen; + } hevc_ctx; +#else + unsigned int dummy; +#endif + }; +}; + +struct core_stream_context; + +struct core_std_spec_operations { + /* Allocates standard specific picture buffers. */ + int (*alloc_picture_buffers)(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pict_resint, + struct vxdio_mempool mem_pool, + struct core_pict_resinfo *pict_res_info); + + /* Frees standard specific picture buffers. */ + int (*free_picture_resource)(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pic_res_int); + + /* Allocates standard specific sequence buffers. */ + int (*alloc_sequence_buffers)(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seq_res_int, + struct vxdio_mempool mem_pool, + struct core_seq_resinfo *seq_res_info); + + /* Frees standard specific sequence buffers. */ + int (*free_sequence_resource)(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seq_res_int); + + /* Returns buffer's sizes (common and standard specific). */ + int (*bufs_get_size)(struct core_stream_context *core_strctx, + const struct vdec_comsequ_hdrinfo *seq_hdrinfo, + struct vdec_pict_size *max_pict_size, + struct core_pict_bufsize_info *size_info, + struct core_seq_resinfo *seq_resinfo, + unsigned char *resource_needed); + + /* Checks whether resource is still suitable. */ + unsigned char (*is_stream_resource_suitable)(struct core_pict_resinfo *pict_resinfo, + struct core_pict_resinfo *old_pict_resinfo, + struct core_seq_resinfo *seq_resinfo, + struct core_seq_resinfo *old_seq_resinfo); +}; + +/* + * This structure contains the core Stream Context. + * @brief core Stream Context + */ +struct core_stream_context { + void **link; /* to be part of single linked list */ + struct core_context *core_ctx; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct vxd_dec_ctx *vxd_dec_context; + + /* list of picture buffers */ + struct lst_t pict_buf_list; + + /* List of picture resources allocated for this stream */ + struct lst_t pict_res_list; + struct lst_t old_pict_res_list; + + struct lst_t aux_pict_res_list; + +#ifdef SEQ_RES_NEEDED + /* List of active sequence resources that are allocated for this stream. */ + struct lst_t seq_res_list; + /* + * List of sequence resources that are allocated for this stream but no + * longer suitable for new sequence(s). + */ + struct lst_t old_seq_res_list; +#endif + + /* List of sequence header information */ + struct lst_t seq_hdr_list; + /* Queue of stream units to be processed */ + struct lst_t str_unit_list; + + struct vdec_comsequ_hdrinfo comseq_hdr_info; + unsigned char opcfg_set; + /* Picture buffer layout to use for decoding. */ + struct vdecdd_ddpict_buf disp_pict_buf; + struct vdec_str_opconfig op_cfg; + unsigned char new_seq; + unsigned char new_op_cfg; + unsigned char no_prev_refs_used; + unsigned int avail_slots; + unsigned int res_avail; + unsigned char stopped; + struct core_pict_resinfo pict_resinfo; + /* Current sequence resource info. */ + struct core_seq_resinfo seq_resinfo; + + /* Reconstructed picture buffer */ + struct vdecdd_ddpict_buf recon_pictbuf; + /* Coded picture size of last reconfiguration */ + struct vdec_pict_size coded_pict_size; + /* Standard specific operations. */ + struct core_std_spec_operations *std_spec_ops; + /* Standard specific context. */ + struct core_std_spec_context std_spec_context; +}; + +#ifdef HAS_HEVC +static int core_free_hevc_picture_resource(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pic_res_int); + +static int core_free_hevc_sequence_resource(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seq_res_int); + +static int core_hevc_bufs_get_size(struct core_stream_context *core_str_ctx, + const struct vdec_comsequ_hdrinfo *seq_hdr_info, + struct vdec_pict_size *max_pict_size, + struct core_pict_bufsize_info *size_info, + struct core_seq_resinfo *seq_res_info, + unsigned char *resource_needed); + +static unsigned char core_is_hevc_stream_resource_suitable + (struct core_pict_resinfo *pict_res_info, + struct core_pict_resinfo *old_pict_res_info, + struct core_seq_resinfo *seq_res_info, + struct core_seq_resinfo *old_seq_res_info); + +static int core_alloc_hevc_specific_seq_buffers(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seq_res_int, + struct vxdio_mempool mempool, + struct core_seq_resinfo *seq_res_info); + +static int core_alloc_hevc_specific_pict_buffers(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pict_res_int, + struct vxdio_mempool mempool, + struct core_pict_resinfo *pict_res_info); +#endif + +static int +core_common_bufs_getsize(struct core_stream_context *core_str_ctx, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + struct vdec_pict_size *max_pict_size, + struct core_pict_bufsize_info *size_info, + struct core_seq_resinfo *seq_res_info, unsigned char *res_needed); + +static struct core_std_spec_operations std_specific_ops[VDEC_STD_MAX - 1] = { + /* AllocPicture FreePicture AllocSeq FreeSeq BufsGetSize IsStreamResourceSuitable */ + /* MPEG2 */ { NULL, NULL, NULL, NULL, NULL, NULL}, + /* MPEG4 */ { NULL, NULL, NULL, NULL, NULL, NULL}, + /* H263 */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* H264 */ { NULL, NULL, NULL, NULL, core_common_bufs_getsize, NULL}, + + /* VC1 */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* AVS */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* REAL */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* JPEG */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* VP6 */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* VP8 */ { NULL, NULL, NULL, NULL, NULL, NULL}, + + /* SORENSON */ { NULL, NULL, NULL, NULL, NULL, NULL}, +#ifdef HAS_HEVC + /* HEVC */ { core_alloc_hevc_specific_pict_buffers, + core_free_hevc_picture_resource, + core_alloc_hevc_specific_seq_buffers, + core_free_hevc_sequence_resource, + core_hevc_bufs_get_size, + core_is_hevc_stream_resource_suitable}, +#else + /* HEVC */ { NULL, NULL, NULL, NULL, NULL, NULL}, +#endif +}; + +#ifdef ERROR_CONCEALMENT +/* + * This structure contains the Error Recovery Frame Store info. + * @brief Error Recovery Frame Store Info + */ +struct core_err_recovery_frame_info { + /* Flag to indicate if Error Recovery Frame Store is enabled for standard. */ + unsigned char enabled; + /* Limitation for maximum frame size based on dimensions. */ + unsigned int max_size; +}; + +static struct core_err_recovery_frame_info err_recovery_frame_info[VDEC_STD_MAX - 1] = { + /* enabled max_frame_size */ + /* MPEG2 */ { TRUE, ~0 }, + /* MPEG4 */ { TRUE, ~0 }, + /* H263 */ { FALSE, 0 }, + /* H264 */ { TRUE, ~0 }, + /* VC1 */ { FALSE, 0 }, + /* AVS */ { FALSE, 0 }, + /* REAL */ { FALSE, 0 }, + /* JPEG */ { FALSE, 0 }, + /* VP6 */ { FALSE, 0 }, + /* VP8 */ { FALSE, 0 }, + /* SORENSON */ { FALSE, 0 }, + /* HEVC */ { TRUE, ~0 }, +}; +#endif + +static void core_fw_response_cb(int res_str_id, unsigned int *msg, unsigned int msg_size, + unsigned int msg_flags) +{ + struct core_stream_context *core_str_ctx; + int ret; + + /* extract core_str_ctx and dec_core_ctx from res_str_id */ + VDEC_ASSERT(res_str_id); + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + pr_err("could not extract core_str_context\n"); + + ret = decoder_service_firmware_response(core_str_ctx->dd_str_ctx->dec_ctx, + msg, msg_size, msg_flags); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + pr_err("decoder_service_firmware_response failed\n"); +} + +/* + * @Function core_initialise + */ +int core_initialise(void *dev_handle, unsigned int int_heap_id, void *vxd_cb_ptr) +{ + struct vdecdd_dd_devconfig dev_cfg_local; + unsigned int num_pipes_local; + int ret; + + if (is_core_initialized) + return IMG_ERROR_INVALID_PARAMETERS; + + is_core_initialized = TRUE; + + global_core_ctx = kzalloc(sizeof(*global_core_ctx), GFP_KERNEL); + if (!global_core_ctx) { + is_core_initialized = FALSE; + return IMG_ERROR_OUT_OF_MEMORY; + } + + global_core_ctx->dev_ctx = kzalloc(sizeof(*global_core_ctx->dev_ctx), GFP_KERNEL); + if (!global_core_ctx->dev_ctx) { + kfree(global_core_ctx); + global_core_ctx = NULL; + is_core_initialized = FALSE; + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* Initialise device context. */ + global_core_ctx->dev_ctx->dev_handle = dev_handle; /* v4L2 dev handle */ + global_core_ctx->vxd_str_processed_cb = (vxd_cb)vxd_cb_ptr; + + ret = decoder_initialise(global_core_ctx->dev_ctx, int_heap_id, + &dev_cfg_local, &num_pipes_local, + &global_core_ctx->dev_ctx->dec_context); + if (ret != IMG_SUCCESS) + goto decoder_init_error; + + global_core_ctx->dev_ctx->internal_heap_id = int_heap_id; + +#ifdef DEBUG_DECODER_DRIVER + /* Dump codec config */ + pr_info("Decode slots/core: %d", dev_cfg_local.num_slots_per_pipe); +#endif + + lst_init(&global_core_ctx->core_str_ctx); + + /* Ensure the resource manager is initialised.. */ + ret = rman_initialise(); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto rman_init_error; + + /* Create resource bucket.. */ + ret = rman_create_bucket(&global_core_ctx->dev_ctx->res_buck_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto create_bucket_error; + + return IMG_SUCCESS; + +create_bucket_error: + rman_deinitialise(); + +rman_init_error: + decoder_deinitialise(global_core_ctx->dev_ctx->dec_context); + +decoder_init_error: + kfree(global_core_ctx->dev_ctx); + global_core_ctx->dev_ctx = NULL; + kfree(global_core_ctx); + global_core_ctx = NULL; + + is_core_initialized = FALSE; + + return ret; +} + +/* + * @Function core_check_decoder_support + * @Description + * This function determines whether Decoder supports bitstream and + * configuration. + */ +static int +core_check_decoder_support(const struct vdecdd_dddev_context *dd_dev_ctx, + const struct vdec_str_configdata *str_cfg_data, + const struct vdec_comsequ_hdrinfo *prev_seq_hdrinfo, + const struct bspp_pict_hdr_info *prev_pict_hdrinfo, + const struct vdecdd_mapbuf_info *map_bufinfo, + struct vdecdd_supp_check *supp_check) +{ + int ret; + struct vdec_unsupp_flags unsupported; + struct vdec_pict_rendinfo disp_pict_rend_info; + + memset(&disp_pict_rend_info, 0, sizeof(struct vdec_pict_rendinfo)); + + /* + * If output picture buffer information is provided create another + * with properties required by bitstream so that it can be compared. + */ + if (supp_check->disp_pictbuf) { + struct vdec_pict_rend_config pict_rend_cfg; + + memset(&pict_rend_cfg, 0, sizeof(pict_rend_cfg)); + + /* + * Cannot validate the display picture buffer layout without + * knowing the pixel format required for the output and the + * sequence information. + */ + if (supp_check->comseq_hdrinfo && supp_check->op_cfg) { + pict_rend_cfg.coded_pict_size = + supp_check->comseq_hdrinfo->max_frame_size; + + pict_rend_cfg.byte_interleave = + supp_check->disp_pictbuf->buf_config.byte_interleave; + + pict_rend_cfg.packed = + supp_check->disp_pictbuf->buf_config.packed; + + pict_rend_cfg.stride_alignment = + supp_check->disp_pictbuf->buf_config.stride_alignment; + + /* + * Recalculate render picture layout based upon + * sequence and output config. + */ + vdecddutils_pictbuf_getinfo(str_cfg_data, + &pict_rend_cfg, + supp_check->op_cfg, + &disp_pict_rend_info); + } + } + /* Check that the decoder supports the picture. */ + ret = decoder_check_support(dd_dev_ctx->dec_context, str_cfg_data, + supp_check->op_cfg, + supp_check->disp_pictbuf, + (disp_pict_rend_info.rendered_size) ? + &disp_pict_rend_info : NULL, + supp_check->comseq_hdrinfo, + supp_check->pict_hdrinfo, + prev_seq_hdrinfo, + prev_pict_hdrinfo, + supp_check->non_cfg_req, + &unsupported, + &supp_check->features); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) { + if (ret == IMG_ERROR_NOT_SUPPORTED) + supp_check->unsupp_flags = unsupported; + } + + return ret; +} + +/* + * @Function core_supported_features + */ +int core_supported_features(struct vdec_features *features) +{ + struct vdecdd_dddev_context *dd_dev_ctx; + + VDEC_ASSERT(global_core_ctx); + + dd_dev_ctx = global_core_ctx->dev_ctx; + VDEC_ASSERT(dd_dev_ctx); + if (!dd_dev_ctx) + return IMG_ERROR_NOT_INITIALISED; + + return decoder_supported_features(dd_dev_ctx->dec_context, features); +} + +/* + * @Function core_stream_stop + */ +int core_stream_stop(unsigned int res_str_id) +{ + int ret = IMG_SUCCESS; + struct vdecdd_str_unit *stop_unit; + struct vdecdd_ddstr_ctx *ddstr_ctx; + struct core_stream_context *core_str_ctx; + + /* + * Stream based messages without a device context + * must have a stream ID. + */ + VDEC_ASSERT(res_str_id); + + if (res_str_id == 0) { + pr_err("Invalid params passed to %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + VDEC_ASSERT(core_str_ctx); + + ddstr_ctx = core_str_ctx->dd_str_ctx; + + /* Validate input arguments */ + VDEC_ASSERT(ddstr_ctx); + + /* + * Disregard this stop request if the stream is currently + * stopped or being stopped. + */ + if (ddstr_ctx->dd_str_state == VDECDD_STRSTATE_PLAYING) { + vdecddutils_create_strunit(&stop_unit, NULL); + if (!stop_unit) { + pr_err("Failed to allocate memory for stop unit\n"); + return IMG_ERROR_OUT_OF_MEMORY; + } + memset(stop_unit, 0, sizeof(*stop_unit)); + + stop_unit->str_unit_type = VDECDD_STRUNIT_STOP; + stop_unit->str_unit_tag = NULL; + stop_unit->decode = FALSE; + + /* + * Since the stop is now to be passed to the decoder signal + * that we're stopping. + */ + ddstr_ctx->dd_str_state = VDECDD_STRSTATE_STOPPING; + decoder_stream_process_unit(ddstr_ctx->dec_ctx, stop_unit); + core_str_ctx->stopped = TRUE; + vdecddutils_free_strunit(stop_unit); + } + + return ret; +} + +/* + * @Function core_is_stream_idle + */ +static unsigned char core_is_stream_idle(struct vdecdd_ddstr_ctx *dd_str_ctx) +{ + unsigned char is_stream_idle; + + is_stream_idle = decoder_is_stream_idle(dd_str_ctx->dec_ctx); + + return is_stream_idle; +} + +/* + * @Function core_stream_destroy + */ +int core_stream_destroy(unsigned int res_str_id) +{ + struct vdecdd_ddstr_ctx *ddstr_ctx; + struct core_stream_context *core_str_ctx; + int ret; + + /* + * Stream based messages without a device context + * must have a stream ID. + */ + VDEC_ASSERT(res_str_id); + + if (res_str_id == 0) { + pr_err("Invalid params passed to %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + VDEC_ASSERT(core_str_ctx); + + ddstr_ctx = core_str_ctx->dd_str_ctx; + + /* Validate input arguments */ + VDEC_ASSERT(ddstr_ctx); + + ret = core_stream_stop(res_str_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + lst_remove(&global_core_ctx->core_str_ctx, core_str_ctx); + + /* Destroy stream if idle otherwise wait and do it later */ + if (core_is_stream_idle(ddstr_ctx)) + rman_free_resource(ddstr_ctx->res_handle); + + pr_debug("Core stream destroy successfully\n"); + /* Return success.. */ + return IMG_SUCCESS; +} + +static int +core_picture_attach_resources(struct core_stream_context *core_str_ctx, + struct vdecdd_str_unit *str_unit, unsigned char check) +{ + unsigned int ret = IMG_SUCCESS; + + /* + * Take sequence header from cache. + * Note: sequence header id must be set in PICTURE_START unit + */ + str_unit->seq_hdr_info = resource_list_getbyid(&core_str_ctx->seq_hdr_list, + str_unit->seq_hdr_id); + + /* Check is not needed e.g. when freeing resources at stream destroy */ + if (check && !str_unit->seq_hdr_info) { + pr_err("[USERSID=0x%08X] Sequence header not available for current picture while attaching", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id); + ret = IMG_ERROR_NOT_SUPPORTED; + } + + return ret; +} + +/* + * @Function core_handle_processed_unit + */ +static int core_handle_processed_unit(struct core_stream_context *c_str_ctx, + struct vdecdd_str_unit *str_unit) +{ + struct bspp_bitstr_seg *bstr_seg; + struct vdecdd_ddstr_ctx *dd_str_ctx = c_str_ctx->dd_str_ctx; + int ret; + struct core_context *g_ctx = global_core_ctx; + + pr_debug("%s stream unit type = %d\n", __func__, str_unit->str_unit_type); + /* check for type of the unit */ + switch (str_unit->str_unit_type) { + case VDECDD_STRUNIT_SEQUENCE_START: + /* nothing to be done as sps is maintained till it changes */ + break; + + case VDECDD_STRUNIT_PICTURE_START: + /* Loop over bit stream segments.. */ + bstr_seg = (struct bspp_bitstr_seg *) + lst_removehead(&str_unit->bstr_seg_list); + + while (bstr_seg) { + lst_add(&c_str_ctx->vxd_dec_context->seg_list, bstr_seg); + if (bstr_seg->bstr_seg_flag & VDECDD_BSSEG_LASTINBUFF && + dd_str_ctx->dd_str_state != VDECDD_STRSTATE_STOPPED) { + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + /* Get access to map info context.. */ + ret = rman_get_resource(bstr_seg->bufmap_id, VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + g_ctx->vxd_str_processed_cb(c_str_ctx->vxd_dec_context, + VXD_CB_STRUNIT_PROCESSED, + bstr_seg->bufmap_id); + } + /* Get next segment. */ + bstr_seg = (struct bspp_bitstr_seg *) + lst_removehead(&str_unit->bstr_seg_list); + } + break; + + case VDECDD_STRUNIT_PICTURE_END: + g_ctx->vxd_str_processed_cb(c_str_ctx->vxd_dec_context, + VXD_CB_PICT_END, 0xFFFF); + break; + + case VDECDD_STRUNIT_STOP: + /* + * Signal that the stream has been stopped in the + * device driver. + */ + dd_str_ctx->dd_str_state = VDECDD_STRSTATE_STOPPED; + + break; + + default: + pr_err("Invalid stream unit type passed\n"); + return IMG_ERROR_GENERIC_FAILURE; + } + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[SID=0x%08X] [UTYPE=0x%08X] PROCESSED", + dd_str_ctx->res_str_id, + str_unit->str_unit_type); +#endif + + /* Return success.. */ + return IMG_SUCCESS; +} + +static int +core_handle_decoded_picture(struct core_stream_context *core_str_ctx, + struct vdecdd_picture *picture, unsigned int type) +{ + /* Pick the client image buffer. */ + struct vdecdd_ddbuf_mapinfo *pictbuf_mapinfo = picture->disp_pict_buf.pict_buf; + + VDEC_ASSERT(pictbuf_mapinfo); + if (!pictbuf_mapinfo) + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + + global_core_ctx->vxd_str_processed_cb(core_str_ctx->vxd_dec_context, + (enum vxd_cb_type)type, pictbuf_mapinfo->buf_map_id); + return IMG_SUCCESS; +} + +static int core_stream_processed_cb(void *handle, int cb_type, void *cb_item) +{ + int ret; + struct core_stream_context *core_str_ctx = + (struct core_stream_context *)handle; + VDEC_ASSERT(core_str_ctx); + if (!core_str_ctx) { + pr_err("NULL handle passed to core callback\n"); + return IMG_ERROR_GENERIC_FAILURE; + } + + pr_debug("%s callback type = %d\n", __func__, cb_type); + /* Based on callback type, retrieve the item */ + switch (cb_type) { + case VXD_CB_STRUNIT_PROCESSED: + { + struct vdecdd_str_unit *str_unit = + (struct vdecdd_str_unit *)cb_item; + VDEC_ASSERT(str_unit); + if (!str_unit) { + pr_err("NULL item passed to core callback type STRUNIT_PROCESSED\n"); + return IMG_ERROR_GENERIC_FAILURE; + } + ret = core_handle_processed_unit(core_str_ctx, str_unit); + if (ret != IMG_SUCCESS) { + pr_err("core_handle_processed_unit returned error\n"); + return ret; + } + break; + } + + case VXD_CB_PICT_DECODED: + case VXD_CB_PICT_DISPLAY: + case VXD_CB_PICT_RELEASE: + { + struct vdecdd_picture *picture = (struct vdecdd_picture *)cb_item; + + if (!picture) { + pr_err("NULL item passed to core callback type PICTURE_DECODED\n"); + return IMG_ERROR_GENERIC_FAILURE; + } + ret = core_handle_decoded_picture(core_str_ctx, picture, cb_type); + break; + } + + case VXD_CB_STR_END: + global_core_ctx->vxd_str_processed_cb(core_str_ctx->vxd_dec_context, + (enum vxd_cb_type)cb_type, 0); + ret = IMG_SUCCESS; + + break; + + case VXD_CB_ERROR_FATAL: + /* + * Whenever the error case occurs, we need to handle the error case. + * Need to forward this error to v4l2 glue layer. + */ + global_core_ctx->vxd_str_processed_cb(core_str_ctx->vxd_dec_context, + (enum vxd_cb_type)cb_type, *((unsigned int *)cb_item)); + ret = IMG_SUCCESS; + break; + + default: + return 0; + } + + return ret; +} + +static int core_decoder_queries(void *handle, int query, void *item) +{ + struct core_stream_context *core_str_ctx = + (struct core_stream_context *)handle; + VDEC_ASSERT(core_str_ctx); + if (!core_str_ctx) { + pr_err("NULL handle passed to %s callback\n", __func__); + return IMG_ERROR_GENERIC_FAILURE; + } + + switch (query) { + case DECODER_CORE_GET_RES_LIMIT: + { + unsigned int num_img_bufs; + unsigned int num_res; + + num_img_bufs = resource_list_getnum(&core_str_ctx->pict_buf_list); + + /* Return the number of internal resources. */ + num_res = core_str_ctx->pict_resinfo.pict_res_num; + + /* Return the minimum of the two. */ + *((unsigned int *)item) = vdec_size_min(num_img_bufs, num_res); + } + break; + + default: + return IMG_ERROR_GENERIC_FAILURE; + } + return IMG_SUCCESS; +} + +static int +core_free_common_picture_resource(struct core_stream_context *core_str_ctx, + struct vdecdd_pict_resint *pict_resint) +{ + int ret = IMG_SUCCESS; + + if (pict_resint->mb_param_buf && pict_resint->mb_param_buf->ddbuf_info.hndl_memory) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("mmu_free for buff_id[%d]\n", + pict_resint->mb_param_buf->ddbuf_info.buff_id); +#endif + ret = mmu_free_mem(core_str_ctx->dd_str_ctx->mmu_str_handle, + &pict_resint->mb_param_buf->ddbuf_info); + if (ret != IMG_SUCCESS) + pr_err("MMU_Free for MBParam buffer failed with error %u", ret); + + kfree(pict_resint->mb_param_buf); + pict_resint->mb_param_buf = NULL; + } + return ret; +} + +static int core_free_resbuf(struct vdecdd_ddbuf_mapinfo **buf_handle, void *mmu_handle) +{ + int ret = IMG_SUCCESS; + struct vdecdd_ddbuf_mapinfo *buf = *buf_handle; + + if (buf) { + if (buf->ddbuf_info.hndl_memory) { + ret = mmu_free_mem(mmu_handle, &buf->ddbuf_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + kfree(buf); + *buf_handle = NULL; + } + return ret; +} + +/* + * @Function core_free_picture_resource + */ +static int +core_free_picture_resource(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pict_resint) +{ + int result = IMG_SUCCESS; + + /* Check input arguments */ + if (!core_strctx || !pict_resint) { + VDEC_ASSERT(0); + return -EINVAL; + } + + result = core_free_common_picture_resource(core_strctx, pict_resint); + + VDEC_ASSERT(core_strctx->std_spec_ops); + if (core_strctx->std_spec_ops->free_picture_resource) + core_strctx->std_spec_ops->free_picture_resource(core_strctx, + pict_resint); + +#ifdef SEQ_RES_NEEDED + if (pict_resint->seq_resint) { + resource_item_return(&pict_resint->seq_resint->ref_count); + pict_resint->seq_resint = 0; + } +#endif + + if (result == IMG_SUCCESS) + kfree(pict_resint); + + return result; +} + +/* + * @Function core_free_sequence_resource + */ +#ifdef SEQ_RES_NEEDED +static int +core_free_common_sequence_resource(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seqres_int) +{ + int result; + + result = core_free_resbuf(&seqres_int->err_pict_buf, + core_strctx->dd_str_ctx->mmu_str_handle); + if (result != IMG_SUCCESS) + pr_err("MMU_Free for Error Recover Frame Store buffer failed with error %u", + result); + + return result; +} + +static void +core_free_sequence_resource(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seqres_int) +{ + VDEC_ASSERT(core_strctx->std_spec_ops); + core_free_common_sequence_resource(core_strctx, seqres_int); + + if (core_strctx->std_spec_ops->free_sequence_resource) + core_strctx->std_spec_ops->free_sequence_resource(core_strctx, seqres_int); + + kfree(seqres_int); +} +#endif + +/* + * @Function core_stream_resource_deprecate + */ +static int core_stream_resource_deprecate(struct core_stream_context *core_str_ctx) +{ + struct vdecdd_pict_resint *picres_int; + int ret; + + /* Free all "old" picture resources since these should now be unused. */ + picres_int = lst_first(&core_str_ctx->old_pict_res_list); + while (picres_int) { + if (picres_int->ref_cnt != 0) { + pr_warn("[USERSID=0x%08X] Internal resource should be unused since it has been deprecated before", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id); + + picres_int = lst_next(picres_int); + } else { + struct vdecdd_pict_resint *picres_int_to_remove = picres_int; + + picres_int = lst_next(picres_int); + + lst_remove(&core_str_ctx->old_pict_res_list, picres_int_to_remove); + ret = core_free_picture_resource(core_str_ctx, picres_int_to_remove); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + } + + /* Move all "active" picture resources to the "old" list if they are still in use. */ + picres_int = lst_removehead(&core_str_ctx->pict_res_list); + while (picres_int) { + /* Remove picture resource from the list. */ + ret = resource_list_remove(&core_str_ctx->aux_pict_res_list, picres_int); + + /* IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE is a valid return code + * e.g. during reconfigure we are clearing the sPictBufferList list + * and then try to remove the buffers again from the same list (empty now) + * though core UNMAP_BUF messages + */ + if (ret != IMG_SUCCESS && ret != IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE) { + pr_err("[USERSID=0x%08X] Failed to remove picture resource", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id); + return ret; + } + /* + * If the active resource is not being used, free now. + * Otherwise add to the old list to be freed later. + */ + if (picres_int->ref_cnt == 0) { + ret = core_free_picture_resource(core_str_ctx, picres_int); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } else { + lst_add(&core_str_ctx->old_pict_res_list, picres_int); + } + picres_int = lst_removehead(&core_str_ctx->pict_res_list); + } + + /* Reset the resource configuration. */ + memset(&core_str_ctx->pict_resinfo, 0, sizeof(core_str_ctx->pict_resinfo)); + +#ifdef SEQ_RES_NEEDED + { + struct vdecdd_seq_resint *seqres_int; + + /* Free all "old" sequence resources since these should now be unused. */ + seqres_int = lst_first(&core_str_ctx->old_seq_res_list); + while (seqres_int) { + if (seqres_int->ref_count != 0) { + pr_warn("[USERSID=0x%08X] Internal sequence resource should be unused since it has been deprecated before", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id); + seqres_int = lst_next(seqres_int); + } else { + struct vdecdd_seq_resint *seqres_int_to_remove = seqres_int; + + seqres_int = lst_next(seqres_int); + + lst_remove(&core_str_ctx->old_seq_res_list, seqres_int_to_remove); + core_free_sequence_resource(core_str_ctx, seqres_int_to_remove); + } + } + + /* Move all "active" sequence resources to the "old" + * list if they are still in use. + */ + seqres_int = lst_removehead(&core_str_ctx->seq_res_list); + while (seqres_int) { + /* + * If the active resource is not being used, free now. + * Otherwise add to the old list to be freed later. + */ + seqres_int->ref_count == 0 ? core_free_sequence_resource(core_str_ctx, + seqres_int) : + lst_add(&core_str_ctx->old_seq_res_list, seqres_int); + + seqres_int = lst_removehead(&core_str_ctx->seq_res_list); + } + + /* Reset the resource configuration. */ + memset(&core_str_ctx->seq_resinfo, 0, sizeof(core_str_ctx->seq_resinfo)); + } +#endif + return IMG_SUCCESS; +} + +/* + * @Function core_stream_resource_destroy + */ +static int core_stream_resource_destroy(struct core_stream_context *core_str_ctx) +{ + struct vdecdd_pict_resint *picres_int; + int ret; + + /* Remove any "active" picture resources allocated for this stream. */ + picres_int = lst_removehead(&core_str_ctx->pict_res_list); + while (picres_int) { + ret = core_free_picture_resource(core_str_ctx, picres_int); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + picres_int = lst_removehead(&core_str_ctx->pict_res_list); + } + + /* Remove any "old" picture resources allocated for this stream. */ + picres_int = lst_removehead(&core_str_ctx->old_pict_res_list); + while (picres_int) { + ret = core_free_picture_resource(core_str_ctx, picres_int); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + picres_int = lst_removehead(&core_str_ctx->old_pict_res_list); + } + + /* Reset the resource configuration. */ + memset(&core_str_ctx->pict_resinfo, 0, sizeof(core_str_ctx->pict_resinfo)); + +#ifdef SEQ_RES_NEEDED + { + struct vdecdd_seq_resint *seqres_int; + + /* Remove any "active" sequence resources allocated for this stream. */ + seqres_int = lst_removehead(&core_str_ctx->seq_res_list); + while (seqres_int) { + core_free_sequence_resource(core_str_ctx, seqres_int); + seqres_int = lst_removehead(&core_str_ctx->seq_res_list); + } + + /* Remove any "old" sequence resources allocated for this stream. */ + seqres_int = lst_removehead(&core_str_ctx->old_seq_res_list); + while (seqres_int) { + core_free_sequence_resource(core_str_ctx, seqres_int); + seqres_int = lst_removehead(&core_str_ctx->old_seq_res_list); + } + + /* Reset the resource configuration. */ + memset(&core_str_ctx->seq_resinfo, 0, sizeof(core_str_ctx->seq_resinfo)); + } +#endif + return IMG_SUCCESS; +} + +/* + * @Function core_fn_free_stream_unit + */ +static int core_fn_free_stream_unit(struct vdecdd_str_unit *str_unit, void *param) +{ + struct core_stream_context *core_str_ctx = (struct core_stream_context *)param; + unsigned int ret = IMG_SUCCESS; + + /* Attach picture resources where required. */ + if (str_unit->str_unit_type == VDECDD_STRUNIT_PICTURE_START) + /* + * Do not force attachment because the resources can be + * unattached yet, e.g. in case of not yet processed picture + * units + */ + ret = core_picture_attach_resources(core_str_ctx, str_unit, FALSE); + + str_unit->decode = FALSE; + + return ret; +} + +/* + * @Function core_fn_free_stream + */ +static void core_fn_free_stream(void *param) +{ + int ret; + struct vdecdd_ddstr_ctx *dd_str_context; + struct vdecdd_dddev_context *dd_dev_ctx; + struct core_stream_context *core_str_ctx; + + /* Validate input arguments */ + VDEC_ASSERT(param); + + core_str_ctx = (struct core_stream_context *)param; + + dd_str_context = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_context); + if (!dd_str_context) + return; + + dd_dev_ctx = dd_str_context->dd_dev_context; + VDEC_ASSERT(dd_dev_ctx); + + if (!lst_empty(&core_str_ctx->str_unit_list)) { + /* + * Try and empty the list. Since this function is tearing down the core stream, + * test result using assert and continue to tidy-up as much as possible. + */ + ret = resource_list_empty(&core_str_ctx->str_unit_list, FALSE, + (resource_pfn_freeitem)core_fn_free_stream_unit, + core_str_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + if (!lst_empty(&core_str_ctx->pict_buf_list)) { + /* + * Try and empty the list. Since this function is tearing down the core stream, + * test result using assert and continue to tidy-up as much as possible. + */ + ret = resource_list_empty(&core_str_ctx->pict_buf_list, TRUE, NULL, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + if (!lst_empty(&core_str_ctx->aux_pict_res_list)) { + /* + * Try and empty the list. Since this function is tearing down the core stream, + * test result using assert and continue to tidy-up as much as possible. + */ + ret = resource_list_empty(&core_str_ctx->aux_pict_res_list, TRUE, NULL, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + if (!lst_empty(&core_str_ctx->seq_hdr_list)) { + /* + * Try and empty the list. Since this function is tearing down the core stream, + * test result using assert and continue to tidy-up as much as possible. + */ + ret = resource_list_empty(&core_str_ctx->seq_hdr_list, FALSE, NULL, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + /* Destroy stream in the Decoder. */ + if (dd_str_context->dec_ctx) { + ret = decoder_stream_destroy(dd_str_context->dec_ctx, FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + dd_str_context->dec_ctx = NULL; + } + + core_stream_resource_destroy(core_str_ctx); + + /* Destroy the MMU context for this stream. */ + if (dd_str_context->mmu_str_handle) { + ret = mmu_stream_destroy(dd_str_context->mmu_str_handle); + + VDEC_ASSERT(ret == IMG_SUCCESS); + dd_str_context->mmu_str_handle = NULL; + } + + /* Destroy the stream resources. */ + if (dd_str_context->res_buck_handle) { + rman_destroy_bucket(dd_str_context->res_buck_handle); + dd_str_context->res_buck_handle = NULL; + } + + /* Free stream context. */ + kfree(dd_str_context); + + /* Free the stream context. */ + kfree(core_str_ctx); +} + +/* + * @Function core_is_unsupported + */ +static unsigned char core_is_unsupported(struct vdec_unsupp_flags *unsupp_flags) +{ + unsigned char unsupported = FALSE; + + if (unsupp_flags->str_cfg || unsupp_flags->seq_hdr || + unsupp_flags->pict_hdr || unsupp_flags->str_opcfg || + unsupp_flags->op_bufcfg) + unsupported = TRUE; + + return unsupported; +} + +int core_stream_create(void *vxd_dec_ctx_arg, + const struct vdec_str_configdata *str_cfg_data, + unsigned int *res_str_id) +{ + int ret; + struct vdecdd_ddstr_ctx *dd_str_context; + struct vdecdd_supp_check supp_check; + struct vdecdd_dddev_context *dd_dev_ctx; + struct core_stream_context *core_str_ctx; + + /* Validate input arguments */ + VDEC_ASSERT(str_cfg_data); + VDEC_ASSERT(res_str_id); + + VDEC_ASSERT(global_core_ctx); + dd_dev_ctx = global_core_ctx->dev_ctx; + + VDEC_ASSERT(dd_dev_ctx); + if (!dd_dev_ctx) + return IMG_ERROR_NOT_INITIALISED; + + /* Allocate Core Stream Context */ + core_str_ctx = kzalloc(sizeof(*core_str_ctx), GFP_KERNEL); + if (!core_str_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + core_str_ctx->core_ctx = global_core_ctx; + core_str_ctx->vxd_dec_context = (struct vxd_dec_ctx *)vxd_dec_ctx_arg; + /* register callback for firmware response */ + core_str_ctx->vxd_dec_context->cb = (decode_cb)core_fw_response_cb; + + lst_init(&core_str_ctx->pict_buf_list); + lst_init(&core_str_ctx->pict_res_list); + lst_init(&core_str_ctx->old_pict_res_list); + lst_init(&core_str_ctx->aux_pict_res_list); + lst_init(&core_str_ctx->seq_hdr_list); + lst_init(&core_str_ctx->str_unit_list); + +#ifdef SEQ_RES_NEEDED + lst_init(&core_str_ctx->seq_res_list); + lst_init(&core_str_ctx->old_seq_res_list); +#endif + + /* Allocate device stream context.. */ + dd_str_context = kzalloc(sizeof(*dd_str_context), GFP_KERNEL); + VDEC_ASSERT(dd_str_context); + if (!dd_str_context) { + kfree(core_str_ctx); + core_str_ctx = NULL; + return IMG_ERROR_OUT_OF_MEMORY; + } + + dd_str_context->dd_dev_context = dd_dev_ctx; + core_str_ctx->dd_str_ctx = dd_str_context; + + /* Check stream configuration. */ + memset(&supp_check, 0x0, sizeof(supp_check)); + ret = core_check_decoder_support(dd_dev_ctx, str_cfg_data, NULL, NULL, NULL, &supp_check); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + if (core_is_unsupported(&supp_check.unsupp_flags)) { + ret = IMG_ERROR_NOT_SUPPORTED; + goto error; + } + + /* Create a bucket for the resources.. */ + ret = rman_create_bucket(&dd_str_context->res_buck_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Register the stream as a device resource.. */ + ret = rman_register_resource(dd_dev_ctx->res_buck_handle, + VDECDD_STREAM_TYPE_ID, + core_fn_free_stream, core_str_ctx, + &dd_str_context->res_handle, + &dd_str_context->res_str_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Create unique Stream Id */ + dd_str_context->km_str_id = core_str_ctx->vxd_dec_context->stream.id; + + /* + * Create stream in the Decoder. + * NOTE: this must take place first since it creates the MMU context. + */ + ret = decoder_stream_create(dd_dev_ctx->dec_context, *str_cfg_data, + dd_str_context->km_str_id, + &dd_str_context->mmu_str_handle, + core_str_ctx->vxd_dec_context, + core_str_ctx, &dd_str_context->dec_ctx, + (void *)core_stream_processed_cb, + (void *)core_decoder_queries); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Setup stream context.. */ + dd_str_context->str_config_data = *str_cfg_data; + dd_str_context->dd_str_state = VDECDD_STRSTATE_STOPPED; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[SID=0x%08X] New stream created [USERSID=0x%08X]", + dd_str_context->res_str_id, str_cfg_data->user_str_id); +#endif + + *res_str_id = dd_str_context->res_str_id; + if (str_cfg_data->vid_std > 0 && str_cfg_data->vid_std <= VDEC_STD_MAX) { + core_str_ctx->std_spec_ops = &std_specific_ops[str_cfg_data->vid_std - 1]; + } else { + pr_err("%s: Invalid parameters\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + lst_add(&global_core_ctx->core_str_ctx, core_str_ctx); + + /* Return success.. */ + return IMG_SUCCESS; + +error: + if (dd_str_context->res_handle) + rman_free_resource(dd_str_context->res_handle); + else + core_fn_free_stream(core_str_ctx); + + return ret; +} + +static int +core_get_resource_availability(struct core_stream_context *core_str_ctx) +{ + unsigned int avail = ~0; + + if (resource_list_getnumavail(&core_str_ctx->pict_buf_list) == 0) + avail &= ~CORE_AVAIL_PICTBUF; + + if (resource_list_getnumavail(&core_str_ctx->aux_pict_res_list) == 0) + avail &= ~CORE_AVAIL_PICTRES; + + if (global_avail_slots == 0) + avail &= ~CORE_AVAIL_CORE; + + return avail; +} + +static int +core_stream_set_pictbuf_config(struct vdecdd_ddstr_ctx *dd_str_ctx, + struct vdec_pict_bufconfig *pictbuf_cfg) +{ + int ret; + + /* Validate input arguments */ + VDEC_ASSERT(dd_str_ctx); + VDEC_ASSERT(pictbuf_cfg); + + /* + * If there are no buffers mapped or the configuration is not set + * (only done when reconfiguring output) then calculate the output + * picture buffer layout. + */ + if (dd_str_ctx->map_buf_info.num_buf == 0 || + dd_str_ctx->disp_pict_buf.buf_config.buf_size == 0) { + struct vdecdd_supp_check supp_check; + struct vdecdd_ddpict_buf disp_pictbuf; + + memset(&disp_pictbuf, 0, sizeof(disp_pictbuf)); + + disp_pictbuf.buf_config = *pictbuf_cfg; + + /* + * Ensure that the external picture buffer information + * is compatible with the hardware and convert to internal + * driver representation. + */ + ret = vdecddutils_convert_buffer_config(&dd_str_ctx->str_config_data, + &disp_pictbuf.buf_config, + &disp_pictbuf.rend_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* + * Provide the current state for validation against the new + * buffer configuration. + */ + memset(&supp_check, 0, sizeof(supp_check)); + supp_check.disp_pictbuf = &disp_pictbuf; + + if (dd_str_ctx->comseq_hdr_info.max_frame_size.width) + supp_check.comseq_hdrinfo = &dd_str_ctx->comseq_hdr_info; + + if (dd_str_ctx->str_op_configured) + supp_check.op_cfg = &dd_str_ctx->opconfig; + + ret = core_check_decoder_support(dd_str_ctx->dd_dev_context, + &dd_str_ctx->str_config_data, + &dd_str_ctx->prev_comseq_hdr_info, + &dd_str_ctx->prev_pict_hdr_info, + &dd_str_ctx->map_buf_info, + &supp_check); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + if (core_is_unsupported(&supp_check.unsupp_flags)) { + ret = IMG_ERROR_NOT_SUPPORTED; + goto error; + } + + dd_str_ctx->disp_pict_buf = disp_pictbuf; + } else { + /* + * Check configuration of buffer matches that for stream + * including any picture buffers that are already mapped. + */ + if (memcmp(pictbuf_cfg, &dd_str_ctx->disp_pict_buf.buf_config, + sizeof(*pictbuf_cfg))) { + /* + * Configuration of output buffer doesn't match the + * rest. + */ + pr_err("[SID=0x%08X] All output buffers must have the same properties.", + dd_str_ctx->res_str_id); + ret = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + } + + /* Return success.. */ + return IMG_SUCCESS; + +error: + return ret; +} + +int +core_stream_set_output_config(unsigned int res_str_id, + struct vdec_str_opconfig *str_opcfg, + struct vdec_pict_bufconfig *pict_bufcfg_handle) +{ + struct vdecdd_supp_check supp_check; + struct vdec_pict_bufconfig pict_buf_cfg; + struct vdec_pict_rendinfo disp_pict_rend_info; + int ret; + + struct vdecdd_ddstr_ctx *dd_str_context; + struct core_stream_context *core_str_ctx; + + /* + * Stream based messages without a device context + * must have a stream ID. + */ + VDEC_ASSERT(res_str_id); + + /* Get access to stream context */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_context = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_context); + VDEC_ASSERT(str_opcfg); + + memset(&supp_check, 0, sizeof(supp_check)); + if (core_str_ctx->new_seq) + supp_check.comseq_hdrinfo = &dd_str_context->comseq_hdr_info; + else + supp_check.comseq_hdrinfo = NULL; + + supp_check.op_cfg = str_opcfg; + + /* + * Validate stream output configuration against display + * buffer properties if no new picture buffer configuration + * is provided. + */ + if (!pict_bufcfg_handle) { + VDEC_ASSERT(dd_str_context->disp_pict_buf.rend_info.rendered_size); + supp_check.disp_pictbuf = &dd_str_context->disp_pict_buf; + } + + /* Validate output configuration. */ + ret = core_check_decoder_support(dd_str_context->dd_dev_context, + &dd_str_context->str_config_data, + &dd_str_context->prev_comseq_hdr_info, + &dd_str_context->prev_pict_hdr_info, + &dd_str_context->map_buf_info, + &supp_check); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return IMG_SUCCESS; + + if (core_is_unsupported(&supp_check.unsupp_flags)) + return IMG_ERROR_NOT_SUPPORTED; + + /* Update the stream output configuration. */ + dd_str_context->opconfig = *str_opcfg; + + /* Mark output as configured. */ + dd_str_context->str_op_configured = TRUE; + + if (pict_bufcfg_handle) { + /* + * Clear/invalidate the latest picture buffer configuration + * since it is easier to reuse the set function to calculate + * for this new output configuration than to determine + * compatibility. Keep a copy beforehand just in case the new + * configuration is invalid. + */ + if (dd_str_context->disp_pict_buf.rend_info.rendered_size != 0) { + pict_buf_cfg = dd_str_context->disp_pict_buf.buf_config; + disp_pict_rend_info = dd_str_context->disp_pict_buf.rend_info; + + memset(&dd_str_context->disp_pict_buf.buf_config, 0, + sizeof(dd_str_context->disp_pict_buf.buf_config)); + memset(&dd_str_context->disp_pict_buf.rend_info, 0, + sizeof(dd_str_context->disp_pict_buf.rend_info)); + } + + /* + * Recalculate the picture buffer internal layout from the + * externalconfiguration. These settings provided by the + * allocator should be adhered to since the display process + * will expect the decoder to use them. + * If the configuration is invalid we need to leave the + * decoder state as it was before. + */ + ret = core_stream_set_pictbuf_config(dd_str_context, pict_bufcfg_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS && dd_str_context->disp_pict_buf.rend_info.rendered_size + != 0) { + /* Restore old picture buffer configuration */ + dd_str_context->disp_pict_buf.buf_config = + pict_buf_cfg; + dd_str_context->disp_pict_buf.rend_info = + disp_pict_rend_info; + return ret; + } + } else if (core_is_unsupported(&supp_check.unsupp_flags)) { + return IMG_ERROR_NOT_SUPPORTED; + } + + /* Return success.. */ + return ret; +} + +/* + * @Function core_stream_play + */ +int core_stream_play(unsigned int res_str_id) +{ + int ret; + struct vdecdd_ddstr_ctx *dd_str_context; + struct core_stream_context *core_str_ctx; + /* Picture buffer layout to use for decoding. */ + struct vdecdd_ddpict_buf *disp_pict_buf; + struct vdec_str_opconfig *op_cfg; + struct vdecdd_supp_check supp_check; + + /* + * Stream based messages without a device context + * must have a stream ID. + */ + VDEC_ASSERT(res_str_id); + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_context = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_context); + + /* Ensure we are stopped. */ + VDEC_ASSERT(dd_str_context->dd_str_state == VDECDD_STRSTATE_STOPPED); + + /* Set "playing". */ + dd_str_context->dd_str_state = VDECDD_STRSTATE_PLAYING; + + /* set that is it not yet in closed GOP */ + core_str_ctx->no_prev_refs_used = TRUE; + + disp_pict_buf = dd_str_context->disp_pict_buf.rend_info.rendered_size ? + &dd_str_context->disp_pict_buf : NULL; + op_cfg = dd_str_context->str_op_configured ? + &dd_str_context->opconfig : NULL; + + if (disp_pict_buf && op_cfg) { + VDEC_ASSERT(!disp_pict_buf->pict_buf); + + if (memcmp(&core_str_ctx->op_cfg, op_cfg, + sizeof(core_str_ctx->op_cfg)) || + memcmp(&core_str_ctx->disp_pict_buf, disp_pict_buf, + sizeof(core_str_ctx->disp_pict_buf))) + core_str_ctx->new_op_cfg = TRUE; + + core_str_ctx->disp_pict_buf = *disp_pict_buf; + core_str_ctx->op_cfg = *op_cfg; + + core_str_ctx->opcfg_set = TRUE; + } else { + core_str_ctx->opcfg_set = FALSE; + /* Must not be decoding without output configuration */ + VDEC_ASSERT(0); + } + + memset(&supp_check, 0, sizeof(supp_check)); + + if (vdec_size_nz(core_str_ctx->comseq_hdr_info.max_frame_size)) + supp_check.comseq_hdrinfo = &core_str_ctx->comseq_hdr_info; + + if (core_str_ctx->opcfg_set) { + supp_check.op_cfg = &core_str_ctx->op_cfg; + supp_check.disp_pictbuf = &core_str_ctx->disp_pict_buf; + } + supp_check.non_cfg_req = TRUE; + ret = core_check_decoder_support(dd_str_context->dd_dev_context, + &dd_str_context->str_config_data, + &dd_str_context->prev_comseq_hdr_info, + &dd_str_context->prev_pict_hdr_info, + &dd_str_context->map_buf_info, + &supp_check); + if (ret != IMG_SUCCESS) + return ret; + + /* Return success.. */ + return IMG_SUCCESS; +} + +/* + * @Function core_deinitialise + */ +int core_deinitialise(void) +{ + struct vdecdd_dddev_context *dd_dev_ctx; + int ret; + + dd_dev_ctx = global_core_ctx->dev_ctx; + VDEC_ASSERT(dd_dev_ctx); + if (!dd_dev_ctx) + return IMG_ERROR_NOT_INITIALISED; + + ret = decoder_deinitialise(dd_dev_ctx->dec_context); + VDEC_ASSERT(ret == IMG_SUCCESS); + + /* Free context resources.. */ + rman_destroy_bucket(dd_dev_ctx->res_buck_handle); + + rman_deinitialise(); + + kfree(dd_dev_ctx); + + global_core_ctx->dev_ctx = NULL; + + kfree(global_core_ctx); + global_core_ctx = NULL; + + is_core_initialized = FALSE; + + pr_debug("Core deinitialise successfully\n"); + return IMG_SUCCESS; +} + +static int core_get_mb_num(unsigned int width, unsigned int height) +{ + /* + * Calculate the number of MBs needed for current video + * sequence settings. + */ + unsigned int width_mb = ALIGN(width, VDEC_MB_DIMENSION) / VDEC_MB_DIMENSION; + unsigned int height_mb = ALIGN(height, 2 * VDEC_MB_DIMENSION) / VDEC_MB_DIMENSION; + + return width_mb * height_mb; +} + +static int core_common_bufs_getsize(struct core_stream_context *core_str_ctx, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + struct vdec_pict_size *max_pict_size, + struct core_pict_bufsize_info *size_info, + struct core_seq_resinfo *seq_res_info, + unsigned char *res_needed) +{ + enum vdec_vid_std vid_std = core_str_ctx->dd_str_ctx->str_config_data.vid_std; + unsigned int std_idx = vid_std - 1; + unsigned int mb_num = 0; + + if (core_str_ctx->dd_str_ctx->str_config_data.vid_std >= VDEC_STD_MAX) + return IMG_ERROR_GENERIC_FAILURE; + + /* Reset the MB parameters buffer size. */ + size_info->mbparams_bufsize = 0; + + if (mbparam_allocinfo[std_idx].alloc_mbparam_bufs) { + *res_needed = TRUE; + + /* + * Calculate the number of MBs needed for current video + * sequence settings. + */ + mb_num = core_get_mb_num(max_pict_size->width, max_pict_size->height); + + /* Calculate the final number of MBs needed. */ + mb_num += mbparam_allocinfo[std_idx].overalloc_mbnum; + + /* Calculate the MB params buffer size. */ + size_info->mbparams_bufsize = mb_num * mbparam_allocinfo[std_idx].mbparam_size; + + /* Adjust the buffer size for MSVDX. */ + vdecddutils_buf_vxd_adjust_size(&size_info->mbparams_bufsize); + + if (comseq_hdrinfo->separate_chroma_planes) + size_info->mbparams_bufsize *= 3; + } + + return IMG_SUCCESS; +} + +/* + * @Function core_pict_res_getinfo + */ +static int +core_pict_res_getinfo(struct core_stream_context *core_str_ctx, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + const struct vdec_str_opconfig *op_cfg, + const struct vdecdd_ddpict_buf *disp_pictbuf, + struct core_pict_resinfo *pict_resinfo, + struct core_seq_resinfo *seq_resinfo) +{ + struct vdec_pict_size coded_pict_size; + struct dec_ctx *decctx; + unsigned char res_needed = FALSE; + int ret; + + /* Reset the picture resource info. */ + memset(pict_resinfo, 0, sizeof(*pict_resinfo)); + + coded_pict_size = comseq_hdrinfo->max_frame_size; + + VDEC_ASSERT(core_str_ctx->std_spec_ops); + if (core_str_ctx->std_spec_ops->bufs_get_size) + core_str_ctx->std_spec_ops->bufs_get_size(core_str_ctx, comseq_hdrinfo, + &coded_pict_size, + &pict_resinfo->size_info, seq_resinfo, &res_needed); + + /* If any picture resources are needed... */ + if (res_needed) { + /* Get the number of resources required. */ + ret = vdecddutils_get_minrequired_numpicts + (&core_str_ctx->dd_str_ctx->str_config_data, + comseq_hdrinfo, op_cfg, + &pict_resinfo->pict_res_num); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + decctx = (struct dec_ctx *)global_core_ctx->dev_ctx->dec_context; + + if (core_str_ctx->dd_str_ctx->str_config_data.vid_std == VDEC_STD_HEVC) + pict_resinfo->pict_res_num += decctx->dev_cfg->num_slots_per_pipe - 1; + else + pict_resinfo->pict_res_num += + decctx->num_pipes * decctx->dev_cfg->num_slots_per_pipe - 1; + } + + return IMG_SUCCESS; +} + +static int core_alloc_resbuf(struct vdecdd_ddbuf_mapinfo **buf_handle, + unsigned int size, void *mmu_handle, + struct vxdio_mempool mem_pool) +{ + int ret; + struct vdecdd_ddbuf_mapinfo *buf; + + *buf_handle = kzalloc(sizeof(**buf_handle), GFP_KERNEL); + buf = *buf_handle; + VDEC_ASSERT(buf); + if (buf) { + buf->mmuheap_id = MMU_HEAP_STREAM_BUFFERS; +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(mmu_handle, buf->mmuheap_id, + mem_pool.mem_heap_id, + mem_pool.mem_attrib, size, + DEV_MMU_PAGE_SIZE, + &buf->ddbuf_info); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + ret = IMG_ERROR_OUT_OF_MEMORY; + } else { + ret = IMG_ERROR_OUT_OF_MEMORY; + } + return ret; +} + +#ifdef SEQ_RES_NEEDED +static int core_alloc_common_sequence_buffers(struct core_stream_context *core_str_ctx, + struct vdecdd_seq_resint *seqres_int, + struct vxdio_mempool mem_pool, + struct core_seq_resinfo *seqres_info, + struct core_pict_resinfo *pictres_info, + const struct vdec_str_opconfig *op_cfg, + const struct vdecdd_ddpict_buf *disp_pict_buf) +{ + int ret = IMG_SUCCESS; +#ifdef ERROR_CONCEALMENT + enum vdec_vid_std vid_std = core_str_ctx->dd_str_ctx->str_config_data.vid_std; + unsigned int std_idx = vid_std - 1; + struct vidio_ddbufinfo *err_buf_info; + + /* Allocate error concealment pattern frame for current sequence */ + if (err_recovery_frame_info[std_idx].enabled) { + struct vdec_pict_bufconfig buf_config; + unsigned int size; + + buf_config = disp_pict_buf->buf_config; + size = buf_config.coded_width * buf_config.coded_height; + + if (err_recovery_frame_info[std_idx].max_size > size) { + seqres_int->err_pict_buf = kzalloc(sizeof(*seqres_int->err_pict_buf), + GFP_KERNEL); + VDEC_ASSERT(seqres_int->err_pict_buf); + if (!seqres_int->err_pict_buf) + return IMG_ERROR_OUT_OF_MEMORY; + + seqres_int->err_pict_buf->mmuheap_id = MMU_HEAP_STREAM_BUFFERS; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("===== %s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(core_str_ctx->dd_str_ctx->mmu_str_handle, + seqres_int->err_pict_buf->mmuheap_id, + mem_pool.mem_heap_id, + (enum sys_emem_attrib)(mem_pool.mem_attrib | + SYS_MEMATTRIB_CPU_WRITE), + buf_config.buf_size, + DEV_MMU_PAGE_ALIGNMENT, + &seqres_int->err_pict_buf->ddbuf_info); + if (ret != IMG_SUCCESS) + return IMG_ERROR_OUT_OF_MEMORY; + + /* make grey pattern - luma & chroma at mid-rail */ + err_buf_info = &seqres_int->err_pict_buf->ddbuf_info; + if (op_cfg->pixel_info.mem_pkg == PIXEL_BIT10_MP) { + unsigned int *out = (unsigned int *)err_buf_info->cpu_virt; + unsigned int i; + + for (i = 0; i < err_buf_info->buf_size / sizeof(unsigned int); i++) + /* See PIXEL_BIT10_MP layout definition */ + out[i] = 0x20080200; + } else { + /* Note: Setting 0x80 also gives grey pattern + * for 10bit upacked MSB format. + */ + memset(err_buf_info->cpu_virt, 0x80, err_buf_info->buf_size); + } + } + } +#endif + return ret; +} +#endif + +/* + * @Function core_do_resource_realloc + */ +static unsigned char core_do_resource_realloc(struct core_stream_context *core_str_ctx, + struct core_pict_resinfo *pictres_info, + struct core_seq_resinfo *seqres_info) +{ + VDEC_ASSERT(core_str_ctx->std_spec_ops); + /* If buffer sizes are sufficient and only the greater number of resources is needed... */ + if (core_str_ctx->pict_resinfo.size_info.mbparams_bufsize >= + pictres_info->size_info.mbparams_bufsize && + (core_str_ctx->std_spec_ops->is_stream_resource_suitable ? + core_str_ctx->std_spec_ops->is_stream_resource_suitable(pictres_info, + &core_str_ctx->pict_resinfo, + seqres_info, &core_str_ctx->seq_resinfo) : TRUE) && + core_str_ctx->pict_resinfo.pict_res_num < pictres_info->pict_res_num) + /* ...full internal resource reallocation is not required. */ + return FALSE; + + /* Otherwise request full internal resource reallocation. */ + return TRUE; +} + +/* + * @Function core_is_stream_resource_suitable + */ +static unsigned char core_is_stream_resource_suitable + (struct core_stream_context *core_str_ctx, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + const struct vdec_str_opconfig *op_cfg, + const struct vdecdd_ddpict_buf *disp_pict_buf, + struct core_pict_resinfo *pictres_info, + struct core_seq_resinfo *seqres_info_ptr) +{ + int ret; + struct core_pict_resinfo aux_pictes_info; + struct core_pict_resinfo *aux_pictes_info_ptr; + struct core_seq_resinfo seqres_info; + + /* If resource info is needed externally, just use it. Otherwise use internal structure. */ + if (pictres_info) + aux_pictes_info_ptr = pictres_info; + else + aux_pictes_info_ptr = &aux_pictes_info; + + if (!seqres_info_ptr) + seqres_info_ptr = &seqres_info; + + /* Get the resource info for current settings. */ + ret = core_pict_res_getinfo(core_str_ctx, comseq_hdrinfo, op_cfg, disp_pict_buf, + aux_pictes_info_ptr, seqres_info_ptr); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return FALSE; + + VDEC_ASSERT(core_str_ctx->std_spec_ops); + if (core_str_ctx->std_spec_ops->is_stream_resource_suitable) { + if (!core_str_ctx->std_spec_ops->is_stream_resource_suitable + (aux_pictes_info_ptr, + &core_str_ctx->pict_resinfo, + seqres_info_ptr, &core_str_ctx->seq_resinfo)) + return FALSE; + } + + /* Check the number of picture resources required against the current number. */ + if (aux_pictes_info_ptr->pict_res_num > core_str_ctx->pict_resinfo.pict_res_num) + return FALSE; + + return TRUE; +} + +static int core_alloc_common_pict_buffers(struct core_stream_context *core_str_ctx, + struct vdecdd_pict_resint *pictres_int, + struct vxdio_mempool mem_pool, + struct core_pict_resinfo *pictres_info) +{ + int ret = IMG_SUCCESS; + + /* If MB params buffers are needed... */ + if (pictres_info->size_info.mbparams_bufsize > 0) + /* Allocate the MB parameters buffer info structure. */ + ret = core_alloc_resbuf(&pictres_int->mb_param_buf, + pictres_info->size_info.mbparams_bufsize, + core_str_ctx->dd_str_ctx->mmu_str_handle, + mem_pool); + + return ret; +} + +/* + * @Function core_stream_resource_create + */ +static int core_stream_resource_create(struct core_stream_context *core_str_ctx, + unsigned char closed_gop, unsigned int mem_heap_id, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + const struct vdec_str_opconfig *op_cfg, + const struct vdecdd_ddpict_buf *disp_pict_buf) +{ + struct vdecdd_pict_resint *pictres_int = NULL; + int ret = IMG_SUCCESS; + unsigned int i, start_cnt = 0; + struct core_pict_resinfo pictres_info; + struct vdecdd_seq_resint *seqres_int = NULL; + struct core_seq_resinfo seqres_info; + struct vxdio_mempool mem_pool; + + mem_pool.mem_heap_id = mem_heap_id; + mem_pool.mem_attrib = (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED + | SYS_MEMATTRIB_WRITECOMBINE | SYS_MEMATTRIB_INTERNAL); + +#ifdef SEQ_RES_NEEDED + seqres_int = lst_first(&core_str_ctx->seq_res_list); +#endif + /* + * Clear the reconstructed picture buffer layout if the previous + * references are no longer used. Only under these circumstances + * should the bitstream resolution change. + */ + if (closed_gop) { + memset(&core_str_ctx->recon_pictbuf.rend_info, 0, + sizeof(core_str_ctx->recon_pictbuf.rend_info)); + memset(&core_str_ctx->coded_pict_size, 0, sizeof(core_str_ctx->coded_pict_size)); + } else { + if (vdec_size_ne(core_str_ctx->coded_pict_size, comseq_hdrinfo->max_frame_size)) { + VDEC_ASSERT(FALSE); + pr_err("Coded picture size changed within the closed GOP (i.e. mismatched references)"); + } + } + + /* If current buffers are not suitable for specified VSH/Output config... */ + if (!core_is_stream_resource_suitable(core_str_ctx, comseq_hdrinfo, + op_cfg, disp_pict_buf, &pictres_info, + &seqres_info)) { + /* If full internal resource reallocation is needed... */ + if (core_do_resource_realloc(core_str_ctx, &pictres_info, &seqres_info)) { + /* + * Mark all the active resources as deprecated and + * free-up where no longer used. + */ + core_stream_resource_deprecate(core_str_ctx); + } else { + /* Use current buffer size settings. */ + pictres_info.size_info = core_str_ctx->pict_resinfo.size_info; + seqres_info = core_str_ctx->seq_resinfo; + + /* Set start counter to only allocate the number of + * resources that are missing. + */ + start_cnt = core_str_ctx->pict_resinfo.pict_res_num; + } + +#ifdef SEQ_RES_NEEDED + /* allocate sequence resources */ + { + seqres_int = kzalloc(sizeof(*seqres_int), GFP_KERNEL); + VDEC_ASSERT(seqres_int); + if (!seqres_int) + goto err_out_of_memory; + + lst_add(&core_str_ctx->seq_res_list, seqres_int); + /* Allocate sequence buffers common for all standards. */ + ret = core_alloc_common_sequence_buffers + (core_str_ctx, seqres_int, mem_pool, + &seqres_info, + &pictres_info, op_cfg, disp_pict_buf); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + + VDEC_ASSERT(core_str_ctx->std_spec_ops); + if (core_str_ctx->std_spec_ops->alloc_sequence_buffers) { + ret = core_str_ctx->std_spec_ops->alloc_sequence_buffers + (core_str_ctx, seqres_int, + mem_pool, &seqres_info); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + } + } +#endif + /* Allocate resources for current settings. */ + for (i = start_cnt; i < pictres_info.pict_res_num; i++) { + /* Allocate the picture resources structure. */ + pictres_int = kzalloc(sizeof(*pictres_int), GFP_KERNEL); + VDEC_ASSERT(pictres_int); + if (!pictres_int) + goto err_out_of_memory; + + /* Allocate picture buffers common for all standards. */ + ret = core_alloc_common_pict_buffers(core_str_ctx, pictres_int, + mem_pool, &pictres_info); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + + /* Allocate standard specific picture buffers. */ + VDEC_ASSERT(core_str_ctx->std_spec_ops); + if (core_str_ctx->std_spec_ops->alloc_picture_buffers) { + ret = core_str_ctx->std_spec_ops->alloc_picture_buffers + (core_str_ctx, pictres_int, + mem_pool, &pictres_info); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + } + + /* attach sequence resources */ +#ifdef SEQ_RES_NEEDED + resource_item_use(&seqres_int->ref_count); + pictres_int->seq_resint = seqres_int; +#endif + lst_add(&core_str_ctx->pict_res_list, pictres_int); + core_str_ctx->pict_resinfo.pict_res_num++; + } + } + + /* + * When demand for picture resources reduces (in quantity) the extra buffers + * are still retained. Preserve the existing count in case the demand increases + * again, at which time these residual buffers won't need to be reallocated. + */ + pictres_info.pict_res_num = core_str_ctx->pict_resinfo.pict_res_num; + + /* Store the current resource config. */ + core_str_ctx->pict_resinfo = pictres_info; + core_str_ctx->seq_resinfo = seqres_info; + + pictres_int = lst_first(&core_str_ctx->pict_res_list); + while (pictres_int) { + /* + * Increment the reference count to indicate that this resource is also + * held by plant until it is added to the Scheduler list. If the resource has + * not just been created it might already be in circulation. + */ + resource_item_use(&pictres_int->ref_cnt); +#ifdef SEQ_RES_NEEDED + /* attach sequence resources */ + resource_item_use(&seqres_int->ref_count); + pictres_int->seq_resint = seqres_int; +#endif + /* Add the internal picture resources to the list. */ + ret = resource_list_add_img(&core_str_ctx->aux_pict_res_list, + pictres_int, 0, &pictres_int->ref_cnt); + + pictres_int = lst_next(pictres_int); + } + + /* + * Set the reconstructed buffer properties if they + * may have been changed. + */ + if (core_str_ctx->recon_pictbuf.rend_info.rendered_size == 0) { + core_str_ctx->recon_pictbuf.rend_info = + disp_pict_buf->rend_info; + core_str_ctx->recon_pictbuf.buf_config = + disp_pict_buf->buf_config; + core_str_ctx->coded_pict_size = comseq_hdrinfo->max_frame_size; + } else { + if (memcmp(&disp_pict_buf->rend_info, + &core_str_ctx->recon_pictbuf.rend_info, + sizeof(core_str_ctx->recon_pictbuf.rend_info))) { + /* + * Reconstructed picture buffer information has changed + * during a closed GOP. + */ + VDEC_ASSERT + ("Reconstructed picture buffer information cannot change within a GOP" + == NULL); + pr_err("Reconstructed picture buffer information cannot change within a GOP."); + return IMG_ERROR_GENERIC_FAILURE; + } + } + + /* + * When demand for picture resources reduces (in quantity) the extra buffers + * are still retained. Preserve the existing count in case the demand increases + * again, at which time these residual buffers won't need to be reallocated. + */ + pictres_info.pict_res_num = core_str_ctx->pict_resinfo.pict_res_num; + + /* Store the current resource config. */ + core_str_ctx->pict_resinfo = pictres_info; + core_str_ctx->seq_resinfo = seqres_info; + + return IMG_SUCCESS; + + /* Handle out of memory errors. */ +err_out_of_memory: + /* Free resources being currently allocated. */ + if (pictres_int) { + core_free_common_picture_resource(core_str_ctx, pictres_int); + if (core_str_ctx->std_spec_ops->free_picture_resource) + core_str_ctx->std_spec_ops->free_picture_resource(core_str_ctx, + pictres_int); + + kfree(pictres_int); + } + +#ifdef SEQ_RES_NEEDED + if (seqres_int) { + core_free_common_sequence_resource(core_str_ctx, seqres_int); + + if (core_str_ctx->std_spec_ops->free_sequence_resource) + core_str_ctx->std_spec_ops->free_sequence_resource(core_str_ctx, + seqres_int); + + VDEC_ASSERT(lst_last(&core_str_ctx->seq_res_list) == seqres_int); + lst_remove(&core_str_ctx->seq_res_list, seqres_int); + kfree(seqres_int); + } +#endif + + /* Free all the other resources. */ + core_stream_resource_destroy(core_str_ctx); + + pr_err("[USERSID=0x%08X] Core not able to allocate stream resources due to lack of memory", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id); + + return IMG_ERROR_OUT_OF_MEMORY; +} + +static int +core_reconfigure_recon_pictbufs(struct core_stream_context *core_str_ctx, + unsigned char no_references) +{ + struct vdecdd_ddstr_ctx *dd_str_ctx; + int ret; + + dd_str_ctx = core_str_ctx->dd_str_ctx; + VDEC_ASSERT(dd_str_ctx->str_op_configured); + + /* Re-configure the internal picture buffers now that none are held. */ + ret = core_stream_resource_create(core_str_ctx, no_references, + dd_str_ctx->dd_dev_context->internal_heap_id, + &dd_str_ctx->comseq_hdr_info, + &dd_str_ctx->opconfig, + &dd_str_ctx->disp_pict_buf); + return ret; +} + +/* + * @Function core_picture_prepare + */ +static int core_picture_prepare(struct core_stream_context *core_str_ctx, + struct vdecdd_str_unit *str_unit) +{ + int ret = IMG_SUCCESS; + struct vdecdd_picture *pict_local = NULL; + unsigned int avail = 0; + unsigned char need_pict_res; + + /* + * For normal decode, setup picture data. + * Preallocate the picture structure. + */ + pict_local = kzalloc(sizeof(*pict_local), GFP_KERNEL); + if (!pict_local) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Determine whether the picture can be decoded. */ + ret = decoder_get_load(core_str_ctx->dd_str_ctx->dec_ctx, &global_avail_slots); + if (ret != IMG_SUCCESS) { + pr_err("No resources avaialable to decode this picture"); + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + goto unwind; + } + + /* + * Load and availability is cached in stream context simply + * for status reporting. + */ + avail = core_get_resource_availability(core_str_ctx); + + if ((avail & CORE_AVAIL_CORE) == 0) { + /* Return straight away if the core is not available */ + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + goto unwind; + } + + if (core_str_ctx->new_op_cfg || core_str_ctx->new_seq) { + /* + * Reconstructed buffers should be checked for reconfiguration + * under these conditions: + * 1. New output configuration, + * 2. New sequence. + * Core can decide to reset the reconstructed buffer properties + * if there are no previous reference pictures used + * (i.e. at a closed GOP). This code must go here because we + * may not stop when new sequence is found or references become + * unused. + */ + ret = core_reconfigure_recon_pictbufs(core_str_ctx, + core_str_ctx->no_prev_refs_used); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto unwind; + } + + /* Update the display information for this picture. */ + ret = vdecddutils_get_display_region(&str_unit->pict_hdr_info->coded_frame_size, + &str_unit->pict_hdr_info->disp_info.enc_disp_region, + &str_unit->pict_hdr_info->disp_info.disp_region); + + if (ret != IMG_SUCCESS) + goto unwind; + + /* Clear internal state */ + core_str_ctx->new_seq = FALSE; + core_str_ctx->new_op_cfg = FALSE; + core_str_ctx->no_prev_refs_used = FALSE; + + /* + * Recalculate this since we might have just created + * internal resources. + */ + core_str_ctx->res_avail = core_get_resource_availability(core_str_ctx); + + /* + * If picture resources were needed for this stream, picture resources + * list wouldn't be empty + */ + need_pict_res = !lst_empty(&core_str_ctx->aux_pict_res_list); + /* If there are resources available */ + if ((core_str_ctx->res_avail & CORE_AVAIL_PICTBUF) && + (!need_pict_res || (core_str_ctx->res_avail & CORE_AVAIL_PICTRES))) { + /* Pick internal picture resources. */ + if (need_pict_res) { + pict_local->pict_res_int = + resource_list_get_avail(&core_str_ctx->aux_pict_res_list); + + VDEC_ASSERT(pict_local->pict_res_int); + if (!pict_local->pict_res_int) { + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + goto unwind; + } + } + + /* Pick the client image buffer. */ + pict_local->disp_pict_buf.pict_buf = + resource_list_get_avail(&core_str_ctx->pict_buf_list); + VDEC_ASSERT(pict_local->disp_pict_buf.pict_buf); + if (!pict_local->disp_pict_buf.pict_buf) { + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + goto unwind; + } + } else { + /* Need resources to process picture start. */ + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + goto unwind; + } + + /* Ensure that the buffer contains layout information. */ + pict_local->disp_pict_buf.rend_info = core_str_ctx->disp_pict_buf.rend_info; + pict_local->disp_pict_buf.buf_config = core_str_ctx->disp_pict_buf.buf_config; + pict_local->op_config = core_str_ctx->op_cfg; + pict_local->last_pict_in_seq = str_unit->last_pict_in_seq; + + str_unit->dd_pict_data = pict_local; + + /* Indicate that all necessary resources are now available. */ + if (core_str_ctx->res_avail != ~0) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("LAST AVAIL: 0x%08X\n", core_str_ctx->res_avail); +#endif + core_str_ctx->res_avail = ~0; + } + +#ifdef DEBUG_DECODER_DRIVER + /* dump decoder internal resource addresses */ + if (pict_local->pict_res_int) { + if (pict_local->pict_res_int->mb_param_buf) { + pr_info("[USERSID=0x%08X] MB parameter buffer device virtual address: 0x%08X", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id, + pict_local->pict_res_int->mb_param_buf->ddbuf_info.dev_virt); + } + + if (core_str_ctx->comseq_hdr_info.separate_chroma_planes) { + pr_info("[USERSID=0x%08X] Display picture virtual address: LUMA 0x%08X, CHROMA 0x%08X, CHROMA2 0x%08X", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id, + pict_local->disp_pict_buf.pict_buf->ddbuf_info.dev_virt, + pict_local->disp_pict_buf.pict_buf->ddbuf_info.dev_virt + + pict_local->disp_pict_buf.rend_info.plane_info + [VDEC_PLANE_VIDEO_U].offset, + pict_local->disp_pict_buf.pict_buf->ddbuf_info.dev_virt + + pict_local->disp_pict_buf.rend_info.plane_info + [VDEC_PLANE_VIDEO_V].offset); + } else { + pr_info("[USERSID=0x%08X] Display picture virtual address: LUMA 0x%08X, CHROMA 0x%08X", + core_str_ctx->dd_str_ctx->str_config_data.user_str_id, + pict_local->disp_pict_buf.pict_buf->ddbuf_info.dev_virt, + pict_local->disp_pict_buf.pict_buf->ddbuf_info.dev_virt + + pict_local->disp_pict_buf.rend_info.plane_info + [VDEC_PLANE_VIDEO_UV].offset); + } + } +#endif + + ret = core_picture_attach_resources(core_str_ctx, str_unit, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto unwind; + + return IMG_SUCCESS; + +unwind: + if (pict_local->pict_res_int) { + resource_item_return(&pict_local->pict_res_int->ref_cnt); + pict_local->pict_res_int = NULL; + } + if (pict_local->disp_pict_buf.pict_buf) { + resource_item_return(&pict_local->disp_pict_buf.pict_buf->ddbuf_info.ref_count); + pict_local->disp_pict_buf.pict_buf = NULL; + } + kfree(pict_local); + return ret; +} + +/* + * @Function core_validate_new_sequence + */ +static int core_validate_new_sequence(struct core_stream_context *core_str_ctx, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo) +{ + int ret; + struct vdecdd_supp_check supp_check; + struct vdecdd_ddstr_ctx *dd_str_ctx; + unsigned int num_req_bufs_prev, num_req_bufs_cur; + struct vdecdd_mapbuf_info mapbuf_info; + + memset(&supp_check, 0, sizeof(supp_check)); + + /* + * Omit picture header from this setup since we can'supp_check + * validate this here. + */ + supp_check.comseq_hdrinfo = comseq_hdrinfo; + + if (core_str_ctx->opcfg_set) { + supp_check.op_cfg = &core_str_ctx->op_cfg; + supp_check.disp_pictbuf = &core_str_ctx->disp_pict_buf; + + ret = vdecddutils_get_minrequired_numpicts + (&core_str_ctx->dd_str_ctx->str_config_data, + &core_str_ctx->comseq_hdr_info, + &core_str_ctx->op_cfg, + &num_req_bufs_prev); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + ret = vdecddutils_get_minrequired_numpicts + (&core_str_ctx->dd_str_ctx->str_config_data, + comseq_hdrinfo, + &core_str_ctx->op_cfg, + &num_req_bufs_cur); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + /* Check if the output configuration is compatible with new VSH. */ + dd_str_ctx = core_str_ctx->dd_str_ctx; + mapbuf_info = dd_str_ctx->map_buf_info; + + /* Check the compatibility of the bitstream data and configuration */ + supp_check.non_cfg_req = TRUE; + ret = core_check_decoder_support(dd_str_ctx->dd_dev_context, + &dd_str_ctx->str_config_data, + &dd_str_ctx->prev_comseq_hdr_info, + &dd_str_ctx->prev_pict_hdr_info, + &mapbuf_info, &supp_check); + if (ret != IMG_SUCCESS) + return ret; + + core_str_ctx->new_seq = TRUE; + + return IMG_SUCCESS; +} + +static int +core_validate_new_picture(struct core_stream_context *core_str_ctx, + const struct bspp_pict_hdr_info *pict_hdrinfo, + unsigned int *features) +{ + int ret; + struct vdecdd_supp_check supp_check; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct vdecdd_mapbuf_info mapbuf_info; + + memset(&supp_check, 0, sizeof(supp_check)); + supp_check.comseq_hdrinfo = &core_str_ctx->comseq_hdr_info; + supp_check.pict_hdrinfo = pict_hdrinfo; + + /* + * They cannot become invalid during a sequence. + * However, output configuration may signal something that + * changes compatibility on a closed GOP within a sequence + * (e.g. resolution may significantly decrease + * in a GOP and scaling wouldn't be supported). This resolution shift + * would not be signalled in the sequence header + * (since that is the maximum) but only + * found now when validating the first picture in the GOP. + */ + if (core_str_ctx->opcfg_set) + supp_check.op_cfg = &core_str_ctx->op_cfg; + + /* + * Check if the new picture is compatible with the + * current driver state. + */ + dd_str_ctx = core_str_ctx->dd_str_ctx; + mapbuf_info = dd_str_ctx->map_buf_info; + + /* Check the compatibility of the bitstream data and configuration */ + supp_check.non_cfg_req = TRUE; + ret = core_check_decoder_support(dd_str_ctx->dd_dev_context, + &dd_str_ctx->str_config_data, + &dd_str_ctx->prev_comseq_hdr_info, + &dd_str_ctx->prev_pict_hdr_info, + &mapbuf_info, &supp_check); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + if (supp_check.unsupp_flags.str_opcfg || supp_check.unsupp_flags.pict_hdr) + return IMG_ERROR_NOT_SUPPORTED; + + /* + * Clear the reconfiguration flags unless triggered by + * unsupported output config. + */ + *features = supp_check.features; + + return IMG_SUCCESS; +} + +/* + * @Function core_stream_submit_unit + */ +int core_stream_submit_unit(unsigned int res_str_id, struct vdecdd_str_unit *str_unit) +{ + int ret; + unsigned char process_str_unit = TRUE; + + struct vdecdd_ddstr_ctx *dd_str_context; + struct core_stream_context *core_str_ctx; + + /* + * Stream based messages without a device context + * must have a stream ID. + */ + VDEC_ASSERT(res_str_id); + VDEC_ASSERT(str_unit); + + if (res_str_id == 0 || !str_unit) { + pr_err("Invalid params passed to %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + VDEC_ASSERT(core_str_ctx); + dd_str_context = core_str_ctx->dd_str_ctx; + VDEC_ASSERT(dd_str_context); + + ret = resource_list_add_img(&core_str_ctx->str_unit_list, str_unit, 0, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + + pr_debug("%s stream unit type = %d\n", __func__, str_unit->str_unit_type); + switch (str_unit->str_unit_type) { + case VDECDD_STRUNIT_SEQUENCE_START: + if (str_unit->seq_hdr_info) { + /* Add sequence header to cache. */ + ret = + resource_list_replace(&core_str_ctx->seq_hdr_list, + str_unit->seq_hdr_info, + str_unit->seq_hdr_info->sequ_hdr_id, + &str_unit->seq_hdr_info->ref_count, + NULL, NULL); + + if (ret != IMG_SUCCESS) + pr_err("[USERSID=0x%08X] Failed to replace resource", + res_str_id); + } else { + /* ...or take from cache. */ + str_unit->seq_hdr_info = + resource_list_getbyid(&core_str_ctx->seq_hdr_list, + str_unit->seq_hdr_id); + } + + VDEC_ASSERT(str_unit->seq_hdr_info); + if (!str_unit->seq_hdr_info) { + pr_err("Sequence header information not available for current picture"); + break; + } + /* + * Check that this latest sequence header information is + * compatible with current state and then if no errors store + * as current. + */ + core_str_ctx->comseq_hdr_info = str_unit->seq_hdr_info->com_sequ_hdr_info; + + ret = core_validate_new_sequence(core_str_ctx, + &str_unit->seq_hdr_info->com_sequ_hdr_info); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_context->prev_comseq_hdr_info = + dd_str_context->comseq_hdr_info; + dd_str_context->comseq_hdr_info = + str_unit->seq_hdr_info->com_sequ_hdr_info; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[SID=0x%08X] VSH: Maximum Frame Resolution [%dx%d]", + dd_str_context->res_str_id, + dd_str_context->comseq_hdr_info.max_frame_size.width, + dd_str_context->comseq_hdr_info.max_frame_size.height); +#endif + + break; + + case VDECDD_STRUNIT_PICTURE_START: + /* + * Check that the picture configuration is compatible + * with the current state. + */ + ret = core_validate_new_picture(core_str_ctx, + str_unit->pict_hdr_info, + &str_unit->features); + if (ret != IMG_SUCCESS) { + if (ret == IMG_ERROR_NOT_SUPPORTED) { + /* + * Do not process stream unit since there is + * something unsupported. + */ + process_str_unit = FALSE; + break; + } + } + + /* Prepare picture for decoding. */ + ret = core_picture_prepare(core_str_ctx, str_unit); + if (ret != IMG_SUCCESS) + if (ret == IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE || + ret == IMG_ERROR_NOT_SUPPORTED) + /* + * Do not process stream unit since there is + * something unsupported or resources are not + * available. + */ + process_str_unit = FALSE; + break; + + default: + /* + * Sequence/picture headers should only be attached to + * corresponding units. + */ + VDEC_ASSERT(!str_unit->seq_hdr_info); + VDEC_ASSERT(!str_unit->pict_hdr_info); + break; + } + + if (process_str_unit) { + /* Submit stream unit to the decoder for processing. */ + str_unit->decode = TRUE; + ret = decoder_stream_process_unit(dd_str_context->dec_ctx, + str_unit); + } else { + ret = IMG_ERROR_GENERIC_FAILURE; + } + + return ret; +} + +/* + * @Function core_stream_fill_pictbuf + */ +int core_stream_fill_pictbuf(unsigned int buf_map_id) +{ + int ret; + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct core_stream_context *core_str_ctx; + + /* Get access to map info context.. */ + ret = rman_get_resource(buf_map_id, VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = ddbuf_map_info->ddstr_context; + + /* Get access to stream context.. */ + ret = rman_get_resource(dd_str_ctx->res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Check buffer type. */ + VDEC_ASSERT(ddbuf_map_info->buf_type == VDEC_BUFTYPE_PICTURE); + + /* Add the image buffer to the list */ + ret = resource_list_add_img(&core_str_ctx->pict_buf_list, ddbuf_map_info, + 0, &ddbuf_map_info->ddbuf_info.ref_count); + + return ret; +} + +/* + * @Function core_fn_free_mapped + */ +static void core_fn_free_mapped(void *param) +{ + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info = + (struct vdecdd_ddbuf_mapinfo *)param; + + /* Validate input arguments */ + VDEC_ASSERT(param); + + /* Do not free the MMU mapping. It is handled by talmmu code. */ + kfree(ddbuf_map_info); +} + +/* + * @Function core_stream_map_buf + */ +int core_stream_map_buf(unsigned int res_str_id, enum vdec_buf_type buf_type, + struct vdec_buf_info *buf_info, unsigned int *buf_map_id) +{ + int ret; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct core_stream_context *core_str_ctx; + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + + /* + * Stream based messages without a device context + * must have a stream ID. + */ + VDEC_ASSERT(res_str_id); + VDEC_ASSERT(buf_type < VDEC_BUFTYPE_MAX); + VDEC_ASSERT(buf_info); + VDEC_ASSERT(buf_map_id); + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_ctx); + + /* Allocate an active stream unit.. */ + ddbuf_map_info = kzalloc(sizeof(*ddbuf_map_info), GFP_KERNEL); + VDEC_ASSERT(ddbuf_map_info); + + if (!ddbuf_map_info) { + pr_err("[SID=0x%08X] Failed to allocate memory for DD buffer map information", + dd_str_ctx->res_str_id); + + return IMG_ERROR_OUT_OF_MEMORY; + } + memset(ddbuf_map_info, 0, sizeof(*ddbuf_map_info)); + + /* Save the stream context etc. */ + ddbuf_map_info->ddstr_context = dd_str_ctx; + ddbuf_map_info->buf_type = buf_type; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d vdec2plus: vxd map buff id %d", __func__, __LINE__, + buf_info->buf_id); +#endif + ddbuf_map_info->buf_id = buf_info->buf_id; + + /* Register the allocation as a stream resource.. */ + ret = rman_register_resource(dd_str_ctx->res_buck_handle, + VDECDD_BUFMAP_TYPE_ID, + core_fn_free_mapped, + ddbuf_map_info, + &ddbuf_map_info->res_handle, + buf_map_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + ddbuf_map_info->buf_map_id = *buf_map_id; + + if (buf_type == VDEC_BUFTYPE_PICTURE) { + if (dd_str_ctx->map_buf_info.num_buf == 0) { + dd_str_ctx->map_buf_info.buf_size = buf_info->buf_size; + dd_str_ctx->map_buf_info.byte_interleave = + buf_info->pictbuf_cfg.byte_interleave; +#ifdef DEBUG_DECODER_DRIVER + pr_info("[SID=0x%08X] Mapped Buffer size: %d (bytes)", + dd_str_ctx->res_str_id, buf_info->buf_size); +#endif + } else { + /* + * Same byte interleaved setting should be used. + * Convert to actual bools by comparing with zero. + */ + if (buf_info->pictbuf_cfg.byte_interleave != + dd_str_ctx->map_buf_info.byte_interleave) { + pr_err("[SID=0x%08X] Buffer cannot be mapped since its byte interleave value (%s) is not the same as buffers already mapped (%s)", + dd_str_ctx->res_str_id, + buf_info->pictbuf_cfg.byte_interleave ? + "ON" : "OFF", + dd_str_ctx->map_buf_info.byte_interleave ? + "ON" : "OFF"); + ret = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + } + + /* Configure the buffer.. */ + ret = core_stream_set_pictbuf_config(dd_str_ctx, &buf_info->pictbuf_cfg); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + } + + /* Map heap from VDEC to MMU. */ + switch (buf_type) { + case VDEC_BUFTYPE_BITSTREAM: + ddbuf_map_info->mmuheap_id = MMU_HEAP_BITSTREAM_BUFFERS; + break; + + case VDEC_BUFTYPE_PICTURE: + mmu_get_heap(buf_info->pictbuf_cfg.stride[VDEC_PLANE_VIDEO_Y], + &ddbuf_map_info->mmuheap_id); + break; + + default: + VDEC_ASSERT(FALSE); + } + + /* Map this buffer into the MMU. */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("----- %s:%d calling MMU_StreamMapExt", __func__, __LINE__); +#endif + ret = mmu_stream_map_ext(dd_str_ctx->mmu_str_handle, + (enum mmu_eheap_id)ddbuf_map_info->mmuheap_id, + ddbuf_map_info->buf_id, + buf_info->buf_size, DEV_MMU_PAGE_SIZE, + buf_info->mem_attrib, + buf_info->cpu_linear_addr, + &ddbuf_map_info->ddbuf_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + if (buf_type == VDEC_BUFTYPE_PICTURE) + dd_str_ctx->map_buf_info.num_buf++; + + /* + * Initialise the reference count to indicate that the client + * still holds the buffer. + */ + ddbuf_map_info->ddbuf_info.ref_count = 1; + + /* Return success.. */ + return IMG_SUCCESS; + +error: + if (ddbuf_map_info) { + if (ddbuf_map_info->res_handle) + rman_free_resource(ddbuf_map_info->res_handle); + else + kfree(ddbuf_map_info); + } + + return ret; +} + +/* + * @Function core_stream_map_buf_sg + */ +int core_stream_map_buf_sg(unsigned int res_str_id, enum vdec_buf_type buf_type, + struct vdec_buf_info *buf_info, + void *sgt, unsigned int *buf_map_id) +{ + int ret; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct core_stream_context *core_str_ctx; + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + + /* + * Resource stream ID cannot be zero. If zero just warning and + * proceeding further will break the code. Return IMG_ERROR_INVALID_ID. + */ + if (res_str_id <= 0) + return IMG_ERROR_INVALID_ID; + + VDEC_ASSERT(buf_type < VDEC_BUFTYPE_MAX); + VDEC_ASSERT(buf_info); + VDEC_ASSERT(buf_map_id); + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, (void **)&core_str_ctx, NULL); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_ctx); + + /* Allocate an active stream unit.. */ + ddbuf_map_info = kzalloc(sizeof(*ddbuf_map_info), GFP_KERNEL); + VDEC_ASSERT(ddbuf_map_info); + + if (!ddbuf_map_info) { + pr_err("[SID=0x%08X] Failed to allocate memory for DD buffer map information", + dd_str_ctx->res_str_id); + + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* Save the stream context etc. */ + ddbuf_map_info->ddstr_context = dd_str_ctx; + ddbuf_map_info->buf_type = buf_type; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d vdec2plus: vxd map buff id %d", __func__, __LINE__, + buf_info->buf_id); +#endif + ddbuf_map_info->buf_id = buf_info->buf_id; + + /* Register the allocation as a stream resource.. */ + ret = rman_register_resource(dd_str_ctx->res_buck_handle, + VDECDD_BUFMAP_TYPE_ID, + core_fn_free_mapped, ddbuf_map_info, + &ddbuf_map_info->res_handle, buf_map_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + ddbuf_map_info->buf_map_id = *buf_map_id; + + if (buf_type == VDEC_BUFTYPE_PICTURE) { + if (dd_str_ctx->map_buf_info.num_buf == 0) { + dd_str_ctx->map_buf_info.buf_size = buf_info->buf_size; + + dd_str_ctx->map_buf_info.byte_interleave = + buf_info->pictbuf_cfg.byte_interleave; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[SID=0x%08X] Mapped Buffer size: %d (bytes)", + dd_str_ctx->res_str_id, buf_info->buf_size); +#endif + } else { + /* + * Same byte interleaved setting should be used. + * Convert to actual bools by comparing with zero. + */ + if (buf_info->pictbuf_cfg.byte_interleave != + dd_str_ctx->map_buf_info.byte_interleave) { + pr_err("[SID=0x%08X] Buffer cannot be mapped since its byte interleave value (%s) is not the same as buffers already mapped (%s)", + dd_str_ctx->res_str_id, + buf_info->pictbuf_cfg.byte_interleave ? + "ON" : "OFF", + dd_str_ctx->map_buf_info.byte_interleave ? + "ON" : "OFF"); + ret = IMG_ERROR_INVALID_PARAMETERS; + goto error; + } + } + + /* Configure the buffer.. */ + ret = core_stream_set_pictbuf_config(dd_str_ctx, &buf_info->pictbuf_cfg); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + } + + /* Map heap from VDEC to MMU. */ + switch (buf_type) { + case VDEC_BUFTYPE_BITSTREAM: + ddbuf_map_info->mmuheap_id = MMU_HEAP_BITSTREAM_BUFFERS; + break; + + case VDEC_BUFTYPE_PICTURE: + mmu_get_heap(buf_info->pictbuf_cfg.stride[VDEC_PLANE_VIDEO_Y], + &ddbuf_map_info->mmuheap_id); + break; + + default: + VDEC_ASSERT(FALSE); + } + + /* Map this buffer into the MMU. */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("----- %s:%d calling MMU_StreamMapExt_sg", __func__, __LINE__); +#endif + ret = + mmu_stream_map_ext_sg(dd_str_ctx->mmu_str_handle, + (enum mmu_eheap_id)ddbuf_map_info->mmuheap_id, + sgt, buf_info->buf_size, DEV_MMU_PAGE_SIZE, + buf_info->mem_attrib, buf_info->cpu_linear_addr, + &ddbuf_map_info->ddbuf_info, + &ddbuf_map_info->buf_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + if (buf_type == VDEC_BUFTYPE_PICTURE) + dd_str_ctx->map_buf_info.num_buf++; + + /* + * Initialise the reference count to indicate that the client + * still holds the buffer. + */ + ddbuf_map_info->ddbuf_info.ref_count = 1; + + /* Return success.. */ + return IMG_SUCCESS; + +error: + if (ddbuf_map_info->res_handle) + rman_free_resource(ddbuf_map_info->res_handle); + else + kfree(ddbuf_map_info); + + return ret; +} + +/* + * @Function core_stream_unmap_buf + */ +int core_stream_unmap_buf(unsigned int buf_map_id) +{ + int ret; + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct core_stream_context *core_str_ctx; + + /* Get access to map info context.. */ + ret = rman_get_resource(buf_map_id, VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = ddbuf_map_info->ddstr_context; + VDEC_ASSERT(dd_str_ctx); + + /* Get access to stream context.. */ + ret = rman_get_resource(dd_str_ctx->res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + VDEC_ASSERT(core_str_ctx); +#ifdef DEBUG_DECODER_DRIVER + pr_info("UNMAP: PM [0x%p] --> VM [0x%08X - 0x%08X] (%d bytes)", + ddbuf_map_info->ddbuf_info.cpu_virt, + ddbuf_map_info->ddbuf_info.dev_virt, + ddbuf_map_info->ddbuf_info.dev_virt + + ddbuf_map_info->ddbuf_info.buf_size, + ddbuf_map_info->ddbuf_info.buf_size); +#endif + + /* Buffer should only be held by the client. */ + VDEC_ASSERT(ddbuf_map_info->ddbuf_info.ref_count == 1); + if (ddbuf_map_info->ddbuf_info.ref_count != 1) + return IMG_ERROR_MEMORY_IN_USE; + + ddbuf_map_info->ddbuf_info.ref_count = 0; + if (ddbuf_map_info->buf_type == VDEC_BUFTYPE_PICTURE) { + /* Remove this picture buffer from pictbuf list */ + ret = resource_list_remove(&core_str_ctx->pict_buf_list, ddbuf_map_info); + + VDEC_ASSERT(ret == IMG_SUCCESS || ret == IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE); + if (ret != IMG_SUCCESS && ret != IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE) + return ret; + + ddbuf_map_info->ddstr_context->map_buf_info.num_buf--; + + /* Clear some state if there are no more mapped buffers. */ + if (dd_str_ctx->map_buf_info.num_buf == 0) { + dd_str_ctx->map_buf_info.buf_size = 0; + dd_str_ctx->map_buf_info.byte_interleave = FALSE; + } + } + + /* Unmap this buffer from the MMU. */ + ret = mmu_free_mem(dd_str_ctx->mmu_str_handle, &ddbuf_map_info->ddbuf_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Free buffer map info. */ + rman_free_resource(ddbuf_map_info->res_handle); + + /* Return success.. */ + return IMG_SUCCESS; +} + +/* + * @Function core_stream_unmap_buf_sg + */ +int core_stream_unmap_buf_sg(unsigned int buf_map_id) +{ + int ret; + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct core_stream_context *core_str_ctx; + + /* Get access to map info context.. */ + ret = rman_get_resource(buf_map_id, VDECDD_BUFMAP_TYPE_ID, (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = ddbuf_map_info->ddstr_context; + VDEC_ASSERT(dd_str_ctx); + + /* Get access to stream context.. */ + ret = rman_get_resource(dd_str_ctx->res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + VDEC_ASSERT(core_str_ctx); + +#ifdef DEBUG_DECODER_DRIVER + pr_info("UNMAP: PM [0x%p] --> VM [0x%08X - 0x%08X] (%d bytes)", + ddbuf_map_info->ddbuf_info.cpu_virt, + ddbuf_map_info->ddbuf_info.dev_virt, + ddbuf_map_info->ddbuf_info.dev_virt + + ddbuf_map_info->ddbuf_info.buf_size, + ddbuf_map_info->ddbuf_info.buf_size); +#endif + + /* Buffer should only be held by the client. */ + VDEC_ASSERT(ddbuf_map_info->ddbuf_info.ref_count == 1); + if (ddbuf_map_info->ddbuf_info.ref_count != 1) + return IMG_ERROR_MEMORY_IN_USE; + + ddbuf_map_info->ddbuf_info.ref_count = 0; + + if (ddbuf_map_info->buf_type == VDEC_BUFTYPE_PICTURE) { + /* Remove this picture buffer from pictbuf list */ + ret = resource_list_remove(&core_str_ctx->pict_buf_list, ddbuf_map_info); + + VDEC_ASSERT(ret == IMG_SUCCESS || ret == IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE); + if (ret != IMG_SUCCESS && ret != IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE) + return ret; + + ddbuf_map_info->ddstr_context->map_buf_info.num_buf--; + + /* + * Clear some state if there are no more + * mapped buffers. + */ + if (dd_str_ctx->map_buf_info.num_buf == 0) { + dd_str_ctx->map_buf_info.buf_size = 0; + dd_str_ctx->map_buf_info.byte_interleave = FALSE; + } + } + + /* Unmap this buffer from the MMU. */ + ret = mmu_free_mem_sg(dd_str_ctx->mmu_str_handle, &ddbuf_map_info->ddbuf_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Free buffer map info. */ + rman_free_resource(ddbuf_map_info->res_handle); + + /* Return success.. */ + return IMG_SUCCESS; +} + +/* + * @Function core_stream_flush + */ +int core_stream_flush(unsigned int res_str_id, unsigned char discard_refs) +{ + struct vdecdd_ddstr_ctx *dd_str_ctx; + struct core_stream_context *core_str_ctx; + int ret; + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, + (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_ctx); + VDEC_ASSERT(dd_str_ctx->dd_str_state == VDECDD_STRSTATE_STOPPED); + + /* + * If unsupported sequence is found, we need to do additional + * check for DPB flush condition + */ + if (!dd_str_ctx->comseq_hdr_info.not_dpb_flush) { + ret = decoder_stream_flush(dd_str_ctx->dec_ctx, discard_refs); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + /* Return success.. */ + return IMG_SUCCESS; +} + +/* + * @Function core_stream_release_bufs + */ +int core_stream_release_bufs(unsigned int res_str_id, enum vdec_buf_type buf_type) +{ + int ret; + struct core_stream_context *core_str_ctx; + struct vdecdd_ddstr_ctx *dd_str_ctx; + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_ctx); + VDEC_ASSERT(buf_type < VDEC_BUFTYPE_MAX); + + switch (buf_type) { + case VDEC_BUFTYPE_PICTURE: + { + /* Empty all the decoded picture related buffer lists. */ + ret = resource_list_empty(&core_str_ctx->pict_buf_list, TRUE, NULL, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + break; + } + + case VDEC_BUFTYPE_BITSTREAM: + { + /* Empty the stream unit queue. */ + ret = resource_list_empty(&core_str_ctx->str_unit_list, FALSE, + (resource_pfn_freeitem)core_fn_free_stream_unit, + core_str_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + break; + } + + case VDEC_BUFTYPE_ALL: + { + /* Empty all the decoded picture related buffer lists. */ + ret = resource_list_empty(&core_str_ctx->pict_buf_list, TRUE, NULL, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + + /* Empty the stream unit queue. */ + ret = resource_list_empty(&core_str_ctx->str_unit_list, FALSE, + (resource_pfn_freeitem)core_fn_free_stream_unit, + core_str_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + break; + } + + default: + { + ret = IMG_ERROR_INVALID_PARAMETERS; + VDEC_ASSERT(FALSE); + break; + } + } + + if (buf_type == VDEC_BUFTYPE_PICTURE || buf_type == VDEC_BUFTYPE_ALL) { + ret = decoder_stream_release_buffers(dd_str_ctx->dec_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + /* Return success.. */ + return IMG_SUCCESS; +} + +/* + * @Function core_stream_get_status + */ +int core_stream_get_status(unsigned int res_str_id, + struct vdecdd_decstr_status *str_st) +{ + int ret; + struct core_stream_context *core_str_ctx; + struct vdecdd_ddstr_ctx *dd_str_ctx; + + /* Get access to stream context.. */ + ret = rman_get_resource(res_str_id, VDECDD_STREAM_TYPE_ID, (void **)&core_str_ctx, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dd_str_ctx = core_str_ctx->dd_str_ctx; + + VDEC_ASSERT(dd_str_ctx); + VDEC_ASSERT(str_st); + + ret = decoder_stream_get_status(dd_str_ctx->dec_ctx, str_st); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Return success.. */ + return IMG_SUCCESS; +} + +#ifdef HAS_HEVC +/* + * @Function core_free_hevc_picture_resource + */ +static int core_free_hevc_picture_resource(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pic_res_int) +{ + int ret = IMG_SUCCESS; + + ret = core_free_resbuf(&pic_res_int->genc_fragment_buf, + core_strctx->dd_str_ctx->mmu_str_handle); + if (ret != IMG_SUCCESS) + pr_err("MMU_Free for Genc Fragment buffer failed with error %u", ret); + + return ret; +} + +/* + * @Function core_free_hevc_sequence_resource + */ +static int core_free_hevc_sequence_resource(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seq_res_int) +{ + unsigned int i; + int local_result = IMG_SUCCESS; + int ret = IMG_SUCCESS; + + for (i = 0; i < GENC_BUFF_COUNT; ++i) { + local_result = core_free_resbuf(&seq_res_int->genc_buffers[i], + core_strctx->dd_str_ctx->mmu_str_handle); + if (local_result != IMG_SUCCESS) { + ret = local_result; + pr_warn("MMU_Free for GENC buffer %u failed with error %u", i, + local_result); + } + } + + local_result = core_free_resbuf(&seq_res_int->intra_buffer, + core_strctx->dd_str_ctx->mmu_str_handle); + if (local_result != IMG_SUCCESS) { + ret = local_result; + pr_warn("MMU_Free for GENC buffer %u failed with error %u", i, local_result); + } + + local_result = core_free_resbuf(&seq_res_int->aux_buffer, + core_strctx->dd_str_ctx->mmu_str_handle); + if (local_result != IMG_SUCCESS) { + ret = local_result; + pr_warn("MMU_Free for GENC buffer %u failed with error %u", i, local_result); + } + + return ret; +} + +/* + * @Function core_hevc_bufs_get_size + */ +static int core_hevc_bufs_get_size(struct core_stream_context *core_strctx, + const struct vdec_comsequ_hdrinfo *seqhdr_info, + struct vdec_pict_size *max_pict_size, + struct core_pict_bufsize_info *size_info, + struct core_seq_resinfo *seqres_info, + unsigned char *resource_needed) +{ + enum vdec_vid_std vid_std = core_strctx->dd_str_ctx->str_config_data.vid_std; + unsigned int std_idx = vid_std - 1; + + static const unsigned short max_slice_segments_list + [HEVC_LEVEL_MAJOR_NUM][HEVC_LEVEL_MINOR_NUM] = { + /* level: 1.0 1.1 1.2 */ + { 16, 0, 0, }, + /* level: 2.0 2.1 2.2 */ + { 16, 20, 0, }, + /* level: 3.0 3.1 3.2 */ + { 30, 40, 0, }, + /* level: 4.0 4.1 4.2 */ + { 75, 75, 0, }, + /* level: 5.0 5.1 5.2 */ + { 200, 200, 200, }, + /* level: 6.0 6.1 6.2 */ + { 600, 600, 600, } + }; + + static const unsigned char max_tile_cols_list + [HEVC_LEVEL_MAJOR_NUM][HEVC_LEVEL_MINOR_NUM] = { + /* level: 1.0 1.1 1.2 */ + { 1, 0, 0, }, + /* level: 2.0 2.1 2.2 */ + { 1, 1, 0, }, + /* level: 3.0 3.1 3.2 */ + { 2, 3, 0, }, + /* level: 4.0 4.1 4.2 */ + { 5, 5, 0, }, + /* level: 5.0 5.1 5.2 */ + { 10, 10, 10, }, + /* level: 6.0 6.1 6.2 */ + { 20, 20, 20, } + }; + + /* TRM 3.11.11 */ + static const unsigned int total_sample_per_mb[PIXEL_FORMAT_444 + 1] = { + 256, 384, 384, 512, 768}; + + static const unsigned int HEVC_LEVEL_IDC_MIN = 30; + static const unsigned int HEVC_LEVEL_IDC_MAX = 186; + static const unsigned int GENC_ALIGNMENT = 0x1000; + static const unsigned int mb_size = 16; + static const unsigned int max_mb_rows_in_ctu = 4; + static const unsigned int bytes_per_fragment_pointer = 16; + + const unsigned int max_tile_height_in_mbs = + seqhdr_info->max_frame_size.height / mb_size; + + signed char level_maj = seqhdr_info->codec_level / 30; + signed char level_min = (seqhdr_info->codec_level % 30) / 3; + + /* + * If we are somehow able to deliver more information here (CTU size, + * number of tile columns/rows) then memory usage could be reduced + */ + const struct pixel_pixinfo *pix_info = &seqhdr_info->pixel_info; + const unsigned int bit_depth = pix_info->bitdepth_y >= pix_info->bitdepth_c ? + pix_info->bitdepth_y : pix_info->bitdepth_c; + unsigned short max_slice_segments; + unsigned char max_tile_cols; + unsigned int raw_byte_per_mb; + unsigned int *genc_fragment_bufsize; + unsigned int *genc_buf_size; + + /* Reset the MB parameters buffer size. */ + size_info->mbparams_bufsize = 0; + *resource_needed = TRUE; + + if (mbparam_allocinfo[std_idx].alloc_mbparam_bufs) { + /* shall be == 64 (0x40)*/ + const unsigned int align = mbparam_allocinfo[std_idx].mbparam_size; + const unsigned int dpb_width = (max_pict_size->width + align * 2 - 1) / align * 2; + const unsigned int pic_height = (max_pict_size->height + align - 1) / align; + const unsigned int pic_width = (max_pict_size->width + align - 1) / align; + + /* calculating for worst case: max frame size, B-frame */ + size_info->mbparams_bufsize = (align * 2) * pic_width * pic_height + + align * dpb_width * pic_height; + + /* Adjust the buffer size for MSVDX. */ + vdecddutils_buf_vxd_adjust_size(&size_info->mbparams_bufsize); + } + + if (seqhdr_info->codec_level > HEVC_LEVEL_IDC_MAX || + seqhdr_info->codec_level < HEVC_LEVEL_IDC_MIN) { + level_maj = 6; + level_min = 2; + } + + if (level_maj > 0 && level_maj <= HEVC_LEVEL_MAJOR_NUM && + level_min >= 0 && level_min < HEVC_LEVEL_MINOR_NUM) { + max_slice_segments = max_slice_segments_list[level_maj - 1][level_min]; + max_tile_cols = max_tile_cols_list[level_maj - 1][level_min]; + } else { + pr_err("%s: Invalid parameters\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + raw_byte_per_mb = total_sample_per_mb[pix_info->chroma_fmt_idc] * + VDEC_ALIGN_SIZE(bit_depth, 8, unsigned int, int) / 8; + + genc_fragment_bufsize = &size_info->hevc_bufsize_pict.genc_fragment_bufsize; + genc_buf_size = &seqres_info->hevc_bufsize_seqres.genc_bufsize; + + *genc_fragment_bufsize = bytes_per_fragment_pointer * (seqhdr_info->max_frame_size.height / + mb_size * max_tile_cols + max_slice_segments - 1) * max_mb_rows_in_ctu; + + /* + * GencBufferSize formula is taken from TRM and found by HW * CSIM teams for a sensible + * streams i.e. size_of_stream < size_of_output_YUV. In videostream data base it's + * possible to find pathological Argon streams that do not fulfill this sensible + * requirement. eg. #58417, #58419, #58421, #58423. To make a #58417 stream running the + * formula below should be changed from (2 * 384) *... ---> (3 * 384) *... + * This solution is applied by DEVA. + */ + *genc_buf_size = 2 * raw_byte_per_mb * seqhdr_info->max_frame_size.width / + mb_size * max_tile_height_in_mbs / 4; + + *genc_buf_size = VDEC_ALIGN_SIZE(*genc_buf_size, GENC_ALIGNMENT, + unsigned int, unsigned int); + *genc_fragment_bufsize = VDEC_ALIGN_SIZE(*genc_fragment_bufsize, GENC_ALIGNMENT, + unsigned int, unsigned int); + +#ifdef DEBUG_DECODER_DRIVER + pr_info("Sizes for GENC in HEVC: 0x%X (frag), 0x%X (x4)", + *genc_fragment_bufsize, + *genc_buf_size); +#endif + + seqres_info->hevc_bufsize_seqres.intra_bufsize = 4 * seqhdr_info->max_frame_size.width; + if (seqhdr_info->pixel_info.mem_pkg != PIXEL_BIT8_MP) + seqres_info->hevc_bufsize_seqres.intra_bufsize *= 2; + + seqres_info->hevc_bufsize_seqres.aux_bufsize = (512 * 1024); + + return IMG_SUCCESS; +} + +/* + * @Function core_is_hevc_stream_resource_suitable + */ +static unsigned char +core_is_hevc_stream_resource_suitable(struct core_pict_resinfo *pict_res_info, + struct core_pict_resinfo *old_pict_res_info, + struct core_seq_resinfo *seq_res_info, + struct core_seq_resinfo *old_seq_res_info) +{ + return (seq_res_info->hevc_bufsize_seqres.genc_bufsize <= + old_seq_res_info->hevc_bufsize_seqres.genc_bufsize && + seq_res_info->hevc_bufsize_seqres.intra_bufsize <= + old_seq_res_info->hevc_bufsize_seqres.intra_bufsize && + seq_res_info->hevc_bufsize_seqres.aux_bufsize <= + old_seq_res_info->hevc_bufsize_seqres.aux_bufsize && + pict_res_info->size_info.hevc_bufsize_pict.genc_fragment_bufsize <= + old_pict_res_info->size_info.hevc_bufsize_pict.genc_fragment_bufsize); +} + +/* + * @Function core_alloc_hevc_specific_seq_buffers + */ +static int +core_alloc_hevc_specific_seq_buffers(struct core_stream_context *core_strctx, + struct vdecdd_seq_resint *seqres_int, + struct vxdio_mempool mempool, + struct core_seq_resinfo *seqres_info) +{ + unsigned int i; + int ret = IMG_SUCCESS; + + /* Allocate GENC buffers */ + for (i = 0; i < GENC_BUFF_COUNT; ++i) { + /* Allocate the GENC buffer info structure. */ + ret = core_alloc_resbuf(&seqres_int->genc_buffers[i], + seqres_info->hevc_bufsize_seqres.genc_bufsize, + core_strctx->dd_str_ctx->mmu_str_handle, + mempool); + if (ret != IMG_SUCCESS) + return ret; + } + + seqres_int->genc_buf_id = ++core_strctx->std_spec_context.hevc_ctx.genc_id_gen; + + /* Allocate the intra buffer info structure. */ + ret = core_alloc_resbuf(&seqres_int->intra_buffer, + seqres_info->hevc_bufsize_seqres.intra_bufsize, + core_strctx->dd_str_ctx->mmu_str_handle, + mempool); + if (ret != IMG_SUCCESS) + return ret; + + /* Allocate the aux buffer info structure. */ + ret = core_alloc_resbuf(&seqres_int->aux_buffer, + seqres_info->hevc_bufsize_seqres.aux_bufsize, + core_strctx->dd_str_ctx->mmu_str_handle, + mempool); + if (ret != IMG_SUCCESS) + return ret; + + return IMG_SUCCESS; +} + +/* + * @Function core_alloc_hevc_specific_pict_buffers + */ +static int +core_alloc_hevc_specific_pict_buffers(struct core_stream_context *core_strctx, + struct vdecdd_pict_resint *pict_res_int, + struct vxdio_mempool mempool, + struct core_pict_resinfo *pict_res_info) +{ + int ret; + + /* Allocate the GENC fragment buffer. */ + ret = core_alloc_resbuf(&pict_res_int->genc_fragment_buf, + pict_res_info->size_info.hevc_bufsize_pict.genc_fragment_bufsize, + core_strctx->dd_str_ctx->mmu_str_handle, + mempool); + + return ret; +} +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/core.h b/drivers/media/platform/vxe-vxd/decoder/core.h --- a/drivers/media/platform/vxe-vxd/decoder/core.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/core.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Decoder CORE and V4L2 Node Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef __CORE_H__ +#define __CORE_H__ + +#include +#include "decoder.h" + +int core_initialise(void *dev_handle, unsigned int internal_heap_id, + void *cb); + +/** + * core_deinitialise - deinitialise core + */ +int core_deinitialise(void); + +int core_supported_features(struct vdec_features *features); + +int core_stream_create(void *vxd_dec_ctx_arg, + const struct vdec_str_configdata *str_cfgdata, + unsigned int *res_str_id); + +int core_stream_destroy(unsigned int res_str_id); + +int core_stream_play(unsigned int res_str_id); + +int core_stream_stop(unsigned int res_str_id); + +int core_stream_map_buf(unsigned int res_str_id, enum vdec_buf_type buf_type, + struct vdec_buf_info *buf_info, unsigned int *buf_map_id); + +int core_stream_map_buf_sg(unsigned int res_str_id, + enum vdec_buf_type buf_type, + struct vdec_buf_info *buf_info, + void *sgt, unsigned int *buf_map_id); + +int core_stream_unmap_buf(unsigned int buf_map_id); + +int core_stream_unmap_buf_sg(unsigned int buf_map_id); + +int core_stream_submit_unit(unsigned int res_str_id, + struct vdecdd_str_unit *str_unit); + +int core_stream_fill_pictbuf(unsigned int buf_map_id); + +/* This function to be called before stream play */ +int core_stream_set_output_config(unsigned int res_str_id, + struct vdec_str_opconfig *str_opcfg, + struct vdec_pict_bufconfig *pict_bufcg); + +int core_stream_flush(unsigned int res_str_id, unsigned char discard_refs); + +int core_stream_release_bufs(unsigned int res_str_id, + enum vdec_buf_type buf_type); + +int core_stream_get_status(unsigned int res_str_id, + struct vdecdd_decstr_status *str_status); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/decoder.c b/drivers/media/platform/vxe-vxd/decoder/decoder.c --- a/drivers/media/platform/vxe-vxd/decoder/decoder.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/decoder.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,4622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD Decoder Component function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include "decoder.h" +#include "dec_resources.h" +#include "dq.h" +#include "hw_control.h" +#include "h264fw_data.h" +#include "idgen_api.h" +#include "img_errors.h" +#ifdef HAS_JPEG +#include "jpegfw_data.h" +#endif +#include "lst.h" +#include "pool_api.h" +#include "resource.h" +#include "translation_api.h" +#include "vdecdd_utils.h" +#include "vdec_mmu_wrapper.h" +#include "vxd_dec.h" + +#define CORE_NUM_DECODE_SLOTS 2 + +#define MAX_PLATFORM_SUPPORTED_HEIGHT 65536 +#define MAX_PLATFORM_SUPPORTED_WIDTH 65536 + +#define MAX_CONCURRENT_STREAMS 16 + +/* Maximum number of unique picture ids within stream. */ +#define DECODER_MAX_PICT_ID GET_STREAM_PICTURE_ID(((1ULL << 32) - 1ULL)) + +/* Maximum number of concurrent pictures within stream. */ +#define DECODER_MAX_CONCURRENT_PICT 0x100 + +static inline unsigned int get_next_picture_id(unsigned int cur_pict_id) +{ + return(cur_pict_id == FWIF_BIT_MASK(FWIF_NUMBITS_STREAM_PICTURE_ID) ? + 1 : cur_pict_id + 1); +} + +static inline unsigned int get_prev_picture_id(unsigned int cur_pict_id) +{ + return(cur_pict_id == 1 ? + FWIF_BIT_MASK(FWIF_NUMBITS_STREAM_PICTURE_ID) : + cur_pict_id - 1); +} + +#define H264_SGM_BUFFER_BYTES_PER_MB 1 +#define H264_SGM_MAX_MBS 3600 + +#define CONTEXT_BUFF_SIZE (72) + +/* + * Number of bits in transaction ID used to represent + * picture number in stream. + */ +#define FWIF_NUMBITS_STREAM_PICTURE_ID 16 +/* + * Number of bits in transaction ID used to represent + * picture number in core. + */ +#define FWIF_NUMBITS_CORE_PICTURE_ID 4 +/* + * Number of bits in transaction ID used to represent + * stream id. + */ +#define FWIF_NUMBITS_STREAM_ID 8 +/* Number of bits in transaction ID used to represent core id. */ +#define FWIF_NUMBITS_CORE_ID 4 + +/* Offset in transaction ID to picture number in stream. */ +#define FWIF_OFFSET_STREAM_PICTURE_ID 0 +/* Offset in transaction ID to picture number in core. */ +#define FWIF_OFFSET_CORE_PICTURE_ID (FWIF_OFFSET_STREAM_PICTURE_ID + \ + FWIF_NUMBITS_STREAM_PICTURE_ID) +/* Offset in transaction ID to stream id. */ +#define FWIF_OFFSET_STREAM_ID (FWIF_OFFSET_CORE_PICTURE_ID + \ + FWIF_NUMBITS_CORE_PICTURE_ID) +/* Offset in transaction ID to core id. */ +#define FWIF_OFFSET_CORE_ID (FWIF_OFFSET_STREAM_ID + \ + FWIF_NUMBITS_STREAM_ID) + +/* Maximum number of unique picture ids within stream. */ +#define DECODER_MAX_PICT_ID GET_STREAM_PICTURE_ID(((1ULL << 32) - 1ULL)) + +/* Maximum number of concurrent pictures within stream. */ +#define DECODER_MAX_CONCURRENT_PICT 0x100 + +#define CREATE_TRANSACTION_ID(core_id, stream_id, core_pic, stream_pic) \ + (SET_CORE_ID((core_id)) | SET_STREAM_ID((stream_id)) | \ + SET_CORE_PICTURE_ID((core_pic)) | SET_STREAM_PICTURE_ID((stream_pic))) + +static inline struct dec_core_ctx *decoder_str_ctx_to_core_ctx(struct dec_str_ctx *decstrctx) +{ + if (decstrctx && decstrctx->decctx) + return decstrctx->decctx->dec_core_ctx; + + else + return NULL; +} + +static const struct vdecdd_dd_devconfig def_dev_cfg = { + CORE_NUM_DECODE_SLOTS, /* ui32NumSlotsPerPipe; */ +}; + +/* + * This array defines names of the VDEC standards. + * Shall be in sync with #VDEC_eVidStd + * @brief Names of the VDEC standards + */ +static unsigned char *vid_std_names[] = { + "VDEC_STD_UNDEFINED", + "VDEC_STD_MPEG2", + "VDEC_STD_MPEG4", + "VDEC_STD_H263", + "VDEC_STD_H264", + "VDEC_STD_VC1", + "VDEC_STD_AVS", + "VDEC_STD_REAL", + "VDEC_STD_JPEG", + "VDEC_STD_VP6", + "VDEC_STD_VP8", + "VDEC_STD_SORENSON", + "VDEC_STD_HEVC" +}; + +#ifdef ERROR_RECOVERY_SIMULATION +extern int fw_error_value; +#endif + +/* + * @Function decoder_set_device_config + */ +static int decoder_set_device_config(const struct vdecdd_dd_devconfig **dd_dev_config) +{ + struct vdecdd_dd_devconfig *local_dev_cfg; + + VDEC_ASSERT(dd_dev_config); + + /* Allocate device configuration structure */ + local_dev_cfg = kzalloc(sizeof(*local_dev_cfg), GFP_KERNEL); + VDEC_ASSERT(local_dev_cfg); + if (!local_dev_cfg) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Set the default device configuration */ + *local_dev_cfg = def_dev_cfg; + + *dd_dev_config = local_dev_cfg; + + return IMG_SUCCESS; +} + +/* + * @Function decoder_set_feature_flags + * @Description + * This function sets the features flags from the core properties. + * @Input p : Pointer to core properties. + * @Input core_feat_flags : Pointer to core feature flags word. + */ +static void decoder_set_feature_flags(struct vxd_coreprops *core_props, + unsigned int *core_feat_flags, + unsigned int *pipe_feat_flags) +{ + unsigned char pipe_minus_one; + + VDEC_ASSERT(core_props); + VDEC_ASSERT(core_feat_flags); + VDEC_ASSERT(pipe_feat_flags); + + for (pipe_minus_one = 0; pipe_minus_one < core_props->num_pixel_pipes; + pipe_minus_one++) { + *core_feat_flags |= pipe_feat_flags[pipe_minus_one] |= + core_props->h264[pipe_minus_one] ? + VDECDD_COREFEATURE_H264 : 0; +#ifdef HAS_JPEG + *core_feat_flags |= pipe_feat_flags[pipe_minus_one] |= + core_props->jpeg[pipe_minus_one] ? + VDECDD_COREFEATURE_JPEG : 0; +#endif +#ifdef HAS_HEVC + *core_feat_flags |= pipe_feat_flags[pipe_minus_one] |= + core_props->hevc[pipe_minus_one] ? + VDECDD_COREFEATURE_HEVC : 0; +#endif + } +} + +/* + * @Function decoder_stream_get_context + * @Description + * This function returns the stream context structure for the given + * stream handle. + * @Return struct dec_str_ctx : This function returns a pointer + * to the stream + * context structure or NULL if not found. + */ +static struct dec_str_ctx *decoder_stream_get_context(void *dec_str_context) +{ + return (struct dec_str_ctx *)dec_str_context; +} + +/* + * @Function decoder_core_enumerate + * @Description + * This function enumerates a decoder core and returns its handle. + * Usage: before calls to other DECODE_Core or DECODE_Stream functions. + * @Input dec_context : Pointer to Decoder context. + * @Input dev_cfg : Device configuration. + * @Return This function returns either IMG_SUCCESS or an + * error code. + */ +static int decoder_core_enumerate(struct dec_ctx *dec_context, + const struct vdecdd_dd_devconfig *dev_cfg, + unsigned int *num_pipes) +{ + struct dec_core_ctx *dec_core_ctx_local; + unsigned int ret; + unsigned int ptd_align = DEV_MMU_PAGE_ALIGNMENT; + + /* Create the core. */ + dec_core_ctx_local = kzalloc(sizeof(*dec_core_ctx_local), GFP_KERNEL); + if (!dec_core_ctx_local) + return IMG_ERROR_OUT_OF_MEMORY; + + dec_core_ctx_local->dec_ctx = (struct dec_ctx *)dec_context; + + /* Initialise the hwctrl block here */ + ret = hwctrl_initialise(dec_core_ctx_local, dec_context->user_data, + dev_cfg, &dec_core_ctx_local->core_props, + &dec_core_ctx_local->hw_ctx); + if (ret != IMG_SUCCESS) + goto error; + + decoder_set_feature_flags(&dec_core_ctx_local->core_props, + &dec_core_ctx_local->core_features, + dec_core_ctx_local->pipe_features); + + /* Perform device setup for master core. */ + if (num_pipes) + *num_pipes = dec_core_ctx_local->core_props.num_pixel_pipes; + + /* DEVA PVDEC FW requires PTD to be 64k aligned. */ + ptd_align = 0x10000; + + /* Create a device MMU context. */ + ret = mmu_device_create(dec_core_ctx_local->core_props.mmu_type, + ptd_align, &dec_context->mmu_dev_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + dec_core_ctx_local->enumerated = TRUE; + + dec_context->dec_core_ctx = dec_core_ctx_local; + + return IMG_SUCCESS; + +error: + if (dec_core_ctx_local) { + unsigned int deinit_result = IMG_SUCCESS; + + /* Destroy a device MMU context. */ + if (dec_context->mmu_dev_handle) { + deinit_result = + mmu_device_destroy(dec_context->mmu_dev_handle); + VDEC_ASSERT(deinit_result == IMG_SUCCESS); + if (deinit_result != IMG_SUCCESS) + pr_warn("MMU_DeviceDestroy() failed to tidy-up after error"); + } + + kfree(dec_core_ctx_local); + dec_core_ctx_local = NULL; + } + + return ret; +} + +/* + * @Function decoder_initialise + */ +int decoder_initialise(void *user_init_data, unsigned int int_heap_id, + struct vdecdd_dd_devconfig *dd_device_config, + unsigned int *num_pipes, + void **dec_ctx_handle) +{ + struct dec_ctx *dec_context = (struct dec_ctx *)*dec_ctx_handle; + int ret; + + if (!dec_context) { + dec_context = kzalloc(sizeof(*dec_context), GFP_KERNEL); + VDEC_ASSERT(dec_context); + if (!dec_context) + return IMG_ERROR_OUT_OF_MEMORY; + + *dec_ctx_handle = dec_context; + } + + /* Determine which standards are supported. */ + memset(&dec_context->sup_stds, 0x0, sizeof(dec_context->sup_stds[VDEC_STD_MAX])); + + dec_context->sup_stds[VDEC_STD_H264] = TRUE; +#ifdef HAS_HEVC + dec_context->sup_stds[VDEC_STD_HEVC] = TRUE; +#endif + if (!dec_context->inited) { + /* Check and store input parameters. */ + dec_context->user_data = user_init_data; + dec_context->dev_handle = + ((struct vdecdd_dddev_context *)user_init_data)->dev_handle; + + /* Initialise the context lists. */ + lst_init(&dec_context->str_list); + + /* Make sure POOL API is initialised */ + ret = pool_init(); + if (ret != IMG_SUCCESS) + goto pool_init_error; + + ret = decoder_set_device_config(&dec_context->dev_cfg); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + dec_context->internal_heap_id = int_heap_id; + + /* Enumerate the master core. */ + ret = decoder_core_enumerate(dec_context, dec_context->dev_cfg, + &dec_context->num_pipes); + if (ret != IMG_SUCCESS) + goto error; + + if (dd_device_config) + *dd_device_config = *dec_context->dev_cfg; + + if (num_pipes) + *num_pipes = dec_context->num_pipes; + + dec_context->inited = TRUE; + } + + return IMG_SUCCESS; + +error: + pool_deinit(); + +pool_init_error: + if (dec_context->dev_cfg) { + kfree((void *)dec_context->dev_cfg); + dec_context->dev_cfg = NULL; + } + + kfree(*dec_ctx_handle); + *dec_ctx_handle = NULL; + + return ret; +} + +/* + * @Function decoder_supported_features + */ +int decoder_supported_features(void *dec_ctx, struct vdec_features *features) +{ + struct dec_ctx *dec_context = (struct dec_ctx *)dec_ctx; + struct dec_core_ctx *dec_core_ctx_local; + + /* Check input parameters. */ + VDEC_ASSERT(dec_context); + VDEC_ASSERT(features); + if (!dec_context || !features) { + pr_err("Invalid parameters!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Ensure that Decoder component is initialised. */ + VDEC_ASSERT(dec_context->inited); + + /* Loop over all cores checking for support. */ + dec_core_ctx_local = dec_context->dec_core_ctx; + VDEC_ASSERT(dec_core_ctx_local); + + /* + * Determine whether the required core attribute + * is present to support requested feature + */ + features->h264 |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_H264) ? TRUE : FALSE; + features->mpeg2 |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_MPEG2) ? TRUE : FALSE; + features->mpeg4 |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_MPEG4) ? TRUE : FALSE; + features->vc1 |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_VC1) ? TRUE : FALSE; + features->avs |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_AVS) ? TRUE : FALSE; + features->real |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_REAL) ? TRUE : FALSE; + features->jpeg |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_JPEG) ? TRUE : FALSE; + features->vp6 |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_VP6) ? TRUE : FALSE; + features->vp8 |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_VP8) ? TRUE : FALSE; + features->hd |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_HD_DECODE) ? TRUE : FALSE; + features->rotation |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_ROTATION) ? TRUE : FALSE; + features->scaling |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_SCALING) ? TRUE : FALSE; + features->hevc |= (dec_core_ctx_local->core_features & + VDECDD_COREFEATURE_HEVC) ? TRUE : FALSE; + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_get_status + */ +int decoder_stream_get_status(void *dec_str_ctx_handle, + struct vdecdd_decstr_status *dec_str_st) +{ + struct dec_str_ctx *dec_str_ctx; + struct dec_decoded_pict *decoded_pict; + struct dec_core_ctx *dec_core_ctx; + unsigned int item; + + VDEC_ASSERT(dec_str_st); + if (!dec_str_st) { + pr_err("Invalid decoder streams status pointer!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + dec_str_ctx = decoder_stream_get_context(dec_str_ctx_handle); + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx) { + pr_err("Invalid decoder stream context handle!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Obtain the state of each core. */ + dec_core_ctx = decoder_str_ctx_to_core_ctx(dec_str_ctx); + VDEC_ASSERT(dec_core_ctx); + + /* + * Obtain the display and release list of first unprocessed + * picture in decoded list + */ + dec_str_ctx->dec_str_st.display_pics = 0; + dec_str_ctx->dec_str_st.release_pics = 0; + decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + while (decoded_pict) { + /* if this is the first unprocessed picture */ + if (!decoded_pict->processed) { + unsigned int idx; + struct vdecfw_buffer_control *buf_ctrl; + + VDEC_ASSERT(decoded_pict->pict_ref_res); + buf_ctrl = + (struct vdecfw_buffer_control *)decoded_pict->pict_ref_res->fw_ctrlbuf.cpu_virt; + VDEC_ASSERT(buf_ctrl); + + /* Get display pictures */ + idx = decoded_pict->disp_idx; + item = dec_str_ctx->dec_str_st.display_pics; + while (idx < buf_ctrl->display_list_length && + item < VDECFW_MAX_NUM_PICTURES) { + dec_str_ctx->dec_str_st.next_display_items[item] = + buf_ctrl->display_list[idx]; + dec_str_ctx->dec_str_st.next_display_item_parent[item] = + decoded_pict->transaction_id; + idx++; + item++; + } + dec_str_ctx->dec_str_st.display_pics = item; + + /* Get release pictures */ + idx = decoded_pict->rel_idx; + item = dec_str_ctx->dec_str_st.release_pics; + while (idx < buf_ctrl->release_list_length && + item < VDECFW_MAX_NUM_PICTURES) { + dec_str_ctx->dec_str_st.next_release_items[item] = + buf_ctrl->release_list[idx]; + dec_str_ctx->dec_str_st.next_release_item_parent[item] = + decoded_pict->transaction_id; + idx++; + item++; + } + dec_str_ctx->dec_str_st.release_pics = item; + break; + } + + if (decoded_pict != dq_last(&dec_str_ctx->str_decd_pict_list)) + decoded_pict = dq_next(decoded_pict); + else + decoded_pict = NULL; + } + + /* Get list of held decoded pictures */ + item = 0; + decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + while (decoded_pict) { + dec_str_ctx->dec_str_st.decoded_picts[item] = + decoded_pict->transaction_id; + item++; + + if (decoded_pict != dq_last(&dec_str_ctx->str_decd_pict_list)) + decoded_pict = dq_next(decoded_pict); + else + decoded_pict = NULL; + } + + VDEC_ASSERT(item == dec_str_ctx->dec_str_st.num_pict_decoded); + *dec_str_st = dec_str_ctx->dec_str_st; + + return IMG_SUCCESS; +} + +/* + * @Function decoder_deinitialise + */ +int decoder_deinitialise(void *dec_ctx) +{ + struct dec_ctx *dec_context = (struct dec_ctx *)dec_ctx; + int ret; + /* Remove and free all core context structures */ + struct dec_decpict *dec_pict; + + if (dec_context && dec_context->inited) { + struct dec_core_ctx *dec_core_ctx_local = + dec_context->dec_core_ctx; + + if (!dec_core_ctx_local) { + pr_warn("%s %d NULL Decoder context passed", __func__, __LINE__); + VDEC_ASSERT(0); + return -EFAULT; + } + + /* Stream list should be empty. */ + if (!lst_empty(&dec_context->str_list)) + pr_warn("%s %d stream list should be empty", __func__, __LINE__); + + /* + * All cores should now be idle since there are no + * connections/streams. + */ + ret = hwctrl_peekheadpiclist(dec_core_ctx_local->hw_ctx, &dec_pict); + VDEC_ASSERT(ret != IMG_SUCCESS); + + /* Destroy a device MMU context. */ + ret = mmu_device_destroy(dec_context->mmu_dev_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Remove and free core context structure */ + dec_core_ctx_local = dec_context->dec_core_ctx; + VDEC_ASSERT(dec_core_ctx_local); + + hwctrl_deinitialise(dec_core_ctx_local->hw_ctx); + + kfree(dec_core_ctx_local); + dec_core_ctx_local = NULL; + + VDEC_ASSERT(dec_context->dev_cfg); + if (dec_context->dev_cfg) + kfree((void *)dec_context->dev_cfg); + + dec_context->user_data = NULL; + + pool_deinit(); + + dec_context->inited = FALSE; + + kfree(dec_context); + } else { + pr_err("Decoder has not been initialised so cannot be de-initialised"); + return IMG_ERROR_NOT_INITIALISED; + } + + pr_debug("Decoder deinitialise successfully\n"); + return IMG_SUCCESS; +} + +/* + * @Function decoder_picture_destroy + * @Description + * Free the picture container and optionally release image buffer back to + * client. + * Default is to decrement the reference count held by this picture. + */ +static int decoder_picture_destroy(struct dec_str_ctx *dec_str_ctx, + unsigned int pict_id, + unsigned char release_image) +{ + struct vdecdd_picture *picture; + int ret; + + VDEC_ASSERT(dec_str_ctx); + + ret = idgen_gethandle(dec_str_ctx->pict_idgen, pict_id, (void **)&picture); + if (ret == IMG_SUCCESS) { + VDEC_ASSERT(picture); + ret = idgen_freeid(dec_str_ctx->pict_idgen, pict_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + if (picture->dec_pict_info) { + /* Destroy the picture */ + kfree(picture->dec_pict_info); + picture->dec_pict_info = NULL; + } + + /* Return unused picture and internal resources */ + if (picture->disp_pict_buf.pict_buf) { + if (release_image) + resource_item_release + (&picture->disp_pict_buf.pict_buf->ddbuf_info.ref_count); + else + resource_item_return + (&picture->disp_pict_buf.pict_buf->ddbuf_info.ref_count); + + memset(&picture->disp_pict_buf, 0, sizeof(picture->disp_pict_buf)); + } + + if (picture->pict_res_int) { + resource_item_return(&picture->pict_res_int->ref_cnt); + picture->pict_res_int = NULL; + } + + kfree(picture); + picture = NULL; + } else { + VDEC_ASSERT(ret == IMG_SUCCESS); + return ret; + } + + return IMG_SUCCESS; +} + +/* + * @Function decoder_decoded_picture_destroy + */ +static int +decoder_decoded_picture_destroy(struct dec_str_ctx *dec_str_ctx, + struct dec_decoded_pict *decoded_pict, + unsigned char release_image) +{ + int ret; + + VDEC_ASSERT(dec_str_ctx); + VDEC_ASSERT(decoded_pict); + + if (decoded_pict->pict) { + VDEC_ASSERT(decoded_pict->pict->pict_id == + GET_STREAM_PICTURE_ID(decoded_pict->transaction_id)); + + ret = decoder_picture_destroy(dec_str_ctx, decoded_pict->pict->pict_id, + release_image); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + decoded_pict->pict = NULL; + } + + dq_remove(decoded_pict); + dec_str_ctx->dec_str_st.num_pict_decoded--; + + resource_item_return(&decoded_pict->pict_ref_res->ref_cnt); + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] COMPLETE", + GET_STREAM_ID(decoded_pict->transaction_id), + decoded_pict->transaction_id); +#endif + + kfree(decoded_pict->first_fld_fwmsg); + decoded_pict->first_fld_fwmsg = NULL; + + kfree(decoded_pict->second_fld_fwmsg); + decoded_pict->second_fld_fwmsg = NULL; + + kfree(decoded_pict); + decoded_pict = NULL; + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_decode_resource_destroy + */ +static int decoder_stream_decode_resource_destroy(void *item, void *free_cb_param) +{ + struct dec_pictdec_res *pict_dec_res = item; + struct dec_str_ctx *dec_str_ctx_local = + (struct dec_str_ctx *)free_cb_param; + int ret; + + VDEC_ASSERT(pict_dec_res); + VDEC_ASSERT(resource_item_isavailable(&pict_dec_res->ref_cnt)); + + /* Free memory (device-based) to store fw contexts for stream. */ + ret = mmu_free_mem(dec_str_ctx_local->mmu_str_handle, &pict_dec_res->fw_ctx_buf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + if (pict_dec_res->h264_sgm_buf.hndl_memory) { + /* Free memory (device-based) to store SGM. */ + ret = mmu_free_mem(dec_str_ctx_local->mmu_str_handle, &pict_dec_res->h264_sgm_buf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + kfree(pict_dec_res); + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_release_buffers + */ +int decoder_stream_release_buffers(void *dec_str_ctx_handle) +{ + struct dec_str_ctx *dec_str_ctx; + struct dec_decoded_pict *decoded_pict; + int ret; + + dec_str_ctx = decoder_stream_get_context(dec_str_ctx_handle); + + /* Decoding queue should be empty since we are stopped */ + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx) { + pr_err("Invalid decoder stream context handle!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + VDEC_ASSERT(lst_empty(&dec_str_ctx->pend_strunit_list)); + + /* Destroy all pictures in the decoded list */ + decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + while (decoded_pict) { + ret = decoder_decoded_picture_destroy(dec_str_ctx, decoded_pict, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + } + + /* if and only if the output buffers were used for reference. */ + if (dec_str_ctx->last_be_pict_dec_res) { + /* + * Clear the firmware context so that reference pictures + * are no longer referred to. + */ + memset(dec_str_ctx->last_be_pict_dec_res->fw_ctx_buf.cpu_virt, 0, + dec_str_ctx->last_be_pict_dec_res->fw_ctx_buf.buf_size); + } + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_reference_resource_destroy + */ +static int decoder_stream_reference_resource_destroy(void *item, void *free_cb_param) +{ + struct dec_pictref_res *pict_ref_res = item; + struct dec_str_ctx *dec_str_ctx_local = + (struct dec_str_ctx *)free_cb_param; + int ret; + + VDEC_ASSERT(pict_ref_res); + VDEC_ASSERT(resource_item_isavailable(&pict_ref_res->ref_cnt)); + + /* Free memory (device-based) to store fw contexts for stream */ + ret = mmu_free_mem(dec_str_ctx_local->mmu_str_handle, &pict_ref_res->fw_ctrlbuf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + kfree(pict_ref_res); + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_destroy + */ +int decoder_stream_destroy(void *dec_str_context, unsigned char abort) +{ + struct dec_str_ctx *dec_str_ctx_local; + struct dec_str_unit *dec_str_unit_local; + struct dec_decoded_pict *decoded_pict_local; + unsigned int i; + int ret; + unsigned int pict_id; + void **res_handle_local; + + /* Required for getting segments from decode picture to free */ + struct dec_decpict_seg *dec_pict_seg_local; + struct dec_ctx *dec_context; + struct dec_core_ctx *dec_core_ctx_local; + + /* Get the Decoder stream context. */ + dec_str_ctx_local = decoder_stream_get_context(dec_str_context); + + VDEC_ASSERT(dec_str_ctx_local); + if (!dec_str_ctx_local) { + pr_err("Invalid decoder stream context handle!"); + return FALSE; + } + VDEC_ASSERT(dec_str_ctx_local->decctx); + + /* Decrement the stream count */ + dec_str_ctx_local->decctx->str_cnt--; + + /* + * Ensure that there are no pictures for this stream outstanding + * on any decoder cores. + * This should not be removed, it is important to see it if + * it ever happens. + * In practice we see it many times with Application Timeout. + */ + if (!abort) + VDEC_ASSERT(lst_empty(&dec_str_ctx_local->pend_strunit_list)); + + /* + * At this point all resources for the stream are guaranteed to + * not be used and no further hardware interrupts will be received. + */ + + /* Destroy all stream units submitted for processing. */ + dec_str_unit_local = + lst_removehead(&dec_str_ctx_local->pend_strunit_list); + while (dec_str_unit_local) { + /* If the unit was submitted for decoding (picture)... */ + if (dec_str_unit_local->dec_pict) { + /* + * Explicitly remove picture from core decode queue + * and destroy. + */ + struct dec_core_ctx *dec_core_ctx_local = + decoder_str_ctx_to_core_ctx(dec_str_ctx_local); + VDEC_ASSERT(dec_core_ctx_local); + + res_handle_local = &dec_str_ctx_local->resources; + + if (!dec_core_ctx_local) { + VDEC_ASSERT(0); + return -EINVAL; + } + + hwctrl_removefrom_piclist(dec_core_ctx_local->hw_ctx, + dec_str_unit_local->dec_pict); + + /* Free decoder picture */ + kfree(dec_str_unit_local->dec_pict->first_fld_fwmsg); + dec_str_unit_local->dec_pict->first_fld_fwmsg = NULL; + + kfree(dec_str_unit_local->dec_pict->second_fld_fwmsg); + dec_str_unit_local->dec_pict->second_fld_fwmsg = NULL; + + dec_res_picture_detach(res_handle_local, dec_str_unit_local->dec_pict); + + /* Free all the segments of the picture */ + dec_pict_seg_local = + lst_removehead(&dec_str_unit_local->dec_pict->dec_pict_seg_list); + while (dec_pict_seg_local) { + if (dec_pict_seg_local->internal_seg) { + VDEC_ASSERT(dec_pict_seg_local->bstr_seg); + kfree(dec_pict_seg_local->bstr_seg); + dec_pict_seg_local->bstr_seg = NULL; + } + + kfree(dec_pict_seg_local); + dec_pict_seg_local = NULL; + + dec_pict_seg_local = + lst_removehead + (&dec_str_unit_local->dec_pict->dec_pict_seg_list); + } + + VDEC_ASSERT(dec_str_unit_local->dec_pict->dec_str_ctx == dec_str_ctx_local); + + dec_str_ctx_local->dec_str_st.num_pict_decoding--; + pict_id = + GET_STREAM_PICTURE_ID(dec_str_unit_local->dec_pict->transaction_id); + + ret = decoder_picture_destroy(dec_str_ctx_local, pict_id, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + kfree(dec_str_unit_local->dec_pict); + dec_str_unit_local->dec_pict = NULL; + } + + /* Free the picture container */ + kfree(dec_str_unit_local); + dec_str_unit_local = NULL; + + dec_str_unit_local = lst_removehead(&dec_str_ctx_local->pend_strunit_list); + } + + /* Destroy all pictures in the decoded list */ + decoded_pict_local = dq_first(&dec_str_ctx_local->str_decd_pict_list); + while (decoded_pict_local) { + ret = decoder_decoded_picture_destroy(dec_str_ctx_local, + decoded_pict_local, + TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + decoded_pict_local = dq_first(&dec_str_ctx_local->str_decd_pict_list); + } + + /* Ensure all picture queues are empty */ + VDEC_ASSERT(lst_empty(&dec_str_ctx_local->pend_strunit_list)); + VDEC_ASSERT(dq_empty(&dec_str_ctx_local->str_decd_pict_list)); + + /* Free memory to store stream context buffer. */ + ret = mmu_free_mem(dec_str_ctx_local->mmu_str_handle, + &dec_str_ctx_local->pvdec_fw_ctx_buf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Release any fw contexts held by stream. */ + if (dec_str_ctx_local->prev_fe_pict_dec_res) + resource_item_return(&dec_str_ctx_local->prev_fe_pict_dec_res->ref_cnt); + + if (dec_str_ctx_local->cur_fe_pict_dec_res) + resource_item_return(&dec_str_ctx_local->cur_fe_pict_dec_res->ref_cnt); + + if (dec_str_ctx_local->last_be_pict_dec_res) + resource_item_return(&dec_str_ctx_local->last_be_pict_dec_res->ref_cnt); + + /* + * Remove the device resources used for decoding and the two + * added to hold the last on front and back-end for stream. + */ + for (i = 0; i < dec_str_ctx_local->num_dec_res + 2; i++) { + ret = resource_list_empty(&dec_str_ctx_local->dec_res_lst, FALSE, + decoder_stream_decode_resource_destroy, + dec_str_ctx_local); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + VDEC_ASSERT(lst_empty(&dec_str_ctx_local->dec_res_lst)); + + /* Remove all stream decode resources. */ + ret = resource_list_empty(&dec_str_ctx_local->ref_res_lst, FALSE, + decoder_stream_reference_resource_destroy, + dec_str_ctx_local); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + VDEC_ASSERT(lst_empty(&dec_str_ctx_local->ref_res_lst)); + + idgen_destroycontext(dec_str_ctx_local->pict_idgen); + + dec_context = dec_str_ctx_local->decctx; + dec_core_ctx_local = decoder_str_ctx_to_core_ctx(dec_str_ctx_local); + + VDEC_ASSERT(dec_context); + VDEC_ASSERT(dec_core_ctx_local); + + res_handle_local = &dec_str_ctx_local->resources; + + if (*res_handle_local) { + ret = dec_res_destroy(dec_str_ctx_local->mmu_str_handle, *res_handle_local); + if (ret != IMG_SUCCESS) + pr_warn("resourceS_Destroy() failed to tidy-up after error"); + + *res_handle_local = NULL; + } + + lst_remove(&dec_str_ctx_local->decctx->str_list, dec_str_ctx_local); + + kfree(dec_str_ctx_local); + dec_str_ctx_local = NULL; + + pr_debug("%s successfully", __func__); + return IMG_SUCCESS; +} + +/* + * @Function decoder_init_avail_slots + */ +static int decoder_init_avail_slots(struct dec_str_ctx *dec_str_context) +{ + VDEC_ASSERT(dec_str_context); + + switch (dec_str_context->config.vid_std) { + case VDEC_STD_H264: + /* + * only first pipe can be master when decoding H264 in + * multipipe mode (FW restriction) + */ + dec_str_context->avail_slots = + dec_str_context->decctx->dev_cfg->num_slots_per_pipe * + dec_str_context->decctx->num_pipes; + break; + + default: + /* all pipes by default */ + dec_str_context->avail_slots = + dec_str_context->decctx->dev_cfg->num_slots_per_pipe; + break; + } + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_decode_resource_create + */ +static int decoder_stream_decode_resource_create(struct dec_str_ctx *dec_str_context) +{ + struct dec_pictdec_res *pict_dec_res; + int ret; + unsigned int mem_heap_id; + enum sys_emem_attrib mem_attribs; + + unsigned char fw_ctx_buf = FALSE; + + /* Validate input arguments */ + if (!dec_str_context || !dec_str_context->decctx || + !dec_str_context->decctx->dev_cfg) { + VDEC_ASSERT(0); + return -EINVAL; + } + + mem_heap_id = dec_str_context->decctx->internal_heap_id; + mem_attribs = (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE); + mem_attribs |= (enum sys_emem_attrib)SYS_MEMATTRIB_INTERNAL; + + /* Allocate the firmware context buffer info structure. */ + pict_dec_res = kzalloc(sizeof(*pict_dec_res), GFP_KERNEL); + VDEC_ASSERT(pict_dec_res); + if (!pict_dec_res) + return IMG_ERROR_OUT_OF_MEMORY; + + /* + * Allocate the firmware context buffer to contain + * data required for subsequent picture. + */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + + ret = mmu_stream_alloc(dec_str_context->mmu_str_handle, + MMU_HEAP_STREAM_BUFFERS, mem_heap_id, + (enum sys_emem_attrib)(mem_attribs | SYS_MEMATTRIB_CPU_READ | + SYS_MEMATTRIB_CPU_WRITE), + sizeof(union dec_fw_contexts), + DEV_MMU_PAGE_ALIGNMENT, + &pict_dec_res->fw_ctx_buf); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + + fw_ctx_buf = TRUE; + + /* + * Clear the context data in preparation for first time + * use by the firmware. + */ + memset(pict_dec_res->fw_ctx_buf.cpu_virt, 0, pict_dec_res->fw_ctx_buf.buf_size); + + switch (dec_str_context->config.vid_std) { + case VDEC_STD_H264: + /* Allocate the SGM buffer */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc + (dec_str_context->mmu_str_handle, + MMU_HEAP_STREAM_BUFFERS, mem_heap_id, + (enum sys_emem_attrib)(mem_attribs | SYS_MEMATTRIB_CPU_WRITE), + H264_SGM_BUFFER_BYTES_PER_MB * + H264_SGM_MAX_MBS, + DEV_MMU_PAGE_ALIGNMENT, + &pict_dec_res->h264_sgm_buf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + + /* Clear the SGM data. */ + memset(pict_dec_res->h264_sgm_buf.cpu_virt, 0, pict_dec_res->h264_sgm_buf.buf_size); + break; + + default: + break; + } + + pict_dec_res->ref_cnt = 1; + + ret = resource_list_add_img(&dec_str_context->dec_res_lst, pict_dec_res, 0, + &pict_dec_res->ref_cnt); + + if (ret != IMG_SUCCESS) { + pr_warn("[USERSID=0x%08X] Failed to add resource", + dec_str_context->config.user_str_id); + } + + return IMG_SUCCESS; + +err_out_of_memory: + if (pict_dec_res) { + if (fw_ctx_buf) + mmu_free_mem(dec_str_context->mmu_str_handle, &pict_dec_res->fw_ctx_buf); + + kfree(pict_dec_res); + pict_dec_res = NULL; + } + + pr_err("[USERSID=0x%08X] Failed to allocate device memory for stream decode resources", + dec_str_context->config.user_str_id); + + return IMG_ERROR_OUT_OF_MEMORY; +} + +/* + * @Function decoder_stream_create + */ +int decoder_stream_create(void *dec_ctx_arg, + struct vdec_str_configdata str_cfg, + unsigned int km_str_id, void **mmu_str_handle, + void *vxd_dec_ctx, void *str_user_int_data, + void **dec_str_ctx_arg, void *decoder_cb, + void *query_cb) +{ + struct dec_ctx *dec_context = (struct dec_ctx *)dec_ctx_arg; + struct dec_str_ctx *dec_str_ctx = NULL; + unsigned int i; + int ret; + unsigned int mem_heap_id; + enum sys_emem_attrib mem_attribs; + struct dec_core_ctx *dec_core_ctx_local; + + /* Check input parameters. */ + VDEC_ASSERT(dec_ctx_arg); + if (!dec_ctx_arg) { + pr_err("Invalid parameters!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (dec_context->str_cnt >= MAX_CONCURRENT_STREAMS) { + pr_err("Device has too many concurrent streams. Number of Concurrent streams allowed: %d.", + MAX_CONCURRENT_STREAMS); + return IMG_ERROR_DEVICE_UNAVAILABLE; + } + + mem_heap_id = dec_context->internal_heap_id; + mem_attribs = (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE); + mem_attribs |= (enum sys_emem_attrib)SYS_MEMATTRIB_INTERNAL; + + /* Allocate Decoder Stream Context */ + dec_str_ctx = kzalloc(sizeof(*dec_str_ctx), GFP_KERNEL); + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Increment the stream counter */ + dec_context->str_cnt++; + + /* + * Initialise the context structure to NULL. Any non-zero + * default values should be set at this point. + */ + dec_str_ctx->config = str_cfg; + dec_str_ctx->vxd_dec_ctx = vxd_dec_ctx; + dec_str_ctx->usr_int_data = str_user_int_data; + dec_str_ctx->decctx = dec_context; + + decoder_init_avail_slots(dec_str_ctx); + + dec_str_ctx->next_dec_pict_id = 1; + dec_str_ctx->next_pict_id_expected = 1; + + dec_str_ctx->km_str_id = km_str_id; + VDEC_ASSERT(dec_str_ctx->km_str_id > 0); + + lst_init(&dec_str_ctx->pend_strunit_list); + dq_init(&dec_str_ctx->str_decd_pict_list); + lst_init(&dec_str_ctx->ref_res_lst); + lst_init(&dec_str_ctx->dec_res_lst); + + ret = idgen_createcontext(DECODER_MAX_PICT_ID + 1, + DECODER_MAX_CONCURRENT_PICT, + TRUE, + &dec_str_ctx->pict_idgen); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Create an MMU context for this stream. */ + ret = mmu_stream_create(dec_context->mmu_dev_handle, + dec_str_ctx->km_str_id, + dec_str_ctx->vxd_dec_ctx, + &dec_str_ctx->mmu_str_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + dec_core_ctx_local = dec_context->dec_core_ctx; + + VDEC_ASSERT(dec_core_ctx_local); + + /* Create core resources */ + ret = dec_res_create(dec_str_ctx->mmu_str_handle, + &dec_core_ctx_local->core_props, + dec_context->dev_cfg->num_slots_per_pipe * + dec_context->num_pipes, + dec_context->internal_heap_id, + &dec_str_ctx->resources); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Allocate the PVDEC firmware context buffer */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(dec_str_ctx->mmu_str_handle, MMU_HEAP_STREAM_BUFFERS, + mem_heap_id, + (enum sys_emem_attrib)(mem_attribs | SYS_MEMATTRIB_CPU_WRITE), + CONTEXT_BUFF_SIZE, + DEV_MMU_PAGE_ALIGNMENT, + &dec_str_ctx->pvdec_fw_ctx_buf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* + * Clear the context data in preparation for + * first time use by the firmware. + */ + memset(dec_str_ctx->pvdec_fw_ctx_buf.cpu_virt, 0, dec_str_ctx->pvdec_fw_ctx_buf.buf_size); + + /* + * Create enough device resources to hold last context on + * front and back-end for stream. + */ + dec_str_ctx->num_dec_res = + dec_str_ctx->decctx->dev_cfg->num_slots_per_pipe * + dec_str_ctx->decctx->num_pipes; + for (i = 0; i < dec_str_ctx->num_dec_res + 2; i++) { + ret = decoder_stream_decode_resource_create(dec_str_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + } + + dec_str_ctx->str_processed_cb = (strunit_processed_cb)decoder_cb; + + dec_str_ctx->core_query_cb = (core_gen_cb)query_cb; + + lst_add(&dec_context->str_list, dec_str_ctx); + + *dec_str_ctx_arg = (void *)dec_str_ctx; + *mmu_str_handle = dec_str_ctx->mmu_str_handle; + + return IMG_SUCCESS; + + /* Roll back in case of errors. */ +error: + decoder_stream_destroy((void *)dec_str_ctx, FALSE); + + return ret; +} + +/* + * @Function decoder_get_decoded_pict + */ +static struct dec_decoded_pict *decoder_get_decoded_pict(unsigned int transaction_id, + struct dq_linkage_t *dq_list) +{ + struct dec_decoded_pict *decoded_pict; + void *item = NULL; + + VDEC_ASSERT(dq_list); + + decoded_pict = dq_first(dq_list); + while (decoded_pict) { + if (decoded_pict->transaction_id == transaction_id) { + item = decoded_pict; + break; + } + + if (decoded_pict != dq_last(dq_list)) + decoded_pict = dq_next(decoded_pict); + else + decoded_pict = NULL; + } + + return item; +} + +/* + * @Function decoder_get_decoded_pict_of_stream + */ +static struct dec_decoded_pict *decoder_get_decoded_pict_of_stream(unsigned int pict_id, + struct dq_linkage_t *dq_list) +{ + struct dec_decoded_pict *decoded_pict; + void *item = NULL; + + VDEC_ASSERT(dq_list); + + decoded_pict = dq_first(dq_list); + while (decoded_pict) { + if (GET_STREAM_PICTURE_ID(decoded_pict->transaction_id) == pict_id) { + item = decoded_pict; + break; + } + + if (decoded_pict != dq_last(dq_list)) + decoded_pict = dq_next(decoded_pict); + else + decoded_pict = NULL; + } + return item; +} + +/* + * @Function decoder_get_next_decpict_contiguous + */ +static struct +dec_decoded_pict *decoder_get_next_decpict_contiguous(struct dec_decoded_pict *decoded_pict, + unsigned int next_dec_pict_id, + struct dq_linkage_t *str_decoded_pict_list) +{ + struct dec_decoded_pict *next_dec_pict = NULL; + struct dec_decoded_pict *result_dec_pict = NULL; + + VDEC_ASSERT(str_decoded_pict_list); + if (!str_decoded_pict_list) { + pr_err("Invalid parameter"); + return NULL; + } + + if (decoded_pict) { + if (decoded_pict != dq_last(str_decoded_pict_list)) { + next_dec_pict = dq_next(decoded_pict); + if (!next_dec_pict) { + VDEC_ASSERT(0); + return NULL; + } + + if (next_dec_pict->pict) { + /* + * If we have no holes in the decoded list + * (i.e. next decoded picture is next in + * bitstream decode order). + */ + if (HAS_X_REACHED_Y(next_dec_pict_id, next_dec_pict->pict->pict_id, + 1 << FWIF_NUMBITS_STREAM_PICTURE_ID, + unsigned int)) { + result_dec_pict = next_dec_pict; + } + } + } + } + + return result_dec_pict; +} + +/* + * @Function decoder_next_picture + * @Description + * Returns the next unprocessed picture or NULL if the next picture is not + * next in bitstream decode order or there are no more decoded pictures in the + * list. + + * @Input psCurrentDecodedPicture : Pointer to current decoded picture. + + * @Input ui32NextDecPictId : Picture ID of next picture in decode + * order. + + * @Input psStrDecdPictList : Pointer to decoded picture list. + + * @Return DECODER_sDecodedPict * : Pointer to next decoded picture to + * process. + */ +static struct dec_decoded_pict *decoder_next_picture(struct dec_decoded_pict *cur_decoded_pict, + unsigned int next_dec_pict_d, + struct dq_linkage_t *str_decodec_pict_list) +{ + struct dec_decoded_pict *ret = NULL; + + VDEC_ASSERT(str_decodec_pict_list); + if (!str_decodec_pict_list) + return NULL; + + if (!cur_decoded_pict) + cur_decoded_pict = dq_first(str_decodec_pict_list); + + if (cur_decoded_pict && !cur_decoded_pict->process_failed) { + /* Search for picture ID greater than picture in list */ + do { + if (!cur_decoded_pict->processed) { + /* + * Return the current one because it has not + * been processed + */ + ret = cur_decoded_pict; + break; + } + /* + * Obtain a pointer to the next picture in bitstream + * decode order. + */ + cur_decoded_pict = decoder_get_next_decpict_contiguous + (cur_decoded_pict, + next_dec_pict_d, + str_decodec_pict_list); + } while (cur_decoded_pict && + !cur_decoded_pict->process_failed); + } + return ret; +} + +/* + * @Function decoder_picture_display + */ +static int decoder_picture_display(struct dec_str_ctx *dec_str_ctx, + unsigned int pict_id, unsigned char last) +{ + struct vdecdd_picture *picture; + int ret; + static unsigned int display_num; + + VDEC_ASSERT(dec_str_ctx); + + ret = idgen_gethandle(dec_str_ctx->pict_idgen, pict_id, (void **)&picture); + if (ret == IMG_SUCCESS) { + struct vdecdd_ddbuf_mapinfo *pict_buf; + + /* validate pointers */ + if (!picture || !picture->dec_pict_info) { + VDEC_ASSERT(0); + return -EIO; + } + + pict_buf = picture->disp_pict_buf.pict_buf; + VDEC_ASSERT(pict_buf); + + /* + * Indicate whether there are more pictures + * coming for display. + */ + picture->dec_pict_info->last_in_seq = last; + + /* Set decode order id */ + picture->dec_pict_info->decode_id = pict_id; + + /* Return the picture to the client for display */ + dec_str_ctx->dec_str_st.total_pict_displayed++; + resource_item_use(&pict_buf->ddbuf_info.ref_count); + display_num++; +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] DISPLAY(%d): PIC_ID[%d]", + dec_str_ctx->config.user_str_id, display_num, pict_id); +#endif + + VDEC_ASSERT(dec_str_ctx->decctx); + ret = dec_str_ctx->str_processed_cb(dec_str_ctx->usr_int_data, + VXD_CB_PICT_DISPLAY, + picture); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* + * All handles will be freed after actually + * displaying the picture. + * Reset them to NULL here to avoid any confusion. + */ + memset(&picture->dec_pict_sup_data, 0, sizeof(picture->dec_pict_sup_data)); + } else { + pr_warn("[USERSID=0x%08X] ERROR: DISPLAY PICTURE HAS AN EXPIRED ID", + dec_str_ctx->config.user_str_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + return IMG_SUCCESS; +} + +#ifdef ERROR_CONCEALMENT +/* + * @Function decoder_get_pict_processing_info + */ +static unsigned char decoder_get_pict_processing_info(struct dec_core_ctx *dec_corectx, + struct dec_str_ctx *dec_strctx, + struct bspp_pict_hdr_info *pict_hdr_info, + struct dec_decoded_pict *decoded_pict, + struct dec_decpict *dec_pict, + unsigned int *pict_last_mb) +{ + int ret = IMG_SUCCESS; + unsigned char pipe_minus1; + struct hwctrl_state last_state; + unsigned int width_in_mb; + unsigned int height_in_mb; + unsigned int i; + + memset(&last_state, 0, sizeof(last_state)); + + VDEC_ASSERT(pict_hdr_info); + width_in_mb = (pict_hdr_info->coded_frame_size.width + + (VDEC_MB_DIMENSION - 1)) / VDEC_MB_DIMENSION; + height_in_mb = (pict_hdr_info->coded_frame_size.height + + (VDEC_MB_DIMENSION - 1)) / VDEC_MB_DIMENSION; + + VDEC_ASSERT(pict_last_mb); + *pict_last_mb = width_in_mb * height_in_mb; + VDEC_ASSERT(decoded_pict); + + if (decoded_pict->first_fld_fwmsg->pict_attrs.pict_attrs.dwrfired || + decoded_pict->second_fld_fwmsg->pict_attrs.pict_attrs.dwrfired || + decoded_pict->first_fld_fwmsg->pict_attrs.pict_attrs.mmufault || + decoded_pict->second_fld_fwmsg->pict_attrs.pict_attrs.mmufault) { + struct dec_pict_attrs *pict_attrs = &decoded_pict->first_fld_fwmsg->pict_attrs; + unsigned char be_found = FALSE; + unsigned int mbs_dropped = 0; + unsigned int mbs_recovered = 0; + unsigned int no_be_wdt = 0; + unsigned int max_y = 0; + unsigned int row_drop = 0; + + VDEC_ASSERT(dec_corectx); + /* Obtain the last available core status - + * cached before clocks where switched off + */ + ret = hwctrl_getcore_cached_status(dec_corectx->hw_ctx, &last_state); + if (ret != IMG_SUCCESS) + return FALSE; + + /* Try to determine pipe where the last picture was decoded on (BE) */ + for (pipe_minus1 = 0; pipe_minus1 < VDEC_MAX_PIXEL_PIPES; pipe_minus1++) { + for (i = VDECFW_CHECKPOINT_BE_END; i >= VDECFW_CHECKPOINT_BE_START; i--) { + struct vxd_pipestate *pipe_state = + &last_state.core_state.fw_state.pipe_state[pipe_minus1]; + + if (!pipe_state->is_pipe_present) + continue; + + if (pipe_state->acheck_point[i] == decoded_pict->transaction_id) { + row_drop += width_in_mb - pipe_state->be_mb.x; + if (pipe_state->be_mb.y > max_y) + max_y = pipe_state->be_mb.y; + + if (pipe_state->be_mbs_dropped > mbs_dropped) + mbs_dropped = pipe_state->be_mbs_dropped; + + if (pipe_state->be_mbs_recovered > mbs_recovered) + mbs_recovered = pipe_state->be_mbs_recovered; + + no_be_wdt += pipe_state->be_errored_slices; + be_found = TRUE; + } + } + if (be_found) + /* No need to check FE as we already have an info from BE */ + continue; + + /* If not found, we probbaly stuck on FE ? */ + for (i = VDECFW_CHECKPOINT_FE_END; i >= VDECFW_CHECKPOINT_FE_START; i--) { + struct vxd_pipestate *pipe_state = + &last_state.core_state.fw_state.pipe_state[pipe_minus1]; + + if (!pipe_state->is_pipe_present) + continue; + + if (pipe_state->acheck_point[i] == decoded_pict->transaction_id) { + /* Mark all MBs as dropped */ + pict_attrs->mbs_dropped = *pict_last_mb; + pict_attrs->mbs_recovered = 0; + return TRUE; + } + } + } + + if (be_found) { + /* Calculate last macroblock number processed on BE */ + unsigned int num_mb_processed = (max_y * width_in_mb) - row_drop; + + /* Sanity check, as HW may signal MbYX position + * beyond picture for corrupted streams + */ + if (num_mb_processed > (*pict_last_mb)) + num_mb_processed = (*pict_last_mb); /* trim */ + + if (((*pict_last_mb) - num_mb_processed) > mbs_dropped) + mbs_dropped = (*pict_last_mb) - num_mb_processed; + + pict_attrs->mbs_dropped = mbs_dropped; + pict_attrs->mbs_recovered = num_mb_processed; + pict_attrs->no_be_wdt = no_be_wdt; + return TRUE; + } + return FALSE; + } + /* Picture was decoded without DWR, so we have already the required info */ + return TRUE; +} +#endif + +/* + * @Function decoder_picture_release + */ +static int decoder_picture_release(struct dec_str_ctx *dec_str_ctx, + unsigned int pict_id, + unsigned char displayed, + unsigned char merged) +{ + struct vdecdd_picture *picture; + int ret; + + /* validate input arguments */ + if (!dec_str_ctx) { + VDEC_ASSERT(0); + return -EINVAL; + } + + ret = idgen_gethandle(dec_str_ctx->pict_idgen, pict_id, (void **)&picture); + if (ret == IMG_SUCCESS) { + if (!picture || !picture->dec_pict_info) { + VDEC_ASSERT(0); + return -EINVAL; + } + + /* Set decode order id */ + picture->dec_pict_info->decode_id = pict_id; + + VDEC_ASSERT(dec_str_ctx->decctx); + + pr_debug("Decoder picture released pict_id = %d\n", pict_id); + ret = dec_str_ctx->str_processed_cb(dec_str_ctx->usr_int_data, + VXD_CB_PICT_RELEASE, + picture); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* + * All handles will be freed after actually displaying + * the picture. Reset them to NULL here to avoid any + * confusion. + */ + memset(&picture->dec_pict_sup_data, 0, sizeof(picture->dec_pict_sup_data)); + } else { + pr_err("[USERSID=0x%08X] ERROR: RELEASE PICTURE HAS AN EXPIRED ID", + dec_str_ctx->config.user_str_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_flush_process_dpb_h264 + */ +static int +decoder_stream_flush_process_dpb_h264(struct dec_str_ctx *dec_str_ctx, + struct dec_decoded_pict *decoded_pict, + unsigned char discard_refs) +{ + int ret; + + struct h264fw_context_data *ctx_data = + (struct h264fw_context_data *)dec_str_ctx->last_be_pict_dec_res->fw_ctx_buf.cpu_virt; + unsigned char found = TRUE; + unsigned int i; + int min_cnt; + int min_cnt_idx; + unsigned int num_display_pics = 0; + unsigned int num_pics_displayed = 0; + struct dec_decoded_pict *display_pict = NULL; + unsigned int last_disp_pict_tid; + unsigned int pict_id; + + /* Determine how many display pictures reside in the DPB */ + if (ctx_data->dpb_size > H264FW_MAX_DPB_SIZE || ctx_data->dpb_size <= 0) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] Incorrect DPB size: %d", + dec_str_ctx->config.user_str_id, ctx_data->dpb_size); +#endif + ctx_data->dpb_size = H264FW_MAX_DPB_SIZE; + } + for (i = 0; i < ctx_data->dpb_size; i++) { + if (ctx_data->dpb[i].transaction_id) + if (ctx_data->dpb[i].needed_for_output) + num_display_pics++; + } + + last_disp_pict_tid = ctx_data->last_displayed_pic_data[0].transaction_id; + /* Check for picture stuck outside the dpb */ + if (last_disp_pict_tid) { + VDEC_ASSERT(last_disp_pict_tid != 0xffffffff); + + display_pict = decoder_get_decoded_pict(last_disp_pict_tid, + &dec_str_ctx->str_decd_pict_list); + + if (display_pict && display_pict->pict && + display_pict->pict->dec_pict_info) { + if (FLAG_IS_SET(ctx_data->prev_display_flags, + VDECFW_BUFFLAG_DISPLAY_FIELD_CODED)) { + if (!FLAG_IS_SET(ctx_data->prev_display_flags, + VDECFW_BUFFLAG_DISPLAY_SINGLE_FIELD)) + display_pict->pict->dec_pict_info->buf_type = + IMG_BUFFERTYPE_PAIR; + else + display_pict->pict->dec_pict_info->buf_type = + FLAG_IS_SET + (ctx_data->prev_display_flags, + VDECFW_BUFFLAG_DISPLAY_BOTTOM_FIELD) ? + IMG_BUFFERTYPE_FIELD_BOTTOM : + IMG_BUFFERTYPE_FIELD_TOP; + } else { + display_pict->pict->dec_pict_info->buf_type = + IMG_BUFFERTYPE_FRAME; + } + } else { + VDEC_ASSERT(display_pict); + VDEC_ASSERT(display_pict && display_pict->pict); + VDEC_ASSERT(display_pict && display_pict->pict && + display_pict->pict->dec_pict_info); + } + + if (display_pict && !display_pict->displayed) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] DISPLAY", + dec_str_ctx->config.user_str_id, + last_disp_pict_tid); +#endif + display_pict->displayed = TRUE; + ret = decoder_picture_display + (dec_str_ctx, GET_STREAM_PICTURE_ID(last_disp_pict_tid), + TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + } + + while (found) { + min_cnt = ((unsigned int)(1 << 31)) - 1; + min_cnt_idx = -1; + found = FALSE; + + /* Loop over the DPB to find the first in order */ + for (i = 0; i < ctx_data->dpb_size; i++) { + if (ctx_data->dpb[i].transaction_id && + (ctx_data->dpb[i].needed_for_output || + discard_refs)) { + if (ctx_data->dpb[i].top_field_order_count < + min_cnt) { + min_cnt = + ctx_data->dpb[i].top_field_order_count; + min_cnt_idx = i; + found = TRUE; + } + } + } + + if (found) { + unsigned int umin_cnt_tid = ctx_data->dpb[min_cnt_idx].transaction_id; + + if (ctx_data->dpb[min_cnt_idx].needed_for_output) { + VDEC_ASSERT(umin_cnt_tid != 0xffffffff); + display_pict = + decoder_get_decoded_pict(umin_cnt_tid, + &dec_str_ctx->str_decd_pict_list); + + if ((display_pict && display_pict->pict && + display_pict->pict->dec_pict_info) && + FLAG_IS_SET(ctx_data->dpb[min_cnt_idx].display_flags, + VDECFW_BUFFLAG_DISPLAY_FIELD_CODED)) { + if (!FLAG_IS_SET(ctx_data->dpb[min_cnt_idx].display_flags, + VDECFW_BUFFLAG_DISPLAY_SINGLE_FIELD)) + display_pict->pict->dec_pict_info->buf_type = + IMG_BUFFERTYPE_PAIR; + else + display_pict->pict->dec_pict_info->buf_type = + FLAG_IS_SET + (ctx_data->dpb + [min_cnt_idx].display_flags, + VDECFW_BUFFLAG_DISPLAY_BOTTOM_FIELD) + ? + IMG_BUFFERTYPE_FIELD_BOTTOM : + IMG_BUFFERTYPE_FIELD_TOP; + display_pict->pict->dec_pict_info->view_id = + ctx_data->dpb[min_cnt_idx].view_id; + } else if ((display_pict && display_pict->pict && + display_pict->pict->dec_pict_info) && + (!FLAG_IS_SET(ctx_data->dpb[min_cnt_idx].display_flags, + VDECFW_BUFFLAG_DISPLAY_FIELD_CODED))){ + display_pict->pict->dec_pict_info->buf_type = + IMG_BUFFERTYPE_FRAME; + display_pict->pict->dec_pict_info->view_id = + ctx_data->dpb[min_cnt_idx].view_id; + } else { + VDEC_ASSERT(display_pict); + VDEC_ASSERT(display_pict && display_pict->pict); + VDEC_ASSERT(display_pict && display_pict->pict && + display_pict->pict->dec_pict_info); + } + + if (display_pict && !display_pict->displayed) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] DISPLAY", + dec_str_ctx->config.user_str_id, + umin_cnt_tid); +#endif + display_pict->displayed = TRUE; + num_pics_displayed++; + ret = decoder_picture_display + (dec_str_ctx, + GET_STREAM_PICTURE_ID(umin_cnt_tid), + num_pics_displayed == num_display_pics ? + TRUE : FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + ctx_data->dpb[min_cnt_idx].needed_for_output = FALSE; + } + + if (discard_refs) { + decoded_pict = + decoder_get_decoded_pict(umin_cnt_tid, + &dec_str_ctx->str_decd_pict_list); + if (decoded_pict) { + /* Signal releasing this picture to upper layers. */ + pict_id = + GET_STREAM_PICTURE_ID(decoded_pict->transaction_id); + decoder_picture_release(dec_str_ctx, + pict_id, + decoded_pict->displayed, + decoded_pict->merged); + /* Destroy the decoded picture. */ + ret = decoder_decoded_picture_destroy(dec_str_ctx, + decoded_pict, FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + ctx_data->dpb[min_cnt_idx].transaction_id = 0; + } + } + } + + VDEC_ASSERT(num_pics_displayed == num_display_pics); + + return IMG_SUCCESS; +} + +#ifdef HAS_HEVC +/* + * decoder_StreamFlushProcessDPB_HEVC + */ +static int decoder_stream_flush_process_dpb_hevc(struct dec_str_ctx *decstr_ctx, + struct dec_decoded_pict *decoded_pict, + unsigned char discard_refs) +{ + int result; + struct hevcfw_ctx_data *ctx = + (struct hevcfw_ctx_data *)decstr_ctx->last_be_pict_dec_res->fw_ctx_buf.cpu_virt; + struct hevcfw_decoded_picture_buffer *dpb; + unsigned char found = TRUE; + unsigned char idx; + int min_poc_val; + signed char dpb_idx; + unsigned char num_display_pics = 0; + unsigned char num_pics_displayed = 0; + struct dec_decoded_pict *display_pict = NULL; + + /* + * Update the fw context for analysing the dpb in order + * to display or release any outstanding picture + */ + dpb = &ctx->dpb; + + /* Determine how many display pictures reside in the DPB. */ + for (idx = 0; idx < HEVCFW_MAX_DPB_SIZE; ++idx) { + struct hevcfw_picture_in_dpb *dpb_pic = &dpb->pictures[idx]; + + if (dpb_pic->valid && dpb_pic->needed_for_output) + ++num_display_pics; + } + + while (found) { + struct hevcfw_picture_in_dpb *dpb_pic; + + min_poc_val = 0x7fffffff; + dpb_idx = HEVCFW_DPB_IDX_INVALID; + found = FALSE; + + /* Loop over the DPB to find the first in order */ + for (idx = 0; idx < HEVCFW_MAX_DPB_SIZE; ++idx) { + dpb_pic = &dpb->pictures[idx]; + if (dpb_pic->valid && (dpb_pic->needed_for_output || discard_refs)) { + if (dpb_pic->picture.pic_order_cnt_val < min_poc_val) { + min_poc_val = dpb_pic->picture.pic_order_cnt_val; + dpb_idx = idx; + found = TRUE; + } + } + } + + if (!found) + break; + + dpb_pic = &dpb->pictures[dpb_idx]; + + if (dpb_pic->needed_for_output) { + unsigned int str_pic_id = GET_STREAM_PICTURE_ID + (dpb_pic->picture.transaction_id); + + VDEC_ASSERT(dpb_pic->picture.transaction_id != 0xffffffff); + display_pict = decoder_get_decoded_pict(dpb_pic->picture.transaction_id, + &decstr_ctx->str_decd_pict_list); + + if (display_pict && display_pict->pict && + display_pict->pict->dec_pict_info) { + display_pict->pict->dec_pict_info->buf_type = IMG_BUFFERTYPE_FRAME; + } else { + VDEC_ASSERT(display_pict); + VDEC_ASSERT(display_pict && display_pict->pict); + VDEC_ASSERT(display_pict && display_pict->pict && + display_pict->pict->dec_pict_info); + + dpb_pic->valid = FALSE; + continue; + } + + if (!display_pict->displayed) { + display_pict->displayed = TRUE; + ++num_pics_displayed; + result = decoder_picture_display(decstr_ctx, str_pic_id, + num_pics_displayed == + num_display_pics); + VDEC_ASSERT(result == IMG_SUCCESS); + if (result != IMG_SUCCESS) + return result; + } + dpb_pic->needed_for_output = FALSE; + } + + if (discard_refs) { + decoded_pict = decoder_get_decoded_pict(dpb_pic->picture.transaction_id, + &decstr_ctx->str_decd_pict_list); + + if (decoded_pict) { + /* Signal releasing this picture to upper layers. */ + decoder_picture_release(decstr_ctx, + GET_STREAM_PICTURE_ID + (decoded_pict->transaction_id), + decoded_pict->displayed, + decoded_pict->merged); + /* Destroy the decoded picture. */ + result = decoder_decoded_picture_destroy(decstr_ctx, decoded_pict, + FALSE); + VDEC_ASSERT(result == IMG_SUCCESS); + if (result != IMG_SUCCESS) + return result; + } + dpb_pic->valid = FALSE; + } + } + + VDEC_ASSERT(num_pics_displayed == num_display_pics); + + return IMG_SUCCESS; +} +#endif + +/* + * @Function decoder_stream_flush_process_dpb + * @Description + * Process DPB fetched from firmware, display and release relevant pictures. + */ +static int decoder_stream_flush_process_dpb(struct dec_str_ctx *dec_str_ctx, + struct dec_decoded_pict *decoded_pict, + unsigned char discard_refs) +{ + int ret = 0; + /* Get oldest reference to display. */ + decoded_pict = dq_last(&dec_str_ctx->str_decd_pict_list); + if (decoded_pict) { + switch (dec_str_ctx->config.vid_std) { + case VDEC_STD_H264: + ret = decoder_stream_flush_process_dpb_h264(dec_str_ctx, decoded_pict, + discard_refs); + + break; +#ifdef HAS_HEVC + case VDEC_STD_HEVC: + decoder_stream_flush_process_dpb_hevc(dec_str_ctx, + decoded_pict, discard_refs); +#endif + break; + + default: + break; + } + } + + return ret; +} + +int decoder_stream_flush(void *dec_str_ctx_arg, unsigned char discard_refs) +{ + struct dec_str_ctx *dec_str_ctx; + struct dec_str_unit *dec_str_unit; + struct dec_decoded_pict *decoded_pict; + int ret = 0; + + dec_str_ctx = decoder_stream_get_context(dec_str_ctx_arg); + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx) { + pr_err("Invalid decoder stream context handle!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* + * Since the stream should be stopped before flushing + * there should be no pictures in the stream list. + */ + dec_str_unit = lst_first(&dec_str_ctx->pend_strunit_list); + while (dec_str_unit) { + VDEC_ASSERT(dec_str_unit->str_unit->str_unit_type != + VDECDD_STRUNIT_PICTURE_START); + dec_str_unit = lst_next(dec_str_unit); + } + + decoded_pict = dq_last(&dec_str_ctx->str_decd_pict_list); + + ret = decoder_stream_flush_process_dpb(dec_str_ctx, decoded_pict, + discard_refs); + if (ret != IMG_SUCCESS) + return ret; + + if (discard_refs) { + while (!dq_empty(&dec_str_ctx->str_decd_pict_list)) { + struct dec_decoded_pict *non_decoded_pict = + dq_first(&dec_str_ctx->str_decd_pict_list); + + if (!non_decoded_pict) { + VDEC_ASSERT(0); + return -EINVAL; + } + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] Decoded picture list contains item ID:0x%08x when DPB is empty", + dec_str_ctx->config.user_str_id, + non_decoded_pict->transaction_id); +#endif + + /* release the buffers back to vxd_decoder */ + decoder_picture_release(dec_str_ctx, GET_STREAM_PICTURE_ID + (non_decoded_pict->transaction_id), FALSE, + FALSE); + + ret = decoder_decoded_picture_destroy(dec_str_ctx, non_decoded_pict, FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + VDEC_ASSERT(dq_empty(&dec_str_ctx->str_decd_pict_list)); + + if (dec_str_ctx->last_be_pict_dec_res) + /* + * Clear the firmware context so that reference + * pictures are no longer referred to. + */ + memset(dec_str_ctx->last_be_pict_dec_res->fw_ctx_buf.cpu_virt, 0, + dec_str_ctx->last_be_pict_dec_res->fw_ctx_buf.buf_size); + } + + pr_debug("Decoder stream flushed successfully\n"); + return IMG_SUCCESS; +} + +/* + * @Function decoder_stream_prepare_ctx + */ +int decoder_stream_prepare_ctx(void *dec_str_ctx_arg, unsigned char flush_dpb) +{ + struct dec_str_ctx *dec_str_ctx = + decoder_stream_get_context(dec_str_ctx_arg); + int ret; + + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] Preparing stream context after seek", + dec_str_ctx->config.user_str_id, + dec_str_ctx->last_fe_transaction_id); +#endif + + if (flush_dpb) { + ret = decoder_stream_flush(dec_str_ctx, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + /* Reset front-end temporary pointers */ + if (dec_str_ctx->prev_fe_pict_dec_res) { + resource_item_return(&dec_str_ctx->prev_fe_pict_dec_res->ref_cnt); + dec_str_ctx->prev_fe_pict_dec_res = NULL; + } + if (dec_str_ctx->cur_fe_pict_dec_res) { + resource_item_return(&dec_str_ctx->cur_fe_pict_dec_res->ref_cnt); + dec_str_ctx->cur_fe_pict_dec_res = NULL; + } + + return IMG_SUCCESS; +} + +/* + * @Function decoder_get_load + */ +int decoder_get_load(void *dec_str_ctx_arg, unsigned int *avail_slots) +{ + struct dec_str_ctx *dec_str_ctx = + decoder_stream_get_context(dec_str_ctx_arg); + struct dec_core_ctx *dec_core_ctx_local = NULL; + + /* Check input parameters. */ + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx || !avail_slots) { + pr_err("Invalid parameters!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + dec_core_ctx_local = decoder_str_ctx_to_core_ctx(dec_str_ctx); + if (!dec_core_ctx_local) { + VDEC_ASSERT(0); + return -EIO; + } + + if (dec_core_ctx_local->busy) + *avail_slots = 0; + else + *avail_slots = dec_str_ctx->avail_slots; + + return IMG_SUCCESS; +} + +static int decoder_check_ref_errors(struct dec_str_ctx *dec_str_ctx, + struct vdecfw_buffer_control *buf_ctrl, + struct vdecdd_picture *picture) +{ + struct dec_decoded_pict *ref_pict; + unsigned int i; + + if (!dec_str_ctx) { + VDEC_ASSERT(0); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!buf_ctrl || !picture) { + pr_err("[USERSID=0x%08X] Invalid parameters for checking reference lists.", + dec_str_ctx->config.user_str_id); + VDEC_ASSERT(0); + return IMG_ERROR_INVALID_PARAMETERS; + } + + for (i = 0; i < VDECFW_MAX_NUM_PICTURES && buf_ctrl->ref_list[i]; + i++) { + ref_pict = decoder_get_decoded_pict_of_stream + (GET_STREAM_PICTURE_ID(buf_ctrl->ref_list[i]), + &dec_str_ctx->str_decd_pict_list); + if (ref_pict && ref_pict->pict && ref_pict->pict->dec_pict_info && + ref_pict->pict->dec_pict_info->err_flags) { + picture->dec_pict_info->err_flags |= + VDEC_ERROR_CORRUPTED_REFERENCE; + pr_warn("Picture decoded using corrupted reference: 0x%08X 0x%08X", + ref_pict->transaction_id, + ref_pict->pict->dec_pict_info->err_flags); + } + } + + return IMG_SUCCESS; +} + +static void decoder_clean_bitstr_segments(struct lst_t *decpict_seglist) +{ + struct dec_decpict_seg *dec_pict_seg; + + while (NULL != (dec_pict_seg = lst_removehead(decpict_seglist))) { + if (dec_pict_seg->internal_seg) { + VDEC_ASSERT(dec_pict_seg->bstr_seg); + kfree(dec_pict_seg->bstr_seg); + dec_pict_seg->bstr_seg = NULL; + } + kfree(dec_pict_seg); + } +} + +static int decoder_wrap_bitstr_segments(struct lst_t *bitstr_seglist, + struct lst_t *decpict_seglist, + unsigned int user_str_id) +{ + /* Required for attaching segments to the decode picture */ + struct bspp_bitstr_seg *bit_str_seg; + struct dec_decpict_seg *dec_pict_seg; + + VDEC_ASSERT(bitstr_seglist); + VDEC_ASSERT(decpict_seglist); + + /* Add the segments to the Decode Picture */ + bit_str_seg = lst_first(bitstr_seglist); + while (bit_str_seg) { + dec_pict_seg = kzalloc(sizeof(*dec_pict_seg), GFP_KERNEL); + VDEC_ASSERT(dec_pict_seg); + if (!dec_pict_seg) + return IMG_ERROR_OUT_OF_MEMORY; + + dec_pict_seg->bstr_seg = bit_str_seg; + dec_pict_seg->internal_seg = FALSE; + lst_add(decpict_seglist, dec_pict_seg); + + bit_str_seg = lst_next(bit_str_seg); + } + return IMG_SUCCESS; +} + +/* + * @Function decoder_picture_decode + */ +static int decoder_picture_decode(struct dec_str_ctx *dec_str_ctx, + struct vdecdd_str_unit *str_unit, + struct dec_decpict **dec_pict_ptr) +{ + struct vdecdd_picture *picture; + struct dec_core_ctx *dec_core_ctx; + struct dec_decpict *dec_pict; + int ret = IMG_SUCCESS; + struct decoder_regsoffsets regs_offsets; + + /* Validate input arguments */ + if (!dec_str_ctx || !str_unit || !str_unit->pict_hdr_info || !dec_pict_ptr) { + VDEC_ASSERT(0); + return -EIO; + } + + picture = (struct vdecdd_picture *)str_unit->dd_pict_data; + dec_core_ctx = decoder_str_ctx_to_core_ctx(dec_str_ctx); + + if (!picture || !dec_core_ctx) { + VDEC_ASSERT(0); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Ensure this is a new picture */ + VDEC_ASSERT(!dec_str_ctx->cur_pict); + VDEC_ASSERT(str_unit->str_unit_type == VDECDD_STRUNIT_PICTURE_START); + + dec_core_ctx->cum_pics++; + + /* Allocate a unique id to the picture */ + ret = idgen_allocid(dec_str_ctx->pict_idgen, picture, &picture->pict_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Allocate the decoded picture information structure. */ + picture->dec_pict_info = kzalloc(sizeof(*picture->dec_pict_info), GFP_KERNEL); + VDEC_ASSERT(picture->dec_pict_info); + if (!picture->dec_pict_info) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Extract decoded information from the stream unit */ + picture->dec_pict_info->err_flags = str_unit->err_flags; + picture->dec_pict_info->first_fld_tag_container.pict_tag_param = + (unsigned long)(str_unit->str_unit_tag); + picture->dec_pict_info->op_config = picture->op_config; + picture->dec_pict_info->rend_info = picture->disp_pict_buf.rend_info; + picture->dec_pict_info->disp_info = str_unit->pict_hdr_info->disp_info; + + /* Extract aux picture information from the stream unit */ + picture->dec_pict_aux_info.seq_hdr_id = + str_unit->seq_hdr_info->sequ_hdr_id; + picture->dec_pict_aux_info.pps_id = + str_unit->pict_hdr_info->pict_aux_data.id; + picture->dec_pict_aux_info.second_pps_id = + str_unit->pict_hdr_info->second_pict_aux_data.id; + + /* Create a new decoder picture container. */ + dec_pict = kzalloc(sizeof(*dec_pict), GFP_KERNEL); + VDEC_ASSERT(dec_pict); + if (!dec_pict) { + ret = IMG_ERROR_OUT_OF_MEMORY; + goto error_dec_pict; + } + + /* Attach decoder/picture context information. */ + dec_pict->dec_str_ctx = dec_str_ctx; + + /* + * Construct the transaction Id. + * This consists of stream and core number in addition to picture + * number in stream and a 4-bit value representing the picture number + * in core. + */ + dec_pict->transaction_id = + CREATE_TRANSACTION_ID(0, dec_str_ctx->km_str_id, dec_core_ctx->cum_pics, + picture->pict_id); + + /* Add picture to core decode list */ + dec_str_ctx->dec_str_st.num_pict_decoding++; + + /* Fake a FW message to process when decoded. */ + dec_pict->first_fld_fwmsg = kzalloc(sizeof(*dec_pict->first_fld_fwmsg), GFP_KERNEL); + VDEC_ASSERT(dec_pict->first_fld_fwmsg); + if (!dec_pict->first_fld_fwmsg) { + ret = IMG_ERROR_OUT_OF_MEMORY; + goto error_fw_msg; + } + + dec_pict->second_fld_fwmsg = + kzalloc(sizeof(*dec_pict->second_fld_fwmsg), GFP_KERNEL); + VDEC_ASSERT(dec_pict->second_fld_fwmsg); + if (!dec_pict->second_fld_fwmsg) { + ret = IMG_ERROR_OUT_OF_MEMORY; + goto error_fw_msg; + } + + /* Add the segments to the Decode Picture */ + ret = decoder_wrap_bitstr_segments(&str_unit->bstr_seg_list, + &dec_pict->dec_pict_seg_list, + dec_str_ctx->config.user_str_id); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_segments; + + /* + * Shuffle the current and previous + * Hold a reference to the last context on the FE + */ + if (dec_str_ctx->prev_fe_pict_dec_res) { + /* Return previous last FW context. */ + resource_item_return(&dec_str_ctx->prev_fe_pict_dec_res->ref_cnt); + + if (resource_item_isavailable(&dec_str_ctx->prev_fe_pict_dec_res->ref_cnt)) { + resource_list_remove(&dec_str_ctx->dec_res_lst, + dec_str_ctx->prev_fe_pict_dec_res); + + resource_list_add_img(&dec_str_ctx->dec_res_lst, + dec_str_ctx->prev_fe_pict_dec_res, 0, + &dec_str_ctx->prev_fe_pict_dec_res->ref_cnt); + } + } + + dec_str_ctx->prev_fe_pict_dec_res = dec_str_ctx->cur_fe_pict_dec_res; + dec_pict->prev_pict_dec_res = dec_str_ctx->prev_fe_pict_dec_res; + + /* Get a new stream decode resource bundle for current picture. */ + dec_pict->cur_pict_dec_res = resource_list_get_avail(&dec_str_ctx->dec_res_lst); + VDEC_ASSERT(dec_pict->cur_pict_dec_res); + if (!dec_pict->cur_pict_dec_res) { + ret = IMG_ERROR_UNEXPECTED_STATE; + goto error_dec_res; + } + + if (dec_str_ctx->config.vid_std == VDEC_STD_H264) { + /* Copy any SGM for current picture. */ + if (str_unit->pict_hdr_info->pict_sgm_data.id != + BSPP_INVALID) { + VDEC_ASSERT(str_unit->pict_hdr_info->pict_sgm_data.size <= + dec_pict->cur_pict_dec_res->h264_sgm_buf.buf_size); + /* Updated in translation_api */ + memcpy(dec_pict->cur_pict_dec_res->h264_sgm_buf.cpu_virt, + str_unit->pict_hdr_info->pict_sgm_data.pic_data, + str_unit->pict_hdr_info->pict_sgm_data.size); + } + } + + dec_pict->cur_pict_dec_res->transaction_id = dec_pict->transaction_id; + dec_str_ctx->cur_fe_pict_dec_res = dec_pict->cur_pict_dec_res; + resource_item_use(&dec_str_ctx->cur_fe_pict_dec_res->ref_cnt); + + /* Get a new control buffer */ + dec_pict->pict_ref_res = + resource_list_get_avail(&dec_str_ctx->ref_res_lst); + VDEC_ASSERT(dec_pict->pict_ref_res); + if (!dec_pict->pict_ref_res) { + ret = IMG_ERROR_UNEXPECTED_STATE; + goto error_ref_res; + } + + VDEC_ASSERT(dec_str_ctx->decctx); + VDEC_ASSERT(dec_str_ctx->decctx->dev_cfg); + + dec_pict->str_pvdec_fw_ctxbuf = &dec_str_ctx->pvdec_fw_ctx_buf; + dec_pict->pict_hdr_info = str_unit->pict_hdr_info; + + /* Obtain (core) resources for the picture */ + ret = dec_res_picture_attach(&dec_str_ctx->resources, + dec_str_ctx->config.vid_std, dec_pict); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_res_attach; + + /* Clear fw context data for re-use */ + memset(dec_pict->cur_pict_dec_res->fw_ctx_buf.cpu_virt, 0, + dec_pict->cur_pict_dec_res->fw_ctx_buf.buf_size); + + /* + * Clear the control data in case the picture is discarded before + * being prepared by firmware. + */ + memset(dec_pict->pict_ref_res->fw_ctrlbuf.cpu_virt, 0, + dec_pict->pict_ref_res->fw_ctrlbuf.buf_size); + + ret = hwctrl_getregsoffset(dec_core_ctx->hw_ctx, ®s_offsets); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_other; + + ret = translation_ctrl_alloc_prepare(&dec_str_ctx->config, str_unit, + dec_pict, + &dec_core_ctx->core_props, + ®s_offsets); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_other; + + ret = hwctrl_picture_submitbatch(dec_core_ctx->hw_ctx, dec_pict, + dec_str_ctx->vxd_dec_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_other; + + VDEC_ASSERT(dec_str_ctx->avail_slots > 0); + dec_str_ctx->avail_slots--; + + VDEC_ASSERT(!dec_core_ctx->busy); + dec_core_ctx->busy = TRUE; + /* Store this transaction ID in stream context */ + dec_str_ctx->last_fe_transaction_id = dec_pict->transaction_id; + dec_str_ctx->cur_pict = (struct dec_decpict *)dec_pict; + + dec_str_ctx->dec_str_st.features = str_unit->features; + + if (str_unit->eop) + dec_pict->eop_found = TRUE; + + *dec_pict_ptr = dec_pict; + + return IMG_SUCCESS; + + /* Roll back in case of errors. */ +error_other: + dec_res_picture_detach(&dec_str_ctx->resources, dec_pict); +error_res_attach: +error_ref_res: +error_dec_res: +error_segments: + decoder_clean_bitstr_segments(&dec_pict->dec_pict_seg_list); + kfree(dec_pict->first_fld_fwmsg); + kfree(dec_pict->second_fld_fwmsg); +error_fw_msg: + kfree(dec_pict); +error_dec_pict: + kfree(picture->dec_pict_info); + + return ret; +} + +/* + * @Function decoder_stream_reference_resource_create + */ +static int +decoder_stream_reference_resource_create(struct dec_str_ctx *dec_str_ctx) +{ + struct dec_pictref_res *pict_ref_res; + int ret; + unsigned int mem_heap_id; + enum sys_emem_attrib mem_attribs; + + if (!dec_str_ctx || !dec_str_ctx->decctx) { + VDEC_ASSERT(0); + return -EINVAL; + } + + mem_heap_id = dec_str_ctx->decctx->internal_heap_id; + mem_attribs = (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE); + mem_attribs |= (enum sys_emem_attrib)SYS_MEMATTRIB_INTERNAL; + + /* Allocate the firmware context buffer info structure. */ + pict_ref_res = kzalloc(sizeof(*pict_ref_res), GFP_KERNEL); + VDEC_ASSERT(pict_ref_res); + if (!pict_ref_res) + return IMG_ERROR_OUT_OF_MEMORY; + + /* + * Allocate the firmware context buffer to contain data required for + * subsequent picture. + */ + +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(dec_str_ctx->mmu_str_handle, MMU_HEAP_STREAM_BUFFERS, mem_heap_id, + (enum sys_emem_attrib)(mem_attribs | SYS_MEMATTRIB_CPU_READ | + SYS_MEMATTRIB_CPU_WRITE), + sizeof(struct vdecfw_buffer_control), + DEV_MMU_PAGE_ALIGNMENT, + &pict_ref_res->fw_ctrlbuf); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto err_out_of_memory; + + /* + * Clear the context data in preparation for first time use by + * the firmware. + */ + memset(pict_ref_res->fw_ctrlbuf.cpu_virt, 0, pict_ref_res->fw_ctrlbuf.buf_size); + + pict_ref_res->ref_cnt = 1; + + ret = resource_list_add_img(&dec_str_ctx->ref_res_lst, pict_ref_res, 0, + &pict_ref_res->ref_cnt); + if (ret != IMG_SUCCESS) { + pr_err("[USERSID=0x%08X] Failed to add resource", dec_str_ctx->config.user_str_id); + return ret; + } + + return IMG_SUCCESS; + +err_out_of_memory: + + kfree(pict_ref_res); + pict_ref_res = NULL; + + pr_err("[USERSID=0x%08X] Failed to allocate device memory for stream reference resources", + dec_str_ctx->config.user_str_id); + + return IMG_ERROR_OUT_OF_MEMORY; +} + +/* + * @Function decoder_picture_finalize + */ +static int decoder_picture_finalize(struct dec_str_ctx *dec_str_ctx, + struct vdecdd_str_unit *str_unit) +{ + struct dec_decpict *dec_pict; + struct dec_core_ctx *dec_core_ctx = NULL; + + VDEC_ASSERT(dec_str_ctx); + + dec_pict = dec_str_ctx->cur_pict; + if (!dec_pict) { + pr_err("[USERSID=0x%08X] Unable to get the current picture from Decoder context", + dec_str_ctx->config.user_str_id); + return IMG_ERROR_GENERIC_FAILURE; + } + + dec_core_ctx = decoder_str_ctx_to_core_ctx(dec_str_ctx); + + if (!dec_core_ctx || !dec_core_ctx->busy) { + VDEC_ASSERT(0); + return -EINVAL; + } + + dec_core_ctx->busy = FALSE; + + /* Picture data are now complete, nullify pointer */ + dec_str_ctx->cur_pict = NULL; + + return IMG_SUCCESS; +} + +/* + * @Function decoder_submit_fragment + */ +static int decoder_submit_fragment(struct dec_str_ctx *dec_str_context, + struct vdecdd_str_unit *str_unit, + unsigned char eop) +{ + struct dec_core_ctx *dec_core_context = NULL; + struct lst_t dec_fragment_seg_list; + struct dec_decpict_seg *dec_pict_seg; + struct dec_pict_fragment *pict_fragment; + int ret = IMG_SUCCESS; + + if (!dec_str_context) { + VDEC_ASSERT(0); + return IMG_ERROR_GENERIC_FAILURE; + } + + if (!dec_str_context->cur_pict) { + pr_err("[USERSID=0x%08X] Unable to get the current picture from Decoder context", + dec_str_context->config.user_str_id); + VDEC_ASSERT(0); + return IMG_ERROR_GENERIC_FAILURE; + } + + dec_core_context = decoder_str_ctx_to_core_ctx(dec_str_context); + if (!dec_core_context) { + VDEC_ASSERT(0); + return IMG_ERROR_GENERIC_FAILURE; + } + + pict_fragment = kzalloc(sizeof(*pict_fragment), GFP_KERNEL); + VDEC_ASSERT(pict_fragment); + if (!pict_fragment) + return IMG_ERROR_OUT_OF_MEMORY; + + lst_init(&dec_fragment_seg_list); + + /* Add the segments to the temporary list */ + ret = decoder_wrap_bitstr_segments(&str_unit->bstr_seg_list, + &dec_fragment_seg_list, + dec_str_context->config.user_str_id); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Prepare ctr alloc for the fragment */ + ret = translation_fragment_prepare(dec_str_context->cur_pict, + &dec_fragment_seg_list, eop, + pict_fragment); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* + * Move segments of the fragment from the temporary list to the picture + * segment list + */ + dec_pict_seg = lst_removehead(&dec_fragment_seg_list); + while (dec_pict_seg) { + lst_add(&dec_str_context->cur_pict->dec_pict_seg_list, + dec_pict_seg); + dec_pict_seg = lst_removehead(&dec_fragment_seg_list); + } + + /* Submit fragment */ + ret = hwctrl_picture_submit_fragment(dec_core_context->hw_ctx, + pict_fragment, + dec_str_context->cur_pict, + dec_str_context->vxd_dec_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + lst_add(&dec_str_context->cur_pict->fragment_list, pict_fragment); + + if (eop) + dec_str_context->cur_pict->eop_found = TRUE; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] FRAGMENT", + dec_str_context->config.user_str_id, + dec_str_context->last_fe_transaction_id); +#endif + + return IMG_SUCCESS; +error: + kfree(pict_fragment); + + return ret; +} + +/* + * @Function decoder_stream_process_unit + */ +int decoder_stream_process_unit(void *dec_str_ctx_arg, + struct vdecdd_str_unit *str_unit) +{ + struct dec_str_ctx *dec_str_ctx = + decoder_stream_get_context(dec_str_ctx_arg); + + struct dec_str_unit *dec_str_unit; + struct dec_decpict *dec_pict = NULL; + unsigned char processed = FALSE; + int ret; + + VDEC_ASSERT(dec_str_ctx); + VDEC_ASSERT(str_unit); + + if (!dec_str_ctx || !str_unit) { + pr_err("Invalid decoder stream context handle!\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + pr_debug("%s : stream unit type = %d\n" + , __func__, str_unit->str_unit_type); + /* Process the stream unit */ + switch (str_unit->str_unit_type) { + case VDECDD_STRUNIT_SEQUENCE_END: + case VDECDD_STRUNIT_ANONYMOUS: + case VDECDD_STRUNIT_CLOSED_GOP: + case VDECDD_STRUNIT_PICTURE_PORTENT: + case VDECDD_STRUNIT_FENCE: + /* Nothing more to do so mark the stream unit as processed */ + processed = TRUE; + break; + + case VDECDD_STRUNIT_STOP: + if (dec_str_ctx->cur_pict && !dec_str_ctx->cur_pict->eop_found) { + ret = decoder_submit_fragment(dec_str_ctx, str_unit, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + ret = decoder_picture_finalize(dec_str_ctx, str_unit); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] FORCED END", + dec_str_ctx->config.user_str_id, + dec_str_ctx->last_fe_transaction_id); +#endif + } + + processed = TRUE; + break; + + case VDECDD_STRUNIT_SEQUENCE_START: + { + unsigned int max_num_activ_pict = 0; + + VDEC_ASSERT(str_unit->seq_hdr_info); + /* + * Determine how many decoded pictures can be held for + * reference in the decoder for this stream. + */ + ret = vdecddutils_ref_pict_get_maxnum(&dec_str_ctx->config, + &str_unit->seq_hdr_info->com_sequ_hdr_info, + &max_num_activ_pict); + if (ret != IMG_SUCCESS) + return ret; + + /* Double for field coding */ + max_num_activ_pict *= 2; + + /* + * Ensure that there are enough resource to have pictures + * filling all slots on all cores. + */ + max_num_activ_pict += + dec_str_ctx->decctx->dev_cfg->num_slots_per_pipe * + dec_str_ctx->decctx->num_pipes; + + /* Increase decoder stream resources if necessary. */ + while (dec_str_ctx->num_ref_res < max_num_activ_pict) { + ret = decoder_stream_reference_resource_create(dec_str_ctx); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dec_str_ctx->num_ref_res++; + } + + /* Nothing more to do so mark the stream unit as processed */ + processed = TRUE; + break; + } + + case VDECDD_STRUNIT_PICTURE_START: + if (str_unit->decode) { + /* Prepare and submit picture to decode. */ + ret = decoder_picture_decode(dec_str_ctx, str_unit, &dec_pict); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] START", + dec_str_ctx->config.user_str_id, + dec_str_ctx->last_fe_transaction_id); +#endif + } else { + processed = TRUE; + } + break; + + case VDECDD_STRUNIT_PICTURE_END: + if (str_unit->decode) { + ret = decoder_picture_finalize(dec_str_ctx, str_unit); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] END", + dec_str_ctx->config.user_str_id, + dec_str_ctx->last_fe_transaction_id); +#endif + } else { + processed = TRUE; + } + break; + + default: + VDEC_ASSERT(FALSE); + break; + } + + /* + * If this or any preceding stream unit(s) could not be + * completely processed, add this unit to the queue. + */ + if (!processed) { + /* Add unit to stream decode list */ + dec_str_unit = kzalloc(sizeof(*dec_str_unit), GFP_KERNEL); + VDEC_ASSERT(dec_str_unit); + if (!dec_str_unit) + return IMG_ERROR_OUT_OF_MEMORY; + + dec_str_unit->str_unit = str_unit; + + /* make PICTURE_START owner of dec_pict */ + if (dec_pict) { + VDEC_ASSERT(str_unit->str_unit_type == VDECDD_STRUNIT_PICTURE_START); + dec_str_unit->dec_pict = dec_pict; + } + + lst_add(&dec_str_ctx->pend_strunit_list, dec_str_unit); + } else { + /* + * If there is nothing being decoded for this stream, + * immediately handle the unit (non-picture so doesn't need + * decoding). Report that this unit has been processed. + */ + VDEC_ASSERT(dec_str_ctx->decctx); + ret = dec_str_ctx->str_processed_cb(dec_str_ctx->usr_int_data, + VXD_CB_STRUNIT_PROCESSED, + str_unit); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + return IMG_SUCCESS; +} + +static int +decoder_get_required_core_features(const struct vdec_str_configdata *str_cfg, + const struct vdec_str_opconfig *op_cfg, + unsigned int *features) +{ + unsigned int features_local = 0; + + VDEC_ASSERT(str_cfg); + VDEC_ASSERT(features); + + /* Check Video Standard. */ + switch (str_cfg->vid_std) { + case VDEC_STD_H264: + features_local = VDECDD_COREFEATURE_H264; + break; +#ifdef HAS_JPEG + case VDEC_STD_JPEG: + features_local = VDECDD_COREFEATURE_JPEG; + break; +#endif +#ifdef HAS_HEVC + case VDEC_STD_HEVC: + features_local = VDECDD_COREFEATURE_HEVC; + break; +#endif + default: + VDEC_ASSERT(FALSE); + break; + } + + *features = features_local; + + return IMG_SUCCESS; +} + +/* + * @Function decoder_is_supported_by_atleast_onepipe + */ +static unsigned char decoder_is_supported_by_atleast_onepipe(unsigned char *features, + unsigned int num_pipes) +{ + unsigned int i; + + VDEC_ASSERT(features); + VDEC_ASSERT(num_pipes <= VDEC_MAX_PIXEL_PIPES); + + for (i = 0; i < num_pipes; i++) { + if (features[i]) + return TRUE; + } + + return FALSE; +} + +/* + * @Function decoder_check_support + */ +int decoder_check_support(void *dec_ctx_arg, + const struct vdec_str_configdata *str_cfg, + const struct vdec_str_opconfig *str_op_cfg, + const struct vdecdd_ddpict_buf *disp_pict_buf, + const struct vdec_pict_rendinfo *req_pict_rendinfo, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + const struct bspp_pict_hdr_info *pict_hdrinfo, + const struct vdec_comsequ_hdrinfo *prev_comseq_hdrinfo, + const struct bspp_pict_hdr_info *prev_pict_hdrinfo, + unsigned char non_cfg_req, + struct vdec_unsupp_flags *unsupported, + unsigned int *features) +{ + struct dec_ctx *dec_ctx = (struct dec_ctx *)dec_ctx_arg; + struct dec_core_ctx *dec_core_ctx; + struct vxd_coreprops *core_props; + const struct vdec_pict_rendinfo *disp_pict_rendinfo = NULL; + int ret = IMG_ERROR_NOT_SUPPORTED; + + /* Ensure input parameters are valid. */ + VDEC_ASSERT(dec_ctx_arg); + VDEC_ASSERT(str_cfg); + VDEC_ASSERT(unsupported); + + if (!dec_ctx_arg || !str_cfg || !unsupported) { + pr_err("Invalid parameters!"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (disp_pict_buf) + disp_pict_rendinfo = &disp_pict_buf->rend_info; + + /* + * Validate compatibility between the supplied configuration/state + * and the master core only at the moment (assumed to have superset + * of features). + * Some features may not be present on any slave cores which might + * cause poor utilisation of hardware. + */ + memset(unsupported, 0, sizeof(*unsupported)); + + dec_core_ctx = dec_ctx->dec_core_ctx; + VDEC_ASSERT(dec_core_ctx); + + core_props = &dec_core_ctx->core_props; + VDEC_ASSERT(core_props); + + /* Check that the video standard is supported */ + switch (str_cfg->vid_std) { + case VDEC_STD_H264: + if (!decoder_is_supported_by_atleast_onepipe(core_props->h264, + core_props->num_pixel_pipes)) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: VIDEO STANDARD (H.264)", + str_cfg->user_str_id); + unsupported->str_cfg |= + VDECDD_UNSUPPORTED_STRCONFIG_STD; + } + + if (comseq_hdrinfo && (H264_PROFILE_MVC_HIGH == + comseq_hdrinfo->codec_profile || H264_PROFILE_MVC_STEREO == + comseq_hdrinfo->codec_profile) && comseq_hdrinfo->num_views > + VDEC_H264_MVC_MAX_VIEWS) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[SW]: NUMBER OF VIEWS", + str_cfg->user_str_id); + unsupported->seq_hdr |= VDECDD_UNSUPPORTED_SEQUHDR_NUM_OF_VIEWS; + } + break; +#ifdef HAS_HEVC + case VDEC_STD_HEVC: + if (!decoder_is_supported_by_atleast_onepipe(core_props->hevc, + core_props->num_pixel_pipes)) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: VIDEO STANDARD (HEVC)", + str_cfg->user_str_id); + unsupported->str_cfg |= VDECDD_UNSUPPORTED_STRCONFIG_STD; + } + if (pict_hdrinfo && pict_hdrinfo->hevc_pict_hdr_info.range_ext_present) + if ((pict_hdrinfo->hevc_pict_hdr_info.is_full_range_ext && + !decoder_is_supported_by_atleast_onepipe(core_props->hevc_range_ext, + core_props->num_pixel_pipes)) || + (!pict_hdrinfo->hevc_pict_hdr_info.is_full_range_ext && + core_props->vidstd_props[str_cfg->vid_std].max_chroma_format == + PIXEL_FORMAT_420)) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: HEVC RANGE EXTENSIONS", + str_cfg->user_str_id); + unsupported->pict_hdr |= VDECDD_UNSUPPORTED_PICTHDR_HEVC_RANGE_EXT; + } + break; +#endif +#ifdef HAS_JPEG + case VDEC_STD_JPEG: + if (!decoder_is_supported_by_atleast_onepipe(core_props->jpeg, + core_props->num_pixel_pipes)) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: VIDEO STANDARD (JPEG)", + str_cfg->user_str_id); + unsupported->str_cfg |= + VDECDD_UNSUPPORTED_STRCONFIG_STD; + } + break; +#endif + default: + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: VIDEO STANDARD (UNKNOWN)", + str_cfg->user_str_id); + unsupported->str_cfg |= + VDECDD_UNSUPPORTED_STRCONFIG_STD; + break; + } + + if (str_op_cfg) { + /* + * Ensure that each display feature is supported by the + * hardware. + */ + if (comseq_hdrinfo) { + /* Validate display pixel format */ + if (non_cfg_req && prev_comseq_hdrinfo && + vdec_size_nz(prev_comseq_hdrinfo->frame_size) && + prev_comseq_hdrinfo->pixel_info.chroma_fmt_idc == + str_op_cfg->pixel_info.chroma_fmt_idc && + comseq_hdrinfo->pixel_info.chroma_fmt_idc != + prev_comseq_hdrinfo->pixel_info.chroma_fmt_idc) { + /* + * If this is a non-configuration request and + * it looks like a new sequence with + * sub-sampling change, just indicate output + * format mismatch without any error messages. + */ + unsupported->str_opcfg |= VDECDD_UNSUPPORTED_OUTPUTCONFIG_PIXFORMAT; + } else { + switch (str_op_cfg->pixel_info.chroma_fmt_idc) { + case PIXEL_FORMAT_420: + if (comseq_hdrinfo->pixel_info.chroma_fmt_idc == + PIXEL_FORMAT_MONO) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: TRANSFORM PIXEL FORMAT FROM 400 TO 420", + str_cfg->user_str_id); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTCONFIG_PIXFORMAT; + } + break; + + case PIXEL_FORMAT_422: + if (comseq_hdrinfo->pixel_info.chroma_fmt_idc == + PIXEL_FORMAT_420 && + str_op_cfg->pixel_info.num_planes > 1) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: REQUESTED NUMBER OF PLANES FOR 422 UPSAMPLING", + str_cfg->user_str_id); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTCONFIG_PIXFORMAT; + } else if (comseq_hdrinfo->pixel_info.chroma_fmt_idc == + PIXEL_FORMAT_MONO) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: TRANSFORM PIXEL FORMAT FROM 400 TO 422", + str_cfg->user_str_id); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTCONFIG_PIXFORMAT; + } + break; + + default: + break; + } + } + } + + if (str_op_cfg->pixel_info.bitdepth_y > + core_props->vidstd_props[str_cfg->vid_std].max_luma_bitdepth || + str_op_cfg->pixel_info.bitdepth_y < 8 || + str_op_cfg->pixel_info.bitdepth_y == 9) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: DISPLAY PICTURE LUMA BIT DEPTH %d [RANGE: 8->%d for %s]", + str_cfg->user_str_id, + str_op_cfg->pixel_info.bitdepth_y, + core_props->vidstd_props[str_cfg->vid_std].max_luma_bitdepth, + vid_std_names[str_cfg->vid_std]); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTCONFIG_PIXFORMAT; + } + + if (str_op_cfg->pixel_info.chroma_fmt_idc != + PIXEL_FORMAT_MONO && + (str_op_cfg->pixel_info.bitdepth_c > + core_props->vidstd_props[str_cfg->vid_std].max_chroma_bitdepth || + str_op_cfg->pixel_info.bitdepth_c < 8 || + str_op_cfg->pixel_info.bitdepth_c == 9)) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: DISPLAY PICTURE CHROMA BIT DEPTH %d [RANGE: 8->%d for %s]", + str_cfg->user_str_id, + str_op_cfg->pixel_info.bitdepth_c, + core_props->vidstd_props[str_cfg->vid_std].max_chroma_bitdepth, + vid_std_names[str_cfg->vid_std]); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTCONFIG_PIXFORMAT; + } + +#ifdef HAS_JPEG + /* Validate display configuration against existing stream configuration.*/ + if (str_cfg->vid_std == VDEC_STD_JPEG) { + if (str_op_cfg->force_oold) { + pr_err("[USERSID=0x%08X] UNSUPPORTED[HW]: OOLD WITH JPEG\n", + str_cfg->user_str_id); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTCONFIG_X_WITH_JPEG; + } + } +#endif + } + + if (disp_pict_rendinfo) { + unsigned int stride_alignment = VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT; + + if (req_pict_rendinfo) { + /* + * Picture size declared in buffer must be at least as + * large as that required by bitstream/output config. + */ + if (!vdec_size_ge(disp_pict_rendinfo->rend_pict_size, + req_pict_rendinfo->rend_pict_size)) { + pr_warn("[USERSID=0x%08X] Picture size of output picture buffer [%d x %d] is not large enough for sequence [%d x %d]", + str_cfg->user_str_id, + disp_pict_rendinfo->rend_pict_size.width, + disp_pict_rendinfo->rend_pict_size.height, + req_pict_rendinfo->rend_pict_size.width, + req_pict_rendinfo->rend_pict_size.height); + unsupported->str_opcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_PICTURE_SIZE; + } + + /* + * Size of each plane must be at least as large + * as that required. + */ + if (disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].size < + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].size) { + pr_warn("[USERSID=0x%08X] Y plane of output picture buffer [%d bytes] is not large enough for bitstream/config [%d bytes]", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].size, + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].size); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_Y_SIZE; + } + + /* + * Stride of each plane must be at least as large as that + * required. + */ + if (disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].stride < + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].stride) { + pr_warn("[USERSID=0x%08X] Y stride of output picture buffer [%d bytes] is not large enough for bitstream/config [%d bytes]", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].stride, + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].stride); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_Y_STRIDE; + } + + /* + * Size of each plane must be at least + * as large as that required. + */ + if (disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].size < + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].size) { + pr_warn("[USERSID=0x%08X] UV plane of output picture buffer [%d bytes] is not large enough for bitstream/config [%d bytes]", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].size, + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].size); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_UV_SIZE; + } + + /* + * Stride of each plane must be at least + * as large as that required. + */ + if (disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].stride < + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].stride) { + pr_warn("[USERSID=0x%08X] UV stride of output picture buffer [%d bytes] is not large enough for bitstream/config [%d bytes]", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].stride, + req_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].stride); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_UV_STRIDE; + } + + if ((req_pict_rendinfo->stride_alignment & + (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT - 1)) != 0) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: STRIDE ALIGNMENT [%d] must be a multiple of %d bytes", + str_cfg->user_str_id, + req_pict_rendinfo->stride_alignment, + VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_64BYTE_STRIDE; + } + + if (req_pict_rendinfo->stride_alignment > 0) + stride_alignment = req_pict_rendinfo->stride_alignment; + } + + if ((disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].stride % + stride_alignment) != 0) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: Y STRIDE [%d] must be a multiple of %d bytes", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_Y].stride, + stride_alignment); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_64BYTE_STRIDE; + } + + if ((disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].stride % + stride_alignment) != 0) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: UV STRIDE [%d] must be a multiple of %d bytes", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_UV].stride, + stride_alignment); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_64BYTE_STRIDE; + } + + if ((disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_V].stride % + stride_alignment) != 0) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: V STRIDE [%d] must be a multiple of %d bytes", + str_cfg->user_str_id, + disp_pict_rendinfo->plane_info[VDEC_PLANE_VIDEO_V].stride, + stride_alignment); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_64BYTE_STRIDE; + } + + if (req_pict_rendinfo) { + if (str_op_cfg) { + if (str_cfg->vid_std != VDEC_STD_JPEG) { + if (str_op_cfg->pixel_info.num_planes <= 2) + /* + * V plane only required when chroma is + * separated. + */ + VDEC_ASSERT(req_pict_rendinfo->plane_info + [VDEC_PLANE_VIDEO_V].size == 0); + + if (str_op_cfg->pixel_info.num_planes <= 3) + /* Alpha planes should not be required. */ + VDEC_ASSERT(req_pict_rendinfo->plane_info + [VDEC_PLANE_VIDEO_A].size == 0); + } + } + + /* Size of buffer must be at least as large as that required. */ + if (disp_pict_rendinfo->rendered_size < + req_pict_rendinfo->rendered_size) { + pr_warn("[USERSID=0x%08X] Output picture buffer [%d bytes] is not large enough for bitstream/config [%d bytes]", + str_cfg->user_str_id, + disp_pict_rendinfo->rendered_size, + req_pict_rendinfo->rendered_size); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_BUFFER_SIZE; + } + } + + if (str_op_cfg) { + if (comseq_hdrinfo) { + if (vdec_size_lt(disp_pict_rendinfo->rend_pict_size, + comseq_hdrinfo->max_frame_size)) { + pr_warn("[USERSID=0x%08X] Buffers [%d x %d] must be large enough to contain the maximum frame size [%d x %d] when not scaling", + str_cfg->user_str_id, + disp_pict_rendinfo->rend_pict_size.width, + disp_pict_rendinfo->rend_pict_size.height, + comseq_hdrinfo->max_frame_size.width, + comseq_hdrinfo->max_frame_size.height); + unsupported->op_bufcfg |= + VDECDD_UNSUPPORTED_OUTPUTBUFCONFIG_PICTURE_SIZE; + } + } + } + } + + if (comseq_hdrinfo) { + unsigned int max_width = + vdec_size_min(core_props->vidstd_props[str_cfg->vid_std].max_width, + MAX_PLATFORM_SUPPORTED_WIDTH); + + unsigned int max_height = + vdec_size_min(core_props->vidstd_props[str_cfg->vid_std].max_height, + MAX_PLATFORM_SUPPORTED_HEIGHT); + + if (comseq_hdrinfo->max_frame_size.width > max_width || + comseq_hdrinfo->max_frame_size.height > max_height) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: FRAME WIDTH %dpx or HEIGHT %dpx are over maximum allowed value [%d, %d]", + str_cfg->user_str_id, + comseq_hdrinfo->max_frame_size.width, + comseq_hdrinfo->max_frame_size.height, + max_width, max_height); + unsupported->seq_hdr |= + VDECDD_UNSUPPORTED_SEQUHDR_SIZE; + } + + if (comseq_hdrinfo->pixel_info.bitdepth_y > + core_props->vidstd_props[str_cfg->vid_std].max_luma_bitdepth || + comseq_hdrinfo->pixel_info.bitdepth_y < 8 || + comseq_hdrinfo->pixel_info.bitdepth_y == 9) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PICTURE LUMA BIT DEPTH %d [RANGE: 8->%d for %s]", + str_cfg->user_str_id, + comseq_hdrinfo->pixel_info.bitdepth_y, + core_props->vidstd_props[str_cfg->vid_std].max_luma_bitdepth, + vid_std_names[str_cfg->vid_std]); + unsupported->seq_hdr |= + VDECDD_UNSUPPORTED_SEQUHDR_PIXFORMAT_BIT_DEPTH; + } + + if (comseq_hdrinfo->pixel_info.chroma_fmt_idc != + PIXEL_FORMAT_MONO && + (comseq_hdrinfo->pixel_info.bitdepth_c > + core_props->vidstd_props[str_cfg->vid_std].max_chroma_bitdepth || + comseq_hdrinfo->pixel_info.bitdepth_c < 8 || + comseq_hdrinfo->pixel_info.bitdepth_c == 9)) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PICTURE CHROMA BIT DEPTH %d [RANGE: 8->%d for %s]", + str_cfg->user_str_id, + comseq_hdrinfo->pixel_info.bitdepth_c, + core_props->vidstd_props[str_cfg->vid_std].max_chroma_bitdepth, + vid_std_names[str_cfg->vid_std]); + unsupported->seq_hdr |= + VDECDD_UNSUPPORTED_SEQUHDR_PIXFORMAT_BIT_DEPTH; + } + + if (comseq_hdrinfo->pixel_info.chroma_fmt_idc != + PIXEL_FORMAT_MONO && + comseq_hdrinfo->pixel_info.bitdepth_y != + comseq_hdrinfo->pixel_info.bitdepth_c) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PICTURE MIXED BIT DEPTH [%d vs %d]", + str_cfg->user_str_id, + comseq_hdrinfo->pixel_info.bitdepth_y, + comseq_hdrinfo->pixel_info.bitdepth_c); + unsupported->seq_hdr |= + VDECDD_UNSUPPORTED_SEQUHDR_PIXFORMAT_BIT_DEPTH; + } + + if (comseq_hdrinfo->pixel_info.chroma_fmt_idc > + core_props->vidstd_props[str_cfg->vid_std].max_chroma_format) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PIXEL FORMAT IDC %s [for %s]", + str_cfg->user_str_id, + comseq_hdrinfo->pixel_info.chroma_fmt_idc < + ARRAY_SIZE + (pix_fmt_idc_names) ? (unsigned char *) + pix_fmt_idc_names[comseq_hdrinfo->pixel_info.chroma_fmt_idc] : + (unsigned char *)"Invalid", + vid_std_names[str_cfg->vid_std]); + unsupported->seq_hdr |= + VDECDD_UNSUPPORTED_SEQUHDR_PIXEL_FORMAT; + } + + if (comseq_hdrinfo->pixel_info.chroma_fmt_idc == + PIXEL_FORMAT_INVALID) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[SW]: UNKNOWN CODED PIXEL FORMAT", + str_cfg->user_str_id); + unsupported->seq_hdr |= + VDECDD_UNSUPPORTED_SEQUHDR_PIXEL_FORMAT; + } + } + + if (pict_hdrinfo && comseq_hdrinfo) { + unsigned int coded_cmd_width; + unsigned int coded_cmd_height; + unsigned int min_width = core_props->vidstd_props[str_cfg->vid_std].min_width; + unsigned int min_height = + ALIGN(core_props->vidstd_props[str_cfg->vid_std].min_height, + (pict_hdrinfo->field) ? + 2 * VDEC_MB_DIMENSION : VDEC_MB_DIMENSION); + unsigned int pict_size_in_mbs; + unsigned int max_height = core_props->vidstd_props[str_cfg->vid_std].max_height; + unsigned int max_width = core_props->vidstd_props[str_cfg->vid_std].max_width; + unsigned int max_mbs = core_props->vidstd_props[str_cfg->vid_std].max_macroblocks; + +#ifdef HAS_JPEG + /* For JPEG, max picture size of four plane images is 16k*16k. */ + if (str_cfg->vid_std == VDEC_STD_JPEG) { + if (comseq_hdrinfo->pixel_info.num_planes >= 4) { + max_width = (max_width > 16 * 1024) ? 16 * 1024 : max_width; + max_height = (max_height > 16 * 1024) ? 16 * 1024 : max_height; + } + } +#endif + + coded_cmd_width = + ALIGN(pict_hdrinfo->coded_frame_size.width, VDEC_MB_DIMENSION); + coded_cmd_height = + ALIGN(pict_hdrinfo->coded_frame_size.height, + pict_hdrinfo->field ? + 2 * VDEC_MB_DIMENSION : VDEC_MB_DIMENSION); + + pict_size_in_mbs = (coded_cmd_width * coded_cmd_height) / + (VDEC_MB_DIMENSION * VDEC_MB_DIMENSION); + + if ((str_cfg->vid_std == VDEC_STD_H264 && + max_mbs && pict_size_in_mbs > max_mbs) || + coded_cmd_width > max_width || + coded_cmd_height > max_height) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PICTURE SIZE %d x %d [MAX: %d x %d or %d MBs]", + str_cfg->user_str_id, + coded_cmd_width, coded_cmd_height, + max_width, max_height, max_mbs); + unsupported->pict_hdr |= VDECDD_UNSUPPORTED_PICTHDR_RESOLUTION; + } + + if (pict_hdrinfo->coded_frame_size.width < min_width || + pict_hdrinfo->coded_frame_size.height < min_height) { +#ifdef USE_STRICT_MIN_PIC_SIZE_CHECK + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PICTURE SIZE %d x %d [MIN: %d x %d]", + str_cfg->user_str_id, + pict_hdrinfo->coded_frame_size.width, + pict_hdrinfo->coded_frame_size.height, + min_width, min_height); + unsupported->pict_hdr |= VDECDD_UNSUPPORTED_PICTHDR_RESOLUTION; +#else /* ndef USE_STRICT_MIN_PIC_SIZE_CHECK */ + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: CODED PICTURE SIZE %d x %d [MIN: %d x %d]", + str_cfg->user_str_id, + pict_hdrinfo->coded_frame_size.width, + pict_hdrinfo->coded_frame_size.height, + min_width, min_height); +#endif /* ndef USE_STRICT_MIN_PIC_SIZE_CHECK */ + } + + if (pict_hdrinfo->pict_sgm_data.id != + BSPP_INVALID && pict_hdrinfo->coded_frame_size.width > 1280) { + pr_warn("[USERSID=0x%08X] UNSUPPORTED[HW]: SGM & coded frame width > 1280", + str_cfg->user_str_id); + unsupported->pict_hdr |= + VDECDD_UNSUPPORTED_PICTHDR_OVERSIZED_SGM; + } + + if (pict_hdrinfo->discontinuous_mbs) + pr_info("Stream has Discontinuous Macroblocks"); + + decoder_get_required_core_features(str_cfg, str_op_cfg, features); + } + + if (unsupported->str_cfg == 0 && unsupported->str_opcfg == 0 && + unsupported->op_bufcfg == 0 && unsupported->pict_hdr == 0) + ret = IMG_SUCCESS; + + return ret; +} + +/* + * @Function decoder_picture_decoded + */ +static int decoder_picture_decoded(struct dec_str_ctx *dec_str_ctx, + struct dec_core_ctx *dec_core_ctx, + struct vdecdd_picture *picture, + struct dec_decpict *dec_pict, + struct bspp_pict_hdr_info *pict_hdrinfo, + struct vdecdd_str_unit *str_unit) +{ + struct dec_fwmsg *first_fld_fwmsg; + struct dec_fwmsg *second_fld_fwmsg; + struct dec_pictref_res *pict_ref_res; + unsigned int transaction_id; + struct dec_decoded_pict *decoded_pict; + struct dec_decoded_pict *next_decoded_pict; + struct vdecdd_ddbuf_mapinfo *pict_buf; + struct dec_decoded_pict *prev_decoded_pict; + struct vdecfw_buffer_control *buf_control; + struct vdec_comsequ_hdrinfo *comseq_hdrinfo; + unsigned int res_limit = 0; + unsigned int dec_pict_num = 0; + unsigned int req_pict_num = 0; + struct dec_decoded_pict *aux_decoded_pict; + struct dec_decoded_pict *displayed_decoded_pict = NULL; + int ret; + unsigned int pict_id; + struct vdec_pict_tag_container *fld_tag_container; +#ifdef ERROR_CONCEALMENT + unsigned int first_field_err_level = 0; + unsigned int second_field_err_level = 0; + unsigned int pict_last_mb = 0; +#endif + struct vxd_dec_ctx *ctx; + unsigned int error_flag = 0; + + VDEC_ASSERT(dec_str_ctx); + VDEC_ASSERT(str_unit); + VDEC_ASSERT(dec_pict); + + first_fld_fwmsg = dec_pict->first_fld_fwmsg; + second_fld_fwmsg = dec_pict->second_fld_fwmsg; + pict_ref_res = dec_pict->pict_ref_res; + transaction_id = dec_pict->transaction_id; + + VDEC_ASSERT(picture); + pict_buf = picture->disp_pict_buf.pict_buf; + VDEC_ASSERT(pict_buf); + comseq_hdrinfo = &pict_buf->ddstr_context->comseq_hdr_info; + + /* Create a container for decoded picture. */ + decoded_pict = kzalloc(sizeof(*decoded_pict), GFP_KERNEL); + VDEC_ASSERT(decoded_pict); + if (!decoded_pict) + return IMG_ERROR_OUT_OF_MEMORY; + + decoded_pict->pict = picture; + decoded_pict->first_fld_fwmsg = first_fld_fwmsg; + decoded_pict->second_fld_fwmsg = second_fld_fwmsg; + decoded_pict->pict_ref_res = pict_ref_res; + decoded_pict->transaction_id = transaction_id; + + /* Populate the decoded picture information structure. */ + picture->dec_pict_info->pict_state = VDEC_PICT_STATE_DECODED; + + memcpy(&picture->dec_pict_info->first_fld_tag_container.pict_hwcrc, + &first_fld_fwmsg->pict_hwcrc, + sizeof(picture->dec_pict_info->first_fld_tag_container.pict_hwcrc)); + + memcpy(&picture->dec_pict_info->second_fld_tag_container.pict_hwcrc, + &second_fld_fwmsg->pict_hwcrc, + sizeof(picture->dec_pict_info->second_fld_tag_container.pict_hwcrc)); + + buf_control = + (struct vdecfw_buffer_control *)decoded_pict->pict_ref_res->fw_ctrlbuf.cpu_virt; + if (buf_control->second_field_of_pair) { + /* Search the first field and fill the second_fld_tag_container */ + unsigned int prev_dec_pict_id = + get_prev_picture_id(GET_STREAM_PICTURE_ID(decoded_pict->transaction_id)); + prev_decoded_pict = + decoder_get_decoded_pict_of_stream(prev_dec_pict_id, + &dec_str_ctx->str_decd_pict_list); + + if (prev_decoded_pict) { + memcpy(&picture->dec_pict_info->second_fld_tag_container.pict_hwcrc, + &prev_decoded_pict->first_fld_fwmsg->pict_hwcrc, + sizeof + (picture->dec_pict_info->second_fld_tag_container.pict_hwcrc)); + } else { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Failed to find decoded picture to attach second_fld_tag_container", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + } + prev_decoded_pict = NULL; + } + + /* Report any issues in decoding */ + if (decoded_pict->pict->dec_pict_info->err_flags) + pr_warn("[USERSID=0x%08X] [PID=0x%08X] BSPP reported errors [flags: 0x%08X]", + dec_str_ctx->config.user_str_id, + decoded_pict->pict->pict_id, + decoded_pict->pict->dec_pict_info->err_flags); + + if ((decoded_pict->first_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_FEERROR_ENTDECERROR)) || + (decoded_pict->second_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_FEERROR_ENTDECERROR))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Front-end HW processing terminated prematurely due to an error.", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + picture->dec_pict_info->err_flags |= VDEC_ERROR_FEHW_DECODE; + } + + if ((decoded_pict->first_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_FEERROR_SRERROR)) || + (decoded_pict->second_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_FEERROR_SRERROR))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] HW Shift Register access returned an error during FEHW parsing.", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + picture->dec_pict_info->err_flags |= VDEC_ERROR_SR_ERROR; + } + + if ((decoded_pict->first_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_FEERROR_HWWDT)) || + (decoded_pict->second_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_FEERROR_HWWDT))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Front-end HW processing timed-out.", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + picture->dec_pict_info->err_flags |= VDEC_ERROR_FEHW_TIMEOUT; + } + + if ((decoded_pict->first_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_MISSING_REFERENCES)) || + (decoded_pict->second_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_MISSING_REFERENCES))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] There are missing references for the current frame. May have corruption", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + /* + * This is not a serious error, indicate host app to drop the + * frame as may have corruption. + */ + picture->dec_pict_info->err_flags |= + VDEC_ERROR_MISSING_REFERENCES; + } + + if ((decoded_pict->first_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_MMCO_ERROR)) || + (decoded_pict->second_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_MMCO_ERROR))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] MMCO error accured when processing the current frame. May have corruption", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + + /* + * This is not a serious error, indicate host app to drop + * the frame as may have corruption. + */ + picture->dec_pict_info->err_flags |= VDEC_ERROR_MMCO; + } + + if ((decoded_pict->first_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_MBS_DROPPED_ERROR)) || + (decoded_pict->second_fld_fwmsg->pict_attrs.fe_err & + FLAG_MASK(VDECFW_MSGFLAG_DECODED_MBS_DROPPED_ERROR))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Some macroblocks were dropped when processing the current frame. May have corruption", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + + /* + * This is not a serious error, indicate host app to + * drop the frame as may have corruption. + */ + picture->dec_pict_info->err_flags |= VDEC_ERROR_MBS_DROPPED; + } + + if (decoded_pict->first_fld_fwmsg->pict_attrs.no_be_wdt > 0) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Back-end HW processing timed-out. Aborted slices %d", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id, + decoded_pict->first_fld_fwmsg->pict_attrs.no_be_wdt); + picture->dec_pict_info->err_flags |= VDEC_ERROR_BEHW_TIMEOUT; + } + + if (decoded_pict->second_fld_fwmsg->pict_attrs.no_be_wdt > 0) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Back-end HW processing timed-out. Aborted slices %d", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id, + decoded_pict->second_fld_fwmsg->pict_attrs.no_be_wdt); + picture->dec_pict_info->err_flags |= VDEC_ERROR_BEHW_TIMEOUT; + } + +#ifdef ERROR_CONCEALMENT + /* Estimate error level in percentage */ + if (decoder_get_pict_processing_info(dec_core_ctx, dec_str_ctx, pict_hdrinfo, + decoded_pict, dec_pict, &pict_last_mb) == TRUE) { + if (pict_last_mb) { + first_field_err_level = 100 - ((100 * (pict_last_mb - + decoded_pict->first_fld_fwmsg->pict_attrs.mbs_dropped + + decoded_pict->first_fld_fwmsg->pict_attrs.mbs_recovered)) / + pict_last_mb); + + second_field_err_level = 100 - ((100 * (pict_last_mb - + decoded_pict->second_fld_fwmsg->pict_attrs.mbs_dropped + + decoded_pict->second_fld_fwmsg->pict_attrs.mbs_recovered)) / + pict_last_mb); + } + + /* does not work properly with discontinuous mbs */ + if (!pict_hdrinfo->discontinuous_mbs) + picture->dec_pict_info->err_level = first_field_err_level > + second_field_err_level ? + first_field_err_level : second_field_err_level; + + VDEC_ASSERT(picture->dec_pict_info->err_level <= 100); + if (picture->dec_pict_info->err_level) + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Picture error level: %d(%%)", + dec_str_ctx->config.user_str_id, decoded_pict->transaction_id, + picture->dec_pict_info->err_level); + } +#endif + + if (decoded_pict->first_fld_fwmsg->pict_attrs.pict_attrs.dwrfired || + decoded_pict->second_fld_fwmsg->pict_attrs.pict_attrs.dwrfired) { + pr_warn("[USERSID=0x%08X] VXD Device Reset (Lockup).", + dec_str_ctx->config.user_str_id); + picture->dec_pict_info->err_flags |= + VDEC_ERROR_SERVICE_TIMER_EXPIRY; + } + + if (decoded_pict->first_fld_fwmsg->pict_attrs.pict_attrs.mmufault || + decoded_pict->second_fld_fwmsg->pict_attrs.pict_attrs.mmufault) { + pr_warn("[USERSID=0x%08X] VXD Device Reset (MMU fault).", + dec_str_ctx->config.user_str_id); + picture->dec_pict_info->err_flags |= VDEC_ERROR_MMU_FAULT; + } + + if (decoded_pict->first_fld_fwmsg->pict_attrs.pict_attrs.deverror || + decoded_pict->second_fld_fwmsg->pict_attrs.pict_attrs.deverror) { + pr_warn("[USERSID=0x%08X] VXD Device Error (e.g. firmware load failed).", + dec_str_ctx->config.user_str_id); + picture->dec_pict_info->err_flags |= VDEC_ERROR_DEVICE; + } + + /* + * Assigned error flag from the decoder error flag for error recovery. + */ + error_flag = picture->dec_pict_info->err_flags; + /* + * Loop over references, for each one find the related picture + * on the decPictList, and propagate errors if needed + */ + ret = + decoder_check_ref_errors(dec_str_ctx, (struct vdecfw_buffer_control *) + decoded_pict->pict_ref_res->fw_ctrlbuf.cpu_virt, + picture); + VDEC_ASSERT(ret == IMG_SUCCESS); + + if (dec_str_ctx->config.vid_std == VDEC_STD_H264) { + /* Attach the supplementary data to the decoded picture. */ + picture->dec_pict_sup_data.raw_vui_data = + pict_hdrinfo->h264_pict_hdr_info.raw_vui_data; + pict_hdrinfo->h264_pict_hdr_info.raw_vui_data = NULL; + + picture->dec_pict_sup_data.raw_sei_list_first_fld = + pict_hdrinfo->h264_pict_hdr_info.raw_sei_data_list_first_field; + pict_hdrinfo->h264_pict_hdr_info.raw_sei_data_list_first_field = NULL; + + picture->dec_pict_sup_data.raw_sei_list_second_fld = + pict_hdrinfo->h264_pict_hdr_info.raw_sei_data_list_second_field; + pict_hdrinfo->h264_pict_hdr_info.raw_sei_data_list_second_field = NULL; + + picture->dec_pict_sup_data.h264_pict_supl_data.nal_ref_idc = + pict_hdrinfo->h264_pict_hdr_info.nal_ref_idc; + + picture->dec_pict_sup_data.h264_pict_supl_data.frame_num = + pict_hdrinfo->h264_pict_hdr_info.frame_num; + } + +#ifdef HAS_HEVC + if (dec_str_ctx->config.vid_std == VDEC_STD_HEVC) { + /* Attach the supplementary data to the decoded picture. */ + picture->dec_pict_sup_data.raw_vui_data = + pict_hdrinfo->hevc_pict_hdr_info.raw_vui_data; + + pict_hdrinfo->hevc_pict_hdr_info.raw_vui_data = NULL; + + picture->dec_pict_sup_data.raw_sei_list_first_fld = + pict_hdrinfo->hevc_pict_hdr_info.raw_sei_datalist_firstfield; + + pict_hdrinfo->hevc_pict_hdr_info.raw_sei_datalist_firstfield = NULL; + + picture->dec_pict_sup_data.raw_sei_list_second_fld = + pict_hdrinfo->hevc_pict_hdr_info.raw_sei_datalist_secondfield; + + pict_hdrinfo->hevc_pict_hdr_info.raw_sei_datalist_secondfield = NULL; + + picture->dec_pict_sup_data.hevc_pict_supl_data.pic_order_cnt = + buf_control->hevc_data.pic_order_count; + } +#endif + + if (!((buf_control->dec_pict_type == IMG_BUFFERTYPE_PAIR && + VDECFW_PICMGMT_FIELD_CODED_PICTURE_EXECUTED(buf_control->picmgmt_flags)) || + FLAG_IS_SET(buf_control->picmgmt_flags, VDECFW_PICMGMTFLAG_PICTURE_EXECUTED))) { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Picture management was not executed for this picture; forcing display.", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + decoded_pict->force_display = TRUE; + } + + dec_str_ctx->dec_str_st.total_pict_finished++; + + /* + * Use NextPictIdExpected to do this check. ui32NextPictId could be + * different from what expected at this point because we failed to + * process a picture the last time run this function (this is still + * an error (unless doing multi-core) but not the error reported here. + */ + if (picture->pict_id != dec_str_ctx->next_pict_id_expected) { + pr_warn("[USERSID=0x%08X] ERROR: MISSING DECODED PICTURE (%d)", + dec_str_ctx->config.user_str_id, + dec_str_ctx->next_dec_pict_id); + } + + dec_str_ctx->next_dec_pict_id = + get_next_picture_id(GET_STREAM_PICTURE_ID(decoded_pict->transaction_id)); + dec_str_ctx->next_pict_id_expected = dec_str_ctx->next_dec_pict_id; + + /* Add the picture itself to the decoded list */ + next_decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + while (next_decoded_pict && + !HAS_X_REACHED_Y(GET_STREAM_PICTURE_ID(next_decoded_pict->transaction_id), + picture->pict_id, + 1 << FWIF_NUMBITS_STREAM_PICTURE_ID, unsigned int)) { + if (next_decoded_pict != + dq_last(&dec_str_ctx->str_decd_pict_list)) + next_decoded_pict = dq_next(next_decoded_pict); + else + next_decoded_pict = NULL; + } + + if (next_decoded_pict) + dq_addbefore(next_decoded_pict, decoded_pict); + else + dq_addtail(&dec_str_ctx->str_decd_pict_list, decoded_pict); + + dec_str_ctx->dec_str_st.num_pict_decoded++; + + pr_debug("%s : number of picture decoded = %d\n" + , __func__, dec_str_ctx->dec_str_st.num_pict_decoded); + /* Process the decoded pictures in the encoded order */ + decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + VDEC_ASSERT(decoded_pict); + if (!decoded_pict) + return IMG_ERROR_UNEXPECTED_STATE; + + ret = dec_str_ctx->str_processed_cb((void *)dec_str_ctx->usr_int_data, + VXD_CB_PICT_DECODED, (void *)picture); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + /* + * Loop on the unprocessed pictures until we failed to process one + * or we have processed them all + */ + for (next_decoded_pict = decoder_next_picture(decoded_pict, + dec_str_ctx->next_dec_pict_id, + &dec_str_ctx->str_decd_pict_list); + next_decoded_pict; + next_decoded_pict = decoder_next_picture(decoded_pict, + dec_str_ctx->next_dec_pict_id, + &dec_str_ctx->str_decd_pict_list)) { + unsigned int i = 0; + struct dec_decoded_pict *display_pict = NULL; + struct dec_decoded_pict *release_pict = NULL; + unsigned char last_to_display_for_seq = FALSE; + + /* + * next_decoded_pict is used to temporarily store decoded_pict + * so that we can clear the bProcessFailed flag before + * returning + */ + decoded_pict = next_decoded_pict; + if (!decoded_pict->force_display) { + struct vdecfw_buffer_control *buf_ctrl = NULL; + + buf_ctrl = (struct vdecfw_buffer_control *) + decoded_pict->pict_ref_res->fw_ctrlbuf.cpu_virt; + + if (buf_ctrl->real_data.width && buf_ctrl->real_data.height) { + /* + * Firmware sets image size as it is in + * bitstream. + */ + picture->dec_pict_info->disp_info.disp_region.width = + buf_ctrl->real_data.width; + picture->dec_pict_info->disp_info.disp_region.height = + buf_ctrl->real_data.height; + picture->dec_pict_info->disp_info.disp_region.top_offset = 0; + picture->dec_pict_info->disp_info.disp_region.left_offset = 0; + + picture->dec_pict_info->rend_info.rend_pict_size.width = + picture->dec_pict_info->disp_info.disp_region.width; + picture->dec_pict_info->rend_info.rend_pict_size.height = + picture->dec_pict_info->disp_info.disp_region.height; + + /* + * Update encoded size with values coded in + * bitstream,so golden image can be loaded + * correctly + */ + picture->dec_pict_info->disp_info.enc_disp_region.width = + buf_ctrl->real_data.width; + picture->dec_pict_info->disp_info.enc_disp_region.height = + buf_ctrl->real_data.height; + } + + decoded_pict->pict->dec_pict_info->timestamp = + buf_ctrl->real_data.timestamp; + decoded_pict->pict->dec_pict_info->disp_info.top_fld_first = + buf_ctrl->top_field_first; + decoded_pict->pict->dec_pict_info->disp_info.top_fld_first = + buf_ctrl->top_field_first; + + decoded_pict->pict->dec_pict_info->id_for_hwcrc_chk = + GET_STREAM_PICTURE_ID(decoded_pict->transaction_id) - 1; + decoded_pict->pict->dec_pict_info->id_for_hwcrc_chk += + dec_str_ctx->dec_str_st.flds_as_frm_decodes; + + if (buf_ctrl->dec_pict_type == IMG_BUFFERTYPE_PAIR && + !buf_ctrl->second_field_of_pair) + dec_str_ctx->dec_str_st.flds_as_frm_decodes++; + + if (buf_ctrl->second_field_of_pair) { + /* + * Second field of pair is always complementary + * type to the eFirstPictTagType of the + * previous picture + */ + unsigned int prev_dec_pict_id = + get_prev_picture_id(GET_STREAM_PICTURE_ID(decoded_pict->transaction_id)); + + prev_decoded_pict = + decoder_get_decoded_pict_of_stream + (prev_dec_pict_id, + &dec_str_ctx->str_decd_pict_list); + if (prev_decoded_pict) { + fld_tag_container = + &prev_decoded_pict->pict->dec_pict_info->second_fld_tag_container; + fld_tag_container->pict_tag_param = + decoded_pict->pict->dec_pict_info->first_fld_tag_container.pict_tag_param; + + /* + * Copy the first field info in the + * proper place + */ + memcpy(&fld_tag_container->pict_hwcrc, + &first_fld_fwmsg->pict_hwcrc, + sizeof(fld_tag_container->pict_hwcrc)); + + /* + * Attach the raw SEI data list for a + * second field to a picture. + */ + prev_decoded_pict->pict->dec_pict_sup_data.raw_sei_list_second_fld = + decoded_pict->pict->dec_pict_sup_data.raw_sei_list_first_fld; + + prev_decoded_pict->pict->dec_pict_info->disp_info.top_fld_first = + buf_ctrl->top_field_first; + + /* Mark this picture as merged fields. */ + prev_decoded_pict->pict->dec_pict_sup_data.merged_flds = + TRUE; + /* Mark the picture that was merged to the previous one. */ + decoded_pict->merged = TRUE; + } else { + pr_warn("[USERSID=0x%08X] [TID 0x%08X] Failed to find decoded picture to attach tag", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); + } + } else { + /* + * Not Second-field-of-pair picture tag + * correlates its Tag to the its type by + * setting the eFirstPictTagType in the + * following way + */ + decoded_pict->pict->dec_pict_info->first_fld_tag_container.pict_type + = + buf_ctrl->dec_pict_type; + memcpy(&picture->dec_pict_info->first_fld_tag_container.pict_hwcrc, + &first_fld_fwmsg->pict_hwcrc, + sizeof + (picture->dec_pict_info->first_fld_tag_container.pict_hwcrc)); + } + + /* + * Update the id of the next picture to process. It has + * to be update always (even if we fail to process) + * This has to be a global flag because it will be + * passed in both decoder_NextPicture (and then to + * DECODER_NextDecPictContiguous inside it) + * and to the corner case check below + */ + dec_str_ctx->next_dec_pict_id = + get_next_picture_id(GET_STREAM_PICTURE_ID + (decoded_pict->transaction_id)); + /* + * Display all the picture in the list that have been + * decoded and signalled by the fw to be displayed + */ + for (i = decoded_pict->disp_idx; + i < buf_ctrl->display_list_length && + !decoded_pict->process_failed; + i++, decoded_pict->disp_idx++) { + /* + * Display picture if it has been decoded + * (i.e. in decoded list). + */ + display_pict = decoder_get_decoded_pict + (buf_ctrl->display_list[i], + &dec_str_ctx->str_decd_pict_list); + if (display_pict) { + if (FLAG_IS_SET(buf_ctrl->display_flags[i], + VDECFW_BUFFLAG_DISPLAY_FIELD_CODED) && + (!FLAG_IS_SET + (buf_ctrl->display_flags[i], + VDECFW_BUFFLAG_DISPLAY_SINGLE_FIELD))) { + display_pict->pict->dec_pict_info->buf_type = + IMG_BUFFERTYPE_PAIR; + if (FLAG_IS_SET + (buf_ctrl->display_flags[i], + VDECFW_BUFFLAG_DISPLAY_INTERLACED_FIELDS)) + display_pict->pict->dec_pict_info->interlaced_flds = + TRUE; + } else if (FLAG_IS_SET + (buf_ctrl->display_flags[i], + VDECFW_BUFFLAG_DISPLAY_FIELD_CODED) && + FLAG_IS_SET + (buf_ctrl->display_flags[i], + VDECFW_BUFFLAG_DISPLAY_SINGLE_FIELD)) { + display_pict->pict->dec_pict_info->buf_type = + FLAG_IS_SET + (buf_ctrl->display_flags[i], + VDECFW_BUFFLAG_DISPLAY_BOTTOM_FIELD) ? + IMG_BUFFERTYPE_FIELD_BOTTOM : + IMG_BUFFERTYPE_FIELD_TOP; + } else { + display_pict->pict->dec_pict_info->buf_type = + IMG_BUFFERTYPE_FRAME; + } + + display_pict->pict->dec_pict_info->view_id = + buf_ctrl->display_view_ids[i]; + + /* + * When no reference pictures are left to + * display and this is the last display + * picture in response to the last decoded + * picture, signal. + */ + if (decoded_pict->pict->last_pict_in_seq && + i == (buf_ctrl->display_list_length - 1)) + last_to_display_for_seq = TRUE; + + if (!display_pict->displayed) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] DISPLAY", + dec_str_ctx->config.user_str_id, + buf_ctrl->display_list[i]); +#endif + display_pict->displayed = TRUE; + pict_id = GET_STREAM_PICTURE_ID + (buf_ctrl->display_list[i]); + + ret = decoder_picture_display + (dec_str_ctx, pict_id, + last_to_display_for_seq); + } + } else { + /* + * In single core scenario should + * not come here. + */ + pr_warn("[USERSID=0x%08X] Failed to find decoded picture [TID = 0x%08X] to send for display", + dec_str_ctx->config.user_str_id, + buf_ctrl->display_list[i]); + } + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + /* Release all unused pictures (firmware request) */ + for (i = decoded_pict->rel_idx; + i < buf_ctrl->release_list_length && + !decoded_pict->process_failed; + i++, decoded_pict->rel_idx++) { + release_pict = decoder_get_decoded_pict + (buf_ctrl->release_list[i], + &dec_str_ctx->str_decd_pict_list); + if (release_pict) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] RELEASE( ): PIC_ID[%d]", + dec_str_ctx->config.user_str_id, + release_pict->pict->pict_id); +#endif + /* + * Signal releasing this picture to upper + * layers. + */ + decoder_picture_release(dec_str_ctx, + GET_STREAM_PICTURE_ID + (buf_ctrl->release_list[i]), + release_pict->displayed, + release_pict->merged); + if (release_pict->processed) { + /* + * If the decoded picture has been + * processed, destroy now. + */ + ret = decoder_decoded_picture_destroy(dec_str_ctx, + release_pict, + FALSE); + } else { + /* + * If the decoded picture is not + * processed just destroy the + * containing picture. + */ + pict_id = GET_STREAM_PICTURE_ID + (buf_ctrl->release_list[i]); + ret = decoder_picture_destroy(dec_str_ctx, + pict_id, FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + release_pict->pict = NULL; + } + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } else { + /* + * In single core scenario should not + * come here. + */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] Failed to find decoded picture [TID = 0x%08X] to release", + dec_str_ctx->config.user_str_id, + buf_ctrl->release_list[i]); +#endif + } + } + } else { + /* Always display the picture if we have no hardware */ + if (!decoded_pict->displayed) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] DISPLAY", + dec_str_ctx->config.user_str_id, + decoded_pict->transaction_id); +#endif + decoded_pict->displayed = TRUE; + ret = decoder_picture_display + (dec_str_ctx, + decoded_pict->pict->pict_id, + decoded_pict->pict->last_pict_in_seq); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + /* Always release the picture if we have no hardware */ + ret = decoder_picture_destroy(dec_str_ctx, + decoded_pict->pict->pict_id, + FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + decoded_pict->pict = NULL; + } + + /* If we have processed the current picture */ + if (!decoded_pict->process_failed) { + decoded_pict->processed = TRUE; + + /* + * If the current picture has been released then + * remove the container from the decoded list + */ + if (!decoded_pict->pict) { + /* + * Only destroy the decoded picture once it is processed + * and the fw has instructed to release the picture. + */ + ret = decoder_decoded_picture_destroy(dec_str_ctx, + decoded_pict, FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + decoded_pict = NULL; + } /* end if (decoded_pict->pict == NULL) */ + } /* end if (!decoded_pict->process_failed) */ + } /* end for */ + + /* + * Always clear the process_failed flag to ensure that this picture + * will be processed on the next function call + */ + if (decoded_pict) + decoded_pict->process_failed = FALSE; + + /* + * Go through the list of decoded pictures to check if there are any + * pictures left for displaying and that are still not displayed due + * to picture management errors. + * Get the minimum required number of picture buffers. + */ + vdecddutils_ref_pict_get_maxnum(&dec_str_ctx->config, + comseq_hdrinfo, &req_pict_num); + req_pict_num += comseq_hdrinfo->interlaced_frames ? 2 : 1; + + ret = dec_str_ctx->core_query_cb(dec_str_ctx->usr_int_data, + DECODER_CORE_GET_RES_LIMIT, + &res_limit); + + /* Start the procedure only if there is enough resources available. */ + if (res_limit >= req_pict_num) { + /* Allow for one picture buffer for display. */ + res_limit--; + + /* + * Count the number of decoded pictures that were not + * displayed yet. + */ + aux_decoded_pict = dq_first(&dec_str_ctx->str_decd_pict_list); + while (aux_decoded_pict) { + if (aux_decoded_pict->pict) { + dec_pict_num++; + if (!displayed_decoded_pict) + displayed_decoded_pict = + aux_decoded_pict; + } + if (aux_decoded_pict != + dq_last(&dec_str_ctx->str_decd_pict_list)) + aux_decoded_pict = dq_next(aux_decoded_pict); + else + aux_decoded_pict = NULL; + } + } + + /* If there is at least one not displayed picture... */ + if (displayed_decoded_pict) { + /* + * While the number of not displayed decoded pictures exceeds + * the number of maximum allowed number of pictures being held + * by VDEC... + */ + while (dec_pict_num > res_limit) { + pr_warn("[USERSID=0x%08X] Number of outstanding decoded pictures exceeded number of available pictures buffers.", + dec_str_ctx->config.user_str_id); + + if (!displayed_decoded_pict) { + VDEC_ASSERT(0); + return -EINVAL; + } + /* Find the picture with the least picture id. */ + aux_decoded_pict = dq_next(displayed_decoded_pict); + while (aux_decoded_pict) { + if (aux_decoded_pict != + dq_last(&dec_str_ctx->str_decd_pict_list)) { + if (aux_decoded_pict->pict && + aux_decoded_pict->pict->pict_id < + displayed_decoded_pict->pict->pict_id) + displayed_decoded_pict = aux_decoded_pict; + + aux_decoded_pict = dq_next(aux_decoded_pict); + } else { + if (aux_decoded_pict->pict && + aux_decoded_pict->pict->pict_id < + displayed_decoded_pict->pict->pict_id) + displayed_decoded_pict = aux_decoded_pict; + + aux_decoded_pict = NULL; + } + } + + /* Display and release the picture with the least picture id. */ + if (!displayed_decoded_pict->displayed) { + pr_warn("[USERSID=0x%08X] [TID=0x%08X] DISPLAY FORCED", + dec_str_ctx->config.user_str_id, + displayed_decoded_pict->transaction_id); + displayed_decoded_pict->displayed = TRUE; + ret = decoder_picture_display + (dec_str_ctx, + displayed_decoded_pict->pict->pict_id, + displayed_decoded_pict->pict->last_pict_in_seq); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } + + ret = decoder_picture_destroy(dec_str_ctx, + displayed_decoded_pict->pict->pict_id, + FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + displayed_decoded_pict->pict = NULL; + displayed_decoded_pict->processed = TRUE; + + ret = decoder_decoded_picture_destroy(dec_str_ctx, displayed_decoded_pict, + FALSE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + displayed_decoded_pict = NULL; + + /* + * Decrease the number of not displayed decoded + * pictures. + */ + dec_pict_num--; + } + } + +#ifdef ERROR_RECOVERY_SIMULATION + /* + * This part of the code should execute only when, DEBUG_FW_ERR_RECOVERY + * flag is enabled. This basically reads the error flag attribute from + * user space to create fake errors for testing the firmware error + * recovery. + */ + if (fw_error_value != VDEC_ERROR_MAX) { + error_flag = error_flag | (1 << fw_error_value); + /* Now lets make it VDEC_ERROR_MAX */ + fw_error_value = VDEC_ERROR_MAX; + } +#endif + + /* + * Whenever the error flag is set, we need to handle the error case. + * Need to forward this error to stream processed callback. + */ + if (error_flag) { + pr_err("%s : %d err_flags: 0x%x\n", __func__, __LINE__, error_flag); + ret = dec_str_ctx->str_processed_cb((void *)dec_str_ctx->usr_int_data, + VXD_CB_ERROR_FATAL, &error_flag); + } + /* + * check for eos on bitstream and propagate the same to picture + * buffer + */ + ctx = dec_str_ctx->vxd_dec_ctx; + ctx->num_decoding--; + if (ctx->eos) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("EOS reached\n"); +#endif + + ret = dec_str_ctx->str_processed_cb((void *)dec_str_ctx->usr_int_data, + VXD_CB_STR_END, NULL); + VDEC_ASSERT(ret == IMG_SUCCESS); + } + + return ret; +} + +/* + * @Function decoder_service_firmware_response + */ +int decoder_service_firmware_response(void *dec_str_ctx_arg, unsigned int *msg, + unsigned int msg_size, unsigned int msg_flags) +{ + int ret = IMG_SUCCESS; + struct dec_decpict *dec_pict = NULL; + unsigned char head_of_queue = TRUE; + struct dec_str_ctx *dec_str_ctx; + struct dec_str_unit *dec_str_unit; + unsigned char pict_start = FALSE; + enum vdecdd_str_unit_type str_unit_type; + struct vdecdd_picture *picture; + struct decoder_pict_fragment *pict_fragment; + struct dec_str_ctx *dec_strctx; + struct dec_core_ctx *dec_core_ctx; + + /* validate input arguments */ + if (!dec_str_ctx_arg || !msg) { + VDEC_ASSERT(0); + return IMG_ERROR_INVALID_PARAMETERS; + } + + dec_strctx = decoder_stream_get_context(dec_str_ctx_arg); + + dec_core_ctx = decoder_str_ctx_to_core_ctx(dec_strctx); + + if (!dec_core_ctx) { + pr_err("%s: dec_core_ctx is NULL\n", __func__); + VDEC_ASSERT(0); + return IMG_ERROR_INVALID_PARAMETERS; + } + + pr_debug("%s : process firmware response\n", __func__); + ret = hwctrl_process_msg(dec_core_ctx->hw_ctx, msg_flags, msg, &dec_pict); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + if (!dec_pict || (dec_pict->state != DECODER_PICTURE_STATE_DECODED && + dec_pict->state != DECODER_PICTURE_STATE_TO_DISCARD)) + return IMG_ERROR_UNEXPECTED_STATE; + + /* + * Try and locate the stream context in the list of active + * streams. + */ + VDEC_ASSERT(dec_core_ctx->dec_ctx); + dec_str_ctx = lst_first(&dec_core_ctx->dec_ctx->str_list); + if (!dec_str_ctx) { + VDEC_ASSERT(0); + return -EINVAL; + } + + while (dec_str_ctx) { + if (dec_str_ctx == dec_pict->dec_str_ctx) + break; + + dec_str_ctx = lst_next(dec_str_ctx); + } + + /* + * If the stream is not in the list of active streams then + * it must have been destroyed. + * This interrupt should be ignored. + */ + if (dec_str_ctx != dec_pict->dec_str_ctx) + return IMG_SUCCESS; + + /* + * Retrieve the picture from the head of the core decode queue + * primarily to obtain the correct stream context. + */ + hwctrl_removefrom_piclist(dec_core_ctx->hw_ctx, dec_pict); + + if (!dec_str_ctx) { + VDEC_ASSERT(0); + return -EINVAL; + } + dec_str_ctx->avail_slots++; + VDEC_ASSERT(dec_str_ctx->avail_slots > 0); + + /* + * Store the stream context of the picture that has been + * decoded. + */ + dec_str_ctx = dec_pict->dec_str_ctx; + VDEC_ASSERT(dec_str_ctx); + + if (!dec_str_ctx) + return IMG_ERROR_UNEXPECTED_STATE; + + /* + * Picture has been discarded before EOP unit, + * recover the decoder to valid state + */ + if (!dec_pict->eop_found) { + VDEC_ASSERT(dec_pict == dec_str_ctx->cur_pict); + + dec_core_ctx->busy = FALSE; + dec_str_ctx->cur_pict = NULL; + } + + /* + * Peek the first stream unit and validate against core + * queue to ensure that this really is the next picture + * for the stream. + */ + dec_str_unit = lst_first(&dec_str_ctx->pend_strunit_list); + if (dec_str_unit) { + if (dec_str_unit->dec_pict != dec_pict) { + head_of_queue = FALSE; + + /* + * For pictures to be decoded + * out-of-order there must be + * more than one decoder core. + */ + VDEC_ASSERT(dec_str_ctx->decctx->num_pipes > 1); + while (dec_str_unit) { + dec_str_unit = lst_next(dec_str_unit); + if (dec_str_unit->dec_pict == dec_pict) + break; + } + } + VDEC_ASSERT(dec_str_unit); + if (!dec_str_unit) + return IMG_ERROR_FATAL; + + VDEC_ASSERT(dec_str_unit->dec_pict == dec_pict); + VDEC_ASSERT(dec_str_unit->str_unit->str_unit_type == + VDECDD_STRUNIT_PICTURE_START); + } + + /* + * Process all units from the pending stream list until + * the next picture start. + */ + while (dec_str_unit && !pict_start) { + /* + * Actually remove the unit now from the + * pending stream list. + */ + lst_remove(&dec_str_ctx->pend_strunit_list, dec_str_unit); + if (!dec_str_unit->str_unit || !dec_pict) + break; + + str_unit_type = dec_str_unit->str_unit->str_unit_type; + + if (str_unit_type != VDECDD_STRUNIT_PICTURE_START) + break; + + dec_str_ctx = dec_pict->dec_str_ctx; + + dec_str_ctx->dec_str_st.num_pict_decoding--; + dec_str_ctx->dec_str_st.total_pict_decoded++; + + ret = idgen_gethandle(dec_str_ctx->pict_idgen, + GET_STREAM_PICTURE_ID(dec_str_unit->dec_pict->transaction_id), + (void **)&picture); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS || !picture) { + pr_err("[USERSID=0x%08X] Failed to find picture from ID", + dec_str_ctx->config.user_str_id); + return IMG_ERROR_INVALID_ID; + } + + VDEC_ASSERT(picture == dec_str_unit->str_unit->dd_pict_data); + + /* Hold a reference to the last context on the BE */ + if (dec_str_ctx->last_be_pict_dec_res && HAS_X_PASSED_Y + (picture->pict_id, + GET_STREAM_PICTURE_ID(dec_str_ctx->last_be_pict_dec_res->transaction_id), + 1 << FWIF_NUMBITS_STREAM_PICTURE_ID, unsigned int)) { + /* Return previous last FW context. */ + resource_item_return(&dec_str_ctx->last_be_pict_dec_res->ref_cnt); + + if (resource_item_isavailable(&dec_str_ctx->last_be_pict_dec_res->ref_cnt + )) { + resource_list_remove(&dec_str_ctx->dec_res_lst, + dec_str_ctx->last_be_pict_dec_res); + resource_list_add_img(&dec_str_ctx->dec_res_lst, + dec_str_ctx->last_be_pict_dec_res, 0, + &dec_str_ctx->last_be_pict_dec_res->ref_cnt); + } + } + if (!dec_str_ctx->last_be_pict_dec_res || + (dec_str_ctx->last_be_pict_dec_res && HAS_X_PASSED_Y + (picture->pict_id, + GET_STREAM_PICTURE_ID(dec_str_ctx->last_be_pict_dec_res->transaction_id), + 1 << FWIF_NUMBITS_STREAM_PICTURE_ID, unsigned int))) { + /* Hold onto last FW context. */ + dec_str_ctx->last_be_pict_dec_res = dec_pict->cur_pict_dec_res; + resource_item_use(&dec_str_ctx->last_be_pict_dec_res->ref_cnt); + } + resource_item_return(&dec_pict->cur_pict_dec_res->ref_cnt); + + if (resource_item_isavailable(&dec_pict->cur_pict_dec_res->ref_cnt)) { + resource_list_remove(&dec_str_ctx->dec_res_lst, + dec_pict->cur_pict_dec_res); + resource_list_add_img(&dec_str_ctx->dec_res_lst, + dec_pict->cur_pict_dec_res, 0, + &dec_pict->cur_pict_dec_res->ref_cnt); + } +#ifdef DEBUG_DECODER_DRIVER + pr_info("[USERSID=0x%08X] [TID=0x%08X] DECODED", + dec_str_ctx->config.user_str_id, + dec_pict->transaction_id); +#endif + + ret = decoder_picture_decoded(dec_str_ctx, dec_core_ctx, + picture, dec_pict, + dec_pict->pict_hdr_info, + dec_str_unit->str_unit); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dec_res_picture_detach(&dec_str_ctx->resources, dec_pict); + + /* Free the segments from the decode picture */ + decoder_clean_bitstr_segments(&dec_pict->dec_pict_seg_list); + + pict_fragment = lst_removehead(&dec_pict->fragment_list); + while (pict_fragment) { + kfree(pict_fragment); + pict_fragment = + lst_removehead(&dec_pict->fragment_list); + } + + pict_start = (!head_of_queue) ? TRUE : FALSE; + + ret = dec_str_ctx->str_processed_cb(dec_str_ctx->usr_int_data, + VXD_CB_STRUNIT_PROCESSED, + dec_str_unit->str_unit); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) { + /* Free decoder picture */ + kfree(dec_pict); + dec_pict = NULL; + return ret; + } + + /* Destroy the Decoder stream unit wrapper */ + kfree(dec_str_unit); + + /* Peek at the next stream unit */ + dec_str_unit = lst_first(&dec_str_ctx->pend_strunit_list); + if (dec_str_unit) + pict_start = (dec_str_unit->str_unit->str_unit_type == + VDECDD_STRUNIT_PICTURE_START && + dec_str_unit->dec_pict != dec_pict); + + /* Free decoder picture */ + kfree(dec_pict); + dec_pict = NULL; + } + + kfree(dec_str_unit); + return ret; +} + +/* + * @Function decoder_is_stream_idle + */ +unsigned char decoder_is_stream_idle(void *dec_str_ctx_handle) +{ + struct dec_str_ctx *dec_str_ctx; + + dec_str_ctx = decoder_stream_get_context(dec_str_ctx_handle); + VDEC_ASSERT(dec_str_ctx); + if (!dec_str_ctx) { + pr_err("Invalid decoder stream context handle!"); + return FALSE; + } + + return lst_empty(&dec_str_ctx->pend_strunit_list); +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/decoder.h b/drivers/media/platform/vxe-vxd/decoder/decoder.h --- a/drivers/media/platform/vxe-vxd/decoder/decoder.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/decoder.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,375 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Decoder Component header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef __DECODER_H__ +#define __DECODER_H__ + +#include "bspp.h" +#include "dq.h" +#ifdef HAS_JPEG +#include "jpegfw_data.h" +#endif +#include "lst.h" +#include "vdecdd_defs.h" +#include "vdec_defs.h" +#include "vid_buf.h" +#include "vxd_ext.h" +#include "vxd_props.h" +#include "hevcfw_data.h" + +#define MAX_CONCURRENT_STREAMS 16 + +enum dec_pict_states { + DECODER_PICTURE_STATE_TO_DECODE = 0, + DECODER_PICTURE_STATE_DECODED, + DECODER_PICTURE_STATE_TO_DISCARD, + DECODER_PICTURE_STATE_MAX, + DECODER_PICTURE_FORCE32BITS = 0x7FFFFFFFU +}; + +enum dec_res_type { + DECODER_RESTYPE_TRANSACTION = 0, + DECODER_RESTYPE_HDR, + DECODER_RESTYPE_BATCH_MSG, +#ifdef HAS_HEVC + DECODER_RESTYPE_PVDEC_BUF, +#endif + DECODER_RESTYPE_MAX, + DECODER_RESTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +enum dec_core_query_type { + DECODER_CORE_GET_RES_LIMIT = 0, + DECODER_CORE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * @Function pfnRefPicGetMaxNum + * @Description + * This is the prototype for functions calculating the maximum number + * of reference pictures required per video standard. + * + * @Input psComSequHdrInfo : A pointer to the common VSH information + * structure. + * + * @Output pui32MaxRefPicNum : A pointer used to return the maximum number + * of reference frames required. + * + * @Return IMG_RESULT : This function returns either IMG_SUCCESS or + * an error code. + */ +typedef int (*ref_pic_get_maximum)(const struct vdec_comsequ_hdrinfo *comseq_hdr_info, + unsigned int *max_ref_pict_num); + +typedef int (*strunit_processed_cb)(void *handle, int cb_type, void *item); + +typedef int (*core_gen_cb)(void *handle, int query, void *item); + +struct dec_ctx; + +/* + * This structure contains the core context. + * @brief Decoder Core Context + */ +struct dec_core_ctx { + void **link; /* to be part of single linked list */ + struct dec_ctx *dec_ctx; + unsigned char enumerated; + unsigned char master; + unsigned char configured; + unsigned int core_features; + unsigned int pipe_features[VDEC_MAX_PIXEL_PIPES]; + struct vxd_coreprops core_props; + void *resources; + void *hw_ctx; + unsigned int cum_pics; + unsigned char busy; +}; + +struct dec_ctx { + unsigned char inited; + void *user_data; + const struct vdecdd_dd_devconfig *dev_cfg; + unsigned int num_pipes; + struct dec_core_ctx *dec_core_ctx; + struct lst_t str_list; + void *mmu_dev_handle; + void *dev_handle; + struct vidio_ddbufinfo ptd_buf_info; + unsigned char sup_stds[VDEC_STD_MAX]; + unsigned int internal_heap_id; + unsigned int str_cnt; +}; + +/* + * This structure contains the device decode resource (used for decoding and + * held for subsequent decoding). + * @brief Decoder Device Resource + */ +struct dec_pictdec_res { + void **link; /* to be part of single linked list */ + unsigned int transaction_id; + struct vidio_ddbufinfo fw_ctx_buf; + struct vidio_ddbufinfo h264_sgm_buf; + unsigned int ref_cnt; +}; + +struct dec_decpict; + +/* + * + * This structure contains the stream context. + * @brief Decoder Stream Context + */ +struct dec_str_ctx { + void **link; /* to be part of single linked list */ + int km_str_id; + struct vdec_str_configdata config; + struct dec_ctx *decctx; + void *vxd_dec_ctx; + void *usr_int_data; + void *mmu_str_handle; + void *pict_idgen; + struct lst_t pend_strunit_list; + struct dq_linkage_t str_decd_pict_list; + unsigned int num_ref_res; + struct lst_t ref_res_lst; + unsigned int num_dec_res; + struct lst_t dec_res_lst; + unsigned int avail_pipes; + unsigned int avail_slots; + struct vdecdd_decstr_status dec_str_st; + struct vidio_ddbufinfo pvdec_fw_ctx_buf; + unsigned int last_fe_transaction_id; + unsigned int next_dec_pict_id; + unsigned int next_pict_id_expected; + struct dec_pictdec_res *cur_fe_pict_dec_res; + struct dec_pictdec_res *prev_fe_pict_dec_res; + struct dec_pictdec_res *last_be_pict_dec_res; + struct dec_decpict *cur_pict; + void *resources; + strunit_processed_cb str_processed_cb; + core_gen_cb core_query_cb; +}; + +/* + * Resource Structure for DECODER_sDdResourceInfo to be used with pools + */ +struct res_resinfo { + void **link; /* to be part of single linked list */ + void *res; + struct vidio_ddbufinfo *ddbuf_info; +}; + +struct vdecdd_ddstr_ctx; + +/* + * This structure contains the Decoded attributes + * @brief Decoded attributes + */ +struct dec_pict_attrs { + unsigned char first_fld_rcvd; + unsigned int fe_err; + unsigned int no_be_wdt; + unsigned int mbs_dropped; + unsigned int mbs_recovered; + struct vxd_pict_attrs pict_attrs; +}; + +/* + * This union contains firmware contexts. Used to allocate buffers for firmware + * context. + */ +union dec_fw_contexts { + struct h264fw_context_data h264_context; +#ifdef HAS_JPEG + struct jpegfw_context_data jpeg_context; +#endif +#ifdef HAS_HEVC + struct hevcfw_ctx_data hevc_context; +#endif +}; + +/* + * for debug + */ +struct dec_fwmsg { + void **link; + struct dec_pict_attrs pict_attrs; + struct vdec_pict_hwcrc pict_hwcrc; +}; + +/* + * This structure contains the stream decode resource (persistent for + * longer than decoding). + * @brief Decoder Stream Resource + */ +struct dec_pictref_res { + void **link; /* to be part of single linked list */ + struct vidio_ddbufinfo fw_ctrlbuf; + unsigned int ref_cnt; +}; + +/* + * This structure defines the decode picture. + * @brief Decoder Picture + */ +struct dec_decpict { + void **link; + unsigned int transaction_id; + void *dec_str_ctx; + unsigned char twopass; + unsigned char first_fld_rcvd; + struct res_resinfo *transaction_info; + struct res_resinfo *hdr_info; +#ifdef HAS_HEVC + struct res_resinfo *pvdec_info; + unsigned int temporal_out_addr; +#endif + struct vdecdd_ddpict_buf *recon_pict; + struct vdecdd_ddpict_buf *alt_pict; + struct res_resinfo *batch_msginfo; + struct vidio_ddbufinfo *intra_bufinfo; + struct vidio_ddbufinfo *auxline_bufinfo; + struct vidio_ddbufinfo *vlc_tables_bufinfo; + struct vidio_ddbufinfo *vlc_idx_tables_bufinfo; + struct vidio_ddbufinfo *start_code_bufinfo; + struct dec_fwmsg *first_fld_fwmsg; + struct dec_fwmsg *second_fld_fwmsg; + struct bspp_pict_hdr_info *pict_hdr_info; + struct dec_pictdec_res *cur_pict_dec_res; + struct dec_pictdec_res *prev_pict_dec_res; + struct dec_pictref_res *pict_ref_res; + struct lst_t dec_pict_seg_list; + struct lst_t fragment_list; + unsigned char eop_found; + unsigned int operating_op; + unsigned short genc_id; + struct vdecdd_ddbuf_mapinfo **genc_bufs; + struct vdecdd_ddbuf_mapinfo *genc_fragment_buf; + unsigned int ctrl_alloc_bytes; + unsigned int ctrl_alloc_offset; + enum dec_pict_states state; + struct vidio_ddbufinfo *str_pvdec_fw_ctxbuf; +}; + +/* + * + * This structure defines the decode picture reference. + * @brief Decoder Picture Reference + */ +struct dec_str_unit { + void **link; /* to be part of single linked list */ + struct dec_decpict *dec_pict; + struct vdecdd_str_unit *str_unit; +}; + +/* + * This structure defines the decoded picture. + * @brief Decoded Picture + */ +struct dec_decoded_pict { + struct dq_linkage_t link; /* to be part of double linked list */ + unsigned int transaction_id; + unsigned char processed; + unsigned char process_failed; + unsigned char force_display; + unsigned char displayed; + unsigned char merged; + unsigned int disp_idx; + unsigned int rel_idx; + struct vdecdd_picture *pict; + struct dec_fwmsg *first_fld_fwmsg; + struct dec_fwmsg *second_fld_fwmsg; + struct dec_pictref_res *pict_ref_res; +}; + +struct dec_pict_fragment { + void **link; /* to be part of single linked list */ + /* Control allocation size in bytes */ + unsigned int ctrl_alloc_bytes; + /* Control allocation offset in bytes */ + unsigned int ctrl_alloc_offset; +}; + +/* + * This structure contains the pointer to the picture segment. + * All the segments could be added to the list in struct dec_decpict, + * but because list items cannot belong to more than one list this wrapper + * is used which is added in the list sDecPictSegList inside struct dec_decpict + * @brief Decoder Picture Segment + */ +struct dec_decpict_seg { + void **link; /* to be part of single linked list */ + struct bspp_bitstr_seg *bstr_seg; + unsigned char internal_seg; +}; + +struct decoder_regsoffsets { + unsigned int vdmc_cmd_offset; + unsigned int vec_offset; + unsigned int entropy_offset; + unsigned int vec_be_regs_offset; + unsigned int vdec_be_codec_regs_offset; +}; + +int decoder_initialise(void *init_usr_data, unsigned int internal_heap_id, + struct vdecdd_dd_devconfig *dd_devcfg, unsigned int *num_pipes, + void **dec_ctx); + +int decoder_deinitialise(void *dec_ctx); + +int decoder_supported_features(void *dec_ctx, struct vdec_features *features); + +int decoder_stream_destroy(void *dec_str_ctx, unsigned char abort); + +int decoder_stream_create(void *dec_ctx, struct vdec_str_configdata str_cfg, + unsigned int kmstr_id, void **mmu_str_handle, + void *vxd_dec_ctx, void *str_usr_int_data, + void **dec_str_ctx, void *decoder_cb, void *query_cb); + +int decoder_stream_prepare_ctx(void *dec_str_ctx, unsigned char flush_dpb); + +int decoder_stream_process_unit(void *dec_str_ctx, + struct vdecdd_str_unit *str_unit); + +int decoder_get_load(void *dec_str_ctx, unsigned int *avail_slots); + +int +decoder_check_support(void *dec_ctx, + const struct vdec_str_configdata *str_cfg, + const struct vdec_str_opconfig *op_cfg, + const struct vdecdd_ddpict_buf *disp_pictbuf, + const struct vdec_pict_rendinfo *req_pict_rendinfo, + const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + const struct bspp_pict_hdr_info *pict_hdrinfo, + const struct vdec_comsequ_hdrinfo *prev_comseq_hdrinfo, + const struct bspp_pict_hdr_info *prev_pict_hdrinfo, + unsigned char non_cfg_req, struct vdec_unsupp_flags *unsupported, + unsigned int *features); + +unsigned char decoder_is_stream_idle(void *dec_str_ctx); + +int decoder_stream_flush(void *dec_str_ctx, unsigned char discard_refs); + +int decoder_stream_release_buffers(void *dec_str_ctx); + +int decoder_stream_get_status(void *dec_str_ctx, + struct vdecdd_decstr_status *dec_str_st); + +int decoder_service_firmware_response(void *dec_str_ctx_arg, unsigned int *msg, + unsigned int msg_size, unsigned int msg_flags); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/dec_resources.c b/drivers/media/platform/vxe-vxd/decoder/dec_resources.c --- a/drivers/media/platform/vxe-vxd/decoder/dec_resources.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/dec_resources.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD Decoder resource allocation and tracking function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include + +#include "decoder.h" +#include "dec_resources.h" +#include "hw_control.h" +#include "h264fw_data.h" +#include "h264_idx.h" +#include "h264_vlc.h" +#include "img_mem.h" +#include "pool_api.h" +#include "vdecdd_utils.h" +#include "vdec_mmu_wrapper.h" +#include "vid_buf.h" +#include "vxd_mmu_defs.h" + +#define DECODER_END_BYTES_SIZE 40 + +#define BATCH_MSG_BUFFER_SIZE (8 * 4096) +#define INTRA_BUF_SIZE (1024 * 32) +#define AUX_LINE_BUFFER_SIZE (512 * 1024) + +static void decres_pack_vlc_tables(unsigned short *packed, + unsigned short *unpacked, + unsigned short size) +{ + unsigned short i, j; + + for (i = 0; i < size; i++) { + j = i * 3; + /* + * opcode 14:12 + * width 11:9 + * symbol 8:0 + */ + packed[i] = 0 | ((unpacked[j]) << 12) | + ((unpacked[j + 1]) << 9) | (unpacked[j + 2]); + } +} + +struct dec_vlctable { + void *data; + unsigned int num_entries; + void *index_table; + unsigned int num_tables; +}; + +/* + * Union with sizes of firmware parser header structure sizes. Dec_resources + * uses the largest to allocate the header buffer. + */ +union decres_fw_hdrs { + struct h264fw_header_data h264_header; +}; + +/* + * This array contains the size of each resource allocation. + * @brief Resource Allocation Sizes + * NOTE: This should be kept in step with #DECODER_eResType. + */ +static const unsigned int res_size[DECODER_RESTYPE_MAX] = { + sizeof(struct vdecfw_transaction), + sizeof(union decres_fw_hdrs), + BATCH_MSG_BUFFER_SIZE, +#ifdef HAS_HEVC + MEM_TO_REG_BUF_SIZE + SLICE_PARAMS_BUF_SIZE + ABOVE_PARAMS_BUF_SIZE, +#endif +}; + +static const unsigned char start_code[] = { + 0x00, 0x00, 0x01, 0x00, +}; + +static void decres_get_vlc_data(struct dec_vlctable *vlc_table, + enum vdec_vid_std vid_std) +{ + switch (vid_std) { + case VDEC_STD_H264: + vlc_table->data = h264_vlc_table_data; + vlc_table->num_entries = h264_vlc_table_size; + vlc_table->index_table = h264_vlc_index_data; + vlc_table->num_tables = h264_vlc_index_size; + break; + + default: + memset(vlc_table, 0x0, sizeof(*vlc_table)); + break; + } +} + +static void decres_fnbuf_info_destructor(void *param, void *cb_handle) +{ + struct vidio_ddbufinfo *dd_bufinfo = (struct vidio_ddbufinfo *)param; + int ret; + void *mmu_handle = cb_handle; + + VDEC_ASSERT(dd_bufinfo); + + ret = mmu_free_mem(mmu_handle, dd_bufinfo); + VDEC_ASSERT(ret == IMG_SUCCESS); + + kfree(dd_bufinfo); + dd_bufinfo = NULL; +} + +int dec_res_picture_detach(void **res_ctx, struct dec_decpict *dec_pict) +{ + struct dec_res_ctx *local_res_ctx; + + VDEC_ASSERT(res_ctx); + VDEC_ASSERT(res_ctx && *res_ctx); + VDEC_ASSERT(dec_pict); + VDEC_ASSERT(dec_pict && dec_pict->transaction_info); + + if (!res_ctx || !(*res_ctx) || !dec_pict || + !dec_pict->transaction_info) { + pr_err("Invalid parameters\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + local_res_ctx = (struct dec_res_ctx *)*res_ctx; + + /* return transaction buffer */ + lst_add(&local_res_ctx->pool_data_list[DECODER_RESTYPE_TRANSACTION], + dec_pict->transaction_info); + pool_resfree(dec_pict->transaction_info->res); + + /* return picture header information buffer */ + lst_add(&local_res_ctx->pool_data_list[DECODER_RESTYPE_HDR], + dec_pict->hdr_info); + pool_resfree(dec_pict->hdr_info->res); + + /* return batch message buffer */ + lst_add(&local_res_ctx->pool_data_list[DECODER_RESTYPE_BATCH_MSG], + dec_pict->batch_msginfo); + pool_resfree(dec_pict->batch_msginfo->res); + +#ifdef HAS_HEVC + if (dec_pict->pvdec_info) { + lst_add(&local_res_ctx->pool_data_list[DECODER_RESTYPE_PVDEC_BUF], + dec_pict->pvdec_info); + pool_resfree(dec_pict->pvdec_info->res); + } +#endif + + return IMG_SUCCESS; +} + +static int decres_get_resource(struct dec_res_ctx *res_ctx, + enum dec_res_type res_type, + struct res_resinfo **res_info, + unsigned char fill_zeros) +{ + struct res_resinfo *local_res_info = NULL; + unsigned int ret = IMG_SUCCESS; + + VDEC_ASSERT(res_ctx); + VDEC_ASSERT(res_info); + + local_res_info = lst_removehead(&res_ctx->pool_data_list[res_type]); + VDEC_ASSERT(local_res_info); + if (local_res_info) { + VDEC_ASSERT(local_res_info->ddbuf_info); + if (local_res_info->ddbuf_info) { + ret = pool_resalloc(res_ctx->res_pool[res_type], local_res_info->res); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) { + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + return ret; + } + + if (fill_zeros) + memset(local_res_info->ddbuf_info->cpu_virt, 0, + local_res_info->ddbuf_info->buf_size); + + *res_info = local_res_info; + } else { + ret = IMG_ERROR_FATAL; + return ret; + } + } else { + ret = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + return ret; + } + + return ret; +} + +int dec_res_picture_attach(void **res_ctx, enum vdec_vid_std vid_std, + struct dec_decpict *dec_pict) +{ + struct dec_res_ctx *local_res_ctx; + int ret; + + VDEC_ASSERT(res_ctx); + VDEC_ASSERT(res_ctx && *res_ctx); + VDEC_ASSERT(dec_pict); + if (!res_ctx || !(*res_ctx) || !dec_pict) { + pr_err("Invalid parameters"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + local_res_ctx = (struct dec_res_ctx *)*res_ctx; + + /* Obtain transaction buffer. */ + ret = decres_get_resource(local_res_ctx, DECODER_RESTYPE_TRANSACTION, + &dec_pict->transaction_info, TRUE); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + /* Obtain picture header information buffer */ + ret = decres_get_resource(local_res_ctx, DECODER_RESTYPE_HDR, + &dec_pict->hdr_info, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + +#ifdef HAS_HEVC + /* Obtain HEVC buffer */ + if (vid_std == VDEC_STD_HEVC) { + ret = decres_get_resource(local_res_ctx, DECODER_RESTYPE_PVDEC_BUF, + &dec_pict->pvdec_info, TRUE); + + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + } +#endif + /* Obtain picture batch message buffer */ + ret = decres_get_resource(local_res_ctx, DECODER_RESTYPE_BATCH_MSG, + &dec_pict->batch_msginfo, TRUE); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + dec_pict->intra_bufinfo = &local_res_ctx->intra_bufinfo; + dec_pict->auxline_bufinfo = &local_res_ctx->auxline_bufinfo; + dec_pict->vlc_tables_bufinfo = + &local_res_ctx->vlc_tables_bufinfo[vid_std]; + dec_pict->vlc_idx_tables_bufinfo = + &local_res_ctx->vlc_idxtables_bufinfo[vid_std]; + dec_pict->start_code_bufinfo = &local_res_ctx->start_code_bufinfo; + + return IMG_SUCCESS; +} + +int dec_res_create(void *mmu_handle, struct vxd_coreprops *core_props, + unsigned int num_dec_slots, + unsigned int mem_heap_id, void **resources) +{ + struct dec_res_ctx *local_res_ctx; + int ret; + unsigned int i = 0; + struct dec_vlctable vlc_table; + enum sys_emem_attrib mem_attrib; + + VDEC_ASSERT(core_props); + VDEC_ASSERT(resources); + if (!core_props || !resources) { + pr_err("Invalid parameters"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + mem_attrib = (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE); + mem_attrib |= (enum sys_emem_attrib)SYS_MEMATTRIB_INTERNAL; + + local_res_ctx = kzalloc(sizeof(*local_res_ctx), GFP_KERNEL); + VDEC_ASSERT(local_res_ctx); + if (!local_res_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Allocate Intra buffer. */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d call MMU_StreamMalloc", __func__, __LINE__); +#endif + + ret = mmu_stream_alloc(mmu_handle, MMU_HEAP_STREAM_BUFFERS, mem_heap_id, + mem_attrib, + core_props->num_pixel_pipes * + INTRA_BUF_SIZE * 3, + DEV_MMU_PAGE_ALIGNMENT, + &local_res_ctx->intra_bufinfo); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Allocate aux line buffer. */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d call MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(mmu_handle, MMU_HEAP_STREAM_BUFFERS, mem_heap_id, + mem_attrib, + AUX_LINE_BUFFER_SIZE * 3 * + core_props->num_pixel_pipes, + DEV_MMU_PAGE_ALIGNMENT, + &local_res_ctx->auxline_bufinfo); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + /* Allocate standard-specific buffers. */ + for (i = VDEC_STD_UNDEFINED + 1; i < VDEC_STD_MAX; i++) { + decres_get_vlc_data(&vlc_table, (enum vdec_vid_std)i); + + if (vlc_table.num_tables > 0) { + /* + * Size of VLC IDX table in bytes. Has to be aligned + * to 4, so transfer to MTX succeeds. + * (VLC IDX is copied to local RAM of MTX) + */ + unsigned int vlc_idxtable_sz = + ALIGN((sizeof(unsigned short) * vlc_table.num_tables * 3), 4); + +#ifdef DEBUG_DECODER_DRIVER + pr_info(" %s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + + ret = mmu_stream_alloc(mmu_handle, + MMU_HEAP_STREAM_BUFFERS, + mem_heap_id, (enum sys_emem_attrib)(mem_attrib | + SYS_MEMATTRIB_CORE_READ_ONLY | + SYS_MEMATTRIB_CPU_WRITE), + sizeof(unsigned short) * vlc_table.num_entries, + DEV_MMU_PAGE_ALIGNMENT, + &local_res_ctx->vlc_tables_bufinfo[i]); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + if (vlc_table.data) + decres_pack_vlc_tables + (local_res_ctx->vlc_tables_bufinfo[i].cpu_virt, + vlc_table.data, + vlc_table.num_entries); + + /* VLC index table */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", + __func__, __LINE__); +#endif + ret = mmu_stream_alloc(mmu_handle, + MMU_HEAP_STREAM_BUFFERS, + mem_heap_id, (enum sys_emem_attrib)(mem_attrib | + SYS_MEMATTRIB_CORE_READ_ONLY | + SYS_MEMATTRIB_CPU_WRITE), + vlc_idxtable_sz, + DEV_MMU_PAGE_ALIGNMENT, + &local_res_ctx->vlc_idxtables_bufinfo[i]); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + if (vlc_table.index_table) + memcpy(local_res_ctx->vlc_idxtables_bufinfo[i].cpu_virt, + vlc_table.index_table, + local_res_ctx->vlc_idxtables_bufinfo[i].buf_size); + } + } + + /* Start code */ +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(mmu_handle, MMU_HEAP_STREAM_BUFFERS, mem_heap_id, + (enum sys_emem_attrib)(mem_attrib | + SYS_MEMATTRIB_CORE_READ_ONLY | + SYS_MEMATTRIB_CPU_WRITE), + sizeof(start_code), + DEV_MMU_PAGE_ALIGNMENT, + &local_res_ctx->start_code_bufinfo); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + memcpy(local_res_ctx->start_code_bufinfo.cpu_virt, start_code, sizeof(start_code)); + + for (i = 0; i < DECODER_RESTYPE_MAX; i++) { + unsigned int j; + + ret = pool_api_create(&local_res_ctx->res_pool[i]); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error; + + lst_init(&local_res_ctx->pool_data_list[i]); + + for (j = 0; j < num_dec_slots; j++) { + struct res_resinfo *local_res_info; + + local_res_info = kzalloc(sizeof(*local_res_info), GFP_KERNEL); + + VDEC_ASSERT(local_res_info); + if (!local_res_info) { + pr_err("Failed to allocate memory\n"); + ret = IMG_ERROR_OUT_OF_MEMORY; + goto error_local_res_info_alloc; + } + + local_res_info->ddbuf_info = kzalloc(sizeof(*local_res_info->ddbuf_info), + GFP_KERNEL); + VDEC_ASSERT(local_res_info->ddbuf_info); + if (!local_res_info->ddbuf_info) { + pr_err("Failed to allocate memory for resource buffer information structure"); + ret = IMG_ERROR_OUT_OF_MEMORY; + goto error_local_dd_buf_alloc; + } + +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s:%d calling MMU_StreamMalloc", __func__, __LINE__); +#endif + ret = mmu_stream_alloc(mmu_handle, MMU_HEAP_STREAM_BUFFERS, + mem_heap_id, (enum sys_emem_attrib)(mem_attrib | + SYS_MEMATTRIB_CPU_READ | + SYS_MEMATTRIB_CPU_WRITE), + res_size[i], + DEV_MMU_PAGE_ALIGNMENT, + local_res_info->ddbuf_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_local_res_alloc; + + /* Register with the buffer pool */ + ret = pool_resreg(local_res_ctx->res_pool[i], + decres_fnbuf_info_destructor, + local_res_info->ddbuf_info, + sizeof(*local_res_info->ddbuf_info), + FALSE, NULL, + &local_res_info->res, mmu_handle); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + goto error_local_res_register; + + lst_add(&local_res_ctx->pool_data_list[i], + local_res_info); + continue; + +/* Roll back in case of local errors. */ +error_local_res_register: mmu_free_mem(mmu_handle, local_res_info->ddbuf_info); + +error_local_res_alloc: kfree(local_res_info->ddbuf_info); + +error_local_dd_buf_alloc: kfree(local_res_info); + +error_local_res_info_alloc: goto error; + } + } + + *resources = (void *)local_res_ctx; + + return IMG_SUCCESS; + +/* Roll back in case of errors. */ +error: dec_res_destroy(mmu_handle, (void *)local_res_ctx); + + return ret; +} + +/* + *@Function RESOURCES_Destroy + * + */ +int dec_res_destroy(void *mmudev_handle, void *res_ctx) +{ + int ret = IMG_SUCCESS; + int ret1 = IMG_SUCCESS; + unsigned int i = 0; + struct res_resinfo *local_res_info; + struct res_resinfo *next_res_info; + + struct dec_res_ctx *local_res_ctx = (struct dec_res_ctx *)res_ctx; + + if (!local_res_ctx) { + pr_err("Invalid parameters"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (local_res_ctx->intra_bufinfo.hndl_memory) { + ret1 = mmu_free_mem(mmudev_handle, &local_res_ctx->intra_bufinfo); + VDEC_ASSERT(ret1 == IMG_SUCCESS); + if (ret1 != IMG_SUCCESS) + ret = ret1; + } + + if (local_res_ctx->auxline_bufinfo.hndl_memory) { + ret1 = mmu_free_mem(mmudev_handle, &local_res_ctx->auxline_bufinfo); + VDEC_ASSERT(ret1 == IMG_SUCCESS); + if (ret1 != IMG_SUCCESS) + ret = ret1; + } + + for (i = 0; i < VDEC_STD_MAX; i++) { + if (local_res_ctx->vlc_tables_bufinfo[i].hndl_memory) { + ret1 = mmu_free_mem(mmudev_handle, &local_res_ctx->vlc_tables_bufinfo[i]); + VDEC_ASSERT(ret1 == IMG_SUCCESS); + if (ret1 != IMG_SUCCESS) + ret = ret1; + } + + if (local_res_ctx->vlc_idxtables_bufinfo[i].hndl_memory) { + ret1 = mmu_free_mem(mmudev_handle, + &local_res_ctx->vlc_idxtables_bufinfo[i]); + VDEC_ASSERT(ret1 == IMG_SUCCESS); + if (ret1 != IMG_SUCCESS) + ret = ret1; + } + } + + if (local_res_ctx->start_code_bufinfo.hndl_memory) { + ret1 = mmu_free_mem(mmudev_handle, &local_res_ctx->start_code_bufinfo); + VDEC_ASSERT(ret1 == IMG_SUCCESS); + if (ret1 != IMG_SUCCESS) + ret = ret1; + } + + for (i = 0; i < DECODER_RESTYPE_MAX; i++) { + if (local_res_ctx->res_pool[i]) { + local_res_info = + lst_first(&local_res_ctx->pool_data_list[i]); + while (local_res_info) { + next_res_info = lst_next(local_res_info); + lst_remove(&local_res_ctx->pool_data_list[i], local_res_info); + ret1 = pool_resdestroy(local_res_info->res, TRUE); + VDEC_ASSERT(ret1 == IMG_SUCCESS); + if (ret1 != IMG_SUCCESS) + ret = ret1; + kfree(local_res_info); + local_res_info = next_res_info; + } + pool_destroy(local_res_ctx->res_pool[i]); + } + } + + kfree(local_res_ctx); + return ret; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/dec_resources.h b/drivers/media/platform/vxe-vxd/decoder/dec_resources.h --- a/drivers/media/platform/vxe-vxd/decoder/dec_resources.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/dec_resources.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Decoder resource allocation and destroy Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#ifndef _DEC_RESOURCES_H_ +#define _DEC_RESOURCES_H_ + +#include "decoder.h" +#include "lst.h" + +/* + * This structure contains the core resources. + * @brief Decoder Core Resources + */ +struct dec_res_ctx { + struct vidio_ddbufinfo intra_bufinfo; + struct vidio_ddbufinfo auxline_bufinfo; + struct vidio_ddbufinfo start_code_bufinfo; + struct vidio_ddbufinfo vlc_tables_bufinfo[VDEC_STD_MAX]; + struct vidio_ddbufinfo vlc_idxtables_bufinfo[VDEC_STD_MAX]; + void *res_pool[DECODER_RESTYPE_MAX]; + struct lst_t pool_data_list[DECODER_RESTYPE_MAX]; +}; + +int dec_res_picture_detach(void **res_ctx, struct dec_decpict *dec_pict); + +int dec_res_picture_attach(void **res_ctx, enum vdec_vid_std vid_std, + struct dec_decpict *dec_pict); + +int dec_res_create(void *mmudev_handle, + struct vxd_coreprops *core_props, unsigned int num_dec_slots, + unsigned int mem_heap_id, void **resources); + +int dec_res_destroy(void *mmudev_handle, void *res_ctx); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/fw_interface.h b/drivers/media/platform/vxe-vxd/decoder/fw_interface.h --- a/drivers/media/platform/vxe-vxd/decoder/fw_interface.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/fw_interface.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,818 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG MSVDX core Registers + * This file contains the MSVDX_CORE_REGS_H Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef FW_INTERFACE_H_ +#define FW_INTERFACE_H_ + +/* TODO For now this macro defined, need to think and enable */ +#define VDEC_USE_PVDEC_COMPATIBILITY 1 + +#define MSG_TYPE_PADDING (0x00) +/* Start of parser specific Host->MTX messages */ +#define MSG_TYPE_START_PSR_HOSTMTX_MSG (0x80) +/* Start of parser specific MTX->Host message */ +#define MSG_TYPE_START_PSR_MTXHOST_MSG (0xC0) + +enum { + FW_DEVA_INIT = MSG_TYPE_START_PSR_HOSTMTX_MSG, + FW_DEVA_DECODE_FE, + FW_DEVA_RES_0, + FW_DEVA_RES_1, + FW_DEVA_DECODE_BE, + FW_DEVA_HOST_BE_OPP, + FW_DEVA_DEBLOCK, + FW_DEVA_INTRA_OOLD, + FW_DEVA_ENDFRAME, + + FW_DEVA_PARSE, + FW_DEVA_PARSE_FRAGMENT, + FW_DEVA_BEGINFRAME, + +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +#ifdef VDEC_USE_PVDEC_SEC + FWBSP_INIT, + FWBSP_PARSE_BITSTREAM, + FWDEC_DECODE, +#endif /* VDEC_USE_PVDEC_SEC */ +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ + + /* Sent by the firmware on the MTX to the host. */ + FW_DEVA_COMPLETED = MSG_TYPE_START_PSR_MTXHOST_MSG, +#ifndef VDEC_USE_PVDEC_COMPATIBILITY + FW_DEVA_RES_2, + FW_DEVA_RES_3, + FW_DEVA_RES_4, + FW_DEVA_RES_5, + + FW_DEVA_RES_6, + FW_DEVA_CONTIGUITY_WARNING, + FW_DEVA_PANIC, + FW_DEVA_RES_7, + FW_DEVA_RES_8, +#else /* ndef VDEC_USE_PVDEC_COMPATIBILITY */ + FW_DEVA_PANIC, + FW_ASSERT, + FW_PERF, + /* An empty completion message sent by new vxd driver */ + FW_VXD_EMPTY_COMPL, + FW_DEC_REQ_RECEIVED, + FW_SO, +#ifdef VDEC_USE_PVDEC_SEC + FWBSP_NEW_SEQ, + FWBSP_NEW_PIC, + FWBSP_BUF_EMPTY, + FWBSP_ERROR, + FWDEC_COMPLETED, +#endif /* VDEC_USE_PVDEC_SEC */ +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ + FW_DEVA_SIGNATURES_LEGACY = 0xD0, + FW_DEVA_SIGNATURES_HEVC = 0xE0, + FW_DEVA_SIGNATURES_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Defines the Host/Firmware communication area */ +#ifndef VDEC_USE_PVDEC_COMPATIBILITY +#define COMMS_HEADER_SIZE (0x34) +#else /* def VDEC_USE_PVDEC_COMPATIBILITY */ +#define COMMS_HEADER_SIZE (0x40) +#endif /* def VDEC_USE_PVDEC_COMPATIBILITY */ +/* dwords */ +#define PVDEC_COM_RAM_FW_STATUS_OFFSET 0x00 +#define PVDEC_COM_RAM_TASK_STATUS_OFFSET 0x04 +#define PVDEC_COM_RAM_FW_ID_OFFSET 0x08 +#define PVDEC_COM_RAM_FW_MTXPC_OFFSET 0x0c +#define PVDEC_COM_RAM_MSG_COUNTER_OFFSET 0x10 +#define PVDEC_COM_RAM_SIGNATURE_OFFSET 0x14 +#define PVDEC_COM_RAM_TO_HOST_BUF_SIZE_AND_OFFSET_OFFSET 0x18 +#define PVDEC_COM_RAM_TO_HOST_RD_INDEX_OFFSET 0x1c +#define PVDEC_COM_RAM_TO_HOST_WRT_INDEX_OFFSET 0x20 +#define PVDEC_COM_RAM_TO_MTX_BUF_SIZE_AND_OFFSET_OFFSET 0x24 +#define PVDEC_COM_RAM_TO_MTX_RD_INDEX_OFFSET 0x28 +#define PVDEC_COM_RAM_FLAGS_OFFSET 0x2c +#define PVDEC_COM_RAM_TO_MTX_WRT_INDEX_OFFSET 0x30 +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +#define PVDEC_COM_RAM_STATE_BUF_SIZE_AND_OFFSET_OFFSET 0x34 +#define PVDEC_COM_RAM_FW_MMU_REPORT_OFFSET 0x38 +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ +/* fields */ +#define PVDEC_COM_RAM_TO_HOST_BUF_SIZE_AND_OFFSET_SIZE_MASK 0xFFFF +#define PVDEC_COM_RAM_TO_HOST_BUF_SIZE_AND_OFFSET_SIZE_SHIFT 0 +#define PVDEC_COM_RAM_TO_HOST_BUF_SIZE_AND_OFFSET_OFFSET_MASK 0xFFFF0000 +#define PVDEC_COM_RAM_TO_HOST_BUF_SIZE_AND_OFFSET_OFFSET_SHIFT 16 + +#define PVDEC_COM_RAM_TO_MTX_BUF_SIZE_AND_OFFSET_SIZE_MASK 0xFFFF +#define PVDEC_COM_RAM_TO_MTX_BUF_SIZE_AND_OFFSET_SIZE_SHIFT 0 +#define PVDEC_COM_RAM_TO_MTX_BUF_SIZE_AND_OFFSET_OFFSET_MASK 0xFFFF0000 +#define PVDEC_COM_RAM_TO_MTX_BUF_SIZE_AND_OFFSET_OFFSET_SHIFT 16 +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +#define PVDEC_COM_RAM_STATE_BUF_SIZE_AND_OFFSET_SIZE_MASK 0xFFFF +#define PVDEC_COM_RAM_STATE_BUF_SIZE_AND_OFFSET_SIZE_SHIFT 0 +#define PVDEC_COM_RAM_STATE_BUF_SIZE_AND_OFFSET_OFFSET_MASK 0xFFFF0000 +#define PVDEC_COM_RAM_STATE_BUF_SIZE_AND_OFFSET_OFFSET_SHIFT 16 +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ +#define PVDEC_COM_RAM_BUF_GET_SIZE(_reg_, _name_) \ + (((_reg_) & PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_SIZE_MASK) >> \ + PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_SIZE_SHIFT) +#define PVDEC_COM_RAM_BUF_GET_OFFSET(_reg_, _name_) \ + (((_reg_) & \ + PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_OFFSET_MASK) >> \ + PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_OFFSET_SHIFT) +#define PVDEC_COM_RAM_BUF_SET_SIZE_AND_OFFSET(_name_, _size_, _offset_) \ + ((((_size_) << \ + PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_SIZE_SHIFT) \ + & PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_SIZE_MASK) | \ + (((_offset_) << \ + PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_OFFSET_SHIFT) \ + & PVDEC_COM_RAM_ ## _name_ ## _BUF_SIZE_AND_OFFSET_OFFSET_MASK)) +/* values */ +/* Firmware ready signature value */ + #define FW_READY_SIGNATURE (0xA5A5A5A5) + +/* Firmware status values */ + #define FW_STATUS_BUSY 0 + #define FW_STATUS_IDLE 1 + #define FW_STATUS_PANIC 2 + #define FW_STATUS_ASSERT 3 + #define FW_STATUS_GAMEOVER 4 + #define FW_STATUS_FEWATCHDOG 5 + #define FW_STATUS_EPWATCHDOG 6 + #define FW_STATUS_BEWATCHDOG 7 +#ifdef VDEC_USE_PVDEC_COMPATIBILITY + #define FW_STATUS_SO 8 + #define FW_STATUS_INIT 0xF +#endif + +/* Decode Message Flags */ + #define FW_DEVA_RENDER_IS_FIRST_SLICE (0x00000001) +/* This is H264 Mbaff - required for state store */ + #define FW_DEVA_FORCE_RECON_WRITE_DISABLE (0x00000002) + #define FW_DEVA_RENDER_IS_LAST_SLICE (0x00000004) +/* Prevents insertion of end of picture or flush at VEC EOS */ + #define FW_DEVA_DECODE_DISABLE_EOF_DETECTION (0x00000008) + + #define FW_DEVA_CONTEXT_BUFFER_INVALID (0x00000010) + #define FW_DEVA_FORCE_ALT_OUTPUT (0x00000020) + #define FW_SECURE_STREAM (0x00000040) + #define FW_LOW_LATENCY (0x00000080) + + #define FW_DEVA_CONTIGUITY_DETECTION (0x00000100) + #define FW_DEVA_FORCE_INIT_CMDS (0x00000200) + #define FW_DEVA_DEBLOCK_ENABLE (0x00000400) +#ifdef VDEC_USE_PVDEC_COMPATIBILITY + #define FW_VDEC_SEND_SIGNATURES (0x00000800) +#else +/* (0x00000800) */ +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ + + #define FW_DEVA_FORCE_AUX_LINE_BUF_DISABLE (0x00001000) +/* + * Cause no response message to be sent, and no interrupt + * generation on successful completion + */ + #define FW_DEVA_RENDER_NO_RESPONSE_MSG (0x00002000) +/* + * Cause an interrupt if a response message is generated + * on successful completion + */ + #define FW_DEVA_RENDER_HOST_INT (0x00004000) +/* Report contiguity errors to host */ + #define FW_DEVA_CONTIGUITY_REPORTING (0x00008000) + + #define FW_DEVA_VC1_SKIPPED_PICTURE (0x00010000) + #define FW_INTERNAL_RENDER_SWITCH (0x00020000) + #define FW_DEVA_UNSUPPORTED (0x00040000) + #define DEBLOCKING_FORCED_OFF (0x00080000) +#ifdef VDEC_USE_PVDEC_COMPATIBILITY + #define FW_VDEC_CMD_PENDING (0x00100000) +#else +/* (0x00100000) */ +#endif +/* Only for debug */ + #define DETECTED_RENDEC_FULL (0x00200000) +/* Only for debug */ + #define DETECTED_RENDEC_EMPTY (0x00400000) + #define FW_ONE_PASS_PARSE (0x00800000) + + #define FW_DEVA_EARLY_COMPLETE (0x01000000) + #define FW_DEVA_FE_EP_SIGNATURES_READY (0x02000000) + #define FW_VEC_EOS (0x04000000) +/* hardware has reported an error relating to this command */ + #define FW_DEVA_ERROR_DETECTED_ENT (0x08000000) + + #define FW_DEVA_ERROR_DETECTED_PIX (0x10000000) + #define FW_DEVA_MP_SYNC (0x20000000) + #define MORE_THAN_ONE_MB (0x40000000) + #define REATTEMPT_SINGLEPIPE (0x80000000) +/* end of message flags */ +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +/* VDEC Decode Message Flags */ +/* + * H.264/H.265 are to be configured in SIZE_DELIMITED mode rather than SCP mode. + */ +#define FW_VDEC_NAL_SIZE_DELIM (0x00000001) +/* Indicates if MMU cache shall be flushed. */ +#define FW_VDEC_MMU_FLUSH_CACHE (0x00000002) +/* end of message flags */ +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ + +/* FW flags */ +/* TODO : Temporary for HW testing */ + #define FWFLAG_DISABLE_VDEB_PRELOAD (0x00000001) + #define FWFLAG_BIG_TO_HOST_BUFFER (0x00000002) +/* FS is default regarless of this flag */ + #define FWFLAG_FORCE_FS_FLOW (0x00000004) + #define FWFLAG_DISABLE_WATCHDOG_TIMERS (0x00000008) + + #define FWFLAG_DISABLE_AEH (0x00000020) + #define FWFLAG_DISABLE_AUTONOMOUS_RESET (0x00000040) + #define FWFLAG_NON_ACCUMULATING_HWSIGS (0x00000080) + + #define FWFLAG_DISABLE_2PASS_DEBLOCK (0x00000100) + #define FWFLAG_NO_INT_ON_TOHOST_FULL (0x00000200) + #define FWFLAG_RETURN_VDEB_CR (0x00000800) + + #define FWFLAG_DISABLE_AUTOCLOCKGATING (0x00001000) + #define FWFLAG_DISABLE_IDLE_GPIO (0x00002000) + #define FWFLAG_XPL (0x00004000) + #define FWFLAG_INFINITE_MTX_TIMEOUT (0x00008000) + + #define FWFLAG_DECOUPLE_BE_FE (0x00010000) + #define FWFLAG_ENABLE_SECURITY (0x00080000) + + #define FWFLAG_ENABLE_CONCEALMENT (0x00100000) +/* Not currently supported */ +/* #define FWFLAG_PREEMPT (0x00200000) */ +/* NA in FS */ + #define FWFLAG_FORCE_FLUSHING (0x00400000) +/* NA in FS */ + #define FWFLAG_DISABLE_GENC_FLUSHING (0x00800000) + + #define FWFLAG_DISABLE_COREWDT_TIMERS (0x01000000) + #define FWFLAG_DISABLE_RENDEC_AUTOFLUSH (0x02000000) + #define FWFLAG_FORCE_STRICT_SINGLEPIPE (0x04000000) + #define FWFLAG_CONSISTENT_MULTIPIPE_FLOW (0x08000000) + + #define FWFLAG_DISABLE_IDLE_FAST_EVAL (0x10000000) + #define FWFLAG_FAKE_COMPLETION (0x20000000) + #define FWFLAG_MAN_PP_CLK (0x40000000) + #define FWFLAG_STACK_CHK (0x80000000) + +/* end of FW flags */ + +#ifdef FW_STACK_USAGE_TRACKING +/* FW task identifiers */ +enum task_id { + TASK_ID_RX = 0, + TASK_ID_TX, + TASK_ID_EP1, + TASK_ID_FE1, + TASK_ID_FE2, + TASK_ID_FE3, + TASK_ID_BE1, + TASK_ID_BE2, + TASK_ID_BE3, + TASK_ID_PARSER, + TASK_ID_MAX, + TASK_ID_FORCE32BITS = 0x7FFFFFFFU +}; + +/* FW task stack info utility macros */ +#define TASK_STACK_SIZE_MASK 0xFFFF +#define TASK_STACK_SIZE_SHIFT 0 +#define TASK_STACK_USED_MASK 0xFFFF0000 +#define TASK_STACK_USED_SHIFT 16 +#define TASK_STACK_SET_INFO(_task_id_, _stack_info_, _size_, _used_) \ + (_stack_info_[_task_id_] = \ + ((_size_) << TASK_STACK_SIZE_SHIFT) | \ + ((_used_) << TASK_STACK_USED_SHIFT)) +#define TASK_STACK_GET_SIZE(_task_id_, _stack_info_) \ + ((_stack_info_[_task_id_] & TASK_STACK_SIZE_MASK) >> \ + TASK_STACK_SIZE_SHIFT) +#define TASK_STACK_GET_USED(_task_id_, _stack_info_) \ + ((_stack_info_[_task_id_] & TASK_STACK_USED_MASK) >> \ + TASK_STACK_USED_SHIFT) +#endif /* FW_STACK_USAGE_TRACKING */ + +/* Control Allocation */ +#define CMD_MASK (0xF0000000) + +/* Ctrl Allocation Header */ +#define CMD_CTRL_ALLOC_HEADER (0x90000000) + +struct ctrl_alloc_header { + unsigned int cmd_additional_params; + unsigned int slice_params; + union { + unsigned int vp8_probability_data; + unsigned int h264_pipeintra_buffersize; + }; + unsigned int chroma_strides; + unsigned int slice_first_mb_yx; + unsigned int pic_last_mb_yx; + /* VC1 only : Store Range Map flags in bottom bits of [0] */ + unsigned int alt_output_addr[2]; + unsigned int alt_output_flags; + /* H264 Only : Extended Operating Mode */ + unsigned int ext_opmode; +}; + +#define CMD_CTRL_ALLOC_HEADER_DWSIZE \ + (sizeof(struct ctrl_alloc_header) / sizeof(unsigned int)) + +/* Additional Parameter flags */ +#define VC1_PARSEHDR_MASK (0x00000001) +#define VC1_SKIPPIC_MASK (0x00000002) + +#define VP6_BUFFOFFSET_MASK (0x0000ffff) +#define VP6_MULTISTREAM_MASK (0x01000000) +#define VP6_FRAMETYPE_MASK (0x02000000) + +#define VP8_BUFFOFFSET_MASK (0x00ffffff) +#define VP8_PARTITIONSCOUNT_MASK (0x0f000000) +#define VP8_PARTITIONSCOUNT_SHIFT (24) + +/* Nop Command */ +#define CMD_NOP (0x00000000) +#define CMD_NOP_DWSIZE (1) + +/* Register Block */ +#define CMD_REGISTER_BLOCK (0x10000000) +#define CMD_REGISTER_BLOCK_PATCHING_REQUIRED (0x01000000) +#define CMD_REGISTER_BLOCK_FLAG_PRELOAD (0x04000000) +#define CMD_REGISTER_BLOCK_FLAG_VLC_DATA (0x08000000) + +/* Rendec Command */ +#define CMD_RENDEC_BLOCK (0x50000000) +#define CMD_RENDEC_BLOCK_FLAG_MASK (0x0F000000) +#define CMD_RENDEC_FORCE (0x08000000) +#define CMD_RENDEC_PATCHING_REQUIRED (0x01000000) +#define CMD_RENDEC_WORD_COUNT_MASK (0x00ff0000) +#define CMD_RENDEC_WORD_COUNT_SHIFT (16) +#define CMD_RENDEC_ADDRESS_MASK (0x0000ffff) +#define CMD_RENDEC_ADDRESS_SHIFT (0) + +#ifndef VDEC_USE_PVDEC_SEC +/* Deblock */ +#define CMD_DEBLOCK (0x70000000) +#define CMD_DEBLOCK_TYPE_STD (0x00000000) +#define CMD_DEBLOCK_TYPE_OOLD (0x00000001) +#define CMD_DEBLOCK_TYPE_SKIP (0x00000002) +/* End Of Frame */ +#define CMD_DEBLOCK_TYPE_EF (0x00000003) + +struct deblock_cmd { + unsigned int cmd; /* 0x70000000 */ + unsigned int source_mb_data; + unsigned int address_a[2]; +}; + +#define CMD_DEBLOCK_DWSIZE (sizeof(DEBLOCK_CMD) / sizeof(unsigned int)) +#endif /* !VDEC_USE_PVDEC_SEC */ + +/* Skip */ +#define CMD_CONDITIONAL_SKIP (0x80000000) +#define CMD_CONDITIONAL_SKIP_DWSIZE (1) +#define CMD_CONDITIONAL_SKIP_DWORDS (0x0000ffff) +#define CMD_CONDITIONAL_SKIP_CONTEXT_SWITCH BIT(20) + +/* DMA */ +#define CMD_DMA (0xE0000000) +#define CMD_DMA_DMA_TYPE_MASK (0x03000000) +#define CMD_DMA_DMA_TYPE_SHIFT (24) +#define CMD_DMA_FLAG_MASK (0x00100000) +#define CMD_DMA_FLAG_SHIFT (20) +#define CMD_DMA_DMA_SIZE_MASK (0x000fffff) + +#define CMD_DMA_OFFSET_FLAG (0x00100000) + +#define CMD_DMA_MAX_OFFSET (0xFFF) +#define CMD_DMA_TYPE_VLC_TABLE (0 << CMD_DMA_DMA_TYPE_SHIFT) +#define CMD_DMA_TYPE_PROBABILITY_DATA BIT(CMD_DMA_DMA_TYPE_SHIFT) + +struct dma_cmd { + unsigned int cmd; + unsigned int dev_virt_add; +}; + +#define CMD_DMA_DWSIZE (sizeof(DMA_CMD) / sizeof(unsigned int)) + +struct dma_cmd_offset_dwsize { + unsigned int cmd; + unsigned int dev_virt_add; + unsigned int byte_offset; +}; + +#define CMD_DMA_OFFSET_DWSIZE (sizeof(DMA_CMD_WITH_OFFSET) / sizeof(unsigned int)) + +/* HOST COPY */ +#define CMD_HOST_COPY (0xF0000000) +#define CMD_HOST_COPY_SIZE_MASK (0x000fffff) + +struct host_copy_cmd { + unsigned int cmd; + unsigned int src_dev_virt_add; + unsigned int dst_dev_virt_add; +}; + +#define CMD_HOST_COPY_DWSIZE (sizeof(HOST_COPY_CMD) / sizeof(unsigned int)) + +/* Shift register setup and Bitstream DMA */ +#define CMD_SR_SETUP (0xB0000000) +#define CMD_SR_ENABLE_RBDU_EXTRACTION (0x00000001) +#define CMD_SR_ENABLE_AES_COUNTER (0x00000002) +#define CMD_SR_VERIFY_STARTCODE (0x00000004) +#define CMD_SR_BITSTR_ADDR_DEREF (0x00000008) +#define CMD_SR_BITSTR_PARSE_KEY (0x00000010) + +struct sr_setup_cmd { + unsigned int cmd; + unsigned int bitstream_offset_bits; + unsigned int bitstream_size_bytes; +}; + +#define CMD_SR_DWSIZE (sizeof(SR_SETUP_CMD) / sizeof(unsigned int)) + +#define CMD_BITSTREAM_DMA (0xA0000000) +#define CMD_BITSTREAM_DMA_DWSIZE (2) +/* VC1 Parse Header Command */ +#define CMD_PARSE_HEADER (0x30000000) +#define CMD_PARSE_HEADER_CONTEXT_MASK (0x000000ff) +#define CMD_PARSE_HEADER_NEWSLICE (0x00000001) +#define CMD_PARSE_HEADER_SKIP_PIC (0x00000002) +#define CMD_PARSE_HEADER_ONEPASSPARSE (0x00000004) +#define CMD_PARSE_HEADER_NUMSLICE_MINUS1 (0x00ffff00) + +struct parse_header_cmd { + unsigned int cmd; + unsigned int seq_hdr_data; + unsigned int pic_dimensions; + unsigned int bitplane_addr[3]; + unsigned int vlc_table_addr; +}; + +#define CMD_PARSE_DWSIZE (sizeof(PARSE_HEADER_CMD) / sizeof(unsigned int)) + +#define CMD_SLICE_INFO (0x20000000) +#define CMD_SLICE_INFO_SLICENUM (0xff000000) +#define CMD_SLICE_INFO_FIRSTMBY (0x00ff0000) +#define CMD_SLICE_INFO_MBBITOFFSET (0x0000ffff) + +struct slice_info { + unsigned char slice_num; + unsigned char slice_first_mby; + unsigned short slice_mb_bitoffset; +}; + +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +/* VDEC extension */ +#define CMD_VDEC_EXT (0xC0000000) +#ifdef VDEC_USE_PVDEC_SEC +/* + * Used only between firmware secure modules FWBSP->FWDEC, + * thus the structure is defined in firmware structures.h + */ +#define CMD_VDEC_SECURE_EXT (0x40000000) +#endif/* VDEC_USE_PVDEC_SEC */ + +#define MEM2REG_SIZE_HOST_PART_MASK 0x0000FFFF +#define MEM2REG_SIZE_HOST_PART_SHIFT 0 + +#define MEM2REG_SIZE_BUF_TOTAL_MASK 0xFFFF0000 +#define MEM2REG_SIZE_BUF_TOTAL_SHIFT 16 + +struct vdec_ext_cmd { + unsigned int cmd; + unsigned int trans_id; + unsigned int hdr_addr; + unsigned int hdr_size; + unsigned int ctx_save_addr; + unsigned int ctx_load_addr; + unsigned int buf_ctrl_addr; + unsigned int seq_addr; + unsigned int pps_addr; + unsigned int pps_2addr; + unsigned int mem_to_reg_addr; + /* 31-16: buff size, 15-0: size filled by host; dwords */ + unsigned int mem_to_reg_size; + unsigned int slice_params_addr; + unsigned int slice_params_size; /* dwords */ + unsigned int last_luma_recon; + unsigned int last_chroma_recon; + unsigned int luma_err_base; + unsigned int chroma_err_base; + unsigned int scaled_display_size; + unsigned int horz_scale_control; + unsigned int vert_scale_control; + unsigned int scale_output_size; + unsigned int vlc_idx_table_size; + unsigned int vlc_idx_table_addr; + unsigned int vlc_tables_size; + unsigned int vlc_tables_addr; + unsigned int display_picture_size; + unsigned int parser_mode; + /* needed for separate colour planes */ + unsigned int intra_buf_base_addr; + unsigned int intra_buf_size_per_plane; + unsigned int intra_buf_size_per_pipe; + unsigned int chroma2reconstructed_addr; + unsigned int luma_alt_addr; + unsigned int chroma_alt_addr; + unsigned int chroma2alt_addr; + unsigned int aux_line_buf_size_per_pipe; + unsigned int aux_line_buffer_base_addr; + unsigned int alt_output_pict_rotation; + /* miscellaneous flags */ + struct { + unsigned is_chromainterleaved : 1; + unsigned is_packedformat : 1; + unsigned is_discontinuousmbs : 1; + }; +}; + +#define CMD_VDEC_EXT_DWSIZE (sizeof(VDEC_EXT_CMD) / sizeof(unsigned int)) +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ + +/* Completion */ +#define CMD_COMPLETION (0x60000000) +#define CMD_COMPLETION_DWSIZE (1) + +#ifdef VDEC_USE_PVDEC_SEC +/* Slice done */ +#define CMD_SLICE_DONE (0x70000000) +#define CMD_SLICE_DONE_DWSIZE (1) +#endif /* VDEC_USE_PVDEC_SEC */ + +/* Bitstream segments */ +#define CMD_BITSTREAM_SEGMENTS (0xD0000000) +#define CMD_BITSTREAM_SEGMENTS_MINUS1_MASK (0x0000001F) +#define CMD_BITSTREAM_PARSE_BLK_MASK (0x0000FF00) +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +#define CMD_BITSTREAM_SEGMENTS_MORE_FOLLOW_MASK (0x00000020) +#define CMD_BITSTREAM_EOP_MASK (0x00000040) +#define CMD_BITSTREAM_BS_TOT_SIZE_WORD_OFFSET (1) +#define CMD_BITSTREAM_BS_SEG_LIST_WORD_OFFSET (2) +#define CMD_BITSTREAM_HDR_DW_SIZE CMD_BITSTREAM_BS_SEG_LIST_WORD_OFFSET + +#define CMD_BITSTREAM_SEGMENTS_MAX_NUM (60) +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ + +#ifdef VDEC_USE_PVDEC_COMPATIBILITY +/* Signatures */ +/* Signature set ids (see hwSignatureModules.c for exact order). */ +/* -- FRONT END/ENTROPY_PIPE ----------------------------------- */ +/* + * Signature group 0: + * REG(PVDEC_ENTROPY, CR_SR_SIGNATURE) + * REG(MSVDX_VEC, CR_SR_CRC) + */ +#define PVDEC_SIGNATURE_GROUP_0 BIT(0) +/* + * Signature group 1: + * REG(PVDEC_ENTROPY, CR_HEVC_PARSER_SIGNATURE) + * REG(PVDEC_ENTROPY, CR_ENCAP_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_1 BIT(1) +/* + * Signature group 2: + * REG(PVDEC_ENTROPY, CR_GENC_ENGINE_OUTPUT_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_2 BIT(2) +/* + * Signature group 3: + * REGREP(PVDEC_ENTROPY, CR_GENC_BUFFER_SIGNATURE, 0) + * REGREP(PVDEC_ENTROPY, CR_GENC_BUFFER_SIGNATURE, 1) + * REGREP(PVDEC_ENTROPY, CR_GENC_BUFFER_SIGNATURE, 2) + * REGREP(PVDEC_ENTROPY, CR_GENC_BUFFER_SIGNATURE, 3) + * REG( PVDEC_ENTROPY, CR_GENC_FRAGMENT_SIGNATURE) + * REG( PVDEC_ENTROPY, CR_GENC_FRAGMENT_READ_SIGNATURE) + * REG( PVDEC_ENTROPY, CR_GENC_FRAGMENT_WRADDR_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_3 BIT(3) +/* -- GENC_DEC -------------------------------------------------- */ +/* + * Signature group 4: + * REG( PVDEC_VEC_BE, CR_GDEC_FRAGMENT_REQ_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_GDEC_SYS_WR_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_GDEC_MEM2REG_SYS_WR_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_SLICE_STRUCTURE_REQ_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_SLICE_STRUCTURE_OVER1K_REQ_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_MEM_STRUCTURE_REQ_SIGNATURE) + * REGREP(PVDEC_VEC_BE, CR_GDEC_DATA_REQ_SIGNATURE, 0) + * REGREP(PVDEC_VEC_BE, CR_GDEC_DATA_REQ_SIGNATURE, 1) + * REGREP(PVDEC_VEC_BE, CR_GDEC_DATA_REQ_SIGNATURE, 2) + * REGREP(PVDEC_VEC_BE, CR_GDEC_DATA_REQ_SIGNATURE, 3) + */ +#define PVDEC_SIGNATURE_GROUP_4 BIT(4) +/* + * Signature group 5: + * REG( PVDEC_VEC_BE, CR_GDEC_FRAGMENT_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_SLICE_STRUCTURE_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_SLICE_STRUCTURE_OVER1K_SIGNATURE) + * REG( PVDEC_VEC_BE, CR_MEM_STRUCTURE_SIGNATURE) + * REGREP(PVDEC_VEC_BE, CR_GDEC_BUFFER_SIGNATURE, 0) + * REGREP(PVDEC_VEC_BE, CR_GDEC_BUFFER_SIGNATURE, 1) + * REGREP(PVDEC_VEC_BE, CR_GDEC_BUFFER_SIGNATURE, 2) + * REGREP(PVDEC_VEC_BE, CR_GDEC_BUFFER_SIGNATURE, 3) + */ +#define PVDEC_SIGNATURE_GROUP_5 BIT(5) +/* -- RESIDUAL AND COMMAND DEBUG--------------------------------- */ +/* + * Signature group 12: + * REG(PVDEC_VEC_BE, CR_DECODE_TO_COMMAND_PRIME_SIGNATURE) + * REG(PVDEC_VEC_BE, CR_DECODE_TO_COMMAND_SECOND_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_12 BIT(12) +/* + * Signature group 13: + * REG(PVDEC_VEC_BE, CR_DECODE_TO_RESIDUAL_PRIME_SIGNATURE) + * REG(PVDEC_VEC_BE, CR_DECODE_TO_RESIDUAL_SECOND_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_13 BIT(13) +/* + * Signature group 14: + * REG(PVDEC_VEC_BE, CR_COMMAND_ABOVE_READ_SIGNATURE) + * REG(PVDEC_VEC_BE, CR_COMMAND_ABOVE_WRITE_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_14 BIT(14) +/* + * Signature group 15: + * REG(PVDEC_VEC_BE, CR_TEMPORAL_READ_SIGNATURE) + * REG(PVDEC_VEC_BE, CR_TEMPORAL_WRITE_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_15 BIT(15) +/* --VEC--------------------------------------------------------- */ +/* + * Signature group 16: + * REG(PVDEC_VEC_BE, CR_COMMAND_OUTPUT_SIGNATURE) + * REG(MSVDX_VEC, CR_VEC_IXFORM_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_16 BIT(16) +/* + * Signature group 17: + * REG(PVDEC_VEC_BE, CR_RESIDUAL_OUTPUT_SIGNATURE) + * REG(MSVDX_VEC, CR_VEC_COMMAND_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_17 BIT(17) +/* --VDMC-------------------------------------------------------- */ +/* + * Signature group 18: + * REG(MSVDX_VDMC, CR_VDMC_REFERENCE_CACHE_SIGNATURE) + * REG(MSVDX_VDMC, CR_VDMC_REFERENCE_CACHE_MEM_WADDR_SIGNATURE) + * REG(MSVDX_VDMC, CR_VDMC_REFERENCE_CACHE_MEM_RADDR_SIGNATURE) + * REG(MSVDX_VDMC, CR_VDMC_REFERENCE_CACHE_MEM_WDATA_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_18 BIT(18) +/* + * Signature group 19: + * REG(MSVDX_VDMC, CR_VDMC_2D_FILTER_PIPELINE_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_19 BIT(19) +/* + * Signature group 20: + * REG(MSVDX_VDMC, CR_VDMC_PIXEL_RECONSTRUCTION_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_20 BIT(20) +/* + * Signature group 21: + * REG(MSVDX_VDMC, CR_VDMC_MCU_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_21 BIT(21) +/* ---VDEB------------------------------------------------------- */ +/* + * Signature group 22: + * REG(MSVDX_VDEB, CR_VDEB_SYS_MEM_RDATA_LUMA_SIGNATURE) + * REG(MSVDX_VDEB, CR_VDEB_SYS_MEM_RDATA_CHROMA_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_22 BIT(22) +/* + * Signature group 23: + * REG(MSVDX_VDEB, CR_VDEB_SYS_MEM_ADDR_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_23 BIT(23) +/* + * Signature group 24: + * REG(MSVDX_VDEB, CR_VDEB_SYS_MEM_WDATA_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_24 BIT(24) +/* ---SCALER----------------------------------------------------- */ +/* + * Signature group 25: + * REG(MSVDX_VDEB, CR_VDEB_SCALE_ADDR_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_25 BIT(25) +/* + * Signature group 26: + * REG(MSVDX_VDEB, CR_VDEB_SCALE_WDATA_SIGNATURE) + */ +#define PVDEC_SIGNATURE_GROUP_26 BIT(26) +/* ---PICTURE CHECKSUM------------------------------------------- */ +/* + * Signature group 27: + * REG(MSVDX_VDEB, CR_VDEB_HEVC_CHECKSUM_LUMA) + * REG(MSVDX_VDEB, CR_VDEB_HEVC_CHECKSUM_CB) + * REG(MSVDX_VDEB, CR_VDEB_HEVC_CHECKSUM_CR) + */ +#define PVDEC_SIGNATURE_GROUP_27 BIT(27) +#define PVDEC_SIGNATURE_NEW_METHOD BIT(31) + +/* Debug messages */ +#define DEBUG_DATA_TYPE_MASK 0xF +#define DEBUG_DATA_TYPE_SHIFT 28 + +#define DEBUG_DATA_MSG_TYPE_MASK 0x1 +#define DEBUG_DATA_MSG_TYPE_SHIFT 15 + +#define DEBUG_DATA_MSG_ARG_COUNT_MASK 0x7 +#define DEBUG_DATA_MSG_ARG_COUNT_SHIFT 12 + +#define DEBUG_DATA_MSG_LINE_NO_MASK 0xFFF +#define DEBUG_DATA_MSG_LINE_NO_SHIFT 0 + +#define DEBUG_DATA_TYPE_HEADER (0) +#define DEBUG_DATA_TYPE_STRING (1) +#define DEBUG_DATA_TYPE_PARAMS (2) +#define DEBUG_DATA_TYPE_MSG (3) +#define DEBUG_DATA_TYPE_PERF (6) + +#define DEBUG_DATA_MSG_TYPE_LOG 0 +#define DEBUG_DATA_MSG_TYPE_ASSERT 1 + +#define DEBUG_DATA_TAPE_PERF_INC_TIME_MASK 0x1 +#define DEBUG_DATA_TYPE_PERF_INC_TIME_SHIFT 28 +#define DEBUG_DATA_TYPE_PERF_INC_TIME 0x1 + +#define DEBUG_DATA_SET_TYPE(val, type, data_type) \ + ({ \ + data_type __val = val; \ + ((__val) = (__val & ~(DEBUG_DATA_TYPE_MASK << DEBUG_DATA_TYPE_SHIFT)) | \ + ((type) << DEBUG_DATA_TYPE_SHIFT)); }) + +#define DEBUG_DATA_MSG_SET_ARG_COUNT(val, ac, data_type) \ + ({ \ + data_type __val = val; \ + (__val = (__val & \ + ~(DEBUG_DATA_MSG_ARG_COUNT_MASK << DEBUG_DATA_MSG_ARG_COUNT_SHIFT)) \ + | ((ac) << DEBUG_DATA_MSG_ARG_COUNT_SHIFT)); }) + +#define DEBUG_DATA_MSG_SET_LINE_NO(val, ln, type) \ + ({ \ + type __val = val; \ + (__val = (__val & \ + ~(DEBUG_DATA_MSG_LINE_NO_MASK << DEBUG_DATA_MSG_LINE_NO_SHIFT)) \ + | ((ln) << DEBUG_DATA_MSG_LINE_NO_SHIFT)); }) + +#define DEBUG_DATA_MSG_SET_TYPE(val, tp, type) \ + ({ \ + type __val = val; \ + (__val = (__val & \ + ~(DEBUG_DATA_MSG_TYPE_MASK << DEBUG_DATA_MSG_TYPE_SHIFT)) \ + | ((tp) << DEBUG_DATA_MSG_TYPE_SHIFT)); }) + +#define DEBUG_DATA_GET_TYPE(val) \ + (((val) >> DEBUG_DATA_TYPE_SHIFT) & DEBUG_DATA_TYPE_MASK) +#define DEBUG_DATA_TYPE_PERF_IS_INC_TIME(val) \ + (((val) >> DEBUG_DATA_TYPE_PERF_INC_TIME_SHIFT) \ + & DEBUG_DATA_TAPE_PERF_INC_TIME_MASK) +#define DEBUG_DATA_MSG_GET_ARG_COUNT(val) \ + (((val) >> DEBUG_DATA_MSG_ARG_COUNT_SHIFT) \ + & DEBUG_DATA_MSG_ARG_COUNT_MASK) +#define DEBUG_DATA_MSG_GET_LINE_NO(val) \ + (((val) >> DEBUG_DATA_MSG_LINE_NO_SHIFT) \ + & DEBUG_DATA_MSG_LINE_NO_MASK) +#define DEBUG_DATA_MSG_GET_TYPE(val) \ + (((val) >> DEBUG_DATA_MSG_TYPE_SHIFT) & DEBUG_DATA_MSG_TYPE_MASK) +#define DEBUG_DATA_MSG_TYPE_IS_ASSERT(val) \ + (DEBUG_DATA_MSG_GET_TYPE(val) == DEBUG_DATA_MSG_TYPE_ASSERT \ + ? IMG_TRUE : IMG_FALSE) +#define DEBUG_DATA_MSG_TYPE_IS_LOG(val) \ + (DEBUG_DATA_MSG_GET_TYPE(val) == DEBUG_DATA_MSG_TYPE_LOG ? \ + IMG_TRUE : IMG_FALSE) + +#define DEBUG_DATA_MSG_LAT(ln, ac, tp) \ + (((ln) << DEBUG_DATA_MSG_LINE_NO_SHIFT) | \ + ((ac) << DEBUG_DATA_MSG_ARG_COUNT_SHIFT) | \ + ((tp) << DEBUG_DATA_MSG_TYPE_SHIFT)) +/* FWBSP-mode specific defines. */ +#ifdef VDEC_USE_PVDEC_SEC +/** + * FWBSP_ENC_BSTR_BUF_QUEUE_LEN - Suggested number of bitstream buffers submitted (queued) + * to firmware for processing at the same time. + */ +#define FWBSP_ENC_BSTR_BUF_QUEUE_LEN 1 + +#endif /* VDEC_USE_PVDEC_SEC */ + +#endif /* VDEC_USE_PVDEC_COMPATIBILITY */ +#endif /* FW_INTERFACE_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/h264fw_data.h b/drivers/media/platform/vxe-vxd/decoder/h264fw_data.h --- a/drivers/media/platform/vxe-vxd/decoder/h264fw_data.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/h264fw_data.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,652 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures for the h264 parser firmware module. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +/* Include shared header version here to replace the standard version */ +#include "h264fw_data_shared.h" + +#ifndef _H264FW_DATA_H_ +#define _H264FW_DATA_H_ + +#include "vdecfw_shared.h" + +/* Maximum number of alternative CPB specifications in the stream */ +#define H264_MAXIMUMVALUEOFCPB_CNT 32 + +/* + * The maximum DPB size is related to the number of MVC views supported + * The size is defined in H.10.2 for the H.264 spec. + * If the number of views needs to be changed the DPB size should be too + * The limits are as follows: + * NumViews 1, 2, 4, 8, 16 + * MaxDpbFrames: 16, 16, 32, 48, 64 + */ +#ifdef H264_ENABLE_MVC +#define H264FW_MAX_NUM_VIEWS 4 +#define H264FW_MAX_DPB_SIZE 32 +#define H264FW_MAX_NUM_MVC_REFS 16 +#else +#define H264FW_MAX_NUM_VIEWS 1 +#define H264FW_MAX_DPB_SIZE 16 +#define H264FW_MAX_NUM_MVC_REFS 1 +#endif + +/* Maximum value for num_ref_frames_in_pic_order_cnt_cycle */ +#define H264FW_MAX_CYCLE_REF_FRAMES 256 + +/* 4x4 scaling list size */ +#define H264FW_4X4_SIZE 16 +/* 8x8 scaling list size */ +#define H264FW_8X8_SIZE 64 +/* Number of 4x4 scaling lists */ +#define H264FW_NUM_4X4_LISTS 6 +/* Number of 8x8 scaling lists */ +#define H264FW_NUM_8X8_LISTS 6 + +/* Number of reference picture lists */ +#define H264FW_MAX_REFPIC_LISTS 2 + +/* + * The maximum number of slice groups + * remove if slice group map is prepared on the host + */ +#define H264FW_MAX_SLICE_GROUPS 8 + +/* The maximum number of planes for 4:4:4 separate color plane streams */ +#define H264FW_MAX_PLANES 3 + +#define H264_MAX_SGM_SIZE 8196 + +#define IS_H264_HIGH_PROFILE(profile_idc, type) \ + ({ \ + type __profile_idc = profile_idc; \ + ((__profile_idc) == H264_PROFILE_HIGH) || \ + ((__profile_idc) == H264_PROFILE_HIGH10) || \ + ((__profile_idc) == H264_PROFILE_HIGH422) || \ + ((__profile_idc) == H264_PROFILE_HIGH444) || \ + ((__profile_idc) == H264_PROFILE_CAVLC444) || \ + ((__profile_idc) == H264_PROFILE_MVC_HIGH) || \ + ((__profile_idc) == H264_PROFILE_MVC_STEREO); }) \ + +/* + * This type describes the H.264 NAL unit types + */ +enum h264_enaltype { + H264FW_NALTYPE_SLICE = 1, + H264FW_NALTYPE_IDRSLICE = 5, + H264FW_NALTYPE_SEI = 6, + H264FW_NALTYPE_SPS = 7, + H264FW_NALTYPE_PPS = 8, + H264FW_NALTYPE_AUD = 9, + H264FW_NALTYPE_EOSEQ = 10, + H264FW_NALTYPE_EOSTR = 11, + H264FW_NALTYPE_PREFIX = 14, + H264FW_NALTYPE_SUBSET_SPS = 15, + H264FW_NALTYPE_AUXILIARY_SLICE = 19, + H264FW_NALTYPE_EXTSLICE = 20, + H264FW_NALTYPE_EXTSLICE_DEPTH_VIEW = 21, + H264FW_NALTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * AVC Profile IDC definitions + */ +enum h264_eprofileidc { + /* YUV 4:4:4/14 "CAVLC 4:4:4 */ + H264_PROFILE_CAVLC444 = 44, + /* YUV 4:2:0/8 "Baseline */ + H264_PROFILE_BASELINE = 66, + /* YUV 4:2:0/8 "Main */ + H264_PROFILE_MAIN = 77, + /* YUV 4:2:0/8 "Scalable" */ + H264_PROFILE_SCALABLE = 83, + /* YUV 4:2:0/8 "Extended" */ + H264_PROFILE_EXTENDED = 88, + /* YUV 4:2:0/8 "High" */ + H264_PROFILE_HIGH = 100, + /* YUV 4:2:0/10 "High 10" */ + H264_PROFILE_HIGH10 = 110, + /* YUV 4:2:0/8 "Multiview High" */ + H264_PROFILE_MVC_HIGH = 118, + /* YUV 4:2:2/10 "High 4:2:2" */ + H264_PROFILE_HIGH422 = 122, + /* YUV 4:2:0/8 "Stereo High" */ + H264_PROFILE_MVC_STEREO = 128, + /* YUV 4:4:4/14 "High 4:4:4" */ + H264_PROFILE_HIGH444 = 244, + H264_PROFILE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the constraint set flags + */ +enum h264fw_econstraint_flag { + /* Compatible with Baseline profile */ + H264FW_CONSTRAINT_BASELINE_SHIFT = 7, + /* Compatible with Main profile */ + H264FW_CONSTRAINT_MAIN_SHIFT = 6, + /* Compatible with Extended profile */ + H264FW_CONSTRAINT_EXTENDED_SHIFT = 5, + /* Compatible with Intra profiles */ + H264FW_CONSTRAINT_INTRA_SHIFT = 4, + /* Compatible with Multiview High profile */ + H264FW_CONSTRAINT_MULTIHIGH_SHIFT = 3, + /* Compatible with Stereo High profile */ + H264FW_CONSTRAINT_STEREOHIGH_SHIFT = 2, + /* Reserved flag */ + H264FW_CONSTRAINT_RESERVED6_SHIFT = 1, + /* Reserved flag */ + H264FW_CONSTRAINT_RESERVED7_SHIFT = 0, + H264FW_CONSTRAINT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This enum describes the reference status of an H.264 picture. + * Unpaired fields should have all eRefStatusX set to the same value + * + * For Frame, Mbaff, and Pair types individual fields and frame ref status + * should be set accordingly. + * + * eRefStatusFrame eRefStatusTop eRefStatusBottom + * UNUSED UNUSED UNUSED + * SHORTTERM SHORTTERM SHORTTERM + * LONGTERM LONGTERM LONGTERM + * + * UNUSED SHORT/LONGTERM UNUSED + * UNUSED UNUSED SHORT/LONGTERM + * + * SHORTTERM LONGTERM SHORTTERM + * SHORTTERM SHORTTERM LONGTERM + * NB: It is not clear from the spec if the Frame should be marked as short + * or long term in this case + */ +enum h264fw_ereference { + /* Picture is unused for reference */ + H264FW_REF_UNUSED = 0, + /* used for short term reference */ + H264FW_REF_SHORTTERM, + /* used for Long Term reference */ + H264FW_REF_LONGTERM, + H264FW_REF_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the picture structure. + */ +enum h264fw_epicture_type { + /* No valid picture */ + H264FW_TYPE_NONE = 0, + /* Picture contains the top (even) lines of the frame */ + H264FW_TYPE_TOP, + /* Picture contains the bottom (odd) lines of the frame */ + H264FW_TYPE_BOTTOM, + /* Picture contains the entire frame */ + H264FW_TYPE_FRAME, + /* Picture contains an MBAFF frame */ + H264FW_TYPE_MBAFF, + /* Picture contains top and bottom lines of the frame */ + H264FW_TYPE_PAIR, + H264FW_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This describes the SPS header data required by the H264 firmware that should + * be supplied by the Host. + */ +struct h264fw_sequence_ps { + /* syntax elements from SPS */ + /* syntax element from bitstream - 8 bit */ + unsigned char profile_idc; + /* syntax element from bitstream - 2 bit */ + unsigned char chroma_format_idc; + /* syntax element from bitstream - 1 bit */ + unsigned char separate_colour_plane_flag; + /* syntax element from bitstream - 3 bit */ + unsigned char bit_depth_luma_minus8; + /* syntax element from bitstream - 3 bit */ + unsigned char bit_depth_chroma_minus8; + /* syntax element from bitstream - 1 bit */ + unsigned char delta_pic_order_always_zero_flag; + /* syntax element from bitstream - 4+ bit */ + unsigned char log2_max_pic_order_cnt_lsb; + /* syntax element from bitstream - 5 bit */ + unsigned char max_num_ref_frames; + /* syntax element from bitstream - 4+ bit */ + unsigned char log2_max_frame_num; + /* syntax element from bitstream - 2 bit */ + unsigned char pic_order_cnt_type; + /* syntax element from bitstream - 1 bit */ + unsigned char frame_mbs_only_flag; + /* syntax element from bitstream - 1 bit */ + unsigned char gaps_in_frame_num_value_allowed_flag; + + /* + * set0--7 flags as they occur in the bitstream (including reserved + * values) + */ + unsigned char constraint_set_flags; + /* syntax element from bitstream - 8 bit */ + unsigned char level_idc; + /* syntax element from bitstream - 8 bit */ + unsigned char num_ref_frames_in_pic_order_cnt_cycle; + /* syntax element from bitstream - 1 bit */ + unsigned char mb_adaptive_frame_field_flag; + /* syntax element from bitstream - 32 bit */ + int offset_for_non_ref_pic; + /* syntax element from bitstream - 32 bit */ + int offset_for_top_to_bottom_field; + + /* syntax element from bitstream */ + unsigned int pic_width_in_mbs_minus1; + /* syntax element from bitstream */ + unsigned int pic_height_in_map_units_minus1; + /* syntax element from bitstream - 1 bit */ + unsigned char direct_8x8_inference_flag; + /* syntax element from bitstream */ + unsigned char qpprime_y_zero_transform_bypass_flag; + + /* syntax element from bitstream - 32 bit each */ + int offset_for_ref_frame[H264FW_MAX_CYCLE_REF_FRAMES]; + + /* From VUI information */ + unsigned char num_reorder_frames; + /* + * From VUI/MVC SEI, 0 indicates not set, any actual 0 value will be + * inferred by the firmware + */ + unsigned char max_dec_frame_buffering; + + /* From SPS MVC Extension - for the current view_id */ + /* Number of views in this stream */ + unsigned char num_views; + /* a Map in order of VOIdx of view_id's */ + unsigned short view_ids[H264FW_MAX_NUM_VIEWS]; + + /* Disable VDMC horizontal/vertical filtering */ + unsigned char disable_vdmc_filt; + /* Disable CABAC 4:4:4 4x4 transform as not available */ + unsigned char transform4x4_mb_not_available; + + /* anchor reference list */ + unsigned short anchor_inter_view_reference_id_list[2] + [H264FW_MAX_NUM_VIEWS][H264FW_MAX_NUM_MVC_REFS]; + /* nonanchor reference list */ + unsigned short non_anchor_inter_view_reference_id_list[2] + [H264FW_MAX_NUM_VIEWS][H264FW_MAX_NUM_MVC_REFS]; + /* number of elements in aui16AnchorInterViewReferenceIndiciesLX[] */ + unsigned short num_anchor_refsx[2][H264FW_MAX_NUM_VIEWS]; + /* number of elements in aui16NonAnchorInterViewReferenceIndiciesLX[] */ + unsigned short num_non_anchor_refsx[2][H264FW_MAX_NUM_VIEWS]; +}; + +/* + * This structure represents HRD parameters. + */ +struct h264fw_hrd { + /* cpb_cnt_minus1 */ + unsigned char cpb_cnt_minus1; + /* bit_rate_scale */ + unsigned char bit_rate_scale; + /* cpb_size_scale */ + unsigned char cpb_size_scale; + /* bit_rate_value_minus1 */ + unsigned int bit_rate_value_minus1[H264_MAXIMUMVALUEOFCPB_CNT]; + /* cpb_size_value_minus1 */ + unsigned int cpb_size_value_minus1[H264_MAXIMUMVALUEOFCPB_CNT]; + /* cbr_flag */ + unsigned char cbr_flag[H264_MAXIMUMVALUEOFCPB_CNT]; + /* initial_cpb_removal_delay_length_minus1 */ + unsigned char initial_cpb_removal_delay_length_minus1; + /* cpb_removal_delay_length_minus1 */ + unsigned char cpb_removal_delay_length_minus1; + /* dpb_output_delay_length_minus1 */ + unsigned char dpb_output_delay_length_minus1; + /* time_offset_length */ + unsigned char time_offset_length; +}; + +/* + * This structure represents the VUI parameters data. + */ +struct h264fw_vui { + int aspect_ratio_info_present_flag; + unsigned char aspect_ratio_idc; + unsigned short sar_width; + unsigned short sar_height; + int overscan_info_present_flag; + int overscan_appropriate_flag; + int video_signal_type_present_flag; + unsigned char video_format; + int video_full_range_flag; + int colour_description_present_flag; + unsigned char colour_primaries; + unsigned char transfer_characteristics; + unsigned char matrix_coefficients; + int chroma_location_info_present_flag; + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + int timing_info_present_flag; + unsigned int num_units_in_tick; + unsigned int time_scale; + int fixed_frame_rate_flag; + int nal_hrd_parameters_present_flag; + struct h264fw_hrd nal_hrd_params; + int vcl_hrd_parameters_present_flag; + struct h264fw_hrd vcl_hrd_params; + int low_delay_hrd_flag; + int pic_struct_present_flag; + int bitstream_restriction_flag; + int motion_vectors_over_pic_boundaries_flag; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_mb_denom; + unsigned int log2_max_mv_length_vertical; + unsigned int log2_max_mv_length_horizontal; + unsigned int num_reorder_frames; + unsigned int max_dec_frame_buffering; +}; + +/* + * This describes the HW specific SPS header data required by the H264 + * firmware that should be supplied by the Host. + */ +struct h264fw_ddsequence_ps { + /* pre-packed registers derived from SPS */ + /* Value for CR_VEC_ENTDEC_FE_CONTROL */ + unsigned int regentdec_control; + + /* NB: This register should contain the 4-bit SGM flag */ + /* Value for CR_VEC_H264_FE_SPS0 & CR_VEC_H264_BE_SPS0 combined */ + unsigned int reg_sps0; + /* Value of CR_VEC_H264_BE_INTRA_8x8 */ + unsigned int reg_beintra; + /* Value of CR_VEC_H264_FE_CABAC444 */ + unsigned int reg_fecaabac444; + /* Treat CABAC 4:4:4 4x4 transform as not available */ + unsigned char transform4x4_mb_notavialbale; + /* Disable VDMC horizontal/vertical filtering */ + unsigned char disable_vdmcfilt; +}; + +/* + * This describes the PPS header data required by the H264 firmware that should + * be supplied by the Host. + */ +struct h264fw_picture_ps { + /* syntax elements from the PPS */ + /* syntax element from bitstream - 1 bit */ + unsigned char deblocking_filter_control_present_flag; + /* syntax element from bitstream - 1 bit */ + unsigned char transform_8x8_mode_flag; + /* syntax element from bitstream - 1 bit */ + unsigned char entropy_coding_mode_flag; + /* syntax element from bitstream - 1 bit */ + unsigned char redundant_pic_cnt_present_flag; + + /* syntax element from bitstream - 2 bit */ + unsigned char weighted_bipred_idc; + /* syntax element from bitstream - 1 bit */ + unsigned char weighted_pred_flag; + /* syntax element from bitstream - 1 bit */ + unsigned char pic_order_present_flag; + + /* 26 + syntax element from bitstream - 7 bit */ + unsigned char pic_init_qp; + /* syntax element from bitstream - 1 bit */ + unsigned char constrained_intra_pred_flag; + /* syntax element from bitstream - 5 bit each */ + unsigned char num_ref_lx_active_minus1[H264FW_MAX_REFPIC_LISTS]; + + /* syntax element from bitstream - 3 bit */ + unsigned char slice_group_map_type; + /* syntax element from bitstream - 3 bit */ + unsigned char num_slice_groups_minus1; + /* syntax element from bitstream - 13 bit */ + unsigned short slice_group_change_rate_minus1; + + /* syntax element from bitstream */ + unsigned int chroma_qp_index_offset; + /* syntax element from bitstream */ + unsigned int second_chroma_qp_index_offset; + + /* scaling lists are derived from both SPS and PPS information */ + /* but will change whenever the PPS changes */ + /* The derived set of tables are associated here with the PPS */ + /* NB: These are in H.264 order */ + /* derived from SPS and PPS - 8 bit each */ + unsigned char scalinglist4x4[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]; + /* derived from SPS and PPS - 8 bit each */ + unsigned char scalinglist8x8[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE]; +}; + +/* + * This describes the HW specific PPS header data required by the H264 + * firmware that should be supplied by the Host. + */ +struct h264fw_dd_picture_ps { + /* values derived from the PPS */ + /* Value for MSVDX_CMDS_SLICE_PARAMS_MODE_CONFIG */ + unsigned char vdmc_mode_config; + + /* pre-packed registers derived from the PPS */ + /* Value for CR_VEC_H264_FE_PPS0 & CR_VEC_H264_BE_PPS0 combined */ + unsigned int reg_pps0; + + /* + * scaling lists are derived from both SPS and PPS information + * but will change whenever the PPS changes + * The derived set of tables are associated here with the PPS + * But this will become invalid if the SPS changes and will have to be + * recalculated + * These tables MUST be aligned on a 32-bit boundary + * NB: These are in MSVDX order + */ + /* derived from SPS and PPS - 8 bit each */ + unsigned char scalinglist4x4[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]; + /* derived from SPS and PPS - 8 bit each */ + unsigned char scalinglist8x8[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE]; +}; + +/* + * This describes the H.264 parser component "Header data", shown in the + * Firmware Memory Layout diagram. This data is required by the H264 firmware + * and should be supplied by the Host. + */ +struct h264fw_header_data { + /* Decode buffers and output control for the current picture */ + /* Primary decode buffer base addresses */ + struct vdecfw_image_buffer primary; + /* buffer base addresses for alternate output */ + struct vdecfw_image_buffer alternate; + /* Output control: rotation, scaling, oold, etc. */ + unsigned int pic_cmds[VDECFW_CMD_MAX]; + /* Macroblock parameters base address for the picture */ + unsigned int mbparams_base_address; + + unsigned int mbparams_size_per_plane; + + /* Buffers for context preload for colour plane switching (6.x.x) */ + unsigned int preload_buffer_base_address + [H264FW_MAX_PLANES]; + + /* + * slice group map should be calculated on Host + * (using some slice params) and base address provided here + */ + /* Base address of active slice group map */ + /* Base address of active slice group map */ + unsigned int slicegroupmap_base_address; + + /* H264 specific control */ + /* do second pass Intra Deblock on frame */ + unsigned int do_old __attribute__ (aligned(4)); + /* set to IMG_FALSE to disable second-pass deblock */ + unsigned int two_pass_flag __attribute__ (aligned(4)); + /* set to IMG_TRUE to disable MVC */ + unsigned int disable_mvc __attribute__ (aligned(4)); + /* + * Do we have second PPS in uipSecondPPSInfoSource provided for the + * second field + */ + unsigned int second_pps __attribute__ (aligned(4)); +}; + +/* + * This describes an H.264 picture. It is part of the Context data + */ +struct h264fw_picture { + /* Primary (reconstructed) picture buffers */ + struct vdecfw_image_buffer primary; + /* Secondary (alternative) picture buffers */ + struct vdecfw_image_buffer alternate; + /* Macroblock parameters base address for the picture */ + unsigned int mbparams_base_address; + + /* Unique ID for this picture */ + unsigned int transaction_id; + /* Picture type */ + enum h264fw_epicture_type pricture_type; + + /* Reference status of the picture */ + enum h264fw_ereference ref_status_bottom; + /* Reference status of the picture */ + enum h264fw_ereference ref_status_top; + /* Reference status of the picture */ + enum h264fw_ereference ref_status_frame; + + /* Frame Number */ + unsigned int frame_number; + /* Short term reference info */ + int fame_number_wrap; + /* long term reference number - should be 8-bit */ + unsigned int longterm_frame_idx; + + /* Top field order count for this picture */ + int top_field_order_count; + /* Bottom field order count for this picture */ + int bottom_field_order_count; + /* MVC view_id */ + unsigned short view_id; + /* + * When picture is in the DPB Offset to use into the MSVDX DPB reg table + * when the current picture is the same view as this. + */ + unsigned char view_dpb_offset; + /* Flags for this picture for the display process */ + unsigned char display_flags; + + /* IMG_FALSE if sent to display, or otherwise not needed for display */ + unsigned char needed_for_output; +}; + +/* + * This structure describes frame data for POC calculation + */ +struct h264fw_poc_picture_data { + /* type 0,1,2 */ + unsigned char mmco_5_flag; + + /* type 0 */ + unsigned char bottom_field_flag; + unsigned short pic_order_cnt_lsb; + int top_field_order_count; + int pic_order_count_msb; + + /* type 1,2 */ + int16 frame_num; + int frame_num_offset; + + /* output */ + int bottom_filed_order_count; +}; + +/* + * This structure describes picture data for determining Complementary + * Field Pairs + */ +struct h264fw_last_pic_data { + /* Unique ID for this picture */ + unsigned int transaction_id; + /* Picture type */ + enum h264fw_epicture_type picture_type; + /* Reference status of the picture */ + enum h264fw_ereference ref_status_frame; + /* Frame Number */ + unsigned int frame_number; + + unsigned int luma_recon; + unsigned int chroma_recon; + unsigned int chroma_2_recon; + unsigned int luma_alter; + unsigned int chroma_alter; + unsigned int chroma_2_alter; + struct vdecfw_image_buffer primary; + struct vdecfw_image_buffer alternate; + unsigned int mbparams_base_address; + /* Top field order count for this picture */ + int top_field_order_count; + /* Bottom field order count for this picture */ + int bottom_field_order_count; +}; + +/* + * This describes the H.264 parser component "Context data", shown in the + * Firmware Memory Layout diagram. This data is the state preserved across + * pictures. It is loaded and saved by the Firmware, but requires the host to + * provide buffer(s) for this. + */ +struct h264fw_context_data { + /* Decoded Picture Buffer */ + struct h264fw_picture dpb[H264FW_MAX_DPB_SIZE]; + /* + * Inter-view reference components - also used as detail of the previous + * picture for any particular view, can be used to determine + * complemetary field pairs + */ + struct h264fw_picture interview_prediction_ref[H264FW_MAX_NUM_VIEWS]; + /* previous ref pic for type0, previous pic for type1&2 */ + struct h264fw_poc_picture_data prev_poc_pic_data[H264FW_MAX_NUM_VIEWS]; + /* previous picture information to detect complementary field pairs */ + struct h264fw_last_pic_data last_pic_data[H264FW_MAX_NUM_VIEWS]; + struct h264fw_last_pic_data last_displayed_pic_data + [H264FW_MAX_NUM_VIEWS]; + + /* previous reference frame number for each view */ + unsigned short prev_ref_frame_num[H264FW_MAX_NUM_VIEWS]; + /* Bitmap of used slots in each view DPB */ + unsigned short dpb_bitmap[H264FW_MAX_NUM_VIEWS]; + + /* DPB size */ + unsigned int dpb_size; + /* Number of pictures in DPB */ + unsigned int dpb_fullness; + + unsigned char prev_display_flags; + int prev_display; + int prev_release; + /* Active parameter sets */ + /* Sequence Parameter Set data */ + struct h264fw_sequence_ps sps; + /* Picture Parameter Set data */ + struct h264fw_picture_ps pps; + /* + * Picture Parameter Set data for second field when in the same buffer + */ + struct h264fw_picture_ps second_pps; + + /* Set if stream is MVC */ + int mvc; + /* DPB long term reference information */ + int max_longterm_frame_idx[H264FW_MAX_NUM_VIEWS]; +}; + +#endif /* _H264FW_DATA_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/h264fw_data_shared.h b/drivers/media/platform/vxe-vxd/decoder/h264fw_data_shared.h --- a/drivers/media/platform/vxe-vxd/decoder/h264fw_data_shared.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/h264fw_data_shared.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,759 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures for the h264 parser firmware module + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#ifdef USE_SHARING +#endif + +#ifndef _H264FW_DATA_H_ +#define _H264FW_DATA_H_ + +#include "vdecfw_share.h" +#include "vdecfw_shared.h" + +#define H264_MAX_SPS_COUNT 32 +#define H264_MAX_PPS_COUNT 256 + +#define H264_SCALING_LISTS_NUM_CHROMA_IDC_NON_3 (8) +#define H264_SCALING_LISTS_NUM_CHROMA_IDC_3 (12) +#define MAX_PIC_SCALING_LIST (12) + +/* Maximum number of alternative CPB specifications in the stream */ +#define H264_MAXIMUMVALUEOFCPB_CNT 32 + +/* + * The maximum DPB size is related to the number of MVC views supported + * The size is defined in H.10.2 for the H.264 spec. + * If the number of views needs to be changed the DPB size should be too + * The limits are as follows: + * NumViews: 1, 2, 4, 8, 16 + * MaxDpbFrames: 16, 16, 32, 48, 64 + */ + +#define H264FW_MAX_NUM_VIEWS 1 +#define H264FW_MAX_DPB_SIZE 16 +#define H264FW_MAX_NUM_MVC_REFS 1 + +/* Number of H264 VLC table configuration registers */ +#define H264FW_NUM_VLC_REG 22 + +/* Maximum value for num_ref_frames_in_pic_order_cnt_cycle */ +#define H264FW_MAX_CYCLE_REF_FRAMES 256 + +/* 4x4 scaling list size */ +#define H264FW_4X4_SIZE 16 +/* 8x8 scaling list size */ +#define H264FW_8X8_SIZE 64 +/* Number of 4x4 scaling lists */ +#define H264FW_NUM_4X4_LISTS 6 +/* Number of 8x8 scaling lists */ +#define H264FW_NUM_8X8_LISTS 6 + +/* Number of reference picture lists */ +#define H264FW_MAX_REFPIC_LISTS 2 + +/* + * The maximum number of slice groups + * remove if slice group map is prepared on the host + */ +#define H264FW_MAX_SLICE_GROUPS 8 + +/* The maximum number of planes for 4:4:4 separate colour plane streams */ +#define H264FW_MAX_PLANES 3 + +#define H264_MAX_SGM_SIZE 8196 + +#define IS_H264_HIGH_PROFILE(profile_idc, type) \ + ({ \ + type __profile_idc = profile_idc; \ + (__profile_idc == H264_PROFILE_HIGH) || \ + (__profile_idc == H264_PROFILE_HIGH10) || \ + (__profile_idc == H264_PROFILE_HIGH422) || \ + (__profile_idc == H264_PROFILE_HIGH444) || \ + (__profile_idc == H264_PROFILE_CAVLC444) || \ + (__profile_idc == H264_PROFILE_MVC_HIGH) || \ + (__profile_idc == H264_PROFILE_MVC_STEREO); }) \ + +/* This type describes the H.264 NAL unit types */ +enum h264_enaltype { + H264FW_NALTYPE_SLICE = 1, + H264FW_NALTYPE_IDRSLICE = 5, + H264FW_NALTYPE_SEI = 6, + H264FW_NALTYPE_SPS = 7, + H264FW_NALTYPE_PPS = 8, + H264FW_NALTYPE_AUD = 9, + H264FW_NALTYPE_EOSEQ = 10, + H264FW_NALTYPE_EOSTR = 11, + H264FW_NALTYPE_PREFIX = 14, + H264FW_NALTYPE_SUBSET_SPS = 15, + H264FW_NALTYPE_AUXILIARY_SLICE = 19, + H264FW_NALTYPE_EXTSLICE = 20, + H264FW_NALTYPE_EXTSLICE_DEPTH_VIEW = 21, + H264FW_NALTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* AVC Profile IDC definitions */ +enum h264_eprofileidc { + H264_PROFILE_CAVLC444 = 44, + H264_PROFILE_BASELINE = 66, + H264_PROFILE_MAIN = 77, + H264_PROFILE_SCALABLE = 83, + H264_PROFILE_EXTENDED = 88, + H264_PROFILE_HIGH = 100, + H264_PROFILE_HIGH10 = 110, + H264_PROFILE_MVC_HIGH = 118, + H264_PROFILE_HIGH422 = 122, + H264_PROFILE_MVC_STEREO = 128, + H264_PROFILE_HIGH444 = 244, + H264_PROFILE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* This type defines the constraint set flags */ +enum h264fw_econstraint_flag { + H264FW_CONSTRAINT_BASELINE_SHIFT = 7, + H264FW_CONSTRAINT_MAIN_SHIFT = 6, + H264FW_CONSTRAINT_EXTENDED_SHIFT = 5, + H264FW_CONSTRAINT_INTRA_SHIFT = 4, + H264FW_CONSTRAINT_MULTIHIGH_SHIFT = 3, + H264FW_CONSTRAINT_STEREOHIGH_SHIFT = 2, + H264FW_CONSTRAINT_RESERVED6_SHIFT = 1, + H264FW_CONSTRAINT_RESERVED7_SHIFT = 0, + H264FW_CONSTRAINT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This enum describes the reference status of an H.264 picture. + * + * Unpaired fields should have all eRefStatusX set to the same value + * + * For Frame, Mbaff, and Pair types individual fields and frame ref status + * should be set accordingly. + * + * eRefStatusFrame eRefStatusTop eRefStatusBottom + * UNUSED UNUSED UNUSED + * SHORTTERM SHORTTERM SHORTTERM + * LONGTERM LONGTERM LONGTERM + * + * UNUSED SHORT/LONGTERM UNUSED + * UNUSED UNUSED SHORT/LONGTERM + * + * SHORTTERM LONGTERM SHORTTERM + * SHORTTERM SHORTTERM LONGTERM + * - NB: It is not clear from the spec if the Frame should be marked as short + * or long term in this case + */ +enum h264fw_ereference { + H264FW_REF_UNUSED = 0, + H264FW_REF_SHORTTERM, + H264FW_REF_LONGTERM, + H264FW_REF_FORCE32BITS = 0x7FFFFFFFU +}; + +/* This type defines the picture structure. */ +enum h264fw_epicture_type { + H264FW_TYPE_NONE = 0, + H264FW_TYPE_TOP, + H264FW_TYPE_BOTTOM, + H264FW_TYPE_FRAME, + H264FW_TYPE_MBAFF, + H264FW_TYPE_PAIR, + H264FW_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This describes the SPS header data required by the H264 firmware that should + * be supplied by the Host. + */ +struct h264fw_sequence_ps { + /* syntax elements from SPS */ + + /* syntax element from bitstream - 8 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, profile_idc); + /* syntax element from bitstream - 2 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_format_idc); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, separate_colour_plane_flag); + /* syntax element from bitstream - 3 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, bit_depth_luma_minus8); + /* syntax element from bitstream - 3 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, bit_depth_chroma_minus8); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, delta_pic_order_always_zero_flag); + /* syntax element from bitstream - 4+ bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_max_pic_order_cnt_lsb); + + /* syntax element from bitstream - 5 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, max_num_ref_frames); + /* syntax element from bitstream - 4+ bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_max_frame_num); + /* syntax element from bitstream - 2 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pic_order_cnt_type); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, frame_mbs_only_flag); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, gaps_in_frame_num_value_allowed_flag); + + /* + * set0--7 flags as they occur in the bitstream + * (including reserved values) + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, constraint_set_flags); + /* syntax element from bitstream - 8 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, level_idc); + /* syntax element from bitstream - 8 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_ref_frames_in_pic_order_cnt_cycle); + + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, mb_adaptive_frame_field_flag); + /* syntax element from bitstream - 32 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, offset_for_non_ref_pic); + /* syntax element from bitstream - 32 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, offset_for_top_to_bottom_field); + + /* syntax element from bitstream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pic_width_in_mbs_minus1); + /* syntax element from bitstream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pic_height_in_map_units_minus1); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, direct_8x8_inference_flag); + /* syntax element from bitstream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, qpprime_y_zero_transform_bypass_flag); + + /* syntax element from bitstream - 32 bit each */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, offset_for_ref_frame[H264FW_MAX_CYCLE_REF_FRAMES]); + + /* From VUI information */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_reorder_frames); + /* + * From VUI/MVC SEI, 0 indicates not set, any actual 0 + * value will be inferred by the firmware + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, max_dec_frame_buffering); + + /* From SPS MVC Extension - for the current view_id */ + + /* Number of views in this stream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_views); + /* a Map in order of VOIdx of view_id's */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, view_ids[H264FW_MAX_NUM_VIEWS]); + + /* Disable VDMC horizontal/vertical filtering */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, disable_vdmc_filt); + /* Disable CABAC 4:4:4 4x4 transform as not available */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transform4x4_mb_not_available); + + /* anchor reference list */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + anchor_inter_view_reference_id_list[2][H264FW_MAX_NUM_VIEWS] + [H264FW_MAX_NUM_MVC_REFS]); + /* nonanchor reference list */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + non_anchor_inter_view_reference_id_list[2][H264FW_MAX_NUM_VIEWS] + [H264FW_MAX_NUM_MVC_REFS]); + /* number of elements in aui16AnchorInterViewReferenceIndiciesLX[] */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + num_anchor_refsx[2][H264FW_MAX_NUM_VIEWS]); + /* number of elements in aui16NonAnchorInterViewReferenceIndiciesLX[] */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + num_non_anchor_refsx[2][H264FW_MAX_NUM_VIEWS]); +}; + +/* + * This structure represents HRD parameters. + */ +struct h264fw_hrd { + /* cpb_cnt_minus1; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cpb_cnt_minus1); + /* bit_rate_scale; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, bit_rate_scale); + /* cpb_size_scale; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cpb_size_scale); + /* bit_rate_value_minus1 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + bit_rate_value_minus1[H264_MAXIMUMVALUEOFCPB_CNT]); + /* cpb_size_value_minus1 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + cpb_size_value_minus1[H264_MAXIMUMVALUEOFCPB_CNT]); + /* cbr_flag */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + cbr_flag[H264_MAXIMUMVALUEOFCPB_CNT]); + /* initial_cpb_removal_delay_length_minus1; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + initial_cpb_removal_delay_length_minus1); + /* cpb_removal_delay_length_minus1; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + cpb_removal_delay_length_minus1); + /* dpb_output_delay_length_minus1; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + dpb_output_delay_length_minus1); + /* time_offset_length; */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, time_offset_length); +}; + +/* + * This structure represents the VUI parameters data. + */ +struct h264fw_vui { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, aspect_ratio_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, aspect_ratio_idc); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, sar_width); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, sar_height); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, overscan_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, overscan_appropriate_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, video_signal_type_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, video_format); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, video_full_range_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, colour_description_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, colour_primaries); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transfer_characteristics); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, matrix_coefficients); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, chroma_location_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, chroma_sample_loc_type_top_field); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, chroma_sample_loc_type_bottom_field); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, timing_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, num_units_in_tick); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, time_scale); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, fixed_frame_rate_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, nal_hrd_parameters_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + struct h264fw_hrd, nal_hrd_params); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, vcl_hrd_parameters_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + struct h264fw_hrd, vcl_hrd_params); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, low_delay_hrd_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pic_struct_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, bitstream_restriction_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, motion_vectors_over_pic_boundaries_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, max_bytes_per_pic_denom); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, max_bits_per_mb_denom); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, log2_max_mv_length_vertical); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, log2_max_mv_length_horizontal); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, num_reorder_frames); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, max_dec_frame_buffering); +}; + +/* + * This describes the HW specific SPS header data required by the H264 + * firmware that should be supplied by the Host. + */ +struct h264fw_ddsequence_ps { + /* Value for CR_VEC_ENTDEC_FE_CONTROL */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, regentdec_control); + + /* NB: This register should contain the 4-bit SGM flag */ + + /* Value for CR_VEC_H264_FE_SPS0 & CR_VEC_H264_BE_SPS0 combined */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, reg_sps0); + /* Value of CR_VEC_H264_BE_INTRA_8x8 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, reg_beintra); + /* Value of CR_VEC_H264_FE_CABAC444 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, reg_fecaabac444); + + /* Treat CABAC 4:4:4 4x4 transform as not available */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + transform4x4_mb_notavialbale); + /* Disable VDMC horizontal/vertical filtering */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + disable_vdmcfilt); +}; + +/* + * This describes the PPS header data required by the H264 firmware that should + * be supplied by the Host. + */ +struct h264fw_picture_ps { + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + deblocking_filter_control_present_flag); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + transform_8x8_mode_flag); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + entropy_coding_mode_flag); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + redundant_pic_cnt_present_flag); + + /* syntax element from bitstream - 2 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + weighted_bipred_idc); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + weighted_pred_flag); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + pic_order_present_flag); + + /* 26 + syntax element from bitstream - 7 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, pic_init_qp); + /* syntax element from bitstream - 1 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + constrained_intra_pred_flag); + /* syntax element from bitstream - 5 bit each */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + num_ref_lx_active_minus1[H264FW_MAX_REFPIC_LISTS]); + + /* syntax element from bitstream - 3 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + slice_group_map_type); + /* syntax element from bitstream - 3 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + num_slice_groups_minus1); + /* syntax element from bitstream - 13 bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + slice_group_change_rate_minus1); + + /* syntax element from bitstream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, + chroma_qp_index_offset); + /* syntax element from bitstream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, + second_chroma_qp_index_offset); + + /* + * scaling lists are derived from both SPS and PPS information + * but will change whenever the PPS changes + * The derived set of tables are associated here with the PPS + * NB: These are in H.264 order + */ + + /* derived from SPS and PPS - 8 bit each */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + scalinglist4x4[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]); + /* derived from SPS and PPS - 8 bit each */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + scalinglist8x8[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE]); +}; + +/* + * This describes the HW specific PPS header data required by the H264 + * firmware that should be supplied by the Host. + */ +struct h264fw_dd_picture_ps { + /* Value for MSVDX_CMDS_SLICE_PARAMS_MODE_CONFIG */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + vdmc_mode_config); + /* Value for CR_VEC_H264_FE_PPS0 & CR_VEC_H264_BE_PPS0 combined */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, reg_pps0); + + /* + * Scaling lists are derived from both SPS and PPS information + * but will change whenever the PPS changes. The derived set of tables + * are associated here with the PPS, but this will become invalid if + * the SPS changes and will have to be recalculated. + * These tables MUST be aligned on a 32-bit boundary + * NB: These are in MSVDX order + */ + + /* derived from SPS and PPS - 8 bit each */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + scalinglist4x4[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]); + /* derived from SPS and PPS - 8 bit each */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + scalinglist8x8[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE]); +}; + +/* + * This describes the H.264 parser component "Header data", shown in the + * Firmware Memory Layout diagram. This data is required by the H264 firmware + * and should be supplied by the Host. + */ +struct h264fw_header_data { + struct vdecfw_image_buffer primary; + struct vdecfw_image_buffer alternate; + + /* Output control: rotation, scaling, oold, etc. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + pic_cmds[VDECFW_CMD_MAX]); + /* Macroblock parameters base address for the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + mbparams_base_address); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + mbparams_size_per_plane); + /* Buffers for context preload for colour plane switching (6.x.x) */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + preload_buffer_base_address[H264FW_MAX_PLANES]); + /* Base address of active slice group map */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + slicegroupmap_base_address); + + /* do second pass Intra Deblock on frame */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, do_old); + /* set to IMG_FALSE to disable second-pass deblock */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + two_pass_flag); + /* set to IMG_TRUE to disable MVC */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + disable_mvc); + /* + * Do we have second PPS in uipSecondPPSInfoSource provided + * for the second field. + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + second_pps); +}; + +/* This describes an H.264 picture. It is part of the Context data */ +struct h264fw_picture { + /* Primary (reconstructed) picture buffers */ + struct vdecfw_image_buffer primary; + /* Secondary (alternative) picture buffers */ + struct vdecfw_image_buffer alternate; + /* Macroblock parameters base address for the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, mbparams_base_address); + + /* Unique ID for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, transaction_id); + /* Picture type */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum h264fw_epicture_type, pricture_type); + + /* Reference status of the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum h264fw_ereference, ref_status_bottom); + /* Reference status of the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum h264fw_ereference, ref_status_top); + /* Reference status of the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum h264fw_ereference, ref_status_frame); + + /* Frame Number */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, frame_number); + /* Short term reference info */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, frame_number_wrap); + /* long term reference number - should be 8-bit */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, longterm_frame_idx); + + /* Top field order count for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, top_field_order_count); + /* Bottom field order count for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, bottom_field_order_count); + + /* MVC view_id */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, view_id); + + /* + * When picture is in the DPB Offset to use into + * the MSVDX DPB reg table when the current + * picture is the same view as this. + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, view_dpb_offset); + /* Flags for this picture for the display process */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, display_flags); + + /* IMG_FALSE if sent to display, or otherwise not needed for display */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, needed_for_output); +}; + +/* This structure describes frame data for POC calculation */ +struct h264fw_poc_picture_data { + /* type 0,1,2 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, mmco_5_flag); + + /* type 0 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, bottom_field_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, pic_order_cnt_lsb); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, top_field_order_count); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pic_order_count_msb); + + /* type 1,2 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, short, frame_num); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, frame_num_offset); + + /* output */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, bottom_filed_order_count); +}; + +/* + * This structure describes picture data for determining + * Complementary Field Pairs + */ +struct h264fw_last_pic_data { + /* Unique ID for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, transaction_id); + /* Picture type */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum h264fw_epicture_type, picture_type); + /* Reference status of the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum h264fw_ereference, ref_status_frame); + /* Frame Number */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, frame_number); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, luma_recon); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, chroma_recon); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, chroma_2_recon); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, luma_alter); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, chroma_alter); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, chroma_2_alter); + + struct vdecfw_image_buffer primary; + struct vdecfw_image_buffer alternate; + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, mbparams_base_address); + /* Top field order count for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, top_field_order_count); + /* Bottom field order count for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, bottom_field_order_count); +}; + +/* + * This describes the H.264 parser component "Context data", shown in the + * Firmware Memory Layout diagram. This data is the state preserved across + * pictures. It is loaded and saved by the Firmware, but requires the host to + * provide buffer(s) for this. + */ +struct h264fw_context_data { + struct h264fw_picture dpb[H264FW_MAX_DPB_SIZE]; + /* + * Inter-view reference components - also used as detail of the previous + * picture for any particular view, can be used to determine + * complemetary field pairs + */ + struct h264fw_picture interview_prediction_ref[H264FW_MAX_NUM_VIEWS]; + /* previous ref pic for type0, previous pic for type1&2 */ + struct h264fw_poc_picture_data prev_poc_pic_data[H264FW_MAX_NUM_VIEWS]; + /* previous picture information to detect complementary field pairs */ + struct h264fw_last_pic_data last_pic_data[H264FW_MAX_NUM_VIEWS]; + struct h264fw_last_pic_data + last_displayed_pic_data[H264FW_MAX_NUM_VIEWS]; + + /* previous reference frame number for each view */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + prev_ref_frame_num[H264FW_MAX_NUM_VIEWS]); + /* Bitmap of used slots in each view DPB */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, + dpb_bitmap[H264FW_MAX_NUM_VIEWS]); + + /* DPB size */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, dpb_size); + /* Number of pictures in DPB */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + dpb_fullness); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + prev_display_flags); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, prev_display); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, prev_release); + /* Sequence Parameter Set data */ + struct h264fw_sequence_ps sps; + /* Picture Parameter Set data */ + struct h264fw_picture_ps pps; + /* Picture Parameter Set data for second field if in the same buffer */ + struct h264fw_picture_ps second_pps; + + /* Set if stream is MVC */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, mvc); + /* DPB long term reference information */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, + max_longterm_frame_idx[H264FW_MAX_NUM_VIEWS]); +}; + +#endif /* _H264FW_DATA_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/h264_idx.h b/drivers/media/platform/vxe-vxd/decoder/h264_idx.h --- a/drivers/media/platform/vxe-vxd/decoder/h264_idx.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/h264_idx.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * h264 idx table definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + */ + +#ifndef __H264_IDX_H__ +#define __H264_IDX_H__ + +#include + +static unsigned short h264_vlc_index_data[38][3] = { + { 2, 5, 0 }, /* NumCoeffTrailingOnes_Table9-5_nC_0-1.out */ + { 0, 3, 76 }, /* NumCoeffTrailingOnes_Table9-5_nC_2-3.out */ + { 0, 3, 160 }, /* NumCoeffTrailingOnes_Table9-5_nC_4-7.out */ + { 0, 2, 231 }, /* NumCoeffTrailingOnesFixedLen.out */ + { 2, 2, 244 }, /* NumCoeffTrailingOnesChromaDC_YUV420.out */ + { 2, 5, 261 }, /* NumCoeffTrailingOnesChromaDC_YUV422.out */ + { 2, 5, 301 }, /* TotalZeros_00.out */ + { 0, 2, 326 }, /* TotalZeros_01.out */ + { 0, 2, 345 }, /* TotalZeros_02.out */ + { 0, 2, 363 }, /* TotalZeros_03.out */ + { 0, 2, 379 }, /* TotalZeros_04.out */ + { 0, 2, 394 }, /* TotalZeros_05.out */ + { 0, 2, 406 }, /* TotalZeros_06.out */ + { 0, 1, 418 }, /* TotalZeros_07.out */ + { 0, 1, 429 }, /* TotalZeros_08.out */ + { 0, 1, 438 }, /* TotalZeros_09.out */ + { 2, 2, 446 }, /* TotalZeros_10.out */ + { 2, 2, 452 }, /* TotalZeros_11.out */ + { 2, 1, 456 }, /* TotalZeros_12.out */ + { 0, 0, 459 }, /* TotalZeros_13.out */ + { 0, 0, 461 }, /* TotalZeros_14.out */ + { 2, 2, 463 }, /* TotalZerosChromaDC_YUV420_00.out */ + { 2, 1, 467 }, /* TotalZerosChromaDC_YUV420_01.out */ + { 0, 0, 470 }, /* TotalZerosChromaDC_YUV420_02.out */ + { 0, 0, 472 }, /* Run_00.out */ + { 2, 1, 474 }, /* Run_01.out */ + { 0, 1, 477 }, /* Run_02.out */ + { 0, 1, 481 }, /* Run_03.out */ + { 1, 1, 487 }, /* Run_04.out */ + { 0, 2, 494 }, /* Run_05.out */ + { 0, 2, 502 }, /* Run_06.out */ + { 2, 4, 520 }, /* TotalZerosChromaDC_YUV422_00.out */ + { 2, 2, 526 }, /* TotalZerosChromaDC_YUV422_01.out */ + { 0, 1, 530 }, /* TotalZerosChromaDC_YUV422_02.out */ + { 1, 2, 534 }, /* TotalZerosChromaDC_YUV422_03.out */ + { 0, 0, 538 }, /* TotalZerosChromaDC_YUV422_04.out */ + { 0, 0, 540 }, /* TotalZerosChromaDC_YUV422_05.out */ + { 0, 0, 542 }, /* TotalZerosChromaDC_YUV422_06.out */ +}; + +static const unsigned char h264_vlc_index_size = 38; + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.c b/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.c --- a/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,3051 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * h.264 secure data unit parsing API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "bspp.h" +#include "bspp_int.h" +#include "h264_secure_parser.h" +#include "pixel_api.h" +#include "swsr.h" +#include "vdec_defs.h" + +/* + * Reduce DPB to 1 when no pic reordering. + */ +#define SL_MAX_REF_IDX 32 +#define VUI_CPB_CNT_MAX 32 +#define MAX_SPS_COUNT 32 +#define MAX_PPS_COUNT 256 +/* changed from 810 */ +#define MAX_SLICE_GROUPMBS 65536 +#define MAX_SPS_COUNT 32 +#define MAX_PPS_COUNT 256 +#define MAX_SLICEGROUP_COUNT 8 +#define MAX_WIDTH_IN_MBS 256 +#define MAX_HEIGHT_IN_MBS 256 +#define MAX_COLOR_PLANE 4 +#define H264_MAX_SGM_SIZE 8196 + +#define H264_MAX_CHROMA_QP_INDEX_OFFSET (12) +#define H264_MIN_CHROMA_QP_INDEX_OFFSET (-12) + +/* + * AVC Profile IDC definitions + */ +enum h264_profile_idc { + h264_profile_cavlc444 = 44, /* YUV 4:4:4/14 "CAVLC 4:4:4" */ + h264_profile_baseline = 66, /* YUV 4:2:0/8 "Baseline" */ + h264_profile_main = 77, /* YUV 4:2:0/8 "Main" */ + h264_profile_scalable = 83, /* YUV 4:2:0/8 "Scalable" */ + h264_profile_extended = 88, /* YUV 4:2:0/8 "Extended" */ + h264_profile_high = 100, /* YUV 4:2:0/8 "High" */ + h264_profile_hig10 = 110, /* YUV 4:2:0/10 "High 10" */ + h264_profile_mvc_high = 118, /* YUV 4:2:0/8 "Multiview High" */ + h264_profile_high422 = 122, /* YUV 4:2:2/10 "High 4:2:2" */ + h264_profile_mvc_stereo = 128, /* YUV 4:2:0/8 "Stereo High" */ + h264_profile_high444 = 244, /* YUV 4:4:4/14 "High 4:4:4" */ + h264_profile_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Remap H.264 colour format into internal representation. + */ +static const enum pixel_fmt_idc pixel_format_idc[] = { + PIXEL_FORMAT_MONO, + PIXEL_FORMAT_420, + PIXEL_FORMAT_422, + PIXEL_FORMAT_444, +}; + +/* + * Pixel Aspect Ratio + */ +static const unsigned short pixel_aspect[17][2] = { + { 0, 1 }, + { 1, 1 }, + { 12, 11 }, + { 10, 11 }, + { 16, 11 }, + { 40, 33 }, + { 24, 11 }, + { 20, 11 }, + { 32, 11 }, + { 80, 33 }, + { 18, 11 }, + { 15, 11 }, + { 64, 33 }, + { 160, 99 }, + { 4, 3 }, + { 3, 2 }, + { 2, 1 }, +}; + +/* + * Table 7-3, 7-4: Default Scaling lists + */ +static const unsigned char default_4x4_intra[16] = { + 6, 13, 13, 20, + 20, 20, 28, 28, + 28, 28, 32, 32, + 32, 37, 37, 42 +}; + +static const unsigned char default_4x4_inter[16] = { + 10, 14, 14, 20, + 20, 20, 24, 24, + 24, 24, 27, 27, + 27, 30, 30, 34 +}; + +static const unsigned char default_8x8_intra[64] = { + 6, 10, 10, 13, 11, 13, 16, 16, + 16, 16, 18, 18, 18, 18, 18, 23, + 23, 23, 23, 23, 23, 25, 25, 25, + 25, 25, 25, 25, 27, 27, 27, 27, + 27, 27, 27, 27, 29, 29, 29, 29, + 29, 29, 29, 31, 31, 31, 31, 31, + 31, 33, 33, 33, 33, 33, 36, 36, + 36, 36, 38, 38, 38, 40, 40, 42 +}; + +static const unsigned char default_8x8_inter[64] = { + 9, 13, 13, 15, 13, 15, 17, 17, + 17, 17, 19, 19, 19, 19, 19, 21, + 21, 21, 21, 21, 21, 22, 22, 22, + 22, 22, 22, 22, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, + 25, 25, 25, 27, 27, 27, 27, 27, + 27, 28, 28, 28, 28, 28, 30, 30, + 30, 30, 32, 32, 32, 33, 33, 35 +}; + +/* + * to be use if no q matrix is chosen + */ +static const unsigned char default_4x4_org[16] = { + 16, 16, 16, 16, + 16, 16, 16, 16, + 16, 16, 16, 16, + 16, 16, 16, 16 +}; + +/* + * to be use if no q matrix is chosen + */ +static const unsigned char default_8x8_org[64] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16 +}; + +/* + * source: ITU-T H.264 2010/03, page 20 Table 6-1 + */ +static const int bspp_subheightc[] = { -1, 2, 1, 1 }; + +/* + * source: ITU-T H.264 2010/03, page 20 Table 6-1 + */ +static const int bspp_subwidthc[] = { -1, 2, 2, 1 }; + +/* + * inline functions for Minimum and Maximum value + */ +static inline unsigned int umin(unsigned int a, unsigned int b) +{ + return (((a) < (b)) ? (a) : (b)); +} + +static inline int smin(int a, int b) +{ + return (((a) < (b)) ? (a) : (b)); +} + +static inline int smax(int a, int b) +{ + return (((a) > (b)) ? (a) : (b)); +} + +static void set_if_not_determined_yet(int *determined, + unsigned char condition, + int *target, + unsigned int value) +{ + if ((!(*determined)) && (condition)) { + *target = value; + *determined = 1; + } +} + +static int bspp_h264_get_subwidthc(int chroma_format_idc, int separate_colour_plane_flag) +{ + return bspp_subwidthc[chroma_format_idc]; +} + +static int bspp_h264_get_subheightc(int chroma_format_idc, int separate_colour_plane_flag) +{ + return bspp_subheightc[chroma_format_idc]; +} + +static unsigned int h264ceillog2(unsigned int value) +{ + unsigned int status = 0; + + value -= 1; + while (value > 0) { + value >>= 1; + status++; + } + return status; +} + +/* + * @Function bspp_h264_set_default_vui + * @Description Sets default values of the VUI info + */ +static void bspp_h264_set_default_vui(struct bspp_h264_vui_info *vui_info) +{ + unsigned int *nal_hrd_bitrate_valueminus1 = NULL; + unsigned int *vcl_hrd_bitrate_valueminus1 = NULL; + unsigned int *nal_hrd_cpbsize_valueminus1 = NULL; + unsigned int *vcl_hrd_cpbsize_valueminus1 = NULL; + unsigned char *nal_hrd_cbr_flag = NULL; + unsigned char *vcl_hrd_cbr_flag = NULL; + + /* Saving pointers */ + nal_hrd_bitrate_valueminus1 = vui_info->nal_hrd_parameters.bit_rate_value_minus1; + vcl_hrd_bitrate_valueminus1 = vui_info->vcl_hrd_parameters.bit_rate_value_minus1; + + nal_hrd_cpbsize_valueminus1 = vui_info->nal_hrd_parameters.cpb_size_value_minus1; + vcl_hrd_cpbsize_valueminus1 = vui_info->vcl_hrd_parameters.cpb_size_value_minus1; + + nal_hrd_cbr_flag = vui_info->nal_hrd_parameters.cbr_flag; + vcl_hrd_cbr_flag = vui_info->vcl_hrd_parameters.cbr_flag; + + /* Cleaning sVUIInfo */ + if (vui_info->nal_hrd_parameters.bit_rate_value_minus1) + memset(vui_info->nal_hrd_parameters.bit_rate_value_minus1, 0x00, + VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned int)); + + if (vui_info->nal_hrd_parameters.cpb_size_value_minus1) + memset(vui_info->nal_hrd_parameters.cpb_size_value_minus1, 0x00, + VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned int)); + + if (vui_info->vcl_hrd_parameters.cpb_size_value_minus1) + memset(vui_info->vcl_hrd_parameters.cpb_size_value_minus1, 0x00, + VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned int)); + + if (vui_info->nal_hrd_parameters.cbr_flag) + memset(vui_info->nal_hrd_parameters.cbr_flag, 0x00, + VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned char)); + + if (vui_info->vcl_hrd_parameters.cbr_flag) + memset(vui_info->vcl_hrd_parameters.cbr_flag, 0x00, + VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned char)); + + /* Make sure you set default for everything */ + memset(vui_info, 0, sizeof(*vui_info)); + vui_info->video_format = 5; + vui_info->colour_primaries = 2; + vui_info->transfer_characteristics = 2; + vui_info->matrix_coefficients = 2; + vui_info->motion_vectors_over_pic_boundaries_flag = 1; + vui_info->max_bytes_per_pic_denom = 2; + vui_info->max_bits_per_mb_denom = 1; + vui_info->log2_max_mv_length_horizontal = 16; + vui_info->log2_max_mv_length_vertical = 16; + +#ifdef REDUCED_DPB_NO_PIC_REORDERING + vui_info->max_dec_frame_buffering = 1; + vui_info->num_reorder_frames = 0; +#else + vui_info->max_dec_frame_buffering = 0; + vui_info->num_reorder_frames = vui_info->max_dec_frame_buffering; +#endif + + /* Restoring pointers */ + vui_info->nal_hrd_parameters.bit_rate_value_minus1 = nal_hrd_bitrate_valueminus1; + vui_info->vcl_hrd_parameters.bit_rate_value_minus1 = vcl_hrd_bitrate_valueminus1; + + vui_info->nal_hrd_parameters.cpb_size_value_minus1 = nal_hrd_cpbsize_valueminus1; + vui_info->vcl_hrd_parameters.cpb_size_value_minus1 = vcl_hrd_cpbsize_valueminus1; + + vui_info->nal_hrd_parameters.cbr_flag = nal_hrd_cbr_flag; + vui_info->vcl_hrd_parameters.cbr_flag = vcl_hrd_cbr_flag; +} + +/* + * @Function bspp_h264_hrd_param_parser + * @Description Parse the HRD parameter + */ +static enum bspp_error_type bspp_h264_hrd_param_parser + (void *swsr_context, + struct bspp_h264_hrdparam_info *h264_hrd_param_info) +{ + unsigned int sched_sel_idx; + + VDEC_ASSERT(swsr_context); + h264_hrd_param_info->cpb_cnt_minus1 = swsr_read_unsigned_expgoulomb(swsr_context); + + if (h264_hrd_param_info->cpb_cnt_minus1 >= 32) + pr_info("pb_cnt_minus1 is not within the range"); + + h264_hrd_param_info->bit_rate_scale = swsr_read_bits(swsr_context, 4); + h264_hrd_param_info->cpb_size_scale = swsr_read_bits(swsr_context, 4); + + if (!h264_hrd_param_info->bit_rate_value_minus1) { + h264_hrd_param_info->bit_rate_value_minus1 = kcalloc + (VDEC_H264_MAXIMUMVALUEOFCPB_CNT, + sizeof(unsigned int), GFP_KERNEL); + VDEC_ASSERT(h264_hrd_param_info->bit_rate_value_minus1); + if (!h264_hrd_param_info->bit_rate_value_minus1) + return BSPP_ERROR_OUT_OF_MEMORY; + } + + if (!h264_hrd_param_info->cpb_size_value_minus1) { + h264_hrd_param_info->cpb_size_value_minus1 = kcalloc + (VDEC_H264_MAXIMUMVALUEOFCPB_CNT, + sizeof(unsigned int), + GFP_KERNEL); + VDEC_ASSERT(h264_hrd_param_info->cpb_size_value_minus1); + if (!h264_hrd_param_info->cpb_size_value_minus1) + return BSPP_ERROR_OUT_OF_MEMORY; + } + + if (!h264_hrd_param_info->cbr_flag) { + h264_hrd_param_info->cbr_flag = + kcalloc(VDEC_H264_MAXIMUMVALUEOFCPB_CNT, sizeof(unsigned char), GFP_KERNEL); + VDEC_ASSERT(h264_hrd_param_info->cbr_flag); + if (!h264_hrd_param_info->cbr_flag) + return BSPP_ERROR_OUT_OF_MEMORY; + } + + for (sched_sel_idx = 0; sched_sel_idx <= h264_hrd_param_info->cpb_cnt_minus1; + sched_sel_idx++) { + h264_hrd_param_info->bit_rate_value_minus1[sched_sel_idx] = + swsr_read_unsigned_expgoulomb(swsr_context); + h264_hrd_param_info->cpb_size_value_minus1[sched_sel_idx] = + swsr_read_unsigned_expgoulomb(swsr_context); + + if (h264_hrd_param_info->cpb_size_value_minus1[sched_sel_idx] == 0xffffffff) + /* 65 bit pattern, 32 0's -1 - 32 0's then value should be 0 */ + h264_hrd_param_info->cpb_size_value_minus1[sched_sel_idx] = 0; + + h264_hrd_param_info->cbr_flag[sched_sel_idx] = swsr_read_bits(swsr_context, 1); + } + + h264_hrd_param_info->initial_cpb_removal_delay_length_minus1 = swsr_read_bits(swsr_context, + 5); + h264_hrd_param_info->cpb_removal_delay_length_minus1 = swsr_read_bits(swsr_context, 5); + h264_hrd_param_info->dpb_output_delay_length_minus1 = swsr_read_bits(swsr_context, 5); + h264_hrd_param_info->time_offset_length = swsr_read_bits(swsr_context, 5); + + return BSPP_ERROR_NONE; +} + +/* + * @Function bspp_h264_get_default_hrd_param + * @Description Get default value of the HRD parameter + */ +static void bspp_h264_get_default_hrd_param(struct bspp_h264_hrdparam_info *h264_hrd_param_info) +{ + /* other parameters already set to '0' */ + h264_hrd_param_info->initial_cpb_removal_delay_length_minus1 = 23; + h264_hrd_param_info->cpb_removal_delay_length_minus1 = 23; + h264_hrd_param_info->dpb_output_delay_length_minus1 = 23; + h264_hrd_param_info->time_offset_length = 24; +} + +/* + * @Function bspp_h264_vui_parser + * @Description Parse the VUI info + */ +static enum bspp_error_type bspp_h264_vui_parser(void *swsr_context, + struct bspp_h264_vui_info *vui_info, + struct bspp_h264_sps_info *sps_info) +{ + enum bspp_error_type vui_parser_error = BSPP_ERROR_NONE; + + vui_info->aspect_ratio_info_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->aspect_ratio_info_present_flag) { + vui_info->aspect_ratio_idc = swsr_read_bits(swsr_context, 8); + /* Extended SAR */ + if (vui_info->aspect_ratio_idc == 255) { + vui_info->sar_width = swsr_read_bits(swsr_context, 16); + vui_info->sar_height = swsr_read_bits(swsr_context, 16); + } else if (vui_info->aspect_ratio_idc < 17) { + vui_info->sar_width = pixel_aspect[vui_info->aspect_ratio_idc][0]; + vui_info->sar_height = pixel_aspect[vui_info->aspect_ratio_idc][1]; + } else { + /* we can consider this error as a aux data error */ + vui_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + } + + vui_info->overscan_info_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->overscan_info_present_flag) + vui_info->overscan_appropriate_flag = swsr_read_bits(swsr_context, 1); + + vui_info->video_signal_type_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->video_signal_type_present_flag) { + vui_info->video_format = swsr_read_bits(swsr_context, 3); + vui_info->video_full_range_flag = swsr_read_bits(swsr_context, 1); + vui_info->colour_description_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->colour_description_present_flag) { + vui_info->colour_primaries = swsr_read_bits(swsr_context, 8); + vui_info->transfer_characteristics = swsr_read_bits(swsr_context, 8); + vui_info->matrix_coefficients = swsr_read_bits(swsr_context, 8); + } + } + + vui_info->chroma_location_info_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->chroma_location_info_present_flag) { + vui_info->chroma_sample_loc_type_top_field = swsr_read_unsigned_expgoulomb + (swsr_context); + vui_info->chroma_sample_loc_type_bottom_field = swsr_read_unsigned_expgoulomb + (swsr_context); + } + + vui_info->timing_info_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->timing_info_present_flag) { + vui_info->num_units_in_tick = swsr_read_bits(swsr_context, 16); + vui_info->num_units_in_tick <<= 16; /* SR can only do up to 31 bit reads */ + vui_info->num_units_in_tick |= swsr_read_bits(swsr_context, 16); + vui_info->time_scale = swsr_read_bits(swsr_context, 16); + vui_info->time_scale <<= 16; /* SR can only do up to 31 bit reads */ + vui_info->time_scale |= swsr_read_bits(swsr_context, 16); + if (!vui_info->num_units_in_tick || !vui_info->time_scale) + vui_parser_error |= BSPP_ERROR_INVALID_VALUE; + + vui_info->fixed_frame_rate_flag = swsr_read_bits(swsr_context, 1); + } + + /* no default values */ + vui_info->nal_hrd_parameters_present_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->nal_hrd_parameters_present_flag) + vui_parser_error |= bspp_h264_hrd_param_parser(swsr_context, + &vui_info->nal_hrd_parameters); + else + bspp_h264_get_default_hrd_param(&vui_info->nal_hrd_parameters); + + vui_info->vcl_hrd_parameters_present_flag = swsr_read_bits(swsr_context, 1); + + if (vui_info->vcl_hrd_parameters_present_flag) + vui_parser_error |= bspp_h264_hrd_param_parser(swsr_context, + &vui_info->vcl_hrd_parameters); + else + bspp_h264_get_default_hrd_param(&vui_info->vcl_hrd_parameters); + + if (vui_info->nal_hrd_parameters_present_flag || vui_info->vcl_hrd_parameters_present_flag) + vui_info->low_delay_hrd_flag = swsr_read_bits(swsr_context, 1); + + vui_info->pic_struct_present_flag = swsr_read_bits(swsr_context, 1); + vui_info->bitstream_restriction_flag = swsr_read_bits(swsr_context, 1); + if (vui_info->bitstream_restriction_flag) { + vui_info->motion_vectors_over_pic_boundaries_flag = swsr_read_bits(swsr_context, 1); + vui_info->max_bytes_per_pic_denom = swsr_read_unsigned_expgoulomb(swsr_context); + vui_info->max_bits_per_mb_denom = swsr_read_unsigned_expgoulomb(swsr_context); + vui_info->log2_max_mv_length_horizontal = + swsr_read_unsigned_expgoulomb(swsr_context); + vui_info->log2_max_mv_length_vertical = swsr_read_unsigned_expgoulomb(swsr_context); + vui_info->num_reorder_frames = swsr_read_unsigned_expgoulomb(swsr_context); + vui_info->max_dec_frame_buffering = swsr_read_unsigned_expgoulomb(swsr_context); + } + + if ((sps_info->profile_idc == h264_profile_baseline || + sps_info->profile_idc == h264_profile_extended) && + sps_info->max_num_ref_frames == 1) { + vui_info->bitstream_restriction_flag = 1; + vui_info->num_reorder_frames = 0; + vui_info->max_dec_frame_buffering = 1; + } + + if (vui_info->num_reorder_frames > 32) + vui_parser_error |= BSPP_ERROR_UNSUPPORTED; + + return vui_parser_error; +} + +/* + * Parse scaling list + */ +static enum bspp_error_type bspp_h264_scl_listparser(void *swsr_context, + unsigned char *scaling_list, + unsigned char sizeof_scaling_list, + unsigned char *usedefaultscalingmatrixflag) +{ + enum bspp_error_type parse_error = BSPP_ERROR_NONE; + int delta_scale; + unsigned int lastscale = 8; + unsigned int nextscale = 8; + unsigned int j; + + VDEC_ASSERT(swsr_context); + VDEC_ASSERT(scaling_list); + VDEC_ASSERT(usedefaultscalingmatrixflag); + + if (!scaling_list || !swsr_context || !usedefaultscalingmatrixflag) { + parse_error = BSPP_ERROR_UNRECOVERABLE; + return parse_error; + } + + /* 7.3.2.1.1 */ + for (j = 0; j < sizeof_scaling_list; j++) { + if (nextscale != 0) { + delta_scale = swsr_read_signed_expgoulomb(swsr_context); + if ((-128 > delta_scale) || delta_scale > 127) + parse_error |= BSPP_ERROR_INVALID_VALUE; + nextscale = (lastscale + delta_scale + 256) & 0xff; + *usedefaultscalingmatrixflag = (j == 0 && nextscale == 0); + } + scaling_list[j] = (nextscale == 0) ? lastscale : nextscale; + lastscale = scaling_list[j]; + } + return parse_error; +} + +/* + * Parse the SPS NAL unit + */ +static enum bspp_error_type bspp_h264_sps_parser(void *swsr_context, + void *str_res, + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info) +{ + unsigned int i; + unsigned char scaling_list_num; + struct bspp_h264_sps_info *sps_info; + struct bspp_h264_vui_info *vui_info; + enum bspp_error_type sps_parser_error = BSPP_ERROR_NONE; + enum bspp_error_type vui_parser_error = BSPP_ERROR_NONE; + + sps_info = &h264_seq_hdr_info->sps_info; + vui_info = &h264_seq_hdr_info->vui_info; + + /* Set always the default VUI/MVCExt, their values + * may be used even if VUI/MVCExt not present + */ + bspp_h264_set_default_vui(vui_info); +#ifdef DEBUG_DECODER_DRIVER + pr_info("Parsing Sequence Parameter Set"); +#endif + sps_info->profile_idc = swsr_read_bits(swsr_context, 8); + if (sps_info->profile_idc != H264_PROFILE_BASELINE && + sps_info->profile_idc != H264_PROFILE_MAIN && + sps_info->profile_idc != H264_PROFILE_SCALABLE && + sps_info->profile_idc != H264_PROFILE_EXTENDED && + sps_info->profile_idc != H264_PROFILE_HIGH && + sps_info->profile_idc != H264_PROFILE_HIGH10 && + sps_info->profile_idc != H264_PROFILE_MVC_HIGH && + sps_info->profile_idc != H264_PROFILE_HIGH422 && + sps_info->profile_idc != H264_PROFILE_CAVLC444 && + sps_info->profile_idc != H264_PROFILE_MVC_STEREO && + sps_info->profile_idc != H264_PROFILE_HIGH444) { + pr_err("Invalid Profile ID [%d],Parsed by BSPP", sps_info->profile_idc); + return BSPP_ERROR_UNSUPPORTED; + } + sps_info->constraint_set_flags = swsr_read_bits(swsr_context, 8); + sps_info->level_idc = swsr_read_bits(swsr_context, 8); + + /* sequence parameter set id */ + sps_info->seq_parameter_set_id = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->seq_parameter_set_id >= MAX_SPS_COUNT) { + pr_err("SPS ID [%d] goes beyond the limit", sps_info->seq_parameter_set_id); + return BSPP_ERROR_UNSUPPORTED; + } + + /* High profile settings */ + if (sps_info->profile_idc == H264_PROFILE_HIGH || + sps_info->profile_idc == H264_PROFILE_HIGH10 || + sps_info->profile_idc == H264_PROFILE_HIGH422 || + sps_info->profile_idc == H264_PROFILE_HIGH444 || + sps_info->profile_idc == H264_PROFILE_CAVLC444 || + sps_info->profile_idc == H264_PROFILE_MVC_HIGH || + sps_info->profile_idc == H264_PROFILE_MVC_STEREO) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("This is High Profile Bitstream"); +#endif + sps_info->chroma_format_idc = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->chroma_format_idc > 3) { + pr_err("chroma_format_idc[%d] is not within the range", + sps_info->chroma_format_idc); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + if (sps_info->chroma_format_idc == 3) + sps_info->separate_colour_plane_flag = swsr_read_bits(swsr_context, 1); + else + sps_info->separate_colour_plane_flag = 0; + + sps_info->bit_depth_luma_minus8 = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->bit_depth_luma_minus8 > 6) + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + + sps_info->bit_depth_chroma_minus8 = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->bit_depth_chroma_minus8 > 6) + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + + sps_info->qpprime_y_zero_transform_bypass_flag = swsr_read_bits(swsr_context, 1); + sps_info->seq_scaling_matrix_present_flag = swsr_read_bits(swsr_context, 1); + if (sps_info->seq_scaling_matrix_present_flag) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("seq_scaling_matrix_present_flag is available"); +#endif + scaling_list_num = (sps_info->chroma_format_idc != 3) ? 8 : 12; + + if (!sps_info->scllst4x4seq) { + sps_info->scllst4x4seq = + kmalloc((sizeof(unsigned char[H264FW_NUM_4X4_LISTS] + [H264FW_4X4_SIZE])), GFP_KERNEL); + if (!sps_info->scllst4x4seq) { + sps_parser_error |= BSPP_ERROR_OUT_OF_MEMORY; + } else { + VDEC_ASSERT(sps_info->scllst4x4seq); + memset(sps_info->scllst4x4seq, 0x00, + sizeof(unsigned char[H264FW_NUM_4X4_LISTS] + [H264FW_4X4_SIZE])); + } + } + if (!sps_info->scllst8x8seq) { + sps_info->scllst8x8seq = + kmalloc((sizeof(unsigned char[H264FW_NUM_8X8_LISTS] + [H264FW_8X8_SIZE])), GFP_KERNEL); + if (!sps_info->scllst8x8seq) { + sps_parser_error |= BSPP_ERROR_OUT_OF_MEMORY; + } else { + VDEC_ASSERT(sps_info->scllst8x8seq); + memset(sps_info->scllst8x8seq, 0x00, + sizeof(unsigned char[H264FW_NUM_8X8_LISTS] + [H264FW_8X8_SIZE])); + } + } + + { + unsigned char(*scllst4x4seq)[H264FW_NUM_4X4_LISTS] + [H264FW_4X4_SIZE] = + (unsigned char (*)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]) + sps_info->scllst4x4seq; + unsigned char(*scllst8x8seq)[H264FW_NUM_8X8_LISTS] + [H264FW_8X8_SIZE] = + (unsigned char (*)[H264FW_NUM_8X8_LISTS] + [H264FW_8X8_SIZE]) + sps_info->scllst8x8seq; + + for (i = 0; i < scaling_list_num; i++) { + unsigned char *ptr = + &sps_info->usedefaultscalingmatrixflag_seq[i]; + + sps_info->seq_scaling_list_present_flag[i] = + swsr_read_bits(swsr_context, 1); + if (sps_info->seq_scaling_list_present_flag[i]) { + if (i < 6) { + sps_parser_error |= + bspp_h264_scl_listparser + (swsr_context, + (*scllst4x4seq)[i], 16, + ptr); + } else { + sps_parser_error |= + bspp_h264_scl_listparser + (swsr_context, + (*scllst8x8seq)[i - 6], 64, + ptr); + } + } + } + } + } + } else { + /* default values in here */ + sps_info->chroma_format_idc = 1; + sps_info->bit_depth_luma_minus8 = 0; + sps_info->bit_depth_chroma_minus8 = 0; + sps_info->qpprime_y_zero_transform_bypass_flag = 0; + sps_info->seq_scaling_matrix_present_flag = 0; + } + + sps_info->log2_max_frame_num_minus4 = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->log2_max_frame_num_minus4 > 12) { + pr_err("log2_max_frame_num_minus4[%d] is not within range [0 - 12]", + sps_info->log2_max_frame_num_minus4); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + + sps_info->pic_order_cnt_type = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->pic_order_cnt_type > 2) { + pr_err("pic_order_cnt_type[%d] is not within range [0 - 2]", + sps_info->pic_order_cnt_type); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + + if (sps_info->pic_order_cnt_type == 0) { + sps_info->log2_max_pic_order_cnt_lsb_minus4 = swsr_read_unsigned_expgoulomb + (swsr_context); + if (sps_info->log2_max_pic_order_cnt_lsb_minus4 > 12) { + pr_err("log2_max_pic_order_cnt_lsb_minus4[%d] is not within range [0 - 12]", + sps_info->log2_max_pic_order_cnt_lsb_minus4); + sps_info->log2_max_pic_order_cnt_lsb_minus4 = 12; + sps_parser_error |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + } else if (sps_info->pic_order_cnt_type == 1) { + sps_info->delta_pic_order_always_zero_flag = swsr_read_bits(swsr_context, 1); + sps_info->offset_for_non_ref_pic = swsr_read_signed_expgoulomb(swsr_context); + sps_info->offset_for_top_to_bottom_field = swsr_read_signed_expgoulomb + (swsr_context); + sps_info->num_ref_frames_in_pic_order_cnt_cycle = swsr_read_unsigned_expgoulomb + (swsr_context); + if (sps_info->num_ref_frames_in_pic_order_cnt_cycle > 255) { + pr_err("num_ref_frames_in_pic_order_cnt_cycle[%d] is not within range [0 - 256]", + sps_info->num_ref_frames_in_pic_order_cnt_cycle); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + + if (!sps_info->offset_for_ref_frame) { + sps_info->offset_for_ref_frame = + kmalloc((H264FW_MAX_CYCLE_REF_FRAMES * sizeof(unsigned int)), + GFP_KERNEL); + if (!sps_info->offset_for_ref_frame) { + pr_err("out of memory"); + sps_parser_error |= BSPP_ERROR_OUT_OF_MEMORY; + } + } + + if (sps_info->offset_for_ref_frame) { + VDEC_ASSERT(sps_info->num_ref_frames_in_pic_order_cnt_cycle <= + H264FW_MAX_CYCLE_REF_FRAMES); + memset(sps_info->offset_for_ref_frame, 0x00, + (H264FW_MAX_CYCLE_REF_FRAMES * sizeof(unsigned int))); + for (i = 0; i < sps_info->num_ref_frames_in_pic_order_cnt_cycle; i++) { + /* check the max value and if it crosses then exit from the loop */ + sps_info->offset_for_ref_frame[i] = swsr_read_signed_expgoulomb + (swsr_context); + } + } + } else if (sps_info->pic_order_cnt_type != 2) { + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + sps_info->max_num_ref_frames = swsr_read_unsigned_expgoulomb(swsr_context); + + if (sps_info->max_num_ref_frames > 16) { + pr_err("num_ref_frames[%d] is not within range [0 - 16]", + sps_info->max_num_ref_frames); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + sps_info->gaps_in_frame_num_value_allowed_flag = swsr_read_bits(swsr_context, 1); + sps_info->pic_width_in_mbs_minus1 = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->pic_width_in_mbs_minus1 >= MAX_WIDTH_IN_MBS) { + pr_err("pic_width_in_mbs_minus1[%d] is not within range", + sps_info->pic_width_in_mbs_minus1); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + sps_info->pic_height_in_map_units_minus1 = swsr_read_unsigned_expgoulomb(swsr_context); + if (sps_info->pic_height_in_map_units_minus1 >= MAX_HEIGHT_IN_MBS) { + pr_err("pic_height_in_map_units_minus1[%d] is not within range", + sps_info->pic_height_in_map_units_minus1); + sps_parser_error |= BSPP_ERROR_INVALID_VALUE; + } + + sps_info->frame_mbs_only_flag = swsr_read_bits(swsr_context, 1); + if (!sps_info->frame_mbs_only_flag) + sps_info->mb_adaptive_frame_field_flag = swsr_read_bits(swsr_context, 1); + else + sps_info->mb_adaptive_frame_field_flag = 0; + + sps_info->direct_8x8_inference_flag = swsr_read_bits(swsr_context, 1); + + sps_info->frame_cropping_flag = swsr_read_bits(swsr_context, 1); + if (sps_info->frame_cropping_flag) { + sps_info->frame_crop_left_offset = swsr_read_unsigned_expgoulomb(swsr_context); + sps_info->frame_crop_right_offset = swsr_read_unsigned_expgoulomb(swsr_context); + sps_info->frame_crop_top_offset = swsr_read_unsigned_expgoulomb(swsr_context); + sps_info->frame_crop_bottom_offset = swsr_read_unsigned_expgoulomb(swsr_context); + } else { + sps_info->frame_crop_left_offset = 0; + sps_info->frame_crop_right_offset = 0; + sps_info->frame_crop_top_offset = 0; + sps_info->frame_crop_bottom_offset = 0; + } + + sps_info->vui_parameters_present_flag = swsr_read_bits(swsr_context, 1); + /* initialise matrix_coefficients to 2 (unspecified) */ + vui_info->matrix_coefficients = 2; + + if (sps_info->vui_parameters_present_flag) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("vui_parameters_present_flag is available"); +#endif + /* save the SPS parse error in temp variable */ + vui_parser_error = bspp_h264_vui_parser(swsr_context, vui_info, sps_info); + if (vui_parser_error != BSPP_ERROR_NONE) + sps_parser_error |= BSPP_ERROR_AUXDATA; + +#ifdef REDUCED_DPB_NO_PIC_REORDERING + vui_info->max_dec_frame_buffering = 1; + vui_info->num_reorder_frames = 0; +#endif + } + + if (sps_info->profile_idc == H264_PROFILE_MVC_HIGH || + sps_info->profile_idc == H264_PROFILE_MVC_STEREO) { + pr_err("No MVC Support for this version\n"); + } + + if (swsr_check_exception(swsr_context) != SWSR_EXCEPT_NO_EXCEPTION) + sps_parser_error |= BSPP_ERROR_INSUFFICIENT_DATA; + + return sps_parser_error; +} + +/* + * Parse the PPS NAL unit + */ +static enum bspp_error_type bspp_h264_pps_parser(void *swsr_context, + void *str_res, + struct bspp_h264_pps_info *h264_pps_info) +{ + int i, group, chroma_format_idc; + unsigned int number_bits_per_slicegroup_id; + unsigned char n_scaling_list; + unsigned char more_rbsp_data; + unsigned int result; + enum bspp_error_type pps_parse_error = BSPP_ERROR_NONE; + + VDEC_ASSERT(swsr_context); + + h264_pps_info->pps_id = swsr_read_unsigned_expgoulomb(swsr_context); + if (h264_pps_info->pps_id >= MAX_PPS_COUNT) { + pr_err("Picture Parameter Set(PPS) ID is not within the range"); + h264_pps_info->pps_id = (int)BSPP_INVALID; + return BSPP_ERROR_UNSUPPORTED; + } + h264_pps_info->seq_parameter_set_id = swsr_read_unsigned_expgoulomb(swsr_context); + if (h264_pps_info->seq_parameter_set_id >= MAX_SPS_COUNT) { + pr_err("Sequence Parameter Set(SPS) ID is not within the range"); + h264_pps_info->seq_parameter_set_id = (int)BSPP_INVALID; + return BSPP_ERROR_UNSUPPORTED; + } + + { + /* + * Get the chroma_format_idc from sps. Because of MVC sharing sps and subset sps ids + * (H.7.4.1.2.1). + * At this point is not clear if this pps refers to an sps or a subset sps. + * It should be finehowever for the case of chroma_format_idc to try and locate + * a subset sps if there isn't a normal one. + */ + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info; + struct bspp_sequence_hdr_info *seq_hdr_info; + + seq_hdr_info = bspp_get_sequ_hdr(str_res, h264_pps_info->seq_parameter_set_id); + + if (!seq_hdr_info) { + seq_hdr_info = bspp_get_sequ_hdr(str_res, + h264_pps_info->seq_parameter_set_id + 32); + if (!seq_hdr_info) + return BSPP_ERROR_NO_SEQUENCE_HDR; + } + + h264_seq_hdr_info = + (struct bspp_h264_seq_hdr_info *)seq_hdr_info->secure_sequence_info; + + chroma_format_idc = h264_seq_hdr_info->sps_info.chroma_format_idc; + } + + h264_pps_info->entropy_coding_mode_flag = swsr_read_bits(swsr_context, 1); + h264_pps_info->pic_order_present_flag = swsr_read_bits(swsr_context, 1); + h264_pps_info->num_slice_groups_minus1 = swsr_read_unsigned_expgoulomb(swsr_context); + if ((h264_pps_info->num_slice_groups_minus1 + 1) > + MAX_SLICEGROUP_COUNT) { + h264_pps_info->num_slice_groups_minus1 = + MAX_SLICEGROUP_COUNT - 1; + pps_parse_error |= BSPP_ERROR_UNRECOVERABLE; + } + + if (h264_pps_info->num_slice_groups_minus1 > 0) { + h264_pps_info->slice_group_map_type = swsr_read_unsigned_expgoulomb(swsr_context); + pr_err("slice_group_map_type is : %d, Parsed by BSPP", + h264_pps_info->slice_group_map_type); + if (h264_pps_info->slice_group_map_type > 6) { + pr_err("slice_group_map_type [%d] is not within the range [ 0- 6 ]", + h264_pps_info->slice_group_map_type); + pps_parse_error |= BSPP_ERROR_UNRECOVERABLE; + } + + if (h264_pps_info->slice_group_map_type == 0) { + for (group = 0; group <= h264_pps_info->num_slice_groups_minus1; group++) { + h264_pps_info->run_length_minus1[group] = + swsr_read_unsigned_expgoulomb(swsr_context); + } + } else if (h264_pps_info->slice_group_map_type == 2) { + for (group = 0; group < h264_pps_info->num_slice_groups_minus1; group++) { + h264_pps_info->top_left[group] = swsr_read_unsigned_expgoulomb + (swsr_context); + h264_pps_info->bottom_right[group] = + swsr_read_unsigned_expgoulomb(swsr_context); + } + } else if (h264_pps_info->slice_group_map_type == 3 || + h264_pps_info->slice_group_map_type == 4 || + h264_pps_info->slice_group_map_type == 5) { + h264_pps_info->slice_group_change_direction_flag = swsr_read_bits + (swsr_context, 1); + h264_pps_info->slice_group_change_rate_minus1 = + swsr_read_unsigned_expgoulomb(swsr_context); + } else if (h264_pps_info->slice_group_map_type == 6) { + h264_pps_info->pic_size_in_map_unit = swsr_read_unsigned_expgoulomb + (swsr_context); + if (h264_pps_info->pic_size_in_map_unit >= H264_MAX_SGM_SIZE) { + pr_err("pic_size_in_map_units_minus1 [%d] is not within the range", + h264_pps_info->pic_size_in_map_unit); + pps_parse_error |= BSPP_ERROR_UNRECOVERABLE; + } + number_bits_per_slicegroup_id = h264ceillog2 + (h264_pps_info->num_slice_groups_minus1 + 1); + + if ((h264_pps_info->pic_size_in_map_unit + 1) > + h264_pps_info->h264_ppssgm_info.slicegroupidnum) { + unsigned char *slice_group_id = + kmalloc(((h264_pps_info->pic_size_in_map_unit + 1) * + sizeof(unsigned char)), + GFP_KERNEL); + if (!slice_group_id) { + pr_err("out of memory"); + pps_parse_error |= BSPP_ERROR_OUT_OF_MEMORY; + } else { + pr_err("reallocating SGM info from size %lu bytes to size %lu bytes", + h264_pps_info->h264_ppssgm_info.slicegroupidnum * + sizeof(unsigned char), + (h264_pps_info->pic_size_in_map_unit + 1) * + sizeof(unsigned char)); + if (h264_pps_info->h264_ppssgm_info.slice_group_id) { + memcpy + (slice_group_id, + h264_pps_info->h264_ppssgm_info.slice_group_id, + h264_pps_info->h264_ppssgm_info.slicegroupidnum * + sizeof(unsigned char)); + kfree + (h264_pps_info->h264_ppssgm_info.slice_group_id); + } + h264_pps_info->h264_ppssgm_info.slicegroupidnum = + (h264_pps_info->pic_size_in_map_unit + 1); + h264_pps_info->h264_ppssgm_info.slice_group_id = + slice_group_id; + } + } + + VDEC_ASSERT((h264_pps_info->pic_size_in_map_unit + 1) <= + h264_pps_info->h264_ppssgm_info.slicegroupidnum); + for (i = 0; i <= h264_pps_info->pic_size_in_map_unit; i++) + h264_pps_info->h264_ppssgm_info.slice_group_id[i] = + swsr_read_bits(swsr_context, number_bits_per_slicegroup_id); + } + } + + for (i = 0; i < H264FW_MAX_REFPIC_LISTS; i++) { + h264_pps_info->num_ref_idx_lx_active_minus1[i] = swsr_read_unsigned_expgoulomb + (swsr_context); + if (h264_pps_info->num_ref_idx_lx_active_minus1[i] >= + SL_MAX_REF_IDX) { + pr_err("num_ref_idx_lx_active_minus1[%d] [%d] is not within the range", + i, h264_pps_info->num_ref_idx_lx_active_minus1[i]); + pps_parse_error |= BSPP_ERROR_UNRECOVERABLE; + } + } + + h264_pps_info->weighted_pred_flag = swsr_read_bits(swsr_context, 1); + h264_pps_info->weighted_bipred_idc = swsr_read_bits(swsr_context, 2); + h264_pps_info->pic_init_qp_minus26 = swsr_read_signed_expgoulomb(swsr_context); + if (h264_pps_info->pic_init_qp_minus26 > 26) + pr_err("pic_init_qp_minus26[%d] is not within the range [-25 , 26]", + h264_pps_info->pic_init_qp_minus26); + + h264_pps_info->pic_init_qs_minus26 = swsr_read_signed_expgoulomb(swsr_context); + if (h264_pps_info->pic_init_qs_minus26 > 26) + pr_err("pic_init_qs_minus26[%d] is not within the range [-25 , 26]", + h264_pps_info->pic_init_qs_minus26); + + h264_pps_info->chroma_qp_index_offset = swsr_read_signed_expgoulomb(swsr_context); + if (h264_pps_info->chroma_qp_index_offset > H264_MAX_CHROMA_QP_INDEX_OFFSET) + h264_pps_info->chroma_qp_index_offset = H264_MAX_CHROMA_QP_INDEX_OFFSET; + + else if (h264_pps_info->chroma_qp_index_offset < H264_MIN_CHROMA_QP_INDEX_OFFSET) + h264_pps_info->chroma_qp_index_offset = H264_MIN_CHROMA_QP_INDEX_OFFSET; + + h264_pps_info->deblocking_filter_control_present_flag = swsr_read_bits(swsr_context, 1); + h264_pps_info->constrained_intra_pred_flag = swsr_read_bits(swsr_context, 1); + h264_pps_info->redundant_pic_cnt_present_flag = swsr_read_bits(swsr_context, 1); + + /* Check for more rbsp data. */ + result = swsr_check_more_rbsp_data(swsr_context, &more_rbsp_data); + if (result == 0 && more_rbsp_data) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("More RBSP data is available"); +#endif + /* Fidelity Range Extensions Stuff */ + h264_pps_info->transform_8x8_mode_flag = swsr_read_bits(swsr_context, 1); + h264_pps_info->pic_scaling_matrix_present_flag = swsr_read_bits(swsr_context, 1); + if (h264_pps_info->pic_scaling_matrix_present_flag) { + if (!h264_pps_info->scllst4x4pic) { + h264_pps_info->scllst4x4pic = + kmalloc((sizeof(unsigned char[H264FW_NUM_4X4_LISTS] + [H264FW_4X4_SIZE])), GFP_KERNEL); + if (!h264_pps_info->scllst4x4pic) { + pps_parse_error |= BSPP_ERROR_OUT_OF_MEMORY; + } else { + VDEC_ASSERT(h264_pps_info->scllst4x4pic); + memset(h264_pps_info->scllst4x4pic, 0x00, + sizeof(unsigned char[H264FW_NUM_4X4_LISTS] + [H264FW_4X4_SIZE])); + } + } + if (!h264_pps_info->scllst8x8pic) { + h264_pps_info->scllst8x8pic = + kmalloc((sizeof(unsigned char[H264FW_NUM_8X8_LISTS] + [H264FW_8X8_SIZE])), GFP_KERNEL); + if (!h264_pps_info->scllst8x8pic) { + pps_parse_error |= BSPP_ERROR_OUT_OF_MEMORY; + } else { + VDEC_ASSERT(h264_pps_info->scllst8x8pic); + memset(h264_pps_info->scllst8x8pic, 0x00, + sizeof(unsigned char[H264FW_NUM_8X8_LISTS] + [H264FW_8X8_SIZE])); + } + } + { + unsigned char(*scllst4x4pic)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE] = + (unsigned char (*)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]) + h264_pps_info->scllst4x4pic; + unsigned char(*scllst8x8pic)[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE] = + (unsigned char (*)[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE]) + h264_pps_info->scllst8x8pic; + + /* + * For chroma_format =3 (YUV444) total list would be 12 + * if transform_8x8_mode_flag is enabled else 6. + */ + n_scaling_list = 6 + (chroma_format_idc != 3 ? 2 : 6) * + h264_pps_info->transform_8x8_mode_flag; + if (n_scaling_list > 12) + pps_parse_error |= BSPP_ERROR_UNRECOVERABLE; + + VDEC_ASSERT(h264_pps_info->scllst4x4pic); + VDEC_ASSERT(h264_pps_info->scllst8x8pic); + for (i = 0; i < n_scaling_list; i++) { + unsigned char *ptr = + &h264_pps_info->usedefaultscalingmatrixflag_pic[i]; + + h264_pps_info->pic_scaling_list_present_flag[i] = + swsr_read_bits(swsr_context, 1); + if (h264_pps_info->pic_scaling_list_present_flag[i]) { + if (i < 6) + pps_parse_error |= + bspp_h264_scl_listparser + (swsr_context, + (*scllst4x4pic)[i], 16, ptr); + else + pps_parse_error |= + bspp_h264_scl_listparser + (swsr_context, + (*scllst8x8pic)[i - 6], 64, ptr); + } + } + } + } + h264_pps_info->second_chroma_qp_index_offset = swsr_read_signed_expgoulomb + (swsr_context); + + if (h264_pps_info->second_chroma_qp_index_offset > H264_MAX_CHROMA_QP_INDEX_OFFSET) + h264_pps_info->second_chroma_qp_index_offset = + H264_MAX_CHROMA_QP_INDEX_OFFSET; + else if (h264_pps_info->second_chroma_qp_index_offset < + H264_MIN_CHROMA_QP_INDEX_OFFSET) + h264_pps_info->second_chroma_qp_index_offset = + H264_MIN_CHROMA_QP_INDEX_OFFSET; + } else { + h264_pps_info->second_chroma_qp_index_offset = + h264_pps_info->chroma_qp_index_offset; + } + + if (swsr_check_exception(swsr_context) != SWSR_EXCEPT_NO_EXCEPTION) + pps_parse_error |= BSPP_ERROR_INSUFFICIENT_DATA; + + return pps_parse_error; +} + +static int bspp_h264_release_sequ_hdr_info(void *str_alloc, void *secure_sps_info) +{ + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info = + (struct bspp_h264_seq_hdr_info *)secure_sps_info; + + if (!h264_seq_hdr_info) + return IMG_ERROR_INVALID_PARAMETERS; + + return 0; +} + +static int bspp_h264_reset_seq_hdr_info(void *secure_sps_info) +{ + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info = NULL; + unsigned int *nal_hrd_bitrate_valueminus1 = NULL; + unsigned int *vcl_hrd_bitrate_valueminus1 = NULL; + unsigned int *nal_hrd_cpbsize_valueminus1 = NULL; + unsigned int *vcl_hrd_cpbsize_valueminus1 = NULL; + unsigned char *nal_hrd_cbrflag = NULL; + unsigned char *vcl_hrd_cbrflag = NULL; + unsigned int *offset_for_ref_frame = NULL; + unsigned char *scllst4x4seq = NULL; + unsigned char *scllst8x8seq = NULL; + + if (!secure_sps_info) + return IMG_ERROR_INVALID_PARAMETERS; + + h264_seq_hdr_info = (struct bspp_h264_seq_hdr_info *)secure_sps_info; + + offset_for_ref_frame = h264_seq_hdr_info->sps_info.offset_for_ref_frame; + scllst4x4seq = h264_seq_hdr_info->sps_info.scllst4x4seq; + scllst8x8seq = h264_seq_hdr_info->sps_info.scllst8x8seq; + nal_hrd_bitrate_valueminus1 = + h264_seq_hdr_info->vui_info.nal_hrd_parameters.bit_rate_value_minus1; + vcl_hrd_bitrate_valueminus1 = + h264_seq_hdr_info->vui_info.vcl_hrd_parameters.bit_rate_value_minus1; + nal_hrd_cpbsize_valueminus1 = + h264_seq_hdr_info->vui_info.nal_hrd_parameters.cpb_size_value_minus1; + vcl_hrd_cpbsize_valueminus1 = + h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cpb_size_value_minus1; + nal_hrd_cbrflag = h264_seq_hdr_info->vui_info.nal_hrd_parameters.cbr_flag; + vcl_hrd_cbrflag = h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cbr_flag; + + /* Cleaning vui_info */ + if (h264_seq_hdr_info->vui_info.nal_hrd_parameters.bit_rate_value_minus1) + memset(h264_seq_hdr_info->vui_info.nal_hrd_parameters.bit_rate_value_minus1, + 0x00, VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned int)); + + if (h264_seq_hdr_info->vui_info.nal_hrd_parameters.cpb_size_value_minus1) + memset(h264_seq_hdr_info->vui_info.nal_hrd_parameters.cpb_size_value_minus1, + 0x00, VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned int)); + + if (h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cpb_size_value_minus1) + memset(h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cpb_size_value_minus1, + 0x00, VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned int)); + + if (h264_seq_hdr_info->vui_info.nal_hrd_parameters.cbr_flag) + memset(h264_seq_hdr_info->vui_info.nal_hrd_parameters.cbr_flag, + 0x00, VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned char)); + + if (h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cbr_flag) + memset(h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cbr_flag, + 0x00, VDEC_H264_MAXIMUMVALUEOFCPB_CNT * sizeof(unsigned char)); + + /* Cleaning sps_info */ + if (h264_seq_hdr_info->sps_info.offset_for_ref_frame) + memset(h264_seq_hdr_info->sps_info.offset_for_ref_frame, 0x00, + H264FW_MAX_CYCLE_REF_FRAMES * sizeof(unsigned int)); + + if (h264_seq_hdr_info->sps_info.scllst4x4seq) + memset(h264_seq_hdr_info->sps_info.scllst4x4seq, 0x00, + sizeof(unsigned char[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE])); + + if (h264_seq_hdr_info->sps_info.scllst8x8seq) + memset(h264_seq_hdr_info->sps_info.scllst8x8seq, 0x00, + sizeof(unsigned char[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE])); + + /* Erasing the structure */ + memset(h264_seq_hdr_info, 0, sizeof(*h264_seq_hdr_info)); + + /* Restoring pointers */ + h264_seq_hdr_info->sps_info.offset_for_ref_frame = offset_for_ref_frame; + h264_seq_hdr_info->sps_info.scllst4x4seq = scllst4x4seq; + h264_seq_hdr_info->sps_info.scllst8x8seq = scllst8x8seq; + + h264_seq_hdr_info->vui_info.nal_hrd_parameters.bit_rate_value_minus1 = + nal_hrd_bitrate_valueminus1; + h264_seq_hdr_info->vui_info.vcl_hrd_parameters.bit_rate_value_minus1 = + vcl_hrd_bitrate_valueminus1; + + h264_seq_hdr_info->vui_info.nal_hrd_parameters.cpb_size_value_minus1 = + nal_hrd_cpbsize_valueminus1; + h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cpb_size_value_minus1 = + vcl_hrd_cpbsize_valueminus1; + + h264_seq_hdr_info->vui_info.nal_hrd_parameters.cbr_flag = nal_hrd_cbrflag; + h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cbr_flag = vcl_hrd_cbrflag; + + return 0; +} + +static int bspp_h264_reset_pps_info(void *secure_pps_info) +{ + struct bspp_h264_pps_info *h264_pps_info = NULL; + unsigned short slicegroupidnum = 0; + unsigned char *slice_group_id = NULL; + unsigned char *scllst4x4pic = NULL; + unsigned char *scllst8x8pic = NULL; + + if (!secure_pps_info) + return IMG_ERROR_INVALID_PARAMETERS; + + h264_pps_info = (struct bspp_h264_pps_info *)secure_pps_info; + + /* + * Storing temp values (we want to leave the SGM structure + * it may be useful again instead of reallocating later + */ + slice_group_id = h264_pps_info->h264_ppssgm_info.slice_group_id; + slicegroupidnum = h264_pps_info->h264_ppssgm_info.slicegroupidnum; + scllst4x4pic = h264_pps_info->scllst4x4pic; + scllst8x8pic = h264_pps_info->scllst8x8pic; + + if (h264_pps_info->h264_ppssgm_info.slice_group_id) + memset(h264_pps_info->h264_ppssgm_info.slice_group_id, 0x00, + h264_pps_info->h264_ppssgm_info.slicegroupidnum * sizeof(unsigned char)); + + if (h264_pps_info->scllst4x4pic) + memset(h264_pps_info->scllst4x4pic, 0x00, + sizeof(unsigned char[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE])); + + if (h264_pps_info->scllst8x8pic) + memset(h264_pps_info->scllst8x8pic, 0x00, + sizeof(unsigned char[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE])); + + /* Erasing the structure */ + memset(h264_pps_info, 0x00, sizeof(*h264_pps_info)); + + /* Copy the temp variable back */ + h264_pps_info->h264_ppssgm_info.slicegroupidnum = slicegroupidnum; + h264_pps_info->h264_ppssgm_info.slice_group_id = slice_group_id; + h264_pps_info->scllst4x4pic = scllst4x4pic; + h264_pps_info->scllst8x8pic = scllst8x8pic; + + return 0; +} + +static enum bspp_error_type bspp_h264_pict_hdr_parser + (void *swsr_context, void *str_res, + struct bspp_h264_slice_hdr_info *h264_slice_hdr_info, + struct bspp_pps_info **pps_info, + struct bspp_sequence_hdr_info **seq_hdr_info, + enum h264_nalunittype nal_unit_type, + unsigned char nal_ref_idc) +{ + enum bspp_error_type slice_parse_error = BSPP_ERROR_NONE; + struct bspp_h264_pps_info *h264_pps_info; + struct bspp_pps_info *pps_info_loc; + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info; + struct bspp_sequence_hdr_info *seq_hdr_info_loc; + int id_loc; + + VDEC_ASSERT(swsr_context); + + memset(h264_slice_hdr_info, 0, sizeof(*h264_slice_hdr_info)); + + h264_slice_hdr_info->first_mb_in_slice = swsr_read_unsigned_expgoulomb(swsr_context); + h264_slice_hdr_info->slice_type = (enum bspp_h264_slice_type)swsr_read_unsigned_expgoulomb + (swsr_context); + if ((unsigned int)h264_slice_hdr_info->slice_type > 9) { + pr_err("Slice Type [%d] invalid, set to P", h264_slice_hdr_info->slice_type); + h264_slice_hdr_info->slice_type = (enum bspp_h264_slice_type)0; + slice_parse_error |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + h264_slice_hdr_info->slice_type = + (enum bspp_h264_slice_type)(h264_slice_hdr_info->slice_type % 5); + + h264_slice_hdr_info->pps_id = swsr_read_unsigned_expgoulomb(swsr_context); + if (h264_slice_hdr_info->pps_id >= MAX_PPS_COUNT) { + pr_err("Picture Parameter ID [%d] invalid, set to 0", h264_slice_hdr_info->pps_id); + h264_slice_hdr_info->pps_id = 0; + slice_parse_error |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + + /* Set relevant PPS and SPS */ + pps_info_loc = bspp_get_pps_hdr(str_res, h264_slice_hdr_info->pps_id); + + if (!pps_info_loc) { + slice_parse_error |= BSPP_ERROR_NO_PPS; + goto error; + } + h264_pps_info = (struct bspp_h264_pps_info *)pps_info_loc->secure_pps_info; + if (!h264_pps_info) { + slice_parse_error |= BSPP_ERROR_NO_PPS; + goto error; + } + VDEC_ASSERT(h264_pps_info->pps_id == h264_slice_hdr_info->pps_id); + *pps_info = pps_info_loc; + + /* seq_parameter_set_id is always in range 0-31, + * so we can add offset indicating subsequence header + */ + id_loc = h264_pps_info->seq_parameter_set_id; + id_loc = (nal_unit_type == H264_NALTYPE_SLICE_SCALABLE || + nal_unit_type == H264_NALTYPE_SLICE_IDR_SCALABLE || + nal_unit_type == H264_NALTYPE_SUBSET_SPS) ? id_loc + 32 : id_loc; + + seq_hdr_info_loc = bspp_get_sequ_hdr(str_res, id_loc); + + if (!seq_hdr_info_loc) { + slice_parse_error |= BSPP_ERROR_NO_SEQUENCE_HDR; + goto error; + } + h264_seq_hdr_info = (struct bspp_h264_seq_hdr_info *)seq_hdr_info_loc->secure_sequence_info; + VDEC_ASSERT((unsigned int)h264_seq_hdr_info->sps_info.seq_parameter_set_id == + h264_pps_info->seq_parameter_set_id); + *seq_hdr_info = seq_hdr_info_loc; + + /* + * For MINIMAL parsing in secure mode, slice header parsing can stop + * here, may be problematic with field-coded streams and splitting + * fields + */ + if (h264_seq_hdr_info->sps_info.separate_colour_plane_flag) + h264_slice_hdr_info->colour_plane_id = swsr_read_bits(swsr_context, 2); + + else + h264_slice_hdr_info->colour_plane_id = 0; + + h264_slice_hdr_info->frame_num = swsr_read_bits + (swsr_context, + h264_seq_hdr_info->sps_info.log2_max_frame_num_minus4 + + 4); + + VDEC_ASSERT(h264_slice_hdr_info->frame_num < + (1UL << (h264_seq_hdr_info->sps_info.log2_max_frame_num_minus4 + 4))); + + if (!h264_seq_hdr_info->sps_info.frame_mbs_only_flag) { + if (h264_slice_hdr_info->slice_type == B_SLICE && + !h264_seq_hdr_info->sps_info.direct_8x8_inference_flag) + slice_parse_error |= BSPP_ERROR_INVALID_VALUE; + + h264_slice_hdr_info->field_pic_flag = swsr_read_bits(swsr_context, 1); + if (h264_slice_hdr_info->field_pic_flag) + h264_slice_hdr_info->bottom_field_flag = swsr_read_bits(swsr_context, 1); + else + h264_slice_hdr_info->bottom_field_flag = 0; + } else { + h264_slice_hdr_info->field_pic_flag = 0; + h264_slice_hdr_info->bottom_field_flag = 0; + } + + /* + * At this point we have everything we need, but we still lack all the + * conditions for detecting new pictures (needed for error cases) + */ + if (nal_unit_type == H264_NALTYPE_IDR_SLICE) + h264_slice_hdr_info->idr_pic_id = swsr_read_unsigned_expgoulomb(swsr_context); + + if (h264_seq_hdr_info->sps_info.pic_order_cnt_type == 0) { + h264_slice_hdr_info->pic_order_cnt_lsb = swsr_read_bits + (swsr_context, + h264_seq_hdr_info->sps_info.log2_max_pic_order_cnt_lsb_minus4 + 4); + if (h264_pps_info->pic_order_present_flag && !h264_slice_hdr_info->field_pic_flag) + h264_slice_hdr_info->delta_pic_order_cnt_bottom = + swsr_read_signed_expgoulomb(swsr_context); + } + + if (h264_seq_hdr_info->sps_info.pic_order_cnt_type == 1 && + !h264_seq_hdr_info->sps_info.delta_pic_order_always_zero_flag) { + h264_slice_hdr_info->delta_pic_order_cnt[0] = swsr_read_signed_expgoulomb + (swsr_context); + if (h264_pps_info->pic_order_present_flag && !h264_slice_hdr_info->field_pic_flag) + h264_slice_hdr_info->delta_pic_order_cnt[1] = swsr_read_signed_expgoulomb + (swsr_context); + } + + if (h264_pps_info->redundant_pic_cnt_present_flag) + h264_slice_hdr_info->redundant_pic_cnt = + swsr_read_unsigned_expgoulomb(swsr_context); + + /* For FMO streams, we need to go further */ + if (h264_pps_info->num_slice_groups_minus1 != 0 && + h264_pps_info->slice_group_map_type >= 3 && + h264_pps_info->slice_group_map_type <= 5) { + if (h264_slice_hdr_info->slice_type == B_SLICE) + swsr_read_bits(swsr_context, 1); + + if (h264_slice_hdr_info->slice_type == P_SLICE || + h264_slice_hdr_info->slice_type == SP_SLICE || + h264_slice_hdr_info->slice_type == B_SLICE) { + h264_slice_hdr_info->num_ref_idx_active_override_flag = + swsr_read_bits(swsr_context, 1); + if (h264_slice_hdr_info->num_ref_idx_active_override_flag) { + h264_slice_hdr_info->num_ref_idx_lx_active_minus1[0] = + swsr_read_unsigned_expgoulomb(swsr_context); + if (h264_slice_hdr_info->slice_type == B_SLICE) + h264_slice_hdr_info->num_ref_idx_lx_active_minus1[1] = + swsr_read_unsigned_expgoulomb(swsr_context); + } + } + + if (h264_slice_hdr_info->slice_type != SI_SLICE && + h264_slice_hdr_info->slice_type != I_SLICE) { + /* Reference picture list modification */ + /* parse reordering info and pack into commands */ + unsigned int i; + unsigned int cmd_num, list_num; + unsigned int command; + + i = (h264_slice_hdr_info->slice_type == B_SLICE) ? 2 : 1; + + for (list_num = 0; list_num < i; list_num++) { + cmd_num = 0; + if (swsr_read_bits(swsr_context, 1)) { + do { + command = + swsr_read_unsigned_expgoulomb(swsr_context); + if (command != 3) { + swsr_read_unsigned_expgoulomb(swsr_context); + cmd_num++; + } + } while (command != 3 && cmd_num <= SL_MAX_REF_IDX); + } + } + } + + if ((h264_pps_info->weighted_pred_flag && + h264_slice_hdr_info->slice_type == P_SLICE) || + (h264_pps_info->weighted_bipred_idc && + h264_slice_hdr_info->slice_type == B_SLICE)) { + int mono_chrome; + unsigned int list, i, j, k; + + mono_chrome = (!h264_seq_hdr_info->sps_info.chroma_format_idc) ? 1 : 0; + + swsr_read_unsigned_expgoulomb(swsr_context); + if (!mono_chrome) + swsr_read_unsigned_expgoulomb(swsr_context); + + k = (h264_slice_hdr_info->slice_type == B_SLICE) ? 2 : 1; + + for (list = 0; list < k; list++) { + for (i = 0; + i <= + h264_slice_hdr_info->num_ref_idx_lx_active_minus1[list]; + i++) { + if (swsr_read_bits(swsr_context, 1)) { + swsr_read_signed_expgoulomb(swsr_context); + swsr_read_signed_expgoulomb(swsr_context); + } + + if (!mono_chrome && (swsr_read_bits(swsr_context, 1))) { + for (j = 0; j < 2; j++) { + swsr_read_signed_expgoulomb + (swsr_context); + swsr_read_signed_expgoulomb + (swsr_context); + } + } + } + } + } + + if (nal_ref_idc != 0) { + unsigned int memmanop; + + if (nal_unit_type == H264_NALTYPE_IDR_SLICE) { + swsr_read_bits(swsr_context, 1); + swsr_read_bits(swsr_context, 1); + } + if (swsr_read_bits(swsr_context, 1)) { + do { + /* clamp 0--6 */ + memmanop = swsr_read_unsigned_expgoulomb + (swsr_context); + if (memmanop != 0 && memmanop != 5) { + if (memmanop == 3) { + swsr_read_unsigned_expgoulomb + (swsr_context); + swsr_read_unsigned_expgoulomb + (swsr_context); + } else { + swsr_read_unsigned_expgoulomb + (swsr_context); + } + } + } while (memmanop != 0); + } + } + + if (h264_pps_info->entropy_coding_mode_flag && + h264_slice_hdr_info->slice_type != I_SLICE) + swsr_read_unsigned_expgoulomb(swsr_context); + + swsr_read_signed_expgoulomb(swsr_context); + + if (h264_slice_hdr_info->slice_type == SP_SLICE || + h264_slice_hdr_info->slice_type == SI_SLICE) { + if (h264_slice_hdr_info->slice_type == SP_SLICE) + swsr_read_bits(swsr_context, 1); + + /* slice_qs_delta */ + swsr_read_signed_expgoulomb(swsr_context); + } + + if (h264_pps_info->deblocking_filter_control_present_flag) { + if (swsr_read_unsigned_expgoulomb(swsr_context) != 1) { + swsr_read_signed_expgoulomb(swsr_context); + swsr_read_signed_expgoulomb(swsr_context); + } + } + + if (h264_pps_info->slice_group_map_type >= 3 && + h264_pps_info->slice_group_map_type <= 5) { + unsigned int num_slice_group_map_units = + (h264_seq_hdr_info->sps_info.pic_height_in_map_units_minus1 + 1) * + (h264_seq_hdr_info->sps_info.pic_width_in_mbs_minus1 + 1); + + unsigned short slice_group_change_rate = + (h264_pps_info->slice_group_change_rate_minus1 + 1); + + unsigned int width = h264ceillog2(num_slice_group_map_units / + slice_group_change_rate + + (num_slice_group_map_units % slice_group_change_rate == + 0 ? 0 : 1) + 1); /* (7-32) */ + h264_slice_hdr_info->slice_group_change_cycle = swsr_read_bits(swsr_context, + width); + } + } + +error: + return slice_parse_error; +} + +static void bspp_h264_select_scaling_list(struct h264fw_picture_ps *h264fw_pps_info, + struct bspp_h264_pps_info *h264_pps_info, + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info) +{ + unsigned int num8x8_lists; + unsigned int i; + const unsigned char *quant_matrix = NULL; + unsigned char (*scllst4x4pic)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE] = + (unsigned char (*)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE])h264_pps_info->scllst4x4pic; + unsigned char (*scllst8x8pic)[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE] = + (unsigned char (*)[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE])h264_pps_info->scllst8x8pic; + + unsigned char (*scllst4x4seq)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE] = + (unsigned char (*)[H264FW_NUM_4X4_LISTS][H264FW_4X4_SIZE]) + h264_seq_hdr_info->sps_info.scllst4x4seq; + unsigned char (*scllst8x8seq)[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE] = + (unsigned char (*)[H264FW_NUM_8X8_LISTS][H264FW_8X8_SIZE]) + h264_seq_hdr_info->sps_info.scllst8x8seq; + + if (h264_seq_hdr_info->sps_info.seq_scaling_matrix_present_flag) { + VDEC_ASSERT(h264_seq_hdr_info->sps_info.scllst4x4seq); + VDEC_ASSERT(h264_seq_hdr_info->sps_info.scllst8x8seq); + } + + if (h264_pps_info->pic_scaling_matrix_present_flag) { + for (i = 0; i < H264FW_NUM_4X4_LISTS; i++) { + if (h264_pps_info->pic_scaling_list_present_flag[i]) { + if (h264_pps_info->usedefaultscalingmatrixflag_pic[i]) + quant_matrix = + (i > 2) ? default_4x4_inter : default_4x4_intra; + else + quant_matrix = (*scllst4x4pic)[i]; + + } else { + if (h264_seq_hdr_info->sps_info.seq_scaling_matrix_present_flag) { + /* SPS matrix present - use fallback rule B */ + /* first 4x4 Intra list */ + if (i == 0) { + if + (h264_seq_hdr_info->sps_info.seq_scaling_list_present_flag[i] && + !h264_seq_hdr_info->sps_info.usedefaultscalingmatrixflag_seq[i]) { + VDEC_ASSERT + (h264_seq_hdr_info->sps_info.scllst4x4seq); + if (scllst4x4seq) + quant_matrix = (*scllst4x4seq)[i]; + } else { + quant_matrix = default_4x4_intra; + } + } + /* first 4x4 Inter list */ + else if (i == 3) { + if + (h264_seq_hdr_info->sps_info.seq_scaling_list_present_flag[i] && + !h264_seq_hdr_info->sps_info.usedefaultscalingmatrixflag_seq[i]) { + VDEC_ASSERT + (h264_seq_hdr_info->sps_info.scllst4x4seq); + if (scllst4x4seq) + quant_matrix = (*scllst4x4seq)[i]; + } else { + quant_matrix = default_4x4_inter; + } + } else { + quant_matrix = + h264fw_pps_info->scalinglist4x4[i - 1]; + } + } else { + /* SPS matrix not present - use fallback rule A */ + /* first 4x4 Intra list */ + if (i == 0) + quant_matrix = default_4x4_intra; + /* first 4x4 Interlist */ + else if (i == 3) + quant_matrix = default_4x4_inter; + else + quant_matrix = + h264fw_pps_info->scalinglist4x4[i - 1]; + } + } + if (!quant_matrix) { + VDEC_ASSERT(0); + return; + } + /* copy correct 4x4 list to output - as selected by PPS */ + memcpy(h264fw_pps_info->scalinglist4x4[i], quant_matrix, + sizeof(h264fw_pps_info->scalinglist4x4[i])); + } + } else { + /* PPS matrix not present, use SPS information */ + if (h264_seq_hdr_info->sps_info.seq_scaling_matrix_present_flag) { + for (i = 0; i < H264FW_NUM_4X4_LISTS; i++) { + if (h264_seq_hdr_info->sps_info.seq_scaling_list_present_flag[i]) { + if + (h264_seq_hdr_info->sps_info.usedefaultscalingmatrixflag_seq + [i]) { + quant_matrix = (i > 2) ? default_4x4_inter + : default_4x4_intra; + } else { + VDEC_ASSERT + (h264_seq_hdr_info->sps_info.scllst4x4seq); + if (scllst4x4seq) + quant_matrix = (*scllst4x4seq)[i]; + } + } else { + /* SPS list not present - use fallback rule A */ + /* first 4x4 Intra list */ + if (i == 0) + quant_matrix = default_4x4_intra; + else if (i == 3) /* first 4x4 Inter list */ + quant_matrix = default_4x4_inter; + else + quant_matrix = + h264fw_pps_info->scalinglist4x4[i - 1]; + } + if (quant_matrix) { + /* copy correct 4x4 list to output - as selected by SPS */ + memcpy(h264fw_pps_info->scalinglist4x4[i], quant_matrix, + sizeof(h264fw_pps_info->scalinglist4x4[i])); + } + } + } else { + /* SPS matrix not present - use flat lists */ + quant_matrix = default_4x4_org; + for (i = 0; i < H264FW_NUM_4X4_LISTS; i++) + memcpy(h264fw_pps_info->scalinglist4x4[i], quant_matrix, + sizeof(h264fw_pps_info->scalinglist4x4[i])); + } + } + + /* 8x8 matrices */ + num8x8_lists = (h264_seq_hdr_info->sps_info.chroma_format_idc == 3) ? 6 : 2; + if (h264_pps_info->transform_8x8_mode_flag) { + unsigned char *seq_scllstflg = + h264_seq_hdr_info->sps_info.seq_scaling_list_present_flag; + unsigned char *def_sclmatflg_seq = + h264_seq_hdr_info->sps_info.usedefaultscalingmatrixflag_seq; + + if (h264_pps_info->pic_scaling_matrix_present_flag) { + for (i = 0; i < num8x8_lists; i++) { + if (h264_pps_info->pic_scaling_list_present_flag[i + + H264FW_NUM_4X4_LISTS]) { + if (h264_pps_info->usedefaultscalingmatrixflag_pic[i + + H264FW_NUM_4X4_LISTS]) { + quant_matrix = (i & 0x1) ? default_8x8_inter + : default_8x8_intra; + } else { + VDEC_ASSERT(h264_pps_info->scllst8x8pic); + if (scllst8x8pic) + quant_matrix = (*scllst8x8pic)[i]; + } + } else { + if + (h264_seq_hdr_info->sps_info.seq_scaling_matrix_present_flag) { + /* SPS matrix present - use fallback rule B */ + /* list 6 - first 8x8 Intra list */ + if (i == 0) { + if (seq_scllstflg[i + + H264FW_NUM_4X4_LISTS] && + !def_sclmatflg_seq[i + + H264FW_NUM_4X4_LISTS]) { + VDEC_ASSERT + (h264_seq_hdr_info->sps_info.scllst8x8seq); + if (scllst8x8seq) + quant_matrix = (*scllst8x8seq)[i]; + } else { + quant_matrix = default_8x8_intra; + } + /* list 7 - first 8x8 Inter list */ + } else if (i == 1) { + if (seq_scllstflg[i + + H264FW_NUM_4X4_LISTS] && + !def_sclmatflg_seq[i + + H264FW_NUM_4X4_LISTS]) { + VDEC_ASSERT + (h264_seq_hdr_info->sps_info.scllst8x8seq); + if (scllst8x8seq) + quant_matrix = (*scllst8x8seq)[i]; + } else { + quant_matrix = default_8x8_inter; + } + } else { + quant_matrix = + h264fw_pps_info->scalinglist8x8[i - 2]; + } + } else { + /* SPS matrix not present - use fallback rule A */ + /* list 6 - first 8x8 Intra list */ + if (i == 0) + quant_matrix = default_8x8_intra; + /* list 7 - first 8x8 Inter list */ + else if (i == 1) + quant_matrix = default_8x8_inter; + else + quant_matrix = + h264fw_pps_info->scalinglist8x8[i - 2]; + } + } + if (quant_matrix) { + /* copy correct 8x8 list to output - as selected by PPS */ + memcpy(h264fw_pps_info->scalinglist8x8[i], quant_matrix, + sizeof(h264fw_pps_info->scalinglist8x8[i])); + } + } + } else { + /* PPS matrix not present, use SPS information */ + if (h264_seq_hdr_info->sps_info.seq_scaling_matrix_present_flag) { + for (i = 0; i < num8x8_lists; i++) { + if (seq_scllstflg[i + H264FW_NUM_4X4_LISTS] && + def_sclmatflg_seq[i + H264FW_NUM_4X4_LISTS]) { + quant_matrix = + (i & 0x1) ? default_8x8_inter : + default_8x8_intra; + } else if ((seq_scllstflg[i + H264FW_NUM_4X4_LISTS]) && + !(def_sclmatflg_seq[i + H264FW_NUM_4X4_LISTS])) { + VDEC_ASSERT + (h264_seq_hdr_info->sps_info.scllst8x8seq); + if (scllst8x8seq) + quant_matrix = (*scllst8x8seq)[i]; + } else if (!(seq_scllstflg[i + H264FW_NUM_4X4_LISTS]) && + (i == 0)) { + /* SPS list not present - use fallback rule A */ + /* list 6 - first 8x8 Intra list */ + quant_matrix = default_8x8_intra; + } else if (!(seq_scllstflg[i + H264FW_NUM_4X4_LISTS]) && + (i == 1)) { + /* list 7 - first 8x8 Inter list */ + quant_matrix = default_8x8_inter; + } else { + quant_matrix = + h264fw_pps_info->scalinglist8x8 + [i - 2]; + } + if (quant_matrix) { + /* copy correct 8x8 list to output - + * as selected by SPS + */ + memcpy(h264fw_pps_info->scalinglist8x8[i], + quant_matrix, + sizeof(h264fw_pps_info->scalinglist8x8[i])); + } + } + } else { + /* SPS matrix not present - use flat lists */ + quant_matrix = default_8x8_org; + for (i = 0; i < num8x8_lists; i++) + memcpy(h264fw_pps_info->scalinglist8x8[i], quant_matrix, + sizeof(h264fw_pps_info->scalinglist8x8[i])); + } + } + } +} + +static void bspp_h264_fwpps_populate(struct bspp_h264_pps_info *h264_pps_info, + struct h264fw_picture_ps *h264fw_pps_info) +{ + h264fw_pps_info->deblocking_filter_control_present_flag = + h264_pps_info->deblocking_filter_control_present_flag; + h264fw_pps_info->transform_8x8_mode_flag = h264_pps_info->transform_8x8_mode_flag; + h264fw_pps_info->entropy_coding_mode_flag = h264_pps_info->entropy_coding_mode_flag; + h264fw_pps_info->redundant_pic_cnt_present_flag = + h264_pps_info->redundant_pic_cnt_present_flag; + h264fw_pps_info->weighted_bipred_idc = h264_pps_info->weighted_bipred_idc; + h264fw_pps_info->weighted_pred_flag = h264_pps_info->weighted_pred_flag; + h264fw_pps_info->pic_order_present_flag = h264_pps_info->pic_order_present_flag; + h264fw_pps_info->pic_init_qp = h264_pps_info->pic_init_qp_minus26 + 26; + h264fw_pps_info->constrained_intra_pred_flag = h264_pps_info->constrained_intra_pred_flag; + VDEC_ASSERT(sizeof(h264fw_pps_info->num_ref_lx_active_minus1) == + sizeof(h264_pps_info->num_ref_idx_lx_active_minus1)); + VDEC_ASSERT(sizeof(h264fw_pps_info->num_ref_lx_active_minus1) == + sizeof(unsigned char) * H264FW_MAX_REFPIC_LISTS); + memcpy(h264fw_pps_info->num_ref_lx_active_minus1, + h264_pps_info->num_ref_idx_lx_active_minus1, + sizeof(h264fw_pps_info->num_ref_lx_active_minus1)); + h264fw_pps_info->slice_group_map_type = h264_pps_info->slice_group_map_type; + h264fw_pps_info->num_slice_groups_minus1 = h264_pps_info->num_slice_groups_minus1; + h264fw_pps_info->slice_group_change_rate_minus1 = + h264_pps_info->slice_group_change_rate_minus1; + h264fw_pps_info->chroma_qp_index_offset = h264_pps_info->chroma_qp_index_offset; + h264fw_pps_info->second_chroma_qp_index_offset = + h264_pps_info->second_chroma_qp_index_offset; +} + +static void bspp_h264_fwseq_hdr_populate(struct bspp_h264_seq_hdr_info *h264_seq_hdr_info, + struct h264fw_sequence_ps *h264_fwseq_hdr_info) +{ + /* Basic SPS */ + h264_fwseq_hdr_info->profile_idc = h264_seq_hdr_info->sps_info.profile_idc; + h264_fwseq_hdr_info->chroma_format_idc = h264_seq_hdr_info->sps_info.chroma_format_idc; + h264_fwseq_hdr_info->separate_colour_plane_flag = + h264_seq_hdr_info->sps_info.separate_colour_plane_flag; + h264_fwseq_hdr_info->bit_depth_luma_minus8 = + h264_seq_hdr_info->sps_info.bit_depth_luma_minus8; + h264_fwseq_hdr_info->bit_depth_chroma_minus8 = + h264_seq_hdr_info->sps_info.bit_depth_chroma_minus8; + h264_fwseq_hdr_info->delta_pic_order_always_zero_flag = + h264_seq_hdr_info->sps_info.delta_pic_order_always_zero_flag; + h264_fwseq_hdr_info->log2_max_pic_order_cnt_lsb = + h264_seq_hdr_info->sps_info.log2_max_pic_order_cnt_lsb_minus4 + 4; + h264_fwseq_hdr_info->max_num_ref_frames = h264_seq_hdr_info->sps_info.max_num_ref_frames; + h264_fwseq_hdr_info->log2_max_frame_num = + h264_seq_hdr_info->sps_info.log2_max_frame_num_minus4 + 4; + h264_fwseq_hdr_info->pic_order_cnt_type = h264_seq_hdr_info->sps_info.pic_order_cnt_type; + h264_fwseq_hdr_info->frame_mbs_only_flag = h264_seq_hdr_info->sps_info.frame_mbs_only_flag; + h264_fwseq_hdr_info->gaps_in_frame_num_value_allowed_flag = + h264_seq_hdr_info->sps_info.gaps_in_frame_num_value_allowed_flag; + h264_fwseq_hdr_info->constraint_set_flags = + h264_seq_hdr_info->sps_info.constraint_set_flags; + h264_fwseq_hdr_info->level_idc = h264_seq_hdr_info->sps_info.level_idc; + h264_fwseq_hdr_info->num_ref_frames_in_pic_order_cnt_cycle = + h264_seq_hdr_info->sps_info.num_ref_frames_in_pic_order_cnt_cycle; + h264_fwseq_hdr_info->mb_adaptive_frame_field_flag = + h264_seq_hdr_info->sps_info.mb_adaptive_frame_field_flag; + h264_fwseq_hdr_info->offset_for_non_ref_pic = + h264_seq_hdr_info->sps_info.offset_for_non_ref_pic; + h264_fwseq_hdr_info->offset_for_top_to_bottom_field = + h264_seq_hdr_info->sps_info.offset_for_top_to_bottom_field; + h264_fwseq_hdr_info->pic_width_in_mbs_minus1 = + h264_seq_hdr_info->sps_info.pic_width_in_mbs_minus1; + h264_fwseq_hdr_info->pic_height_in_map_units_minus1 = + h264_seq_hdr_info->sps_info.pic_height_in_map_units_minus1; + h264_fwseq_hdr_info->direct_8x8_inference_flag = + h264_seq_hdr_info->sps_info.direct_8x8_inference_flag; + h264_fwseq_hdr_info->qpprime_y_zero_transform_bypass_flag = + h264_seq_hdr_info->sps_info.qpprime_y_zero_transform_bypass_flag; + + if (h264_seq_hdr_info->sps_info.offset_for_ref_frame) + memcpy(h264_fwseq_hdr_info->offset_for_ref_frame, + h264_seq_hdr_info->sps_info.offset_for_ref_frame, + sizeof(h264_fwseq_hdr_info->offset_for_ref_frame)); + else + memset(h264_fwseq_hdr_info->offset_for_ref_frame, 0x00, + sizeof(h264_fwseq_hdr_info->offset_for_ref_frame)); + + memset(h264_fwseq_hdr_info->anchor_inter_view_reference_id_list, 0x00, + sizeof(h264_fwseq_hdr_info->anchor_inter_view_reference_id_list)); + memset(h264_fwseq_hdr_info->non_anchor_inter_view_reference_id_list, 0x00, + sizeof(h264_fwseq_hdr_info->non_anchor_inter_view_reference_id_list)); + +#ifdef REDUCED_DPB_NO_PIC_REORDERING + /* From VUI */ + h264_fwseq_hdr_info->max_dec_frame_buffering = + h264_seq_hdr_info->vui_info.max_dec_frame_buffering; + h264_fwseq_hdr_info->num_reorder_frames = h264_seq_hdr_info->vui_info.num_reorder_frames; +#else + /* From VUI */ + if (h264_seq_hdr_info->vui_info.bitstream_restriction_flag) { + VDEC_ASSERT(h264_seq_hdr_info->sps_info.vui_parameters_present_flag); + h264_fwseq_hdr_info->max_dec_frame_buffering = + h264_seq_hdr_info->vui_info.max_dec_frame_buffering; + h264_fwseq_hdr_info->num_reorder_frames = + h264_seq_hdr_info->vui_info.num_reorder_frames; + } else { + h264_fwseq_hdr_info->max_dec_frame_buffering = 1; + h264_fwseq_hdr_info->num_reorder_frames = 16; + } +#endif +} + +static void bspp_h264_commonseq_hdr_populate(struct bspp_h264_seq_hdr_info *h264_seq_hdr_info, + struct vdec_comsequ_hdrinfo *comseq_hdr_info) +{ + struct bspp_h264_sps_info *sps_info = &h264_seq_hdr_info->sps_info; + struct bspp_h264_vui_info *vui_info = &h264_seq_hdr_info->vui_info; + + comseq_hdr_info->codec_profile = sps_info->profile_idc; + comseq_hdr_info->codec_level = sps_info->level_idc; + + if (sps_info->vui_parameters_present_flag && vui_info->timing_info_present_flag) { + comseq_hdr_info->frame_rate_num = vui_info->time_scale; + comseq_hdr_info->frame_rate_den = 2 * vui_info->num_units_in_tick; + comseq_hdr_info->frame_rate = ((long)comseq_hdr_info->frame_rate_num) / + ((long)comseq_hdr_info->frame_rate_den); + } + + /* + * ColorSpace Description was present in the VUI parameters. + * copy it in CommonSeqHdr info for use by application. + */ + if (vui_info->video_signal_type_present_flag & vui_info->colour_description_present_flag) { + comseq_hdr_info->color_space_info.is_present = TRUE; + comseq_hdr_info->color_space_info.color_primaries = vui_info->colour_primaries; + comseq_hdr_info->color_space_info.transfer_characteristics = + vui_info->transfer_characteristics; + comseq_hdr_info->color_space_info.matrix_coefficients = + vui_info->matrix_coefficients; + } + + if (vui_info->aspect_ratio_info_present_flag) { + comseq_hdr_info->aspect_ratio_num = vui_info->sar_width; + comseq_hdr_info->aspect_ratio_den = vui_info->sar_height; + } + + comseq_hdr_info->interlaced_frames = sps_info->frame_mbs_only_flag ? 0 : 1; + + /* pixel_info populate */ + VDEC_ASSERT(sps_info->chroma_format_idc < 4); + comseq_hdr_info->pixel_info.chroma_fmt = (sps_info->chroma_format_idc == 0) ? 0 : 1; + comseq_hdr_info->pixel_info.chroma_fmt_idc = pixel_format_idc[sps_info->chroma_format_idc]; + comseq_hdr_info->pixel_info.chroma_interleave = + ((sps_info->chroma_format_idc == 0) || + (sps_info->chroma_format_idc == 3 && sps_info->separate_colour_plane_flag)) ? + PIXEL_INVALID_CI : PIXEL_UV_ORDER; + comseq_hdr_info->pixel_info.num_planes = + (sps_info->chroma_format_idc == 0) ? 1 : + (sps_info->chroma_format_idc == 3 && sps_info->separate_colour_plane_flag) ? 3 : 2; + comseq_hdr_info->pixel_info.bitdepth_y = sps_info->bit_depth_luma_minus8 + 8; + comseq_hdr_info->pixel_info.bitdepth_c = sps_info->bit_depth_chroma_minus8 + 8; + comseq_hdr_info->pixel_info.mem_pkg = + (comseq_hdr_info->pixel_info.bitdepth_y > 8 || + comseq_hdr_info->pixel_info.bitdepth_c > 8) ? + PIXEL_BIT10_MSB_MP : PIXEL_BIT8_MP; + comseq_hdr_info->pixel_info.pixfmt = + pixel_get_pixfmt(comseq_hdr_info->pixel_info.chroma_fmt_idc, + comseq_hdr_info->pixel_info.chroma_interleave, + comseq_hdr_info->pixel_info.mem_pkg, + comseq_hdr_info->pixel_info.bitdepth_y, + comseq_hdr_info->pixel_info.bitdepth_c, + comseq_hdr_info->pixel_info.num_planes); + + /* max_frame_size populate */ + comseq_hdr_info->max_frame_size.width = (sps_info->pic_width_in_mbs_minus1 + 1) * 16; + /* + * H264 has always coded size MB aligned. For sequences which *may* have Field-Coded + * pictures, as described by the frame_mbs_only_flag, the pic_height_in_map_units_minus1 + * refers to field height in MBs, so to find the actual Frame height we need to do + * Field_MBs_InHeight * 32 + */ + comseq_hdr_info->max_frame_size.height = (sps_info->pic_height_in_map_units_minus1 + 1) * + (sps_info->frame_mbs_only_flag ? 1 : 2) * 16; + + /* Passing 2*N to vxd_dec so that get_nbuffers can use formula N+3 for all codecs*/ + comseq_hdr_info->max_ref_frame_num = 2 * sps_info->max_num_ref_frames; + + comseq_hdr_info->field_codec_mblocks = sps_info->mb_adaptive_frame_field_flag; + comseq_hdr_info->min_pict_buf_num = vui_info->max_dec_frame_buffering; + + /* orig_display_region populate */ + if (sps_info->frame_cropping_flag) { + int sub_width_c, sub_height_c, crop_unit_x, crop_unit_y; + int frame_crop_left, frame_crop_right, frame_crop_top, frame_crop_bottom; + + sub_width_c = bspp_h264_get_subwidthc(sps_info->chroma_format_idc, + sps_info->separate_colour_plane_flag); + + sub_height_c = bspp_h264_get_subheightc(sps_info->chroma_format_idc, + sps_info->separate_colour_plane_flag); + + /* equation source: ITU-T H.264 2010/03, page 77 */ + /* ChromaArrayType == 0 */ + if (sps_info->separate_colour_plane_flag || sps_info->chroma_format_idc == 0) { + /* (7-18) */ + crop_unit_x = 1; + /* (7-19) */ + crop_unit_y = 2 - sps_info->frame_mbs_only_flag; + /* ChromaArrayType == chroma_format_idc */ + } else { + /* (7-20) */ + crop_unit_x = sub_width_c; + /* (7-21) */ + crop_unit_y = sub_height_c * (2 - sps_info->frame_mbs_only_flag); + } + + VDEC_ASSERT(sps_info->frame_crop_left_offset <= + (comseq_hdr_info->max_frame_size.width / crop_unit_x) - + (sps_info->frame_crop_right_offset + 1)); + + VDEC_ASSERT(sps_info->frame_crop_top_offset <= + (comseq_hdr_info->max_frame_size.height / crop_unit_y) - + (sps_info->frame_crop_bottom_offset + 1)); + frame_crop_left = crop_unit_x * sps_info->frame_crop_left_offset; + frame_crop_right = comseq_hdr_info->max_frame_size.width - + (crop_unit_x * sps_info->frame_crop_right_offset); + frame_crop_top = crop_unit_y * sps_info->frame_crop_top_offset; + frame_crop_bottom = comseq_hdr_info->max_frame_size.height - + (crop_unit_y * sps_info->frame_crop_bottom_offset); + comseq_hdr_info->orig_display_region.left_offset = (unsigned int)frame_crop_left; + comseq_hdr_info->orig_display_region.top_offset = (unsigned int)frame_crop_top; + comseq_hdr_info->orig_display_region.width = (frame_crop_right - frame_crop_left); + comseq_hdr_info->orig_display_region.height = (frame_crop_bottom - frame_crop_top); + } else { + comseq_hdr_info->orig_display_region.left_offset = 0; + comseq_hdr_info->orig_display_region.top_offset = 0; + comseq_hdr_info->orig_display_region.width = comseq_hdr_info->max_frame_size.width; + comseq_hdr_info->orig_display_region.height = + comseq_hdr_info->max_frame_size.height; + } + +#ifdef REDUCED_DPB_NO_PIC_REORDERING + comseq_hdr_info->max_reorder_picts = vui_info->max_dec_frame_buffering; +#else + if (sps_info->vui_parameters_present_flag && vui_info->bitstream_restriction_flag) + comseq_hdr_info->max_reorder_picts = vui_info->max_dec_frame_buffering; + else + comseq_hdr_info->max_reorder_picts = 0; +#endif + comseq_hdr_info->separate_chroma_planes = + h264_seq_hdr_info->sps_info.separate_colour_plane_flag ? 1 : 0; +} + +static void bspp_h264_pict_hdr_populate(enum h264_nalunittype nal_unit_type, + struct bspp_h264_slice_hdr_info *h264_slice_hdr_info, + struct vdec_comsequ_hdrinfo *comseq_hdr_info, + struct bspp_pict_hdr_info *pict_hdr_info) +{ + /* + * H264 has slice coding type, not picture. The bReference contrary to the rest of the + * standards is set explicitly from the NAL externally (see just below the call to + * bspp_h264_pict_hdr_populate) pict_hdr_info->bReference = ? (Set externally for H264) + */ + pict_hdr_info->intra_coded = (nal_unit_type == H264_NALTYPE_IDR_SLICE) ? 1 : 0; + pict_hdr_info->field = h264_slice_hdr_info->field_pic_flag; + + pict_hdr_info->post_processing = 0; + /* For H264 Maximum and Coded sizes are the same */ + pict_hdr_info->coded_frame_size.width = comseq_hdr_info->max_frame_size.width; + /* For H264 Maximum and Coded sizes are the same */ + pict_hdr_info->coded_frame_size.height = comseq_hdr_info->max_frame_size.height; + /* + * For H264 Encoded Display size has been precomputed as part of the + * common sequence info + */ + pict_hdr_info->disp_info.enc_disp_region = comseq_hdr_info->orig_display_region; + /* + * For H264 there is no resampling, so encoded and actual display + * regions are the same + */ + pict_hdr_info->disp_info.disp_region = comseq_hdr_info->orig_display_region; + /* H264 does not have that */ + pict_hdr_info->disp_info.num_pan_scan_windows = 0; + memset(pict_hdr_info->disp_info.pan_scan_windows, 0, + sizeof(pict_hdr_info->disp_info.pan_scan_windows)); +} + +static int bspp_h264_destroy_seq_hdr_info(const void *secure_sps_info) +{ + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info = NULL; + + if (!secure_sps_info) + return IMG_ERROR_INVALID_PARAMETERS; + + h264_seq_hdr_info = (struct bspp_h264_seq_hdr_info *)secure_sps_info; + + /* Cleaning vui_info */ + kfree(h264_seq_hdr_info->vui_info.nal_hrd_parameters.bit_rate_value_minus1); + kfree(h264_seq_hdr_info->vui_info.nal_hrd_parameters.cpb_size_value_minus1); + kfree(h264_seq_hdr_info->vui_info.nal_hrd_parameters.cbr_flag); + kfree(h264_seq_hdr_info->vui_info.vcl_hrd_parameters.bit_rate_value_minus1); + kfree(h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cpb_size_value_minus1); + kfree(h264_seq_hdr_info->vui_info.vcl_hrd_parameters.cbr_flag); + + /* Cleaning sps_info */ + kfree(h264_seq_hdr_info->sps_info.offset_for_ref_frame); + kfree(h264_seq_hdr_info->sps_info.scllst4x4seq); + kfree(h264_seq_hdr_info->sps_info.scllst8x8seq); + + return 0; +} + +static int bspp_h264_destroy_pps_info(const void *secure_pps_info) +{ + struct bspp_h264_pps_info *h264_pps_info = NULL; + + if (!secure_pps_info) + return IMG_ERROR_INVALID_PARAMETERS; + + h264_pps_info = (struct bspp_h264_pps_info *)secure_pps_info; + kfree(h264_pps_info->h264_ppssgm_info.slice_group_id); + h264_pps_info->h264_ppssgm_info.slicegroupidnum = 0; + kfree(h264_pps_info->scllst4x4pic); + kfree(h264_pps_info->scllst8x8pic); + + return 0; +} + +static int bspp_h264_destroy_data(enum bspp_unit_type data_type, void *data_handle) +{ + int result = 0; + + if (!data_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + switch (data_type) { + case BSPP_UNIT_SEQUENCE: + result = bspp_h264_destroy_seq_hdr_info(data_handle); + break; + case BSPP_UNIT_PPS: + result = bspp_h264_destroy_pps_info(data_handle); + break; + default: + break; + } + return result; +} + +static void bspp_h264_generate_slice_groupmap(struct bspp_h264_slice_hdr_info *h264_slice_hdr_info, + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info, + struct bspp_h264_pps_info *h264_pps_info, + unsigned char *map_unit_to_slice_groupmap, + unsigned int map_size) +{ + int group; + unsigned int num_slice_group_mapunits; + unsigned int i = 0, j, k = 0; + unsigned char num_slice_groups = h264_pps_info->num_slice_groups_minus1 + 1; + unsigned int pic_width_in_mbs = h264_seq_hdr_info->sps_info.pic_width_in_mbs_minus1 + 1; + unsigned int pic_height_in_map_units = + h264_seq_hdr_info->sps_info.pic_height_in_map_units_minus1 + 1; + + num_slice_group_mapunits = map_size; + if (h264_pps_info->slice_group_map_type == 6) { + if ((unsigned int)num_slice_groups != num_slice_group_mapunits) { + VDEC_ASSERT + ("wrong pps->num_slice_group_map_units_minus1 for used SPS and FMO type 6" + == + NULL); + if (num_slice_group_mapunits > + h264_pps_info->h264_ppssgm_info.slicegroupidnum) + num_slice_group_mapunits = + h264_pps_info->h264_ppssgm_info.slicegroupidnum; + } + } + + /* only one slice group */ + if (h264_pps_info->num_slice_groups_minus1 == 0) { + memset(map_unit_to_slice_groupmap, 0, map_size * sizeof(unsigned char)); + return; + } + if (h264_pps_info->num_slice_groups_minus1 >= MAX_SLICEGROUP_COUNT) { + memset(map_unit_to_slice_groupmap, 0, map_size * sizeof(unsigned char)); + return; + } + if (h264_pps_info->slice_group_map_type == 0) { + do { + for (group = + 0; + group <= h264_pps_info->num_slice_groups_minus1 && + i < num_slice_group_mapunits; + i += h264_pps_info->run_length_minus1[group++] + 1) { + for (j = 0; + j <= h264_pps_info->run_length_minus1[group] && + i + j < num_slice_group_mapunits; + j++) + map_unit_to_slice_groupmap[i + j] = group; + } + } while (i < num_slice_group_mapunits); + } else if (h264_pps_info->slice_group_map_type == 1) { + for (i = 0; i < num_slice_group_mapunits; i++) { + map_unit_to_slice_groupmap[i] = ((i % pic_width_in_mbs) + + (((i / pic_width_in_mbs) * + (h264_pps_info->num_slice_groups_minus1 + 1)) / 2)) % + (h264_pps_info->num_slice_groups_minus1 + 1); + } + } else if (h264_pps_info->slice_group_map_type == 2) { + unsigned int y_top_left, x_top_left, y_bottom_right, x_bottom_right, x, y; + + for (i = 0; i < num_slice_group_mapunits; i++) + map_unit_to_slice_groupmap[i] = h264_pps_info->num_slice_groups_minus1; + + for (group = h264_pps_info->num_slice_groups_minus1 - 1; group >= 0; group--) { + y_top_left = h264_pps_info->top_left[group] / pic_width_in_mbs; + x_top_left = h264_pps_info->top_left[group] % pic_width_in_mbs; + y_bottom_right = h264_pps_info->bottom_right[group] / pic_width_in_mbs; + x_bottom_right = h264_pps_info->bottom_right[group] % pic_width_in_mbs; + for (y = y_top_left; y <= y_bottom_right; y++) + for (x = x_top_left; x <= x_bottom_right; x++) { + if (h264_pps_info->top_left[group] > + h264_pps_info->bottom_right[group] || + h264_pps_info->bottom_right[group] >= + num_slice_group_mapunits) + continue; + map_unit_to_slice_groupmap[y * pic_width_in_mbs + + x] = group; + } + } + } else if (h264_pps_info->slice_group_map_type == 3) { + int left_bound, top_bound, right_bound, bottom_bound; + int x, y, x_dir, y_dir; + int map_unit_vacant; + + unsigned int mapunits_in_slicegroup_0 = + umin((unsigned int)((h264_pps_info->slice_group_change_rate_minus1 + 1) * + h264_slice_hdr_info->slice_group_change_cycle), + (unsigned int)num_slice_group_mapunits); + + for (i = 0; i < num_slice_group_mapunits; i++) + map_unit_to_slice_groupmap[i] = 2; + + x = (pic_width_in_mbs - h264_pps_info->slice_group_change_direction_flag) / 2; + y = (pic_height_in_map_units - h264_pps_info->slice_group_change_direction_flag) / + 2; + + left_bound = x; + top_bound = y; + right_bound = x; + bottom_bound = y; + + x_dir = h264_pps_info->slice_group_change_direction_flag - 1; + y_dir = h264_pps_info->slice_group_change_direction_flag; + + for (k = 0; k < num_slice_group_mapunits; k += map_unit_vacant) { + map_unit_vacant = + (map_unit_to_slice_groupmap[y * pic_width_in_mbs + x] == + 2); + if (map_unit_vacant) + map_unit_to_slice_groupmap[y * pic_width_in_mbs + x] = + (k >= mapunits_in_slicegroup_0); + + if (x_dir == -1 && x == left_bound) { + left_bound = smax(left_bound - 1, 0); + x = left_bound; + x_dir = 0; + y_dir = 2 * h264_pps_info->slice_group_change_direction_flag - 1; + } else if (x_dir == 1 && x == right_bound) { + right_bound = smin(right_bound + 1, (int)pic_width_in_mbs - 1); + x = right_bound; + x_dir = 0; + y_dir = 1 - 2 * h264_pps_info->slice_group_change_direction_flag; + } else if (y_dir == -1 && y == top_bound) { + top_bound = smax(top_bound - 1, 0); + y = top_bound; + x_dir = 1 - 2 * h264_pps_info->slice_group_change_direction_flag; + y_dir = 0; + } else if (y_dir == 1 && y == bottom_bound) { + bottom_bound = smin(bottom_bound + 1, + (int)pic_height_in_map_units - 1); + y = bottom_bound; + x_dir = 2 * h264_pps_info->slice_group_change_direction_flag - 1; + y_dir = 0; + } else { + x = x + x_dir; + y = y + y_dir; + } + } + } else if (h264_pps_info->slice_group_map_type == 4) { + unsigned int mapunits_in_slicegroup_0 = + umin((unsigned int)((h264_pps_info->slice_group_change_rate_minus1 + 1) * + h264_slice_hdr_info->slice_group_change_cycle), + (unsigned int)num_slice_group_mapunits); + unsigned int sizeof_upper_left_group = + h264_pps_info->slice_group_change_direction_flag ? + (num_slice_group_mapunits - + mapunits_in_slicegroup_0) : mapunits_in_slicegroup_0; + for (i = 0; i < num_slice_group_mapunits; i++) { + if (i < sizeof_upper_left_group) + map_unit_to_slice_groupmap[i] = + h264_pps_info->slice_group_change_direction_flag; + + else + map_unit_to_slice_groupmap[i] = 1 - + h264_pps_info->slice_group_change_direction_flag; + } + } else if (h264_pps_info->slice_group_map_type == 5) { + unsigned int mapunits_in_slicegroup_0 = + umin((unsigned int)((h264_pps_info->slice_group_change_rate_minus1 + 1) * + h264_slice_hdr_info->slice_group_change_cycle), + (unsigned int)num_slice_group_mapunits); + unsigned int sizeof_upper_left_group = + h264_pps_info->slice_group_change_direction_flag ? + (num_slice_group_mapunits - + mapunits_in_slicegroup_0) : mapunits_in_slicegroup_0; + + for (j = 0; j < (unsigned int)pic_width_in_mbs; j++) { + for (i = 0; i < (unsigned int)pic_height_in_map_units; i++) { + if (k++ < sizeof_upper_left_group) + map_unit_to_slice_groupmap[i * pic_width_in_mbs + j] = + h264_pps_info->slice_group_change_direction_flag; + else + map_unit_to_slice_groupmap[i * pic_width_in_mbs + j] = + 1 - + h264_pps_info->slice_group_change_direction_flag; + } + } + } else if (h264_pps_info->slice_group_map_type == 6) { + VDEC_ASSERT(num_slice_group_mapunits <= + h264_pps_info->h264_ppssgm_info.slicegroupidnum); + for (i = 0; i < num_slice_group_mapunits; i++) + map_unit_to_slice_groupmap[i] = + h264_pps_info->h264_ppssgm_info.slice_group_id[i]; + } +} + +static int bspp_h264_parse_mvc_slice_extension(void *swsr_context, + struct bspp_h264_inter_pict_ctx *inter_pict_ctx) +{ + if (!swsr_read_bits(swsr_context, 1)) { + swsr_read_bits(swsr_context, 7); + inter_pict_ctx->current_view_id = swsr_read_bits(swsr_context, 10); + swsr_read_bits(swsr_context, 6); + return 1; + } + + return 0; +} + +static int bspp_h264_unitparser_compile_sgmdata + (struct bspp_h264_slice_hdr_info *h264_slice_hdr_info, + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info, + struct bspp_h264_pps_info *h264_pps_info, + struct bspp_pict_hdr_info *pict_hdr_info) +{ + memset(&pict_hdr_info->pict_sgm_data, 0, sizeof(*&pict_hdr_info->pict_sgm_data)); + + pict_hdr_info->pict_sgm_data.id = 1; + + /* Allocate memory for SGM. */ + pict_hdr_info->pict_sgm_data.size = + (h264_seq_hdr_info->sps_info.pic_height_in_map_units_minus1 + 1) * + (h264_seq_hdr_info->sps_info.pic_width_in_mbs_minus1 + 1); + + pict_hdr_info->pict_sgm_data.pic_data = kmalloc((pict_hdr_info->pict_sgm_data.size), + GFP_KERNEL); + VDEC_ASSERT(pict_hdr_info->pict_sgm_data.pic_data); + if (!pict_hdr_info->pict_sgm_data.pic_data) { + pict_hdr_info->pict_sgm_data.id = BSPP_INVALID; + return IMG_ERROR_OUT_OF_MEMORY; + } + + bspp_h264_generate_slice_groupmap(h264_slice_hdr_info, h264_seq_hdr_info, h264_pps_info, + pict_hdr_info->pict_sgm_data.pic_data, + pict_hdr_info->pict_sgm_data.size); + + /* check the discontinuous_mbs_flaginCurrFrame flag for FMO */ + /* NO FMO support */ + pict_hdr_info->discontinuous_mbs = 0; + + return 0; +} + +static int bspp_h264_unit_parser(void *swsr_context, struct bspp_unit_data *unit_data) +{ + unsigned int result = 0; + enum bspp_error_type parse_error = BSPP_ERROR_NONE; + enum h264_nalunittype nal_unit_type; + unsigned char nal_ref_idc; + struct bspp_h264_inter_pict_ctx *interpicctx; + struct bspp_sequence_hdr_info *out_seq_info; + unsigned char id; + + interpicctx = &unit_data->parse_state->inter_pict_ctx->h264_ctx; + out_seq_info = unit_data->out.sequ_hdr_info; + + /* At this point we should be EXACTLY at the NALTYPE byte */ + /* parse the nal header type */ + swsr_read_bits(swsr_context, 1); + nal_ref_idc = swsr_read_bits(swsr_context, 2); + nal_unit_type = (enum h264_nalunittype)swsr_read_bits(swsr_context, 5); + + switch (unit_data->unit_type) { + case BSPP_UNIT_SEQUENCE: + VDEC_ASSERT(nal_unit_type == H264_NALTYPE_SEQUENCE_PARAMETER_SET || + nal_unit_type == H264_NALTYPE_SUBSET_SPS); + { + unsigned char id_loc; + /* Parse SPS structure */ + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info = + (struct bspp_h264_seq_hdr_info *)(out_seq_info->secure_sequence_info); + /* FW SPS Data structure */ + struct bspp_ddbuf_array_info *tmp = &out_seq_info->fw_sequence; + struct h264fw_sequence_ps *h264_fwseq_hdr_info = + (struct h264fw_sequence_ps *)((unsigned char *)tmp->ddbuf_info.cpu_virt_addr + + tmp->buf_offset); + /* Common Sequence Header Info */ + struct vdec_comsequ_hdrinfo *comseq_hdr_info = + &out_seq_info->sequ_hdr_info.com_sequ_hdr_info; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("Unit Parser:Found SEQUENCE_PARAMETER_SET NAL unit"); +#endif + VDEC_ASSERT(h264_seq_hdr_info); + VDEC_ASSERT(h264_fwseq_hdr_info); + if (!h264_seq_hdr_info) + return IMG_ERROR_ALREADY_COMPLETE; + + if (!h264_fwseq_hdr_info) + return IMG_ERROR_ALREADY_COMPLETE; + + /* Call SPS parser to populate the "Parse SPS Structure" */ + unit_data->parse_error |= + bspp_h264_sps_parser(swsr_context, unit_data->str_res_handle, + h264_seq_hdr_info); + /* From "Parse SPS Structure" populate the "FW SPS Data Structure" */ + bspp_h264_fwseq_hdr_populate(h264_seq_hdr_info, h264_fwseq_hdr_info); + /* + * From "Parse SPS Structure" populate the + * "Common Sequence Header Info" + */ + bspp_h264_commonseq_hdr_populate(h264_seq_hdr_info, comseq_hdr_info); + /* Set the SPS ID */ + /* + * seq_parameter_set_id is always in range 0-31, so we can + * add offset indicating subsequence header + */ + id_loc = h264_seq_hdr_info->sps_info.seq_parameter_set_id; + out_seq_info->sequ_hdr_info.sequ_hdr_id = + (nal_unit_type == H264_NALTYPE_SLICE_SCALABLE || + nal_unit_type == H264_NALTYPE_SLICE_IDR_SCALABLE || + nal_unit_type == H264_NALTYPE_SUBSET_SPS) ? id_loc + 32 : id_loc; + + /* + * Set the first SPS ID as Active SPS ID for SEI parsing + * to cover the case of not having SeiBufferingPeriod to + * give us the SPS ID + */ + if (interpicctx->active_sps_for_sei_parsing == BSPP_INVALID) + interpicctx->active_sps_for_sei_parsing = + h264_seq_hdr_info->sps_info.seq_parameter_set_id; + } + break; + + case BSPP_UNIT_PPS: + VDEC_ASSERT(nal_unit_type == H264_NALTYPE_PICTURE_PARAMETER_SET); + { + /* Parse PPS structure */ + struct bspp_h264_pps_info *h264_pps_info = + (struct bspp_h264_pps_info *)(unit_data->out.pps_info->secure_pps_info); + /* FW PPS Data structure */ + struct bspp_ddbuf_array_info *tmp = &unit_data->out.pps_info->fw_pps; + struct h264fw_picture_ps *h264fw_pps_info = + (struct h264fw_picture_ps *)((unsigned char *) + tmp->ddbuf_info.cpu_virt_addr + tmp->buf_offset); + +#ifdef DEBUG_DECODER_DRIVER + pr_info("Unit Parser:Found PICTURE_PARAMETER_SET NAL unit"); +#endif + VDEC_ASSERT(h264_pps_info); + VDEC_ASSERT(h264fw_pps_info); + + /* Call PPS parser to populate the "Parse PPS Structure" */ + unit_data->parse_error |= + bspp_h264_pps_parser(swsr_context, unit_data->str_res_handle, + h264_pps_info); + /* From "Parse PPS Structure" populate the "FW PPS Data Structure" + * - the scaling lists + */ + bspp_h264_fwpps_populate(h264_pps_info, h264fw_pps_info); + /* Set the PPS ID */ + unit_data->out.pps_info->pps_id = h264_pps_info->pps_id; + } + break; + + case BSPP_UNIT_PICTURE: + if (nal_unit_type == H264_NALTYPE_SLICE_PREFIX) { + if (bspp_h264_parse_mvc_slice_extension(swsr_context, interpicctx)) + pr_err("%s: No MVC support\n", __func__); + } else if (nal_unit_type == H264_NALTYPE_SLICE_SCALABLE || + nal_unit_type == H264_NALTYPE_SLICE_IDR_SCALABLE || + nal_unit_type == H264_NALTYPE_SLICE || + nal_unit_type == H264_NALTYPE_IDR_SLICE) { + struct bspp_h264_slice_hdr_info h264_slice_hdr_info; + struct bspp_h264_pps_info *h264_pps_info; + struct bspp_pps_info *pps_info; + struct h264fw_picture_ps *h264fw_pps_info; + struct h264fw_sequence_ps *h264_fwseq_hdr_info; + struct bspp_h264_seq_hdr_info *h264_seq_hdr_info; + struct bspp_sequence_hdr_info *sequ_hdr_info; + struct bspp_ddbuf_array_info *tmp1; + struct bspp_ddbuf_array_info *tmp2; + int current_pic_is_new = 0; + int determined = 0; + int id_loc; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("Unit Parser:Found PICTURE DATA unit"); +#endif + + unit_data->slice = 1; + unit_data->ext_slice = 0; + + if (nal_unit_type == H264_NALTYPE_SLICE_SCALABLE || + nal_unit_type == H264_NALTYPE_SLICE_IDR_SCALABLE) { + pr_err("%s: No SVC support\n", __func__); + } + + VDEC_ASSERT(unit_data->out.pict_hdr_info); + if (!unit_data->out.pict_hdr_info) + return IMG_ERROR_CANCELLED; + + /* Default */ + unit_data->out.pict_hdr_info->discontinuous_mbs = 0; + + /* + * Parse the Pic Header, return Parse SPS/PPS + * structures + */ + parse_error = bspp_h264_pict_hdr_parser(swsr_context, + unit_data->str_res_handle, + &h264_slice_hdr_info, + &pps_info, + &sequ_hdr_info, + nal_unit_type, + nal_ref_idc); + + if (parse_error) { + unit_data->parse_error |= parse_error; + return IMG_ERROR_CANCELLED; + } + + /* + * We are signalling closed GOP at every I frame + * This does not conform 100% with the + * specification but insures that seeking always + * works. + */ + unit_data->new_closed_gop = h264_slice_hdr_info.slice_type == + I_SLICE ? 1 : 0; + + /* + * Now pps_info and sequ_hdr_info contain the + * PPS/SPS info related to this picture + */ + h264_pps_info = (struct bspp_h264_pps_info *)pps_info->secure_pps_info; + h264_seq_hdr_info = + (struct bspp_h264_seq_hdr_info *)sequ_hdr_info->secure_sequence_info; + + tmp1 = &pps_info->fw_pps; + tmp2 = &sequ_hdr_info->fw_sequence; + + h264fw_pps_info = (struct h264fw_picture_ps *)((unsigned char *) + tmp1->ddbuf_info.cpu_virt_addr + tmp1->buf_offset); + h264_fwseq_hdr_info = (struct h264fw_sequence_ps *)((unsigned char *) + tmp2->ddbuf_info.cpu_virt_addr + tmp2->buf_offset); + VDEC_ASSERT(h264_slice_hdr_info.pps_id == h264_pps_info->pps_id); + VDEC_ASSERT(h264_pps_info->seq_parameter_set_id == + (unsigned int)h264_seq_hdr_info->sps_info.seq_parameter_set_id); + + /* + * Update the decoding-related FW SPS info related to the current picture + * with the SEI data that were potentially received and also relate to + * the current info. Until we receive the picture we do not know which + * sequence to update with the SEI data. + * Setfrom last SEI, needed for decoding + */ + h264_fwseq_hdr_info->disable_vdmc_filt = interpicctx->disable_vdmc_filt; + h264_fwseq_hdr_info->transform4x4_mb_not_available = + interpicctx->b4x4transform_mb_unavailable; + + /* + * Determine if current slice is a new picture, and update the related + * params for future reference + * Order of checks is important + */ + { + struct bspp_parse_state *state = unit_data->parse_state; + + set_if_not_determined_yet(&determined, state->new_view, + ¤t_pic_is_new, 1); + set_if_not_determined_yet(&determined, state->next_pic_is_new, + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + (h264_slice_hdr_info.redundant_pic_cnt > 0), + ¤t_pic_is_new, 0); + set_if_not_determined_yet + (&determined, + (state->prev_frame_num != + h264_slice_hdr_info.frame_num), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + (state->prev_pps_id != h264_slice_hdr_info.pps_id), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + (state->prev_field_pic_flag != + h264_slice_hdr_info.field_pic_flag), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + ((h264_slice_hdr_info.field_pic_flag) && + (state->prev_bottom_pic_flag != + h264_slice_hdr_info.bottom_field_flag)), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + ((state->prev_nal_ref_idc == 0 || nal_ref_idc == 0) && + (state->prev_nal_ref_idc != nal_ref_idc)), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + ((h264_seq_hdr_info->sps_info.pic_order_cnt_type == 0) && + ((state->prev_pic_order_cnt_lsb != + h264_slice_hdr_info.pic_order_cnt_lsb) || + (state->prev_delta_pic_order_cnt_bottom != + h264_slice_hdr_info.delta_pic_order_cnt_bottom))), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + ((h264_seq_hdr_info->sps_info.pic_order_cnt_type == 1) && + ((state->prev_delta_pic_order_cnt[0] != + h264_slice_hdr_info.delta_pic_order_cnt[0]) || + (state->prev_delta_pic_order_cnt[1] != + h264_slice_hdr_info.delta_pic_order_cnt[1]))), + ¤t_pic_is_new, 1); + set_if_not_determined_yet + (&determined, + ((state->prev_nal_unit_type == + (int)H264_NALTYPE_IDR_SLICE || + nal_unit_type == (int)H264_NALTYPE_IDR_SLICE) && + (state->prev_nal_unit_type != + (int)nal_unit_type)), + ¤t_pic_is_new, 1); + set_if_not_determined_yet(&determined, + ((state->prev_nal_unit_type == + (int)H264_NALTYPE_IDR_SLICE) && + (state->prev_idr_pic_id != + h264_slice_hdr_info.idr_pic_id)), + ¤t_pic_is_new, 1); + + /* + * Update whatever is not updated already in different places of + * the code or just needs to be updated here + */ + state->prev_frame_num = h264_slice_hdr_info.frame_num; + state->prev_pps_id = h264_slice_hdr_info.pps_id; + state->prev_field_pic_flag = + h264_slice_hdr_info.field_pic_flag; + state->prev_nal_ref_idc = nal_ref_idc; + state->prev_pic_order_cnt_lsb = + h264_slice_hdr_info.pic_order_cnt_lsb; + state->prev_delta_pic_order_cnt_bottom = + h264_slice_hdr_info.delta_pic_order_cnt_bottom; + state->prev_delta_pic_order_cnt[0] = + h264_slice_hdr_info.delta_pic_order_cnt[0]; + state->prev_delta_pic_order_cnt[1] = + h264_slice_hdr_info.delta_pic_order_cnt[1]; + state->prev_nal_unit_type = (int)nal_unit_type; + state->prev_idr_pic_id = h264_slice_hdr_info.idr_pic_id; + } + + /* Detect second field and manage the prev_bottom_pic_flag flag */ + if (h264_slice_hdr_info.field_pic_flag && current_pic_is_new) { + unit_data->parse_state->prev_bottom_pic_flag = + h264_slice_hdr_info.bottom_field_flag; + } + + /* Detect ASO Just met new pic */ + id = h264_slice_hdr_info.colour_plane_id; + if (current_pic_is_new) { + unsigned int i; + + for (i = 0; i < MAX_COMPONENTS; i++) + unit_data->parse_state->prev_first_mb_in_slice[i] = 0; + } else if (unit_data->parse_state->prev_first_mb_in_slice[id] > + h264_slice_hdr_info.first_mb_in_slice) { + /* We just found ASO */ + unit_data->parse_state->discontinuous_mb = 1; + } + unit_data->parse_state->prev_first_mb_in_slice[id] = + h264_slice_hdr_info.first_mb_in_slice; + + /* We may already knew we were DiscontinuousMB */ + if (unit_data->parse_state->discontinuous_mb) + unit_data->out.pict_hdr_info->discontinuous_mbs = + unit_data->parse_state->discontinuous_mb; + + /* + * We want to calculate the scaling lists only once per picture/field, + * not every slice We want to populate the VDEC Picture Header Info + * only once per picture/field, not every slice + */ + if (current_pic_is_new) { + /* Common Sequence Header Info fetched */ + struct vdec_comsequ_hdrinfo *comseq_hdr_info = + &sequ_hdr_info->sequ_hdr_info.com_sequ_hdr_info; + struct bspp_pict_data *type_pict_aux_data; + + unit_data->parse_state->next_pic_is_new = 0; + + /* Generate SGM for this picture */ + if (h264_pps_info->num_slice_groups_minus1 != 0 && + h264_pps_info->slice_group_map_type <= 6) { + bspp_h264_unitparser_compile_sgmdata + (&h264_slice_hdr_info, + h264_seq_hdr_info, + h264_pps_info, + unit_data->out.pict_hdr_info); + } else { + unit_data->out.pict_hdr_info->pict_sgm_data.pic_data = NULL; + unit_data->out.pict_hdr_info->pict_sgm_data.bufmap_id = 0; + unit_data->out.pict_hdr_info->pict_sgm_data.buf_offset = 0; + unit_data->out.pict_hdr_info->pict_sgm_data.id = + BSPP_INVALID; + unit_data->out.pict_hdr_info->pict_sgm_data.size = 0; + } + + unit_data->parse_state->discontinuous_mb = + unit_data->out.pict_hdr_info->discontinuous_mbs; + + /* + * Select the scaling lists based on h264_pps_info and + * h264_seq_hdr_info and pass them to h264fw_pps_info + */ + bspp_h264_select_scaling_list(h264fw_pps_info, + h264_pps_info, + h264_seq_hdr_info); + + /* + * Uses the common sequence/SINGLE-slice info to populate the + * VDEC Picture Header Info + */ + bspp_h264_pict_hdr_populate(nal_unit_type, &h264_slice_hdr_info, + comseq_hdr_info, + unit_data->out.pict_hdr_info); + + /* Store some raw bitstream fields for output. */ + unit_data->out.pict_hdr_info->h264_pict_hdr_info.frame_num = + h264_slice_hdr_info.frame_num; + unit_data->out.pict_hdr_info->h264_pict_hdr_info.nal_ref_idc = + nal_ref_idc; + + /* + * Update the display-related picture header information with + * the related SEI parsed data The display-related SEI is + * used only for the first picture after the SEI + */ + if (!interpicctx->sei_info_attached_to_pic) { + interpicctx->sei_info_attached_to_pic = 1; + if (interpicctx->active_sps_for_sei_parsing != + h264_seq_hdr_info->sps_info.seq_parameter_set_id) { + /* + * We tried to guess the SPS ID that we should use + * to parse the SEI, but we guessed wrong + */ + pr_err("Parsed SEI with wrong SPS, data may be parsed wrong"); + } + unit_data->out.pict_hdr_info->disp_info.repeat_first_fld = + interpicctx->repeat_first_field; + unit_data->out.pict_hdr_info->disp_info.max_frm_repeat = + interpicctx->max_frm_repeat; + /* SEI - Not supported */ + } + + /* + * For Idr slices update the Active + * Sequence ID for SEI parsing, + * error resilient + */ + if (nal_unit_type == H264_NALTYPE_IDR_SLICE) + interpicctx->active_sps_for_sei_parsing = + h264_seq_hdr_info->sps_info.seq_parameter_set_id; + + /* + * Choose the appropriate auxiliary data + * structure to populate. + */ + if (unit_data->parse_state->second_field_flag) + type_pict_aux_data = + &unit_data->out.pict_hdr_info->second_pict_aux_data; + + else + type_pict_aux_data = + &unit_data->out.pict_hdr_info->pict_aux_data; + + /* + * We have no container for the PPS that + * passes down to the kernel, for this + * reason the h264 secure parser needs + * to populate that info into the + * picture header (Second)PictAuxData. + */ + type_pict_aux_data->bufmap_id = pps_info->bufmap_id; + type_pict_aux_data->buf_offset = pps_info->buf_offset; + type_pict_aux_data->pic_data = (void *)h264fw_pps_info; + type_pict_aux_data->id = h264_pps_info->pps_id; + type_pict_aux_data->size = sizeof(struct h264fw_picture_ps); + + pps_info->ref_count++; + + /* This info comes from NAL directly */ + unit_data->out.pict_hdr_info->ref = (nal_ref_idc == 0) ? 0 : 1; + } + if (nal_unit_type == H264_NALTYPE_IDR_SLICE) + unit_data->new_closed_gop = 1; + + /* Return the SPS ID */ + /* + * seq_parameter_set_id is always in range 0-31, + * so we can add offset indicating subsequence header + */ + id_loc = h264_pps_info->seq_parameter_set_id; + unit_data->pict_sequ_hdr_id = + (nal_unit_type == H264_NALTYPE_SLICE_SCALABLE || + nal_unit_type == + H264_NALTYPE_SLICE_IDR_SCALABLE) ? id_loc + 32 : id_loc; + + } else if (nal_unit_type == H264_NALTYPE_SLICE_PARTITION_A || + nal_unit_type == H264_NALTYPE_SLICE_PARTITION_B || + nal_unit_type == H264_NALTYPE_SLICE_PARTITION_C) { + unit_data->slice = 1; + + pr_err("Unsupported Slice NAL type: %d", nal_unit_type); + unit_data->parse_error = BSPP_ERROR_UNSUPPORTED; + } + break; + + case BSPP_UNIT_UNCLASSIFIED: + if (nal_unit_type == H264_NALTYPE_ACCESS_UNIT_DELIMITER) { + unit_data->parse_state->next_pic_is_new = 1; + } else if (nal_unit_type == H264_NALTYPE_SLICE_PREFIX || + nal_unit_type == H264_NALTYPE_SUBSET_SPS) { + /* if mvc disabled do nothing */ + } else { + /* Should not have any other type of unclassified data. */ + pr_err("unclassified data detected!\n"); + } + break; + + case BSPP_UNIT_NON_PICTURE: + if (nal_unit_type == H264_NALTYPE_END_OF_SEQUENCE || + nal_unit_type == H264_NALTYPE_END_OF_STREAM) { + unit_data->parse_state->next_pic_is_new = 1; + } else if (nal_unit_type == H264_NALTYPE_FILLER_DATA || + nal_unit_type == H264_NALTYPE_SEQUENCE_PARAMETER_SET_EXTENSION || + nal_unit_type == H264_NALTYPE_AUXILIARY_SLICE) { + } else if (nal_unit_type == H264_NALTYPE_SLICE_SCALABLE || + nal_unit_type == H264_NALTYPE_SLICE_IDR_SCALABLE) { + /* if mvc disabled do nothing */ + } else { + /* Should not have any other type of non-picture data. */ + VDEC_ASSERT(0); + } + break; + + case BSPP_UNIT_UNSUPPORTED: + pr_err("Unsupported NAL type: %d", nal_unit_type); + unit_data->parse_error = BSPP_ERROR_UNKNOWN_DATAUNIT_DETECTED; + break; + + default: + VDEC_ASSERT(0); + break; + } + + return result; +} + +static int bspp_h264releasedata(void *str_alloc, enum bspp_unit_type data_type, void *data_handle) +{ + int result = 0; + + if (!data_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + switch (data_type) { + case BSPP_UNIT_SEQUENCE: + result = bspp_h264_release_sequ_hdr_info(str_alloc, data_handle); + break; + default: + break; + } + + return result; +} + +static int bspp_h264resetdata(enum bspp_unit_type data_type, void *data_handle) +{ + int result = 0; + + if (!data_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + switch (data_type) { + case BSPP_UNIT_SEQUENCE: + result = bspp_h264_reset_seq_hdr_info(data_handle); + break; + case BSPP_UNIT_PPS: + result = bspp_h264_reset_pps_info(data_handle); + break; + default: + break; + } + + return result; +} + +static void bspp_h264parse_codecconfig(void *swsr_ctx, + unsigned int *unitcount, + unsigned int *unit_arraycount, + unsigned int *delimlength, + unsigned int *size_delimlength) +{ + unsigned long long value = 6; + + /* + * Set the shift-register up to provide next 6 bytes + * without emulation prevention detection. + */ + swsr_consume_delim(swsr_ctx, SWSR_EMPREVENT_NONE, 0, &value); + + /* + * Codec config header must be read for size delimited data (H.264) + * to get to the start of each unit. + * This parsing follows section 5.2.4.1.1 of ISO/IEC 14496-15:2004(E). + */ + /* Configuration version. */ + swsr_read_bits(swsr_ctx, 8); + /* AVC Profile Indication. */ + swsr_read_bits(swsr_ctx, 8); + /* Profile compatibility. */ + swsr_read_bits(swsr_ctx, 8); + /* AVC Level Indication. */ + swsr_read_bits(swsr_ctx, 8); + *delimlength = ((swsr_read_bits(swsr_ctx, 8) & 0x3) + 1) * 8; + *unitcount = swsr_read_bits(swsr_ctx, 8) & 0x1f; + + /* Size delimiter is only 2 bytes for H.264 codec configuration. */ + *size_delimlength = 2 * 8; +} + +static void bspp_h264update_unitcounts(void *swsr_ctx, + unsigned int *unitcount, + unsigned int *unit_arraycount) +{ + if (*unitcount == 0) { + unsigned long long value = 1; + + /* + * Set the shift-register up to provide next 1 byte without + * emulation prevention detection. + */ + swsr_consume_delim(swsr_ctx, SWSR_EMPREVENT_NONE, 0, &value); + + *unitcount = swsr_read_bits(swsr_ctx, 8); + } + + (*unitcount)--; +} + +/* + * Sets the parser configuration + */ +int bspp_h264_set_parser_config(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *pvidstd_features, + struct bspp_swsr_ctx *pswsr_ctx, + struct bspp_parser_callbacks *pparser_callbacks, + struct bspp_inter_pict_data *pinterpict_data) +{ + /* Set h.246 parser callbacks. */ + pparser_callbacks->parse_unit_cb = bspp_h264_unit_parser; + pparser_callbacks->release_data_cb = bspp_h264releasedata; + pparser_callbacks->reset_data_cb = bspp_h264resetdata; + pparser_callbacks->destroy_data_cb = bspp_h264_destroy_data; + pparser_callbacks->parse_codec_config_cb = bspp_h264parse_codecconfig; + pparser_callbacks->update_unit_counts_cb = bspp_h264update_unitcounts; + + /* Set h.246 specific features. */ + pvidstd_features->seq_size = sizeof(struct bspp_h264_seq_hdr_info); + pvidstd_features->uses_pps = 1; + pvidstd_features->pps_size = sizeof(struct bspp_h264_pps_info); + + /* Set h.246 specific shift register config. */ + pswsr_ctx->emulation_prevention = SWSR_EMPREVENT_00000300; + pinterpict_data->h264_ctx.active_sps_for_sei_parsing = BSPP_INVALID; + + if (bstr_format == VDEC_BSTRFORMAT_DEMUX_BYTESTREAM || + bstr_format == VDEC_BSTRFORMAT_ELEMENTARY) { + pswsr_ctx->sr_config.delim_type = SWSR_DELIM_SCP; + pswsr_ctx->sr_config.delim_length = 3 * 8; + pswsr_ctx->sr_config.scp_value = 0x000001; + } else if (bstr_format == VDEC_BSTRFORMAT_DEMUX_SIZEDELIMITED) { + pswsr_ctx->sr_config.delim_type = SWSR_DELIM_SIZE; + /* Set the default size-delimiter number of bits */ + pswsr_ctx->sr_config.delim_length = 4 * 8; + } else { + VDEC_ASSERT(0); + return IMG_ERROR_NOT_SUPPORTED; + } + + return 0; +} + +/* + * This function determines the BSPP unit type based on the + * provided bitstream (H264 specific) unit type + */ +void bspp_h264_determine_unittype(unsigned char bitstream_unittype, + int disable_mvc, + enum bspp_unit_type *bspp_unittype) +{ + unsigned char type = bitstream_unittype & 0x1f; + + switch (type) { + case H264_NALTYPE_SLICE_PREFIX: + *bspp_unittype = disable_mvc ? BSPP_UNIT_UNCLASSIFIED : BSPP_UNIT_PICTURE; + break; + case H264_NALTYPE_SUBSET_SPS: + *bspp_unittype = disable_mvc ? BSPP_UNIT_UNCLASSIFIED : BSPP_UNIT_SEQUENCE; + break; + case H264_NALTYPE_SLICE_SCALABLE: + case H264_NALTYPE_SLICE_IDR_SCALABLE: + *bspp_unittype = disable_mvc ? BSPP_UNIT_NON_PICTURE : BSPP_UNIT_PICTURE; + break; + case H264_NALTYPE_SEQUENCE_PARAMETER_SET: + *bspp_unittype = BSPP_UNIT_SEQUENCE; + break; + case H264_NALTYPE_PICTURE_PARAMETER_SET: + *bspp_unittype = BSPP_UNIT_PPS; + break; + case H264_NALTYPE_SLICE: + case H264_NALTYPE_SLICE_PARTITION_A: + case H264_NALTYPE_SLICE_PARTITION_B: + case H264_NALTYPE_SLICE_PARTITION_C: + case H264_NALTYPE_IDR_SLICE: + *bspp_unittype = BSPP_UNIT_PICTURE; + break; + case H264_NALTYPE_ACCESS_UNIT_DELIMITER: + case H264_NALTYPE_SUPPLEMENTAL_ENHANCEMENT_INFO: + /* + * Each of these NAL units should not change unit type if + * current is picture, since they can occur anywhere, any number + * of times + */ + *bspp_unittype = BSPP_UNIT_UNCLASSIFIED; + break; + case H264_NALTYPE_END_OF_SEQUENCE: + case H264_NALTYPE_END_OF_STREAM: + case H264_NALTYPE_FILLER_DATA: + case H264_NALTYPE_SEQUENCE_PARAMETER_SET_EXTENSION: + case H264_NALTYPE_AUXILIARY_SLICE: + *bspp_unittype = BSPP_UNIT_NON_PICTURE; + break; + default: + *bspp_unittype = BSPP_UNIT_UNSUPPORTED; + break; + } +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.h b/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.h --- a/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/h264_secure_parser.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * h.264 secure data unit parsing API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ +#ifndef __H264SECUREPARSER_H__ +#define __H264SECUREPARSER_H__ + +#include "bspp_int.h" +#include "vdec_defs.h" + +/* + * enum h264_nalunittype + * @Description Contains H264 NAL unit types + */ +enum h264_nalunittype { + H264_NALTYPE_UNSPECIFIED = 0, + H264_NALTYPE_SLICE = 1, + H264_NALTYPE_SLICE_PARTITION_A = 2, + H264_NALTYPE_SLICE_PARTITION_B = 3, + H264_NALTYPE_SLICE_PARTITION_C = 4, + H264_NALTYPE_IDR_SLICE = 5, + H264_NALTYPE_SUPPLEMENTAL_ENHANCEMENT_INFO = 6, + H264_NALTYPE_SEQUENCE_PARAMETER_SET = 7, + H264_NALTYPE_PICTURE_PARAMETER_SET = 8, + H264_NALTYPE_ACCESS_UNIT_DELIMITER = 9, + H264_NALTYPE_END_OF_SEQUENCE = 10, + H264_NALTYPE_END_OF_STREAM = 11, + H264_NALTYPE_FILLER_DATA = 12, + H264_NALTYPE_SEQUENCE_PARAMETER_SET_EXTENSION = 13, + H264_NALTYPE_SLICE_PREFIX = 14, + H264_NALTYPE_SUBSET_SPS = 15, + H264_NALTYPE_AUXILIARY_SLICE = 19, + H264_NALTYPE_SLICE_SCALABLE = 20, + H264_NALTYPE_SLICE_IDR_SCALABLE = 21, + H264_NALTYPE_MAX = 31, + H264_NALTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * struct bspp_h264_sps_info + * @Description H264 SPS parsed information + */ +struct bspp_h264_sps_info { + unsigned int profile_idc; + unsigned int constraint_set_flags; + unsigned int level_idc; + unsigned char seq_parameter_set_id; + unsigned char chroma_format_idc; + int separate_colour_plane_flag; + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned char qpprime_y_zero_transform_bypass_flag; + int seq_scaling_matrix_present_flag; + unsigned char seq_scaling_list_present_flag[12]; + unsigned int log2_max_frame_num_minus4; + unsigned int pic_order_cnt_type; + unsigned int log2_max_pic_order_cnt_lsb_minus4; + int delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + unsigned int num_ref_frames_in_pic_order_cnt_cycle; + unsigned int *offset_for_ref_frame; + unsigned int max_num_ref_frames; + int gaps_in_frame_num_value_allowed_flag; + unsigned int pic_width_in_mbs_minus1; + unsigned int pic_height_in_map_units_minus1; + int frame_mbs_only_flag; + int mb_adaptive_frame_field_flag; + int direct_8x8_inference_flag; + int frame_cropping_flag; + unsigned int frame_crop_left_offset; + unsigned int frame_crop_right_offset; + unsigned int frame_crop_top_offset; + unsigned int frame_crop_bottom_offset; + int vui_parameters_present_flag; + /* mvc_vui_parameters_present_flag; UNUSED */ + int bmvcvuiparameterpresentflag; + /* + * scaling lists are derived from both SPS and PPS information + * but will change whenever the PPS changes + * The derived set of tables are associated here with the PPS + * NB: These are in H.264 order + */ + /* derived from SPS and PPS - 8 bit each */ + unsigned char *scllst4x4seq; + /* derived from SPS and PPS - 8 bit each */ + unsigned char *scllst8x8seq; + /* This is not direct parsed data, though it is extracted */ + unsigned char usedefaultscalingmatrixflag_seq[12]; +}; + +struct bspp_h264_hrdparam_info { + unsigned char cpb_cnt_minus1; + unsigned char bit_rate_scale; + unsigned char cpb_size_scale; + unsigned int *bit_rate_value_minus1; + unsigned int *cpb_size_value_minus1; + unsigned char *cbr_flag; + unsigned char initial_cpb_removal_delay_length_minus1; + unsigned char cpb_removal_delay_length_minus1; + unsigned char dpb_output_delay_length_minus1; + unsigned char time_offset_length; +}; + +struct bspp_h264_vui_info { + unsigned char aspect_ratio_info_present_flag; + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + unsigned char overscan_info_present_flag; + unsigned char overscan_appropriate_flag; + unsigned char video_signal_type_present_flag; + unsigned int video_format; + unsigned char video_full_range_flag; + unsigned char colour_description_present_flag; + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + unsigned char chroma_location_info_present_flag; + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + unsigned char timing_info_present_flag; + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned char fixed_frame_rate_flag; + unsigned char nal_hrd_parameters_present_flag; + struct bspp_h264_hrdparam_info nal_hrd_parameters; + unsigned char vcl_hrd_parameters_present_flag; + struct bspp_h264_hrdparam_info vcl_hrd_parameters; + unsigned char low_delay_hrd_flag; + unsigned char pic_struct_present_flag; + unsigned char bitstream_restriction_flag; + unsigned char motion_vectors_over_pic_boundaries_flag; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_mb_denom; + unsigned int log2_max_mv_length_vertical; + unsigned int log2_max_mv_length_horizontal; + unsigned int num_reorder_frames; + unsigned int max_dec_frame_buffering; +}; + +/* + * struct bspp_h264_seq_hdr_info + * @Description Contains everything parsed from the Sequence Header. + */ +struct bspp_h264_seq_hdr_info { + /* Video sequence header information */ + struct bspp_h264_sps_info sps_info; + /* VUI sequence header information. */ + struct bspp_h264_vui_info vui_info; +}; + +/** + * struct bspp_h264_ppssgm_info - This structure contains H264 PPS parse data. + * @slice_group_id: slice_group_id + * @slicegroupidnum: slicegroupidnum + */ +struct bspp_h264_ppssgm_info { + unsigned char *slice_group_id; + unsigned short slicegroupidnum; +}; + +/* + * struct bspp_h264_pps_info + * @Description This structure contains H264 PPS parse data. + */ +struct bspp_h264_pps_info { + /* pic_parameter_set_id: defines the PPS ID of the current PPS */ + int pps_id; + /* seq_parameter_set_id: defines the SPS that current PPS points to */ + int seq_parameter_set_id; + int entropy_coding_mode_flag; + int pic_order_present_flag; + unsigned char num_slice_groups_minus1; + unsigned char slice_group_map_type; + unsigned short run_length_minus1[8]; + unsigned short top_left[8]; + unsigned short bottom_right[8]; + int slice_group_change_direction_flag; + unsigned short slice_group_change_rate_minus1; + unsigned short pic_size_in_map_unit; + struct bspp_h264_ppssgm_info h264_ppssgm_info; + unsigned char num_ref_idx_lx_active_minus1[H264FW_MAX_REFPIC_LISTS]; + int weighted_pred_flag; + unsigned char weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + int deblocking_filter_control_present_flag; + int constrained_intra_pred_flag; + int redundant_pic_cnt_present_flag; + int transform_8x8_mode_flag; + int pic_scaling_matrix_present_flag; + unsigned char pic_scaling_list_present_flag[12]; + int second_chroma_qp_index_offset; + + /* + * scaling lists are derived from both SPS and PPS information + * but will change whenever the PPS changes + * The derived set of tables are associated here with the PPS + * NB: These are in H.264 order + */ + /* derived from SPS and PPS - 8 bit each */ + unsigned char *scllst4x4pic; + /* derived from SPS and PPS - 8 bit each */ + unsigned char *scllst8x8pic; + /* This is not direct parsed data, though it is extracted */ + unsigned char usedefaultscalingmatrixflag_pic[12]; +}; + +/* + * enum bspp_h264_slice_type + * @Description contains H264 slice types + */ +enum bspp_h264_slice_type { + P_SLICE = 0, + B_SLICE, + I_SLICE, + SP_SLICE, + SI_SLICE, + SLICE_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * struct bspp_h264_slice_hdr_info + * @Description This structure contains H264 slice header information + */ +struct bspp_h264_slice_hdr_info { + unsigned short first_mb_in_slice; + enum bspp_h264_slice_type slice_type; + + /* data to ID new picture */ + unsigned int pps_id; + unsigned int frame_num; + unsigned char colour_plane_id; + unsigned char field_pic_flag; + unsigned char bottom_field_flag; + unsigned int idr_pic_id; + unsigned int pic_order_cnt_lsb; + int delta_pic_order_cnt_bottom; + int delta_pic_order_cnt[2]; + unsigned int redundant_pic_cnt; + + /* Things we need to read out when doing In Secure */ + unsigned char num_ref_idx_active_override_flag; + unsigned char num_ref_idx_lx_active_minus1[2]; + unsigned short slice_group_change_cycle; +}; + +/* + * @Function bspp_h264_set_parser_config + * @Description Sets the parser configuration + */ +int bspp_h264_set_parser_config(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *pvidstd_features, + struct bspp_swsr_ctx *pswsr_ctx, + struct bspp_parser_callbacks *pparser_callbacks, + struct bspp_inter_pict_data *pinterpict_data); + +/* + * @Function bspp_h264_determine_unittype + * @Description This function determines the BSPP unit type based on the + * provided bitstream (H264 specific) unit type + */ +void bspp_h264_determine_unittype(unsigned char bitstream_unittype, + int disable_mvc, + enum bspp_unit_type *pbsppunittype); + +#endif /*__H264SECUREPARSER_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/h264_vlc.h b/drivers/media/platform/vxe-vxd/decoder/h264_vlc.h --- a/drivers/media/platform/vxe-vxd/decoder/h264_vlc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/h264_vlc.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,604 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * h264 vlc table definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + */ + +#ifndef __H264_VLC_H__ +#define __H264_VLC_H__ + +#include + +static unsigned short h264_vlc_table_data[] = { +/* NumCoeffTrailingOnes_Table9-5_nC_0-1.out */ + 4, 0, 0, + 4, 1, 5, + 4, 2, 10, + 2, 1, 4, + 2, 1, 6, + 0, 1, 8, + 0, 2, 11, + 4, 0, 15, + 4, 1, 4, + 4, 1, 9, + 4, 0, 19, + 4, 1, 14, + 4, 1, 23, + 4, 1, 27, + 4, 1, 18, + 4, 1, 13, + 4, 1, 8, + 2, 5, 8, + 0, 1, 50, + 0, 0, 53, + 0, 0, 54, + 4, 2, 31, + 4, 2, 22, + 4, 2, 17, + 4, 2, 12, + 0, 2, 7, + 0, 2, 14, + 0, 2, 21, + 0, 2, 28, + 0, 1, 35, + 4, 5, 53, + 3, 5, 0, + 4, 2, 32, + 4, 2, 38, + 4, 2, 33, + 4, 2, 28, + 4, 2, 43, + 4, 2, 34, + 4, 2, 29, + 4, 2, 24, + 4, 2, 51, + 4, 2, 46, + 4, 2, 41, + 4, 2, 40, + 4, 2, 47, + 4, 2, 42, + 4, 2, 37, + 4, 2, 36, + 4, 2, 59, + 4, 2, 54, + 4, 2, 49, + 4, 2, 48, + 4, 2, 55, + 4, 2, 50, + 4, 2, 45, + 4, 2, 44, + 4, 2, 67, + 4, 2, 62, + 4, 2, 61, + 4, 2, 56, + 4, 2, 63, + 4, 2, 58, + 4, 2, 57, + 4, 2, 52, + 4, 1, 64, + 4, 1, 66, + 4, 1, 65, + 4, 1, 60, + 4, 1, 39, + 4, 1, 30, + 4, 1, 25, + 4, 1, 20, + 4, 0, 35, + 4, 0, 26, + 4, 0, 21, + 4, 0, 16, +/* NumCoeffTrailingOnes_Table9-5_nC_2-3.out */ + 0, 2, 16, + 0, 1, 73, + 0, 1, 76, + 0, 0, 79, + 4, 3, 19, + 4, 3, 15, + 4, 2, 10, + 4, 2, 10, + 4, 1, 5, + 4, 1, 5, + 4, 1, 5, + 4, 1, 5, + 4, 1, 0, + 4, 1, 0, + 4, 1, 0, + 4, 1, 0, + 2, 5, 8, + 0, 1, 49, + 0, 0, 52, + 0, 0, 53, + 4, 2, 35, + 4, 2, 22, + 4, 2, 21, + 4, 2, 12, + 0, 2, 7, + 0, 2, 14, + 0, 2, 21, + 1, 1, 28, + 0, 1, 34, + 4, 5, 63, + 3, 5, 0, + 4, 2, 47, + 4, 2, 38, + 4, 2, 37, + 4, 2, 32, + 4, 2, 43, + 4, 2, 34, + 4, 2, 33, + 4, 2, 28, + 4, 2, 44, + 4, 2, 46, + 4, 2, 45, + 4, 2, 40, + 4, 2, 51, + 4, 2, 42, + 4, 2, 41, + 4, 2, 36, + 4, 2, 59, + 4, 2, 54, + 4, 2, 53, + 4, 2, 52, + 4, 2, 55, + 4, 2, 50, + 4, 2, 49, + 4, 2, 48, + 0, 1, 3, + 4, 1, 58, + 4, 1, 56, + 4, 1, 61, + 4, 1, 60, + 4, 1, 62, + 4, 1, 57, + 4, 1, 67, + 4, 1, 66, + 4, 1, 65, + 4, 1, 64, + 4, 1, 39, + 4, 1, 30, + 4, 1, 29, + 4, 1, 24, + 4, 0, 20, + 4, 0, 26, + 4, 0, 25, + 4, 0, 16, + 4, 1, 31, + 4, 1, 18, + 4, 1, 17, + 4, 1, 8, + 4, 1, 27, + 4, 1, 14, + 4, 1, 13, + 4, 1, 4, + 4, 0, 23, + 4, 0, 9, +/* NumCoeffTrailingOnes_Table9-5_nC_4-7.out */ + 2, 1, 16, + 0, 2, 50, + 0, 1, 57, + 0, 1, 60, + 6, 0, 10, + 6, 0, 8, + 0, 0, 61, + 0, 0, 62, + 4, 3, 31, + 4, 3, 27, + 4, 3, 23, + 4, 3, 19, + 4, 3, 15, + 4, 3, 10, + 4, 3, 5, + 4, 3, 0, + 0, 2, 3, + 0, 2, 10, + 0, 3, 17, + 4, 2, 51, + 4, 2, 46, + 4, 2, 41, + 4, 2, 36, + 4, 2, 47, + 4, 2, 42, + 4, 2, 37, + 4, 2, 32, + 4, 2, 48, + 4, 2, 54, + 4, 2, 49, + 4, 2, 44, + 4, 2, 55, + 4, 2, 50, + 4, 2, 45, + 4, 2, 40, + 3, 3, 0, + 4, 3, 64, + 4, 3, 67, + 4, 3, 66, + 4, 3, 65, + 4, 3, 60, + 4, 3, 63, + 4, 3, 62, + 4, 3, 61, + 4, 3, 56, + 4, 3, 59, + 4, 3, 58, + 4, 3, 57, + 4, 3, 52, + 4, 2, 53, + 4, 2, 53, + 4, 2, 28, + 4, 2, 24, + 4, 2, 38, + 4, 2, 20, + 4, 2, 43, + 4, 2, 34, + 4, 2, 33, + 4, 2, 16, + 4, 1, 12, + 4, 1, 30, + 4, 1, 29, + 4, 1, 8, + 4, 1, 39, + 4, 1, 26, + 4, 1, 25, + 4, 1, 4, + 4, 0, 13, + 4, 0, 35, + 4, 0, 14, + 4, 0, 9, +/* NumCoeffTrailingOnesFixedLen.out */ + 2, 1, 8, + 5, 2, 6, + 5, 2, 10, + 5, 2, 14, + 5, 2, 18, + 5, 2, 22, + 5, 2, 26, + 5, 2, 30, + 5, 1, 4, + 0, 0, 2, + 5, 0, 2, + 3, 0, 0, + 4, 0, 0, +/* NumCoeffTrailingOnesChromaDC_YUV420.out */ + 4, 0, 5, + 4, 1, 0, + 4, 2, 10, + 0, 2, 1, + 1, 1, 8, + 0, 0, 10, + 4, 2, 16, + 4, 2, 12, + 4, 2, 8, + 4, 2, 15, + 4, 2, 9, + 4, 2, 4, + 4, 0, 19, + 4, 1, 18, + 4, 1, 17, + 4, 0, 14, + 4, 0, 13, +/* NumCoeffTrailingOnesChromaDC_YUV422.out */ + 4, 0, 0, + 4, 1, 5, + 4, 2, 10, + 0, 2, 4, + 4, 4, 15, + 4, 5, 19, + 2, 3, 9, + 4, 2, 27, + 4, 2, 23, + 4, 2, 18, + 4, 2, 14, + 4, 2, 13, + 4, 2, 9, + 4, 2, 8, + 4, 2, 4, + 0, 1, 5, + 0, 1, 8, + 0, 1, 11, + 0, 1, 14, + 1, 2, 17, + 4, 1, 22, + 4, 1, 17, + 4, 1, 16, + 4, 1, 12, + 4, 1, 31, + 4, 1, 26, + 4, 1, 21, + 4, 1, 20, + 4, 1, 35, + 4, 1, 30, + 4, 1, 25, + 4, 1, 24, + 4, 1, 34, + 4, 1, 33, + 4, 1, 29, + 4, 1, 28, + 3, 2, 0, + 3, 2, 0, + 3, 2, 0, + 4, 2, 32, +/* TotalZeros_00.out */ + 4, 0, 0, + 0, 0, 6, + 0, 0, 7, + 0, 0, 8, + 0, 0, 9, + 0, 0, 10, + 0, 2, 11, + 4, 0, 2, + 4, 0, 1, + 4, 0, 4, + 4, 0, 3, + 4, 0, 6, + 4, 0, 5, + 4, 0, 8, + 4, 0, 7, + 4, 0, 10, + 4, 0, 9, + 3, 2, 0, + 4, 2, 15, + 4, 2, 14, + 4, 2, 13, + 4, 1, 12, + 4, 1, 12, + 4, 1, 11, + 4, 1, 11, +/* TotalZeros_01.out */ + 1, 1, 8, + 0, 0, 14, + 0, 0, 15, + 4, 2, 4, + 4, 2, 3, + 4, 2, 2, + 4, 2, 1, + 4, 2, 0, + 0, 1, 3, + 4, 1, 10, + 4, 1, 9, + 4, 1, 14, + 4, 1, 13, + 4, 1, 12, + 4, 1, 11, + 4, 0, 8, + 4, 0, 7, + 4, 0, 6, + 4, 0, 5, +/* TotalZeros_02.out */ + 0, 1, 8, + 0, 0, 13, + 0, 0, 14, + 4, 2, 7, + 4, 2, 6, + 4, 2, 3, + 4, 2, 2, + 4, 2, 1, + 0, 0, 4, + 4, 1, 12, + 4, 1, 10, + 4, 1, 9, + 4, 0, 13, + 4, 0, 11, + 4, 0, 8, + 4, 0, 5, + 4, 0, 4, + 4, 0, 0, +/* TotalZeros_03.out */ + 0, 1, 8, + 0, 0, 11, + 0, 0, 12, + 4, 2, 8, + 4, 2, 6, + 4, 2, 5, + 4, 2, 4, + 4, 2, 1, + 4, 1, 12, + 4, 1, 11, + 4, 1, 10, + 4, 1, 0, + 4, 0, 9, + 4, 0, 7, + 4, 0, 3, + 4, 0, 2, +/* TotalZeros_04.out */ + 2, 1, 8, + 0, 0, 10, + 0, 0, 11, + 4, 2, 7, + 4, 2, 6, + 4, 2, 5, + 4, 2, 4, + 4, 2, 3, + 4, 0, 10, + 4, 1, 9, + 4, 1, 11, + 4, 0, 8, + 4, 0, 2, + 4, 0, 1, + 4, 0, 0, +/* TotalZeros_05.out */ + 2, 2, 8, + 4, 2, 9, + 4, 2, 7, + 4, 2, 6, + 4, 2, 5, + 4, 2, 4, + 4, 2, 3, + 4, 2, 2, + 4, 0, 8, + 4, 1, 1, + 4, 2, 0, + 4, 2, 10, +/* TotalZeros_06.out */ + 2, 2, 8, + 4, 2, 8, + 4, 2, 6, + 4, 2, 4, + 4, 2, 3, + 4, 2, 2, + 4, 1, 5, + 4, 1, 5, + 4, 0, 7, + 4, 1, 1, + 4, 2, 0, + 4, 2, 9, +/* TotalZeros_07.out */ + 2, 3, 4, + 0, 0, 8, + 4, 1, 5, + 4, 1, 4, + 4, 0, 7, + 4, 1, 1, + 4, 2, 2, + 4, 3, 0, + 4, 3, 8, + 4, 0, 6, + 4, 0, 3, +/* TotalZeros_08.out */ + 2, 3, 4, + 4, 1, 6, + 4, 1, 4, + 4, 1, 3, + 4, 0, 5, + 4, 1, 2, + 4, 2, 7, + 4, 3, 0, + 4, 3, 1, +/* TotalZeros_09.out */ + 2, 2, 4, + 4, 1, 5, + 4, 1, 4, + 4, 1, 3, + 4, 0, 2, + 4, 1, 6, + 4, 2, 0, + 4, 2, 1, +/* TotalZeros_10.out */ + 4, 0, 4, + 0, 0, 3, + 4, 2, 2, + 5, 0, 0, + 4, 0, 3, + 4, 0, 5, +/* TotalZeros_11.out */ + 4, 0, 3, + 4, 1, 2, + 4, 2, 4, + 5, 0, 0, +/* TotalZeros_12.out */ + 4, 0, 2, + 4, 1, 3, + 5, 0, 0, +/* TotalZeros_13.out */ + 5, 0, 0, + 4, 0, 2, +/* TotalZeros_14.out */ + 4, 0, 0, + 4, 0, 1, +/* TotalZerosChromaDC_YUV420_00.out */ + 4, 0, 0, + 4, 1, 1, + 4, 2, 2, + 4, 2, 3, +/* TotalZerosChromaDC_YUV420_01.out */ + 4, 0, 0, + 4, 1, 1, + 4, 1, 2, +/* TotalZerosChromaDC_YUV420_02.out */ + 4, 0, 1, + 4, 0, 0, +/* Run_00.out */ + 4, 0, 1, + 4, 0, 0, +/* Run_01.out */ + 4, 0, 0, + 4, 1, 1, + 4, 1, 2, +/* Run_02.out */ + 4, 1, 3, + 4, 1, 2, + 4, 1, 1, + 4, 1, 0, +/* Run_03.out */ + 0, 0, 4, + 4, 1, 2, + 4, 1, 1, + 4, 1, 0, + 4, 0, 4, + 4, 0, 3, +/* Run_04.out */ + 0, 1, 3, + 4, 1, 1, + 4, 1, 0, + 4, 1, 5, + 4, 1, 4, + 4, 1, 3, + 4, 1, 2, +/* Run_05.out */ + 4, 2, 1, + 4, 2, 2, + 4, 2, 4, + 4, 2, 3, + 4, 2, 6, + 4, 2, 5, + 4, 1, 0, + 4, 1, 0, +/* Run_06.out */ + 2, 5, 8, + 4, 2, 6, + 4, 2, 5, + 4, 2, 4, + 4, 2, 3, + 4, 2, 2, + 4, 2, 1, + 4, 2, 0, + 4, 0, 7, + 4, 1, 8, + 4, 2, 9, + 4, 3, 10, + 4, 4, 11, + 4, 5, 12, + 2, 1, 1, + 4, 0, 13, + 4, 1, 14, + 3, 1, 0, +/* TotalZerosChromaDC_YUV422_00.out */ + 4, 0, 0, + 6, 0, 0, + 6, 0, 1, + 4, 3, 5, + 4, 4, 6, + 4, 4, 7, +/* TotalZerosChromaDC_YUV422_01.out */ + 6, 1, 1, + 4, 1, 1, + 4, 2, 2, + 4, 2, 0, +/* TotalZerosChromaDC_YUV422_02.out */ + 5, 0, 0, + 4, 1, 2, + 4, 1, 3, + 5, 0, 2, +/* TotalZerosChromaDC_YUV422_03.out */ + 6, 0, 0, + 4, 1, 3, + 4, 2, 0, + 4, 2, 4, +/* TotalZerosChromaDC_YUV422_04.out */ + 5, 0, 0, + 5, 0, 1, +/* TotalZerosChromaDC_YUV422_05.out */ + 5, 0, 0, + 4, 0, 2, +/* TotalZerosChromaDC_YUV422_06.out */ + 4, 0, 0, + 4, 0, 1 +}; + +static const unsigned short h264_vlc_table_size = 544; + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/hevcfw_data.h b/drivers/media/platform/vxe-vxd/decoder/hevcfw_data.h --- a/drivers/media/platform/vxe-vxd/decoder/hevcfw_data.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/hevcfw_data.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,472 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures for the hevc parser firmware module. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +/* Include shared header version here to replace the standard version. */ +#include "hevcfw_data_shared.h" + +#ifndef _HEVCFW_DATA_H_ +#define _HEVCFW_DATA_H_ + +#include "vdecfw_shared.h" + +#define HEVC_MAX_SPS_COUNT 16 +#define HEVC_MAX_PPS_COUNT 64 + +#define HEVCFW_MAX_NUM_PROFILE_IDC 32 + +#define HEVCFW_MAX_NUM_REF_PICS 16 +#define HEVCFW_MAX_NUM_ST_REF_PIC_SETS 65 +#define HEVCFW_MAX_NUM_LT_REF_PICS 32 +#define HEVCFW_MAX_NUM_SUBLAYERS 7 +#define HEVCFW_SCALING_LISTS_BUFSIZE 256 +#define HEVCFW_MAX_TILE_COLS 20 +#define HEVCFW_MAX_TILE_ROWS 22 + +#define HEVCFW_MAX_CHROMA_QP 6 + +#define HEVCFW_MAX_DPB_SIZE HEVCFW_MAX_NUM_REF_PICS +#define HEVCFW_REF_PIC_LIST0 0 +#define HEVCFW_REF_PIC_LIST1 1 +#define HEVCFW_NUM_REF_PIC_LISTS 2 +#define HEVCFW_NUM_DPB_DIFF_REGS 4 + +/* non-critical errors */ +#define HEVC_ERR_INVALID_VALUE (20) +#define HEVC_ERR_CORRECTION_VALIDVALUE (21) + +#define HEVC_IS_ERR_CRITICAL(err) \ + ((err) > HEVC_ERR_CORRECTION_VALIDVALUE ? 1 : 0) + +/* critical errors */ +#define HEVC_ERR_INV_VIDEO_DIMENSION (22) +#define HEVC_ERR_NO_SEQUENCE_HDR (23) +#define HEVC_ERR_SPS_EXT_UNSUPP (24 | VDECFW_UNSUPPORTED_CODE_BASE) +#define HEVC_ERR_PPS_EXT_UNSUPP (25 | VDECFW_UNSUPPORTED_CODE_BASE) + +#define HEVC_ERR_FAILED_TO_STORE_VPS (100) +#define HEVC_ERR_FAILED_TO_STORE_SPS (101) +#define HEVC_ERR_FAILED_TO_STORE_PPS (102) + +#define HEVC_ERR_FAILED_TO_FETCH_VPS (103) +#define HEVC_ERR_FAILED_TO_FETCH_SPS (104) +#define HEVC_ERR_FAILED_TO_FETCH_PPS (105) +/* HEVC Scaling Lists (all values are maximum possible ones) */ +#define HEVCFW_SCALING_LIST_NUM_SIZES 4 +#define HEVCFW_SCALING_LIST_NUM_MATRICES 6 +#define HEVCFW_SCALING_LIST_MATRIX_SIZE 64 + +struct hevcfw_scaling_listdata { + unsigned char dc_coeffs + [HEVCFW_SCALING_LIST_NUM_SIZES - 2] + [HEVCFW_SCALING_LIST_NUM_MATRICES]; + unsigned char lists + [HEVCFW_SCALING_LIST_NUM_SIZES] + [HEVCFW_SCALING_LIST_NUM_MATRICES] + [HEVCFW_SCALING_LIST_MATRIX_SIZE]; +}; + +/* HEVC Video Profile_Tier_Level */ +struct hevcfw_profile_tier_level { + unsigned char general_profile_space; + unsigned char general_tier_flag; + unsigned char general_profile_idc; + unsigned char general_profile_compatibility_flag[HEVCFW_MAX_NUM_PROFILE_IDC]; + unsigned char general_progressive_source_flag; + unsigned char general_interlaced_source_flag; + unsigned char general_non_packed_constraint_flag; + unsigned char general_frame_only_constraint_flag; + unsigned char general_max_12bit_constraint_flag; + unsigned char general_max_10bit_constraint_flag; + unsigned char general_max_8bit_constraint_flag; + unsigned char general_max_422chroma_constraint_flag; + unsigned char general_max_420chroma_constraint_flag; + unsigned char general_max_monochrome_constraint_flag; + unsigned char general_intra_constraint_flag; + unsigned char general_one_picture_only_constraint_flag; + unsigned char general_lower_bit_rate_constraint_flag; + unsigned char general_level_idc; + unsigned char sub_layer_profile_present_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_level_present_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_profile_space[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_tier_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_profile_idc[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_profile_compatibility_flag[HEVCFW_MAX_NUM_SUBLAYERS - + 1][HEVCFW_MAX_NUM_PROFILE_IDC]; + unsigned char sub_layer_progressive_source_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_interlaced_source_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_non_packed_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_frame_only_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_12bit_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_10bit_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_8bit_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_422chroma_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_420chroma_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_monochrome_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_intra_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_one_picture_only_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_lower_bit_rate_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_level_idc[HEVCFW_MAX_NUM_SUBLAYERS - 1]; +}; + +struct hevcfw_video_ps { + int is_different; + int is_sent; + int is_available; + unsigned char vps_video_parameter_set_id; + unsigned char vps_reserved_three_2bits; + unsigned char vps_max_layers_minus1; + unsigned char vps_max_sub_layers_minus1; + unsigned char vps_temporal_id_nesting_flag; + unsigned short vps_reserved_0xffff_16bits; + struct hevcfw_profile_tier_level profile_tier_level; +}; + +/* HEVC Video Usability Information */ +struct hevcfw_vui_params { + unsigned char aspect_ratio_info_present_flag; + unsigned char aspect_ratio_idc; + unsigned short sar_width; + unsigned short sar_height; + unsigned char overscan_info_present_flag; + unsigned char overscan_appropriate_flag; + unsigned char video_signal_type_present_flag; + unsigned char video_format; + unsigned char video_full_range_flag; + unsigned char colour_description_present_flag; + unsigned char colour_primaries; + unsigned char transfer_characteristics; + unsigned char matrix_coeffs; + unsigned char chroma_loc_info_present_flag; + unsigned char chroma_sample_loc_type_top_field; + unsigned char chroma_sample_loc_type_bottom_field; + unsigned char neutral_chroma_indication_flag; + unsigned char field_seq_flag; + unsigned char frame_field_info_present_flag; + unsigned char default_display_window_flag; + unsigned short def_disp_win_left_offset; + unsigned short def_disp_win_right_offset; + unsigned short def_disp_win_top_offset; + unsigned short def_disp_win_bottom_offset; + unsigned char vui_timing_info_present_flag; + unsigned int vui_num_units_in_tick; + unsigned int vui_time_scale; +}; + +/* HEVC Short Term Reference Picture Set */ +struct hevcfw_short_term_ref_picset { + unsigned char num_negative_pics; + unsigned char num_positive_pics; + short delta_poc_s0[HEVCFW_MAX_NUM_REF_PICS]; + short delta_poc_s1[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char used_bycurr_pic_s0[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char used_bycurr_pic_s1[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char num_delta_pocs; +}; + +/* + * This describes the SPS header data required by the HEVC firmware that should + * be supplied by the Host. + */ +struct hevcfw_sequence_ps { + /* syntax elements from SPS */ + unsigned short pic_width_in_luma_samples; + unsigned short pic_height_in_luma_samples; + unsigned char num_short_term_ref_pic_sets; + unsigned char num_long_term_ref_pics_sps; + unsigned short lt_ref_pic_poc_lsb_sps[HEVCFW_MAX_NUM_LT_REF_PICS]; + unsigned char used_by_curr_pic_lt_sps_flag[HEVCFW_MAX_NUM_LT_REF_PICS]; + struct hevcfw_short_term_ref_picset st_rps_list[HEVCFW_MAX_NUM_ST_REF_PIC_SETS]; + unsigned char sps_max_sub_layers_minus1; + unsigned char sps_max_dec_pic_buffering_minus1[HEVCFW_MAX_NUM_SUBLAYERS]; + unsigned char sps_max_num_reorder_pics[HEVCFW_MAX_NUM_SUBLAYERS]; + unsigned int sps_max_latency_increase_plus1[HEVCFW_MAX_NUM_SUBLAYERS]; + unsigned char max_transform_hierarchy_depth_inter; + unsigned char max_transform_hierarchy_depth_intra; + unsigned char log2_diff_max_min_transform_block_size; + unsigned char log2_min_transform_block_size_minus2; + unsigned char log2_diff_max_min_luma_coding_block_size; + unsigned char log2_min_luma_coding_block_size_minus3; + unsigned char chroma_format_idc; + unsigned char separate_colour_plane_flag; + unsigned char num_extra_slice_header_bits; + unsigned char log2_max_pic_order_cnt_lsb_minus4; + unsigned char long_term_ref_pics_present_flag; + unsigned char sample_adaptive_offset_enabled_flag; + unsigned char sps_temporal_mvp_enabled_flag; + unsigned char bit_depth_luma_minus8; + unsigned char bit_depth_chroma_minus8; + unsigned char pcm_sample_bit_depth_luma_minus1; + unsigned char pcm_sample_bit_depth_chroma_minus1; + unsigned char log2_min_pcm_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_pcm_luma_coding_block_size; + unsigned char pcm_loop_filter_disabled_flag; + unsigned char amp_enabled_flag; + unsigned char pcm_enabled_flag; + unsigned char strong_intra_smoothing_enabled_flag; + unsigned char scaling_list_enabled_flag; + unsigned char transform_skip_rotation_enabled_flag; + unsigned char transform_skip_context_enabled_flag; + unsigned char implicit_rdpcm_enabled_flag; + unsigned char explicit_rdpcm_enabled_flag; + unsigned char extended_precision_processing_flag; + unsigned char intra_smoothing_disabled_flag; + unsigned char high_precision_offsets_enabled_flag; + unsigned char persistent_rice_adaptation_enabled_flag; + unsigned char cabac_bypass_alignment_enabled_flag; + /* derived elements */ + unsigned int pic_size_in_ctbs_y; + unsigned short pic_height_in_ctbs_y; + unsigned short pic_width_in_ctbs_y; + unsigned char ctb_size_y; + unsigned char ctb_log2size_y; + int max_pic_order_cnt_lsb; + unsigned int sps_max_latency_pictures[HEVCFW_MAX_NUM_SUBLAYERS]; + unsigned char pps_seq_parameter_set_id; + unsigned char sps_video_parameter_set_id; + unsigned char sps_temporal_id_nesting_flag; + unsigned char sps_seq_parameter_set_id; + /* local */ + unsigned char conformance_window_flag; + unsigned short conf_win_left_offset; + unsigned short conf_win_right_offset; + unsigned short conf_win_top_offset; + unsigned short conf_win_bottom_offset; + unsigned char sps_sub_layer_ordering_info_present_flag; + unsigned char sps_scaling_list_data_present_flag; + unsigned char vui_parameters_present_flag; + unsigned char sps_extension_present_flag; + struct hevcfw_vui_params vui_params; + /* derived elements */ + unsigned char sub_width_c; + unsigned char sub_height_c; + struct hevcfw_profile_tier_level profile_tier_level; + struct hevcfw_scaling_listdata scaling_listdata; +}; + +/* + * This describes the HEVC parser component "Header data", shown in the + * Firmware Memory Layout diagram. This data is required by the HEVC firmware + * and should be supplied by the Host. + */ +struct hevcfw_headerdata { + /* Decode buffers and output control for the current picture */ + /* Primary decode buffer base addresses */ + struct vdecfw_image_buffer primary; + /* buffer base addresses for alternate output */ + struct vdecfw_image_buffer alternate; + /* address of buffer for temporal mv params */ + unsigned int temporal_outaddr; +}; + +/* + * This describes the PPS header data required by the HEVC firmware that should + * be supplied by the Host. + */ +struct hevcfw_picture_ps { + /* syntax elements from the PPS */ + unsigned char pps_pic_parameter_set_id; + unsigned char num_tile_columns_minus1; + unsigned char num_tile_rows_minus1; + unsigned char diff_cu_qp_delta_depth; + unsigned char init_qp_minus26; + unsigned char pps_beta_offset_div2; + unsigned char pps_tc_offset_div2; + unsigned char pps_cb_qp_offset; + unsigned char pps_cr_qp_offset; + unsigned char log2_parallel_merge_level_minus2; + unsigned char dependent_slice_segments_enabled_flag; + unsigned char output_flag_present_flag; + unsigned char num_extra_slice_header_bits; + unsigned char lists_modification_present_flag; + unsigned char cabac_init_present_flag; + unsigned char weighted_pred_flag; + unsigned char weighted_bipred_flag; + unsigned char pps_slice_chroma_qp_offsets_present_flag; + unsigned char deblocking_filter_override_enabled_flag; + unsigned char tiles_enabled_flag; + unsigned char entropy_coding_sync_enabled_flag; + unsigned char slice_segment_header_extension_present_flag; + unsigned char transquant_bypass_enabled_flag; + unsigned char cu_qp_delta_enabled_flag; + unsigned char transform_skip_enabled_flag; + unsigned char sign_data_hiding_enabled_flag; + unsigned char num_ref_idx_l0_default_active_minus1; + unsigned char num_ref_idx_l1_default_active_minus1; + unsigned char constrained_intra_pred_flag; + unsigned char pps_deblocking_filter_disabled_flag; + unsigned char pps_loop_filter_across_slices_enabled_flag; + unsigned char loop_filter_across_tiles_enabled_flag; + /* rewritten from SPS, maybe at some point we could get rid of this */ + unsigned char scaling_list_enabled_flag; + unsigned char log2_max_transform_skip_block_size_minus2; + unsigned char cross_component_prediction_enabled_flag; + unsigned char chroma_qp_offset_list_enabled_flag; + unsigned char diff_cu_chroma_qp_offset_depth; + /* + * PVDEC derived elements. HEVCFW_SCALING_LISTS_BUFSIZE is + * multiplied by 2 to ensure that there will be space for address of + * each element. These addresses are completed in lower layer. + */ + unsigned int scaling_lists[HEVCFW_SCALING_LISTS_BUFSIZE * 2]; + /* derived elements */ + unsigned short col_bd[HEVCFW_MAX_TILE_COLS + 1]; + unsigned short row_bd[HEVCFW_MAX_TILE_ROWS + 1]; + + unsigned char chroma_qp_offset_list_len_minus1; + unsigned char cb_qp_offset_list[HEVCFW_MAX_CHROMA_QP]; + unsigned char cr_qp_offset_list[HEVCFW_MAX_CHROMA_QP]; + + unsigned char uniform_spacing_flag; + unsigned char column_width_minus1[HEVCFW_MAX_TILE_COLS]; + unsigned char row_height_minus1[HEVCFW_MAX_TILE_ROWS]; + + unsigned char pps_seq_parameter_set_id; + unsigned char deblocking_filter_control_present_flag; + unsigned char pps_scaling_list_data_present_flag; + unsigned char pps_extension_present_flag; + + struct hevcfw_scaling_listdata scaling_list; +}; + +/* This enum determines reference picture status */ +enum hevcfw_reference_type { + HEVCFW_REF_UNUSED = 0, + HEVCFW_REF_SHORTTERM, + HEVCFW_REF_LONGTERM, + HEVCFW_REF_FORCE32BITS = 0x7FFFFFFFU +}; + +/* This describes an HEVC picture. It is part of the Context data */ +struct hevcfw_picture { + /* Primary (reconstructed) picture buffers */ + struct vdecfw_image_buffer primary; + /* Secondary (alternative) picture buffers */ + struct vdecfw_image_buffer alternate; + /* Unique ID for this picture */ + unsigned int transaction_id; + /* nut of first ssh of picture, determines picture type */ + unsigned char nalunit_type; + /* Picture Order Count (frame number) */ + int pic_order_cnt_val; + /* Slice Picture Order Count Lsb */ + int slice_pic_ordercnt_lsb; + unsigned char pic_output_flag; + /* information about long-term pictures */ + unsigned short dpb_longterm_flags; + unsigned int dpb_pic_order_diff[HEVCFW_NUM_DPB_DIFF_REGS]; + /* address of buffer for temporal mv params */ + unsigned int temporal_outaddr; + /* worst case Dpb diff for the current pic */ + unsigned int dpb_diff; +}; + +/* + * This is a wrapper for a picture to hold it in a Decoded Picture Buffer + * for further reference + */ +struct hevcfw_picture_in_dpb { + /* DPB data about the picture */ + enum hevcfw_reference_type ref_type; + unsigned char valid; + unsigned char needed_for_output; + unsigned char pic_latency_count; + /* Picture itself */ + struct hevcfw_picture picture; +}; + +/* + * This describes an HEVC's Decoded Picture Buffer (DPB). + * It is part of the Context data + */ +#define HEVCFW_DPB_IDX_INVALID -1 + +struct hevcfw_decoded_picture_buffer { + /* reference pictures */ + struct hevcfw_picture_in_dpb pictures[HEVCFW_MAX_DPB_SIZE]; + /* organizational data of DPB */ + unsigned int fullness; +}; + +/* + * This describes an HEVC's Reference Picture Set (RPS). + * It is part of the Context data + */ +struct hevcfw_reference_picture_set { + /* sizes of poc lists */ + unsigned char num_pocst_curr_before; + unsigned char num_pocst_curr_after; + unsigned char num_pocst_foll; + unsigned char num_poclt_curr; + unsigned char num_poclt_foll; + /* poc lists */ + int pocst_curr_before[HEVCFW_MAX_NUM_REF_PICS]; + int pocst_curr_after[HEVCFW_MAX_NUM_REF_PICS]; + int pocst_foll[HEVCFW_MAX_NUM_REF_PICS]; + int poclt_curr[HEVCFW_MAX_NUM_REF_PICS]; + int poclt_foll[HEVCFW_MAX_NUM_REF_PICS]; + /* derived elements */ + unsigned char curr_delta_pocmsb_presentflag[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char foll_delta_pocmsb_presentflag[HEVCFW_MAX_NUM_REF_PICS]; + /* reference picture sets: indices in DPB */ + unsigned char ref_picsetlt_curr[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char ref_picsetlt_foll[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char ref_picsetst_curr_before[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char ref_picsetst_curr_after[HEVCFW_MAX_NUM_REF_PICS]; + unsigned char ref_picsetst_foll[HEVCFW_MAX_NUM_REF_PICS]; +}; + +/* + * This describes the HEVC parser component "Context data", shown in the + * Firmware Memory Layout diagram. This data is the state preserved across + * pictures. It is loaded and saved by the Firmware, but requires the host to + * provide buffer(s) for this. + */ +struct hevcfw_ctx_data { + struct hevcfw_sequence_ps sps; + struct hevcfw_picture_ps pps; + /* + * data from last picture with TemporalId = 0 that is not a RASL, RADL + * or sub-layer non-reference picture + */ + int prev_pic_order_cnt_lsb; + int prev_pic_order_cnt_msb; + unsigned char last_irapnorasl_output_flag; + /* + * Decoded Pictures Buffer holds information about decoded pictures + * needed for further INTER decoding + */ + struct hevcfw_decoded_picture_buffer dpb; + /* Reference Picture Set is determined on per-picture basis */ + struct hevcfw_reference_picture_set rps; + /* + * Reference Picture List is determined using data from Reference + * Picture Set and from Slice (Segment) Header on per-slice basis + */ + unsigned char ref_pic_list[HEVCFW_NUM_REF_PIC_LISTS][HEVCFW_MAX_NUM_REF_PICS]; + /* + * Reference Picture List used to send reflist to the host, the only + * difference is that missing references are marked + * with HEVCFW_DPB_IDX_INVALID + */ + unsigned char ref_pic_listhlp[HEVCFW_NUM_REF_PIC_LISTS][HEVCFW_MAX_NUM_REF_PICS]; + + unsigned int pic_count; + unsigned int slice_segment_count; + /* There was EOS NAL detected and no new picture yet */ + int eos_detected; + /* This is first picture after EOS NAL */ + int first_after_eos; +}; + +#endif /* _HEVCFW_DATA_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/hevcfw_data_shared.h b/drivers/media/platform/vxe-vxd/decoder/hevcfw_data_shared.h --- a/drivers/media/platform/vxe-vxd/decoder/hevcfw_data_shared.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/hevcfw_data_shared.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,767 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures for the hevc parser firmware module + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreming + * Sidraya Jayagond + */ +#ifdef USE_SHARING +#endif + +#ifndef _HEVCFW_DATA_H_ +#define _HEVCFW_DATA_H_ + +#include "vdecfw_share.h" +#include "vdecfw_shared.h" + +#define HEVC_MAX_VPS_COUNT 16 +#define HEVC_MAX_SPS_COUNT 16 +#define HEVC_MAX_PPS_COUNT 64 + +#define HEVCFW_MAX_NUM_PROFILE_IDC 32 +#define HEVCFW_MAX_VPS_OP_SETS_PLUS1 1024 +#define HEVCFW_MAX_VPS_NUH_RESERVED_ZERO_LAYER_ID_PLUS1 1 + +#define HEVCFW_MAX_NUM_REF_PICS 16 +#define HEVCFW_MAX_NUM_ST_REF_PIC_SETS 65 +#define HEVCFW_MAX_NUM_LT_REF_PICS 32 +#define HEVCFW_MAX_NUM_SUBLAYERS 7 +#define HEVCFW_SCALING_LISTS_BUFSIZE 256 +#define HEVCFW_MAX_TILE_COLS 20 +#define HEVCFW_MAX_TILE_ROWS 22 + +#define HEVCFW_MAX_CHROMA_QP 6 + +#define HEVCFW_MAX_DPB_SIZE HEVCFW_MAX_NUM_REF_PICS +#define HEVCFW_REF_PIC_LIST0 0 +#define HEVCFW_REF_PIC_LIST1 1 +#define HEVCFW_NUM_REF_PIC_LISTS 2 +#define HEVCFW_NUM_DPB_DIFF_REGS 4 + +/* non-critical errors*/ +#define HEVC_ERR_INVALID_VALUE (20) +#define HEVC_ERR_CORRECTION_VALIDVALUE (21) + +#define HEVC_IS_ERR_CRITICAL(err) \ + ((err) > HEVC_ERR_CORRECTION_VALIDVALUE ? 1 : 0) + +/* critical errors*/ +#define HEVC_ERR_INV_VIDEO_DIMENSION (22) +#define HEVC_ERR_NO_SEQUENCE_HDR (23) +#define HEVC_ERR_SPS_EXT_UNSUPP (24 | VDECFW_UNSUPPORTED_CODE_BASE) +#define HEVC_ERR_PPS_EXT_UNSUPP (25 | VDECFW_UNSUPPORTED_CODE_BASE) + +#define HEVC_ERR_FAILED_TO_STORE_VPS (100) +#define HEVC_ERR_FAILED_TO_STORE_SPS (101) +#define HEVC_ERR_FAILED_TO_STORE_PPS (102) + +#define HEVC_ERR_FAILED_TO_FETCH_VPS (103) +#define HEVC_ERR_FAILED_TO_FETCH_SPS (104) +#define HEVC_ERR_FAILED_TO_FETCH_PPS (105) +/* HEVC Scaling Lists (all values are maximum possible ones) */ +#define HEVCFW_SCALING_LIST_NUM_SIZES 4 +#define HEVCFW_SCALING_LIST_NUM_MATRICES 6 +#define HEVCFW_SCALING_LIST_MATRIX_SIZE 64 + +struct hevcfw_scaling_listdata { + unsigned char dc_coeffs + [HEVCFW_SCALING_LIST_NUM_SIZES - 2] + [HEVCFW_SCALING_LIST_NUM_MATRICES]; + + unsigned char lists + [HEVCFW_SCALING_LIST_NUM_SIZES] + [HEVCFW_SCALING_LIST_NUM_MATRICES] + [HEVCFW_SCALING_LIST_MATRIX_SIZE]; +}; + +/* HEVC Video Profile_Tier_Level */ +struct hevcfw_profile_tier_level { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_profile_space); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_tier_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_profile_idc); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + general_profile_compatibility_flag + [HEVCFW_MAX_NUM_PROFILE_IDC]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_progressive_source_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_interlaced_source_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_non_packed_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_frame_only_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_max_12bit_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_max_10bit_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_max_8bit_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_max_422chroma_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_max_420chroma_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_max_monochrome_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_intra_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + general_one_picture_only_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_lower_bit_rate_constraint_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, general_level_idc); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_profile_present_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_level_present_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_profile_space[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_tier_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_profile_idc[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_profile_compatibility_flag + [HEVCFW_MAX_NUM_SUBLAYERS - 1][HEVCFW_MAX_NUM_PROFILE_IDC]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_progressive_source_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_interlaced_source_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_non_packed_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_frame_only_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_max_12bit_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_max_10bit_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_max_8bit_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_max_422chroma_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_max_420chroma_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_max_monochrome_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_intra_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_one_picture_only_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_lower_bit_rate_constraint_flag[HEVCFW_MAX_NUM_SUBLAYERS - 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sub_layer_level_idc[HEVCFW_MAX_NUM_SUBLAYERS - 1]); +}; + +struct hevcfw_video_ps { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, is_different); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, is_sent); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, is_available); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vps_video_parameter_set_id); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vps_reserved_three_2bits); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vps_max_layers_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vps_max_sub_layers_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vps_temporal_id_nesting_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, vps_reserved_0xffff_16bits); + struct hevcfw_profile_tier_level profile_tier_level; +}; + +/* HEVC Video Usability Information */ +struct hevcfw_vui_params { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, aspect_ratio_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, aspect_ratio_idc); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, sar_width); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, sar_height); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, overscan_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, overscan_appropriate_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, video_signal_type_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, video_format); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, video_full_range_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, colour_description_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, colour_primaries); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transfer_characteristics); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, matrix_coeffs); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_loc_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_sample_loc_type_top_field); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_sample_loc_type_bottom_field); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, neutral_chroma_indication_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, field_seq_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, frame_field_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, default_display_window_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, def_disp_win_left_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, def_disp_win_right_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, def_disp_win_top_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, def_disp_win_bottom_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vui_timing_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, vui_num_units_in_tick); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, vui_time_scale); +}; + +/* HEVC Short Term Reference Picture Set */ +struct hevcfw_short_term_ref_picset { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_negative_pics); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_positive_pics); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + short, delta_poc_s0[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + short, delta_poc_s1[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, used_bycurr_pic_s0[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, used_bycurr_pic_s1[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_delta_pocs); +}; + +/* + * This describes the SPS header data required by the HEVC firmware that should + * be supplied by the Host. + */ +struct hevcfw_sequence_ps { + /* syntax elements from SPS */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, pic_width_in_luma_samples); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, pic_height_in_luma_samples); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_short_term_ref_pic_sets); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_long_term_ref_pics_sps); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, + lt_ref_pic_poc_lsb_sps[HEVCFW_MAX_NUM_LT_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + used_by_curr_pic_lt_sps_flag[HEVCFW_MAX_NUM_LT_REF_PICS]); + struct hevcfw_short_term_ref_picset st_rps_list[HEVCFW_MAX_NUM_ST_REF_PIC_SETS]; + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_max_sub_layers_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sps_max_dec_pic_buffering_minus1[HEVCFW_MAX_NUM_SUBLAYERS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + sps_max_num_reorder_pics[HEVCFW_MAX_NUM_SUBLAYERS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + sps_max_latency_increase_plus1[HEVCFW_MAX_NUM_SUBLAYERS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, max_transform_hierarchy_depth_inter); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, max_transform_hierarchy_depth_intra); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_diff_max_min_transform_block_size); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_min_transform_block_size_minus2); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + log2_diff_max_min_luma_coding_block_size); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_min_luma_coding_block_size_minus3); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_format_idc); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, separate_colour_plane_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_extra_slice_header_bits); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_max_pic_order_cnt_lsb_minus4); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, long_term_ref_pics_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sample_adaptive_offset_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_temporal_mvp_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, bit_depth_luma_minus8); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, bit_depth_chroma_minus8); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pcm_sample_bit_depth_luma_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pcm_sample_bit_depth_chroma_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + log2_min_pcm_luma_coding_block_size_minus3); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + log2_diff_max_min_pcm_luma_coding_block_size); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pcm_loop_filter_disabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, amp_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pcm_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, strong_intra_smoothing_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, scaling_list_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transform_skip_rotation_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transform_skip_context_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, implicit_rdpcm_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, explicit_rdpcm_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, extended_precision_processing_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, intra_smoothing_disabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, high_precision_offsets_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, persistent_rice_adaptation_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cabac_bypass_alignment_enabled_flag); + /* derived elements */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pic_size_in_ctbs_y); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, pic_height_in_ctbs_y); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, pic_width_in_ctbs_y); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, ctb_size_y); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, ctb_log2size_y); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, max_pic_order_cnt_lsb); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + sps_max_latency_pictures[HEVCFW_MAX_NUM_SUBLAYERS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_seq_parameter_set_id); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_video_parameter_set_id); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_temporal_id_nesting_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_seq_parameter_set_id); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, conformance_window_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, conf_win_left_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, conf_win_right_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, conf_win_top_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, conf_win_bottom_offset); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_sub_layer_ordering_info_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_scaling_list_data_present_flag); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, vui_parameters_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sps_extension_present_flag); + + struct hevcfw_vui_params vui_params; + /* derived elements */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sub_width_c); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sub_height_c); + + struct hevcfw_profile_tier_level profile_tier_level; + struct hevcfw_scaling_listdata scaling_listdata; +}; + +/* + * This describes the HEVC parser component "Header data", shown in the + * Firmware Memory Layout diagram. This data is required by the HEVC firmware + * and should be supplied by the Host. + */ +struct hevcfw_headerdata { + /* Decode buffers and output control for the current picture */ + /* Primary decode buffer base addresses */ + struct vdecfw_image_buffer primary; + /* buffer base addresses for alternate output */ + struct vdecfw_image_buffer alternate; + /* address of buffer for temporal mv params */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, temporal_outaddr); +}; + +/* + * This describes the PPS header data required by the HEVC firmware that should + * be supplied by the Host. + */ +struct hevcfw_picture_ps { + /* syntax elements from the PPS */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_pic_parameter_set_id); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_tile_columns_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_tile_rows_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, diff_cu_qp_delta_depth); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, init_qp_minus26); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_beta_offset_div2); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_tc_offset_div2); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_cb_qp_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_cr_qp_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, log2_parallel_merge_level_minus2); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, dependent_slice_segments_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, output_flag_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_extra_slice_header_bits); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, lists_modification_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cabac_init_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, weighted_pred_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, weighted_bipred_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + pps_slice_chroma_qp_offsets_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + deblocking_filter_override_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, tiles_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, entropy_coding_sync_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + slice_segment_header_extension_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transquant_bypass_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cu_qp_delta_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, transform_skip_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, sign_data_hiding_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_ref_idx_l0_default_active_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_ref_idx_l1_default_active_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, constrained_intra_pred_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_deblocking_filter_disabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + pps_loop_filter_across_slices_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, loop_filter_across_tiles_enabled_flag); + + /* rewritten from SPS, maybe at some point we could get rid of this */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, scaling_list_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + log2_max_transform_skip_block_size_minus2); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + cross_component_prediction_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_qp_offset_list_enabled_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, diff_cu_chroma_qp_offset_depth); + /* + * PVDEC derived elements. HEVCFW_SCALING_LISTS_BUFSIZE is + * multiplied by 2 to ensure that there will be space for address of + * each element. These addresses are completed in lower layer. + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + scaling_lists[HEVCFW_SCALING_LISTS_BUFSIZE * 2]); + /* derived elements */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, col_bd[HEVCFW_MAX_TILE_COLS + 1]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, row_bd[HEVCFW_MAX_TILE_ROWS + 1]); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, chroma_qp_offset_list_len_minus1); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cb_qp_offset_list[HEVCFW_MAX_CHROMA_QP]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, cr_qp_offset_list[HEVCFW_MAX_CHROMA_QP]); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, uniform_spacing_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + column_width_minus1[HEVCFW_MAX_TILE_COLS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + row_height_minus1[HEVCFW_MAX_TILE_ROWS]); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_seq_parameter_set_id); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, deblocking_filter_control_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_scaling_list_data_present_flag); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pps_extension_present_flag); + + struct hevcfw_scaling_listdata scaling_list; +}; + +/* This enum determines reference picture status */ +enum hevcfw_reference_type { + HEVCFW_REF_UNUSED = 0, + HEVCFW_REF_SHORTTERM, + HEVCFW_REF_LONGTERM, + HEVCFW_REF_FORCE32BITS = 0x7FFFFFFFU +}; + +/* This describes an HEVC picture. It is part of the Context data */ +struct hevcfw_picture { + /* Primary (reconstructed) picture buffers */ + struct vdecfw_image_buffer primary; + /* Secondary (alternative) picture buffers */ + struct vdecfw_image_buffer alternate; + /* Unique ID for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, transaction_id); + /* nut of first ssh of picture, determines picture type */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, nalunit_type); + /* Picture Order Count (frame number) */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pic_order_cnt_val); + /* Slice Picture Order Count Lsb */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, slice_pic_ordercnt_lsb); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pic_output_flag); + /* information about long-term pictures */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, dpb_longterm_flags); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + dpb_pic_order_diff[HEVCFW_NUM_DPB_DIFF_REGS]); + /* address of buffer for temporal mv params */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, temporal_outaddr); + /* worst case Dpb diff for the current pic */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, dpb_diff); +}; + +/* + * This is a wrapper for a picture to hold it in a Decoded Picture Buffer + * for further reference + */ +struct hevcfw_picture_in_dpb { + /* DPB data about the picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum hevcfw_reference_type, ref_type); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, valid); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, needed_for_output); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, pic_latency_count); + /* Picture itself */ + struct hevcfw_picture picture; +}; + +/* + * This describes an HEVC's Decoded Picture Buffer (DPB). + * It is part of the Context data + */ + +#define HEVCFW_DPB_IDX_INVALID -1 + +struct hevcfw_decoded_picture_buffer { + /* reference pictures */ + struct hevcfw_picture_in_dpb pictures[HEVCFW_MAX_DPB_SIZE]; + /* organizational data of DPB */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, fullness); +}; + +/* + * This describes an HEVC's Reference Picture Set (RPS). + * It is part of the Context data + */ +struct hevcfw_reference_picture_set { + /* sizes of poc lists */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_pocst_curr_before); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_pocst_curr_after); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_pocst_foll); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_poclt_curr); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, num_poclt_foll); + /* poc lists */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pocst_curr_before[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pocst_curr_after[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pocst_foll[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, poclt_curr[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, poclt_foll[HEVCFW_MAX_NUM_REF_PICS]); + /* derived elements */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + curr_delta_pocmsb_presentflag[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + foll_delta_pocmsb_presentflag[HEVCFW_MAX_NUM_REF_PICS]); + /* reference picture sets: indices in DPB */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, ref_picsetlt_curr[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, ref_picsetlt_foll[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + ref_picsetst_curr_before[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + ref_picsetst_curr_after[HEVCFW_MAX_NUM_REF_PICS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, ref_picsetst_foll[HEVCFW_MAX_NUM_REF_PICS]); +}; + +/* + * This describes the HEVC parser component "Context data", shown in the + * Firmware Memory Layout diagram. This data is the state preserved across + * pictures. It is loaded and saved by the Firmware, but requires the host to + * provide buffer(s) for this. + */ +struct hevcfw_ctx_data { + struct hevcfw_sequence_ps sps; + struct hevcfw_picture_ps pps; + /* + * data from last picture with TemporalId = 0 that is not a RASL, RADL + * or sub-layer non-reference picture + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, prev_pic_order_cnt_lsb); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, prev_pic_order_cnt_msb); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, last_irapnorasl_output_flag); + /* + * Decoded Pictures Buffer holds information about decoded pictures + * needed for further INTER decoding + */ + struct hevcfw_decoded_picture_buffer dpb; + /* Reference Picture Set is determined on per-picture basis */ + struct hevcfw_reference_picture_set rps; + /* + * Reference Picture List is determined using data from Reference + * Picture Set and from Slice (Segment) Header on per-slice basis + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, + ref_pic_list[HEVCFW_NUM_REF_PIC_LISTS][HEVCFW_MAX_NUM_REF_PICS]); + /* + * Reference Picture List used to send reflist to the host, the only + * difference is that missing references are marked + * with HEVCFW_DPB_IDX_INVALID + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, + ref_pic_listhlp[HEVCFW_NUM_REF_PIC_LISTS][HEVCFW_MAX_NUM_REF_PICS]); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pic_count); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, slice_segment_count); + /* There was EOS NAL detected and no new picture yet */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, eos_detected); + /* This is first picture after EOS NAL */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, first_after_eos); +}; + +#endif /* _HEVCFW_DATA_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.c b/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.c --- a/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,2895 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * hevc secure data unit parsing API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "bspp_int.h" +#include "hevc_secure_parser.h" +#include "hevcfw_data.h" +#include "pixel_api.h" +#include "swsr.h" +#include "vdec_defs.h" +#include "vdecdd_utils.h" + +#if defined(DEBUG_DECODER_DRIVER) +#define BSPP_HEVC_SYNTAX(fmt, ...) pr_info("[hevc] " fmt, ## __VA_ARGS__) + +#else + +#define BSPP_HEVC_SYNTAX(fmt, ...) +#endif + +static void HEVC_SWSR_U1(unsigned char *what, unsigned char *where, void *swsr_ctx) +{ + *where = swsr_read_bits(swsr_ctx, 1); +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s, u(1) : %u", what, *where); +#endif +} + +static void HEVC_SWSR_UN(unsigned char *what, unsigned int *where, + unsigned char numbits, void *swsr_ctx) +{ + *where = swsr_read_bits(swsr_ctx, numbits); +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s, u(%u) : %u", what, numbits, *where); +#endif +} + +static void HEVC_SWSR_UE(unsigned char *what, unsigned int *where, void *swsr_ctx) +{ + *where = swsr_read_unsigned_expgoulomb(swsr_ctx); +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s, ue(v) : %u", what, *where); +#endif +} + +static void HEVC_SWSR_SE(unsigned char *what, int *where, void *swsr_ctx) +{ + *where = swsr_read_signed_expgoulomb(swsr_ctx); +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s, se(v) : %u", what, *where); +#endif +} + +static void HEVC_SWSR_FN(unsigned char *what, unsigned char *where, + unsigned char numbits, unsigned char pattern, + enum bspp_error_type *bspperror, void *swsr_ctx) +{ + *where = swsr_read_bits(swsr_ctx, numbits); +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s, f(%u) : %u", what, numbits, *where); +#endif + if (*where != pattern) { + *bspperror |= BSPP_ERROR_INVALID_VALUE; + pr_warn("Invalid value of %s (f(%u), expected: %u, got: %u)", + what, numbits, pattern, *where); + } +} + +static void HEVC_UCHECK(unsigned char *what, unsigned int val, + unsigned int expected, + enum bspp_error_type *bspperror) +{ + if (val != expected) { + *bspperror |= BSPP_ERROR_INVALID_VALUE; + pr_warn("Invalid value of %s (expected: %u, got: %u)", + what, expected, val); + } +} + +static void HEVC_RANGEUCHECK(unsigned char *what, unsigned int val, + unsigned int min, unsigned int max, + enum bspp_error_type *bspperror) +{ + if ((min > 0 && val < min) || val > max) { + *bspperror |= BSPP_ERROR_INVALID_VALUE; + pr_warn("Value of %s out of range (expected: [%u, %u], got: %u)", + what, min, max, val); + } +} + +static void HEVC_RANGESCHECK(unsigned char *what, int val, int min, int max, + enum bspp_error_type *bspperror) +{ + if (val < min || val > max) { + *bspperror |= BSPP_ERROR_INVALID_VALUE; + pr_warn("Value of %s out of range (expected: [%d, %d], got: %d)", + what, min, max, val); + } +} + +#define HEVC_STATIC_ASSERT(expr) ((void)sizeof(unsigned char[1 - 2 * !(expr)])) + +#define HEVC_MIN(a, b, type) ({ \ + type __a = a; \ + type __b = b; \ + (((__a) <= (__b)) ? (__a) : (__b)); }) +#define HEVC_MAX(a, b, type) ({ \ + type __a = a; \ + type __b = b; \ + (((__a) >= (__b)) ? (__a) : (__b)); }) +#define HEVC_ALIGN(_val, _alignment, type) ({ \ + type val = _val; \ + type alignment = _alignment; \ + (((val) + (alignment) - 1) & ~((alignment) - 1)); }) + +static const enum pixel_fmt_idc pixelformat_idc[] = { + PIXEL_FORMAT_MONO, + PIXEL_FORMAT_420, + PIXEL_FORMAT_422, + PIXEL_FORMAT_444 +}; + +static enum bspp_error_type bspp_hevc_parse_vps(void *sr_ctx, struct bspp_hevc_vps *vps); + +static void bspp_hevc_sublayhrdparams(void *sr_ctx, + struct bspp_hevc_hrd_parameters *hrdparams, + unsigned char sublayer_id); + +static void bspp_hevc_parsehrdparams(void *sr_ctx, + struct bspp_hevc_hrd_parameters *hrdparams, + unsigned char common_infpresent, + unsigned char max_numsublayers_minus1); + +static enum bspp_error_type bspp_hevc_parsesps(void *sr_ctx, + void *str_res, + struct bspp_hevc_sps *sps); + +static enum bspp_error_type bspp_hevc_parsepps(void *sr_ctx, void *str_res, + struct bspp_hevc_pps *pps); + +static int bspp_hevc_reset_ppsinfo(void *secure_ppsinfo); + +static void bspp_hevc_dotilecalculations(struct bspp_hevc_sps *sps, + struct bspp_hevc_pps *pps); + +static enum bspp_error_type bspp_hevc_parse_slicesegmentheader + (void *sr_ctx, void *str_res, + struct bspp_hevc_slice_segment_header *ssh, + unsigned char nalunit_type, + struct bspp_vps_info **vpsinfo, + struct bspp_sequence_hdr_info **spsinfo, + struct bspp_pps_info **ppsinfo); + +static enum bspp_error_type bspp_hevc_parse_profiletierlevel + (void *sr_ctx, + struct bspp_hevc_profile_tierlevel *ptl, + unsigned char vps_maxsublayers_minus1); + +static void bspp_hevc_getdefault_scalinglist(unsigned char size_id, unsigned char matrix_id, + const unsigned char **default_scalinglist, + unsigned int *size); + +static enum bspp_error_type bspp_hevc_parse_scalinglistdata + (void *sr_ctx, + struct bspp_hevc_scalinglist_data *scaling_listdata); + +static void bspp_hevc_usedefault_scalinglists(struct bspp_hevc_scalinglist_data *scaling_listdata); + +static enum bspp_error_type bspp_hevc_parse_shortterm_refpicset + (void *sr_ctx, + struct bspp_hevc_shortterm_refpicset *st_refpicset, + unsigned char st_rps_idx, + unsigned char in_slice_header); + +static void bspp_hevc_fillcommonseqhdr(struct bspp_hevc_sps *sps, + struct vdec_comsequ_hdrinfo *common_seq); + +static void bspp_hevc_fillpicturehdr(struct vdec_comsequ_hdrinfo *common_seq, + enum hevc_nalunittype nalunit_type, + struct bspp_pict_hdr_info *picture_hdr, + struct bspp_hevc_sps *sps, + struct bspp_hevc_pps *pps, + struct bspp_hevc_vps *vps); + +static void bspp_hevc_fill_fwsps(struct bspp_hevc_sps *sps, + struct hevcfw_sequence_ps *fwsps); + +static void bspp_hevc_fill_fwst_rps(struct bspp_hevc_shortterm_refpicset *strps, + struct hevcfw_short_term_ref_picset *fwstrps); + +static void bspp_hevc_fill_fwpps(struct bspp_hevc_pps *pps, + struct hevcfw_picture_ps *fw_pps); + +static void bspp_hevc_fill_fw_scaling_lists(struct bspp_hevc_pps *pps, + struct bspp_hevc_sps *sps, + struct hevcfw_picture_ps *fw_pps); + +static unsigned int bspp_ceil_log2(unsigned int linear_val); + +static unsigned char bspp_hevc_picture_is_irap(enum hevc_nalunittype nalunit_type); + +static unsigned char bspp_hevc_picture_is_cra(enum hevc_nalunittype nalunit_type); + +static unsigned char bspp_hevc_picture_is_idr(enum hevc_nalunittype nalunit_type); + +static unsigned char bspp_hevc_picture_is_bla(enum hevc_nalunittype nalunit_type); + +static unsigned char bspp_hevc_picture_getnorasl_outputflag + (enum hevc_nalunittype nalunit_type, + struct bspp_hevc_inter_pict_ctx *inter_pict_ctx); + +static unsigned char bspp_hevc_range_extensions_is_enabled + (struct bspp_hevc_profile_tierlevel *profile_tierlevel); + +static int bspp_hevc_unitparser(void *swsr_ctx, struct bspp_unit_data *unitdata) +{ + void *sr_ctx = swsr_ctx; + int result = 0; + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + struct bspp_inter_pict_data *inter_pict_ctx = + unitdata->parse_state->inter_pict_ctx; + unsigned char forbidden_zero_bit = 0; + unsigned char nal_unit_type = 0; + unsigned char nuh_layer_id = 0; + unsigned char nuh_temporal_id_plus1 = 0; + + HEVC_SWSR_FN("forbidden_zero_bit", &forbidden_zero_bit, 1, 0, &parse_err, sr_ctx); + HEVC_SWSR_UN("nal_unit_type", (unsigned int *)&nal_unit_type, 6, sr_ctx); + /* for current version of HEVC nuh_layer_id "shall be equal to 0" */ + HEVC_SWSR_FN("nuh_layer_id", &nuh_layer_id, 6, 0, &parse_err, sr_ctx); + HEVC_SWSR_UN("nuh_temporal_id_plus1", (unsigned int *)&nuh_temporal_id_plus1, 3, sr_ctx); + + switch (unitdata->unit_type) { + case BSPP_UNIT_VPS: + { + struct bspp_hevc_vps *vps = + (struct bspp_hevc_vps *)unitdata->out.vps_info->secure_vpsinfo; + + unitdata->parse_error |= bspp_hevc_parse_vps(sr_ctx, vps); + unitdata->out.vps_info->vps_id = + vps->vps_video_parameter_set_id; + } + break; + + case BSPP_UNIT_SEQUENCE: + { + struct bspp_ddbuf_array_info *tmp; + struct hevcfw_sequence_ps *fwsps; + struct vdec_comsequ_hdrinfo *common_seq; + struct bspp_hevc_sps *sps = + (struct bspp_hevc_sps *)unitdata->out.sequ_hdr_info->secure_sequence_info; + + unitdata->parse_error |= bspp_hevc_parsesps(sr_ctx, + unitdata->str_res_handle, + sps); + unitdata->out.sequ_hdr_info->sequ_hdr_info.sequ_hdr_id = + sps->sps_seq_parameter_set_id; + + tmp = &unitdata->out.sequ_hdr_info->fw_sequence; + /* handle firmware headers */ + fwsps = + (struct hevcfw_sequence_ps *)((unsigned char *)tmp->ddbuf_info.cpu_virt_addr + + tmp->buf_offset); + + bspp_hevc_fill_fwsps(sps, fwsps); + + /* handle common sequence header */ + common_seq = + &unitdata->out.sequ_hdr_info->sequ_hdr_info.com_sequ_hdr_info; + + bspp_hevc_fillcommonseqhdr(sps, common_seq); + } + break; + + case BSPP_UNIT_PPS: + { + struct bspp_ddbuf_array_info *tmp; + struct hevcfw_picture_ps *fw_pps; + struct bspp_hevc_pps *pps = + (struct bspp_hevc_pps *)unitdata->out.pps_info->secure_pps_info; + + unitdata->parse_error |= bspp_hevc_parsepps(sr_ctx, + unitdata->str_res_handle, + pps); + unitdata->out.pps_info->pps_id = pps->pps_pic_parameter_set_id; + + tmp = &unitdata->out.pps_info->fw_pps; + /* handle firmware headers */ + fw_pps = + (struct hevcfw_picture_ps *)((unsigned char *)tmp->ddbuf_info.cpu_virt_addr + + tmp->buf_offset); + bspp_hevc_fill_fwpps(pps, fw_pps); + } + break; + + case BSPP_UNIT_PICTURE: + { + struct bspp_hevc_slice_segment_header ssh; + struct bspp_vps_info *vps_info = NULL; + struct bspp_sequence_hdr_info *sequ_hdr_info = NULL; + struct bspp_hevc_sps *hevc_sps = NULL; + struct bspp_pps_info *ppsinfo = NULL; + enum bspp_error_type parse_error; + struct bspp_ddbuf_array_info *tmp; + struct hevcfw_picture_ps *fw_pps; + struct bspp_pict_data *pictdata; + struct bspp_hevc_pps *pps; + + /* + * EOS has to be attached to picture data, so it can be used + * for NoRaslOutputFlag calculation in FW + */ + inter_pict_ctx->hevc_ctx.eos_detected = 0; + if (nal_unit_type == HEVC_NALTYPE_EOS) { + inter_pict_ctx->hevc_ctx.eos_detected = 1; + break; + } + + parse_error = bspp_hevc_parse_slicesegmentheader(sr_ctx, + unitdata->str_res_handle, + &ssh, + nal_unit_type, + &vps_info, + &sequ_hdr_info, + &ppsinfo); + unitdata->parse_error |= parse_error; + unitdata->slice = 1; + + if (parse_error != BSPP_ERROR_NONE && + parse_error != BSPP_ERROR_CORRECTION_VALIDVALUE) { + result = IMG_ERROR_CANCELLED; + break; + } + + /* if we just started new picture. */ + if (ssh.first_slice_segment_in_pic_flag) { + tmp = &ppsinfo->fw_pps; + /* handle firmware headers */ + fw_pps = + (struct hevcfw_picture_ps *)((unsigned char *)tmp->ddbuf_info.cpu_virt_addr + + tmp->buf_offset); + + inter_pict_ctx->hevc_ctx.first_after_eos = 0; + if (inter_pict_ctx->hevc_ctx.eos_detected) { + inter_pict_ctx->hevc_ctx.first_after_eos = 1; + inter_pict_ctx->hevc_ctx.eos_detected = 0; + } + + /* fill common picture header */ + bspp_hevc_fillpicturehdr(&sequ_hdr_info->sequ_hdr_info.com_sequ_hdr_info, + (enum hevc_nalunittype)nal_unit_type, + unitdata->out.pict_hdr_info, + (struct bspp_hevc_sps *) + sequ_hdr_info->secure_sequence_info, + (struct bspp_hevc_pps *)ppsinfo->secure_pps_info, + (struct bspp_hevc_vps *)vps_info->secure_vpsinfo); + + bspp_hevc_fill_fw_scaling_lists(ppsinfo->secure_pps_info, + sequ_hdr_info->secure_sequence_info, + fw_pps); + + pictdata = &unitdata->out.pict_hdr_info->pict_aux_data; + /* + * We have no container for the PPS that passes down + * to the kernel, for this reason the hevc secure parser + * needs to populate that info into the picture + * header PictAuxData. + */ + pictdata->bufmap_id = ppsinfo->bufmap_id; + pictdata->buf_offset = ppsinfo->buf_offset; + pictdata->pic_data = fw_pps; + pictdata->id = fw_pps->pps_pic_parameter_set_id; + pictdata->size = sizeof(*fw_pps); + + ppsinfo->ref_count++; + + /* new Coded Video Sequence indication */ + if (nal_unit_type == HEVC_NALTYPE_IDR_W_RADL || + nal_unit_type == HEVC_NALTYPE_IDR_N_LP || + nal_unit_type == HEVC_NALTYPE_BLA_N_LP || + nal_unit_type == HEVC_NALTYPE_BLA_W_RADL || + nal_unit_type == HEVC_NALTYPE_BLA_W_LP || + nal_unit_type == HEVC_NALTYPE_CRA) { + unitdata->new_closed_gop = 1; + inter_pict_ctx->hevc_ctx.seq_pic_count = 0; + } + + /* Attach SEI data to the picture. */ + if (!inter_pict_ctx->hevc_ctx.sei_info_attached_to_pic) { + /* + * If there is already a non-empty SEI list + * available + */ + if (inter_pict_ctx->hevc_ctx.sei_rawdata_list) { + /* attach it to the picture header. */ + unitdata->out.pict_hdr_info->hevc_pict_hdr_info.raw_sei_datalist_firstfield + = + (void *)inter_pict_ctx->hevc_ctx.sei_rawdata_list; + inter_pict_ctx->hevc_ctx.sei_info_attached_to_pic = 1; + } else { + /* Otherwise expose a handle a picture header field to + * attach SEI list later. + */ + inter_pict_ctx->hevc_ctx.hndl_pichdr_sei_rawdata_list = + &unitdata->out.pict_hdr_info->hevc_pict_hdr_info.raw_sei_datalist_firstfield; + } + } + + /* Attach raw VUI data to the picture header. */ + hevc_sps = (struct bspp_hevc_sps *)sequ_hdr_info->secure_sequence_info; + if (hevc_sps->vui_raw_data) { + hevc_sps->vui_raw_data->ref_count++; + unitdata->out.pict_hdr_info->hevc_pict_hdr_info.raw_vui_data = + (void *)hevc_sps->vui_raw_data; + } + + inter_pict_ctx->hevc_ctx.seq_pic_count++; + + /* NoOutputOfPriorPicsFlag */ + inter_pict_ctx->not_dpb_flush = 0; + if (unitdata->new_closed_gop && + bspp_hevc_picture_is_irap((enum hevc_nalunittype)nal_unit_type) && + bspp_hevc_picture_getnorasl_outputflag((enum hevc_nalunittype) + nal_unit_type, + &inter_pict_ctx->hevc_ctx)) { + if (bspp_hevc_picture_is_cra((enum hevc_nalunittype)nal_unit_type)) + inter_pict_ctx->not_dpb_flush = 1; + else + inter_pict_ctx->not_dpb_flush = + ssh.no_output_of_prior_pics_flag; + } + + unitdata->parse_state->next_pic_is_new = 0; + } + + pps = (struct bspp_hevc_pps *)ppsinfo->secure_pps_info; + unitdata->pict_sequ_hdr_id = pps->pps_seq_parameter_set_id; + } + break; + + case BSPP_UNIT_UNCLASSIFIED: + case BSPP_UNIT_NON_PICTURE: + case BSPP_UNIT_UNSUPPORTED: + break; + + default: + VDEC_ASSERT("Unknown BSPP Unit Type" == NULL); + break; + } + + return result; +} + +static void bspp_hevc_initialiseparsing(struct bspp_parse_state *parse_state) +{ + /* Indicate that SEI info has not yet been attached to this picture. */ + parse_state->inter_pict_ctx->hevc_ctx.sei_info_attached_to_pic = 0; +} + +static void bspp_hevc_finaliseparsing(void *str_alloc, struct bspp_parse_state *parse_state) +{ + /* + * If SEI info has not yet been attached to the picture and + * there is anything to be attached. + */ + if (!parse_state->inter_pict_ctx->hevc_ctx.sei_info_attached_to_pic && + parse_state->inter_pict_ctx->hevc_ctx.sei_rawdata_list) { + /* attach the SEI list if there is a handle provided for that. */ + if (parse_state->inter_pict_ctx->hevc_ctx.hndl_pichdr_sei_rawdata_list) { + /* Attach the raw SEI list to the picture. */ + *parse_state->inter_pict_ctx->hevc_ctx.hndl_pichdr_sei_rawdata_list = + (void *)parse_state->inter_pict_ctx->hevc_ctx.sei_rawdata_list; + /* Reset the inter-picture data. */ + parse_state->inter_pict_ctx->hevc_ctx.hndl_pichdr_sei_rawdata_list = NULL; + } else { + /* Nowhere to attach the raw SEI list, so just free it. */ + bspp_freeraw_sei_datalist + (str_alloc, parse_state->inter_pict_ctx->hevc_ctx.sei_rawdata_list); + } + } + + /* Indicate that SEI info has been attached to the picture. */ + parse_state->inter_pict_ctx->hevc_ctx.sei_info_attached_to_pic = 1; + /* Reset the inter-picture SEI list. */ + parse_state->inter_pict_ctx->hevc_ctx.sei_rawdata_list = NULL; +} + +static enum bspp_error_type bspp_hevc_parse_vps(void *sr_ctx, struct bspp_hevc_vps *vps) +{ + unsigned int parse_err = BSPP_ERROR_NONE; + unsigned int i, j; + + VDEC_ASSERT(vps); + VDEC_ASSERT(sr_ctx); + + memset(vps, 0, sizeof(struct bspp_hevc_vps)); + + HEVC_SWSR_UN("vps_video_parameter_set_id", + (unsigned int *)&vps->vps_video_parameter_set_id, 4, sr_ctx); + HEVC_SWSR_UN("vps_reserved_three_2bits", + (unsigned int *)&vps->vps_reserved_three_2bits, 2, sr_ctx); + HEVC_SWSR_UN("vps_max_layers_minus1", + (unsigned int *)&vps->vps_max_layers_minus1, 6, sr_ctx); + HEVC_SWSR_UN("vps_max_sub_layers_minus1", + (unsigned int *)&vps->vps_max_sub_layers_minus1, 3, sr_ctx); + HEVC_RANGEUCHECK("vps_max_sub_layers_minus1", vps->vps_max_sub_layers_minus1, 0, + HEVC_MAX_NUM_SUBLAYERS - 1, &parse_err); + HEVC_SWSR_U1("vps_temporal_id_nesting_flag", + &vps->vps_temporal_id_nesting_flag, sr_ctx); + HEVC_SWSR_UN("vps_reserved_0xffff_16bits", + (unsigned int *)&vps->vps_reserved_0xffff_16bits, 16, sr_ctx); + + if (vps->vps_max_sub_layers_minus1 == 0) + HEVC_UCHECK("vps_temporal_id_nesting_flag", + vps->vps_temporal_id_nesting_flag, 1, &parse_err); + + parse_err |= bspp_hevc_parse_profiletierlevel(sr_ctx, &vps->profiletierlevel, + vps->vps_max_sub_layers_minus1); + + HEVC_SWSR_U1("vps_sub_layer_ordering_info_present_flag", + &vps->vps_sub_layer_ordering_info_present_flag, sr_ctx); + for (i = vps->vps_sub_layer_ordering_info_present_flag ? + 0 : vps->vps_max_sub_layers_minus1; + i <= vps->vps_max_sub_layers_minus1; ++i) { + HEVC_SWSR_UE("vps_max_dec_pic_buffering_minus1", + (unsigned int *)&vps->vps_max_dec_pic_buffering_minus1[i], sr_ctx); + HEVC_SWSR_UE("vps_max_num_reorder_pics", + (unsigned int *)&vps->vps_max_num_reorder_pics[i], sr_ctx); + HEVC_SWSR_UE("vps_max_latency_increase_plus1", + (unsigned int *)&vps->vps_max_latency_increase_plus1[i], sr_ctx); + } + + HEVC_SWSR_UN("vps_max_layer_id", (unsigned int *)&vps->vps_max_layer_id, 6, sr_ctx); + HEVC_SWSR_UE("vps_num_layer_sets_minus1", + (unsigned int *)&vps->vps_num_layer_sets_minus1, sr_ctx); + + for (i = 1; i <= vps->vps_num_layer_sets_minus1; ++i) { + for (j = 0; j <= vps->vps_max_layer_id; ++j) { + HEVC_SWSR_U1("layer_id_included_flag", + &vps->layer_id_included_flag[i][j], sr_ctx); + } + } + + HEVC_SWSR_U1("vps_timing_info_present_flag", &vps->vps_timing_info_present_flag, sr_ctx); + if (vps->vps_timing_info_present_flag) { + HEVC_SWSR_UN("vps_num_units_in_tick", + (unsigned int *)&vps->vps_num_units_in_tick, 32, sr_ctx); + HEVC_SWSR_UN("vps_time_scale", + (unsigned int *)&vps->vps_time_scale, 32, sr_ctx); + HEVC_SWSR_U1("vps_poc_proportional_to_timing_flag", + &vps->vps_poc_proportional_to_timing_flag, sr_ctx); + if (vps->vps_poc_proportional_to_timing_flag) + HEVC_SWSR_UE("vps_num_ticks_poc_diff_one_minus1", + (unsigned int *)&vps->vps_num_ticks_poc_diff_one_minus1, + sr_ctx); + + HEVC_SWSR_UE("vps_num_hrd_parameters", + (unsigned int *)&vps->vps_num_hrd_parameters, sr_ctx); + + /* consume hrd_parameters */ + for (i = 0; i < vps->vps_num_hrd_parameters; i++) { + unsigned short hrd_layer_set_idx; + unsigned char cprms_present_flag = 1; + struct bspp_hevc_hrd_parameters hrdparams; + + HEVC_SWSR_UE("hrd_layer_set_idx", + (unsigned int *)&hrd_layer_set_idx, sr_ctx); + if (i > 0) + HEVC_SWSR_U1("cprms_present_flag", &cprms_present_flag, sr_ctx); + + bspp_hevc_parsehrdparams(sr_ctx, &hrdparams, + cprms_present_flag, + vps->vps_max_sub_layers_minus1); + } + } + HEVC_SWSR_U1("vps_extension_flag", &vps->vps_extension_flag, sr_ctx); + + return (enum bspp_error_type)parse_err; +} + +static void bspp_hevc_sublayhrdparams(void *sr_ctx, + struct bspp_hevc_hrd_parameters *hrdparams, + unsigned char sublayer_id) +{ + unsigned char i; + unsigned char cpb_cnt = hrdparams->cpb_cnt_minus1[sublayer_id]; + struct bspp_hevc_sublayer_hrd_parameters *sublay_hrdparams = + &hrdparams->sublayhrdparams[sublayer_id]; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(hrdparams); + VDEC_ASSERT(cpb_cnt < HEVC_MAX_CPB_COUNT); + VDEC_ASSERT(sublayer_id < HEVC_MAX_NUM_SUBLAYERS); + + for (i = 0; i <= cpb_cnt; i++) { + HEVC_SWSR_UE("bit_rate_value_minus1", + (unsigned int *)&sublay_hrdparams->bit_rate_value_minus1[i], sr_ctx); + HEVC_SWSR_UE("cpb_size_value_minus1", + (unsigned int *)&sublay_hrdparams->cpb_size_value_minus1[i], sr_ctx); + if (hrdparams->sub_pic_hrd_params_present_flag) { + HEVC_SWSR_UE("cpb_size_du_value_minus1", + (unsigned int *) + &sublay_hrdparams->cpb_size_du_value_minus1[i], + sr_ctx); + HEVC_SWSR_UE("bit_rate_du_value_minus1", + (unsigned int *) + &sublay_hrdparams->bit_rate_du_value_minus1[i], + sr_ctx); + } + HEVC_SWSR_U1("cbr_flag", &sublay_hrdparams->cbr_flag[i], sr_ctx); + } +} + +static void bspp_hevc_parsehrdparams(void *sr_ctx, + struct bspp_hevc_hrd_parameters *hrdparams, + unsigned char common_infpresent, + unsigned char max_numsublayers_minus1) +{ + unsigned char i; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(hrdparams); + VDEC_ASSERT(max_numsublayers_minus1 < HEVC_MAX_NUM_SUBLAYERS); + + memset(hrdparams, 0, sizeof(struct bspp_hevc_hrd_parameters)); + + if (common_infpresent) { + HEVC_SWSR_U1("nal_hrd_parameters_present_flag", + &hrdparams->nal_hrd_parameters_present_flag, sr_ctx); + HEVC_SWSR_U1("vcl_hrd_parameters_present_flag", + &hrdparams->vcl_hrd_parameters_present_flag, sr_ctx); + if (hrdparams->nal_hrd_parameters_present_flag || + hrdparams->vcl_hrd_parameters_present_flag) { + HEVC_SWSR_U1("sub_pic_hrd_params_present_flag", + &hrdparams->sub_pic_hrd_params_present_flag, + sr_ctx); + if (hrdparams->sub_pic_hrd_params_present_flag) { + HEVC_SWSR_UN("tick_divisor_minus2", + (unsigned int *)&hrdparams->tick_divisor_minus2, + 8, sr_ctx); + HEVC_SWSR_UN + ("du_cpb_removal_delay_increment_length_minus1", + (unsigned int *) + &hrdparams->du_cpb_removal_delay_increment_length_minus1, + 5, sr_ctx); + HEVC_SWSR_U1("sub_pic_cpb_params_in_pic_timing_sei_flag", + &hrdparams->sub_pic_cpb_params_in_pic_timing_sei_flag, + sr_ctx); + HEVC_SWSR_UN("dpb_output_delay_du_length_minus1", + (unsigned int *) + &hrdparams->dpb_output_delay_du_length_minus1, + 5, sr_ctx); + } + HEVC_SWSR_UN("bit_rate_scale", + (unsigned int *)&hrdparams->bit_rate_scale, 4, sr_ctx); + HEVC_SWSR_UN("cpb_size_scale", + (unsigned int *)&hrdparams->cpb_size_scale, 4, sr_ctx); + if (hrdparams->sub_pic_hrd_params_present_flag) + HEVC_SWSR_UN("cpb_size_du_scale", + (unsigned int *)&hrdparams->cpb_size_du_scale, + 4, sr_ctx); + + HEVC_SWSR_UN("initial_cpb_removal_delay_length_minus1", + (unsigned int *) + &hrdparams->initial_cpb_removal_delay_length_minus1, + 5, sr_ctx); + HEVC_SWSR_UN("au_cpb_removal_delay_length_minus1", + (unsigned int *)&hrdparams->au_cpb_removal_delay_length_minus1, + 5, sr_ctx); + HEVC_SWSR_UN("dpb_output_delay_length_minus1", + (unsigned int *)&hrdparams->dpb_output_delay_length_minus1, + 5, sr_ctx); + } + } + for (i = 0; i <= max_numsublayers_minus1; i++) { + HEVC_SWSR_U1("fixed_pic_rate_general_flag", + &hrdparams->fixed_pic_rate_general_flag[i], sr_ctx); + hrdparams->fixed_pic_rate_within_cvs_flag[i] = + hrdparams->fixed_pic_rate_general_flag[i]; + if (!hrdparams->fixed_pic_rate_general_flag[i]) + HEVC_SWSR_U1("fixed_pic_rate_within_cvs_flag", + &hrdparams->fixed_pic_rate_within_cvs_flag[i], + sr_ctx); + + if (hrdparams->fixed_pic_rate_within_cvs_flag[i]) + HEVC_SWSR_UE("elemental_duration_in_tc_minus1", + (unsigned int *)&hrdparams->elemental_duration_in_tc_minus1[i], + sr_ctx); + else + HEVC_SWSR_U1("low_delay_hrd_flag", + &hrdparams->low_delay_hrd_flag[i], sr_ctx); + + if (!hrdparams->low_delay_hrd_flag[i]) + HEVC_SWSR_UE("cpb_cnt_minus1", + (unsigned int *)&hrdparams->cpb_cnt_minus1[i], sr_ctx); + + if (hrdparams->nal_hrd_parameters_present_flag) + bspp_hevc_sublayhrdparams(sr_ctx, hrdparams, i); + + if (hrdparams->vcl_hrd_parameters_present_flag) + bspp_hevc_sublayhrdparams(sr_ctx, hrdparams, i); + } +} + +static enum bspp_error_type bspp_hevc_parsevui_parameters + (void *sr_ctx, + struct bspp_hevc_vui_params *vui_params, + unsigned char sps_max_sub_layers_minus1) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(vui_params); + + memset(vui_params, 0, sizeof(struct bspp_hevc_vui_params)); + + HEVC_SWSR_U1("aspect_ratio_info_present_flag", + &vui_params->aspect_ratio_info_present_flag, sr_ctx); + if (vui_params->aspect_ratio_info_present_flag) { + HEVC_SWSR_UN("aspect_ratio_idc", + (unsigned int *)&vui_params->aspect_ratio_idc, 8, sr_ctx); + if (vui_params->aspect_ratio_idc == HEVC_EXTENDED_SAR) { + HEVC_SWSR_UN("sar_width", + (unsigned int *)&vui_params->sar_width, 16, sr_ctx); + HEVC_SWSR_UN("sar_height", + (unsigned int *)&vui_params->sar_height, 16, sr_ctx); + } + } + HEVC_SWSR_U1("overscan_info_present_flag", + &vui_params->overscan_info_present_flag, sr_ctx); + + if (vui_params->overscan_info_present_flag) + HEVC_SWSR_U1("overscan_appropriate_flag", + &vui_params->overscan_appropriate_flag, sr_ctx); + + HEVC_SWSR_U1("video_signal_type_present_flag", + &vui_params->video_signal_type_present_flag, sr_ctx); + + if (vui_params->video_signal_type_present_flag) { + HEVC_SWSR_UN("video_format", + (unsigned int *)&vui_params->video_format, 3, sr_ctx); + HEVC_SWSR_U1("video_full_range_flag", + &vui_params->video_full_range_flag, sr_ctx); + HEVC_SWSR_U1("colour_description_present_flag", + &vui_params->colour_description_present_flag, + sr_ctx); + if (vui_params->colour_description_present_flag) { + HEVC_SWSR_UN("colour_primaries", + (unsigned int *)&vui_params->colour_primaries, 8, sr_ctx); + HEVC_SWSR_UN("transfer_characteristics", + (unsigned int *)&vui_params->transfer_characteristics, + 8, sr_ctx); + HEVC_SWSR_UN("matrix_coeffs", + (unsigned int *)&vui_params->matrix_coeffs, 8, sr_ctx); + } + } + + HEVC_SWSR_U1("chroma_loc_info_present_flag", + &vui_params->chroma_loc_info_present_flag, sr_ctx); + if (vui_params->chroma_loc_info_present_flag) { + HEVC_SWSR_UE("chroma_sample_loc_type_top_field", + (unsigned int *)&vui_params->chroma_sample_loc_type_top_field, + sr_ctx); + HEVC_RANGEUCHECK("chroma_sample_loc_type_top_field", + vui_params->chroma_sample_loc_type_top_field, + 0, 5, &parse_err); + HEVC_SWSR_UE("chroma_sample_loc_type_bottom_field", + (unsigned int *)&vui_params->chroma_sample_loc_type_bottom_field, + sr_ctx); + HEVC_RANGEUCHECK("chroma_sample_loc_type_bottom_field", + vui_params->chroma_sample_loc_type_bottom_field, + 0, 5, &parse_err); + } + HEVC_SWSR_U1("neutral_chroma_indication_flag", + &vui_params->neutral_chroma_indication_flag, sr_ctx); + HEVC_SWSR_U1("field_seq_flag", + &vui_params->field_seq_flag, sr_ctx); + HEVC_SWSR_U1("frame_field_info_present_flag", + &vui_params->frame_field_info_present_flag, sr_ctx); + HEVC_SWSR_U1("default_display_window_flag", + &vui_params->default_display_window_flag, sr_ctx); + if (vui_params->default_display_window_flag) { + HEVC_SWSR_UE("def_disp_win_left_offset", + (unsigned int *)&vui_params->def_disp_win_left_offset, sr_ctx); + HEVC_SWSR_UE("def_disp_win_right_offset", + (unsigned int *)&vui_params->def_disp_win_right_offset, sr_ctx); + HEVC_SWSR_UE("def_disp_win_top_offset", + (unsigned int *)&vui_params->def_disp_win_top_offset, sr_ctx); + HEVC_SWSR_UE("def_disp_win_bottom_offset", + (unsigned int *)&vui_params->def_disp_win_bottom_offset, sr_ctx); + } + HEVC_SWSR_U1("vui_timing_info_present_flag", + &vui_params->vui_timing_info_present_flag, sr_ctx); + if (vui_params->vui_timing_info_present_flag) { + HEVC_SWSR_UN("vui_num_units_in_tick", + (unsigned int *)&vui_params->vui_num_units_in_tick, 32, sr_ctx); + HEVC_SWSR_UN("vui_time_scale", + (unsigned int *)&vui_params->vui_time_scale, 32, sr_ctx); + HEVC_SWSR_U1("vui_poc_proportional_to_timing_flag", + &vui_params->vui_poc_proportional_to_timing_flag, + sr_ctx); + if (vui_params->vui_poc_proportional_to_timing_flag) + HEVC_SWSR_UE("vui_num_ticks_poc_diff_one_minus1", + (unsigned int *)&vui_params->vui_num_ticks_poc_diff_one_minus1, + sr_ctx); + + HEVC_SWSR_U1("vui_hrd_parameters_present_flag", + &vui_params->vui_hrd_parameters_present_flag, + sr_ctx); + if (vui_params->vui_hrd_parameters_present_flag) + bspp_hevc_parsehrdparams(sr_ctx, &vui_params->vui_hrd_params, + 1, sps_max_sub_layers_minus1); + } + HEVC_SWSR_U1("bitstream_restriction_flag", + &vui_params->bitstream_restriction_flag, sr_ctx); + + if (vui_params->bitstream_restriction_flag) { + HEVC_SWSR_U1("tiles_fixed_structure_flag", + &vui_params->tiles_fixed_structure_flag, sr_ctx); + HEVC_SWSR_U1("motion_vectors_over_pic_boundaries_flag", + &vui_params->motion_vectors_over_pic_boundaries_flag, + sr_ctx); + HEVC_SWSR_U1("restricted_ref_pic_lists_flag", + &vui_params->restricted_ref_pic_lists_flag, sr_ctx); + + HEVC_SWSR_UE("min_spatial_segmentation_idc", + (unsigned int *)&vui_params->min_spatial_segmentation_idc, sr_ctx); + HEVC_RANGEUCHECK("min_spatial_segmentation_idc", + vui_params->min_spatial_segmentation_idc, + 0, 4095, &parse_err); + + HEVC_SWSR_UE("max_bytes_per_pic_denom", + (unsigned int *)&vui_params->max_bytes_per_pic_denom, sr_ctx); + HEVC_RANGEUCHECK("max_bytes_per_pic_denom", vui_params->max_bytes_per_pic_denom, + 0, 16, &parse_err); + + HEVC_SWSR_UE("max_bits_per_min_cu_denom", + (unsigned int *)&vui_params->max_bits_per_min_cu_denom, sr_ctx); + HEVC_RANGEUCHECK("max_bits_per_min_cu_denom", vui_params->max_bits_per_min_cu_denom, + 0, 16, &parse_err); + + HEVC_SWSR_UE("log2_max_mv_length_horizontal", + (unsigned int *)&vui_params->log2_max_mv_length_horizontal, sr_ctx); + HEVC_RANGEUCHECK("log2_max_mv_length_horizontal", + vui_params->log2_max_mv_length_horizontal, + 0, 16, &parse_err); + + HEVC_SWSR_UE("log2_max_mv_length_vertical", + (unsigned int *)&vui_params->log2_max_mv_length_vertical, sr_ctx); + HEVC_RANGEUCHECK("log2_max_mv_length_vertical", + vui_params->log2_max_mv_length_vertical, + 0, 15, &parse_err); + } + + return parse_err; +} + +static enum bspp_error_type bspp_hevc_parse_spsrange_extensions + (void *sr_ctx, + struct bspp_hevc_sps_range_exts *range_exts) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(range_exts); + + memset(range_exts, 0, sizeof(struct bspp_hevc_sps_range_exts)); + + HEVC_SWSR_U1("transform_skip_rotation_enabled_flag", + &range_exts->transform_skip_rotation_enabled_flag, sr_ctx); + HEVC_SWSR_U1("transform_skip_context_enabled_flag", + &range_exts->transform_skip_context_enabled_flag, sr_ctx); + HEVC_SWSR_U1("implicit_rdpcm_enabled_flag", + &range_exts->implicit_rdpcm_enabled_flag, sr_ctx); + HEVC_SWSR_U1("explicit_rdpcm_enabled_flag", + &range_exts->explicit_rdpcm_enabled_flag, sr_ctx); + HEVC_SWSR_U1("extended_precision_processing_flag", + &range_exts->extended_precision_processing_flag, sr_ctx); + HEVC_UCHECK("extended_precision_processing_flag", + range_exts->extended_precision_processing_flag, + 0, &parse_err); + HEVC_SWSR_U1("intra_smoothing_disabled_flag", + &range_exts->intra_smoothing_disabled_flag, sr_ctx); + HEVC_SWSR_U1("high_precision_offsets_enabled_flag", + &range_exts->high_precision_offsets_enabled_flag, sr_ctx); + HEVC_SWSR_U1("persistent_rice_adaptation_enabled_flag", + &range_exts->persistent_rice_adaptation_enabled_flag, + sr_ctx); + HEVC_SWSR_U1("cabac_bypass_alignment_enabled_flag", + &range_exts->cabac_bypass_alignment_enabled_flag, sr_ctx); + + return parse_err; +} + +static unsigned char +bspp_hevc_checksps_range_extensions(struct bspp_hevc_sps_range_exts *range_exts) +{ + VDEC_ASSERT(range_exts); + + if (range_exts->transform_skip_rotation_enabled_flag || + range_exts->transform_skip_context_enabled_flag || + range_exts->implicit_rdpcm_enabled_flag || + range_exts->explicit_rdpcm_enabled_flag || + range_exts->extended_precision_processing_flag || + range_exts->intra_smoothing_disabled_flag || + range_exts->persistent_rice_adaptation_enabled_flag || + range_exts->cabac_bypass_alignment_enabled_flag) + return 1; + /* + * Note: high_precision_offsets_enabled_flag is supported even + * if hw capabilities (bHevcRangeExt is not set) + */ + return 0; +} + +static enum bspp_error_type bspp_hevc_parsesps(void *sr_ctx, + void *str_res, + struct bspp_hevc_sps *sps) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + unsigned char i; + unsigned int min_cblog2_size_y; + + if (!sr_ctx || !sps) { + VDEC_ASSERT(0); + return BSPP_ERROR_INVALID_VALUE; + } + + memset(sps, 0, sizeof(struct bspp_hevc_sps)); + + HEVC_SWSR_UN("sps_video_parameter_set_id", + (unsigned int *)&sps->sps_video_parameter_set_id, 4, sr_ctx); + HEVC_SWSR_UN("sps_max_sub_layers_minus1", + (unsigned int *)&sps->sps_max_sub_layers_minus1, 3, sr_ctx); + HEVC_RANGEUCHECK("sps_max_sub_layers_minus1", sps->sps_max_sub_layers_minus1, 0, + HEVC_MAX_NUM_SUBLAYERS - 1, &parse_err); + HEVC_SWSR_U1("sps_temporal_id_nesting_flag", + &sps->sps_temporal_id_nesting_flag, sr_ctx); + + if (sps->sps_max_sub_layers_minus1 == 0) + HEVC_UCHECK("sps_temporal_id_nesting_flag", + sps->sps_temporal_id_nesting_flag, 1, &parse_err); + + parse_err |= bspp_hevc_parse_profiletierlevel + (sr_ctx, &sps->profile_tier_level, + sps->sps_max_sub_layers_minus1); + + HEVC_SWSR_UE("sps_seq_parameter_set_id", + (unsigned int *)&sps->sps_seq_parameter_set_id, sr_ctx); + HEVC_RANGEUCHECK("sps_seq_parameter_set_id", sps->sps_seq_parameter_set_id, 0, + HEVC_MAX_SPS_COUNT - 1, &parse_err); + + HEVC_SWSR_UE("chroma_format_idc", (unsigned int *)&sps->chroma_format_idc, sr_ctx); + HEVC_RANGEUCHECK("chroma_format_idc", sps->chroma_format_idc, 0, 3, &parse_err); + + if (sps->chroma_format_idc == 3) + HEVC_SWSR_U1("separate_colour_plane_flag", + &sps->separate_colour_plane_flag, sr_ctx); + + HEVC_SWSR_UE("pic_width_in_luma_samples", + (unsigned int *)&sps->pic_width_in_luma_samples, sr_ctx); + HEVC_SWSR_UE("pic_height_in_luma_samples", + (unsigned int *)&sps->pic_height_in_luma_samples, sr_ctx); + + HEVC_SWSR_U1("conformance_window_flag", &sps->conformance_window_flag, sr_ctx); + + if (sps->pic_width_in_luma_samples == 0 || + sps->pic_height_in_luma_samples == 0) { + pr_warn("Invalid video dimensions (%u, %u)", + sps->pic_width_in_luma_samples, + sps->pic_height_in_luma_samples); + parse_err |= BSPP_ERROR_UNRECOVERABLE; + } + + if (sps->conformance_window_flag) { + HEVC_SWSR_UE("conf_win_left_offset", + (unsigned int *)&sps->conf_win_left_offset, sr_ctx); + HEVC_SWSR_UE("conf_win_right_offset", + (unsigned int *)&sps->conf_win_right_offset, sr_ctx); + HEVC_SWSR_UE("conf_win_top_offset", + (unsigned int *)&sps->conf_win_top_offset, sr_ctx); + HEVC_SWSR_UE("conf_win_bottom_offset", + (unsigned int *)&sps->conf_win_bottom_offset, sr_ctx); + } + + HEVC_SWSR_UE("bit_depth_luma_minus8", + (unsigned int *)&sps->bit_depth_luma_minus8, sr_ctx); + HEVC_RANGEUCHECK("bit_depth_luma_minus8", + sps->bit_depth_luma_minus8, 0, 6, &parse_err); + HEVC_SWSR_UE("bit_depth_chroma_minus8", + (unsigned int *)&sps->bit_depth_chroma_minus8, sr_ctx); + HEVC_RANGEUCHECK("bit_depth_chroma_minus8", sps->bit_depth_chroma_minus8, + 0, 6, &parse_err); + + HEVC_SWSR_UE("log2_max_pic_order_cnt_lsb_minus4", + (unsigned int *)&sps->log2_max_pic_order_cnt_lsb_minus4, sr_ctx); + HEVC_RANGEUCHECK("log2_max_pic_order_cnt_lsb_minus4", + sps->log2_max_pic_order_cnt_lsb_minus4, + 0, 12, &parse_err); + + HEVC_SWSR_U1("sps_sub_layer_ordering_info_present_flag", + &sps->sps_sub_layer_ordering_info_present_flag, sr_ctx); + for (i = (sps->sps_sub_layer_ordering_info_present_flag ? + 0 : sps->sps_max_sub_layers_minus1); + i <= sps->sps_max_sub_layers_minus1; ++i) { + HEVC_SWSR_UE("sps_max_dec_pic_buffering_minus1", + (unsigned int *)&sps->sps_max_dec_pic_buffering_minus1[i], sr_ctx); + HEVC_SWSR_UE("sps_max_num_reorder_pics", + (unsigned int *)&sps->sps_max_num_reorder_pics[i], sr_ctx); + HEVC_SWSR_UE("sps_max_latency_increase_plus1", + (unsigned int *)&sps->sps_max_latency_increase_plus1[i], sr_ctx); + } + + HEVC_SWSR_UE("log2_min_luma_coding_block_size_minus3", + (unsigned int *)&sps->log2_min_luma_coding_block_size_minus3, sr_ctx); + HEVC_SWSR_UE("log2_diff_max_min_luma_coding_block_size", + (unsigned int *)&sps->log2_diff_max_min_luma_coding_block_size, sr_ctx); + HEVC_SWSR_UE("log2_min_transform_block_size_minus2", + (unsigned int *)&sps->log2_min_transform_block_size_minus2, sr_ctx); + HEVC_SWSR_UE("log2_diff_max_min_transform_block_size", + (unsigned int *)&sps->log2_diff_max_min_transform_block_size, sr_ctx); + HEVC_SWSR_UE("max_transform_hierarchy_depth_inter", + (unsigned int *)&sps->max_transform_hierarchy_depth_inter, sr_ctx); + HEVC_SWSR_UE("max_transform_hierarchy_depth_intra", + (unsigned int *)&sps->max_transform_hierarchy_depth_intra, sr_ctx); + + HEVC_SWSR_U1("scaling_list_enabled_flag", &sps->scaling_list_enabled_flag, sr_ctx); + + if (sps->scaling_list_enabled_flag) { + HEVC_SWSR_U1("sps_scaling_list_data_present_flag", + &sps->sps_scaling_list_data_present_flag, sr_ctx); + if (sps->sps_scaling_list_data_present_flag) + parse_err |= bspp_hevc_parse_scalinglistdata(sr_ctx, + &sps->scalinglist_data); + else + bspp_hevc_usedefault_scalinglists(&sps->scalinglist_data); + } + + HEVC_SWSR_U1("amp_enabled_flag", &sps->amp_enabled_flag, sr_ctx); + HEVC_SWSR_U1("sample_adaptive_offset_enabled_flag", + &sps->sample_adaptive_offset_enabled_flag, sr_ctx); + HEVC_SWSR_U1("pcm_enabled_flag", &sps->pcm_enabled_flag, sr_ctx); + + if (sps->pcm_enabled_flag) { + HEVC_SWSR_UN("pcm_sample_bit_depth_luma_minus1", + (unsigned int *)&sps->pcm_sample_bit_depth_luma_minus1, + 4, sr_ctx); + HEVC_SWSR_UN("pcm_sample_bit_depth_chroma_minus1", + (unsigned int *)&sps->pcm_sample_bit_depth_chroma_minus1, + 4, sr_ctx); + HEVC_SWSR_UE("log2_min_pcm_luma_coding_block_size_minus3", + (unsigned int *)&sps->log2_min_pcm_luma_coding_block_size_minus3, + sr_ctx); + HEVC_SWSR_UE("log2_diff_max_min_pcm_luma_coding_block_size", + (unsigned int *)&sps->log2_diff_max_min_pcm_luma_coding_block_size, + sr_ctx); + HEVC_SWSR_U1("pcm_loop_filter_disabled_flag", + &sps->pcm_loop_filter_disabled_flag, sr_ctx); + } else { + sps->pcm_sample_bit_depth_luma_minus1 = 7; + sps->pcm_sample_bit_depth_chroma_minus1 = 7; + sps->log2_min_pcm_luma_coding_block_size_minus3 = 0; + sps->log2_diff_max_min_pcm_luma_coding_block_size = 2; + } + + HEVC_SWSR_UE("num_short_term_ref_pic_sets", + (unsigned int *)&sps->num_short_term_ref_pic_sets, sr_ctx); + HEVC_RANGEUCHECK("num_short_term_ref_pic_sets", sps->num_short_term_ref_pic_sets, 0, + HEVC_MAX_NUM_ST_REF_PIC_SETS - 1, &parse_err); + + for (i = 0; i < sps->num_short_term_ref_pic_sets; ++i) { + parse_err |= bspp_hevc_parse_shortterm_refpicset(sr_ctx, + sps->rps_list, + i, + 0); + } + + HEVC_SWSR_U1("long_term_ref_pics_present_flag", + &sps->long_term_ref_pics_present_flag, sr_ctx); + if (sps->long_term_ref_pics_present_flag) { + HEVC_SWSR_UE("num_long_term_ref_pics_sps", + (unsigned int *)&sps->num_long_term_ref_pics_sps, sr_ctx); + HEVC_RANGEUCHECK("num_long_term_ref_pics_sps", + sps->num_long_term_ref_pics_sps, 0, + HEVC_MAX_NUM_LT_REF_PICS, &parse_err); + for (i = 0; i < sps->num_long_term_ref_pics_sps; ++i) { + HEVC_SWSR_UN("lt_ref_pic_poc_lsb_sps", + (unsigned int *)&sps->lt_ref_pic_poc_lsb_sps[i], + sps->log2_max_pic_order_cnt_lsb_minus4 + 4, + sr_ctx); + HEVC_SWSR_U1("used_by_curr_pic_lt_sps_flag", + &sps->used_by_curr_pic_lt_sps_flag[i], + sr_ctx); + } + } + + HEVC_SWSR_U1("sps_temporal_mvp_enabled_flag", &sps->sps_temporal_mvp_enabled_flag, sr_ctx); + HEVC_SWSR_U1("strong_intra_smoothing_enabled_flag", + &sps->strong_intra_smoothing_enabled_flag, sr_ctx); + HEVC_SWSR_U1("vui_parameters_present_flag", &sps->vui_parameters_present_flag, sr_ctx); + + if (sps->vui_parameters_present_flag) + bspp_hevc_parsevui_parameters(sr_ctx, &sps->vui_params, + sps->sps_max_sub_layers_minus1); + + HEVC_SWSR_U1("sps_extension_present_flag", &sps->sps_extension_present_flag, sr_ctx); + if (sps->sps_extension_present_flag && + bspp_hevc_range_extensions_is_enabled(&sps->profile_tier_level)) { + HEVC_SWSR_U1("sps_range_extensions_flag", &sps->sps_range_extensions_flag, sr_ctx); + + HEVC_SWSR_UN("sps_extension_7bits", (unsigned int *)&sps->sps_extension_7bits, 7, + sr_ctx); + /* + * ignore extension data. Although we inform + * if some non-zero data was found + */ + HEVC_UCHECK("sps_extension_7bits", sps->sps_extension_7bits, 0, &parse_err); + /* + * TODO ?: the newest HEVC spec (10/2014) splits + * "sps_extension_7bits" to * sps_multilayer_extension_flag (1) + * sps_extension_6bits (6) + */ + if (sps->sps_range_extensions_flag) + parse_err |= bspp_hevc_parse_spsrange_extensions + (sr_ctx, &sps->range_exts); + } + /* + * calculate "derived" variables needed further in the parsing process + * (of other headers) and save them for later use + */ + sps->sub_width_c = 1; + sps->sub_height_c = 1; + if (sps->chroma_format_idc == 2) { + sps->sub_width_c = 2; + } else if (sps->chroma_format_idc == 1) { + sps->sub_width_c = 2; + sps->sub_height_c = 2; + } + + min_cblog2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3; + sps->ctb_log2size_y = + min_cblog2_size_y + sps->log2_diff_max_min_luma_coding_block_size; + sps->ctb_size_y = 1 << sps->ctb_log2size_y; + + if (sps->ctb_size_y > 0) { + /* use integer division with rounding up */ + sps->pic_width_in_ctbs_y = + (sps->pic_width_in_luma_samples + sps->ctb_size_y - 1) + / sps->ctb_size_y; + sps->pic_height_in_ctbs_y = + (sps->pic_height_in_luma_samples + sps->ctb_size_y - 1) + / sps->ctb_size_y; + } else { + parse_err |= BSPP_ERROR_INVALID_VALUE; + } + + sps->pic_size_in_ctbs_y = + sps->pic_width_in_ctbs_y * sps->pic_height_in_ctbs_y; + + sps->max_pic_order_cnt_lsb = + 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + + for (i = 0; i <= sps->sps_max_sub_layers_minus1; ++i) { + sps->sps_max_latency_pictures[i] = + sps->sps_max_num_reorder_pics[i] + + sps->sps_max_latency_increase_plus1[i] - 1; + } + + BSPP_HEVC_SYNTAX("ctb_size_y: %u", sps->ctb_size_y); + BSPP_HEVC_SYNTAX("pic_width_in_ctbs_y: %u", sps->pic_width_in_ctbs_y); + BSPP_HEVC_SYNTAX("pic_height_in_ctbs_y: %u", sps->pic_height_in_ctbs_y); + BSPP_HEVC_SYNTAX("pic_size_in_ctbs_y: %u", sps->pic_size_in_ctbs_y); + + return parse_err; +} + +static int bspp_hevc_release_sequhdrinfo(void *str_alloc, void *secure_spsinfo) +{ + struct bspp_hevc_sps *hevc_sps = (struct bspp_hevc_sps *)secure_spsinfo; + + if (!hevc_sps) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Release the raw VIU data. */ + bspp_streamrelese_rawbstrdataplain(str_alloc, (void *)hevc_sps->vui_raw_data); + return 0; +} + +static int bspp_hevc_releasedata(void *str_alloc, enum bspp_unit_type data_type, + void *data_handle) +{ + int result = 0; + + if (!data_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + switch (data_type) { + case BSPP_UNIT_SEQUENCE: + result = bspp_hevc_release_sequhdrinfo(str_alloc, data_handle); + break; + default: + break; + } + + return result; +} + +static int bspp_hevc_reset_ppsinfo(void *secure_ppsinfo) +{ + struct bspp_hevc_pps *hevc_pps = NULL; + + if (!secure_ppsinfo) + return IMG_ERROR_INVALID_PARAMETERS; + + hevc_pps = (struct bspp_hevc_pps *)secure_ppsinfo; + + memset(hevc_pps, 0, sizeof(*hevc_pps)); + + return 0; +} + +static int bspp_hevc_resetdata(enum bspp_unit_type data_type, void *data_handle) +{ + int result = 0; + + switch (data_type) { + case BSPP_UNIT_PPS: + result = bspp_hevc_reset_ppsinfo(data_handle); + break; + default: + break; + } + return result; +} + +static enum bspp_error_type bspp_hevc_parsepps_range_extensions + (void *sr_ctx, + struct bspp_hevc_pps_range_exts *range_exts, + unsigned char transform_skip_enabled_flag, + unsigned char log2_diff_max_min_luma_coding_block_size) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(range_exts); + + memset(range_exts, 0, sizeof(struct bspp_hevc_pps_range_exts)); + + if (transform_skip_enabled_flag) + HEVC_SWSR_UE("log2_max_transform_skip_block_size_minus2", + (unsigned int *)&range_exts->log2_max_transform_skip_block_size_minus2, + sr_ctx); + + HEVC_SWSR_U1("cross_component_prediction_enabled_flag", + &range_exts->cross_component_prediction_enabled_flag, + sr_ctx); + HEVC_UCHECK("cross_component_prediction_enabled_flag", + range_exts->cross_component_prediction_enabled_flag, 0, + &parse_err); + + HEVC_SWSR_U1("chroma_qp_offset_list_enabled_flag", + &range_exts->chroma_qp_offset_list_enabled_flag, sr_ctx); + + if (range_exts->chroma_qp_offset_list_enabled_flag) { + unsigned char i; + + HEVC_SWSR_UE("diff_cu_chroma_qp_offset_depth", + (unsigned int *)&range_exts->diff_cu_chroma_qp_offset_depth, + sr_ctx); + HEVC_RANGEUCHECK("diff_cu_chroma_qp_offset_depth", + range_exts->diff_cu_chroma_qp_offset_depth, 0, + log2_diff_max_min_luma_coding_block_size, + &parse_err); + + HEVC_SWSR_UE("chroma_qp_offset_list_len_minus1", + (unsigned int *)&range_exts->chroma_qp_offset_list_len_minus1, + sr_ctx); + HEVC_RANGEUCHECK("chroma_qp_offset_list_len_minus1", + range_exts->chroma_qp_offset_list_len_minus1, + 0, HEVC_MAX_CHROMA_QP - 1, &parse_err); + for (i = 0; i <= range_exts->chroma_qp_offset_list_len_minus1; i++) { + HEVC_SWSR_SE("cb_qp_offset_list", + (int *)&range_exts->cb_qp_offset_list[i], sr_ctx); + HEVC_RANGESCHECK("cb_qp_offset_list", range_exts->cb_qp_offset_list[i], + -12, 12, &parse_err); + HEVC_SWSR_SE("cr_qp_offset_list", + (int *)&range_exts->cr_qp_offset_list[i], sr_ctx); + HEVC_RANGESCHECK("cr_qp_offset_list", range_exts->cr_qp_offset_list[i], + -12, 12, &parse_err); + } + } + HEVC_SWSR_UE("log2_sao_offset_scale_luma", + (unsigned int *)&range_exts->log2_sao_offset_scale_luma, sr_ctx); + HEVC_UCHECK("log2_sao_offset_scale_luma", + range_exts->log2_sao_offset_scale_luma, 0, &parse_err); + HEVC_SWSR_UE("log2_sao_offset_scale_chroma", + (unsigned int *)&range_exts->log2_sao_offset_scale_chroma, sr_ctx); + HEVC_UCHECK("log2_sao_offset_scale_chroma", + range_exts->log2_sao_offset_scale_chroma, 0, &parse_err); + + return parse_err; +} + +static unsigned char bspp_hevc_checkppsrangeextensions + (struct bspp_hevc_pps_range_exts *range_exts) +{ + VDEC_ASSERT(range_exts); + + if (range_exts->log2_max_transform_skip_block_size_minus2 || + range_exts->cross_component_prediction_enabled_flag) + return 1; + /* + * Note: chroma_qp_offset_list_enabled_flag is supported even + * if hw capabilities (bHevcRangeExt is not set) + */ + return 0; +} + +static enum bspp_error_type bspp_hevc_parsepps + (void *sr_ctx, void *str_res, + struct bspp_hevc_pps *pps) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + struct bspp_sequence_hdr_info *spsinfo = NULL; + struct bspp_hevc_sps *sps = NULL; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(pps); + memset(pps, 0, sizeof(struct bspp_hevc_pps)); + + HEVC_SWSR_UE("pps_pic_parameter_set_id", + (unsigned int *)&pps->pps_pic_parameter_set_id, sr_ctx); + HEVC_RANGEUCHECK("pps_pic_parameter_set_id", pps->pps_pic_parameter_set_id, 0, + HEVC_MAX_PPS_COUNT - 1, &parse_err); + HEVC_SWSR_UE("pps_seq_parameter_set_id", + (unsigned int *)&pps->pps_seq_parameter_set_id, sr_ctx); + HEVC_RANGEUCHECK("pps_seq_parameter_set_id", pps->pps_seq_parameter_set_id, 0, + HEVC_MAX_SPS_COUNT - 1, &parse_err); + + spsinfo = bspp_get_sequ_hdr(str_res, pps->pps_seq_parameter_set_id); + if (!spsinfo) { + parse_err |= BSPP_ERROR_NO_SEQUENCE_HDR; + } else { + sps = (struct bspp_hevc_sps *)spsinfo->secure_sequence_info; + VDEC_ASSERT(sps->sps_seq_parameter_set_id == + pps->pps_seq_parameter_set_id); + } + + HEVC_SWSR_U1("dependent_slice_segments_enabled_flag", + &pps->dependent_slice_segments_enabled_flag, sr_ctx); + HEVC_SWSR_U1("output_flag_present_flag", + &pps->output_flag_present_flag, sr_ctx); + HEVC_SWSR_UN("num_extra_slice_header_bits", + (unsigned int *)&pps->num_extra_slice_header_bits, 3, sr_ctx); + HEVC_SWSR_U1("sign_data_hiding_enabled_flag", &pps->sign_data_hiding_enabled_flag, sr_ctx); + HEVC_SWSR_U1("cabac_init_present_flag", &pps->cabac_init_present_flag, sr_ctx); + HEVC_SWSR_UE("num_ref_idx_l0_default_active_minus1", + (unsigned int *)&pps->num_ref_idx_l0_default_active_minus1, sr_ctx); + HEVC_RANGEUCHECK("num_ref_idx_l0_default_active_minus1", + pps->num_ref_idx_l0_default_active_minus1, 0, 14, &parse_err); + HEVC_SWSR_UE("num_ref_idx_l1_default_active_minus1", + (unsigned int *)&pps->num_ref_idx_l1_default_active_minus1, sr_ctx); + HEVC_RANGEUCHECK("num_ref_idx_l1_default_active_minus1", + pps->num_ref_idx_l1_default_active_minus1, 0, 14, &parse_err); + HEVC_SWSR_SE("init_qp_minus26", (int *)&pps->init_qp_minus26, sr_ctx); + + if (sps) + HEVC_RANGESCHECK("init_qp_minus26", pps->init_qp_minus26, + -(26 + (6 * sps->bit_depth_luma_minus8)), 25, &parse_err); + + HEVC_SWSR_U1("constrained_intra_pred_flag", &pps->constrained_intra_pred_flag, sr_ctx); + HEVC_SWSR_U1("transform_skip_enabled_flag", &pps->transform_skip_enabled_flag, sr_ctx); + + HEVC_SWSR_U1("cu_qp_delta_enabled_flag", &pps->cu_qp_delta_enabled_flag, sr_ctx); + + if (pps->cu_qp_delta_enabled_flag) + HEVC_SWSR_UE("diff_cu_qp_delta_depth", + (unsigned int *)&pps->diff_cu_qp_delta_depth, sr_ctx); + + HEVC_SWSR_SE("pps_cb_qp_offset", (int *)&pps->pps_cb_qp_offset, sr_ctx); + HEVC_RANGESCHECK("pps_cb_qp_offset", pps->pps_cb_qp_offset, -12, 12, &parse_err); + HEVC_SWSR_SE("pps_cr_qp_offset", (int *)&pps->pps_cr_qp_offset, sr_ctx); + HEVC_RANGESCHECK("pps_cr_qp_offset", pps->pps_cr_qp_offset, -12, 12, &parse_err); + HEVC_SWSR_U1("pps_slice_chroma_qp_offsets_present_flag", + &pps->pps_slice_chroma_qp_offsets_present_flag, sr_ctx); + HEVC_SWSR_U1("weighted_pred_flag", &pps->weighted_pred_flag, sr_ctx); + HEVC_SWSR_U1("weighted_bipred_flag", &pps->weighted_bipred_flag, sr_ctx); + HEVC_SWSR_U1("transquant_bypass_enabled_flag", + &pps->transquant_bypass_enabled_flag, sr_ctx); + HEVC_SWSR_U1("tiles_enabled_flag", &pps->tiles_enabled_flag, sr_ctx); + HEVC_SWSR_U1("entropy_coding_sync_enabled_flag", + &pps->entropy_coding_sync_enabled_flag, sr_ctx); + + if (pps->tiles_enabled_flag) { + HEVC_SWSR_UE("num_tile_columns_minus1", + (unsigned int *)&pps->num_tile_columns_minus1, sr_ctx); + HEVC_RANGEUCHECK("num_tile_columns_minus1", pps->num_tile_columns_minus1, 0, + HEVC_MAX_TILE_COLS - 1, &parse_err); + + if (pps->num_tile_columns_minus1 > HEVC_MAX_TILE_COLS) + pps->num_tile_columns_minus1 = HEVC_MAX_TILE_COLS; + + HEVC_SWSR_UE("num_tile_rows_minus1", (unsigned int *)&pps->num_tile_rows_minus1, + sr_ctx); + HEVC_RANGEUCHECK("num_tile_rows_minus1", pps->num_tile_rows_minus1, 0, + HEVC_MAX_TILE_ROWS - 1, &parse_err); + + if (pps->num_tile_rows_minus1 > HEVC_MAX_TILE_ROWS) + pps->num_tile_rows_minus1 = HEVC_MAX_TILE_ROWS; + + HEVC_SWSR_U1("uniform_spacing_flag", &pps->uniform_spacing_flag, sr_ctx); + + if (!pps->uniform_spacing_flag) { + unsigned char i = 0; + + for (i = 0; i < pps->num_tile_columns_minus1; ++i) + HEVC_SWSR_UE("column_width_minus1", + (unsigned int *)&pps->column_width_minus1[i], + sr_ctx); + + for (i = 0; i < pps->num_tile_rows_minus1; ++i) + HEVC_SWSR_UE("row_height_minus1", + (unsigned int *)&pps->row_height_minus1[i], + sr_ctx); + } + HEVC_SWSR_U1("loop_filter_across_tiles_enabled_flag", + &pps->loop_filter_across_tiles_enabled_flag, sr_ctx); + } else { + pps->loop_filter_across_tiles_enabled_flag = 1; + } + + HEVC_SWSR_U1("pps_loop_filter_across_slices_enabled_flag", + &pps->pps_loop_filter_across_slices_enabled_flag, sr_ctx); + + HEVC_SWSR_U1("deblocking_filter_control_present_flag", + &pps->deblocking_filter_control_present_flag, sr_ctx); + + if (pps->deblocking_filter_control_present_flag) { + HEVC_SWSR_U1("deblocking_filter_override_enabled_flag", + &pps->deblocking_filter_override_enabled_flag, sr_ctx); + HEVC_SWSR_U1("pps_deblocking_filter_disabled_flag", + &pps->pps_deblocking_filter_disabled_flag, sr_ctx); + if (!pps->pps_deblocking_filter_disabled_flag) { + HEVC_SWSR_SE("pps_beta_offset_div2", (int *)&pps->pps_beta_offset_div2, + sr_ctx); + HEVC_RANGESCHECK("pps_beta_offset_div2", pps->pps_beta_offset_div2, -6, 6, + &parse_err); + HEVC_SWSR_SE("pps_tc_offset_div2", (int *)&pps->pps_tc_offset_div2, sr_ctx); + HEVC_RANGESCHECK("pps_tc_offset_div2", pps->pps_tc_offset_div2, -6, 6, + &parse_err); + } + } + + HEVC_SWSR_U1("pps_scaling_list_data_present_flag", + &pps->pps_scaling_list_data_present_flag, sr_ctx); + if (pps->pps_scaling_list_data_present_flag) + parse_err |= bspp_hevc_parse_scalinglistdata(sr_ctx, &pps->scaling_list); + + HEVC_SWSR_U1("lists_modification_present_flag", + &pps->lists_modification_present_flag, sr_ctx); + HEVC_SWSR_UE("log2_parallel_merge_level_minus2", + (unsigned int *)&pps->log2_parallel_merge_level_minus2, sr_ctx); + HEVC_SWSR_U1("slice_segment_header_extension_present_flag", + &pps->slice_segment_header_extension_present_flag, sr_ctx); + + HEVC_SWSR_U1("pps_extension_present_flag", &pps->pps_extension_present_flag, sr_ctx); + if (pps->pps_extension_present_flag && + bspp_hevc_range_extensions_is_enabled(&sps->profile_tier_level)) { + HEVC_SWSR_U1("pps_range_extensions_flag", + &pps->pps_range_extensions_flag, sr_ctx); + HEVC_SWSR_UN("pps_extension_7bits", + (unsigned int *)&pps->pps_extension_7bits, 7, sr_ctx); + /* + * ignore extension data. Although we inform + * if some non-zero data was found + */ + HEVC_UCHECK("pps_extension_7bits", pps->pps_extension_7bits, 0, &parse_err); + + /* + * TODO ?: the newest HEVC spec (10/2014) splits "pps_extension_7bits" to + * pps_multilayer_extension_flag (1) + * pps_extension_6bits (6) + */ + if (pps->pps_range_extensions_flag && sps) { + parse_err |= bspp_hevc_parsepps_range_extensions + (sr_ctx, + &pps->range_exts, + pps->transform_skip_enabled_flag, + sps->log2_diff_max_min_luma_coding_block_size); + } + } + + /* calculate derived elements */ + if (pps->tiles_enabled_flag && sps) + bspp_hevc_dotilecalculations(sps, pps); + + return parse_err; +} + +static void bspp_hevc_dotilecalculations(struct bspp_hevc_sps *sps, + struct bspp_hevc_pps *pps) +{ + unsigned short colwidth[HEVC_MAX_TILE_COLS]; + unsigned short rowheight[HEVC_MAX_TILE_ROWS]; + unsigned char i; + + if (!pps->tiles_enabled_flag) { + pps->max_tile_height_in_ctbs_y = sps->pic_height_in_ctbs_y; + return; + } + + if (pps->uniform_spacing_flag) { + for (i = 0; i <= pps->num_tile_columns_minus1; ++i) { + colwidth[i] = ((i + 1) * sps->pic_width_in_ctbs_y) / + (pps->num_tile_columns_minus1 + 1) - + (i * sps->pic_width_in_ctbs_y) / + (pps->num_tile_columns_minus1 + 1); + } + + for (i = 0; i <= pps->num_tile_rows_minus1; ++i) { + rowheight[i] = ((i + 1) * sps->pic_height_in_ctbs_y) / + (pps->num_tile_rows_minus1 + 1) - + (i * sps->pic_height_in_ctbs_y) / + (pps->num_tile_rows_minus1 + 1); + } + + pps->max_tile_height_in_ctbs_y = rowheight[0]; + } else { + pps->max_tile_height_in_ctbs_y = 0; + + colwidth[pps->num_tile_columns_minus1] = sps->pic_width_in_ctbs_y; + for (i = 0; i <= pps->num_tile_columns_minus1; ++i) { + colwidth[i] = pps->column_width_minus1[i] + 1; + colwidth[pps->num_tile_columns_minus1] -= colwidth[i]; + } + + rowheight[pps->num_tile_rows_minus1] = sps->pic_height_in_ctbs_y; + for (i = 0; i <= pps->num_tile_rows_minus1; ++i) { + rowheight[i] = pps->row_height_minus1[i] + 1; + rowheight[pps->num_tile_rows_minus1] -= rowheight[i]; + + if (rowheight[i] > pps->max_tile_height_in_ctbs_y) + pps->max_tile_height_in_ctbs_y = rowheight[i]; + } + + if (rowheight[pps->num_tile_rows_minus1] > pps->max_tile_height_in_ctbs_y) + pps->max_tile_height_in_ctbs_y = + rowheight[pps->num_tile_rows_minus1]; + } + + for (i = 0; i <= pps->num_tile_columns_minus1; ++i) + pps->col_bd[i + 1] = pps->col_bd[i] + colwidth[i]; + + for (i = 0; i <= pps->num_tile_rows_minus1; ++i) + pps->row_bd[i + 1] = pps->row_bd[i] + rowheight[i]; +} + +static enum bspp_error_type bspp_hevc_parse_slicesegmentheader + (void *sr_ctx, void *str_res, + struct bspp_hevc_slice_segment_header *ssh, + unsigned char nalunit_type, + struct bspp_vps_info **vpsinfo, + struct bspp_sequence_hdr_info **spsinfo, + struct bspp_pps_info **ppsinfo) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + struct bspp_hevc_pps *pps = NULL; + struct bspp_hevc_sps *sps = NULL; + struct bspp_hevc_vps *vps = NULL; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(ssh); + VDEC_ASSERT(vpsinfo); + VDEC_ASSERT(spsinfo); + VDEC_ASSERT(ppsinfo); + + memset(ssh, 0, sizeof(struct bspp_hevc_slice_segment_header)); + + HEVC_SWSR_U1("first_slice_segment_in_pic_flag", + &ssh->first_slice_segment_in_pic_flag, sr_ctx); + + if (bspp_hevc_picture_is_irap((enum hevc_nalunittype)nalunit_type)) + HEVC_SWSR_U1("no_output_of_prior_pics_flag", + &ssh->no_output_of_prior_pics_flag, sr_ctx); + + HEVC_SWSR_UE("slice_pic_parameter_set_id", (unsigned int *)&ssh->slice_pic_parameter_set_id, + sr_ctx); + HEVC_RANGEUCHECK("slice_pic_parameter_set_id", ssh->slice_pic_parameter_set_id, 0, + HEVC_MAX_PPS_COUNT - 1, &parse_err); + + if (ssh->slice_pic_parameter_set_id >= HEVC_MAX_PPS_COUNT) { + pr_warn("PPS Id invalid (%u), setting to 0", + ssh->slice_pic_parameter_set_id); + ssh->slice_pic_parameter_set_id = 0; + parse_err &= ~BSPP_ERROR_INVALID_VALUE; + parse_err |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + + /* set PPS */ + *ppsinfo = bspp_get_pps_hdr(str_res, ssh->slice_pic_parameter_set_id); + if (!(*ppsinfo)) { + parse_err |= BSPP_ERROR_NO_PPS; + goto error; + } + pps = (struct bspp_hevc_pps *)(*ppsinfo)->secure_pps_info; + if (!pps) { + parse_err |= BSPP_ERROR_NO_PPS; + goto error; + } + VDEC_ASSERT(pps->pps_pic_parameter_set_id == ssh->slice_pic_parameter_set_id); + + *spsinfo = bspp_get_sequ_hdr(str_res, pps->pps_seq_parameter_set_id); + if (!(*spsinfo)) { + parse_err |= BSPP_ERROR_NO_SEQUENCE_HDR; + goto error; + } + sps = (struct bspp_hevc_sps *)(*spsinfo)->secure_sequence_info; + VDEC_ASSERT(sps->sps_seq_parameter_set_id == pps->pps_seq_parameter_set_id); + + *vpsinfo = bspp_get_vpshdr(str_res, sps->sps_video_parameter_set_id); + if (!(*vpsinfo)) { + parse_err |= BSPP_ERROR_NO_VPS; + goto error; + } + vps = (struct bspp_hevc_vps *)(*vpsinfo)->secure_vpsinfo; + VDEC_ASSERT(vps->vps_video_parameter_set_id == sps->sps_video_parameter_set_id); + + if (!ssh->first_slice_segment_in_pic_flag) { + if (pps->dependent_slice_segments_enabled_flag) + HEVC_SWSR_U1("dependent_slice_segment_flag", + &ssh->dependent_slice_segment_flag, sr_ctx); + + HEVC_SWSR_UN("slice_segment_address", + (unsigned int *)&ssh->slice_segment_address, + bspp_ceil_log2(sps->pic_size_in_ctbs_y), sr_ctx); + } + +error: + return parse_err; +} + +static enum bspp_error_type bspp_hevc_parse_profiletierlevel + (void *sr_ctx, + struct bspp_hevc_profile_tierlevel *ptl, + unsigned char vps_maxsublayers_minus1) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + unsigned char i, j; + unsigned int res = 0; + + VDEC_ASSERT(sr_ctx); + VDEC_ASSERT(ptl); + VDEC_ASSERT(vps_maxsublayers_minus1 < HEVC_MAX_NUM_SUBLAYERS); + + memset(ptl, 0, sizeof(struct bspp_hevc_profile_tierlevel)); + + HEVC_SWSR_UN("general_profile_space", (unsigned int *)&ptl->general_profile_space, 2, + sr_ctx); + HEVC_SWSR_U1("general_tier_flag", &ptl->general_tier_flag, sr_ctx); + HEVC_SWSR_UN("general_profile_idc", (unsigned int *)&ptl->general_profile_idc, 5, sr_ctx); + + for (j = 0; j < HEVC_MAX_NUM_PROFILE_IDC; ++j) { + HEVC_SWSR_U1("general_profile_compatibility_flag", + &ptl->general_profile_compatibility_flag[j], + sr_ctx); + } + + HEVC_SWSR_U1("general_progressive_source_flag", + &ptl->general_progressive_source_flag, sr_ctx); + HEVC_SWSR_U1("general_interlaced_source_flag", + &ptl->general_interlaced_source_flag, sr_ctx); + HEVC_SWSR_U1("general_non_packed_constraint_flag", + &ptl->general_non_packed_constraint_flag, sr_ctx); + HEVC_SWSR_U1("general_frame_only_constraint_flag", + &ptl->general_frame_only_constraint_flag, sr_ctx); + + if (ptl->general_profile_idc == 4 || + ptl->general_profile_compatibility_flag[4]) { + HEVC_SWSR_U1("general_max_12bit_constraint_flag", + &ptl->general_max_12bit_constraint_flag, sr_ctx); + HEVC_SWSR_U1("general_max_10bit_constraint_flag", + &ptl->general_max_10bit_constraint_flag, sr_ctx); + HEVC_SWSR_U1("general_max_8bit_constraint_flag", + &ptl->general_max_8bit_constraint_flag, sr_ctx); + HEVC_SWSR_U1("general_max_422chroma_constraint_flag", + &ptl->general_max_422chroma_constraint_flag, + sr_ctx); + HEVC_SWSR_U1("general_max_420chroma_constraint_flag", + &ptl->general_max_420chroma_constraint_flag, + sr_ctx); + HEVC_SWSR_U1("general_max_monochrome_constraint_flag", + &ptl->general_max_monochrome_constraint_flag, + sr_ctx); + HEVC_SWSR_U1("general_intra_constraint_flag", + &ptl->general_intra_constraint_flag, sr_ctx); + HEVC_SWSR_U1("general_one_picture_only_constraint_flag", + &ptl->general_one_picture_only_constraint_flag, + sr_ctx); + HEVC_SWSR_U1("general_lower_bit_rate_constraint_flag", + &ptl->general_lower_bit_rate_constraint_flag, + sr_ctx); + HEVC_SWSR_UN("general_reserved_zero_35bits", &res, 32, sr_ctx); + HEVC_UCHECK("general_reserved_zero_35bits", res, 0, &parse_err); + HEVC_SWSR_UN("general_reserved_zero_35bits", &res, 3, sr_ctx); + HEVC_UCHECK("general_reserved_zero_35bits", res, 0, &parse_err); + } else { + HEVC_SWSR_UN("general_reserved_zero_44bits (1)", &res, 32, sr_ctx); + HEVC_UCHECK("general_reserved_zero_44bits (1)", res, 0, &parse_err); + HEVC_SWSR_UN("general_reserved_zero_44bits (2)", &res, 12, sr_ctx); + HEVC_UCHECK("general_reserved_zero_44bits (2)", res, 0, &parse_err); + } + + HEVC_SWSR_UN("general_level_idc", (unsigned int *)&ptl->general_level_idc, 8, sr_ctx); + HEVC_RANGEUCHECK("general_level_idc", ptl->general_level_idc, + HEVC_LEVEL_IDC_MIN, HEVC_LEVEL_IDC_MAX, &parse_err); + + for (i = 0; i < vps_maxsublayers_minus1; ++i) { + HEVC_SWSR_U1("sub_layer_profile_present_flag", + &ptl->sub_layer_profile_present_flag[i], sr_ctx); + HEVC_SWSR_U1("sub_layer_level_present_flag", + &ptl->sub_layer_level_present_flag[i], sr_ctx); + } + + if (vps_maxsublayers_minus1 > 0) { + for (i = vps_maxsublayers_minus1; i < 8; ++i) { + HEVC_SWSR_UN("reserved_zero_2bits", &res, 2, sr_ctx); + HEVC_UCHECK("reserved_zero_2bits", res, 0, &parse_err); + } + } + + for (i = 0; i < vps_maxsublayers_minus1; ++i) { + if (ptl->sub_layer_profile_present_flag[i]) { + HEVC_SWSR_UN("sub_layer_profile_space", + (unsigned int *)&ptl->sub_layer_profile_space[i], 2, sr_ctx); + HEVC_SWSR_U1("sub_layer_tier_flag", &ptl->sub_layer_tier_flag[i], sr_ctx); + HEVC_SWSR_UN("sub_layer_profile_idc", + (unsigned int *)&ptl->sub_layer_profile_idc[i], 5, sr_ctx); + for (j = 0; j < HEVC_MAX_NUM_PROFILE_IDC; ++j) + HEVC_SWSR_U1("sub_layer_profile_compatibility_flag", + &ptl->sub_layer_profile_compatibility_flag[i][j], + sr_ctx); + + HEVC_SWSR_U1("sub_layer_progressive_source_flag", + &ptl->sub_layer_progressive_source_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_interlaced_source_flag", + &ptl->sub_layer_interlaced_source_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_non_packed_constraint_flag", + &ptl->sub_layer_non_packed_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_frame_only_constraint_flag", + &ptl->sub_layer_frame_only_constraint_flag[i], + sr_ctx); + + if (ptl->sub_layer_profile_idc[i] == 4 || + ptl->sub_layer_profile_compatibility_flag[i][4]) { + HEVC_SWSR_U1("sub_layer_max_12bit_constraint_flag", + &ptl->sub_layer_max_12bit_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_max_10bit_constraint_flag", + &ptl->sub_layer_max_10bit_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_max_8bit_constraint_flag", + &ptl->sub_layer_max_8bit_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_max_422chroma_constraint_flag", + &ptl->sub_layer_max_422chroma_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_max_420chroma_constraint_flag", + &ptl->sub_layer_max_420chroma_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_max_monochrome_constraint_flag", + &ptl->sub_layer_max_monochrome_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_intra_constraint_flag", + &ptl->sub_layer_intra_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_one_picture_only_constraint_flag", + &ptl->sub_layer_one_picture_only_constraint_flag[i], + sr_ctx); + HEVC_SWSR_U1("sub_layer_lower_bit_rate_constraint_flag", + &ptl->sub_layer_lower_bit_rate_constraint_flag[i], + sr_ctx); + HEVC_SWSR_UN("sub_layer_reserved_zero_35bits", + &res, 32, sr_ctx); + HEVC_UCHECK("sub_layer_reserved_zero_35bits", + res, 0, &parse_err); + HEVC_SWSR_UN("sub_layer_reserved_zero_35bits", + &res, 3, sr_ctx); + HEVC_UCHECK("sub_layer_reserved_zero_35bits", + res, 0, &parse_err); + } else { + HEVC_SWSR_UN("sub_layer_reserved_zero_44bits (1)", + &res, 32, sr_ctx); + HEVC_UCHECK("sub_layer_reserved_zero_44bits (1)", + res, 0, &parse_err); + HEVC_SWSR_UN("sub_layer_reserved_zero_44bits (2)", + &res, 12, sr_ctx); + HEVC_UCHECK("sub_layer_reserved_zero_44bits (2)", + res, 0, &parse_err); + } + } + if (ptl->sub_layer_level_present_flag[i]) + HEVC_SWSR_UN("sub_layer_level_idc", + (unsigned int *)&ptl->sub_layer_level_idc[i], 8, sr_ctx); + } + return parse_err; +} + +/* Default scaling lists */ +#define HEVC_SCALING_LIST_0_SIZE 16 +#define HEVC_SCALING_LIST_123_SIZE 64 + +static const unsigned char def_4x4[HEVC_SCALING_LIST_0_SIZE] = { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 +}; + +static const unsigned char def_8x8_intra[HEVC_SCALING_LIST_123_SIZE] = { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 17, 16, 17, 18, + 17, 18, 18, 17, 18, 21, 19, 20, 21, 20, 19, 21, 24, 22, 22, 24, + 24, 22, 22, 24, 25, 25, 27, 30, 27, 25, 25, 29, 31, 35, 35, 31, + 29, 36, 41, 44, 41, 36, 47, 54, 54, 47, 65, 70, 65, 88, 88, 115 +}; + +static const unsigned char def_8x8_inter[HEVC_SCALING_LIST_123_SIZE] = { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, + 18, 18, 18, 18, 18, 20, 20, 20, 20, 20, 20, 20, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 28, 28, 28, 28, 28, + 28, 33, 33, 33, 33, 33, 41, 41, 41, 41, 54, 54, 54, 71, 71, 91 +}; + +/* + * Scan order mapping when translating scaling lists from bitstream order + * to PVDEC order + */ +static const unsigned char HEVC_INV_ZZ_SCAN4[HEVC_SCALING_LIST_MATRIX_SIZE / 4] = { + 0, 1, 2, 4, 3, 6, 7, 10, 5, 8, 9, 12, 11, 13, 14, 15 +}; + +static const unsigned char HEVC_INV_ZZ_SCAN8[HEVC_SCALING_LIST_MATRIX_SIZE] = { + 0, 1, 2, 4, 3, 6, 7, 11, 5, 8, 9, 13, 12, 17, 18, 24, + 10, 15, 16, 22, 21, 28, 29, 36, 23, 30, 31, 38, 37, 43, 44, 49, + 14, 19, 20, 26, 25, 32, 33, 40, 27, 34, 35, 42, 41, 47, 48, 53, + 39, 45, 46, 51, 50, 54, 55, 58, 52, 56, 57, 60, 59, 61, 62, 63 +}; + +static void bspp_hevc_getdefault_scalinglist + (unsigned char size_id, unsigned char matrix_id, + const unsigned char **default_scalinglist, + unsigned int *size) +{ + static const unsigned char *defaultlists + [HEVC_SCALING_LIST_NUM_SIZES][HEVC_SCALING_LIST_NUM_MATRICES] = { + { def_4x4, def_4x4, def_4x4, def_4x4, def_4x4, def_4x4 }, + { def_8x8_intra, def_8x8_intra, def_8x8_intra, + def_8x8_inter, def_8x8_inter, def_8x8_inter }, + { def_8x8_intra, def_8x8_intra, def_8x8_intra, + def_8x8_inter, def_8x8_inter, def_8x8_inter }, + { def_8x8_intra, def_8x8_inter, NULL, NULL, NULL, NULL } + }; + + static const unsigned int lists_sizes + [HEVC_SCALING_LIST_NUM_SIZES][HEVC_SCALING_LIST_NUM_MATRICES] = { + { sizeof(def_4x4), sizeof(def_4x4), sizeof(def_4x4), + sizeof(def_4x4), sizeof(def_4x4), sizeof(def_4x4) }, + { sizeof(def_8x8_intra), sizeof(def_8x8_intra), + sizeof(def_8x8_intra), sizeof(def_8x8_inter), + sizeof(def_8x8_inter), sizeof(def_8x8_inter) }, + { sizeof(def_8x8_intra), sizeof(def_8x8_intra), + sizeof(def_8x8_intra), sizeof(def_8x8_inter), + sizeof(def_8x8_inter), sizeof(def_8x8_inter) }, + { sizeof(def_8x8_intra), sizeof(def_8x8_inter), 0, 0, 0, 0 } + }; + + /* to assert that input to this function was correct */ + VDEC_ASSERT(size_id < 4); + VDEC_ASSERT(size_id < 3 ? (matrix_id < 6) : (matrix_id < 2)); + + *default_scalinglist = defaultlists[size_id][matrix_id]; + *size = lists_sizes[size_id][matrix_id]; +} + +static enum bspp_error_type bspp_hevc_parse_scalinglistdata + (void *sr_ctx, + struct bspp_hevc_scalinglist_data *scaling_listdata) +{ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + unsigned char size_id, matrix_id; + + for (size_id = 0; size_id < HEVC_SCALING_LIST_NUM_SIZES; ++size_id) { + for (matrix_id = 0; matrix_id < ((size_id == 3) ? 2 : 6); + ++matrix_id) { + /* + * Select scaling list on which we will operate in + * the iteration + */ + unsigned char *scalinglist = scaling_listdata->lists[size_id][matrix_id]; + + unsigned char scaling_list_pred_mode_flag = 0; + + HEVC_SWSR_U1("scaling_list_pred_mode_flag", + &scaling_list_pred_mode_flag, sr_ctx); + if (!scaling_list_pred_mode_flag) { + unsigned char scaling_list_pred_matrix_id_delta = 0; + const unsigned char *defaultlist = NULL; + unsigned int listsize = 0; + + HEVC_SWSR_UE("scaling_list_pred_matrixid_delta", + (unsigned int *)&scaling_list_pred_matrix_id_delta, + sr_ctx); + + bspp_hevc_getdefault_scalinglist(size_id, + matrix_id, + &defaultlist, + &listsize); + + if (scaling_list_pred_matrix_id_delta == 0) { + /* use default one */ + memcpy(scalinglist, defaultlist, listsize); + if (size_id > 1) + scaling_listdata->dccoeffs[size_id - + 2][matrix_id] = 8 + 8; + } else { + unsigned char ref_matrix_id = + matrix_id - scaling_list_pred_matrix_id_delta; + unsigned char *refscalinglist = + scaling_listdata->lists[size_id][ref_matrix_id]; + /* + * use reference list given by + * scaling_list_pred_matrix_id_delta + */ + memcpy(scalinglist, refscalinglist, listsize); + if (size_id > 1) + scaling_listdata->dccoeffs[size_id - 2][matrix_id] = + scaling_listdata->dccoeffs[size_id - + 2][ref_matrix_id]; + } + } else { + /* + * scaling list coefficients + * signalled explicitly + */ + static const short coef_startvalue = 8; + static const unsigned char matrix_max_coef_num = 64; + + short next_coef = coef_startvalue; + unsigned char coef_num = + HEVC_MIN(matrix_max_coef_num, + (1 << (4 + (size_id << 1))), unsigned char); + + unsigned char i; + + if (size_id > 1) { + short scaling_list_dc_coef_minus8 = 0; + + HEVC_SWSR_SE("scaling_list_dc_coef_minus8", + (int *)&scaling_list_dc_coef_minus8, + sr_ctx); + HEVC_RANGESCHECK("scaling_list_dc_coef_minus8", + scaling_list_dc_coef_minus8, + -7, 247, &parse_err); + + next_coef = scaling_list_dc_coef_minus8 + 8; + scaling_listdata->dccoeffs[size_id - 2][matrix_id] = + (unsigned char)next_coef; + } + for (i = 0; i < coef_num; ++i) { + short scaling_list_delta_coef = 0; + + HEVC_SWSR_SE("scaling_list_delta_coef", + (int *)&scaling_list_delta_coef, sr_ctx); + HEVC_RANGESCHECK("scaling_list_delta_coef", + scaling_list_delta_coef, -128, 127, + &parse_err); + + next_coef = (next_coef + scaling_list_delta_coef + 256) & + 0xFF; + scalinglist[i] = next_coef; + } + } + } + } + +#ifdef DEBUG_DECODER_DRIVER + /* print calculated scaling lists */ + for (size_id = 0; size_id < HEVC_SCALING_LIST_NUM_SIZES; ++size_id) { + for (matrix_id = 0; matrix_id < ((size_id == 3) ? 2 : 6); + ++matrix_id) { + unsigned char i = 0; + /* + * Select scaling list on which we will operate + * in the iteration + */ + unsigned char *scalinglist = scaling_listdata->lists[size_id][matrix_id]; + + for (; i < ((size_id == 0) ? 16 : 64); ++i) { + BSPP_HEVC_SYNTAX("scalinglist[%u][%u][%u] = %u", + size_id, + matrix_id, + i, + scalinglist[i]); + } + } + } +#endif + + return parse_err; +} + +static void +bspp_hevc_usedefault_scalinglists(struct bspp_hevc_scalinglist_data *scaling_listdata) +{ + unsigned char size_id, matrix_id; + + for (size_id = 0; size_id < HEVC_SCALING_LIST_NUM_SIZES; ++size_id) { + for (matrix_id = 0; matrix_id < ((size_id == 3) ? 2 : 6); + ++matrix_id) { + unsigned char *list = scaling_listdata->lists[size_id][matrix_id]; + const unsigned char *defaultlist = NULL; + unsigned int listsize = 0; + + bspp_hevc_getdefault_scalinglist(size_id, matrix_id, &defaultlist, + &listsize); + + memcpy(list, defaultlist, listsize); + } + } + + memset(scaling_listdata->dccoeffs, 8 + 8, sizeof(scaling_listdata->dccoeffs)); +} + +static enum bspp_error_type bspp_hevc_parse_shortterm_refpicset + (void *sr_ctx, + struct bspp_hevc_shortterm_refpicset *st_refpicset, + unsigned char st_rps_idx, + unsigned char in_slice_header) +{ + /* + * Note: unfortunately short term ref pic set has to be + * "partially-decoded" and parsed at the same time because derived + * syntax elements are used for prediction of subsequent + * short term ref pic sets. + */ + enum bspp_error_type parse_err = BSPP_ERROR_NONE; + + struct bspp_hevc_shortterm_refpicset *strps = + &st_refpicset[st_rps_idx]; + unsigned char inter_ref_pic_set_prediction_flag = 0; + unsigned int i = 0; + + memset(strps, 0, sizeof(*strps)); + + if (st_rps_idx != 0) { + HEVC_SWSR_U1("inter_ref_pic_set_prediction_flag", + &inter_ref_pic_set_prediction_flag, sr_ctx); + } + + if (inter_ref_pic_set_prediction_flag) { + signed char j = 0; + unsigned char j_8 = 0; + unsigned char ref_rps_idx = 0; + int delta_rps = 0; + unsigned char i = 0; + unsigned char delta_idx_minus1 = 0; + unsigned char delta_rps_sign = 0; + unsigned short abs_delta_rps_minus1 = 0; + unsigned char used_by_curr_pic_flag[HEVC_MAX_NUM_REF_PICS]; + unsigned char use_delta_flag[HEVC_MAX_NUM_REF_PICS]; + + struct bspp_hevc_shortterm_refpicset *ref_strps = NULL; + + if (in_slice_header) { + HEVC_SWSR_UE("delta_idx_minus1", (unsigned int *)&delta_idx_minus1, sr_ctx); + HEVC_RANGEUCHECK("delta_idx_minus1", delta_idx_minus1, 0, st_rps_idx - 1, + &parse_err); + } + + HEVC_SWSR_U1("delta_rps_sign", &delta_rps_sign, sr_ctx); + HEVC_SWSR_UE("abs_delta_rps_minus1", (unsigned int *)&abs_delta_rps_minus1, sr_ctx); + HEVC_RANGEUCHECK("abs_delta_rps_minus1", abs_delta_rps_minus1, 0, ((1 << 15) - 1), + &parse_err); + + ref_rps_idx = st_rps_idx - (delta_idx_minus1 + 1); + ref_strps = &st_refpicset[ref_rps_idx]; + + memset(use_delta_flag, 1, sizeof(use_delta_flag)); + + for (j_8 = 0; j_8 <= ref_strps->num_delta_pocs; ++j_8) { + HEVC_SWSR_U1("used_by_curr_pic_flag", &used_by_curr_pic_flag[j_8], sr_ctx); + if (!used_by_curr_pic_flag[j_8]) + HEVC_SWSR_U1("use_delta_flag", &use_delta_flag[j_8], sr_ctx); + } + + delta_rps = + (1 - 2 * delta_rps_sign) * (abs_delta_rps_minus1 + 1); + + /* + * predict delta POC values of current strps from + * reference strps + */ + for (j = ref_strps->num_positive_pics - 1; j >= 0; --j) { + int dpoc = ref_strps->delta_poc_s1[j] + delta_rps; + + if (dpoc < 0 && use_delta_flag[ref_strps->num_negative_pics + j]) { + strps->delta_poc_s0[i] = dpoc; + strps->used_bycurr_pic_s0[i++] = + used_by_curr_pic_flag[ref_strps->num_negative_pics + j]; + } + } + + if (delta_rps < 0 && use_delta_flag[ref_strps->num_delta_pocs]) { + strps->delta_poc_s0[i] = delta_rps; + strps->used_bycurr_pic_s0[i++] = + used_by_curr_pic_flag[ref_strps->num_delta_pocs]; + } + + for (j_8 = 0; j_8 < ref_strps->num_negative_pics; ++j_8) { + int dpoc = ref_strps->delta_poc_s0[j_8] + delta_rps; + + if (dpoc < 0 && use_delta_flag[j_8]) { + strps->delta_poc_s0[i] = dpoc; + strps->used_bycurr_pic_s0[i++] = used_by_curr_pic_flag[j_8]; + } + } + + strps->num_negative_pics = i; + + i = 0; + for (j = ref_strps->num_negative_pics - 1; j >= 0; --j) { + int dpoc = ref_strps->delta_poc_s0[j] + delta_rps; + + if (dpoc > 0 && use_delta_flag[j]) { + strps->delta_poc_s1[i] = dpoc; + strps->used_bycurr_pic_s1[i++] = + used_by_curr_pic_flag[j]; + } + } + + if (delta_rps > 0 && use_delta_flag[ref_strps->num_delta_pocs]) { + strps->delta_poc_s1[i] = delta_rps; + strps->used_bycurr_pic_s1[i++] = + used_by_curr_pic_flag[ref_strps->num_delta_pocs]; + } + + for (j_8 = 0; j_8 < ref_strps->num_positive_pics; ++j_8) { + int dpoc = ref_strps->delta_poc_s1[j_8] + delta_rps; + + if (dpoc > 0 && use_delta_flag[ref_strps->num_negative_pics + j_8]) { + strps->delta_poc_s1[i] = dpoc; + strps->used_bycurr_pic_s1[i++] = + used_by_curr_pic_flag[ref_strps->num_negative_pics + j_8]; + } + } + + strps->num_positive_pics = i; + strps->num_delta_pocs = strps->num_negative_pics + strps->num_positive_pics; + if (strps->num_delta_pocs > (HEVC_MAX_NUM_REF_PICS - 1)) { + strps->num_delta_pocs = HEVC_MAX_NUM_REF_PICS - 1; + parse_err |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + } else { + unsigned char num_negative_pics = 0; + unsigned char num_positive_pics = 0; + unsigned short delta_poc_s0_minus1[HEVC_MAX_NUM_REF_PICS]; + unsigned char used_by_curr_pic_s0_flag[HEVC_MAX_NUM_REF_PICS]; + unsigned short delta_poc_s1_minus1[HEVC_MAX_NUM_REF_PICS]; + unsigned char used_by_curr_pic_s1_flag[HEVC_MAX_NUM_REF_PICS]; + unsigned char j = 0; + + HEVC_SWSR_UE("num_negative_pics", (unsigned int *)&num_negative_pics, sr_ctx); + if (num_negative_pics > HEVC_MAX_NUM_REF_PICS) { + num_negative_pics = HEVC_MAX_NUM_REF_PICS; + parse_err |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + HEVC_SWSR_UE("num_positive_pics", (unsigned int *)&num_positive_pics, sr_ctx); + if (num_positive_pics > HEVC_MAX_NUM_REF_PICS) { + num_positive_pics = HEVC_MAX_NUM_REF_PICS; + parse_err |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + + for (j = 0; j < num_negative_pics; ++j) { + HEVC_SWSR_UE("delta_poc_s0_minus1", + (unsigned int *)&delta_poc_s0_minus1[j], sr_ctx); + HEVC_RANGEUCHECK("delta_poc_s0_minus1", delta_poc_s0_minus1[j], 0, + ((1 << 15) - 1), &parse_err); + HEVC_SWSR_U1("used_by_curr_pic_s0_flag", + &used_by_curr_pic_s0_flag[j], sr_ctx); + + if (j == 0) + strps->delta_poc_s0[j] = + -(delta_poc_s0_minus1[j] + 1); + else + strps->delta_poc_s0[j] = strps->delta_poc_s0[j - 1] - + (delta_poc_s0_minus1[j] + 1); + + strps->used_bycurr_pic_s0[j] = used_by_curr_pic_s0_flag[j]; + } + + for (j = 0; j < num_positive_pics; j++) { + HEVC_SWSR_UE("delta_poc_s1_minus1", + (unsigned int *)&delta_poc_s1_minus1[j], sr_ctx); + HEVC_RANGEUCHECK("delta_poc_s1_minus1", delta_poc_s1_minus1[j], 0, + ((1 << 15) - 1), &parse_err); + HEVC_SWSR_U1("used_by_curr_pic_s1_flag", + &used_by_curr_pic_s1_flag[j], sr_ctx); + + if (j == 0) + strps->delta_poc_s1[j] = + (delta_poc_s1_minus1[j] + 1); + else + strps->delta_poc_s1[j] = strps->delta_poc_s1[j - 1] + + (delta_poc_s1_minus1[j] + 1); + strps->used_bycurr_pic_s1[j] = used_by_curr_pic_s1_flag[j]; + } + + strps->num_negative_pics = num_negative_pics; + strps->num_positive_pics = num_positive_pics; + strps->num_delta_pocs = strps->num_negative_pics + strps->num_positive_pics; + if (strps->num_delta_pocs > (HEVC_MAX_NUM_REF_PICS - 1)) { + strps->num_delta_pocs = HEVC_MAX_NUM_REF_PICS - 1; + parse_err |= BSPP_ERROR_CORRECTION_VALIDVALUE; + } + } + + BSPP_HEVC_SYNTAX + ("strps[%u]: num_delta_pocs: %u (%u (num_negative_pics) + %u (num_positive_pics))", + st_rps_idx, strps->num_delta_pocs, strps->num_negative_pics, + strps->num_positive_pics); + + for (i = 0; i < strps->num_negative_pics; ++i) { + BSPP_HEVC_SYNTAX("StRps[%u][%u]: delta_poc_s0: %d, used_bycurr_pic_s0: %u", + st_rps_idx, i, strps->delta_poc_s0[i], + strps->used_bycurr_pic_s0[i]); + } + + for (i = 0; i < strps->num_positive_pics; ++i) { + BSPP_HEVC_SYNTAX("StRps[%u][%u]: delta_poc_s1: %d, used_bycurr_pic_s1: %u", + st_rps_idx, i, strps->delta_poc_s1[i], + strps->used_bycurr_pic_s1[i]); + } + + return parse_err; +} + +static void bspp_hevc_fillcommonseqhdr(struct bspp_hevc_sps *sps, + struct vdec_comsequ_hdrinfo *common_seq) +{ + struct bspp_hevc_vui_params *vui = &sps->vui_params; + unsigned char chroma_idc = sps->chroma_format_idc; + struct pixel_pixinfo *pixel_info = &common_seq->pixel_info; + unsigned int maxsub_layersmin1; + unsigned int maxdpb_size; + struct vdec_rect *rawdisp_region; + + common_seq->codec_profile = sps->profile_tier_level.general_profile_idc; + common_seq->codec_level = sps->profile_tier_level.general_level_idc; + + if (sps->vui_parameters_present_flag && + vui->vui_timing_info_present_flag) { + common_seq->frame_rate_num = vui->vui_time_scale; + common_seq->frame_rate_den = vui->vui_num_units_in_tick; + common_seq->frame_rate = + 1 * common_seq->frame_rate_num / common_seq->frame_rate_den; + } + + if (vui->aspect_ratio_info_present_flag) { + common_seq->aspect_ratio_num = vui->sar_width; + common_seq->aspect_ratio_den = vui->sar_height; + } + + common_seq->interlaced_frames = 0; + + /* handle pixel format definitions */ + pixel_info->chroma_fmt = chroma_idc == 0 ? 0 : 1; + pixel_info->chroma_fmt_idc = pixelformat_idc[chroma_idc]; + pixel_info->chroma_interleave = + chroma_idc == 0 ? PIXEL_INVALID_CI : PIXEL_UV_ORDER; + pixel_info->bitdepth_y = sps->bit_depth_luma_minus8 + 8; + pixel_info->bitdepth_c = sps->bit_depth_chroma_minus8 + 8; + + pixel_info->mem_pkg = (pixel_info->bitdepth_y > 8 || + (pixel_info->bitdepth_c > 8 && pixel_info->chroma_fmt)) ? + PIXEL_BIT10_MSB_MP : PIXEL_BIT8_MP; + pixel_info->num_planes = + chroma_idc == 0 ? 1 : (sps->separate_colour_plane_flag ? 3 : 2); + + pixel_info->pixfmt = pixel_get_pixfmt(pixel_info->chroma_fmt_idc, + pixel_info->chroma_interleave, + pixel_info->mem_pkg, + pixel_info->bitdepth_y, + pixel_info->chroma_fmt ? + pixel_info->bitdepth_c : PIXEL_INVALID_BDC, + pixel_info->num_planes); + + common_seq->max_frame_size.width = sps->pic_width_in_ctbs_y * sps->ctb_size_y; + common_seq->max_frame_size.height = sps->pic_height_in_ctbs_y * sps->ctb_size_y; + + common_seq->frame_size.width = sps->pic_width_in_luma_samples; + common_seq->frame_size.height = sps->pic_height_in_luma_samples; + + /* Get HEVC max num ref pictures and pass to bspp info*/ + vdecddutils_ref_pic_hevc_get_maxnum(common_seq, &common_seq->max_ref_frame_num); + + common_seq->field_codec_mblocks = 0; + + maxsub_layersmin1 = sps->sps_max_sub_layers_minus1; + maxdpb_size = + HEVC_MAX(sps->sps_max_dec_pic_buffering_minus1[maxsub_layersmin1] + 1, + sps->sps_max_num_reorder_pics[maxsub_layersmin1], unsigned char); + + if (sps->sps_max_latency_increase_plus1[maxsub_layersmin1]) { + maxdpb_size = + HEVC_MAX(maxdpb_size, + sps->sps_max_latency_pictures[maxsub_layersmin1], unsigned int); + } + + maxdpb_size = HEVC_MIN(maxdpb_size, + HEVC_MAX_NUM_REF_IDX_ACTIVE + 1, unsigned int); + + common_seq->min_pict_buf_num = HEVC_MAX(maxdpb_size, 6, unsigned int); + + common_seq->picture_reordering = 1; + common_seq->post_processing = 0; + + /* handle display region calculation */ + rawdisp_region = &common_seq->raw_display_region; + + rawdisp_region->width = sps->pic_width_in_luma_samples; + rawdisp_region->height = sps->pic_height_in_luma_samples; + rawdisp_region->top_offset = 0; + rawdisp_region->left_offset = 0; + + if (sps->conformance_window_flag) { + struct vdec_rect *disp_region = + &common_seq->orig_display_region; + + disp_region->top_offset = + sps->sub_height_c * sps->conf_win_top_offset; + disp_region->left_offset = + sps->sub_width_c * sps->conf_win_left_offset; + disp_region->width = + sps->pic_width_in_luma_samples - + disp_region->left_offset - + sps->sub_width_c * sps->conf_win_right_offset; + disp_region->height = + sps->pic_height_in_luma_samples - + disp_region->top_offset - + sps->sub_height_c * sps->conf_win_bottom_offset; + } else { + common_seq->orig_display_region = + common_seq->raw_display_region; + } +} + +static void bspp_hevc_fillpicturehdr(struct vdec_comsequ_hdrinfo *common_seq, + enum hevc_nalunittype nalunit_type, + struct bspp_pict_hdr_info *picture_hdr, + struct bspp_hevc_sps *sps, + struct bspp_hevc_pps *pps, + struct bspp_hevc_vps *vps) +{ + picture_hdr->intra_coded = (nalunit_type == HEVC_NALTYPE_IDR_W_RADL || + nalunit_type == HEVC_NALTYPE_IDR_N_LP); + picture_hdr->field = 0; + picture_hdr->post_processing = 0; + picture_hdr->discontinuous_mbs = 0; + picture_hdr->pict_aux_data.id = BSPP_INVALID; + picture_hdr->second_pict_aux_data.id = BSPP_INVALID; + picture_hdr->pict_sgm_data.id = BSPP_INVALID; + picture_hdr->coded_frame_size.width = + HEVC_ALIGN(sps->pic_width_in_luma_samples, HEVC_MIN_CODED_UNIT_SIZE, unsigned int); + picture_hdr->coded_frame_size.height = + HEVC_ALIGN(sps->pic_height_in_luma_samples, HEVC_MIN_CODED_UNIT_SIZE, unsigned int); + picture_hdr->disp_info.enc_disp_region = common_seq->orig_display_region; + picture_hdr->disp_info.disp_region = common_seq->orig_display_region; + picture_hdr->disp_info.raw_disp_region = common_seq->raw_display_region; + picture_hdr->disp_info.num_pan_scan_windows = 0; + picture_hdr->hevc_pict_hdr_info.range_ext_present = + (sps->profile_tier_level.general_profile_idc == 4) || + sps->profile_tier_level.general_profile_compatibility_flag[4]; + + picture_hdr->hevc_pict_hdr_info.is_full_range_ext = 0; + if (picture_hdr->hevc_pict_hdr_info.range_ext_present && + (bspp_hevc_checkppsrangeextensions(&pps->range_exts) || + bspp_hevc_checksps_range_extensions(&sps->range_exts))) + picture_hdr->hevc_pict_hdr_info.is_full_range_ext = 1; + + memset(picture_hdr->disp_info.pan_scan_windows, 0, + sizeof(picture_hdr->disp_info.pan_scan_windows)); +} + +static void bspp_hevc_fill_fwsps(struct bspp_hevc_sps *sps, struct hevcfw_sequence_ps *fwsps) +{ + unsigned char i; + + fwsps->pic_width_in_luma_samples = sps->pic_width_in_luma_samples; + fwsps->pic_height_in_luma_samples = sps->pic_height_in_luma_samples; + fwsps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; + fwsps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps; + fwsps->sps_max_sub_layers_minus1 = sps->sps_max_sub_layers_minus1; + fwsps->max_transform_hierarchy_depth_inter = + sps->max_transform_hierarchy_depth_inter; + fwsps->max_transform_hierarchy_depth_intra = + sps->max_transform_hierarchy_depth_intra; + fwsps->log2_diff_max_min_transform_block_size = + sps->log2_diff_max_min_transform_block_size; + fwsps->log2_min_transform_block_size_minus2 = + sps->log2_min_transform_block_size_minus2; + fwsps->log2_diff_max_min_luma_coding_block_size = + sps->log2_diff_max_min_luma_coding_block_size; + fwsps->log2_min_luma_coding_block_size_minus3 = + sps->log2_min_luma_coding_block_size_minus3; + + HEVC_STATIC_ASSERT(sizeof(sps->sps_max_dec_pic_buffering_minus1) == + sizeof(fwsps->sps_max_dec_pic_buffering_minus1)); + memcpy(fwsps->sps_max_dec_pic_buffering_minus1, sps->sps_max_dec_pic_buffering_minus1, + sizeof(fwsps->sps_max_dec_pic_buffering_minus1[0]) * + (sps->sps_max_sub_layers_minus1 + 1)); + + HEVC_STATIC_ASSERT(sizeof(sps->sps_max_num_reorder_pics) == + sizeof(fwsps->sps_max_num_reorder_pics)); + memcpy(fwsps->sps_max_num_reorder_pics, sps->sps_max_num_reorder_pics, + sizeof(fwsps->sps_max_num_reorder_pics[0]) * + (sps->sps_max_sub_layers_minus1 + 1)); + + HEVC_STATIC_ASSERT(sizeof(sps->sps_max_latency_increase_plus1) == + sizeof(fwsps->sps_max_latency_increase_plus1)); + memcpy(fwsps->sps_max_latency_increase_plus1, sps->sps_max_latency_increase_plus1, + sizeof(fwsps->sps_max_latency_increase_plus1[0]) * + (sps->sps_max_sub_layers_minus1 + 1)); + + fwsps->chroma_format_idc = sps->chroma_format_idc; + fwsps->separate_colour_plane_flag = sps->separate_colour_plane_flag; + fwsps->log2_max_pic_order_cnt_lsb_minus4 = + sps->log2_max_pic_order_cnt_lsb_minus4; + fwsps->long_term_ref_pics_present_flag = + sps->long_term_ref_pics_present_flag; + fwsps->sample_adaptive_offset_enabled_flag = + sps->sample_adaptive_offset_enabled_flag; + fwsps->sps_temporal_mvp_enabled_flag = + sps->sps_temporal_mvp_enabled_flag; + fwsps->bit_depth_luma_minus8 = sps->bit_depth_luma_minus8; + fwsps->bit_depth_chroma_minus8 = sps->bit_depth_chroma_minus8; + fwsps->pcm_sample_bit_depth_luma_minus1 = + sps->pcm_sample_bit_depth_luma_minus1; + fwsps->pcm_sample_bit_depth_chroma_minus1 = + sps->pcm_sample_bit_depth_chroma_minus1; + fwsps->log2_min_pcm_luma_coding_block_size_minus3 = + sps->log2_min_pcm_luma_coding_block_size_minus3; + fwsps->log2_diff_max_min_pcm_luma_coding_block_size = + sps->log2_diff_max_min_pcm_luma_coding_block_size; + fwsps->pcm_loop_filter_disabled_flag = + sps->pcm_loop_filter_disabled_flag; + fwsps->amp_enabled_flag = sps->amp_enabled_flag; + fwsps->pcm_enabled_flag = sps->pcm_enabled_flag; + fwsps->strong_intra_smoothing_enabled_flag = + sps->strong_intra_smoothing_enabled_flag; + fwsps->scaling_list_enabled_flag = sps->scaling_list_enabled_flag; + fwsps->transform_skip_rotation_enabled_flag = + sps->range_exts.transform_skip_rotation_enabled_flag; + fwsps->transform_skip_context_enabled_flag = + sps->range_exts.transform_skip_context_enabled_flag; + fwsps->implicit_rdpcm_enabled_flag = + sps->range_exts.implicit_rdpcm_enabled_flag; + fwsps->explicit_rdpcm_enabled_flag = + sps->range_exts.explicit_rdpcm_enabled_flag; + fwsps->extended_precision_processing_flag = + sps->range_exts.extended_precision_processing_flag; + fwsps->intra_smoothing_disabled_flag = + sps->range_exts.intra_smoothing_disabled_flag; + /* high precision makes no sense for 8 bit luma & chroma, + * so forward this parameter only when bitdepth > 8 + */ + if (sps->bit_depth_luma_minus8 || sps->bit_depth_chroma_minus8) + fwsps->high_precision_offsets_enabled_flag = + sps->range_exts.high_precision_offsets_enabled_flag; + + fwsps->persistent_rice_adaptation_enabled_flag = + sps->range_exts.persistent_rice_adaptation_enabled_flag; + fwsps->cabac_bypass_alignment_enabled_flag = + sps->range_exts.cabac_bypass_alignment_enabled_flag; + + HEVC_STATIC_ASSERT(sizeof(sps->lt_ref_pic_poc_lsb_sps) == + sizeof(fwsps->lt_ref_pic_poc_lsb_sps)); + HEVC_STATIC_ASSERT(sizeof(sps->used_by_curr_pic_lt_sps_flag) == + sizeof(fwsps->used_by_curr_pic_lt_sps_flag)); + memcpy(fwsps->lt_ref_pic_poc_lsb_sps, sps->lt_ref_pic_poc_lsb_sps, + sizeof(fwsps->lt_ref_pic_poc_lsb_sps[0]) * + sps->num_long_term_ref_pics_sps); + memcpy(fwsps->used_by_curr_pic_lt_sps_flag, sps->used_by_curr_pic_lt_sps_flag, + sizeof(fwsps->used_by_curr_pic_lt_sps_flag[0]) * sps->num_long_term_ref_pics_sps); + + for (i = 0; i < sps->num_short_term_ref_pic_sets; ++i) + bspp_hevc_fill_fwst_rps(&sps->rps_list[i], &fwsps->st_rps_list[i]); + + /* derived elements */ + fwsps->pic_size_in_ctbs_y = sps->pic_size_in_ctbs_y; + fwsps->pic_height_in_ctbs_y = sps->pic_height_in_ctbs_y; + fwsps->pic_width_in_ctbs_y = sps->pic_width_in_ctbs_y; + fwsps->ctb_size_y = sps->ctb_size_y; + fwsps->ctb_log2size_y = sps->ctb_log2size_y; + fwsps->max_pic_order_cnt_lsb = sps->max_pic_order_cnt_lsb; + + HEVC_STATIC_ASSERT(sizeof(sps->sps_max_latency_pictures) == + sizeof(fwsps->sps_max_latency_pictures)); + memcpy(fwsps->sps_max_latency_pictures, sps->sps_max_latency_pictures, + sizeof(fwsps->sps_max_latency_pictures[0]) * + (sps->sps_max_sub_layers_minus1 + 1)); +} + +static void bspp_hevc_fill_fwst_rps(struct bspp_hevc_shortterm_refpicset *strps, + struct hevcfw_short_term_ref_picset *fwstrps) +{ + fwstrps->num_delta_pocs = strps->num_delta_pocs; + fwstrps->num_negative_pics = strps->num_negative_pics; + fwstrps->num_positive_pics = strps->num_positive_pics; + + HEVC_STATIC_ASSERT(sizeof(strps->delta_poc_s0) == + sizeof(fwstrps->delta_poc_s0)); + memcpy(fwstrps->delta_poc_s0, strps->delta_poc_s0, + sizeof(fwstrps->delta_poc_s0[0]) * strps->num_negative_pics); + + HEVC_STATIC_ASSERT(sizeof(strps->delta_poc_s1) == + sizeof(fwstrps->delta_poc_s1)); + memcpy(fwstrps->delta_poc_s1, strps->delta_poc_s1, + sizeof(fwstrps->delta_poc_s1[0]) * strps->num_positive_pics); + + HEVC_STATIC_ASSERT(sizeof(strps->used_bycurr_pic_s0) == + sizeof(fwstrps->used_bycurr_pic_s0)); + memcpy(fwstrps->used_bycurr_pic_s0, strps->used_bycurr_pic_s0, + sizeof(fwstrps->used_bycurr_pic_s0[0]) * strps->num_negative_pics); + + HEVC_STATIC_ASSERT(sizeof(strps->used_bycurr_pic_s1) == + sizeof(fwstrps->used_bycurr_pic_s1)); + memcpy(fwstrps->used_bycurr_pic_s1, strps->used_bycurr_pic_s1, + sizeof(fwstrps->used_bycurr_pic_s1[0]) * strps->num_positive_pics); +} + +static void bspp_hevc_fill_fwpps(struct bspp_hevc_pps *pps, struct hevcfw_picture_ps *fw_pps) +{ + fw_pps->pps_pic_parameter_set_id = pps->pps_pic_parameter_set_id; + fw_pps->num_tile_columns_minus1 = pps->num_tile_columns_minus1; + fw_pps->num_tile_rows_minus1 = pps->num_tile_rows_minus1; + fw_pps->diff_cu_qp_delta_depth = pps->diff_cu_qp_delta_depth; + fw_pps->init_qp_minus26 = pps->init_qp_minus26; + fw_pps->pps_beta_offset_div2 = pps->pps_beta_offset_div2; + fw_pps->pps_tc_offset_div2 = pps->pps_tc_offset_div2; + fw_pps->pps_cb_qp_offset = pps->pps_cb_qp_offset; + fw_pps->pps_cr_qp_offset = pps->pps_cr_qp_offset; + fw_pps->log2_parallel_merge_level_minus2 = + pps->log2_parallel_merge_level_minus2; + + fw_pps->dependent_slice_segments_enabled_flag = + pps->dependent_slice_segments_enabled_flag; + fw_pps->output_flag_present_flag = pps->output_flag_present_flag; + fw_pps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits; + fw_pps->lists_modification_present_flag = + pps->lists_modification_present_flag; + fw_pps->cabac_init_present_flag = pps->cabac_init_present_flag; + fw_pps->weighted_pred_flag = pps->weighted_pred_flag; + fw_pps->weighted_bipred_flag = pps->weighted_bipred_flag; + fw_pps->pps_slice_chroma_qp_offsets_present_flag = + pps->pps_slice_chroma_qp_offsets_present_flag; + fw_pps->deblocking_filter_override_enabled_flag = + pps->deblocking_filter_override_enabled_flag; + fw_pps->tiles_enabled_flag = pps->tiles_enabled_flag; + fw_pps->entropy_coding_sync_enabled_flag = + pps->entropy_coding_sync_enabled_flag; + fw_pps->slice_segment_header_extension_present_flag = + pps->slice_segment_header_extension_present_flag; + fw_pps->transquant_bypass_enabled_flag = + pps->transquant_bypass_enabled_flag; + fw_pps->cu_qp_delta_enabled_flag = pps->cu_qp_delta_enabled_flag; + fw_pps->transform_skip_enabled_flag = pps->transform_skip_enabled_flag; + fw_pps->sign_data_hiding_enabled_flag = + pps->sign_data_hiding_enabled_flag; + fw_pps->num_ref_idx_l0_default_active_minus1 = + pps->num_ref_idx_l0_default_active_minus1; + fw_pps->num_ref_idx_l1_default_active_minus1 = + pps->num_ref_idx_l1_default_active_minus1; + fw_pps->constrained_intra_pred_flag = pps->constrained_intra_pred_flag; + fw_pps->pps_deblocking_filter_disabled_flag = + pps->pps_deblocking_filter_disabled_flag; + fw_pps->pps_loop_filter_across_slices_enabled_flag = + pps->pps_loop_filter_across_slices_enabled_flag; + fw_pps->loop_filter_across_tiles_enabled_flag = + pps->loop_filter_across_tiles_enabled_flag; + fw_pps->log2_max_transform_skip_block_size_minus2 = + pps->range_exts.log2_max_transform_skip_block_size_minus2; + fw_pps->cross_component_prediction_enabled_flag = + pps->range_exts.cross_component_prediction_enabled_flag; + fw_pps->chroma_qp_offset_list_enabled_flag = + pps->range_exts.chroma_qp_offset_list_enabled_flag; + fw_pps->diff_cu_chroma_qp_offset_depth = + pps->range_exts.diff_cu_chroma_qp_offset_depth; + fw_pps->chroma_qp_offset_list_len_minus1 = + pps->range_exts.chroma_qp_offset_list_len_minus1; + memcpy(fw_pps->cb_qp_offset_list, pps->range_exts.cb_qp_offset_list, + sizeof(pps->range_exts.cb_qp_offset_list)); + memcpy(fw_pps->cr_qp_offset_list, pps->range_exts.cr_qp_offset_list, + sizeof(pps->range_exts.cr_qp_offset_list)); + + /* derived elements */ + HEVC_STATIC_ASSERT(sizeof(pps->col_bd) == sizeof(fw_pps->col_bd)); + HEVC_STATIC_ASSERT(sizeof(pps->row_bd) == sizeof(fw_pps->row_bd)); + memcpy(fw_pps->col_bd, pps->col_bd, sizeof(fw_pps->col_bd)); + memcpy(fw_pps->row_bd, pps->row_bd, sizeof(fw_pps->row_bd)); +} + +static void bspp_hevc_fill_fw_scaling_lists(struct bspp_hevc_pps *pps, + struct bspp_hevc_sps *sps, + struct hevcfw_picture_ps *fw_pps) +{ + signed char size_id, matrix_id; + unsigned char *scalinglist; + /* + * We are starting at 1 to leave space for addresses, + * filled by lower layer + */ + unsigned int *scaling_lists = &fw_pps->scaling_lists[1]; + unsigned char i; + + struct bspp_hevc_scalinglist_data *scaling_listdata = + pps->pps_scaling_list_data_present_flag ? + &pps->scaling_list : + &sps->scalinglist_data; + + if (!sps->scaling_list_enabled_flag) + return; + + fw_pps->scaling_list_enabled_flag = sps->scaling_list_enabled_flag; + + for (size_id = HEVC_SCALING_LIST_NUM_SIZES - 1; + size_id >= 0; --size_id) { + const unsigned char *zz = + (size_id == 0 ? HEVC_INV_ZZ_SCAN4 : HEVC_INV_ZZ_SCAN8); + + for (matrix_id = 0; matrix_id < ((size_id == 3) ? 2 : 6); + ++matrix_id) { + /* + * Select scaling list on which we will operate + * in the iteration + */ + scalinglist = + scaling_listdata->lists[size_id][matrix_id]; + + for (i = 0; i < ((size_id == 0) ? 16 : 64); i += 4) { + *scaling_lists = + scalinglist[zz[i + 3]] << 24 | + scalinglist[zz[i + 2]] << 16 | + scalinglist[zz[i + 1]] << 8 | + scalinglist[zz[i]]; + scaling_lists += 2; + } + } + } + + for (i = 0; i < 2; ++i) { + *scaling_lists = scaling_listdata->dccoeffs[1][i]; + scaling_lists += 2; + } + + for (i = 0; i < 6; ++i) { + *scaling_lists = scaling_listdata->dccoeffs[0][i]; + scaling_lists += 2; + } +} + +static unsigned int bspp_ceil_log2(unsigned int linear_val) +{ + unsigned int log_val = 0; + + if (linear_val > 0) + --linear_val; + + while (linear_val > 0) { + linear_val >>= 1; + ++log_val; + } + + return log_val; +} + +static unsigned char bspp_hevc_picture_is_irap(enum hevc_nalunittype nalunit_type) +{ + return (nalunit_type >= HEVC_NALTYPE_BLA_W_LP) && + (nalunit_type <= HEVC_NALTYPE_RSV_IRAP_VCL23); +} + +static unsigned char bspp_hevc_picture_is_cra(enum hevc_nalunittype nalunit_type) +{ + return (nalunit_type == HEVC_NALTYPE_CRA); +} + +static unsigned char bspp_hevc_picture_is_idr(enum hevc_nalunittype nalunit_type) +{ + return (nalunit_type == HEVC_NALTYPE_IDR_N_LP) || + (nalunit_type == HEVC_NALTYPE_IDR_W_RADL); +} + +static unsigned char bspp_hevc_picture_is_bla(enum hevc_nalunittype nalunit_type) +{ + return (nalunit_type >= HEVC_NALTYPE_BLA_W_LP) && + (nalunit_type <= HEVC_NALTYPE_BLA_N_LP); +} + +static unsigned char bspp_hevc_picture_getnorasl_outputflag + (enum hevc_nalunittype nalunit_type, + struct bspp_hevc_inter_pict_ctx *inter_pict_ctx) +{ + VDEC_ASSERT(inter_pict_ctx); + + if (bspp_hevc_picture_is_idr(nalunit_type) || + bspp_hevc_picture_is_bla(nalunit_type) || + inter_pict_ctx->first_after_eos || + (bspp_hevc_picture_is_cra(nalunit_type) && inter_pict_ctx->seq_pic_count == 1)) + return 1; + + return 0; +} + +static unsigned char bspp_hevc_range_extensions_is_enabled + (struct bspp_hevc_profile_tierlevel *profile_tierlevel) +{ + unsigned char is_enabled; + + is_enabled = profile_tierlevel->general_profile_idc >= 4 || + profile_tierlevel->general_profile_compatibility_flag[4]; + + return is_enabled; +} + +static void bspp_hevc_parse_codec_config(void *hndl_swsr_ctx, unsigned int *unit_count, + unsigned int *unit_array_count, + unsigned int *delim_length, + unsigned int *size_delim_length) +{ + unsigned long long value = 23; + + /* + * Set the shift-register up to provide next 23 bytes + * without emulation prevention detection. + */ + swsr_consume_delim(hndl_swsr_ctx, SWSR_EMPREVENT_NONE, 0, &value); + /* + * Codec config header must be read for size delimited data (HEVC) + * to get to the start of each unit. + * This parsing follows section 8.3.3.1.2 of ISO/IEC 14496-15:2013. + */ + swsr_read_bits(hndl_swsr_ctx, 8 * 4); + swsr_read_bits(hndl_swsr_ctx, 8 * 4); + swsr_read_bits(hndl_swsr_ctx, 8 * 4); + swsr_read_bits(hndl_swsr_ctx, 8 * 4); + swsr_read_bits(hndl_swsr_ctx, 8 * 4); + swsr_read_bits(hndl_swsr_ctx, 8); + + *delim_length = ((swsr_read_bits(hndl_swsr_ctx, 8) & 0x3) + 1) * 8; + *unit_array_count = swsr_read_bits(hndl_swsr_ctx, 8); + + /* Size delimiter is only 2 bytes for HEVC codec configuration. */ + *size_delim_length = 2 * 8; +} + +static void bspp_hevc_update_unitcounts(void *hndl_swsr_ctx, unsigned int *unit_count, + unsigned int *unit_array_count) +{ + if (*unit_array_count != 0) { + unsigned long long value = 3; + + if (*unit_count == 0) { + /* + * Set the shift-register up to provide next 3 bytes + * without emulation prevention detection. + */ + swsr_consume_delim(hndl_swsr_ctx, SWSR_EMPREVENT_NONE, 0, &value); + + swsr_read_bits(hndl_swsr_ctx, 8); + *unit_count = swsr_read_bits(hndl_swsr_ctx, 16); + + (*unit_array_count)--; + (*unit_count)--; + } + } +} + +void bspp_hevc_determine_unittype(unsigned char bitstream_unittype, + int disable_mvc, + enum bspp_unit_type *bspp_unittype) +{ + /* 6 bits for NAL Unit Type in HEVC */ + unsigned char type = (bitstream_unittype >> 1) & 0x3f; + + switch (type) { + case HEVC_NALTYPE_VPS: + *bspp_unittype = BSPP_UNIT_VPS; + break; + + case HEVC_NALTYPE_SPS: + *bspp_unittype = BSPP_UNIT_SEQUENCE; + break; + + case HEVC_NALTYPE_PPS: + *bspp_unittype = BSPP_UNIT_PPS; + break; + + case HEVC_NALTYPE_TRAIL_N: + case HEVC_NALTYPE_TRAIL_R: + case HEVC_NALTYPE_TSA_N: + case HEVC_NALTYPE_TSA_R: + case HEVC_NALTYPE_STSA_N: + case HEVC_NALTYPE_STSA_R: + case HEVC_NALTYPE_RADL_N: + case HEVC_NALTYPE_RADL_R: + case HEVC_NALTYPE_RASL_N: + case HEVC_NALTYPE_RASL_R: + case HEVC_NALTYPE_BLA_W_LP: + case HEVC_NALTYPE_BLA_W_RADL: + case HEVC_NALTYPE_BLA_N_LP: + case HEVC_NALTYPE_IDR_W_RADL: + case HEVC_NALTYPE_IDR_N_LP: + case HEVC_NALTYPE_CRA: + case HEVC_NALTYPE_EOS: + /* Attach EOS to picture data, so it can be detected in FW */ + *bspp_unittype = BSPP_UNIT_PICTURE; + break; + + case HEVC_NALTYPE_AUD: + case HEVC_NALTYPE_PREFIX_SEI: + case HEVC_NALTYPE_SUFFIX_SEI: + case HEVC_NALTYPE_EOB: + case HEVC_NALTYPE_FD: + *bspp_unittype = BSPP_UNIT_NON_PICTURE; + break; + + default: + *bspp_unittype = BSPP_UNIT_UNSUPPORTED; + break; + } +} + +int bspp_hevc_set_parser_config(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *pvidstd_features, + struct bspp_swsr_ctx *pswsr_ctx, + struct bspp_parser_callbacks *parser_callbacks, + struct bspp_inter_pict_data *pinterpict_data) +{ + /* set HEVC parser callbacks. */ + parser_callbacks->parse_unit_cb = bspp_hevc_unitparser; + parser_callbacks->release_data_cb = bspp_hevc_releasedata; + parser_callbacks->reset_data_cb = bspp_hevc_resetdata; + parser_callbacks->parse_codec_config_cb = bspp_hevc_parse_codec_config; + parser_callbacks->update_unit_counts_cb = bspp_hevc_update_unitcounts; + parser_callbacks->initialise_parsing_cb = bspp_hevc_initialiseparsing; + parser_callbacks->finalise_parsing_cb = bspp_hevc_finaliseparsing; + + /* Set HEVC specific features. */ + pvidstd_features->seq_size = sizeof(struct bspp_hevc_sequ_hdr_info); + pvidstd_features->uses_vps = 1; + pvidstd_features->vps_size = sizeof(struct bspp_hevc_vps); + pvidstd_features->uses_pps = 1; + pvidstd_features->pps_size = sizeof(struct bspp_hevc_pps); + + /* Set HEVC specific shift register config. */ + pswsr_ctx->emulation_prevention = SWSR_EMPREVENT_00000300; + + if (bstr_format == VDEC_BSTRFORMAT_DEMUX_BYTESTREAM || + bstr_format == VDEC_BSTRFORMAT_ELEMENTARY) { + pswsr_ctx->sr_config.delim_type = SWSR_DELIM_SCP; + pswsr_ctx->sr_config.delim_length = 3 * 8; + pswsr_ctx->sr_config.scp_value = 0x000001; + } else if (bstr_format == VDEC_BSTRFORMAT_DEMUX_SIZEDELIMITED) { + pswsr_ctx->sr_config.delim_type = SWSR_DELIM_SIZE; + pswsr_ctx->sr_config.delim_length = 4 * 8; + } else { + return IMG_ERROR_NOT_SUPPORTED; + } + + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.h b/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.h --- a/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/hevc_secure_parser.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,455 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * h.264 secure data unit parsing API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreming + * Sidraya Jayagond + */ +#ifndef __HEVCSECUREPARSER_H__ +#define __HEVCSECUREPARSER_H__ + +#include "bspp_int.h" + +#define HEVC_MAX_NUM_PROFILE_IDC (32) +#define HEVC_MAX_NUM_SUBLAYERS (7) +#define HEVC_MAX_VPS_OP_SETS_PLUS1 (1024) +#define HEVC_MAX_VPS_NUH_RESERVED_ZERO_LAYER_ID_PLUS1 (1) +#define HEVC_MAX_NUM_REF_PICS (16) +#define HEVC_MAX_NUM_ST_REF_PIC_SETS (65) +#define HEVC_MAX_NUM_LT_REF_PICS (32) +#define HEVC_MAX_NUM_REF_IDX_ACTIVE (15) +#define HEVC_LEVEL_IDC_MIN (30) +#define HEVC_LEVEL_IDC_MAX (186) +#define HEVC_1_0_PROFILE_IDC_MAX (3) +#define HEVC_MAX_CPB_COUNT (32) +#define HEVC_MIN_CODED_UNIT_SIZE (8) + +/* hevc scaling lists (all values are maximum possible ones) */ +#define HEVC_SCALING_LIST_NUM_SIZES (4) +#define HEVC_SCALING_LIST_NUM_MATRICES (6) +#define HEVC_SCALING_LIST_MATRIX_SIZE (64) + +#define HEVC_MAX_TILE_COLS (20) +#define HEVC_MAX_TILE_ROWS (22) + +#define HEVC_EXTENDED_SAR (255) + +#define HEVC_MAX_CHROMA_QP (6) + +enum hevc_nalunittype { + HEVC_NALTYPE_TRAIL_N = 0, + HEVC_NALTYPE_TRAIL_R = 1, + HEVC_NALTYPE_TSA_N = 2, + HEVC_NALTYPE_TSA_R = 3, + HEVC_NALTYPE_STSA_N = 4, + HEVC_NALTYPE_STSA_R = 5, + HEVC_NALTYPE_RADL_N = 6, + HEVC_NALTYPE_RADL_R = 7, + HEVC_NALTYPE_RASL_N = 8, + HEVC_NALTYPE_RASL_R = 9, + HEVC_NALTYPE_RSV_VCL_N10 = 10, + HEVC_NALTYPE_RSV_VCL_R11 = 11, + HEVC_NALTYPE_RSV_VCL_N12 = 12, + HEVC_NALTYPE_RSV_VCL_R13 = 13, + HEVC_NALTYPE_RSV_VCL_N14 = 14, + HEVC_NALTYPE_RSV_VCL_R15 = 15, + HEVC_NALTYPE_BLA_W_LP = 16, + HEVC_NALTYPE_BLA_W_RADL = 17, + HEVC_NALTYPE_BLA_N_LP = 18, + HEVC_NALTYPE_IDR_W_RADL = 19, + HEVC_NALTYPE_IDR_N_LP = 20, + HEVC_NALTYPE_CRA = 21, + HEVC_NALTYPE_RSV_IRAP_VCL22 = 22, + HEVC_NALTYPE_RSV_IRAP_VCL23 = 23, + HEVC_NALTYPE_VPS = 32, + HEVC_NALTYPE_SPS = 33, + HEVC_NALTYPE_PPS = 34, + HEVC_NALTYPE_AUD = 35, + HEVC_NALTYPE_EOS = 36, + HEVC_NALTYPE_EOB = 37, + HEVC_NALTYPE_FD = 38, + HEVC_NALTYPE_PREFIX_SEI = 39, + HEVC_NALTYPE_SUFFIX_SEI = 40, + HEVC_NALTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +enum bspp_hevcslicetype { + HEVC_SLICE_B = 0, + HEVC_SLICE_P = 1, + HEVC_SLICE_I = 2, + HEVC_SLICE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* HEVC NAL unit header */ +struct bspp_hevcnalheader { + unsigned char nal_unit_type; + unsigned char nuh_layer_id; + unsigned char nuh_temporal_id_plus1; +}; + +/* HEVC video profile_tier_level */ +struct bspp_hevc_profile_tierlevel { + unsigned char general_profile_space; + unsigned char general_tier_flag; + unsigned char general_profile_idc; + unsigned char general_profile_compatibility_flag[HEVC_MAX_NUM_PROFILE_IDC]; + unsigned char general_progressive_source_flag; + unsigned char general_interlaced_source_flag; + unsigned char general_non_packed_constraint_flag; + unsigned char general_frame_only_constraint_flag; + unsigned char general_max_12bit_constraint_flag; + unsigned char general_max_10bit_constraint_flag; + unsigned char general_max_8bit_constraint_flag; + unsigned char general_max_422chroma_constraint_flag; + unsigned char general_max_420chroma_constraint_flag; + unsigned char general_max_monochrome_constraint_flag; + unsigned char general_intra_constraint_flag; + unsigned char general_one_picture_only_constraint_flag; + unsigned char general_lower_bit_rate_constraint_flag; + unsigned char general_level_idc; + unsigned char sub_layer_profile_present_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_level_present_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_profile_space[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_tier_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_profile_idc[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_profile_compatibility_flag[HEVC_MAX_NUM_SUBLAYERS - + 1][HEVC_MAX_NUM_PROFILE_IDC]; + unsigned char sub_layer_progressive_source_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_interlaced_source_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_non_packed_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_frame_only_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_12bit_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_10bit_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_8bit_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_422chroma_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_420chroma_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_max_monochrome_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_intra_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_one_picture_only_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_lower_bit_rate_constraint_flag[HEVC_MAX_NUM_SUBLAYERS - 1]; + unsigned char sub_layer_level_idc[HEVC_MAX_NUM_SUBLAYERS - 1]; +}; + +/* HEVC sub layer HRD parameters */ +struct bspp_hevc_sublayer_hrd_parameters { + unsigned char bit_rate_value_minus1[HEVC_MAX_CPB_COUNT]; + unsigned char cpb_size_value_minus1[HEVC_MAX_CPB_COUNT]; + unsigned char cpb_size_du_value_minus1[HEVC_MAX_CPB_COUNT]; + unsigned char bit_rate_du_value_minus1[HEVC_MAX_CPB_COUNT]; + unsigned char cbr_flag[HEVC_MAX_CPB_COUNT]; +}; + +/* HEVC HRD parameters */ +struct bspp_hevc_hrd_parameters { + unsigned char nal_hrd_parameters_present_flag; + unsigned char vcl_hrd_parameters_present_flag; + unsigned char sub_pic_hrd_params_present_flag; + unsigned char tick_divisor_minus2; + unsigned char du_cpb_removal_delay_increment_length_minus1; + unsigned char sub_pic_cpb_params_in_pic_timing_sei_flag; + unsigned char dpb_output_delay_du_length_minus1; + unsigned char bit_rate_scale; + unsigned char cpb_size_scale; + unsigned char cpb_size_du_scale; + unsigned char initial_cpb_removal_delay_length_minus1; + unsigned char au_cpb_removal_delay_length_minus1; + unsigned char dpb_output_delay_length_minus1; + unsigned char fixed_pic_rate_general_flag[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char fixed_pic_rate_within_cvs_flag[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char elemental_duration_in_tc_minus1[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char low_delay_hrd_flag[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char cpb_cnt_minus1[HEVC_MAX_NUM_SUBLAYERS]; + struct bspp_hevc_sublayer_hrd_parameters sublayhrdparams[HEVC_MAX_NUM_SUBLAYERS]; +}; + +/* HEVC video parameter set */ +struct bspp_hevc_vps { + unsigned char is_different; + unsigned char is_sent; + unsigned char is_available; + unsigned char vps_video_parameter_set_id; + unsigned char vps_reserved_three_2bits; + unsigned char vps_max_layers_minus1; + unsigned char vps_max_sub_layers_minus1; + unsigned char vps_temporal_id_nesting_flag; + unsigned short vps_reserved_0xffff_16bits; + struct bspp_hevc_profile_tierlevel profiletierlevel; + unsigned char vps_max_dec_pic_buffering_minus1[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char vps_max_num_reorder_pics[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char vps_max_latency_increase_plus1[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char vps_sub_layer_ordering_info_present_flag; + unsigned char vps_max_layer_id; + unsigned char vps_num_layer_sets_minus1; + unsigned char layer_id_included_flag[HEVC_MAX_VPS_OP_SETS_PLUS1] + [HEVC_MAX_VPS_NUH_RESERVED_ZERO_LAYER_ID_PLUS1]; + unsigned char vps_timing_info_present_flag; + unsigned int vps_num_units_in_tick; + unsigned int vps_time_scale; + unsigned char vps_poc_proportional_to_timing_flag; + unsigned char vps_num_ticks_poc_diff_one_minus1; + unsigned char vps_num_hrd_parameters; + unsigned char *hrd_layer_set_idx; + unsigned char *cprms_present_flag; + unsigned char vps_extension_flag; + unsigned char vps_extension_data_flag; +}; + +/* HEVC scaling lists */ +struct bspp_hevc_scalinglist_data { + unsigned char dccoeffs[HEVC_SCALING_LIST_NUM_SIZES - 2][HEVC_SCALING_LIST_NUM_MATRICES]; + unsigned char lists[HEVC_SCALING_LIST_NUM_SIZES][HEVC_SCALING_LIST_NUM_MATRICES] + [HEVC_SCALING_LIST_MATRIX_SIZE]; +}; + +/* HEVC short term reference picture set */ +struct bspp_hevc_shortterm_refpicset { + unsigned char num_negative_pics; + unsigned char num_positive_pics; + short delta_poc_s0[HEVC_MAX_NUM_REF_PICS]; + short delta_poc_s1[HEVC_MAX_NUM_REF_PICS]; + unsigned char used_bycurr_pic_s0[HEVC_MAX_NUM_REF_PICS]; + unsigned char used_bycurr_pic_s1[HEVC_MAX_NUM_REF_PICS]; + unsigned char num_delta_pocs; +}; + +/* HEVC video usability information */ +struct bspp_hevc_vui_params { + unsigned char aspect_ratio_info_present_flag; + unsigned char aspect_ratio_idc; + unsigned short sar_width; + unsigned short sar_height; + unsigned char overscan_info_present_flag; + unsigned char overscan_appropriate_flag; + unsigned char video_signal_type_present_flag; + unsigned char video_format; + unsigned char video_full_range_flag; + unsigned char colour_description_present_flag; + unsigned char colour_primaries; + unsigned char transfer_characteristics; + unsigned char matrix_coeffs; + unsigned char chroma_loc_info_present_flag; + unsigned char chroma_sample_loc_type_top_field; + unsigned char chroma_sample_loc_type_bottom_field; + unsigned char neutral_chroma_indication_flag; + unsigned char field_seq_flag; + unsigned char frame_field_info_present_flag; + unsigned char default_display_window_flag; + unsigned short def_disp_win_left_offset; + unsigned short def_disp_win_right_offset; + unsigned short def_disp_win_top_offset; + unsigned short def_disp_win_bottom_offset; + unsigned char vui_timing_info_present_flag; + unsigned int vui_num_units_in_tick; + unsigned int vui_time_scale; + unsigned char vui_poc_proportional_to_timing_flag; + unsigned int vui_num_ticks_poc_diff_one_minus1; + unsigned char vui_hrd_parameters_present_flag; + struct bspp_hevc_hrd_parameters vui_hrd_params; + unsigned char bitstream_restriction_flag; + unsigned char tiles_fixed_structure_flag; + unsigned char motion_vectors_over_pic_boundaries_flag; + unsigned char restricted_ref_pic_lists_flag; + unsigned short min_spatial_segmentation_idc; + unsigned char max_bytes_per_pic_denom; + unsigned char max_bits_per_min_cu_denom; + unsigned char log2_max_mv_length_horizontal; + unsigned char log2_max_mv_length_vertical; +}; + +/* HEVC sps range extensions */ +struct bspp_hevc_sps_range_exts { + unsigned char transform_skip_rotation_enabled_flag; + unsigned char transform_skip_context_enabled_flag; + unsigned char implicit_rdpcm_enabled_flag; + unsigned char explicit_rdpcm_enabled_flag; + unsigned char extended_precision_processing_flag; + unsigned char intra_smoothing_disabled_flag; + unsigned char high_precision_offsets_enabled_flag; + unsigned char persistent_rice_adaptation_enabled_flag; + unsigned char cabac_bypass_alignment_enabled_flag; +}; + +/* HEVC sequence parameter set */ +struct bspp_hevc_sps { + unsigned char is_different; + unsigned char is_sent; + unsigned char is_available; + unsigned char sps_video_parameter_set_id; + unsigned char sps_max_sub_layers_minus1; + unsigned char sps_temporal_id_nesting_flag; + struct bspp_hevc_profile_tierlevel profile_tier_level; + unsigned char sps_seq_parameter_set_id; + unsigned char chroma_format_idc; + unsigned char separate_colour_plane_flag; + unsigned int pic_width_in_luma_samples; + unsigned int pic_height_in_luma_samples; + unsigned char conformance_window_flag; + unsigned short conf_win_left_offset; + unsigned short conf_win_right_offset; + unsigned short conf_win_top_offset; + unsigned short conf_win_bottom_offset; + unsigned char bit_depth_luma_minus8; + unsigned char bit_depth_chroma_minus8; + unsigned char log2_max_pic_order_cnt_lsb_minus4; + unsigned char sps_sub_layer_ordering_info_present_flag; + unsigned char sps_max_dec_pic_buffering_minus1[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char sps_max_num_reorder_pics[HEVC_MAX_NUM_SUBLAYERS]; + unsigned int sps_max_latency_increase_plus1[HEVC_MAX_NUM_SUBLAYERS]; + unsigned char log2_min_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_luma_coding_block_size; + unsigned char log2_min_transform_block_size_minus2; + unsigned char log2_diff_max_min_transform_block_size; + unsigned char max_transform_hierarchy_depth_inter; + unsigned char max_transform_hierarchy_depth_intra; + unsigned char scaling_list_enabled_flag; + unsigned char sps_scaling_list_data_present_flag; + struct bspp_hevc_scalinglist_data scalinglist_data; + unsigned char amp_enabled_flag; + unsigned char sample_adaptive_offset_enabled_flag; + unsigned char pcm_enabled_flag; + unsigned char pcm_sample_bit_depth_luma_minus1; + unsigned char pcm_sample_bit_depth_chroma_minus1; + unsigned char log2_min_pcm_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_pcm_luma_coding_block_size; + unsigned char pcm_loop_filter_disabled_flag; + unsigned char num_short_term_ref_pic_sets; + struct bspp_hevc_shortterm_refpicset rps_list[HEVC_MAX_NUM_ST_REF_PIC_SETS]; + unsigned char long_term_ref_pics_present_flag; + unsigned char num_long_term_ref_pics_sps; + unsigned short lt_ref_pic_poc_lsb_sps[HEVC_MAX_NUM_LT_REF_PICS]; + unsigned char used_by_curr_pic_lt_sps_flag[HEVC_MAX_NUM_LT_REF_PICS]; + unsigned char sps_temporal_mvp_enabled_flag; + unsigned char strong_intra_smoothing_enabled_flag; + unsigned char vui_parameters_present_flag; + struct bspp_hevc_vui_params vui_params; + unsigned char sps_extension_present_flag; + unsigned char sps_range_extensions_flag; + struct bspp_hevc_sps_range_exts range_exts; + unsigned char sps_extension_7bits; + unsigned char sps_extension_data_flag; + /* derived elements */ + unsigned char sub_width_c; + unsigned char sub_height_c; + unsigned char ctb_log2size_y; + unsigned char ctb_size_y; + unsigned int pic_width_in_ctbs_y; + unsigned int pic_height_in_ctbs_y; + unsigned int pic_size_in_ctbs_y; + int max_pic_order_cnt_lsb; + unsigned int sps_max_latency_pictures[HEVC_MAX_NUM_SUBLAYERS]; + /* raw vui data as extracted from bitstream. */ + struct bspp_raw_bitstream_data *vui_raw_data; +}; + +/** + * struct bspp_hevc_sequ_hdr_info - This structure contains HEVC sequence + * header information (VPS, SPS, VUI) + * contains everything parsed from the + * video/sequence header. + * @vps: HEVC sequence header information + * @sps:HEVC sequence header information + */ +struct bspp_hevc_sequ_hdr_info { + struct bspp_hevc_vps vps; + struct bspp_hevc_sps sps; +}; + +/* HEVC pps range extensions */ +struct bspp_hevc_pps_range_exts { + unsigned char log2_max_transform_skip_block_size_minus2; + unsigned char cross_component_prediction_enabled_flag; + unsigned char chroma_qp_offset_list_enabled_flag; + unsigned char diff_cu_chroma_qp_offset_depth; + unsigned char chroma_qp_offset_list_len_minus1; + unsigned char cb_qp_offset_list[HEVC_MAX_CHROMA_QP]; + unsigned char cr_qp_offset_list[HEVC_MAX_CHROMA_QP]; + unsigned char log2_sao_offset_scale_luma; + unsigned char log2_sao_offset_scale_chroma; +}; + +/* HEVC picture parameter set */ +struct bspp_hevc_pps { + unsigned char is_available; + unsigned char is_param_copied; + unsigned char pps_pic_parameter_set_id; + unsigned char pps_seq_parameter_set_id; + unsigned char dependent_slice_segments_enabled_flag; + unsigned char output_flag_present_flag; + unsigned char num_extra_slice_header_bits; + unsigned char sign_data_hiding_enabled_flag; + unsigned char cabac_init_present_flag; + unsigned char num_ref_idx_l0_default_active_minus1; + unsigned char num_ref_idx_l1_default_active_minus1; + unsigned char init_qp_minus26; + unsigned char constrained_intra_pred_flag; + unsigned char transform_skip_enabled_flag; + unsigned char cu_qp_delta_enabled_flag; + unsigned char diff_cu_qp_delta_depth; + int pps_cb_qp_offset; + int pps_cr_qp_offset; + unsigned char pps_slice_chroma_qp_offsets_present_flag; + unsigned char weighted_pred_flag; + unsigned char weighted_bipred_flag; + unsigned char transquant_bypass_enabled_flag; + unsigned char tiles_enabled_flag; + unsigned char entropy_coding_sync_enabled_flag; + unsigned char num_tile_columns_minus1; + unsigned char num_tile_rows_minus1; + unsigned char uniform_spacing_flag; + unsigned char column_width_minus1[HEVC_MAX_TILE_COLS]; + unsigned char row_height_minus1[HEVC_MAX_TILE_ROWS]; + unsigned char loop_filter_across_tiles_enabled_flag; + unsigned char pps_loop_filter_across_slices_enabled_flag; + unsigned char deblocking_filter_control_present_flag; + unsigned char deblocking_filter_override_enabled_flag; + unsigned char pps_deblocking_filter_disabled_flag; + unsigned char pps_beta_offset_div2; + unsigned char pps_tc_offset_div2; + unsigned char pps_scaling_list_data_present_flag; + struct bspp_hevc_scalinglist_data scaling_list; + unsigned char lists_modification_present_flag; + unsigned char log2_parallel_merge_level_minus2; + unsigned char slice_segment_header_extension_present_flag; + unsigned char pps_extension_present_flag; + unsigned char pps_range_extensions_flag; + struct bspp_hevc_pps_range_exts range_exts; + unsigned char pps_extension_7bits; + unsigned char pps_extension_data_flag; + /* derived elements */ + unsigned short col_bd[HEVC_MAX_TILE_COLS + 1]; + unsigned short row_bd[HEVC_MAX_TILE_ROWS + 1]; + /* PVDEC derived elements */ + unsigned int max_tile_height_in_ctbs_y; +}; + +/* HEVC slice segment header */ +struct bspp_hevc_slice_segment_header { + unsigned char bslice_is_idr; + unsigned char first_slice_segment_in_pic_flag; + unsigned char no_output_of_prior_pics_flag; + unsigned char slice_pic_parameter_set_id; + unsigned char dependent_slice_segment_flag; + unsigned int slice_segment_address; +}; + +/* + * @Function bspp_hevc_set_parser_config + * sets the parser configuration. + */ +int bspp_hevc_set_parser_config(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *pvidstd_features, + struct bspp_swsr_ctx *pswsr_ctx, + struct bspp_parser_callbacks *pparser_callbacks, + struct bspp_inter_pict_data *pinterpict_data); + +void bspp_hevc_determine_unittype(unsigned char bitstream_unittype, + int disable_mvc, + enum bspp_unit_type *bspp_unittype); + +#endif /*__H264SECUREPARSER_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/hw_control.c b/drivers/media/platform/vxe-vxd/decoder/hw_control.c --- a/drivers/media/platform/vxe-vxd/decoder/hw_control.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/hw_control.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD DEC Hardware control implementation + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include +#include + +#include "decoder.h" +#include "hw_control.h" +#include "img_msvdx_vdmc_regs.h" +#include "img_pvdec_core_regs.h" +#include "img_pvdec_pixel_regs.h" +#include "img_pvdec_test_regs.h" +#include "img_vdec_fw_msg.h" +#include "img_video_bus4_mmu_regs.h" +#include "img_msvdx_core_regs.h" +#include "reg_io2.h" +#include "vdecdd_defs.h" +#include "vxd_dec.h" +#include "vxd_ext.h" +#include "vxd_int.h" +#include "vxd_pvdec_priv.h" + +#define MSG_GROUP_MASK 0xf0 + +struct hwctrl_ctx { + unsigned int is_initialised; + unsigned int is_on_seq_replay; + unsigned int replay_tid; + unsigned int num_pipes; + struct vdecdd_dd_devconfig devconfig; + void *hndl_vxd; + void *dec_core; + void *comp_init_userdata; + struct vidio_ddbufinfo dev_ptd_bufinfo; + struct lst_t pend_pict_list; + struct hwctrl_msgstatus host_msg_status; + void *hmsg_task_event; + void *hmsg_task_kick; + void *hmsg_task; + unsigned int is_msg_task_active; + struct hwctrl_state state; + struct hwctrl_state prev_state; + unsigned int is_prev_hw_state_set; + unsigned int is_fatal_state; +}; + +struct vdeckm_context { + unsigned int core_num; + struct vxd_coreprops props; + unsigned short current_msgid; + unsigned char reader_active; + void *comms_ram_addr; + unsigned int state_offset; + unsigned int state_size; +}; + +/* + * Panic reason identifier. + */ +enum pvdec_panic_reason { + PANIC_REASON_OTHER = 0, + PANIC_REASON_WDT, + PANIC_REASON_READ_TIMEOUT, + PANIC_REASON_CMD_TIMEOUT, + PANIC_REASON_MMU_FAULT, + PANIC_REASON_MAX, + PANIC_REASON_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Panic reason strings. + * NOTE: Should match the pvdec_panic_reason ids. + */ +static unsigned char *apanic_reason[PANIC_REASON_MAX] = { + [PANIC_REASON_OTHER] = "Other", + [PANIC_REASON_WDT] = "Watch Dog Timeout", + [PANIC_REASON_READ_TIMEOUT] = "Read Timeout", + [PANIC_REASON_CMD_TIMEOUT] = "Command Timeout", + [PANIC_REASON_MMU_FAULT] = "MMU Page Fault" +}; + +/* + * Maximum length of the panic reason string. + */ +#define PANIC_REASON_LEN (255) + +static struct vdeckm_context acore_ctx[VXD_MAX_CORES] = {0}; + +static int vdeckm_getregsoffsets(const void *hndl_vxd, + struct decoder_regsoffsets *regs_offsets) +{ + struct vdeckm_context *core_ctx = (struct vdeckm_context *)hndl_vxd; + + if (!core_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + regs_offsets->vdmc_cmd_offset = MSVDX_CMD_OFFSET; + regs_offsets->vec_offset = MSVDX_VEC_OFFSET; + regs_offsets->entropy_offset = PVDEC_ENTROPY_OFFSET; + regs_offsets->vec_be_regs_offset = PVDEC_VEC_BE_OFFSET; + regs_offsets->vdec_be_codec_regs_offset = PVDEC_VEC_BE_CODEC_OFFSET; + + return IMG_SUCCESS; +} + +static int vdeckm_send_message(const void *hndl_vxd, + struct hwctrl_to_kernel_msg *to_kernelmsg, + void *vxd_dec_ctx) +{ + struct vdeckm_context *core_ctx = (struct vdeckm_context *)hndl_vxd; + unsigned int count = 0; + unsigned int *msg; + + if (!core_ctx || !to_kernelmsg) + return IMG_ERROR_INVALID_PARAMETERS; + + msg = kzalloc(VXD_SIZE_MSG_BUFFER, GFP_KERNEL); + if (!msg) + return IMG_ERROR_OUT_OF_MEMORY; + + msg[count++] = to_kernelmsg->flags; + msg[count++] = to_kernelmsg->msg_size; + + memcpy(&msg[count], to_kernelmsg->msg_hdr, to_kernelmsg->msg_size); + + core_ctx->reader_active = 1; + + if (!(to_kernelmsg->msg_hdr)) { + kfree(msg); + return IMG_ERROR_INVALID_PARAMETERS; + } + + pr_debug("[HWCTRL] adding message to vxd queue\n"); + vxd_send_msg(vxd_dec_ctx, (struct vxd_fw_msg *)msg); + + kfree(msg); + + return 0; +} + +static void vdeckm_return_msg(const void *hndl_vxd, + struct hwctrl_to_kernel_msg *to_kernelmsg) +{ + if (to_kernelmsg) + kfree(to_kernelmsg->msg_hdr); +} + +static int vdeckm_handle_mtxtohost_msg(unsigned int *msg, struct lst_t *pend_pict_list, + enum vxd_msg_attr *msg_attr, + struct dec_decpict **decpict, + unsigned char msg_type, + unsigned int trans_id) +{ + struct dec_decpict *pdec_pict; + + switch (msg_type) { + case FW_DEVA_COMPLETED: + { + struct dec_pict_attrs *pict_attrs = NULL; + unsigned short error_flags = 0; + unsigned int no_bewdts = 0; + unsigned int mbs_dropped = 0; + unsigned int mbs_recovered = 0; + unsigned char flag = 0; + + pr_debug("Received message from firmware\n"); + error_flags = MEMIO_READ_FIELD(msg, FW_DEVA_COMPLETED_ERROR_FLAGS); + + no_bewdts = MEMIO_READ_FIELD(msg, FW_DEVA_COMPLETED_NUM_BEWDTS); + + mbs_dropped = MEMIO_READ_FIELD(msg, FW_DEVA_COMPLETED_NUM_MBSDROPPED); + + mbs_recovered = MEMIO_READ_FIELD(msg, FW_DEVA_COMPLETED_NUM_MBSRECOVERED); + + pdec_pict = lst_first(pend_pict_list); + while (pdec_pict) { + if (pdec_pict->transaction_id == trans_id) + break; + pdec_pict = lst_next(pdec_pict); + } + /* + * We must have a picture in the list that matches + * the transaction id + */ + if (!pdec_pict) + return IMG_ERROR_FATAL; + + if (!(pdec_pict->first_fld_fwmsg) || !(pdec_pict->second_fld_fwmsg)) + return IMG_ERROR_FATAL; + + flag = pdec_pict->first_fld_fwmsg->pict_attrs.first_fld_rcvd; + if (flag) { + pict_attrs = &pdec_pict->second_fld_fwmsg->pict_attrs; + } else { + pict_attrs = &pdec_pict->first_fld_fwmsg->pict_attrs; + flag = 1; + } + + pict_attrs->fe_err = (unsigned int)error_flags; + pict_attrs->no_be_wdt = no_bewdts; + pict_attrs->mbs_dropped = mbs_dropped; + pict_attrs->mbs_recovered = mbs_recovered; + /* + * We may successfully replayed the picture, + * so reset the error flags + */ + pict_attrs->pict_attrs.dwrfired = 0; + pict_attrs->pict_attrs.mmufault = 0; + pict_attrs->pict_attrs.deverror = 0; + + *msg_attr = VXD_MSG_ATTR_DECODED; + *decpict = pdec_pict; + break; + } + + case FW_DEVA_PANIC: + { + unsigned int panic_info = MEMIO_READ_FIELD(msg, FW_DEVA_PANIC_ERROR_INT); + unsigned char panic_reason[PANIC_REASON_LEN] = "Reason(s): "; + unsigned char is_panic_reson_identified = 0; + /* + * Create panic reason string. + */ + if (REGIO_READ_FIELD(panic_info, PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, + CR_HOST_SYS_WDT)) { + strncat(panic_reason, apanic_reason[PANIC_REASON_WDT], + PANIC_REASON_LEN - 1); + is_panic_reson_identified = 1; + } + if (REGIO_READ_FIELD(panic_info, PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, + CR_HOST_READ_TIMEOUT_PROC_IRQ)) { + strncat(panic_reason, apanic_reason[PANIC_REASON_READ_TIMEOUT], + PANIC_REASON_LEN - 1); + is_panic_reson_identified = 1; + } + if (REGIO_READ_FIELD(panic_info, PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, + CR_HOST_COMMAND_TIMEOUT_PROC_IRQ)) { + strncat(panic_reason, apanic_reason[PANIC_REASON_CMD_TIMEOUT], + PANIC_REASON_LEN - 1); + is_panic_reson_identified = 1; + } + if (!is_panic_reson_identified) { + strncat(panic_reason, apanic_reason[PANIC_REASON_OTHER], + PANIC_REASON_LEN - 1); + } + panic_reason[strlen(panic_reason) - 2] = 0; + if (trans_id != 0) + pr_err("TID=0x%08X [FIRMWARE PANIC %s]\n", trans_id, panic_reason); + else + pr_err("TID=NULL [GENERAL FIRMWARE PANIC %s]\n", panic_reason); + + break; + } + + case FW_ASSERT: + { + unsigned int fwfile_namehash = MEMIO_READ_FIELD(msg, FW_ASSERT_FILE_NAME_HASH); + unsigned int fwfile_line = MEMIO_READ_FIELD(msg, FW_ASSERT_FILE_LINE); + + pr_err("ASSERT file name hash:0x%08X line number:%d\n", + fwfile_namehash, fwfile_line); + break; + } + + case FW_SO: + { + unsigned int task_name = MEMIO_READ_FIELD(msg, FW_SO_TASK_NAME); + unsigned char sztaskname[sizeof(unsigned int) + 1]; + + sztaskname[0] = task_name >> 24; + sztaskname[1] = (task_name >> 16) & 0xff; + sztaskname[2] = (task_name >> 8) & 0xff; + sztaskname[3] = task_name & 0xff; + if (sztaskname[3] != 0) + sztaskname[4] = 0; + pr_warn("STACK OVERFLOW for %s task\n", sztaskname); + break; + } + + case FW_VXD_EMPTY_COMPL: + /* + * Empty completion message sent as response to init, + * configure etc The architecture of vxd.ko module + * requires the firmware to send a reply for every + * message submitted by the user space. + */ + break; + + default: + break; + } + + return 0; +} + +static int vdeckm_handle_hosttomtx_msg(unsigned int *msg, struct lst_t *pend_pict_list, + enum vxd_msg_attr *msg_attr, + struct dec_decpict **decpict, + unsigned char msg_type, + unsigned int trans_id, + unsigned int msg_flags) +{ + struct dec_decpict *pdec_pict; + + pr_debug("Received message from HOST\n"); + + switch (msg_type) { + case FW_DEVA_PARSE: + { + struct dec_pict_attrs *pict_attrs = NULL; + unsigned char flag = 0; + + pdec_pict = lst_first(pend_pict_list); + while (pdec_pict) { + if (pdec_pict->transaction_id == trans_id) + break; + + pdec_pict = lst_next(pdec_pict); + } + + /* + * We must have a picture in the list that matches + * the transaction id + */ + if (!pdec_pict) { + pr_err("Firmware decoded message received\n"); + pr_err("no pending picture\n"); + return IMG_ERROR_FATAL; + } + + if (!(pdec_pict->first_fld_fwmsg) || !(pdec_pict->second_fld_fwmsg)) { + pr_err("invalid pending picture struct\n"); + return IMG_ERROR_FATAL; + } + + flag = pdec_pict->first_fld_fwmsg->pict_attrs.first_fld_rcvd; + if (flag) { + pict_attrs = &pdec_pict->second_fld_fwmsg->pict_attrs; + } else { + pict_attrs = &pdec_pict->first_fld_fwmsg->pict_attrs; + flag = 1; + } + + /* + * The below info is fetched from firmware state + * afterwards, so just set this to zero for now. + */ + pict_attrs->fe_err = 0; + pict_attrs->no_be_wdt = 0; + pict_attrs->mbs_dropped = 0; + pict_attrs->mbs_recovered = 0; + + vxd_get_pictattrs(msg_flags, &pict_attrs->pict_attrs); + vxd_get_msgerrattr(msg_flags, msg_attr); + + if (*msg_attr == VXD_MSG_ATTR_FATAL) + pr_err("[TID=0x%08X] [DECODE_FAILED]\n", trans_id); + if (*msg_attr == VXD_MSG_ATTR_CANCELED) + pr_err("[TID=0x%08X] [DECODE_CANCELED]\n", trans_id); + + *decpict = pdec_pict; + break; + } + + case FW_DEVA_PARSE_FRAGMENT: + /* + * Do nothing - Picture holds the list of fragments. + * So, in case of any error those would be replayed + * anyway. + */ + break; + default: + pr_warn("Unknown message received 0x%02x\n", msg_type); + break; + } + + return 0; +} + +static int vdeckm_process_msg(const void *hndl_vxd, unsigned int *msg, + struct lst_t *pend_pict_list, + unsigned int msg_flags, + enum vxd_msg_attr *msg_attr, + struct dec_decpict **decpict) +{ + struct vdeckm_context *core_ctx = (struct vdeckm_context *)hndl_vxd; + unsigned char msg_type; + unsigned char msg_group; + unsigned int trans_id = 0; + struct vdec_pict_hwcrc *pict_hwcrc = NULL; + struct dec_decpict *pdec_pict; + + if (!core_ctx || !msg || !msg_attr || !pend_pict_list || !decpict) + return IMG_ERROR_INVALID_PARAMETERS; + + *msg_attr = VXD_MSG_ATTR_NONE; + *decpict = NULL; + + trans_id = MEMIO_READ_FIELD(msg, FW_DEVA_GENMSG_TRANS_ID); + msg_type = MEMIO_READ_FIELD(msg, FW_DEVA_GENMSG_MSG_TYPE); + msg_group = msg_type & MSG_GROUP_MASK; + + switch (msg_group) { + case MSG_TYPE_START_PSR_MTXHOST_MSG: + vdeckm_handle_mtxtohost_msg(msg, pend_pict_list, msg_attr, + decpict, msg_type, trans_id); + break; + /* + * Picture decode has been returned as unprocessed. + * Locate the picture with corresponding TID and mark + * it as decoded with errors. + */ + case MSG_TYPE_START_PSR_HOSTMTX_MSG: + vdeckm_handle_hosttomtx_msg(msg, pend_pict_list, msg_attr, + decpict, msg_type, trans_id, + msg_flags); + break; + + case FW_DEVA_SIGNATURES_HEVC: + case FW_DEVA_SIGNATURES_LEGACY: + { + unsigned int *signatures = msg + (FW_DEVA_SIGNATURES_SIGNATURES_OFFSET / + sizeof(unsigned int)); + unsigned char sigcount = MEMIO_READ_FIELD(msg, FW_DEVA_SIGNATURES_MSG_SIZE) - + ((FW_DEVA_SIGNATURES_SIZE / sizeof(unsigned int)) - 1); + unsigned int selected = MEMIO_READ_FIELD(msg, FW_DEVA_SIGNATURES_SIGNATURE_SELECT); + unsigned char i, j = 0; + + pdec_pict = lst_first(pend_pict_list); + while (pdec_pict) { + if (pdec_pict->transaction_id == trans_id) + break; + pdec_pict = lst_next(pdec_pict); + } + + /* We must have a picture in the list that matches the tid */ + VDEC_ASSERT(pdec_pict); + if (!pdec_pict) { + pr_err("Firmware signatures message received with no pending picture\n"); + return IMG_ERROR_FATAL; + } + + VDEC_ASSERT(pdec_pict->first_fld_fwmsg); + VDEC_ASSERT(pdec_pict->second_fld_fwmsg); + if (!pdec_pict->first_fld_fwmsg || !pdec_pict->second_fld_fwmsg) { + pr_err("Invalid pending picture struct\n"); + return IMG_ERROR_FATAL; + } + if (pdec_pict->first_fld_fwmsg->pict_hwcrc.first_fld_rcvd) { + pict_hwcrc = &pdec_pict->second_fld_fwmsg->pict_hwcrc; + } else { + pict_hwcrc = &pdec_pict->first_fld_fwmsg->pict_hwcrc; + if (selected & (PVDEC_SIGNATURE_GROUP_20 | PVDEC_SIGNATURE_GROUP_24)) + pdec_pict->first_fld_fwmsg->pict_hwcrc.first_fld_rcvd = TRUE; + } + + for (i = 0; i < 32; i++) { + unsigned int group = selected & (1 << i); + + switch (group) { + case PVDEC_SIGNATURE_GROUP_20: + pict_hwcrc->crc_vdmc_pix_recon = signatures[j++]; + break; + + case PVDEC_SIGNATURE_GROUP_24: + pict_hwcrc->vdeb_sysmem_wrdata = signatures[j++]; + break; + + default: + break; + } + } + + /* sanity check */ + sigcount -= j; + VDEC_ASSERT(sigcount == 0); + + /* + * suppress PVDEC_SIGNATURE_GROUP_1 and notify + * only about groups used for verification + */ +#ifdef DEBUG_DECODER_DRIVER + if (selected & (PVDEC_SIGNATURE_GROUP_20 | PVDEC_SIGNATURE_GROUP_24)) + pr_info("[TID=0x%08X] [SIGNATURES]\n", trans_id); +#endif + + *decpict = pdec_pict; + + break; + } + + default: { +#ifdef DEBUG_DECODER_DRIVER + unsigned short msg_size, i; + + pr_warn("Unknown message type received: 0x%x", msg_type); + + msg_size = MEMIO_READ_FIELD(msg, FW_DEVA_GENMSG_MSG_SIZE); + + for (i = 0; i < msg_size; i++) + pr_info("0x%04x: 0x%08x\n", i, msg[i]); +#endif + break; + } + } + + return 0; +} + +static void vdeckm_vlr_copy(void *dst, void *src, unsigned int size) +{ + unsigned int *pdst = (unsigned int *)dst; + unsigned int *psrc = (unsigned int *)src; + + size /= 4; + while (size--) + *pdst++ = *psrc++; +} + +static int vdeckm_get_core_state(const void *hndl_vxd, struct vxd_states *state) +{ + struct vdeckm_context *core_ctx = (struct vdeckm_context *)hndl_vxd; + struct vdecfw_pvdecfirmwarestate firmware_state; + unsigned char pipe = 0; + +#ifdef ERROR_RECOVERY_SIMULATION + /* + * if disable_fw_irq_value is not zero, return error. If processed further + * the kernel will crash because we have ignored the interrupt, but here + * we will try to access comms_ram_addr which will result in crash. + */ + if (disable_fw_irq_value != 0) + return IMG_ERROR_INVALID_PARAMETERS; +#endif + + if (!core_ctx || !state) + return IMG_ERROR_INVALID_PARAMETERS; + + /* + * If state is requested for the first time. + */ + if (core_ctx->state_size == 0) { + unsigned int regval; + /* + * get the state buffer info. + */ + regval = *((unsigned int *)core_ctx->comms_ram_addr + + (PVDEC_COM_RAM_STATE_BUF_SIZE_AND_OFFSET_OFFSET / sizeof(unsigned int))); + core_ctx->state_size = PVDEC_COM_RAM_BUF_GET_SIZE(regval, STATE); + core_ctx->state_offset = PVDEC_COM_RAM_BUF_GET_OFFSET(regval, STATE); + } + + /* + * If state buffer is available. + */ + if (core_ctx->state_size) { + /* + * Determine the latest transaction to have passed each + * checkpoint in the firmware. + * Read the firmware state from VEC Local RAM + */ + vdeckm_vlr_copy(&firmware_state, (unsigned char *)core_ctx->comms_ram_addr + + core_ctx->state_offset, core_ctx->state_size); + + for (pipe = 0; pipe < core_ctx->props.num_pixel_pipes; pipe++) { + /* + * Set pipe presence. + */ + state->fw_state.pipe_state[pipe].is_pipe_present = 1; + + /* + * For checkpoints copy message ids here. These will + * be translated into transaction ids later. + */ + memcpy(state->fw_state.pipe_state[pipe].acheck_point, + firmware_state.pipestate[pipe].check_point, + sizeof(state->fw_state.pipe_state[pipe].acheck_point)); + state->fw_state.pipe_state[pipe].firmware_action = + firmware_state.pipestate[pipe].firmware_action; + state->fw_state.pipe_state[pipe].cur_codec = + firmware_state.pipestate[pipe].curr_codec; + state->fw_state.pipe_state[pipe].fe_slices = + firmware_state.pipestate[pipe].fe_slices; + state->fw_state.pipe_state[pipe].be_slices = + firmware_state.pipestate[pipe].be_slices; + state->fw_state.pipe_state[pipe].fe_errored_slices = + firmware_state.pipestate[pipe].fe_errored_slices; + state->fw_state.pipe_state[pipe].be_errored_slices = + firmware_state.pipestate[pipe].be_errored_slices; + state->fw_state.pipe_state[pipe].be_mbs_dropped = + firmware_state.pipestate[pipe].be_mbs_dropped; + state->fw_state.pipe_state[pipe].be_mbs_recovered = + firmware_state.pipestate[pipe].be_mbs_recovered; + state->fw_state.pipe_state[pipe].fe_mb.x = + firmware_state.pipestate[pipe].last_fe_mb_xy & 0xFF; + state->fw_state.pipe_state[pipe].fe_mb.y = + (firmware_state.pipestate[pipe].last_fe_mb_xy >> 16) & 0xFF; + state->fw_state.pipe_state[pipe].be_mb.x = + REGIO_READ_FIELD(firmware_state.pipestate[pipe].last_be_mb_xy, + MSVDX_VDMC, + CR_VDMC_MACROBLOCK_NUMBER, + CR_VDMC_MACROBLOCK_X_OFFSET); + state->fw_state.pipe_state[pipe].be_mb.y = + REGIO_READ_FIELD(firmware_state.pipestate[pipe].last_be_mb_xy, + MSVDX_VDMC, + CR_VDMC_MACROBLOCK_NUMBER, + CR_VDMC_MACROBLOCK_Y_OFFSET); + } + } + + return 0; +} + +static int vdeckm_prepare_batch(struct vdeckm_context *core_ctx, + const struct hwctrl_batch_msgdata *batch_msgdata, + unsigned char **msg) +{ + unsigned char vdec_flags = 0; + unsigned short flags = 0; + unsigned char *pmsg = kzalloc(FW_DEVA_DECODE_SIZE, GFP_KERNEL); + struct vidio_ddbufinfo *pbatch_msg_bufinfo = batch_msgdata->batchmsg_bufinfo; + + if (!pmsg) + return IMG_ERROR_MALLOC_FAILED; + + if (batch_msgdata->size_delimited_mode) + vdec_flags |= FW_VDEC_NAL_SIZE_DELIM; + + flags |= FW_DEVA_RENDER_HOST_INT; + + /* + * Message type and stream ID + */ + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_GENMSG_MSG_TYPE, FW_DEVA_PARSE, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_CTRL_ALLOC_ADDR, + (unsigned int)pbatch_msg_bufinfo->dev_virt, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_BUFFER_SIZE, + batch_msgdata->ctrl_alloc_bytes / sizeof(unsigned int), unsigned char*); + + /* + * Operating mode and decode flags + */ + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_OPERATING_MODE, batch_msgdata->operating_mode, + unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_FLAGS, flags, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_VDEC_FLAGS, vdec_flags, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_GENC_ID, batch_msgdata->genc_id, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_MB_LOAD, batch_msgdata->mb_load, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_STREAMID, + GET_STREAM_ID(batch_msgdata->transaction_id), unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_EXT_STATE_BUFFER, + (unsigned int)batch_msgdata->pvdec_fwctx->dev_virt, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_MSG_ID, ++core_ctx->current_msgid, + unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_TRANS_ID, batch_msgdata->transaction_id, + unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_TILE_CFG, batch_msgdata->tile_cfg, unsigned char*); + + /* + * size of message + */ + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_GENMSG_MSG_SIZE, + FW_DEVA_DECODE_SIZE / sizeof(unsigned int), unsigned char*); + + *msg = pmsg; + + return 0; +} + +static int vdeckm_prepare_fragment(struct vdeckm_context *core_ctx, + const struct hwctrl_fragment_msgdata + *fragment_msgdata, + unsigned char **msg) +{ + struct vidio_ddbufinfo *pbatch_msg_bufinfo = NULL; + unsigned char *pmsg = NULL; + + pbatch_msg_bufinfo = fragment_msgdata->batchmsg_bufinfo; + + if (!(fragment_msgdata->batchmsg_bufinfo)) { + pr_err("Batch message info missing!\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + pmsg = kzalloc(FW_DEVA_DECODE_FRAGMENT_SIZE, GFP_KERNEL); + if (!pmsg) + return IMG_ERROR_MALLOC_FAILED; + /* + * message type and stream id + */ + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_GENMSG_MSG_TYPE, + FW_DEVA_PARSE_FRAGMENT, unsigned char*); + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_MSG_ID, ++core_ctx->current_msgid, unsigned char*); + + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_FRAGMENT_CTRL_ALLOC_ADDR, + (unsigned int)pbatch_msg_bufinfo->dev_virt + + fragment_msgdata->ctrl_alloc_offset, unsigned char*); + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_DECODE_FRAGMENT_BUFFER_SIZE, + fragment_msgdata->ctrl_alloc_bytes / sizeof(unsigned int), + unsigned char*); + + /* + * size of message + */ + MEMIO_WRITE_FIELD(pmsg, FW_DEVA_GENMSG_MSG_SIZE, + FW_DEVA_DECODE_FRAGMENT_SIZE / sizeof(unsigned int), unsigned char*); + + *msg = pmsg; + + return 0; +} + +static int vdeckm_get_message(const void *hndl_vxd, const enum hwctrl_msgid msgid, + const struct hwctrl_msgdata *msgdata, + struct hwctrl_to_kernel_msg *to_kernelmsg) +{ + unsigned int result = 0; + struct vdeckm_context *core_ctx = (struct vdeckm_context *)hndl_vxd; + + if (!core_ctx || !to_kernelmsg || !msgdata) + return IMG_ERROR_INVALID_PARAMETERS; + + switch (msgid) { + case HWCTRL_MSGID_BATCH: + result = vdeckm_prepare_batch(core_ctx, &msgdata->batch_msgdata, + &to_kernelmsg->msg_hdr); + break; + + case HWCTRL_MSGID_FRAGMENT: + result = vdeckm_prepare_fragment(core_ctx, &msgdata->fragment_msgdata, + &to_kernelmsg->msg_hdr); + vxd_set_msgflag(VXD_MSG_FLAG_DROP, &to_kernelmsg->flags); + break; + + default: + result = IMG_ERROR_GENERIC_FAILURE; + pr_err("got a message that is not supported by PVDEC"); + break; + } + + if (result == 0) { + /* Set the stream ID for the next message to be sent. */ + to_kernelmsg->km_str_id = msgdata->km_str_id; + to_kernelmsg->msg_size = MEMIO_READ_FIELD(to_kernelmsg->msg_hdr, + FW_DEVA_GENMSG_MSG_SIZE) * + sizeof(unsigned int); + } + + return result; +} + +static void hwctrl_dump_state(struct vxd_states *prev_state, + struct vxd_states *cur_state, + unsigned char pipe_minus1) +{ + pr_info("Back-End MbX [% 10d]", + prev_state->fw_state.pipe_state[pipe_minus1].be_mb.x); + pr_info("Back-End MbY [% 10d]", + prev_state->fw_state.pipe_state[pipe_minus1].be_mb.y); + pr_info("Front-End MbX [% 10d]", + prev_state->fw_state.pipe_state[pipe_minus1].fe_mb.x); + pr_info("Front-End MbY [% 10d]", + prev_state->fw_state.pipe_state[pipe_minus1].fe_mb.y); + pr_info("VDECFW_CHECKPOINT_BE_PICTURE_COMPLETE [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_BE_PICTURE_COMPLETE]); + pr_info("VDECFW_CHECKPOINT_BE_1SLICE_DONE [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_BE_1SLICE_DONE]); + pr_info("VDECFW_CHECKPOINT_BE_PICTURE_STARTED [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_BE_PICTURE_STARTED]); + pr_info("VDECFW_CHECKPOINT_FE_PICTURE_COMPLETE [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_FE_PICTURE_COMPLETE]); + pr_info("VDECFW_CHECKPOINT_FE_PARSE_DONE [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_FE_PARSE_DONE]); + pr_info("VDECFW_CHECKPOINT_FE_1SLICE_DONE [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_FE_1SLICE_DONE]); + pr_info("VDECFW_CHECKPOINT_ENTDEC_STARTED [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_ENTDEC_STARTED]); + pr_info("VDECFW_CHECKPOINT_FIRMWARE_SAVED [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_FIRMWARE_SAVED]); + pr_info("VDECFW_CHECKPOINT_PICMAN_COMPLETE [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_PICMAN_COMPLETE]); + pr_info("VDECFW_CHECKPOINT_FIRMWARE_READY [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_FIRMWARE_READY]); + pr_info("VDECFW_CHECKPOINT_PICTURE_STARTED [0x%08X]", + cur_state->fw_state.pipe_state[pipe_minus1].acheck_point + [VDECFW_CHECKPOINT_PICTURE_STARTED]); +} + +static unsigned int hwctrl_calculate_load(struct bspp_pict_hdr_info *pict_hdr_info) +{ + return (((pict_hdr_info->coded_frame_size.width + 15) / 16) + * ((pict_hdr_info->coded_frame_size.height + 15) / 16)); +} + +static int hwctrl_send_batch_message(struct hwctrl_ctx *hwctx, + struct dec_decpict *decpict, + void *vxd_dec_ctx) +{ + int result; + struct hwctrl_to_kernel_msg to_kernelmsg = {0}; + struct vidio_ddbufinfo *batchmsg_bufinfo = + decpict->batch_msginfo->ddbuf_info; + struct hwctrl_msgdata msg_data; + struct hwctrl_batch_msgdata *batch_msgdata = &msg_data.batch_msgdata; + + memset(&msg_data, 0, sizeof(msg_data)); + + msg_data.km_str_id = GET_STREAM_ID(decpict->transaction_id); + + batch_msgdata->batchmsg_bufinfo = batchmsg_bufinfo; + + batch_msgdata->transaction_id = decpict->transaction_id; + batch_msgdata->pvdec_fwctx = decpict->str_pvdec_fw_ctxbuf; + batch_msgdata->ctrl_alloc_bytes = decpict->ctrl_alloc_bytes; + batch_msgdata->operating_mode = decpict->operating_op; + batch_msgdata->genc_id = decpict->genc_id; + batch_msgdata->mb_load = hwctrl_calculate_load(decpict->pict_hdr_info); + batch_msgdata->size_delimited_mode = + (decpict->pict_hdr_info->parser_mode != VDECFW_SCP_ONLY) ? + (1) : (0); + + result = vdeckm_get_message(hwctx->hndl_vxd, HWCTRL_MSGID_BATCH, + &msg_data, &to_kernelmsg); + if (result != 0) { + pr_err("failed to get decode message\n"); + return result; + } + + pr_debug("[HWCTRL] send batch message\n"); + result = vdeckm_send_message(hwctx->hndl_vxd, &to_kernelmsg, + vxd_dec_ctx); + if (result != 0) + return result; + + vdeckm_return_msg(hwctx->hndl_vxd, &to_kernelmsg); + + return 0; +} + +int hwctrl_process_msg(void *hndl_hwctx, unsigned int msg_flags, unsigned int *msg, + struct dec_decpict **decpict) +{ + int result; + struct hwctrl_ctx *hwctx; + enum vxd_msg_attr msg_attr = VXD_MSG_ATTR_NONE; + struct dec_decpict *pdecpict = NULL; + unsigned int val_first = 0; + unsigned int val_sec = 0; + + if (!hndl_hwctx || !msg || !decpict) { + VDEC_ASSERT(0); + return IMG_ERROR_INVALID_PARAMETERS; + } + + hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + *decpict = NULL; + + pr_debug("[HWCTRL] : process message\n"); + result = vdeckm_process_msg(hwctx->hndl_vxd, msg, &hwctx->pend_pict_list, msg_flags, + &msg_attr, &pdecpict); + + /* validate pointers before using them */ + if (!pdecpict || !pdecpict->first_fld_fwmsg || !pdecpict->second_fld_fwmsg) { + VDEC_ASSERT(0); + return -EIO; + } + + val_first = pdecpict->first_fld_fwmsg->pict_attrs.pict_attrs.deverror; + val_sec = pdecpict->second_fld_fwmsg->pict_attrs.pict_attrs.deverror; + + if (val_first || val_sec) + pr_err("device signaled critical error!!!\n"); + + if (msg_attr == VXD_MSG_ATTR_DECODED) { + pdecpict->state = DECODER_PICTURE_STATE_DECODED; + /* + * We have successfully decoded a picture as normally or + * after the replay. + * Mark HW is in good state. + */ + hwctx->is_fatal_state = 0; + } else if (msg_attr == VXD_MSG_ATTR_FATAL) { + struct hwctrl_state state; + unsigned char pipe_minus1 = 0; + + memset(&state, 0, sizeof(state)); + + result = hwctrl_get_core_status(hwctx, &state); + if (result == 0) { + hwctx->is_prev_hw_state_set = 1; + memcpy(&hwctx->prev_state, &state, sizeof(struct hwctrl_state)); + + for (pipe_minus1 = 0; pipe_minus1 < hwctx->num_pipes; + pipe_minus1++) { + hwctrl_dump_state(&state.core_state, &state.core_state, + pipe_minus1); + } + } + } + *decpict = pdecpict; + + return 0; +} + +int hwctrl_getcore_cached_status(void *hndl_hwctx, struct hwctrl_state *state) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + if (hwctx->is_prev_hw_state_set) + memcpy(state, &hwctx->prev_state, sizeof(struct hwctrl_state)); + else + return IMG_ERROR_UNEXPECTED_STATE; + + return 0; +} + +int hwctrl_get_core_status(void *hndl_hwctx, struct hwctrl_state *state) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + unsigned int result = IMG_ERROR_GENERIC_FAILURE; + + if (!hwctx->is_fatal_state && state) { + struct vxd_states *pcorestate = NULL; + + pcorestate = &state->core_state; + + memset(pcorestate, 0, sizeof(*(pcorestate))); + + result = vdeckm_get_core_state(hwctx->hndl_vxd, pcorestate); + } + + return result; +} + +int hwctrl_is_on_seq_replay(void *hndl_hwctx) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + return hwctx->is_on_seq_replay; +} + +int hwctrl_picture_submitbatch(void *hndl_hwctx, struct dec_decpict *decpict, void *vxd_dec_ctx) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + if (hwctx->is_initialised) { + lst_add(&hwctx->pend_pict_list, decpict); + if (!hwctx->is_on_seq_replay) + return hwctrl_send_batch_message(hwctx, decpict, vxd_dec_ctx); + } + + return 0; +} + +int hwctrl_getpicpend_pictlist(void *hndl_hwctx, unsigned int transaction_id, + struct dec_decpict **decpict) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + struct dec_decpict *dec_pic; + + dec_pic = lst_first(&hwctx->pend_pict_list); + while (dec_pic) { + if (dec_pic->transaction_id == transaction_id) { + *decpict = dec_pic; + break; + } + dec_pic = lst_next(dec_pic); + } + + if (!dec_pic) + return IMG_ERROR_INVALID_ID; + + return 0; +} + +int hwctrl_peekheadpiclist(void *hndl_hwctx, struct dec_decpict **decpict) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + if (hwctx) + *decpict = lst_first(&hwctx->pend_pict_list); + + if (*decpict) + return 0; + + return IMG_ERROR_GENERIC_FAILURE; +} + +int hwctrl_getdecodedpicture(void *hndl_hwctx, struct dec_decpict **decpict) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + if (hwctx) { + struct dec_decpict *cur_decpict; + /* + * Ensure that this picture is in the list. + */ + cur_decpict = lst_first(&hwctx->pend_pict_list); + while (cur_decpict) { + if (cur_decpict->state == DECODER_PICTURE_STATE_DECODED) { + *decpict = cur_decpict; + return 0; + } + + cur_decpict = lst_next(cur_decpict); + } + } + + return IMG_ERROR_VALUE_OUT_OF_RANGE; +} + +void hwctrl_removefrom_piclist(void *hndl_hwctx, struct dec_decpict *decpict) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + if (hwctx) { + struct dec_decpict *cur_decpict; + /* + * Ensure that this picture is in the list. + */ + cur_decpict = lst_first(&hwctx->pend_pict_list); + while (cur_decpict) { + if (cur_decpict == decpict) { + lst_remove(&hwctx->pend_pict_list, decpict); + break; + } + + cur_decpict = lst_next(cur_decpict); + } + } +} + +int hwctrl_getregsoffset(void *hndl_hwctx, struct decoder_regsoffsets *regs_offsets) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + return vdeckm_getregsoffsets(hwctx->hndl_vxd, regs_offsets); +} + +static int pvdec_create(struct vxd_dev *vxd, struct vxd_coreprops *core_props, + void **hndl_vdeckm_context) +{ + struct vdeckm_context *corectx; + struct vxd_core_props hndl_core_props; + int result; + + if (!hndl_vdeckm_context || !core_props) + return IMG_ERROR_INVALID_PARAMETERS; + + /* + * Obtain core context. + */ + corectx = &acore_ctx[0]; + + memset(corectx, 0, sizeof(*corectx)); + + corectx->core_num = 0; + + result = vxd_pvdec_get_props(vxd->dev, vxd->reg_base, &hndl_core_props); + if (result != 0) + return result; + + vxd_get_coreproperties(&hndl_core_props, &corectx->props); + + memcpy(core_props, &corectx->props, sizeof(*core_props)); + + *hndl_vdeckm_context = corectx; + + return 0; +} + +int hwctrl_deinitialise(void *hndl_hwctx) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + + if (hwctx->is_initialised) { + kfree(hwctx); + hwctx = NULL; + } + + return 0; +} + +int hwctrl_initialise(void *dec_core, void *comp_int_userdata, + const struct vdecdd_dd_devconfig *dd_devconfig, + struct vxd_coreprops *core_props, void **hndl_hwctx) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)*hndl_hwctx; + int result; + + if (!hwctx) { + hwctx = kzalloc(sizeof(*(hwctx)), GFP_KERNEL); + if (!hwctx) + return IMG_ERROR_OUT_OF_MEMORY; + + *hndl_hwctx = hwctx; + } + + if (!hwctx->is_initialised) { + hwctx->hndl_vxd = ((struct dec_core_ctx *)dec_core)->dec_ctx->dev_handle; + result = pvdec_create(hwctx->hndl_vxd, core_props, &hwctx->hndl_vxd); + if (result != 0) + goto error; + + lst_init(&hwctx->pend_pict_list); + + hwctx->devconfig = *dd_devconfig; + hwctx->num_pipes = core_props->num_pixel_pipes; + hwctx->comp_init_userdata = comp_int_userdata; + hwctx->dec_core = dec_core; + hwctx->is_initialised = 1; + hwctx->is_on_seq_replay = 0; + hwctx->is_fatal_state = 0; + } + + return 0; +error: + hwctrl_deinitialise(*hndl_hwctx); + + return result; +} + +static int hwctrl_send_fragment_message(struct hwctrl_ctx *hwctx, + struct dec_pict_fragment *pict_fragment, + struct dec_decpict *decpict, + void *vxd_dec_ctx) +{ + int result; + struct hwctrl_to_kernel_msg to_kernelmsg = {0}; + struct hwctrl_msgdata msg_data; + struct hwctrl_fragment_msgdata *pfragment_msgdata = + &msg_data.fragment_msgdata; + + msg_data.km_str_id = GET_STREAM_ID(decpict->transaction_id); + + pfragment_msgdata->ctrl_alloc_bytes = pict_fragment->ctrl_alloc_bytes; + + pfragment_msgdata->ctrl_alloc_offset = pict_fragment->ctrl_alloc_offset; + + pfragment_msgdata->batchmsg_bufinfo = decpict->batch_msginfo->ddbuf_info; + + result = vdeckm_get_message(hwctx->hndl_vxd, HWCTRL_MSGID_FRAGMENT, &msg_data, + &to_kernelmsg); + if (result != 0) { + pr_err("Failed to get decode message\n"); + return result; + } + + result = vdeckm_send_message(hwctx->hndl_vxd, &to_kernelmsg, vxd_dec_ctx); + if (result != 0) + return result; + + vdeckm_return_msg(hwctx->hndl_vxd, &to_kernelmsg); + + return 0; +} + +int hwctrl_picture_submit_fragment(void *hndl_hwctx, + struct dec_pict_fragment *pict_fragment, + struct dec_decpict *decpict, + void *vxd_dec_ctx) +{ + struct hwctrl_ctx *hwctx = (struct hwctrl_ctx *)hndl_hwctx; + unsigned int result = 0; + + if (hwctx->is_initialised) { + result = hwctrl_send_fragment_message(hwctx, pict_fragment, + decpict, vxd_dec_ctx); + if (result != 0) + pr_err("Failed to send fragment message to firmware !"); + } + + return result; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/hw_control.h b/drivers/media/platform/vxe-vxd/decoder/hw_control.h --- a/drivers/media/platform/vxe-vxd/decoder/hw_control.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/hw_control.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC Hardware control implementation + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef _HW_CONTROL_H +#define _HW_CONTROL_H + +#include "bspp.h" +#include "decoder.h" +#include "fw_interface.h" +#include "img_dec_common.h" +#include "img_errors.h" +#include "lst.h" +#include "mem_io.h" +#include "vdecdd_defs.h" +#include "vdecfw_shared.h" +#include "vid_buf.h" +#include "vxd_ext.h" +#include "vxd_props.h" + +/* Size of additional buffers needed for each HEVC picture */ +#ifdef HAS_HEVC + +/* Empirically defined */ +#define MEM_TO_REG_BUF_SIZE 0x2000 + +/* + * Max. no. of slices found in stream db: approx. 2200, + * set MAX_SLICES to 2368 to get buffer size page aligned + */ +#define MAX_SLICES 2368 +#define SLICE_PARAMS_SIZE 64 +#define SLICE_PARAMS_BUF_SIZE (MAX_SLICES * SLICE_PARAMS_SIZE) + +/* + * Size of buffer for "above params" structure, sufficient for stream of width 8192 + * 192 * (8192/64) == 0x6000, see "above_param_size" in TRM + */ +#define ABOVE_PARAMS_BUF_SIZE 0x6000 +#endif + +enum hwctrl_msgid { + HWCTRL_MSGID_BATCH = 0, + HWCTRL_MSGID_FRAGMENT = 1, + CORE_MSGID_MAX, + CORE_MSGID_FORCE32BITS = 0x7FFFFFFFU +}; + +struct hwctrl_to_kernel_msg { + unsigned int msg_size; + unsigned int km_str_id; + unsigned int flags; + unsigned char *msg_hdr; +}; + +struct hwctrl_batch_msgdata { + struct vidio_ddbufinfo *batchmsg_bufinfo; + struct vidio_ddbufinfo *pvdec_fwctx; + unsigned int ctrl_alloc_bytes; + unsigned int operating_mode; + unsigned int transaction_id; + unsigned int tile_cfg; + unsigned int genc_id; + unsigned int mb_load; + unsigned int size_delimited_mode; +}; + +struct hwctrl_fragment_msgdata { + struct vidio_ddbufinfo *batchmsg_bufinfo; + unsigned int ctrl_alloc_offset; + unsigned int ctrl_alloc_bytes; +}; + +struct hwctrl_msgdata { + unsigned int km_str_id; + struct hwctrl_batch_msgdata batch_msgdata; + struct hwctrl_fragment_msgdata fragment_msgdata; +}; + +/* + * This structure contains MSVDX Message information. + */ +struct hwctrl_msgstatus { + unsigned char control_fence_id[VDECFW_MSGID_CONTROL_TYPES]; + unsigned char decode_fence_id[VDECFW_MSGID_DECODE_TYPES]; + unsigned char completion_fence_id[VDECFW_MSGID_COMPLETION_TYPES]; +}; + +/* + * this structure contains the HWCTRL Core state. + */ +struct hwctrl_state { + struct vxd_states core_state; + struct hwctrl_msgstatus fwmsg_status; + struct hwctrl_msgstatus hostmsg_status; +}; + +int hwctrl_picture_submit_fragment(void *hndl_hwctx, + struct dec_pict_fragment *pict_fragment, + struct dec_decpict *decpict, + void *vxd_dec_ctx); + +int hwctrl_process_msg(void *hndl_hwct, unsigned int msg_flags, unsigned int *msg, + struct dec_decpict **decpict); + +int hwctrl_getcore_cached_status(void *hndl_hwctx, struct hwctrl_state *state); + +int hwctrl_get_core_status(void *hndl_hwctx, struct hwctrl_state *state); + +int hwctrl_is_on_seq_replay(void *hndl_hwctx); + +int hwctrl_picture_submitbatch(void *hndl_hwctx, struct dec_decpict *decpict, + void *vxd_dec_ctx); + +int hwctrl_getpicpend_pictlist(void *hndl_hwctx, unsigned int transaction_id, + struct dec_decpict **decpict); + +int hwctrl_peekheadpiclist(void *hndl_hwctx, struct dec_decpict **decpict); + +int hwctrl_getdecodedpicture(void *hndl_hwctx, struct dec_decpict **decpict); + +void hwctrl_removefrom_piclist(void *hndl_hwctx, struct dec_decpict *decpict); + +int hwctrl_getregsoffset(void *hndl_hwctx, + struct decoder_regsoffsets *regs_offsets); + +int hwctrl_initialise(void *dec_core, void *comp_int_userdata, + const struct vdecdd_dd_devconfig *dd_devconfig, + struct vxd_coreprops *core_props, void **hndl_hwctx); + +int hwctrl_deinitialise(void *hndl_hwctx); + +#endif /* _HW_CONTROL_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_dec_common.h b/drivers/media/platform/vxe-vxd/decoder/img_dec_common.h --- a/drivers/media/platform/vxe-vxd/decoder/img_dec_common.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_dec_common.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG DEC common header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef _IMG_DEC_COMMON_H +#define _IMG_DEC_COMMON_H + +#include + +#define VXD_MAX_PIPES 2 +#define MAX_DST_BUFFERS 32 + +/* Helpers for parsing core properties. Based on HW registers layout. */ +#define VXD_GET_BITS(v, lb, rb, type) \ + ({ \ + type __rb = (rb); \ + (((v) >> (__rb)) & ((1 << ((lb) - __rb + 1)) - 1)); }) +#define VXD_GET_BIT(v, b) (((v) >> (b)) & 1) + +/* Get major core revision. */ +#define VXD_MAJ_REV(props) (VXD_GET_BITS((props).core_rev, 23, 16, unsigned int)) +/* Get minor core revision. */ +#define VXD_MIN_REV(props) (VXD_GET_BITS((props).core_rev, 15, 8, unsigned int)) +/* Get maint core revision. */ +#define VXD_MAINT_REV(props) (VXD_GET_BITS((props).core_rev, 7, 0, unsigned int)) +/* Get number of entropy pipes available (HEVC). */ +#define VXD_NUM_ENT_PIPES(props) ((props).pvdec_core_id & 0xF) +/* Get number of pixel pipes available (other standards). */ +#define VXD_NUM_PIX_PIPES(props) (((props).pvdec_core_id & 0xF0) >> 4) +/* Get number of bits used by external memory interface. */ +#define VXD_EXTRN_ADDR_WIDTH(props) ((((props).mmu_config0 & 0xF0) >> 4) + 32) + +/* Check whether specific standard is supported by the pixel pipe. */ +#define VXD_HAS_MPEG2(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 0) +#define VXD_HAS_MPEG4(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 1) +#define VXD_HAS_H264(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 2) +#define VXD_HAS_VC1(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 3) +#define VXD_HAS_WMV9(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 4) +#define VXD_HAS_JPEG(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 5) +#define VXD_HAS_MPEG4_DATA_PART(props, pipe) \ + VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 6) +#define VXD_HAS_AVS(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 7) +#define VXD_HAS_REAL(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 8) +#define VXD_HAS_VP6(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 9) +#define VXD_HAS_VP8(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 10) +#define VXD_HAS_SORENSON(props, pipe) \ + VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 11) +#define VXD_HAS_HEVC(props, pipe) VXD_GET_BIT(props.pixel_pipe_cfg[pipe], 22) + +/* Check whether specific feature is supported by the pixel pipe */ + +/* + * Max picture size for HEVC still picture profile is 64k wide and/or 64k + * high. + */ +#define VXD_HAS_HEVC_64K_STILL(props, pipe) \ + (VXD_GET_BIT((props).pixel_misc_cfg[pipe], 24)) + +/* Pixel processing pipe index. */ +#define VXD_PIX_PIPE_ID(props, pipe) \ + (VXD_GET_BITS((props).pixel_misc_cfg[pipe], 18, 16, unsigned int)) + +/* Number of stream supported by the pixel pipe DMAC and shift register. */ +#define VXD_PIX_NUM_STRS(props, pipe) \ + (VXD_GET_BITS((props).pixel_misc_cfg[pipe], 13, 12, unsigned int) + 1) + +/* Is scaling supported. */ +#define VXD_HAS_SCALING(props, pipe) \ + (VXD_GET_BIT((props).pixel_misc_cfg[pipe], 9)) + +/* Is rotation supported. */ +#define VXD_HAS_ROTATION(props, pipe) \ + (VXD_GET_BIT((props).pixel_misc_cfg[pipe], 8)) + +/* Are HEVC range extensions supported. */ +#define VXD_HAS_HEVC_REXT(props, pipe) \ + (VXD_GET_BIT((props).pixel_misc_cfg[pipe], 7)) + +/* Maximum bit depth supported by the pipe. */ +#define VXD_MAX_BIT_DEPTH(props, pipe) \ + (VXD_GET_BITS((props).pixel_misc_cfg[pipe], 6, 4, unsigned int) + 8) + +/* + * Maximum chroma fomar supported by the pipe in HEVC mode. + * 0x1 - 4:2:0 + * 0x2 - 4:2:2 + * 0x3 - 4:4:4 + */ +#define VXD_MAX_HEVC_CHROMA_FMT(props, pipe) \ + (VXD_GET_BITS((props).pixel_misc_cfg[pipe], 3, 2, unsigned int)) + +/* + * Maximum chroma format supported by the pipe in H264 mode. + * 0x1 - 4:2:0 + * 0x2 - 4:2:2 + * 0x3 - 4:4:4 + */ +#define VXD_MAX_H264_CHROMA_FMT(props, pipe) \ + (VXD_GET_BITS((props).pixel_misc_cfg[pipe], 1, 0, unsigned int)) + +/* + * Maximum frame width and height supported in MSVDX pipeline. + */ +#define VXD_MAX_WIDTH_MSVDX(props) \ + (2 << (VXD_GET_BITS((props).pixel_max_frame_cfg, 4, 0, unsigned int))) +#define VXD_MAX_HEIGHT_MSVDX(props) \ + (2 << (VXD_GET_BITS((props).pixel_max_frame_cfg, 12, 8, unsigned int))) + +/* + * Maximum frame width and height supported in PVDEC pipeline. + */ +#define VXD_MAX_WIDTH_PVDEC(props) \ + (2 << (VXD_GET_BITS((props).pixel_max_frame_cfg, 20, 16, unsigned int))) +#define VXD_MAX_HEIGHT_PVDEC(props) \ + (2 << (VXD_GET_BITS((props).pixel_max_frame_cfg, 28, 24, unsigned int))) + +#define PVDEC_COMMS_RAM_OFFSET 0x00002000 +#define PVDEC_COMMS_RAM_SIZE 0x00001000 +#define PVDEC_ENTROPY_OFFSET 0x00003000 +#define PVDEC_ENTROPY_SIZE 0x1FF +#define PVDEC_VEC_BE_OFFSET 0x00005000 +#define PVDEC_VEC_BE_SIZE 0x3FF +#define PVDEC_VEC_BE_CODEC_OFFSET 0x00005400 +#define MSVDX_VEC_OFFSET 0x00006000 +#define MSVDX_VEC_SIZE 0x7FF +#define MSVDX_CMD_OFFSET 0x00007000 + +/* + * Virtual memory heap address ranges for tiled + * and non-tiled buffers. Addresses within each + * range should be assigned to the appropriate + * buffers by the UM driver and mapped into the + * device using the corresponding KM driver ioctl. + */ +#define PVDEC_HEAP_UNTILED_START 0x00400000ul +#define PVDEC_HEAP_UNTILED_SIZE 0x3FC00000ul +#define PVDEC_HEAP_TILE512_START 0x40000000ul +#define PVDEC_HEAP_TILE512_SIZE 0x10000000ul +#define PVDEC_HEAP_TILE1024_START 0x50000000ul +#define PVDEC_HEAP_TILE1024_SIZE 0x20000000ul +#define PVDEC_HEAP_TILE2048_START 0x70000000ul +#define PVDEC_HEAP_TILE2048_SIZE 0x30000000ul +#define PVDEC_HEAP_TILE4096_START 0xA0000000ul +#define PVDEC_HEAP_TILE4096_SIZE 0x30000000ul +#define PVDEC_HEAP_BITSTREAM_START 0xD2000000ul +#define PVDEC_HEAP_BITSTREAM_SIZE 0x0A000000ul +#define PVDEC_HEAP_STREAM_START 0xE4000000ul +#define PVDEC_HEAP_STREAM_SIZE 0x1C000000ul + +/* + * Max size of the message payload, in bytes. There are 7 bits used to encode + * the message size in the firmware interface. + */ +#define VXD_MAX_PAYLOAD_SIZE (127 * sizeof(unsigned int)) +/* Max size of the input message in bytes. */ +#define VXD_MAX_INPUT_SIZE (VXD_MAX_PAYLOAD_SIZE + sizeof(struct vxd_fw_msg)) +/* + * Min size of the input message. Two words needed for message header and + * stream PTD + */ +#define VXD_MIN_INPUT_SIZE 2 +/* + * Offset of the stream PTD within message. This word has to be left null in + * submitted message, driver will fill it in with an appropriate value. + */ +#define VXD_PTD_MSG_OFFSET 1 + +/* Read flags */ +#define VXD_FW_MSG_RD_FLAGS_MASK 0xffff +/* Driver watchdog interrupted processing of the message. */ +#define VXD_FW_MSG_FLAG_DWR 0x1 +/* VXD MMU fault occurred when the message was processed. */ +#define VXD_FW_MSG_FLAG_MMU_FAULT 0x2 +/* Invalid input message, e.g. the message was too large. */ +#define VXD_FW_MSG_FLAG_INV 0x4 +/* I/O error occurred when the message was processed. */ +#define VXD_FW_MSG_FLAG_DEV_ERR 0x8 +/* + * Driver error occurred when the message was processed, e.g. failed to + * allocate memory. + */ +#define VXD_FW_MSG_FLAG_DRV_ERR 0x10 +/* + * Item was canceled, without being fully processed + * i.e. corresponding stream was destroyed. + */ +#define VXD_FW_MSG_FLAG_CANCELED 0x20 +/* Firmware internal error occurred when the message was processed */ +#define VXD_FW_MSG_FLAG_FATAL 0x40 + +/* Write flags */ +#define VXD_FW_MSG_WR_FLAGS_MASK 0xffff0000 +/* Indicates that message shall be dropped after sending it to the firmware. */ +#define VXD_FW_MSG_FLAG_DROP 0x10000 +/* + * Indicates that message shall be exclusively handled by + * the firmware/hardware. Any other pending messages are + * blocked until such message is handled. + */ +#define VXD_FW_MSG_FLAG_EXCL 0x20000 + +#define VXD_MSG_SIZE(msg) (sizeof(struct vxd_fw_msg) + ((msg).payload_size)) + +/* Header included at the beginning of firmware binary */ +struct vxd_fw_hdr { + unsigned int core_size; + unsigned int blob_size; + unsigned int firmware_id; + unsigned int timestamp; +}; + +/* + * struct vxd_dev_fw - Core component will allocate a buffer for firmware. + * This structure holds the information about the firmware + * binary. + * @buf_id: The buffer id allocation + * @hdr: firmware header information + * @fw_size: The size of the fw. Set after successful firmware request. + */ +struct vxd_dev_fw { + int buf_id; + struct vxd_fw_hdr *hdr; + unsigned int fw_size; + unsigned char ready; +}; + +/* + * struct vxd_core_props - contains HW core properties + * @core_rev: Core revision based on register CR_PVDEC_CORE_REV + * @pvdec_core_id: PVDEC Core id based on register CR_PVDEC_CORE_ID + * @mmu_config0: MMU configuration 0 based on register MMU_CONFIG0 + * @mmu_config1: MMU configuration 1 based on register MMU_CONFIG1 + * @mtx_ram_size: size of the MTX RAM based on register CR_PROC_DEBUG + * @pixel_max_frame_cfg: indicates the max frame height and width for + * PVDEC pipeline and MSVDX pipeline based on register + * MAX_FRAME_CONFIG + * @pixel_pipe_cfg: pipe configuration which codecs are supported in a + * Pixel Processing Pipe, based on register + * PIXEL_PIPE_CONFIG + * @pixel_misc_cfg: Additional pipe configuration eg. supported scaling + * or rotation, based on register PIXEL_MISC_CONFIG + * @dbg_fifo_size: contains the depth of the Debug FIFO, based on + * register CR_PROC_DEBUG_FIFO_SIZE + */ +struct vxd_core_props { + unsigned int core_rev; + unsigned int pvdec_core_id; + unsigned int mmu_config0; + unsigned int mmu_config1; + unsigned int mtx_ram_size; + unsigned int pixel_max_frame_cfg; + unsigned int pixel_pipe_cfg[VXD_MAX_PIPES]; + unsigned int pixel_misc_cfg[VXD_MAX_PIPES]; + unsigned int dbg_fifo_size; +}; + +struct vxd_alloc_data { + unsigned int heap_id; /* [IN] Heap ID of allocator */ + unsigned int size; /* [IN] Size of device memory (in bytes) */ + unsigned int attributes; /* [IN] Attributes of buffer */ + unsigned int buf_id; /* [OUT] Generated buffer ID */ +}; + +struct vxd_free_data { + unsigned int buf_id; /* [IN] ID of device buffer to free */ +}; +#endif /* _IMG_DEC_COMMON_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_cmds.h b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_cmds.h --- a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_cmds.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_cmds.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG MSVDX core Registers + * This file contains the MSVDX_CORE_REGS_H Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_MSVDX_CMDS_H +#define _IMG_MSVDX_CMDS_H + +#define MSVDX_CMDS_HORIZONTAL_LUMA_COEFFICIENTS_OFFSET (0x0060) +#define MSVDX_CMDS_VERTICAL_LUMA_COEFFICIENTS_OFFSET (0x0070) +/** + * MSVDX_CMDS_HORIZONTAL_CHROMA_COEFFICIENTS_OFFSET - + * MSVDX_CMDS, VERTICAL_LUMA_COEFFICIENTS, VER_LUMA_COEFF_0 + */ +#define MSVDX_CMDS_HORIZONTAL_CHROMA_COEFFICIENTS_OFFSET (0x0080) +/* MSVDX_CMDS, HORIZONTAL_CHROMA_COEFFICIENTS, HOR_CHROMA_COEFF_0 */ +#define MSVDX_CMDS_VERTICAL_CHROMA_COEFFICIENTS_OFFSET (0x0090) +/* MSVDX_CMDS, DISPLAY_PICTURE_SIZE, DISPLAY_PICTURE_HEIGHT */ +#define MSVDX_CMDS_DISPLAY_PICTURE_SIZE_DISPLAY_PICTURE_HEIGHT_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_DISPLAY_PICTURE_SIZE_DISPLAY_PICTURE_HEIGHT_SHIFT (12) +/* MSVDX_CMDS, DISPLAY_PICTURE_SIZE, DISPLAY_PICTURE_WIDTH */ +#define MSVDX_CMDS_DISPLAY_PICTURE_SIZE_DISPLAY_PICTURE_WIDTH_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_DISPLAY_PICTURE_SIZE_DISPLAY_PICTURE_WIDTH_SHIFT (0) +#define MSVDX_CMDS_PVDEC_DISPLAY_PICTURE_SIZE_OFFSET (0x00B0) +#define MSVDX_CMDS_PVDEC_DISPLAY_PICTURE_SIZE_PVDEC_DISPLAY_PICTURE_HEIGHT_MIN1_LSBMASK \ + (0x0000FFFF) +#define MSVDX_CMDS_PVDEC_DISPLAY_PICTURE_SIZE_PVDEC_DISPLAY_PICTURE_HEIGHT_MIN1_SHIFT (16) +/* MSVDX_CMDS, PVDEC_DISPLAY_PICTURE_SIZE, PVDEC_DISPLAY_PICTURE_WIDTH_MIN1 */ +#define MSVDX_CMDS_PVDEC_DISPLAY_PICTURE_SIZE_PVDEC_DISPLAY_PICTURE_WIDTH_MIN1_LSBMASK \ + (0x0000FFFF) +#define MSVDX_CMDS_PVDEC_DISPLAY_PICTURE_SIZE_PVDEC_DISPLAY_PICTURE_WIDTH_MIN1_SHIFT (0) +/* MSVDX_CMDS, CODED_PICTURE_SIZE, CODED_PICTURE_HEIGHT */ +#define MSVDX_CMDS_CODED_PICTURE_SIZE_CODED_PICTURE_HEIGHT_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_CODED_PICTURE_SIZE_CODED_PICTURE_HEIGHT_SHIFT (12) +/* MSVDX_CMDS, CODED_PICTURE_SIZE, CODED_PICTURE_WIDTH */ +#define MSVDX_CMDS_CODED_PICTURE_SIZE_CODED_PICTURE_WIDTH_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_CODED_PICTURE_SIZE_CODED_PICTURE_WIDTH_SHIFT (0) +#define MSVDX_CMDS_PVDEC_CODED_PICTURE_SIZE_OFFSET (0x00B4) +/* MSVDX_CMDS, OPERATING_MODE, USE_EXT_ROW_STRIDE */ +#define MSVDX_CMDS_OPERATING_MODE_USE_EXT_ROW_STRIDE_MASK (0x10000000) +#define MSVDX_CMDS_OPERATING_MODE_USE_EXT_ROW_STRIDE_LSBMASK (0x00000001) +#define MSVDX_CMDS_OPERATING_MODE_USE_EXT_ROW_STRIDE_SHIFT (28) +/* MSVDX_CMDS, OPERATING_MODE, CHROMA_INTERLEAVED */ +#define MSVDX_CMDS_OPERATING_MODE_CHROMA_INTERLEAVED_MASK (0x08000000) +#define MSVDX_CMDS_OPERATING_MODE_CHROMA_INTERLEAVED_LSBMASK (0x00000001) +#define MSVDX_CMDS_OPERATING_MODE_CHROMA_INTERLEAVED_SHIFT (27) +/* MSVDX_CMDS, OPERATING_MODE, ROW_STRIDE */ +#define MSVDX_CMDS_OPERATING_MODE_ROW_STRIDE_MASK (0x07000000) +#define MSVDX_CMDS_OPERATING_MODE_ROW_STRIDE_LSBMASK (0x00000007) +#define MSVDX_CMDS_OPERATING_MODE_ROW_STRIDE_SHIFT (24) +/* MSVDX_CMDS, OPERATING_MODE, CODEC_PROFILE */ +#define MSVDX_CMDS_OPERATING_MODE_CODEC_PROFILE_MASK (0x00300000) +#define MSVDX_CMDS_OPERATING_MODE_CODEC_PROFILE_LSBMASK (0x00000003) +#define MSVDX_CMDS_OPERATING_MODE_CODEC_PROFILE_SHIFT (20) +/* MSVDX_CMDS, OPERATING_MODE, CODEC_MODE */ +#define MSVDX_CMDS_OPERATING_MODE_CODEC_MODE_MASK (0x000F0000) +#define MSVDX_CMDS_OPERATING_MODE_CODEC_MODE_LSBMASK (0x0000000F) +#define MSVDX_CMDS_OPERATING_MODE_CODEC_MODE_SHIFT (16) +/* MSVDX_CMDS, OPERATING_MODE, ASYNC_MODE */ +#define MSVDX_CMDS_OPERATING_MODE_ASYNC_MODE_MASK (0x00006000) +#define MSVDX_CMDS_OPERATING_MODE_ASYNC_MODE_LSBMASK (0x00000003) +#define MSVDX_CMDS_OPERATING_MODE_ASYNC_MODE_SHIFT (13) +/* MSVDX_CMDS, OPERATING_MODE, CHROMA_FORMAT */ +#define MSVDX_CMDS_OPERATING_MODE_CHROMA_FORMAT_MASK (0x00001000) +#define MSVDX_CMDS_OPERATING_MODE_CHROMA_FORMAT_LSBMASK (0x00000001) +#define MSVDX_CMDS_OPERATING_MODE_CHROMA_FORMAT_SHIFT (12) +/* MSVDX_CMDS, OPERATING_MODE, PIC_QUANT */ +#define MSVDX_CMDS_PVDEC_OPERATING_MODE_OFFSET (0x00A0) +/* MSVDX_CMDS, EXT_OP_MODE, BIT_DEPTH_CHROMA_MINUS8 */ +#define MSVDX_CMDS_EXT_OP_MODE_BIT_DEPTH_CHROMA_MINUS8_MASK (0x00003000) +#define MSVDX_CMDS_EXT_OP_MODE_BIT_DEPTH_CHROMA_MINUS8_LSBMASK (0x00000003) +#define MSVDX_CMDS_EXT_OP_MODE_BIT_DEPTH_CHROMA_MINUS8_SHIFT (12) +/* MSVDX_CMDS, EXT_OP_MODE, BIT_DEPTH_LUMA_MINUS8 */ +#define MSVDX_CMDS_EXT_OP_MODE_BIT_DEPTH_LUMA_MINUS8_MASK (0x00000300) +#define MSVDX_CMDS_EXT_OP_MODE_BIT_DEPTH_LUMA_MINUS8_LSBMASK (0x00000003) +#define MSVDX_CMDS_EXT_OP_MODE_BIT_DEPTH_LUMA_MINUS8_SHIFT (8) +/* MSVDX_CMDS, EXT_OP_MODE, MEMORY_PACKING */ +#define MSVDX_CMDS_EXT_OP_MODE_MEMORY_PACKING_MASK (0x00000008) +#define MSVDX_CMDS_EXT_OP_MODE_MEMORY_PACKING_LSBMASK (0x00000001) +#define MSVDX_CMDS_EXT_OP_MODE_MEMORY_PACKING_SHIFT (3) +/* MSVDX_CMDS, EXT_OP_MODE, CHROMA_FORMAT_IDC */ +#define MSVDX_CMDS_EXT_OP_MODE_CHROMA_FORMAT_IDC_MASK (0x00000003) +#define MSVDX_CMDS_EXT_OP_MODE_CHROMA_FORMAT_IDC_LSBMASK (0x00000003) +#define MSVDX_CMDS_EXT_OP_MODE_CHROMA_FORMAT_IDC_SHIFT (0) +#define MSVDX_CMDS_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES_OFFSET (0x000C) +/* + * MSVDX_CMDS, LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES, + * LUMA_RECON_BASE_ADDR + */ +#define MSVDX_CMDS_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES_OFFSET (0x0010) +///* MSVDX_CMDS, AUX_MSB_BUFFER_BASE_ADDRESSES, AUX_MSB_BUFFER_BASE_ADDR */ +#define MSVDX_CMDS_INTRA_BUFFER_BASE_ADDRESS_OFFSET (0x0018) +/* MSVDX_CMDS, INTRA_BUFFER_BASE_ADDRESS, INTRA_BASE_ADDR */ + +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_OFFSET (0x001C) + +/* MSVDX_CMDS, MC_CACHE_CONFIGURATION, CONFIG_REF_CHROMA_ADJUST */ +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_REF_CHROMA_ADJUST_MASK (0x01000000) +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_REF_CHROMA_ADJUST_LSBMASK (0x00000001) +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_REF_CHROMA_ADJUST_SHIFT (24) +/* MSVDX_CMDS, MC_CACHE_CONFIGURATION, CONFIG_REF_OFFSET */ +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_REF_OFFSET_MASK (0x00FFF000) +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_REF_OFFSET_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_REF_OFFSET_SHIFT (12) +/* MSVDX_CMDS, MC_CACHE_CONFIGURATION, CONFIG_ROW_OFFSET */ +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_ROW_OFFSET_MASK (0x0000003F) +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_ROW_OFFSET_LSBMASK (0x0000003F) +#define MSVDX_CMDS_MC_CACHE_CONFIGURATION_CONFIG_ROW_OFFSET_SHIFT (0) +/* MSVDX_CMDS, H264_WEIGHTED_FACTOR_DENOMINATOR, Y_LOG2_WEIGHT_DENOM */ +#define MSVDX_CMDS_VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS_OFFSET (0x0028) +/* MSVDX_CMDS, VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS, LUMA_RANGE_BASE_ADDR */ +#define MSVDX_CMDS_VC1_CHROMA_RANGE_MAPPING_BASE_ADDRESS_OFFSET (0x002C) +/* MSVDX_CMDS, VC1_RANGE_MAPPING_FLAGS, LUMA_RANGE_MAP */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_OFFSET (0x003C) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, EXT_ROT_ROW_STRIDE */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_EXT_ROT_ROW_STRIDE_MASK (0xFFC00000) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_EXT_ROT_ROW_STRIDE_LSBMASK \ + (0x000003FF) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_EXT_ROT_ROW_STRIDE_SHIFT (22) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, PACKED_422_OUTPUT */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_PACKED_422_OUTPUT_MASK (0x00000800) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_PACKED_422_OUTPUT_LSBMASK \ + (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_PACKED_422_OUTPUT_SHIFT (11) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, USE_AUX_LINE_BUF */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_USE_AUX_LINE_BUF_MASK (0x00000400) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_USE_AUX_LINE_BUF_LSBMASK (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_USE_AUX_LINE_BUF_SHIFT (10) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, SCALE_INPUT_SIZE_SEL */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_SCALE_INPUT_SIZE_SEL_MASK \ + (0x00000200) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_SCALE_INPUT_SIZE_SEL_LSBMASK \ + (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_SCALE_INPUT_SIZE_SEL_SHIFT (9) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, USE_EXT_ROT_ROW_STRIDE */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_USE_EXT_ROT_ROW_STRIDE_MASK \ + (0x00000100) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_USE_EXT_ROT_ROW_STRIDE_LSBMASK \ + (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_USE_EXT_ROT_ROW_STRIDE_SHIFT (8) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, ROTATION_ROW_STRIDE */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_ROTATION_ROW_STRIDE_MASK (0x00000070) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_ROTATION_ROW_STRIDE_LSBMASK \ + (0x00000007) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_ROTATION_ROW_STRIDE_SHIFT (4) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, ROTATION_MODE */ +#define MSVDX_CMDS_EXTENDED_ROW_STRIDE_OFFSET (0x0040) + +/* MSVDX_CMDS, EXTENDED_ROW_STRIDE, EXT_ROW_STRIDE */ +#define MSVDX_CMDS_EXTENDED_ROW_STRIDE_EXT_ROW_STRIDE_MASK (0x0003FFC0) +#define MSVDX_CMDS_EXTENDED_ROW_STRIDE_EXT_ROW_STRIDE_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_EXTENDED_ROW_STRIDE_EXT_ROW_STRIDE_SHIFT (6) +/* MSVDX_CMDS, EXTENDED_ROW_STRIDE, REF_PIC_MMU_TILED */ +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_OFFSET (0x01AC) +/* MSVDX_CMDS, CHROMA_ROW_STRIDE, ALT_CHROMA_ROW_STRIDE */ +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_ALT_CHROMA_ROW_STRIDE_MASK (0xFFC00000) +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_ALT_CHROMA_ROW_STRIDE_LSBMASK (0x000003FF) +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_ALT_CHROMA_ROW_STRIDE_SHIFT (22) +/* MSVDX_CMDS, CHROMA_ROW_STRIDE, CHROMA_ROW_STRIDE */ +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_CHROMA_ROW_STRIDE_MASK (0x0003FFC0) +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_CHROMA_ROW_STRIDE_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_CHROMA_ROW_STRIDE_CHROMA_ROW_STRIDE_SHIFT (6) +/* MSVDX_CMDS, RPR_PICTURE_SIZE, RPR_PICTURE_WIDTH */ +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_OFFSET (0x0050) +/* MSVDX_CMDS, SCALED_DISPLAY_SIZE, SCALE_DISPLAY_HEIGHT */ +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_SCALE_DISPLAY_HEIGHT_MASK (0x00FFF000) +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_SCALE_DISPLAY_HEIGHT_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_SCALE_DISPLAY_HEIGHT_SHIFT (12) +/* MSVDX_CMDS, SCALED_DISPLAY_SIZE, SCALE_DISPLAY_WIDTH */ +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_SCALE_DISPLAY_WIDTH_MASK (0x00000FFF) +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_SCALE_DISPLAY_WIDTH_LSBMASK (0x00000FFF) +#define MSVDX_CMDS_SCALED_DISPLAY_SIZE_SCALE_DISPLAY_WIDTH_SHIFT (0) +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_OFFSET (0x00B8) +/* MSVDX_CMDS, PVDEC_SCALED_DISPLAY_SIZE, PVDEC_SCALE_DISPLAY_HEIGHT */ +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_PVDEC_SCALE_DISPLAY_HEIGHT_MASK (0xFFFF0000) +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_PVDEC_SCALE_DISPLAY_HEIGHT_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_PVDEC_SCALE_DISPLAY_HEIGHT_SHIFT (16) +/* MSVDX_CMDS, PVDEC_SCALED_DISPLAY_SIZE, PVDEC_SCALE_DISPLAY_WIDTH */ +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_PVDEC_SCALE_DISPLAY_WIDTH_MASK (0x0000FFFF) +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_PVDEC_SCALE_DISPLAY_WIDTH_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_PVDEC_SCALE_DISPLAY_WIDTH_SHIFT (0) +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_OFFSET (0x0054) +/* MSVDX_CMDS, HORIZONTAL_SCALE_CONTROL, HORIZONTAL_INITIAL_POS */ +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_HORIZONTAL_INITIAL_POS_MASK (0xFFFF0000) +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_HORIZONTAL_INITIAL_POS_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_HORIZONTAL_INITIAL_POS_SHIFT (16) +/* MSVDX_CMDS, HORIZONTAL_SCALE_CONTROL, HORIZONTAL_SCALE_PITCH */ +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_HORIZONTAL_SCALE_PITCH_MASK (0x0000FFFF) +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_HORIZONTAL_SCALE_PITCH_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_HORIZONTAL_SCALE_PITCH_SHIFT (0) +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_OFFSET (0x0058) +/* MSVDX_CMDS, VERTICAL_SCALE_CONTROL, VERTICAL_INITIAL_POS */ +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_VERTICAL_INITIAL_POS_MASK (0xFFFF0000) +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_VERTICAL_INITIAL_POS_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_VERTICAL_INITIAL_POS_SHIFT (16) +/* MSVDX_CMDS, VERTICAL_SCALE_CONTROL, VERTICAL_SCALE_PITCH */ +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_VERTICAL_SCALE_PITCH_MASK (0x0000FFFF) +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_VERTICAL_SCALE_PITCH_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_VERTICAL_SCALE_CONTROL_VERTICAL_SCALE_PITCH_SHIFT (0) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_OFFSET (0x01B4) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, ALT_BIT_DEPTH_CHROMA_MINUS8 */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_BIT_DEPTH_CHROMA_MINUS8_MASK (0x00007000) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_BIT_DEPTH_CHROMA_MINUS8_LSBMASK \ + (0x00000007) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_BIT_DEPTH_CHROMA_MINUS8_SHIFT (12) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, ALT_BIT_DEPTH_LUMA_MINUS8 */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_BIT_DEPTH_LUMA_MINUS8_MASK (0x00000700) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_BIT_DEPTH_LUMA_MINUS8_LSBMASK (0x00000007) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_BIT_DEPTH_LUMA_MINUS8_SHIFT (8) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, SCALE_LUMA_BIFILTER_HORIZ */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_LUMA_BIFILTER_HORIZ_MASK (0x00000080) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_LUMA_BIFILTER_HORIZ_LSBMASK (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_LUMA_BIFILTER_HORIZ_SHIFT (7) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, SCALE_LUMA_BIFILTER_VERT */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_LUMA_BIFILTER_VERT_MASK (0x00000040) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_LUMA_BIFILTER_VERT_LSBMASK (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_LUMA_BIFILTER_VERT_SHIFT (6) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, SCALE_CHROMA_BIFILTER_HORIZ */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_BIFILTER_HORIZ_MASK (0x00000020) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_BIFILTER_HORIZ_LSBMASK \ + (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_BIFILTER_HORIZ_SHIFT (5) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, SCALE_CHROMA_BIFILTER_VERT */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_BIFILTER_VERT_MASK (0x00000010) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_BIFILTER_VERT_LSBMASK \ + (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_BIFILTER_VERT_SHIFT (4) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, ALT_MEMORY_PACKING */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_MEMORY_PACKING_MASK (0x00000008) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_MEMORY_PACKING_LSBMASK (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_MEMORY_PACKING_SHIFT (3) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, SCALE_CHROMA_RESAMP_ONLY */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_RESAMP_ONLY_MASK (0x00000004) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_RESAMP_ONLY_LSBMASK (0x00000001) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_SCALE_CHROMA_RESAMP_ONLY_SHIFT (2) +/* MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, ALT_OUTPUT_FORMAT */ +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_OUTPUT_FORMAT_MASK (0x00000003) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_OUTPUT_FORMAT_LSBMASK (0x00000003) +#define MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_ALT_OUTPUT_FORMAT_SHIFT (0) +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_OFFSET (0x01B8) +/* MSVDX_CMDS, SCALE_OUTPUT_SIZE, SCALE_OUTPUT_HEIGHT_MIN1 */ +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_SCALE_OUTPUT_HEIGHT_MIN1_MASK (0xFFFF0000) +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_SCALE_OUTPUT_HEIGHT_MIN1_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_SCALE_OUTPUT_HEIGHT_MIN1_SHIFT (16) +/* MSVDX_CMDS, SCALE_OUTPUT_SIZE, SCALE_OUTPUT_WIDTH_MIN1 */ +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_SCALE_OUTPUT_WIDTH_MIN1_MASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_SCALE_OUTPUT_WIDTH_MIN1_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_OUTPUT_SIZE_SCALE_OUTPUT_WIDTH_MIN1_SHIFT (0) +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_OFFSET (0x01BC) +/* MSVDX_CMDS, SCALE_HORIZONTAL_CHROMA, CHROMA_HORIZONTAL_INITIAL */ +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_CHROMA_HORIZONTAL_INITIAL_MASK (0xFFFF0000) +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_CHROMA_HORIZONTAL_INITIAL_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_CHROMA_HORIZONTAL_INITIAL_SHIFT (16) +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_CHROMA_HORIZONTAL_PITCH_MASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_CHROMA_HORIZONTAL_PITCH_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_CHROMA_HORIZONTAL_PITCH_SHIFT (0) +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_OFFSET (0x01C0) +/* MSVDX_CMDS, SCALE_VERTICAL_CHROMA, CHROMA_VERTICAL_INITIAL */ +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_CHROMA_VERTICAL_INITIAL_MASK (0xFFFF0000) +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_CHROMA_VERTICAL_INITIAL_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_CHROMA_VERTICAL_INITIAL_SHIFT (16) +/* MSVDX_CMDS, SCALE_VERTICAL_CHROMA, CHROMA_VERTICAL_PITCH */ +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_CHROMA_VERTICAL_PITCH_MASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_CHROMA_VERTICAL_PITCH_LSBMASK (0x0000FFFF) +#define MSVDX_CMDS_SCALE_VERTICAL_CHROMA_CHROMA_VERTICAL_PITCH_SHIFT (0) +/* MSVDX_CMDS, MULTICORE_OPERATING_MODE, MBLK_ROW_OFFSET */ +#define MSVDX_CMDS_AUX_LINE_BUFFER_BASE_ADDRESS_OFFSET (0x01EC) + +#endif /* _IMG_MSVDX_CMDS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_core_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_core_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_core_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_core_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG MSVDX core Registers + * This file contains the MSVDX_CORE_REGS_H Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_MSVDX_CORE_REGS_H +#define _IMG_MSVDX_CORE_REGS_H + +#define MSVDX_CORE_CR_MMU_TILE_NO_ENTRIES (4) +#define MSVDX_CORE_CR_MMU_TILE_EXT_NO_ENTRIES (4) + +#endif /* _IMG_MSVDX_CORE_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vdmc_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vdmc_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vdmc_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vdmc_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG MSVDX VDMC Registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_MSVDX_VDMC_REGS_H +#define _IMG_MSVDX_VDMC_REGS_H + +/* MSVDX_VDMC, CR_VDMC_MACROBLOCK_NUMBER, CR_VDMC_MACROBLOCK_X_OFFSET */ +#define MSVDX_VDMC_CR_VDMC_MACROBLOCK_NUMBER_CR_VDMC_MACROBLOCK_X_OFFSET_MASK (0x0000FFFF) +#define MSVDX_VDMC_CR_VDMC_MACROBLOCK_NUMBER_CR_VDMC_MACROBLOCK_X_OFFSET_SHIFT (0) + +/* MSVDX_VDMC, CR_VDMC_MACROBLOCK_NUMBER, CR_VDMC_MACROBLOCK_Y_OFFSET */ +#define MSVDX_VDMC_CR_VDMC_MACROBLOCK_NUMBER_CR_VDMC_MACROBLOCK_Y_OFFSET_MASK (0xFFFF0000) +#define MSVDX_VDMC_CR_VDMC_MACROBLOCK_NUMBER_CR_VDMC_MACROBLOCK_Y_OFFSET_SHIFT (16) + +#endif /* _IMG_MSVDX_VDMC_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vec_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vec_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vec_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_msvdx_vec_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG MSVDX VEC Registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#if !defined(__MSVDX_VEC_REGS_H__) +#define __MSVDX_VEC_REGS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + /* MSVDX_VEC, CR_VEC_VLR_COMMANDS_NUM, VLR_COMMANDS_STORE_NUMBER_OF_CMDS */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR0_OFFSET (0x00EC) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_ADDR0, VLC_TABLE_ADDR0 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR0_VLC_TABLE_ADDR0_MASK (0x000007FF) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_ADDR15, VLC_TABLE_ADDR31 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR16_OFFSET (0x01C0) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_ADDR15, VLC_TABLE_ADDR31 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR16_OFFSET (0x01C0) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_ADDR18, VLC_TABLE_ADDR37 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_WIDTH0_OFFSET (0x012C) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_ADDR0, VLC_TABLE_ADDR1 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR0_VLC_TABLE_ADDR1_SHIFT (11) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_INITIAL_WIDTH0, VLC_TABLE_INITIAL_WIDTH0 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_WIDTH0_VLC_TABLE_INITIAL_WIDTH0_MASK (0x00000007) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_INITIAL_WIDTH0, VLC_TABLE_INITIAL_WIDTH1 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_WIDTH0_VLC_TABLE_INITIAL_WIDTH1_SHIFT (3) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_INITIAL_OPCODE0, VLC_TABLE_INITIAL_OPCODE0 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_OPCODE0_VLC_TABLE_INITIAL_OPCODE0_MASK \ + (0x00000003) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_INITIAL_WIDTH3, VLC_TABLE_INITIAL_WIDTH37 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_OPCODE0_OFFSET (0x013C) + + /* MSVDX_VEC, CR_VEC_VLC_TABLE_INITIAL_OPCODE0, VLC_TABLE_INITIAL_OPCODE1 */ +#define MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_OPCODE0_VLC_TABLE_INITIAL_OPCODE1_SHIFT (2) + +#ifdef __cplusplus +} +#endif + +#endif /* __MSVDX_VEC_REGS_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_pixfmts.h b/drivers/media/platform/vxe-vxd/decoder/img_pixfmts.h --- a/drivers/media/platform/vxe-vxd/decoder/img_pixfmts.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_pixfmts.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC SYSDEV and UI Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + */ + +#ifndef __IMG_PIXFMTS_H__ +#define __IMG_PIXFMTS_H__ +/* + * @brief Old pixel format definition + * + * @note These definitions are different in HW documentation(current to HW doc): + * @li PL8 is defined as PL111 + * @li PL12 is sometime used wrongly for monochrome formats instead of PL_Y + */ +enum img_pixfmt { + IMG_PIXFMT_CLUT1 = 0, + IMG_PIXFMT_CLUT2 = 1, + IMG_PIXFMT_CLUT4 = 2, + IMG_PIXFMT_I4A4 = 3, + IMG_PIXFMT_I8A8 = 4, + IMG_PIXFMT_A8I8 = 51, + IMG_PIXFMT_RGB8 = 5, + IMG_PIXFMT_RGB332 = 6, + IMG_PIXFMT_RGB555 = 7, + IMG_PIXFMT_ARGB4444 = 8, + IMG_PIXFMT_ABGR4444 = 57, + IMG_PIXFMT_RGBA4444 = 58, + IMG_PIXFMT_BGRA4444 = 59, + IMG_PIXFMT_ARGB1555 = 9, + IMG_PIXFMT_ABGR1555 = 60, + IMG_PIXFMT_RGBA5551 = 61, + IMG_PIXFMT_BGRA5551 = 62, + IMG_PIXFMT_RGB565 = 10, + IMG_PIXFMT_BGR565 = 63, + IMG_PIXFMT_RGB888 = 11, + IMG_PIXFMT_RSGSBS888 = 68, + IMG_PIXFMT_ARGB8888 = 12, + IMG_PIXFMT_ABGR8888 = 41, + IMG_PIXFMT_BGRA8888 = 42, + IMG_PIXFMT_RGBA8888 = 56, + IMG_PIXFMT_ARGB8332 = 43, + IMG_PIXFMT_ARGB8161616 = 64, + IMG_PIXFMT_ARGB2101010 = 67, + IMG_PIXFMT_UYVY8888 = 13, + IMG_PIXFMT_VYUY8888 = 14, + IMG_PIXFMT_YVYU8888 = 15, + IMG_PIXFMT_YUYV8888 = 16, + IMG_PIXFMT_UYVY10101010 = 17, + IMG_PIXFMT_VYAUYA8888 = 18, + IMG_PIXFMT_YUV101010 = 19, + IMG_PIXFMT_AYUV4444 = 20, + IMG_PIXFMT_YUV888 = 21, + IMG_PIXFMT_AYUV8888 = 22, + IMG_PIXFMT_AYUV2101010 = 23, + IMG_PIXFMT_411PL111YUV8 = 120, + IMG_PIXFMT_411PL12YUV8 = 121, + IMG_PIXFMT_411PL12YVU8 = 24, + IMG_PIXFMT_420PL12YUV8 = 25, + IMG_PIXFMT_420PL12YVU8 = 26, + IMG_PIXFMT_422PL12YUV8 = 27, + IMG_PIXFMT_422PL12YVU8 = 28, + IMG_PIXFMT_420PL8YUV8 = 47, + IMG_PIXFMT_422PL8YUV8 = 48, + IMG_PIXFMT_420PL12YUV8_A8 = 31, + IMG_PIXFMT_422PL12YUV8_A8 = 32, + IMG_PIXFMT_PL12Y8 = 33, + IMG_PIXFMT_PL12YV8 = 35, + IMG_PIXFMT_PL12IMC2 = 36, + IMG_PIXFMT_A4 = 37, + IMG_PIXFMT_A8 = 38, + IMG_PIXFMT_YUV8 = 39, + IMG_PIXFMT_CVBS10 = 40, + IMG_PIXFMT_PL12YV12 = 44, +#if ((!defined(METAG) && !defined(MTXG)) || defined(__linux__)) + IMG_PIXFMT_F16 = 52, + IMG_PIXFMT_F32 = 53, + IMG_PIXFMT_F16F16F16F16 = 65, +#endif + IMG_PIXFMT_L16 = 54, + IMG_PIXFMT_L32 = 55, + IMG_PIXFMT_Y1 = 66, + IMG_PIXFMT_444PL111YUV8 = 69, + IMG_PIXFMT_444PL12YUV8 = 137, + IMG_PIXFMT_444PL12YVU8 = 138, + IMG_PIXFMT_PL12Y10 = 34, + IMG_PIXFMT_PL12Y10_LSB = 96, + IMG_PIXFMT_PL12Y10_MSB = 97, + IMG_PIXFMT_420PL8YUV10 = 49, + IMG_PIXFMT_420PL111YUV10_LSB = 71, + IMG_PIXFMT_420PL111YUV10_MSB = 72, + IMG_PIXFMT_420PL12YUV10 = 29, + IMG_PIXFMT_420PL12YUV10_LSB = 74, + IMG_PIXFMT_420PL12YUV10_MSB = 75, + IMG_PIXFMT_420PL12YVU10 = 45, + IMG_PIXFMT_420PL12YVU10_LSB = 77, + IMG_PIXFMT_420PL12YVU10_MSB = 78, + IMG_PIXFMT_422PL8YUV10 = 50, + IMG_PIXFMT_422PL111YUV10_LSB = 122, + IMG_PIXFMT_422PL111YUV10_MSB = 123, + IMG_PIXFMT_422PL12YUV10 = 30, + IMG_PIXFMT_422PL12YUV10_LSB = 80, + IMG_PIXFMT_422PL12YUV10_MSB = 81, + IMG_PIXFMT_422PL12YVU10 = 46, + IMG_PIXFMT_422PL12YVU10_LSB = 83, + IMG_PIXFMT_422PL12YVU10_MSB = 84, + IMG_PIXFMT_444PL111YUV10 = 85, + IMG_PIXFMT_444PL111YUV10_LSB = 86, + IMG_PIXFMT_444PL111YUV10_MSB = 87, + IMG_PIXFMT_444PL12YUV10 = 139, + IMG_PIXFMT_444PL12YUV10_LSB = 141, + IMG_PIXFMT_444PL12YUV10_MSB = 142, + IMG_PIXFMT_444PL12YVU10 = 140, + IMG_PIXFMT_444PL12YVU10_LSB = 143, + IMG_PIXFMT_444PL12YVU10_MSB = 144, + IMG_PIXFMT_420PL12Y8UV10 = 88, + IMG_PIXFMT_420PL12Y8UV10_LSB = 98, + IMG_PIXFMT_420PL12Y8UV10_MSB = 99, + IMG_PIXFMT_420PL12Y8VU10 = 89, + IMG_PIXFMT_420PL12Y8VU10_LSB = 100, + IMG_PIXFMT_420PL12Y8VU10_MSB = 101, + IMG_PIXFMT_420PL111Y8UV10 = 70, + IMG_PIXFMT_420PL111Y8UV10_LSB = 127, + IMG_PIXFMT_420PL111Y8UV10_MSB = 125, + IMG_PIXFMT_422PL12Y8UV10 = 90, + IMG_PIXFMT_422PL12Y8UV10_LSB = 102, + IMG_PIXFMT_422PL12Y8UV10_MSB = 103, + IMG_PIXFMT_422PL12Y8VU10 = 91, + IMG_PIXFMT_422PL12Y8VU10_LSB = 104, + IMG_PIXFMT_422PL12Y8VU10_MSB = 105, + IMG_PIXFMT_444PL12Y8UV10 = 151, + IMG_PIXFMT_444PL12Y8UV10_LSB = 153, + IMG_PIXFMT_444PL12Y8UV10_MSB = 154, + IMG_PIXFMT_444PL12Y8VU10 = 152, + IMG_PIXFMT_444PL12Y8VU10_LSB = 155, + IMG_PIXFMT_444PL12Y8VU10_MSB = 156, + IMG_PIXFMT_420PL12Y10UV8 = 92, + IMG_PIXFMT_420PL12Y10UV8_LSB = 106, + IMG_PIXFMT_420PL12Y10UV8_MSB = 107, + + IMG_PIXFMT_420PL12Y10VU8 = 93, + IMG_PIXFMT_420PL12Y10VU8_LSB = 108, + IMG_PIXFMT_420PL12Y10VU8_MSB = 109, + + IMG_PIXFMT_420PL111Y10UV8 = 129, + IMG_PIXFMT_420PL111Y10UV8_LSB = 133, + IMG_PIXFMT_420PL111Y10UV8_MSB = 131, + IMG_PIXFMT_422PL12Y10UV8 = 94, + IMG_PIXFMT_422PL12Y10UV8_LSB = 110, + IMG_PIXFMT_422PL12Y10UV8_MSB = 111, + IMG_PIXFMT_422PL12Y10VU8 = 95, + IMG_PIXFMT_422PL12Y10VU8_LSB = 112, + IMG_PIXFMT_422PL12Y10VU8_MSB = 113, + + IMG_PIXFMT_444PL111Y10UV8 = 114, + IMG_PIXFMT_444PL111Y10UV8_LSB = 115, + IMG_PIXFMT_444PL111Y10UV8_MSB = 116, + IMG_PIXFMT_444PL111Y8UV10 = 117, + IMG_PIXFMT_444PL111Y8UV10_LSB = 118, + IMG_PIXFMT_444PL111Y8UV10_MSB = 119, + IMG_PIXFMT_444PL12Y10UV8 = 145, + IMG_PIXFMT_444PL12Y10UV8_LSB = 147, + IMG_PIXFMT_444PL12Y10UV8_MSB = 148, + IMG_PIXFMT_444PL12Y10VU8 = 146, + IMG_PIXFMT_444PL12Y10VU8_LSB = 149, + IMG_PIXFMT_444PL12Y10VU8_MSB = 150, + IMG_PIXFMT_422PL111Y8UV10 = 124, + IMG_PIXFMT_422PL111Y8UV10_MSB = 126, + IMG_PIXFMT_422PL111Y8UV10_LSB = 128, + + IMG_PIXFMT_422PL111Y10UV8 = 130, + IMG_PIXFMT_422PL111Y10UV8_LSB = 134, + IMG_PIXFMT_422PL111Y10UV8_MSB = 132, + IMG_PIXFMT_420PL8YUV12 = 160, + IMG_PIXFMT_422PL8YUV12 = 161, + IMG_PIXFMT_444PL8YUV12 = 162, + IMG_PIXFMT_420PL8YUV14 = 163, + IMG_PIXFMT_422PL8YUV14 = 164, + IMG_PIXFMT_444PL8YUV14 = 165, + IMG_PIXFMT_420PL8YUV16 = 166, + IMG_PIXFMT_422PL8YUV16 = 167, + IMG_PIXFMT_444PL8YUV16 = 168, + IMG_PIXFMT_UNDEFINED = 255, + + IMG_PIXFMT_ARBPLANAR8 = 65536, + IMG_PIXFMT_ARBPLANAR8_LAST = IMG_PIXFMT_ARBPLANAR8 + 0xffff, + IMG_PIXFMT_FORCE32BITS = 0x7FFFFFFFU +}; + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_profiles_levels.h b/drivers/media/platform/vxe-vxd/decoder/img_profiles_levels.h --- a/drivers/media/platform/vxe-vxd/decoder/img_profiles_levels.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_profiles_levels.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC SYSDEV and UI Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef __IMG_PROFILES_LEVELS_H +#define __IMG_PROFILES_LEVELS_H + +#include "vdecdd_utils.h" + +/* Minimum level value for h.264 */ +#define H264_LEVEL_MIN (9) +/* Maximum level value for h.264 */ +#define H264_LEVEL_MAX (52) +/* Number of major levels for h.264 (5 + 1 for special levels) */ +#define H264_LEVEL_MAJOR_NUM (6) +/* Number of minor levels for h.264 */ +#define H264_LEVEL_MINOR_NUM (4) +/* Number of major levels for HEVC */ +#define HEVC_LEVEL_MAJOR_NUM (6) +/* Number of minor levels for HEVC */ +#define HEVC_LEVEL_MINOR_NUM (3) + +#endif /*__IMG_PROFILES_LEVELS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_pvdec_core_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_pvdec_core_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_pvdec_core_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_pvdec_core_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG PVDEC CORE Registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_PVDEC_CORE_REGS_H +#define _IMG_PVDEC_CORE_REGS_H + +/* PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, CR_HOST_SYS_WDT */ +#define PVDEC_CORE_CR_PVDEC_HOST_INTERRUPT_STATUS_CR_HOST_SYS_WDT_MASK (0x10000000) + +#define PVDEC_CORE_CR_PVDEC_HOST_INTERRUPT_STATUS_CR_HOST_SYS_WDT_SHIFT (28) + +/* PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, CR_HOST_READ_TIMEOUT_PROC_IRQ */ +#define PVDEC_CORE_CR_PVDEC_HOST_INTERRUPT_STATUS_CR_HOST_READ_TIMEOUT_PROC_IRQ_MASK \ + (0x08000000) + +/* PVDEC_CORE, CR_PVDEC_CORE_REV, CR_PVDEC_MAJOR_REV */ +#define PVDEC_CORE_CR_PVDEC_CORE_REV_CR_PVDEC_MAJOR_REV_MASK (0x00FF0000) +#define PVDEC_CORE_CR_PVDEC_CORE_REV_CR_PVDEC_MAJOR_REV_SHIFT (16) + +/* PVDEC_CORE, CR_PVDEC_CORE_REV, CR_PVDEC_MINOR_REV */ +#define PVDEC_CORE_CR_PVDEC_CORE_REV_CR_PVDEC_MINOR_REV_MASK (0x0000FF00) +#define PVDEC_CORE_CR_PVDEC_CORE_REV_CR_PVDEC_MINOR_REV_SHIFT (8) + +/* PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, CR_HOST_READ_TIMEOUT_PROC_IRQ */ +#define PVDEC_CORE_CR_PVDEC_HOST_INTERRUPT_STATUS_CR_HOST_READ_TIMEOUT_PROC_IRQ_SHIFT (27) + +/* PVDEC_CORE, CR_PVDEC_HOST_INTERRUPT_STATUS, CR_HOST_COMMAND_TIMEOUT_PROC_IRQ */ +#define PVDEC_CORE_CR_PVDEC_HOST_INTERRUPT_STATUS_CR_HOST_COMMAND_TIMEOUT_PROC_IRQ_MASK \ + (0x04000000) +#define PVDEC_CORE_CR_PVDEC_HOST_INTERRUPT_STATUS_CR_HOST_COMMAND_TIMEOUT_PROC_IRQ_SHIFT \ + (26) + +/* PVDEC_CORE, CR_PVDEC_CORE_ID, CR_GROUP_ID */ +#define PVDEC_CORE_CR_PVDEC_CORE_ID_CR_GROUP_ID_MASK (0xFF000000) +#define PVDEC_CORE_CR_PVDEC_CORE_ID_CR_GROUP_ID_SHIFT (24) + +/* PVDEC_CORE, CR_PVDEC_CORE_REV, CR_PVDEC_MAINT_REV */ +#define PVDEC_CORE_CR_PVDEC_CORE_REV_CR_PVDEC_MAINT_REV_MASK (0x000000FF) +#define PVDEC_CORE_CR_PVDEC_CORE_REV_CR_PVDEC_MAINT_REV_SHIFT (0) + +/* PVDEC_CORE, CR_PVDEC_CORE_ID, CR_CORE_ID */ +#define PVDEC_CORE_CR_PVDEC_CORE_ID_CR_CORE_ID_MASK (0x00FF0000) +#define PVDEC_CORE_CR_PVDEC_CORE_ID_CR_CORE_ID_SHIFT (16) + +/* PVDEC_CORE, CR_PVDEC_CORE_ID, CR_PVDEC_CORE_CONFIG */ +#define PVDEC_CORE_CR_PVDEC_CORE_ID_CR_PVDEC_CORE_CONFIG_MASK (0x0000FFFF) +#define PVDEC_CORE_CR_PVDEC_CORE_ID_CR_PVDEC_CORE_CONFIG_SHIFT (0) + +#endif /* _IMG_PVDEC_CORE_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_pvdec_pixel_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_pvdec_pixel_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_pvdec_pixel_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_pvdec_pixel_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG PVDEC pixel Registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_PVDEC_PIXEL_REGS_H +#define _IMG_PVDEC_PIXEL_REGS_H + +/* PVDEC_PIXEL, CR_MAX_FRAME_CONFIG, CR_PVDEC_HOR_MSB */ +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_PVDEC_HOR_MSB_MASK (0x001F0000) + +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_PVDEC_HOR_MSB_SHIFT (16) + +/* PVDEC_PIXEL, CR_MAX_FRAME_CONFIG, CR_PVDEC_VER_MSB */ +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_PVDEC_VER_MSB_MASK (0x1F000000) +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_PVDEC_VER_MSB_SHIFT (24) + +/* PVDEC_PIXEL, CR_MAX_FRAME_CONFIG, CR_MSVDX_HOR_MSB */ +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_MSVDX_HOR_MSB_MASK (0x0000001F) +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_MSVDX_HOR_MSB_SHIFT (0) + +/* PVDEC_PIXEL, CR_MAX_FRAME_CONFIG, CR_MSVDX_VER_MSB */ +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_MSVDX_VER_MSB_MASK (0x00001F00) +#define PVDEC_PIXEL_CR_MAX_FRAME_CONFIG_CR_MSVDX_VER_MSB_SHIFT (8) + +#endif /* _IMG_PVDEC_PIXEL_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_pvdec_test_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_pvdec_test_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_pvdec_test_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_pvdec_test_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG PVDEC test Registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_PVDEC_TEST_REGS_H +#define _IMG_PVDEC_TEST_REGS_H + +/* PVDEC_TEST, RAND_STL_MEM_RDATA_CONFIG, STALL_ENABLE_MEM_RDATA */ +#define PVDEC_TEST_MEM_READ_LATENCY_OFFSET (0x00F0) + +/* PVDEC_TEST, MEM_READ_LATENCY, READ_RESPONSE_RAND_LATENCY */ +#define PVDEC_TEST_MEM_WRITE_RESPONSE_LATENCY_OFFSET (0x00F4) + +/* PVDEC_TEST, MEM_WRITE_RESPONSE_LATENCY, WRITE_RESPONSE_RAND_LATENCY */ +#define PVDEC_TEST_MEM_CTRL_OFFSET (0x00F8) + +/* PVDEC_TEST, RAND_STL_MEM_WDATA_CONFIG, STALL_ENABLE_MEM_WDATA */ +#define PVDEC_TEST_RAND_STL_MEM_WRESP_CONFIG_OFFSET (0x00E8) + +/* PVDEC_TEST, RAND_STL_MEM_WRESP_CONFIG, STALL_ENABLE_MEM_WRESP */ +#define PVDEC_TEST_RAND_STL_MEM_RDATA_CONFIG_OFFSET (0x00EC) + +/* PVDEC_TEST, MEMORY_BUS2_MONITOR_2, BUS2_ADDR */ +#define PVDEC_TEST_RAND_STL_MEM_CMD_CONFIG_OFFSET (0x00E0) + +/* PVDEC_TEST, RAND_STL_MEM_CMD_CONFIG, STALL_ENABLE_MEM_CMD */ +#define PVDEC_TEST_RAND_STL_MEM_WDATA_CONFIG_OFFSET (0x00E4) + +#endif /* _IMG_PVDEC_TEST_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_vdec_fw_msg.h b/drivers/media/platform/vxe-vxd/decoder/img_vdec_fw_msg.h --- a/drivers/media/platform/vxe-vxd/decoder/img_vdec_fw_msg.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_vdec_fw_msg.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG VDEC firmware messages + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_VDEC_FW_MSG_H +#define _IMG_VDEC_FW_MSG_H + +#include + +/* FW_DEVA_COMPLETED ERROR_FLAGS */ +#define FW_DEVA_COMPLETED_ERROR_FLAGS_TYPE unsigned short +#define FW_DEVA_COMPLETED_ERROR_FLAGS_MASK (0xFFFF) +#define FW_DEVA_COMPLETED_ERROR_FLAGS_SHIFT (0) +#define FW_DEVA_COMPLETED_ERROR_FLAGS_OFFSET (0x000C) + +/* FW_DEVA_COMPLETED NUM_BEWDTS */ +#define FW_DEVA_COMPLETED_NUM_BEWDTS_TYPE unsigned int +#define FW_DEVA_COMPLETED_NUM_BEWDTS_MASK (0xFFFFFFFF) +#define FW_DEVA_COMPLETED_NUM_BEWDTS_SHIFT (0) +#define FW_DEVA_COMPLETED_NUM_BEWDTS_OFFSET (0x0010) + +/* FW_DEVA_COMPLETED NUM_MBSDROPPED */ +#define FW_DEVA_COMPLETED_NUM_MBSDROPPED_TYPE unsigned int +#define FW_DEVA_COMPLETED_NUM_MBSDROPPED_MASK (0xFFFFFFFF) +#define FW_DEVA_COMPLETED_NUM_MBSDROPPED_SHIFT (0) +#define FW_DEVA_COMPLETED_NUM_MBSDROPPED_OFFSET (0x0014) + +/* FW_DEVA_COMPLETED NUM_MBSRECOVERED */ +#define FW_DEVA_COMPLETED_NUM_MBSRECOVERED_TYPE unsigned int +#define FW_DEVA_COMPLETED_NUM_MBSRECOVERED_MASK (0xFFFFFFFF) +#define FW_DEVA_COMPLETED_NUM_MBSRECOVERED_SHIFT (0) +#define FW_DEVA_COMPLETED_NUM_MBSRECOVERED_OFFSET (0x0018) + +/* FW_DEVA_PANIC ERROR_INT */ +#define FW_DEVA_PANIC_ERROR_INT_TYPE unsigned int +#define FW_DEVA_PANIC_ERROR_INT_MASK (0xFFFFFFFF) +#define FW_DEVA_PANIC_ERROR_INT_SHIFT (0) +#define FW_DEVA_PANIC_ERROR_INT_OFFSET (0x000C) + +/* FW_ASSERT FILE_NAME_HASH */ +#define FW_ASSERT_FILE_NAME_HASH_TYPE unsigned int +#define FW_ASSERT_FILE_NAME_HASH_MASK (0xFFFFFFFF) +#define FW_ASSERT_FILE_NAME_HASH_SHIFT (0) +#define FW_ASSERT_FILE_NAME_HASH_OFFSET (0x0004) + +/* FW_ASSERT FILE_LINE */ +#define FW_ASSERT_FILE_LINE_TYPE unsigned int +#define FW_ASSERT_FILE_LINE_MASK (0xFFFFFFFE) +#define FW_ASSERT_FILE_LINE_SHIFT (1) +#define FW_ASSERT_FILE_LINE_OFFSET (0x0008) + +/* FW_SO TASK_NAME */ +#define FW_SO_TASK_NAME_TYPE unsigned int +#define FW_SO_TASK_NAME_MASK (0xFFFFFFFF) +#define FW_SO_TASK_NAME_SHIFT (0) +#define FW_SO_TASK_NAME_OFFSET (0x0004) + +/* FW_DEVA_GENMSG TRANS_ID */ +#define FW_DEVA_GENMSG_TRANS_ID_TYPE unsigned int +#define FW_DEVA_GENMSG_TRANS_ID_MASK (0xFFFFFFFF) +#define FW_DEVA_GENMSG_TRANS_ID_SHIFT (0) +#define FW_DEVA_GENMSG_TRANS_ID_OFFSET (0x0008) + +/* FW_DEVA_GENMSG MSG_TYPE */ +#define FW_DEVA_GENMSG_MSG_TYPE_TYPE unsigned char +#define FW_DEVA_GENMSG_MSG_TYPE_MASK (0xFF) +#define FW_DEVA_GENMSG_MSG_TYPE_SHIFT (0) +#define FW_DEVA_GENMSG_MSG_TYPE_OFFSET (0x0001) + +/* FW_DEVA_SIGNATURES SIGNATURES */ +#define FW_DEVA_SIGNATURES_SIGNATURES_OFFSET (0x0010) + +/* FW_DEVA_SIGNATURES MSG_SIZE */ +#define FW_DEVA_SIGNATURES_MSG_SIZE_TYPE unsigned char +#define FW_DEVA_SIGNATURES_MSG_SIZE_MASK (0x7F) +#define FW_DEVA_SIGNATURES_MSG_SIZE_SHIFT (0) +#define FW_DEVA_SIGNATURES_MSG_SIZE_OFFSET (0x0000) + +/* FW_DEVA_CONTIGUITY_WARNING BEGIN_MB_NUM */ +#define FW_DEVA_SIGNATURES_SIZE (20) + +/* FW_DEVA_SIGNATURES SIGNATURE_SELECT */ +#define FW_DEVA_SIGNATURES_SIGNATURE_SELECT_TYPE unsigned int +#define FW_DEVA_SIGNATURES_SIGNATURE_SELECT_MASK (0xFFFFFFFF) +#define FW_DEVA_SIGNATURES_SIGNATURE_SELECT_SHIFT (0) +#define FW_DEVA_SIGNATURES_SIGNATURE_SELECT_OFFSET (0x000C) + +/* FW_DEVA_GENMSG TRANS_ID */ +#define FW_DEVA_DECODE_SIZE (52) + +/* FW_DEVA_DECODE CTRL_ALLOC_ADDR */ +#define FW_DEVA_DECODE_CTRL_ALLOC_ADDR_TYPE unsigned int +#define FW_DEVA_DECODE_CTRL_ALLOC_ADDR_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_CTRL_ALLOC_ADDR_SHIFT (0) +#define FW_DEVA_DECODE_CTRL_ALLOC_ADDR_OFFSET (0x0010) + +/* FW_DEVA_DECODE BUFFER_SIZE */ +#define FW_DEVA_DECODE_BUFFER_SIZE_TYPE unsigned short +#define FW_DEVA_DECODE_BUFFER_SIZE_MASK (0xFFFF) +#define FW_DEVA_DECODE_BUFFER_SIZE_SHIFT (0) +#define FW_DEVA_DECODE_BUFFER_SIZE_OFFSET (0x000E) + +/* FW_DEVA_DECODE OPERATING_MODE */ +#define FW_DEVA_DECODE_OPERATING_MODE_TYPE unsigned int +#define FW_DEVA_DECODE_OPERATING_MODE_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_OPERATING_MODE_OFFSET (0x0018) +#define FW_DEVA_DECODE_OPERATING_MODE_SHIFT (0) + +/* FW_DEVA_DECODE FLAGS */ +#define FW_DEVA_DECODE_FLAGS_TYPE unsigned short +#define FW_DEVA_DECODE_FLAGS_MASK (0xFFFF) +#define FW_DEVA_DECODE_FLAGS_SHIFT (0) +#define FW_DEVA_DECODE_FLAGS_OFFSET (0x000C) + +/* FW_DEVA_DECODE VDEC_FLAGS */ +#define FW_DEVA_DECODE_VDEC_FLAGS_TYPE unsigned char +#define FW_DEVA_DECODE_VDEC_FLAGS_MASK (0xFF) +#define FW_DEVA_DECODE_VDEC_FLAGS_SHIFT (0) +#define FW_DEVA_DECODE_VDEC_FLAGS_OFFSET (0x001E) + +/* FW_DEVA_DECODE GENC_ID */ +#define FW_DEVA_DECODE_GENC_ID_TYPE unsigned int +#define FW_DEVA_DECODE_GENC_ID_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_GENC_ID_SHIFT (0) +#define FW_DEVA_DECODE_GENC_ID_OFFSET (0x0028) + +/* FW_DEVA_DECODE MB_LOAD */ +#define FW_DEVA_DECODE_MB_LOAD_TYPE unsigned int +#define FW_DEVA_DECODE_MB_LOAD_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_MB_LOAD_OFFSET (0x0030) +#define FW_DEVA_DECODE_MB_LOAD_SHIFT (0) +#define FW_DEVA_DECODE_FRAGMENT_SIZE (16) + +/* FW_DEVA_DECODE STREAMID */ +#define FW_DEVA_DECODE_STREAMID_TYPE unsigned char +#define FW_DEVA_DECODE_STREAMID_MASK (0xFF) +#define FW_DEVA_DECODE_STREAMID_OFFSET (0x001F) +#define FW_DEVA_DECODE_STREAMID_SHIFT (0) + +/* FW_DEVA_DECODE EXT_STATE_BUFFER */ +#define FW_DEVA_DECODE_EXT_STATE_BUFFER_TYPE unsigned int +#define FW_DEVA_DECODE_EXT_STATE_BUFFER_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_EXT_STATE_BUFFER_OFFSET (0x0020) +#define FW_DEVA_DECODE_EXT_STATE_BUFFER_SHIFT (0) + +/* FW_DEVA_DECODE MSG_ID */ +#define FW_DEVA_DECODE_MSG_ID_TYPE unsigned short +#define FW_DEVA_DECODE_MSG_ID_MASK (0xFFFF) +#define FW_DEVA_DECODE_MSG_ID_OFFSET (0x0002) +#define FW_DEVA_DECODE_MSG_ID_SHIFT (0) + +/* FW_DEVA_DECODE TRANS_ID */ +#define FW_DEVA_DECODE_TRANS_ID_TYPE unsigned int +#define FW_DEVA_DECODE_TRANS_ID_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_TRANS_ID_OFFSET (0x0008) +#define FW_DEVA_DECODE_TRANS_ID_SHIFT (0) + +/* FW_DEVA_DECODE TILE_CFG */ +#define FW_DEVA_DECODE_TILE_CFG_TYPE unsigned int +#define FW_DEVA_DECODE_TILE_CFG_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_TILE_CFG_OFFSET (0x0024) +#define FW_DEVA_DECODE_TILE_CFG_SHIFT (0) + +/* FW_DEVA_GENMSG MSG_SIZE */ +#define FW_DEVA_GENMSG_MSG_SIZE_TYPE unsigned char +#define FW_DEVA_GENMSG_MSG_SIZE_MASK (0x7F) +#define FW_DEVA_GENMSG_MSG_SIZE_OFFSET (0x0000) +#define FW_DEVA_GENMSG_MSG_SIZE_SHIFT (0) + +/* FW_DEVA_DECODE_FRAGMENT CTRL_ALLOC_ADDR */ +#define FW_DEVA_DECODE_FRAGMENT_CTRL_ALLOC_ADDR_TYPE unsigned int +#define FW_DEVA_DECODE_FRAGMENT_CTRL_ALLOC_ADDR_MASK (0xFFFFFFFF) +#define FW_DEVA_DECODE_FRAGMENT_CTRL_ALLOC_ADDR_OFFSET (0x000C) +#define FW_DEVA_DECODE_FRAGMENT_CTRL_ALLOC_ADDR_SHIFT (0) + +/* FW_DEVA_DECODE_FRAGMENT BUFFER_SIZE */ +#define FW_DEVA_DECODE_FRAGMENT_BUFFER_SIZE_TYPE unsigned short +#define FW_DEVA_DECODE_FRAGMENT_BUFFER_SIZE_MASK (0xFFFF) +#define FW_DEVA_DECODE_FRAGMENT_BUFFER_SIZE_OFFSET (0x000A) +#define FW_DEVA_DECODE_FRAGMENT_BUFFER_SIZE_SHIFT (0) + +#endif /* _IMG_VDEC_FW_MSG_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/img_video_bus4_mmu_regs.h b/drivers/media/platform/vxe-vxd/decoder/img_video_bus4_mmu_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/img_video_bus4_mmu_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/img_video_bus4_mmu_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG video bus4 mmu registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _IMG_VIDEO_BUS4_MMU_REGS_H +#define _IMG_VIDEO_BUS4_MMU_REGS_H + +#define IMG_VIDEO_BUS4_MMU_MMU_DIR_BASE_ADDR_OFFSET (0x0020) + +/* IMG_VIDEO_BUS4_MMU, MMU_ADDRESS_CONTROL, MMU_BYPASS */ +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK (0x00000001) +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT (0) + +/* IMG_VIDEO_BUS4_MMU, REQUEST_LIMITED_THROUGHPUT, REQUEST_GAP */ +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_OFFSET (0x0070) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_SOFT_RESET */ +#define IMG_VIDEO_BUS4_MMU_MMU_BANK_INDEX_OFFSET (0x0010) +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT (28) + +/* IMG_VIDEO_BUS4_MMU, MMU_TILE_MAX_ADDR, TILE_MAX_ADDR */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL0_OFFSET (0x0000) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL0, MMU_TILING_SCHEME */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL0_MMU_TILING_SCHEME_MASK (0x00000001) +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL0_MMU_TILING_SCHEME_SHIFT (0) + +/* IMG_VIDEO_BUS4_MMU, MMU_TILE_CFG, TILE_STRIDE */ +#define IMG_VIDEO_BUS4_MMU_MMU_TILE_MIN_ADDR_STRIDE (4) +#define IMG_VIDEO_BUS4_MMU_MMU_TILE_MIN_ADDR_OFFSET (0x0050) + +/* IMG_VIDEO_BUS4_MMU, MMU_TILE_MIN_ADDR, TILE_MIN_ADDR */ +#define IMG_VIDEO_BUS4_MMU_MMU_TILE_MAX_ADDR_OFFSET (0x0060) +#define IMG_VIDEO_BUS4_MMU_MMU_TILE_MAX_ADDR_STRIDE (4) + +/* IMG_VIDEO_BUS4_MMU, MMU_STATUS1, MMU_FAULT_RNW */ +#define IMG_VIDEO_BUS4_MMU_MMU_MEM_REQ_OFFSET (0x0090) +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS1_MMU_FAULT_RNW_MASK (0x10000000) +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS1_MMU_FAULT_RNW_SHIFT (28) + +/* IMG_VIDEO_BUS4_MMU, MMU_MEM_REQ, TAG_OUTSTANDING */ +#define IMG_VIDEO_BUS4_MMU_MMU_MEM_REQ_TAG_OUTSTANDING_MASK (0x000003FF) +#define IMG_VIDEO_BUS4_MMU_MMU_MEM_REQ_TAG_OUTSTANDING_SHIFT (0) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL0, USE_TILE_STRIDE_PER_CONTEXT */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_OFFSET (0x0008) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_SOFT_RESET */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK (0x10000000) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_PAUSE_SET */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_PAUSE_SET_MASK (0x01000000) +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_PAUSE_SET_SHIFT (24) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_PAUSE_CLEAR */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_MASK (0x02000000) +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_SHIFT (25) + +/* IMG_VIDEO_BUS4_MMU, MMU_ADDRESS_CONTROL, UPPER_ADDRESS_FIXED */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG0_OFFSET (0x0080) + +/* IMG_VIDEO_BUS4_MMU, MMU_MEM_REQ, INT_PROTOCOL_FAULT */ +#define IMG_VIDEO_BUS4_MMU_MMU_MEM_EXT_OUTSTANDING_OFFSET (0x0094) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONFIG0, TAGS_SUPPORTED */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG1_OFFSET (0x0084) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_INVALDC */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_INVALDC_MASK (0x00000F00) +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL1_MMU_INVALDC_SHIFT (8) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONFIG1, SUPPORT_SECURE */ +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_OFFSET (0x0088) + +/* IMG_VIDEO_BUS4_MMU, MMU_STATUS0, MMU_FAULT_ADDR */ +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS1_OFFSET (0x008C) +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_MMU_FAULT_ADDR_SHIFT (12) + +/* IMG_VIDEO_BUS4_MMU, MMU_STATUS0, MMU_FAULT_ADDR */ +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_MMU_FAULT_ADDR_MASK (0xFFFFF000) + +/* IMG_VIDEO_BUS4_MMU, MMU_STATUS0, MMU_PF_N_RW */ +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_MMU_PF_N_RW_MASK (0x00000001) +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_MMU_PF_N_RW_SHIFT (0) + +/* IMG_VIDEO_BUS4_MMU, MMU_STATUS1, MMU_FAULT_REQ_ID */ +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_MASK (0x003F0000) +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_SHIFT (16) + +/* IMG_VIDEO_BUS4_MMU, MMU_STATUS0, MMU_SECURE_FAULT */ +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_MMU_SECURE_FAULT_MASK (0x00000002) +#define IMG_VIDEO_BUS4_MMU_MMU_STATUS0_MMU_SECURE_FAULT_SHIFT (1) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONFIG1, SUPPORT_STRIDE_PER_CONTEXT */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG1_SUPPORT_STRIDE_PER_CONTEXT_MASK (0x20000000) +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG1_SUPPORT_STRIDE_PER_CONTEXT_SHIFT (29) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONFIG1, SUPPORT_SECURE */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG1_SUPPORT_SECURE_MASK (0x80000000) +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG1_SUPPORT_SECURE_SHIFT (31) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONFIG0, EXTENDED_ADDR_RANGE */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_MASK (0x000000F0) +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_SHIFT (4) + +/* IMG_VIDEO_BUS4_MMU, MMU_CONFIG0, GROUP_OVERRIDE_SIZE */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_MASK (0x00000700) +#define IMG_VIDEO_BUS4_MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_SHIFT (8) + +#endif /* _IMG_VIDEO_BUS4_MMU_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/jpegfw_data.h b/drivers/media/platform/vxe-vxd/decoder/jpegfw_data.h --- a/drivers/media/platform/vxe-vxd/decoder/jpegfw_data.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/jpegfw_data.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures for the h264 parser firmware module. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include "jpegfw_data_shared.h" + +#ifndef _JPEGFW_DATA_H_ +#define _JPEGFW_DATA_H_ + +#define JPEG_VDEC_8x8_DCT_SIZE 64 //!< Number of elements in 8x8 DCT +#define JPEG_VDEC_MAX_COMPONENTS 4 //!< Maximum number of component in JPEG +#define JPEG_VDEC_MAX_SETS_HUFFMAN_TABLES 2 //!< Maximum set of huffman table in JPEG +#define JPEG_VDEC_MAX_QUANT_TABLES 4 //!< Maximum set of quantisation table in JPEG +#define JPEG_VDEC_TABLE_CLASS_NUM 2 //!< Maximum set of class of huffman table in JPEG +#define JPEG_VDEC_PLANE_MAX 4 //!< Maximum number of planes + +struct hentry { + unsigned short code; + unsigned char codelen; + unsigned char value; +}; + +/** + * struct vdec_jpeg_huffman_tableinfo - This structure contains JPEG huffmant table + * @bits: number of bits + * @values: codeword value + * + * NOTE: Should only contain JPEG specific information. + * JPEG Huffman Table Information + */ +struct vdec_jpeg_huffman_tableinfo { + /* number of bits */ + unsigned char bits[16]; + /* codeword value */ + unsigned char values[256]; +}; + +/* + * This structure contains JPEG DeQunatisation table + * NOTE: Should only contain JPEG specific information. + * @brief JPEG Dequantisation Table Information + */ +struct vdec_jpeg_de_quant_tableinfo { + /* Qunatisation precision */ + unsigned char precision; + /* Qunatisation Value for 8x8 DCT */ + unsigned short elements[64]; +}; + +/* + * This describes the JPEG parser component "Header data", shown in the + * Firmware Memory Layout diagram. This data is required by the JPEG firmware + * and should be supplied by the Host. + */ +struct jpegfw_header_data { + /* Primary decode buffer base addresses */ + struct vdecfw_image_buffer primary; + /* Reference (output) picture base addresses */ + unsigned int plane_offsets[JPEG_VDEC_PLANE_MAX]; + /* SOS fields count value */ + unsigned char hdr_sos_count; +}; + +/* + * This describes the JPEG parser component "Context data". + * JPEG does not need any data to be saved between pictures, this structure + * is needed only to fit in firmware framework. + */ +struct jpegfw_context_data { + unsigned int dummy; +}; + +#endif /* _JPEGFW_DATA_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/jpegfw_data_shared.h b/drivers/media/platform/vxe-vxd/decoder/jpegfw_data_shared.h --- a/drivers/media/platform/vxe-vxd/decoder/jpegfw_data_shared.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/jpegfw_data_shared.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures for the hevc parser firmware module + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + */ +#ifdef USE_SHARING +#endif + +#ifndef _JPEGFW_DATA_H_ +#define _JPEGFW_DATA_H_ + +#include "vdecfw_share.h" +#include "vdecfw_shared.h" + +#define JPEG_VDEC_8x8_DCT_SIZE 64 //!< Number of elements in 8x8 DCT +#define JPEG_VDEC_MAX_COMPONENTS 4 //!< Maximum number of component in JPEG +#define JPEG_VDEC_MAX_SETS_HUFFMAN_TABLES 2 //!< Maximum set of huffman table in JPEG +#define JPEG_VDEC_MAX_QUANT_TABLES 4 //!< Maximum set of quantisation table in JPEG +#define JPEG_VDEC_TABLE_CLASS_NUM 2 //!< Maximum set of class of huffman table in JPEG +#define JPEG_VDEC_PLANE_MAX 4 //!< Maximum number of planes + +struct hentry { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, code); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, codelen); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, value); +}; + +/* + * This structure contains JPEG huffmant table + * NOTE: Should only contain JPEG specific information. + * @brief JPEG Huffman Table Information + */ +struct vdec_jpeg_huffman_tableinfo { + /* number of bits */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, bits[16]); + /* codeword value */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, values[256]); +}; + +/* + * This structure contains JPEG DeQunatisation table + * NOTE: Should only contain JPEG specific information. + * @brief JPEG Dequantisation Table Information + */ +struct vdec_jpeg_de_quant_tableinfo { + /* Qunatisation precision */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, precision); + /* Qunatisation Value for 8x8 DCT */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned short, elements[64]); +}; + +/* + * This describes the JPEG parser component "Header data", shown in the + * Firmware Memory Layout diagram. This data is required by the JPEG firmware + * and should be supplied by the Host. + */ +struct jpegfw_header_data { + /* Primary decode buffer base addresses */ + struct vdecfw_image_buffer primary; + /* Reference (output) picture base addresses */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + plane_offsets[JPEG_VDEC_PLANE_MAX]); + /* SOS fields count value */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned char, hdr_sos_count); +}; + +/* + * This describes the JPEG parser component "Context data". + * JPEG does not need any data to be saved between pictures, this structure + * is needed only to fit in firmware framework. + */ +struct jpegfw_context_data { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, dummy); +}; + +#endif /* _JPEGFW_DATA_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.c b/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.c --- a/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * h.264 secure data unit parsing API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "bspp_int.h" +#include "jpeg_secure_parser.h" +#include "jpegfw_data.h" +#include "swsr.h" + +#define JPEG_MCU_SIZE 8 + +#define JPEG_MAX_COMPONENTS 4 +#define MAX_SETS_HUFFMAN_TABLES 2 +#define MAX_QUANT_TABLES 4 + +#define TABLE_CLASS_DC 0 +#define TABLE_CLASS_AC 1 +#define TABLE_CLASS_NUM 2 + +/* Marker Codes */ +#define CODE_SOF_BASELINE 0xC0 +#define CODE_SOF1 0xC1 +#define CODE_SOF2 0xC2 +#define CODE_SOF3 0xC3 +#define CODE_SOF5 0xC5 +#define CODE_SOF6 0xC6 +#define CODE_SOF7 0xC7 +#define CODE_SOF8 0xC8 +#define CODE_SOF9 0xC9 +#define CODE_SOF10 0xCA +#define CODE_SOF11 0xCB +#define CODE_SOF13 0xCD +#define CODE_SOF14 0xCE +#define CODE_SOF15 0xCF +#define CODE_DHT 0xC4 +#define CODE_RST0 0xD0 +#define CODE_RST1 0xD1 +#define CODE_RST2 0xD2 +#define CODE_RST3 0xD3 +#define CODE_RST4 0xD4 +#define CODE_RST5 0xD5 +#define CODE_RST6 0xD6 +#define CODE_RST7 0xD7 +#define CODE_SOI 0xD8 +#define CODE_EOI 0xD9 +#define CODE_SOS 0xDA +#define CODE_DQT 0xDB +#define CODE_DRI 0xDD +#define CODE_APP0 0xE0 +#define CODE_APP1 0xE1 +#define CODE_APP2 0xE2 +#define CODE_APP3 0xE3 +#define CODE_APP4 0xE4 +#define CODE_APP5 0xE5 +#define CODE_APP6 0xE6 +#define CODE_APP7 0xE7 +#define CODE_APP8 0xE8 +#define CODE_APP9 0xE9 +#define CODE_APP10 0xEA +#define CODE_APP11 0xEB +#define CODE_APP12 0xEC +#define CODE_APP13 0xED +#define CODE_APP14 0xEE +#define CODE_APP15 0xEF +#define CODE_M_DAC 0xCC +#define CODE_COMMENT 0xFE + +enum bspp_exception_handler { + /* BSPP parse exception handler */ + BSPP_EXCEPTION_HANDLER_NONE = 0x00, + /* Jump at exception (external use) */ + BSPP_EXCEPTION_HANDLER_JUMP, + BSPP_EXCEPTION_HANDLER_FORCE32BITS = 0x7FFFFFFFU +}; + +struct components { + unsigned char identifier; + unsigned char horz_factor; + unsigned char vert_factor; + unsigned char quant_table; +}; + +struct jpeg_segment_sof { + unsigned char precision; + unsigned short height; + unsigned short width; + unsigned char component; + struct components components[JPEG_VDEC_MAX_COMPONENTS]; +}; + +struct jpeg_segment_header { + unsigned char type; + unsigned short payload_size; +}; + +/* + * Read bitstream data that may LOOK like SCP + * (but in fact is regular data and should be read as such) + * @return 8bits read from the bitstream + */ +static unsigned char bspp_jpeg_readbyte_asdata(void *swsr_ctx) +{ + if (swsr_check_delim_or_eod(swsr_ctx) == SWSR_FOUND_DELIM) { + swsr_consume_delim(swsr_ctx, SWSR_EMPREVENT_NONE, 8, NULL); + return 0xFF; + } else { + return swsr_read_bits(swsr_ctx, 8); + } +} + +/* + * Read bitstream data that may LOOK like SCP + * (but in fact be regular data should be read as such) + * @return 16bits read from the bitstream + */ +static unsigned short bspp_jpeg_readword_asdata(void *swsr_ctx) +{ + unsigned short byte1 = bspp_jpeg_readbyte_asdata(swsr_ctx); + unsigned short byte2 = bspp_jpeg_readbyte_asdata(swsr_ctx); + + return (byte1 << 8 | byte2); +} + +/* + * Access regular bitstream data that may LOOK like SCP + * (but in fact be regular data) + */ +static void bspp_jpeg_consume_asdata(void *swsr_ctx, int len) +{ + while (len > 0) { + bspp_jpeg_readbyte_asdata(swsr_ctx); + len--; + } +} + +/* + * Parse SOF segment + */ +static enum bspp_error_type bspp_jpeg_segment_parse_sof(void *swsr_ctx, + struct jpeg_segment_sof *sof_header) +{ + unsigned char comp_ind; + + sof_header->precision = swsr_read_bits(swsr_ctx, 8); + if (sof_header->precision != 8) { + pr_warn("Sample precision has invalid value %d\n", + sof_header->precision); + return BSPP_ERROR_INVALID_VALUE; + } + + sof_header->height = bspp_jpeg_readword_asdata(swsr_ctx); + sof_header->width = bspp_jpeg_readword_asdata(swsr_ctx); + if (sof_header->height < JPEG_MCU_SIZE || sof_header->width < JPEG_MCU_SIZE) { + pr_warn("Sample X/Y smaller than macroblock\n"); + return BSPP_ERROR_INVALID_VALUE; + } + sof_header->component = swsr_read_bits(swsr_ctx, 8); + if (sof_header->component > JPEG_MAX_COMPONENTS) { + pr_warn("Number of components (%d) is greater than max allowed\n", + sof_header->component); + return BSPP_ERROR_INVALID_VALUE; + } + /* parse the component */ + for (comp_ind = 0; comp_ind < sof_header->component; comp_ind++) { + sof_header->components[comp_ind].identifier = swsr_read_bits(swsr_ctx, 8); + sof_header->components[comp_ind].horz_factor = swsr_read_bits(swsr_ctx, 4); + sof_header->components[comp_ind].vert_factor = swsr_read_bits(swsr_ctx, 4); + sof_header->components[comp_ind].quant_table = swsr_read_bits(swsr_ctx, 8); + + pr_debug("components[%d]=(identifier=%d; horz_factor=%d; vert_factor=%d; quant_table=%d)", + comp_ind, + sof_header->components[comp_ind].identifier, + sof_header->components[comp_ind].horz_factor, + sof_header->components[comp_ind].vert_factor, + sof_header->components[comp_ind].quant_table); + } + + return BSPP_ERROR_NONE; +} + +/* + * Seeks to delimeter if we're not already on one + */ +static enum swsr_found bspp_jpeg_tryseek_delimeter(void *swsr_ctx) +{ + enum swsr_found was_delim_or_eod = swsr_check_delim_or_eod(swsr_ctx); + + if (was_delim_or_eod != SWSR_FOUND_DELIM) + was_delim_or_eod = swsr_seek_delim_or_eod(swsr_ctx); + + return was_delim_or_eod; +} + +static enum swsr_found bspp_jpeg_tryconsume_delimeters(void *swsr_ctx) +{ + enum swsr_found is_delim_or_eod = swsr_check_delim_or_eod(swsr_ctx); + + while (is_delim_or_eod == SWSR_FOUND_DELIM) { + swsr_consume_delim(swsr_ctx, SWSR_EMPREVENT_NONE, 8, NULL); + is_delim_or_eod = swsr_check_delim_or_eod(swsr_ctx); + } + return is_delim_or_eod; +} + +static enum swsr_found bspp_jpeg_tryseek_and_consume_delimeters(void *swsr_ctx) +{ + enum swsr_found is_delim_or_eod; + + bspp_jpeg_tryseek_delimeter(swsr_ctx); + is_delim_or_eod = bspp_jpeg_tryconsume_delimeters(swsr_ctx); + return is_delim_or_eod; +} + +/* + * Read segment type and size + * @return IMG_TRUE when header is found, + * IMG_FALSE if it has to be called again + */ +static unsigned char bspp_jpeg_segment_read_header(void *swsr_ctx, + struct bspp_unit_data *unit_data, + struct jpeg_segment_header *jpeg_segment_header) +{ + bspp_jpeg_tryconsume_delimeters(swsr_ctx); + jpeg_segment_header->type = swsr_read_bits(swsr_ctx, 8); + + if (jpeg_segment_header->type != 0) + pr_debug("NAL=0x%x\n", jpeg_segment_header->type); + + jpeg_segment_header->payload_size = 0; + + switch (jpeg_segment_header->type) { + case CODE_SOS: + case CODE_DRI: + case CODE_SOF_BASELINE: + case CODE_SOF1: + case CODE_SOF2: + case CODE_SOF3: + case CODE_SOF5: + case CODE_SOF6: + case CODE_SOF7: + case CODE_SOF8: + case CODE_SOF9: + case CODE_SOF10: + case CODE_SOF11: + case CODE_SOF13: + case CODE_SOF14: + case CODE_SOF15: + case CODE_APP0: + case CODE_APP1: + case CODE_APP2: + case CODE_APP3: + case CODE_APP4: + case CODE_APP5: + case CODE_APP6: + case CODE_APP7: + case CODE_APP8: + case CODE_APP9: + case CODE_APP10: + case CODE_APP11: + case CODE_APP12: + case CODE_APP13: + case CODE_APP14: + case CODE_APP15: + case CODE_DHT: + case CODE_DQT: + case CODE_COMMENT: + { + jpeg_segment_header->payload_size = + bspp_jpeg_readword_asdata(swsr_ctx) - 2; + } + break; + case CODE_EOI: + case CODE_SOI: + case CODE_RST0: + case CODE_RST1: + case CODE_RST2: + case CODE_RST3: + case CODE_RST4: + case CODE_RST5: + case CODE_RST6: + case CODE_RST7: + /* + * jpeg_segment_header->payload_size reset to 0 previously, + * so just break. + */ + break; + case 0: + { + /* + * Emulation prevention is OFF which means that 0 after + * 0xff will not be swallowed + * and has to be treated as data + */ + bspp_jpeg_tryseek_and_consume_delimeters(swsr_ctx); + return 0; + } + default: + { + pr_err("BAD NAL=%#x\n", jpeg_segment_header->type); + unit_data->parse_error |= BSPP_ERROR_UNRECOVERABLE; + } + } + + pr_debug("payloadSize=%#x\n", jpeg_segment_header->payload_size); + return 1; +} + +static void bspp_jpeg_calculate_mcus(struct jpeg_segment_sof *data_sof, + unsigned char *alignment_width, + unsigned char *alignment_height) +{ + unsigned char i; + unsigned char max_horz_factor = 0; + unsigned char max_vert_factor = 0; + unsigned short mcu_width = 0; + unsigned short mcu_height = 0; + + /* Determine maximum scale factors */ + for (i = 0; i < data_sof->component; i++) { + unsigned char horz_factor = data_sof->components[i].horz_factor; + unsigned char vert_factor = data_sof->components[i].vert_factor; + + max_horz_factor = horz_factor > max_horz_factor ? horz_factor : max_horz_factor; + max_vert_factor = vert_factor > max_vert_factor ? vert_factor : max_vert_factor; + } + /* + * Alignment we want to have must be: + * - mutliple of VDEC_MB_DIMENSION + * - at least of the size that will fit whole MCUs + */ + *alignment_width = + VDEC_ALIGN_SIZE((8 * max_horz_factor), VDEC_MB_DIMENSION, + unsigned int, unsigned int); + *alignment_height = + VDEC_ALIGN_SIZE((8 * max_vert_factor), VDEC_MB_DIMENSION, + unsigned int, unsigned int); + + /* Calculate dimensions in MCUs */ + mcu_width += (data_sof->width + (8 * max_horz_factor) - 1) / (8 * max_horz_factor); + mcu_height += (data_sof->height + (8 * max_vert_factor) - 1) / (8 * max_vert_factor); + +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s; w=%d; w[MCU]=%d\n", __func__, data_sof->width, mcu_width); + pr_info("%s; h=%d; h[MCU]=%d\n", __func__, data_sof->height, mcu_height); +#endif +} + +static int bspp_jpeg_common_seq_hdr_populate(struct jpeg_segment_sof *sof_header, + struct vdec_comsequ_hdrinfo *com_sequ_hdr_info, + unsigned char alignment_width, + unsigned char alignment_height) +{ + unsigned short i; + int res; + struct img_pixfmt_desc format_desc; + + memset(&format_desc, 0, sizeof(struct img_pixfmt_desc)); + memset(com_sequ_hdr_info, 0, sizeof(*com_sequ_hdr_info)); + + com_sequ_hdr_info->max_frame_size.width = VDEC_ALIGN_SIZE(sof_header->width, + alignment_width, + unsigned int, unsigned int); + com_sequ_hdr_info->max_frame_size.height = VDEC_ALIGN_SIZE(sof_header->height, + alignment_height, unsigned int, + unsigned int); + com_sequ_hdr_info->frame_size.width = sof_header->width; + com_sequ_hdr_info->frame_size.height = sof_header->height; + com_sequ_hdr_info->orig_display_region.width = sof_header->width; + com_sequ_hdr_info->orig_display_region.height = sof_header->height; + + com_sequ_hdr_info->pixel_info.bitdepth_y = 8; + com_sequ_hdr_info->pixel_info.bitdepth_c = 8; + com_sequ_hdr_info->pixel_info.num_planes = sof_header->component; + /* actually we have to set foramt accroding to the following table + * H1 V1 H2 V2 H3 V3 J:a:b h/v + * 1 1 1 1 1 1 4:4:4 1/1 + * 1 2 1 1 1 1 4:4:0 1/2 + * 1 4 1 1 1 1 4:4:1* 1/4 + * 1 4 1 2 1 2 4:4:0 1/2 + * 2 1 1 1 1 1 4:2:2 2/1 + * 2 2 1 1 1 1 4:2:0 2/2 + * 2 2 2 1 2 1 4:4:0 1/2 + * 2 4 1 1 1 1 4:2:1* 2/4 + * 4 1 1 1 1 1 4:1:1 4/1 + * 4 1 2 1 2 1 4:2:2 2/1 + * 4 2 1 1 1 1 4:1:0 4/2 + * 4 4 2 2 2 2 4:2:0 2/2 + */ + if (sof_header->component == (JPEG_MAX_COMPONENTS - 1)) { + com_sequ_hdr_info->pixel_info.chroma_fmt = PIXEL_MULTICHROME; + if ((sof_header->components[1].horz_factor == 1 && + sof_header->components[1].vert_factor == 1) && + (sof_header->components[2].horz_factor == 1 && + sof_header->components[2].vert_factor == 1)) { + if (sof_header->components[0].horz_factor == 1 && + sof_header->components[0].vert_factor == 1) { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = PIXEL_FORMAT_444; + } else if (sof_header->components[0].horz_factor == 2) { + if (sof_header->components[0].vert_factor == 1) { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = + PIXEL_FORMAT_422; + } else if (sof_header->components[0].vert_factor == 2) { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = + PIXEL_FORMAT_420; + } else { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = + PIXEL_FORMAT_444; + } + } else if ((sof_header->components[0].horz_factor == 4) && + (sof_header->components[0].vert_factor == 1)) { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = PIXEL_FORMAT_411; + } else { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = PIXEL_FORMAT_444; + } + } else { + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = PIXEL_FORMAT_444; + } + } else { + com_sequ_hdr_info->pixel_info.chroma_fmt = PIXEL_MONOCHROME; + com_sequ_hdr_info->pixel_info.chroma_fmt_idc = PIXEL_FORMAT_MONO; + } + + for (i = 0; (i < sof_header->component) && (i < IMG_MAX_NUM_PLANES); i++) { + format_desc.planes[i] = 1; + format_desc.h_numer[i] = sof_header->components[i].horz_factor; + format_desc.v_numer[i] = sof_header->components[i].vert_factor; + } + + res = pixel_gen_pixfmt(&com_sequ_hdr_info->pixel_info.pixfmt, &format_desc); + if (res != 0) { + pr_err("Failed to generate pixel format.\n"); + return res; + } + + return 0; +} + +static void bspp_jpeg_pict_hdr_populate(struct jpeg_segment_sof *sof_header, + struct bspp_pict_hdr_info *pict_hdr_info) +{ + memset(pict_hdr_info, 0, sizeof(*pict_hdr_info)); + + pict_hdr_info->intra_coded = 1; + pict_hdr_info->ref = 0; + + pict_hdr_info->coded_frame_size.width = (unsigned int)sof_header->width; + pict_hdr_info->coded_frame_size.height = (unsigned int)sof_header->height; + pict_hdr_info->disp_info.enc_disp_region.width = (unsigned int)sof_header->width; + pict_hdr_info->disp_info.enc_disp_region.height = (unsigned int)sof_header->height; + + pict_hdr_info->pict_aux_data.id = BSPP_INVALID; + pict_hdr_info->second_pict_aux_data.id = BSPP_INVALID; + pict_hdr_info->pict_sgm_data.id = BSPP_INVALID; +} + +static int bspp_jpeg_parse_picture_unit(void *swsr_ctx, + struct bspp_unit_data *unit_data) +{ + /* assume we'll be fine */ + unit_data->parse_error = BSPP_ERROR_NONE; + + while ((unit_data->parse_error == BSPP_ERROR_NONE) && + !(unit_data->slice || unit_data->extracted_all_data)) { + struct jpeg_segment_header segment_header; + /* + * Try hard to read segment header. The only limit we set here is EOD- + * if it happens, we will get an exception, to stop this madness. + */ + while (!bspp_jpeg_segment_read_header(swsr_ctx, unit_data, &segment_header) && + unit_data->parse_error == BSPP_ERROR_NONE) + ; + + switch (segment_header.type) { + case CODE_SOF1: + case CODE_SOF2: + case CODE_SOF3: + case CODE_SOF5: + case CODE_SOF6: + case CODE_SOF8: + case CODE_SOF9: + case CODE_SOF10: + case CODE_SOF11: + case CODE_SOF13: + case CODE_SOF14: + case CODE_SOF15: + { + bspp_jpeg_consume_asdata(swsr_ctx, segment_header.payload_size); + bspp_jpeg_tryseek_delimeter(swsr_ctx); + unit_data->extracted_all_data = 1; + unit_data->slice = 1; + unit_data->parse_error |= BSPP_ERROR_UNSUPPORTED; + return IMG_ERROR_NOT_SUPPORTED; + } + case CODE_SOI: + { + /* + * Reinitialize context at the beginning of each image + */ + } + break; + case CODE_EOI: + { + /* + * Some more frames can be concatenated after SOI, + * but we'll discard it for now + */ + while (bspp_jpeg_tryseek_and_consume_delimeters(swsr_ctx) != SWSR_FOUND_EOD) + ; + unit_data->extracted_all_data = 1; + return 0; + } + case CODE_SOF_BASELINE: + { + int res; + unsigned char alignment_width = 0; + unsigned char alignment_height = 0; + struct jpeg_segment_sof sof_data; + + struct bspp_sequ_hdr_info *sequ_hdr_info = + &unit_data->impl_sequ_hdr_info->sequ_hdr_info; + + memset(&sof_data, 0, sizeof(*&sof_data)); + + /* SOF is the only segment we are interested in- parse it */ + unit_data->parse_error |= bspp_jpeg_segment_parse_sof(swsr_ctx, &sof_data); + /* + * to correctly allocate size for frame we need to have correct MCUs to + * get alignment info + */ + bspp_jpeg_calculate_mcus(&sof_data, &alignment_width, &alignment_height); + + /* fill in headers expected by BSPP framework */ + res = bspp_jpeg_common_seq_hdr_populate(&sof_data, + &sequ_hdr_info->com_sequ_hdr_info, + alignment_width, + alignment_height); + if (res != 0) { + unit_data->parse_error |= BSPP_ERROR_UNRECOVERABLE; + return res; + } + + bspp_jpeg_pict_hdr_populate(&sof_data, unit_data->out.pict_hdr_info); + + /* fill in sequence IDs for header and picture */ + sequ_hdr_info->sequ_hdr_id = BSPP_DEFAULT_SEQUENCE_ID; + unit_data->pict_sequ_hdr_id = BSPP_DEFAULT_SEQUENCE_ID; + + /* reset SOS fields counter value */ + unit_data->out.pict_hdr_info->sos_count = 0; + } + break; + case CODE_SOS: + { + /* increment the SOS fields counter */ + unit_data->out.pict_hdr_info->sos_count++; + + unit_data->slice = 1; + bspp_jpeg_consume_asdata(swsr_ctx, segment_header.payload_size); + return 0; + } + case CODE_DRI: + break; + default: + { +#ifdef DEBUG_DECODER_DRIVER + pr_info("Skipping over 0x%x bytes\n", segment_header.payload_size); +#endif + bspp_jpeg_consume_asdata(swsr_ctx, segment_header.payload_size); + } + break; + } + /* + * After parsing segment we should already be on delimeter. + * Consume it, so header parsing can be started. + */ + bspp_jpeg_tryseek_and_consume_delimeters(swsr_ctx); + } + return 0; +} + +int bspp_jpeg_unit_parser(void *swsr_ctx, struct bspp_unit_data *unit_data) +{ + int retval = 0; + + switch (unit_data->unit_type) { + case BSPP_UNIT_PICTURE: + { + retval = bspp_jpeg_parse_picture_unit(swsr_ctx, unit_data); + unit_data->new_closed_gop = 1; + } + break; + default: + { + unit_data->parse_error = BSPP_ERROR_INVALID_VALUE; + } + break; + } + + return retval; +} + +int bspp_jpeg_setparser_config(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *pvidstd_features, + struct bspp_swsr_ctx *pswsr_ctx, + struct bspp_parser_callbacks *pparser_callbacks, + struct bspp_inter_pict_data *pinterpict_data) +{ + /* Set JPEG parser callbacks. */ + pparser_callbacks->parse_unit_cb = bspp_jpeg_unit_parser; + + /* Set JPEG specific features. */ + pvidstd_features->seq_size = sizeof(struct bspp_jpeg_sequ_hdr_info); + pvidstd_features->uses_vps = 0; + pvidstd_features->uses_pps = 0; + + /* Set JPEG specific shift register config. */ + pswsr_ctx->emulation_prevention = SWSR_EMPREVENT_NONE; + pswsr_ctx->sr_config.delim_type = SWSR_DELIM_SCP; + pswsr_ctx->sr_config.delim_length = 8; + pswsr_ctx->sr_config.scp_value = 0xFF; + + return 0; +} + +void bspp_jpeg_determine_unit_type(unsigned char bitstream_unittype, + int disable_mvc, + enum bspp_unit_type *bspp_unittype) +{ + *bspp_unittype = BSPP_UNIT_PICTURE; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.h b/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.h --- a/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/jpeg_secure_parser.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * JPEG secure data unit parsing API. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ +#ifndef __JPEGSECUREPARSER_H__ +#define __JPEGSECUREPARSER_H__ + +#include "bspp_int.h" + +/** + * struct bspp_jpeg_sequ_hdr_info - bspp_jpeg_sequ_hdr_info dummu structure + * @dummy: dummy structure + */ +struct bspp_jpeg_sequ_hdr_info { + unsigned int dummy; +}; + +int bspp_jpeg_setparser_config(enum vdec_bstr_format bstr_format, + struct bspp_vid_std_features *pvidstd_features, + struct bspp_swsr_ctx *pswsr_ctx, + struct bspp_parser_callbacks *pparser_callbacks, + struct bspp_inter_pict_data *pinterpict_data); + +void bspp_jpeg_determine_unit_type(unsigned char bitstream_unittype, + int disable_mvc, + enum bspp_unit_type *bspp_unittype); + +#endif /*__JPEGSECUREPARSER_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/mem_io.h b/drivers/media/platform/vxe-vxd/decoder/mem_io.h --- a/drivers/media/platform/vxe-vxd/decoder/mem_io.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/mem_io.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG PVDEC pixel Registers + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _MEM_IO_H +#define _MEM_IO_H + +#include + +#include "reg_io2.h" + +#define MEMIO_CHECK_ALIGNMENT(vpmem) \ + IMG_ASSERT((vpmem)) + +#define MEMIO_READ_FIELD(vpmem, field) \ + ((((*((field ## _TYPE *)(((unsigned long)(vpmem)) + field ## _OFFSET))) & \ + field ## _MASK) >> field ## _SHIFT)) + +#define MEMIO_WRITE_FIELD(vpmem, field, value, type) \ + do { \ + type __vpmem = vpmem; \ + MEMIO_CHECK_ALIGNMENT(__vpmem); \ + (*((field ## _TYPE *)(((unsigned long)(__vpmem)) + \ + field ## _OFFSET))) = \ + (field ## _TYPE)(((*((field ## _TYPE *)(((unsigned long)(__vpmem)) + \ + field ## _OFFSET))) & \ + ~(field ## _TYPE)field ## _MASK) | \ + (field ## _TYPE)(((value) << field ## _SHIFT) & \ + field ## _MASK)); \ + } while (0) \ + +#endif /* _MEM_IO_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/mmu_defs.h b/drivers/media/platform/vxe-vxd/decoder/mmu_defs.h --- a/drivers/media/platform/vxe-vxd/decoder/mmu_defs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/mmu_defs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * V-DEC MMU Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + */ + +#ifndef _VXD_MMU_DEF_H_ +#define _VXD_MMU_DEF_H_ + +/* + * This type defines MMU variant. + */ +enum mmu_etype { + MMU_TYPE_NONE = 0, + MMU_TYPE_32BIT, + MMU_TYPE_36BIT, + MMU_TYPE_40BIT, + MMU_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/** + * enum mmu_eheap_id - This type defines the MMU heaps. + * @MMU_HEAP_IMAGE_BUFFERS_UNTILED: Heap for untiled video buffers + * @MMU_HEAP_BITSTREAM_BUFFERS : Heap for bitstream buffers + * @MMU_HEAP_STREAM_BUFFERS : Heap for Stream buffers + * @MMU_HEAP_MAX : Number of heaps + * @MMU_HEAP_FORCE32BITS: MMU_HEAP_FORCE32BITS + */ +enum mmu_eheap_id { + MMU_HEAP_IMAGE_BUFFERS_UNTILED = 0x00, + MMU_HEAP_BITSTREAM_BUFFERS, + MMU_HEAP_STREAM_BUFFERS, + MMU_HEAP_MAX, + MMU_HEAP_FORCE32BITS = 0x7FFFFFFFU +}; + +#endif /* _VXD_MMU_DEFS_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/pixel_api.c b/drivers/media/platform/vxe-vxd/decoder/pixel_api.c --- a/drivers/media/platform/vxe-vxd/decoder/pixel_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/pixel_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,895 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pixel processing function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "img_errors.h" +#include "img_pixfmts.h" +#include "pixel_api.h" +#include "vdec_defs.h" + +#define NUM_OF_FORMATS 17 +#define PIXNAME(x) /* Pixel name support not enabled */ +#define FACT_SPEC_FORMAT_NUM_PLANES 4 +#define FACT_SPEC_FORMAT_PLANE_UNUSED 0xf +#define FACT_SPEC_FORMAT_PLANE_CODE_BITS 4 +#define FACT_SPEC_FORMAT_PLANE_CODE_MASK 3 +#define FACT_SPEC_FORMAT_MIN_FACT_VAL 1 + +/* + * @brief Pointer to the default format in the asPixelFormats array + * default format is an invalid format + * @note pointer set by initSearch() + * This pointer is also used to know if the arrays were sorted + */ +static struct pixel_pixinfo *def_fmt; + +/* + * @brief Actual array storing the pixel formats information. + */ +static struct pixel_pixinfo pix_fmts[NUM_OF_FORMATS] = { + { + IMG_PIXFMT_420PL12YUV8, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT8_MP, + PIXEL_FORMAT_420, + 8, + 8, + 2 + }, + + { + IMG_PIXFMT_420PL12YVU8, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT8_MP, + PIXEL_FORMAT_420, + 8, + 8, + 2 + }, + + { + IMG_PIXFMT_420PL12YUV10, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MP, + PIXEL_FORMAT_420, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_420PL12YVU10, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MP, + PIXEL_FORMAT_420, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_420PL12YUV10_MSB, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MSB_MP, + PIXEL_FORMAT_420, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_420PL12YVU10_MSB, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MSB_MP, + PIXEL_FORMAT_420, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_420PL12YUV10_LSB, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_LSB_MP, + PIXEL_FORMAT_420, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_420PL12YVU10_LSB, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_LSB_MP, + PIXEL_FORMAT_420, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_422PL12YUV8, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT8_MP, + PIXEL_FORMAT_422, + 8, + 8, + 2 + }, + + { + IMG_PIXFMT_422PL12YVU8, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT8_MP, + PIXEL_FORMAT_422, + 8, + 8, + 2 + }, + + { + IMG_PIXFMT_422PL12YUV10, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MP, + PIXEL_FORMAT_422, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_422PL12YVU10, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MP, + PIXEL_FORMAT_422, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_422PL12YUV10_MSB, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MSB_MP, + PIXEL_FORMAT_422, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_422PL12YVU10_MSB, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_MSB_MP, + PIXEL_FORMAT_422, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_422PL12YUV10_LSB, + PIXEL_UV_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_LSB_MP, + PIXEL_FORMAT_422, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_422PL12YVU10_LSB, + PIXEL_VU_ORDER, + PIXEL_MULTICHROME, + PIXEL_BIT10_LSB_MP, + PIXEL_FORMAT_422, + 10, + 10, + 2 + }, + + { + IMG_PIXFMT_UNDEFINED, + PIXEL_INVALID_CI, + 0, + (enum pixel_mem_packing)0, + PIXEL_FORMAT_INVALID, + 0, + 0, + 0 + } +}; + +static struct pixel_pixinfo_table pixinfo_table[] = { + { + IMG_PIXFMT_420PL12YUV8_A8, + { + PIXNAME(IMG_PIXFMT_420PL12YUV8_A8) + 16, + 16, + 16, + 0, + 16, + TRUE, + TRUE, + 4, + TRUE + } + }, + + { + IMG_PIXFMT_422PL12YUV8_A8, + { + PIXNAME(IMG_PIXFMT_422PL12YUV8_A8) + 16, + 16, + 16, + 0, + 16, + TRUE, + FALSE, + 4, + TRUE + } + }, + + { + IMG_PIXFMT_420PL12YUV8, + { + PIXNAME(IMG_PIXFMT_420PL12YUV8) + 16, + 16, + 16, + 0, + 0, + TRUE, + TRUE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_420PL12YVU8, + { + PIXNAME(IMG_PIXFMT_420PL12YVU8) + 16, + 16, + 16, + 0, + 0, + TRUE, + TRUE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_420PL12YUV10, + { + PIXNAME(IMG_PIXFMT_420PL12YUV10) + 12, + 16, + 16, + 0, + 0, + TRUE, + TRUE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_420PL12YVU10, + { + PIXNAME(IMG_PIXFMT_420PL12YVU10) + 12, + 16, + 16, + 0, + 0, + TRUE, + TRUE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_420PL12YUV10_MSB, + { + PIXNAME(IMG_PIXFMT_420PL12YUV10_MSB) + 8, + 16, + 16, + 0, + 0, + TRUE, + TRUE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_420PL12YVU10_MSB, + { + PIXNAME(IMG_PIXFMT_420PL12YVU10_MSB) + 8, + 16, + 16, + 0, + 0, + TRUE, + TRUE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_422PL12YUV8, + { + PIXNAME(IMG_PIXFMT_422PL12YUV8) + 16, + 16, + 16, + 0, + 0, + TRUE, + FALSE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_422PL12YVU8, + { + PIXNAME(IMG_PIXFMT_422PL12YVU8) + 16, + 16, + 16, + 0, + 0, + TRUE, + FALSE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_422PL12YUV10, + { + PIXNAME(IMG_PIXFMT_422PL12YUV10) + 12, + 16, + 16, + 0, + 0, + TRUE, + FALSE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_422PL12YVU10, + { + PIXNAME(IMG_PIXFMT_422PL12YVU10) + 12, + 16, + 16, + 0, + 0, + TRUE, + FALSE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_422PL12YUV10_MSB, + { + PIXNAME(IMG_PIXFMT_422PL12YUV10_MSB) + 8, + 16, + 16, + 0, + 0, + TRUE, + FALSE, + 4, + FALSE + } + }, + + { + IMG_PIXFMT_422PL12YVU10_MSB, + { + PIXNAME(IMG_PIXFMT_422PL12YVU10_MSB) + 8, + 16, + 16, + 0, + 0, + TRUE, + FALSE, + 4, + FALSE + } + }, +}; + +static struct pixel_pixinfo_table* +pixel_get_pixelinfo_from_pixfmt(enum img_pixfmt pix_fmt) +{ + unsigned int i; + unsigned char found = FALSE; + struct pixel_pixinfo_table *this_pixinfo_table_entry = NULL; + + for (i = 0; + i < (sizeof(pixinfo_table) / sizeof(struct pixel_pixinfo_table)); + i++) { + if (pix_fmt == pixinfo_table[i].pix_color_fmt) { + /* + * There must only be one entry per pixel colour format + * in the table + */ + VDEC_ASSERT(!found); + found = TRUE; + this_pixinfo_table_entry = &pixinfo_table[i]; + + /* + * We deliberately do NOT break here - scan rest of + * table to ensure there are not duplicate entries + */ + } + } + return this_pixinfo_table_entry; +} + +/* + * @brief Array containing string lookup of pixel format IDC. + * @warning this must be kept in step with PIXEL_FormatIdc. + */ +unsigned char pix_fmt_idc_names[6][16] = { + "Monochrome", + "4:1:1", + "4:2:0", + "4:2:2", + "4:4:4", + "Invalid", +}; + +static int pixel_compare_pixfmts(const void *a, const void *b) +{ + return ((struct pixel_pixinfo *)a)->pixfmt - + ((struct pixel_pixinfo *)b)->pixfmt; +} + +static struct pixel_info* +pixel_get_bufinfo_from_pixfmt(enum img_pixfmt pix_fmt) +{ + struct pixel_pixinfo_table *pixinfo_table_entry = NULL; + struct pixel_info *pix_info = NULL; + + pixinfo_table_entry = pixel_get_pixelinfo_from_pixfmt(pix_fmt); + VDEC_ASSERT(pixinfo_table_entry); + if (pixinfo_table_entry) + pix_info = &pixinfo_table_entry->info; + + return pix_info; +} + +/* + * @brief Search a pixel format based on its attributes rather than its format + * enum. + * @warning use PIXEL_Comparpix_fmts to search by enum + */ +static int pixel_compare_pixinfo(const void *a, const void *b) +{ + int result = 0; + const struct pixel_pixinfo *fmt_a = (struct pixel_pixinfo *)a; + const struct pixel_pixinfo *fmt_b = (struct pixel_pixinfo *)b; + + result = fmt_a->chroma_fmt_idc - fmt_b->chroma_fmt_idc; + if (result != 0) + return result; + + result = fmt_a->mem_pkg - fmt_b->mem_pkg; + if (result != 0) + return result; + + result = fmt_a->chroma_interleave - fmt_b->chroma_interleave; + if (result != 0) + return result; + + result = fmt_a->bitdepth_y - fmt_b->bitdepth_y; + if (result != 0) + return result; + + result = fmt_a->bitdepth_c - fmt_b->bitdepth_c; + if (result != 0) + return result; + + result = fmt_a->num_planes - fmt_b->num_planes; + if (result != 0) + return result; + + return result; +} + +static void pixel_init_search(void) +{ + static unsigned int search_inited; + + search_inited++; + if (search_inited == 1) { + if (!def_fmt) { + int i = 0; + + i = NUM_OF_FORMATS - 1; + while (i >= 0) { + if (IMG_PIXFMT_UNDEFINED == + pix_fmts[i].pixfmt) { + def_fmt = &pix_fmts[i]; + break; + } + } + VDEC_ASSERT(def_fmt); + } + } else { + search_inited--; + } +} + +static struct pixel_pixinfo *pixel_search_fmt(const struct pixel_pixinfo *key, + unsigned char enum_only) +{ + struct pixel_pixinfo *fmt_found = NULL; + int (*compar)(const void *pixfmt1, const void *pixfmt2); + + if (enum_only) + compar = &pixel_compare_pixfmts; + else + compar = &pixel_compare_pixinfo; + + { + unsigned int i; + + for (i = 0; i < NUM_OF_FORMATS; i++) { + if (compar(key, &pix_fmts[i]) == 0) { + fmt_found = &pix_fmts[i]; + break; + } + } + } + return fmt_found; +} + +/* + * @brief Set a pixel format info structure to the default. + * @warning This MODIDIFES the pointer therefore you shouldn't + * call it on pointer you got from the library! + */ +static void pixel_pixinfo_defaults(struct pixel_pixinfo *to_def) +{ + if (!def_fmt) + pixel_init_search(); + + memcpy(to_def, def_fmt, sizeof(struct pixel_pixinfo)); +} + +enum img_pixfmt pixel_get_pixfmt(enum pixel_fmt_idc chroma_fmt_idc, + enum pixel_chroma_interleaved + chroma_interleaved, + enum pixel_mem_packing mem_pkg, + unsigned int bitdepth_y, unsigned int bitdepth_c, + unsigned int num_planes) +{ + unsigned int internal_num_planes = (num_planes == 0 || num_planes > 4) ? 2 : + num_planes; + struct pixel_pixinfo key; + struct pixel_pixinfo *fmt_found = NULL; + + if (chroma_fmt_idc != PIXEL_FORMAT_MONO && + chroma_fmt_idc != PIXEL_FORMAT_411 && + chroma_fmt_idc != PIXEL_FORMAT_420 && + chroma_fmt_idc != PIXEL_FORMAT_422 && + chroma_fmt_idc != PIXEL_FORMAT_444) + return IMG_PIXFMT_UNDEFINED; + + /* valid bit depth 8, 9, 10, or 16/0 for 422 */ + if (bitdepth_y < 8 || bitdepth_y > 10) + return IMG_PIXFMT_UNDEFINED; + + /* valid bit depth 8, 9, 10, or 16/0 for 422 */ + if (bitdepth_c < 8 || bitdepth_c > 10) + return IMG_PIXFMT_UNDEFINED; + + key.pixfmt = IMG_PIXFMT_UNDEFINED; + key.chroma_fmt_idc = chroma_fmt_idc; + key.chroma_interleave = chroma_interleaved; + key.mem_pkg = mem_pkg; + key.bitdepth_y = bitdepth_y; + key.bitdepth_c = bitdepth_c; + key.num_planes = internal_num_planes; + + /* + * 9 and 10 bits formats are handled in the same way, and there is only + * one entry in the PixelFormat table + */ + if (key.bitdepth_y == 9) + key.bitdepth_y = 10; + + /* + * 9 and 10 bits formats are handled in the same way, and there is only + * one entry in the PixelFormat table + */ + if (key.bitdepth_c == 9) + key.bitdepth_c = 10; + + pixel_init_search(); + + /* do not search by format */ + fmt_found = pixel_search_fmt(&key, FALSE); + if (!fmt_found) + return IMG_PIXFMT_UNDEFINED; + + return fmt_found->pixfmt; +} + +static void pixel_get_internal_pixelinfo(struct pixel_pixinfo *pixinfo, + struct pixel_info *pix_bufinfo) +{ + if (pixinfo->bitdepth_y == 8 && pixinfo->bitdepth_c == 8) + pix_bufinfo->pixels_in_bop = 16; + else if (pixinfo->mem_pkg == PIXEL_BIT10_MP) + pix_bufinfo->pixels_in_bop = 12; + else + pix_bufinfo->pixels_in_bop = 8; + + if (pixinfo->bitdepth_y == 8) + pix_bufinfo->ybytes_in_bop = pix_bufinfo->pixels_in_bop; + else + pix_bufinfo->ybytes_in_bop = 16; + + if (pixinfo->chroma_fmt_idc == PIXEL_FORMAT_MONO) { + pix_bufinfo->uvbytes_in_bop = 0; + } else if (pixinfo->bitdepth_c == 8) { + pix_bufinfo->uvbytes_in_bop = pix_bufinfo->pixels_in_bop; + if (pixinfo->chroma_fmt_idc == PIXEL_FORMAT_422 && pixinfo->num_planes == 1) { + pix_bufinfo->uvbytes_in_bop = 0; + pix_bufinfo->pixels_in_bop = 8; + } + } else { + pix_bufinfo->uvbytes_in_bop = 16; + } + + if (pixinfo->chroma_fmt_idc == PIXEL_FORMAT_444) + pix_bufinfo->uvbytes_in_bop *= 2; + + if (pixinfo->chroma_interleave == PIXEL_INVALID_CI) { + pix_bufinfo->uvbytes_in_bop /= 2; + pix_bufinfo->vbytes_in_bop = pix_bufinfo->uvbytes_in_bop; + } else { + pix_bufinfo->vbytes_in_bop = 0; + } + + pix_bufinfo->alphabytes_in_bop = 0; + + if (pixinfo->num_planes == 1) + pix_bufinfo->is_planar = FALSE; + else + pix_bufinfo->is_planar = TRUE; + + if (pixinfo->chroma_fmt_idc == PIXEL_FORMAT_420) + pix_bufinfo->uv_height_halved = TRUE; + else + pix_bufinfo->uv_height_halved = FALSE; + + if (pixinfo->chroma_fmt_idc == PIXEL_FORMAT_444) + pix_bufinfo->uv_stride_ratio_times4 = 8; + else + pix_bufinfo->uv_stride_ratio_times4 = 4; + + if (pixinfo->chroma_interleave == PIXEL_INVALID_CI) + pix_bufinfo->uv_stride_ratio_times4 /= 2; + + pix_bufinfo->has_alpha = FALSE; +} + +static void pixel_yuv_get_descriptor_int(struct pixel_info *pixinfo, + struct img_pixfmt_desc *pix_desc) +{ + pix_desc->bop_denom = pixinfo->pixels_in_bop; + pix_desc->h_denom = (pixinfo->uv_stride_ratio_times4 == 2 || + !pixinfo->is_planar) ? 2 : 1; + pix_desc->v_denom = (pixinfo->uv_height_halved || !pixinfo->is_planar) + ? 2 : 1; + + pix_desc->planes[0] = TRUE; + pix_desc->bop_numer[0] = pixinfo->ybytes_in_bop; + pix_desc->h_numer[0] = pix_desc->h_denom; + pix_desc->v_numer[0] = pix_desc->v_denom; + + pix_desc->planes[1] = pixinfo->is_planar; + pix_desc->bop_numer[1] = pixinfo->uvbytes_in_bop; + pix_desc->h_numer[1] = (pix_desc->h_denom * pixinfo->uv_stride_ratio_times4) / 4; + pix_desc->v_numer[1] = 1; + + pix_desc->planes[2] = (pixinfo->vbytes_in_bop > 0) ? TRUE : FALSE; + pix_desc->bop_numer[2] = pixinfo->vbytes_in_bop; + pix_desc->h_numer[2] = (pixinfo->vbytes_in_bop > 0) ? 1 : 0; + pix_desc->v_numer[2] = (pixinfo->vbytes_in_bop > 0) ? 1 : 0; + + pix_desc->planes[3] = pixinfo->has_alpha; + pix_desc->bop_numer[3] = pixinfo->alphabytes_in_bop; + pix_desc->h_numer[3] = pix_desc->h_denom; + pix_desc->v_numer[3] = pix_desc->v_denom; +} + +int pixel_yuv_get_desc(struct pixel_pixinfo *pix_info, struct img_pixfmt_desc *pix_desc) +{ + struct pixel_info int_pix_info; + + struct pixel_info *int_pix_info_old = NULL; + enum img_pixfmt pix_fmt = pixel_get_pixfmt(pix_info->chroma_fmt_idc, + pix_info->chroma_interleave, + pix_info->mem_pkg, + pix_info->bitdepth_y, + pix_info->bitdepth_c, + pix_info->num_planes); + + /* Validate the output from new function. */ + if (pix_fmt != IMG_PIXFMT_UNDEFINED) + int_pix_info_old = pixel_get_bufinfo_from_pixfmt(pix_fmt); + + pixel_get_internal_pixelinfo(pix_info, &int_pix_info); + + if (int_pix_info_old) { + VDEC_ASSERT(int_pix_info_old->has_alpha == + int_pix_info.has_alpha); + VDEC_ASSERT(int_pix_info_old->is_planar == + int_pix_info.is_planar); + VDEC_ASSERT(int_pix_info_old->uv_height_halved == + int_pix_info.uv_height_halved); + VDEC_ASSERT(int_pix_info_old->alphabytes_in_bop == + int_pix_info.alphabytes_in_bop); + VDEC_ASSERT(int_pix_info_old->pixels_in_bop == + int_pix_info.pixels_in_bop); + VDEC_ASSERT(int_pix_info_old->uvbytes_in_bop == + int_pix_info.uvbytes_in_bop); + VDEC_ASSERT(int_pix_info_old->uv_stride_ratio_times4 == + int_pix_info.uv_stride_ratio_times4); + VDEC_ASSERT(int_pix_info_old->vbytes_in_bop == + int_pix_info.vbytes_in_bop); + VDEC_ASSERT(int_pix_info_old->ybytes_in_bop == + int_pix_info.ybytes_in_bop); + } + + pixel_yuv_get_descriptor_int(&int_pix_info, pix_desc); + + return IMG_SUCCESS; +} + +struct pixel_pixinfo *pixel_get_pixinfo(const enum img_pixfmt pix_fmt) +{ + struct pixel_pixinfo key; + struct pixel_pixinfo *fmt_found = NULL; + + pixel_init_search(); + pixel_pixinfo_defaults(&key); + key.pixfmt = pix_fmt; + + fmt_found = pixel_search_fmt(&key, TRUE); + if (!fmt_found) + return def_fmt; + return fmt_found; +} + +int pixel_get_fmt_desc(enum img_pixfmt pix_fmt, struct img_pixfmt_desc *pix_desc) +{ + if (pix_fmt >= IMG_PIXFMT_ARBPLANAR8 && pix_fmt <= IMG_PIXFMT_ARBPLANAR8_LAST) { + unsigned int i; + unsigned short spec; + + pix_desc->bop_denom = 1; + pix_desc->h_denom = 1; + pix_desc->v_denom = 1; + + spec = (pix_fmt - IMG_PIXFMT_ARBPLANAR8) & 0xffff; + for (i = 0; i < FACT_SPEC_FORMAT_NUM_PLANES; i++) { + unsigned char code = (spec >> FACT_SPEC_FORMAT_PLANE_CODE_BITS * + (FACT_SPEC_FORMAT_NUM_PLANES - 1 - i)) & 0xf; + pix_desc->bop_numer[i] = 1; + pix_desc->h_numer[i] = ((code >> 2) & FACT_SPEC_FORMAT_PLANE_CODE_MASK) + + FACT_SPEC_FORMAT_MIN_FACT_VAL; + pix_desc->v_numer[i] = (code & FACT_SPEC_FORMAT_PLANE_CODE_MASK) + + FACT_SPEC_FORMAT_MIN_FACT_VAL; + if (i == 0 || code != FACT_SPEC_FORMAT_PLANE_UNUSED) { + pix_desc->planes[i] = TRUE; + + pix_desc->h_denom = + pix_desc->h_denom > pix_desc->h_numer[i] ? + pix_desc->h_denom : pix_desc->h_numer[i]; + + pix_desc->v_denom = + pix_desc->v_denom > pix_desc->v_numer[i] ? + pix_desc->v_denom : pix_desc->v_numer[i]; + } else { + pix_desc->planes[i] = FALSE; + } + } + } else { + struct pixel_info *info = + pixel_get_bufinfo_from_pixfmt(pix_fmt); + if (!info) { + VDEC_ASSERT(0); + return -EINVAL; + } + + pixel_yuv_get_descriptor_int(info, pix_desc); + } + + return IMG_SUCCESS; +} + +int pixel_gen_pixfmt(enum img_pixfmt *pix_fmt, struct img_pixfmt_desc *pix_desc) +{ + unsigned short spec = 0, i; + unsigned char code; + + for (i = 0; i < FACT_SPEC_FORMAT_NUM_PLANES; i++) { + if (pix_desc->planes[i] != 1) { + code = FACT_SPEC_FORMAT_PLANE_UNUSED; + } else { + code = (((pix_desc->h_numer[i] - FACT_SPEC_FORMAT_MIN_FACT_VAL) & + FACT_SPEC_FORMAT_PLANE_CODE_MASK) << 2) | + ((pix_desc->v_numer[i] - FACT_SPEC_FORMAT_MIN_FACT_VAL) & + FACT_SPEC_FORMAT_PLANE_CODE_MASK); + } + spec |= (code << FACT_SPEC_FORMAT_PLANE_CODE_BITS * + (FACT_SPEC_FORMAT_NUM_PLANES - 1 - i)); + } + + *pix_fmt = (enum img_pixfmt)(IMG_PIXFMT_ARBPLANAR8 | spec); + + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/pixel_api.h b/drivers/media/platform/vxe-vxd/decoder/pixel_api.h --- a/drivers/media/platform/vxe-vxd/decoder/pixel_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/pixel_api.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Pixel processing functions header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#ifndef __PIXEL_API_H__ +#define __PIXEL_API_H__ + +#include + +#include "img_errors.h" +#include "img_pixfmts.h" + +#define PIXEL_MULTICHROME TRUE +#define PIXEL_MONOCHROME FALSE +#define IMG_MAX_NUM_PLANES 4 +#define PIXEL_INVALID_BDC 8 + +extern unsigned char pix_fmt_idc_names[6][16]; + +struct img_pixfmt_desc { + unsigned char planes[IMG_MAX_NUM_PLANES]; + unsigned int bop_denom; + unsigned int bop_numer[IMG_MAX_NUM_PLANES]; + unsigned int h_denom; + unsigned int v_denom; + unsigned int h_numer[IMG_MAX_NUM_PLANES]; + unsigned int v_numer[IMG_MAX_NUM_PLANES]; +}; + +/* + * @brief This type defines memory chroma interleaved order + */ +enum pixel_chroma_interleaved { + PIXEL_INVALID_CI = 0, + PIXEL_UV_ORDER = 1, + PIXEL_VU_ORDER = 2, + PIXEL_YAYB_ORDER = 4, + PIXEL_AYBY_ORDER = 8, + PIXEL_ORDER_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * @brief This macro translates enum pixel_chroma_interleaved values into + * value that can be used to write HW registers directly. + */ +#define PIXEL_GET_HW_CHROMA_INTERLEAVED(value) \ + ((value) & PIXEL_VU_ORDER ? TRUE : FALSE) + +/* + * @brief This type defines memory packing types + */ +enum pixel_mem_packing { + PIXEL_BIT8_MP = 0, + PIXEL_BIT10_MSB_MP = 1, + PIXEL_BIT10_LSB_MP = 2, + PIXEL_BIT10_MP = 3, + PIXEL_DEFAULT_MP = 0xff, + PIXEL_DEFAULT_FORCE32BITS = 0x7FFFFFFFU +}; + +static inline unsigned char pixel_get_hw_memory_packing(enum pixel_mem_packing value) +{ + return value == PIXEL_BIT8_MP ? FALSE : + value == PIXEL_BIT10_MSB_MP ? FALSE : + value == PIXEL_BIT10_LSB_MP ? FALSE : + value == PIXEL_BIT10_MP ? TRUE : FALSE; +} + +/* + * @brief This type defines chroma formats + */ +enum pixel_fmt_idc { + PIXEL_FORMAT_MONO = 0, + PIXEL_FORMAT_411 = 1, + PIXEL_FORMAT_420 = 2, + PIXEL_FORMAT_422 = 3, + PIXEL_FORMAT_444 = 4, + PIXEL_FORMAT_INVALID = 0xFF, + PIXEL_FORMAT_FORCE32BITS = 0x7FFFFFFFU +}; + +static inline int pixel_get_hw_chroma_format_idc(enum pixel_fmt_idc value) +{ + return value == PIXEL_FORMAT_MONO ? 0 : + value == PIXEL_FORMAT_420 ? 1 : + value == PIXEL_FORMAT_422 ? 2 : + value == PIXEL_FORMAT_444 ? 3 : + PIXEL_FORMAT_INVALID; +} + +/* + * @brief This structure contains information about the pixel formats + */ +struct pixel_pixinfo { + enum img_pixfmt pixfmt; + enum pixel_chroma_interleaved chroma_interleave; + unsigned char chroma_fmt; + enum pixel_mem_packing mem_pkg; + enum pixel_fmt_idc chroma_fmt_idc; + unsigned int bitdepth_y; + unsigned int bitdepth_c; + unsigned int num_planes; +}; + +/* + * @brief This type defines the image in memory + */ +struct pixel_info { + unsigned int pixels_in_bop; + unsigned int ybytes_in_bop; + unsigned int uvbytes_in_bop; + unsigned int vbytes_in_bop; + unsigned int alphabytes_in_bop; + unsigned char is_planar; + unsigned char uv_height_halved; + unsigned int uv_stride_ratio_times4; + unsigned char has_alpha; +}; + +struct pixel_pixinfo_table { + enum img_pixfmt pix_color_fmt; + struct pixel_info info; +}; + +struct pixel_pixinfo *pixel_get_pixinfo(const enum img_pixfmt pixfmt); + +enum img_pixfmt pixel_get_pixfmt(enum pixel_fmt_idc chroma_fmt_idc, + enum pixel_chroma_interleaved + chroma_interleaved, + enum pixel_mem_packing mem_packing, + unsigned int bitdepth_y, unsigned int bitdepth_c, + unsigned int num_planes); + +int pixel_yuv_get_desc(struct pixel_pixinfo *pix_info, + struct img_pixfmt_desc *desc); + +int pixel_get_fmt_desc(enum img_pixfmt pixfmt, + struct img_pixfmt_desc *fmt_desc); + +int pixel_gen_pixfmt(enum img_pixfmt *pix_fmt, struct img_pixfmt_desc *pix_desc); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/pvdec_entropy_regs.h b/drivers/media/platform/vxe-vxd/decoder/pvdec_entropy_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/pvdec_entropy_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/pvdec_entropy_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC Common low level core interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef __PVDEC_ENTROPY_REGS_H__ +#define __PVDEC_ENTROPY_REGS_H__ + +/* + * PVDEC_ENTROPY, CR_ENTROPY_SHIFTREG_CONTROL, SR_SW_RESET + */ +#define PVDEC_ENTROPY_CR_GENC_BUFFER_SIZE_OFFSET (0x0100) + +/* + * PVDEC_ENTROPY, CR_GENC_BUFFER_SIZE, GENC_BUFFER_SIZE + */ +#define PVDEC_ENTROPY_CR_GENC_BUFFER_BASE_ADDRESS_OFFSET (0x0110) + +/* + * PVDEC_ENTROPY, CR_ENTROPY_SLICE_PARAMETER_SIZE, SLICE_PARAMETER_SIZE + */ +#define PVDEC_ENTROPY_CR_GENC_FRAGMENT_BASE_ADDRESS_OFFSET (0x0098) + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/pvdec_int.h b/drivers/media/platform/vxe-vxd/decoder/pvdec_int.h --- a/drivers/media/platform/vxe-vxd/decoder/pvdec_int.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/pvdec_int.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low-level PVDEC interface component. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#ifndef __PVDEC_INT_H__ +#define __PVDEC_INT_H__ + +#include "hw_control.h" +#include "vxd_ext.h" +#include "vxd_props.h" + +/* How many VLC IDX addresses fits in single address register */ +#define PVDECIO_VLC_IDX_ADDR_PARTS 2 + +/* How many VLC IDX initial fits in single width register */ +#define PVDECIO_VLC_IDX_WIDTH_PARTS 10 + +/* How many VLC IDX initial opcodes fits in single opcode register */ +#define PVDECIO_VLC_IDX_OPCODE_PARTS 16 + +/* + * Length (shift) of VLC IDX opcode field. We're taking [0][1] here, as it + * corresponds to shift of one element + */ +#define PVDECIO_VLC_IDX_ADDR_ID 2 + +/* + * Mask for VLC IDX address field. We're taking [0][0] here, as it corresponds + * to unshifted mask + */ +#define PVDECIO_VLC_IDX_ADDR_MASK MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR0_VLC_TABLE_ADDR0_MASK + +/* + * Length (shift) of VLC IDX address field. We're taking [0][1] here, as it + * corresponds to shift of one element + */ +#define PVDECIO_VLC_IDX_ADDR_SHIFT MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR0_VLC_TABLE_ADDR1_SHIFT +#define PVDECIO_VLC_IDX_WIDTH_ID 1 + +/* + * Mask for VLC IDX width field. We're taking [0][0] here, as it corresponds + * to unshifted mask + */ +#define PVDECIO_VLC_IDX_WIDTH_MASK \ + MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_WIDTH0_VLC_TABLE_INITIAL_WIDTH0_MASK + +/* + * Length (shift) of VLC IDX width field. We're taking [0][1] here, as it + * corresponds to shift of one element + */ +#define PVDECIO_VLC_IDX_WIDTH_SHIFT \ + MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_WIDTH0_VLC_TABLE_INITIAL_WIDTH1_SHIFT + +#define PVDECIO_VLC_IDX_OPCODE_ID 0 + +/* + * Length (shift) of VLC IDX opcode field. We're taking [0][1] here, as it + * corresponds to shift of one element + */ +#define PVDECIO_VLC_IDX_OPCODE_SHIFT \ + MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_OPCODE0_VLC_TABLE_INITIAL_OPCODE1_SHIFT + +/* This comes from DEVA PVDEC FW */ +#define CTRL_ALLOC_MAX_SEGMENT_SIZE 1024 + +/* + * Mask for VLC IDX opcode field. We're taking [0][0] here, as it corresponds + * to unshifted mask + */ +#define PVDECIO_VLC_IDX_OPCODE_MASK \ + MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_OPCODE0_VLC_TABLE_INITIAL_OPCODE0_MASK + +#endif /* __PVDEC_INT_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/pvdec_vec_be_regs.h b/drivers/media/platform/vxe-vxd/decoder/pvdec_vec_be_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/pvdec_vec_be_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/pvdec_vec_be_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC Common low level core interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef __PVDEC_VEC_BE_REGS_H__ +#define __PVDEC_VEC_BE_REGS_H__ + +#define PVDEC_VEC_BE_CR_GENC_BUFFER_SIZE_OFFSET (0x0040) + +/* + * PVDEC_VEC_BE, CR_GENC_BUFFER_SIZE, GENC_BUFFER_SIZE + */ +#define PVDEC_VEC_BE_CR_GENC_BUFFER_BASE_ADDRESS_OFFSET (0x0050) + +/* + * PVDEC_VEC_BE, CR_MEM_TO_REG_CONTROL, MEM_TO_REG_NUM_PAIRS + */ +#define PVDEC_VEC_BE_CR_GENC_FRAGMENT_BASE_ADDRESS_OFFSET (0x0030) + +/* + * PVDEC_VEC_BE, CR_GENC_CONTEXT1, GENC_CONTEXT1_1 + */ +#define PVDEC_VEC_BE_CR_ABOVE_PARAM_BASE_ADDRESS_OFFSET (0x00C0) + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/reg_io2.h b/drivers/media/platform/vxe-vxd/decoder/reg_io2.h --- a/drivers/media/platform/vxe-vxd/decoder/reg_io2.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/reg_io2.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG MSVDX core Registers + * This file contains the MSVDX_CORE_REGS_H Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef REG_IO2_H_ +#define REG_IO2_H_ + +#define IMG_ASSERT(expected) \ + ((void)((expected) || \ + (pr_err("Assertion failed: %s, file %s, line %d\n", \ + #expected, __FILE__, __LINE__), dump_stack(), 0))) + +/* This macro is used to extract a field from a register. */ +#define REGIO_READ_FIELD(regval, group, reg, field) \ + (((regval) & group ## _ ## reg ## _ ## field ## _MASK) >> \ + group ## _ ## reg ## _ ## field ## _SHIFT) + +#if (defined WIN32 || defined __linux__) && !defined NO_REGIO_CHECK_FIELD_VALUE +/* + * Only provide register field range checking for Windows and + * Linux builds + * Simple range check that ensures that if bits outside the valid field + * range are set, that the provided value is at least consistent with a + * negative value (i.e.: all top bits are set to 1). + * Cannot perform more comprehensive testing without knowing + * whether field + * should be interpreted as signed or unsigned. + */ +#define REGIO_CHECK_VALUE_FITS_WITHIN_FIELD(group, reg, field, value, type) \ + { \ + type __value = value; \ + unsigned int temp = (unsigned int)(__value); \ + if (temp > group ## _ ## reg ## _ ## field ## _LSBMASK) { \ + IMG_ASSERT((((unsigned int)__value) & \ + (unsigned int)~(group ## _ ## reg ## _ ## field ## _LSBMASK)) == \ + (unsigned int)~(group ## _ ## reg ## _ ## field ## _LSBMASK)); \ + } \ + } +#else +#define REGIO_CHECK_VALUE_FITS_WITHIN_FIELD(group, reg, field, value, type) +#endif + +/* This macro is used to update the value of a field in a register. */ +#define REGIO_WRITE_FIELD(regval, group, reg, field, value, reg_type, val_type) \ + { \ + reg_type __regval = regval; \ + val_type __value = value; \ + REGIO_CHECK_VALUE_FITS_WITHIN_FIELD(group, reg, field, __value, val_type); \ + (regval) = \ + ((__regval) & ~(group ## _ ## reg ## _ ## field ## _MASK)) | \ + (((unsigned int)(__value) << (group ## _ ## reg ## _ ## field ## _SHIFT)) & \ + (group ## _ ## reg ## _ ## field ## _MASK)); \ + } + +/* This macro is used to update the value of a field in a register. */ +#define REGIO_WRITE_FIELD_LITE(regval, group, reg, field, value, type) \ +{ \ + type __value = value; \ + REGIO_CHECK_VALUE_FITS_WITHIN_FIELD(group, reg, field, __value, type); \ + (regval) |= ((unsigned int)(__value) << (group ## _ ## reg ## _ ## field ## _SHIFT)); \ +} + +#endif /* REG_IO2_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/scaler_setup.h b/drivers/media/platform/vxe-vxd/decoder/scaler_setup.h --- a/drivers/media/platform/vxe-vxd/decoder/scaler_setup.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/scaler_setup.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC constants calculation and scalling coefficients + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + */ + +#ifndef _SCALER_SETUP_H +#define _SCALER_SETUP_H + +#define LOWP 11 +#define HIGHP 14 + +#define FIXED(a, digits) ((int)((a) * (1 << (digits)))) + +struct scaler_params { + unsigned int vert_pitch; + unsigned int vert_startpos; + unsigned int vert_pitch_chroma; + unsigned int vert_startpos_chroma; + unsigned int horz_pitch; + unsigned int horz_startpos; + unsigned int horz_pitch_chroma; + unsigned int horz_startpos_chroma; + unsigned char fixed_point_shift; +}; + +struct scaler_filter { + unsigned char bhoriz_bilinear; + unsigned char bvert_bilinear; +}; + +struct scaler_pitch { + int horiz_luma; + int vert_luma; + int horiz_chroma; + int vert_chroma; +}; + +struct scaler_config { + enum vdec_vid_std vidstd; + const struct vxd_coreprops *coreprops; + struct pixel_pixinfo *in_pixel_info; + const struct pixel_pixinfo *out_pixel_info; + unsigned char bfield_coded; + unsigned char bseparate_chroma_planes; + unsigned int recon_width; + unsigned int recon_height; + unsigned int mb_width; + unsigned int mb_height; + unsigned int scale_width; + unsigned int scale_height; +}; + +#endif /* _SCALER_SETUP_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/swsr.c b/drivers/media/platform/vxe-vxd/decoder/swsr.c --- a/drivers/media/platform/vxe-vxd/decoder/swsr.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/swsr.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1657 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Software Shift Register Access fucntions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "swsr.h" +#include "vdec_defs.h" + +#define NBIT_8BYTE_MASK(n) ((1ULL << (n)) - 1) + +/* Input FIFO length (in bytes). */ +#define SWSR_INPUT_FIFO_LENGTH 8 + +/* Output FIFO length (in bits). */ +#define SWSR_OUTPUT_FIFO_LENGTH 64 + +#define SWSR_NALTYPE_LENGTH 8 + +#define SWSR_MAX_SYNTAX_LENGTH 32 + +#define SWSR_ASSERT(expected) ({WARN_ON(!(expected)); 0; }) + +struct swsr_buffer { + void **lst_link; + /* Pointer to bitstream data. */ + unsigned char *data; + /* Number of bytes of bitstream */ + unsigned long long num_bytes; + /* Index (in bytes) to next data within the buffer */ + unsigned long long byte_offset; + /* Number of bytes read from input FIFO */ + unsigned long long num_bytes_read; +}; + +struct swsr_input { + /* Bitstream data (byte-based and pre emu prev) - left aligned. */ + unsigned long long fifo; + /* Number of *bytes* in Input FIFO */ + unsigned int num_bytes; + struct swsr_config config; + /* Emulation prevention mode used to process data in Input FIFO */ + enum swsr_emprevent emprevent; + /* Number of bytes in emulation prevention sequence */ + unsigned int emprev_seq_len; + /* Size of bitstream declared at initialisation */ + unsigned long long bitstream_size; + /* + * Number of bytes required from input buffer before checking + * next emulation prevention sequence. + */ + unsigned int bytes_for_next_sequ; + /* Byte count read from size delimiter */ + unsigned long long byte_count; + unsigned long long bytes_read_since_delim; + /* Cumulative offset (in bytes) into input buffer data */ + unsigned long long bitstream_offset; + /* Bitstream delimiter found (see #SWSR_delim_type) */ + unsigned char delim_found; + /* + * No More Valid Data before next delimiter. + * Set only for SWSR_EMPREVENT_00000300. + */ + unsigned char no_moredata; + /* Pointer to current input buffer in the context of Input FIFO */ + struct swsr_buffer *buf; + /* Start offset within buffer of current delimited unit */ + long delimited_unit_start_offset; + /* Size of current delimited unit (if already calculated) */ + unsigned int delimited_unit_size; + /* Current bit offset within the current delimited unit */ + unsigned int delimunit_bitofst; +}; + +struct swsr_output { + /* + * Bitstream data (post emulation prevention removal + * delimiter checking) - left aligned. + */ + unsigned long long fifo; + /* Number of *bits* in Output FIFO */ + unsigned int num_bits; + unsigned long long totalbits_consumed; +}; + +struct swsr_buffer_ctx { + /* + * Callback function to notify event and provide/request data. + * See #SWSR_eCbEvent for event types and description + * of CB argument usage. + */ + swsr_callback_fxn cb_fxn; + /* Caller supplied pointer for callback */ + void *cb_param; + /* List of buffers */ + struct lst_t free_buffer_list; + /* + * List of buffers (#SWSR_sBufferCtx) whose data reside + * in the Input/Output FIFOs. + */ + struct lst_t used_buffer_list; +}; + +struct swsr_context { + /* IMG_TRUE if the context is initialised */ + unsigned char initialised; + /* A pointer to an exception handler */ + swsr_except_handler_fxn exception_handler_fxn; + /* Caller supplied pointer */ + void *pexception_param; + /* Last recorded exception */ + enum swsr_exception exception; + /* Buffer context data */ + struct swsr_buffer_ctx buffer_ctx; + /* Context of shift register input. */ + struct swsr_input input; + /* Context of shift register output */ + struct swsr_output output; +}; + +static unsigned long long left_aligned_nbit_8byte_mask(unsigned int mask, unsigned int nbits) +{ + return (((unsigned long long)mask << (64 - nbits)) | + (unsigned long long)NBIT_8BYTE_MASK(64 - nbits)); +} + +/* + * buffer has been exhausted and there is still more bytes declared in bitstream + */ +static int swsr_extractbyte(struct swsr_context *ctx, unsigned char *byte_ext) +{ + struct swsr_input *input; + struct swsr_buffer_ctx *buf_ctx; + unsigned char byte = 0; + unsigned long long cur_byte_offset; + unsigned int result = 0; + + if (!ctx || !byte_ext) + return IMG_ERROR_FATAL; + + input = &ctx->input; + buf_ctx = &ctx->buffer_ctx; + + cur_byte_offset = input->bitstream_offset; + + if (input->buf && input->buf->byte_offset < input->buf->num_bytes) { + input->bitstream_offset++; + byte = input->buf->data[input->buf->byte_offset++]; + } else if (input->bitstream_offset < input->bitstream_size) { + struct swsr_buffer *buffer; + + buffer = lst_removehead(&buf_ctx->free_buffer_list); + if (!buffer) + return IMG_ERROR_FATAL; + + buffer->num_bytes_read = 0; + buffer->byte_offset = 0; + + buf_ctx->cb_fxn(SWSR_EVENT_INPUT_BUFFER_START, + buf_ctx->cb_param, 0, + &buffer->data, &buffer->num_bytes); + SWSR_ASSERT(buffer->data && buffer->num_bytes > 0); + + if (buffer->data && buffer->num_bytes > 0) { + input->buf = buffer; + + /* Add input buffer to output buffer list. */ + lst_add(&buf_ctx->used_buffer_list, input->buf); + + input->bitstream_offset++; + byte = input->buf->data[input->buf->byte_offset++]; + } + } + + { + struct swsr_buffer *buffer = input->buf; + + if (!buffer) + buffer = lst_first(&buf_ctx->used_buffer_list); + + if (!buffer || buffer->num_bytes_read > buffer->num_bytes) { + input->delimited_unit_start_offset = -1; + input->delimited_unit_size = 0; + } + } + /* If the bitstream offset hasn't increased we failed to read a byte. */ + if (cur_byte_offset == input->bitstream_offset) { + input->buf = NULL; + result = IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + + *byte_ext = byte; + + return result; +} + +static unsigned char swsr_checkfor_delimiter(struct swsr_context *ctx) +{ + struct swsr_input *input; + unsigned char delim_found = 0; + + input = &ctx->input; + + /* Check for delimiter. */ + if (input->config.delim_type == SWSR_DELIM_SCP) { + unsigned int shift = (SWSR_INPUT_FIFO_LENGTH * 8) + - input->config.delim_length; + unsigned long long sequ = input->fifo >> shift; + + /* + * Check if the SCP value is matched outside of + * emulation prevention data. + */ + if (sequ == input->config.scp_value && input->bytes_for_next_sequ == 0) + delim_found = 1; + + } else if (input->config.delim_type == SWSR_DELIM_SIZE) { + delim_found = (input->bytes_read_since_delim >= input->byte_count) ? 1 : 0; + } + + return delim_found; +} + +static int swsr_increment_cur_bufoffset(struct swsr_context *ctx) +{ + struct swsr_buffer_ctx *buf_ctx; + struct swsr_buffer *cur_buf; + + buf_ctx = &ctx->buffer_ctx; + + /* Update the number of bytes read from input FIFO for current buffer */ + cur_buf = lst_first(&buf_ctx->used_buffer_list); + if (cur_buf->num_bytes_read >= cur_buf->num_bytes) { + /* Mark current bitstream buffer as fully consumed */ + cur_buf->num_bytes_read = cur_buf->num_bytes; + + /* Notify the application that the old buffer is exhausted. */ + buf_ctx->cb_fxn(SWSR_EVENT_OUTPUT_BUFFER_END, + buf_ctx->cb_param, 0, + NULL, NULL); + + /* + * Discard the buffer whose data was at the head of + * the input FIFO. + */ + cur_buf = lst_removehead(&buf_ctx->used_buffer_list); + /* Add the buffer container to free list. */ + lst_add(&buf_ctx->free_buffer_list, cur_buf); + + /* + * Since the byte that we read was actually from the next + * buffer increment it's counter. + */ + cur_buf = lst_first(&buf_ctx->used_buffer_list); + cur_buf->num_bytes_read++; + } else { + cur_buf->num_bytes_read++; + } + + return 0; +} + +static enum swsr_found swsr_readbyte_from_inputfifo(struct swsr_context *ctx, + unsigned char *byte) +{ + struct swsr_input *input; + enum swsr_found found = SWSR_FOUND_NONE; + unsigned int result = 0; + + input = &ctx->input; + + input->delim_found |= swsr_checkfor_delimiter(ctx); + + /* + * Refill the input FIFO before checking for emulation prevention etc. + * The only exception is when there are no more bytes left to extract + * from input buffer. + */ + while (input->num_bytes < SWSR_INPUT_FIFO_LENGTH && result == 0) { + unsigned char byte; + + result = swsr_extractbyte(ctx, &byte); + if (result == 0) { + input->fifo |= ((unsigned long long)byte << + ((SWSR_INPUT_FIFO_LENGTH - 1 - input->num_bytes) * 8)); + input->num_bytes += 1; + } + } + + if (input->num_bytes == 0) { + found = SWSR_FOUND_EOD; + } else if (!input->delim_found) { + /* + * Check for emulation prevention when enabled and enough + * bytes are remaining in input FIFO. + */ + if (input->emprevent != SWSR_EMPREVENT_NONE && + /* + * Ensure you have enough bytes to check for emulation + * prevention. + */ + input->num_bytes >= input->emprev_seq_len && + (input->config.delim_type != SWSR_DELIM_SIZE || + /* + * Ensure that you don't remove emu bytes beyond current + * delimited unit. + */ + ((input->bytes_read_since_delim + input->emprev_seq_len) < + input->byte_count)) && input->bytes_for_next_sequ == 0) { + unsigned char emprev_removed = 0; + unsigned int shift = (SWSR_INPUT_FIFO_LENGTH - input->emprev_seq_len) * 8; + unsigned long long sequ = input->fifo >> shift; + + if (input->emprevent == SWSR_EMPREVENT_00000300) { + if ((sequ & 0xffffff00) == 0x00000300) { + if ((sequ & 0x000000ff) > 0x03) + pr_err("Invalid start code emulation preventionbytes found\n"); + + /* + * Instead of trying to remove the emulation prevention + * byte from the middle of the FIFO simply make it zero + * and drop the next byte from the FIFO which will + * also be zero. + */ + input->fifo &= left_aligned_nbit_8byte_mask + (0xffff00ff, + input->emprev_seq_len * 8); + input->fifo <<= 8; + + emprev_removed = 1; + } else if ((sequ & 0xffffffff) == 0x00000000 || + (sequ & 0xffffffff) == 0x00000001) { + input->no_moredata = 1; + } + } else if (input->emprevent == SWSR_EMPREVENT_ff00) { + if (sequ == 0xff00) { + /* Remove the zero byte. */ + input->fifo <<= 8; + input->fifo |= (0xff00ULL << shift); + emprev_removed = 1; + } + } else if (input->emprevent == SWSR_EMPREVENT_000002) { + /* + * Remove the emulation prevention bytes + * if we find 22 consecutive 0 bits + * (from a byte-aligned position?!) + */ + if (sequ == 0x000002) { + /* + * Appear to "remove" the 0x02 byte by clearing + * it and then dropping the top (zero) byte. + */ + input->fifo &= left_aligned_nbit_8byte_mask + (0xffff00, + input->emprev_seq_len * 8); + input->fifo <<= 8; + emprev_removed = 1; + } + } + + if (emprev_removed) { + input->num_bytes--; + input->bytes_read_since_delim++; + + /* Increment the buffer offset for the + * byte that has been removed. + */ + swsr_increment_cur_bufoffset(ctx); + + /* + * Signal that two more new bytes in the emulation + * prevention sequence are required before another match + * can be made. + */ + input->bytes_for_next_sequ = input->emprev_seq_len - 2; + } + } + + if (input->bytes_for_next_sequ > 0) + input->bytes_for_next_sequ--; + + /* return the first bytes from read data */ + *byte = (unsigned char)(input->fifo >> ((SWSR_INPUT_FIFO_LENGTH - 1) * 8)); + input->fifo <<= 8; + + input->num_bytes--; + input->bytes_read_since_delim++; + + /* Increment the buffer offset for byte that has been read. */ + swsr_increment_cur_bufoffset(ctx); + + found = SWSR_FOUND_DATA; + } else { + found = SWSR_FOUND_DELIM; + } + + return found; +} + +static enum swsr_found swsr_consumebyte_from_inputfifo + (struct swsr_context *ctx, unsigned char *byte) +{ + enum swsr_found found; + + found = swsr_readbyte_from_inputfifo(ctx, byte); + + if (found == SWSR_FOUND_DATA) { + /* Only whole bytes can be read from Input FIFO. */ + ctx->output.totalbits_consumed += 8; + ctx->input.delimunit_bitofst += 8; + } + + return found; +} + +static int swsr_fill_outputfifo(struct swsr_context *ctx) +{ + unsigned char byte; + enum swsr_found found = SWSR_FOUND_DATA; + + /* Fill output FIFO with whole bytes up to (but not over) max length */ + while (ctx->output.num_bits <= (SWSR_OUTPUT_FIFO_LENGTH - 8) && found == SWSR_FOUND_DATA) { + found = swsr_readbyte_from_inputfifo(ctx, &byte); + if (found == SWSR_FOUND_DATA) { + ctx->output.fifo |= ((unsigned long long)byte << + (SWSR_OUTPUT_FIFO_LENGTH - 8 - ctx->output.num_bits)); + ctx->output.num_bits += 8; + } + } + + return 0; +} + +static unsigned int swsr_getbits_from_outputfifo(struct swsr_context *ctx, + unsigned int numbits, + unsigned char bconsume) +{ + unsigned int bitsread; + + /* + * Fetch more bits from the input FIFO if the output FIFO + * doesn't have enough bits to satisfy the request on its own. + */ + if (numbits > ctx->output.num_bits) + swsr_fill_outputfifo(ctx); + + /* Ensure that are now enough bits in the output FIFO. */ + if (numbits > ctx->output.num_bits) { + /* Tried to access into an SCP or other delimiter. */ + if (ctx->input.delim_found) { + ctx->exception = SWSR_EXCEPT_ACCESS_INTO_SCP; + } else { + /* + * Data has been exhausted if after extracting bits + * there are still not enough bits in the internal + * storage to fulfil the number requested. + */ + ctx->exception = SWSR_EXCEPT_ACCESS_BEYOND_EOD; + } + + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + /* Return zero if the bits couldn't be obtained */ + bitsread = 0; + } else { + unsigned int shift; + + /* Extract all the bits from the output FIFO */ + shift = (SWSR_OUTPUT_FIFO_LENGTH - numbits); + bitsread = (unsigned int)(ctx->output.fifo >> shift); + + if (bconsume) { + /* Update output FIFO. */ + ctx->output.fifo <<= numbits; + ctx->output.num_bits -= numbits; + } + } + + if (bconsume && ctx->exception == SWSR_EXCEPT_NO_EXCEPTION) { + ctx->output.totalbits_consumed += numbits; + ctx->input.delimunit_bitofst += numbits; + } + + /* Return the bits */ + return bitsread; +} + +int swsr_read_signed_expgoulomb(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + unsigned int exp_goulomb; + unsigned char unsign; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + /* Read unsigned value then convert to signed value */ + exp_goulomb = swsr_read_unsigned_expgoulomb(ctx); + + unsign = exp_goulomb & 1; + exp_goulomb >>= 1; + exp_goulomb = (unsign) ? exp_goulomb + 1 : -(int)exp_goulomb; + + if (ctx->exception != SWSR_EXCEPT_NO_EXCEPTION) + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + /* Return the signed value */ + return exp_goulomb; +} + +static unsigned int swsr_readunsigned_expgoulomb(struct swsr_context *ctx) +{ + unsigned int numbits = 0; + unsigned int bitpeeked; + unsigned int bitread; + unsigned int setbits; + unsigned int expgoulomb; + + /* Loop until we have found a non-zero nibble or reached 31 0-bits */ + /* first read is 3 bits only to prevent an illegal 32-bit peek */ + numbits = 1; + do { + bitpeeked = swsr_peekbits(ctx, numbits); + /* Check for non-zero nibble */ + if (bitpeeked != 0) + break; + + numbits++; + + } while (numbits < 32); + + /* Correct the number of leading zero bits */ + numbits--; + + if (bitpeeked) { + /* read leading zeros and 1-bit */ + bitread = swsr_read_bits(ctx, numbits + 1); + if (bitread != 1) + ctx->exception = SWSR_EXCEPT_EXPGOULOMB_ERROR; + } else { + /* + * read 31 zero bits - special case to deal with 31 or 32 + * leading zeros + */ + bitread = swsr_read_bits(ctx, 31); + if (bitread != 0) + ctx->exception = SWSR_EXCEPT_EXPGOULOMB_ERROR; + + /* + * next 3 bits make either 31 0-bit code:'1xx', + * or 32 0-bit code:'010' + */ + /* + * only valid 32 0-bit code is:'0..010..0' + * and results in 0xffffffff + */ + bitpeeked = swsr_peekbits(ctx, 3); + + if (ctx->exception == SWSR_EXCEPT_NO_EXCEPTION) { + if (0x4 & bitpeeked) { + bitread = swsr_read_bits(ctx, 1); + numbits = 31; + } else { + if (bitpeeked != 2) + ctx->exception = SWSR_EXCEPT_EXPGOULOMB_ERROR; + + bitread = swsr_read_bits(ctx, 3); + bitread = swsr_read_bits(ctx, 31); + if (bitread != 0) + ctx->exception = SWSR_EXCEPT_EXPGOULOMB_ERROR; + + return 0xffffffff; + } + } else { + /* encountered an exception while reading code */ + /* just return a valid value */ + return 0; + } + } + + /* read data bits */ + bitread = 0; + if (numbits) + bitread = swsr_read_bits(ctx, numbits); + + /* convert exp-goulomb to value */ + setbits = (1 << numbits) - 1; + expgoulomb = setbits + bitread; + /* Return the value */ + return expgoulomb; +} + +unsigned int swsr_read_unsigned_expgoulomb(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + unsigned int value; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + value = swsr_readunsigned_expgoulomb(ctx); + + if (ctx->exception != SWSR_EXCEPT_NO_EXCEPTION) + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return value; +} + +enum swsr_exception swsr_check_exception(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + enum swsr_exception exception; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return (enum swsr_exception)IMG_ERROR_INVALID_PARAMETERS; + } + + exception = ctx->exception; + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return (enum swsr_exception)IMG_ERROR_NOT_INITIALISED; + } + + ctx->exception = SWSR_EXCEPT_NO_EXCEPTION; + return exception; +} + +int swsr_check_more_rbsp_data(void *ctx_hndl, unsigned char *more_rbsp_data) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + + int rembitsinbyte; + unsigned char currentbyte; + int numof_aligned_rembits; + unsigned long long rest_alignedbytes; + unsigned char moredata = 0; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + if (ctx->input.emprevent != SWSR_EMPREVENT_00000300) { + pr_err("SWSR cannot determine More RBSP data for a stream without SWSR_EMPREVENT_00000300: %s\n", + __func__); + return IMG_ERROR_OPERATION_PROHIBITED; + } + + /* + * Always fill the output FIFO to ensure the no_moredata flag is set + * when there are enough remaining bytes + */ + + swsr_fill_outputfifo(ctx); + + if (ctx->output.num_bits != 0) { + /* Calculate the number of bits in the MS byte */ + rembitsinbyte = (ctx->output.num_bits & 0x7); + if (rembitsinbyte == 0) + rembitsinbyte = 8; + + numof_aligned_rembits = (ctx->output.num_bits - rembitsinbyte); + + /* Peek the value of last byte. */ + currentbyte = swsr_peekbits(ctx, rembitsinbyte); + rest_alignedbytes = (ctx->output.fifo >> + (64 - ctx->output.num_bits)) & + ((1ULL << numof_aligned_rembits) - 1); + + if ((currentbyte == (1 << (rembitsinbyte - 1))) && + (numof_aligned_rembits == 0 || (rest_alignedbytes == 0 && + ((((((unsigned int)numof_aligned_rembits >> 3)) < + ctx->input.emprev_seq_len) && + ctx->input.num_bytes == 0) || ctx->input.no_moredata)))) + moredata = 0; + else + moredata = 1; + } + + *more_rbsp_data = moredata; + + return 0; +} + +unsigned int swsr_read_onebit(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + unsigned int bitread; + + /* Validate input arguments. */ + if (!ctx_hndl) { + VDEC_ASSERT(0); + return -EIO; + } + + ctx = (struct swsr_context *)ctx_hndl; + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + /* Optimize with inline code (specific version of call below). */ + bitread = swsr_read_bits(ctx, 1); + + return bitread; +} + +unsigned int swsr_read_bits(void *ctx_hndl, unsigned int no_bits) +{ + struct swsr_context *ctx; + + /* Validate input arguments. */ + if (!ctx_hndl) { + VDEC_ASSERT(0); + return -EIO; + } + + ctx = (struct swsr_context *)ctx_hndl; + + /* Validate input arguments. */ + if (!ctx->initialised) { + pr_err("%s: Invalid SWSR context\n", __func__); + ctx->exception = SWSR_EXCEPT_INVALID_CONTEXT; + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return 0; + } + + if (no_bits > SWSR_MAX_SYNTAX_LENGTH) { + pr_err("Maximum symbol length exceeded\n"); + ctx->exception = SWSR_EXCEPT_WRONG_CODEWORD_ERROR; + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return 0; + } + + return swsr_getbits_from_outputfifo(ctx, no_bits, 1); +} + +int swsr_read_signedbits(void *ctx_hndl, unsigned int no_bits) +{ + struct swsr_context *ctx; + int outbits = 0; + + /* Validate input arguments. */ + if (!ctx_hndl) { + VDEC_ASSERT(0); + return -EIO; + } + + ctx = (struct swsr_context *)ctx_hndl; + + /* Check if the context has been initialized. */ + if (!ctx->initialised) { + pr_err("%s: Invalid SWSR context\n", __func__); + ctx->exception = SWSR_EXCEPT_INVALID_CONTEXT; + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return 0; + } + + if ((no_bits + 1) > SWSR_MAX_SYNTAX_LENGTH) { + pr_err("Maximum symbol length exceeded\n"); + ctx->exception = SWSR_EXCEPT_WRONG_CODEWORD_ERROR; + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return 0; + } + outbits = swsr_getbits_from_outputfifo(ctx, no_bits, 1); + + return (swsr_getbits_from_outputfifo(ctx, 1, 1)) ? -outbits : outbits; +} + +unsigned int swsr_peekbits(void *ctx_hndl, unsigned int no_bits) +{ + struct swsr_context *ctx; + + /* validate input parameters */ + if (!ctx_hndl) { + VDEC_ASSERT(0); + return -EIO; + } + + ctx = (struct swsr_context *)ctx_hndl; + + /* Validate input arguments. */ + if (!ctx->initialised) { + pr_err("%s: Invalid SWSR context\n", __func__); + ctx->exception = SWSR_EXCEPT_INVALID_CONTEXT; + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return 0; + } + + if (no_bits > SWSR_MAX_SYNTAX_LENGTH) { + pr_err("Maximum symbol length exceeded\n"); + ctx->exception = SWSR_EXCEPT_WRONG_CODEWORD_ERROR; + ctx->exception_handler_fxn(ctx->exception, ctx->pexception_param); + + return 0; + } + + return swsr_getbits_from_outputfifo(ctx, no_bits, 0); +} + +int swsr_byte_align(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + unsigned int numbits; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + numbits = (ctx->output.num_bits & 0x7); + /* Read the required number of bits if not already byte-aligned. */ + if (numbits != 0) + swsr_read_bits(ctx, numbits); + + SWSR_ASSERT((ctx->output.num_bits & 0x7) == 0); + + return 0; +} + +int swsr_get_total_bitsconsumed(void *ctx_hndl, unsigned long long *total_bitsconsumed) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + + /* Validate input arguments. */ + if (!ctx || !total_bitsconsumed) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + *total_bitsconsumed = ctx->output.totalbits_consumed; + + return 0; +} + +int swsr_get_byte_offset_curbuf(void *ctx_hndl, unsigned long long *byte_offset) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + struct swsr_buffer *outbuf; + + /* Validate input arguments. */ + if (!ctx || !byte_offset) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + if (ctx->output.num_bits != 0) { + pr_err("SWSR output FIFO not empty. First seek to next delimiter: %s\n", + __func__); + return IMG_ERROR_OPERATION_PROHIBITED; + } + + outbuf = lst_first(&ctx->buffer_ctx.used_buffer_list); + if (outbuf) + *byte_offset = outbuf->num_bytes_read; + else + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + + return 0; +} + +static int swsr_update_emprevent(enum swsr_emprevent emprevent, + struct swsr_context *ctx) +{ + struct swsr_input *input; + + input = &ctx->input; + + input->emprevent = emprevent; + switch (input->emprevent) { + case SWSR_EMPREVENT_00000300: + input->emprev_seq_len = 4; + break; + + case SWSR_EMPREVENT_ff00: + input->emprev_seq_len = 2; + break; + + case SWSR_EMPREVENT_000002: + input->emprev_seq_len = 3; + break; + + default: + input->emprev_seq_len = 0; + break; + } + + return 0; +} + +int swsr_consume_delim(void *ctx_hndl, enum swsr_emprevent emprevent, + unsigned int size_delim_length, unsigned long long *byte_count) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + struct swsr_input *input; + unsigned long long delimiter = 0; + + /* Validate input arguments. */ + if (!ctx || emprevent >= SWSR_EMPREVENT_MAX || + (ctx->input.config.delim_type == SWSR_DELIM_SIZE && + size_delim_length > SWSR_MAX_DELIM_LENGTH)) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + if (ctx->input.config.delim_type == SWSR_DELIM_SIZE && + size_delim_length == 0 && !byte_count) { + pr_err("Byte count value must be provided when size delimiter is zero length: %s\n", + __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + input = &ctx->input; + + /* + * Ensure that the input is at a delimiter since emulation prevention + * removal will not have spanned into this next unit. + * This allows emulation prevention detection modes to be changed. + * Now check for delimiter. + */ + input->delim_found = swsr_checkfor_delimiter(ctx); + + if (!input->delim_found) + return IMG_ERROR_UNEXPECTED_STATE; + + /* Output bitstream FIFOs should be empty. */ + /* NOTE: flush output queue using seek function. */ + SWSR_ASSERT(ctx->output.num_bits == 0); + + /* Only update the delimiter length for size delimiters. */ + if (input->config.delim_type == SWSR_DELIM_SIZE) + input->config.delim_length = size_delim_length; + + /* Update the emulation prevention detection/removal scheme */ + swsr_update_emprevent(emprevent, ctx); + + /* + * Peek at the NAL type and return in callback only + * when delimiter is in bitstream. + */ + if (input->config.delim_length) { + unsigned int shift; + unsigned char naltype; + + /* + * Peek at the next 8-bits after the delimiter that + * resides in internal FIFO. + */ + shift = SWSR_OUTPUT_FIFO_LENGTH - + (input->config.delim_length + SWSR_NALTYPE_LENGTH); + naltype = (input->fifo >> shift) & NBIT_8BYTE_MASK(SWSR_NALTYPE_LENGTH); + + /* + * Notify caller of NAL type so that bitstream segmentation + * can take place before the delimiter is consumed + */ + ctx->buffer_ctx.cb_fxn(SWSR_EVENT_DELIMITER_NAL_TYPE, ctx->buffer_ctx.cb_param, + naltype, NULL, NULL); + } + + /* + * Clear the delimiter found flag and reset bytes read to allow + * reading of data from input FIFO. + */ + input->delim_found = 0; + + if (input->config.delim_length != 0) { + unsigned long long scpvalue = input->config.scp_value; + unsigned int i; + unsigned char byte = 0; + + /* + * Ensure that delimiter is not detected while delimiter + * is read. + */ + if (input->config.delim_type == SWSR_DELIM_SIZE) { + input->bytes_read_since_delim = 0; + input->byte_count = (input->config.delim_length + 7) / 8; + } else if (input->config.delim_type == SWSR_DELIM_SCP) { + input->config.scp_value = 0xdeadbeefdeadbeefUL; + } + + /* + * Fill output FIFO only with bytes at least partially + * used for delimiter. + */ + for (i = 0; i < ((input->config.delim_length + 7) / 8); i++) { + swsr_readbyte_from_inputfifo(ctx, &byte); + + ctx->output.fifo |= ((unsigned long long)byte << + (SWSR_OUTPUT_FIFO_LENGTH - 8 - ctx->output.num_bits)); + ctx->output.num_bits += 8; + } + + /* + * Read delimiter from output FIFO leaving any remaining + * non-byte-aligned bits behind. + */ + delimiter = swsr_getbits_from_outputfifo(ctx, input->config.delim_length, 1); + + /* Restore SCP value. */ + if (input->config.delim_type == SWSR_DELIM_SCP) + input->config.scp_value = scpvalue; + } else { + /* + * For size delimited bitstreams without a delimiter use + * the byte count provided. + */ + SWSR_ASSERT(*byte_count > 0); + delimiter = *byte_count; + SWSR_ASSERT(input->config.delim_type == SWSR_DELIM_SIZE); + } + + if (input->config.delim_type == SWSR_DELIM_SCP) + SWSR_ASSERT((delimiter & NBIT_8BYTE_MASK(input->config.delim_length)) == + input->config.scp_value); + else if (input->config.delim_type == SWSR_DELIM_SIZE) { + input->byte_count = delimiter; + + /* Return byte count if argument provided. */ + if (byte_count) + *byte_count = input->byte_count; + } + + input->bytes_read_since_delim = 0; + { + struct swsr_buffer *buffer = input->buf; + + if (!buffer) + buffer = lst_first(&ctx->buffer_ctx.used_buffer_list); + if (buffer) + input->delimited_unit_start_offset = (long)buffer->num_bytes_read; + else + input->delimited_unit_start_offset = 0; + } + input->delimited_unit_size = 0; + input->delimunit_bitofst = 0; + + input->no_moredata = 0; + + return 0; +} + +enum swsr_found swsr_seek_delim_or_eod(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + enum swsr_found found = SWSR_FOUND_DATA; + unsigned char byte; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return (enum swsr_found)IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return (enum swsr_found)IMG_ERROR_NOT_INITIALISED; + } + + /* Read the residual contents of the output FIFO */ + swsr_byte_align(ctx); + while (ctx->output.num_bits > 0) { + SWSR_ASSERT((ctx->output.num_bits & 0x7) == 0); + swsr_read_bits(ctx, 8); + } + SWSR_ASSERT(ctx->output.num_bits == 0); + if (ctx->input.config.delim_type == SWSR_DELIM_SCP) { + struct swsr_input *input = &ctx->input; + struct swsr_output *output = &ctx->output; + + while (found == SWSR_FOUND_DATA) { + unsigned char *offset; + unsigned int delimlength_inbytes; + unsigned char *startoffset; + unsigned long long mask; + unsigned long long scp; + unsigned char scpfirstbyte; + + /* + * ensure that all the data in the input FIFO comes + * from the current buffer + */ + if (input->buf && input->buf->byte_offset <= input->num_bytes) { + found = swsr_consumebyte_from_inputfifo(ctx, &byte); + continue; + } + + /* consume remaining bytes from the FIFO */ + if (!input->buf) { + found = swsr_consumebyte_from_inputfifo(ctx, &byte); + continue; + } + + delimlength_inbytes = (input->config.delim_length + 7) / 8; + + /* + * Make the mask and the scp value byte aligned to + * speed up things + */ + mask = ((1UL << input->config.delim_length) - 1) << + (8 * delimlength_inbytes - input->config.delim_length); + scp = input->config.scp_value << + (8 * delimlength_inbytes - input->config.delim_length); + scpfirstbyte = (scp >> 8 * (delimlength_inbytes - 1)) & 0xFF; + + /* rollback the input FIFO */ + input->buf->byte_offset -= input->num_bytes; + input->buf->num_bytes_read -= input->num_bytes; + input->bitstream_offset -= input->num_bytes; + input->num_bytes = 0; + input->fifo = 0; + + startoffset = input->buf->data + input->buf->byte_offset; + + while (found == SWSR_FOUND_DATA) { + offset = memchr(input->buf->data + input->buf->byte_offset, + scpfirstbyte, + input->buf->num_bytes - + (input->buf->byte_offset + delimlength_inbytes - + 1)); + + if (offset) { + unsigned int i; + + /* + * load bytes that might be SCP into + * the FIFO + */ + for (i = 0; i < delimlength_inbytes; i++) { + input->fifo <<= 8; + input->fifo |= offset[i]; + } + + input->buf->byte_offset = offset - input->buf->data; + + if ((input->fifo & mask) == scp) { + unsigned long long bytesread = offset + - startoffset; + + /* + * Scp found, fill the rest of + * the FIFO + */ + for (i = delimlength_inbytes; + i < SWSR_INPUT_FIFO_LENGTH && + input->buf->byte_offset + i < + input->buf->num_bytes; + i++) { + input->fifo <<= 8; + input->fifo |= offset[i]; + } + + input->fifo <<= (SWSR_INPUT_FIFO_LENGTH - i) * 8; + + input->bytes_for_next_sequ = 0; + input->num_bytes = i; + + input->buf->byte_offset += i; + + input->buf->num_bytes_read = offset - + input->buf->data; + input->bitstream_offset += bytesread + i; + + output->totalbits_consumed += bytesread * 8; + + input->delimunit_bitofst += bytesread * 8; + + output->num_bits = 0; + output->fifo = 0; + + SWSR_ASSERT(swsr_checkfor_delimiter(ctx)); + + found = SWSR_FOUND_DELIM; + } else { + input->buf->byte_offset++; + } + } else { + /* End of the current buffer */ + unsigned int bytesread = input->buf->num_bytes - + (startoffset - input->buf->data); + unsigned int i; + + /* update offsets */ + input->bitstream_offset += bytesread; + output->totalbits_consumed += bytesread * 8; + input->delimunit_bitofst += bytesread * 8; + + input->buf->byte_offset = input->buf->num_bytes; + input->buf->num_bytes_read = input->buf->num_bytes - + (delimlength_inbytes - 1); + + /* load remaining bytes to FIFO */ + offset = input->buf->data + + input->buf->num_bytes - + (delimlength_inbytes - 1); + for (i = 0; i < delimlength_inbytes - 1; + i++) { + input->fifo <<= 8; + input->fifo |= offset[i]; + } + + input->fifo <<= (SWSR_INPUT_FIFO_LENGTH - i) * 8; + + input->bytes_for_next_sequ = 0; + input->num_bytes = delimlength_inbytes - 1; + + output->num_bits = 0; + output->fifo = 0; + + /* + * Consume a few bytes from the next + * byte to check if there is scp on + * buffers boundary + */ + for (i = 0; + i < delimlength_inbytes && found == SWSR_FOUND_DATA; + i++) { + found = swsr_consumebyte_from_inputfifo(ctx, &byte); + SWSR_ASSERT(found != SWSR_FOUND_NONE); + } + + break; + } + } + } + } else { + /* + * Extract data from input FIFO until data is not found either + * because we have run out or a SCP has been detected. + */ + while (found == SWSR_FOUND_DATA) { + found = swsr_consumebyte_from_inputfifo(ctx, &byte); + SWSR_ASSERT(found != SWSR_FOUND_NONE); + } + } + + /* + * When the end of data has been reached there should be no + * more data in the input FIFO. + */ + if (found == SWSR_FOUND_EOD) + SWSR_ASSERT(ctx->input.num_bytes == 0); + + SWSR_ASSERT(found != SWSR_FOUND_DATA); + return found; +} + +enum swsr_found swsr_check_delim_or_eod(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + enum swsr_found found = SWSR_FOUND_DATA; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + + return (enum swsr_found)IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + + return (enum swsr_found)IMG_ERROR_NOT_INITIALISED; + } + + /* + * End of data when all FIFOs are empty and there is nothing left to + * read from the input buffers. + */ + if (ctx->output.num_bits == 0 && ctx->input.num_bytes == 0 && + ctx->input.bitstream_offset >= ctx->input.bitstream_size) + found = SWSR_FOUND_EOD; + else if (ctx->output.num_bits == 0 && swsr_checkfor_delimiter(ctx)) { + /* + * Output queue is empty and delimiter is at the head of + * input queue. + */ + found = SWSR_FOUND_DELIM; + } + + return found; +} + +int swsr_start_bitstream(void *ctx_hndl, const struct swsr_config *config, + unsigned long long bitstream_size, enum swsr_emprevent emprevent) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + struct swsr_buffer *buffer; + unsigned int result; + + /* Validate input arguments. */ + if (!ctx || !config || config->delim_type >= SWSR_DELIM_MAX || + config->delim_length > SWSR_MAX_DELIM_LENGTH || + config->scp_value > NBIT_8BYTE_MASK(config->delim_length) || + emprevent >= SWSR_EMPREVENT_MAX) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + /* Move all used buffers into free list */ + buffer = lst_removehead(&ctx->buffer_ctx.used_buffer_list); + while (buffer) { + lst_add(&ctx->buffer_ctx.free_buffer_list, buffer); + buffer = lst_removehead(&ctx->buffer_ctx.used_buffer_list); + } + + /* Clear all the shift-register state (except config) */ + memset(&ctx->input, 0, sizeof(ctx->input)); + memset(&ctx->output, 0, sizeof(ctx->output)); + + /* Update input FIFO configuration */ + ctx->input.bitstream_size = bitstream_size; + ctx->input.config = *config; + result = swsr_update_emprevent(emprevent, ctx); + SWSR_ASSERT(result == 0); + + /* + * Signal delimiter found to ensure that no data is read out of + * input FIFO + * while fetching the first bitstream data into input FIFO. + */ + ctx->input.delim_found = 1; + result = swsr_fill_outputfifo(ctx); + SWSR_ASSERT(result == 0); + + /* Now check for delimiter. */ + ctx->input.delim_found = swsr_checkfor_delimiter(ctx); + + return 0; +} + +int swsr_deinitialise(void *ctx_hndl) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + struct swsr_buffer *buffer; + + /* Validate input arguments. */ + if (!ctx) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + /* Free all used buffer containers */ + buffer = lst_removehead(&ctx->buffer_ctx.used_buffer_list); + while (buffer) { + kfree(buffer); + buffer = lst_removehead(&ctx->buffer_ctx.used_buffer_list); + } + + /* Free all free buffer containers. */ + buffer = lst_removehead(&ctx->buffer_ctx.free_buffer_list); + while (buffer) { + kfree(buffer); + buffer = lst_removehead(&ctx->buffer_ctx.free_buffer_list); + } + + ctx->initialised = 0; + kfree(ctx); + + return 0; +} + +int swsr_initialise(swsr_except_handler_fxn exception_handler_fxn, + void *exception_cbparam, swsr_callback_fxn callback_fxn, + void *cb_param, void **ctx_hndl) +{ + struct swsr_context *ctx; + struct swsr_buffer *buffer; + unsigned int i; + unsigned int result; + + /* Validate input arguments. */ + if (!exception_handler_fxn || !exception_cbparam || !callback_fxn || + !cb_param || !ctx_hndl) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Allocate and initialise shift-register context */ + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + VDEC_ASSERT(0); + return -EINVAL; + } + + /* Setup shift-register context */ + ctx->exception_handler_fxn = exception_handler_fxn; + ctx->pexception_param = exception_cbparam; + + ctx->buffer_ctx.cb_fxn = callback_fxn; + ctx->buffer_ctx.cb_param = cb_param; + + /* + * Allocate a new buffer container for each byte in internal storage. + * This is the theoretical maximum number of buffers in the SWSR at + * any one time. + */ + for (i = 0; i < SWSR_INPUT_FIFO_LENGTH + (SWSR_OUTPUT_FIFO_LENGTH / 8); + i++) { + /* Allocate a buffer container */ + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + SWSR_ASSERT(buffer); + if (!buffer) { + result = IMG_ERROR_OUT_OF_MEMORY; + goto error; + } + + /* Add container to free list */ + lst_add(&ctx->buffer_ctx.free_buffer_list, buffer); + } + + SWSR_ASSERT(SWSR_MAX_SYNTAX_LENGTH <= (sizeof(unsigned int) * 8)); + + ctx->initialised = 1; + *ctx_hndl = ctx; + + return 0; +error: + buffer = lst_removehead(&ctx->buffer_ctx.free_buffer_list); + while (buffer) { + kfree(buffer); + buffer = lst_removehead(&ctx->buffer_ctx.free_buffer_list); + } + kfree(ctx); + + return result; +} + +static unsigned char swsr_israwdata_extraction_supported(struct swsr_context *ctx) +{ + /* + * For now only h.264/HEVC like 0x000001 SCP delimited + * bistreams are supported. + */ + if (ctx->input.config.delim_type == SWSR_DELIM_SCP && + ctx->input.config.delim_length == (3 * 8) && + ctx->input.config.scp_value == 0x000001) + return 1; + + return 0; +} + +static int swsr_getcurrent_delimited_unitsize(struct swsr_context *ctx, unsigned int *size) +{ + struct swsr_buffer *buf; + + buf = ctx->input.buf; + if (!buf) + buf = lst_first(&ctx->buffer_ctx.used_buffer_list); + + if (buf && ctx->input.delimited_unit_start_offset >= 0 && + ctx->input.delimited_unit_start_offset < buf->num_bytes) { + unsigned long long bufptr = + (unsigned long long)ctx->input.delimited_unit_start_offset; + unsigned int zeros = 0; + + /* Scan the current buffer for the next SCP. */ + while (1) { + /* Look for two consecutive 0 bytes. */ + while ((bufptr < buf->num_bytes) && (zeros < 2)) { + if (buf->data[bufptr++] == 0) + zeros++; + else + zeros = 0; + } + /* + * If we're not at the end of the buffer already and + * the next byte is 1, we've got it. + */ + /* + * If we're at the end of the buffer, just assume + * we've got it too + * as we do not support buffer spanning units. + */ + if (bufptr < buf->num_bytes && buf->data[bufptr] == 1) { + break; + } else if (bufptr == buf->num_bytes) { + zeros = 0; + break; + } + /* + * Finally just decrease the number of 0s found + * already and go on scanning. + */ + else + zeros = 1; + } + /* Calculate the unit size. */ + ctx->input.delimited_unit_size = (unsigned int)(bufptr - + (unsigned long long)ctx->input.delimited_unit_start_offset) - zeros; + *size = ctx->input.delimited_unit_size; + } else { + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + + return 0; +} + +int swsr_get_current_delimited_unitsize(void *ctx_hndl, unsigned int *size) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + + /* Validate input arguments. */ + if (!ctx || !size) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + if (!swsr_israwdata_extraction_supported(ctx)) + return IMG_ERROR_NOT_SUPPORTED; + + return swsr_getcurrent_delimited_unitsize(ctx, size); +} + +int swsr_get_current_delimited_unit(void *ctx_hndl, unsigned char *data, unsigned int *size) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + struct swsr_buffer *buf; + unsigned int copysize; + + /* Validate input arguments. */ + if (!ctx || !data || !size || *size == 0) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + if (!swsr_israwdata_extraction_supported(ctx)) + return IMG_ERROR_NOT_SUPPORTED; + + buf = ctx->input.buf; + if (!buf) + buf = lst_first(&ctx->buffer_ctx.used_buffer_list); + + if (buf && ctx->input.delimited_unit_start_offset >= 0) { + if (ctx->input.delimited_unit_size == 0) + swsr_getcurrent_delimited_unitsize(ctx, ©size); + + if (ctx->input.delimited_unit_size < *size) + *size = ctx->input.delimited_unit_size; + + memcpy(data, buf->data + ctx->input.delimited_unit_start_offset, *size); + } else { + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + + return 0; +} + +int swsr_get_current_delimited_unit_bit_offset(void *ctx_hndl, unsigned int *bit_offset) +{ + struct swsr_context *ctx = (struct swsr_context *)ctx_hndl; + + /* Validate input arguments. */ + if (!ctx || !bit_offset) { + pr_err("Invalid arguments to function: %s\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!ctx->initialised) { + pr_err("SWSR not yet initialised: %s\n", __func__); + return IMG_ERROR_NOT_INITIALISED; + } + + if (!swsr_israwdata_extraction_supported(ctx)) + return IMG_ERROR_NOT_SUPPORTED; + + if (ctx->input.delimited_unit_start_offset >= 0) + *bit_offset = ctx->input.delimunit_bitofst; + + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/swsr.h b/drivers/media/platform/vxe-vxd/decoder/swsr.h --- a/drivers/media/platform/vxe-vxd/decoder/swsr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/swsr.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Software Shift Register Access fucntions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstreming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ +#ifndef _SWSR_H +#define _SWSR_H + +#include + +#include "img_errors.h" +#include "lst.h" + +#define SWSR_MAX_DELIM_LENGTH (8 * 8) + +enum swsr_exception { + SWSR_EXCEPT_NO_EXCEPTION = 0x00, + SWSR_EXCEPT_ENCAPULATION_ERROR1, + SWSR_EXCEPT_ENCAPULATION_ERROR2, + SWSR_EXCEPT_ACCESS_INTO_SCP, + SWSR_EXCEPT_ACCESS_BEYOND_EOD, + SWSR_EXCEPT_EXPGOULOMB_ERROR, + SWSR_EXCEPT_WRONG_CODEWORD_ERROR, + SWSR_EXCEPT_NO_SCP, + SWSR_EXCEPT_INVALID_CONTEXT, + SWSR_EXCEPT_FORCE32BITS = 0x7FFFFFFFU +}; + +enum swsr_cbevent { + SWSR_EVENT_INPUT_BUFFER_START = 0, + SWSR_EVENT_OUTPUT_BUFFER_END, + SWSR_EVENT_DELIMITER_NAL_TYPE, + SWSR_EVENT_FORCE32BITS = 0x7FFFFFFFU +}; + +enum swsr_found { + SWSR_FOUND_NONE = 0, + SWSR_FOUND_EOD, + SWSR_FOUND_DELIM, + SWSR_FOUND_DATA, + SWSR_FOUND_FORCE32BITS = 0x7FFFFFFFU +}; + +enum swsr_delim_type { + SWSR_DELIM_NONE = 0, + SWSR_DELIM_SCP, + SWSR_DELIM_SIZE, + SWSR_DELIM_MAX, + SWSR_DELIM_FORCE32BITS = 0x7FFFFFFFU +}; + +enum swsr_emprevent { + SWSR_EMPREVENT_NONE = 0x00, + SWSR_EMPREVENT_00000300, + SWSR_EMPREVENT_ff00, + SWSR_EMPREVENT_000002, + SWSR_EMPREVENT_MAX, + SWSR_EMPREVENT_FORCE32BITS = 0x7FFFFFFFU +}; + +struct swsr_config { + enum swsr_delim_type delim_type; + unsigned int delim_length; + unsigned long long scp_value; +}; + +/* + * This is the function prototype for the caller supplier exception handler. + * + * NOTE: The internally recorded exception is reset to #SWSR_EXCEPT_NO_EXCEPTION + * on return from SWSR_CheckException() or a call to the caller supplied + * exception handler see #SWSR_pfnExceptHandler. + * + * NOTE: By defining an exception handler the caller can handle Shift Register + * errors as they occur - for example, using a structure exception mechanism + * such as setjmp/longjmp. + */ +typedef void (*swsr_except_handler_fxn)(enum swsr_exception exception, + void *callback_param); + +/* + * This is the function prototype for the caller supplier to retrieve the data + * from the application + */ +typedef void (*swsr_callback_fxn)(enum swsr_cbevent event, + void *priv_data, + unsigned char nal_type, unsigned char **data_buffer, + unsigned long long *data_size); + +int swsr_get_total_bitsconsumed(void *context, unsigned long long *total_bitsconsumed); + +/* + * This function is used to return the offset into the current bitstream buffer + * on the shift-register output FIFO. Call after #SWSR_SeekDelimOrEOD to + * determine the offset of an delimiter. + */ +int swsr_get_byte_offset_curbuf(void *context, unsigned long long *byte_offset); + +/* + * This function is used to read a signed Exp-Goulomb value from the Shift + * Register. + * + * NOTE: If this function is used to attempt to read into a Start-Code-Prefix + * or beyond the End-Of-Data then and exception is generated which can be + * handled by the caller supplied exception handler see + * #SWSR_pfnExceptionHandler. If no exception handler has been supplied (or the + * exception handler returns) then the exception is recorded and can be obtained + * using SWSR_CheckException(). In this event the function returns 0. + */ +int swsr_read_signed_expgoulomb(void *context); + +/* + * This function is used to read a unsigned Exp-Goulomb value from the Shift + * Register. + * + * NOTE: If this function is used to attempt to read into a Start-Code-Prefix + * or beyond the End-Of-Data then and exception is generated which can be + * handled by the caller supplied exception handler see + * #SWSR_pfnExceptionHandler. If no exception handler has been supplied (or the + * exception handler returns) then the exception is recorded and can be obtained + * using SWSR_CheckException(). In this event the function returns 0. + */ +unsigned int swsr_read_unsigned_expgoulomb(void *context); + +/* + * This function is used to check for exceptions. + * + * NOTE: The internally recorded exception is reset to #SWSR_EXCEPT_NO_EXCEPTION + * on return from SWSR_CheckException() or a call to the caller supplied + * exception handler see #SWSR_pfnExceptionHandler. + */ +enum swsr_exception swsr_check_exception(void *context); + +/* + * This function is used to check for bitstream data with + * SWSR_EMPREVENT_00000300 whether more RBSP data is present. + */ +int swsr_check_more_rbsp_data(void *context, unsigned char *more_rbsp_data); + +/* + * This function is used to read a single bit from the Shift Register. + * + * NOTE: If this function is used to attempt to read into a Start-Code-Prefix + * or beyond the End-Of-Data then and exception is generated which can be + * handled by the caller supplied exception handler see + * #SWSR_pfnExceptionHandler. If no exception handler has been supplied (or the + * exception handler returns) then the exception is recorded and can be obtained + * using SWSR_CheckException(). In this event the function returns 0. + */ +unsigned int swsr_read_onebit(void *context); + +/* + * This function is used to consume a number of bits from the Shift Register. + * + * NOTE: If this function is used to attempt to read into a Start-Code-Prefix + * or beyond the End-Of-Data then and exception is generated which can be + * handled by the caller supplied exception handler see + * #SWSR_pfnExceptionHandler. If no exception handler has been supplied (or the + * exception handler returns) then the exception is recorded and can be obtained + * using SWSR_CheckException(). In this event the function returns 0. + */ +unsigned int swsr_read_bits(void *context, unsigned int no_bits); + +int swsr_read_signedbits(void *context, unsigned int no_bits); + +/* + * This function is used to peek at number of bits from the Shift Register. The + * bits are not consumed. + * + * NOTE: If this function is used to attempt to read into a Start-Code-Prefix + * or beyond the End-Of-Data then and exception is generated which can be + * handled by the caller supplied exception handler see + * #SWSR_pfnExceptionHandler. If no exception handler has been supplied (or + * the exception handler returns) then the exception is recorded and can be + * obtained using SWSR_CheckException(). In this event the function returns 0. + */ +unsigned int swsr_peekbits(void *context, unsigned int no_bits); + +/* + * Makes the shift-register output byte-aligned by consuming the remainder of + * the current partially read byte. + */ +int swsr_byte_align(void *context); + +/* + * Consume the next delimiter whose length should be specified if delimiter type + * is #SWSR_DELIM_SIZE. The emulation prevention detection/removal scheme can + * also be specified for this and subsequent units. + * + * Consumes the unit delimiter from the bitstream buffer. The delimiter type + * depends upon the bitstream format. + */ +int swsr_consume_delim(void *context, + enum swsr_emprevent emprevent, + unsigned int size_delim_length, + unsigned long long *byte_count); + +/* + * Seek for the next delimiter or end of bitstream data if no delimiter is + * found. + */ +enum swsr_found swsr_seek_delim_or_eod(void *context); + +/* + * Check if shift-register is at a delimiter or end of data. + */ +enum swsr_found swsr_check_delim_or_eod(void *context); + +/* + * This function automatically fetches the first bitstream buffer (using + * callback with event type #SWSR_EVENT_INPUT_BUFFER_START) before returning. + */ +int swsr_start_bitstream(void *context, + const struct swsr_config *pconfig, + unsigned long long bitstream_size, + enum swsr_emprevent emprevent); + +/* + * This function is used to de-initialise the Shift Register. + */ +int swsr_deinitialise(void *context); + +/* + * This function is used to initialise the Shift Register. + * + * NOTE: If no exception handler is provided (pfnExceptionHandler == IMG_NULL) + * then the caller must check for exceptions using the function + * SWSR_CheckException(). + * + * NOTE: If pui8RbduBuffer is IMG_NULL then the bit stream is not encapsulated + * so the Shift Register needn't perform and de-encapsulation. However, + * if this is not IMG_NULL then, from time to time, the Shift Register APIs + * will de-encapsulate portions of the bit stream into this intermediate buffer + * - the larger the buffer the less frequent the de-encapsulation function + * needs to be called. + */ +int swsr_initialise(swsr_except_handler_fxn exception_handler_fxn, + void *exception_cbparam, + swsr_callback_fxn callback_fxn, + void *cb_param, + void **context); + +/* + * This function is used to return the size in bytes of the delimited unit + * that's currently being processed. + * + * NOTE: This size includes all the emulation prevention bytes present + * in the delimited unit. + */ +int swsr_get_current_delimited_unitsize(void *context, unsigned int *size); + +/* + * This function is used to copy the delimited unit that's currently being + * processed to the provided buffer. + * + * NOTE: This delimited unit includes all the emulation prevention bytes present + * in it. + */ +int swsr_get_current_delimited_unit(void *context, unsigned char *data, unsigned int *size); + +/* + * This function is used to return the bit offset the shift register is at + * in processing the current delimited unit. + * + * NOTE: This offset does not count emulation prevention bytes. + */ +int swsr_get_current_delimited_unit_bit_offset(void *context, unsigned int *bit_offset); + +#endif /* _SWSR_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/translation_api.c b/drivers/media/platform/vxe-vxd/decoder/translation_api.c --- a/drivers/media/platform/vxe-vxd/decoder/translation_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/translation_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1725 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VDECDD translation APIs. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +/* As of now we are defining HAS_H264 */ +#define HAS_H264 +#define VDEC_USE_PVDEC + +#include +#include +#include +#include +#include + +#include "fw_interface.h" +#ifdef HAS_H264 +#include "h264fw_data.h" +#endif /* HAS_H264 */ +#include "hw_control.h" +#include "img_errors.h" +#include "img_msvdx_cmds.h" +#include "img_msvdx_vec_regs.h" +#ifdef VDEC_USE_PVDEC +#include "pvdec_int.h" +#include "img_pvdec_core_regs.h" +#endif +#include "img_video_bus4_mmu_regs.h" +#include "lst.h" +#include "reg_io2.h" +#include "rman_api.h" +#include "translation_api.h" +#include "vdecdd_defs.h" +#include "vdecdd_utils.h" +#include "vdecfw_share.h" +#include "vxd_int.h" +#include "vxd_props.h" + +#ifdef HAS_HEVC +#include "hevcfw_data.h" +#include "pvdec_entropy_regs.h" +#include "pvdec_vec_be_regs.h" +#endif + +#ifdef HAS_JPEG +#include "jpegfw_data.h" +#endif /* HAS_JPEG */ + +#define NO_VALUE 0 + +/* + * Discontinuity in layout of VEC_VLC_TABLE* registers. + * Address of VEC_VLC_TABLE_ADDR16 does not immediately follow + * VEC_VLC_TABLE_ADDR15, see TRM. + */ +#define VEC_VLC_TABLE_ADDR_PT1_SIZE 16 /* in 32-bit words */ +#define VEC_VLC_TABLE_ADDR_DISCONT (VEC_VLC_TABLE_ADDR_PT1_SIZE * \ + PVDECIO_VLC_IDX_ADDR_PARTS) + +/* + * now it can be done by VXD_GetCodecMode + * Imply standard from OperatingMode. + * As of now only H264 supported through the file. + */ +#define CODEC_MODE_JPEG 0x0 +#define CODEC_MODE_H264 0x1 +#define CODEC_MODE_REAL8 0x8 +#define CODEC_MODE_REAL9 0x9 + +/* + * This enum defines values of ENTDEC_BE_MODE field of VEC_ENTDEC_BE_CONTROL + * register and ENTDEC_FE_MODE field of VEC_ENTDEC_FE_CONTROL register. + */ +enum decode_mode { + /* JPEG */ + VDEC_ENTDEC_MODE_JPEG = 0x0, + /* H264 (MPEG4/AVC) */ + VDEC_ENTDEC_MODE_H264 = 0x1, + VDEC_ENTDEC_MODE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This has all that it needs to translate a Stream Unit for a picture into a + * transaction. + */ +static int translation_set_buffer(struct vdecdd_ddpict_buf *picbuf, + struct vdecfw_image_buffer *image_buffer) +{ + unsigned int i; + + for (i = 0; i < VDEC_PLANE_MAX; i++) { + image_buffer->byte_offset[i] = + (unsigned int)GET_HOST_ADDR(&picbuf->pict_buf->ddbuf_info) + + picbuf->rend_info.plane_info[i].offset; + pr_debug("%s image_buffer->byte_offset[%d] = 0x%x\n", + __func__, i, image_buffer->byte_offset[i]); + } + return IMG_SUCCESS; +} + +#ifdef HAS_HEVC +/* + * @Function translation_hevc_header + */ +static int translation_hevc_header(struct vdecdd_picture *picture, + struct dec_decpict *dec_pict, + struct hevcfw_headerdata *header_data) +{ + translation_set_buffer(dec_pict->recon_pict, &header_data->primary); + + if (dec_pict->alt_pict) + translation_set_buffer(dec_pict->alt_pict, &header_data->alternate); + + VDEC_ASSERT(picture); + VDEC_ASSERT(picture->pict_res_int); + VDEC_ASSERT(picture->pict_res_int->mb_param_buf); + header_data->temporal_outaddr = (unsigned int)GET_HOST_ADDR + (&picture->pict_res_int->mb_param_buf->ddbuf_info); + + return IMG_SUCCESS; +} +#endif + +#ifdef HAS_H264 +static int translation_h264header(struct vdecdd_picture *pspicture, + struct dec_decpict *dec_pict, + struct h264fw_header_data *psheaderdata, + struct vdec_str_configdata *psstrconfigdata) +{ + psheaderdata->two_pass_flag = dec_pict->pict_hdr_info->discontinuous_mbs; + psheaderdata->disable_mvc = psstrconfigdata->disable_mvc; + + /* + * As of now commenting the mb params base address as we are not using, + * if needed in future please un comment and make the allocation for + * pict_res_int. + */ + /* Obtain the MB parameter address from the stream unit. */ + if (pspicture->pict_res_int->mb_param_buf) { + psheaderdata->mbparams_base_address = + (unsigned int)GET_HOST_ADDR(&pspicture->pict_res_int->mb_param_buf->ddbuf_info); + psheaderdata->mbparams_size_per_plane = + pspicture->pict_res_int->mb_param_buf->ddbuf_info.buf_size / 3; + } else { + psheaderdata->mbparams_base_address = 0; + psheaderdata->mbparams_size_per_plane = 0; + } + psheaderdata->slicegroupmap_base_address = + (unsigned int)GET_HOST_ADDR(&dec_pict->cur_pict_dec_res->h264_sgm_buf); + + translation_set_buffer(dec_pict->recon_pict, &psheaderdata->primary); + + if (dec_pict->alt_pict) + translation_set_buffer(dec_pict->alt_pict, &psheaderdata->alternate); + + /* Signal whether we have PPS for the second field. */ + if (pspicture->dec_pict_aux_info.second_pps_id == BSPP_INVALID) + psheaderdata->second_pps = 0; + else + psheaderdata->second_pps = 1; + + return IMG_SUCCESS; +} +#endif /* HAS_H264 */ + +#ifdef HAS_JPEG + +static int translation_jpegheader(const struct bspp_sequ_hdr_info *seq, + const struct dec_decpict *dec_pict, + const struct bspp_pict_hdr_info *pict_hdrinfo, + struct jpegfw_header_data *header_data) +{ + unsigned int i; + + /* Output picture planes addresses */ + for (i = 0; i < seq->com_sequ_hdr_info.pixel_info.num_planes; i++) { + header_data->plane_offsets[i] = + (unsigned int)GET_HOST_ADDR(&dec_pict->recon_pict->pict_buf->ddbuf_info) + + dec_pict->recon_pict->rend_info.plane_info[i].offset; + } + + /* copy the expected SOS fields number */ + header_data->hdr_sos_count = pict_hdrinfo->sos_count; + + translation_set_buffer(dec_pict->recon_pict, &header_data->primary); + + return IMG_SUCCESS; +} +#endif /* HAS_JPEG */ +/* + * This function translates host video standard enum (VDEC_eVidStd) into + * firmware video standard enum (VDECFW_eCodecType); + */ +static int translation_get_codec(enum vdec_vid_std evidstd, + enum vdecfw_codectype *pecodec) +{ + enum vdecfw_codectype ecodec = VDEC_CODEC_NONE; + unsigned int result = IMG_ERROR_NOT_SUPPORTED; + + /* Translate from video standard to firmware codec. */ + switch (evidstd) { + #ifdef HAS_H264 + case VDEC_STD_H264: + ecodec = VDECFW_CODEC_H264; + result = IMG_SUCCESS; + break; + #endif /* HAS_H264 */ +#ifdef HAS_HEVC + case VDEC_STD_HEVC: + ecodec = VDECFW_CODEC_HEVC; + result = IMG_SUCCESS; + break; +#endif /* HAS_HEVC */ +#ifdef HAS_JPEG + case VDEC_STD_JPEG: + ecodec = VDECFW_CODEC_JPEG; + result = IMG_SUCCESS; + break; +#endif + default: + result = IMG_ERROR_NOT_SUPPORTED; + break; + } + *pecodec = ecodec; + return result; +} + +/* + * This function is used to obtain buffer for sequence header. + */ +static int translation_get_seqhdr(struct vdecdd_str_unit *psstrunit, + struct dec_decpict *psdecpict, + unsigned int *puipseqaddr) +{ + /* + * ending Sequence info only if its a First Pic of Sequence, or a Start + * of Closed GOP + */ + if (psstrunit->pict_hdr_info->first_pic_of_sequence || psstrunit->closed_gop) { + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + /* Get access to map info context */ + int result = rman_get_resource(psstrunit->seq_hdr_info->bufmap_id, + VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(result == IMG_SUCCESS); + if (result != IMG_SUCCESS) + return result; + + *puipseqaddr = GET_HOST_ADDR_OFFSET(&ddbuf_map_info->ddbuf_info, + psstrunit->seq_hdr_info->buf_offset); + } else { + *puipseqaddr = 0; + } + return IMG_SUCCESS; +} + +/* + * This function is used to obtain buffer for picture parameter set. + */ +static int translation_get_ppshdr(struct vdecdd_str_unit *psstrunit, + struct dec_decpict *psdecpict, + unsigned int *puipppsaddr) +{ + if (psstrunit->pict_hdr_info->pict_aux_data.id != BSPP_INVALID) { + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + int result; + + VDEC_ASSERT(psstrunit->pict_hdr_info->pict_aux_data.pic_data); + /* Get access to map info context */ + result = rman_get_resource(psstrunit->pict_hdr_info->pict_aux_data.bufmap_id, + VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(result == IMG_SUCCESS); + + if (result != IMG_SUCCESS) + return result; + *puipppsaddr = + GET_HOST_ADDR_OFFSET(&ddbuf_map_info->ddbuf_info, + psstrunit->pict_hdr_info->pict_aux_data.buf_offset); + } else { + *puipppsaddr = 0; + } + return IMG_SUCCESS; +} + +/* + * This function is used to obtain buffer for second picture parameter set. + */ +static int translation_getsecond_ppshdr(struct vdecdd_str_unit *psstrunit, + unsigned int *puisecond_ppshdr) +{ + if (psstrunit->pict_hdr_info->second_pict_aux_data.id != + BSPP_INVALID) { + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + int result; + void *pic_data = + psstrunit->pict_hdr_info->second_pict_aux_data.pic_data; + + VDEC_ASSERT(pic_data); + result = rman_get_resource(psstrunit->pict_hdr_info->second_pict_aux_data.bufmap_id, + VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(result == IMG_SUCCESS); + + if (result != IMG_SUCCESS) + return result; + + *puisecond_ppshdr = + GET_HOST_ADDR_OFFSET + (&ddbuf_map_info->ddbuf_info, + psstrunit->pict_hdr_info->second_pict_aux_data.buf_offset); + } else { + *puisecond_ppshdr = 0; + } + return IMG_SUCCESS; +} + +/* + * Returns address from which FW should download its shared context. + */ +static unsigned int translation_getctx_loadaddr(struct dec_decpict *psdecpict) +{ + if (psdecpict->prev_pict_dec_res) + return GET_HOST_ADDR(&psdecpict->prev_pict_dec_res->fw_ctx_buf); + + /* + * No previous context exists, using current context leads to + * problems on replay so just say to FW to use clean one. + * This is NULL as integer to avoid pointer size warnings due + * to type casting. + */ + return 0; +} + +static void translation_setup_std_header + (struct vdec_str_configdata *str_configdata, + struct dec_decpict *dec_pict, + struct vdecdd_str_unit *str_unit, unsigned int *psr_hdrsize, + struct vdecdd_picture *picture, unsigned int *picture_cmds, + enum vdecfw_parsermode *parser_mode) +{ + switch (str_configdata->vid_std) { +#ifdef HAS_H264 + case VDEC_STD_H264: + { + struct h264fw_header_data *header_data = + (struct h264fw_header_data *) + dec_pict->hdr_info->ddbuf_info->cpu_virt; + *parser_mode = str_unit->pict_hdr_info->parser_mode; + + if (str_unit->pict_hdr_info->parser_mode != + VDECFW_SCP_ONLY) { + pr_warn("VDECFW_SCP_ONLY mode supported in PVDEC FW\n"); + } + /* Reset header data. */ + memset(header_data, 0, sizeof(*(header_data))); + + /* Prepare active parameter sets. */ + translation_h264header(picture, dec_pict, header_data, str_configdata); + + /* Setup header size in the transaction. */ + *psr_hdrsize = sizeof(struct h264fw_header_data); + break; + } +#endif /* HAS_H264 */ + +#ifdef HAS_HEVC + case VDEC_STD_HEVC: + { + struct hevcfw_headerdata *header_data = + (struct hevcfw_headerdata *)dec_pict->hdr_info->ddbuf_info->cpu_virt; + *parser_mode = str_unit->pict_hdr_info->parser_mode; + + /* Reset header data. */ + memset(header_data, 0, sizeof(*header_data)); + + /* Prepare active parameter sets. */ + translation_hevc_header(picture, dec_pict, header_data); + + /* Setup header size in the transaction. */ + *psr_hdrsize = sizeof(struct hevcfw_headerdata); + break; + } +#endif +#ifdef HAS_JPEG + case VDEC_STD_JPEG: + { + struct jpegfw_header_data *header_data = + (struct jpegfw_header_data *)dec_pict->hdr_info->ddbuf_info->cpu_virt; + const struct bspp_sequ_hdr_info *seq = str_unit->seq_hdr_info; + const struct bspp_pict_hdr_info *pict_hdr_info = str_unit->pict_hdr_info; + + /* Reset header data. */ + memset(header_data, 0, sizeof(*(header_data))); + + /* Prepare active parameter sets. */ + translation_jpegheader(seq, dec_pict, pict_hdr_info, header_data); + + /* Setup header size in the transaction. */ + *psr_hdrsize = sizeof(struct jpegfw_header_data); + break; + } +#endif + default: + VDEC_ASSERT(NULL == "Unknown standard!"); + *psr_hdrsize = 0; + break; + } +} + +#define VDEC_INITIAL_DEVA_DMA_CMD_SIZE 3 +#define VDEC_SINLGE_DEVA_DMA_CMD_SIZE 2 + +#ifdef VDEC_USE_PVDEC +/* + * Creates DEVA bitstream segments command and saves is to control allocation + * buffer. + */ +static int translation_pvdec_adddma_transfers + (struct lst_t *decpic_seglist, unsigned int **dma_cmdbuf, + int cmd_bufsize, struct dec_decpict *psdecpict, int eop) +{ + /* + * DEVA's bitstream DMA command is made out of chunks with following + * layout ('+' sign is used to mark actual words in command): + * + * + Bitstream HDR, type unsigned int, consists of: + * - command id (CMD_BITSTREAM_SEGMENTS), + * - number of segments in this chunk, + * - optional CMD_BITSTREAM_SEGMENTS_MORE_FOLLOW_MASK + * + * + Bitstream total size, type unsigned int, + * represents size of all segments in all chunks + * + * Segments of following type (can repeat up to + * CMD_BITSTREAM_SEGMENTS_MINUS1_MASK + 1 times) + * + * + Bitstream segment address, type unsigned int + * + * + Bitstream segment size, type unsigned int + * + * Subsequent chunks are present when + * CMD_BITSTREAM_SEGMENTS_MORE_FOLLOW_MASK flag is set in Bitstream HDR. + */ + struct dec_decpict_seg *dec_picseg = (struct dec_decpict_seg *)lst_first(decpic_seglist); + unsigned int *cmd = *dma_cmdbuf; + unsigned int *dma_hdr = cmd; + unsigned int segcount = 0; + unsigned int bitstream_size = 0; + + /* + * Two words for DMA command header (setup later as we need to find out + * count of BS segments). + */ + cmd += CMD_BITSTREAM_HDR_DW_SIZE; + cmd_bufsize -= CMD_BITSTREAM_HDR_DW_SIZE; + if (cmd_bufsize < 0) { + pr_err("Buffer for DMA command too small.\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!dec_picseg) { + /* No segments to be send to FW: preparing fake one */ + cmd_bufsize -= VDEC_SINLGE_DEVA_DMA_CMD_SIZE; + if (cmd_bufsize < 0) { + pr_err("Buffer for DMA command too small.\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + segcount++; + + /* zeroing bitstream size and bitstream offset */ + *(cmd++) = 0; + *(cmd++) = 0; + } + + /* Loop through all bitstream segments */ + while (dec_picseg) { + if (dec_picseg->bstr_seg && (dec_picseg->bstr_seg->bstr_seg_flag + & VDECDD_BSSEG_SKIP) == 0) { + unsigned int result; + struct vdecdd_ddbuf_mapinfo *ddbuf_map_info; + + segcount++; + /* Two words for each added bitstream segment */ + cmd_bufsize -= VDEC_SINLGE_DEVA_DMA_CMD_SIZE; + if (cmd_bufsize < 0) { + pr_err("Buffer for DMA command too small.\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + /* Insert SCP/SC if needed */ + if (dec_picseg->bstr_seg->bstr_seg_flag & + VDECDD_BSSEG_INSERTSCP) { + unsigned int startcode_length = + psdecpict->start_code_bufinfo->buf_size; + + if (dec_picseg->bstr_seg->bstr_seg_flag & + VDECDD_BSSEG_INSERT_STARTCODE) { + unsigned char *start_code = + psdecpict->start_code_bufinfo->cpu_virt; + start_code[startcode_length - 1] = + dec_picseg->bstr_seg->start_code_suffix; + } else { + startcode_length -= 1; + } + + segcount++; + *(cmd++) = startcode_length; + bitstream_size += startcode_length; + + *(cmd++) = psdecpict->start_code_bufinfo->dev_virt; + + if (((segcount % + (CMD_BITSTREAM_SEGMENTS_MINUS1_MASK + 1)) == 0)) + /* + * we have reached max number of + * bitstream segments for current + * command make pui32Cmd point to next + * BS command + */ + cmd += CMD_BITSTREAM_HDR_DW_SIZE; + } + /* Get access to map info context */ + result = rman_get_resource(dec_picseg->bstr_seg->bufmap_id, + VDECDD_BUFMAP_TYPE_ID, + (void **)&ddbuf_map_info, NULL); + VDEC_ASSERT(result == IMG_SUCCESS); + if (result != IMG_SUCCESS) + return result; + + *(cmd++) = (dec_picseg->bstr_seg->data_size); + bitstream_size += dec_picseg->bstr_seg->data_size; + + *(cmd++) = ddbuf_map_info->ddbuf_info.dev_virt + + dec_picseg->bstr_seg->data_byte_offset; + + if (((segcount % + (CMD_BITSTREAM_SEGMENTS_MINUS1_MASK + 1)) == 0) && + (lst_next(dec_picseg))) + /* + * we have reached max number of bitstream + * segments for current command make pui32Cmd + * point to next BS command + */ + cmd += CMD_BITSTREAM_HDR_DW_SIZE; + } + dec_picseg = lst_next(dec_picseg); + } + + if (segcount > CMD_BITSTREAM_SEGMENTS_MAX_NUM) { + pr_err("Too many bitstream segments to transfer.\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + while (segcount > (CMD_BITSTREAM_SEGMENTS_MINUS1_MASK + 1)) { + *dma_hdr++ = CMD_BITSTREAM_SEGMENTS | + CMD_BITSTREAM_SEGMENTS_MORE_FOLLOW_MASK | + CMD_BITSTREAM_SEGMENTS_MINUS1_MASK; + *dma_hdr++ = bitstream_size; + /* + * make pui32DmaHdr point to next chunk by skipping bitstream + * Segments + */ + dma_hdr += (2 * (CMD_BITSTREAM_SEGMENTS_MINUS1_MASK + 1)); + segcount -= (CMD_BITSTREAM_SEGMENTS_MINUS1_MASK + 1); + } + *dma_hdr = eop ? CMD_BITSTREAM_EOP_MASK : 0; + *dma_hdr++ |= CMD_BITSTREAM_SEGMENTS | (segcount - 1); + *dma_hdr = bitstream_size; + + /* + * Let caller know where we finished. Pointer to location one word after + * end of our command buffer + */ + *dma_cmdbuf = cmd; + return IMG_SUCCESS; +} + +/* + * Creates DEVA control allocation buffer header. + */ +static void translation_pvdec_ctrl_setuphdr + (struct ctrl_alloc_header *ctrlalloc_hdr, + unsigned int *pic_cmds) +{ + ctrlalloc_hdr->cmd_additional_params = CMD_CTRL_ALLOC_HEADER; + ctrlalloc_hdr->ext_opmode = pic_cmds[VDECFW_CMD_EXT_OP_MODE]; + ctrlalloc_hdr->chroma_strides = + pic_cmds[VDECFW_CMD_CHROMA_ROW_STRIDE]; + ctrlalloc_hdr->alt_output_addr[0] = + pic_cmds[VDECFW_CMD_LUMA_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + ctrlalloc_hdr->alt_output_addr[1] = + pic_cmds[VDECFW_CMD_CHROMA_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + ctrlalloc_hdr->alt_output_flags = + pic_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION]; +} + +/* + * Creates DEVA VLC DMA command and saves is to control allocation buffer. + */ +static int translation_pvdecsetup_vlcdma + (struct vidio_ddbufinfo *vlctables_bufinfo, + unsigned int **dmacmd_buf, unsigned int cmdbuf_size) +{ + unsigned int cmd_dma; + unsigned int *cmd = *dmacmd_buf; + + /* Check if VLC tables fit in one DMA transfer */ + if (vlctables_bufinfo->buf_size > CMD_DMA_DMA_SIZE_MASK) { + pr_err("VLC tables won't fit into one DMA transfer!\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Check if we have enough space in control allocation buffer. */ + if (cmdbuf_size < VDEC_SINLGE_DEVA_DMA_CMD_SIZE) { + pr_err("Buffer for DMA command too small.\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Construct DMA command */ + cmd_dma = CMD_DMA | CMD_DMA_TYPE_VLC_TABLE | + vlctables_bufinfo->buf_size; + + /* Add command to control allocation */ + *cmd++ = cmd_dma; + *cmd++ = vlctables_bufinfo->dev_virt; + + /* + * Let caller know where we finished. Pointer to location one word after + * end of our command buffer + */ + *dmacmd_buf = cmd; + return IMG_SUCCESS; +} + +/* + * Creates DEVA commands for configuring VLC tables and saves them into + * control allocation buffer. + */ +static int translation_pvdecsetup_vlctables + (unsigned short vlc_index_data[][3], unsigned int num_tables, + unsigned int **ctrl_allocbuf, unsigned int ctrl_allocsize, + unsigned int msvdx_vecoffset) +{ + unsigned int i; + unsigned int word_count; + unsigned int reg_val; + unsigned int *ctrl_allochdr; + + unsigned int *ctrl_alloc = *ctrl_allocbuf; + + /* Calculate the number of words needed for VLC control allocations. */ + /* + * 3 words for control allocation headers (we are writing 3 chunks: + * addresses, widths, opcodes) + */ + unsigned int req_elems = 3 + + (ALIGN(num_tables, PVDECIO_VLC_IDX_WIDTH_PARTS) / + PVDECIO_VLC_IDX_WIDTH_PARTS) + + (ALIGN(num_tables, PVDECIO_VLC_IDX_ADDR_PARTS) / + PVDECIO_VLC_IDX_ADDR_PARTS) + + (ALIGN(num_tables, PVDECIO_VLC_IDX_OPCODE_PARTS) / + PVDECIO_VLC_IDX_OPCODE_PARTS); + + /* + * Addresses chunk has to be split in two, if number of tables exceeds + * VEC_VLC_TABLE_ADDR_DISCONT (see layout of VEC_VLC_TABLE_ADDR* + * registers in TRM) + */ + if (num_tables > VEC_VLC_TABLE_ADDR_DISCONT) + /* We need additional control allocation header */ + req_elems += 1; + + if (ctrl_allocsize < req_elems) { + pr_err("Buffer for VLC IDX commands too small.\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* + * Write VLC IDX addresses. Chunks for VEC_VLC_TABLE_ADDR[0-15] and + * VEC_VLC_TABLE_ADDR[16-18] registers. + */ + ctrl_allochdr = ctrl_alloc++; + *ctrl_allochdr = CMD_REGISTER_BLOCK | CMD_REGISTER_BLOCK_FLAG_VLC_DATA | + (MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR0_OFFSET + msvdx_vecoffset); + /* Reset the word count. */ + word_count = 0; + + /* Process VLC index table. */ + i = 0; + reg_val = 0; + while (i < num_tables) { + VDEC_ASSERT((vlc_index_data[i][PVDECIO_VLC_IDX_ADDR_ID] & + ~PVDECIO_VLC_IDX_ADDR_MASK) == 0); + /* Pack the addresses into a word. */ + reg_val |= ((vlc_index_data[i][PVDECIO_VLC_IDX_ADDR_ID] & + PVDECIO_VLC_IDX_ADDR_MASK) << + ((i % PVDECIO_VLC_IDX_ADDR_PARTS) * + PVDECIO_VLC_IDX_ADDR_SHIFT)); + + /* If we reached the end of VEC_VLC_TABLE_ADDR[0-15] area... */ + if (i == VEC_VLC_TABLE_ADDR_DISCONT) { + /* + * Finalize command header for VEC_VLC_TABLE_ADDR[0-15] + * register chunk. + */ + *ctrl_allochdr |= word_count << 16; + /* + * Reserve and preset command header for + * VEC_VLC_TABLE_ADDR[16-18] register chunk. + */ + ctrl_allochdr = ctrl_alloc++; + *ctrl_allochdr = CMD_REGISTER_BLOCK | + CMD_REGISTER_BLOCK_FLAG_VLC_DATA | + (MSVDX_VEC_CR_VEC_VLC_TABLE_ADDR16_OFFSET + + msvdx_vecoffset); + /* Reset the word count. */ + word_count = 0; + } + + /* + * If all the addresses are packed in this word or that's the + * last iteration + */ + if (((i % PVDECIO_VLC_IDX_ADDR_PARTS) == + (PVDECIO_VLC_IDX_ADDR_PARTS - 1)) || + (i == (num_tables - 1))) { + /* + * Add VLC table address to this chunk and increase + * words count. + */ + *ctrl_alloc++ = reg_val; + word_count++; + /* Reset address value. */ + reg_val = 0; + } + + i++; + } + + /* + * Finalize the current command header for VEC_VLC_TABLE_ADDR register + * chunk. + */ + *ctrl_allochdr |= word_count << 16; + + /* + * Start new commands chunk for VEC_VLC_TABLE_INITIAL_WIDTH[0-3] + * registers. + */ + + /* + * Reserve and preset command header for + * VEC_VLC_TABLE_INITIAL_WIDTH[0-3] register chunk. + */ + ctrl_allochdr = ctrl_alloc++; + *ctrl_allochdr = CMD_REGISTER_BLOCK | CMD_REGISTER_BLOCK_FLAG_VLC_DATA | + (MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_WIDTH0_OFFSET + + msvdx_vecoffset); + /* Reset the word count. */ + word_count = 0; + + /* Process VLC index table. */ + i = 0; + reg_val = 0; + + while (i < num_tables) { + VDEC_ASSERT((vlc_index_data[i][PVDECIO_VLC_IDX_WIDTH_ID] & + ~PVDECIO_VLC_IDX_WIDTH_MASK) == 0); + /* Pack the widths into a word. */ + reg_val |= ((vlc_index_data[i][PVDECIO_VLC_IDX_WIDTH_ID] & + PVDECIO_VLC_IDX_WIDTH_MASK) << + (i % PVDECIO_VLC_IDX_WIDTH_PARTS) * + PVDECIO_VLC_IDX_WIDTH_SHIFT); + + /* + * If all the widths are packed in this word or that's the last + * iteration. + */ + if (((i % PVDECIO_VLC_IDX_WIDTH_PARTS) == + (PVDECIO_VLC_IDX_WIDTH_PARTS - 1)) || + (i == (num_tables - 1))) { + /* + * Add VLC table width to this chunk and increase words + * count. + */ + *ctrl_alloc++ = reg_val; + word_count++; + /* Reset width value. */ + reg_val = 0; + } + i++; + } + + /* + * Finalize command header for VEC_VLC_TABLE_INITIAL_WIDTH[0-3] register + * chunk. + */ + *ctrl_allochdr |= word_count << 16; + + /* + * Start new commands chunk for VEC_VLC_TABLE_INITIAL_OPCODE[0-2] + * registers. + * Reserve and preset command header for + * VEC_VLC_TABLE_INITIAL_OPCODE[0-2] register chunk + */ + ctrl_allochdr = ctrl_alloc++; + *ctrl_allochdr = CMD_REGISTER_BLOCK | CMD_REGISTER_BLOCK_FLAG_VLC_DATA | + (MSVDX_VEC_CR_VEC_VLC_TABLE_INITIAL_OPCODE0_OFFSET + + msvdx_vecoffset); + /* Reset the word count. */ + word_count = 0; + + /* Process VLC index table. */ + i = 0; + reg_val = 0; + + while (i < num_tables) { + VDEC_ASSERT((vlc_index_data[i][PVDECIO_VLC_IDX_OPCODE_ID] & + ~PVDECIO_VLC_IDX_OPCODE_MASK) == 0); + /* Pack the opcodes into a word. */ + reg_val |= ((vlc_index_data[i][PVDECIO_VLC_IDX_OPCODE_ID] & + PVDECIO_VLC_IDX_OPCODE_MASK) << + (i % PVDECIO_VLC_IDX_OPCODE_PARTS) * + PVDECIO_VLC_IDX_OPCODE_SHIFT); + + /* + * If all the opcodes are packed in this word or that's the last + * iteration. + */ + if (((i % PVDECIO_VLC_IDX_OPCODE_PARTS) == + (PVDECIO_VLC_IDX_OPCODE_PARTS - 1)) || + (i == (num_tables - 1))) { + /* + * Add VLC table opcodes to this chunk and increase + * words count. + */ + *ctrl_alloc++ = reg_val; + word_count++; + /* Reset width value. */ + reg_val = 0; + } + i++; + } + + /* + * Finalize command header for VEC_VLC_TABLE_INITIAL_OPCODE[0-2] + * register chunk. + */ + *ctrl_allochdr |= word_count << 16; + + /* Update caller with current location of control allocation pointer */ + *ctrl_allocbuf = ctrl_alloc; + return IMG_SUCCESS; +} + +/* + * fills in a rendec command chunk in the command buffer. + */ +static void fill_rendec_chunk(int num, ...) +{ + va_list valist; + unsigned int i, j = 0; + unsigned int chunk_word_count = 0; + unsigned int used_word_count = 0; + int aux_array_size = 0; + unsigned int *pic_cmds; + unsigned int **ctrl_allocbuf; + unsigned int ctrl_allocsize; + unsigned int vdmc_cmd_offset; + unsigned int offset; + unsigned int *buf; + /* 5 is the fixed arguments passed to fill_rendec_chunk function */ + enum vdecfw_picture_cmds *aux_array = kmalloc((sizeof(unsigned int) * + (num - 5)), GFP_KERNEL); + if (!aux_array) + return; + + /* initialize valist for num number of arguments */ + va_start(valist, num); + + pic_cmds = va_arg(valist, unsigned int *); + ctrl_allocbuf = va_arg(valist, unsigned int **); + ctrl_allocsize = va_arg(valist, unsigned int); + vdmc_cmd_offset = va_arg(valist, unsigned int); + offset = va_arg(valist, unsigned int); + buf = *ctrl_allocbuf; + + aux_array_size = (sizeof(unsigned int) * (num - 5)); + /* + * access all the arguments assigned to valist, we have already + * read till 5 + */ + for (i = 6, j = 0; i <= num; i++, j++) + aux_array[j] = (enum vdecfw_picture_cmds)va_arg(valist, int); + + /* clean memory reserved for valist */ + va_end(valist); + chunk_word_count = aux_array_size / + sizeof(enum vdecfw_picture_cmds); + if ((chunk_word_count + 1) > (ctrl_allocsize - used_word_count)) { + kfree(aux_array); + return; + } + if ((chunk_word_count & ~(CMD_RENDEC_WORD_COUNT_MASK >> + CMD_RENDEC_WORD_COUNT_SHIFT)) != 0) { + kfree(aux_array); + return; + } + used_word_count += chunk_word_count + 1; + *buf++ = CMD_RENDEC_BLOCK | (chunk_word_count << 16) | + (vdmc_cmd_offset + offset); + + for (i = 0; i < chunk_word_count; i++) + *buf++ = pic_cmds[aux_array[i]]; + + *ctrl_allocbuf = buf; + /* free the memory */ + kfree(aux_array); +} + +/* + * Creates DEVA commands for configuring rendec and writes them into control + * allocation buffer. + */ +static void translation_pvdec_setup_commands(unsigned int *pic_cmds, + unsigned int **ctrl_allocbuf, + unsigned int ctrl_allocsize, + unsigned int vdmc_cmd_offset) +{ + unsigned int codec_mode; + + codec_mode = REGIO_READ_FIELD(pic_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, CODEC_MODE); + + if (codec_mode != CODEC_MODE_H264) + /* chunk with cache settings at 0x01C */ + /* + * here first argument 6 says there are 6 number of arguments + * being passed to fill_rendec_chunk function. + */ + fill_rendec_chunk(6, pic_cmds, ctrl_allocbuf, ctrl_allocsize, + vdmc_cmd_offset, + MSVDX_CMDS_MC_CACHE_CONFIGURATION_OFFSET, + VDECFW_CMD_MC_CACHE_CONFIGURATION); + + /* chunk with extended row stride at 0x03C */ + /* + * here first argument 6 says there are 6 number of arguments + * being passed to fill_rendec_chunk function. + */ + fill_rendec_chunk(6, pic_cmds, ctrl_allocbuf, ctrl_allocsize, + vdmc_cmd_offset, + MSVDX_CMDS_EXTENDED_ROW_STRIDE_OFFSET, + VDECFW_CMD_EXTENDED_ROW_STRIDE); + + /* chunk with alternative output control at 0x1B4 */ + /* + * here first argument 6 says there are 6 number of arguments + * being passed to fill_rendec_chunk function. + */ + fill_rendec_chunk(6, pic_cmds, ctrl_allocbuf, ctrl_allocsize, + vdmc_cmd_offset, + MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_OFFSET, + VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL); + + /* scaling chunks */ + if (pic_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE]) { + if (codec_mode != CODEC_MODE_REAL8 && codec_mode != CODEC_MODE_REAL9) { + /* + * chunk with scale display size, scale H/V control at + * 0x0050 + */ + /* + * here first argument 8 says there are 8 number of + * arguments being passed to fill_rendec_chunk function. + */ + fill_rendec_chunk(8, pic_cmds, ctrl_allocbuf, + ctrl_allocsize, vdmc_cmd_offset, + MSVDX_CMDS_SCALED_DISPLAY_SIZE_OFFSET, + VDECFW_CMD_SCALED_DISPLAY_SIZE, + VDECFW_CMD_HORIZONTAL_SCALE_CONTROL, + VDECFW_CMD_VERTICAL_SCALE_CONTROL); + + /* chunk with luma/chorma H/V coeffs at 0x0060 */ + /* + * here first argument 21 says there are 21 number of + * arguments being passed to fill_rendec_chunk function. + */ + fill_rendec_chunk(21, pic_cmds, ctrl_allocbuf, + ctrl_allocsize, vdmc_cmd_offset, + MSVDX_CMDS_HORIZONTAL_LUMA_COEFFICIENTS_OFFSET, + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_0, + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_1, + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_2, + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_3, + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_0, + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_1, + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_2, + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_3, + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_0, + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_1, + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_2, + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_3, + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_0, + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_1, + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_2, + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_3); + + /* + * chunk with scale output size, scale H/V chroma at + * 0x01B8 + */ + /* + * here first argument 8 says there are 8 number of + * arguments being passed to fill_rendec_chunk function. + */ + fill_rendec_chunk(8, pic_cmds, ctrl_allocbuf, + ctrl_allocsize, vdmc_cmd_offset, + MSVDX_CMDS_SCALE_OUTPUT_SIZE_OFFSET, + VDECFW_CMD_SCALE_OUTPUT_SIZE, + VDECFW_CMD_SCALE_HORIZONTAL_CHROMA, + VDECFW_CMD_SCALE_VERTICAL_CHROMA); + } + } +} + +#ifdef HAS_HEVC +/* + * @Function translation_pvdec_setup_pvdec_commands + */ +static int translation_pvdec_setup_pvdec_commands(struct vdecdd_picture *picture, + struct dec_decpict *dec_pict, + struct vdecdd_str_unit *str_unit, + struct decoder_regsoffsets *regs_offsets, + unsigned int **ctrl_allocbuf, + unsigned int ctrl_alloc_size, + unsigned int *mem_to_reg_host_part, + unsigned int *pict_cmds) +{ + const unsigned int genc_buf_cnt = 4; + /* We have two chunks: for GENC buffers addresses and sizes*/ + const unsigned int genc_conf_items = 2; + const unsigned int pipe = 0xf << 16; /* Instruct H/W to write to current pipe */ + /* We need to configure address and size of each GENC buffer */ + const unsigned int genc_words_cnt = genc_buf_cnt * genc_conf_items; + struct vdecdd_ddbuf_mapinfo **genc_buffers = + picture->pict_res_int->seq_resint->genc_buffers; + unsigned int memto_reg_used; /* in bytes */ + unsigned int i; + unsigned int *ctrl_alloc = *ctrl_allocbuf; + unsigned int *mem_to_reg = (unsigned int *)dec_pict->pvdec_info->ddbuf_info->cpu_virt; + unsigned int reg = 0; + + if (ctrl_alloc_size < genc_words_cnt + genc_conf_items) { + pr_err("Buffer for GENC config too small."); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Insert command header for GENC buffers sizes */ + *ctrl_alloc++ = CMD_REGISTER_BLOCK | (genc_buf_cnt << 16) | + (PVDEC_ENTROPY_CR_GENC_BUFFER_SIZE_OFFSET + regs_offsets->entropy_offset); + for (i = 0; i < genc_buf_cnt; i++) + *ctrl_alloc++ = genc_buffers[i]->ddbuf_info.buf_size; + + /* Insert command header for GENC buffers addresses */ + *ctrl_alloc++ = CMD_REGISTER_BLOCK | (genc_buf_cnt << 16) | + (PVDEC_ENTROPY_CR_GENC_BUFFER_BASE_ADDRESS_OFFSET + regs_offsets->entropy_offset); + for (i = 0; i < genc_buf_cnt; i++) + *ctrl_alloc++ = genc_buffers[i]->ddbuf_info.dev_virt; + + /* Insert GENC fragment buffer address */ + *ctrl_alloc++ = CMD_REGISTER_BLOCK | (1 << 16) | + (PVDEC_ENTROPY_CR_GENC_FRAGMENT_BASE_ADDRESS_OFFSET + regs_offsets->entropy_offset); + *ctrl_alloc++ = picture->pict_res_int->genc_fragment_buf->ddbuf_info.dev_virt; + + /* Return current location in control allocation buffer to caller */ + *ctrl_allocbuf = ctrl_alloc; + + reg = 0; + REGIO_WRITE_FIELD_LITE + (reg, + MSVDX_CMDS, PVDEC_DISPLAY_PICTURE_SIZE, PVDEC_DISPLAY_PICTURE_WIDTH_MIN1, + str_unit->pict_hdr_info->coded_frame_size.width - 1, unsigned int); + REGIO_WRITE_FIELD_LITE + (reg, + MSVDX_CMDS, PVDEC_DISPLAY_PICTURE_SIZE, PVDEC_DISPLAY_PICTURE_HEIGHT_MIN1, + str_unit->pict_hdr_info->coded_frame_size.height - 1, unsigned int); + + /* + * Pvdec operating mode needs to be submitted before any other commands. + * This will be set in FW. Make sure it's the first command in Mem2Reg buffer. + */ + VDEC_ASSERT((unsigned int *)dec_pict->pvdec_info->ddbuf_info->cpu_virt == mem_to_reg); + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_PVDEC_OPERATING_MODE_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = 0x0; /* has to be updated in the F/W */ + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_MC_CACHE_CONFIGURATION_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = 0x0; /* has to be updated in the F/W */ + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_PVDEC_DISPLAY_PICTURE_SIZE_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = reg; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_PVDEC_CODED_PICTURE_SIZE_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = reg; + + /* scaling configuration */ + if (pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE]) { + *mem_to_reg++ = pipe | + (MSVDX_CMDS_PVDEC_SCALED_DISPLAY_SIZE_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_HORIZONTAL_SCALE_CONTROL_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_SCALE_CONTROL]; + *mem_to_reg++ = pipe | + (MSVDX_CMDS_VERTICAL_SCALE_CONTROL_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_SCALE_CONTROL]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_SCALE_OUTPUT_SIZE_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_SCALE_OUTPUT_SIZE]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_SCALE_HORIZONTAL_CHROMA_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_SCALE_HORIZONTAL_CHROMA]; + *mem_to_reg++ = pipe | + (MSVDX_CMDS_SCALE_VERTICAL_CHROMA_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_SCALE_VERTICAL_CHROMA]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_HORIZONTAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_0]; + *mem_to_reg++ = pipe | + (4 + MSVDX_CMDS_HORIZONTAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_1]; + *mem_to_reg++ = pipe | + (8 + MSVDX_CMDS_HORIZONTAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_2]; + *mem_to_reg++ = pipe | + (12 + MSVDX_CMDS_HORIZONTAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_3]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_VERTICAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_0]; + *mem_to_reg++ = pipe | + (4 + MSVDX_CMDS_VERTICAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_1]; + *mem_to_reg++ = pipe | + (8 + MSVDX_CMDS_VERTICAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_2]; + *mem_to_reg++ = pipe | + (12 + MSVDX_CMDS_VERTICAL_LUMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_3]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_HORIZONTAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_0]; + *mem_to_reg++ = pipe | + (4 + MSVDX_CMDS_HORIZONTAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_1]; + *mem_to_reg++ = pipe | + (8 + MSVDX_CMDS_HORIZONTAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_2]; + *mem_to_reg++ = pipe | + (12 + MSVDX_CMDS_HORIZONTAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_3]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_VERTICAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_0]; + *mem_to_reg++ = pipe | + (4 + MSVDX_CMDS_VERTICAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_1]; + *mem_to_reg++ = pipe | + (8 + MSVDX_CMDS_VERTICAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_2]; + *mem_to_reg++ = pipe | + (12 + MSVDX_CMDS_VERTICAL_CHROMA_COEFFICIENTS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_3]; + } + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_EXTENDED_ROW_STRIDE_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_EXTENDED_ROW_STRIDE]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_ALTERNATIVE_OUTPUT_CONTROL_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_CHROMA_ROW_STRIDE_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_CHROMA_ROW_STRIDE]; + + /* Setup MEM_TO_REG buffer */ + for (i = 0; i < genc_buf_cnt; i++) { + *mem_to_reg++ = pipe | (PVDEC_VEC_BE_CR_GENC_BUFFER_SIZE_OFFSET + + regs_offsets->vec_be_regs_offset + i * sizeof(unsigned int)); + *mem_to_reg++ = genc_buffers[i]->ddbuf_info.buf_size; + *mem_to_reg++ = pipe | (PVDEC_VEC_BE_CR_GENC_BUFFER_BASE_ADDRESS_OFFSET + + regs_offsets->vec_be_regs_offset + i * sizeof(unsigned int)); + *mem_to_reg++ = genc_buffers[i]->ddbuf_info.dev_virt; + } + + *mem_to_reg++ = pipe | + (PVDEC_VEC_BE_CR_GENC_FRAGMENT_BASE_ADDRESS_OFFSET + + regs_offsets->vec_be_regs_offset); + *mem_to_reg++ = picture->pict_res_int->genc_fragment_buf->ddbuf_info.dev_virt; + + *mem_to_reg++ = pipe | + (PVDEC_VEC_BE_CR_ABOVE_PARAM_BASE_ADDRESS_OFFSET + + regs_offsets->vec_be_regs_offset); + + *mem_to_reg++ = dec_pict->pvdec_info->ddbuf_info->dev_virt + + MEM_TO_REG_BUF_SIZE + SLICE_PARAMS_BUF_SIZE; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS]; + + /* alternative picture configuration */ + if (dec_pict->alt_pict) { + *mem_to_reg++ = pipe | + (MSVDX_CMDS_VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_LUMA_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_VC1_CHROMA_RANGE_MAPPING_BASE_ADDRESS_OFFSET + + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_CHROMA_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + } + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_AUX_LINE_BUFFER_BASE_ADDRESS_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_AUX_LINE_BUFFER_BASE_ADDRESS]; + + *mem_to_reg++ = pipe | + (MSVDX_CMDS_INTRA_BUFFER_BASE_ADDRESS_OFFSET + regs_offsets->vdmc_cmd_offset); + *mem_to_reg++ = pict_cmds[VDECFW_CMD_INTRA_BUFFER_BASE_ADDRESS]; + + /* Make sure we fit in buffer */ + memto_reg_used = (unsigned long)mem_to_reg - + (unsigned long)dec_pict->pvdec_info->ddbuf_info->cpu_virt; + + VDEC_ASSERT(memto_reg_used < MEM_TO_REG_BUF_SIZE); + + *mem_to_reg_host_part = memto_reg_used / sizeof(unsigned int); + + return IMG_SUCCESS; +} +#endif + +/* + * Creates DEVA commands for configuring rendec and writes them into control + * allocation buffer. + */ +static int translation_pvdecsetup_vdecext + (struct vdec_ext_cmd *vdec_ext, + struct dec_decpict *dec_pict, unsigned int *pic_cmds, + struct vdecdd_str_unit *str_unit, enum vdec_vid_std vid_std, + enum vdecfw_parsermode parser_mode) +{ + int result; + unsigned int trans_id = dec_pict->transaction_id; + + VDEC_ASSERT(dec_pict->recon_pict); + + vdec_ext->cmd = CMD_VDEC_EXT; + vdec_ext->trans_id = trans_id; + + result = translation_get_seqhdr(str_unit, dec_pict, &vdec_ext->seq_addr); + VDEC_ASSERT(result == IMG_SUCCESS); + if (result != IMG_SUCCESS) + return result; + + result = translation_get_ppshdr(str_unit, dec_pict, &vdec_ext->pps_addr); + VDEC_ASSERT(result == IMG_SUCCESS); + if (result != IMG_SUCCESS) + return result; + + result = translation_getsecond_ppshdr(str_unit, &vdec_ext->pps_2addr); + if (result != IMG_SUCCESS) + return result; + + vdec_ext->hdr_addr = GET_HOST_ADDR(dec_pict->hdr_info->ddbuf_info); + + vdec_ext->ctx_load_addr = translation_getctx_loadaddr(dec_pict); + vdec_ext->ctx_save_addr = GET_HOST_ADDR(&dec_pict->cur_pict_dec_res->fw_ctx_buf); + vdec_ext->buf_ctrl_addr = GET_HOST_ADDR(&dec_pict->pict_ref_res->fw_ctrlbuf); + if (dec_pict->prev_pict_dec_res) { + /* + * Copy the previous firmware context to the current one in case + * picture management fails in firmware. + */ + memcpy(dec_pict->cur_pict_dec_res->fw_ctx_buf.cpu_virt, + dec_pict->prev_pict_dec_res->fw_ctx_buf.cpu_virt, + dec_pict->prev_pict_dec_res->fw_ctx_buf.buf_size); + } + + vdec_ext->last_luma_recon = + pic_cmds[VDECFW_CMD_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS]; + vdec_ext->last_chroma_recon = + pic_cmds[VDECFW_CMD_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS]; + + vdec_ext->luma_err_base = + pic_cmds[VDECFW_CMD_LUMA_ERROR_PICTURE_BASE_ADDRESS]; + vdec_ext->chroma_err_base = + pic_cmds[VDECFW_CMD_CHROMA_ERROR_PICTURE_BASE_ADDRESS]; + + vdec_ext->scaled_display_size = + pic_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE]; + vdec_ext->horz_scale_control = + pic_cmds[VDECFW_CMD_HORIZONTAL_SCALE_CONTROL]; + vdec_ext->vert_scale_control = + pic_cmds[VDECFW_CMD_VERTICAL_SCALE_CONTROL]; + vdec_ext->scale_output_size = pic_cmds[VDECFW_CMD_SCALE_OUTPUT_SIZE]; + + vdec_ext->intra_buf_base_addr = + pic_cmds[VDECFW_CMD_INTRA_BUFFER_BASE_ADDRESS]; + vdec_ext->intra_buf_size_per_pipe = + pic_cmds[VDECFW_CMD_INTRA_BUFFER_SIZE_PER_PIPE]; + vdec_ext->intra_buf_size_per_plane = + pic_cmds[VDECFW_CMD_INTRA_BUFFER_PLANE_SIZE]; + vdec_ext->aux_line_buffer_base_addr = + pic_cmds[VDECFW_CMD_AUX_LINE_BUFFER_BASE_ADDRESS]; + vdec_ext->aux_line_buf_size_per_pipe = + pic_cmds[VDECFW_CMD_AUX_LINE_BUFFER_SIZE_PER_PIPE]; + vdec_ext->alt_output_pict_rotation = + pic_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION]; + vdec_ext->chroma2reconstructed_addr = + pic_cmds[VDECFW_CMD_CHROMA2_RECONSTRUCTED_PICTURE_BASE_ADDRESS]; + vdec_ext->luma_alt_addr = + pic_cmds[VDECFW_CMD_LUMA_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + vdec_ext->chroma_alt_addr = + pic_cmds[VDECFW_CMD_CHROMA_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + vdec_ext->chroma2alt_addr = + pic_cmds[VDECFW_CMD_CHROMA2_ALTERNATIVE_PICTURE_BASE_ADDRESS]; + + if (vid_std == VDEC_STD_VC1) { + struct vidio_ddbufinfo *vlc_idx_tables_bufinfo = + dec_pict->vlc_idx_tables_bufinfo; + struct vidio_ddbufinfo *vlc_tables_bufinfo = + dec_pict->vlc_tables_bufinfo; + + vdec_ext->vlc_idx_table_size = vlc_idx_tables_bufinfo->buf_size; + vdec_ext->vlc_idx_table_addr = vlc_idx_tables_bufinfo->buf_size; + vdec_ext->vlc_tables_size = vlc_tables_bufinfo->buf_size; + vdec_ext->vlc_tables_size = vlc_tables_bufinfo->buf_size; + } else { + vdec_ext->vlc_idx_table_size = 0; + vdec_ext->vlc_idx_table_addr = 0; + vdec_ext->vlc_tables_size = 0; + vdec_ext->vlc_tables_size = 0; + } + + vdec_ext->display_picture_size = pic_cmds[VDECFW_CMD_DISPLAY_PICTURE]; + vdec_ext->parser_mode = parser_mode; + + /* miscellaneous flags */ + vdec_ext->is_chromainterleaved = + REGIO_READ_FIELD(pic_cmds[VDECFW_CMD_OPERATING_MODE], MSVDX_CMDS, OPERATING_MODE, + CHROMA_INTERLEAVED); + vdec_ext->is_discontinuousmbs = + dec_pict->pict_hdr_info->discontinuous_mbs; + +#ifdef HAS_HEVC + if (dec_pict->pvdec_info) { + vdec_ext->mem_to_reg_addr = dec_pict->pvdec_info->ddbuf_info->dev_virt; + vdec_ext->slice_params_addr = dec_pict->pvdec_info->ddbuf_info->dev_virt + + MEM_TO_REG_BUF_SIZE; + vdec_ext->slice_params_size = SLICE_PARAMS_BUF_SIZE; + } + if (vid_std == VDEC_STD_HEVC) { + struct vdecdd_picture *picture = (struct vdecdd_picture *)str_unit->dd_pict_data; + + VDEC_ASSERT(picture); + /* 10-bit packed output format indicator */ + vdec_ext->is_packedformat = picture->op_config.pixel_info.mem_pkg == + PIXEL_BIT10_MP ? 1 : 0; + } +#endif + return IMG_SUCCESS; +} + +/* + * NOTE : + * translation_configure_tiling is not supported as of now. + */ +int translation_ctrl_alloc_prepare(struct vdec_str_configdata *pstr_config_data, + struct vdecdd_str_unit *str_unit, + struct dec_decpict *dec_pict, + const struct vxd_coreprops *core_props, + struct decoder_regsoffsets *regs_offset) +{ + int result; + unsigned int *cmd_buf; + unsigned int hdr_size = 0; + unsigned int pict_cmds[VDECFW_CMD_MAX]; + enum vdecfw_codectype codec; + struct vxd_buffers buffers; + struct vdec_ext_cmd *vdec_ext; + enum vdecfw_parsermode parser_mode = VDECFW_SCP_ONLY; + struct vidio_ddbufinfo *batch_msgbuf_info = + dec_pict->batch_msginfo->ddbuf_info; + struct lst_t *decpic_seg_list = &dec_pict->dec_pict_seg_list; + unsigned int memto_reg_host_part = 0; + + unsigned long ctrl_alloc = (unsigned long)batch_msgbuf_info->cpu_virt; + unsigned long ctrl_alloc_end = ctrl_alloc + batch_msgbuf_info->buf_size; + + struct vdecdd_picture *picture = + (struct vdecdd_picture *)str_unit->dd_pict_data; + + memset(pict_cmds, 0, sizeof(pict_cmds)); + memset(&buffers, 0, sizeof(buffers)); + + VDEC_ASSERT(batch_msgbuf_info->buf_size >= CTRL_ALLOC_MAX_SEGMENT_SIZE); + memset(batch_msgbuf_info->cpu_virt, 0, batch_msgbuf_info->buf_size); + + /* Construct transaction based on new picture. */ + VDEC_ASSERT(str_unit->str_unit_type == VDECDD_STRUNIT_PICTURE_START); + + /* Obtain picture data. */ + picture = (struct vdecdd_picture *)str_unit->dd_pict_data; + dec_pict->recon_pict = &picture->disp_pict_buf; + + result = translation_get_codec(pstr_config_data->vid_std, &codec); + if (result != IMG_SUCCESS) + return result; + + translation_setup_std_header(pstr_config_data, dec_pict, str_unit, &hdr_size, picture, + pict_cmds, &parser_mode); + + buffers.recon_pict = dec_pict->recon_pict; + buffers.alt_pict = dec_pict->alt_pict; + +#ifdef HAS_HEVC + /* Set pipe offsets to device buffers */ + if (pstr_config_data->vid_std == VDEC_STD_HEVC) { + /* FW in multipipe requires this buffers to be allocated per stream */ + if (picture->pict_res_int && picture->pict_res_int->seq_resint && + picture->pict_res_int->seq_resint->intra_buffer && + picture->pict_res_int->seq_resint->aux_buffer) { + buffers.intra_bufinfo = + &picture->pict_res_int->seq_resint->intra_buffer->ddbuf_info; + buffers.auxline_bufinfo = + &picture->pict_res_int->seq_resint->aux_buffer->ddbuf_info; + } + } else { + buffers.intra_bufinfo = dec_pict->intra_bufinfo; + buffers.auxline_bufinfo = dec_pict->auxline_bufinfo; + } + + if (buffers.intra_bufinfo) + buffers.intra_bufsize_per_pipe = buffers.intra_bufinfo->buf_size / + core_props->num_pixel_pipes; + if (buffers.auxline_bufinfo) + buffers.auxline_bufsize_per_pipe = buffers.auxline_bufinfo->buf_size / + core_props->num_pixel_pipes; +#endif + +#ifdef ERROR_CONCEALMENT + if (picture->pict_res_int && picture->pict_res_int->seq_resint) + if (picture->pict_res_int->seq_resint->err_pict_buf) + buffers.err_pict_bufinfo = + &picture->pict_res_int->seq_resint->err_pict_buf->ddbuf_info; +#endif + + /* + * Prepare Reconstructed Picture Configuration + * Note: we are obtaining values of registers prepared basing on header + * files generated from MSVDX *dev files. + * That's allowed, as layout of registers: MSVDX_CMDS_OPERATING_MODE, + * MSVDX_CMDS_EXTENDED_ROW_STRIDE, + * MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + * MSVDX_CMDS_CHROMA_ROW_STRIDE is the same for both MSVDX and PVDEC. + */ + vxd_set_reconpictcmds(str_unit, pstr_config_data, &picture->op_config, core_props, + &buffers, pict_cmds); + + /* Alternative Picture Configuration */ + if (dec_pict->alt_pict) { + dec_pict->twopass = picture->op_config.force_oold; + buffers.btwopass = dec_pict->twopass; + /* + * Alternative Picture Configuration + * Note: we are obtaining values of registers prepared basing + * on header files generated from MSVDX *dev files. + * That's allowed, as layout of registers: + * MSVDX_CMDS_OPERATING_MODE, MSVDX_CMDS_EXTENDED_ROW_STRIDE, + * MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + * MSVDX_CMDS_CHROMA_ROW_STRIDE is the same for both MSVDX and + * PVDEC. + */ + /* + * Configure second buffer for out-of-loop processing + * (e.g. scaling etc.). + */ + vxd_set_altpictcmds(str_unit, pstr_config_data, &picture->op_config, core_props, + &buffers, pict_cmds); + } + + /* + * Setup initial simple bitstream configuration to be used by parser + * task + */ + cmd_buf = (unsigned int *)ctrl_alloc; + result = translation_pvdec_adddma_transfers + (decpic_seg_list, &cmd_buf, + (ctrl_alloc_end - (unsigned long)cmd_buf) / sizeof(unsigned int), + dec_pict, str_unit->eop); + if (result != IMG_SUCCESS) + return result; + + if ((unsigned long)(cmd_buf + (sizeof(struct ctrl_alloc_header) + + sizeof(struct vdec_ext_cmd)) / sizeof(unsigned int)) >= + ctrl_alloc_end) + return IMG_ERROR_INVALID_PARAMETERS; + + /* + * Setup regular control allocation message. Start with control + * allocation header + */ + translation_pvdec_ctrl_setuphdr((struct ctrl_alloc_header *)cmd_buf, pict_cmds); + /* Setup additional params for VP8 */ + cmd_buf += sizeof(struct ctrl_alloc_header) / sizeof(unsigned int); + + /* Reserve space for VDEC extension command and fill it */ + vdec_ext = (struct vdec_ext_cmd *)cmd_buf; + cmd_buf += sizeof(struct vdec_ext_cmd) / sizeof(unsigned int); + + result = translation_pvdecsetup_vdecext(vdec_ext, dec_pict, pict_cmds, + str_unit, + pstr_config_data->vid_std, + parser_mode); + if (result != IMG_SUCCESS) + return result; + + vdec_ext->hdr_size = hdr_size; + + /* Add VLC tables to control allocation, skip when VC1 */ + if (pstr_config_data->vid_std != VDEC_STD_VC1 && + dec_pict->vlc_idx_tables_bufinfo && + dec_pict->vlc_idx_tables_bufinfo->cpu_virt) { + unsigned short *vlc_idx_tables = (unsigned short *) + dec_pict->vlc_idx_tables_bufinfo->cpu_virt; + /* + * Get count of elements in VLC idx table. Each element is made + * of 3 IMG_UINT16, see e.g. mpeg2_idx.c + */ + unsigned int vlc_idx_count = + dec_pict->vlc_idx_tables_bufinfo->buf_size / + (3 * sizeof(unsigned short)); + + /* Add command to DMA VLC */ + result = translation_pvdecsetup_vlcdma + (dec_pict->vlc_tables_bufinfo, &cmd_buf, + (ctrl_alloc_end - (unsigned long)cmd_buf) / sizeof(unsigned int)); + + if (result != IMG_SUCCESS) + return result; + + /* Add command to configure VLC tables */ + result = translation_pvdecsetup_vlctables + ((unsigned short (*)[3])vlc_idx_tables, vlc_idx_count, &cmd_buf, + (ctrl_alloc_end - (unsigned long)cmd_buf) / sizeof(unsigned int), + regs_offset->vec_offset); + + if (result != IMG_SUCCESS) + return result; + } + + /* Setup commands for standards other than HEVC */ + if (pstr_config_data->vid_std != VDEC_STD_HEVC) { + translation_pvdec_setup_commands + (pict_cmds, &cmd_buf, + (ctrl_alloc_end - (unsigned long)cmd_buf) / sizeof(unsigned int), + regs_offset->vdmc_cmd_offset); + } + + /* Setup commands for HEVC */ + vdec_ext->mem_to_reg_size = 0; + +#ifdef HAS_HEVC + if (pstr_config_data->vid_std == VDEC_STD_HEVC) { + result = translation_pvdec_setup_pvdec_commands + (picture, dec_pict, str_unit, + regs_offset, &cmd_buf, + (ctrl_alloc_end - (unsigned long)cmd_buf) / sizeof(unsigned int), + &memto_reg_host_part, pict_cmds); + if (result != IMG_SUCCESS) { + pr_err("Failed to setup VDMC & VDEB firmware commands."); + return result; + } + + /* Set size of MemToReg buffer in VDEC extension command */ + VDEC_ASSERT(MEM_TO_REG_BUF_SIZE < + (MEM2REG_SIZE_BUF_TOTAL_MASK >> MEM2REG_SIZE_BUF_TOTAL_SHIFT)); + VDEC_ASSERT(memto_reg_host_part < + (MEM2REG_SIZE_HOST_PART_MASK >> MEM2REG_SIZE_HOST_PART_SHIFT)); + + vdec_ext->mem_to_reg_size = (MEM_TO_REG_BUF_SIZE << MEM2REG_SIZE_BUF_TOTAL_SHIFT) | + (memto_reg_host_part << MEM2REG_SIZE_HOST_PART_SHIFT); + + dec_pict->genc_id = picture->pict_res_int->seq_resint->genc_buf_id; + dec_pict->genc_bufs = picture->pict_res_int->seq_resint->genc_buffers; + } +#endif + /* Finally mark end of commands */ + *(cmd_buf++) = CMD_COMPLETION; + + /* Print message for debugging */ + { + int i; + + for (i = 0; i < ((unsigned long)cmd_buf - ctrl_alloc) / sizeof(unsigned int); i++) + pr_debug("ctrl_alloc_buf[%d] == %08x\n", i, + ((unsigned int *)ctrl_alloc)[i]); + } + /* Transfer control allocation command to device memory */ + dec_pict->ctrl_alloc_bytes = ((unsigned long)cmd_buf - ctrl_alloc); + dec_pict->ctrl_alloc_offset = dec_pict->ctrl_alloc_bytes; + dec_pict->operating_op = pict_cmds[VDECFW_CMD_OPERATING_MODE]; + + /* + * NOTE : Nothing related to tiling will be used. + * result = translation_ConfigureTiling(psStrUnit, psDecPict, + * psCoreProps); + */ + + return result; +}; + +int translation_fragment_prepare(struct dec_decpict *dec_pict, + struct lst_t *decpic_seg_list, int eop, + struct dec_pict_fragment *pict_fragement) +{ + int result; + unsigned int *cmd_buf; + struct vidio_ddbufinfo *batchmsg_bufinfo; + unsigned long ctrl_alloc; + unsigned long ctrl_alloc_end; + + if (!dec_pict || !dec_pict->batch_msginfo || + !decpic_seg_list || !pict_fragement) + return IMG_ERROR_INVALID_PARAMETERS; + + batchmsg_bufinfo = dec_pict->batch_msginfo->ddbuf_info; + + ctrl_alloc = (unsigned long)batchmsg_bufinfo->cpu_virt + + dec_pict->ctrl_alloc_offset; + ctrl_alloc_end = (unsigned long)batchmsg_bufinfo->cpu_virt + + batchmsg_bufinfo->buf_size; + + /* + * Setup initial simple bitstream configuration to be used by parser + * task + */ + cmd_buf = (unsigned int *)ctrl_alloc; + result = translation_pvdec_adddma_transfers + (decpic_seg_list, &cmd_buf, + (ctrl_alloc_end - (unsigned long)cmd_buf) / sizeof(unsigned int), + dec_pict, eop); + + if (result != IMG_SUCCESS) + return result; + + /* Finally mark end of commands */ + *(cmd_buf++) = CMD_COMPLETION; + + /* Transfer control allocation command to device memory */ + pict_fragement->ctrl_alloc_offset = dec_pict->ctrl_alloc_offset; + pict_fragement->ctrl_alloc_bytes = + ((unsigned long)cmd_buf - ctrl_alloc); + + dec_pict->ctrl_alloc_offset += pict_fragement->ctrl_alloc_bytes; + + return result; +}; +#endif /* VDEC_USE_PVDEC */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/translation_api.h b/drivers/media/platform/vxe-vxd/decoder/translation_api.h --- a/drivers/media/platform/vxe-vxd/decoder/translation_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/translation_api.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VDECDD translation API's. + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ +#ifndef __TRANSLATION_API_H__ +#define __TRANSLATION_API_H__ + +#include "decoder.h" +#include "hw_control.h" +#include "vdecdd_defs.h" +#include "vdec_defs.h" +#include "vxd_props.h" + +/* + * This function submits a stream unit for translation + * into a control allocation buffer used in PVDEC operation. + */ +int translation_ctrl_alloc_prepare + (struct vdec_str_configdata *psstr_config_data, + struct vdecdd_str_unit *psstrunit, + struct dec_decpict *psdecpict, + const struct vxd_coreprops *core_props, + struct decoder_regsoffsets *regs_offset); + +/* + * TRANSLATION_FragmentPrepare. + */ +int translation_fragment_prepare(struct dec_decpict *psdecpict, + struct lst_t *decpic_seg_list, int eop, + struct dec_pict_fragment *pict_fragement); + +#endif /* __TRANSLATION_API_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdecdd_defs.h b/drivers/media/platform/vxe-vxd/decoder/vdecdd_defs.h --- a/drivers/media/platform/vxe-vxd/decoder/vdecdd_defs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdecdd_defs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Decoder device driver header definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include + +#include "img_profiles_levels.h" +#include "pixel_api.h" +#include "vdecdd_utils.h" + +/* + * Tests if chroma offset (immediately after size of luma) is exactly + * aligned to buffer alignment constraint. + */ +static inline unsigned char is_packedbuf_chroma_aligned(unsigned int offset, + unsigned int color_plane, + unsigned int align) +{ + return(color_plane != VDEC_PLANE_VIDEO_Y ? TRUE : + (offset == ALIGN(offset, align) ? TRUE : FALSE)); +} + +/* + * < h.264 MaxDpbMbs values per profile (see Table A-1 of Rec. ITU-T H.264 + * (03/2010)). + * NOTE: Level 1b will be treated as 1.1 in case of Baseline, + * Constrained Baseline, Main, and Extended profiles as the value of the + * constraint_set3_flag is not available in #VDEC_sComSequHdrInfo structure. + */ +static unsigned int h264_max_dpb_mbs[H264_LEVEL_MAJOR_NUM][H264_LEVEL_MINOR_NUM] = { + /* level: n/a n/a n/a 1.0b */ + { 396, 396, 396, 396 }, + /* level: 1.0 1.1 1.2 1.3 */ + { 396, 900, 2376, 2376 }, + /* level: 2.0 2.1 2.2 n/a */ + { 2376, 4752, 8100, 8100 }, + /* level: 3.0 3.1 3.2 n/a */ + { 8100, 18000, 20480, 20480}, + /* level: 4.0 4.1 4.2 n/a */ + { 32768, 32768, 34816, 34816}, + /* level: 5.0 5.1 5.2 n/a */ + { 110400, 184320, 184320, 184320} +}; + +typedef int (*fn_ref_pic_get_max_num)(const struct vdec_comsequ_hdrinfo + *comseq_hdrinfo, unsigned int *max_ref_pic_num); + +void vdecddutils_buf_vxd_adjust_size(unsigned int *buf_size) +{ + /* Align the buffer size to VXD page size. */ + *buf_size = ALIGN(*buf_size, VDEC_VXD_BUF_ALIGNMENT); +} + +static int vdecddutils_ref_pic_h264_get_maxnum + (const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + unsigned int *max_ref_pic_num) +{ + unsigned int pic_width_mb; + unsigned int pic_height_mb; + unsigned int lvl_major = 0; + unsigned int lvl_minor = 0; + + /* Pre-validate level. */ + if (comseq_hdrinfo->codec_level < H264_LEVEL_MIN || + comseq_hdrinfo->codec_level > H264_LEVEL_MAX) { + pr_warn("Wrong H264 level value: %u", + comseq_hdrinfo->codec_level); + } + + if (comseq_hdrinfo->max_reorder_picts) { + *max_ref_pic_num = comseq_hdrinfo->max_reorder_picts; + } else { + /* Calculate level major and minor. */ + lvl_major = comseq_hdrinfo->codec_level / 10; + lvl_minor = comseq_hdrinfo->codec_level % 10; + + /* Calculate picture sizes in MBs. */ + pic_width_mb = (comseq_hdrinfo->max_frame_size.width + + (VDEC_MB_DIMENSION - 1)) / VDEC_MB_DIMENSION; + pic_height_mb = (comseq_hdrinfo->max_frame_size.height + + (VDEC_MB_DIMENSION - 1)) / VDEC_MB_DIMENSION; + + /* Validate lvl_minor */ + if (lvl_minor > 3) { + pr_warn("Wrong H264 lvl_minor level value: %u, overriding with 3", + lvl_minor); + lvl_minor = 3; + } + /* Validate lvl_major */ + if (lvl_major > 5) { + pr_warn("Wrong H264 lvl_major level value: %u, overriding with 5", + lvl_major); + lvl_major = 5; + } + + /* + * Calculate the maximum number of reference pictures + * required based on level. + */ + *max_ref_pic_num = h264_max_dpb_mbs[lvl_major][lvl_minor] / + (pic_width_mb * pic_height_mb); + if (*max_ref_pic_num > 16) + *max_ref_pic_num = 16; + } + + /* Return success. */ + return IMG_SUCCESS; +} + +#ifdef HAS_HEVC +/* + * @Function vdecddutils_ref_pic_hevc_get_maxnum + */ +int vdecddutils_ref_pic_hevc_get_maxnum(const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + unsigned int *max_ref_picnum) +{ + static const unsigned int HEVC_LEVEL_IDC_MIN = 30; + static const unsigned int HEVC_LEVEL_IDC_MAX = 186; + + static const unsigned int + max_luma_ps_list[HEVC_LEVEL_MAJOR_NUM][HEVC_LEVEL_MINOR_NUM] = { + /* level: 1.0 1.1 1.2 */ + { 36864, 0, 0, }, + /* level: 2.0 2.1 2.2 */ + { 122880, 245760, 0, }, + /* level: 3.0 3.1 3.2 */ + { 552960, 983040, 0, }, + /* level: 4.0 4.1 4.2 */ + { 2228224, 2228224, 0, }, + /* level: 5.0 5.1 5.2 */ + { 8912896, 8912896, 8912896, }, + /* level: 6.0 6.1 6.2 */ + { 35651584, 35651584, 35651584, } + }; + + /* ITU-T H.265 04/2013 A.4.1 */ + + const unsigned int max_dpb_picbuf = 6; + + /* this is rounded to whole Ctbs */ + unsigned int pic_size_in_samples_Y = comseq_hdrinfo->frame_size.height * + comseq_hdrinfo->frame_size.width; + + signed char level_maj, level_min; + unsigned int max_luma_ps; + + /* some error resilience */ + if (comseq_hdrinfo->codec_level > HEVC_LEVEL_IDC_MAX || + comseq_hdrinfo->codec_level < HEVC_LEVEL_IDC_MIN) { + pr_warn("HEVC Codec level out of range: %u, falling back to %u", + comseq_hdrinfo->codec_level, + comseq_hdrinfo->min_pict_buf_num); + + *max_ref_picnum = comseq_hdrinfo->min_pict_buf_num; + return IMG_SUCCESS; + } + + level_maj = comseq_hdrinfo->codec_level / 30; + level_min = (comseq_hdrinfo->codec_level % 30) / 3; + + if (level_maj > 0 && level_maj <= HEVC_LEVEL_MAJOR_NUM && + level_min >= 0 && level_min < HEVC_LEVEL_MINOR_NUM) { + max_luma_ps = max_luma_ps_list[level_maj - 1][level_min]; + } else { + pr_err("%s: Invalid parameters\n", __func__); + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (max_luma_ps == 0) { + pr_err("Wrong HEVC level value: %u.%u (general_level_idc: %u)", + level_maj, level_min, comseq_hdrinfo->codec_level); + + return IMG_ERROR_VALUE_OUT_OF_RANGE; + } + + if (max_luma_ps < pic_size_in_samples_Y) + pr_warn("HEVC PicSizeInSamplesY too large for level (%u > %u)", + pic_size_in_samples_Y, max_luma_ps); + + if (pic_size_in_samples_Y <= (max_luma_ps >> 2)) + *max_ref_picnum = vdec_size_min(4 * max_dpb_picbuf, 16); + else if (pic_size_in_samples_Y <= (max_luma_ps >> 1)) + *max_ref_picnum = vdec_size_min(2 * max_dpb_picbuf, 16); + else if (pic_size_in_samples_Y <= ((3 * max_luma_ps) >> 2)) + *max_ref_picnum = vdec_size_min((4 * max_dpb_picbuf) / 3, 16); + else + *max_ref_picnum = max_dpb_picbuf; + + /* Return success. */ + return IMG_SUCCESS; +} +#endif + +#ifdef HAS_JPEG +static int vdecddutils_ref_pic_jpeg_get_maxnum(const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + unsigned int *max_ref_picnum) +{ + /* No reference frames for JPEG. */ + *max_ref_picnum = 0; + + /* Return success. */ + return IMG_SUCCESS; +} +#endif + +/* + * The array of pointers to functions calculating the maximum number + * of reference pictures required for each supported video standard. + * NOTE: The table is indexed by #VDEC_eVidStd enum values. + */ +static fn_ref_pic_get_max_num ref_pic_get_maxnum[VDEC_STD_MAX - 1] = { + NULL, + NULL, + NULL, + vdecddutils_ref_pic_h264_get_maxnum, + NULL, + NULL, + NULL, +#ifdef HAS_JPEG + vdecddutils_ref_pic_jpeg_get_maxnum, +#else + NULL, +#endif + NULL, + NULL, + NULL, +#ifdef HAS_HEVC + vdecddutils_ref_pic_hevc_get_maxnum +#else + NULL +#endif +}; + +int +vdecddutils_ref_pict_get_maxnum(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_comsequ_hdrinfo *comseq_hdr_info, + unsigned int *num_picts) +{ + int ret = IMG_SUCCESS; + + /* Validate input params. */ + if (str_cfg_data->vid_std == VDEC_STD_UNDEFINED || str_cfg_data->vid_std >= VDEC_STD_MAX) + return IMG_ERROR_VALUE_OUT_OF_RANGE; + + /* Call the function related to the provided video standard. */ + ret = ref_pic_get_maxnum[str_cfg_data->vid_std - 1](comseq_hdr_info, + num_picts); + if (ret != IMG_SUCCESS) + pr_warn("[USERSID=0x%08X] Failed to get number of reference pictures", + str_cfg_data->user_str_id); + + /* + * For non-conformant stream use the + * max(*pui32NumPicts,comseq_hdrinfo->ui32MinPicBufNum) + */ + if (*num_picts < comseq_hdr_info->min_pict_buf_num) + *num_picts = comseq_hdr_info->min_pict_buf_num; + + /* + * Increase for MVC: mvcScaleFactor = 2 (H.10.2) and additional pictures + * for a StoreInterViewOnlyRef case (C.4.5.2) + */ + if (comseq_hdr_info->num_views > 1) { + *num_picts *= 2; + *num_picts += comseq_hdr_info->num_views - 1; + } + + return ret; +} + +static void vdecddutils_update_rend_pictsize(struct vdec_pict_size pict_size, + struct vdec_pict_size *rend_pict_size) +{ + if (rend_pict_size->width == 0) { + rend_pict_size->width = pict_size.width; + } else { + /* Take the smallest resolution supported by all the planes */ + rend_pict_size->width = (pict_size.width < + rend_pict_size->width) ? + pict_size.width : + rend_pict_size->width; + } + if (rend_pict_size->height == 0) { + rend_pict_size->height = pict_size.height; + } else { + /* Take the smallest resolution supported by all the planes. */ + rend_pict_size->height = (pict_size.height < + rend_pict_size->height) ? + pict_size.height : + rend_pict_size->height; + } +} + +int vdecddutils_convert_buffer_config(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_bufconfig *pict_bufcfg, + struct vdec_pict_rendinfo *pict_rend_info) +{ + const struct pixel_pixinfo *pix_info; + struct img_pixfmt_desc pixfmt; + unsigned int i; + unsigned int total_vert_samples = 0; + unsigned int vert_samples[IMG_MAX_NUM_PLANES]; + unsigned int plane_size = 0; + unsigned int plane_offset = 0; + struct vdec_pict_size pict_size; + + /* Validate inputs. */ + VDEC_ASSERT(str_cfg_data); + VDEC_ASSERT(pict_bufcfg); + VDEC_ASSERT(pict_rend_info); + + /* Reset picture buffer allocation data. */ + memset(pict_rend_info, 0x0, sizeof(*pict_rend_info)); + + pr_debug("%s picture buffer pixel_fmt = %d\n", __func__, pict_bufcfg->pixel_fmt); + /* Get pixel format info for regular pixel formats... */ + if (pict_bufcfg->pixel_fmt < IMG_PIXFMT_ARBPLANAR8) { + pix_info = pixel_get_pixinfo(pict_bufcfg->pixel_fmt); + pixel_yuv_get_desc((struct pixel_pixinfo *)pix_info, &pixfmt); + } else { + pixel_get_fmt_desc(pict_bufcfg->pixel_fmt, &pixfmt); + } + + /* + * Construct the render region information from the picture + * buffer configuration. + */ + for (i = 0; i < IMG_MAX_NUM_PLANES; i++) { + if (pixfmt.planes[i]) { + unsigned int plane_align = VDEC_VXD_PICTBUF_ALIGNMENT; + + /* + * Determine the offset (in bytes) to this plane. + * This is zero for the first (luma) plane and at the + * end of the previous plane for all subsequent planes. + */ + plane_offset = plane_offset + plane_size; + + /* + * Calculate the minimum number of vertical samples + * for this plane. + */ + vert_samples[i] = + ((pict_bufcfg->coded_height + + pixfmt.v_denom - 1) / pixfmt.v_denom) * + pixfmt.v_numer[i]; + + /* + * Calculate the mimimum plane size from the stride and + * decode picture height. Packed buffers have the luma + * and chroma exactly adjacent and consequently the + * chroma plane offset is equal to this plane size. + */ + plane_size = pict_bufcfg->stride[i] * vert_samples[i]; + plane_size = ALIGN(plane_size, plane_align); + + if (!pict_bufcfg->packed && pict_bufcfg->chroma_offset[i]) { + unsigned int max_plane_size; + + max_plane_size = + pict_bufcfg->chroma_offset[i] - plane_offset; + + if (plane_size > max_plane_size) { + pr_err("Chroma offset [%d bytes] is not large enough to fit minimum plane data [%d bytes] at offset [%d]", + pict_bufcfg->chroma_offset[i], + plane_size, plane_offset); + return IMG_ERROR_INVALID_PARAMETERS; + } + + plane_size = max_plane_size; + + vert_samples[i] = plane_size / + pict_bufcfg->stride[i]; + } else { + if (pict_bufcfg->chroma_offset[i] && (plane_offset + plane_size) != + pict_bufcfg->chroma_offset[i]) { + pr_err("Chroma offset specified [%d bytes] should match that required for plane size calculated from stride and height [%d bytes]", + pict_bufcfg->chroma_offset[i], + plane_offset + plane_size); + return IMG_ERROR_INVALID_PARAMETERS; + } + } + + pict_rend_info->plane_info[i].offset = plane_offset; + pict_rend_info->plane_info[i].stride = + pict_bufcfg->stride[i]; + pict_rend_info->plane_info[i].size = plane_size; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("VDECDDUTILS_ConvertBufferConfig() plane %d stride %u size %u offset %u", + i, pict_rend_info->plane_info[i].stride, + pict_rend_info->plane_info[i].size, + pict_rend_info->plane_info[i].offset); +#endif + + pict_rend_info->rendered_size += + pict_rend_info->plane_info[i].size; + + total_vert_samples += vert_samples[i]; + + /* Calculate the render region maximum picture size. */ + pict_size.width = (pict_rend_info->plane_info[i].stride * + pixfmt.bop_denom) / pixfmt.bop_numer[i]; + pict_size.height = (vert_samples[i] * pixfmt.v_denom) / pixfmt.v_numer[i]; + vdecddutils_update_rend_pictsize(pict_size, + &pict_rend_info->rend_pict_size); + } + } +#ifdef DEBUG_DECODER_DRIVER + pr_info("VDECDDUTILS_ConvertBufferConfig() total required %u (inc. alignment for addressing/tiling) vs. buffer %u", + pict_rend_info->rendered_size, pict_bufcfg->buf_size); +#endif + + /* Ensure that the buffer size is large enough to hold the data */ + if (pict_bufcfg->buf_size < pict_rend_info->rendered_size) { + pr_err("Buffer size [%d bytes] should be at least as large as rendered data (inc. any enforced gap between planes) [%d bytes]", + pict_bufcfg->buf_size, + pict_rend_info->rendered_size); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Whole buffer should be marked as rendered region */ + pict_rend_info->rendered_size = pict_bufcfg->buf_size; + /* Use the actual stride alignment */ + pict_rend_info->stride_alignment = pict_bufcfg->stride_alignment; + + return IMG_SUCCESS; +} + +static unsigned char vdecddutils_is_secondary_op_required(const struct vdec_comsequ_hdrinfo + *comseq_hdr_info, + const struct vdec_str_opconfig + *op_cfg) +{ + unsigned char result = TRUE; + + if (!op_cfg->force_oold && + !comseq_hdr_info->post_processing && + comseq_hdr_info->pixel_info.chroma_fmt_idc == + op_cfg->pixel_info.chroma_fmt_idc && + comseq_hdr_info->pixel_info.bitdepth_y == + op_cfg->pixel_info.bitdepth_y && + comseq_hdr_info->pixel_info.bitdepth_c == + op_cfg->pixel_info.bitdepth_c) + /* + * The secondary output is not required (if we have it we will + * not use it for transformation (e.g. scaling. rotating or + * up/down-sampling). + */ + result = FALSE; + + return result; +} + +int vdecddutils_get_minrequired_numpicts(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_comsequ_hdrinfo *comseq_hdr_info, + const struct vdec_str_opconfig *op_cfg, + unsigned int *num_picts) +{ + int ret; + unsigned int max_held_picnum; + + /* If any operation requiring internal buffers is to be applied... */ + if (vdecddutils_is_secondary_op_required(comseq_hdr_info, op_cfg)) { + /* + * Reference picture buffers will be allocated internally, + * but there may be a number of picture buffers to which + * out-of-display-order pictures will be decoded. These + * buffers need to be allocated externally, so there's a + * need to calculate the number of out-of-(display)-order + * pictures required for the provided video standard. + */ + ret = vdecddutils_ref_pict_get_maxnum(str_cfg_data, comseq_hdr_info, + &max_held_picnum); + if (ret != IMG_SUCCESS) + return ret; + } else { + /* + * All the reference picture buffers have to be allocated + * externally, so there's a need to calculate the number of + * reference picture buffers required for the provided video + * standard. + */ + ret = vdecddutils_ref_pict_get_maxnum(str_cfg_data, comseq_hdr_info, + &max_held_picnum); + if (ret != IMG_SUCCESS) + return ret; + } + + /* + * Calculate the number of picture buffers required as the maximum + * number of picture buffers to be held onto by the driver plus the + * current picture buffer. + */ + *num_picts = max_held_picnum + + (comseq_hdr_info->interlaced_frames ? 2 : 1); + + return IMG_SUCCESS; +} + +static void vdecddutils_get_codedsize(const struct vdec_pict_rend_config *pict_rend_cfg, + struct vdec_pict_size *decoded_pict_size) +{ + decoded_pict_size->width = pict_rend_cfg->coded_pict_size.width; + decoded_pict_size->height = pict_rend_cfg->coded_pict_size.height; +} + +static unsigned char vdecddutils_is_packed(const struct vdec_pict_rendinfo *pict_rend_info, + const struct vdec_pict_rend_config *pict_rend_cfg) +{ + unsigned char packed = TRUE; + unsigned int pict_buf_align; + + /* Validate inputs. */ + VDEC_ASSERT(pict_rend_info); + VDEC_ASSERT(pict_rend_cfg); + + pict_buf_align = VDEC_VXD_PICTBUF_ALIGNMENT; + + if (pict_rend_info->plane_info[VDEC_PLANE_VIDEO_Y].size != + pict_rend_info->plane_info[VDEC_PLANE_VIDEO_UV].offset) { + /* Planes that are not adjacent cannot be packed */ + packed = FALSE; + } else if (!is_packedbuf_chroma_aligned(pict_rend_info->plane_info + [VDEC_PLANE_VIDEO_UV].offset, + VDEC_PLANE_VIDEO_Y, + pict_buf_align)) { + /* Chroma plane must be aligned for packed buffers. */ + VDEC_ASSERT(pict_rend_info->plane_info[VDEC_PLANE_VIDEO_Y].size == + pict_rend_info->plane_info[VDEC_PLANE_VIDEO_UV].offset); + packed = FALSE; + } + + return packed; +} + +static int vdecddutils_get_stride + (const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_rend_config *pict_rend_cfg, + unsigned int vert_samples, unsigned int *h_stride, + enum vdec_color_planes color_planes) +{ + unsigned int hw_h_stride = *h_stride; + + /* + * If extended strides are to be used or indexed strides failed, + * make extended stride alignment. + */ + hw_h_stride = ALIGN(hw_h_stride, + pict_rend_cfg->stride_alignment > 0 ? + pict_rend_cfg->stride_alignment : + VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT); + + /* A zero-value indicates unsupported stride */ + if (hw_h_stride == 0) + /* No valid stride found */ + return IMG_ERROR_NOT_SUPPORTED; + + *h_stride = hw_h_stride; + + return IMG_SUCCESS; +} + +static int vdecddutils_get_render_info(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_rend_config *pict_rend_cfg, + const struct pixel_pixinfo *pix_info, + struct vdec_pict_rendinfo *pict_rend_info) +{ + unsigned int i; + struct img_pixfmt_desc pixfmt; + struct vdec_pict_size coded_pict_size; + unsigned char single_stride = FALSE; + unsigned int vert_sample[IMG_MAX_NUM_PLANES] = {0}; + unsigned int total_vert_samples; + unsigned int largest_stride; + unsigned int result; + + /* Reset the output structure. */ + memset(pict_rend_info, 0, sizeof(*pict_rend_info)); + + /* Ensure that the coded sizes are in whole macroblocks. */ + if ((pict_rend_cfg->coded_pict_size.width & + (VDEC_MB_DIMENSION - 1)) != 0 || + (pict_rend_cfg->coded_pict_size.height & + (VDEC_MB_DIMENSION - 1)) != 0) { + pr_err("Invalid render configuration coded picture size [%d x %d]. It should be a whole number of MBs in each dimension", + pict_rend_cfg->coded_pict_size.width, + pict_rend_cfg->coded_pict_size.height); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Check if the stride alignment is multiple of default. */ + if ((pict_rend_cfg->stride_alignment & + (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT - 1)) != 0) { + pr_err("Invalid stride alignment %d used. It should be multiple of %d.", + pict_rend_cfg->stride_alignment, + VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT); + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Get pixel format info for regular pixel formats... */ + if (pix_info->pixfmt < IMG_PIXFMT_ARBPLANAR8) + pixel_yuv_get_desc((struct pixel_pixinfo *)pix_info, &pixfmt); + else + pixel_get_fmt_desc(pix_info->pixfmt, &pixfmt); + + /* Get the coded size for the appropriate orientation */ + vdecddutils_get_codedsize(pict_rend_cfg, &coded_pict_size); + + /* + * Calculate the hardware (inc. constraints) strides and + * number of vertical samples for each plane. + */ + total_vert_samples = 0; + largest_stride = 0; + for (i = 0; i < IMG_MAX_NUM_PLANES; i++) { + if (pixfmt.planes[i]) { + unsigned int h_stride; + + /* Horizontal stride must be for a multiple of BOPs. */ + h_stride = ((coded_pict_size.width + + pixfmt.bop_denom - 1) / + pixfmt.bop_denom) * pixfmt.bop_numer[i]; + + /* + * Vertical only has to satisfy whole pixel of + * samples. + */ + vert_sample[i] = ((coded_pict_size.height + + pixfmt.v_denom - 1) / + pixfmt.v_denom) * pixfmt.v_numer[i]; + + /* + * Obtain a horizontal stride supported by the hardware + * (inc. constraints). + */ + result = vdecddutils_get_stride(str_cfg_data, pict_rend_cfg, vert_sample[i], + &h_stride, (enum vdec_color_planes)i); + if (result != IMG_SUCCESS) { + VDEC_ASSERT(0); + pr_err("No valid VXD stride found for picture with decoded dimensions [%d x %d] and min stride [%d]", + coded_pict_size.width, coded_pict_size.height, h_stride); + return result; + } + + pict_rend_info->plane_info[i].stride = h_stride; + if (i == VDEC_PLANE_VIDEO_UV && (str_cfg_data->vid_std == VDEC_STD_H264 || + str_cfg_data->vid_std == VDEC_STD_HEVC)) { + struct pixel_pixinfo *info = + pixel_get_pixinfo(pix_info->pixfmt); + VDEC_ASSERT(PIXEL_FORMAT_INVALID != + info->chroma_fmt_idc); + } + + total_vert_samples += vert_sample[i]; + if (h_stride > largest_stride) + largest_stride = h_stride; + } + } + pict_rend_info->stride_alignment = + pict_rend_cfg->stride_alignment > 0 ? + pict_rend_cfg->stride_alignment : + VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT; + + if (pict_rend_cfg->packed) + single_stride = TRUE; + +#ifdef HAS_JPEG + /* JPEG hardware uses a single (luma) stride for all planes. */ + if (str_cfg_data->vid_std == VDEC_STD_JPEG) { + single_stride = true; + + /* Luma should be largest for this to be used for all planes. */ + VDEC_ASSERT(largest_stride == + pict_rend_info->plane_info[VDEC_PLANE_VIDEO_Y].stride); + } +#endif + + /* Calculate plane sizes. */ + for (i = 0; i < IMG_MAX_NUM_PLANES; i++) { + if (pixfmt.planes[i]) { + struct vdec_pict_size pict_size; + unsigned int vert_samples = vert_sample[i]; + unsigned int plane_align = VDEC_VXD_PICTBUF_ALIGNMENT; + + if (single_stride) + pict_rend_info->plane_info[i].stride = + largest_stride; + + pict_rend_info->plane_info[i].size = + pict_rend_info->plane_info[i].stride * + vert_samples; + pict_rend_info->plane_info[i].size = + ALIGN(pict_rend_info->plane_info[i].size, plane_align); + /* + * Ensure that the total buffer rendered size is + * rounded-up to the picture buffer alignment so that + * this plane (within this single buffer) can be + * correctly addressed by the hardware at this byte + * offset. + */ + if (i == 1 && pict_rend_cfg->packed) + /* + * Packed buffers must have chroma plane + * already aligned since this was factored + * into the stride/size calculation. + */ + VDEC_ASSERT(pict_rend_info->rendered_size == + ALIGN(pict_rend_info->rendered_size, plane_align)); + + pict_rend_info->plane_info[i].offset = pict_rend_info->rendered_size; + + /* Update the total buffer size (inc. this plane). */ + pict_rend_info->rendered_size += + pict_rend_info->plane_info[i].size; + + /* + * Update the maximum render picture size supported + * by all planes of this buffer. + */ + pict_size.width = (pict_rend_info->plane_info[i].stride * + pixfmt.bop_denom) / pixfmt.bop_numer[i]; + + pict_size.height = (vert_sample[i] * pixfmt.v_denom) / pixfmt.v_numer[i]; + + vdecddutils_update_rend_pictsize(pict_size, + &pict_rend_info->rend_pict_size); + +#ifdef DEBUG_DECODER_DRIVER + pr_info("vdecddutils_GetRenderInfo() plane %d stride %u size %u offset %u", + i, pict_rend_info->plane_info[i].stride, + pict_rend_info->plane_info[i].size, + pict_rend_info->plane_info[i].offset); +#endif + } + } + +#ifdef DEBUG_DECODER_DRIVER + pr_info("vdecddutils_GetRenderInfo() total %u (inc. alignment for addressing/tiling)", + pict_rend_info->rendered_size); +#endif + + return IMG_SUCCESS; +} + +int vdecddutils_pictbuf_getconfig(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_rend_config *pict_rend_cfg, + const struct vdec_str_opconfig *str_opcfg, + struct vdec_pict_bufconfig *pict_bufcfg) +{ + struct vdec_pict_rendinfo disp_pict_rendinfo; + struct vdec_pict_size coded_pict_size; + unsigned int ret, i; + unsigned int size0, size1; + + /* Validate inputs. */ + VDEC_ASSERT(str_cfg_data); + VDEC_ASSERT(pict_rend_cfg); + VDEC_ASSERT(str_opcfg); + VDEC_ASSERT(pict_bufcfg); + + /* Clear the picture buffer config before populating */ + memset(pict_bufcfg, 0, sizeof(struct vdec_pict_bufconfig)); + + /* Determine the rounded-up coded sizes (compatible with hardware) */ + ret = vdecddutils_get_render_info(str_cfg_data, + pict_rend_cfg, + &str_opcfg->pixel_info, + &disp_pict_rendinfo); + if (ret != IMG_SUCCESS) + return ret; + + /* Get the coded size for the appropriate orientation */ + vdecddutils_get_codedsize(pict_rend_cfg, &coded_pict_size); + + pict_bufcfg->coded_width = coded_pict_size.width; + pict_bufcfg->coded_height = coded_pict_size.height; + + /* + * Use the luma stride for all planes in buffer. + * Additional chroma stride may be needed for other pixel formats. + */ + for (i = 0; i < VDEC_PLANE_MAX; i++) + pict_bufcfg->stride[i] = disp_pict_rendinfo.plane_info[i].stride; + + /* + * Pixel information is taken from that + * specified for display. + */ + pict_bufcfg->pixel_fmt = str_opcfg->pixel_info.pixfmt; + pr_debug("picture buffer pixel_fmt = %d\n", pict_bufcfg->pixel_fmt); + + /* Tiling scheme is taken from render configuration */ + pict_bufcfg->byte_interleave = pict_rend_cfg->byte_interleave; + pr_debug("picture buffer byte_interleave = %d\n", pict_bufcfg->byte_interleave); + /* Stride alignment */ + pict_bufcfg->stride_alignment = pict_rend_cfg->stride_alignment > 0 ? + pict_rend_cfg->stride_alignment : VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT; + + pr_debug("picture buffer stride_alignment = %d\n", pict_bufcfg->stride_alignment); + /* Chroma offset taken as calculated for render configuration. */ + pict_bufcfg->chroma_offset[0] = disp_pict_rendinfo.plane_info[VDEC_PLANE_VIDEO_UV].offset; + pict_bufcfg->chroma_offset[1] = disp_pict_rendinfo.plane_info[VDEC_PLANE_VIDEO_V].offset; + + if (pict_rend_cfg->packed && str_opcfg->pixel_info.num_planes > 1) { + pict_bufcfg->packed = vdecddutils_is_packed(&disp_pict_rendinfo, pict_rend_cfg); + if (!pict_bufcfg->packed) { + /* Report if unable to meet request to pack. */ + pr_err("Request for packed buffer could not be met"); + return IMG_ERROR_NOT_SUPPORTED; + } + + size0 = ALIGN(pict_bufcfg->chroma_offset[0], VDEC_VXD_PICTBUF_ALIGNMENT); + size1 = ALIGN(pict_bufcfg->chroma_offset[1], VDEC_VXD_PICTBUF_ALIGNMENT); + + if (pict_bufcfg->chroma_offset[0] != size0 || + pict_bufcfg->chroma_offset[1] != size1) { + pr_err("Chroma plane could not be located on a %d byte boundary (investigate stride calculations)", + VDEC_VXD_PICTBUF_ALIGNMENT); + return IMG_ERROR_NOT_SUPPORTED; + } + } else { + pict_bufcfg->packed = FALSE; + } + + pict_bufcfg->buf_size = disp_pict_rendinfo.rendered_size; + + /* Return success */ + return IMG_SUCCESS; +} + +int vdecddutils_get_display_region(const struct vdec_pict_size *coded_size, + const struct vdec_rect *orig_disp_region, + struct vdec_rect *disp_region) +{ + int ret = IMG_SUCCESS; + + /* Validate inputs. */ + VDEC_ASSERT(coded_size); + VDEC_ASSERT(orig_disp_region); + VDEC_ASSERT(disp_region); + if (!coded_size || !orig_disp_region || !disp_region) + return IMG_ERROR_INVALID_PARAMETERS; + + /* + * In the simplest case the display region is the same as + * that defined in the bitstream. + */ + *disp_region = *orig_disp_region; + + if (orig_disp_region->height == 0 || orig_disp_region->width == 0 || + coded_size->height == 0 || coded_size->width == 0) { + pr_err("Invalid params to calculate display region:"); + pr_err("Display Size: [%d,%d]", orig_disp_region->width, orig_disp_region->height); + pr_err("Coded Size : [%d,%d]", coded_size->width, coded_size->height); + return IMG_ERROR_INVALID_PARAMETERS; + } + + return ret; +} + +int vdecddutils_pictbuf_getinfo(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_rend_config *pict_rend_cfg, + const struct vdec_str_opconfig *str_op_cfg, + struct vdec_pict_rendinfo *pict_rend_info) +{ + unsigned int ret; + + /* Validate inputs. */ + VDEC_ASSERT(str_cfg_data); + VDEC_ASSERT(pict_rend_cfg); + VDEC_ASSERT(str_op_cfg); + VDEC_ASSERT(pict_rend_info); + + ret = vdecddutils_get_render_info(str_cfg_data, pict_rend_cfg, + &str_op_cfg->pixel_info, + pict_rend_info); + VDEC_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + return IMG_SUCCESS; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.c b/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.c --- a/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD Decoder device driver utility functions implementation + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include + +#include "bspp.h" +#include "vdecdd_utils.h" + +/* + * @Function VDECDDUTILS_FreeStrUnit + */ +int vdecddutils_free_strunit(struct vdecdd_str_unit *str_unit) +{ + struct bspp_bitstr_seg *bstr_seg; + + /* Loop over bit stream segments */ + bstr_seg = (struct bspp_bitstr_seg *)lst_removehead(&str_unit->bstr_seg_list); + while (bstr_seg) { + /* Free segment. */ + kfree(bstr_seg); + + /* Get next segment. */ + bstr_seg = (struct bspp_bitstr_seg *)lst_removehead(&str_unit->bstr_seg_list); + } + + /* Free the sequence header */ + if (str_unit->seq_hdr_info) { + str_unit->seq_hdr_info->ref_count--; + if (str_unit->seq_hdr_info->ref_count == 0) { + kfree(str_unit->seq_hdr_info); + str_unit->seq_hdr_info = NULL; + } + } + + /* Free the picture header... */ + if (str_unit->pict_hdr_info) { + kfree(str_unit->pict_hdr_info->pict_sgm_data.pic_data); + str_unit->pict_hdr_info->pict_sgm_data.pic_data = NULL; + + kfree(str_unit->pict_hdr_info); + str_unit->pict_hdr_info = NULL; + } + + /* Free stream unit. */ + kfree(str_unit); + str_unit = NULL; + + /* Return success */ + return IMG_SUCCESS; +} + +/* + * @Function: VDECDDUTILS_CreateStrUnit + * @Description: this function allocate a structure for a complete data unit + */ +int vdecddutils_create_strunit(struct vdecdd_str_unit **str_unit_handle, + struct lst_t *bs_list) +{ + struct vdecdd_str_unit *str_unit; + struct bspp_bitstr_seg *bstr_seg; + + str_unit = kzalloc(sizeof(*str_unit), GFP_KERNEL); + VDEC_ASSERT(str_unit); + if (!str_unit) + return IMG_ERROR_OUT_OF_MEMORY; + + if (bs_list) { + /* copy BS list to this list */ + lst_init(&str_unit->bstr_seg_list); + for (bstr_seg = lst_first(bs_list); bstr_seg; + bstr_seg = lst_first(bs_list)) { + bstr_seg = lst_removehead(bs_list); + lst_add(&str_unit->bstr_seg_list, bstr_seg); + } + } + + *str_unit_handle = str_unit; + + return IMG_SUCCESS; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.h b/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.h --- a/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdecdd_utils.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Decoder device driver utility header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#ifndef __VDECDD_UTILS_H__ +#define __VDECDD_UTILS_H__ + +#include "img_errors.h" +#include "vdecdd_defs.h" + +/* The picture buffer alignment (in bytes) for VXD. */ +#define VDEC_VXD_PICTBUF_ALIGNMENT (64) +/* The buffer alignment (in bytes) for VXD. */ +#define VDEC_VXD_BUF_ALIGNMENT (4096) +/* The extended stride alignment for VXD. */ +#define VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT (64) +/* Macroblock dimension (width and height) in pixels. */ +#define VDEC_MB_DIMENSION (16) + +static inline unsigned int vdec_size_min(unsigned int a, unsigned int b) +{ + return a <= b ? a : b; +} + +static inline unsigned char vdec_size_lt(struct vdec_pict_size sa, struct vdec_pict_size sb) +{ + return (sa.width < sb.width && sa.height <= sb.height) || + (sa.width <= sb.width && sa.height < sb.height); +} + +static inline unsigned char vdec_size_ge(struct vdec_pict_size sa, struct vdec_pict_size sb) +{ + return sa.width >= sb.width && sa.height >= sb.height; +} + +static inline unsigned char vdec_size_ne(struct vdec_pict_size sa, struct vdec_pict_size sb) +{ + return sa.width != sb.width || sa.height != sb.height; +} + +static inline unsigned char vdec_size_nz(struct vdec_pict_size sa) +{ + return sa.width != 0 && sa.height != 0; +} + +int vdecddutils_free_strunit(struct vdecdd_str_unit *str_unit); + +int vdecddutils_create_strunit(struct vdecdd_str_unit **str_unit_handle, + struct lst_t *bs_list); + +int vdecddutils_ref_pict_get_maxnum(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_comsequ_hdrinfo *comseq_hdr_info, + unsigned int *num_picts); + +int vdecddutils_get_minrequired_numpicts(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_comsequ_hdrinfo *comseq_hdr_info, + const struct vdec_str_opconfig *op_cfg, + unsigned int *num_picts); + +int vdecddutils_pictbuf_getconfig(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_rend_config *pict_rend_cfg, + const struct vdec_str_opconfig *str_opcfg, + struct vdec_pict_bufconfig *pict_bufcfg); + +int vdecddutils_pictbuf_getinfo(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_rend_config *pict_rend_cfg, + const struct vdec_str_opconfig *str_opcfg, + struct vdec_pict_rendinfo *pict_rend_info); + +int vdecddutils_convert_buffer_config(const struct vdec_str_configdata *str_cfg_data, + const struct vdec_pict_bufconfig *pict_bufcfg, + struct vdec_pict_rendinfo *pict_rend_info); + +int vdecddutils_get_display_region(const struct vdec_pict_size *coded_size, + const struct vdec_rect *orig_disp_region, + struct vdec_rect *disp_region); + +void vdecddutils_buf_vxd_adjust_size(unsigned int *buf_size); + +int vdecddutils_ref_pic_hevc_get_maxnum(const struct vdec_comsequ_hdrinfo *comseq_hdrinfo, + unsigned int *max_ref_picnum); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdec_defs.h b/drivers/media/platform/vxe-vxd/decoder/vdec_defs.h --- a/drivers/media/platform/vxe-vxd/decoder/vdec_defs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdec_defs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,548 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD Decoder common header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef __VDEC_DEFS_H__ +#define __VDEC_DEFS_H__ + +#include "img_mem.h" +#include "img_pixfmts.h" +#ifdef HAS_JPEG +#include "jpegfw_data.h" +#endif +#include "pixel_api.h" +#include "vdecfw_shared.h" + +#define VDEC_MAX_PANSCAN_WINDOWS 4 +#define VDEC_MB_DIMENSION (16) + +#define MAX_PICS_IN_SYSTEM (8) +#define SEQUENCE_SLOTS (8) +#define PPS_SLOTS (8) +/* Only for HEVC */ +#define VPS_SLOTS (16) +#define MAX_VPSS (MAX_PICS_IN_SYSTEM + VPS_SLOTS) +#define MAX_SEQUENCES (MAX_PICS_IN_SYSTEM + SEQUENCE_SLOTS) +#define MAX_PPSS (MAX_PICS_IN_SYSTEM + PPS_SLOTS) + +#define VDEC_H264_MAXIMUMVALUEOFCPB_CNT 32 +#define VDEC_H264_MVC_MAX_VIEWS (H264FW_MAX_NUM_VIEWS) + +#define VDEC_ASSERT(expected) ({ WARN_ON(!(expected)); 0; }) + +#define VDEC_ALIGN_SIZE(_val, _alignment, val_type, align_type) \ + ({ \ + val_type val = _val; \ + align_type alignment = _alignment; \ + (((val) + (alignment) - 1) & ~((alignment) - 1)); }) + +/* + * This type defines the video standard. + * @brief VDEC Video Standards + */ +enum vdec_vid_std { + VDEC_STD_UNDEFINED = 0, + VDEC_STD_MPEG2, + VDEC_STD_MPEG4, + VDEC_STD_H263, + VDEC_STD_H264, + VDEC_STD_VC1, + VDEC_STD_AVS, + VDEC_STD_REAL, + VDEC_STD_JPEG, + VDEC_STD_VP6, + VDEC_STD_VP8, + VDEC_STD_SORENSON, + VDEC_STD_HEVC, + VDEC_STD_MAX, + VDEC_STD_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the bitstream format. Should be done at the + * start of decoding. + * @brief VDEC Bitstream Format + */ +enum vdec_bstr_format { + VDEC_BSTRFORMAT_UNDEFINED = 0, + VDEC_BSTRFORMAT_ELEMENTARY, + VDEC_BSTRFORMAT_DEMUX_BYTESTREAM, + VDEC_BSTRFORMAT_DEMUX_SIZEDELIMITED, + VDEC_BSTRFORMAT_MAX, + VDEC_BSTRFORMAT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the Type of payload. Could change with every buffer. + * @brief VDEC Bitstream Element Type + */ +enum vdec_bstr_element_type { + VDEC_BSTRELEMENT_UNDEFINED = 0, + VDEC_BSTRELEMENT_UNSPECIFIED, + VDEC_BSTRELEMENT_CODEC_CONFIG, + VDEC_BSTRELEMENT_PICTURE_DATA, + VDEC_BSTRELEMENT_MAX, + VDEC_BSTRELEMENT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This structure contains the stream configuration details. + * @brief VDEC Stream Configuration Information + */ +struct vdec_str_configdata { + enum vdec_vid_std vid_std; + enum vdec_bstr_format bstr_format; + unsigned int user_str_id; + unsigned char update_yuv; + unsigned char bandwidth_efficient; + unsigned char disable_mvc; + unsigned char full_scan; + unsigned char immediate_decode; + unsigned char intra_frame_closed_gop; +}; + +/* + * This type defines the buffer type categories. + * @brief Buffer Types + */ +enum vdec_buf_type { + VDEC_BUFTYPE_BITSTREAM, + VDEC_BUFTYPE_PICTURE, + VDEC_BUFTYPE_ALL, + VDEC_BUFTYPE_MAX, + VDEC_BUFTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This structure contains information related to a picture plane. + * @brief Picture Plane Information + */ +struct vdec_plane_info { + unsigned int offset; + unsigned int stride; + unsigned int size; +}; + +/* + * This structure describes the VDEC picture dimensions. + * @brief VDEC Picture Size + */ +struct vdec_pict_size { + unsigned int width; + unsigned int height; +}; + +/* + * This enumeration defines the colour plane indices. + * @brief Colour Plane Indices + */ +enum vdec_color_planes { + VDEC_PLANE_VIDEO_Y = 0, + VDEC_PLANE_VIDEO_YUV = 0, + VDEC_PLANE_VIDEO_U = 1, + VDEC_PLANE_VIDEO_UV = 1, + VDEC_PLANE_VIDEO_V = 2, + VDEC_PLANE_VIDEO_A = 3, + VDEC_PLANE_LIGHT_R = 0, + VDEC_PLANE_LIGHT_G = 1, + VDEC_PLANE_LIGHT_B = 2, + VDEC_PLANE_INK_C = 0, + VDEC_PLANE_INK_M = 1, + VDEC_PLANE_INK_Y = 2, + VDEC_PLANE_INK_K = 3, + VDEC_PLANE_MAX = 4, + VDEC_PLANE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This structure describes the rendered region of a picture buffer (i.e. where + * the image data is written. + * @brief Picture Buffer Render Information + */ +struct vdec_pict_rendinfo { + unsigned int rendered_size; + struct vdec_plane_info plane_info[VDEC_PLANE_MAX]; + unsigned int stride_alignment; + struct vdec_pict_size rend_pict_size; +}; + +/* + * This structure contains information required to configure the picture + * buffers + * @brief Picture Buffer Configuration + */ +struct vdec_pict_bufconfig { + unsigned int coded_width; + unsigned int coded_height; + enum img_pixfmt pixel_fmt; + unsigned int stride[IMG_MAX_NUM_PLANES]; + unsigned int stride_alignment; + unsigned char byte_interleave; + unsigned int buf_size; + unsigned char packed; + unsigned int chroma_offset[IMG_MAX_NUM_PLANES]; + unsigned int plane_size[IMG_MAX_NUM_PLANES]; +}; + +/* + * This structure describes the VDEC Display Rectangle. + * @brief VDEC Display Rectangle + */ +struct vdec_rect { + unsigned int top_offset; + unsigned int left_offset; + unsigned int width; + unsigned int height; +}; + +/* + * This structure contains the Color Space Description that may be present + * in SequenceDisplayExtn(MPEG2), VUI parameters(H264), Visual Object(MPEG4) + * for the application to use. + * @brief Stream Color Space Properties + */ +struct vdec_color_space_desc { + unsigned char is_present; + unsigned char color_primaries; + unsigned char transfer_characteristics; + unsigned char matrix_coefficients; +}; + +/* + * This structure contains common (standard agnostic) sequence header + * information, which is required for image buffer allocation and display. + * @brief Sequence Header Information (common) + */ +struct vdec_comsequ_hdrinfo { + unsigned int codec_profile; + unsigned int codec_level; + unsigned int bitrate; + long frame_rate; + unsigned int frame_rate_num; + unsigned int frame_rate_den; + unsigned int aspect_ratio_num; + unsigned int aspect_ratio_den; + unsigned char interlaced_frames; + struct pixel_pixinfo pixel_info; + struct vdec_pict_size max_frame_size; + unsigned int max_ref_frame_num; + struct vdec_pict_size frame_size; + unsigned char field_codec_mblocks; + unsigned int min_pict_buf_num; + unsigned char picture_reordering; + unsigned char post_processing; + struct vdec_rect orig_display_region; + struct vdec_rect raw_display_region; + unsigned int num_views; + unsigned int max_reorder_picts; + unsigned char separate_chroma_planes; + unsigned char not_dpb_flush; + struct vdec_color_space_desc color_space_info; +}; + +/* + * This structure contains the standard specific codec configuration + * @brief Codec configuration + */ +struct vdec_codec_config { + unsigned int default_height; + unsigned int default_width; +}; + +/* + * This structure describes the decoded picture attributes (relative to the + * encoded, where necessary, e.g. rotation angle). + * @brief Stream Output Configuration + */ +struct vdec_str_opconfig { + struct pixel_pixinfo pixel_info; + unsigned char force_oold; +}; + +/* + * This type defines the "play" mode. + * @brief Play Mode + */ +enum vdec_play_mode { + VDEC_PLAYMODE_PARSE_ONLY, + VDEC_PLAYMODE_NORMAL_DECODE, + VDEC_PLAYMODE_MAX, + VDEC_PLAYMODE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the bitstream processing error info. + * @brief Bitstream Processing Error Info + */ +struct vdec_bstr_err_info { + unsigned int sequence_err; + unsigned int picture_err; + unsigned int other_err; +}; + +/* + * This structure describes the VDEC Pan Scan Window. + * @brief VDEC Pan Scan Window + */ +struct vdec_window { + unsigned int ui32topoffset; + unsigned int ui32leftoffset; + unsigned int ui32width; + unsigned int ui32height; +}; + +/* + * This structure contains the VDEC picture display properties. + * @brief VDEC Picture Display Properties + */ +struct vdec_pict_disp_info { + struct vdec_rect enc_disp_region; + struct vdec_rect disp_region; + struct vdec_rect raw_disp_region; + unsigned char top_fld_first; + unsigned char out_top_fld_first; + unsigned int max_frm_repeat; + unsigned int repeat_first_fld; + unsigned int num_pan_scan_windows; + struct vdec_window pan_scan_windows[VDEC_MAX_PANSCAN_WINDOWS]; +}; + +/* + * This structure contains VXD hardware signatures. + * @brief VXD Hardware signatures + */ +struct vdec_pict_hwcrc { + unsigned char first_fld_rcvd; + unsigned int crc_vdmc_pix_recon; + unsigned int vdeb_sysmem_wrdata; +}; + +struct vdec_features { + unsigned char valid; + unsigned char mpeg2; + unsigned char mpeg4; + unsigned char h264; + unsigned char vc1; + unsigned char avs; + unsigned char real; + unsigned char jpeg; + unsigned char vp6; + unsigned char vp8; + unsigned char hevc; + unsigned char hd; + unsigned char rotation; + unsigned char scaling; + unsigned char scaling_oold; + unsigned char scaling_extnd_strides; +}; + +/* + * This type defines the auxiliary info for picture queued for decoding. + * @brief Auxiliary Decoding Picture Info + */ +struct vdec_dec_pict_auxinfo { + unsigned int seq_hdr_id; + unsigned int pps_id; + unsigned int second_pps_id; + unsigned char not_decoded; +}; + +/* + * This type defines the decoded picture state. + * @brief Decoded Picture State + */ +enum vdec_pict_state { + VDEC_PICT_STATE_NOT_DECODED, + VDEC_PICT_STATE_DECODED, + VDEC_PICT_STATE_TERMINATED, + VDEC_PICT_STATE_MAX, + VDEC_PICT_STATE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the container for various picture tags. + * @brief Picture Tag Container + */ +struct vdec_pict_tag_container { + enum img_buffer_type pict_type; + unsigned long long pict_tag_param; + unsigned long long sideband_info; + struct vdec_pict_hwcrc pict_hwcrc; +}; + +/* + * This structure describes raw bitstream data chunk. + * @brief Raw Bitstream Data Chunk + */ +struct vdec_raw_bstr_data { + unsigned int size; + unsigned int bit_offset; + unsigned char *data; + struct vdec_raw_bstr_data *next; +}; + +/* + * This type defines the supplementary picture data. + * @brief Supplementary Picture Data + */ +struct vdec_pict_supl_data { + struct vdec_raw_bstr_data *raw_vui_data; + struct vdec_raw_bstr_data *raw_sei_list_first_fld; + struct vdec_raw_bstr_data *raw_sei_list_second_fld; + union { + struct h264_pict_supl_data { + unsigned char nal_ref_idc; + unsigned short frame_num; + } data; + }; +}; + +/* + * This structure contains decoded picture information for display. + * @brief Decoded Picture Information + */ +struct vdec_dec_pict_info { + enum vdec_pict_state pict_state; + enum img_buffer_type buf_type; + unsigned char interlaced_flds; + unsigned int err_flags; + unsigned int err_level; + struct vdec_pict_tag_container first_fld_tag_container; + struct vdec_pict_tag_container second_fld_tag_container; + struct vdec_str_opconfig op_config; + struct vdec_pict_rendinfo rend_info; + struct vdec_pict_disp_info disp_info; + unsigned int last_in_seq; + unsigned int decode_id; + unsigned int id_for_hwcrc_chk; + unsigned short view_id; + unsigned int timestamp; + struct vdec_pict_supl_data pict_supl_data; +}; + +struct vdec_pict_rend_config { + struct vdec_pict_size coded_pict_size; + unsigned char packed; + unsigned char byte_interleave; + unsigned int stride_alignment; +}; + +/* + * This structure contains unsupported feature flags. + * @brief Unsupported Feature Flags + */ +struct vdec_unsupp_flags { + unsigned int str_cfg; + unsigned int str_opcfg; + unsigned int op_bufcfg; + unsigned int seq_hdr; + unsigned int pict_hdr; +}; + +/* + * This type defines the error , error in parsing, error in decoding etc. + * @brief VDEC parsing/decoding error Information + */ +enum vdec_error_type { + VDEC_ERROR_NONE = (0), + VDEC_ERROR_SR_ERROR = (1 << 0), + VDEC_ERROR_FEHW_TIMEOUT = (1 << 1), + VDEC_ERROR_FEHW_DECODE = (1 << 2), + VDEC_ERROR_BEHW_TIMEOUT = (1 << 3), + VDEC_ERROR_SERVICE_TIMER_EXPIRY = (1 << 4), + VDEC_ERROR_MISSING_REFERENCES = (1 << 5), + VDEC_ERROR_MMU_FAULT = (1 << 6), + VDEC_ERROR_DEVICE = (1 << 7), + VDEC_ERROR_CORRUPTED_REFERENCE = (1 << 8), + VDEC_ERROR_MMCO = (1 << 9), + VDEC_ERROR_MBS_DROPPED = (1 << 10), + VDEC_ERROR_MAX = (1 << 11), + VDEC_ERROR_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This structure contains information relating to a buffer. + * @brief Buffer Information + */ +struct vdec_buf_info { + void *cpu_linear_addr; + unsigned int buf_id; + struct vdec_pict_bufconfig pictbuf_cfg; + int fd; + /* The following are fields used internally within VDEC... */ + unsigned int buf_size; + enum sys_emem_attrib mem_attrib; + void *buf_alloc_handle; + void *buf_map_handle; +}; + +#ifdef HAS_JPEG +/* + * This structure contains JPEG sequence header information. + * NOTE: Should only contain JPEG specific information. + * @brief JPEG sequence header Information + */ +struct vdec_jpeg_sequ_hdr_info { + /* total component in jpeg */ + unsigned char num_component; + /* precision */ + unsigned char precision; +}; + +/* + * This structure contains JPEG start of frame segment header + * NOTE: Should only contain JPEG specific information. + * @brief JPEG SOF header Information + */ +struct vdec_jpeg_sof_component_hdr { + /* component identifier. */ + unsigned char identifier; + /* Horizontal scaling. */ + unsigned char horz_factor; + /* Verticale scaling */ + unsigned char vert_factor; + /* Qunatisation tables . */ + unsigned char quant_table; +}; + +/* + * This structure contains JPEG start of scan segment header + * NOTE: Should only contain JPEG specific information. + * @brief JPEG SOS header Information + */ +struct vdec_jpeg_sos_component_hdr { + /* component identifier. */ + unsigned char component_index; + /* Huffman DC tables. */ + unsigned char dc_table; + /* Huffman AC table .*/ + unsigned char ac_table; +}; + +struct vdec_jpeg_pict_hdr_info { + /* Start of frame component header */ + struct vdec_jpeg_sof_component_hdr sof_comp[JPEG_VDEC_MAX_COMPONENTS]; + /* Start of Scan component header */ + struct vdec_jpeg_sos_component_hdr sos_comp[JPEG_VDEC_MAX_COMPONENTS]; + /* Huffman tables */ + struct vdec_jpeg_huffman_tableinfo huff_tables[JPEG_VDEC_TABLE_CLASS_NUM] + [JPEG_VDEC_MAX_SETS_HUFFMAN_TABLES]; + /* Quantization tables */ + struct vdec_jpeg_de_quant_tableinfo quant_tables[JPEG_VDEC_MAX_QUANT_TABLES]; + /* Number of MCU in the restart interval */ + unsigned short interval; + unsigned int test; +}; +#endif + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdecfw_shared.h b/drivers/media/platform/vxe-vxd/decoder/vdecfw_shared.h --- a/drivers/media/platform/vxe-vxd/decoder/vdecfw_shared.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdecfw_shared.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,893 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public data structures and enums for the firmware + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifdef USE_SHARING +#endif + +#ifndef _VDECFW_H_ +#define _VDECFW_H_ + +#include "img_msvdx_core_regs.h" +#include "vdecfw_share.h" + +/* brief This type defines the buffer type */ +enum img_buffer_type { + IMG_BUFFERTYPE_FRAME = 0, + IMG_BUFFERTYPE_FIELD_TOP, + IMG_BUFFERTYPE_FIELD_BOTTOM, + IMG_BUFFERTYPE_PAIR, + IMG_BUFFERTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Number of scaling coefficients */ +#define VDECFW_NUM_SCALE_COEFFS 4 + +/* + * maximum number of pictures handled by the firmware + * for H.264 (largest requirement): 32 for 4 view MVC + */ +#define VDECFW_MAX_NUM_PICTURES 32 +#define VDECFW_MAX_NUM_VIEWS 4 +#define EMERALD_CORE 6 + +/* + * maximum number of colocated pictures handled by + * firmware in FWBSP mode + */ +#define VDECFWBSP_MAX_NUM_COL_PICS 16 + +/* Maximum number of colour planes. */ +#define VDECFW_PLANE_MAX 4 + +#define VDECFW_NON_EXISTING_PICTURE_TID (0xffffffff) + +#define NO_VALUE 0 + +/* Indicates whether a cyclic sequence number (x) has reached another (y). */ +#define HAS_X_REACHED_Y(x, y, range, type) \ + ({ \ + type __x = x; \ + type __y = y; \ + type __range = range; \ + (((((__x) - (__y) + (__range)) % (__range)) <= \ + (((__y) - (__x) + (__range)) % (__range))) ? TRUE : FALSE); }) + +/* Indicates whether a cyclic sequence number (x) has passed another (y). */ +#define HAS_X_PASSED_Y(x, y, range, type) \ + ({ \ + type __x = x; \ + type __y = y; \ + type __range = range; \ + (((((__x) - (__y) + (__range)) % (__range)) < \ + (((__y) - (__x) + (__range)) % (__range))) ? TRUE : FALSE); }) + +#define FWIF_BIT_MASK(num) ((1 << (num)) - 1) + +/* + * Number of bits in transaction ID used to represent picture number in stream. + */ +#define FWIF_NUMBITS_STREAM_PICTURE_ID 16 +/* Number of bits in transaction ID used to represent picture number in core. */ +#define FWIF_NUMBITS_CORE_PICTURE_ID 4 +/* Number of bits in transaction ID used to represent stream id. */ +#define FWIF_NUMBITS_STREAM_ID 8 +/* Number of bits in transaction ID used to represent core id. */ +#define FWIF_NUMBITS_CORE_ID 4 + +/* Offset in transaction ID to picture number in stream. */ +#define FWIF_OFFSET_STREAM_PICTURE_ID 0 +/* Offset in transaction ID to picture number in core. */ +#define FWIF_OFFSET_CORE_PICTURE_ID \ + (FWIF_OFFSET_STREAM_PICTURE_ID + FWIF_NUMBITS_STREAM_PICTURE_ID) +/* Offset in transaction ID to stream id. */ +#define FWIF_OFFSET_STREAM_ID \ + (FWIF_OFFSET_CORE_PICTURE_ID + FWIF_NUMBITS_CORE_PICTURE_ID) +/* Offset in transaction ID to core id. */ +#define FWIF_OFFSET_CORE_ID \ + (FWIF_OFFSET_STREAM_ID + FWIF_NUMBITS_STREAM_ID) + +/* Picture id (stream) from transaction id. */ +#define GET_STREAM_PICTURE_ID(transaction_id) \ + ((transaction_id) & FWIF_BIT_MASK(FWIF_NUMBITS_STREAM_PICTURE_ID)) +/* Picture id (core) from transaction id. */ +#define GET_CORE_PICTURE_ID(transaction_id) \ + (((transaction_id) >> FWIF_OFFSET_CORE_PICTURE_ID) & \ + FWIF_BIT_MASK(FWIF_NUMBITS_CORE_PICTURE_ID)) +/* Stream id from transaction id. */ +#define GET_STREAM_ID(transaction_id) \ + (((transaction_id) >> FWIF_OFFSET_STREAM_ID) & \ + FWIF_BIT_MASK(FWIF_NUMBITS_STREAM_ID)) +/* Core id from transaction id. */ +#define GET_CORE_ID(transaction_id) \ + (((transaction_id) >> FWIF_OFFSET_CORE_ID) & \ + FWIF_BIT_MASK(FWIF_NUMBITS_CORE_ID)) + +/* Picture id (stream) for transaction id. */ +#define SET_STREAM_PICTURE_ID(str_pic_id) \ + (((str_pic_id) & FWIF_BIT_MASK(FWIF_NUMBITS_STREAM_PICTURE_ID)) << \ + FWIF_OFFSET_STREAM_PICTURE_ID) +/* Picture id (core) for transaction id. */ +#define SET_CORE_PICTURE_ID(core_pic_id) \ + (((core_pic_id) % (1 << FWIF_NUMBITS_CORE_PICTURE_ID)) << \ + FWIF_OFFSET_CORE_PICTURE_ID) +/* Stream id for transaction id. */ +#define SET_STREAM_ID(stream_id) \ + (((stream_id) & FWIF_BIT_MASK(FWIF_NUMBITS_STREAM_ID)) << \ + FWIF_OFFSET_STREAM_ID) +/* Core id for transaction id. */ +#define SET_CORE_ID(core_id) \ + (((core_id) & FWIF_BIT_MASK(FWIF_NUMBITS_CORE_ID)) << \ + FWIF_OFFSET_CORE_ID) +/* flag checking */ +#define FLAG_MASK(_flagname_) ((1 << _flagname_ ## _SHIFT)) +#define FLAG_IS_SET(_flagsword_, _flagname_) \ + (((_flagsword_) & FLAG_MASK(_flagname_)) ? TRUE : FALSE) + +/* This type defines the parser component types */ +enum vdecfw_codectype { + VDECFW_CODEC_H264 = 0, /* H.264, AVC, MVC */ + VDECFW_CODEC_MPEG4, /* MPEG4, H.263, DivX, Sorenson */ + VDECFW_CODEC_VP8, /* VP8 */ + + VDECFW_CODEC_VC1, /* VC1 (includes WMV9) */ + VDECFW_CODEC_MPEG2, /* MPEG2 */ + + VDECFW_CODEC_JPEG, /* JPEG */ + + VDECFW_CODEC_VP6, /* VP6 */ + VDECFW_CODEC_AVS, /* AVS */ + VDECFW_CODEC_RV, /* RV30, RV40 */ + + VDECFW_CODEC_HEVC, /* HEVC/H265 */ + + VDECFW_CODEC_VP9, /* VP9 */ + + VDECFW_CODEC_MAX, /* End Marker */ + + VDEC_CODEC_NONE = -1, /* No codec */ + VDEC_CODEC_FORCE32BITS = 0x7FFFFFFFU +}; + +/* This type defines the FW parser mode - SCP, size delimited, etc. */ +enum vdecfw_parsermode { + /* Every NAL is expected to have SCP */ + VDECFW_SCP_ONLY = 0, + /* Every NAL is expect to be size delimited with field size 4 */ + VDECFW_SIZE_DELIMITED_4_ONLY, + /* Every NAL is expect to be size delimited with field size 2 */ + VDECFW_SIZE_DELIMITED_2_ONLY, + /* Every NAL is expect to be size delimited with field size 1 */ + VDECFW_SIZE_DELIMITED_1_ONLY, + /* Size of NAL is provided in the picture header */ + VDECFW_SIZE_SIDEBAND, + /* Unit is a skipped picture with no data to process */ + VDECFW_SKIPPED_PICTURE, + VDECFW_SKIPPED_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This enum defines values of ENTDEC_BE_MODE field of VEC_ENTDEC_BE_CONTROL + * register and ENTDEC_FE_MODE field of VEC_ENTDEC_FE_CONTROL register. + */ +enum vdecfw_msvdxentdecmode { + /* JPEG */ + VDECFW_ENTDEC_MODE_JPEG = 0x0, + /* H264 (MPEG4/AVC) */ + VDECFW_ENTDEC_MODE_H264 = 0x1, + /* VC1 */ + VDECFW_ENTDEC_MODE_VC1 = 0x2, + /* MPEG2 */ + VDECFW_ENTDEC_MODE_MPEG2 = 0x3, + /* MPEG4 */ + VDECFW_ENTDEC_MODE_MPEG4 = 0x4, + /* AVS */ + VDECFW_ENTDEC_MODE_AVS = 0x5, + /* WMV9 */ + VDECFW_ENTDEC_MODE_WMV9 = 0x6, + /* MPEG1 */ + VDECFW_ENTDEC_MODE_MPEG1 = 0x7, + /* RealVideo8, with ENTDEC_[BE|FE]_EXTENDED_MODE bit set */ + VDECFW_ENTDEC_MODE_EXT_REAL8 = 0x0, + /* RealVideo9, with ENTDEC_[BE|FE]_EXTENDED_MODE bit set */ + VDECFW_ENTDEC_MODE_EXT_REAL9 = 0x1, + /* VP6, with ENTDEC_[BE|FE]_EXTENDED_MODE bit set */ + VDECFW_ENTDEC_MODE_EXT_VP6 = 0x2, + /* VP8, with ENTDEC_[BE|FE]_EXTENDED_MODE bit set */ + VDECFW_ENTDEC_MODE_EXT_VP8 = 0x3, + /* SVC, with ENTDEC_[BE|FE]_EXTENDED_MODE bit set */ + VDECFW_ENTDEC_MODE_EXT_SVC = 0x4, + VDECFW_ENTDEC_MODE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This describes the Firmware Parser checkpoints in VEC Local RAM. + * Each checkpoint is updated with the TransactionID of the picture as it passes + * that point in its decode. Together they describe the current position of + * pictures in the VXD/Firmware pipeline. + * + * Numbers indicate point in the "VDEC Firmware Component Timing" diagram. + */ +enum vdecfw_progresscheckpoint { + /* Decode message has been read */ + VDECFW_CHECKPOINT_PICTURE_STARTED = 1, + /* Firmware has been loaded and bitstream DMA started */ + VDECFW_CHECKPOINT_FIRMWARE_READY = 2, + /* Picture management operations have completed */ + VDECFW_CHECKPOINT_PICMAN_COMPLETE = 3, + /* Firmware context for this picture has been saved */ + VDECFW_CHECKPOINT_FIRMWARE_SAVED = 4, + /* + * 1st Picture/Slice header has been read, + * registers written and Entdec started + */ + VDECFW_CHECKPOINT_ENTDEC_STARTED = 5, + /* 1st Slice has been completed by Entdec */ + VDECFW_CHECKPOINT_FE_1SLICE_DONE = 6, + /* Parsing of picture has completed on FE */ + VDECFW_CHECKPOINT_FE_PARSE_DONE = 7, + /* Picture end code has been read and picture closed */ + VDECFW_CHECKPOINT_FE_PICTURE_COMPLETE = 8, + /* Picture has started decoding on VXD Backend */ + VDECFW_CHECKPOINT_BE_PICTURE_STARTED = 9, + /* 1st Slice has completed on VXD Backend */ + VDECFW_CHECKPOINT_BE_1SLICE_DONE = 10, + /* Picture decode has completed and done message sent to the Host */ + VDECFW_CHECKPOINT_BE_PICTURE_COMPLETE = 11, +#ifndef FW_STACK_USAGE_TRACKING + /* General purpose check point 1 */ + VDECFW_CHECKPOINT_AUX1 = 12, + /* General purpose check point 2 */ + VDECFW_CHECKPOINT_AUX2 = 13, + /* General purpose check point 3 */ + VDECFW_CHECKPOINT_AUX3 = 14, + /* General purpose check point 4 */ + VDECFW_CHECKPOINT_AUX4 = 15, +#endif /* ndef FW_STACK_USAGE_TRACKING */ + VDECFW_CHECKPOINT_MAX, + /* + * Indicate which checkpoints mark the start and end of each + * group (FW, FE and BE). + * The start and end values should be updated if new checkpoints are + * added before the current start or after the current end of any group. + */ + VDECFW_CHECKPOINT_FW_START = VDECFW_CHECKPOINT_PICTURE_STARTED, + VDECFW_CHECKPOINT_FW_END = VDECFW_CHECKPOINT_FIRMWARE_SAVED, + VDECFW_CHECKPOINT_FE_START = VDECFW_CHECKPOINT_ENTDEC_STARTED, + VDECFW_CHECKPOINT_FE_END = VDECFW_CHECKPOINT_FE_PICTURE_COMPLETE, + VDECFW_CHECKPOINT_BE_START = VDECFW_CHECKPOINT_BE_PICTURE_STARTED, + VDECFW_CHECKPOINT_BE_END = VDECFW_CHECKPOINT_BE_PICTURE_COMPLETE, + VDECFW_CHECKPOINT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Number of auxiliary firmware checkpoints. */ +#define VDECFW_CHECKPOINT_AUX_COUNT 4 +/* This describes the action currently being done by the Firmware. */ +enum vdecfw_firmwareaction { + VDECFW_FWACT_IDLE = 1, /* Firmware is currently doing nothing */ + VDECFW_FWACT_BASE_LOADING_PSR, /* Loading parser context */ + VDECFW_FWACT_BASE_SAVING_PSR, /* Saving parser context */ + VDECFW_FWACT_BASE_LOADING_BEMOD, /* Loading Backend module */ + VDECFW_FWACT_BASE_LOADING_FEMOD, /* Loading Frontend module */ + VDECFW_FWACT_PARSER_SLICE, /* Parser active: parsing slice */ + VDECFW_FWACT_PARSER_PM, /* Parser active: picture management */ + VDECFE_FWACT_BEMOD_ACTIVE, /* Backend module active */ + VDECFE_FWACT_FEMOD_ACTIVE, /* Frontend module active */ + VDECFW_FWACT_MAX, + VDECFW_FWACT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This describes the FE_ERR flags word in the VDECFW_MSGID_PIC_DECODED message + */ +enum vdecfw_msgflagdecodedfeerror { + /* Front-end hardware watchdog timeout (FE_WDT_CM0) */ + VDECFW_MSGFLAG_DECODED_FEERROR_HWWDT_SHIFT = 0, + /* Front-end entdec error (VEC_ERROR_DETECTED_ENTDEC) */ + VDECFW_MSGFLAG_DECODED_FEERROR_ENTDECERROR_SHIFT, + /* Shift-register error (VEC_ERROR_DETECTED_SR) */ + VDECFW_MSGFLAG_DECODED_FEERROR_SRERROR_SHIFT, + /* For cases when B frame comes after I without P. */ + VDECFW_MSGFLAG_DECODED_MISSING_REFERENCES_SHIFT, + /* MMCO operation failed. */ + VDECFW_MSGFLAG_DECODED_MMCO_ERROR_SHIFT, + /* Back-end WDT timeout */ + VDECFW_MSGFLAG_DECODED_BEERROR_HWWDT_SHIFT, + /* Some macroblocks were dropped */ + VDECFW_MSGFLAG_DECODED_MBS_DROPPED_ERROR_SHIFT, + VDECFW_MSGFLAG_DECODED_FEERROR_MAX, + VDECFW_MSGFLAG_DECODED_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This type defines the IDs of the messages used to communicate with the + * Firmware. + * + * The Firmware has 3 message buffers, each buffer uses a different set of IDs. + * The buffers are: + * Host -> FW -Control messages(High Priority: processed in interrupt context) + * Host -> FW -Decode commands and associated information + * (Normal Priority: processed in baseloop) + * FW -> Host -Completion message + */ +enum vdecfw_message_id { + /* Control Messages */ + /* + * Host -> FW Padding message + * Sent to optionally pad the message buffer + */ + VDECFW_MSGID_BASE_PADDING = 0x01, + /* + * Host -> FW Initialisation message Initialisation should be + * sent *immediately* after loading the base component + * ie. while the FW is idle + */ + VDECFW_MSGID_FIRMWARE_INIT, + /* + * Host -> FW Configuration message + * Configuration should be setup after loading the base component + * and before decoding the next picture ie. while the FW is idle + */ + VDECFW_MSGID_FIRMWARE_CONFIG, + /* + * Host -> FW Control message + * Firmware control command to have immediate affect + * eg. Stop stream, return CRCs, return Performance Data + */ + VDECFW_MSGID_FIRMWARE_CONTROL, + VDECFW_MSGID_CONTROL_MAX, + /* Decode Commands */ + /* + * Host -> FW Padding message + * Sent to optionally pad the message buffer + */ + VDECFW_MSGID_PSR_PADDING = 0x40, + /* + * Host -> FW Decode message + * Describes the picture to decode + */ + VDECFW_MSGID_DECODE_PICTURE, + /* + * Host -> FW Bitstream buffer information + * Information describing a bitstream buffer to DMA to VXD + */ + VDECFW_MSGID_BITSTREAM_BUFFER, + /* + * Host -> FW Fence message + * Generate an interrupt when this is read, + * FenceID should be written to a location in VLR + */ + VDECFW_MSGID_FENCE, + /* + * Host -> FW Batch message + * Contains a pointer to a host memory buffer + * containing a batch of decode command FW messages + */ + VDECFW_MSGID_BATCH, + VDECFW_MSGID_DECODE_MAX, + /* Completion Messages */ + /* + * FW -> Host Padding message + * Sent to optionally pad the message buffer + */ + VDECFW_MSGID_BE_PADDING = 0x80, + /* + * FW -> Host Decoded Picture message + * Notification of decoded picture including errors recorded + */ + VDECFW_MSGID_PIC_DECODED, + /* + * FW -> Host CRC message + * Optionally sent with Decoded Picture message, contains VXD CRCs + */ + VDECFW_MSGID_PIC_CRCS, + /* + * FW -> Host Performance message + * Optional timestamps at the decode checkpoints and other information + * about the image to assist in measuring performance + */ + VDECFW_MSGID_PIC_PERFORMANCE, + /* FW -> Host POST calculation test message */ + VDECFW_MSGID_PIC_POST_RESP, + VDECFW_MSGID_COMPLETION_MAX, + VDECFW_MSGID_FORCE32BITS = 0x7FFFFFFFU +}; + +#define VDECFW_MSGID_CONTROL_TYPES \ + (VDECFW_MSGID_CONTROL_MAX - VDECFW_MSGID_BASE_PADDING) +#define VDECFW_MSGID_DECODE_TYPES \ + (VDECFW_MSGID_DECODE_MAX - VDECFW_MSGID_PSR_PADDING) +#define VDECFW_MSGID_COMPLETION_TYPES \ + (VDECFW_MSGID_COMPLETION_MAX - VDECFW_MSGID_BE_PADDING) + +/* This describes the layout of PVDEC Firmware state indicators in Comms RAM. */ + +/* Maximum number of PVDEC decoding pipes per core supported. */ +#define VDECFW_MAX_DP 3 + +struct vdecfw_pvdecpipestate { + /* TransactionID at each checkpoint */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, check_point[VDECFW_CHECKPOINT_MAX]); + /* VDECFW_eFirmwareAction (UINT32 used to guarantee size) */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, firmware_action); + /* Number of FE Slices processed for the last picture in FE */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, fe_slices); + /* Number of BE Slices processed for the last picture in BE */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, be_slices); + /* + * Number of FE Slices being detected as erroed for the last picture + * in FE + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, fe_errored_slices); + /* + * Number of BE Slices being detected as erroed for the last picture + * in BE + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, be_errored_slices); + /* Number of BE macroblocks dropped for the last picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, be_mbs_dropped); + /* Number of BE macroblocks recovered for the last picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, be_mbs_recovered); + /* Number of FE macroblocks processed for the last picture in FE */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, last_fe_mb_xy); + /* Number of BE macroblocks processed for the last picture in BE */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, last_be_mb_xy); + /* VDECFW_eCodecType - Codec currently loaded */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, curr_codec); + /* TRUE if this pipe is available for processing */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, pipe_present); +}; + +#ifdef FW_STACK_USAGE_TRACKING +/* Stack usage info array size. */ +#define VDECFW_STACK_INFO_SIZE (VDECFW_MAX_DP * VDECFW_CHECKPOINT_AUX_COUNT) +#endif /* FW_STACK_USAGE_TRACKING */ +struct vdecfw_pvdecfirmwarestate { + /* + * Indicates generic progress taken by firmware + * (must be the first item) + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, fwstep); + /* Pipe state array. */ + struct vdecfw_pvdecpipestate pipestate[VDECFW_MAX_DP]; +#ifdef FW_STACK_USAGE_TRACKING + /* Stack usage info array. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, unsigned int, + stackinfo[VDECFW_STACK_INFO_SIZE]); +#endif /* FW_STACK_USAGE_TRACKING */ +}; + +/* + * This describes the flags word in the aui8DisplayFlags + * in VDECFW_sBufferControl + */ +enum vdecfw_bufflagdisplay { + /* TID has been flushed with a "no display" indication */ + VDECFW_BUFFLAG_DISPLAY_NODISPLAY_SHIFT = 0, + /* TID contains an unpaired field */ + VDECFW_BUFFLAG_DISPLAY_SINGLE_FIELD_SHIFT = 1, + /* TID contains field coded picture(s) - single field or pair */ + VDECFW_BUFFLAG_DISPLAY_FIELD_CODED_SHIFT = 2, + /* if TID contains a single field, this defines which field that is */ + VDECFW_BUFFLAG_DISPLAY_BOTTOM_FIELD_SHIFT = 3, + /* if TID contains a frame with two interlaced fields */ + VDECFW_BUFFLAG_DISPLAY_INTERLACED_FIELDS_SHIFT = 4, + /* End marker */ + VDECFW_BUFFLAG_DISPLAY_MAX = 8, + VDECFW_BUFFLAG_DISPLAY_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This describes the flags in the ui8PictMgmtFlags field in + * VDECFW_sBufferControl + */ +enum vdecfw_picmgmflags { + /* Picture management for this picture successfully executed */ + VDECFW_PICMGMTFLAG_PICTURE_EXECUTED_SHIFT = 0, + /* + * Picture management for the first field of this picture + * successfully executed + */ + VDECFW_PICMGMTFLAG_1ST_FIELD_EXECUTED_SHIFT = 0, + /* + * Picture management for the second field of this picture + * successfully executed + */ + VDECFW_PICMGMTFLAG_2ND_FIELD_EXECUTED_SHIFT = 1, + VDECFW_PICMGMTFLAG_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Macro for checking if picture management was successfully executed for + * field coded picture + */ +#define VDECFW_PICMGMT_FIELD_CODED_PICTURE_EXECUTED(_flagsword_) \ + ((FLAG_IS_SET(buf_control->picmgmt_flags, \ + VDECFW_PICMGMTFLAG_1ST_FIELD_EXECUTED) && \ + FLAG_IS_SET(buf_control->picmgmt_flags, \ + VDECFW_PICMGMTFLAG_2ND_FIELD_EXECUTED)) ? \ + TRUE : FALSE) +/* This describes the REAL related data for the current picture. */ +struct vdecfw_real_data { + /* Picture width */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, width); + /* Picture height */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, height); + /* Scaled Picture Width */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, scaled_width); + /* Scaled Picture Height */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, scaled_height); + /* Timestamp parsed in the firmware */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, timestamp); +}; + +/* This describes the HEVC related data for the current picture. */ +struct vdecfw_hevcdata { + /* POC */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, int, pic_order_count); +}; + +/* + * This describes the buffer control structure that is used by the firmware to + * signal to the Host to control the display and release of buffers. + */ +struct vdecfw_buffer_control { + /* + * List of TransactionIDs indicating buffers ready to display, + * in display order + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, display_list[VDECFW_MAX_NUM_PICTURES]); + /* + * List of TransactionIDs indicating buffers that are no longer + * required for reference + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + release_list[VDECFW_MAX_NUM_PICTURES + + VDECFW_MAX_NUM_VIEWS]); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, + display_view_ids[VDECFW_MAX_NUM_PICTURES]); + /* List of flags for each TID in the DisplayList */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, display_flags[VDECFW_MAX_NUM_PICTURES]); + /* Number of TransactionIDs in aui32DisplayList */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, display_list_length); + /* Number of TransactionIDs in aui32ReleaseList */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, release_list_length); + union { + struct vdecfw_real_data real_data; + struct vdecfw_hevcdata hevc_data; + }; + /* + * Refers to the picture decoded with the current transaction ID + * (not affected by merge with field of previous transaction ID) + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum img_buffer_type, dec_pict_type); + /* Set if the current field is a pair to the previous field */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, second_field_of_pair); + /* + * Set if for a pair we decoded first the top field or + * if we have only top field + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, top_field_first); + /* Top field is first to be displayed */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, out_top_field_first); + /* Picture management flags for this picture */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, picmgmt_flags); + /* + * List of TransactionIDs indicating buffers used as references + * when decoding current picture + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, ref_list[VDECFW_MAX_NUM_PICTURES]); +}; + +/* + * This describes an image buffer for one picture supplied to + * the firmware by the host + */ +struct vdecfw_image_buffer { + /* Virtual Address of each plane */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, byte_offset[VDECFW_PLANE_MAX]); +}; + +/* This type defines the picture commands that are prepared for the firmware. */ +enum vdecfw_picture_cmds { + /* Reconstructed buffer */ + /* DISPLAY_PICTURE_SIZE */ + VDECFW_CMD_DISPLAY_PICTURE, + /* CODED_PICTURE_SIZE */ + VDECFW_CMD_CODED_PICTURE, + /* OPERATING_MODE */ + VDECFW_CMD_OPERATING_MODE, + /* LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES */ + VDECFW_CMD_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS, + /* CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES */ + VDECFW_CMD_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS, + /* CHROMA2_RECONSTRUCTED_PICTURE_BASE_ADDRESSES */ + VDECFW_CMD_CHROMA2_RECONSTRUCTED_PICTURE_BASE_ADDRESS, + /* VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS */ + VDECFW_CMD_LUMA_ALTERNATIVE_PICTURE_BASE_ADDRESS, + /* VC1_CHROMA_RANGE_MAPPING_BASE_ADDRESS */ + VDECFW_CMD_CHROMA_ALTERNATIVE_PICTURE_BASE_ADDRESS, + /* CHROMA2_ALTERNATIVE_PICTURE_BASE_ADDRESS */ + VDECFW_CMD_CHROMA2_ALTERNATIVE_PICTURE_BASE_ADDRESS, + /* LUMA_ERROR_PICTURE_BASE_ADDRESSES */ + VDECFW_CMD_LUMA_ERROR_PICTURE_BASE_ADDRESS, + /* CHROMA_ERROR_PICTURE_BASE_ADDRESSES */ + VDECFW_CMD_CHROMA_ERROR_PICTURE_BASE_ADDRESS, + /* AUX_MSB_BUFFER_BASE_ADDRESSES (VC-1 only) */ + VDECFW_CMD_AUX_MSB_BUFFER, + /* INTRA_BUFFER_BASE_ADDRESS (various) */ + VDECFW_CMD_INTRA_BUFFER_BASE_ADDRESS, + /* AUX_LINE_BUFFER_BASE_ADDRESS */ + VDECFW_CMD_AUX_LINE_BUFFER_BASE_ADDRESS, + /* MBFLAGS_BUFFER_BASE_ADDRESSES (VP8 only) */ + VDECFW_CMD_MBFLAGS_BUFFER_BASE_ADDRESS, + /* FIRST_PARTITION_BASE_ADDRESSES (VP8 only) */ + VDECFW_CMD_FIRST_PARTITION_BUFFER_BASE_ADDRESS, + /* CURRENT_PICTURE_BUFFER_BASE_ADDRESSES (VP8 only) */ + VDECFW_CMD_CURRENT_PICTURE_BUFFER_BASE_ADDRESS, + /* SEGMENTID_BUFFER_BASE_ADDRESSES (VP8 only) */ + VDECFW_CMD_SEGMENTID_BASE_ADDRESS, + /* EXT_OP_MODE (H.264 only) */ + VDECFW_CMD_EXT_OP_MODE, + /* MC_CACHE_CONFIGURATION */ + VDECFW_CMD_MC_CACHE_CONFIGURATION, + /* Alternative output buffer (rotation etc.) */ + /* ALTERNATIVE_OUTPUT_PICTURE_ROTATION */ + VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + /* EXTENDED_ROW_STRIDE */ + VDECFW_CMD_EXTENDED_ROW_STRIDE, + /* CHROMA_ROW_STRIDE (H.264 only) */ + VDECFW_CMD_CHROMA_ROW_STRIDE, + /* ALTERNATIVE_OUTPUT_CONTROL */ + VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL, + /* RPR specific commands */ + /* RPR_AX_INITIAL */ + VDECFW_CMD_RPR_AX_INITIAL, + /* RPR_AX_INCREMENT */ + VDECFW_CMD_RPR_AX_INCREMENT, + /* RPR_AY_INITIAL */ + VDECFW_CMD_RPR_AY_INITIAL, + /* RPR_AY_INCREMENT */ + VDECFW_CMD_RPR_AY_INCREMENT, + /* RPR_PICTURE_SIZE */ + VDECFW_CMD_RPR_PICTURE_SIZE, + /* Scaling specific params */ + /* SCALED_DISPLAY_SIZE */ + VDECFW_CMD_SCALED_DISPLAY_SIZE, + /* HORIZONTAL_SCALE_CONTROL */ + VDECFW_CMD_HORIZONTAL_SCALE_CONTROL, + /* SCALE_HORIZONTAL_CHROMA (H.264 only) */ + VDECFW_CMD_SCALE_HORIZONTAL_CHROMA, + /* VERTICAL_SCALE_CONTROL */ + VDECFW_CMD_VERTICAL_SCALE_CONTROL, + /* SCALE_VERTICAL_CHROMA (H.264 only) */ + VDECFW_CMD_SCALE_VERTICAL_CHROMA, + /* HORIZONTAL_LUMA_COEFFICIENTS_0 */ + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_0, + /* HORIZONTAL_LUMA_COEFFICIENTS_1 */ + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_1, + /* HORIZONTAL_LUMA_COEFFICIENTS_2 */ + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_2, + /* HORIZONTAL_LUMA_COEFFICIENTS_3 */ + VDECFW_CMD_HORIZONTAL_LUMA_COEFFICIENTS_3, + /* VERTICAL_LUMA_COEFFICIENTS_0 */ + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_0, + /* VERTICAL_LUMA_COEFFICIENTS_1 */ + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_1, + /* VERTICAL_LUMA_COEFFICIENTS_2 */ + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_2, + /* VERTICAL_LUMA_COEFFICIENTS_3 */ + VDECFW_CMD_VERTICAL_LUMA_COEFFICIENTS_3, + /* HORIZONTAL_CHROMA_COEFFICIENTS_0 */ + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_0, + /* HORIZONTAL_CHROMA_COEFFICIENTS_1 */ + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_1, + /* HORIZONTAL_CHROMA_COEFFICIENTS_2 */ + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_2, + /* HORIZONTAL_CHROMA_COEFFICIENTS_3 */ + VDECFW_CMD_HORIZONTAL_CHROMA_COEFFICIENTS_3, + /* VERTICAL_CHROMA_COEFFICIENTS_0 */ + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_0, + /* VERTICAL_CHROMA_COEFFICIENTS_1 */ + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_1, + /* VERTICAL_CHROMA_COEFFICIENTS_2 */ + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_2, + /* VERTICAL_CHROMA_COEFFICIENTS_3 */ + VDECFW_CMD_VERTICAL_CHROMA_COEFFICIENTS_3, + /* SCALE_OUTPUT_SIZE */ + VDECFW_CMD_SCALE_OUTPUT_SIZE, + /* VDECFW_CMD_INTRA_BUFFER_PLANE_SIZE */ + VDECFW_CMD_INTRA_BUFFER_PLANE_SIZE, + /* VDECFW_CMD_INTRA_BUFFER_SIZE_PER_PIPE */ + VDECFW_CMD_INTRA_BUFFER_SIZE_PER_PIPE, + /* VDECFW_CMD_AUX_LINE_BUFFER_SIZE_PER_PIPE */ + VDECFW_CMD_AUX_LINE_BUFFER_SIZE_PER_PIPE, + VDECFW_SLICE_X_MB_OFFSET, + VDECFW_SLICE_Y_MB_OFFSET, + VDECFW_SLICE_TYPE, + VDECFW_CMD_MAX, + VDECFW_CMD_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Size of relocation data attached to VDECFW_sTransaction message in words */ +#define VDECFW_RELOC_SIZE 125 + +/* This structure defines the MMU Tile configuration. */ +struct vdecfw_mmu_tile_config { + /* MMU_CONTROL2 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned char, tilig_scheme); + /* MMU_TILE */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + mmu_tiling[MSVDX_CORE_CR_MMU_TILE_NO_ENTRIES]); + /* MMU_TILE_EXT */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, + mmu_tiling_ext[MSVDX_CORE_CR_MMU_TILE_EXT_NO_ENTRIES]); +}; + +/* + * This structure contains the transaction attributes to be given to the + * firmware + * @brief Transaction Attributes + */ +struct vdecfw_transaction { + /* Unique identifier for the picture (driver-wide). */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, transation_id); + /* Codec */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum vdecfw_codectype, codec); + /* + * Flag to indicate that the stream needs to ge handled + * in secure memory (if available) + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + int, secure_stream); + /* Unique identifier for the current stream */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, stream_id); + /* Dictates to the FW parser how the NALs are delimited */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + enum vdecfw_parsermode, parser_mode); + /* Address from which to load the parser context data. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, ctx_load_addr); + /* + * Address to save the parser state data including the updated + * "parser context data". + */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, ctx_save_addr); + /* Size of the parser context data in bytes. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, ctx_size); + /* Address to save the "buffer control" data. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, ctrl_save_addr); + /* Size of the buffer control data in bytes. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, ctrl_size); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pict_cmds[VDECFW_CMD_MAX]); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pic_width_inmbs); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, pic_height_inmbs); + + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, mbparams_base_addr); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, mbparams_size_per_plane); + /* Address of VLC table data. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, vlc_tables_addr); + /* Size of the VLC table data in bytes. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, vlc_tables_size); + /* Address of VLC index table data. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, vlc_index_table_addr); + /* Size of the VLC index table data in bytes. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, vlc_index_table_size); + /* Address of parser picture header. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, psr_hdr_addr); + /* Size of the parser picture header in bytes. */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, psr_hdr_size); + /* Address of Sequence Info in the Host (secure) */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, sequence_info_source); + /* Address of PPS Info in the Host (secure) */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, pps_info_source); + /* Address of Second PPS Info in the Host (secure) */ + IMG_ALIGN_FIELD(VDECFW_SHARE_PTR_ALIGNMENT, + unsigned int, second_pps_info_source); + /* MMU Tile config comes down with each transaction. */ + struct vdecfw_mmu_tile_config mmu_tile_config; +}; + +/* + * This structure contains the info for extracting a subset of VLC tables + * indexed inside the index table. + * aui32VlcTablesOffset is the offset to the first table inside the index table + * aui32VlcConsecutiveTables indicates the consecutive number of entries (from + * aui32VlcTablesOffset to aui32VlcTablesOffset+aui32VlcConsecutiveTables) + * which will be copied. + */ +struct vdecfw_vlc_table_info { + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, vlc_table_offset); + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned short, vlc_consecutive_tables); +}; + +/* This structure defines the RENDEC buffer configuration. */ +struct vdecfw_rendec_config { + /* VEC_RENDEC_CONTROL0 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, regvec_rendec_control0); + /* VEC_RENDEC_CONTROL1 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, regvec_rendec_control1); + /* VEC_RENDEC_BASE_ADDR0 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, rendec_buffer_baseaddr0); + /* VEC_RENDEC_BASE_ADDR1 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, rendec_buffer_baseaddr1); + /* VEC_RENDEC_BUFFER_SIZE */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, regvec_rendec_buffer_size); + /* VEC_RENDEC_CONTEXT0 - VEC_RENDEC_CONTEXT5 */ + IMG_ALIGN_FIELD(VDECFW_SHARE_DEFAULT_ALIGNMENT, + unsigned int, rendec_initial_ctx[6]); +}; + +#endif /* _VDECFW_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdecfw_share.h b/drivers/media/platform/vxe-vxd/decoder/vdecfw_share.h --- a/drivers/media/platform/vxe-vxd/decoder/vdecfw_share.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdecfw_share.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC SYSDEV and UI Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ +#ifndef _VDECFW_SHARE_H_ +#define _VDECFW_SHARE_H_ + +/* + * This macro sets alignment for a field structure. + * Parameters : + * a - alignment value + * t - field type + * n - field name + */ +#define IMG_ALIGN_FIELD(a, t, n) t n __aligned(a) + +/* END of vdecfw_share_macros.h */ + +/* + * Field alignments in shared data structures + */ +/* Default field alignment */ +#define VDECFW_SHARE_DEFAULT_ALIGNMENT 4 +/* Pointer field alignment */ +#define VDECFW_SHARE_PTR_ALIGNMENT 4 + +#endif /* _VDECFW_SHARE_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.c b/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.c --- a/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VDEC MMU Functions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include "img_dec_common.h" +#include "lst.h" +#include "talmmu_api.h" +#include "vdec_defs.h" +#include "vdec_mmu_wrapper.h" +#include "vxd_dec.h" + +#define GUARD_BAND 0x1000 + +struct mmuheap { + unsigned char *name; + enum mmu_eheap_id heap_id; + enum talmmu_heap_type heap_type; + unsigned int start_offset; + unsigned int size; + unsigned char *mem_space; + unsigned char use_guard_band; + unsigned char image_buffers; +}; + +static const struct mmuheap mmu_heaps[MMU_HEAP_MAX] = { + { "Image untiled", MMU_HEAP_IMAGE_BUFFERS_UNTILED, + TALMMU_HEAP_PERCONTEXT, PVDEC_HEAP_UNTILED_START, + PVDEC_HEAP_UNTILED_SIZE, "MEMBE", 1, 1 }, + + { "Bitstream", MMU_HEAP_BITSTREAM_BUFFERS, + TALMMU_HEAP_PERCONTEXT, PVDEC_HEAP_BITSTREAM_START, + PVDEC_HEAP_BITSTREAM_SIZE, "MEMDMAC_02", 1, 0 }, + + { "Stream", MMU_HEAP_STREAM_BUFFERS, + TALMMU_HEAP_PERCONTEXT, PVDEC_HEAP_STREAM_START, + PVDEC_HEAP_STREAM_SIZE, "MEM", 1, 0 }, +}; + +/* + * @Heap ID + * @Heap type + * @Heap flags + * @Memory space name + * @Start address (virtual) + * @Size of heap, in bytes + */ +static struct talmmu_heap_info heap_info = { + MMU_HEAP_IMAGE_BUFFERS_UNTILED, + TALMMU_HEAP_PERCONTEXT, + TALMMU_HEAPFLAGS_NONE, + "MEMBE", + 0, + 0, +}; + +/* + * This structure contains the device context. + * @brief VDECDD MMU Device Context + * @devmem_template_hndl: Handle for MMU template. + * @devmem_ctx_hndl: Handle for MMU context. + * @str_list: List of streams. + */ +struct mmu_dev_context { + void *devmem_template_hndl; + void *devmem_ctx_hndl; + struct lst_t str_list; + unsigned int ctx_id; + unsigned int next_ctx_id; +}; + +/* + * This structure contains the stream context. + * @brief VDECDD MMU Stream Context + * @LST_LINK: List link (allows the structure to be part of a MeOS list). + * @devmem_ctx_hndl: Handle for MMU context. + * @dev_ctx: Pointer to device context. + * @ctx_id: MMU context Id. + * km_str_id: Stream ID used in communication with new KM interface + */ +struct mmu_str_context { + void **link; + void *devmem_ctx_hndl; + struct mmu_dev_context *dev_ctx; + unsigned int ctx_id; + void *ptd_memspace_hndl; + unsigned int int_reg_num; + unsigned int km_str_id; + struct vxd_dec_ctx *vxd_dec_context; +}; + +static unsigned int set_attributes(enum sys_emem_attrib mem_attrib) +{ + unsigned int attrib = 0; + + if (mem_attrib & SYS_MEMATTRIB_CACHED) + attrib |= MEM_ATTR_CACHED; + + if (mem_attrib & SYS_MEMATTRIB_UNCACHED) + attrib |= MEM_ATTR_UNCACHED; + + if (mem_attrib & SYS_MEMATTRIB_WRITECOMBINE) + attrib |= MEM_ATTR_WRITECOMBINE; + + if (mem_attrib & SYS_MEMATTRIB_SECURE) + attrib |= MEM_ATTR_SECURE; + + return attrib; +} + +/* + * @Function mmu_dev_mem_context_create + */ +static int mmu_devmem_context_create(struct mmu_dev_context *dev_ctx, void **mmu_ctx_hndl) +{ + int result; + void *devmem_heap_hndl; + union talmmu_heap_options heap_opt1; + unsigned int i; + unsigned char use_guardband; + enum talmmu_heap_option_id heap_option_id; + + dev_ctx->next_ctx_id++; + + /* Create a context from the template */ + result = talmmu_devmem_ctx_create(dev_ctx->devmem_template_hndl, dev_ctx->next_ctx_id, + mmu_ctx_hndl); + if (result != IMG_SUCCESS) + return result; + + /* Apply options to heaps. */ + heap_opt1.guardband_opt.guardband = GUARD_BAND; + + for (i = 0; i < MMU_HEAP_MAX; i++) { + result = talmmu_get_heap_handle(mmu_heaps[i].heap_id, *mmu_ctx_hndl, + &devmem_heap_hndl); + if (result != IMG_SUCCESS) + return result; + + use_guardband = mmu_heaps[i].use_guard_band; + heap_option_id = TALMMU_HEAP_OPT_ADD_GUARD_BAND; + if (use_guardband) + talmmu_devmem_heap_options(devmem_heap_hndl, heap_option_id, heap_opt1); + } + + return IMG_SUCCESS; +} + +/* + * @Function mmu_device_create + */ +int mmu_device_create(enum mmu_etype mmu_type_arg, + unsigned int ptd_alignment, + void **mmudev_handle) +{ + int result = IMG_SUCCESS; + enum talmmu_mmu_type talmmu_type = + TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR; + unsigned int i; + struct mmu_dev_context *dev_ctx; + struct talmmu_devmem_info dev_mem_info; + + /* Set the TAL MMU type. */ + switch (mmu_type_arg) { + case MMU_TYPE_32BIT: + talmmu_type = TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR; + break; + + case MMU_TYPE_36BIT: + talmmu_type = TALMMU_MMUTYPE_4K_PAGES_36BIT_ADDR; + break; + + case MMU_TYPE_40BIT: + talmmu_type = TALMMU_MMUTYPE_4K_PAGES_40BIT_ADDR; + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Allocate a device context structure */ + dev_ctx = kzalloc(sizeof(*dev_ctx), GFP_KERNEL); + if (!dev_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + /* Initialise stream list. */ + lst_init(&dev_ctx->str_list); + + /* Initialise TALMMU. */ + result = talmmu_init(); + if (result != IMG_SUCCESS) + goto error_tal_init; + + dev_mem_info.device_id = 0; + dev_mem_info.mmu_type = talmmu_type; + dev_mem_info.dev_flags = TALMMU_DEVFLAGS_NONE; + dev_mem_info.pagedir_memspace_name = "MEM"; + dev_mem_info.pagetable_memspace_name = NULL; + dev_mem_info.page_size = DEV_MMU_PAGE_SIZE; + dev_mem_info.ptd_alignment = ptd_alignment; + + result = talmmu_devmem_template_create(&dev_mem_info, &dev_ctx->devmem_template_hndl); + if (result != IMG_SUCCESS) + goto error_tal_template; + + /* Add heaps to template */ + for (i = 0; i < MMU_HEAP_MAX; i++) { + heap_info.heap_id = mmu_heaps[i].heap_id; + heap_info.heap_type = mmu_heaps[i].heap_type; + heap_info.memspace_name = mmu_heaps[i].name; + heap_info.size = mmu_heaps[i].size; + heap_info.basedev_virtaddr = mmu_heaps[i].start_offset; + + result = talmmu_devmem_heap_add(dev_ctx->devmem_template_hndl, &heap_info); + if (result != IMG_SUCCESS) + goto error_tal_heap; + } + + /* Create the device context. */ + result = mmu_devmem_context_create(dev_ctx, &dev_ctx->devmem_ctx_hndl); + if (result != IMG_SUCCESS) + goto error_mmu_context; + + dev_ctx->ctx_id = dev_ctx->next_ctx_id; + + /* Return the device context. */ + *mmudev_handle = dev_ctx; + + return IMG_SUCCESS; + + /* Roll back in case of errors. */ +error_mmu_context: +error_tal_heap: + talmmu_devmem_template_destroy(dev_ctx->devmem_template_hndl); +error_tal_template: + talmmu_deinit(); +error_tal_init: + kfree(dev_ctx); + return result; +} + +/* + * @Function mmu_device_destroy + */ +int mmu_device_destroy(void *mmudev_handle) +{ + struct mmu_dev_context *dev_ctx = mmudev_handle; + unsigned int result; + struct mmu_str_context *str_ctx; + + /* Validate inputs. */ + if (!mmudev_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Destroy all streams associated with the device. */ + str_ctx = lst_first(&dev_ctx->str_list); + while (str_ctx) { + result = mmu_stream_destroy(str_ctx); + if (result != IMG_SUCCESS) + return result; + /* See if there are more streams. */ + str_ctx = lst_first(&dev_ctx->str_list); + } + + /* Destroy the device context */ + result = talmmu_devmem_ctx_destroy(dev_ctx->devmem_ctx_hndl); + if (result != IMG_SUCCESS) + return result; + + /* Destroy the template. */ + result = talmmu_devmem_template_destroy(dev_ctx->devmem_template_hndl); + if (result != IMG_SUCCESS) + return result; + + talmmu_deinit(); + + kfree(dev_ctx); + return IMG_SUCCESS; +} + +/* + * @Function mmu_stream_create + * @Description + * This function is used to create and initialise the MMU stream context. + * @Input mmudev_handle : The MMU device handle. + * @Input km_str_id : Stream Id used in communication with KM driver. + * @Output mmu_str_hndl : A pointer used to return the MMU stream + * handle. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_stream_create(void *mmudev_handle, + unsigned int km_str_id, + void *vxd_dec_ctx_arg, + void **mmu_str_hndl) +{ + struct mmu_dev_context *dev_ctx = mmudev_handle; + struct mmu_str_context *str_ctx; + int res; + + /* Validate inputs. */ + if (!mmudev_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Allocate a stream context structure */ + str_ctx = kzalloc(sizeof(*str_ctx), GFP_KERNEL); + if (!str_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + str_ctx->km_str_id = km_str_id; + str_ctx->dev_ctx = dev_ctx; + str_ctx->int_reg_num = 32; + str_ctx->vxd_dec_context = (struct vxd_dec_ctx *)vxd_dec_ctx_arg; + + /* Create a stream context. */ + res = mmu_devmem_context_create(dev_ctx, &str_ctx->devmem_ctx_hndl); + if (res != IMG_SUCCESS) { + kfree(str_ctx); + return res; + } + + str_ctx->ctx_id = dev_ctx->next_ctx_id; + + /* Add stream to list. */ + lst_add(&dev_ctx->str_list, str_ctx); + + *mmu_str_hndl = str_ctx; + + return IMG_SUCCESS; +} + +/* + * @Function mmu_stream_destroy + * @Description + * This function is used to create and initialise the MMU stream context. + * NOTE: Destroy automatically frees and memory allocated using + * mmu_stream_malloc(). + * @Input mmu_str_hndl : The MMU stream handle. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_stream_destroy(void *mmu_str_hndl) +{ + struct mmu_str_context *str_ctx = mmu_str_hndl; + int res; + + /* Validate inputs. */ + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* remove stream to list. */ + lst_remove(&str_ctx->dev_ctx->str_list, str_ctx); + + /* Destroy the device context */ + res = talmmu_devmem_ctx_destroy(str_ctx->devmem_ctx_hndl); + if (res != IMG_SUCCESS) + return res; + + kfree(str_ctx); + + return IMG_SUCCESS; +} + +/* + * @Function mmu_malloc + */ +static int mmu_alloc(void *devmem_ctx_hndl, + struct vxd_dec_ctx *vxd_dec_ctx_arg, + enum mmu_eheap_id heap_id, + unsigned int mem_heap_id, + enum sys_emem_attrib mem_attrib, + unsigned int size, + unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info) +{ + int result; + void *devmem_heap_hndl; + struct vxd_free_data free_data; + struct vxd_dec_ctx *ctx; + struct vxd_dev *vxd; + struct vxd_alloc_data alloc_data; + unsigned int flags; + + if (!devmem_ctx_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Round size up to next multiple of physical pages */ + if ((size % HOST_MMU_PAGE_SIZE) != 0) + size = ((size / HOST_MMU_PAGE_SIZE) + 1) * HOST_MMU_PAGE_SIZE; + + /* Allocate memory */ + ctx = vxd_dec_ctx_arg; + vxd = ctx->dev; + + alloc_data.heap_id = mem_heap_id; + alloc_data.size = ddbuf_info->buf_size; + + alloc_data.attributes = set_attributes(mem_attrib); + + result = img_mem_alloc(vxd->dev, ctx->mem_ctx, alloc_data.heap_id, alloc_data.size, + (enum mem_attr)alloc_data.attributes, + (int *)&ddbuf_info->buff_id); + if (result != IMG_SUCCESS) + goto error_alloc; + + ddbuf_info->is_internal = 1; + + if (mem_attrib & SYS_MEMATTRIB_SECURE) { + ddbuf_info->cpu_virt = NULL; + } else { + /* Map the buffer to CPU */ + result = img_mem_map_km(ctx->mem_ctx, ddbuf_info->buff_id); + if (result) { + dev_err(vxd->dev, "%s: failed to map buf to cpu!(%d)\n", __func__, result); + goto error_get_heap_handle; + } + ddbuf_info->cpu_virt = img_mem_get_kptr(ctx->mem_ctx, ddbuf_info->buff_id); + } + + /* Get heap handle */ + result = talmmu_get_heap_handle(heap_id, devmem_ctx_hndl, &devmem_heap_hndl); + if (result != IMG_SUCCESS) + goto error_get_heap_handle; + + /* Allocate device "virtual" memory. */ + result = talmmu_devmem_addr_alloc(devmem_ctx_hndl, devmem_heap_hndl, size, alignment, + &ddbuf_info->hndl_memory); + if (result != IMG_SUCCESS) + goto error_mem_map_ext_mem; + + /* Get the device virtual address. */ + result = talmmu_get_dev_virt_addr(ddbuf_info->hndl_memory, &ddbuf_info->dev_virt); + if (result != IMG_SUCCESS) + goto error_get_dev_virt_addr; + + flags = VXD_MAP_FLAG_NONE; + + if (mem_attrib & SYS_MEMATTRIB_CORE_READ_ONLY) + flags |= VXD_MAP_FLAG_READ_ONLY; + + if (mem_attrib & SYS_MEMATTRIB_CORE_WRITE_ONLY) + flags |= VXD_MAP_FLAG_WRITE_ONLY; + + result = vxd_map_buffer(vxd, ctx, ddbuf_info->kmstr_id, ddbuf_info->buff_id, + ddbuf_info->dev_virt, + flags); + + if (result != IMG_SUCCESS) + goto error_map_dev; + + return IMG_SUCCESS; + +error_map_dev: +error_get_dev_virt_addr: + talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + ddbuf_info->hndl_memory = NULL; +error_mem_map_ext_mem: +error_get_heap_handle: + free_data.buf_id = ddbuf_info->buff_id; + img_mem_free(ctx->mem_ctx, free_data.buf_id); +error_alloc: + return result; +} + +/* + * @Function mmu_stream_malloc + */ +int mmu_stream_alloc(void *mmu_str_hndl, + enum mmu_eheap_id heap_id, + unsigned int mem_heap_id, + enum sys_emem_attrib mem_attrib, + unsigned int size, + unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info) +{ + struct mmu_str_context *str_ctx = + (struct mmu_str_context *)mmu_str_hndl; + int result; + + /* Validate inputs. */ + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Check if device level heap. */ + switch (heap_id) { + case MMU_HEAP_IMAGE_BUFFERS_UNTILED: + case MMU_HEAP_BITSTREAM_BUFFERS: + case MMU_HEAP_STREAM_BUFFERS: + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + ddbuf_info->kmstr_id = str_ctx->km_str_id; + + /* Allocate device memory. */ + result = mmu_alloc(str_ctx->devmem_ctx_hndl, str_ctx->vxd_dec_context, heap_id, mem_heap_id, + mem_attrib, size, alignment, ddbuf_info); + if (result != IMG_SUCCESS) + return result; + + return IMG_SUCCESS; +} + +/* + * @Function mmu_stream_map_ext_sg + */ +int mmu_stream_map_ext_sg(void *mmu_str_hndl, + enum mmu_eheap_id heap_id, + void *sgt, + unsigned int size, + unsigned int alignment, + enum sys_emem_attrib mem_attrib, + void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info, + unsigned int *buff_id) +{ + struct mmu_str_context *str_ctx = + (struct mmu_str_context *)mmu_str_hndl; + int result; + void *devmem_heap_hndl; + unsigned int flags; + + struct vxd_dec_ctx *ctx = str_ctx->vxd_dec_context; + struct vxd_dev *vxd = ctx->dev; + + /* Validate inputs. */ + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Check if device level heap. */ + switch (heap_id) { + case MMU_HEAP_IMAGE_BUFFERS_UNTILED: + case MMU_HEAP_BITSTREAM_BUFFERS: + case MMU_HEAP_STREAM_BUFFERS: + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!str_ctx->devmem_ctx_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Round size up to next multiple of physical pages */ + if ((size % HOST_MMU_PAGE_SIZE) != 0) + size = ((size / HOST_MMU_PAGE_SIZE) + 1) * HOST_MMU_PAGE_SIZE; + + result = img_mem_import(vxd->dev, ctx->mem_ctx, ddbuf_info->buf_size, + (enum mem_attr)set_attributes(mem_attrib), + (int *)buff_id); + if (result != IMG_SUCCESS) + return result; + + if (mem_attrib & SYS_MEMATTRIB_SECURE) + ddbuf_info->cpu_virt = NULL; + + ddbuf_info->buff_id = *buff_id; + ddbuf_info->is_internal = 0; + + ddbuf_info->kmstr_id = str_ctx->km_str_id; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Ensure the address of the buffer is at least page aligned. */ + ddbuf_info->cpu_virt = cpu_linear_addr; + + /* Get heap handle */ + result = talmmu_get_heap_handle(heap_id, str_ctx->devmem_ctx_hndl, &devmem_heap_hndl); + if (result != IMG_SUCCESS) + return result; + + /* Allocate device "virtual" memory. */ + result = talmmu_devmem_addr_alloc(str_ctx->devmem_ctx_hndl, devmem_heap_hndl, size, + alignment, + &ddbuf_info->hndl_memory); + if (result != IMG_SUCCESS) + return result; + + /* Get the device virtual address. */ + result = talmmu_get_dev_virt_addr(ddbuf_info->hndl_memory, &ddbuf_info->dev_virt); + if (result != IMG_SUCCESS) + goto error_get_dev_virt_addr; + + /* Map memory to the device */ + flags = VXD_MAP_FLAG_NONE; + + if (mem_attrib & SYS_MEMATTRIB_CORE_READ_ONLY) + flags |= VXD_MAP_FLAG_READ_ONLY; + + if (mem_attrib & SYS_MEMATTRIB_CORE_WRITE_ONLY) + flags |= VXD_MAP_FLAG_WRITE_ONLY; + + result = vxd_map_buffer_sg(vxd, ctx, ddbuf_info->kmstr_id, ddbuf_info->buff_id, sgt, + ddbuf_info->dev_virt, + flags); + + if (result != IMG_SUCCESS) + goto error_map_dev; + + return IMG_SUCCESS; + +error_map_dev: +error_get_dev_virt_addr: + talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + ddbuf_info->hndl_memory = NULL; + return result; +} + +/* + * @Function mmu_stream_map_ext + */ +int mmu_stream_map_ext(void *mmu_str_hndl, + enum mmu_eheap_id heap_id, + unsigned int buff_id, + unsigned int size, + unsigned int alignment, + enum sys_emem_attrib mem_attrib, + void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info) +{ + struct mmu_str_context *str_ctx = + (struct mmu_str_context *)mmu_str_hndl; + int result; + void *devmem_heap_hndl; + struct vxd_dec_ctx *ctx; + struct vxd_dev *vxd; + unsigned int flags; + + /* Validate inputs. */ + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Check if device level heap. */ + switch (heap_id) { + case MMU_HEAP_IMAGE_BUFFERS_UNTILED: + case MMU_HEAP_BITSTREAM_BUFFERS: + case MMU_HEAP_STREAM_BUFFERS: + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Round size up to next multiple of physical pages */ + if ((size % HOST_MMU_PAGE_SIZE) != 0) + size = ((size / HOST_MMU_PAGE_SIZE) + 1) * HOST_MMU_PAGE_SIZE; + + ddbuf_info->buff_id = buff_id; + ddbuf_info->is_internal = 0; + + ddbuf_info->kmstr_id = str_ctx->km_str_id; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Ensure the address of the buffer is at least page aligned. */ + ddbuf_info->cpu_virt = cpu_linear_addr; + + /* Get heap handle */ + result = talmmu_get_heap_handle(heap_id, str_ctx->devmem_ctx_hndl, &devmem_heap_hndl); + if (result != IMG_SUCCESS) + return result; + + /* Allocate device "virtual" memory. */ + result = talmmu_devmem_addr_alloc(str_ctx->devmem_ctx_hndl, devmem_heap_hndl, size, + alignment, + &ddbuf_info->hndl_memory); + if (result != IMG_SUCCESS) + return result; + + /* Get the device virtual address. */ + result = talmmu_get_dev_virt_addr(ddbuf_info->hndl_memory, &ddbuf_info->dev_virt); + if (result != IMG_SUCCESS) + return result; + + /* + * Map device memory (allocated from outside VDEC) + * into the stream PTD. + */ + ctx = str_ctx->vxd_dec_context; + vxd = ctx->dev; + + flags = VXD_MAP_FLAG_NONE; + + if (mem_attrib & SYS_MEMATTRIB_CORE_READ_ONLY) + flags |= VXD_MAP_FLAG_READ_ONLY; + + if (mem_attrib & SYS_MEMATTRIB_CORE_WRITE_ONLY) + flags |= VXD_MAP_FLAG_WRITE_ONLY; + + result = vxd_map_buffer(vxd, ctx, ddbuf_info->kmstr_id, ddbuf_info->buff_id, + ddbuf_info->dev_virt, + flags); + if (result != IMG_SUCCESS) + return result; + + return IMG_SUCCESS; +} + +/* + * @Function mmu_free_mem + */ +int mmu_free_mem(void *mmustr_hndl, struct vidio_ddbufinfo *ddbuf_info) +{ + int tmp_result; + int result = IMG_SUCCESS; + struct vxd_dec_ctx *ctx; + struct vxd_dev *vxd; + + struct mmu_str_context *str_ctx = + (struct mmu_str_context *)mmustr_hndl; + + /* Validate inputs. */ + if (!ddbuf_info) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!str_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Unmap the memory mapped to the device */ + ctx = str_ctx->vxd_dec_context; + vxd = ctx->dev; + + tmp_result = vxd_unmap_buffer(vxd, ctx, ddbuf_info->kmstr_id, ddbuf_info->buff_id); + if (tmp_result != IMG_SUCCESS) + result = tmp_result; + + /* + * Unmapping the memory mapped to the device - done + * Free the memory. + */ + tmp_result = talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + if (tmp_result != IMG_SUCCESS) + result = tmp_result; + + if (ddbuf_info->is_internal) { + struct vxd_free_data free_data = { ddbuf_info->buff_id }; + + img_mem_free(ctx->mem_ctx, free_data.buf_id); + } + + return result; +} + +/* + * @Function mmu_free_mem + */ +int mmu_free_mem_sg(void *mmustr_hndl, struct vidio_ddbufinfo *ddbuf_info) +{ + int tmp_result; + int result = IMG_SUCCESS; + struct vxd_dec_ctx *ctx; + struct vxd_dev *vxd; + struct vxd_free_data free_data; + + struct mmu_str_context *str_ctx = + (struct mmu_str_context *)mmustr_hndl; + + /* Validate inputs. */ + if (!ddbuf_info) + return IMG_ERROR_INVALID_PARAMETERS; + + if (!str_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + free_data.buf_id = ddbuf_info->buff_id; + /* Unmap the memory mapped to the device */ + ctx = str_ctx->vxd_dec_context; + vxd = ctx->dev; + + tmp_result = vxd_unmap_buffer(vxd, ctx, ddbuf_info->kmstr_id, ddbuf_info->buff_id); + if (tmp_result != IMG_SUCCESS) + result = tmp_result; + + /* + * Unmapping the memory mapped to the device - done + * Free the memory. + */ + tmp_result = talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + if (tmp_result != IMG_SUCCESS) + result = tmp_result; + + /* + * for external mem manager buffers, just cleanup the idr list and + * buffer objects + */ + img_mem_free_bufid(ctx->mem_ctx, free_data.buf_id); + + return result; +} + +/* + * @Function MMU_GetHeap + */ +int mmu_get_heap(unsigned int image_stride, enum mmu_eheap_id *heap_id) +{ + unsigned int i; + unsigned char found = FALSE; + + for (i = 0; i < MMU_HEAP_MAX; i++) { + if (mmu_heaps[i].image_buffers) { + *heap_id = mmu_heaps[i].heap_id; + found = TRUE; + break; + } + } + + VDEC_ASSERT(found); + if (!found) + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + + return IMG_SUCCESS; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.h b/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.h --- a/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vdec_mmu_wrapper.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VDEC MMU Functions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Lakshmi Sankar + * + * Re-written for upstream + * Sidraya Jayagond + */ + +#include +#include +#include +#include + +#include "img_errors.h" +#include "img_mem.h" +#include "lst.h" +#include "mmu_defs.h" +#include "vid_buf.h" + +#ifndef _VXD_MMU_H_ +#define _VXD_MMU_H_ + +/* Page size of the device MMU */ +#define DEV_MMU_PAGE_SIZE (0x1000) +/* Page alignment of the device MMU */ +#define DEV_MMU_PAGE_ALIGNMENT (0x1000) + +#define HOST_MMU_PAGE_SIZE PAGE_SIZE + +/* + * @Function mmu_stream_get_ptd_handle + * @Description + * This function is used to obtain the stream PTD (Page Table Directory)handle + * @Input mmu_str_handle : MMU stream handle. + * @Output str_ptd : Pointer to stream PTD handle. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_stream_get_ptd_handle(void *mmu_str_handle, void **str_ptd); + +/* + * @Function mmu_device_create + * @Description + * This function is used to create and initialise the MMU device context. + * @Input mmu_type : MMU type. + * @Input ptd_alignment : Alignment of Page Table directory. + * @Output mmudev_hndl : A pointer used to return the + * MMU device handle. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_device_create(enum mmu_etype mmu_type, + unsigned int ptd_alignment, + void **mmudev_hndl); + +/* + * @Function mmu_device_destroy + * @Description + * This function is used to create and initialise the MMU device context. + * NOTE: Destroy device automatically destroys any streams and frees and + * memory allocated using MMU_StreamMalloc(). + * @Input mmudev_hndl : The MMU device handle. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_device_destroy(void *mmudev_hndl); + +/* + * @Function mmu_stream_create + * @Description + * This function is used to create and initialise the MMU stream context. + * @Input mmudev_hndl : The MMU device handle. + * @Input km_str_id : Stream Id used in communication with KM driver. + * @Output mmustr_hndl : A pointer used to return the MMU stream handle. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_stream_create(void *mmudev_hndl, unsigned int km_str_id, void *vxd_dec_ctx, + void **mmustr_hndl); + +/** + * mmu_stream_destroy - This function is used to create and initialise the MMU stream context. + * @mmustr_hndl : The MMU stream handle. + * Return IMG_SUCCESS or an error code. + * + * NOTE: Destroy automatically frees and memory allocated using + * mmu_stream_malloc(). + */ +int mmu_stream_destroy(void *mmustr_hndl); + +/* + * @Function mmu_stream_alloc + * @Description + * This function is used to allocate stream memory. + * @Input mmustr_hndl : The MMU stream handle. + * @Input heap_id : The MMU heap Id. + * @Input mem_heap_id : Memory heap id + * @Input mem_attrib : Memory attributes + * @Input size : The size, in bytes, to be allocated. + * @Input alignment : The required byte alignment + * (1, 2, 4, 8, 16 etc). + * @Output ddbuf_info : A pointer to a #vidio_ddbufinfo structure + * used to return the buffer info. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_stream_alloc(void *mmustr_hndl, + enum mmu_eheap_id heap_id, + unsigned int mem_heap_id, + enum sys_emem_attrib mem_attrib, + unsigned int size, + unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function mmu_stream_map_ext + * @Description + * This function is used to malloc device memory (virtual memory), but mapping + * this to memory that has already been allocated (externally). + * NOTE: Memory can be freed using MMU_Free(). However, this does not + * free the memory provided by the caller via pvCpuLinearAddr. + * @Input mmustr_hndl : The MMU stream handle. + * @Input heap_id : The heap Id. + * @Input buff_id : The buffer Id. + * @Input size : The size, in bytes, to be allocated. + * @Input alignment : The required byte alignment (1, 2, 4, 8, 16 etc). + * @Input mem_attrib : Memory attributes + * @Input cpu_linear_addr : CPU linear address of the memory + * to be allocated for the device. + * @Output ddbuf_info : A pointer to a #vidio_ddbufinfo structure + * used to return the buffer info. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_stream_map_ext(void *mmustr_hndl, + enum mmu_eheap_id heap_id, + unsigned int buff_id, + unsigned int size, + unsigned int alignment, + enum sys_emem_attrib mem_attrib, + void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info); + +int mmu_stream_map_ext_sg(void *mmustr_hndl, + enum mmu_eheap_id heap_id, + void *sgt, + unsigned int size, + unsigned int alignment, + enum sys_emem_attrib mem_attrib, + void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info, + unsigned int *buff_id); + +/* + * @Function mmu_free_mem + * @Description + * This function is used to free device memory. + * @Input ps_dd_buf_info : A pointer to a #vidio_ddbufinfo structure. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_free_mem(void *mmustr_hndl, struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function mmu_free_mem + * @Description + * This function is used to free device memory. + * @Input ps_dd_buf_info : A pointer to a #vidio_ddbufinfo structure. + * @Return IMG_SUCCESS or an error code. + */ +int mmu_free_mem_sg(void *mmustr_hndl, struct vidio_ddbufinfo *ddbuf_info); + +int mmu_get_heap(unsigned int image_stride, enum mmu_eheap_id *heap_id); + +#endif /* _VXD_MMU_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_core.c b/drivers/media/platform/vxe-vxd/decoder/vxd_core.c --- a/drivers/media/platform/vxe-vxd/decoder/vxd_core.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_core.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1684 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC VXD Core component function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "img_dec_common.h" +#include "vxd_pvdec_priv.h" +#include "img_errors.h" + +#define VXD_RENDEC_SIZE (5 * 1024 * 1024) + +#define VXD_MSG_CNT_SHIFT 8 +#define VXD_MSG_CNT_MASK 0xff00 +#define VXD_MAX_MSG_CNT ((1 << VXD_MSG_CNT_SHIFT) - 1) +#define VXD_MSG_STR_MASK 0xff +#define VXD_INVALID_ID (-1) + +#define MAP_FIRMWARE_TO_STREAM 1 + +/* Has to be used with VXD->mutex acquired! */ +#define VXD_GEN_MSG_ID(VXD, STR_ID, MSG_ID, vxd_type, str_type) \ + do { \ + vxd_type __VXD = VXD; \ + str_type __STR_ID = STR_ID; \ + WARN_ON((__STR_ID) > VXD_MSG_STR_MASK); \ + (__VXD)->msg_cnt = (__VXD)->msg_cnt + 1 % (VXD_MAX_MSG_CNT); \ + (MSG_ID) = ((__VXD)->msg_cnt << VXD_MSG_CNT_SHIFT) | \ + ((__STR_ID) & VXD_MSG_STR_MASK); \ + } while (0) + +/* Have to be used with VXD->mutex acquired! */ +#define VXD_RET_MSG_ID(VXD) ((VXD)->msg_cnt--) + +#define VXD_MSG_ID_GET_STR_ID(MSG_ID) \ + ((MSG_ID) & VXD_MSG_STR_MASK) + +#define VXD_MSG_ID_GET_CNT(MSG_ID) \ + (((MSG_ID) & VXD_MSG_CNT_MASK) >> VXD_MSG_CNT_SHIFT) + +static const unsigned char *drv_fw_name = "pvdec_full_bin.fw"; + +/* Driver context */ +static struct { + /* Available memory heaps. List of */ + struct list_head heaps; + /* heap id for all internal allocations (rendec, firmware) */ + int internal_heap_id; + + /* Memory Management context for driver */ + struct mem_ctx *mem_ctx; + + /* List of associated */ + struct list_head devices; + + /* Virtual addresses of shared buffers, common for all streams. */ + struct { + unsigned int fw_addr; /* Firmware blob */ + unsigned int rendec_addr; /* Rendec buffer */ + } virt_space; + + int initialised; +} vxd_drv; + +/* + * struct vxd_heap - node for heaps list + * @id: heap id + * @list: Entry in + */ +struct vxd_heap { + int id; + struct list_head list; +}; + +static void img_mmu_callback(enum mmu_callback_type callback_type, + int buff_id, void *data) +{ + struct vxd_dev *vxd = data; + + if (!vxd) + return; + + if (callback_type == MMU_CALLBACK_MAP) + return; + + if (vxd->hw_on) + vxd_pvdec_mmu_flush(vxd->dev, vxd->reg_base); +} + +static int vxd_is_apm_required(struct vxd_dev *vxd) +{ + return vxd->hw_on; +} + +/* + * Power on the HW. + * Call with vxd->mutex acquired. + */ +static int vxd_make_hw_on_locked(struct vxd_dev *vxd, unsigned int fw_ptd) +{ + unsigned int fw_size; + struct vxd_fw_hdr *fw_hdr; + struct vxd_ena_params ena_params; + int ret; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s:%d\n", __func__, __LINE__); +#endif + if (vxd->hw_on) + return 0; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: enabling HW\n", __func__); +#endif + + fw_size = vxd->firmware.fw_size; + fw_hdr = vxd->firmware.hdr; + if (!fw_size || !fw_hdr) { + dev_err(vxd->dev, "%s: firmware missing!\n", __func__); + return -ENOENT; + } + + memset(&ena_params, 0, sizeof(struct vxd_ena_params)); + + ena_params.fw_buf_size = fw_size - sizeof(struct vxd_fw_hdr); + ena_params.fw_buf_virt_addr = vxd_drv.virt_space.fw_addr; + ena_params.ptd = fw_ptd; + ena_params.boot_poll.msleep_cycles = 50; + ena_params.crc = 0; + ena_params.rendec_addr = vxd_drv.virt_space.rendec_addr; + ena_params.rendec_size = (VXD_NUM_PIX_PIPES(vxd->props) * + VXD_RENDEC_SIZE) / 4096u; + + ena_params.secure = 0; + ena_params.wait_dbg_fifo = 0; + ena_params.mem_staller.data = NULL; + ena_params.mem_staller.size = 0; + + ret = vxd_pvdec_ena(vxd->dev, vxd->reg_base, &ena_params, + fw_hdr, &vxd->freq_khz); + /* + * Ignore the return code, proceed as usual, it will be returned anyway. + * The HW is turned on, so we can perform post mortem analysis, + * and collect the fw logs when available. + */ + + vxd->hw_on = 1; + + return ret; +} + +/* + * Power off the HW. + * Call with vxd->mutex acquired. + */ +static void vxd_make_hw_off_locked(struct vxd_dev *vxd, unsigned char suspending) +{ + int ret; + + if (!vxd->hw_on) + return; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s:%d\n", __func__, __LINE__); +#endif + + ret = vxd_pvdec_dis(vxd->dev, vxd->reg_base); + vxd->hw_on = 0; + if (ret) + dev_err(vxd->dev, "%s: failed to power off the VXD!\n", __func__); +} + +/* + * Moves all valid items from the queue of items being currently processed to + * the pending queue. + * Call with vxd->mutex locked + */ +static void vxd_rewind_msgs_locked(struct vxd_dev *vxd) +{ + struct vxd_item *item, *tmp; + + if (list_empty(&vxd->msgs)) + return; + + list_for_each_entry_safe(item, tmp, &vxd->msgs, list) + list_move(&item->list, &vxd->pend); +} + +static void vxd_report_item_locked(struct vxd_dev *vxd, + struct vxd_item *item, + unsigned int flags) +{ + struct vxd_stream *stream; + + __list_del_entry(&item->list); + stream = idr_find(vxd->streams, item->stream_id); + if (!stream) { + /* + * Failed to find associated stream. Probably it was + * already destroyed -- drop the item + */ +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: drop item %p [0x%x]\n", __func__, item, item->msg_id); +#endif + kfree(item); + } else { + item->msg.out_flags |= flags; + list_add_tail(&item->list, &stream->ctx->items_done); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: waking %p\n", __func__, stream->ctx); + + dev_info(vxd->dev, "%s: signaling worker for %p\n", __func__, stream->ctx); +#endif + schedule_work(stream->ctx->work); + } +} + +/* + * Rewind all items to the pending queue and report those to listener. + * Postpone the reset. + * Call with vxd->mutex acquired. + */ +static void vxd_emrg_reset_locked(struct vxd_dev *vxd, unsigned int flags) +{ + cancel_delayed_work(vxd->dwork); + + vxd->emergency = 1; + +#ifdef ERROR_RECOVERY_SIMULATION + if (disable_fw_irq_value != 0) { + /* + * Previously we have disabled IRQ, now enable it. This + * condition will occur only when the firmware non responsiveness + * will be detected on vxd_worker thread. Once we reproduce the + * issue we will enable the IRQ so that the code flow continues. + */ + enable_irq(g_module_irq); + } +#endif + + /* + * If the firmware sends more than one reply per item, it's possible + * that corresponding item was already removed from vxd-msgs, but the + * HW was still processing it and MMU page fault could happen and + * trigger execution of this function. So make sure that vxd->msgs + * is not empty before rewinding items. + */ + if (!list_empty(&vxd->msgs)) + /* Move all valid items to the pending queue */ + vxd_rewind_msgs_locked(vxd); + + { + struct vxd_item *item, *tmp; + + list_for_each_entry_safe(item, tmp, &vxd->pend, list) { + /* + * Exclusive items that were on the pending list + * must be reported as canceled + */ + if ((item->msg.out_flags & VXD_FW_MSG_FLAG_EXCL) && !item->msg_id) + item->msg.out_flags |= VXD_FW_MSG_FLAG_CANCELED; + + vxd_report_item_locked(vxd, item, flags); + } + } +} + +static void vxd_handle_io_error_locked(struct vxd_dev *vxd) +{ + struct vxd_item *item, *tmp; + unsigned int pend_flags = !vxd->hw_on ? VXD_FW_MSG_FLAG_DEV_ERR : + VXD_FW_MSG_FLAG_CANCELED; + + list_for_each_entry_safe(item, tmp, &vxd->msgs, list) + vxd_report_item_locked(vxd, item, VXD_FW_MSG_FLAG_DEV_ERR); + + list_for_each_entry_safe(item, tmp, &vxd->pend, list) + vxd_report_item_locked(vxd, item, pend_flags); +} + +static void vxd_sched_worker_locked(struct vxd_dev *vxd, unsigned int delay_ms) +{ + unsigned long long work_at = jiffies + msecs_to_jiffies(delay_ms); + int ret; + + /* + * Try to queue the work. + * This may be also called from the worker context, + * so we need to re-arm anyway in case of error + */ + ret = schedule_delayed_work(vxd->dwork, work_at - jiffies); + if (ret) { + /* Work is already in the queue */ + /* + * Check if new requested time is "before" + * the last "time" we scheduled this work at, + * if not, do nothing, the worker will do + * recalculation for APM/DWR afterwards + */ + if (time_before((unsigned long)work_at, (unsigned long)vxd->work_sched_at)) { + /* + * Canceling & rescheduling might be problematic, + * so just modify it, when needed + */ + ret = mod_delayed_work(system_wq, vxd->dwork, work_at - jiffies); + if (!ret) + dev_err(vxd->dev, "%s: failed to modify work!\n", __func__); + /* + * Record the 'time' this work + * has been rescheduled at + */ + vxd->work_sched_at = work_at; + } + } else { + /* Record the 'time' this work has been scheduled at */ + vxd->work_sched_at = work_at; + } +} + +static void vxd_monitor_locked(struct vxd_dev *vxd) +{ + /* HW is dead, not much sense in rescheduling */ + if (vxd->hw_dead) + return; + + /* + * We are not processing anything, but pending list is not empty + * probably the message fifo is full, so retrigger the worker. + */ + if (!list_empty(&vxd->pend) && list_empty(&vxd->msgs)) + vxd_sched_worker_locked(vxd, 1); + + if (list_empty(&vxd->pend) && list_empty(&vxd->msgs) && vxd_is_apm_required(vxd)) { +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: scheduling APM work (%d ms)!\n", __func__, vxd->hw_pm_delay); +#endif + /* + * No items to process and no items being processed - + * disable the HW + */ + vxd->pm_start = jiffies; + vxd_sched_worker_locked(vxd, vxd->hw_pm_delay); + return; + } + + if (vxd->hw_dwr_period > 0 && !list_empty(&vxd->msgs)) { +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: scheduling DWR work (%d ms)!\n", + __func__, vxd->hw_dwr_period); +#endif + vxd->dwr_start = jiffies; + vxd_sched_worker_locked(vxd, vxd->hw_dwr_period); + } +} + +/* + * Take first item from pending list and submit it to the hardware. + * Has to be called with vxd->mutex locked. + */ +static int vxd_sched_single_locked(struct vxd_dev *vxd) +{ + struct vxd_item *item = NULL; + unsigned long msg_size; + int ret; + + item = list_first_entry(&vxd->pend, struct vxd_item, list); + + msg_size = item->msg.payload_size / sizeof(unsigned int); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: checking msg_size: %zu, item: %p\n", __func__, msg_size, item); +#endif + + /* + * In case of exclusive item check if hw/fw is + * currently processing anything. + * If so we need to wait until items are returned back. + */ + if ((item->msg.out_flags & VXD_FW_MSG_FLAG_EXCL) && !list_empty(&vxd->msgs) && + /* + * We can move forward if message + * is about to be dropped. + */ + !(item->msg.out_flags & VXD_FW_MSG_FLAG_DROP)) + + ret = -EBUSY; + else + /* + * Check if there's enough space + * in comms RAM to submit the message. + */ + ret = vxd_pvdec_msg_fit(vxd->dev, vxd->reg_base, msg_size); + + if (ret == 0) { + unsigned short msg_id; + + VXD_GEN_MSG_ID(vxd, item->stream_id, msg_id, struct vxd_dev*, unsigned int); + + /* submit the message to the hardware */ + ret = vxd_pvdec_send_msg(vxd->dev, vxd->reg_base, + (unsigned int *)item->msg.payload, msg_size, + msg_id, vxd); + if (ret) { + dev_err(vxd->dev, "%s: failed to send msg!\n", __func__); + VXD_RET_MSG_ID(vxd); + } else { + if (item->msg.out_flags & VXD_FW_MSG_FLAG_DROP) { + __list_del_entry(&item->list); + kfree(item); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: drop msg 0x%x! (user requested)\n", + __func__, msg_id); +#endif + } else { + item->msg_id = msg_id; +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, + "%s: moving item %p, id 0x%x to msgs\n", + __func__, item, item->msg_id); +#endif + list_move(&item->list, &vxd->msgs); + } + + vxd_monitor_locked(vxd); + } + + } else if (ret == -EINVAL) { + dev_warn(vxd->dev, "%s: invalid msg!\n", __func__); + vxd_report_item_locked(vxd, item, VXD_FW_MSG_FLAG_INV); + /* + * HW is ok, the message was invalid, so don't return an + * error + */ + ret = 0; + } else if (ret == -EBUSY) { + /* + * Not enough space. Message is already in the pending queue, + * so it will be submitted once we've got space. Delayed work + * might have been canceled (if we are currently processing + * threaded irq), so make sure that DWR will trigger if it's + * enabled. + */ + vxd_monitor_locked(vxd); + } else { + dev_err(vxd->dev, "%s: failed to check space for msg!\n", __func__); + } + + return ret; +} + +/* + * Take items from pending list and submit them to the hardware, if space is + * available in the ring buffer. + * Call with vxd->mutex locked + */ +static void vxd_schedule_locked(struct vxd_dev *vxd) +{ + unsigned char emergency = vxd->emergency; + int ret; + + /* if HW is dead, inform the UM and skip */ + if (vxd->hw_dead) { + vxd_handle_io_error_locked(vxd); + return; + } + + if (!vxd->hw_on && !list_empty(&vxd->msgs)) + dev_err(vxd->dev, "%s: msgs not empty when the HW is off!\n", __func__); + + if (list_empty(&vxd->pend)) { + vxd_monitor_locked(vxd); + return; + } + + /* + * If the emergency routine was fired, the hw was left ON,so the UM + * could do the post mortem analysis before submitting the next items. + * Now we can switch off the hardware. + */ + if (emergency) { + vxd->emergency = 0; + vxd_make_hw_off_locked(vxd, FALSE); + usleep_range(1000, 2000); + } + + /* Try to schedule */ + ret = 0; + while (!list_empty(&vxd->pend) && ret == 0) { + struct vxd_item *item; + struct vxd_stream *stream; + + item = list_first_entry(&vxd->pend, struct vxd_item, list); + stream = idr_find(vxd->streams, item->stream_id); + + ret = vxd_make_hw_on_locked(vxd, stream->ptd); + if (ret) { + dev_err(vxd->dev, "%s: failed to start HW!\n", __func__); + vxd->hw_dead = 1; + vxd_handle_io_error_locked(vxd); + return; + } + + ret = vxd_sched_single_locked(vxd); + } + + if (ret != 0 && ret != -EBUSY) { + dev_err(vxd->dev, "%s: failed to schedule, emrg: %d!\n", __func__, emergency); + if (emergency) { + /* + * Failed to schedule in the emergency mode -- + * there's no hope. Power off the HW, mark all + * items as failed and return them. + */ + vxd_handle_io_error_locked(vxd); + return; + } + /* Let worker try to handle it */ + vxd_sched_worker_locked(vxd, 0); + } +} + +static void stream_worker(void *work) +{ + struct vxd_dec_ctx *ctx = NULL; + struct vxd_dev *vxd = NULL; + struct vxd_item *item; + + work = get_work_buff(work, FALSE); + ctx = container_of(work, struct vxd_dec_ctx, work); + vxd = ctx->dev; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: got work for ctx %p\n", __func__, ctx); +#endif + + mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_CORE); + + while (!list_empty(&ctx->items_done)) { + item = list_first_entry(&ctx->items_done, struct vxd_item, list); + + item->msg.out_flags &= VXD_FW_MSG_RD_FLAGS_MASK; + +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "%s: item: %p, payload_size: %d, flags: 0x%x\n", + __func__, item, item->msg.payload_size, + item->msg.out_flags); +#endif + + if (ctx->cb) + ctx->cb(ctx->res_str_id, item->msg.payload, + item->msg.payload_size, item->msg.out_flags); + + __list_del_entry(&item->list); + kfree(item); + } + mutex_unlock(ctx->mutex); +} + +int vxd_create_ctx(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx) +{ + int ret = 0; + unsigned int fw_load_retries = 2 * 1000; + + while (!vxd->firmware.ready) { + usleep_range(1000, 2000); + fw_load_retries--; + } + if (vxd->firmware.buf_id == 0) { + dev_err(vxd->dev, "%s: request fw not yet done!\n", __func__); + return -EAGAIN; + } + + /* Create memory management context for HW buffers */ + ret = img_mem_create_ctx(&ctx->mem_ctx); + if (ret) { + dev_err(vxd->dev, "%s: failed to create mem context (err:%d)!\n", __func__, ret); + return ret; + } + + ret = img_mmu_ctx_create(vxd->dev, vxd->mmu_config_addr_width, + ctx->mem_ctx, vxd_drv.internal_heap_id, + img_mmu_callback, vxd, &ctx->mmu_ctx); + if (ret) { + dev_err(vxd->dev, "%s:%d: failed to create mmu ctx\n", __func__, __LINE__); + ret = -EPERM; + goto out_destroy_ctx; + } + + ret = img_mmu_map(ctx->mmu_ctx, vxd->mem_ctx, vxd->firmware.buf_id, + vxd_drv.virt_space.fw_addr, + VXD_MMU_PTD_FLAG_READ_ONLY); + if (ret) { + dev_err(vxd->dev, "%s:%d: failed to map firmware buffer\n", __func__, __LINE__); + ret = -EPERM; + goto out_destroy_mmu_ctx; + } + + ret = img_mmu_map(ctx->mmu_ctx, vxd->mem_ctx, vxd->rendec_buf_id, + vxd_drv.virt_space.rendec_addr, + VXD_MMU_PTD_FLAG_NONE); + if (ret) { + dev_err(vxd->dev, "%s:%d: failed to map rendec buffer\n", __func__, __LINE__); + ret = -EPERM; + goto out_unmap_fw; + } + + ret = img_mmu_get_ptd(ctx->mmu_ctx, &ctx->ptd); + if (ret) { + dev_err(vxd->dev, "%s:%d: failed to get PTD\n", __func__, __LINE__); + ret = -EPERM; + goto out_unmap_rendec; + } + + /* load fw - turned Hw on */ + ret = vxd_make_hw_on_locked(vxd, ctx->ptd); + if (ret) { + dev_err(vxd->dev, "%s:%d: failed to start HW\n", __func__, __LINE__); + ret = -EPERM; + vxd->hw_on = FALSE; + goto out_unmap_rendec; + } + + init_work(&ctx->work, stream_worker, HWA_DECODER); + if (!ctx->work) { + ret = ENOMEM; + goto out_unmap_rendec; + } + + vxd->fw_refcnt++; + + return ret; + +out_unmap_rendec: + img_mmu_unmap(ctx->mmu_ctx, vxd->mem_ctx, vxd->rendec_buf_id); +out_unmap_fw: + img_mmu_unmap(ctx->mmu_ctx, vxd->mem_ctx, vxd->firmware.buf_id); + +out_destroy_mmu_ctx: + img_mmu_ctx_destroy(ctx->mmu_ctx); +out_destroy_ctx: + img_mem_destroy_ctx(ctx->mem_ctx); + return ret; +} + +void vxd_destroy_ctx(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx) +{ + vxd->fw_refcnt--; + + flush_work(ctx->work); + + img_mmu_unmap(ctx->mmu_ctx, vxd->mem_ctx, vxd->rendec_buf_id); + + img_mmu_unmap(ctx->mmu_ctx, vxd->mem_ctx, vxd->firmware.buf_id); + + img_mmu_ctx_destroy(ctx->mmu_ctx); + + img_mem_destroy_ctx(ctx->mem_ctx); + + if (vxd->fw_refcnt == 0) { +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "FW: put %s\n", drv_fw_name); +#endif + /* Poke the monitor to finally switch off the hw, when needed */ + vxd_monitor_locked(vxd); + } +} + +/* Top half */ +irqreturn_t vxd_handle_irq(void *dev) +{ + struct vxd_dev *vxd = ((const struct device *)dev)->driver_data; + struct vxd_hw_state *hw_state = &vxd->state.hw_state; + int ret; + + if (!vxd) + return IRQ_NONE; + + ret = vxd_pvdec_clear_int(vxd->reg_base, &hw_state->irq_status); + + if (!hw_state->irq_status || ret == IRQ_NONE) + dev_warn(dev, "Got spurious interrupt!\n"); + + return (irqreturn_t)ret; +} + +static void vxd_drop_msg_locked(const struct vxd_dev *vxd) +{ + int ret; + + ret = vxd_pvdec_recv_msg(vxd->dev, vxd->reg_base, NULL, 0, (struct vxd_dev *)vxd); + if (ret) + dev_warn(vxd->dev, "%s: failed to receive msg!\n", __func__); +} + +#ifdef DEBUG_DECODER_DRIVER +static void vxd_dbg_dump_msg(const void *dev, const unsigned char *func, + const unsigned int *payload, + unsigned long msg_size) +{ + unsigned int i; + + for (i = 0; i < msg_size; i++) + dev_dbg(dev, "%s: msg %d: 0x%08x\n", func, i, payload[i]); +} +#endif + +static struct vxd_item *vxd_get_orphaned_item_locked(struct vxd_dev *vxd, + unsigned short msg_id, + unsigned long msg_size) +{ + struct vxd_stream *stream; + struct vxd_item *item; + unsigned short str_id = VXD_MSG_ID_GET_STR_ID(msg_id); + + /* Try to find associated stream */ + stream = idr_find(vxd->streams, str_id); + if (!stream) { + /* Failed to find associated stream. */ +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: failed to find str_id: %u\n", __func__, str_id); +#endif + return NULL; + } + + item = kzalloc(sizeof(*item) + (msg_size * sizeof(unsigned int)), GFP_KERNEL); + if (!item) + return NULL; + + item->msg.out_flags = 0; + item->stream_id = str_id; + item->msg.payload_size = msg_size * sizeof(unsigned int); + if (vxd_pvdec_recv_msg(vxd->dev, vxd->reg_base, item->msg.payload, msg_size, vxd)) { + dev_err(vxd->dev, "%s: failed to receive msg from VXD!\n", __func__); + item->msg.out_flags |= VXD_FW_MSG_FLAG_DEV_ERR; + } +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: item: %p str_id: %u\n", __func__, item, str_id); +#endif + /* + * Need to put this item on the vxd->msgs list. + * It will be removed after. + */ + list_add_tail(&item->list, &vxd->msgs); + +#ifdef DEBUG_DECODER_DRIVER + vxd_dbg_dump_msg(vxd->dev, __func__, item->msg.payload, msg_size); +#endif + + return item; +} + +/* + * Fetch and process a single message from the MTX->host ring buffer. + * parameter is used to indicate if there are more messages pending. + * parameter indicates if there is some serious situation detected. + * Has to be called with vxd->mutex locked. + */ +static void vxd_handle_single_msg_locked(struct vxd_dev *vxd, + unsigned char *no_more, + unsigned char *fatal) +{ + int ret; + unsigned short msg_id, str_id; + unsigned long msg_size; /* size in dwords */ + struct vxd_item *item = NULL, *tmp, *it; + struct vxd_stream *stream; + void *dev = vxd->dev; + unsigned char not_last_msg; + + /* get the message size and id */ + ret = vxd_pvdec_pend_msg_info(dev, vxd->reg_base, &msg_size, &msg_id, + ¬_last_msg); + if (ret) { + dev_err(dev, "%s: failed to get pending msg size!\n", __func__); + *no_more = TRUE; /* worker will HW failure */ + return; + } + + if (msg_size == 0) { + *no_more = TRUE; + return; + } + *no_more = FALSE; + + str_id = VXD_MSG_ID_GET_STR_ID(msg_id); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: [msg] size: %zu, cnt: %u, str_id: %u, id: 0x%x\n", + __func__, msg_size, VXD_MSG_ID_GET_CNT(msg_id), + str_id, msg_id); + dev_dbg(dev, "%s: [msg] not last: %u\n", __func__, not_last_msg); +#endif + + cancel_delayed_work(vxd->dwork); + + /* Find associated item */ + list_for_each_entry_safe_reverse(it, tmp, &vxd->msgs, list) { +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: checking item %p [0x%x] [des: %d]\n", + __func__, it, it->msg_id, it->destroy); +#endif + if (it->msg_id == msg_id) { + item = it; + break; + } + } + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: found item %p [destroy: %d]\n", + __func__, item, item ? item->destroy : VXD_INVALID_ID); +#endif + + /* Find associated stream */ + stream = idr_find(vxd->streams, str_id); + /* + * Check for firmware condition in case + * when unexpected item is received. + */ + if (!item && !stream && vxd_pvdec_check_fw_status(dev, vxd->reg_base)) { + struct vxd_item *orphan; + /* + * Lets forward the fatal info to listeners first, relaying + * on the head of the msg queue. + */ + /* TODO: forward fatal info to all attached processes */ + item = list_entry(vxd->msgs.prev, struct vxd_item, list); + orphan = vxd_get_orphaned_item_locked(vxd, item->msg_id, msg_size); + if (!orphan) { + dev_warn(dev, "%s: drop msg 0x%x! (no orphan)\n", __func__, item->msg_id); + vxd_drop_msg_locked(vxd); + } + + *fatal = TRUE; + return; + } + + if ((item && item->destroy) || !stream) { + /* + * Item was marked for destruction or we failed to find + * associated stream. Probably it was already destroyed -- + * just ignore the message. + */ + if (item) { + __list_del_entry(&item->list); + kfree(item); + item = NULL; + } + dev_warn(dev, "%s: drop msg 0x%x! (no owner)\n", __func__, msg_id); + vxd_drop_msg_locked(vxd); + return; + } + + /* Remove item from vxd->msgs list */ + if (item && item->msg_id == msg_id && !not_last_msg) + __list_del_entry(&item->list); + + /* + * If there's no such item on a list, or the one + * found is too small to fit the output, or it's not supposed to be + * released, allocate a new one. + */ + if (!item || (msg_size * sizeof(unsigned int) > item->msg.payload_size) || not_last_msg) { + struct vxd_item *new_item; + + new_item = kzalloc(sizeof(*new_item) + + (msg_size * sizeof(unsigned int)), GFP_KERNEL); + if (item) { + if (!new_item) { + /* + * Failed to allocate new item. Mark item as + * errored and continue best effort, provide + * only part of the message to the userspace + */ + dev_err(dev, "%s: failed to alloc new item!\n", __func__); + msg_size = item->msg.payload_size / sizeof(unsigned int); + item->msg.out_flags |= VXD_FW_MSG_FLAG_DRV_ERR; + } else { + *new_item = *item; + /* + * Do not free the old item if subsequent + * messages are expected (it also wasn't + * removed from the vxd->msgs list, so we are + * not losing a pointer here). + */ + if (!not_last_msg) + kfree(item); + item = new_item; + } + } else { + if (!new_item) { + /* + * We have no place to put the message, we have + * to drop it + */ + dev_err(dev, "%s: drop msg 0x%08x! (no mem)\n", __func__, msg_id); + vxd_drop_msg_locked(vxd); + return; + } + /* + * There was no corresponding item on the + * list and we've allocated + * a new one. Initialize it + */ + new_item->msg.out_flags = 0; + new_item->stream_id = str_id; + item = new_item; + } + } + ret = vxd_pvdec_recv_msg(dev, vxd->reg_base, item->msg.payload, msg_size, vxd); + if (ret) { + dev_err(dev, "%s: failed to receive msg from VXD!\n", __func__); + item->msg.out_flags |= VXD_FW_MSG_FLAG_DEV_ERR; + } + item->msg.payload_size = msg_size * sizeof(unsigned int); + +#ifdef DEBUG_DECODER_DRIVER + vxd_dbg_dump_msg(dev, __func__, item->msg.payload, msg_size); + + dev_dbg(dev, "%s: adding to done list, item: %p, msg_size: %zu\n", + __func__, item, msg_size); +#endif + list_add_tail(&item->list, &stream->ctx->items_done); + +#ifdef DEBUG_DECODER_DRIVER + dev_info(dev, "%s: signaling worker for %p\n", __func__, stream->ctx); +#endif + schedule_work(stream->ctx->work); +} + +/* Bottom half */ +irqreturn_t vxd_handle_thread_irq(void *dev) +{ + unsigned char no_more = FALSE; + unsigned char fatal = FALSE; + struct vxd_dev *vxd = ((const struct device *)dev)->driver_data; + struct vxd_hw_state *hw_state = &vxd->state.hw_state; + irqreturn_t ret = IRQ_HANDLED; + + if (!vxd) + return IRQ_NONE; + + mutex_lock(vxd->mutex); + + /* Spurious interrupt? */ + if (unlikely(!vxd->hw_on || vxd->hw_dead)) { + ret = IRQ_NONE; + goto out_unlock; + } + + /* Check for critical exception - only MMU faults for now */ + if (vxd_pvdec_check_irq(dev, vxd->reg_base, hw_state->irq_status) < 0) { +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "device MMU fault: resetting!!!\n"); +#endif + vxd_emrg_reset_locked(vxd, VXD_FW_MSG_FLAG_MMU_FAULT); + goto out_unlock; + } + + /* + * Single interrupt can correspond to multiple messages, handle them + * all. + */ + while (!no_more) + vxd_handle_single_msg_locked(vxd, &no_more, &fatal); + + if (fatal) { +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "fw fatal condition: resetting!!!\n"); +#endif + /* Try to recover ... */ + vxd_emrg_reset_locked(vxd, VXD_FW_MSG_FLAG_FATAL); + } else { + /* Try to submit items to the HW */ + vxd_schedule_locked(vxd); + } + +out_unlock: + hw_state->irq_status = 0; + mutex_unlock(vxd->mutex); + + return ret; +} + +static void vxd_worker(void *work) +{ + struct vxd_dev *vxd = NULL; + struct vxd_hw_state state = { 0 }; + struct vxd_item *item_tail; + + work = get_delayed_work_buff(work, FALSE); + vxd = container_of(work, struct vxd_dev, dwork); + mutex_lock(vxd->mutex); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: jif: %lu, pm: %llu dwr: %llu\n", __func__, + jiffies, vxd->pm_start, vxd->dwr_start); +#endif + + /* + * Disable the hardware if it has been idle for vxd->hw_pm_delay + * milliseconds. Or simply leave the function without doing anything + * if the HW is not supposed to be turned off. + */ + if (list_empty(&vxd->pend) && list_empty(&vxd->msgs)) { + if (vxd_is_apm_required(vxd)) { + unsigned long long dst = vxd->pm_start + + msecs_to_jiffies(vxd->hw_pm_delay); + + if (time_is_before_eq_jiffies((unsigned long)dst)) { +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: pm, power off\n", __func__); +#endif + vxd_make_hw_off_locked(vxd, FALSE); + } else { + unsigned long long targ = dst - jiffies; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: pm, reschedule: %llu\n", __func__, targ); +#endif + vxd_sched_worker_locked(vxd, jiffies_to_msecs(targ)); + } + } + goto out_unlock; + } + + /* + * We are not processing anything, but pending list is not empty (if it + * was, we would enter above. This can happen upon + * specific conditions, when input message occupies almost whole + * host->MTX ring buffer and is followed by large padding message. + */ + if (list_empty(&vxd->msgs)) { + vxd_schedule_locked(vxd); + goto out_unlock; + } + + /* Skip emergency reset if it's disabled. */ + if (vxd->hw_dwr_period <= 0) { +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: skip watchdog\n", __func__); +#endif + goto out_unlock; + } else { + /* Recalculate DWR when needed */ + unsigned long long dst = vxd->dwr_start + + msecs_to_jiffies(vxd->hw_dwr_period); + + if (time_is_after_jiffies((unsigned long)dst)) { + unsigned long long targ = dst - jiffies; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: dwr, reschedule: %llu\n", __func__, targ); +#endif + vxd_sched_worker_locked(vxd, jiffies_to_msecs(targ)); + goto out_unlock; + } + } + + /* Get ID of the oldest item being processed by the HW */ + item_tail = list_entry(vxd->msgs.prev, struct vxd_item, list); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: tail_item: %p, id: 0x%x\n", __func__, item_tail, + item_tail->msg_id); +#endif + + /* Get HW and firmware state */ + vxd_pvdec_get_state(vxd->dev, vxd->reg_base, VXD_NUM_PIX_PIPES(vxd->props), &state); + + if (vxd->state.msg_id_tail == item_tail->msg_id && + !memcmp(&state, &vxd->state.hw_state, + sizeof(struct vxd_hw_state))) { + vxd->state.msg_id_tail = 0; + memset(&vxd->state.hw_state, 0, sizeof(vxd->state.hw_state)); + dev_err(vxd->dev, "device DWR(%ums) expired: resetting!!!\n", + vxd->hw_dwr_period); + vxd_emrg_reset_locked(vxd, VXD_FW_MSG_FLAG_DWR); + } else { + /* Record current state */ + vxd->state.msg_id_tail = item_tail->msg_id; + vxd->state.hw_state = state; + + /* Submit items to the HW, if space is available. */ + vxd_schedule_locked(vxd); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: scheduling DWR work (%d ms)!\n", + __func__, vxd->hw_dwr_period); +#endif + vxd_sched_worker_locked(vxd, vxd->hw_dwr_period); + } + +out_unlock: + mutex_unlock(vxd->mutex); +} + +/* + * Lazy initialization of main driver context (when first core is probed -- we + * need heap configuration from sysdev to allocate firmware buffers. + */ +int vxd_init(void *dev, struct vxd_dev *vxd, + const struct heap_config heap_configs[], int heaps) +{ + int ret, i; + + INIT_LIST_HEAD(&vxd_drv.heaps); + vxd_drv.internal_heap_id = VXD_INVALID_ID; + + vxd_drv.mem_ctx = NULL; + + INIT_LIST_HEAD(&vxd_drv.devices); + + vxd_drv.virt_space.fw_addr = 0x42000; + vxd_drv.virt_space.rendec_addr = 0xe0000000; + + vxd_drv.initialised = 0; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: vxd drv init, params:\n", __func__); +#endif + + /* Initialise memory management component */ + for (i = 0; i < heaps; i++) { + struct vxd_heap *heap; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: adding heap of type %d\n", + __func__, heap_configs[i].type); +#endif + + heap = kzalloc(sizeof(*heap), GFP_KERNEL); + if (!heap) { + ret = -ENOMEM; + goto heap_add_failed; + } + + ret = img_mem_add_heap(&heap_configs[i], &heap->id); + if (ret < 0) { + dev_err(dev, "%s: failed to init heap (type %d)!\n", + __func__, heap_configs[i].type); + kfree(heap); + goto heap_add_failed; + } + list_add(&heap->list, &vxd_drv.heaps); + + /* Implicitly, first heap is used for internal allocations */ + if (vxd_drv.internal_heap_id < 0) { + vxd_drv.internal_heap_id = heap->id; +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: using heap %d for internal alloc\n", + __func__, vxd_drv.internal_heap_id); +#endif + } + } + + /* Do not proceed if internal heap not defined */ + if (vxd_drv.internal_heap_id < 0) { + dev_err(dev, "%s: failed to locate heap for internal alloc\n", __func__); + ret = -EINVAL; + /* Loop registered heaps just for sanity */ + goto heap_add_failed; + } + + /* Create memory management context for HW buffers */ + ret = img_mem_create_ctx(&vxd_drv.mem_ctx); + if (ret) { + dev_err(dev, "%s: failed to create mem context (err:%d)!\n", __func__, ret); + goto create_mem_context_failed; + } + + vxd->mem_ctx = vxd_drv.mem_ctx; + + /* Allocate rendec buffer */ + ret = img_mem_alloc(dev, vxd_drv.mem_ctx, vxd_drv.internal_heap_id, + VXD_RENDEC_SIZE * VXD_NUM_PIX_PIPES(vxd->props), + (enum mem_attr)0, &vxd->rendec_buf_id); + if (ret) { + dev_err(dev, "%s: alloc rendec buffer failed (err:%d)!\n", __func__, ret); + goto create_mem_context_failed; + } + + init_delayed_work(&vxd->dwork, vxd_worker, HWA_DECODER); + if (!vxd->dwork) { + ret = ENOMEM; + goto create_mem_context_failed; + } + + vxd_drv.initialised = 1; +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: vxd drv init done\n", __func__); +#endif + return 0; + +create_mem_context_failed: +heap_add_failed: + while (!list_empty(&vxd_drv.heaps)) { + struct vxd_heap *heap; + + heap = list_first_entry(&vxd_drv.heaps, struct vxd_heap, list); + __list_del_entry(&heap->list); + img_mem_del_heap(heap->id); + kfree(heap); + } + vxd_drv.internal_heap_id = VXD_INVALID_ID; + return ret; +} + +/* + * Get internal_heap_id + * TODO: Only error checking is if < 0, so if the stored value is < 0, then + * just passing the value to caller still conveys error. + * Caller must error check. + */ +int vxd_g_internal_heap_id(void) +{ + return vxd_drv.internal_heap_id; +} + +void vxd_deinit(struct vxd_dev *vxd) +{ + cancel_delayed_work_sync(vxd->dwork); + vxd_make_hw_off_locked(vxd, FALSE); + + /* Destroy memory management context */ + if (vxd_drv.mem_ctx) { + /* Deallocate rendec buffer */ + img_mem_free(vxd_drv.mem_ctx, vxd->rendec_buf_id); + + img_mem_destroy_ctx(vxd_drv.mem_ctx); + vxd_drv.mem_ctx = NULL; + } + + /* Deinitialize memory management component */ + while (!list_empty(&vxd_drv.heaps)) { + struct vxd_heap *heap; + + heap = list_first_entry(&vxd_drv.heaps, struct vxd_heap, list); + __list_del_entry(&heap->list); + img_mem_del_heap(heap->id); + kfree(heap); + } + + vxd_drv.internal_heap_id = VXD_INVALID_ID; + vxd_drv.mem_ctx = NULL; + vxd_drv.virt_space.fw_addr = 0x0; + vxd_drv.virt_space.rendec_addr = 0x0; + vxd_drv.initialised = 0; + +#ifdef ERROR_RECOVERY_SIMULATION + /* free the kernel object created to debug */ + kobject_put(vxd_dec_kobject); +#endif +} + +static void vxd_fw_loaded(const struct firmware *fw, void *context) +{ + struct vxd_dev *vxd = context; + unsigned long bin_size; + int buf_id; + struct vxd_fw_hdr *hdr; + void *buf_kptr; + int ret; + unsigned long size = 0; + const unsigned char *data = NULL; + + if (!fw) { + dev_err(vxd->dev, "Firmware binary is not present\n"); + vxd->no_fw = 1; + return; + } + + size = fw->size; + data = fw->data; + +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "FW: acquired %s size %zu\n", drv_fw_name, size); +#endif + + /* Sanity verification of the firmware */ + if (size < sizeof(struct vxd_fw_hdr)) { + dev_err(vxd->dev, "%s: firmware file too small!\n", __func__); + goto out; + } + + bin_size = size - sizeof(struct vxd_fw_hdr); + ret = img_mem_alloc(vxd->dev, vxd_drv.mem_ctx, vxd_drv.internal_heap_id, + bin_size, (enum mem_attr)0, &buf_id); + if (ret) { + dev_err(vxd->dev, "%s: failed to alloc fw buffer (err:%d)!\n", __func__, ret); + goto out; + } + + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) + goto out_release_buf; + + /* Store firmware header in vxd context */ + memcpy(hdr, data, sizeof(struct vxd_fw_hdr)); + +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "FW: info cs: %u, bs: %u, id: 0x%08x, ts: %u\n", + hdr->core_size, hdr->blob_size, + hdr->firmware_id, hdr->timestamp); +#endif + + /* Check if header is consistent */ + if (hdr->core_size > bin_size || hdr->blob_size > bin_size) { + dev_err(vxd->dev, "%s: got invalid firmware!\n", __func__); + goto out_release_hdr; + } + + /* Map the firmware buffer to CPU */ + ret = img_mem_map_km(vxd_drv.mem_ctx, buf_id); + if (ret) { + dev_err(vxd->dev, "%s: failed to map FW buf to cpu! (%d)\n", __func__, ret); + goto out_release_hdr; + } + + /* Copy firmware to device buffer */ + buf_kptr = img_mem_get_kptr(vxd_drv.mem_ctx, buf_id); + memcpy(buf_kptr, data + sizeof(struct vxd_fw_hdr), size - sizeof(struct vxd_fw_hdr)); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: FW: copied to buffer %d kptr 0x%p\n", __func__, buf_id, buf_kptr); +#endif + + img_mem_sync_cpu_to_device(vxd_drv.mem_ctx, buf_id); + + vxd->firmware.fw_size = size; + vxd->firmware.buf_id = buf_id; + vxd->firmware.hdr = hdr; + vxd->firmware.ready = TRUE; + + release_firmware(fw); + complete_all(vxd->firmware_loading_complete); + pr_debug("Firmware loaded successfully ..!!\n"); + return; + +out_release_hdr: + kfree(hdr); +out_release_buf: + img_mem_free(vxd_drv.mem_ctx, buf_id); +out: + release_firmware(fw); + complete_all(vxd->firmware_loading_complete); + kfree(vxd->firmware_loading_complete); + vxd->firmware_loading_complete = NULL; +} + +/* + * Takes the firmware from the file system and allocates a buffer + */ +int vxd_prepare_fw(struct vxd_dev *vxd) +{ + int ret; + + /* Fetch firmware from the file system */ + struct completion **firmware_loading_complete = + (struct completion **)&vxd->firmware_loading_complete; + + *firmware_loading_complete = kmalloc(sizeof(*firmware_loading_complete), GFP_KERNEL); + if (!(*firmware_loading_complete)) { + pr_err("Memory allocation failed for init_completion\n"); + return -ENOMEM; + } + init_completion(*firmware_loading_complete); + + if (!vxd->firmware_loading_complete) + return -ENOMEM; + + vxd->firmware.ready = FALSE; + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + drv_fw_name, vxd->dev, GFP_KERNEL, vxd, + vxd_fw_loaded); + if (ret < 0) { + dev_err(vxd->dev, "request_firmware_nowait err: %d\n", ret); + complete_all(vxd->firmware_loading_complete); + kfree(vxd->firmware_loading_complete); + vxd->firmware_loading_complete = NULL; + } + + return ret; +} + +/* + * Cleans firmware resources + */ +void vxd_clean_fw_resources(struct vxd_dev *vxd) +{ +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s:%d\n", __func__, __LINE__); +#endif + + wait_for_completion(vxd->firmware_loading_complete); + kfree(vxd->firmware_loading_complete); + vxd->firmware_loading_complete = NULL; + + if (vxd->firmware.fw_size) { + img_mem_free(vxd_drv.mem_ctx, vxd->firmware.buf_id); + kfree(vxd->firmware.hdr); + vxd->firmware.hdr = NULL; +#ifdef DEBUG_DECODER_DRIVER + dev_info(vxd->dev, "FW: released %s\n", drv_fw_name); +#endif + vxd->firmware.buf_id = VXD_INVALID_ID; + } +} + +/* + * Submit a message to the VXD. + * is used to verify that requested stream id (item->stream_id) is valid + * for this ctx + */ +int vxd_send_msg(struct vxd_dec_ctx *ctx, struct vxd_fw_msg *msg) +{ + struct vxd_dev *vxd = ctx->dev; + unsigned long msg_size; + struct vxd_item *item; + struct vxd_stream *stream; + int ret; + + if (msg->payload_size < VXD_MIN_INPUT_SIZE) + return -EINVAL; + + if (msg->payload_size % sizeof(unsigned int)) { + dev_err(vxd->dev, "msg size not aligned! (%u)\n", + msg->payload_size); + return -EINVAL; + } + + msg_size = VXD_MSG_SIZE(*msg); + + if (msg_size > VXD_MAX_INPUT_SIZE) + return -EINVAL; + + /* Verify that the gap was left for stream PTD */ + if (msg->payload[VXD_PTD_MSG_OFFSET] != 0) { + dev_err(vxd->dev, "%s: PTD gap missing!\n", __func__); + return -EINVAL; + } + + ret = mutex_lock_interruptible_nested(ctx->mutex, SUBCLASS_VXD_CORE); + if (ret) + return ret; + + stream = idr_find(vxd->streams, ctx->stream.id); + if (!stream) { + dev_warn(vxd->dev, "%s: invalid stream id requested! (%u)\n", + __func__, ctx->stream.id); + + ret = -EINVAL; + goto out_unlock; + } + + item = kmalloc(sizeof(*item) + msg->payload_size, GFP_KERNEL); + if (!item) { + ret = -ENOMEM; + goto out_unlock; + } + + memcpy(&item->msg, msg, msg_size); + + msg->out_flags &= VXD_FW_MSG_WR_FLAGS_MASK; + item->stream_id = ctx->stream.id; + item->msg_id = 0; + item->msg.out_flags = msg->out_flags; + item->destroy = 0; + + /* + * Inject the stream PTD into the message. It was already verified that + * there is enough space. + */ + item->msg.payload[VXD_PTD_MSG_OFFSET] = stream->ptd; + + list_add_tail(&item->list, &vxd->pend); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, + "%s: added item %p to pend, ptd: 0x%x, str: %u flags: 0x%x\n", + __func__, item, stream->ptd, stream->id, item->msg.out_flags); +#endif + + vxd_schedule_locked(vxd); + +out_unlock: + mutex_unlock(ctx->mutex); + + return ret; +} + +int vxd_suspend_dev(void *dev) +{ + struct vxd_dev *vxd = platform_get_drvdata(to_platform_device(dev)); + + mutex_lock(vxd->mutex); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: taking a nap!\n", __func__); +#endif + + /* Cancel the worker first */ + cancel_delayed_work(vxd->dwork); + + /* Forcing hardware disable */ + vxd_make_hw_off_locked(vxd, TRUE); + + /* Move all valid items to the pending queue */ + vxd_rewind_msgs_locked(vxd); + + mutex_unlock(vxd->mutex); + + return 0; +} + +int vxd_resume_dev(void *dev) +{ + struct vxd_dev *vxd = platform_get_drvdata(to_platform_device(dev)); + int ret = 0; + + mutex_lock(vxd->mutex); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: waking up!\n", __func__); +#endif + + mutex_unlock(vxd->mutex); + + return ret; +} + +int vxd_map_buffer_sg(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx, + unsigned int str_id, + unsigned int buff_id, + void *sgt, unsigned int virt_addr, + unsigned int map_flags) +{ + struct vxd_stream *stream; + unsigned int flags = VXD_MMU_PTD_FLAG_NONE; + int ret; + + ret = mutex_lock_interruptible_nested(ctx->mutex, SUBCLASS_VXD_CORE); + if (ret) + return ret; + + stream = idr_find(vxd->streams, str_id); + if (!stream) { + dev_err(vxd->dev, "%s: stream %d not found!\n", __func__, str_id); + ret = -EINVAL; + goto out_unlock; + } + + if ((map_flags & (VXD_MAP_FLAG_READ_ONLY | VXD_MAP_FLAG_WRITE_ONLY)) + == (VXD_MAP_FLAG_READ_ONLY | VXD_MAP_FLAG_WRITE_ONLY)) { + dev_err(vxd->dev, "%s: Bogus mapping flags 0x%x!\n", __func__, + map_flags); + ret = -EINVAL; + goto out_unlock; + } + + /* Convert permission flags to internal definitions */ + if (map_flags & VXD_MAP_FLAG_READ_ONLY) + flags |= VXD_MMU_PTD_FLAG_READ_ONLY; + + if (map_flags & VXD_MAP_FLAG_WRITE_ONLY) + flags |= VXD_MMU_PTD_FLAG_WRITE_ONLY; + + ret = img_mmu_map_sg(stream->mmu_ctx, ctx->mem_ctx, buff_id, sgt, virt_addr, flags); + if (ret) { + dev_err(vxd->dev, "%s: map failed!\n", __func__); + goto out_unlock; + } + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, + "%s: mapped buf %u to 0x%08x, str_id: %u flags: 0x%x\n", + __func__, buff_id, virt_addr, str_id, flags); +#endif + +out_unlock: + mutex_unlock(ctx->mutex); + return ret; +} + +int vxd_map_buffer(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx, unsigned int str_id, + unsigned int buff_id, + unsigned int virt_addr, + unsigned int map_flags) +{ + struct vxd_stream *stream; + unsigned int flags = VXD_MMU_PTD_FLAG_NONE; + int ret; + + ret = mutex_lock_interruptible_nested(ctx->mutex, SUBCLASS_VXD_CORE); + if (ret) + return ret; + + stream = idr_find(vxd->streams, str_id); + if (!stream) { + dev_err(vxd->dev, "%s: stream %d not found!\n", __func__, str_id); + ret = -EINVAL; + goto out_unlock; + } + + if ((map_flags & (VXD_MAP_FLAG_READ_ONLY | VXD_MAP_FLAG_WRITE_ONLY)) + == (VXD_MAP_FLAG_READ_ONLY | VXD_MAP_FLAG_WRITE_ONLY)) { + dev_err(vxd->dev, "%s: Bogus mapping flags 0x%x!\n", __func__, map_flags); + ret = -EINVAL; + goto out_unlock; + } + + /* Convert permission flags to internal definitions */ + if (map_flags & VXD_MAP_FLAG_READ_ONLY) + flags |= VXD_MMU_PTD_FLAG_READ_ONLY; + + if (map_flags & VXD_MAP_FLAG_WRITE_ONLY) + flags |= VXD_MMU_PTD_FLAG_WRITE_ONLY; + + ret = img_mmu_map(stream->mmu_ctx, ctx->mem_ctx, buff_id, virt_addr, flags); + if (ret) { + dev_err(vxd->dev, "%s: map failed!\n", __func__); + goto out_unlock; + } + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, + "%s: mapped buf %u to 0x%08x, str_id: %u flags: 0x%x\n", + __func__, buff_id, virt_addr, str_id, flags); +#endif + +out_unlock: + mutex_unlock(ctx->mutex); + return ret; +} + +int vxd_unmap_buffer(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx, + unsigned int str_id, unsigned int buff_id) +{ + struct vxd_stream *stream; + int ret; + + ret = mutex_lock_interruptible_nested(ctx->mutex, SUBCLASS_VXD_CORE); + if (ret) + return ret; + + stream = idr_find(vxd->streams, str_id); + if (!stream) { + dev_err(vxd->dev, "%s: stream %d not found!\n", __func__, str_id); + ret = -EINVAL; + goto out_unlock; + } + + ret = img_mmu_unmap(stream->mmu_ctx, ctx->mem_ctx, buff_id); + if (ret) { + dev_err(vxd->dev, "%s: map failed!\n", __func__); + goto out_unlock; + } + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(vxd->dev, "%s: unmapped buf %u str_id: %u\n", __func__, buff_id, str_id); +#endif + +out_unlock: mutex_unlock(ctx->mutex); + return ret; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_dec.c b/drivers/media/platform/vxe-vxd/decoder/vxd_dec.c --- a/drivers/media/platform/vxe-vxd/decoder/vxd_dec.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_dec.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC SYSDEV and UI Interface function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include + +#include "core.h" +#include "h264fw_data.h" +#include "hevcfw_data.h" +#include "img_dec_common.h" +#include "vxd_pvdec_priv.h" + +unsigned int get_nbuffers(enum vdec_vid_std std, int w, int h, + unsigned int max_num_ref_frames) +{ + unsigned int nbuffers; + + switch (std) { + case VDEC_STD_H264: + /* + * Request number of buffers from header bspp information + * using formula N + Display Lag + * Parser is passing (2*N) + */ + if (max_num_ref_frames == 0) { + nbuffers = DISPLAY_LAG + min(MAX_CAPBUFS_H264, + (184320 / ((w / 16) * (h / 16)))); + } else { + nbuffers = max_num_ref_frames + DISPLAY_LAG; + } + break; + case VDEC_STD_HEVC: + if (max_num_ref_frames == 0) { + if ((w * h) <= (HEVC_MAX_LUMA_PS >> 2)) + nbuffers = 16; + else if ((w * h) <= (HEVC_MAX_LUMA_PS >> 1)) + nbuffers = 12; + else if ((w * h) <= ((3 * HEVC_MAX_LUMA_PS) >> 2)) + nbuffers = 8; + else + nbuffers = 6; + nbuffers += DISPLAY_LAG; + } else { + nbuffers = max_num_ref_frames + DISPLAY_LAG; + } + break; +#ifdef HAS_JPEG + case VDEC_STD_JPEG: + /* + * Request number of output buffers based on h264 spec + * + display delay + */ + nbuffers = DISPLAY_LAG + min(MAX_CAPBUFS_H264, + (184320 / ((w / 16) * (h / 16)))); + break; +#endif + default: + nbuffers = 0; + } + + return nbuffers; +} + +int vxd_dec_alloc_bspp_resource(struct vxd_dec_ctx *ctx, enum vdec_vid_std vid_std) +{ + struct vxd_dev *vxd_dev = ctx->dev; + struct device *dev = vxd_dev->v4l2_dev.dev; + struct vdec_buf_info buf_info; + struct bspp_ddbuf_array_info *fw_sequ = ctx->fw_sequ; + struct bspp_ddbuf_array_info *fw_pps = ctx->fw_pps; + int attributes = 0, heap_id = 0, size = 0; + int i, ret = 0; + + attributes = SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE | + SYS_MEMATTRIB_INTERNAL | SYS_MEMATTRIB_CPU_WRITE; + heap_id = vxd_g_internal_heap_id(); + + size = vid_std == VDEC_STD_HEVC ? + sizeof(struct hevcfw_sequence_ps) : sizeof(struct h264fw_sequence_ps); + +#ifdef HAS_JPEG + if (vid_std == VDEC_STD_JPEG) + size = sizeof(struct vdec_jpeg_sequ_hdr_info); +#endif + + for (i = 0; i < MAX_SEQUENCES; i++) { + ret = img_mem_alloc(vxd_dev->dev, ctx->mem_ctx, heap_id, + size, (enum mem_attr)attributes, + (int *)&fw_sequ[i].ddbuf_info.buf_id); + if (ret) { + dev_err(dev, "Couldn't allocate sequ buffer %d\n", i); + return -ENOMEM; + } + ret = img_mem_map_km(ctx->mem_ctx, fw_sequ[i].ddbuf_info.buf_id); + if (ret) { + dev_err(dev, "Couldn't map sequ buffer %d\n", i); + return -ENOMEM; + } + fw_sequ[i].ddbuf_info.cpu_virt_addr = img_mem_get_kptr + (ctx->mem_ctx, + fw_sequ[i].ddbuf_info.buf_id); + fw_sequ[i].buf_offset = 0; + fw_sequ[i].buf_element_size = size; + fw_sequ[i].ddbuf_info.buf_size = size; + fw_sequ[i].ddbuf_info.mem_attrib = (enum sys_emem_attrib)attributes; + memset(fw_sequ[i].ddbuf_info.cpu_virt_addr, 0, size); + + buf_info.cpu_linear_addr = + fw_sequ[i].ddbuf_info.cpu_virt_addr; + buf_info.buf_size = size; + buf_info.fd = -1; + buf_info.buf_id = fw_sequ[i].ddbuf_info.buf_id; + buf_info.mem_attrib = + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE | + SYS_MEMATTRIB_INPUT | SYS_MEMATTRIB_CPU_WRITE); + + ret = core_stream_map_buf(ctx->res_str_id, VDEC_BUFTYPE_BITSTREAM, &buf_info, + &fw_sequ[i].ddbuf_info.bufmap_id); + if (ret) { + dev_err(dev, "sps core_stream_map_buf failed\n"); + return ret; + } + } + +#ifdef HAS_JPEG + if (vid_std == VDEC_STD_JPEG) + return 0; +#endif + + size = vid_std == VDEC_STD_HEVC ? + sizeof(struct hevcfw_picture_ps) : sizeof(struct h264fw_picture_ps); + + for (i = 0; i < MAX_PPSS; i++) { + ret = img_mem_alloc(vxd_dev->dev, ctx->mem_ctx, heap_id, size, + (enum mem_attr)attributes, + (int *)&fw_pps[i].ddbuf_info.buf_id); + if (ret) { + dev_err(dev, "Couldn't allocate sequ buffer %d\n", i); + return -ENOMEM; + } + ret = img_mem_map_km(ctx->mem_ctx, fw_pps[i].ddbuf_info.buf_id); + if (ret) { + dev_err(dev, "Couldn't map sequ buffer %d\n", i); + return -ENOMEM; + } + fw_pps[i].ddbuf_info.cpu_virt_addr = img_mem_get_kptr(ctx->mem_ctx, + fw_pps[i].ddbuf_info.buf_id); + fw_pps[i].buf_offset = 0; + fw_pps[i].buf_element_size = size; + fw_pps[i].ddbuf_info.buf_size = size; + fw_pps[i].ddbuf_info.mem_attrib = (enum sys_emem_attrib)attributes; + memset(fw_pps[i].ddbuf_info.cpu_virt_addr, 0, size); + + buf_info.cpu_linear_addr = + fw_pps[i].ddbuf_info.cpu_virt_addr; + buf_info.buf_size = size; + buf_info.fd = -1; + buf_info.buf_id = fw_pps[i].ddbuf_info.buf_id; + buf_info.mem_attrib = + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE | + SYS_MEMATTRIB_INPUT | SYS_MEMATTRIB_CPU_WRITE); + + ret = core_stream_map_buf(ctx->res_str_id, VDEC_BUFTYPE_BITSTREAM, &buf_info, + &fw_pps[i].ddbuf_info.bufmap_id); + if (ret) { + dev_err(dev, "pps core_stream_map_buf failed\n"); + return ret; + } + } + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_dec.h b/drivers/media/platform/vxe-vxd/decoder/vxd_dec.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_dec.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_dec.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,477 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMG DEC SYSDEV and UI Interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef _VXD_DEC_H +#define _VXD_DEC_H + +#include +#include +#include +#include +#include +#include + +#include "bspp.h" +#include "img_dec_common.h" +#include "img_mem_man.h" +#include "img_pixfmts.h" +#include "pixel_api.h" +#include "vdecdd_defs.h" +#include "vdec_defs.h" +#include "work_queue.h" + +#define VXD_MIN_STREAM_ID 1 +#define VXD_MAX_STREAMS_PER_DEV 254 +#define VXD_MAX_STREAM_ID (VXD_MIN_STREAM_ID + VXD_MAX_STREAMS_PER_DEV) + +#define CODEC_NONE -1 +#define CODEC_H264_DEC 0 +#define CODEC_MPEG4_DEC 1 +#define CODEC_VP8_DEC 2 +#define CODEC_VC1_DEC 3 +#define CODEC_MPEG2_DEC 4 +#define CODEC_JPEG_DEC 5 +#define CODEC_VP9_DEC 6 +#define CODEC_HEVC_DEC 7 + +#define MAX_SEGMENTS 6 +#define HW_ALIGN 64 + +#define MAX_BUF_TRACE 30 + +#define MAX_CAPBUFS_H264 16 +#define DISPLAY_LAG 3 +#define HEVC_MAX_LUMA_PS 35651584 + +#define MAX_PLANES 3 + +enum { + Q_DATA_SRC = 0, + Q_DATA_DST = 1, + Q_DATA_FORCE32BITS = 0x7FFFFFFFU +}; + +enum { + IMG_DEC_FMT_TYPE_CAPTURE = 0x01, + IMG_DEC_FMT_TYPE_OUTPUT = 0x10, + IMG_DEC_FMT_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +enum vxd_map_flags { + VXD_MAP_FLAG_NONE = 0x0, + VXD_MAP_FLAG_READ_ONLY = 0x1, + VXD_MAP_FLAG_WRITE_ONLY = 0x2, + VXD_MAP_FLAG_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * struct vxd_fw_msg - This structure holds the information about the message + * exchanged in read/write between Kernel and firmware. + * + * @out_flags: indicating the type of message + * @payload_size: size of payload in bytes + * @payload: data which is send to firmware + */ +struct vxd_fw_msg { + unsigned int out_flags; + unsigned int payload_size; + unsigned int payload[0]; +}; + +/* HW state */ +struct vxd_hw_state { + unsigned int fw_counter; + unsigned int fe_status[VXD_MAX_PIPES]; + unsigned int be_status[VXD_MAX_PIPES]; + unsigned int dmac_status[VXD_MAX_PIPES][2]; /* Cover DMA chan 2/3*/ + unsigned int irq_status; +}; + +/* + * struct vxd_state - contains VXD HW state + * + * @hw_state: HW state + * @msg_id_tail: msg id of the oldest item being processed + */ +struct vxd_state { + struct vxd_hw_state hw_state; + unsigned short msg_id_tail; +}; + +/* + * struct vxd_dec_fmt - contains info for each of the supported video format + * + * @fourcc: V4L2 pixel format FCC identifier + * @num_planes: number of planes required for luma and chroma + * @type: CAPTURE or OUTPUT + * @std: VDEC video standard + * @pixfmt: IMG pixel format + * @interleave: Chroma interleave order + * @idc: Chroma format + * @size_num: Numberator used to calculate image size + * @size_den: Denominator used to calculate image size + * @bytes_pp: Bytes per pixel for this format + */ +struct vxd_dec_fmt { + unsigned int fourcc; + unsigned int num_planes; + unsigned char type; + enum vdec_vid_std std; + enum img_pixfmt pixfmt; + enum pixel_chroma_interleaved interleave; + enum pixel_fmt_idc idc; + int size_num; + int size_den; + int bytes_pp; +}; + +/* + * struct vxd_item - contains information about the item sent to fw + * + * @list: item to be linked list to items_done, msgs, or pend. + * @stream_id: stream id + * @msg_id: message id + * @destroy: item belongs to the stream which is destroyed + * @msg: contains msg between kernel and fw + */ +struct vxd_item { + struct list_head list; + unsigned int stream_id; + unsigned int msg_id; + struct { + unsigned destroy : 1; + }; + struct vxd_fw_msg msg; +}; + +enum vxd_cb_type { + VXD_CB_STRUNIT_PROCESSED, + VXD_CB_SPS_RELEASE, + VXD_CB_PPS_RELEASE, + VXD_CB_PICT_DECODED, + VXD_CB_PICT_DISPLAY, + VXD_CB_PICT_RELEASE, + VXD_CB_PICT_END, + VXD_CB_STR_END, + VXD_CB_ERROR_FATAL, + VXD_CB_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * vxd_cb - Return a resource to vxd + * + * @ctx: the vxd stream context + * @type: the type of message + * @buf_map_id: the buf_map_id of the resource being returned + */ +typedef void (*vxd_cb)(void *ctx, enum vxd_cb_type type, unsigned int buf_map_id); + +/* + * struct vxd_return - contains information about items returning from core + * + * @type: Type of item being returned + * @buf_map_id: mmu mapped id of buffer being returned + */ +struct vxd_return { + void *work; + struct vxd_dec_ctx *ctx; + enum vxd_cb_type type; + unsigned int buf_map_id; +}; + +/* + * struct vxd_dec_q_data - contains queue data information + * + * @fmt: format info + * @width: frame width + * @height: frame height + * @bytesperline: bytes per line in memory + * @size_image: image size in memory + */ +struct vxd_dec_q_data { + struct vxd_dec_fmt *fmt; + unsigned int width; + unsigned int height; + unsigned int bytesperline[MAX_PLANES]; + unsigned int size_image[MAX_PLANES]; +}; + +/* + * struct time_prof - contains time taken by decoding information + * + * @id: id info + * @start_time: start time + * @end_time: end time + */ +struct time_prof { + unsigned int id; + long long start_time; + long long end_time; +}; + +/* + * struct vxd_dev - The struct containing decoder driver internal parameters. + * + * @v4l2_dev: main struct of V4L2 device drivers + * @dev: platform device driver + * @vfd_dec: video device structure to create and manage the V4L2 device node. + * @plat_dev: linux platform device + * @struct v4l2_m2m_dev: mem2mem device + * @mutex: mutex to protect certain ongoing operation. + * @module_irq: a threaded request IRQ for the device + * @reg_base: base address of the IMG VXD hw registers + * @props: contains HW properties + * @mmu_config_addr_width: indicates the number of extended address bits + * (above 32) that the external memory interface + * uses, based on EXTENDED_ADDR_RANGE field of + * MMU_CONFIG0 + * @rendec_buf_id: buffer id for rendec buffer allocation + * @firmware: firmware information based on vxd_dev_fw structure + * @firmware_loading_complete: loading completion + * @no_fw: Just to check if firmware is present in /lib + * @fw_refcnt: firmware reference counter + * @hw_on: indication if hw is on or off + * @hw_dead: indication if hw is dead + * @lock: basic primitive for locking through spinlock + * @state: internal state handling of vxd state + * @msgs: linked list of msgs with vxd_item + * @pend: linked list of pending msgs to be sent to fw + * @msg_cnt: counter of messages submitted to VXD. Wraps every VXD_MSG_ID_MASK + * @freq_khz: Core clock frequency measured during boot of firmware + * @streams: unique id for the stream + * @mem_ctx: memory management context for HW buffers + * @dwork: use for Power Management and Watchdog + * @work_sched_at: the time of the last work has been scheduled at + * @emergency: indicates if emergency condition occurred + * @dbgfs_ctx: pointer to debug FS context. + * @hw_pm_delay: delay before performaing PM + * @hw_dwr_period: period for checking for dwr + * @pm_start: time, in jiffies, when core become idle + * @dwr_start: time, in jiffies, when dwr has been started + */ +struct vxd_dev { + struct v4l2_device v4l2_dev; + void *dev; + struct video_device *vfd_dec; + struct platform_device *plat_dev; + struct v4l2_m2m_dev *m2m_dev; + struct mutex *mutex; /* Per device mutex */ + int module_irq; + void __iomem *reg_base; + struct vxd_core_props props; + unsigned int mmu_config_addr_width; + int rendec_buf_id; + struct vxd_dev_fw firmware; + void *firmware_loading_complete; + unsigned char no_fw; + unsigned char fw_refcnt; + unsigned int hw_on; + unsigned int hw_dead; + void *lock; /* basic device level spinlock */ + struct vxd_state state; + struct list_head msgs; + struct list_head pend; + int msg_cnt; + unsigned int freq_khz; + struct idr *streams; + struct mem_ctx *mem_ctx; + void *dwork; + unsigned long long work_sched_at; + unsigned int emergency; + void *dbgfs_ctx; + unsigned int hw_pm_delay; + unsigned int hw_dwr_period; + unsigned long long pm_start; + unsigned long long dwr_start; + struct time_prof time_fw[MAX_BUF_TRACE]; + struct time_prof time_drv[MAX_BUF_TRACE]; + + /* The variables defined below are used in RTOS only. */ + /* This variable holds queue handler */ + void *vxd_worker_queue_handle; + void *vxd_worker_queue_sem_handle; +}; + +/* + * struct vxd_stream - holds stream-related info + * + * @ctx: associated vxd_dec_ctx + * @mmu_ctx: MMU context for this stream + * @ptd: ptd for the stream + * @id: unique stream id + */ +struct vxd_stream { + struct vxd_dec_ctx *ctx; + struct mmu_ctx *mmu_ctx; + unsigned int ptd; + unsigned int id; +}; + +/* + * struct vxd_buffer - holds per buffer info. + * @buffer: the vb2_v4l2_buffer + * @list: list head for gathering in linked list + * @mapped: is this buffer mapped yet + * @reuse: is the buffer ready for reuse + * @buf_map_id: the mapped buffer id + * @buf_info: the buffer info for submitting to map + * @bstr_info: the buffer info for submitting to bspp + * @seq_unit: the str_unit for submitting sps + * @seq_unit: the str_unit for submitting pps and segments + * @seq_unit: the str_unit for submitting picture_end + */ +struct vxd_buffer { + struct v4l2_m2m_buffer buffer; + struct list_head list; + unsigned char mapped; + unsigned char reuse; + unsigned int buf_map_id; + struct vdec_buf_info buf_info; + struct bspp_ddbuf_info bstr_info; + struct vdecdd_str_unit seq_unit; + struct vdecdd_str_unit pic_unit; + struct vdecdd_str_unit end_unit; + struct bspp_preparsed_data preparsed_data; +}; + +typedef void (*decode_cb)(int res_str_id, unsigned int *msg, unsigned int msg_size, + unsigned int msg_flags); + +/* + * struct vxd_dec_ctx - holds per stream data. Each playback has its own + * vxd_dec_ctx + * + * @fh: V4L2 file handler + * @dev: pointer to the device main information. + * @ctrl_hdl_dec: v4l2 custom control command for video decoder + * @mem_ctx: mem context for this stream + * @mmu_ctx: MMU context for this stream + * @ptd: page table information + * @items_done: linked list of items is ready + * @width: frame width + * @height: frame height + * @width_orig: original frame width (before padding) + * @height_orig: original frame height (before padding) + * @q_data: Queue data information of src[0] and dst[1] + * @stream: stream-related info + * @work: work queue for message handling + * @return_queue: list of resources returned from core + * @out_buffers: list of all output buffers + * @cap_buffers: list of all capture buffers except those in reuse_queue + * @reuse_queue: list of capture buffers waiting for core to signal reuse + * @res_str_id: Core stream id + * @stream_created: Core stream is created + * @stream_configured: Core stream is configured + * @opconfig_pending: Core opconfig is pending stream_create + * @src_streaming: V4L2 src stream is streaming + * @dst_streaming: V4L2 dst stream is streaming + * @core_streaming: core is streaming + * @aborting: signal job abort on next irq + * @str_opcfg: core output config + * @pict_bufcfg: core picture buffer config + * @bspp_context: BSPP Stream context handle + * @seg_list: list of bspp_bitstr_seg for submitting to BSPP + * @fw_sequ: BSPP sps resource + * @fw_pps: BSPP pps resource + * @cb: registered callback for incoming messages + * @mutex: mutex to protect context specific state machine + */ +struct vxd_dec_ctx { + struct v4l2_fh fh; + struct vxd_dev *dev; + struct mem_ctx *mem_ctx; + struct mmu_ctx *mmu_ctx; + unsigned int ptd; + struct list_head items_done; + unsigned int width; + unsigned int height; + unsigned int width_orig; + unsigned int height_orig; + struct vxd_dec_q_data q_data[2]; + struct vxd_stream stream; + void *work; + struct list_head return_queue; + struct list_head out_buffers; + struct list_head cap_buffers; + struct list_head reuse_queue; + unsigned int res_str_id; + unsigned char stream_created; + unsigned char stream_configured; + unsigned char opconfig_pending; + unsigned char src_streaming; + unsigned char dst_streaming; + unsigned char core_streaming; + unsigned char aborting; + unsigned char eos; + unsigned char stop_initiated; + unsigned char flag_last; + unsigned char num_decoding; + unsigned int max_num_ref_frames; + struct vdec_str_opconfig str_opcfg; + struct vdec_pict_bufconfig pict_bufcfg; + void *bspp_context; + struct bspp_bitstr_seg bstr_segments[MAX_SEGMENTS]; + struct lst_t seg_list; + struct bspp_ddbuf_array_info fw_sequ[MAX_SEQUENCES]; + struct bspp_ddbuf_array_info fw_pps[MAX_PPSS]; + decode_cb cb; + struct mutex *mutex; /* Per stream mutex */ + + /* The below variable used only in Rtos */ + void *mm_return_resource; /* Place holder for CB to application */ + void *stream_worker_queue_handle; + void *stream_worker_queue_sem_handle; + // lock is used to synchronize the stream worker and process function + void *lock; + /* "sem_eos" this semaphore variable used to wait until all frame decoded */ + void *sem_eos; +}; + +irqreturn_t vxd_handle_irq(void *dev); +irqreturn_t vxd_handle_thread_irq(void *dev); +int vxd_init(void *dev, struct vxd_dev *vxd, const struct heap_config heap_configs[], int heaps); +int vxd_g_internal_heap_id(void); +void vxd_deinit(struct vxd_dev *vxd); +int vxd_prepare_fw(struct vxd_dev *vxd); +void vxd_clean_fw_resources(struct vxd_dev *vxd); +int vxd_send_msg(struct vxd_dec_ctx *ctx, struct vxd_fw_msg *msg); +int vxd_suspend_dev(void *dev); +int vxd_resume_dev(void *dev); + +int vxd_create_ctx(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx); +void vxd_destroy_ctx(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx); + +int vxd_map_buffer_sg(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx, + unsigned int str_id, unsigned int buff_id, + void *sgt, unsigned int virt_addr, + unsigned int map_flags); +int vxd_map_buffer(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx, unsigned int str_id, + unsigned int buff_id, unsigned int virt_addr, unsigned int map_flags); +int vxd_unmap_buffer(struct vxd_dev *vxd, struct vxd_dec_ctx *ctx, + unsigned int str_id, unsigned int buff_id); + +unsigned int get_nbuffers(enum vdec_vid_std std, int w, int h, unsigned int max_num_ref_frames); + +int vxd_dec_alloc_bspp_resource(struct vxd_dec_ctx *ctx, enum vdec_vid_std vid_std); + +#ifdef ERROR_RECOVERY_SIMULATION +/* sysfs read write functions */ +ssize_t vxd_sysfs_show(struct kobject *vxd_dec_kobject, + struct kobj_attribute *attr, char *buf); + +ssize_t vxd_sysfs_store(struct kobject *vxd_dec_kobject, + struct kobj_attribute *attr, const char *buf, unsigned long count); +#endif +#endif /* _VXD_DEC_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_ext.h b/drivers/media/platform/vxe-vxd/decoder/vxd_ext.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_ext.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_ext.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC Low-level device interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + */ + +#ifndef _VXD_EXT_H +#define _VXD_EXT_H + +#define VLR_COMPLETION_COMMS_AREA_SIZE 476 + +/* Word Size of buffer used to pass messages between LISR and HISR */ +#define VXD_SIZE_MSG_BUFFER (1 * 1024) + +/* This structure describes macroblock coordinates. */ +struct vxd_mb_coords { + unsigned int x; + unsigned int y; +}; + +/* This structure contains firmware and decoding pipe state information. */ +struct vxd_pipestate { + unsigned char is_pipe_present; + unsigned char cur_codec; + unsigned int acheck_point[VDECFW_CHECKPOINT_MAX]; + unsigned int firmware_action; + unsigned int fe_slices; + unsigned int be_slices; + unsigned int fe_errored_slices; + unsigned int be_errored_slices; + unsigned int be_mbs_dropped; + unsigned int be_mbs_recovered; + struct vxd_mb_coords fe_mb; + struct vxd_mb_coords be_mb; +}; + +/* This structure contains firmware and decoder core state information. */ +struct vxd_firmware_state { + unsigned int fw_step; + struct vxd_pipestate pipe_state[VDECFW_MAX_DP]; +}; + +/* This structure contains the video decoder device state. */ +struct vxd_states { + struct vxd_firmware_state fw_state; +}; + +struct vxd_pict_attrs { + unsigned int dwrfired; + unsigned int mmufault; + unsigned int deverror; +}; + +/* This type defines the message attributes. */ +enum vxd_msg_attr { + VXD_MSG_ATTR_NONE = 0, + VXD_MSG_ATTR_DECODED = 1, + VXD_MSG_ATTR_FATAL = 2, + VXD_MSG_ATTR_CANCELED = 3, + VXD_MSG_ATTR_FORCE32BITS = 0x7FFFFFFFU +}; + +enum vxd_msg_flag { + VXD_MSG_FLAG_DROP = 0, + VXD_MSG_FLAG_EXCL = 1, + VXD_MSG_FLAG_FORCE32BITS = 0x7FFFFFFFU +}; + +#endif /* VXD_EXT_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_int.c b/drivers/media/platform/vxe-vxd/decoder/vxd_int.c --- a/drivers/media/platform/vxe-vxd/decoder/vxd_int.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_int.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VXD DEC Common low level core interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include +#include + +#include "bspp.h" +#include "fw_interface.h" +#include "h264fw_data.h" +#include "img_errors.h" +#include "img_dec_common.h" +#include "img_pvdec_core_regs.h" +#include "img_pvdec_pixel_regs.h" +#include "img_pvdec_test_regs.h" +#include "img_vdec_fw_msg.h" +#include "img_video_bus4_mmu_regs.h" +#include "img_msvdx_core_regs.h" +#include "img_msvdx_cmds.h" +#include "reg_io2.h" +#include "scaler_setup.h" +#include "vdecdd_defs.h" +#include "vdecdd_utils.h" +#include "vdecfw_shared.h" +#include "vdec_defs.h" +#include "vxd_ext.h" +#include "vxd_int.h" +#include "vxd_props.h" + +#define MSVDX_CACHE_REF_OFFSET_V100 (72L) +#define MSVDX_CACHE_ROW_OFFSET_V100 (4L) + +#define MSVDX_CACHE_REF_OFFSET_V550 (144L) +#define MSVDX_CACHE_ROW_OFFSET_V550 (8L) + +#define GET_BITS(v, lb, n) (((v) >> (lb)) & ((1 << (n)) - 1)) +#define IS_PVDEC_PIPELINE(std) ((std) == VDEC_STD_HEVC ? 1 : 0) + +static int amsvdx_codecmode[VDEC_STD_MAX] = { + /* Invalid */ + -1, + /* MPEG2 */ + 3, + /* MPEG4 */ + 4, + /* H263 */ + 4, + /* H264 */ + 1, + /* VC1 */ + 2, + /* AVS */ + 5, + /* RealVideo (8) */ + 8, + /* JPEG */ + 0, + /* On2 VP6 */ + 10, + /* On2 VP8 */ + 11, + /* Invalid */ +#ifdef HAS_VP9 + /* On2 VP9 */ + 13, +#endif + /* Sorenson */ + 4, + /* HEVC */ + 12, +}; + +struct msvdx_scaler_coeff_cmds { + unsigned int acmd_horizluma_coeff[VDECFW_NUM_SCALE_COEFFS]; + unsigned int acmd_vertluma_coeff[VDECFW_NUM_SCALE_COEFFS]; + unsigned int acmd_horizchroma_coeff[VDECFW_NUM_SCALE_COEFFS]; + unsigned int acmd_vertchroma_coeff[VDECFW_NUM_SCALE_COEFFS]; +}; + +static struct vxd_vidstd_props astd_props[] = { + { VDEC_STD_MPEG2, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_MPEG4, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_H263, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_H264, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0x10000, 8, + 8, PIXEL_FORMAT_420 }, + { VDEC_STD_VC1, CORE_REVISION(7, 0, 0), 80, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_AVS, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_REAL, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_JPEG, CORE_REVISION(7, 0, 0), 64, 16, 32768, 32768, 0, 8, 8, + PIXEL_FORMAT_444 }, + { VDEC_STD_VP6, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_VP8, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, 8, + PIXEL_FORMAT_420 }, + { VDEC_STD_SORENSON, CORE_REVISION(7, 0, 0), 64, 16, 4096, 4096, 0, 8, + 8, PIXEL_FORMAT_420 }, + { VDEC_STD_HEVC, CORE_REVISION(7, 0, 0), 64, 16, 8192, 8192, 0, 8, 8, + PIXEL_FORMAT_420 }, +}; + +enum vdec_msvdx_async_mode { + VDEC_MSVDX_ASYNC_NORMAL, + VDEC_MSVDX_ASYNC_VDMC, + VDEC_MSVDX_ASYNC_VDEB, + VDEC_MSVDX_ASYNC_FORCE32BITS = 0x7FFFFFFFU +}; + +/* MSVDX row strides for video buffers. */ +static const unsigned int amsvdx_64byte_row_stride[] = { + 384, 768, 1280, 1920, 512, 1024, 2048, 4096 +}; + +/* MSVDX row strides for jpeg buffers. */ +static const unsigned int amsvdx_jpeg_row_stride[] = { + 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768 +}; + +/* VXD Core major revision. */ +static unsigned int maj_rev; +/* VXD Core minor revision. */ +static unsigned int min_rev; +/* VXD Core maintenance revision. */ +static unsigned int maint_rev; + +static int get_stride_code(enum vdec_vid_std vidstd, unsigned int row_stride) +{ + unsigned int i; + + if (vidstd == VDEC_STD_JPEG) { + for (i = 0; i < (sizeof(amsvdx_jpeg_row_stride) / + sizeof(amsvdx_jpeg_row_stride[0])); i++) { + if (amsvdx_jpeg_row_stride[i] == row_stride) + return i; + } + } else { + for (i = 0; i < (sizeof(amsvdx_64byte_row_stride) / + sizeof(amsvdx_64byte_row_stride[0])); i++) { + if (amsvdx_64byte_row_stride[i] == row_stride) + return i; + } + } + + return -1; +} + +/* Obtains the hardware defined video profile. */ +static unsigned int vxd_getprofile(enum vdec_vid_std vidstd, unsigned int std_profile) +{ + unsigned int profile = 0; + + switch (vidstd) { + case VDEC_STD_H264: + switch (std_profile) { + case H264_PROFILE_BASELINE: + profile = 0; + break; + + /* + * Extended may be attempted as Baseline or + * Main depending on the constraint_set_flags + */ + case H264_PROFILE_EXTENDED: + case H264_PROFILE_MAIN: + profile = 1; + break; + + case H264_PROFILE_HIGH: + case H264_PROFILE_HIGH444: + case H264_PROFILE_HIGH422: + case H264_PROFILE_HIGH10: + case H264_PROFILE_CAVLC444: + case H264_PROFILE_MVC_HIGH: + case H264_PROFILE_MVC_STEREO: + profile = 2; + break; + default: + profile = 2; + break; + } + break; + + default: + profile = 0; + break; + } + + return profile; +} + +static int vxd_getcoreproperties(struct vxd_coreprops *coreprops, + unsigned int corerev, + unsigned int pvdec_coreid, unsigned int mmu_config0, + unsigned int mmu_config1, unsigned int *pixel_pipecfg, + unsigned int *pixel_misccfg, unsigned int max_framecfg) +{ + unsigned int group_id; + unsigned int core_id; + unsigned int core_config; + unsigned int extended_address_range; + unsigned char group_size = 0; + unsigned char pipe_minus1 = 0; + unsigned int max_h264_hw_chromaformat = 0; + unsigned int max_hevc_hw_chromaformat = 0; + unsigned int max_bitdepth_luma = 0; + unsigned int i; + + struct pvdec_core_rev core_rev; + + if (!coreprops || !pixel_pipecfg || !pixel_misccfg) + return IMG_ERROR_INVALID_PARAMETERS; + + /* PVDEC Core Revision Information */ + core_rev.maj_rev = REGIO_READ_FIELD(corerev, PVDEC_CORE, CR_PVDEC_CORE_REV, + CR_PVDEC_MAJOR_REV); + core_rev.min_rev = REGIO_READ_FIELD(corerev, PVDEC_CORE, CR_PVDEC_CORE_REV, + CR_PVDEC_MINOR_REV); + core_rev.maint_rev = REGIO_READ_FIELD(corerev, PVDEC_CORE, CR_PVDEC_CORE_REV, + CR_PVDEC_MAINT_REV); + + /* core id */ + group_id = REGIO_READ_FIELD(pvdec_coreid, PVDEC_CORE, CR_PVDEC_CORE_ID, CR_GROUP_ID); + core_id = REGIO_READ_FIELD(pvdec_coreid, PVDEC_CORE, CR_PVDEC_CORE_ID, CR_CORE_ID); + + /* Ensure that the core is IMG Video Decoder (PVDEC). */ + if (group_id != 3 || core_id != 3) + return IMG_ERROR_DEVICE_NOT_FOUND; + + core_config = REGIO_READ_FIELD(pvdec_coreid, PVDEC_CORE, + CR_PVDEC_CORE_ID, CR_PVDEC_CORE_CONFIG); + + memset(coreprops, 0, sizeof(*(coreprops))); + + /* Construct core version name. */ + snprintf(coreprops->aversion, VER_STR_LEN, "%d.%d.%d", + core_rev.maj_rev, core_rev.min_rev, core_rev.maint_rev); + + coreprops->mmu_support_stride_per_context = + REGIO_READ_FIELD(mmu_config1, IMG_VIDEO_BUS4_MMU, + MMU_CONFIG1, + SUPPORT_STRIDE_PER_CONTEXT) == 1 ? 1 : 0; + + coreprops->mmu_support_secure = REGIO_READ_FIELD(mmu_config1, IMG_VIDEO_BUS4_MMU, + MMU_CONFIG1, SUPPORT_SECURE) == 1 ? 1 : 0; + + extended_address_range = REGIO_READ_FIELD(mmu_config0, IMG_VIDEO_BUS4_MMU, + MMU_CONFIG0, EXTENDED_ADDR_RANGE); + + switch (extended_address_range) { + case 0: + coreprops->mmu_type = MMU_TYPE_32BIT; + break; + case 4: + coreprops->mmu_type = MMU_TYPE_36BIT; + break; + case 8: + coreprops->mmu_type = MMU_TYPE_40BIT; + break; + default: + return IMG_ERROR_NOT_SUPPORTED; + } + + group_size += REGIO_READ_FIELD(mmu_config0, IMG_VIDEO_BUS4_MMU, + MMU_CONFIG0, GROUP_OVERRIDE_SIZE); + + coreprops->num_entropy_pipes = core_config & 0xF; + coreprops->num_pixel_pipes = core_config >> 4 & 0xF; +#ifdef DEBUG_DECODER_DRIVER + pr_info("PVDEC revision %08x detected, id %08x.\n", corerev, core_id); + pr_info("Found %d entropy pipe(s), %d pixel pipe(s), %d group size", + coreprops->num_entropy_pipes, coreprops->num_pixel_pipes, + group_size); +#endif + + /* Set global rev info variables used by macros */ + maj_rev = core_rev.maj_rev; + min_rev = core_rev.min_rev; + maint_rev = core_rev.maint_rev; + + /* Default settings */ + for (i = 0; i < ARRAY_SIZE(astd_props); i++) { + struct vxd_vidstd_props *pvidstd_props = + &coreprops->vidstd_props[astd_props[i].vidstd]; + /* + * Update video standard properties if the core is beyond + * specified version and the properties are for newer cores + * than the previous. + */ + if (FROM_REV(MAJOR_REVISION((int)astd_props[i].core_rev), + MINOR_REVISION((int)astd_props[i].core_rev), + MAINT_REVISION((int)astd_props[i].core_rev), int) && + astd_props[i].core_rev >= pvidstd_props->core_rev) { + *pvidstd_props = astd_props[i]; + + if (pvidstd_props->vidstd != VDEC_STD_JPEG && + (FROM_REV(8, 0, 0, int)) && (pvidstd_props->vidstd == + VDEC_STD_HEVC ? 1 : 0)) { + /* + * override default values with values + * specified in HW (register does not + * exist in previous cores) + */ + pvidstd_props->max_width = + 2 << REGIO_READ_FIELD(max_framecfg, + PVDEC_PIXEL, + CR_MAX_FRAME_CONFIG, + CR_PVDEC_HOR_MSB); + + pvidstd_props->max_height = + 2 << REGIO_READ_FIELD(max_framecfg, + PVDEC_PIXEL, + CR_MAX_FRAME_CONFIG, + CR_PVDEC_VER_MSB); + } else if (pvidstd_props->vidstd != VDEC_STD_JPEG && + (FROM_REV(8, 0, 0, int))) { + pvidstd_props->max_width = + 2 << REGIO_READ_FIELD(max_framecfg, + PVDEC_PIXEL, + CR_MAX_FRAME_CONFIG, + CR_MSVDX_HOR_MSB); + + pvidstd_props->max_height = + 2 << REGIO_READ_FIELD(max_framecfg, + PVDEC_PIXEL, + CR_MAX_FRAME_CONFIG, + CR_MSVDX_VER_MSB); + } + } + } + + /* Populate the core properties. */ + if (GET_BITS(core_config, 11, 1)) + coreprops->hd_support = 1; + + for (pipe_minus1 = 0; pipe_minus1 < coreprops->num_pixel_pipes; + pipe_minus1++) { + unsigned int current_bitdepth = + GET_BITS(pixel_misccfg[pipe_minus1], 4, 3) + 8; + unsigned int current_h264_hw_chromaformat = + GET_BITS(pixel_misccfg[pipe_minus1], 0, 2); + unsigned int current_hevc_hw_chromaformat = + GET_BITS(pixel_misccfg[pipe_minus1], 2, 2); +#ifdef DEBUG_DECODER_DRIVER + pr_info("cur_bitdepth: %d cur_h264_hw_chromaformat: %d", + current_bitdepth, current_h264_hw_chromaformat); + pr_info("cur_hevc_hw_chromaformat: %d pipe_minus1: %d\n", + current_hevc_hw_chromaformat, pipe_minus1); +#endif + + if (GET_BITS(pixel_misccfg[pipe_minus1], 8, 1)) + coreprops->rotation_support[pipe_minus1] = 1; + + if (GET_BITS(pixel_misccfg[pipe_minus1], 9, 1)) + coreprops->scaling_support[pipe_minus1] = 1; + + coreprops->num_streams[pipe_minus1] = + GET_BITS(pixel_misccfg[pipe_minus1], 12, 2) + 1; + + /* Video standards. */ + coreprops->mpeg2[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 0, 1) ? 1 : 0; + coreprops->mpeg4[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 1, 1) ? 1 : 0; + coreprops->h264[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 2, 1) ? 1 : 0; + coreprops->vc1[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 3, 1) ? 1 : 0; + coreprops->jpeg[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 5, 1) ? 1 : 0; + coreprops->avs[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 7, 1) ? 1 : 0; + coreprops->real[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 8, 1) ? 1 : 0; + coreprops->vp6[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 9, 1) ? 1 : 0; + coreprops->vp8[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 10, 1) ? 1 : 0; + coreprops->hevc[pipe_minus1] = + GET_BITS(pixel_pipecfg[pipe_minus1], 22, 1) ? 1 : 0; + + max_bitdepth_luma = (max_bitdepth_luma > current_bitdepth ? + max_bitdepth_luma : current_bitdepth); + max_h264_hw_chromaformat = (max_h264_hw_chromaformat > + current_h264_hw_chromaformat ? max_h264_hw_chromaformat + : current_h264_hw_chromaformat); + max_hevc_hw_chromaformat = (max_hevc_hw_chromaformat > + current_hevc_hw_chromaformat ? max_hevc_hw_chromaformat + : current_hevc_hw_chromaformat); + } + + /* Override default bit-depth with value signalled explicitly by core. */ + coreprops->vidstd_props[0].max_luma_bitdepth = max_bitdepth_luma; + coreprops->vidstd_props[0].max_chroma_bitdepth = + coreprops->vidstd_props[0].max_luma_bitdepth; + + for (i = 1; i < VDEC_STD_MAX; i++) { + coreprops->vidstd_props[i].max_luma_bitdepth = + coreprops->vidstd_props[0].max_luma_bitdepth; + coreprops->vidstd_props[i].max_chroma_bitdepth = + coreprops->vidstd_props[0].max_chroma_bitdepth; + } + + switch (max_h264_hw_chromaformat) { + case 1: + coreprops->vidstd_props[VDEC_STD_H264].max_chroma_format = + PIXEL_FORMAT_420; + break; + + case 2: + coreprops->vidstd_props[VDEC_STD_H264].max_chroma_format = + PIXEL_FORMAT_422; + break; + + case 3: + coreprops->vidstd_props[VDEC_STD_H264].max_chroma_format = + PIXEL_FORMAT_444; + break; + + default: + break; + } + + switch (max_hevc_hw_chromaformat) { + case 1: + coreprops->vidstd_props[VDEC_STD_HEVC].max_chroma_format = + PIXEL_FORMAT_420; + break; + + case 2: + coreprops->vidstd_props[VDEC_STD_HEVC].max_chroma_format = + PIXEL_FORMAT_422; + break; + + case 3: + coreprops->vidstd_props[VDEC_STD_HEVC].max_chroma_format = + PIXEL_FORMAT_444; + break; + + default: + break; + } + + return 0; +} + +static unsigned char vxd_is_supported_byatleast_onepipe(const unsigned char *features, + unsigned int num_pipes) +{ + unsigned int i; + + VDEC_ASSERT(features); + VDEC_ASSERT(num_pipes <= VDEC_MAX_PIXEL_PIPES); + + for (i = 0; i < num_pipes; i++) { + if (features[i]) + return 1; + } + + return 0; +} + +void vxd_set_reconpictcmds(const struct vdecdd_str_unit *str_unit, + const struct vdec_str_configdata *str_configdata, + const struct vdec_str_opconfig *output_config, + const struct vxd_coreprops *coreprops, + const struct vxd_buffers *buffers, + unsigned int *pict_cmds) +{ + struct pixel_pixinfo *pixel_info; + unsigned int row_stride_code; + unsigned char benable_auxline_buf = 1; + + unsigned int coded_height; + unsigned int coded_width; + unsigned int disp_height; + unsigned int disp_width; + unsigned int profile; + unsigned char plane; + unsigned int y_stride; + unsigned int uv_stride; + unsigned int v_stride; + unsigned int cache_ref_offset; + unsigned int cache_row_offset; + + if (str_configdata->vid_std == VDEC_STD_JPEG) { + disp_height = 0; + disp_width = 0; + coded_height = 0; + coded_width = 0; + } else { + coded_height = ALIGN(str_unit->pict_hdr_info->coded_frame_size.height, + (str_unit->pict_hdr_info->field) ? + 2 * VDEC_MB_DIMENSION : VDEC_MB_DIMENSION); + /* Hardware field is coded size - 1 */ + coded_height -= 1; + + coded_width = ALIGN(str_unit->pict_hdr_info->coded_frame_size.width, + VDEC_MB_DIMENSION); + /* Hardware field is coded size - 1 */ + coded_width -= 1; + + disp_height = str_unit->pict_hdr_info->disp_info.enc_disp_region.height + + str_unit->pict_hdr_info->disp_info.enc_disp_region.left_offset - 1; + disp_width = str_unit->pict_hdr_info->disp_info.enc_disp_region.width + + str_unit->pict_hdr_info->disp_info.enc_disp_region.top_offset - 1; + } + /* + * Display picture size (DISPLAY_PICTURE) + * The display to be written is not the actual video size to be + * displayed but a number that has to differ from the coded pixel size + * by less than 1MB (coded_size-display_size <= 0x0F). Because H264 can + * have a different display size, we need to check and write + * the coded_size again in the display_size register if this condition + * is not fulfilled. + */ + if (str_configdata->vid_std != VDEC_STD_VC1 && ((coded_height - disp_height) > 0x0F)) { + REGIO_WRITE_FIELD_LITE(pict_cmds[VDECFW_CMD_DISPLAY_PICTURE], + MSVDX_CMDS, DISPLAY_PICTURE_SIZE, + DISPLAY_PICTURE_HEIGHT, + coded_height, unsigned int); + } else { + REGIO_WRITE_FIELD_LITE(pict_cmds[VDECFW_CMD_DISPLAY_PICTURE], + MSVDX_CMDS, DISPLAY_PICTURE_SIZE, + DISPLAY_PICTURE_HEIGHT, + disp_height, unsigned int); + } + + if (((coded_width - disp_width) > 0x0F)) { + REGIO_WRITE_FIELD_LITE(pict_cmds[VDECFW_CMD_DISPLAY_PICTURE], + MSVDX_CMDS, DISPLAY_PICTURE_SIZE, + DISPLAY_PICTURE_WIDTH, + coded_width, unsigned int); + } else { + REGIO_WRITE_FIELD_LITE(pict_cmds[VDECFW_CMD_DISPLAY_PICTURE], + MSVDX_CMDS, DISPLAY_PICTURE_SIZE, + DISPLAY_PICTURE_WIDTH, + disp_width, unsigned int); + } + + REGIO_WRITE_FIELD_LITE(pict_cmds[VDECFW_CMD_CODED_PICTURE], + MSVDX_CMDS, CODED_PICTURE_SIZE, + CODED_PICTURE_HEIGHT, + coded_height, unsigned int); + REGIO_WRITE_FIELD_LITE(pict_cmds[VDECFW_CMD_CODED_PICTURE], + MSVDX_CMDS, CODED_PICTURE_SIZE, + CODED_PICTURE_WIDTH, + coded_width, unsigned int); + + /* + * For standards where dpb_diff != 1 and chroma format != 420 + * cache_ref_offset has to be calculated in the F/W. + */ + if (str_configdata->vid_std != VDEC_STD_HEVC && str_configdata->vid_std != VDEC_STD_H264) { + unsigned int log2_size, cache_size, luma_size; + unsigned char is_hevc_supported, is_hevc444_supported = 0; + + is_hevc_supported = + vxd_is_supported_byatleast_onepipe(coreprops->hevc, + coreprops->num_pixel_pipes); + + if (is_hevc_supported) { + is_hevc444_supported = + coreprops->vidstd_props[VDEC_STD_HEVC].max_chroma_format == + PIXEL_FORMAT_444 ? 1 : 0; + } + + log2_size = 9 + (is_hevc_supported ? 1 : 0) + (is_hevc444_supported ? 1 : 0); + cache_size = 3 << log2_size; + luma_size = (cache_size * 2) / 3; + cache_ref_offset = (luma_size * 15) / 32; + cache_ref_offset = (cache_ref_offset + 7) & (~7); + cache_row_offset = 0x0C; + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_MC_CACHE_CONFIGURATION], + MSVDX_CMDS, MC_CACHE_CONFIGURATION, + CONFIG_REF_CHROMA_ADJUST, 1, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_MC_CACHE_CONFIGURATION], + MSVDX_CMDS, MC_CACHE_CONFIGURATION, + CONFIG_REF_OFFSET, cache_ref_offset, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_MC_CACHE_CONFIGURATION], + MSVDX_CMDS, MC_CACHE_CONFIGURATION, + CONFIG_ROW_OFFSET, cache_row_offset, + unsigned int, unsigned int); + } + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, CODEC_MODE, + amsvdx_codecmode[str_configdata->vid_std], + unsigned int, unsigned int); + + profile = str_unit->seq_hdr_info->com_sequ_hdr_info.codec_profile; + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, CODEC_PROFILE, + vxd_getprofile(str_configdata->vid_std, profile), + unsigned int, unsigned int); + + plane = str_unit->seq_hdr_info->com_sequ_hdr_info.separate_chroma_planes; + pixel_info = &str_unit->seq_hdr_info->com_sequ_hdr_info.pixel_info; + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, CHROMA_FORMAT, plane ? + 0 : pixel_info->chroma_fmt, unsigned int, int); + + if (str_configdata->vid_std != VDEC_STD_JPEG) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_EXT_OP_MODE], + MSVDX_CMDS, EXT_OP_MODE, CHROMA_FORMAT_IDC, plane ? + 0 : pixel_get_hw_chroma_format_idc + (pixel_info->chroma_fmt_idc), + unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_EXT_OP_MODE], + MSVDX_CMDS, EXT_OP_MODE, MEMORY_PACKING, + output_config->pixel_info.mem_pkg == + PIXEL_BIT10_MP ? 1 : 0, unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_EXT_OP_MODE], + MSVDX_CMDS, EXT_OP_MODE, BIT_DEPTH_LUMA_MINUS8, + pixel_info->bitdepth_y - 8, + unsigned int, unsigned int); + + if (pixel_info->chroma_fmt_idc == PIXEL_FORMAT_MONO) { + /* + * For monochrome streams use the same bit depth for + * chroma and luma. + */ + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_EXT_OP_MODE], + MSVDX_CMDS, EXT_OP_MODE, + BIT_DEPTH_CHROMA_MINUS8, + pixel_info->bitdepth_y - 8, + unsigned int, unsigned int); + } else { + /* + * For normal streams use the appropriate bit depth for chroma. + */ + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_EXT_OP_MODE], MSVDX_CMDS, + EXT_OP_MODE, BIT_DEPTH_CHROMA_MINUS8, + pixel_info->bitdepth_c - 8, + unsigned int, unsigned int); + } + } else { + pict_cmds[VDECFW_CMD_EXT_OP_MODE] = 0; + } + + if (str_configdata->vid_std != VDEC_STD_JPEG) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], MSVDX_CMDS, + OPERATING_MODE, CHROMA_INTERLEAVED, + PIXEL_GET_HW_CHROMA_INTERLEAVED + (output_config->pixel_info.chroma_interleave), + unsigned int, int); + } + + if (str_configdata->vid_std == VDEC_STD_JPEG) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, ASYNC_MODE, + VDEC_MSVDX_ASYNC_VDMC, + unsigned int, unsigned int); + } + + if (str_configdata->vid_std == VDEC_STD_H264) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], MSVDX_CMDS, + OPERATING_MODE, ASYNC_MODE, + str_unit->pict_hdr_info->discontinuous_mbs ? + VDEC_MSVDX_ASYNC_VDMC : VDEC_MSVDX_ASYNC_NORMAL, + unsigned int, int); + } + + y_stride = buffers->recon_pict->rend_info.plane_info[VDEC_PLANE_VIDEO_Y].stride; + uv_stride = buffers->recon_pict->rend_info.plane_info[VDEC_PLANE_VIDEO_UV].stride; + v_stride = buffers->recon_pict->rend_info.plane_info[VDEC_PLANE_VIDEO_V].stride; + + if (((y_stride % (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT)) == 0) && + ((uv_stride % (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT)) == 0) && + ((v_stride % (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT)) == 0)) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, + USE_EXT_ROW_STRIDE, 1, unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_EXTENDED_ROW_STRIDE], + MSVDX_CMDS, EXTENDED_ROW_STRIDE, + EXT_ROW_STRIDE, y_stride >> 6, unsigned int, unsigned int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_CHROMA_ROW_STRIDE], + MSVDX_CMDS, CHROMA_ROW_STRIDE, + CHROMA_ROW_STRIDE, uv_stride >> 6, unsigned int, unsigned int); + } else { + row_stride_code = get_stride_code(str_configdata->vid_std, y_stride); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, ROW_STRIDE, + row_stride_code & 0x7, unsigned int, unsigned int); + + if (str_configdata->vid_std == VDEC_STD_JPEG) { + /* + * Use the unused chroma interleaved flag + * to hold MSB of row stride code + */ + IMG_ASSERT(row_stride_code < 16); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_OPERATING_MODE], + MSVDX_CMDS, OPERATING_MODE, + CHROMA_INTERLEAVED, + row_stride_code >> 3, unsigned int, unsigned int); + } else { + IMG_ASSERT(row_stride_code < 8); + } + } + pict_cmds[VDECFW_CMD_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(&buffers->recon_pict->pict_buf->ddbuf_info) + + buffers->recon_pict->rend_info.plane_info[0].offset; + + pict_cmds[VDECFW_CMD_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(&buffers->recon_pict->pict_buf->ddbuf_info) + + buffers->recon_pict->rend_info.plane_info[1].offset; + + pict_cmds[VDECFW_CMD_CHROMA2_RECONSTRUCTED_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(&buffers->recon_pict->pict_buf->ddbuf_info) + + buffers->recon_pict->rend_info.plane_info[2].offset; + + pict_cmds[VDECFW_CMD_LUMA_ERROR_PICTURE_BASE_ADDRESS] = 0; + pict_cmds[VDECFW_CMD_CHROMA_ERROR_PICTURE_BASE_ADDRESS] = 0; + +#ifdef ERROR_CONCEALMENT + /* update error concealment frame info if available */ + if (buffers->err_pict_bufinfo) { + pict_cmds[VDECFW_CMD_LUMA_ERROR_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(buffers->err_pict_bufinfo) + + buffers->recon_pict->rend_info.plane_info[0].offset; + + pict_cmds[VDECFW_CMD_CHROMA_ERROR_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(buffers->err_pict_bufinfo) + + buffers->recon_pict->rend_info.plane_info[1].offset; + } +#endif + + pict_cmds[VDECFW_CMD_INTRA_BUFFER_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(buffers->intra_bufinfo); + pict_cmds[VDECFW_CMD_INTRA_BUFFER_PLANE_SIZE] = + buffers->intra_bufsize_per_pipe / 3; + pict_cmds[VDECFW_CMD_INTRA_BUFFER_SIZE_PER_PIPE] = + buffers->intra_bufsize_per_pipe; + pict_cmds[VDECFW_CMD_AUX_LINE_BUFFER_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(buffers->auxline_bufinfo); + pict_cmds[VDECFW_CMD_AUX_LINE_BUFFER_SIZE_PER_PIPE] = + buffers->auxline_bufsize_per_pipe; + + /* + * for pvdec we need to set this registers even if we don't + * use alternative output + */ + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_BIT_DEPTH_CHROMA_MINUS8, + output_config->pixel_info.bitdepth_c - 8, unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_BIT_DEPTH_LUMA_MINUS8, + output_config->pixel_info.bitdepth_y - 8, unsigned int, unsigned int); + + /* + * this is causing corruption in RV40 and VC1 streams with + * scaling/rotation enabled on Coral, so setting to 0 + */ + benable_auxline_buf = benable_auxline_buf && + (str_configdata->vid_std != VDEC_STD_REAL) && + (str_configdata->vid_std != VDEC_STD_VC1); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + USE_AUX_LINE_BUF, benable_auxline_buf ? 1 : 0, unsigned int, int); +} + +void vxd_set_altpictcmds(const struct vdecdd_str_unit *str_unit, + const struct vdec_str_configdata *str_configdata, + const struct vdec_str_opconfig *output_config, + const struct vxd_coreprops *coreprops, + const struct vxd_buffers *buffers, + unsigned int *pict_cmds) +{ + unsigned int row_stride_code; + unsigned int y_stride; + unsigned int uv_stride; + unsigned int v_stride; + + y_stride = buffers->alt_pict->rend_info.plane_info[VDEC_PLANE_VIDEO_Y].stride; + uv_stride = buffers->alt_pict->rend_info.plane_info[VDEC_PLANE_VIDEO_UV].stride; + v_stride = buffers->alt_pict->rend_info.plane_info[VDEC_PLANE_VIDEO_V].stride; + + if (((y_stride % (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT)) == 0) && + ((uv_stride % (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT)) == 0) && + ((v_stride % (VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT)) == 0)) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + USE_EXT_ROT_ROW_STRIDE, 1, unsigned int, int); + + /* 64-byte (min) aligned luma stride value. */ + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION], + MSVDX_CMDS, + ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + EXT_ROT_ROW_STRIDE, y_stride >> 6, + unsigned int, unsigned int); + + /* 64-byte (min) aligned chroma stride value. */ + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_CHROMA_ROW_STRIDE], + MSVDX_CMDS, CHROMA_ROW_STRIDE, + ALT_CHROMA_ROW_STRIDE, uv_stride >> 6, + unsigned int, unsigned int); + } else { + /* + * Obtain the code for buffer stride + * (must be less than 8, i.e. not JPEG strides) + */ + row_stride_code = + get_stride_code(str_configdata->vid_std, y_stride); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION], + MSVDX_CMDS, + ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + ROTATION_ROW_STRIDE, row_stride_code & 0x7, + unsigned int, unsigned int); + } + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + SCALE_INPUT_SIZE_SEL, + ((output_config->pixel_info.chroma_fmt_idc != + str_unit->seq_hdr_info->com_sequ_hdr_info.pixel_info.chroma_fmt_idc)) ? + 1 : 0, unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_PICTURE_ROTATION], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, + PACKED_422_OUTPUT, + (output_config->pixel_info.chroma_fmt_idc == + PIXEL_FORMAT_422 && + output_config->pixel_info.num_planes == 1) ? 1 : 0, + unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_OUTPUT_FORMAT, + str_unit->seq_hdr_info->com_sequ_hdr_info.separate_chroma_planes ? + 0 : pixel_get_hw_chroma_format_idc + (output_config->pixel_info.chroma_fmt_idc), + unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_BIT_DEPTH_CHROMA_MINUS8, + output_config->pixel_info.bitdepth_c - 8, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_BIT_DEPTH_LUMA_MINUS8, + output_config->pixel_info.bitdepth_y - 8, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_MEMORY_PACKING, + (output_config->pixel_info.mem_pkg == + PIXEL_BIT10_MP) ? 1 : 0, unsigned int, int); + + pict_cmds[VDECFW_CMD_LUMA_ALTERNATIVE_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(&buffers->alt_pict->pict_buf->ddbuf_info) + + buffers->alt_pict->rend_info.plane_info[0].offset; + + pict_cmds[VDECFW_CMD_CHROMA_ALTERNATIVE_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(&buffers->alt_pict->pict_buf->ddbuf_info) + + buffers->alt_pict->rend_info.plane_info[1].offset; + + pict_cmds[VDECFW_CMD_CHROMA2_ALTERNATIVE_PICTURE_BASE_ADDRESS] = + (unsigned int)GET_HOST_ADDR(&buffers->alt_pict->pict_buf->ddbuf_info) + + buffers->alt_pict->rend_info.plane_info[2].offset; +} + +int vxd_getscalercmds(const struct scaler_config *scaler_config, + const struct scaler_pitch *pitch, + const struct scaler_filter *filter, + const struct pixel_pixinfo *out_loop_pixel_info, + struct scaler_params *params, + unsigned int *pict_cmds) +{ + const struct vxd_coreprops *coreprops = scaler_config->coreprops; + /* + * Indirectly detect decoder core type (if HEVC is supported, it has + * to be PVDEC core) and decide if to force luma re-sampling. + */ + unsigned char bforce_luma_resampling = coreprops->hevc[0]; + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_OUTPUT_FORMAT, + scaler_config->bseparate_chroma_planes ? 0 : + pixel_get_hw_chroma_format_idc(out_loop_pixel_info->chroma_fmt_idc), + unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + SCALE_CHROMA_RESAMP_ONLY, bforce_luma_resampling ? 0 : + (pitch->horiz_luma == FIXED(1, HIGHP)) && + (pitch->vert_luma == FIXED(1, HIGHP)), unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, ALT_MEMORY_PACKING, + pixel_get_hw_memory_packing(out_loop_pixel_info->mem_pkg), + unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_BIT_DEPTH_LUMA_MINUS8, + out_loop_pixel_info->bitdepth_y - 8, + unsigned int, unsigned int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + ALT_BIT_DEPTH_CHROMA_MINUS8, + out_loop_pixel_info->bitdepth_c - 8, + unsigned int, unsigned int); + + /* Scale luma bifilter is always 0 for now */ + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + SCALE_LUMA_BIFILTER_HORIZ, + 0, unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + SCALE_LUMA_BIFILTER_VERT, + 0, unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + SCALE_CHROMA_BIFILTER_HORIZ, + filter->bhoriz_bilinear ? 1 : 0, + unsigned int, int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_ALTERNATIVE_OUTPUT_CONTROL], + MSVDX_CMDS, ALTERNATIVE_OUTPUT_CONTROL, + SCALE_CHROMA_BIFILTER_VERT, + filter->bvert_bilinear ? 1 : 0, unsigned int, int); + + /* for cores 7.x.x and more, precision 3.13 */ + params->fixed_point_shift = 13; + + /* Calculate the fixed-point versions for use by the hardware. */ + params->vert_pitch = (int)((pitch->vert_luma + + (1 << (HIGHP - params->fixed_point_shift - 1))) >> + (HIGHP - params->fixed_point_shift)); + params->vert_startpos = params->vert_pitch >> 1; + params->vert_pitch_chroma = (int)((pitch->vert_chroma + + (1 << (HIGHP - params->fixed_point_shift - 1))) >> + (HIGHP - params->fixed_point_shift)); + params->vert_startpos_chroma = params->vert_pitch_chroma >> 1; + params->horz_pitch = (int)(pitch->horiz_luma >> + (HIGHP - params->fixed_point_shift)); + params->horz_startpos = params->horz_pitch >> 1; + params->horz_pitch_chroma = (int)(pitch->horiz_chroma >> + (HIGHP - params->fixed_point_shift)); + params->horz_startpos_chroma = params->horz_pitch_chroma >> 1; + +#ifdef HAS_HEVC + if (scaler_config->vidstd == VDEC_STD_HEVC) { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE], + MSVDX_CMDS, PVDEC_SCALED_DISPLAY_SIZE, + PVDEC_SCALE_DISPLAY_WIDTH, + scaler_config->recon_width - 1, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE], + MSVDX_CMDS, PVDEC_SCALED_DISPLAY_SIZE, + PVDEC_SCALE_DISPLAY_HEIGHT, + scaler_config->recon_height - 1, + unsigned int, unsigned int); + } else { + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE], + MSVDX_CMDS, SCALED_DISPLAY_SIZE, + SCALE_DISPLAY_WIDTH, + scaler_config->recon_width - 1, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE], + MSVDX_CMDS, SCALED_DISPLAY_SIZE, + SCALE_DISPLAY_HEIGHT, + scaler_config->recon_height - 1, + unsigned int, unsigned int); + } +#else + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE], + MSVDX_CMDS, SCALED_DISPLAY_SIZE, + SCALE_DISPLAY_WIDTH, + scaler_config->recon_width - 1, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALED_DISPLAY_SIZE], + MSVDX_CMDS, SCALED_DISPLAY_SIZE, SCALE_DISPLAY_HEIGHT, + scaler_config->recon_height - 1, + unsigned int, unsigned int); +#endif + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALE_OUTPUT_SIZE], + MSVDX_CMDS, SCALE_OUTPUT_SIZE, + SCALE_OUTPUT_WIDTH_MIN1, + scaler_config->scale_width - 1, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALE_OUTPUT_SIZE], + MSVDX_CMDS, SCALE_OUTPUT_SIZE, + SCALE_OUTPUT_HEIGHT_MIN1, + scaler_config->scale_height - 1, + unsigned int, unsigned int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_HORIZONTAL_SCALE_CONTROL], + MSVDX_CMDS, HORIZONTAL_SCALE_CONTROL, + HORIZONTAL_SCALE_PITCH, params->horz_pitch, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_HORIZONTAL_SCALE_CONTROL], + MSVDX_CMDS, HORIZONTAL_SCALE_CONTROL, + HORIZONTAL_INITIAL_POS, params->horz_startpos, + unsigned int, unsigned int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALE_HORIZONTAL_CHROMA], + MSVDX_CMDS, SCALE_HORIZONTAL_CHROMA, + CHROMA_HORIZONTAL_PITCH, params->horz_pitch_chroma, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALE_HORIZONTAL_CHROMA], + MSVDX_CMDS, SCALE_HORIZONTAL_CHROMA, + CHROMA_HORIZONTAL_INITIAL, + params->horz_startpos_chroma, + unsigned int, unsigned int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_VERTICAL_SCALE_CONTROL], + MSVDX_CMDS, VERTICAL_SCALE_CONTROL, + VERTICAL_SCALE_PITCH, params->vert_pitch, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_VERTICAL_SCALE_CONTROL], + MSVDX_CMDS, VERTICAL_SCALE_CONTROL, + VERTICAL_INITIAL_POS, params->vert_startpos, + unsigned int, unsigned int); + + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALE_VERTICAL_CHROMA], + MSVDX_CMDS, SCALE_VERTICAL_CHROMA, + CHROMA_VERTICAL_PITCH, params->vert_pitch_chroma, + unsigned int, unsigned int); + REGIO_WRITE_FIELD(pict_cmds[VDECFW_CMD_SCALE_VERTICAL_CHROMA], + MSVDX_CMDS, SCALE_VERTICAL_CHROMA, + CHROMA_VERTICAL_INITIAL, + params->vert_startpos_chroma, + unsigned int, unsigned int); + return 0; +} + +unsigned int vxd_get_codedpicsize(unsigned short width_min1, unsigned short height_min1) +{ + unsigned int reg = 0; + + REGIO_WRITE_FIELD_LITE(reg, MSVDX_CMDS, CODED_PICTURE_SIZE, + CODED_PICTURE_WIDTH, width_min1, + unsigned short); + REGIO_WRITE_FIELD_LITE(reg, MSVDX_CMDS, CODED_PICTURE_SIZE, + CODED_PICTURE_HEIGHT, height_min1, + unsigned short); + + return reg; +} + +unsigned char vxd_get_codedmode(enum vdec_vid_std vidstd) +{ + return (unsigned char)amsvdx_codecmode[vidstd]; +} + +void vxd_get_coreproperties(void *hndl_coreproperties, + struct vxd_coreprops *vxd_coreprops) +{ + struct vxd_core_props *props = + (struct vxd_core_props *)hndl_coreproperties; + + vxd_getcoreproperties(vxd_coreprops, props->core_rev, + props->pvdec_core_id, + props->mmu_config0, + props->mmu_config1, + props->pixel_pipe_cfg, + props->pixel_misc_cfg, + props->pixel_max_frame_cfg); +} + +int vxd_get_pictattrs(unsigned int flags, struct vxd_pict_attrs *pict_attrs) +{ + if (flags & (VXD_FW_MSG_FLAG_DWR | VXD_FW_MSG_FLAG_FATAL)) + pict_attrs->dwrfired = 1; + if (flags & VXD_FW_MSG_FLAG_MMU_FAULT) + pict_attrs->mmufault = 1; + if (flags & VXD_FW_MSG_FLAG_DEV_ERR) + pict_attrs->deverror = 1; + + return 0; +} + +int vxd_get_msgerrattr(unsigned int flags, enum vxd_msg_attr *msg_attr) +{ + if ((flags & ~VXD_FW_MSG_FLAG_CANCELED)) + *msg_attr = VXD_MSG_ATTR_FATAL; + else if ((flags & VXD_FW_MSG_FLAG_CANCELED)) + *msg_attr = VXD_MSG_ATTR_CANCELED; + else + *msg_attr = VXD_MSG_ATTR_NONE; + + return 0; +} + +int vxd_set_msgflag(enum vxd_msg_flag input_flag, unsigned int *flags) +{ + switch (input_flag) { + case VXD_MSG_FLAG_DROP: + *flags |= VXD_FW_MSG_FLAG_DROP; + break; + case VXD_MSG_FLAG_EXCL: + *flags |= VXD_FW_MSG_FLAG_EXCL; + break; + default: + return IMG_ERROR_FATAL; + } + + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_int.h b/drivers/media/platform/vxe-vxd/decoder/vxd_int.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_int.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_int.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD DEC Common low level core interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ +#ifndef _VXD_INT_H +#define _VXD_INT_H + +#include "fw_interface.h" +#include "scaler_setup.h" +#include "vdecdd_defs.h" +#include "vdecfw_shared.h" +#include "vdec_defs.h" +#include "vxd_ext.h" +#include "vxd_props.h" + +/* + * Size of buffer used for batching messages + */ +#define BATCH_MSG_BUFFER_SIZE (8 * 4096) + +#define INTRA_BUF_SIZE (1024 * 32) +#define AUX_LINE_BUFFER_SIZE (512 * 1024) + +#define MAX_PICTURE_WIDTH (4096) +#define MAX_PICTURE_HEIGHT (4096) + +/* + * this macro returns the host address of device buffer. + */ +#define GET_HOST_ADDR(buf) ((buf)->dev_virt) + +#define GET_HOST_ADDR_OFFSET(buf, offset) (((buf)->dev_virt) + (offset)) + +/* + * The extended stride alignment for VXD. + */ +#define VDEC_VXD_EXT_STRIDE_ALIGNMENT_DEFAULT (64) + +struct vxd_buffers { + struct vdecdd_ddpict_buf *recon_pict; + struct vdecdd_ddpict_buf *alt_pict; + struct vidio_ddbufinfo *intra_bufinfo; + struct vidio_ddbufinfo *auxline_bufinfo; + struct vidio_ddbufinfo *err_pict_bufinfo; + unsigned int intra_bufsize_per_pipe; + unsigned int auxline_bufsize_per_pipe; + struct vidio_ddbufinfo *msb_bufinfo; + unsigned char btwopass; +}; + +struct pvdec_core_rev { + unsigned int maj_rev; + unsigned int min_rev; + unsigned int maint_rev; + unsigned int int_rev; +}; + +/* + * this has all that it needs to translate a Stream Unit for a picture + * into a transaction. + */ +void vxd_set_altpictcmds(const struct vdecdd_str_unit *str_unit, + const struct vdec_str_configdata *str_configdata, + const struct vdec_str_opconfig *output_config, + const struct vxd_coreprops *coreprops, + const struct vxd_buffers *buffers, + unsigned int *pict_cmds); + +/* + * this has all that it needs to translate a Stream Unit for + * a picture into a transaction. + */ +void vxd_set_reconpictcmds(const struct vdecdd_str_unit *str_unit, + const struct vdec_str_configdata *str_configdata, + const struct vdec_str_opconfig *output_config, + const struct vxd_coreprops *coreprops, + const struct vxd_buffers *buffers, + unsigned int *pict_cmds); + +int vxd_getscalercmds(const struct scaler_config *scaler_config, + const struct scaler_pitch *pitch, + const struct scaler_filter *filter, + const struct pixel_pixinfo *out_loop_pixel_info, + struct scaler_params *params, + unsigned int *pict_cmds); + +/* + * this creates value of MSVDX_CMDS_CODED_PICTURE_SIZE register. + */ +unsigned int vxd_get_codedpicsize(unsigned short width_min1, unsigned short height_min1); + +/* + * return HW codec mode based on video standard. + */ +unsigned char vxd_get_codedmode(enum vdec_vid_std vidstd); + +/* + * translates core properties to the form of the struct vxd_coreprops struct. + */ +void vxd_get_coreproperties(void *hndl_coreproperties, + struct vxd_coreprops *vxd_coreprops); + +/* + * translates picture attributes to the form of the VXD_sPictAttrs struct. + */ +int vxd_get_pictattrs(unsigned int flags, struct vxd_pict_attrs *pict_attrs); + +/* + * translates message attributes to the form of the VXD_eMsgAttr struct. + */ +int vxd_get_msgerrattr(unsigned int flags, enum vxd_msg_attr *msg_attr); + +/* + * sets a message flag. + */ +int vxd_set_msgflag(enum vxd_msg_flag input_flag, unsigned int *flags); + +#endif /* _VXD_INT_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_mmu_defs.h b/drivers/media/platform/vxe-vxd/decoder/vxd_mmu_defs.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_mmu_defs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_mmu_defs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * V-DEC MMU Definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + */ + +#ifndef _VXD_MMU_DEF_H_ +#define _VXD_MMU_DEF_H_ + +/* + * This type defines the MMU heaps. + * @0: Heap for untiled video buffers + * @1: Heap for bitstream buffers + * @2: Heap for Stream buffers + * @3: Number of heaps + */ +enum mmu_eheap_id { + MMU_HEAP_IMAGE_BUFFERS_UNTILED = 0x00, + MMU_HEAP_BITSTREAM_BUFFERS, + MMU_HEAP_STREAM_BUFFERS, + MMU_HEAP_MAX, + MMU_HEAP_FORCE32BITS = 0x7FFFFFFFU +}; + +#endif /* _VXD_MMU_DEFS_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_props.h b/drivers/media/platform/vxe-vxd/decoder/vxd_props.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_props.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_props.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low-level VXD interface component + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + */ + +#ifndef _VXD_PROPS_H +#define _VXD_PROPS_H + +#include "vdec_defs.h" +#include "imgmmu.h" + +#define VDEC_MAX_PIXEL_PIPES 2 + +#define VXD_MAX_CORES 1 +#define VER_STR_LEN 64 + +#define CORE_REVISION(maj, min, maint) \ + ((((maj) & 0xff) << 16) | (((min) & 0xff) << 8) | (((maint) & 0xff))) +#define MAJOR_REVISION(rev) (((rev) >> 16) & 0xff) +#define MINOR_REVISION(rev) (((rev) >> 8) & 0xff) +#define MAINT_REVISION(rev) ((rev) & 0xff) + +#define FROM_REV(maj, min, maint, type) \ + ({ \ + type __maj = maj; \ + type __min = min; \ + (((maj_rev) > (__maj)) || \ + (((maj_rev) == (__maj)) && ((min_rev) > (__min))) || \ + (((maj_rev) == (__maj)) && ((min_rev) == (__min)) && \ + ((int)(maint_rev) >= (maint)))); }) + +struct vxd_vidstd_props { + enum vdec_vid_std vidstd; + unsigned int core_rev; + unsigned int min_width; + unsigned int min_height; + unsigned int max_width; + unsigned int max_height; + unsigned int max_macroblocks; + unsigned int max_luma_bitdepth; + unsigned int max_chroma_bitdepth; + enum pixel_fmt_idc max_chroma_format; +}; + +struct vxd_coreprops { + unsigned char aversion[VER_STR_LEN]; + unsigned char mpeg2[VDEC_MAX_PIXEL_PIPES]; + unsigned char mpeg4[VDEC_MAX_PIXEL_PIPES]; + unsigned char h264[VDEC_MAX_PIXEL_PIPES]; + unsigned char vc1[VDEC_MAX_PIXEL_PIPES]; + unsigned char avs[VDEC_MAX_PIXEL_PIPES]; + unsigned char real[VDEC_MAX_PIXEL_PIPES]; + unsigned char jpeg[VDEC_MAX_PIXEL_PIPES]; + unsigned char vp6[VDEC_MAX_PIXEL_PIPES]; + unsigned char vp8[VDEC_MAX_PIXEL_PIPES]; + unsigned char hevc[VDEC_MAX_PIXEL_PIPES]; + unsigned char rotation_support[VDEC_MAX_PIXEL_PIPES]; + unsigned char scaling_support[VDEC_MAX_PIXEL_PIPES]; + unsigned char hd_support; + unsigned int num_streams[VDEC_MAX_PIXEL_PIPES]; + unsigned int num_entropy_pipes; + unsigned int num_pixel_pipes; + struct vxd_vidstd_props vidstd_props[VDEC_STD_MAX]; + enum mmu_etype mmu_type; + unsigned char mmu_support_stride_per_context; + unsigned char mmu_support_secure; + /* Range extensions supported by hw -> used only by hevc */ + unsigned char hevc_range_ext[VDEC_MAX_PIXEL_PIPES]; +}; + +#endif /* _VXD_PROPS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec.c b/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec.c --- a/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1745 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC PVDEC function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "img_dec_common.h" +#include "img_pvdec_test_regs.h" +#include "img_video_bus4_mmu_regs.h" +#include "vxd_pvdec_priv.h" +#include "vxd_pvdec_regs.h" + +#ifdef PVDEC_SINGLETHREADED_IO +static DEFINE_SPINLOCK(pvdec_irq_lock); +static ulong pvdec_irq_flags; +#endif + +static const ulong vxd_plat_poll_udelay = 100; + +/* This function will return reminder and quotient */ +static inline unsigned int do_divide(unsigned long long *n, unsigned int base) +{ + unsigned int remainder = *n % base; + *n = *n / base; + return remainder; +} + +/* + * Reads PROC_DEBUG register and provides number of MTX RAM banks + * and their size + */ +static int pvdec_get_mtx_ram_info(void __iomem *reg_base, int *bank_cnt, + unsigned long *bank_size, + unsigned long *last_bank_size) +{ + unsigned int ram_bank_count, reg; + + reg = VXD_RD_REG(reg_base, PVDEC_CORE, PROC_DEBUG); + ram_bank_count = VXD_RD_REG_FIELD(reg, PVDEC_CORE, PROC_DEBUG, MTX_RAM_BANKS); + if (!ram_bank_count) + return -EIO; + + if (bank_cnt) + *bank_cnt = ram_bank_count; + + if (bank_size) { + unsigned int ram_bank_size = VXD_RD_REG_FIELD(reg, PVDEC_CORE, + PROC_DEBUG, MTX_RAM_BANK_SIZE); + *bank_size = 1 << (ram_bank_size + 2); + } + + if (last_bank_size) { + unsigned int last_bank = VXD_RD_REG_FIELD(reg, PVDEC_CORE, PROC_DEBUG, + MTX_LAST_RAM_BANK_SIZE); + unsigned char new_representation = VXD_RD_REG_FIELD(reg, + PVDEC_CORE, PROC_DEBUG, MTX_RAM_NEW_REPRESENTATION); + if (new_representation) { + *last_bank_size = 1024 * last_bank; + } else { + *last_bank_size = 1 << (last_bank + 2); + if (bank_cnt && last_bank == 13 && *bank_cnt == 4) { + /* + * VXD hardware ambiguity: + * old cores confuse 120k and 128k + * So assume worst case. + */ + *last_bank_size -= 0x2000; + } + } + } + + return 0; +} + +/* Provides size of MTX RAM in bytes */ +static int pvdec_get_mtx_ram_size(void __iomem *reg_base, unsigned int *ram_size) +{ + int bank_cnt, ret; + unsigned long bank_size, last_bank_size; + + ret = pvdec_get_mtx_ram_info(reg_base, &bank_cnt, &bank_size, &last_bank_size); + if (ret) + return ret; + + *ram_size = (bank_cnt - 1) * bank_size + last_bank_size; + + return 0; +} + +/* Poll for single register-based transfer to/from MTX to complete */ +static unsigned int pvdec_wait_mtx_reg_access(void __iomem *reg_base, unsigned int *mtx_fault) +{ + unsigned int pvdec_timeout = PVDEC_TIMEOUT_COUNTER, reg; + + do { + /* Check MTX is OK */ + reg = VXD_RD_REG(reg_base, MTX_CORE, MTX_FAULT0); + if (reg != 0) { + *mtx_fault = reg; + return -EIO; + } + + pvdec_timeout--; + reg = VXD_RD_REG(reg_base, MTX_CORE, MTX_REG_READ_WRITE_REQUEST); + } while ((VXD_RD_REG_FIELD(reg, MTX_CORE, + MTX_REG_READ_WRITE_REQUEST, + MTX_DREADY) == 0) && + (pvdec_timeout != 0)); + + if (pvdec_timeout == 0) + return -EIO; + + return 0; +} + +static void pvdec_mtx_status_dump(void __iomem *reg_base, unsigned int *status) +{ + unsigned int reg; + + pr_debug("%s: *** dumping status ***\n", __func__); + +#define READ_MTX_REG(_NAME_) \ + do { \ + unsigned int val; \ + VXD_WR_REG(reg_base, MTX_CORE, \ + MTX_REG_READ_WRITE_REQUEST, reg); \ + if (pvdec_wait_mtx_reg_access(reg_base, ®)) { \ + pr_debug("%s: " \ + "MTX REG RD fault: 0x%08x\n", __func__, reg); \ + break; \ + } \ + val = VXD_RD_REG(reg_base, MTX_CORE, MTX_REG_READ_WRITE_DATA); \ + if (status) \ + *status++ = val; \ + pr_debug("%s: " _NAME_ ": 0x%08x\n", __func__, val); \ + } while (0) + + reg = 0; + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* Read */ + MTX_REG_READ_WRITE_REQUEST, MTX_RNW, 1); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* PC or PCX */ + MTX_REG_READ_WRITE_REQUEST, MTX_USPECIFIER, 5); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* PC */ + MTX_REG_READ_WRITE_REQUEST, MTX_RSPECIFIER, 0); + READ_MTX_REG("MTX PC"); + + reg = 0; + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* Read */ + MTX_REG_READ_WRITE_REQUEST, MTX_RNW, 1); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* PC or PCX */ + MTX_REG_READ_WRITE_REQUEST, MTX_USPECIFIER, 5); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* PCX */ + MTX_REG_READ_WRITE_REQUEST, MTX_RSPECIFIER, 1); + READ_MTX_REG("MTX PCX"); + + reg = 0; + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* Read */ + MTX_REG_READ_WRITE_REQUEST, MTX_RNW, 1); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* A0StP */ + MTX_REG_READ_WRITE_REQUEST, MTX_USPECIFIER, 3); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, + MTX_REG_READ_WRITE_REQUEST, MTX_RSPECIFIER, 0); + READ_MTX_REG("MTX A0STP"); + + reg = 0; + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* Read */ + MTX_REG_READ_WRITE_REQUEST, MTX_RNW, 1); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, /* A0FrP */ + MTX_REG_READ_WRITE_REQUEST, MTX_USPECIFIER, 3); + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_REG_READ_WRITE_REQUEST, MTX_RSPECIFIER, 1); + READ_MTX_REG("MTX A0FRP"); +#undef PRINT_MTX_REG + + pr_debug("%s: *** status dump done ***\n", __func__); +} + +static void pvdec_prep_fw_upload(const void *dev, + void __iomem *reg_base, + struct vxd_ena_params *ena_params, + unsigned char dma_channel) +{ + unsigned int fw_vxd_virt_addr = ena_params->fw_buf_virt_addr; + unsigned int vxd_ptd_addr = ena_params->ptd; + unsigned int reg = 0; + int i; + unsigned int flags = PVDEC_FWFLAG_FORCE_FS_FLOW | + PVDEC_FWFLAG_DISABLE_GENC_FLUSHING | + PVDEC_FWFLAG_DISABLE_AUTONOMOUS_RESET | + PVDEC_FWFLAG_DISABLE_IDLE_GPIO | + PVDEC_FWFLAG_ENABLE_ERROR_CONCEALMENT; + + if (ena_params->secure) + flags |= PVDEC_FWFLAG_BIG_TO_HOST_BUFFER; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: fw_virt: 0x%x, ptd: 0x%x, dma ch: %u, flags: 0x%x\n", + __func__, fw_vxd_virt_addr, vxd_ptd_addr, dma_channel, flags); +#endif + + /* Reset MTX */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SOFT_RESET, MTX_RESET, 1); + VXD_WR_REG(reg_base, MTX_CORE, MTX_SOFT_RESET, reg); + /* + * NOTE: The MTX reset bit is WRITE ONLY, so we cannot + * check the reset procedure has finished, thus BEWARE to put + * any MTX_CORE* access just after this line + */ + + /* Clear COMMS RAM header */ + for (i = 0; i < PVDEC_FW_COMMS_HDR_SIZE; i++) + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + i * sizeof(unsigned int), 0); + + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_FLAGS_OFFSET, flags); + /* Do not wait for debug FIFO flag - set it only when requested */ + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_SIGNATURE_OFFSET, + !ena_params->wait_dbg_fifo); + + /* + * Clear the bypass bits and enable extended addressing in MMU. + * Firmware depends on this configuration, so we have to set it, + * even if firmware is being uploaded via registers. + */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_ADDRESS_CONTROL, UPPER_ADDR_FIXED, 0); + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_ADDRESS_CONTROL, MMU_ENA_EXT_ADDR, 1); + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_ADDRESS_CONTROL, MMU_BYPASS, 0); + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_ADDRESS_CONTROL, reg); + + /* + * Buffer device virtual address. + * This is an address of a firmware blob, firmware reads this base + * address from DMAC_SETUP register and uses to load the modules, so it + * has to be set even when uploading the FW via registers. + */ + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_SETUP, fw_vxd_virt_addr, dma_channel); + + /* + * Set base address of PTD. Same as before, has to be configured even + * when uploading the firmware via regs, FW uses it to execute DMA + * before switching to stream MMU context. + */ + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_DIR_BASE_ADDR, vxd_ptd_addr); + + /* Configure MMU bank index - Use bank 0 */ + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_BANK_INDEX, 0); + + /* Set the MTX timer divider register */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_TIMERDIV, TIMER_EN, 1); + /* + * Setting max freq - divide by 1 for better measurement accuracy + * during fw upload stage + */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_TIMERDIV, TIMER_DIV, 0); + VXD_WR_REG(reg_base, MTX_CORE, MTX_SYSC_TIMERDIV, reg); +} + +static int pvdec_check_fw_sig(void __iomem *reg_base) +{ + unsigned int fw_sig = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + + PVDEC_FW_SIGNATURE_OFFSET); + + if (fw_sig != PVDEC_FW_READY_SIG) + return -EIO; + + return 0; +} + +static void pvdec_kick_mtx(void __iomem *reg_base) +{ + unsigned int reg = 0; + + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_KICKI, MTX_KICKI, 1); + VXD_WR_REG(reg_base, MTX_CORE, MTX_KICKI, reg); +} + +static int pvdec_write_vlr(void __iomem *reg_base, const unsigned int *buf, + unsigned long size_dwrds, int off_dwrds) +{ + unsigned int i; + + if (((off_dwrds + size_dwrds) * sizeof(unsigned int)) > VLR_SIZE) + return -EINVAL; + + for (i = 0; i < size_dwrds; i++) { + int off = (off_dwrds + i) * sizeof(unsigned int); + + VXD_WR_REG_ABS(reg_base, (VLR_OFFSET + off), *buf); + buf++; + } + + return 0; +} + +static int pvdec_poll_fw_boot(void __iomem *reg_base, struct vxd_boot_poll_params *poll_params) +{ + unsigned int i; + + for (i = 0; i < 25; i++) { + if (!pvdec_check_fw_sig(reg_base)) + return 0; + usleep_range(100, 110); + } + for (i = 0; i < poll_params->msleep_cycles; i++) { + if (!pvdec_check_fw_sig(reg_base)) + return 0; + msleep(100); + } + return -EIO; +} + +static int pvdec_read_vlr(void __iomem *reg_base, unsigned int *buf, + unsigned long size_dwrds, int off_dwrds) +{ + unsigned int i; + + if (((off_dwrds + size_dwrds) * sizeof(unsigned int)) > VLR_SIZE) + return -EINVAL; + + for (i = 0; i < size_dwrds; i++) { + int off = (off_dwrds + i) * sizeof(unsigned int); + *buf++ = VXD_RD_REG_ABS(reg_base, (VLR_OFFSET + off)); + } + + return 0; +} + +/* Get configuration of a ring buffer used to send messages to the MTX */ +static int pvdec_get_to_mtx_cfg(void __iomem *reg_base, unsigned long *size, int *off, + unsigned int *wr_idx, unsigned int *rd_idx) +{ + unsigned int to_mtx_cfg; + int to_mtx_off, ret; + + ret = pvdec_check_fw_sig(reg_base); + if (ret) + return ret; + + to_mtx_cfg = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_MTX_BUF_CONF_OFFSET); + + *size = PVDEC_FW_COM_BUF_SIZE(to_mtx_cfg); + to_mtx_off = PVDEC_FW_COM_BUF_OFF(to_mtx_cfg); + + if (to_mtx_off % 4) + return -EIO; + + to_mtx_off /= sizeof(unsigned int); + *off = to_mtx_off; + + *wr_idx = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_MTX_WR_IDX_OFFSET); + *rd_idx = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_MTX_RD_IDX_OFFSET); + + if ((*rd_idx >= *size) || (*wr_idx >= *size)) + return -EIO; + + return 0; +} + +/* Submit a padding message to the host->MTX ring buffer */ +static int pvdec_send_pad_msg(void __iomem *reg_base) +{ + int ret, pad_size, to_mtx_off; /* offset in dwords */ + unsigned int wr_idx, rd_idx; /* indicies in dwords */ + unsigned long pad_msg_size = 1, to_mtx_size; /* size in dwords */ + const unsigned long max_msg_size = VXD_MAX_PAYLOAD_SIZE / sizeof(unsigned int); + unsigned int pad_msg; + + ret = pvdec_get_to_mtx_cfg(reg_base, &to_mtx_size, &to_mtx_off, &wr_idx, &rd_idx); + if (ret) + return ret; + + pad_size = to_mtx_size - wr_idx; /* size in dwords */ + + if (pad_size <= 0) { + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_MTX_WR_IDX_OFFSET, 0); + return 0; + } + + while (pad_size > 0) { + int cur_pad_size = pad_size > max_msg_size ? + max_msg_size : pad_size; + + pad_msg = 0; + pad_msg = VXD_WR_REG_FIELD(pad_msg, PVDEC_FW, DEVA_GENMSG, MSG_SIZE, cur_pad_size); + pad_msg = VXD_WR_REG_FIELD(pad_msg, PVDEC_FW, DEVA_GENMSG, + MSG_TYPE, PVDEC_FW_MSG_TYPE_PADDING); + + ret = pvdec_write_vlr(reg_base, &pad_msg, pad_msg_size, to_mtx_off + wr_idx); + if (ret) + return ret; + + wr_idx += cur_pad_size; + + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_MTX_WR_IDX_OFFSET, wr_idx); + + pad_size -= cur_pad_size; + + pvdec_kick_mtx(reg_base); + } + + wr_idx = 0; + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_MTX_WR_IDX_OFFSET, wr_idx); + + return 0; +} + +/* + * Check if there is enough space in comms RAM to submit a + * dwords long message. Submit a padding message if necessary and requested. + * + * Returns 0 if there is space for a message. + * Returns -EINVAL when msg is too big or empty. + * Returns -EIO when there was a problem accessing the HW. + * Returns -EBUSY when there is not ennough space. + */ +static int pvdec_check_comms_space(void __iomem *reg_base, unsigned long msg_size, + unsigned char send_padding) +{ + int ret, to_mtx_off; /* offset in dwords */ + unsigned int wr_idx, rd_idx; /* indicies in dwords */ + unsigned long to_mtx_size; /* size in dwords */ + + ret = pvdec_get_to_mtx_cfg(reg_base, &to_mtx_size, &to_mtx_off, &wr_idx, &rd_idx); + if (ret) + return ret; + + /* Enormous or empty message, won't fit */ + if (msg_size >= to_mtx_size || !msg_size) + return -EINVAL; + + /* Buffer does not wrap */ + if (wr_idx >= rd_idx) { + /* Is there enough space to put the message? */ + if (wr_idx + msg_size < to_mtx_size) + return 0; + + if (!send_padding) + return -EBUSY; + + /* Check if it's ok to send a padding message */ + if (rd_idx == 0) + return -EBUSY; + + /* Send a padding message */ + ret = pvdec_send_pad_msg(reg_base); + if (ret) + return ret; + + /* + * And check if there's enough space at the beginning + * of a buffer + */ + if (msg_size >= rd_idx) + return -EBUSY; /* Not enough space at the beginning */ + + } else { /* Buffer wraps */ + if (wr_idx + msg_size >= rd_idx) + return -EBUSY; /* Not enough space! */ + } + + return 0; +} + +/* Get configuration of a ring buffer used to receive messages from the MTX */ +static int pvdec_get_to_host_cfg(void __iomem *reg_base, unsigned long *size, int *off, + unsigned int *wr_idx, unsigned int *rd_idx) +{ + unsigned int to_host_cfg; + int to_host_off, ret; + + ret = pvdec_check_fw_sig(reg_base); + if (ret) + return ret; + + to_host_cfg = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_HOST_BUF_CONF_OFFSET); + + *size = PVDEC_FW_COM_BUF_SIZE(to_host_cfg); + to_host_off = PVDEC_FW_COM_BUF_OFF(to_host_cfg); + + if (to_host_off % 4) + return -EIO; + + to_host_off /= sizeof(unsigned int); + *off = to_host_off; + + *wr_idx = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_HOST_WR_IDX_OFFSET); + *rd_idx = VXD_RD_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_TO_HOST_RD_IDX_OFFSET); + + if ((*rd_idx >= *size) || (*wr_idx >= *size)) + return -EIO; + + return 0; +} + +static void pvdec_select_pipe(void __iomem *reg_base, unsigned char pipe) +{ + unsigned int reg = 0; + + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_HOST_PIPE_SELECT, PIPE_SEL, pipe); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_HOST_PIPE_SELECT, reg); +} + +static void pvdec_pre_boot_setup(const void *dev, + void __iomem *reg_base, + struct vxd_ena_params *ena_params) +{ + /* Memory staller pre boot settings */ + if (ena_params->mem_staller.data) { + unsigned char size = ena_params->mem_staller.size; + + if (size == PVDEC_CORE_MEMSTALLER_ELEMENTS) { + unsigned int *data = ena_params->mem_staller.data; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: Setting up memory staller", __func__); +#endif + /* + * Data structure represents PVDEC_TEST memory staller + * registers according to TRM 5.25 section + */ + VXD_WR_REG(reg_base, PVDEC_TEST, MEM_READ_LATENCY, data[0]); + VXD_WR_REG(reg_base, PVDEC_TEST, MEM_WRITE_RESPONSE_LATENCY, data[1]); + VXD_WR_REG(reg_base, PVDEC_TEST, MEM_CTRL, data[2]); + VXD_WR_REG(reg_base, PVDEC_TEST, RAND_STL_MEM_CMD_CONFIG, data[3]); + VXD_WR_REG(reg_base, PVDEC_TEST, RAND_STL_MEM_WDATA_CONFIG, data[4]); + VXD_WR_REG(reg_base, PVDEC_TEST, RAND_STL_MEM_WRESP_CONFIG, data[5]); + VXD_WR_REG(reg_base, PVDEC_TEST, RAND_STL_MEM_RDATA_CONFIG, data[6]); + } else { + dev_warn(dev, "%s: Wrong layout of mem staller config (%u)!", + __func__, size); + } + } +} + +static void pvdec_post_boot_setup(const void *dev, + void __iomem *reg_base, + unsigned int freq_khz) +{ + int reg; + + /* + * Configure VXD MMU to use video tiles (256x16) and unique + * strides per context as default. There is currently no + * override mechanism. + */ + reg = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL0); + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL0, + MMU_TILING_SCHEME, 0); + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL0, + USE_TILE_STRIDE_PER_CTX, 1); + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL0, reg); + + /* + * Setup VXD MMU with the tile heap device virtual address + * ranges. + */ + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MIN_ADDR, + PVDEC_HEAP_TILE512_START, 0); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MAX_ADDR, + PVDEC_HEAP_TILE512_START + PVDEC_HEAP_TILE512_SIZE - 1, 0); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MIN_ADDR, + PVDEC_HEAP_TILE1024_START, 1); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MAX_ADDR, + PVDEC_HEAP_TILE1024_START + PVDEC_HEAP_TILE1024_SIZE - 1, 1); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MIN_ADDR, + PVDEC_HEAP_TILE2048_START, 2); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MAX_ADDR, + PVDEC_HEAP_TILE2048_START + PVDEC_HEAP_TILE2048_SIZE - 1, 2); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MIN_ADDR, + PVDEC_HEAP_TILE4096_START, 3); + VXD_WR_RPT_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_TILE_MAX_ADDR, + PVDEC_HEAP_TILE4096_START + PVDEC_HEAP_TILE4096_SIZE - 1, 3); + + /* Disable timer */ + VXD_WR_REG(reg_base, MTX_CORE, MTX_SYSC_TIMERDIV, 0); + + reg = 0; + if (freq_khz) + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_TIMERDIV, TIMER_DIV, + PVDEC_CALC_TIMER_DIV(freq_khz / 1000)); + else + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_TIMERDIV, + TIMER_DIV, PVDEC_CLK_MHZ_DEFAULT - 1); + + /* Enable the MTX timer with final settings */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_TIMERDIV, TIMER_EN, 1); + VXD_WR_REG(reg_base, MTX_CORE, MTX_SYSC_TIMERDIV, reg); +} + +static void pvdec_clock_measure(void __iomem *reg_base, + struct timespec64 *start_time, + unsigned int *start_ticks) +{ + local_irq_disable(); + ktime_get_real_ts64(start_time); + *start_ticks = VXD_RD_REG(reg_base, MTX_CORE, MTX_SYSC_TXTIMER); + local_irq_enable(); +} + +static int pvdec_clock_calculate(const void *dev, + void __iomem *reg_base, + struct timespec64 *start_time, + unsigned int start_ticks, + unsigned int *freq_khz) +{ + struct timespec64 end_time, dif_time; + long long span_nsec = 0; + unsigned int stop_ticks, tot_ticks; + + local_irq_disable(); + ktime_get_real_ts64(&end_time); + + stop_ticks = VXD_RD_REG(reg_base, MTX_CORE, MTX_SYSC_TXTIMER); + local_irq_enable(); + + *(struct timespec64 *)(&dif_time) = timespec64_sub(*((struct timespec64 *)(&end_time)), + *((struct timespec64 *)(&start_time))); + + span_nsec = timespec64_to_ns((const struct timespec64 *)&dif_time); + + /* Sanity check for mtx timer */ + if (!stop_ticks || stop_ticks < start_ticks) { + dev_err(dev, "%s: invalid ticks (0x%x -> 0x%x)\n", + __func__, start_ticks, stop_ticks); + return -EIO; + } + tot_ticks = stop_ticks - start_ticks; + + if (span_nsec) { + unsigned long long res = (unsigned long long)tot_ticks * 1000000UL; + + do_divide(&res, span_nsec); + *freq_khz = (unsigned int)res; + if (*freq_khz < 1000) + *freq_khz = 1000; /* 1MHz */ + } else { + dev_err(dev, "%s: generic failure!\n", __func__); + *freq_khz = 0; + return -ERANGE; + } + + return 0; +} + +static int pvdec_wait_dma_done(const void *dev, + void __iomem *reg_base, + unsigned long size, + unsigned char dma_channel) +{ + unsigned int reg, timeout = PVDEC_TIMEOUT_COUNTER, prev_count, count = size; + + do { + usleep_range(300, 310); + prev_count = count; + reg = VXD_RD_RPT_REG(reg_base, DMAC, DMAC_COUNT, dma_channel); + count = VXD_RD_REG_FIELD(reg, DMAC, DMAC_COUNT, CNT); + /* Check for dma progress */ + if (count == prev_count) { + /* There could be a bus lag, protect against that */ + timeout--; + if (timeout == 0) { + dev_err(dev, "%s FW DMA failed! (0x%x)\n", __func__, count); + return -EIO; + } + } else { + /* Reset timeout counter */ + timeout = PVDEC_TIMEOUT_COUNTER; + } + } while (count > 0); + + return 0; +} + +static int pvdec_start_fw_dma(const void *dev, + void __iomem *reg_base, + unsigned char dma_channel, + unsigned long fw_buf_size, + unsigned int *freq_khz) +{ + unsigned int reg = 0; + int ret = 0; + + fw_buf_size = fw_buf_size / sizeof(unsigned int); +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: dma FW upload, fw_buf_size: %zu (dwords)\n", __func__, fw_buf_size); +#endif + + pvdec_select_pipe(reg_base, 1); + + reg = VXD_RD_REG(reg_base, PVDEC_PIXEL, PIXEL_MAN_CLK_ENA); + reg = VXD_WR_REG_FIELD(reg, PVDEC_PIXEL, PIXEL_MAN_CLK_ENA, PIXEL_DMAC_MAN_CLK_ENA, 1); + reg = VXD_WR_REG_FIELD(reg, PVDEC_PIXEL, PIXEL_MAN_CLK_ENA, PIXEL_REG_MAN_CLK_ENA, 1); + VXD_WR_REG(reg_base, PVDEC_PIXEL, PIXEL_MAN_CLK_ENA, reg); + + /* + * Setup MTX to receive DMA + * DMA transfers to/from the MTX have to be 32-bit aligned and + * in multiples of 32 bits + */ + VXD_WR_REG(reg_base, MTX_CORE, MTX_SYSC_CDMAA, 0); /* MTX: 0x80900000 */ + + reg = 0; + /* Burst size in multiples of 64 bits (allowed values are 2 or 4) */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_CDMAC, BURSTSIZE, 0); + /* 0 - write to MTX memory */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_CDMAC, RNW, 0); + /* Begin transfer */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_CDMAC, ENABLE, 1); + /* Transfer size */ + reg = VXD_WR_REG_FIELD(reg, MTX_CORE, MTX_SYSC_CDMAC, LENGTH, + ((fw_buf_size + 7) & (~7)) + 8); + VXD_WR_REG(reg_base, MTX_CORE, MTX_SYSC_CDMAC, reg); + + /* Boot MTX once transfer is done */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PROC_DMAC_CONTROL, + BOOT_ON_DMA_CH0, 1); + VXD_WR_REG(reg_base, PVDEC_CORE, PROC_DMAC_CONTROL, reg); + + /* Toggle channel 0 usage between MTX and other PVDEC peripherals */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, PVDEC_PIXEL, PIXEL_CONTROL_0, + DMAC_CH_SEL_FOR_MTX, 0); + VXD_WR_REG(reg_base, PVDEC_PIXEL, PIXEL_CONTROL_0, reg); + + /* Reset DMA channel first */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, SRST, 1); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_COUNT, reg, dma_channel); + + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, LIST_EN, 0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, CNT, 0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, EN, 0); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_COUNT, reg, dma_channel); + + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, SRST, 0); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_COUNT, reg, dma_channel); + + /* + * Setup a Simple DMA for Ch0 + * Specify the holdover period to use for the channel + */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PER_HOLD, PER_HOLD, 7); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_PER_HOLD, reg, dma_channel); + + /* Clear the DMAC Stats */ + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_IRQ_STAT, 0, dma_channel); + + reg = 0; + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PERIPH_ADDR, ADDR, + MTX_CORE_MTX_SYSC_CDMAT_OFFSET); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_PERIPH_ADDR, reg, dma_channel); + + /* Clear peripheral register address */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PERIPH, ACC_DEL, 0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PERIPH, INCR, DMAC_INCR_OFF); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PERIPH, BURST, DMAC_BURST_1); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PERIPH, EXT_BURST, DMAC_EXT_BURST_0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_PERIPH, EXT_SA, 0); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_PERIPH, reg, dma_channel); + + /* + * Now start the transfer by setting the list enable bit in + * the count register + */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, TRANSFER_IEN, 1); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, PW, DMAC_PWIDTH_32_BIT); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, DIR, DMAC_MEM_TO_VXD); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, PI, DMAC_INCR_ON); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, LIST_FIN_CTL, 0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, LIST_EN, 0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, ENABLE_2D_MODE, 0); + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, CNT, fw_buf_size); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_COUNT, reg, dma_channel); + + reg = VXD_WR_REG_FIELD(reg, DMAC, DMAC_COUNT, EN, 1); + VXD_WR_RPT_REG(reg_base, DMAC, DMAC_COUNT, reg, dma_channel); + + /* NOTE: The MTX timer starts once DMA boot is triggered */ + { + struct timespec64 host_time; + unsigned int mtx_time; + + pvdec_clock_measure(reg_base, &host_time, &mtx_time); + + ret = pvdec_wait_dma_done(dev, reg_base, fw_buf_size, dma_channel); + if (!ret) { + if (pvdec_clock_calculate(dev, reg_base, &host_time, mtx_time, + freq_khz) < 0) + dev_dbg(dev, "%s: measure info not available!\n", __func__); + } + } + + return ret; +} + +static int pvdec_set_clocks(void __iomem *reg_base, unsigned int req_clocks) +{ + unsigned int clocks = 0, reg; + unsigned int pvdec_timeout; + + /* Turn on core clocks only */ + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, + PVDEC_REG_MAN_CLK_ENA, 1); + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, CORE_MAN_CLK_ENA, 1); + + /* Wait until core clocks set */ + pvdec_timeout = PVDEC_TIMEOUT_COUNTER; + do { + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_MAN_CLK_ENA, clocks); + udelay(vxd_plat_poll_udelay); + reg = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_MAN_CLK_ENA); + pvdec_timeout--; + } while (reg != clocks && pvdec_timeout != 0); + + if (pvdec_timeout == 0) + return -EIO; + + /* Write requested clocks */ + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_MAN_CLK_ENA, req_clocks); + + return 0; +} + +static int pvdec_enable_clocks(void __iomem *reg_base) +{ + unsigned int clocks = 0; + + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, + PVDEC_REG_MAN_CLK_ENA, 1); + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, + CORE_MAN_CLK_ENA, 1); + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, + MEM_MAN_CLK_ENA, 1); + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, + PROC_MAN_CLK_ENA, 1); + clocks = VXD_WR_REG_FIELD(clocks, PVDEC_CORE, PVDEC_MAN_CLK_ENA, + PIXEL_PROC_MAN_CLK_ENA, 1); + + return pvdec_set_clocks(reg_base, clocks); +} + +static int pvdec_disable_clocks(void __iomem *reg_base) +{ + return pvdec_set_clocks(reg_base, 0); +} + +static void pvdec_ena_mtx_int(void __iomem *reg_base) +{ + unsigned int reg = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_HOST_INT_ENA); + + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_INT_STAT, HOST_PROC_IRQ, 1); + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_INT_STAT, HOST_MMU_FAULT_IRQ, 1); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_HOST_INT_ENA, reg); +} + +static void pvdec_check_mmu_requests(void __iomem *reg_base, + unsigned int mmu_checks, + unsigned int max_attempts) +{ + unsigned int reg, i, checks = 0; + + for (i = 0; i < max_attempts; i++) { + reg = VXD_RD_REG(reg_base, + IMG_VIDEO_BUS4_MMU, MMU_MEM_REQ); + reg = VXD_RD_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_MEM_REQ, TAG_OUTSTANDING); + if (reg) { + udelay(vxd_plat_poll_udelay); + continue; + } + + /* Read READ_WORDS_OUTSTANDING */ + reg = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_MEM_EXT_OUTSTANDING); + reg = VXD_RD_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_MEM_EXT_OUTSTANDING, + READ_WORDS); + if (!reg) { + checks++; + if (checks == mmu_checks) + break; + } else { /* Reset the counter and continue */ + checks = 0; + } + } + + if (checks != mmu_checks) + pr_warn("Checking for MMU outstanding requests failed!\n"); +} + +static int pvdec_reset(void __iomem *reg_base, unsigned char skip_pipe_clocks) +{ + unsigned int reg = 0; + unsigned char pipe, num_ent_pipes, num_pix_pipes; + unsigned int core_id, pvdec_timeout; + + core_id = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_CORE_ID); + + num_ent_pipes = VXD_RD_REG_FIELD(core_id, PVDEC_CORE, PVDEC_CORE_ID, ENT_PIPES); + num_pix_pipes = VXD_RD_REG_FIELD(core_id, PVDEC_CORE, PVDEC_CORE_ID, PIX_PIPES); + + if (num_pix_pipes == 0 || num_pix_pipes > VXD_MAX_PIPES) + return -EINVAL; + + /* Clear interrupt enabled flag */ + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_HOST_INT_ENA, 0); + + /* Clear any pending interrupt flags */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_INT_CLEAR, IRQ_CLEAR, 0xFFFF); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_INT_CLEAR, reg); + + /* Turn all clocks on - don't touch reserved bits! */ + pvdec_set_clocks(reg_base, 0xFFFF0113); + + if (!skip_pipe_clocks) { + for (pipe = 1; pipe <= num_pix_pipes; pipe++) { + pvdec_select_pipe(reg_base, pipe); + /* Turn all available clocks on - skip reserved bits! */ + VXD_WR_REG(reg_base, PVDEC_PIXEL, PIXEL_MAN_CLK_ENA, 0xFFBF0FFF); + } + + for (pipe = 1; pipe <= num_ent_pipes; pipe++) { + pvdec_select_pipe(reg_base, pipe); + /* Turn all available clocks on - skip reserved bits! */ + VXD_WR_REG(reg_base, PVDEC_ENTROPY, ENTROPY_MAN_CLK_ENA, 0x5); + } + } + + /* 1st MMU outstanding requests check */ + pvdec_check_mmu_requests(reg_base, 1000, 2000); + + /* Make sure MMU is not under reset MMU_SOFT_RESET -> 0 */ + pvdec_timeout = PVDEC_TIMEOUT_COUNTER; + do { + reg = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1); + reg = VXD_RD_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_SOFT_RESET); + udelay(vxd_plat_poll_udelay); + pvdec_timeout--; + } while (reg != 0 && pvdec_timeout != 0); + + if (pvdec_timeout == 0) { + pr_err("Waiting for MMU soft reset(1) timed out!\n"); + pvdec_mtx_status_dump(reg_base, NULL); + } + + /* Write 1 to MMU_PAUSE_SET */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_PAUSE_SET, 1); + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, reg); + + /* 2nd MMU outstanding requests check */ + pvdec_check_mmu_requests(reg_base, 100, 1000); + + /* Issue software reset for all but MMU/core */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_SOFT_RST, PVDEC_PIXEL_PROC_SOFT_RST, 0xFF); + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_SOFT_RST, PVDEC_ENTROPY_SOFT_RST, 0xFF); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_SOFT_RST, reg); + + VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_SOFT_RST); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_SOFT_RST, 0); + + /* Write 1 to MMU_PAUSE_CLEAR in MMU_CONTROL1 reg */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_PAUSE_CLEAR, 1); + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, reg); + + /* Confirm MMU_PAUSE_SET is cleared */ + pvdec_timeout = PVDEC_TIMEOUT_COUNTER; + do { + reg = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1); + reg = VXD_RD_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_PAUSE_SET); + udelay(vxd_plat_poll_udelay); + pvdec_timeout--; + } while (reg != 0 && pvdec_timeout != 0); + + if (pvdec_timeout == 0) { + pr_err("Waiting for MMU pause clear timed out!\n"); + pvdec_mtx_status_dump(reg_base, NULL); + return -EIO; + } + + /* Issue software reset for MMU */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_SOFT_RESET, 1); + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, reg); + + /* Wait until MMU_SOFT_RESET -> 0 */ + pvdec_timeout = PVDEC_TIMEOUT_COUNTER; + do { + reg = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1); + reg = VXD_RD_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_SOFT_RESET); + udelay(vxd_plat_poll_udelay); + pvdec_timeout--; + } while (reg != 0 && pvdec_timeout != 0); + + if (pvdec_timeout == 0) { + pr_err("Waiting for MMU soft reset(2) timed out!\n"); + pvdec_mtx_status_dump(reg_base, NULL); + } + + /* Issue software reset for entire PVDEC */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_SOFT_RST, PVDEC_SOFT_RST, 0x1); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_SOFT_RST, reg); + + /* Waiting for reset bit to be cleared */ + pvdec_timeout = PVDEC_TIMEOUT_COUNTER; + do { + reg = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_SOFT_RST); + reg = VXD_RD_REG_FIELD(reg, PVDEC_CORE, PVDEC_SOFT_RST, PVDEC_SOFT_RST); + udelay(vxd_plat_poll_udelay); + pvdec_timeout--; + } while (reg != 0 && pvdec_timeout != 0); + + if (pvdec_timeout == 0) { + pr_err("Waiting for PVDEC soft reset timed out!\n"); + pvdec_mtx_status_dump(reg_base, NULL); + return -EIO; + } + + /* Clear interrupt enabled flag */ + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_HOST_INT_ENA, 0); + + /* Clear any pending interrupt flags */ + reg = 0; + reg = VXD_WR_REG_FIELD(reg, PVDEC_CORE, PVDEC_INT_CLEAR, IRQ_CLEAR, 0xFFFF); + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_INT_CLEAR, reg); + return 0; +} + +static int pvdec_get_properties(void __iomem *reg_base, + struct vxd_core_props *props) +{ + unsigned int major, minor, maint, group_id, core_id; + unsigned char num_pix_pipes, pipe; + + if (!props) + return -EINVAL; + + /* PVDEC Core Revision Information */ + props->core_rev = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_CORE_REV); + major = VXD_RD_REG_FIELD(props->core_rev, PVDEC_CORE, PVDEC_CORE_REV, PVDEC_MAJOR_REV); + minor = VXD_RD_REG_FIELD(props->core_rev, PVDEC_CORE, PVDEC_CORE_REV, PVDEC_MINOR_REV); + maint = VXD_RD_REG_FIELD(props->core_rev, PVDEC_CORE, PVDEC_CORE_REV, PVDEC_MAINT_REV); + + /* Core ID */ + props->pvdec_core_id = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_CORE_ID); + group_id = VXD_RD_REG_FIELD(props->pvdec_core_id, PVDEC_CORE, PVDEC_CORE_ID, GROUP_ID); + core_id = VXD_RD_REG_FIELD(props->pvdec_core_id, PVDEC_CORE, PVDEC_CORE_ID, CORE_ID); + + /* Ensure that the core is IMG Video Decoder (PVDEC). */ + if (group_id != 3 || core_id != 3) { + pr_err("Wrong core revision %d.%d.%d !!!\n", major, minor, maint); + return -EIO; + } + + props->mmu_config0 = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONFIG0); + props->mmu_config1 = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONFIG1); + + num_pix_pipes = VXD_NUM_PIX_PIPES(*props); + + if (unlikely(num_pix_pipes > VXD_MAX_PIPES)) { + pr_warn("Too many pipes detected!\n"); + num_pix_pipes = VXD_MAX_PIPES; + } + + for (pipe = 1; pipe <= num_pix_pipes; ++pipe) { + pvdec_select_pipe(reg_base, pipe); + if (pipe < VXD_MAX_PIPES) { + props->pixel_pipe_cfg[pipe - 1] = + VXD_RD_REG(reg_base, PVDEC_PIXEL, PIXEL_PIPE_CONFIG); + props->pixel_misc_cfg[pipe - 1] = + VXD_RD_REG(reg_base, PVDEC_PIXEL, PIXEL_MISC_CONFIG); + /* + * Detect pipe access problems. + * Pipe config shall always indicate + * a non zero value (at least one standard supported)! + */ + if (!props->pixel_pipe_cfg[pipe - 1]) + pr_warn("Pipe config info is wrong!\n"); + } + } + + pvdec_select_pipe(reg_base, 1); + props->pixel_max_frame_cfg = VXD_RD_REG(reg_base, PVDEC_PIXEL, MAX_FRAME_CONFIG); + + { + unsigned int fifo_ctrl = VXD_RD_REG(reg_base, PVDEC_CORE, PROC_DBG_FIFO_CTRL0); + + props->dbg_fifo_size = VXD_RD_REG_FIELD(fifo_ctrl, + PVDEC_CORE, + PROC_DBG_FIFO_CTRL0, + PROC_DBG_FIFO_SIZE); + } + + return 0; +} + +int vxd_pvdec_init(const void *dev, void __iomem *reg_base) +{ + int ret; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: trying to reset VXD, reg base: %p\n", __func__, reg_base); +#endif + + ret = pvdec_enable_clocks(reg_base); + if (ret) { + dev_err(dev, "%s: failed to enable clocks!\n", __func__); + return ret; + } + + ret = pvdec_reset(reg_base, FALSE); + if (ret) { + dev_err(dev, "%s: VXD reset failed!\n", __func__); + return ret; + } + + pvdec_ena_mtx_int(reg_base); + + return 0; +} + +/* Send dwords long message */ +int vxd_pvdec_send_msg(const void *dev, + void __iomem *reg_base, + unsigned int *msg, + unsigned long msg_size, + unsigned short msg_id, + struct vxd_dev *ctx) +{ + int ret, to_mtx_off; /* offset in dwords */ + unsigned int wr_idx, rd_idx; /* indicies in dwords */ + unsigned long to_mtx_size; /* size in dwords */ + unsigned int msg_wrd; + struct timespec64 time; + static int cnt; + + ktime_get_real_ts64(&time); + + ctx->time_fw[cnt].start_time = timespec64_to_ns((const struct timespec64 *)&time); + ctx->time_fw[cnt].id = msg_id; + cnt++; + + if (cnt >= ARRAY_SIZE(ctx->time_fw)) + cnt = 0; + + ret = pvdec_get_to_mtx_cfg(reg_base, &to_mtx_size, &to_mtx_off, &wr_idx, &rd_idx); + if (ret) { + dev_err(dev, "%s: failed to obtain mtx ring buffer config!\n", __func__); + return ret; + } + + /* populate the size and id fields in the message header */ + msg_wrd = VXD_RD_MSG_WRD(msg, PVDEC_FW, DEVA_GENMSG); + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_GENMSG, MSG_SIZE, msg_size); + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_GENMSG, MSG_ID, msg_id); + VXD_WR_MSG_WRD(msg, PVDEC_FW, DEVA_GENMSG, msg_wrd); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: [msg out] size: %zu, id: 0x%x, type: 0x%x\n", __func__, msg_size, msg_id, + VXD_RD_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_GENMSG, MSG_TYPE)); + dev_dbg(dev, "%s: to_mtx: (%zu @ %d), wr_idx: %d, rd_idx: %d\n", + __func__, to_mtx_size, to_mtx_off, wr_idx, rd_idx); +#endif + + ret = pvdec_check_comms_space(reg_base, msg_size, FALSE); + if (ret) { + dev_err(dev, "%s: invalid message or not enough space (%d)!\n", __func__, ret); + return ret; + } + + ret = pvdec_write_vlr(reg_base, msg, msg_size, to_mtx_off + wr_idx); + if (ret) { + dev_err(dev, "%s: failed to write msg to vlr!\n", __func__); + return ret; + } + + wr_idx += msg_size; + if (wr_idx == to_mtx_size) + wr_idx = 0; + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + + PVDEC_FW_TO_MTX_WR_IDX_OFFSET, wr_idx); + + pvdec_kick_mtx(reg_base); + + return 0; +} + +/* Fetch size (in dwords) of message pending from MTX */ +int vxd_pvdec_pend_msg_info(const void *dev, void __iomem *reg_base, + unsigned long *size, + unsigned short *msg_id, + unsigned char *not_last_msg) +{ + int ret, to_host_off; /* offset in dwords */ + unsigned int wr_idx, rd_idx; /* indicies in dwords */ + unsigned long to_host_size; /* size in dwords */ + unsigned int val = 0; + + ret = pvdec_get_to_host_cfg(reg_base, &to_host_size, &to_host_off, &wr_idx, &rd_idx); + if (ret) { + dev_err(dev, "%s: failed to obtain host ring buffer config!\n", __func__); + return ret; + } + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: to host: (%zu @ %d), wr: %u, rd: %u\n", __func__, + to_host_size, to_host_off, wr_idx, rd_idx); +#endif + + if (wr_idx == rd_idx) { + *size = 0; + *msg_id = 0; + return 0; + } + + ret = pvdec_read_vlr(reg_base, &val, 1, to_host_off + rd_idx); + if (ret) { + dev_err(dev, "%s: failed to read first word!\n", __func__); + return ret; + } + + *size = VXD_RD_REG_FIELD(val, PVDEC_FW, DEVA_GENMSG, MSG_SIZE); + *msg_id = VXD_RD_REG_FIELD(val, PVDEC_FW, DEVA_GENMSG, MSG_ID); + *not_last_msg = VXD_RD_REG_FIELD(val, PVDEC_FW, DEVA_GENMSG, NOT_LAST_MSG); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: [msg in] rd_idx: %d, size: %zu, id: 0x%04x, type: 0x%x\n", + __func__, rd_idx, *size, *msg_id, + VXD_RD_REG_FIELD(val, PVDEC_FW, DEVA_GENMSG, MSG_TYPE)); +#endif + + return 0; +} + +/* + * Receive message from the MTX and place it in a dwords long + * buffer. If the provided buffer is too small to hold the message, only part + * of it will be placed in a buffer, but the ring buffer read index will be + * moved so that message is no longer available. + */ +int vxd_pvdec_recv_msg(const void *dev, void __iomem *reg_base, + unsigned int *buf, + unsigned long buf_size, + struct vxd_dev *vxd) +{ + int ret, to_host_off; /* offset in dwords */ + unsigned int wr_idx, rd_idx; /* indicies in dwords */ + unsigned long to_host_size, msg_size, to_read; /* sizes in dwords */ + unsigned int val = 0; + struct timespec64 time; + unsigned short msg_id; + int loop; + + ret = pvdec_get_to_host_cfg(reg_base, &to_host_size, + &to_host_off, &wr_idx, &rd_idx); + if (ret) { + dev_err(dev, "%s: failed to obtain host ring buffer config!\n", __func__); + return ret; + } + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: to host: (%zu @ %d), wr: %u, rd: %u\n", __func__, + to_host_size, to_host_off, wr_idx, rd_idx); +#endif + + /* Obtain the message size */ + ret = pvdec_read_vlr(reg_base, &val, 1, to_host_off + rd_idx); + if (ret) { + dev_err(dev, "%s: failed to read first word!\n", __func__); + return ret; + } + msg_size = VXD_RD_REG_FIELD(val, PVDEC_FW, DEVA_GENMSG, MSG_SIZE); + + to_read = (msg_size > buf_size) ? buf_size : msg_size; + + /* Does the message wrap? */ + if (to_read + rd_idx > to_host_size) { + unsigned long chunk_size = to_host_size - rd_idx; + + ret = pvdec_read_vlr(reg_base, buf, chunk_size, to_host_off + rd_idx); + if (ret) { + dev_err(dev, "%s: failed to read chunk before wrap!\n", __func__); + return ret; + } + to_read -= chunk_size; + buf += chunk_size; + rd_idx = 0; + msg_size -= chunk_size; + } + + /* + * If the message wrapped, read the second chunk. + * If it didn't, read first and only chunk + */ + ret = pvdec_read_vlr(reg_base, buf, to_read, to_host_off + rd_idx); + if (ret) { + dev_err(dev, "%s: failed to read message from vlr!\n", __func__); + return ret; + } + + /* Update read index in the ring buffer */ + rd_idx = (rd_idx + msg_size) % to_host_size; + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + + PVDEC_FW_TO_HOST_RD_IDX_OFFSET, rd_idx); + + msg_id = VXD_RD_REG_FIELD(val, PVDEC_FW, DEVA_GENMSG, MSG_ID); + + ktime_get_real_ts64(&time); + for (loop = 0; loop < ARRAY_SIZE(vxd->time_fw); loop++) { + if (vxd->time_fw[loop].id == msg_id) { + vxd->time_fw[loop].end_time = + timespec64_to_ns((const struct timespec64 *)&time); +#ifdef DEBUG_DECODER_DRIVER + dev_info(dev, "fw decode time is %llu us for msg_id x%0x\n", + div_s64(vxd->time_fw[loop].end_time - + vxd->time_fw[loop].start_time, 1000), msg_id); +#endif + break; + } + } + + if (loop == ARRAY_SIZE(vxd->time_fw)) + dev_err(dev, "fw decode time for msg_id x%0x is not measured\n", msg_id); + + return 0; +} + +int vxd_pvdec_check_fw_status(const void *dev, void __iomem *reg_base) +{ + int ret; + unsigned int val = 0; + + /* Obtain current fw status */ + ret = pvdec_read_vlr(reg_base, &val, 1, PVDEC_FW_STATUS_OFFSET); + if (ret) { + dev_err(dev, "%s: failed to read fw status!\n", __func__); + return ret; + } + + /* Check for fatal condition */ + if (val == PVDEC_FW_STATUS_PANIC || val == PVDEC_FW_STATUS_ASSERT || + val == PVDEC_FW_STATUS_SO) + return -1; + + return 0; +} + +static int pvdec_send_init_msg(const void *dev, + void __iomem *reg_base, + struct vxd_ena_params *ena_params) +{ + unsigned short msg_id = 0; + unsigned int msg[PVDEC_FW_DEVA_INIT_MSG_WRDS] = { 0 }, msg_wrd = 0; + struct vxd_dev *vxd; + int ret; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: rendec: %d@0x%x, crc: 0x%x\n", __func__, + ena_params->rendec_size, ena_params->rendec_addr, ena_params->crc); +#endif + + vxd = kzalloc(sizeof(*vxd), GFP_KERNEL); + if (!vxd) + return -1; + + /* message type */ + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_GENMSG, MSG_TYPE, + PVDEC_FW_MSG_TYPE_INIT); + VXD_WR_MSG_WRD(msg, PVDEC_FW, DEVA_GENMSG, msg_wrd); + + /* rendec address */ + VXD_WR_MSG_WRD(msg, PVDEC_FW_DEVA_INIT, RENDEC_ADDR0, ena_params->rendec_addr); + + /* rendec size */ + msg_wrd = 0; + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_INIT, RENDEC_SIZE0, + ena_params->rendec_size); + VXD_WR_MSG_WRD(msg, PVDEC_FW_DEVA_INIT, RENDEC_SIZE0, msg_wrd); + + /* HEVC configuration */ + msg_wrd = 0; + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_INIT, + HEVC_CFG_MAX_H_FOR_PIPE_WAIT, 0xFFFF); + VXD_WR_MSG_WRD(msg, PVDEC_FW_DEVA_INIT, HEVC_CFG, msg_wrd); + + /* signature select */ + VXD_WR_MSG_WRD(msg, PVDEC_FW_DEVA_INIT, SIG_SELECT, ena_params->crc); + + /* partial frame notification timer divider */ + msg_wrd = 0; + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_INIT, PFNT_DIV, PVDEC_PFNT_DIV); + VXD_WR_MSG_WRD(msg, PVDEC_FW_DEVA_INIT, PFNT_DIV, msg_wrd); + + /* firmware watchdog timeout value */ + msg_wrd = VXD_WR_REG_FIELD(msg_wrd, PVDEC_FW, DEVA_INIT, FWWDT_MS, ena_params->fwwdt_ms); + VXD_WR_MSG_WRD(msg, PVDEC_FW_DEVA_INIT, FWWDT_MS, msg_wrd); + + ret = vxd_pvdec_send_msg(dev, reg_base, msg, ARRAY_SIZE(msg), msg_id, vxd); + kfree(vxd); + + return ret; +} + +int vxd_pvdec_ena(const void *dev, void __iomem *reg_base, + struct vxd_ena_params *ena_params, + struct vxd_fw_hdr *fw_hdr, + unsigned int *freq_khz) +{ + int ret; + unsigned int mtx_ram_size = 0; + unsigned char dma_channel = 0; + + ret = vxd_pvdec_init(dev, reg_base); + if (ret) { + dev_err(dev, "%s: PVDEC init failed!\n", __func__); + return ret; + } + + ret = pvdec_get_mtx_ram_size(reg_base, &mtx_ram_size); + if (ret) { + dev_err(dev, "%s: failed to get MTX RAM size!\n", __func__); + return ret; + } + + if (mtx_ram_size < fw_hdr->core_size) { + dev_err(dev, "%s: FW larger than MTX RAM size (%u < %d)!\n", + __func__, mtx_ram_size, fw_hdr->core_size); + return -EINVAL; + } + + /* Apply pre boot settings - if any */ + pvdec_pre_boot_setup(dev, reg_base, ena_params); + + pvdec_prep_fw_upload(dev, reg_base, ena_params, dma_channel); + + ret = pvdec_start_fw_dma(dev, reg_base, dma_channel, fw_hdr->core_size, freq_khz); + + if (ret) { + dev_err(dev, "%s: failed to load FW! (%d)", __func__, ret); + pvdec_mtx_status_dump(reg_base, NULL); + return ret; + } + + /* Apply final settings - if any */ + pvdec_post_boot_setup(dev, reg_base, *freq_khz); + + ret = pvdec_poll_fw_boot(reg_base, &ena_params->boot_poll); + if (ret) { + dev_err(dev, "%s: FW failed to boot! (%d)!\n", __func__, ret); + return ret; + } + + ret = pvdec_send_init_msg(dev, reg_base, ena_params); + if (ret) { + dev_err(dev, "%s: failed to send init message! (%d)!\n", __func__, ret); + return ret; + } + + return 0; +} + +int vxd_pvdec_dis(const void *dev, void __iomem *reg_base) +{ + int ret = pvdec_enable_clocks(reg_base); + + if (ret) { + dev_err(dev, "%s: failed to enable clocks! (%d)\n", __func__, ret); + return ret; + } + + ret = pvdec_reset(reg_base, TRUE); + if (ret) { + dev_err(dev, "%s: VXD reset failed! (%d)\n", __func__, ret); + return ret; + } + + ret = pvdec_disable_clocks(reg_base); + if (ret) { + dev_err(dev, "%s: VXD disable clocks failed! (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +/* + * Invalidate VXD's MMU cache. + */ +int vxd_pvdec_mmu_flush(const void *dev, void __iomem *reg_base) +{ + unsigned int reg = VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1); + + if (reg == PVDEC_INVALID_HW_STATE) { + dev_err(dev, "%s: invalid HW state!\n", __func__); + return -EIO; + } + + reg = VXD_WR_REG_FIELD(reg, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, MMU_INVALDC, 0xF); + VXD_WR_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_CONTROL1, reg); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: device MMU cache invalidated!\n", __func__); +#endif + + return 0; +} + +irqreturn_t vxd_pvdec_clear_int(void __iomem *reg_base, unsigned int *irq_status) +{ + irqreturn_t ret = IRQ_NONE; + unsigned int enabled; + unsigned int status = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_INT_STAT); + + enabled = VXD_RD_REG(reg_base, PVDEC_CORE, PVDEC_HOST_INT_ENA); + + status &= enabled; + /* Store the last irq status */ + *irq_status |= status; + + if (status & (PVDEC_CORE_PVDEC_INT_STAT_HOST_MMU_FAULT_IRQ_MASK | + PVDEC_CORE_PVDEC_INT_STAT_HOST_PROC_IRQ_MASK)) + ret = IRQ_WAKE_THREAD; + + /* Disable MMU interrupts - clearing is not enough */ + if (status & PVDEC_CORE_PVDEC_INT_STAT_HOST_MMU_FAULT_IRQ_MASK) { + enabled &= ~PVDEC_CORE_PVDEC_INT_STAT_HOST_MMU_FAULT_IRQ_MASK; + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_HOST_INT_ENA, enabled); + } + + VXD_WR_REG(reg_base, PVDEC_CORE, PVDEC_INT_CLEAR, status); + + return ret; +} + +/* + * Check if there's enough space in comms RAM to submit dwords long + * message. This function also submits a padding message if it will be + * necessary for this particular message. + * + * return 0 if there is enough space, + * return -EBUSY if there is not enough space, + * return another fault code in case of an error. + */ +int vxd_pvdec_msg_fit(const void *dev, void __iomem *reg_base, unsigned long msg_size) +{ + int ret = pvdec_check_comms_space(reg_base, msg_size, TRUE); + + /* + * In specific environment, when to_mtx buffer is small, and messages + * the userspace is submitting are large (e.g. FWBSP flow), it's + * possible that firmware will consume the padding message sent by + * vxd_pvdec_msg_fit() immediately. Retry the check. + */ + if (ret == -EBUSY) { + unsigned int flags = VXD_RD_REG_ABS(reg_base, + VLR_OFFSET + PVDEC_FW_FLAGS_OFFSET) | + PVDEC_FWFLAG_FAKE_COMPLETION; + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "comms space full, asking fw to send empty msg when space is available"); +#endif + + VXD_WR_REG_ABS(reg_base, VLR_OFFSET + PVDEC_FW_FLAGS_OFFSET, flags); + ret = pvdec_check_comms_space(reg_base, msg_size, FALSE); + } + + return ret; +} + +void vxd_pvdec_get_state(const void *dev, void __iomem *reg_base, + unsigned int num_pipes, + struct vxd_hw_state *state) +{ + unsigned char pipe; +#ifdef DEBUG_DECODER_DRIVER + unsigned int state_cfg = VXD_RD_REG_ABS(reg_base, (VLR_OFFSET + + PVDEC_FW_STATE_BUF_CFG_OFFSET)); + + unsigned short state_size = PVDEC_FW_COM_BUF_SIZE(state_cfg); + unsigned short state_off = PVDEC_FW_COM_BUF_OFF(state_cfg); + + /* + * The generic fw progress counter + * is the first element in the fw state + */ + dev_dbg(dev, "%s: state off: 0x%x, size: 0x%x\n", __func__, state_off, state_size); + state->fw_counter = VXD_RD_REG_ABS(reg_base, (VLR_OFFSET + state_off)); + dev_dbg(dev, "%s: fw_counter: 0x%x\n", __func__, state->fw_counter); +#endif + + /* We just combine the macroblocks being processed by the HW */ + for (pipe = 0; pipe < num_pipes; pipe++) { + unsigned int p_off = VXD_GET_PIPE_OFF(num_pipes, pipe + 1); + unsigned int reg_val; + + /* Front-end */ + unsigned int reg_off = VXD_GET_REG_OFF(PVDEC_ENTROPY, ENTROPY_LAST_MB); + + state->fe_status[pipe] = VXD_RD_REG_ABS(reg_base, reg_off + p_off); + + reg_off = VXD_GET_REG_OFF(MSVDX_VEC, VEC_ENTDEC_INFORMATION); + state->fe_status[pipe] |= VXD_RD_REG_ABS(reg_base, reg_off + p_off); + + /* Back-end */ + reg_off = VXD_GET_REG_OFF(PVDEC_VEC_BE, VEC_BE_STATUS); + state->be_status[pipe] = VXD_RD_REG_ABS(reg_base, reg_off + p_off); + reg_off = VXD_GET_REG_OFF(MSVDX_VDMC, VDMC_MACROBLOCK_NUMBER); + state->be_status[pipe] |= VXD_RD_REG_ABS(reg_base, reg_off + p_off); + + /* + * Take DMAC channels 2/3 into consideration to cover + * parser progress on SR1/2 + */ + reg_off = VXD_GET_RPT_REG_OFF(DMAC, DMAC_COUNT, 2); + reg_val = VXD_RD_REG_ABS(reg_base, reg_off + p_off); + state->dmac_status[pipe][0] = VXD_RD_REG_FIELD(reg_val, DMAC, DMAC_COUNT, CNT); + reg_off = VXD_GET_RPT_REG_OFF(DMAC, DMAC_COUNT, 3); + reg_val = VXD_RD_REG_ABS(reg_base, reg_off + p_off); + state->dmac_status[pipe][1] = VXD_RD_REG_FIELD(reg_val, DMAC, DMAC_COUNT, CNT); + } +} + +/* + * Check for the source of the last interrupt. + * + * return 0 if nothing serious happened, + * return -EFAULT if there was a critical interrupt detected. + */ +int vxd_pvdec_check_irq(const void *dev, void __iomem *reg_base, unsigned int irq_status) +{ + if (irq_status & PVDEC_CORE_PVDEC_INT_STAT_HOST_MMU_FAULT_IRQ_MASK) { + unsigned int status0 = + VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_STATUS0); + unsigned int status1 = + VXD_RD_REG(reg_base, IMG_VIDEO_BUS4_MMU, MMU_STATUS1); + + unsigned int addr = VXD_RD_REG_FIELD(status0, IMG_VIDEO_BUS4_MMU, + MMU_STATUS0, MMU_FAULT_ADDR) << 12; + unsigned char reason = VXD_RD_REG_FIELD(status0, IMG_VIDEO_BUS4_MMU, + MMU_STATUS0, MMU_PF_N_RW); + unsigned char requestor = VXD_RD_REG_FIELD(status1, IMG_VIDEO_BUS4_MMU, + MMU_STATUS1, MMU_FAULT_REQ_ID); + unsigned char type = VXD_RD_REG_FIELD(status1, IMG_VIDEO_BUS4_MMU, + MMU_STATUS1, MMU_FAULT_RNW); + unsigned char secure = VXD_RD_REG_FIELD(status0, IMG_VIDEO_BUS4_MMU, + MMU_STATUS0, MMU_SECURE_FAULT); + +#ifdef DEBUG_DECODER_DRIVER + dev_dbg(dev, "%s: MMU Page Fault s0:%08x s1:%08x", __func__, status0, status1); +#endif + + dev_err(dev, "%s: MMU %s fault from %s while %s @ 0x%08X", __func__, + (reason) ? "Page" : "Protection", + (requestor & (0x1)) ? "dmac" : + (requestor & (0x2)) ? "vec" : + (requestor & (0x4)) ? "vdmc" : + (requestor & (0x8)) ? "vdeb" : "unknown source", + (type) ? "reading" : "writing", addr); + + if (secure) + dev_err(dev, "%s: MMU security policy violation detected!", __func__); + + return -EFAULT; + } + + return 0; +} + +/* + * This functions enables the clocks, fetches the core properties, stores them + * in the structure and DISABLES the clocks. Do not call when hardware + * is busy! + */ +int vxd_pvdec_get_props(const void *dev, void __iomem *reg_base, struct vxd_core_props *props) +{ +#ifdef DEBUG_DECODER_DRIVER + unsigned char num_pix_pipes, pipe; +#endif + int ret = pvdec_enable_clocks(reg_base); + + if (ret) { + dev_err(dev, "%s: failed to enable clocks!\n", __func__); + return ret; + } + + ret = pvdec_get_mtx_ram_size(reg_base, &props->mtx_ram_size); + if (ret) { + dev_err(dev, "%s: failed to get MTX ram size!\n", __func__); + return ret; + } + + ret = pvdec_get_properties(reg_base, props); + if (ret) { + dev_err(dev, "%s: failed to get VXD props!\n", __func__); + return ret; + } + + if (pvdec_disable_clocks(reg_base)) + dev_err(dev, "%s: failed to disable clocks!\n", __func__); + +#ifdef DEBUG_DECODER_DRIVER + num_pix_pipes = VXD_NUM_PIX_PIPES(*props); + + /* Warning already raised in pvdec_get_properties() */ + if (unlikely(num_pix_pipes > VXD_MAX_PIPES)) + num_pix_pipes = VXD_MAX_PIPES; + dev_dbg(dev, "%s: core_rev: 0x%08x\n", __func__, props->core_rev); + dev_dbg(dev, "%s: pvdec_core_id: 0x%08x\n", __func__, props->pvdec_core_id); + dev_dbg(dev, "%s: mmu_config0: 0x%08x\n", __func__, props->mmu_config0); + dev_dbg(dev, "%s: mmu_config1: 0x%08x\n", __func__, props->mmu_config1); + dev_dbg(dev, "%s: mtx_ram_size: %u\n", __func__, props->mtx_ram_size); + dev_dbg(dev, "%s: pix max frame: 0x%08x\n", __func__, props->pixel_max_frame_cfg); + + for (pipe = 1; pipe <= num_pix_pipes; ++pipe) + dev_dbg(dev, "%s: pipe %u, 0x%08x, misc 0x%08x\n", + __func__, pipe, props->pixel_pipe_cfg[pipe - 1], + props->pixel_misc_cfg[pipe - 1]); + dev_dbg(dev, "%s: dbg fifo size: %u\n", __func__, props->dbg_fifo_size); +#endif + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_priv.h b/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_priv.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_priv.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_priv.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD PVDEC Private header file + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Amit Makani + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef _VXD_PVDEC_PRIV_H +#define _VXD_PVDEC_PRIV_H +#include + +#include "img_dec_common.h" +#include "vxd_pvdec_regs.h" +#include "vxd_dec.h" + +#ifdef ERROR_RECOVERY_SIMULATION +/* kernel object used to debug. Declared in v4l2_int.c */ +extern struct kobject *vxd_dec_kobject; +extern int disable_fw_irq_value; +extern int g_module_irq; +#endif + +struct vxd_boot_poll_params { + unsigned int msleep_cycles; +}; + +struct vxd_ena_params { + struct vxd_boot_poll_params boot_poll; + + unsigned long fw_buf_size; + unsigned int fw_buf_virt_addr; + /* + * VXD's MMU virtual address of a firmware + * buffer. + */ + unsigned int ptd; /* Shifted physical address of PTD */ + + /* Required for firmware upload via registers. */ + struct { + const unsigned char *buf; /* Firmware blob buffer */ + + } regs_data; + + struct { + unsigned secure : 1; /* Secure flow indicator. */ + unsigned wait_dbg_fifo : 1; /* + * Indicates that fw shall use + * blocking mode when putting logs + * into debug fifo + */ + }; + + /* Structure containing memory staller configuration */ + struct { + unsigned int *data; /* Configuration data array */ + unsigned char size; /* Configuration size in dwords */ + + } mem_staller; + + unsigned int fwwdt_ms; /* Firmware software watchdog timeout value */ + + unsigned int crc; /* HW signatures to be enabled by firmware */ + unsigned int rendec_addr; /* VXD's virtual address of a rendec buffer */ + unsigned short rendec_size; /* Size of a rendec buffer in 4K pages */ +}; + +int vxd_pvdec_init(const void *dev, void __iomem *reg_base); + +int vxd_pvdec_ena(const void *dev, void __iomem *reg_base, + struct vxd_ena_params *ena_params, struct vxd_fw_hdr *hdr, + unsigned int *freq_khz); + +int vxd_pvdec_dis(const void *dev, void __iomem *reg_base); + +int vxd_pvdec_mmu_flush(const void *dev, void __iomem *reg_base); + +int vxd_pvdec_send_msg(const void *dev, void __iomem *reg_base, + unsigned int *msg, unsigned long msg_size, unsigned short msg_id, + struct vxd_dev *ctx); + +int vxd_pvdec_pend_msg_info(const void *dev, void __iomem *reg_base, + unsigned long *size, unsigned short *msg_id, + unsigned char *not_last_msg); + +int vxd_pvdec_recv_msg(const void *dev, void __iomem *reg_base, + unsigned int *buf, unsigned long buf_size, struct vxd_dev *ctx); + +int vxd_pvdec_check_fw_status(const void *dev, void __iomem *reg_base); + +unsigned long vxd_pvdec_peek_mtx_fifo(const void *dev, + void __iomem *reg_base); + +unsigned long vxd_pvdec_read_mtx_fifo(const void *dev, void __iomem *reg_base, + unsigned int *buf, unsigned long size); + +irqreturn_t vxd_pvdec_clear_int(void __iomem *reg_base, unsigned int *irq_status); + +int vxd_pvdec_check_irq(const void *dev, void __iomem *reg_base, + unsigned int irq_status); + +int vxd_pvdec_msg_fit(const void *dev, void __iomem *reg_base, + unsigned long msg_size); + +void vxd_pvdec_get_state(const void *dev, void __iomem *reg_base, + unsigned int num_pipes, struct vxd_hw_state *state); + +int vxd_pvdec_get_props(const void *dev, void __iomem *reg_base, + struct vxd_core_props *props); + +unsigned long vxd_pvdec_get_dbg_fifo_size(void __iomem *reg_base); + +int vxd_pvdec_dump_mtx_ram(const void *dev, void __iomem *reg_base, + unsigned int addr, unsigned int count, unsigned int *buf); + +int vxd_pvdec_dump_mtx_status(const void *dev, void __iomem *reg_base, + unsigned int *array, unsigned int array_size); + +#endif /* _VXD_PVDEC_PRIV_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_regs.h b/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_regs.h --- a/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_pvdec_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,779 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * VXD PVDEC registers header file + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * + * Re-written for upstreamimg + * Sidraya Jayagond + * Prashanth Kumar Amai + */ + +#ifndef VXD_PVDEC_REGS_H +#define VXD_PVDEC_REGS_H + +/* ************************* VXD-specific values *************************** */ +/* 0x10 for code, 0x18 for data. */ +#define PVDEC_MTX_CORE_MEM 0x18 +/* Iteration time out counter for MTX I/0. */ +#define PVDEC_TIMEOUT_COUNTER 1000 +/* Partial frame notification timer divider. */ +#define PVDEC_PFNT_DIV 0 +/* Value returned by register reads when HW enters invalid state (FPGA) */ +#define PVDEC_INVALID_HW_STATE 0x000dead1 + +/* Default core clock for pvdec */ +#define PVDEC_CLK_MHZ_DEFAULT 200 + +/* Offsets of registers groups within VXD. */ +#define PVDEC_PROC_OFFSET 0x0000 +/* 0x34c: Skip DMA registers when running against CSIM (vritual platform) */ +#define PVDEC_PROC_SIZE 0x34C /* 0x3FF */ + +#define PVDEC_CORE_OFFSET 0x0400 +#define PVDEC_CORE_SIZE 0x3FF + +#define MTX_CORE_OFFSET PVDEC_PROC_OFFSET +#define MTX_CORE_SIZE PVDEC_PROC_SIZE + +#define VIDEO_BUS4_MMU_OFFSET 0x1000 +#define VIDEO_BUS4_MMU_SIZE 0x1FF + +#define IMG_VIDEO_BUS4_MMU_OFFSET VIDEO_BUS4_MMU_OFFSET +#define IMG_VIDEO_BUS4_MMU_SIZE VIDEO_BUS4_MMU_SIZE + +#define VLR_OFFSET 0x2000 +#define VLR_SIZE 0x1000 + +/* PVDEC_ENTROPY defined in uapi/vxd_pvdec.h */ + +#define PVDEC_PIXEL_OFFSET 0x4000 +#define PVDEC_PIXEL_SIZE 0x1FF + +/* PVDEC_VEC_BE defined in uapi/vxd_pvdec.h */ + +/* MSVDX_VEC defined in uapi/vxd_pvdec.h */ + +#define MSVDX_VDMC_OFFSET 0x6800 +#define MSVDX_VDMC_SIZE 0x7F + +#define DMAC_OFFSET 0x6A00 +#define DMAC_SIZE 0x1FF + +#define PVDEC_TEST_OFFSET 0xFF00 +#define PVDEC_TEST_SIZE 0xFF + +/* *********************** firmware specific values ************************* */ + +/* layout of COMMS RAM */ + +#define PVDEC_FW_COMMS_HDR_SIZE 0x38 + +#define PVDEC_FW_STATUS_OFFSET 0x00 +#define PVDEC_FW_TASK_STATUS_OFFSET 0x04 +#define PVDEC_FW_ID_OFFSET 0x08 +#define PVDEC_FW_MTXPC_OFFSET 0x0c +#define PVDEC_FW_MSG_COUNTER_OFFSET 0x10 +#define PVDEC_FW_SIGNATURE_OFFSET 0x14 +#define PVDEC_FW_TO_HOST_BUF_CONF_OFFSET 0x18 +#define PVDEC_FW_TO_HOST_RD_IDX_OFFSET 0x1c +#define PVDEC_FW_TO_HOST_WR_IDX_OFFSET 0x20 +#define PVDEC_FW_TO_MTX_BUF_CONF_OFFSET 0x24 +#define PVDEC_FW_TO_MTX_RD_IDX_OFFSET 0x28 +#define PVDEC_FW_FLAGS_OFFSET 0x2c +#define PVDEC_FW_TO_MTX_WR_IDX_OFFSET 0x30 +#define PVDEC_FW_STATE_BUF_CFG_OFFSET 0x34 + +/* firmware status */ + +#define PVDEC_FW_STATUS_PANIC 0x2 +#define PVDEC_FW_STATUS_ASSERT 0x3 +#define PVDEC_FW_STATUS_SO 0x8 + +/* firmware flags */ + +#define PVDEC_FWFLAG_BIG_TO_HOST_BUFFER 0x00000002 +#define PVDEC_FWFLAG_FORCE_FS_FLOW 0x00000004 +#define PVDEC_FWFLAG_DISABLE_WATCHDOGS 0x00000008 +#define PVDEC_FWFLAG_DISABLE_AUTONOMOUS_RESET 0x00000040 +#define PVDEC_FWFLAG_DISABLE_IDLE_GPIO 0x00002000 +#define PVDEC_FWFLAG_ENABLE_ERROR_CONCEALMENT 0x00100000 +#define PVDEC_FWFLAG_DISABLE_GENC_FLUSHING 0x00800000 +#define PVDEC_FWFLAG_FAKE_COMPLETION 0x20000000 +#define PVDEC_FWFLAG_DISABLE_COREWDT_TIMERS 0x01000000 + +/* firmware message header */ + +#define PVDEC_FW_DEVA_GENMSG_OFFSET 0 + +#define PVDEC_FW_DEVA_GENMSG_MSG_ID_MASK 0xFFFF0000 +#define PVDEC_FW_DEVA_GENMSG_MSG_ID_SHIFT 16 + +#define PVDEC_FW_DEVA_GENMSG_MSG_TYPE_MASK 0xFF00 +#define PVDEC_FW_DEVA_GENMSG_MSG_TYPE_SHIFT 8 + +#define PVDEC_FW_DEVA_GENMSG_NOT_LAST_MSG_MASK 0x80 +#define PVDEC_FW_DEVA_GENMSG_NOT_LAST_MSG_SHIFT 7 + +#define PVDEC_FW_DEVA_GENMSG_MSG_SIZE_MASK 0x7F +#define PVDEC_FW_DEVA_GENMSG_MSG_SIZE_SHIFT 0 + +/* firmware init message */ + +#define PVDEC_FW_DEVA_INIT_MSG_WRDS 9 + +#define PVDEC_FW_DEVA_INIT_RENDEC_ADDR0_OFFSET 0xC + +#define PVDEC_FW_DEVA_INIT_RENDEC_SIZE0_OFFSET 0x10 +#define PVDEC_FW_DEVA_INIT_RENDEC_SIZE0_MASK 0xFFFF +#define PVDEC_FW_DEVA_INIT_RENDEC_SIZE0_SHIFT 0 + +#define PVDEC_FW_DEVA_INIT_HEVC_CFG_OFFSET 0x14 +#define PVDEC_FW_DEVA_INIT_HEVC_CFG_MAX_H_FOR_PIPE_WAIT_MASK 0xFFFF0000 +#define PVDEC_FW_DEVA_INIT_HEVC_CFG_MAX_H_FOR_PIPE_WAIT_SHIFT 16 +#define PVDEC_FW_DEVA_INIT_HEVC_CFG_MIN_H_FOR_DUAL_PIPE_MASK 0xFFFF +#define PVDEC_FW_DEVA_INIT_HEVC_CFG_MIN_H_FOR_DUAL_PIPE_SHIFT 0 + +#define PVDEC_FW_DEVA_INIT_SIG_SELECT_OFFSET 0x18 + +#define PVDEC_FW_DEVA_INIT_DBG_DELAYS_OFFSET 0x1C + +#define PVDEC_FW_DEVA_INIT_PFNT_DIV_OFFSET 0x20 +#define PVDEC_FW_DEVA_INIT_PFNT_DIV_MASK 0xFFFF0000 +#define PVDEC_FW_DEVA_INIT_PFNT_DIV_SHIFT 16 + +#define PVDEC_FW_DEVA_INIT_FWWDT_MS_OFFSET 0x20 +#define PVDEC_FW_DEVA_INIT_FWWDT_MS_MASK 0xFFFF +#define PVDEC_FW_DEVA_INIT_FWWDT_MS_SHIFT 0 + +/* firmware message types */ +#define PVDEC_FW_MSG_TYPE_PADDING 0 +#define PVDEC_FW_MSG_TYPE_INIT 0x80 + +/* miscellaneous */ + +#define PVDEC_FW_READY_SIG 0xa5a5a5a5 + +#define PVDEC_FW_COM_BUF_SIZE(cfg) ((cfg) & 0x0000ffff) +#define PVDEC_FW_COM_BUF_OFF(cfg) (((cfg) & 0xffff0000) >> 16) + +/* + * Timer divider calculation macro. + * NOTE: The Timer divider is only 8bit field + * so we set it for 2MHz timer base to cover wider + * range of core frequencies on real platforms (freq > 255MHz) + */ +#define PVDEC_CALC_TIMER_DIV(val) (((val) - 1) / 2) + +#define MTX_CORE_STATUS_ELEMENTS 4 + +#define PVDEC_CORE_MEMSTALLER_ELEMENTS 7 + +/* ********************** PVDEC_CORE registers group ************************ */ + +/* register PVDEC_SOFT_RESET */ +#define PVDEC_CORE_PVDEC_SOFT_RST_OFFSET 0x0000 + +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_PIXEL_PROC_SOFT_RST_MASK 0xFF000000 +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_PIXEL_PROC_SOFT_RST_SHIFT 24 + +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_ENTROPY_SOFT_RST_MASK 0x00FF0000 +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_ENTROPY_SOFT_RST_SHIFT 16 + +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_MMU_SOFT_RST_MASK 0x00000002 +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_MMU_SOFT_RST_SHIFT 1 + +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_SOFT_RST_MASK 0x00000001 +#define PVDEC_CORE_PVDEC_SOFT_RST_PVDEC_SOFT_RST_SHIFT 0 + +/* register PVDEC_HOST_INTERRUPT_STATUS */ +#define PVDEC_CORE_PVDEC_INT_STAT_OFFSET 0x0010 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_SYS_WDT_MASK 0x10000000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_SYS_WDT_SHIFT 28 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_READ_TIMEOUT_PROC_IRQ_MASK 0x08000000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_READ_TIMEOUT_PROC_IRQ_SHIFT 27 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_COMMAND_TIMEOUT_PROC_IRQ_MASK 0x04000000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_COMMAND_TIMEOUT_PROC_IRQ_SHIFT 26 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_READ_TIMEOUT_HOST_IRQ_MASK 0x02000000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_READ_TIMEOUT_HOST_IRQ_SHIFT 25 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_COMMAND_TIMEOUT_HOST_IRQ_MASK 0x01000000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_COMMAND_TIMEOUT_HOST_IRQ_SHIFT 24 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_PROC_GPIO_IRQ_MASK 0x00200000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_PROC_GPIO_IRQ_SHIFT 21 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_PROC_IRQ_MASK 0x00100000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_PROC_IRQ_SHIFT 20 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_MMU_FAULT_IRQ_MASK 0x00010000 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_MMU_FAULT_IRQ_SHIFT 16 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_PIXEL_PROCESSING_IRQ_MASK 0x0000FF00 +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_PIXEL_PROCESSING_IRQ_SHIFT 8 + +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_ENTROPY_PIPE_IRQ_MASK 0x000000FF +#define PVDEC_CORE_PVDEC_INT_STAT_HOST_ENTROPY_PIPE_IRQ_SHIFT 0 + +/* register PVDEC_INTERRUPT_CLEAR */ +#define PVDEC_CORE_PVDEC_INT_CLEAR_OFFSET 0x0014 + +#define PVDEC_CORE_PVDEC_INT_CLEAR_IRQ_CLEAR_MASK 0xFFFF0000 +#define PVDEC_CORE_PVDEC_INT_CLEAR_IRQ_CLEAR_SHIFT 16 + +/* register PVDEC_HOST_INTERRUPT_ENABLE */ +#define PVDEC_CORE_PVDEC_HOST_INT_ENA_OFFSET 0x0018 + +#define PVDEC_CORE_PVDEC_HOST_INT_ENA_HOST_IRQ_ENABLE_MASK 0xFFFF0000 +#define PVDEC_CORE_PVDEC_HOST_INT_ENA_HOST_IRQ_ENABLE_SHIFT 16 + +/* Register PVDEC_MAN_CLK_ENABLE */ +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_OFFSET 0x0040 + +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_PIXEL_PROC_MAN_CLK_ENA_MASK 0xFF000000 +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_PIXEL_PROC_MAN_CLK_ENA_SHIFT 24 + +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_ENTROPY_PIPE_MAN_CLK_ENA_MASK 0x00FF0000 +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_ENTROPY_PIPE_MAN_CLK_ENA_SHIFT 16 + +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_MEM_MAN_CLK_ENA_MASK 0x00000100 +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_MEM_MAN_CLK_ENA_SHIFT 8 + +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_PVDEC_REG_MAN_CLK_ENA_MASK 0x00000010 +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_PVDEC_REG_MAN_CLK_ENA_SHIFT 4 + +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_PROC_MAN_CLK_ENA_MASK 0x00000002 +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_PROC_MAN_CLK_ENA_SHIFT 1 + +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_CORE_MAN_CLK_ENA_MASK 0x00000001 +#define PVDEC_CORE_PVDEC_MAN_CLK_ENA_CORE_MAN_CLK_ENA_SHIFT 0 + +/* register PVDEC_HOST_PIPE_SELECT */ +#define PVDEC_CORE_PVDEC_HOST_PIPE_SELECT_OFFSET 0x0060 + +#define PVDEC_CORE_PVDEC_HOST_PIPE_SELECT_PIPE_SEL_MASK 0x0000000F +#define PVDEC_CORE_PVDEC_HOST_PIPE_SELECT_PIPE_SEL_SHIFT 0 + +/* register PROC_DEBUG */ +#define PVDEC_CORE_PROC_DEBUG_OFFSET 0x0100 + +#define PVDEC_CORE_PROC_DEBUG_MTX_LAST_RAM_BANK_SIZE_MASK 0xFF000000 +#define PVDEC_CORE_PROC_DEBUG_MTX_LAST_RAM_BANK_SIZE_SHIFT 24 + +#define PVDEC_CORE_PROC_DEBUG_MTX_RAM_BANK_SIZE_MASK 0x000F0000 +#define PVDEC_CORE_PROC_DEBUG_MTX_RAM_BANK_SIZE_SHIFT 16 + +#define PVDEC_CORE_PROC_DEBUG_MTX_RAM_BANKS_MASK 0x00000F00 +#define PVDEC_CORE_PROC_DEBUG_MTX_RAM_BANKS_SHIFT 8 + +#define PVDEC_CORE_PROC_DEBUG_MTX_RAM_NEW_REPRESENTATION_MASK 0x00000080 +#define PVDEC_CORE_PROC_DEBUG_MTX_RAM_NEW_REPRESENTATION_SHIFT 7 + +#define PVDEC_CORE_PROC_DEBUG_PROC_DBG_GPIO_OUT_MASK 0x00000018 +#define PVDEC_CORE_PROC_DEBUG_PROC_DBG_GPIO_OUT_SHIFT 3 + +#define PVDEC_CORE_PROC_DEBUG_PROC_DBG_IS_SLAVE_MASK 0x00000004 +#define PVDEC_CORE_PROC_DEBUG_PROC_DBG_IS_SLAVE_SHIFT 2 + +#define PVDEC_CORE_PROC_DEBUG_PROC_DBG_GPIO_IN_MASK 0x00000003 +#define PVDEC_CORE_PROC_DEBUG_PROC_DBG_GPIO_IN_SHIFT 0 + +/* register PROC_DMAC_CONTROL */ +#define PVDEC_CORE_PROC_DMAC_CONTROL_OFFSET 0x0104 + +#define PVDEC_CORE_PROC_DMAC_CONTROL_BOOT_ON_DMA_CH0_MASK 0x80000000 +#define PVDEC_CORE_PROC_DMAC_CONTROL_BOOT_ON_DMA_CH0_SHIFT 31 + +/* register PROC_DEBUG_FIFO */ +#define PVDEC_CORE_PROC_DBG_FIFO_OFFSET 0x0108 + +#define PVDEC_CORE_PROC_DBG_FIFO_PROC_DBG_FIFO_MASK 0xFFFFFFFF +#define PVDEC_CORE_PROC_DBG_FIFO_PROC_DBG_FIFO_SHIFT 0 + +/* register PROC_DEBUG_FIFO_CTRL_0 */ +#define PVDEC_CORE_PROC_DBG_FIFO_CTRL0_OFFSET 0x010C + +#define PVDEC_CORE_PROC_DBG_FIFO_CTRL0_PROC_DBG_FIFO_COUNT_MASK 0xFFFF0000 +#define PVDEC_CORE_PROC_DBG_FIFO_CTRL0_PROC_DBG_FIFO_COUNT_SHIFT 16 + +#define PVDEC_CORE_PROC_DBG_FIFO_CTRL0_PROC_DBG_FIFO_SIZE_MASK 0x0000FFFF +#define PVDEC_CORE_PROC_DBG_FIFO_CTRL0_PROC_DBG_FIFO_SIZE_SHIFT 0 + +/* register PVDEC_CORE_ID */ +#define PVDEC_CORE_PVDEC_CORE_ID_OFFSET 0x0230 + +#define PVDEC_CORE_PVDEC_CORE_ID_GROUP_ID_MASK 0xFF000000 +#define PVDEC_CORE_PVDEC_CORE_ID_GROUP_ID_SHIFT 24 + +#define PVDEC_CORE_PVDEC_CORE_ID_CORE_ID_MASK 0x00FF0000 +#define PVDEC_CORE_PVDEC_CORE_ID_CORE_ID_SHIFT 16 + +#define PVDEC_CORE_PVDEC_CORE_ID_PVDEC_CORE_CONFIG_MASK 0x0000FFFF +#define PVDEC_CORE_PVDEC_CORE_ID_PVDEC_CORE_CONFIG_SHIFT 0 + +#define PVDEC_CORE_PVDEC_CORE_ID_ENT_PIPES_MASK 0x0000000F +#define PVDEC_CORE_PVDEC_CORE_ID_ENT_PIPES_SHIFT 0 + +#define PVDEC_CORE_PVDEC_CORE_ID_PIX_PIPES_MASK 0x000000F0 +#define PVDEC_CORE_PVDEC_CORE_ID_PIX_PIPES_SHIFT 4 + +/* register PVDEC_CORE_REV */ +#define PVDEC_CORE_PVDEC_CORE_REV_OFFSET 0x0240 + +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_DESIGNER_MASK 0xFF000000 +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_DESIGNER_SHIFT 24 + +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_MAJOR_REV_MASK 0x00FF0000 +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_MAJOR_REV_SHIFT 16 + +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_MINOR_REV_MASK 0x0000FF00 +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_MINOR_REV_SHIFT 8 + +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_MAINT_REV_MASK 0x000000FF +#define PVDEC_CORE_PVDEC_CORE_REV_PVDEC_MAINT_REV_SHIFT 0 + +/* *********************** MTX_CORE registers group ************************* */ + +/* register MTX_ENABLE */ +#define MTX_CORE_MTX_ENABLE_OFFSET 0x0000 + +/* register MTX_SYSC_TXTIMER. Note: it's not defined in PVDEC TRM. */ +#define MTX_CORE_MTX_SYSC_TXTIMER_OFFSET 0x0010 + +/* register MTX_KICKI */ +#define MTX_CORE_MTX_KICKI_OFFSET 0x0088 + +#define MTX_CORE_MTX_KICKI_MTX_KICKI_MASK 0x0000FFFF +#define MTX_CORE_MTX_KICKI_MTX_KICKI_SHIFT 0 + +/* register MTX_FAULT0 */ +#define MTX_CORE_MTX_FAULT0_OFFSET 0x0090 + +/* register MTX_REGISTER_READ_WRITE_DATA */ +#define MTX_CORE_MTX_REG_READ_WRITE_DATA_OFFSET 0x00F8 + +/* register MTX_REGISTER_READ_WRITE_REQUEST */ +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_OFFSET 0x00FC + +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_DREADY_MASK 0x80000000 +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_DREADY_SHIFT 31 + +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_RNW_MASK 0x00010000 +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_RNW_SHIFT 16 + +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_RSPECIFIER_MASK 0x00000070 +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_RSPECIFIER_SHIFT 4 + +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_USPECIFIER_MASK 0x0000000F +#define MTX_CORE_MTX_REG_READ_WRITE_REQUEST_MTX_USPECIFIER_SHIFT 0 + +/* register MTX_RAM_ACCESS_DATA_EXCHANGE */ +#define MTX_CORE_MTX_RAM_ACCESS_DATA_EXCHANGE_OFFSET 0x0100 + +/* register MTX_RAM_ACCESS_DATA_TRANSFER */ +#define MTX_CORE_MTX_RAM_ACCESS_DATA_TRANSFER_OFFSET 0x0104 + +/* register MTX_RAM_ACCESS_CONTROL */ +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_OFFSET 0x0108 + +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCMID_MASK 0x0FF00000 +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCMID_SHIFT 20 + +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCM_ADDR_MASK 0x000FFFFC +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCM_ADDR_SHIFT 2 + +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCMAI_MASK 0x00000002 +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCMAI_SHIFT 1 + +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCMR_MASK 0x00000001 +#define MTX_CORE_MTX_RAM_ACCESS_CONTROL_MTX_MCMR_SHIFT 0 + +/* register MTX_RAM_ACCESS_STATUS */ +#define MTX_CORE_MTX_RAM_ACCESS_STATUS_OFFSET 0x010C + +#define MTX_CORE_MTX_RAM_ACCESS_STATUS_MTX_MTX_MCM_STAT_MASK 0x00000001 +#define MTX_CORE_MTX_RAM_ACCESS_STATUS_MTX_MTX_MCM_STAT_SHIFT 0 + +/* register MTX_SOFT_RESET */ +#define MTX_CORE_MTX_SOFT_RESET_OFFSET 0x0200 + +#define MTX_CORE_MTX_SOFT_RESET_MTX_RESET_MASK 0x00000001 +#define MTX_CORE_MTX_SOFT_RESET_MTX_RESET_SHIFT 0 + +/* register MTX_SYSC_TIMERDIV */ +#define MTX_CORE_MTX_SYSC_TIMERDIV_OFFSET 0x0208 + +#define MTX_CORE_MTX_SYSC_TIMERDIV_TIMER_EN_MASK 0x00010000 +#define MTX_CORE_MTX_SYSC_TIMERDIV_TIMER_EN_SHIFT 16 + +#define MTX_CORE_MTX_SYSC_TIMERDIV_TIMER_DIV_MASK 0x000000FF +#define MTX_CORE_MTX_SYSC_TIMERDIV_TIMER_DIV_SHIFT 0 + +/* register MTX_SYSC_CDMAA */ +#define MTX_CORE_MTX_SYSC_CDMAA_OFFSET 0x0344 + +#define MTX_CORE_MTX_SYSC_CDMAA_CDMAA_ADDRESS_MASK 0x03FFFFFC +#define MTX_CORE_MTX_SYSC_CDMAA_CDMAA_ADDRESS_SHIFT 2 + +/* register MTX_SYSC_CDMAC */ +#define MTX_CORE_MTX_SYSC_CDMAC_OFFSET 0x0340 + +#define MTX_CORE_MTX_SYSC_CDMAC_BURSTSIZE_MASK 0x07000000 +#define MTX_CORE_MTX_SYSC_CDMAC_BURSTSIZE_SHIFT 24 + +#define MTX_CORE_MTX_SYSC_CDMAC_RNW_MASK 0x00020000 +#define MTX_CORE_MTX_SYSC_CDMAC_RNW_SHIFT 17 + +#define MTX_CORE_MTX_SYSC_CDMAC_ENABLE_MASK 0x00010000 +#define MTX_CORE_MTX_SYSC_CDMAC_ENABLE_SHIFT 16 + +#define MTX_CORE_MTX_SYSC_CDMAC_LENGTH_MASK 0x0000FFFF +#define MTX_CORE_MTX_SYSC_CDMAC_LENGTH_SHIFT 0 + +/* register MTX_SYSC_CDMAT */ +#define MTX_CORE_MTX_SYSC_CDMAT_OFFSET 0x0350 + +/* ****************** IMG_VIDEO_BUS4_MMU registers group ******************** */ + +/* register MMU_CONTROL0_ */ +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL0_USE_TILE_STRIDE_PER_CTX_MASK 0x00010000 +#define IMG_VIDEO_BUS4_MMU_MMU_CONTROL0_USE_TILE_STRIDE_PER_CTX_SHIFT 16 + +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_MMU_ENA_EXT_ADDR_MASK 0x00000010 +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_MMU_ENA_EXT_ADDR_SHIFT 4 + +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_UPPER_ADDR_FIXED_MASK 0x00FF0000 +#define IMG_VIDEO_BUS4_MMU_MMU_ADDRESS_CONTROL_UPPER_ADDR_FIXED_SHIFT 16 + +#define IMG_VIDEO_BUS4_MMU_MMU_MEM_EXT_OUTSTANDING_READ_WORDS_MASK 0x0000FFFF +#define IMG_VIDEO_BUS4_MMU_MMU_MEM_EXT_OUTSTANDING_READ_WORDS_SHIFT 0 + +/* *************************** MMU-related values ************************** */ + +/* MMU page size */ + +enum { + VXD_MMU_SOFT_PAGE_SIZE_PAGE_64K = 0x4, + VXD_MMU_SOFT_PAGE_SIZE_PAGE_16K = 0x2, + VXD_MMU_SOFT_PAGE_SIZE_PAGE_4K = 0x0, + VXD_MMU_SOFT_PAGE_SIZE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* MMU PTD entry flags */ +enum { + VXD_MMU_PTD_FLAG_NONE = 0x0, + VXD_MMU_PTD_FLAG_VALID = 0x1, + VXD_MMU_PTD_FLAG_WRITE_ONLY = 0x2, + VXD_MMU_PTD_FLAG_READ_ONLY = 0x4, + VXD_MMU_PTD_FLAG_CACHE_COHERENCY = 0x8, + VXD_MMU_PTD_FLAG_FORCE32BITS = 0x7FFFFFFFU +}; + +/* ********************* PVDEC_PIXEL registers group *********************** */ + +/* register PVDEC_PIXEL_PIXEL_CONTROL_0 */ +#define PVDEC_PIXEL_PIXEL_CONTROL_0_OFFSET 0x0004 + +#define PVDEC_PIXEL_PIXEL_CONTROL_0_DMAC_CH_SEL_FOR_MTX_MASK 0x0000000E +#define PVDEC_PIXEL_PIXEL_CONTROL_0_DMAC_CH_SEL_FOR_MTX_SHIFT 1 + +#define PVDEC_PIXEL_PIXEL_CONTROL_0_PROC_DMAC_CH0_SEL_MASK 0x00000001 +#define PVDEC_PIXEL_PIXEL_CONTROL_0_PROC_DMAC_CH0_SEL_SHIFT 0 + +/* register PVDEC_PIXEL_MAN_CLK_ENABLE */ +#define PVDEC_PIXEL_PIXEL_MAN_CLK_ENA_OFFSET 0x0020 + +#define PVDEC_PIXEL_PIXEL_MAN_CLK_ENA_PIXEL_REG_MAN_CLK_ENA_MASK 0x00020000 +#define PVDEC_PIXEL_PIXEL_MAN_CLK_ENA_PIXEL_REG_MAN_CLK_ENA_SHIFT 17 + +#define PVDEC_PIXEL_PIXEL_MAN_CLK_ENA_PIXEL_DMAC_MAN_CLK_ENA_MASK 0x00010000 +#define PVDEC_PIXEL_PIXEL_MAN_CLK_ENA_PIXEL_DMAC_MAN_CLK_ENA_SHIFT 16 + +/* register PIXEL_PIPE_CONFIG */ +#define PVDEC_PIXEL_PIXEL_PIPE_CONFIG_OFFSET 0x00C0 + +/* register PIXEL_MISC_CONFIG */ +#define PVDEC_PIXEL_PIXEL_MISC_CONFIG_OFFSET 0x00C4 + +/* register MAX_FRAME_CONFIG */ +#define PVDEC_PIXEL_MAX_FRAME_CONFIG_OFFSET 0x00C8 + +/* ********************* PVDEC_ENTROPY registers group ********************* */ + +/* Register PVDEC_ENTROPY_MAN_CLK_ENABLE */ +#define PVDEC_ENTROPY_ENTROPY_MAN_CLK_ENA_OFFSET 0x0020 + +/* Register PVDEC_ENTROPY_LAST_LAST_MB */ +#define PVDEC_ENTROPY_ENTROPY_LAST_MB_OFFSET 0x00BC + +/* ********************* PVDEC_VEC_BE registers group ********************** */ + +/* Register PVDEC_VEC_BE_VEC_BE_STATUS */ +#define PVDEC_VEC_BE_VEC_BE_STATUS_OFFSET 0x0018 + +/* ********************* MSVDX_VEC registers group ************************* */ + +/* Register MSVDX_VEC_VEC_ENTDEC_INFORMATION */ +#define MSVDX_VEC_VEC_ENTDEC_INFORMATION_OFFSET 0x00AC + +/* ********************* MSVDX_VDMC registers group ************************ */ + +/* Register MSVDX_VDMC_VDMC_MACROBLOCK_NUMBER */ +#define MSVDX_VDMC_VDMC_MACROBLOCK_NUMBER_OFFSET 0x0048 + +/* ************************** DMAC registers group ************************* */ + +/* register DMAC_SETUP */ +#define DMAC_DMAC_SETUP_OFFSET 0x0000 +#define DMAC_DMAC_SETUP_STRIDE 32 +#define DMAC_DMAC_SETUP_NO_ENTRIES 6 + +/* register DMAC_COUNT */ +#define DMAC_DMAC_COUNT_OFFSET 0x0004 +#define DMAC_DMAC_COUNT_STRIDE 32 +#define DMAC_DMAC_COUNT_NO_ENTRIES 6 + +#define DMAC_DMAC_COUNT_LIST_IEN_MASK 0x80000000 +#define DMAC_DMAC_COUNT_LIST_IEN_SHIFT 31 + +#define DMAC_DMAC_COUNT_BSWAP_MASK 0x40000000 +#define DMAC_DMAC_COUNT_BSWAP_SHIFT 30 + +#define DMAC_DMAC_COUNT_TRANSFER_IEN_MASK 0x20000000 +#define DMAC_DMAC_COUNT_TRANSFER_IEN_SHIFT 29 + +#define DMAC_DMAC_COUNT_PW_MASK 0x18000000 +#define DMAC_DMAC_COUNT_PW_SHIFT 27 + +#define DMAC_DMAC_COUNT_DIR_MASK 0x04000000 +#define DMAC_DMAC_COUNT_DIR_SHIFT 26 + +#define DMAC_DMAC_COUNT_PI_MASK 0x03000000 +#define DMAC_DMAC_COUNT_PI_SHIFT 24 + +#define DMAC_DMAC_COUNT_LIST_FIN_CTL_MASK 0x00400000 +#define DMAC_DMAC_COUNT_LIST_FIN_CTL_SHIFT 22 + +#define DMAC_DMAC_COUNT_DREQ_MASK 0x00100000 +#define DMAC_DMAC_COUNT_DREQ_SHIFT 20 + +#define DMAC_DMAC_COUNT_SRST_MASK 0x00080000 +#define DMAC_DMAC_COUNT_SRST_SHIFT 19 + +#define DMAC_DMAC_COUNT_LIST_EN_MASK 0x00040000 +#define DMAC_DMAC_COUNT_LIST_EN_SHIFT 18 + +#define DMAC_DMAC_COUNT_ENABLE_2D_MODE_MASK 0x00020000 +#define DMAC_DMAC_COUNT_ENABLE_2D_MODE_SHIFT 17 + +#define DMAC_DMAC_COUNT_EN_MASK 0x00010000 +#define DMAC_DMAC_COUNT_EN_SHIFT 16 + +#define DMAC_DMAC_COUNT_CNT_MASK 0x0000FFFF +#define DMAC_DMAC_COUNT_CNT_SHIFT 0 + +/* register DMAC_PERIPH */ +#define DMAC_DMAC_PERIPH_OFFSET 0x0008 +#define DMAC_DMAC_PERIPH_STRIDE 32 +#define DMAC_DMAC_PERIPH_NO_ENTRIES 6 + +#define DMAC_DMAC_PERIPH_ACC_DEL_MASK 0xE0000000 +#define DMAC_DMAC_PERIPH_ACC_DEL_SHIFT 29 + +#define DMAC_DMAC_PERIPH_INCR_MASK 0x08000000 +#define DMAC_DMAC_PERIPH_INCR_SHIFT 27 + +#define DMAC_DMAC_PERIPH_BURST_MASK 0x07000000 +#define DMAC_DMAC_PERIPH_BURST_SHIFT 24 + +#define DMAC_DMAC_PERIPH_EXT_BURST_MASK 0x000F0000 +#define DMAC_DMAC_PERIPH_EXT_BURST_SHIFT 16 + +#define DMAC_DMAC_PERIPH_EXT_SA_MASK 0x0000000F +#define DMAC_DMAC_PERIPH_EXT_SA_SHIFT 0 + +/* register DMAC_IRQ_STAT */ +#define DMAC_DMAC_IRQ_STAT_OFFSET 0x000C +#define DMAC_DMAC_IRQ_STAT_STRIDE 32 +#define DMAC_DMAC_IRQ_STAT_NO_ENTRIES 6 + +/* register DMAC_PERIPHERAL_ADDR */ +#define DMAC_DMAC_PERIPH_ADDR_OFFSET 0x0014 +#define DMAC_DMAC_PERIPH_ADDR_STRIDE 32 +#define DMAC_DMAC_PERIPH_ADDR_NO_ENTRIES 6 + +#define DMAC_DMAC_PERIPH_ADDR_ADDR_MASK 0x007FFFFF +#define DMAC_DMAC_PERIPH_ADDR_ADDR_SHIFT 0 + +/* register DMAC_PER_HOLD */ +#define DMAC_DMAC_PER_HOLD_OFFSET 0x0018 +#define DMAC_DMAC_PER_HOLD_STRIDE 32 +#define DMAC_DMAC_PER_HOLD_NO_ENTRIES 6 + +#define DMAC_DMAC_PER_HOLD_PER_HOLD_MASK 0x0000001F +#define DMAC_DMAC_PER_HOLD_PER_HOLD_SHIFT 0 + +#define DMAC_DMAC_SOFT_RESET_OFFSET 0x00C0 + +/* ************************** DMAC-related values *************************** */ + +/* + * This type defines whether the peripheral address is static or + * auto-incremented. (see the TRM "Transfer Sequence Linked-list - INCR") + */ +enum { + DMAC_INCR_OFF = 0, /* No action, no increment. */ + DMAC_INCR_ON = 1, /* Generate address increment. */ + DMAC_INCR_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Burst size settings (see the TRM "Transfer Sequence Linked-list - BURST"). */ +enum { + DMAC_BURST_0 = 0x0, /* burst size of 0 */ + DMAC_BURST_1 = 0x1, /* burst size of 1 */ + DMAC_BURST_2 = 0x2, /* burst size of 2 */ + DMAC_BURST_3 = 0x3, /* burst size of 3 */ + DMAC_BURST_4 = 0x4, /* burst size of 4 */ + DMAC_BURST_5 = 0x5, /* burst size of 5 */ + DMAC_BURST_6 = 0x6, /* burst size of 6 */ + DMAC_BURST_7 = 0x7, /* burst size of 7 */ + DMAC_BURST_8 = 0x8, /* burst size of 8 */ + DMAC_BURST_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Extended burst size settings (see TRM "Transfer Sequence Linked-list - + * EXT_BURST"). + */ +enum { + DMAC_EXT_BURST_0 = 0x0, /* no extension */ + DMAC_EXT_BURST_1 = 0x1, /* extension of 8 */ + DMAC_EXT_BURST_2 = 0x2, /* extension of 16 */ + DMAC_EXT_BURST_3 = 0x3, /* extension of 24 */ + DMAC_EXT_BURST_4 = 0x4, /* extension of 32 */ + DMAC_EXT_BURST_5 = 0x5, /* extension of 40 */ + DMAC_EXT_BURST_6 = 0x6, /* extension of 48 */ + DMAC_EXT_BURST_7 = 0x7, /* extension of 56 */ + DMAC_EXT_BURST_8 = 0x8, /* extension of 64 */ + DMAC_EXT_BURST_9 = 0x9, /* extension of 72 */ + DMAC_EXT_BURST_10 = 0xa, /* extension of 80 */ + DMAC_EXT_BURST_11 = 0xb, /* extension of 88 */ + DMAC_EXT_BURST_12 = 0xc, /* extension of 96 */ + DMAC_EXT_BURST_13 = 0xd, /* extension of 104 */ + DMAC_EXT_BURST_14 = 0xe, /* extension of 112 */ + DMAC_EXT_BURST_15 = 0xf, /* extension of 120 */ + DMAC_EXT_BURST_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Transfer direction. */ +enum { + DMAC_MEM_TO_VXD = 0x0, + DMAC_VXD_TO_MEM = 0x1, + DMAC_VXD_TO_FORCE32BITS = 0x7FFFFFFFU +}; + +/* How much to increment the peripheral address. */ +enum { + DMAC_PI_1 = 0x2, /* increment by 1 */ + DMAC_PI_2 = 0x1, /* increment by 2 */ + DMAC_PI_4 = 0x0, /* increment by 4 */ + DMAC_PI_FORCE32BITS = 0x7FFFFFFFU +}; + +/* Peripheral width settings (see TRM "Transfer Sequence Linked-list - PW"). */ +enum { + DMAC_PWIDTH_32_BIT = 0x0, /* Peripheral width 32-bit. */ + DMAC_PWIDTH_16_BIT = 0x1, /* Peripheral width 16-bit. */ + DMAC_PWIDTH_8_BIT = 0x2, /* Peripheral width 8-bit. */ + DMAC_PWIDTH_FORCE32BITS = 0x7FFFFFFFU +}; + +/* ******************************* macros ********************************** */ + +#ifdef PVDEC_SINGLETHREADED_IO +/* Write to the register */ +#define VXD_WR_REG_ABS(base, addr, val) \ + ({ spin_lock_irqsave(&pvdec_irq_lock, pvdec_irq_flags); \ + iowrite32((val), (addr) + (base)); \ + spin_unlock_irqrestore(&pvdec_irq_lock, (unsigned long)pvdec_irq_flags); }) + +/* Read the register */ +#define VXD_RD_REG_ABS(base, addr) \ + ({ unsigned int reg; \ + spin_lock_irqsave(&pvdec_irq_lock, pvdec_irq_flags); \ + reg = ioread32((addr) + (base)); \ + spin_unlock_irqrestore(&pvdec_irq_lock, (unsigned long)pvdec_irq_flags); \ + reg; }) +#else /* ndef PVDEC_SINGLETHREADED_IO */ + +/* Write to the register */ +#define VXD_WR_REG_ABS(base, addr, val) \ + (iowrite32((val), (addr) + (base))) + +/* Read the register */ +#define VXD_RD_REG_ABS(base, addr) \ + (ioread32((addr) + (base))) + +#endif + +/* Get offset of a register */ +#define VXD_GET_REG_OFF(group, reg) \ + (group ## _OFFSET + group ## _ ## reg ## _OFFSET) + +/* Get offset of a repated register */ +#define VXD_GET_RPT_REG_OFF(group, reg, index) \ + (VXD_GET_REG_OFF(group, reg) + ((index) * group ## _ ## reg ## _STRIDE)) + +/* Extract field from a register */ +#define VXD_RD_REG_FIELD(val, group, reg, field) \ + (((val) & group ## _ ## reg ## _ ## field ## _MASK) >> \ + group ## _ ## reg ## _ ## field ## _SHIFT) + +/* Shift provided value by number of bits relevant to register specification */ +#define VXD_ENC_REG_FIELD(group, reg, field, val) \ + ((unsigned int)(val) << (group ## _ ## reg ## _ ## field ## _SHIFT)) + +/* Update the field in a register */ +#define VXD_WR_REG_FIELD(reg_val, group, reg, field, val) \ + (((reg_val) & ~(group ## _ ## reg ## _ ## field ## _MASK)) | \ + (VXD_ENC_REG_FIELD(group, reg, field, val) & \ + (group ## _ ## reg ## _ ## field ## _MASK))) + +/* Write to a register */ +#define VXD_WR_REG(base, group, reg, val) \ + VXD_WR_REG_ABS(base, VXD_GET_REG_OFF(group, reg), val) + +/* Write to a repeated register */ +#define VXD_WR_RPT_REG(base, group, reg, val, index) \ + VXD_WR_REG_ABS(base, VXD_GET_RPT_REG_OFF(group, reg, index), val) + +/* Read a register */ +#define VXD_RD_REG(base, group, reg) \ + VXD_RD_REG_ABS(base, VXD_GET_REG_OFF(group, reg)) + +/* Read a repeated register */ +#define VXD_RD_RPT_REG(base, group, reg, index) \ + VXD_RD_REG_ABS(base, VXD_GET_RPT_REG_OFF(group, reg, index)) + +/* Insert word into the message buffer */ +#define VXD_WR_MSG_WRD(buf, msg_type, wrd, val) \ + (((unsigned int *)buf)[(msg_type ## _ ## wrd ## _OFFSET) / sizeof(unsigned int)] = \ + val) + +/* Get a word from the message buffer */ +#define VXD_RD_MSG_WRD(buf, msg_type, wrd) \ + (((unsigned int *)buf)[(msg_type ## _ ## wrd ## _OFFSET) / sizeof(unsigned int)]) + +/* Get offset for pipe register */ +#define VXD_GET_PIPE_OFF(num_pipes, pipe) \ + ((num_pipes) > 1 ? ((pipe) << 16) : 0) + +#endif /* VXD_PVDEC_REGS_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/decoder/vxd_v4l2.c b/drivers/media/platform/vxe-vxd/decoder/vxd_v4l2.c --- a/drivers/media/platform/vxe-vxd/decoder/vxd_v4l2.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/decoder/vxd_v4l2.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,2126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG DEC V4L2 Interface function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Angela Stegmaier + * David Huang + * + * Re-written for upstreaming + * Prashanth Kumar Amai + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ERROR_RECOVERY_SIMULATION +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CAPTURE_CONTIG_ALLOC +#include +#endif + +#include "core.h" +#include "h264fw_data.h" +#include "hevcfw_data.h" +#include "img_dec_common.h" +#include "vxd_pvdec_priv.h" +#include "vxd_dec.h" +#include "img_errors.h" + +#define VXD_DEC_SPIN_LOCK_NAME "vxd-dec" +#define IMG_VXD_DEC_MODULE_NAME "vxd-dec" + +#ifdef ERROR_RECOVERY_SIMULATION +/* This code should be execute only in debug flag */ +/* + * vxd decoder kernel object to create sysfs to debug error recovery and firmware + * watchdog timer. This kernel object will create a directory under /sys/kernel, + * containing two files fw_error_value and disable_fw_irq. + */ +struct kobject *vxd_dec_kobject; + +/* fw_error_value is the variable used to handle fw_error_attr */ +int fw_error_value = VDEC_ERROR_MAX; + +/* irq for the module, stored globally so can be accessed from sysfs */ +int g_module_irq; + +/* + * fw_error_attr. Application can set the value of this attribute, based on the + * firmware error that needs to be reproduced. + */ +struct kobj_attribute fw_error_attr = + __ATTR(fw_error_value, 0660, vxd_sysfs_show, vxd_sysfs_store); + +/* disable_fw_irq_value is variable to handle disable_fw_irq_attr */ +int disable_fw_irq_value; + +/* + * disable_fw_irq_attr. Application can set the value of this attribute. 1 to + * disable irq. 0 to enable irq. + */ +struct kobj_attribute disable_fw_irq_attr = + __ATTR(disable_fw_irq_value, 0660, vxd_sysfs_show, vxd_sysfs_store); + +/* + * Group attribute so that we can create and destroy all of them at once. + */ +struct attribute *attrs[] = { + &fw_error_attr.attr, + &disable_fw_irq_attr.attr, + NULL, /* Terminate list of attributes with NULL */ +}; + +/* + * An unnamed attribute group will put all of the attributes directly in + * the kobject directory. If we specify a name, a sub directory will be + * created for the attributes with the directory being the name of the + * attribute group + */ +struct attribute_group attr_group = { + .attrs = attrs, +}; + +#endif + +static struct heap_config vxd_dec_heap_configs[] = { + { + .type = MEM_HEAP_TYPE_UNIFIED, + .options.unified = { + .gfp_type = __GFP_DMA32 | __GFP_ZERO, + }, + .to_dev_addr = NULL, + }, +}; + +static struct vxd_dec_fmt vxd_dec_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_CAPTURE, + .std = VDEC_STD_UNDEFINED, + .pixfmt = IMG_PIXFMT_420PL12YUV8, + .interleave = PIXEL_UV_ORDER, + .idc = PIXEL_FORMAT_420, + .size_num = 3, + .size_den = 2, + .bytes_pp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_CAPTURE, + .std = VDEC_STD_UNDEFINED, + .pixfmt = IMG_PIXFMT_422PL12YUV8, + .interleave = PIXEL_UV_ORDER, + .idc = PIXEL_FORMAT_422, + .size_num = 2, + .size_den = 1, + .bytes_pp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_TI1210, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_CAPTURE, + .std = VDEC_STD_UNDEFINED, + .pixfmt = IMG_PIXFMT_420PL12YUV10_MSB, + .interleave = PIXEL_UV_ORDER, + .idc = PIXEL_FORMAT_420, + .size_num = 3, + .size_den = 2, + .bytes_pp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_TI1610, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_CAPTURE, + .std = VDEC_STD_UNDEFINED, + .pixfmt = IMG_PIXFMT_422PL12YUV10_MSB, + .interleave = PIXEL_UV_ORDER, + .idc = PIXEL_FORMAT_422, + .size_num = 2, + .size_den = 1, + .bytes_pp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_OUTPUT, + .std = VDEC_STD_H264, + .pixfmt = IMG_PIXFMT_UNDEFINED, + .interleave = PIXEL_INVALID_CI, + .idc = PIXEL_FORMAT_INVALID, + .size_num = 1, + .size_den = 1, + .bytes_pp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_OUTPUT, + .std = VDEC_STD_HEVC, + .pixfmt = IMG_PIXFMT_UNDEFINED, + .interleave = PIXEL_INVALID_CI, + .idc = PIXEL_FORMAT_INVALID, + .size_num = 1, + .size_den = 1, + .bytes_pp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_MJPEG, + .num_planes = 1, + .type = IMG_DEC_FMT_TYPE_OUTPUT, + .std = VDEC_STD_JPEG, + .pixfmt = IMG_PIXFMT_UNDEFINED, + .interleave = PIXEL_INVALID_CI, + .idc = PIXEL_FORMAT_INVALID, + .size_num = 1, + .size_den = 1, + .bytes_pp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .num_planes = 3, + .type = IMG_DEC_FMT_TYPE_CAPTURE, + .std = VDEC_STD_UNDEFINED, + .pixfmt = 86031, + .interleave = PIXEL_UV_ORDER, + .idc = PIXEL_FORMAT_420, + .size_num = 2, + .size_den = 1, + .bytes_pp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .num_planes = 3, + .type = IMG_DEC_FMT_TYPE_CAPTURE, + .std = VDEC_STD_UNDEFINED, + .pixfmt = 81935, + .interleave = PIXEL_UV_ORDER, + .idc = PIXEL_FORMAT_422, + .size_num = 3, + .size_den = 1, + .bytes_pp = 1, + }, +}; + +#ifdef ERROR_RECOVERY_SIMULATION +ssize_t vxd_sysfs_show(struct kobject *vxd_dec_kobject, + struct kobj_attribute *attr, char *buf) + +{ + int var = 0; + + if (strcmp(attr->attr.name, "fw_error_value") == 0) + var = fw_error_value; + + else + var = disable_fw_irq_value; + + return sprintf(buf, "%d\n", var); +} + +ssize_t vxd_sysfs_store(struct kobject *vxd_dec_kobject, + struct kobj_attribute *attr, + const char *buf, unsigned long count) +{ + int var = 0, rv = 0; + + rv = sscanf(buf, "%du", &var); + + if (strcmp(attr->attr.name, "fw_error_value") == 0) { + fw_error_value = var; + } else { + disable_fw_irq_value = var; + /* + * if disable_fw_irq_value is not zero, disable the irq to reproduce + * firmware non responsiveness in vxd_worker. + */ + if (disable_fw_irq_value != 0) { + /* just ignore the irq */ + disable_irq(g_module_irq); + } + } + return sprintf((char *)buf, "%d\n", var); +} +#endif + +static struct vxd_dec_ctx *file2ctx(struct file *file) +{ + return container_of(file->private_data, struct vxd_dec_ctx, fh); +} + +static irqreturn_t soft_thread_irq(int irq, void *dev_id) +{ + struct platform_device *pdev = (struct platform_device *)dev_id; + + if (!pdev) + return IRQ_NONE; + + return vxd_handle_thread_irq(&pdev->dev); +} + +static irqreturn_t hard_isrcb(int irq, void *dev_id) +{ + struct platform_device *pdev = (struct platform_device *)dev_id; + + if (!pdev) + return IRQ_NONE; + + return vxd_handle_irq(&pdev->dev); +} + +static struct vxd_buffer *find_buffer(unsigned int buf_map_id, struct list_head *head) +{ + struct list_head *list; + struct vxd_buffer *buf = NULL; + + list_for_each(list, head) { + buf = list_entry(list, struct vxd_buffer, list); + if (buf->buf_map_id == buf_map_id) + break; + buf = NULL; + } + return buf; +} + +static void return_worker(void *work) +{ + struct vxd_dec_ctx *ctx; + struct vxd_return *res; + struct device *dev; + struct timespec64 time; + int loop; + + work = get_work_buff(work, TRUE); + + res = container_of(work, struct vxd_return, work); + ctx = res->ctx; + dev = ctx->dev->dev; + switch (res->type) { + case VXD_CB_PICT_DECODED: + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + ktime_get_real_ts64(&time); + for (loop = 0; loop < ARRAY_SIZE(ctx->dev->time_drv); loop++) { + if (ctx->dev->time_drv[loop].id == res->buf_map_id) { + ctx->dev->time_drv[loop].end_time = + timespec64_to_ns(&time); +#ifdef DEBUG_DECODER_DRIVER + dev_info(dev, "picture buf decode time is %llu us for buf_map_id 0x%x\n", + div_s64(ctx->dev->time_drv[loop].end_time - + ctx->dev->time_drv[loop].start_time, 1000), + res->buf_map_id); +#endif + break; + } + } + + if (loop == ARRAY_SIZE(ctx->dev->time_drv)) + dev_err(dev, "picture buf decode for buf_map_id x%0x is not measured\n", + res->buf_map_id); + break; + + default: + break; + } + kfree(res->work); + kfree(res); +} + +static void vxd_error_recovery(struct vxd_dec_ctx *ctx) +{ + int ret = -1; + + /* + * In the previous frame decoding fatal error has been detected + * so we need to reload the firmware to make it alive. + */ + pr_debug("Reloading the firmware because of previous error\n"); + vxd_clean_fw_resources(ctx->dev); + ret = vxd_prepare_fw(ctx->dev); + if (ret) + pr_err("Reloading the firmware failed!!"); +} + +static struct vxd_dec_q_data *get_q_data(struct vxd_dec_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return &ctx->q_data[Q_DATA_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return &ctx->q_data[Q_DATA_DST]; + default: + return NULL; + } + return NULL; +} + +static void vxd_return_resource(void *ctx_handle, enum vxd_cb_type type, + unsigned int buf_map_id) +{ + struct vxd_return *res; + struct vxd_buffer *buf = NULL; + struct vb2_v4l2_buffer *vb; + struct vxd_dec_ctx *ctx = (struct vxd_dec_ctx *)ctx_handle; + struct v4l2_event event = {}; + struct device *dev = ctx->dev->dev; + int i; + struct vxd_dec_q_data *q_data; + + if (ctx->aborting) { + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + ctx->aborting = 0; + return; + } + + switch (type) { + case VXD_CB_STRUNIT_PROCESSED: + + buf = find_buffer(buf_map_id, &ctx->out_buffers); + if (!buf) { + dev_err(dev, "Could not locate buf_map_id=0x%x in OUTPUT buffers list\n", + buf_map_id); + break; + } + buf->buffer.vb.field = V4L2_FIELD_NONE; + q_data = get_q_data(ctx, buf->buffer.vb.vb2_buf.vb2_queue->type); + if (!q_data) + return; + + for (i = 0; i < q_data->fmt->num_planes; i++) + vb2_set_plane_payload(&buf->buffer.vb.vb2_buf, i, + ctx->pict_bufcfg.plane_size[i]); + + v4l2_m2m_buf_done(&buf->buffer.vb, VB2_BUF_STATE_DONE); + break; + case VXD_CB_SPS_RELEASE: + break; + case VXD_CB_PPS_RELEASE: + break; + case VXD_CB_PICT_DECODED: + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) + return; + res->ctx = ctx; + res->type = type; + res->buf_map_id = buf_map_id; + + init_work(&res->work, return_worker, HWA_DECODER); + if (!res->work) + return; + + schedule_work(res->work); + + break; + case VXD_CB_PICT_DISPLAY: + buf = find_buffer(buf_map_id, &ctx->cap_buffers); + if (!buf) { + dev_err(dev, "Could not locate buf_map_id=0x%x in CAPTURE buffers list\n", + buf_map_id); + break; + } + buf->reuse = FALSE; + buf->buffer.vb.field = V4L2_FIELD_NONE; + q_data = get_q_data(ctx, buf->buffer.vb.vb2_buf.vb2_queue->type); + if (!q_data) + return; + + for (i = 0; i < q_data->fmt->num_planes; i++) + vb2_set_plane_payload(&buf->buffer.vb.vb2_buf, i, + ctx->pict_bufcfg.plane_size[i]); + + v4l2_m2m_buf_done(&buf->buffer.vb, VB2_BUF_STATE_DONE); + break; + case VXD_CB_PICT_RELEASE: + buf = find_buffer(buf_map_id, &ctx->reuse_queue); + if (buf) { + buf->reuse = TRUE; + list_move_tail(&buf->list, &ctx->cap_buffers); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, &buf->buffer.vb); + break; + } + buf = find_buffer(buf_map_id, &ctx->cap_buffers); + if (!buf) { + dev_err(dev, "Could not locate buf_map_id=0x%x in CAPTURE buffers list\n", + buf_map_id); + + break; + } + buf->reuse = TRUE; + + break; + case VXD_CB_PICT_END: + break; + case VXD_CB_STR_END: + event.type = V4L2_EVENT_EOS; + v4l2_event_queue_fh(&ctx->fh, &event); + if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0) { + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + vb->flags |= V4L2_BUF_FLAG_LAST; + + q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (!q_data) + break; + + for (i = 0; i < q_data->fmt->num_planes; i++) + vb2_set_plane_payload(&vb->vb2_buf, i, 0); + + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE); + } else { + ctx->flag_last = TRUE; + } + break; + case VXD_CB_ERROR_FATAL: + /* + * There has been FW error, so we need to reload the firmware. + */ + vxd_error_recovery(ctx); + + /* + * Just send zero size buffer to v4l2 application, + * informing the error condition. + */ + if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0) { + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (!q_data) + break; + + for (i = 0; i < q_data->fmt->num_planes; i++) + vb2_set_plane_payload(&vb->vb2_buf, i, 0); + + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE); + } else { + ctx->flag_last = TRUE; + } + break; + default: + break; + } +} + +static int vxd_dec_submit_opconfig(struct vxd_dec_ctx *ctx) +{ + int ret = 0; + + if (ctx->stream_created) { + ret = core_stream_set_output_config(ctx->res_str_id, + &ctx->str_opcfg, + &ctx->pict_bufcfg); + if (ret) { + dev_err(ctx->dev->dev, "core_stream_set_output_config failed\n"); + ctx->opconfig_pending = TRUE; + return ret; + } + ctx->opconfig_pending = FALSE; + ctx->stream_configured = TRUE; + } else { + ctx->opconfig_pending = TRUE; + } + return ret; +} + +static int vxd_dec_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vq); + struct vxd_dec_q_data *q_data; + struct vxd_dec_q_data *src_q_data; + int i; + unsigned int hw_nbuffers = 0; + + q_data = get_q_data(ctx, vq->type); + if (!q_data) + return -EINVAL; + + if (*nplanes) { + /* This is being called from CREATEBUFS, perform validation */ + if (*nplanes != q_data->fmt->num_planes) + return -EINVAL; + + for (i = 0; i < *nplanes; i++) { + if (sizes[i] != q_data->size_image[i]) + return -EINVAL; + } + + return 0; + } + + *nplanes = q_data->fmt->num_planes; + + if (!V4L2_TYPE_IS_OUTPUT(vq->type)) { + src_q_data = &ctx->q_data[Q_DATA_SRC]; + if (src_q_data) + hw_nbuffers = get_nbuffers(src_q_data->fmt->std, + q_data->width, + q_data->height, + ctx->max_num_ref_frames); + } + + *nbuffers = max(*nbuffers, hw_nbuffers); + + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->size_image[i]; + + return 0; +} + +static int vxd_dec_buf_prepare(struct vb2_buffer *vb) +{ + struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct device *dev = ctx->dev->dev; + struct vxd_dec_q_data *q_data; + void *sgt; +#ifdef CAPTURE_CONTIG_ALLOC + struct page *new_page; +#else + void *sgl; +#endif + struct sg_table *sgt_new; + void *sgl_new; + int pages; + int nents = 0; + int size = 0; + int plane, num_planes, ret = 0; + struct vxd_buffer *buf = + container_of(vb, struct vxd_buffer, buffer.vb.vb2_buf); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (!q_data) + return -EINVAL; + + num_planes = q_data->fmt->num_planes; + + for (plane = 0; plane < num_planes; plane++) { + if (vb2_plane_size(vb, plane) < q_data->size_image[plane]) { + dev_err(dev, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, plane), + (long)q_data->size_image[plane]); + return -EINVAL; + } + } + + if (buf->mapped) + return 0; + + buf->buf_info.cpu_linear_addr = vb2_plane_vaddr(vb, 0); + buf->buf_info.buf_size = vb2_plane_size(vb, 0); + buf->buf_info.fd = -1; + sgt = vb2_dma_sg_plane_desc(vb, 0); + if (!sgt) { + dev_err(dev, "Could not get sg_table from plane 0\n"); + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) { + ret = core_stream_map_buf_sg(ctx->res_str_id, + VDEC_BUFTYPE_BITSTREAM, + &buf->buf_info, sgt, + &buf->buf_map_id); + if (ret) { + dev_err(dev, "OUTPUT core_stream_map_buf_sg failed\n"); + return ret; + } + + buf->bstr_info.buf_size = q_data->size_image[0]; + buf->bstr_info.cpu_virt_addr = buf->buf_info.cpu_linear_addr; + buf->bstr_info.mem_attrib = + SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE | + SYS_MEMATTRIB_INPUT | SYS_MEMATTRIB_CPU_WRITE; + buf->bstr_info.bufmap_id = buf->buf_map_id; + lst_init(&buf->seq_unit.bstr_seg_list); + lst_init(&buf->pic_unit.bstr_seg_list); + lst_init(&buf->end_unit.bstr_seg_list); + + list_add_tail(&buf->list, &ctx->out_buffers); + } else { + /* Create a single sgt from the plane(s) */ + sgt_new = kmalloc(sizeof(*sgt_new), GFP_KERNEL); + if (!sgt_new) + return -EINVAL; + + for (plane = 0; plane < num_planes; plane++) { + size += ALIGN(vb2_plane_size(vb, plane), PAGE_SIZE); + sgt = vb2_dma_sg_plane_desc(vb, plane); + if (!sgt) { + dev_err(dev, "Could not get sg_table from plane %d\n", plane); + kfree(sgt_new); + return -EINVAL; + } +#ifdef CAPTURE_CONTIG_ALLOC + nents += 1; +#else + nents += sg_nents(img_mmu_get_sgl(sgt)); +#endif + } + buf->buf_info.buf_size = size; + + pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + ret = sg_alloc_table(sgt_new, nents, GFP_KERNEL); + if (ret) { + kfree(sgt_new); + return -EINVAL; + } + sgl_new = img_mmu_get_sgl(sgt_new); + + for (plane = 0; plane < num_planes; plane++) { + sgt = vb2_dma_sg_plane_desc(vb, plane); + if (!sgt) { + dev_err(dev, "Could not get sg_table from plane %d\n", plane); + sg_free_table(sgt_new); + kfree(sgt_new); + return -EINVAL; + } +#ifdef CAPTURE_CONTIG_ALLOC + new_page = phys_to_page(vb2_dma_contig_plane_dma_addr(vb, plane)); + sg_set_page(sgl_new, new_page, ALIGN(vb2_plane_size(vb, plane), + PAGE_SIZE), 0); + sgl_new = sg_next(sgl_new); +#else + sgl = img_mmu_get_sgl(sgt); + + while (sgl) { + sg_set_page(sgl_new, sg_page(sgl), img_mmu_get_sgl_length(sgl), 0); + sgl = sg_next(sgl); + sgl_new = sg_next(sgl_new); + } +#endif + } + + buf->buf_info.pictbuf_cfg = ctx->pict_bufcfg; + ret = core_stream_map_buf_sg(ctx->res_str_id, + VDEC_BUFTYPE_PICTURE, + &buf->buf_info, sgt_new, + &buf->buf_map_id); + sg_free_table(sgt_new); + kfree(sgt_new); + if (ret) { + dev_err(dev, "CAPTURE core_stream_map_buf_sg failed\n"); + return ret; + } + list_add_tail(&buf->list, &ctx->cap_buffers); + } + buf->mapped = TRUE; + buf->reuse = TRUE; + + return 0; +} + +static void vxd_dec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vxd_buffer *buf = + container_of(vb, struct vxd_buffer, buffer.vb.vb2_buf); + struct vxd_dec_q_data *q_data; + int i; + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + } else { + mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2); + if (buf->reuse) { + mutex_unlock(ctx->mutex); + if (ctx->flag_last) { + q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + vbuf->flags |= V4L2_BUF_FLAG_LAST; + + for (i = 0; i < q_data->fmt->num_planes; i++) + vb2_set_plane_payload(&vbuf->vb2_buf, i, 0); + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + } else { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + } + } else { + list_move_tail(&buf->list, &ctx->reuse_queue); + mutex_unlock(ctx->mutex); + } + } +} + +static void vxd_dec_return_all_buffers(struct vxd_dec_ctx *ctx, + struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vb; + unsigned long flags; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (!vb) + break; + + spin_lock_irqsave(ctx->dev->lock, flags); + v4l2_m2m_buf_done(vb, state); + spin_unlock_irqrestore(ctx->dev->lock, (unsigned long)flags); + } +} + +static int vxd_dec_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + int ret = 0; + struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vq); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + ctx->dst_streaming = TRUE; + else + ctx->src_streaming = TRUE; + + if (ctx->dst_streaming && ctx->src_streaming && !ctx->core_streaming) { + if (!ctx->stream_configured) { + vxd_dec_return_all_buffers(ctx, vq, VB2_BUF_STATE_ERROR); + return -EINVAL; + } + ctx->eos = FALSE; + ctx->stop_initiated = FALSE; + ctx->flag_last = FALSE; + ret = core_stream_play(ctx->res_str_id); + if (ret) { + vxd_dec_return_all_buffers(ctx, vq, VB2_BUF_STATE_ERROR); + return ret; + } + ctx->core_streaming = TRUE; + } + + return 0; +} + +static void vxd_dec_stop_streaming(struct vb2_queue *vq) +{ + struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vq); + struct list_head *list; + struct list_head *temp; + struct vxd_buffer *buf = NULL; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + ctx->dst_streaming = FALSE; + else + ctx->src_streaming = FALSE; + + if (ctx->core_streaming) { + core_stream_stop(ctx->res_str_id); + ctx->core_streaming = FALSE; + + core_stream_flush(ctx->res_str_id, TRUE); + } + + /* unmap all the output and capture plane buffers */ + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + list_for_each(list, &ctx->out_buffers) { + buf = list_entry(list, struct vxd_buffer, list); + core_stream_unmap_buf_sg(buf->buf_map_id); + buf->mapped = FALSE; + __list_del_entry(&buf->list); + } + } else { + list_for_each_safe(list, temp, &ctx->reuse_queue) { + buf = list_entry(list, struct vxd_buffer, list); + list_move_tail(&buf->list, &ctx->cap_buffers); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, &buf->buffer.vb); + } + + list_for_each(list, &ctx->cap_buffers) { + buf = list_entry(list, struct vxd_buffer, list); + core_stream_unmap_buf_sg(buf->buf_map_id); + buf->mapped = FALSE; + __list_del_entry(&buf->list); + } + } + + vxd_dec_return_all_buffers(ctx, vq, VB2_BUF_STATE_ERROR); +} + +static struct vb2_ops vxd_dec_video_ops = { + .queue_setup = vxd_dec_queue_setup, + .buf_prepare = vxd_dec_buf_prepare, + .buf_queue = vxd_dec_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vxd_dec_start_streaming, + .stop_streaming = vxd_dec_stop_streaming, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct vxd_dec_ctx *ctx = priv; + struct vxd_dev *vxd = ctx->dev; + int ret = 0; + + /* src_vq */ + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct vxd_buffer); + src_vq->ops = &vxd_dec_video_ops; + src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = vxd->mutex; + src_vq->dev = vxd->v4l2_dev.dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + /* dst_vq */ + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct vxd_buffer); + dst_vq->ops = &vxd_dec_video_ops; +#ifdef CAPTURE_CONTIG_ALLOC + dst_vq->mem_ops = &vb2_dma_contig_memops; +#else + dst_vq->mem_ops = &vb2_dma_sg_memops; +#endif + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = vxd->mutex; + dst_vq->dev = vxd->v4l2_dev.dev; + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return ret; +} + +static int vxd_dec_open(struct file *file) +{ + struct vxd_dev *vxd = video_drvdata(file); + struct vxd_dec_ctx *ctx; + struct vxd_dec_q_data *s_q_data; + int i, ret = 0; + + dev_dbg(vxd->dev, "%s:%d vxd %p\n", __func__, __LINE__, vxd); + + if (vxd->no_fw) { + dev_err(vxd->dev, "Error!! fw binary is not present"); + return -1; + } + + mutex_lock_nested(vxd->mutex, SUBCLASS_BASE); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock(vxd->mutex); + return -ENOMEM; + } + ctx->dev = vxd; + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + + s_q_data = &ctx->q_data[Q_DATA_SRC]; + s_q_data->fmt = &vxd_dec_formats[0]; + s_q_data->width = 1920; + s_q_data->height = 1080; + for (i = 0; i < s_q_data->fmt->num_planes; i++) { + s_q_data->bytesperline[i] = s_q_data->width; + s_q_data->size_image[i] = s_q_data->bytesperline[i] * s_q_data->height; + } + + ctx->q_data[Q_DATA_DST] = *s_q_data; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vxd->m2m_dev, ctx, &queue_init); + if (IS_ERR_VALUE((unsigned long)ctx->fh.m2m_ctx)) { + ret = (long)(ctx->fh.m2m_ctx); + goto exit; + } + + v4l2_fh_add(&ctx->fh); + + ret = idr_alloc_cyclic(vxd->streams, &ctx->stream, VXD_MIN_STREAM_ID, VXD_MAX_STREAM_ID, + GFP_KERNEL); + if (ret < VXD_MIN_STREAM_ID || ret > VXD_MAX_STREAM_ID) { + dev_err(vxd->dev, "%s: stream id creation failed!\n", + __func__); + ret = -EFAULT; + goto exit; + } + + ctx->stream.id = ret; + ctx->stream.ctx = ctx; + + ctx->stream_created = FALSE; + ctx->stream_configured = FALSE; + ctx->src_streaming = FALSE; + ctx->dst_streaming = FALSE; + ctx->core_streaming = FALSE; + ctx->eos = FALSE; + ctx->stop_initiated = FALSE; + ctx->flag_last = FALSE; + + lst_init(&ctx->seg_list); + for (i = 0; i < MAX_SEGMENTS; i++) + lst_add(&ctx->seg_list, &ctx->bstr_segments[i]); + + if (vxd_create_ctx(vxd, ctx)) + goto out_idr_remove; + + ctx->stream.mmu_ctx = ctx->mmu_ctx; + ctx->stream.ptd = ctx->ptd; + + ctx->mutex = kzalloc(sizeof(*ctx->mutex), GFP_KERNEL); + if (!ctx->mutex) { + ret = -ENOMEM; + goto out_idr_remove; + } + mutex_init(ctx->mutex); + + INIT_LIST_HEAD(&ctx->items_done); + INIT_LIST_HEAD(&ctx->reuse_queue); + INIT_LIST_HEAD(&ctx->return_queue); + INIT_LIST_HEAD(&ctx->out_buffers); + INIT_LIST_HEAD(&ctx->cap_buffers); + + mutex_unlock(vxd->mutex); + + return 0; + +out_idr_remove: + idr_remove(vxd->streams, ctx->stream.id); + +exit: + v4l2_fh_exit(&ctx->fh); + get_work_buff(ctx->work, TRUE); + kfree(ctx->work); + kfree(ctx); + mutex_unlock(vxd->mutex); + return ret; +} + +static int vxd_dec_release(struct file *file) +{ + struct vxd_dev *vxd = video_drvdata(file); + struct vxd_dec_ctx *ctx = file2ctx(file); + struct bspp_ddbuf_array_info *fw_sequ = ctx->fw_sequ; + struct bspp_ddbuf_array_info *fw_pps = ctx->fw_pps; + int i, ret = 0; + struct vxd_dec_q_data *s_q_data; + + s_q_data = &ctx->q_data[Q_DATA_SRC]; + + if (ctx->stream_created) { + bspp_stream_destroy(ctx->bspp_context); + + for (i = 0; i < MAX_SEQUENCES; i++) { + core_stream_unmap_buf(fw_sequ[i].ddbuf_info.bufmap_id); + img_mem_free(ctx->mem_ctx, fw_sequ[i].ddbuf_info.buf_id); + } + + if (s_q_data->fmt->std != VDEC_STD_JPEG) { + for (i = 0; i < MAX_PPSS; i++) { + core_stream_unmap_buf(fw_pps[i].ddbuf_info.bufmap_id); + img_mem_free(ctx->mem_ctx, fw_pps[i].ddbuf_info.buf_id); + } + } + core_stream_destroy(ctx->res_str_id); + ctx->stream_created = FALSE; + } + + mutex_lock_nested(vxd->mutex, SUBCLASS_BASE); + + vxd_destroy_ctx(vxd, ctx); + + idr_remove(vxd->streams, ctx->stream.id); + + v4l2_fh_del(&ctx->fh); + + v4l2_fh_exit(&ctx->fh); + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + mutex_destroy(ctx->mutex); + kfree(ctx->mutex); + ctx->mutex = NULL; + + get_work_buff(ctx->work, TRUE); + kfree(ctx->work); + kfree(ctx); + + mutex_unlock(vxd->mutex); + + return ret; +} + +static int vxd_dec_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + strncpy(cap->driver, IMG_VXD_DEC_MODULE_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, IMG_VXD_DEC_MODULE_NAME, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", IMG_VXD_DEC_MODULE_NAME); + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int __enum_fmt(struct v4l2_fmtdesc *f, unsigned int type) +{ + int i, index; + struct vxd_dec_fmt *fmt = NULL; + + index = 0; + for (i = 0; i < ARRAY_SIZE(vxd_dec_formats); ++i) { + if (vxd_dec_formats[i].type & type) { + if (index == f->index) { + fmt = &vxd_dec_formats[i]; + break; + } + index++; + } + } + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static int vxd_dec_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + if (V4L2_TYPE_IS_OUTPUT(f->type)) + return __enum_fmt(f, IMG_DEC_FMT_TYPE_OUTPUT); + + return __enum_fmt(f, IMG_DEC_FMT_TYPE_CAPTURE); +} + +static struct vxd_dec_fmt *find_format(struct v4l2_format *f, unsigned int type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vxd_dec_formats); ++i) { + if (vxd_dec_formats[i].fourcc == f->fmt.pix_mp.pixelformat && + vxd_dec_formats[i].type == type) + return &vxd_dec_formats[i]; + } + return NULL; +} + +static unsigned int get_sizeimage(int w, int h, struct vxd_dec_fmt *fmt, int plane) +{ + switch (fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + return ((plane == 0) ? (w * h) : (w * h / 2)); + case V4L2_PIX_FMT_YUV422M: + return (w * h); + default: + return (w * h * fmt->size_num / fmt->size_den); + } + + return 0; +} + +static unsigned int get_stride(int w, struct vxd_dec_fmt *fmt) +{ + return (ALIGN(w, HW_ALIGN) * fmt->bytes_pp); +} + +/* + * @ Function vxd_get_header_info + * Run bspp stream submit and preparse once before device_run + * To retrieve header information + */ +static int vxd_get_header_info(void *priv) +{ + struct vxd_dec_ctx *ctx = priv; + struct vxd_dev *vxd_dev = ctx->dev; + struct device *dev = vxd_dev->v4l2_dev.dev; + struct vb2_v4l2_buffer *src_vb; + struct vxd_buffer *src_vxdb; + struct vxd_buffer *dst_vxdb; + struct bspp_preparsed_data *preparsed_data; + unsigned int data_size; + int ret; + + /* + * Checking for queued buffer. + * If no next buffer present, do not get information from header. + * Else, get header information and store for later use. + */ + src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + if (!src_vb) { + dev_warn(dev, "get_header_info Next src buffer is null\n"); + return IMG_ERROR_INVALID_PARAMETERS; + } + mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2); + + src_vxdb = container_of(src_vb, struct vxd_buffer, buffer.vb); + /* Setting dst_vxdb to arbitrary value (using src_vb) for now */ + dst_vxdb = container_of(src_vb, struct vxd_buffer, buffer.vb); + + preparsed_data = &dst_vxdb->preparsed_data; + + data_size = vb2_get_plane_payload(&src_vxdb->buffer.vb.vb2_buf, 0); + + ret = bspp_stream_submit_buffer(ctx->bspp_context, + &src_vxdb->bstr_info, + src_vxdb->buf_map_id, + data_size, NULL, + VDEC_BSTRELEMENT_UNSPECIFIED); + if (ret) { + dev_err(dev, "get_header_info bspp_stream_submit_buffer failed %d\n", ret); + return ret; + } + mutex_unlock(ctx->mutex); + + ret = bspp_stream_preparse_buffers(ctx->bspp_context, NULL, 0, + &ctx->seg_list, + preparsed_data, ctx->eos); + if (ret) { + dev_err(dev, "get_header_info bspp_stream_preparse_buffers failed %d\n", ret); + return ret; + } + + if (preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_frame_size.height && + preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_ref_frame_num) { + ctx->height = preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_frame_size.height; + ctx->max_num_ref_frames = + preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_ref_frame_num; + } else { + dev_err(dev, "get_header_info preparsed data is null %d\n", ret); + return ret; + } + + return 0; +} + +static int vxd_dec_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct vxd_dec_ctx *ctx = file2ctx(file); + struct vxd_dec_q_data *q_data; + struct vxd_dev *vxd_dev = ctx->dev; + unsigned int i = 0; + int ret = 0; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->num_planes = q_data->fmt->num_planes; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* The buffer contains compressed image. */ + pix_mp->width = ctx->width; + pix_mp->height = ctx->height; + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage = q_data->size_image[0]; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + /* The buffer contains decoded YUV image. */ + pix_mp->width = ctx->width; + pix_mp->height = ctx->height; + for (i = 0; i < q_data->fmt->num_planes; i++) { + pix_mp->plane_fmt[i].bytesperline = get_stride(pix_mp->width, q_data->fmt); + pix_mp->plane_fmt[i].sizeimage = get_sizeimage + (pix_mp->plane_fmt[i].bytesperline, + ctx->height, q_data->fmt, i); + } + } else { + dev_err(vxd_dev->v4l2_dev.dev, "Wrong V4L2_format type\n"); + return -EINVAL; + } + + return ret; +} + +static int vxd_dec_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vxd_dec_ctx *ctx = file2ctx(file); + struct vxd_dev *vxd_dev = ctx->dev; + struct vxd_dec_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + unsigned int i = 0; + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + fmt = find_format(f, IMG_DEC_FMT_TYPE_OUTPUT); + if (!fmt) { + dev_err(vxd_dev->v4l2_dev.dev, "Unsupported format for source.\n"); + return -EINVAL; + } + /* + * Allocation for worst case input frame size: + * I frame with full YUV size (YUV422) + */ + plane_fmt[0].sizeimage = ALIGN(pix_mp->width, HW_ALIGN) * + ALIGN(pix_mp->height, HW_ALIGN) * 2; + } else { + fmt = find_format(f, IMG_DEC_FMT_TYPE_CAPTURE); + if (!fmt) { + dev_err(vxd_dev->v4l2_dev.dev, "Unsupported format for dest.\n"); + return -EINVAL; + } + for (i = 0; i < fmt->num_planes; i++) { + plane_fmt[i].bytesperline = get_stride(pix_mp->width, fmt); + plane_fmt[i].sizeimage = get_sizeimage(plane_fmt[i].bytesperline, + pix_mp->height, fmt, i); + } + pix_mp->num_planes = fmt->num_planes; + pix_mp->flags = 0; + } + + if (pix_mp->field == V4L2_FIELD_ANY) + pix_mp->field = V4L2_FIELD_NONE; + + return ret; +} + +static int vxd_dec_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp; + struct vxd_dec_ctx *ctx = file2ctx(file); + struct vxd_dev *vxd_dev = ctx->dev; + struct device *dev = vxd_dev->v4l2_dev.dev; + struct vxd_dec_q_data *q_data; + struct vb2_queue *vq; + struct vdec_str_configdata strcfgdata; + int ret = 0; + unsigned char i = 0, j = 0; + + pix_mp = &f->fmt.pix_mp; + + if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + int res = vxd_get_header_info(ctx); + + if (res == 0) + pix_mp->height = ctx->height; + } + + ret = vxd_dec_try_fmt(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + dev_err(dev, "Queue is busy\n"); + return -EBUSY; + } + + q_data = get_q_data(ctx, f->type); + + if (!q_data) + return -EINVAL; + + /* + * saving the original dimensions to pass to gstreamer (to remove the green + * padding on kmsink) + */ + ctx->width_orig = pix_mp->width; + ctx->height_orig = pix_mp->height; + + ctx->width = pix_mp->width; + ctx->height = pix_mp->height; + + q_data->width = pix_mp->width; + q_data->height = pix_mp->height; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + q_data->fmt = find_format(f, IMG_DEC_FMT_TYPE_OUTPUT); + q_data->size_image[0] = pix_mp->plane_fmt[0].sizeimage; + + if (!ctx->stream_created) { + strcfgdata.vid_std = q_data->fmt->std; + + if (strcfgdata.vid_std == VDEC_STD_UNDEFINED) { + dev_err(dev, "Invalid input format\n"); + return -EINVAL; + } + strcfgdata.bstr_format = VDEC_BSTRFORMAT_ELEMENTARY; + strcfgdata.user_str_id = ctx->stream.id; + strcfgdata.update_yuv = FALSE; + strcfgdata.bandwidth_efficient = FALSE; + strcfgdata.disable_mvc = FALSE; + strcfgdata.full_scan = FALSE; + strcfgdata.immediate_decode = TRUE; + strcfgdata.intra_frame_closed_gop = TRUE; + + ret = core_stream_create(ctx, &strcfgdata, &ctx->res_str_id); + if (ret) { + dev_err(dev, "Core stream create failed\n"); + return -EINVAL; + } + ctx->stream_created = TRUE; + if (ctx->opconfig_pending) { + ret = vxd_dec_submit_opconfig(ctx); + if (ret) { + dev_err(dev, "Output config failed\n"); + return -EINVAL; + } + } + + vxd_dec_alloc_bspp_resource(ctx, strcfgdata.vid_std); + ret = bspp_stream_create(&strcfgdata, + &ctx->bspp_context, + ctx->fw_sequ, + ctx->fw_pps); + if (ret) { + dev_err(dev, "BSPP stream create failed %d\n", ret); + return ret; + } + } else if (q_data->fmt != + find_format(f, IMG_DEC_FMT_TYPE_OUTPUT)) { + dev_err(dev, "Input format already set\n"); + return -EBUSY; + } + } else { + q_data->fmt = find_format(f, IMG_DEC_FMT_TYPE_CAPTURE); + for (i = 0; i < q_data->fmt->num_planes; i++) { + q_data->size_image[i] = + get_sizeimage(get_stride(pix_mp->width, q_data->fmt), + ctx->height, q_data->fmt, i); + } + + ctx->str_opcfg.pixel_info.pixfmt = q_data->fmt->pixfmt; + ctx->str_opcfg.pixel_info.chroma_interleave = q_data->fmt->interleave; + ctx->str_opcfg.pixel_info.chroma_fmt = TRUE; + ctx->str_opcfg.pixel_info.chroma_fmt_idc = q_data->fmt->idc; + + if (q_data->fmt->pixfmt == IMG_PIXFMT_420PL12YUV10_MSB || + q_data->fmt->pixfmt == IMG_PIXFMT_422PL12YUV10_MSB) { + ctx->str_opcfg.pixel_info.mem_pkg = PIXEL_BIT10_MSB_MP; + ctx->str_opcfg.pixel_info.bitdepth_y = 10; + ctx->str_opcfg.pixel_info.bitdepth_c = 10; + } else { + ctx->str_opcfg.pixel_info.mem_pkg = PIXEL_BIT8_MP; + ctx->str_opcfg.pixel_info.bitdepth_y = 8; + ctx->str_opcfg.pixel_info.bitdepth_c = 8; + } + + ctx->str_opcfg.force_oold = FALSE; + + ctx->pict_bufcfg.coded_width = pix_mp->width; + ctx->pict_bufcfg.coded_height = pix_mp->height; + ctx->pict_bufcfg.pixel_fmt = q_data->fmt->pixfmt; + for (i = 0; i < pix_mp->num_planes; i++) { + q_data->bytesperline[i] = get_stride(q_data->width, q_data->fmt); + if (q_data->bytesperline[i] < + pix_mp->plane_fmt[0].bytesperline) + q_data->bytesperline[i] = + ALIGN(pix_mp->plane_fmt[0].bytesperline, HW_ALIGN); + pix_mp->plane_fmt[0].bytesperline = + q_data->bytesperline[i]; + ctx->pict_bufcfg.stride[i] = q_data->bytesperline[i]; + } + for (j = i; j < IMG_MAX_NUM_PLANES; j++) { + if ((i - 1) < 0) + i++; + ctx->pict_bufcfg.stride[j] = + q_data->bytesperline[i - 1]; + } + ctx->pict_bufcfg.stride_alignment = HW_ALIGN; + ctx->pict_bufcfg.byte_interleave = FALSE; + for (i = 0; i < pix_mp->num_planes; i++) { + unsigned int plane_size = + get_sizeimage(ctx->pict_bufcfg.stride[i], + ctx->pict_bufcfg.coded_height, + q_data->fmt, i); + ctx->pict_bufcfg.buf_size += ALIGN(plane_size, PAGE_SIZE); + ctx->pict_bufcfg.plane_size[i] = plane_size; + pix_mp->plane_fmt[i].sizeimage = plane_size; + } + if (q_data->fmt->pixfmt == 86031 || + q_data->fmt->pixfmt == 81935) { + /* Handle the v4l2 multi-planar formats */ + ctx->str_opcfg.pixel_info.num_planes = 3; + ctx->pict_bufcfg.packed = FALSE; + for (i = 0; i < pix_mp->num_planes; i++) { + ctx->pict_bufcfg.chroma_offset[i] = + ALIGN(pix_mp->plane_fmt[i].sizeimage, PAGE_SIZE); + ctx->pict_bufcfg.chroma_offset[i] += + (i ? ctx->pict_bufcfg.chroma_offset[i - 1] : 0); + } + } else { + /* IMG Decoders support only multi-planar formats */ + ctx->str_opcfg.pixel_info.num_planes = 2; + ctx->pict_bufcfg.packed = TRUE; + ctx->pict_bufcfg.chroma_offset[0] = 0; + ctx->pict_bufcfg.chroma_offset[1] = 0; + } + + vxd_dec_submit_opconfig(ctx); + } + + return ret; +} + +static int vxd_dec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + if (sub->type != V4L2_EVENT_EOS) + return -EINVAL; + + v4l2_event_subscribe(fh, sub, 0, NULL); + return 0; +} + +static int vxd_dec_try_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) +{ + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + return 0; +} + +static int vxd_dec_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) +{ + struct vxd_dec_ctx *ctx = file2ctx(file); + + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + +#ifdef DEBUG_DECODER_DRIVER + pr_info("%s CMD_STOP\n", __func__); +#endif + /* + * When stop command is received, notify device_run if it is + * scheduled to run, or tell the decoder that eos has + * happened. + */ + mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2); + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("V4L2 src bufs not empty, set a flag to notify device_run\n"); +#endif + ctx->stop_initiated = TRUE; + mutex_unlock(ctx->mutex); + } else { + if (ctx->num_decoding) { +#ifdef DEBUG_DECODER_DRIVER + pr_info("buffers are still being decoded, so just set eos flag\n"); +#endif + ctx->eos = TRUE; + mutex_unlock(ctx->mutex); + } else { + mutex_unlock(ctx->mutex); +#ifdef DEBUG_DECODER_DRIVER + pr_info("All buffers are decoded, so issue dummy stream end\n"); +#endif + vxd_return_resource((void *)ctx, VXD_CB_STR_END, 0); + } + } + + return 0; +} + +static int vxd_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vxd_dec_ctx *ctx = file2ctx(file); + bool def_bounds = true; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + def_bounds = false; + break; + case V4L2_SEL_TGT_CROP: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + def_bounds = false; + break; + default: + return -EINVAL; + } + + if (def_bounds) { + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->width_orig; + s->r.height = ctx->height_orig; + } + + return 0; +} + +static const struct v4l2_ioctl_ops vxd_dec_ioctl_ops = { + .vidioc_querycap = vxd_dec_querycap, + + .vidioc_enum_fmt_vid_cap = vxd_dec_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = vxd_dec_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vxd_dec_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vxd_dec_s_fmt, + + .vidioc_enum_fmt_vid_out = vxd_dec_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = vxd_dec_g_fmt, + .vidioc_try_fmt_vid_out_mplane = vxd_dec_try_fmt, + .vidioc_s_fmt_vid_out_mplane = vxd_dec_s_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = vxd_dec_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_decoder_cmd = vxd_dec_try_cmd, + .vidioc_decoder_cmd = vxd_dec_cmd, + + .vidioc_g_selection = vxd_g_selection, +}; + +static const struct v4l2_file_operations vxd_dec_fops = { + .owner = THIS_MODULE, + .open = vxd_dec_open, + .release = vxd_dec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static void device_run(void *priv) +{ + struct vxd_dec_ctx *ctx = priv; + struct vxd_dev *vxd_dev = ctx->dev; + struct device *dev = vxd_dev->v4l2_dev.dev; + struct vb2_v4l2_buffer *src_vb; + struct vb2_v4l2_buffer *dst_vb; + struct vxd_buffer *src_vxdb; + struct vxd_buffer *dst_vxdb; + struct bspp_bitstr_seg *item = NULL, *next = NULL; + struct bspp_preparsed_data *preparsed_data; + unsigned int data_size; + int ret; + struct timespec64 time; + static int cnt; + int i; + + mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2); + ctx->num_decoding++; + + src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (!src_vb) + dev_err(dev, "Next src buffer is null\n"); + + dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!dst_vb) + dev_err(dev, "Next dst buffer is null\n"); + + src_vxdb = container_of(src_vb, struct vxd_buffer, buffer.vb); + dst_vxdb = container_of(dst_vb, struct vxd_buffer, buffer.vb); + + preparsed_data = &dst_vxdb->preparsed_data; + + data_size = vb2_get_plane_payload(&src_vxdb->buffer.vb.vb2_buf, 0); + + ret = bspp_stream_submit_buffer(ctx->bspp_context, + &src_vxdb->bstr_info, + src_vxdb->buf_map_id, + data_size, NULL, + VDEC_BSTRELEMENT_UNSPECIFIED); + if (ret) + dev_err(dev, "bspp_stream_submit_buffer failed %d\n", ret); + + if (ctx->stop_initiated && + (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0)) + ctx->eos = TRUE; + + mutex_unlock(ctx->mutex); + + ret = bspp_stream_preparse_buffers(ctx->bspp_context, NULL, 0, &ctx->seg_list, + preparsed_data, ctx->eos); + if (ret) + dev_err(dev, "bspp_stream_preparse_buffers failed %d\n", ret); + + ktime_get_real_ts64(&time); + vxd_dev->time_drv[cnt].start_time = timespec64_to_ns(&time); + vxd_dev->time_drv[cnt].id = dst_vxdb->buf_map_id; + cnt++; + + if (cnt >= ARRAY_SIZE(vxd_dev->time_drv)) + cnt = 0; + + core_stream_fill_pictbuf(dst_vxdb->buf_map_id); + + if (preparsed_data->new_sequence) { + src_vxdb->seq_unit.str_unit_type = + VDECDD_STRUNIT_SEQUENCE_START; + src_vxdb->seq_unit.str_unit_handle = ctx; + src_vxdb->seq_unit.err_flags = 0; + src_vxdb->seq_unit.dd_data = NULL; + src_vxdb->seq_unit.seq_hdr_info = + &preparsed_data->sequ_hdr_info; + src_vxdb->seq_unit.seq_hdr_id = 0; + src_vxdb->seq_unit.closed_gop = TRUE; + src_vxdb->seq_unit.eop = FALSE; + src_vxdb->seq_unit.pict_hdr_info = NULL; + src_vxdb->seq_unit.dd_pict_data = NULL; + src_vxdb->seq_unit.last_pict_in_seq = FALSE; + src_vxdb->seq_unit.str_unit_tag = NULL; + src_vxdb->seq_unit.decode = FALSE; + src_vxdb->seq_unit.features = 0; + core_stream_submit_unit(ctx->res_str_id, &src_vxdb->seq_unit); + } + + src_vxdb->pic_unit.str_unit_type = VDECDD_STRUNIT_PICTURE_START; + src_vxdb->pic_unit.str_unit_handle = ctx; + src_vxdb->pic_unit.err_flags = 0; + /* Move the processed segments to the submission buffer */ + for (i = 0; i < BSPP_MAX_PICTURES_PER_BUFFER; i++) { + item = lst_first(&preparsed_data->picture_data.pre_pict_seg_list[i]); + while (item) { + next = lst_next(item); + lst_remove(&preparsed_data->picture_data.pre_pict_seg_list[i], item); + lst_add(&src_vxdb->pic_unit.bstr_seg_list, item); + item = next; + } + /* Move the processed segments to the submission buffer */ + item = lst_first(&preparsed_data->picture_data.pict_seg_list[i]); + while (item) { + next = lst_next(item); + lst_remove(&preparsed_data->picture_data.pict_seg_list[i], item); + lst_add(&src_vxdb->pic_unit.bstr_seg_list, item); + item = next; + } + } + + src_vxdb->pic_unit.dd_data = NULL; + src_vxdb->pic_unit.seq_hdr_info = NULL; + src_vxdb->pic_unit.seq_hdr_id = 0; + if (preparsed_data->new_sequence) + src_vxdb->pic_unit.closed_gop = TRUE; + else + src_vxdb->pic_unit.closed_gop = FALSE; + src_vxdb->pic_unit.eop = TRUE; + src_vxdb->pic_unit.eos = ctx->eos; + src_vxdb->pic_unit.pict_hdr_info = + &preparsed_data->picture_data.pict_hdr_info; + src_vxdb->pic_unit.dd_pict_data = NULL; + src_vxdb->pic_unit.last_pict_in_seq = FALSE; + src_vxdb->pic_unit.str_unit_tag = NULL; + src_vxdb->pic_unit.decode = FALSE; + src_vxdb->pic_unit.features = 0; + core_stream_submit_unit(ctx->res_str_id, &src_vxdb->pic_unit); + + src_vxdb->end_unit.str_unit_type = VDECDD_STRUNIT_PICTURE_END; + src_vxdb->end_unit.str_unit_handle = ctx; + src_vxdb->end_unit.err_flags = 0; + src_vxdb->end_unit.dd_data = NULL; + src_vxdb->end_unit.seq_hdr_info = NULL; + src_vxdb->end_unit.seq_hdr_id = 0; + src_vxdb->end_unit.closed_gop = FALSE; + src_vxdb->end_unit.eop = FALSE; + src_vxdb->end_unit.eos = ctx->eos; + src_vxdb->end_unit.pict_hdr_info = NULL; + src_vxdb->end_unit.dd_pict_data = NULL; + src_vxdb->end_unit.last_pict_in_seq = FALSE; + src_vxdb->end_unit.str_unit_tag = NULL; + src_vxdb->end_unit.decode = FALSE; + src_vxdb->end_unit.features = 0; + core_stream_submit_unit(ctx->res_str_id, &src_vxdb->end_unit); +} + +static int job_ready(void *priv) +{ + struct vxd_dec_ctx *ctx = priv; + + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < 1 || + v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < 1 || + !ctx->core_streaming) + return 0; + + return 1; +} + +static void job_abort(void *priv) +{ + struct vxd_dec_ctx *ctx = priv; + + /* Cancel the transaction at next callback */ + ctx->aborting = 1; +} + +static const struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_ready = job_ready, + .job_abort = job_abort, +}; + +static const struct of_device_id vxd_dec_of_match[] = { + {.compatible = "img,d5500-vxd"}, + { /* end */}, +}; +MODULE_DEVICE_TABLE(of, vxd_dec_of_match); + +static int vxd_dec_probe(struct platform_device *pdev) +{ + struct vxd_dev *vxd; + struct resource *res; + const struct of_device_id *of_dev_id; + int ret; + int module_irq; + struct video_device *vfd; + + struct heap_config *heap_configs; + int num_heaps; + unsigned int i_heap_id; + /* Protect structure fields */ + spinlock_t **lock; + + of_dev_id = of_match_device(vxd_dec_of_match, &pdev->dev); + if (!of_dev_id) { + dev_err(&pdev->dev, "%s: Unable to match device\n", __func__); + return -ENODEV; + } + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + + vxd = devm_kzalloc(&pdev->dev, sizeof(*vxd), GFP_KERNEL); + if (!vxd) + return -ENOMEM; + + vxd->dev = &pdev->dev; + vxd->plat_dev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vxd->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR_VALUE((unsigned long)vxd->reg_base)) + return (long)(vxd->reg_base); + + module_irq = platform_get_irq(pdev, 0); + if (module_irq < 0) + return -ENXIO; + vxd->module_irq = module_irq; +#ifdef ERROR_RECOVERY_SIMULATION + g_module_irq = module_irq; +#endif + + heap_configs = vxd_dec_heap_configs; + num_heaps = ARRAY_SIZE(vxd_dec_heap_configs); + + vxd->mutex = kzalloc(sizeof(*vxd->mutex), GFP_KERNEL); + if (!vxd->mutex) + return -ENOMEM; + + mutex_init(vxd->mutex); + platform_set_drvdata(pdev, vxd); + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "%s: failed to enable clock, status = %d\n", __func__, ret); + goto exit; + } + + /* Read HW properties */ + ret = vxd_pvdec_get_props(vxd->dev, vxd->reg_base, &vxd->props); + if (ret) { + dev_err(&pdev->dev, "%s: failed to fetch core properties!\n", __func__); + ret = -ENXIO; + goto out_put_sync; + } + vxd->mmu_config_addr_width = VXD_EXTRN_ADDR_WIDTH(vxd->props); +#ifdef DEBUG_DECODER_DRIVER + dev_info(&pdev->dev, "hw:%u.%u.%u, num_pix: %d, num_ent: %d, mmu: %d, MTX RAM: %d\n", + VXD_MAJ_REV(vxd->props), + VXD_MIN_REV(vxd->props), + VXD_MAINT_REV(vxd->props), + VXD_NUM_PIX_PIPES(vxd->props), + VXD_NUM_ENT_PIPES(vxd->props), + VXD_EXTRN_ADDR_WIDTH(vxd->props), + vxd->props.mtx_ram_size); +#endif + + INIT_LIST_HEAD(&vxd->msgs); + INIT_LIST_HEAD(&vxd->pend); + + /* initialize memory manager */ + ret = img_mem_init(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize memory\n"); + ret = -ENOMEM; + goto out_put_sync; + } + vxd->streams = kzalloc(sizeof(*vxd->streams), GFP_KERNEL); + if (!vxd->streams) { + ret = -ENOMEM; + goto out_init; + } + + idr_init(vxd->streams); + + ret = vxd_init(&pdev->dev, vxd, heap_configs, num_heaps); + if (ret) { + dev_err(&pdev->dev, "%s: main component initialisation failed!\n", __func__); + goto out_idr_init; + } + + /* initialize core */ + i_heap_id = vxd_g_internal_heap_id(); + if (i_heap_id < 0) { + dev_err(&pdev->dev, "%s: Invalid internal heap id", __func__); + goto out_vxd_init; + } + ret = core_initialise(vxd, i_heap_id, vxd_return_resource); + if (ret) { + dev_err(&pdev->dev, "%s: core initialization failed!", __func__); + goto out_vxd_init; + } + + vxd->fw_refcnt = 0; + vxd->hw_on = 0; + +#ifdef DEBUG_DECODER_DRIVER + vxd->hw_pm_delay = 10000; + vxd->hw_dwr_period = 10000; +#else + vxd->hw_pm_delay = 1000; + vxd->hw_dwr_period = 1000; +#endif + ret = vxd_prepare_fw(vxd); + if (ret) { + dev_err(&pdev->dev, "%s fw acquire failed!", __func__); + goto out_core_init; + } + + if (vxd->no_fw) { + dev_err(&pdev->dev, "%s fw acquire failed!", __func__); + goto out_core_init; + } + + lock = (spinlock_t **)&vxd->lock; + *lock = kzalloc(sizeof(spinlock_t), GFP_KERNEL); + + if (!(*lock)) { + pr_err("Memory allocation failed for spin-lock\n"); + ret = ENOMEM; + goto out_core_init; + } + spin_lock_init(*lock); + + ret = v4l2_device_register(&pdev->dev, &vxd->v4l2_dev); + if (ret) + goto out_clean_fw; + +#ifdef ERROR_RECOVERY_SIMULATION + /* + * create a sysfs entry here, to debug firmware error recovery. + */ + vxd_dec_kobject = kobject_create_and_add("vxd_decoder", kernel_kobj); + if (!vxd_dec_kobject) { + dev_err(&pdev->dev, "Failed to create kernel object\n"); + goto out_clean_fw; + } + + ret = sysfs_create_group(vxd_dec_kobject, &attr_group); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs files\n"); + kobject_put(vxd_dec_kobject); + } +#endif + + vfd = video_device_alloc(); + if (!vfd) { + dev_err(&pdev->dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto out_v4l2_device; + } + + snprintf(vfd->name, sizeof(vfd->name), "%s", IMG_VXD_DEC_MODULE_NAME); + vfd->fops = &vxd_dec_fops; + vfd->ioctl_ops = &vxd_dec_ioctl_ops; + vfd->minor = -1; + vfd->release = video_device_release; + vfd->vfl_dir = VFL_DIR_M2M; + vfd->v4l2_dev = &vxd->v4l2_dev; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vfd->lock = vxd->mutex; + + vxd->vfd_dec = vfd; + video_set_drvdata(vfd, vxd); + + ret = devm_request_threaded_irq(&pdev->dev, module_irq, (irq_handler_t)hard_isrcb, + (irq_handler_t)soft_thread_irq, IRQF_SHARED, + IMG_VXD_DEC_MODULE_NAME, pdev); + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + goto out_vid_dev; + } + + vxd->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR_VALUE((unsigned long)vxd->m2m_dev)) { + dev_err(&pdev->dev, "Failed to init mem2mem device\n"); + ret = -EINVAL; + goto out_vid_dev; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to register video device\n"); + goto out_vid_reg; + } + v4l2_info(&vxd->v4l2_dev, "decoder registered as /dev/video%d\n", vfd->num); + + return 0; + +out_vid_reg: + v4l2_m2m_release(vxd->m2m_dev); + +out_vid_dev: + video_device_release(vfd); + +out_v4l2_device: + v4l2_device_unregister(&vxd->v4l2_dev); + +out_clean_fw: + vxd_clean_fw_resources(vxd); + +out_core_init: + core_deinitialise(); + +out_vxd_init: + vxd_deinit(vxd); + +out_idr_init: + idr_destroy(vxd->streams); + kfree(vxd->streams); + +out_init: + img_mem_exit(); + +out_put_sync: + pm_runtime_put_sync(&pdev->dev); + +exit: + pm_runtime_disable(&pdev->dev); + mutex_destroy(vxd->mutex); + kfree(vxd->mutex); + vxd->mutex = NULL; + + return ret; +} + +static int vxd_dec_remove(struct platform_device *pdev) +{ + struct vxd_dev *vxd = platform_get_drvdata(pdev); + + core_deinitialise(); + + vxd_clean_fw_resources(vxd); + vxd_deinit(vxd); + idr_destroy(vxd->streams); + kfree(vxd->streams); + get_delayed_work_buff(&vxd->dwork, TRUE); + kfree(&vxd->lock); + img_mem_exit(); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + kfree(vxd->dwork); + mutex_destroy(vxd->mutex); + kfree(vxd->mutex); + vxd->mutex = NULL; + + video_unregister_device(vxd->vfd_dec); + v4l2_m2m_release(vxd->m2m_dev); + v4l2_device_unregister(&vxd->v4l2_dev); + + return 0; +} + +static int __maybe_unused vxd_dec_suspend(struct device *dev) +{ + int ret = 0; + + ret = vxd_suspend_dev(dev); + if (ret) + dev_err(dev, "failed to suspend core hw!\n"); + + return ret; +} + +static int __maybe_unused vxd_dec_resume(struct device *dev) +{ + int ret = 0; + + ret = vxd_resume_dev(dev); + if (ret) + dev_err(dev, "failed to resume core hw!\n"); + + return ret; +} + +static UNIVERSAL_DEV_PM_OPS(vxd_dec_pm_ops, + vxd_dec_suspend, vxd_dec_resume, NULL); + +static struct platform_driver vxd_dec_driver = { + .probe = vxd_dec_probe, + .remove = vxd_dec_remove, + .driver = { + .name = "img_dec", + .pm = &vxd_dec_pm_ops, + .of_match_table = vxd_dec_of_match, + }, +}; +module_platform_driver(vxd_dec_driver); + +MODULE_AUTHOR("Prashanth Kumar Amai Sidraya Jayagond "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("IMG D5520 video decoder driver"); diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_binaries/ALL_CODECS_FW_ALL_pipes_2_contexts_8_hwconfig_1_bin.c b/drivers/media/platform/vxe-vxd/encoder/fw_binaries/ALL_CODECS_FW_ALL_pipes_2_contexts_8_hwconfig_1_bin.c --- a/drivers/media/platform/vxe-vxd/encoder/fw_binaries/ALL_CODECS_FW_ALL_pipes_2_contexts_8_hwconfig_1_bin.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_binaries/ALL_CODECS_FW_ALL_pipes_2_contexts_8_hwconfig_1_bin.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,29013 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Encoder FW binary file + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +/* note that order of defines has to match the structure declaration! + */ + +unsigned char *all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_define_names_array[] = { + "TOPAZHP_NUM_PIPES", + "TOPAZHP_MAX_BU_SUPPORT", + "MAX_REF_B_LEVELS_FW", + "SEI_INSERTION", + "TOPAZHP_MAX_NUM_STREAMS", +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_define_values_array[] = { + 2, + (TOPAZHP_MAX_BU_SUPPORT_HD), + 0, + 1, + 8, +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_text[] = { + 0x9040c001, + 0xc80993fe, + 0xc0000e42, + 0xc8290e00, + 0xcc3e8426, + 0xc8298420, + 0xceea8622, + 0x9e838660, + 0xc8099e43, + 0xcdd40d46, + 0xc8090d40, + 0xcdd60946, + 0xc8090900, + 0xc00a0e42, + 0xc8090e40, + 0xc00e87c2, + 0x9c1887c0, + 0x0c020802, + 0x09820d82, + 0x09020d02, + 0x08820c82, + 0x9320fffe, + 0xa401c838, + 0x0dc6c809, + 0x0d80cdd4, + 0x0e42c809, + 0x0c66b080, + 0x0882a992, + 0x9ff3a48d, + 0x93e0ffff, + 0x80819d13, + 0xa205f839, + 0x03070707, + 0x9e970685, + 0xc8090383, + 0xcdd60ac6, + 0xc8090aa0, + 0xcdd61ac4, + 0x060f1a80, + 0x07fac101, + 0x018d058d, + 0x9c62008f, + 0x9320ffff, + 0xc101060b, + 0x9c6206da, + 0x9380ffff, + 0x018d058d, + 0x460cb700, + 0x4594b780, + 0xa6059c01, + 0xc8090687, + 0xcdd60ac6, + 0xc8090aa0, + 0xcdd61ac4, + 0x060b1a80, + 0x06dac101, + 0xffff9c62, + 0xf9f89380, + 0xf9f8aa9d, + 0x9c22aa1d, + 0x420cb700, + 0xc000587c, + 0xe0003800, + 0xc0003800, + 0x9c22901a, + 0x9c8fc127, + 0x080a9c22, + 0x9c81c017, + 0x9c80c071, + 0x9c80c017, + 0x0d849c22, + 0x9e5a5db0, + 0x4018b960, + 0x0900c021, + 0x0940c00e, + 0xaa45f031, + 0xf0009dad, + 0x0910a261, + 0x9341ffff, + 0xc3fe9e5c, + 0xc02129c0, + 0xc0010a00, + 0xc00e3988, + 0x9dcd0a30, + 0xa1e1f000, + 0xc0219c22, + 0xd1100d80, + 0x9d3d05b7, + 0x2244aa61, + 0xffff7115, + 0x9c229384, + 0xd011a605, + 0xc3fe0eb2, + 0xc28029c0, + 0x020b5ab0, + 0x0a00c021, + 0x398cc001, + 0xc00e0685, + 0x9dcd0a30, + 0xa1e1f000, + 0xc00e9eab, + 0x0d020992, + 0x0902cff0, + 0x0a80c021, + 0x9bdbfff4, + 0x0ac0c00e, + 0x4018b960, + 0xaa619d5d, + 0xa225f231, + 0xffff0a90, + 0xb79f9361, + 0xb7bf7f6e, + 0x8c407fee, + 0xfffd9c22, + 0xf0129040, + 0x9e582d36, + 0xc0009e5a, + 0xc18093a4, + 0x9e515a0b, + 0xd2247500, + 0x9e825988, + 0x9c83c810, + 0x0106c101, + 0x4418b313, + 0x9080c000, + 0xa0c5f031, + 0x93c1ffff, + 0x11b6c101, + 0x90c0c000, + 0xa146d029, + 0x9120c000, + 0x9e540d02, + 0xb33474c0, + 0xc8104436, + 0xffff9c83, + 0x9c2292a1, + 0xa285f839, + 0xc00272d7, + 0x70d79022, + 0xc0009e59, + 0x0a8690a6, + 0x9100c000, + 0xd0101d04, + 0xc10104b4, + 0x0aff01b4, + 0xc0017c46, + 0xf0139124, + 0xc0012936, + 0xd12290a4, + 0x9e885e0b, + 0x5808c200, + 0x9e99610b, + 0x00947500, + 0xc81001b4, + 0xc2809c83, + 0xb3235908, + 0xc0004c18, + 0x9e9590e0, + 0xaa29e059, + 0xa209e059, + 0x9361ffff, + 0x1520c101, + 0x9100c000, + 0xa966c059, + 0xa126c059, + 0x9100c000, + 0x0a029e52, + 0x7088d012, + 0x9c83c810, + 0x9281ffff, + 0xf9f89e58, + 0x9c22aa9d, + 0x59300904, + 0x9d299da9, + 0x9e919e90, + 0x76c00005, + 0x8700c021, + 0x0da2c020, + 0x8500c021, + 0x0800c021, + 0x0c00c021, + 0x0c80c021, + 0x1db0d021, + 0x08820d08, + 0x8730c00e, + 0x8540c00e, + 0x0850c00e, + 0x0c60c00e, + 0x0cf0c00e, + 0x92a0c001, + 0xaa41d9d0, + 0xa95dd990, + 0x5a40c200, + 0x9e2e3244, + 0xa261f000, + 0xaa49d9d0, + 0xa945d9d0, + 0x5a40c200, + 0x9d8d3244, + 0xa261f000, + 0xaa51d9d0, + 0xa94dd9d0, + 0x5a40c200, + 0x9d8e3244, + 0xa261f000, + 0xaa59d9d0, + 0xa955d9d0, + 0x5a40c200, + 0x9d9e3244, + 0xa261f000, + 0x5e10d1a2, + 0xc3fe08a0, + 0xc0012a40, + 0x83853a08, + 0xa261f000, + 0x70460d84, + 0xfffe0d40, + 0x9c229166, + 0x8420a61d, + 0x0b820307, + 0xc001a19a, + 0xf04892a0, + 0xa91aaac6, + 0x9e6b7740, + 0xc001672b, + 0xf0489162, + 0x05d6a9ce, + 0xc3b41d84, + 0xc1019924, + 0xf208628b, + 0x018ba34a, + 0xc3b4058d, + 0x628b991c, + 0x6979d031, + 0x16ebd110, + 0xa041f208, + 0xa2c5f208, + 0x430cb780, + 0x0679d110, + 0xaa09f248, + 0xf2086009, + 0xb780a041, + 0x0128430c, + 0xa945f008, + 0x000a6005, + 0xa049f208, + 0x0b300b84, + 0x430cb780, + 0x5b90d3a4, + 0x0579d110, + 0xaa15f288, + 0xfffe71c8, + 0xb79f9086, + 0xb7bf7dee, + 0xb7df7e6e, + 0xb7ff7eee, + 0xc0027f6e, + 0x9c228c20, + 0xb7a0a60d, + 0x9e5e430c, + 0xf2489e9e, + 0xf248a9ae, + 0xd120aaa5, + 0x018b01d7, + 0xc3b41d84, + 0x9e8598da, + 0xc101636b, + 0x9eb366db, + 0xf2109e6b, + 0xc3b4a349, + 0xd13298d0, + 0xf210628b, + 0xc101a041, + 0xf210136a, + 0xb780a345, + 0xf248430c, + 0x6009aa09, + 0xa041f210, + 0x430cb780, + 0xaa05f208, + 0x000c6009, + 0xa049f210, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x5db00d84, + 0xc0219e5c, + 0xc0100a00, + 0x9dcd0a00, + 0xa162f000, + 0x592809bc, + 0xcff05990, + 0xc00f2980, + 0xc021297c, + 0x31260d80, + 0x0d80c00e, + 0xf0009dbe, + 0x9c22a161, + 0x5db00d84, + 0x0992c00e, + 0xcff00d02, + 0xfff20902, + 0xaa1d91c0, + 0x09bc0405, + 0xd0117500, + 0xd0120e32, + 0xd0a2299e, + 0xc0015cc0, + 0xd2249004, + 0x9e545930, + 0x0a00c021, + 0x0a00c010, + 0xf0009dcd, + 0xc100a062, + 0x9e525a18, + 0x2a00cffc, + 0x0900c021, + 0x0930c010, + 0xf0009dad, + 0xc180a261, + 0xcff05a10, + 0xc0112a00, + 0xc0003a00, + 0xd22492e0, + 0x9e545930, + 0x0a00c021, + 0x0a10c010, + 0xf0009dcd, + 0x5958a062, + 0x9dcd0a20, + 0xa161f000, + 0x5a10c180, + 0x2a00cff0, + 0x3a00c031, + 0xc00f9e5a, + 0x324428fc, + 0x0d00c021, + 0xc00e3242, + 0x9dae0d00, + 0xa261f000, + 0xa61d9c22, + 0x8400c00a, + 0x430cb7c0, + 0x0802c004, + 0xb55fa011, + 0x74807dec, + 0xa945f208, + 0x7decb79f, + 0x7d74b57f, + 0x5b99c100, + 0x4422b340, + 0x5a18c380, + 0xa0117104, + 0x7c74b55f, + 0x7cecb53f, + 0xf248000d, + 0xf248a94d, + 0x9e4dab46, + 0xb7809e9f, + 0xb7a040cd, + 0xd0125eb4, + 0xc006136c, + 0xd0209244, + 0x9e7311a8, + 0x9811c3b4, + 0x701b9e6c, + 0x4434b304, + 0xb347711f, + 0xf2084454, + 0xf208a9c1, + 0x657daa29, + 0xb740008d, + 0xd0f2572b, + 0xd0200eae, + 0xb77f0138, + 0x74887468, + 0x2e81cffc, + 0x293ed3f1, + 0x9172c000, + 0x9e93a892, + 0x0a029e6b, + 0x0892010f, + 0xc000a21d, + 0xa8929120, + 0x9e6b0a02, + 0xa21d9e93, + 0x0896010f, + 0x9b57fff4, + 0x7468b79f, + 0x29ced3f2, + 0x9b48fff4, + 0xaa25f208, + 0x0659d110, + 0xa225f208, + 0xa94df248, + 0xc0007104, + 0x0a029186, + 0xa225f208, + 0xa949f248, + 0xaa21f208, + 0xf2080244, + 0xb740a221, + 0xf208430c, + 0xf208aa21, + 0xf008a926, + 0x6129a945, + 0x0124c101, + 0xa129f208, + 0x4314b700, + 0x574ab780, + 0xc0007506, + 0x750a90a2, + 0x9144c000, + 0xaa11a911, + 0x0a08090c, + 0x00899e91, + 0x9100c000, + 0xa911aa11, + 0x090c0a08, + 0x00859ea1, + 0x7decb71f, + 0x5dccb780, + 0x7c6cb77f, + 0xa8110100, + 0x6679d131, + 0xcffe9e83, + 0xcffe28fc, + 0xd0202cfc, + 0xd0200334, + 0xd0101110, + 0xda101516, + 0x5d18a241, + 0xcffe5930, + 0xc2002a7c, + 0xc1015a48, + 0xd1100524, + 0xb79f0729, + 0x71887cec, + 0x9322c001, + 0x8c80f292, + 0x8c84f271, + 0x0c029e68, + 0x91a0c000, + 0x7c6ab77f, + 0xa1b1da31, + 0x7eb2b57f, + 0x7f32b55f, + 0xa231da29, + 0x9e44a111, + 0xa9c1da10, + 0xcffea911, + 0xd0202a7c, + 0x09100142, + 0x0242c101, + 0xcffe7217, + 0xcffe2a7c, + 0xcffe2d7c, + 0x0c10297c, + 0xc0200820, + 0xc0103a00, + 0xd0123d00, + 0xfffe19a6, + 0xb77f9366, + 0xb75f7df4, + 0xe2927d6c, + 0xfff48d00, + 0x000d99b3, + 0x746eb79f, + 0x74eeb7bf, + 0x756eb7df, + 0x75eeb7ff, + 0x8c00c00c, + 0xa61d9c22, + 0x8400c00c, + 0x430cb7c0, + 0x40d5b700, + 0x000d9e4f, + 0x5732b720, + 0xaa4df248, + 0xaac5f248, + 0x76400f86, + 0x79f4b57f, + 0x11c1d120, + 0xd002040f, + 0x9e9d0c72, + 0xb51f018b, + 0xb55f7874, + 0xb55f7d6c, + 0xb53f78f4, + 0xb51f796c, + 0xc3947b74, + 0x9eb09b14, + 0xa946f208, + 0x5eccb780, + 0xa941f208, + 0x7d6cb77f, + 0xb3047008, + 0x711b4434, + 0x4454b345, + 0x9e695d19, + 0x7bf4b5bf, + 0xaa69f208, + 0xb71f629b, + 0xd0326670, + 0x74c00ade, + 0xb55f0128, + 0xc0047af4, + 0xb3300802, + 0x9e924422, + 0x7a6cb55f, + 0xb75f2ec1, + 0xc00e7aec, + 0x9e6b2c7c, + 0x9e799e81, + 0x05810a02, + 0xa015a21d, + 0xfff4a012, + 0xa9929a54, + 0x5a8dc280, + 0x9a46fff4, + 0xaa49f248, + 0xc0017510, + 0xf2089114, + 0xf248aa61, + 0x0a20a951, + 0xc0017104, + 0xa8159008, + 0x4314b740, + 0x7a74b71f, + 0xf010020a, + 0x9ea6a946, + 0x7aecb75f, + 0xcffea992, + 0x9e792f7c, + 0x5d0c9e6b, + 0x05200a02, + 0xa21d048d, + 0x9a2bfff4, + 0xfff4a992, + 0xb5df9a1f, + 0xc0007df4, + 0xab1690a0, + 0x7df4b5df, + 0xaa65f208, + 0x0659d110, + 0xa265f208, + 0xa94df248, + 0xc0007104, + 0x0a029186, + 0xa265f208, + 0xa949f248, + 0xaa61f208, + 0xf2080244, + 0xb780a261, + 0xf208430c, + 0xf208a961, + 0xf208a966, + 0x6245aa05, + 0x0244c101, + 0xa269f208, + 0x4314b7e0, + 0xa971f210, + 0x5819d124, + 0xb51f9e44, + 0xc2007cf4, + 0x71045a18, + 0x90e2c000, + 0xb53f0882, + 0xc00a7c6c, + 0xf2509140, + 0xb77faa75, + 0xd0327bec, + 0xd1100af8, + 0x6239056b, + 0xa92af210, + 0xa9edf210, + 0xd0319e96, + 0xb75f0b4e, + 0xa9927cec, + 0x7b6cb73f, + 0xd1202b41, + 0xcffe0135, + 0x0a062f7c, + 0x048d018d, + 0xfff4a21d, + 0xf21099cc, + 0x0268aa25, + 0xa225f210, + 0xa97df250, + 0xc0007104, + 0x0a029186, + 0xa225f210, + 0xa979f250, + 0xaa21f210, + 0xf2100244, + 0xb720a221, + 0xf210430c, + 0xf210aa21, + 0xb740a926, + 0x6129422d, + 0x0124c101, + 0xa129f210, + 0x572bb780, + 0xc0007502, + 0xd0119144, + 0x040d0e62, + 0xcffe9ea1, + 0xc0022cfc, + 0x750491a0, + 0x9144c000, + 0x0e62d011, + 0x9ea0048d, + 0x2c7ccffe, + 0x9040c002, + 0x0af0d072, + 0x5a0dc300, + 0x0669d110, + 0x432db7a0, + 0xa929f210, + 0x0389a992, + 0xcffe02d4, + 0xfff42bfc, + 0xb75f9979, + 0xb73f7cec, + 0xa9927b6c, + 0x9eb99eaa, + 0x018d8506, + 0xfff4a11f, + 0xf2109976, + 0x0268aa25, + 0xa225f210, + 0xa97df250, + 0xc0007104, + 0x0a029186, + 0xa225f210, + 0xa979f250, + 0xaa21f210, + 0xf2100244, + 0xb740a221, + 0xf210430c, + 0xf210aa21, + 0xf008a926, + 0x9eb9a95d, + 0x6129040d, + 0x0124c101, + 0xa129f210, + 0x7d6cb71f, + 0x78ecb75f, + 0x430cb720, + 0x02a80200, + 0x5dadb720, + 0x7becb79f, + 0x7df4b77f, + 0xd01060c3, + 0xda081516, + 0x0203a0a1, + 0xcffea815, + 0xc2002a7c, + 0x9e595a48, + 0x12401506, + 0x797cb75f, + 0x02425d18, + 0x0244c101, + 0x02445930, + 0xb59f7135, + 0xc0007c6c, + 0xb71f9182, + 0xb740706a, + 0x0d824304, + 0x287ccffe, + 0xc0029d8d, + 0xa9929020, + 0x9914fff4, + 0x9320c003, + 0x7d6ab75f, + 0x0d34d012, + 0x0624d010, + 0xa121da10, + 0xa041da08, + 0xa949f040, + 0xcffe9d4d, + 0xe31328fc, + 0x74908d00, + 0xf010a095, + 0xc0008026, + 0xb71f9254, + 0xd0127b6a, + 0xda080d38, + 0xda10a001, + 0xcffea1c1, + 0xd01129fc, + 0xcffe0a32, + 0xb59f2a7c, + 0xd0107dec, + 0xd0110524, + 0x9d2d0e22, + 0xe3130248, + 0xe0108d00, + 0x9d4d8126, + 0xa3c1d808, + 0x8026f010, + 0x0da4d012, + 0xa382da08, + 0x0536d010, + 0x0e32d011, + 0xe3139d2d, + 0x02488d00, + 0x7decb75f, + 0x80a6f020, + 0xd0119d4d, + 0x09040e36, + 0xf0100248, + 0x01858126, + 0xda089d4d, + 0xd010a922, + 0xd0100607, + 0xa8150517, + 0x9e47a895, + 0x9ea00783, + 0x72c49e91, + 0xf0200804, + 0x08888126, + 0x3b80c010, + 0x3f80c020, + 0x2c7ccffe, + 0x2cfccffe, + 0x9306fffc, + 0xfff4a992, + 0xb77f98a5, + 0xda087d74, + 0xb75fa9a1, + 0xe31279ec, + 0xffd48d00, + 0xb71f9b9d, + 0xb79f7c6c, + 0xb7bf726e, + 0xb7df72ee, + 0xb7ff736e, + 0xc00e73ee, + 0x9c228c00, + 0xa6059c22, + 0x4c8cb7a0, + 0x6da2c146, + 0x06bbd110, + 0x008b099a, + 0x6031b76d, + 0xc3940d84, + 0x008b9961, + 0x60a9b78d, + 0x287cc00e, + 0x08027100, + 0x0802d001, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xb7409e5c, + 0xc1464c8c, + 0x02446a22, + 0x0902c021, + 0x430cb580, + 0x4490b560, + 0x0920c030, + 0xaa619d2d, + 0xc0015dc0, + 0x9e5b2dbc, + 0x2a42cfff, + 0x9dad3246, + 0xa261f000, + 0xb7209c22, + 0x9e58430c, + 0x4829b78a, + 0x71172208, + 0x90c4c000, + 0x48a9b78a, + 0x9c222008, + 0x4588b780, + 0x9c222008, + 0xb720a60d, + 0x0b06430c, + 0x4731b7aa, + 0x46a9b7aa, + 0x90e0c000, + 0x0a52d011, + 0xc00e0289, + 0xd2242afc, + 0x715b51d4, + 0x2dfcc00e, + 0x90e8c000, + 0x9bd9fff4, + 0xffff7400, + 0x000b9224, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x430cb720, + 0x4cadb782, + 0x5b2db743, + 0xb5823244, + 0xb7204cad, + 0xb782430c, + 0xc0104cad, + 0xb5822a04, + 0xb7204cad, + 0xb782430c, + 0xc0204cad, + 0xb3407d00, + 0x3a0448a2, + 0x4cadb582, + 0x0d849c22, + 0x9e5c5db0, + 0x0a00c021, + 0x0a00c010, + 0xf0009dcd, + 0x09bca162, + 0x59285990, + 0x2980cff0, + 0x297cc00f, + 0x3980c011, + 0x0d80c021, + 0xc00e31b4, + 0x9dbe0d80, + 0xa1e1f000, + 0xa61d9c22, + 0x8400c002, + 0x0abed031, + 0x2ac19e9f, + 0xa119a192, + 0xc001a116, + 0xc1fc9300, + 0xc0fc7562, + 0xb3560f02, + 0xa9164446, + 0x9e73a992, + 0xfff40902, + 0xa9929bce, + 0x068d0b82, + 0x9bcaffd4, + 0x010fab19, + 0x92e0c000, + 0xc0007dde, + 0xa9929144, + 0x8d00e092, + 0xffd4018f, + 0xe0919a1d, + 0x77c08d00, + 0xc0000b84, + 0xd02990e2, + 0x1f84aa45, + 0xa241d208, + 0x1e840b04, + 0xffff777f, + 0xa91a9124, + 0x12dcc101, + 0xa11a052c, + 0x052ca916, + 0x7540a116, + 0x9124fffe, + 0x7c6eb79f, + 0x7ceeb7bf, + 0x7d6eb7df, + 0x7deeb7ff, + 0x8c00c004, + 0xa61d9c22, + 0x8400c002, + 0x9e9f9e5c, + 0x5a30c200, + 0x0a00c021, + 0xc110a19a, + 0x09820a30, + 0xf0009dcd, + 0xd032a1e1, + 0x2ec10efe, + 0xa115a112, + 0x9180c002, + 0x7762c1fc, + 0x0f02c0fc, + 0x4846b356, + 0x9e760d02, + 0x7df4b55f, + 0x7d74b55f, + 0x7cf4b55f, + 0x7c74b55f, + 0xe0920b82, + 0x77c08d80, + 0x0534d010, + 0x9162c000, + 0x1f84a991, + 0xaa65d029, + 0xd008a191, + 0xc000a241, + 0xd0089080, + 0x0b84a3c2, + 0x2afef031, + 0xc0000d04, + 0xa99a9244, + 0x8d00e092, + 0x19f0d051, + 0x9987ffd4, + 0xb5bf9eaa, + 0xb5bf7dec, + 0xb5bf7d6c, + 0xb5bf7cec, + 0xe0007c6c, + 0xfffe1b04, + 0xa91692c4, + 0x9e73a99a, + 0xffd4010d, + 0xa99a9b23, + 0xffd416dc, + 0xa9969b39, + 0xa19605bc, + 0xfffd7740, + 0xb79f92a4, + 0xb7bf7c6e, + 0xb7df7cee, + 0xb7ff7d6e, + 0xc0047dee, + 0x9c228c00, + 0x430cb720, + 0x0992c144, + 0x5badb743, + 0x6c31b76d, + 0xfffc9e8a, + 0xf8399100, + 0x9e90a285, + 0xaa4df008, + 0x0caed091, + 0xa949f008, + 0x5815c080, + 0x0244c101, + 0x76400120, + 0x41ccb580, + 0x414cb540, + 0x9202c000, + 0x40ccb780, + 0x404cb740, + 0x4254b740, + 0x0a061128, + 0x5208c200, + 0x35229ea1, + 0x4254b540, + 0x5e30d1a2, + 0x08fec00f, + 0xcff19ea3, + 0xc0210d02, + 0xc0100d80, + 0xc1409c83, + 0xc00e0de0, + 0xcff008fc, + 0xc0000d00, + 0xf0299200, + 0x9e55aa65, + 0x2242214a, + 0xc2005921, + 0x32445a20, + 0x4006ba24, + 0xf0009dbe, + 0xffffa261, + 0xb7809241, + 0x0a04404c, + 0x404cb580, + 0xaa9df9f8, + 0xa6859c22, + 0x87029e91, + 0x85220685, + 0x0b7ec00e, + 0x882bf210, + 0xc2007640, + 0xc0025153, + 0xf0089182, + 0x9e69aa61, + 0x2a1ce000, + 0xc28022a2, + 0xb4235920, + 0xf0084442, + 0xd010aa61, + 0x2a1ca962, + 0x8821f310, + 0x1242c101, + 0x400aba0c, + 0x52411003, + 0x5921c200, + 0xc00e9e95, + 0xd0312a7c, + 0xc2000910, + 0x3454520b, + 0x2aced3f2, + 0x92eac000, + 0x5224c200, + 0x32429e51, + 0xa261d010, + 0xa961f008, + 0x2a2ed011, + 0x0619d110, + 0xd110750e, + 0xd0090615, + 0xf0080db2, + 0xc000a261, + 0xd0109240, + 0xf008a062, + 0xd110aa61, + 0x9e810619, + 0xf0080242, + 0xcffea261, + 0xd0322cfc, + 0xfffda164, + 0x9e589220, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c40, + 0x9e58a60d, + 0x9e569e9e, + 0x709b0ee2, + 0x923cc000, + 0x9eb39e6d, + 0x2afccffe, + 0x112a5575, + 0x9e739e83, + 0x297ccffe, + 0x2d7cc00e, + 0x9b8dfff4, + 0x1ea0010b, + 0xffff7771, + 0xb79f9184, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xa60d9c22, + 0x9e589e56, + 0x09069e9e, + 0xc0000d02, + 0x13649080, + 0xd0110124, + 0x710c1a22, + 0x2eaed3f2, + 0xffff0d04, + 0x9e6d9306, + 0x9080c000, + 0x9b69fff4, + 0x0a52d011, + 0x9e837510, + 0x0d029e73, + 0x1aa00922, + 0x92d2ffff, + 0x294ed3f1, + 0xfff40d04, + 0xc0009b5a, + 0xd3f291e0, + 0x9e6c2ace, + 0x52d1c200, + 0x295ed3f2, + 0x9b4ffff4, + 0xc2009e6c, + 0x136a52b0, + 0xd0317750, + 0x9e831e50, + 0x09229e73, + 0x91b2ffff, + 0x296ed3f2, + 0xb79f9e6a, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0x92e0fff9, + 0x1d20f011, + 0xd01d9e9a, + 0xd00d0224, + 0xc0001a42, + 0x1205909a, + 0x9e531244, + 0xfffd9ea2, + 0x02079140, + 0xa1c1d010, + 0x9ea29e53, + 0xfff60c82, + 0xa6059240, + 0x02870d0a, + 0x9b9dfff4, + 0x430cb720, + 0x7ea9b785, + 0xc0007500, + 0x02039122, + 0x0a00c098, + 0xaa01d208, + 0x3a040248, + 0x9ea29e83, + 0xb79f018b, + 0xb7bf7f6e, + 0x8c407fee, + 0x90a0fffc, + 0xb720a605, + 0xf011430c, + 0xc0020ea0, + 0xb7450a02, + 0xb3547ea9, + 0x02894424, + 0x9e9d7480, + 0x90a2c000, + 0xba2c0248, + 0x75404000, + 0x9116c000, + 0x0d02120b, + 0x4000ba2c, + 0x9060c000, + 0xfff40d06, + 0x9e839b66, + 0xd0129e6b, + 0xb79f1952, + 0xb7bf7f6e, + 0x8c407fee, + 0x9360fffa, + 0x8460a60d, + 0x8c88e051, + 0xa2190a02, + 0xa211a215, + 0x7d6cb59f, + 0x402db580, + 0xa9e1f008, + 0x07059e5e, + 0xc004198c, + 0xd01274c4, + 0xc0020a92, + 0xd1a691b2, + 0x8520590c, + 0x8044e050, + 0x9120c002, + 0x91c0c002, + 0x9360c002, + 0x9140c003, + 0x91e0c003, + 0x9300c003, + 0x90c0c017, + 0x9280c016, + 0x93a0c015, + 0x9280c001, + 0x9120c011, + 0x9220c011, + 0x9320c011, + 0x9060c013, + 0x91a0c013, + 0x93c0c013, + 0x90c0c014, + 0x9340c011, + 0x9020c019, + 0x9280c018, + 0x9000c017, + 0x9180c017, + 0x9300c017, + 0x90e0c003, + 0x92a0c003, + 0x9360c006, + 0x9120c007, + 0x93c0c007, + 0x9200c00d, + 0x9180c000, + 0x9140c000, + 0x9240c018, + 0x9240c01a, + 0x9320c01a, + 0x92e0c01b, + 0xc01d0802, + 0xd0729000, + 0xe0711924, + 0x058b8d80, + 0x91a0c000, + 0x430cb780, + 0x8d80e071, + 0xc09c058b, + 0xd2080a1c, + 0xd120a902, + 0xfff41125, + 0xc01c9b2b, + 0xd3f19020, + 0xe0712aae, + 0x9eaa8d80, + 0x0916058b, + 0x9a55fff4, + 0x430cb780, + 0x0a1cc09c, + 0xa281d208, + 0x9200c01b, + 0x292ed3f2, + 0x8d80e071, + 0xc016058b, + 0xf01091a0, + 0x0d06aacd, + 0x8d80e071, + 0x9e524afd, + 0x2a9c058b, + 0x93e0c013, + 0xaacdf010, + 0x8d80e071, + 0x0d02058b, + 0xfff40906, + 0x4afd9a32, + 0x9e832a9c, + 0x8d80e071, + 0xc00e010b, + 0xc01a0d7e, + 0xb7809120, + 0x0d02430c, + 0x8d80e071, + 0x0a40c09a, + 0xaa01d208, + 0x090a058b, + 0xd0027500, + 0xc0190d22, + 0xb7209320, + 0x7640430c, + 0xb7840a82, + 0xd002752d, + 0x75000ad2, + 0x9204c000, + 0xc0007540, + 0xb74491a2, + 0xb78475ad, + 0xe0716fad, + 0x058b8d80, + 0x090a7104, + 0x9060c013, + 0x6fadb744, + 0x75adb784, + 0xc0027088, + 0x0d069004, + 0x8d80e071, + 0x058b9e52, + 0x99f1fff4, + 0xc0007540, + 0xb72093a2, + 0xb785430c, + 0x75007ea9, + 0x91c2c000, + 0x6cadb784, + 0xc0007502, + 0x9e839122, + 0x8d80e071, + 0x0d2ec002, + 0x90e0c000, + 0xe0719e83, + 0xc0028d80, + 0x09220d2a, + 0x99d3fff4, + 0xe0719e83, + 0x09168d80, + 0xfff40d1e, + 0xb78099cc, + 0x9e83430c, + 0x8d80e071, + 0x0a04c098, + 0xa902d208, + 0x9a43fff4, + 0x9e830d06, + 0x8d80e071, + 0xc0169e52, + 0x02039360, + 0x0a40c09a, + 0xaa01d208, + 0xc0167500, + 0xc00992a2, + 0xb7809180, + 0x0d02430c, + 0x8d80e071, + 0x0a14c098, + 0xaa01d208, + 0x0906058b, + 0xd0017500, + 0xc0160d22, + 0xb7809020, + 0xc098430c, + 0xd2080a10, + 0x7500aa01, + 0x9122c000, + 0x8d80e071, + 0x0d2a058b, + 0xc0150912, + 0x9ea29220, + 0x8d80e071, + 0x0906058b, + 0x9140c015, + 0x4314b720, + 0x6ccdb784, + 0xd0227504, + 0xc0011524, + 0x9e4c93a4, + 0xc0969e4a, + 0xd2080a54, + 0xb744aa01, + 0xc09875d5, + 0xc2000910, + 0xc1015a08, + 0xb76400c2, + 0xd008722d, + 0xd011a941, + 0x72990a32, + 0xd0020d02, + 0x74800d22, + 0x9042c001, + 0x7ec9b785, + 0xd0127500, + 0xc0001124, + 0x9e4c9244, + 0xc0960902, + 0xd2080a58, + 0xc200aa01, + 0xc1015a08, + 0xb72400c2, + 0x0884722d, + 0xd00270c2, + 0x9e4c0922, + 0xc0989e51, + 0xd2080a0c, + 0x3218aa01, + 0x3144d020, + 0x9e529e4c, + 0x0a08c098, + 0xaa01d208, + 0x3228e000, + 0x91a2fffd, + 0xe0710d06, + 0x9e528d80, + 0xfff4058b, + 0xb7209934, + 0x0203430c, + 0x0a08c098, + 0xaa01d208, + 0xc0007500, + 0x9e839122, + 0x8d80e071, + 0x9a07fff4, + 0x92e0c000, + 0xc0960203, + 0xd2080a54, + 0xb744aa01, + 0x9e8375b5, + 0x5a08c200, + 0xb72400c2, + 0xe071722d, + 0xd1208d80, + 0xba121115, + 0xfff44001, + 0xb7209a0d, + 0x0203430c, + 0x0a10c098, + 0xaa01d208, + 0xc0037500, + 0x02039342, + 0x0a0cc098, + 0xaa01d208, + 0xc0027500, + 0x020392c4, + 0x0a08c098, + 0xaa01d208, + 0xc0960103, + 0x75000958, + 0x90c2c000, + 0xaa41d008, + 0x9260c002, + 0xc0960203, + 0xd2080a54, + 0xd008aa01, + 0x9e83a941, + 0x5a08c200, + 0x00c2d020, + 0x00a25908, + 0x722db724, + 0x7255b724, + 0x8d80e071, + 0x1113d120, + 0x90e0c002, + 0x430cb720, + 0xc0980203, + 0xd2080a0c, + 0x7500aa01, + 0x9284c000, + 0x6f2db784, + 0xc0027500, + 0x02039044, + 0x0a54c096, + 0xaa01d208, + 0x5a08c200, + 0xb78400c2, + 0x7500722d, + 0x92a4c001, + 0xe0710d06, + 0x9e528d80, + 0xfff4058b, + 0xb72098b2, + 0x0203430c, + 0x0a0cc098, + 0xaa01d208, + 0xc0007500, + 0x9e839122, + 0x8d80e071, + 0x9985fff4, + 0x92e0c000, + 0xc0960203, + 0xd2080a58, + 0xc200aa01, + 0xb7445a08, + 0x00c275b5, + 0x722db724, + 0xe0719e83, + 0xd1208d80, + 0xba121115, + 0xfff44001, + 0x9e83998b, + 0x8d80e071, + 0xc0020d0e, + 0xe0719200, + 0x058b8d80, + 0xfff70d02, + 0xb72092c0, + 0xe071430c, + 0x058b8d80, + 0x75b5b744, + 0xc0060916, + 0xb72091a0, + 0xe071430c, + 0x058b8d80, + 0x75b5b744, + 0xc0060922, + 0xb7209060, + 0xe071430c, + 0x058b8d80, + 0x75b5b744, + 0x5d21090a, + 0x9300c005, + 0x430cb720, + 0x67adb784, + 0xd01a7502, + 0xc0001248, + 0xb7619174, + 0x9ea34d2d, + 0x9811c374, + 0x0a027400, + 0x0a42d001, + 0x09c0f012, + 0x04850d0a, + 0x1ca2d001, + 0x76c09e4a, + 0x8d80e071, + 0x4842b332, + 0xc00b058b, + 0xb7209020, + 0xe071430c, + 0x058b8d80, + 0x6d35b744, + 0xfff42d04, + 0xc00a98b8, + 0xb72092e0, + 0x7640430c, + 0xb7240d02, + 0xd001752d, + 0xe0710d22, + 0x00928d80, + 0x0522c101, + 0xc00e058b, + 0x091a2d7c, + 0x9040c00a, + 0x430cb720, + 0x8d80e071, + 0xb744058b, + 0x090a7535, + 0x91c0c003, + 0x430cb720, + 0x4d2db761, + 0x67b5b764, + 0xc00074c0, + 0x0a8690a4, + 0x9220c000, + 0xc0029e9a, + 0xc0000902, + 0xd0119100, + 0x01091a22, + 0xc00e0524, + 0x7680297c, + 0x9336ffff, + 0xc3540285, + 0x9e829bb8, + 0x8d80e071, + 0xc00e058b, + 0xd3f12d7c, + 0xc008295e, + 0xb7209220, + 0xe071430c, + 0x058b8d80, + 0x75adb7a4, + 0xc03e090a, + 0xd2a42afc, + 0xffd45921, + 0x9e839be2, + 0x8d80e071, + 0x295ed3f2, + 0x91e0c000, + 0x8d80e071, + 0xc00e058b, + 0x09220d7e, + 0x9bd3ffd4, + 0xe0719e83, + 0xc00e8d80, + 0x09220d7e, + 0x9180c007, + 0xaa4df010, + 0xd0114a7d, + 0x754e2ace, + 0x90c2c007, + 0x8d80e071, + 0x0d02058b, + 0xffd40906, + 0x9e839bbc, + 0x8d80e071, + 0x0d02010b, + 0x92c0c006, + 0x5614b740, + 0x8d80e071, + 0x9e54058b, + 0xc2400922, + 0x0d04a946, + 0x2d7cc00e, + 0x9100c006, + 0x430cb720, + 0x8d80e071, + 0xb784058b, + 0x09066cad, + 0x0d027502, + 0x0d22d001, + 0x9340c005, + 0x430cb720, + 0x7ea9b785, + 0xd0017500, + 0xc0000a46, + 0xb78590e2, + 0x9e497f29, + 0x0a044242, + 0x294ed3f2, + 0x8d80e071, + 0x090a058b, + 0x9080c005, + 0x5614b740, + 0x8d80e071, + 0x9e52058b, + 0xa946c220, + 0xc0040916, + 0xb7209320, + 0xe071430c, + 0x058b8d80, + 0x7f31b745, + 0x45220906, + 0x91c0c004, + 0x430cb720, + 0x67adb780, + 0xc0007500, + 0xb78690c4, + 0xc00050ad, + 0xb7819380, + 0x75045dad, + 0x9164c000, + 0xb7860902, + 0xd0126bad, + 0xc8127088, + 0xb5407088, + 0xb72067ad, + 0xb781430c, + 0x75045dad, + 0x9122c000, + 0xb7460a02, + 0xd0126dad, + 0xb5807104, + 0xb7a067ad, + 0x0d82430c, + 0xb760008b, + 0xfa0867ad, + 0xc354a921, + 0x008b9a64, + 0x67b5b500, + 0x430cb720, + 0x8d80e071, + 0xb740058b, + 0xb72067b5, + 0x095e682d, + 0x1115d120, + 0x90e0c001, + 0x430cb720, + 0x8d80e071, + 0xb740058b, + 0xc00067b5, + 0xb78093a0, + 0xb720558c, + 0xc240430c, + 0x0103aa45, + 0x4ab5b746, + 0xc0c47500, + 0xc0000960, + 0xd00890c2, + 0xc000aa41, + 0xd00890c0, + 0xc101aa41, + 0xe0710244, + 0xb5868d80, + 0x058b4aad, + 0xffd4095e, + 0xc0019b63, + 0xb72090a0, + 0xb784430c, + 0x75066cad, + 0x1124d011, + 0x9282c000, + 0x67adb784, + 0xc0c40103, + 0x75000960, + 0x9142c000, + 0x4badb781, + 0xa941d008, + 0x61290a04, + 0x9080c000, + 0xa941d008, + 0x8d80e071, + 0x292ed3f2, + 0x091e058b, + 0x9adfffd4, + 0x7d6cb77f, + 0x9e729eb3, + 0x8d08e052, + 0x9bb1ffd4, + 0xb79f0806, + 0xb7bf7d6e, + 0xb7df7dee, + 0xc0027e6e, + 0x9c228c40, + 0xc002a61d, + 0x9e5f8440, + 0xabc6f031, + 0x0a72d011, + 0x5a30c200, + 0x0d829ea6, + 0x0a00c040, + 0x0f00c021, + 0xa191a215, + 0x7decb55f, + 0x7d74b53f, + 0xb57fa099, + 0x9e567c74, + 0x7a74b57f, + 0x7af4b57f, + 0x7b74b57f, + 0x7bf4b57f, + 0x0f50c040, + 0x0a02c801, + 0xf0009dee, + 0x0687a261, + 0xc002a19e, + 0xf2089200, + 0x028daa41, + 0x75000b10, + 0x9102c000, + 0xc0007504, + 0x750290a2, + 0x9284c000, + 0x09d8d011, + 0xa962d008, + 0x0c827504, + 0xe0d19ebb, + 0xd0028d00, + 0xffd40c92, + 0x58089a38, + 0x08060360, + 0x9200c000, + 0x7decb75f, + 0x7d74b73f, + 0xe0d29ebb, + 0x018b8d00, + 0x9ba0ffd4, + 0x1e72d011, + 0x03c0d020, + 0xa9a1f208, + 0xc00074c0, + 0xa19d90a4, + 0x91e0c000, + 0xc00074d8, + 0xa9199124, + 0x0e860d06, + 0xa142d808, + 0x9220c000, + 0xc0007740, + 0xa91d9144, + 0x02200e82, + 0x2a7cc00e, + 0xc000a21d, + 0xd11090c0, + 0xd3f20651, + 0xb79f2ace, + 0x711f7a6c, + 0x91e6fffd, + 0xc0007740, + 0xa91d9322, + 0x1e52d011, + 0xc1f05920, + 0x2a7c2900, + 0xa9113244, + 0x32445940, + 0xc021a915, + 0xc0080900, + 0x9dad0900, + 0xa261f000, + 0x0a06a91a, + 0xa241d810, + 0xd808a919, + 0x7500aa41, + 0x9284c000, + 0xb79fa915, + 0xc0217c6c, + 0x09700900, + 0x2a7ec7ff, + 0xf0009dad, + 0xb79fa261, + 0xd1107aec, + 0x9dee1679, + 0xa261f000, + 0x7becb71f, + 0x7b6eb79f, + 0x7beeb7bf, + 0x7c6eb7df, + 0x7ceeb7ff, + 0x8c40c004, + 0xa61d9c22, + 0xc1800d84, + 0x9e745f30, + 0xc0409e76, + 0x9ea50a00, + 0xc021078d, + 0xc0210b00, + 0x02870f80, + 0x0e80c021, + 0x0b82c008, + 0xc0400b40, + 0xc0000fd0, + 0xc03c93e0, + 0xc01e757e, + 0xb3540a7e, + 0xc2004434, + 0x12d85914, + 0x2900c3fe, + 0xf0009dfe, + 0x9d5ea161, + 0x9dedaa61, + 0xa3e1f000, + 0x9dde3a40, + 0xa261f000, + 0x09929eba, + 0x010f058d, + 0x9afbff94, + 0xffff7540, + 0xc0089044, + 0x9ded0a02, + 0xa261f000, + 0x7e6eb79f, + 0x7eeeb7bf, + 0x7f6eb7df, + 0x7feeb7ff, + 0x8c00c002, + 0xa6059c22, + 0x5db00d84, + 0xc0089e5d, + 0xc0210e82, + 0x0ac00a80, + 0xf0009ddd, + 0x9e5aa2e2, + 0x0900c021, + 0x0900c040, + 0xa9629d2d, + 0xd05174c0, + 0xc0003e20, + 0xd0d19082, + 0x9dad3e20, + 0xa261f000, + 0x09929e6a, + 0xff94050b, + 0x9ddd9ac6, + 0xa2e2f000, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xc008a61d, + 0xf2128420, + 0x9e5e8c88, + 0x03850705, + 0x9e6a9e9a, + 0xc0080a82, + 0xb5bf0982, + 0xffd45e6a, + 0x9eb39872, + 0x9eb99e72, + 0x8c80e231, + 0x050b018b, + 0x9ab6fff4, + 0x018b9eb3, + 0x9bb9fff4, + 0x75eeb79f, + 0x766eb7bf, + 0x76eeb7df, + 0x776eb7ff, + 0x8c20c00a, + 0xb7209c22, + 0x09824f0c, + 0xb9600c96, + 0x9e8a4038, + 0x01030203, + 0x0a39cfca, + 0x0929cfdc, + 0x0d09cfac, + 0x40a9b57f, + 0x4129b57f, + 0x4029b560, + 0x64a9b575, + 0xa082f208, + 0xa0c2f008, + 0xa1c1f010, + 0x08a0c146, + 0x90e1ffff, + 0x0c7f9e99, + 0x4608b560, + 0x91c0c000, + 0x430cb740, + 0x04a5d110, + 0x6fadb789, + 0x02446a32, + 0x0a20c138, + 0xa012d208, + 0x2e1ed3f1, + 0x71170c84, + 0x5908d224, + 0x91d8ffff, + 0x588cb740, + 0xb9600a02, + 0x9ea24058, + 0xa106c220, + 0xffff0a04, + 0x9c229381, + 0x588cb720, + 0x09029e5b, + 0x0d34d012, + 0x4038b960, + 0xd2080222, + 0x7115aa09, + 0x90c4c000, + 0x297cc00e, + 0x90c0c000, + 0xffff0904, + 0x090292a1, + 0x588cb780, + 0x01280904, + 0x0a049ea2, + 0x11a8d000, + 0x90e0c000, + 0xaa41d008, + 0xa245d008, + 0xffff1904, + 0xd0119361, + 0xd0100a34, + 0x9c22a249, + 0xc912a61d, + 0xc9129cc4, + 0xb7a09cce, + 0xb7a05814, + 0xf010478c, + 0x0507a1ed, + 0x1fd6d032, + 0xd0510b86, + 0x0f020b50, + 0x167ad010, + 0x4a7d9e6a, + 0x9c83c810, + 0xaa5dd1e9, + 0xd0206a52, + 0xf01001ca, + 0x7500aa6d, + 0x93a4c000, + 0x0e20d051, + 0xa003f208, + 0xa3edf010, + 0x0e38d011, + 0xa803f208, + 0xaa61d050, + 0xa961f010, + 0x4508b580, + 0x9c629e94, + 0xa843f208, + 0xb5c09eaa, + 0xf2084510, + 0xfffea3ad, + 0x750292e0, + 0x91c4c000, + 0x0e20d051, + 0xa003f208, + 0x0e30d051, + 0xa803f208, + 0xaa61d050, + 0x9240c000, + 0x9181fffe, + 0x9200c003, + 0x0e20d051, + 0xa003f208, + 0x4b8cb780, + 0xa803f208, + 0x478cb780, + 0xaa01d248, + 0x4508b580, + 0x9120c003, + 0x0a02c021, + 0x9d4d0a10, + 0xdfe0aa61, + 0xc0007d00, + 0xc10090c2, + 0xc0010a82, + 0xb7609120, + 0x0b0a4688, + 0x74c0010d, + 0x0a02c021, + 0x0a00c030, + 0x1962d001, + 0xa9629d4d, + 0x9e512d71, + 0x3122290c, + 0xf0009dcd, + 0xc100a161, + 0xc017080a, + 0x74c09c81, + 0x90a4c000, + 0xff949e9b, + 0xc171996d, + 0xc8179c81, + 0x9eb39c84, + 0x2afccffe, + 0x9964ff94, + 0xff940d8a, + 0xc0219961, + 0xc0300902, + 0x9d2d0900, + 0x3a0caa61, + 0xf0009dad, + 0xb740a261, + 0x018b4a8c, + 0x2954d012, + 0x2980c100, + 0xb9600d86, + 0xf0084050, + 0x7506aa41, + 0x90a4c000, + 0xc00074c0, + 0x750490c0, + 0x90a4c000, + 0xb41a7680, + 0x19504a24, + 0x9221ffff, + 0x9160c000, + 0x478cb780, + 0xaa0df208, + 0xfffc7502, + 0xfffc93a4, + 0xb79f9160, + 0xb7bf7e6e, + 0xb7df7eee, + 0xb7ff7f6e, + 0xc0027fee, + 0x9c228c00, + 0xb780a605, + 0x9e5b4508, + 0x4794b760, + 0xb7a06a52, + 0xd1204488, + 0xfff401c7, + 0x9eab9b23, + 0x7f6eb79f, + 0x7feeb7bf, + 0xffb38c40, + 0xa60592e0, + 0x9080c000, + 0x9be8fff4, + 0x430cb720, + 0x47a9b7aa, + 0xffb49eab, + 0x700a9a86, + 0xffff0d92, + 0xb79f92a2, + 0xb7bf7f6e, + 0x8c407fee, + 0x9180ffb4, + 0xc002a61d, + 0x9e5e8400, + 0xa116a199, + 0xc000a111, + 0xfff49080, + 0xb7809bcb, + 0x0d924408, + 0x7d3ec01c, + 0x9324ffff, + 0x4408b780, + 0x0d02c021, + 0x0d10c010, + 0xb5800a04, + 0x9d2e4408, + 0xc021ab62, + 0xc0100902, + 0xd0720920, + 0x9d2d2eee, + 0x9e55aa61, + 0x29ced071, + 0xc0000385, + 0xfff49180, + 0x9d5d9ba9, + 0xd072ab62, + 0x9d7d2eee, + 0xd071aa61, + 0xd01229ce, + 0xd0710fd2, + 0x0d8a2e7e, + 0xffff7106, + 0x0a029202, + 0x7c6cb59f, + 0x7cecb59f, + 0x7d6cb59f, + 0x7decb59f, + 0xb9600389, + 0xaa154078, + 0xe0939d7d, + 0xc2708d00, + 0xf010a905, + 0x0b848026, + 0xa101d208, + 0x92a1ffff, + 0x9eb39dcb, + 0xff940982, + 0x9eb398aa, + 0x8d00e092, + 0xff940982, + 0xaa9198cd, + 0x4c8cb780, + 0x5e88c280, + 0x6aa2c146, + 0x02d89eb3, + 0x0aa0c050, + 0xa922f208, + 0x7decb79f, + 0xe050018f, + 0x0902a94a, + 0xff94a21d, + 0x9eb39a3d, + 0x2f01cffe, + 0x9a52ff94, + 0x9eb3aa1d, + 0x8d00e092, + 0x09824a7d, + 0x7decb59f, + 0x987fff94, + 0xaaa1f208, + 0x9eb39e6c, + 0xa926e240, + 0x0902018f, + 0x9a3dffb4, + 0xff949eb3, + 0xc0049a3b, + 0x0a0277c0, + 0x4824b374, + 0x2a7c9e72, + 0xc0213244, + 0xc0100902, + 0x9dad0910, + 0xa261f000, + 0xe0929eb3, + 0x09828d00, + 0x9886ff94, + 0xb79fa91f, + 0x71357dec, + 0x92c4ffff, + 0x4408b780, + 0x1a04a919, + 0xb5807480, + 0xc0004408, + 0xb7809164, + 0xc0a4430c, + 0xd2080a10, + 0x7500aa01, + 0x9102c000, + 0xff940d8e, + 0x0d8a9837, + 0x9834ff94, + 0x7c6eb79f, + 0x7ceeb7bf, + 0x7d6eb7df, + 0x7deeb7ff, + 0x8c00c004, + 0xf8399c22, + 0x8440a205, + 0x9e9b9e5a, + 0x5a20c100, + 0xc7f09e53, + 0xe0522a00, + 0xc0018d00, + 0xa2113a04, + 0xfff4a096, + 0xf9f89b1f, + 0x9c22aa15, + 0xa205f839, + 0x0a028440, + 0x0a20c146, + 0x4c8cb720, + 0x00c26229, + 0x5aadb783, + 0x5ca0c180, + 0x5b35b703, + 0x2a7cc006, + 0xb7039ea3, + 0xc7f05bad, + 0xb7232c80, + 0x34965c2d, + 0x9e539e9b, + 0x8d00e052, + 0xa019a016, + 0xa092a09d, + 0x9af8fff4, + 0xaa15f9f8, + 0xa6059c22, + 0x0d80c021, + 0x06b7d110, + 0xc0000685, + 0xb78091c0, + 0x0a044688, + 0x4688b580, + 0x9abafff4, + 0x4688b780, + 0xb5801a04, + 0x9d5d4688, + 0x791baa61, + 0xffff0d8e, + 0xb79f9202, + 0xb7bf7f6e, + 0x8c407fee, + 0xa6059c22, + 0xc4019e5c, + 0xc0210902, + 0x9e5d0a00, + 0x0900c008, + 0x9dcd0a20, + 0xa161f000, + 0xc0080992, + 0xc0210d02, + 0xfff40a80, + 0x0ac09bce, + 0x0a02c008, + 0xf0009ddd, + 0xb79fa261, + 0xb7bf7f6e, + 0x8c407fee, + 0xa61d9c22, + 0xb7208440, + 0xd132430c, + 0x06876f31, + 0x46b1b76a, + 0x04e3d110, + 0x05d60b02, + 0x4294b560, + 0x4c2bb5c1, + 0x430cb780, + 0xd0120c06, + 0xd1100d32, + 0x5d300669, + 0x0a48c012, + 0xa301da08, + 0x430cb720, + 0xd1109e52, + 0xc02104e3, + 0xb5c00900, + 0xb78066ad, + 0xc01a430c, + 0xd1100920, + 0xc0120669, + 0xf2080a60, + 0xb720a301, + 0xd110430c, + 0xb50004e3, + 0xb78065b5, + 0xd110430c, + 0xc0120669, + 0xd2080a20, + 0x9dada301, + 0xa361f000, + 0xc0089e54, + 0xc0210902, + 0x0a400a00, + 0xf0009dcd, + 0xb7a0a161, + 0x9e51430c, + 0x0d00c040, + 0x046ac101, + 0x0d80d3f1, + 0x604cb780, + 0xa965f008, + 0x0880c021, + 0x08c0c046, + 0x9d9d0244, + 0xa261f000, + 0x0d00c021, + 0xf0009dae, + 0xb702a361, + 0xd0105450, + 0xe0530636, + 0xc1018d00, + 0xc2000240, + 0x9d4d5a08, + 0xf010008b, + 0x050d81a6, + 0x5cadb700, + 0xa9e1f008, + 0xa8e1f208, + 0x00ddd120, + 0x0d40c012, + 0x0155d120, + 0xc0109e42, + 0x9e840c90, + 0xb7209c62, + 0x7740430c, + 0x04e3d110, + 0x67adb5c0, + 0x4314b720, + 0x04e2d010, + 0x714db780, + 0x5f2db720, + 0xb5800242, + 0xc000714d, + 0x9eb790e4, + 0xa061f208, + 0x9240c008, + 0x4294b760, + 0xd0110d7f, + 0xc2000e32, + 0x01095a30, + 0x0900c021, + 0x0940c00e, + 0xf0009dad, + 0x0910a162, + 0xf0009dad, + 0x0910a361, + 0xf0009dad, + 0xc021a361, + 0xc00e0a00, + 0x9dcd0a70, + 0xa361f000, + 0x430cb720, + 0x2dfcc00e, + 0xd110010d, + 0x09c204e3, + 0x5fadb720, + 0x1910d052, + 0x7d6eb79f, + 0x7deeb7bf, + 0x7e6eb7df, + 0x7eeeb7ff, + 0x8c40c002, + 0x9280ff84, + 0x4729b7ea, + 0xc380070f, + 0x02895a30, + 0x0a80c021, + 0x0a80c116, + 0x9100c002, + 0x5f4db780, + 0xc0027500, + 0xb7809002, + 0x750067cd, + 0x9162c000, + 0xb5e09eab, + 0xcfdf67d5, + 0xcfea0dfc, + 0xfff40d80, + 0xb7409af0, + 0xd0914314, + 0xc1016e59, + 0xb74200e4, + 0x02445429, + 0x74800248, + 0x00c5d120, + 0x91e2c000, + 0x4c4bb781, + 0x0902c201, + 0x0900c004, + 0x5a40c200, + 0x2a7cc00f, + 0xc0003244, + 0xb7819140, + 0xc2004c2b, + 0xc00f5a40, + 0xc2012a7c, + 0xb5803a00, + 0xb78066ad, + 0xd131430c, + 0x00986cd1, + 0x66adb720, + 0xf0009ddd, + 0x0f04a0e1, + 0x0a81cf00, + 0x430cb720, + 0x117cc101, + 0x46a9b78a, + 0x12a8d020, + 0xd1317088, + 0xd0206f51, + 0xfffd00e2, + 0xb7ca91b6, + 0xc0034729, + 0xd0209200, + 0xd1321368, + 0xd0916ee1, + 0xd0106d69, + 0x9ea806d2, + 0x5f4cb780, + 0xc0037500, + 0xb7229022, + 0xb7605448, + 0x00a25e4d, + 0xc1010092, + 0xb7610092, + 0xc3344c33, + 0x008b98c4, + 0x67adb500, + 0x430cb720, + 0x04d3d110, + 0x0910d3f1, + 0xaa41f008, + 0x67adb720, + 0xf0081242, + 0xb720a241, + 0xd0104314, + 0xb78004d2, + 0xb720714d, + 0x124267ad, + 0x714db580, + 0x430cb780, + 0x0659d110, + 0xc0120089, + 0xb78008a0, + 0x75004029, + 0x90c2c000, + 0x4031b5e0, + 0x90a0c000, + 0xb5400906, + 0xb7004029, + 0xd1324314, + 0xd0106d61, + 0xb76004a0, + 0x74c05f2d, + 0x9382c000, + 0x5429b742, + 0xe053026c, + 0x02448d00, + 0x5a08c200, + 0xb7009d4d, + 0xf0105ccc, + 0x048480a6, + 0xa8a1f208, + 0x0d40c012, + 0xc0109eb3, + 0x05040c90, + 0x9c629e84, + 0xa021f208, + 0xb7201b04, + 0xb78a4314, + 0x718846c9, + 0x91b6fffc, + 0x430cb720, + 0x712db740, + 0xfff97480, + 0xb78a9024, + 0x028546a9, + 0x9220c000, + 0x67adb780, + 0xc0007500, + 0xd0129122, + 0xb5a009a2, + 0x5db067ad, + 0x9a1dfff4, + 0x428cb780, + 0xb7200a04, + 0xb580430c, + 0xb740428c, + 0xb78a428c, + 0xb74a4729, + 0x708846b1, + 0x2e2ed3f1, + 0xc0041228, + 0x00c26a22, + 0x9014ffff, + 0x2e2ed3f1, + 0x0f8ec001, + 0xc0020b02, + 0xc0010bc2, + 0xce000f0a, + 0xb5800f80, + 0xc005428c, + 0xb78a93a0, + 0x122846a9, + 0x6ac1d132, + 0x04d3d110, + 0x65adb780, + 0xc0057500, + 0xd0129182, + 0xb5c009a2, + 0x5db067ad, + 0xc0219e5c, + 0xc0400a00, + 0x9dcd0a00, + 0xa3e1f000, + 0x99ddfff4, + 0x428cb7a0, + 0x0952d011, + 0x02055930, + 0x0a00c021, + 0x0a20c040, + 0xaa619d4d, + 0x430cb720, + 0x9e929e7b, + 0x04d3d110, + 0x0d00c021, + 0x65adb580, + 0x430cb720, + 0x0d40c046, + 0x04d3d110, + 0x65adb780, + 0xc2002246, + 0xb5805a35, + 0x9d2e672d, + 0xb720a962, + 0x9e91430c, + 0x04d3d110, + 0x0c80c021, + 0x65b5b540, + 0x430cb720, + 0x0cc0c00e, + 0x04d3d110, + 0xb7409e8b, + 0xc01065b5, + 0xf0100d80, + 0xb760aa61, + 0xb720672d, + 0xc1015fad, + 0x12460244, + 0xf0101242, + 0xb720a261, + 0xd110430c, + 0xb78004d3, + 0x9d9e602d, + 0xa261f000, + 0xc0210205, + 0xc00e0a00, + 0x9dcd0a50, + 0xa361f000, + 0x9dcd0a10, + 0xa361f000, + 0x9dcd0a10, + 0xa361f000, + 0x0900c021, + 0x0930c00e, + 0xf0009dad, + 0xb720a362, + 0xc00e5fad, + 0x9eab2afc, + 0x1910d052, + 0x010d09c2, + 0x9af2ff74, + 0xff749eab, + 0xb7809b09, + 0xd110430c, + 0xc0100659, + 0xf2080a00, + 0xb780a301, + 0xd110430c, + 0xc00e0659, + 0xf2080a60, + 0xb780a305, + 0xd110430c, + 0xc00e0659, + 0xf2080a60, + 0xb720a301, + 0xd110430c, + 0xb5c004d3, + 0xb72067ad, + 0xb5c0430c, + 0xb720712d, + 0xd110430c, + 0xb5c004d3, + 0xb78065ad, + 0x0a04428c, + 0x428cb580, + 0x430cb720, + 0x428cb740, + 0x4729b78a, + 0xfff97088, + 0xb79f93d4, + 0xb7bf7d6e, + 0xb7df7dee, + 0xb7ff7e6e, + 0xc0027eee, + 0x9c228c40, + 0xb720a605, + 0x9e5d430c, + 0x4829b78a, + 0x22d8d020, + 0xc0007346, + 0xc0009244, + 0xffd49080, + 0xb7209bc1, + 0x0d92430c, + 0x48a9b78a, + 0xffff7b59, + 0xc0009304, + 0xffd49380, + 0xb7209bb5, + 0xb740430c, + 0x0d924588, + 0x4829b78a, + 0x22444a7d, + 0xffff790a, + 0x322a9284, + 0x4588b580, + 0x4829b78a, + 0xb58a3258, + 0xb7204829, + 0xb78a430c, + 0x325848a9, + 0x0d82c021, + 0x48a9b58a, + 0x0da0c030, + 0xa9629d3e, + 0x4488b740, + 0x59080a3e, + 0x5208c200, + 0x51a8c200, + 0x4a7d21b8, + 0x25229ea1, + 0x35229e99, + 0xf0009dbe, + 0xb79fa162, + 0xb7bf7f6e, + 0x8c407fee, + 0xa6059c22, + 0x478cb780, + 0xb7400906, + 0xf2084c14, + 0xf010a10d, + 0x0a10a043, + 0xa803f208, + 0x4a8cb7a0, + 0xb5800a02, + 0xd3724508, + 0x010b1ad8, + 0xaa4df1e9, + 0xc000750c, + 0x0d9290e2, + 0x9b60ffd4, + 0x92e0ffff, + 0xffff709b, + 0xb78092a4, + 0xf2084c0c, + 0xb79fa803, + 0xb7bf7f6e, + 0x8c407fee, + 0xb7409c22, + 0x0d064b0c, + 0x4058b960, + 0xaa41f008, + 0xb4127508, + 0x09504a22, + 0x9341ffff, + 0xb7209c22, + 0xb785430c, + 0x750044ab, + 0x9142c000, + 0x48a9b78a, + 0x42289e5a, + 0x48a9b58a, + 0x9300c000, + 0xb74a9e5c, + 0x4a7d4829, + 0x4000ba24, + 0xb54a2144, + 0xb7204829, + 0xb74a430c, + 0x224448a9, + 0x48a9b58a, + 0x4588b780, + 0x42289e5a, + 0x4588b580, + 0x0982c021, + 0x09a0c030, + 0xa9629d3d, + 0x4488b740, + 0x2e3ed031, + 0xc2005908, + 0x4a7d5208, + 0x25269ea3, + 0xf0009dbd, + 0xfffda162, + 0xf8399340, + 0xb720a205, + 0xb783430c, + 0xb58a5b2d, + 0xb72047a9, + 0xb783430c, + 0xc2005b2d, + 0xb58a5a21, + 0xb7204729, + 0xb786430c, + 0xb58a76ab, + 0xb72046a9, + 0xb78d430c, + 0xb58a6da9, + 0xb7204629, + 0xb76a430c, + 0xfff447b1, + 0xb7209b19, + 0x0982430c, + 0xb74a0dff, + 0xba1246b1, + 0xc0004001, + 0xb7899140, + 0x6a326fcd, + 0xc1380242, + 0xd2080a20, + 0xb720a192, + 0xc101430c, + 0xc2000234, + 0xd0205908, + 0xb74a00a2, + 0x09844729, + 0xffff7104, + 0xb76a919c, + 0xf9f847b1, + 0xfffcaa1d, + 0xa60d9000, + 0x8400c004, + 0xc0210705, + 0x03070d02, + 0xc0309e95, + 0x9d2e0d00, + 0x0205a961, + 0x2a70c03e, + 0xcfc00a10, + 0xc03e290d, + 0x32442a70, + 0xf0009dae, + 0xc021a261, + 0xc0300902, + 0x9d2d0920, + 0xc180aa61, + 0x9e515d60, + 0x2a7ec00f, + 0x9dad3242, + 0xa261f000, + 0x76d6097f, + 0xa121d210, + 0x90a4c002, + 0x2a3ed3f1, + 0xd1a4750c, + 0xc0105921, + 0xd2269312, + 0xb720590c, + 0x8520430c, + 0x8044e050, + 0x91c0c000, + 0x91a0c010, + 0x91c0c000, + 0x9340c000, + 0x9380c000, + 0x90a0c010, + 0x9380c000, + 0x4835b545, + 0x93e0c00f, + 0x0a060103, + 0x0970c0a2, + 0xa241d008, + 0x430cb780, + 0x0a74c0a2, + 0xa102d208, + 0x9220c00f, + 0x6735b544, + 0x91a0c00f, + 0x68b5b544, + 0x9120c00f, + 0x5e30d122, + 0x2d7cc03e, + 0x5d18d122, + 0x2a00cffd, + 0xb5813244, + 0xc00e482d, + 0x76d89380, + 0x9084c002, + 0x430cb720, + 0x5e2db781, + 0xc0017500, + 0xd3f191a2, + 0xc01c2a3e, + 0xb59f753e, + 0xc0006268, + 0xc18091c2, + 0xc2005a21, + 0xb59f5a0c, + 0xb55f786c, + 0xb55f6368, + 0xc00062e8, + 0xd1a491e0, + 0x09025941, + 0x5a21c180, + 0x62e8b59f, + 0x6370b55f, + 0x6268b55f, + 0x786cb55f, + 0x430cb720, + 0x8d80e112, + 0x6cadb729, + 0x9c629e8c, + 0x90e0c00d, + 0x67a9b566, + 0x430cb720, + 0x5a21c180, + 0x5941c180, + 0x6829b586, + 0x430cb720, + 0x68a9b546, + 0x92e0c00c, + 0xc00476da, + 0xffd49124, + 0x9e839a1c, + 0x9eaa0a86, + 0x2dfcc00e, + 0xd21055cc, + 0xc00ea021, + 0xfff42dfc, + 0xd2109a2d, + 0xe111a9a2, + 0x050d8d00, + 0x09a2c002, + 0x9905ff94, + 0xaa21d210, + 0x52b0c200, + 0x29ded3f2, + 0x9aa9fff4, + 0x8c80e111, + 0x430cb780, + 0x50a9b740, + 0xb75f0244, + 0xc0987068, + 0xd2080a60, + 0xb780a115, + 0xb72050a9, + 0xc200430c, + 0x00c25a08, + 0x796cb79f, + 0x63adb582, + 0x8c80e111, + 0x50a9b780, + 0x430cb720, + 0x5a08c200, + 0xb79f00c2, + 0xb58279ec, + 0xe111682d, + 0xb7808c80, + 0xb72050a9, + 0xc200430c, + 0x00c25a08, + 0x7a6cb79f, + 0x6cadb582, + 0x8c80e111, + 0x50a9b780, + 0x430cb720, + 0x5a08c200, + 0xb79f00c2, + 0xb5827aec, + 0xe111712d, + 0xb7808c80, + 0xb72050a9, + 0xc200430c, + 0x00c25a08, + 0x7b6cb79f, + 0x75adb582, + 0x8c80e111, + 0x50a9b780, + 0x430cb720, + 0x5a08c200, + 0xb79f00c2, + 0xb5827bec, + 0xe1117a2d, + 0xb75f8c80, + 0xb7807c6e, + 0xb72050a9, + 0xc200430c, + 0x00c25a0c, + 0x61afb541, + 0x91a0c008, + 0xc00176dc, + 0xd3f192e4, + 0x75022a3e, + 0x90c2c001, + 0xc0007502, + 0x750492e6, + 0x93e4c007, + 0x430cb720, + 0x5a21c180, + 0x5941c180, + 0x4535b5c2, + 0x430cb720, + 0x6c29b588, + 0x430cb720, + 0x6ca9b548, + 0x91a0c007, + 0x430cb720, + 0x5a41c180, + 0x45b5b5c2, + 0x430cb720, + 0x6d29b588, + 0x9020c007, + 0x430cb720, + 0x5a41c180, + 0x4635b5c2, + 0x430cb720, + 0x6da9b588, + 0x92a0c006, + 0xc00376de, + 0xffd49164, + 0x9e85995a, + 0xc00e0a86, + 0x9e6b2efc, + 0x52acc200, + 0x2afcc00e, + 0xfff49eab, + 0xe111996b, + 0x050d8d00, + 0x0982c004, + 0xff94058b, + 0x058b9844, + 0x9826ff74, + 0xfff49eab, + 0xb78099ea, + 0xd032430c, + 0x0982296e, + 0x0629d110, + 0xc1369e9b, + 0xd2080a20, + 0xb720a18d, + 0xc300430c, + 0xcfc05a18, + 0xb5822a01, + 0xb7807ead, + 0xd031430c, + 0x00986ca9, + 0x78ecb79f, + 0x712db589, + 0x0624d010, + 0x796cb75f, + 0x0045d120, + 0x430cb780, + 0x2900c01e, + 0xc1365915, + 0xd2080a28, + 0xc000a101, + 0xb78090c0, + 0xb589404d, + 0xd3f170ad, + 0xb7602d3e, + 0xc100430c, + 0x9d4d5a08, + 0xc1360207, + 0xd2080a28, + 0xd110a902, + 0xe1130605, + 0xc2008d00, + 0x72955a08, + 0x80a6e020, + 0x0d8400c6, + 0x90b2ffff, + 0x9120c003, + 0xc00176ea, + 0xf01190a4, + 0xc0002a32, + 0xffd49362, + 0x9e8398ea, + 0x9eaa0a86, + 0x2dfcc00e, + 0xc00e55cc, + 0xd2102dfc, + 0xfff4a021, + 0xb74098fb, + 0xd210430c, + 0x050da9a2, + 0x0920c046, + 0x09d2c002, + 0x9040c001, + 0x430cb720, + 0x4f2db582, + 0x9060c002, + 0xc00176ec, + 0xffd490a4, + 0x9e8398c8, + 0x9eaa0a86, + 0x2dfcc00e, + 0xc00e55cc, + 0xd2102dfc, + 0xfff4a021, + 0xb74098d9, + 0xd210430c, + 0x050da9a2, + 0x0970c048, + 0x0982c002, + 0x9bafff74, + 0xaa21d210, + 0x52b0c200, + 0x29ded3f2, + 0x9953fff4, + 0x93a0c000, + 0xc00076e0, + 0xb7809164, + 0x0906430c, + 0x0a10c0a4, + 0xa101d208, + 0x9220c000, + 0xc00076c6, + 0xc2d490c4, + 0xc0009a1f, + 0xb7409140, + 0xd1a2598c, + 0xe2205e08, + 0x9ea4aa05, + 0xb7209c62, + 0x0203430c, + 0x0a10c0a4, + 0xaa01d208, + 0xc0007500, + 0x0a029222, + 0x44abb585, + 0x430cb720, + 0x4829b78a, + 0x48b1b76a, + 0x45b49ea2, + 0x2dfcc00e, + 0x991dfff4, + 0x7aeeb79f, + 0x7b6eb7bf, + 0x7beeb7df, + 0x8c60c004, + 0xa61d9c22, + 0x8400c002, + 0xd050a192, + 0x0c82aa61, + 0x0ca0c146, + 0x9ea51a08, + 0x4c8cb780, + 0x2efcc00e, + 0xb7e064d3, + 0x9e4a4794, + 0x03a8a096, + 0x9eba9eb9, + 0x0882010f, + 0x0c80c06c, + 0x0960c06c, + 0x0d00c06e, + 0xa09d028f, + 0xb55fa09a, + 0xb55f7dec, + 0x058b7d74, + 0x9ab6ff74, + 0x430cb780, + 0x0a38c0a2, + 0xab01d208, + 0x90a0c000, + 0xffd40d92, + 0xa99a9821, + 0xb78d008f, + 0xd01060a9, + 0xb7c0a961, + 0x70884c94, + 0x9282ffff, + 0xc0007580, + 0xb7209164, + 0xb76a430c, + 0xff7447b1, + 0x74009ab2, + 0x9104ffff, + 0xff74058b, + 0xa9199a91, + 0xd0089ea9, + 0xb76daa41, + 0x710660c9, + 0x9102c001, + 0xc180aa15, + 0xb73f5890, + 0x00987df4, + 0x009cc101, + 0x4cadb783, + 0x4db5b743, + 0x4c2db743, + 0x4d2db723, + 0x404db580, + 0xd0129ea9, + 0xb52309b2, + 0xb79f5bcd, + 0x099a7d6c, + 0x5acdb543, + 0xa102f208, + 0x297cc006, + 0xc2f4a11d, + 0x008b9bb5, + 0x60a9b50d, + 0xff74058b, + 0xb7209a5f, + 0xa99e430c, + 0x8d00e091, + 0x5bb5b743, + 0x5b2db763, + 0x990bfff4, + 0x7068b79f, + 0x753ec01c, + 0x90c4c000, + 0x9a78ff74, + 0x7068b51f, + 0x7070b77f, + 0x55ac0c86, + 0x2dfcc00e, + 0x9beaffd4, + 0x4490b760, + 0x4c8cb740, + 0x7068b77f, + 0xc1469e5c, + 0x00c46a22, + 0x5ab5b743, + 0x5d3d9e5a, + 0xffd42d04, + 0xb77f98b9, + 0x0d067070, + 0xc00e55cc, + 0xfff42dfc, + 0xf250985e, + 0x7508aa61, + 0x90a4c000, + 0xf2500d86, + 0xb720a1e2, + 0xb740430c, + 0xb7604914, + 0xf0104e8c, + 0x7508aa41, + 0x91c4c000, + 0x47a9b78a, + 0xa961d008, + 0xc01c2244, + 0xc0007d3e, + 0x0a0690a2, + 0xa241f010, + 0x4894b720, + 0xc1460d50, + 0x728209a0, + 0x9124ffff, + 0x7482a91d, + 0x90e4fffa, + 0x4794b740, + 0xa141f050, + 0x4608b780, + 0x0a04a992, + 0x4608b580, + 0xf0100a1a, + 0xb79fa26d, + 0xb7bf7c6e, + 0xb7df7cee, + 0xb7ff7d6e, + 0xc0047dee, + 0x9c228c00, + 0xc006a61d, + 0xb7208440, + 0xf1d2430c, + 0xb7838d00, + 0xb7c35c2d, + 0xa2155b2d, + 0x5badb783, + 0x0a06a219, + 0x5218c200, + 0xb7239ea5, + 0xc00e5aad, + 0x058b2efc, + 0xffd4a091, + 0xb7209b73, + 0xe1d14314, + 0xb9608d00, + 0xb7ed4028, + 0xb7ed6049, + 0x9e4960d1, + 0x4c2db783, + 0xa241f008, + 0x4cadb783, + 0xa245f008, + 0x4d2db783, + 0xa249f008, + 0x4dadb783, + 0xf00808c0, + 0x0940a24d, + 0x91c1ffff, + 0xb5aa0a82, + 0xb7204849, + 0xd3f2430c, + 0xc00629ee, + 0xb5aa0992, + 0xa91a48a9, + 0x430cb740, + 0x9a25ff74, + 0x430cb720, + 0xb5aa058b, + 0xb7204831, + 0xb5aa430c, + 0xb78048b1, + 0xc0a4430c, + 0xd2080a10, + 0xb720a281, + 0xb5a5430c, + 0xffd444ab, + 0xb7209bbc, + 0x9eaa430c, + 0x4028b960, + 0x6029b5ed, + 0x430cb720, + 0x60b1b5ed, + 0x430cb720, + 0xaa41f210, + 0x5c90c100, + 0x0493d110, + 0xb5830d04, + 0xb7204c2d, + 0xf210430c, + 0xd110aa45, + 0xb5830493, + 0xb7204cad, + 0xf210430c, + 0xd110aa49, + 0xb5830493, + 0xb7804d2d, + 0xf210430c, + 0x0f40a94d, + 0x0498c101, + 0x4dcdb543, + 0x9381fffe, + 0x430cb720, + 0xb583aa11, + 0xb7205aad, + 0xb5c3430c, + 0xb7205b2d, + 0xaa19430c, + 0x5badb583, + 0x430cb720, + 0xb583aa15, + 0xb7205c2d, + 0xb780430c, + 0xb58a70a9, + 0xb72047a9, + 0xb780430c, + 0xb58a7029, + 0xb7204729, + 0xb780430c, + 0xb58a6fa9, + 0xb72046a9, + 0xb780430c, + 0xb58a6f29, + 0xb7204629, + 0xb780430c, + 0x7504572b, + 0x91d2c001, + 0x0a42c809, + 0x0a30c0ec, + 0x5cadb580, + 0x430cb720, + 0x0a42c809, + 0x0a40c06c, + 0x5d2db580, + 0x430cb720, + 0x44b5b760, + 0x46b5b740, + 0x452db740, + 0x472db780, + 0x572bb760, + 0x6127c101, + 0x6245c101, + 0xd02074c0, + 0xc00102c4, + 0xb7409024, + 0xb78048ad, + 0x6245492d, + 0x06d8c101, + 0x9300c000, + 0x0a42c809, + 0x0a30c0aa, + 0x5cadb580, + 0x430cb720, + 0x0a42c809, + 0x0a40c080, + 0x5d2db580, + 0x430cb780, + 0xa905f248, + 0xaa09f248, + 0x62c4d032, + 0x430cb720, + 0x0a02c100, + 0xb5809e6b, + 0xb7a05ead, + 0x008b430c, + 0x5eb5b760, + 0x99d7c2f4, + 0xb500008b, + 0xb7a05ead, + 0xf248430c, + 0x7500aa35, + 0x9242c000, + 0xb760008b, + 0x01895eb5, + 0x61c7d132, + 0x2d81cffc, + 0x99c3c2f4, + 0xc0007400, + 0x008b90a2, + 0x5eadb500, + 0x430cb720, + 0x5e19d2a2, + 0x5dadb580, + 0x4314b740, + 0xaa4df050, + 0xa945f050, + 0x1a040244, + 0xa24df050, + 0x430cb7a0, + 0xaaa6f248, + 0xa9aef248, + 0xc2f49e6b, + 0xc10199a4, + 0xf248600b, + 0xb720a02d, + 0xb720430c, + 0xb78046b5, + 0xb740472d, + 0xb7604535, + 0xb74044b5, + 0xb760492d, + 0xc10148ad, + 0x65276243, + 0xc1016127, + 0x02440244, + 0x5a19c200, + 0x5e2db580, + 0x430cb740, + 0xaa45f008, + 0x7d3ec004, + 0x90e4c001, + 0xaa51f008, + 0x2a7ce002, + 0x9024c001, + 0x01899ea2, + 0x91c0c000, + 0x5f4db560, + 0x430cb720, + 0xb56000a2, + 0xb72067ad, + 0x00a2430c, + 0x65adb560, + 0x430cb720, + 0x6d21d131, + 0x4629b78a, + 0x00a2d020, + 0x0d047299, + 0x9146ffff, + 0xb5800a02, + 0xb79f712d, + 0xb7bf776e, + 0xb7df77ee, + 0xb7ff786e, + 0xc00878ee, + 0x9c228c40, + 0xb7a0a605, + 0x0e86430c, + 0xb763008b, + 0xb7a35b35, + 0xc2005bad, + 0xc00e55ac, + 0xffd42dfc, + 0xb72099e5, + 0x9eaa430c, + 0x0982c008, + 0x6c31b76d, + 0x0918d191, + 0x98bbff74, + 0x430cb720, + 0x5b35b743, + 0x56a8c200, + 0x2dded3f2, + 0x9a5dffd4, + 0x430cb720, + 0x0a18d191, + 0x612db582, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xb7a0a605, + 0x0e86430c, + 0xb763008b, + 0xb7a35b35, + 0xc2005bad, + 0xc00e55ac, + 0xffd42dfc, + 0xb72099b5, + 0x9eaa430c, + 0x0982c008, + 0x6c31b76d, + 0xc0180103, + 0xff740930, + 0xb720988a, + 0xb743430c, + 0xc2005b35, + 0xd3f256a8, + 0xb79f2dde, + 0xb7bf7f6e, + 0x8c407fee, + 0x90e0ffd1, + 0xb7a0a60d, + 0xb7204994, + 0x09024c8c, + 0x0d96050b, + 0xb9600185, + 0x0a024038, + 0x0a20c146, + 0x02426229, + 0x0a18c144, + 0xa182f208, + 0xf0310904, + 0xffffa1d5, + 0xb7409281, + 0xc809478c, + 0xcbdc0a46, + 0xf0480a30, + 0xf008a1c1, + 0x0287a255, + 0xffd40b06, + 0xf23199c6, + 0xd3f1aa35, + 0x0d82295e, + 0x9eb27500, + 0xc0000a84, + 0x9e5b90e2, + 0x4508b5c0, + 0x9a44ffb4, + 0xffff7550, + 0xb79f9204, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0x5db09c22, + 0x0d80c021, + 0x0da0c102, + 0xaa619d3e, + 0xc40059b8, + 0xcbfe2980, + 0x32462a7d, + 0xf0009dbe, + 0x9c22a261, + 0x430cb720, + 0x9e5b0926, + 0x6735b744, + 0xd0110802, + 0x75101e22, + 0x4826b322, + 0x9c8bc010, + 0x9160c000, + 0xaa01d208, + 0xc0007106, + 0xc00e90a4, + 0x9c22287c, + 0x02030804, + 0x0a74c098, + 0xffff0884, + 0xc00e9261, + 0x9c22087e, + 0xb740a605, + 0xd1a2430c, + 0x00d45e88, + 0x6fadb789, + 0x02446a32, + 0x0a20c138, + 0xa911d208, + 0xd2080904, + 0xb740a111, + 0x00d4430c, + 0x6fadb789, + 0x02446a32, + 0x0a20c138, + 0xaa11d208, + 0xc0007502, + 0xc00e90d4, + 0xc001087e, + 0xc10192c0, + 0x090200a6, + 0x4829b54c, + 0x430cb720, + 0x5e18d1a2, + 0x0982c004, + 0x04b3d110, + 0x5914b760, + 0x4929b54c, + 0x01c7d120, + 0x430cb780, + 0xc05e0258, + 0xf2080a60, + 0xb720a101, + 0xb543430c, + 0xb720402d, + 0x00d2430c, + 0x40adb543, + 0x430cb720, + 0x41adb543, + 0x9a2fff34, + 0x430cb740, + 0xb78900d4, + 0x6a326fad, + 0xc1380244, + 0xd2080a20, + 0xb79fa811, + 0xb7bf7f6e, + 0x8c407fee, + 0xb7609c22, + 0x0d02430c, + 0x4078b960, + 0x0134c101, + 0xc1360205, + 0xd2080a2c, + 0x7500aa01, + 0x92a4c000, + 0x0920c136, + 0xd0080a04, + 0xb740a24d, + 0xd031430c, + 0xc00e6e29, + 0x02442d7c, + 0x0a20c138, + 0xd208097f, + 0xc000a111, + 0x0d0490e0, + 0x9041ffff, + 0x0d7ec00e, + 0x9c229e50, + 0xb720a61d, + 0x9e5f430c, + 0xc0a40203, + 0xd2080a10, + 0xb7caaa01, + 0x75004729, + 0x93a4c005, + 0x5a88c300, + 0x02d2d020, + 0xb7899e69, + 0xc01c6fad, + 0xc001753e, + 0x9eb39004, + 0xfff49eae, + 0x9e699bbc, + 0x9180c000, + 0x984cffb4, + 0x430cb7a0, + 0xd1109eb3, + 0xfff406eb, + 0x008b9bb0, + 0x6fadb509, + 0x430cb720, + 0xd1100d92, + 0xb78904e3, + 0xc01c6fad, + 0xffff753e, + 0xb7209162, + 0xc300430c, + 0xd0205908, + 0x9e8a00a2, + 0x6fcdb789, + 0x0d10c09c, + 0x00c26a32, + 0x712db729, + 0x568cb780, + 0xe0409e8b, + 0xd010a1c6, + 0x7500aa41, + 0x9284c003, + 0xc0009eb3, + 0xffb491c0, + 0xb7809819, + 0x9eb3430c, + 0x0a10c0a4, + 0xaa01d208, + 0xc0037500, + 0xfff49184, + 0xc01c9b1b, + 0x0d92743e, + 0x91e2ffff, + 0xc00175c0, + 0xb7209062, + 0x0982430c, + 0xb76a9e9a, + 0xb72a46b1, + 0xd1a24729, + 0x01095e30, + 0x0900c021, + 0x0930c140, + 0x91a0c000, + 0xf0009dad, + 0xd011a162, + 0x9dcd0a28, + 0xa162f000, + 0xc1000984, + 0xc1010900, + 0x71020236, + 0x925cffff, + 0x4314b720, + 0x5988d324, + 0x59b0c300, + 0x04b2d010, + 0x0980c021, + 0x6fb5b749, + 0x09c0c146, + 0x6d29d031, + 0x0624d010, + 0x0122c101, + 0x0920c138, + 0xa951d008, + 0x0244c101, + 0xc2000244, + 0xc1015a08, + 0xb78000c2, + 0xb7294f8c, + 0x9ea270ad, + 0xa0e9e020, + 0xf0009dbd, + 0xb786a0e1, + 0x750040c9, + 0x9184c000, + 0x53cbb78c, + 0xc0007500, + 0xb76690e2, + 0x9eb34a4d, + 0x99cbff94, + 0x430cb780, + 0xc09c0906, + 0xd2080a10, + 0xb79fa101, + 0xb7bf7e6e, + 0xb7df7eee, + 0xb7ff7f6e, + 0xc0027fee, + 0x9c228c00, + 0xb720a605, + 0x9e5d430c, + 0x63adb744, + 0xc0017ca0, + 0xb7819202, + 0x75045dad, + 0x90e4c000, + 0x5a21c100, + 0xb5842a04, + 0xb7207e2b, + 0xb781430c, + 0x1a105dad, + 0xc0007502, + 0x0a0690b2, + 0x7f2bb584, + 0x430cb720, + 0x6e29b788, + 0x9e8b0183, + 0x5333b741, + 0x6aadb749, + 0x098cc026, + 0xc04a01b8, + 0x9e940df0, + 0xb7209c62, + 0xb784430c, + 0xcffe63ad, + 0xb5842a3d, + 0xb72063ad, + 0xb78c430c, + 0x7500532b, + 0x9222c001, + 0x6cadb744, + 0xc0007484, + 0xb7819124, + 0x75004bad, + 0x9084c001, + 0x9140c000, + 0xc0007482, + 0xb78190e4, + 0x75024b2d, + 0x9344c000, + 0xc09c0203, + 0xd2080a14, + 0x7500aa01, + 0x9244c000, + 0xc0007486, + 0x02039144, + 0x0a40c09a, + 0xaa01d208, + 0xc0007500, + 0xb78190e4, + 0x750a5dad, + 0x90a4c000, + 0xb58c0a02, + 0xb720532b, + 0x0203430c, + 0x0a18c0a4, + 0xaa01d208, + 0x6cb5b764, + 0xc0007500, + 0xb74193e2, + 0xb70953ab, + 0xb76c6c2d, + 0x9eaa532b, + 0x9c629e84, + 0x430cb720, + 0x5dadb781, + 0x75021a10, + 0x90f2c000, + 0x532bb78c, + 0xc0017500, + 0x02039064, + 0x0a20c09c, + 0xa001d208, + 0x9380c000, + 0xc00076c4, + 0xb7499164, + 0x020375a9, + 0x0a20c09c, + 0xa101d208, + 0x9200c000, + 0x76c60103, + 0x0920c09c, + 0x90c4c000, + 0x7629b789, + 0x9080c000, + 0x7529b789, + 0xa241d008, + 0x430cb720, + 0x5dadb781, + 0x75021a10, + 0x90f2c000, + 0x532bb78c, + 0xc0007500, + 0xb74a9344, + 0xb76046a9, + 0x01835514, + 0x09a0c09c, + 0x9140c000, + 0xaa61d008, + 0x9e529e91, + 0xa229c030, + 0x297cc00e, + 0x4729b78a, + 0x0922d012, + 0xffff7104, + 0xb79f9268, + 0xb7bf7f6e, + 0x8c407fee, + 0xb7209c22, + 0x05964314, + 0x0da0c098, + 0xa8edd010, + 0x6e4db704, + 0x4000ba09, + 0xd001747f, + 0xb3401802, + 0x009248a2, + 0x0092c101, + 0x532bb789, + 0x2a60c00e, + 0x5a0fc200, + 0x9c220040, + 0xb7a0a61d, + 0x0687430c, + 0x020b9e9e, + 0x0a58c096, + 0xa982d208, + 0xfff49e57, + 0x020b9bda, + 0x0a54c096, + 0xa982d208, + 0xfff40301, + 0x020b9bd2, + 0x0a50c096, + 0xaa01d208, + 0x9e819ea8, + 0xc0980258, + 0xd2080a20, + 0xb744aa0d, + 0xba246e4c, + 0x753f4000, + 0x19a2d001, + 0x9202c000, + 0xc096020b, + 0xd2080a30, + 0x0092a881, + 0xb789009a, + 0xc00e532b, + 0xc2002a60, + 0x01c45a0f, + 0xc0007780, + 0x9eaa9102, + 0xc0260d82, + 0xc0010d50, + 0x75c69320, + 0x91e2c000, + 0xd01d7186, + 0xd01e1266, + 0xd012123c, + 0xd03119c2, + 0xc0286e39, + 0xc0010a00, + 0x020b90e0, + 0x0a08c098, + 0xaa01d208, + 0xc0007500, + 0x9e699222, + 0x0a02c409, + 0x622db580, + 0x0a42c001, + 0x0a00c010, + 0x6335b5c0, + 0x62adb580, + 0x9220c001, + 0x0c0270d3, + 0x1617d11e, + 0x1233d11d, + 0x1942d012, + 0x7280cc12, + 0x6e39d031, + 0x0a60c09c, + 0x0158d020, + 0xaa49f010, + 0x76c09e69, + 0x622db580, + 0xaa45f010, + 0x62adb580, + 0xaa41f010, + 0x632db580, + 0x9236c000, + 0x5d60d0a2, + 0x2a6ed3f1, + 0x5a20c200, + 0xd3f13244, + 0xc00e293e, + 0x59403a7c, + 0xb5803244, + 0xb79f622d, + 0xb7bf7e6e, + 0xb7df7eee, + 0xb7ff7f6e, + 0xc0027fee, + 0x9c228c00, + 0x8420a605, + 0x430cb720, + 0xb5bf0a82, + 0xb72a7e6a, + 0xb72046a9, + 0x04075514, + 0x02079e8b, + 0xa969c010, + 0x04859e43, + 0x8c80e031, + 0xff749ea2, + 0xb7209b4b, + 0x018b430c, + 0x46b1b76a, + 0x984bff94, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x9c228c60, + 0x4314b720, + 0x5cb0d1a2, + 0xb7859e9a, + 0xc1407ec9, + 0xc0210880, + 0xc2000880, + 0xc0205a24, + 0x3a042a00, + 0xf0009d9d, + 0x0185a261, + 0xff820906, + 0xf8399320, + 0x8420a205, + 0x430cb720, + 0xb59f0a02, + 0xb7417e6a, + 0x02035f2b, + 0x0a20c09c, + 0xd2087480, + 0xc000a981, + 0xb7409104, + 0xe0326ead, + 0xfff48d00, + 0xf9f89bd3, + 0x9c22aa19, + 0xa205f839, + 0xb7208420, + 0x0a02430c, + 0x7e6ab59f, + 0x5f2bb741, + 0x74809ea2, + 0x9204c000, + 0xc0a40203, + 0xd2080a14, + 0xb720a102, + 0xe032430c, + 0x09ea8d00, + 0x6cadb740, + 0x9bb4fff4, + 0xaa19f9f8, + 0xa6059c22, + 0x430cb720, + 0xb7810687, + 0x75045dad, + 0x93c4c000, + 0x558cb780, + 0xc0409e5a, + 0x7500aa45, + 0x9142c000, + 0x2ebed3f1, + 0xfff49eab, + 0x9eab9bb2, + 0x9180c000, + 0xc0a40203, + 0xd2080a14, + 0x7500aa01, + 0x90c2c000, + 0x2dfcc00e, + 0x9bbefff4, + 0x430cb720, + 0xc09c0203, + 0xd2080a18, + 0x7500aa01, + 0x9162c001, + 0x5dadb781, + 0xc0017504, + 0xb78690c4, + 0x75004129, + 0x9022c001, + 0x558cb780, + 0xc0409e6a, + 0x7500aa45, + 0x9142c000, + 0x68adb760, + 0x2dded3f2, + 0x09060d02, + 0x9bd6ff74, + 0x430cb720, + 0x2dded3f2, + 0xb7600d02, + 0x0906692d, + 0x7f6eb79f, + 0x7feeb7bf, + 0xff7e8c40, + 0xb79f90e0, + 0xb7bf7f6e, + 0x8c407fee, + 0xa61d9c22, + 0x558cb740, + 0x4314b720, + 0x9e920a02, + 0xc0200687, + 0xb741a269, + 0xd0115dcd, + 0x75021a28, + 0x9172c000, + 0x534bb78c, + 0xc0007500, + 0xb74090c2, + 0xc0006e55, + 0xb7849140, + 0xc2006ccd, + 0xc1015a08, + 0xb74000c2, + 0x748a6cb5, + 0x90a2c000, + 0xc0000906, + 0xb78090e0, + 0x9e6b500c, + 0xa965c040, + 0x46d1b76a, + 0xb7409e53, + 0xc0205514, + 0xff74a96a, + 0xb7209b8b, + 0xb7814314, + 0x1a105dcd, + 0xc0037502, + 0xb78c91b2, + 0x7500534b, + 0x9102c003, + 0x0ed2d011, + 0x0b02c008, + 0x5ab0c280, + 0xc0219eae, + 0x0f400f00, + 0xf0009dee, + 0x010ba361, + 0x0900c040, + 0x0900c021, + 0xaa619d2d, + 0x3a40c002, + 0xf0009dad, + 0xd2a2a261, + 0x9ead5c88, + 0x0092c101, + 0x6fb5b749, + 0x0e80c021, + 0xd0310ea0, + 0xd0106d29, + 0xc1010624, + 0xc1380122, + 0xd0080920, + 0xc101a951, + 0x02440244, + 0x5a08c200, + 0x00c2c101, + 0x0a02c401, + 0x70adb7e9, + 0x0a00c008, + 0xf0009dde, + 0x9eaba261, + 0x09929eb2, + 0xff14010d, + 0x0a0299f6, + 0xf0009dde, + 0x9deea261, + 0xa361f000, + 0xc021020b, + 0xc0400a00, + 0x9d4d0a20, + 0x1a10a962, + 0xaa619d4d, + 0x0a80c021, + 0x0ac0c046, + 0xa9619d5d, + 0xc0007480, + 0xd12290e2, + 0x112e5e35, + 0x11282a7c, + 0x430cb720, + 0x76adb544, + 0x7e6eb79f, + 0x7eeeb7bf, + 0x7f6eb7df, + 0x7feeb7ff, + 0x8c00c002, + 0xa6059c22, + 0x068774c0, + 0x9324c000, + 0x500cb780, + 0xc0409e5a, + 0x7500aa45, + 0x9222c000, + 0x430cb720, + 0xc09c0203, + 0xd2080a10, + 0x7500aa01, + 0x90e2c000, + 0x4729b78a, + 0xc0017117, + 0xb7209302, + 0x0203430c, + 0x0a38c0a2, + 0xaa01d208, + 0xc0007500, + 0x010393c4, + 0x4729b78a, + 0x091cc0a4, + 0xa941d008, + 0x4629b76a, + 0x124ac101, + 0xd0126245, + 0xc10119b2, + 0xc0a2029a, + 0xc1010ac0, + 0xd20805b8, + 0xc2b4aaa5, + 0x08049bcf, + 0xc0007140, + 0xb7409244, + 0xd2a24f94, + 0xd2a25e30, + 0x9e535d08, + 0x0a00c021, + 0xa945e030, + 0x0a40c146, + 0xf0009dcd, + 0xb79fa161, + 0xb7bf7f6e, + 0x8c407fee, + 0xa60d9c22, + 0x030774c6, + 0xc0009e5d, + 0x74c490a2, + 0x90a4c000, + 0xc0009eb6, + 0xb74092c0, + 0x0205430c, + 0x0a14c09c, + 0xaa01d208, + 0xc0007500, + 0x0f0690a4, + 0x9120c000, + 0xc06e0205, + 0x71480a10, + 0xd0020f02, + 0xf2080f62, + 0xb720aa31, + 0x73994314, + 0x9344c000, + 0x58cdb783, + 0xa221f208, + 0x430cb720, + 0x592db723, + 0xa0a5f208, + 0x430cb720, + 0x59adb723, + 0xa0a9f208, + 0x430cb720, + 0x5a2db723, + 0xa0adf208, + 0x90e0c001, + 0x5c88d322, + 0x46d1b76a, + 0x0092c101, + 0x6a35b740, + 0x09c2c010, + 0xff34010b, + 0xb7209aea, + 0xf208430c, + 0xb583aa21, + 0xb72058ad, + 0xf208430c, + 0xb583aa25, + 0xb720592d, + 0xf208430c, + 0xb583aa29, + 0xb72059ad, + 0xf208430c, + 0xb583aa2d, + 0xb7205a2d, + 0xd0524314, + 0xb7880ad8, + 0xba0c6c49, + 0xc0004008, + 0x5888922a, + 0x0092c101, + 0x46d1b76a, + 0x46b5b742, + 0xc00e9e6a, + 0xff3409f2, + 0x0a169abc, + 0xa231f208, + 0xaa21d210, + 0x0982c400, + 0x0902c200, + 0xa221f210, + 0xa92ef208, + 0x2d7dc9fe, + 0xa12ef208, + 0x430cb720, + 0x752db784, + 0xb3437500, + 0x75004422, + 0x4422b342, + 0x9e533126, + 0xf2083126, + 0xb720a12d, + 0xb784430c, + 0x75026cad, + 0x90c2c000, + 0x3900c020, + 0xa12df208, + 0xaa2df208, + 0x3a00c010, + 0xa22df208, + 0x430cb720, + 0x6cadb784, + 0xc0007506, + 0xf20891c4, + 0xc080aa21, + 0xc0007d00, + 0x010391c2, + 0x0914c098, + 0xc0000a02, + 0x010390c0, + 0x0914c098, + 0xd0080a0a, + 0xf208a241, + 0xc040aa21, + 0xc0027d00, + 0xb72093a2, + 0xb784430c, + 0x75046cad, + 0x92c4c002, + 0xc0980183, + 0xd0080990, + 0x7480a961, + 0x91c4c002, + 0x702db784, + 0x7d3edffd, + 0x92c2c000, + 0xc0980203, + 0xd2080a08, + 0x7500aa01, + 0x9082c001, + 0xc0980203, + 0xd2080a0c, + 0x7500aa01, + 0x9382c000, + 0xa161d008, + 0x90a0c001, + 0x01030203, + 0x0a58c096, + 0x0954c096, + 0xa902d208, + 0xaa41d008, + 0xc0007299, + 0xb7859164, + 0x75007ea9, + 0x90c4c000, + 0xa261d008, + 0x91e0c000, + 0xc0980103, + 0x0a060910, + 0xa241d008, + 0x430cb780, + 0xc0980902, + 0xd2080a14, + 0xb780a101, + 0xc098430c, + 0xd2080a10, + 0x7500aa01, + 0x9102c000, + 0xaa2df208, + 0x2a7dcfbe, + 0xa22df208, + 0x430cb720, + 0x5dadb781, + 0xc0007504, + 0xf2089102, + 0xcfdeaa2d, + 0xf2082a7d, + 0xb720a22d, + 0xb781430c, + 0x75045dad, + 0x9304c001, + 0xa92df208, + 0x0a5ecfff, + 0x0a7cc7fe, + 0x2128d020, + 0xa12ef208, + 0x430cb720, + 0xc09a0203, + 0xd2080a40, + 0x7500aa01, + 0x90e2c000, + 0x4b2db781, + 0xc0007502, + 0xb78690e4, + 0x75004029, + 0x9142c000, + 0xc8009e53, + 0x32262900, + 0x3a20c001, + 0xa22df208, + 0x430cb720, + 0x4b2db781, + 0xc0007502, + 0xf2089164, + 0xcfffaa2d, + 0xc8fe0962, + 0x2244097c, + 0xa22df208, + 0x430cb780, + 0xa9adf208, + 0x0d02c011, + 0x0a08c098, + 0xa901d208, + 0xc0215964, + 0x31262900, + 0xa12df208, + 0x430cb780, + 0x0a0cc098, + 0xaa01d208, + 0x5a68c200, + 0x2a00c041, + 0xf2083128, + 0xb780a12d, + 0xcfef430c, + 0xc098297e, + 0xd2080a14, + 0x7500aa01, + 0xb3420a02, + 0x9e544444, + 0xf2083144, + 0xb720a12d, + 0xb781430c, + 0x7500612d, + 0x90c2c000, + 0x3940c001, + 0xa12df208, + 0x430cb720, + 0x5e2db781, + 0xc0007508, + 0xb78191a4, + 0x75045dad, + 0x9104c000, + 0xaa2df208, + 0x3a00c201, + 0xa22df208, + 0x9eab7780, + 0x09829eb2, + 0x09b2d001, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0xffdd8c60, + 0xf8399320, + 0x04b6a285, + 0xb7600407, + 0x9e485114, + 0x518cb780, + 0xa8aac030, + 0x5094b760, + 0xc00f59c0, + 0xc84029fc, + 0xc030aa05, + 0x9e9da98a, + 0x5a20c200, + 0x76c0349a, + 0xe0329e48, + 0xcff08d80, + 0x32082a00, + 0xa9f9d9d0, + 0x90c2c000, + 0x3a00c201, + 0x90e0c000, + 0xc0007680, + 0xc1019082, + 0x74803a00, + 0x9082c000, + 0x3a00c011, + 0xc0007440, + 0x74c09122, + 0x3a00c021, + 0x9082c000, + 0x3a00c041, + 0x5d08d028, + 0x549cb740, + 0xe0209e2d, + 0xf9f8a26d, + 0x9c22aa9d, + 0x8420a61d, + 0x558cb780, + 0xb7c09e5e, + 0xc2404314, + 0xa199aa45, + 0x9e8f9e73, + 0x8c80e0b1, + 0x09c4c09a, + 0x9e937500, + 0xabe1d008, + 0x7e29b7bf, + 0x7e33b7bf, + 0x7d33b73f, + 0x9244c000, + 0xb7849e71, + 0x7500752d, + 0x90e4c000, + 0x6cadb784, + 0xc0007502, + 0x9e7190e2, + 0x6cadb704, + 0x9060c000, + 0x77400802, + 0x9142c000, + 0xb7849e71, + 0xd1106c2d, + 0x9ea30639, + 0x2dfccffe, + 0xc09c9e74, + 0xd2080a18, + 0x7480a901, + 0x90a4c000, + 0xc0019e90, + 0x9e749100, + 0x0a38c0a2, + 0xaa01d208, + 0xc0007500, + 0x0c0690a2, + 0x93a0c000, + 0xc1019e71, + 0xb78a01ec, + 0x710c4729, + 0x9124c000, + 0xc0a20207, + 0xd2080a20, + 0xc000a812, + 0x010791c0, + 0xc0a20207, + 0xc0a20924, + 0xd2080a20, + 0xd008aa11, + 0xd020a951, + 0x75421044, + 0x90d2c000, + 0x00ecc101, + 0x4a31b56a, + 0xc0007640, + 0x9e719104, + 0x6729b786, + 0xc0067500, + 0xb7209062, + 0x5d184314, + 0x5908c380, + 0x049dd110, + 0x684db784, + 0x4a31b76a, + 0x2d00c03c, + 0x5a38c200, + 0x2a00cc00, + 0x4b29b76a, + 0x2970c002, + 0x31289e51, + 0xb78c5de0, + 0x3122534b, + 0x59c09e59, + 0x31263122, + 0xd0117500, + 0xd0202a06, + 0xc00031a8, + 0xc0809082, + 0x9e4c3d80, + 0x0a18c09c, + 0xaa01d208, + 0xc0007500, + 0xc0409082, + 0x77c03d80, + 0x9082c000, + 0x3d80c100, + 0x570cb780, + 0x5938d324, + 0x5d40d022, + 0xaa45c240, + 0x2d00cc00, + 0xc2009e51, + 0xc2805a08, + 0x324259e0, + 0xc00f3246, + 0xd020297c, + 0x9e4c3144, + 0xc0967740, + 0xd2080a50, + 0xb740aa01, + 0xd002590c, + 0xc2003d24, + 0xc1015a08, + 0xf208024c, + 0xc380aa11, + 0xc101588c, + 0x75000092, + 0x5a18c300, + 0xa9190244, + 0x3d22d002, + 0xf2080289, + 0xb721a109, + 0xc12461af, + 0xf2080e82, + 0xf208a18e, + 0xfa08a112, + 0x9e6ba081, + 0x9eb39eaa, + 0xfef40ec0, + 0xc2509a52, + 0x0ac07740, + 0x92e4ffff, + 0x568cb780, + 0x5a88d324, + 0xd1109eb3, + 0xc00406d9, + 0xf2080982, + 0xc124a922, + 0xfef40902, + 0x9eb39bed, + 0x9804ff14, + 0xb7869e71, + 0x75006729, + 0x92c2c000, + 0x4f8cb740, + 0xf208aa19, + 0xd110a922, + 0x0a3c0555, + 0xa9c1f008, + 0x0d00c004, + 0xa122f208, + 0x2a410246, + 0xa241f008, + 0x9300c000, + 0xa922f208, + 0xc0049eb3, + 0xc1240982, + 0xff140902, + 0x9eb39be0, + 0x7deeb79f, + 0x7e6eb7bf, + 0x7eeeb7df, + 0x7f6eb7ff, + 0x8c20c002, + 0x9280fefe, + 0x7deeb79f, + 0x7e6eb7bf, + 0x7eeeb7df, + 0x7f6eb7ff, + 0x8c20c002, + 0xb7209c22, + 0x9e5a430c, + 0xc0a20203, + 0xd2080a38, + 0x7500aa01, + 0x9222c000, + 0x02039e8a, + 0x0d20c0a4, + 0x0a40c0a2, + 0xa901d208, + 0xaa41d010, + 0x70880802, + 0x0802d003, + 0xb7859c22, + 0x75007ea9, + 0x9162c000, + 0x500cb780, + 0xaa45c040, + 0xc0007500, + 0x08069082, + 0x02229c22, + 0xc0a20109, + 0xc0a20920, + 0xd2080a40, + 0xd008aa05, + 0x0802a951, + 0xd0037104, + 0x9c220802, + 0x430cb720, + 0x02030c06, + 0x0a20c136, + 0xa902d208, + 0x46b1b76a, + 0xa886d228, + 0x19049e52, + 0xc0007097, + 0xb74a9096, + 0x9e404729, + 0x29aed3f1, + 0x7913520c, + 0x90a4c000, + 0xffff7095, + 0xd0319244, + 0x6a520a24, + 0x478cb740, + 0xd0310244, + 0xf0080948, + 0x7508aa41, + 0xc1360203, + 0xb4020a20, + 0xd2084a22, + 0x9c22a181, + 0x430cb740, + 0xc1360205, + 0xd2080a24, + 0x7500aa01, + 0x90e4c000, + 0xc1360205, + 0xd2080a20, + 0x9e5aa182, + 0xc2000a06, + 0xb7405208, + 0xc136430c, + 0xd0080924, + 0x9e53a942, + 0xd0083246, + 0x9c22a241, + 0x9e5da605, + 0x9080c000, + 0x9846ff74, + 0x430cb780, + 0xc1360d92, + 0xd2080a20, + 0x710aaa01, + 0x92c4ffff, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0x550cb780, + 0xb7209e5a, + 0xc040430c, + 0x5db0aa45, + 0x67adb741, + 0x2a7cc002, + 0x0d80c021, + 0xc1163244, + 0x9dbe0df0, + 0xa261f000, + 0xa60d9c22, + 0x430cb720, + 0x02039e5e, + 0x0a30c096, + 0xaa01d208, + 0xc0047500, + 0xb7a49344, + 0xb76468ad, + 0x7540672d, + 0x9222c000, + 0x67b5b744, + 0xc000729b, + 0x02039188, + 0x0a34c096, + 0xa901d208, + 0x1254c101, + 0xc0007104, + 0x74c090c6, + 0x9022c004, + 0x01030287, + 0x67adb784, + 0x0934c096, + 0xa941d008, + 0x71041258, + 0x92a8c003, + 0x46b1b76a, + 0xc2000e86, + 0xc00e55ac, + 0xff942dfc, + 0xb7209819, + 0xc004430c, + 0xb78409d2, + 0xc09667ad, + 0x125808b4, + 0x4029b580, + 0x430cb720, + 0xc0960203, + 0xd2080a34, + 0x1a04aa01, + 0x4badb581, + 0x430cb720, + 0xc0960203, + 0xb7410a34, + 0xd2085c35, + 0xb76aaa01, + 0x010346b1, + 0x5a1cc200, + 0x0d01cff0, + 0x0528c101, + 0x0960c09c, + 0x9acfff14, + 0x430cb720, + 0x46b1b76a, + 0x55acc200, + 0x2dfcc00e, + 0x9871ff94, + 0x478cb740, + 0xaa41f048, + 0xc0007508, + 0xf0489084, + 0xb720a2c2, + 0x0203430c, + 0x0a34c096, + 0xaa01d208, + 0x532bb749, + 0xc2001a04, + 0xcff05a0c, + 0xc00e291d, + 0x32442a60, + 0x532bb589, + 0x430cb740, + 0xc09c0205, + 0xd2080a14, + 0x7500aa01, + 0x9224c000, + 0xc0960205, + 0xd2080a30, + 0x0092a881, + 0xb7890094, + 0xc00e532b, + 0xc2002a60, + 0xd2085a0f, + 0xb720a241, + 0xd208430c, + 0xb744aa41, + 0xba246e2d, + 0x02444000, + 0x752db584, + 0x430cb720, + 0x752db784, + 0x08f0c098, + 0x4029b580, + 0x430cb720, + 0x752db784, + 0x6d2db744, + 0xb5841244, + 0xb79f752d, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xa61d9c22, + 0x8460c010, + 0x4314b7a0, + 0x9e699e6c, + 0x0a10c0a4, + 0xa902d208, + 0x5e35b7e1, + 0xc0337680, + 0x0a069184, + 0x44abb585, + 0x430cb780, + 0x0585096a, + 0x0a1cc09c, + 0xa101d208, + 0x430cb720, + 0xb5440405, + 0xb72079b5, + 0xb76a430c, + 0xba1b46a9, + 0xc0004000, + 0xb5049080, + 0xb7207a55, + 0xd110430c, + 0x0d840637, + 0x4731b74a, + 0x5908c200, + 0x00a2d020, + 0xffff7115, + 0x0203925c, + 0x0a40c09a, + 0xaa01d208, + 0xc0007500, + 0xb78490e2, + 0x0a0475ad, + 0x75adb584, + 0x430cb740, + 0x0930c096, + 0xaa41d008, + 0xd0080a04, + 0xb780a241, + 0x9ea2430c, + 0xc0960109, + 0xc0960d30, + 0xd0100934, + 0xd008aa41, + 0x7104a941, + 0x1248d011, + 0x4442b422, + 0x430cb720, + 0x692db784, + 0xb5841a04, + 0xb740692d, + 0x0205430c, + 0x0a30c096, + 0xa881d208, + 0x00929e92, + 0xb7890094, + 0xc09a532b, + 0x2a100d40, + 0x5a0bc200, + 0xa241d010, + 0x4314b720, + 0xc0969e4c, + 0xd2080a30, + 0x0092a881, + 0x0092c101, + 0x532bb789, + 0xc0f00109, + 0xd1242900, + 0xc2005821, + 0x9e435831, + 0x70c0c812, + 0xb5842a0c, + 0xb7206ccd, + 0x09044314, + 0x9e4a0385, + 0x0930c096, + 0xa8c1d008, + 0xc1010092, + 0xb7890092, + 0xc00e532b, + 0xc2002a60, + 0xb59d5a0f, + 0xd0087468, + 0x7500aa41, + 0x9084c006, + 0x67cdb784, + 0x6e4db584, + 0x430cb720, + 0x4835b745, + 0xc0027684, + 0xb7449182, + 0x748067ad, + 0x9242c000, + 0x4b2db781, + 0xc0007500, + 0xb78490e2, + 0x7500692d, + 0x911cc000, + 0xc0007682, + 0x768090a2, + 0x92e4c001, + 0x6d2db784, + 0x1128e000, + 0x9142c000, + 0x4aadb781, + 0xc0007088, + 0x768090a8, + 0x93c4c000, + 0xc09c0103, + 0x0a060914, + 0xa241d008, + 0x430cb720, + 0xb5440902, + 0xb72075ad, + 0xb544430c, + 0xb720752d, + 0xb784430c, + 0xb58467ad, + 0xb7206d2d, + 0x0a7f430c, + 0x7468b55d, + 0x6fadb584, + 0x430cb720, + 0xb5840a06, + 0xb7206cad, + 0xb781430c, + 0xb5844b2d, + 0xb720692d, + 0xc00e430c, + 0xb5850a7e, + 0xb720482d, + 0xb781430c, + 0x75065dad, + 0x9144c000, + 0xc09c0203, + 0xd2080a18, + 0x7500aa01, + 0x9342c002, + 0xc0980103, + 0xd008091c, + 0x0d86aa41, + 0xd0080a04, + 0xb780a241, + 0x9ea2430c, + 0xc0980109, + 0xc0980d1c, + 0xd0100918, + 0xd008aa41, + 0x7104a941, + 0x1244d014, + 0x4448b422, + 0x93a0c000, + 0xaa41d010, + 0x0639d110, + 0xa25dd008, + 0x430cb780, + 0x0146c101, + 0xc0980185, + 0xc0980a18, + 0xd2080980, + 0xd008a901, + 0x0d84aa7d, + 0xd0207104, + 0xc0001144, + 0xd0089086, + 0xb720a17e, + 0xc101430c, + 0x01090216, + 0xc0980203, + 0xd2080a18, + 0x9e8aaa01, + 0x0900c098, + 0x0d1cc098, + 0xfffe72d9, + 0xb78892b8, + 0x02186e29, + 0x0a00c098, + 0xaa1dd208, + 0xc0980218, + 0xd2080a20, + 0x1904a90d, + 0xa10dd208, + 0x4314b740, + 0xc09a9e54, + 0xd2080a40, + 0x7500aa01, + 0x91c2c001, + 0x2bfcc00e, + 0x062fd110, + 0xc0989e52, + 0xd2080a00, + 0xc096aa1d, + 0xd0080950, + 0xb780a241, + 0x0109430c, + 0x0950c096, + 0xa942d008, + 0xa941d1e8, + 0x0244c101, + 0x0a20c098, + 0xa10dd208, + 0x430cb720, + 0xc0960203, + 0xd2080a50, + 0xb744aa01, + 0xc20075ad, + 0x00c25a08, + 0x722db544, + 0x92a0c001, + 0xc0989e54, + 0xd2080a18, + 0xd110a901, + 0xc0980525, + 0xd0080900, + 0x0a04aa5d, + 0xa25dd008, + 0x430cb780, + 0xc0980109, + 0xd0080918, + 0x0228a941, + 0x09089ea2, + 0x0d00c098, + 0xaa5dd010, + 0xc0007104, + 0x1a0890b8, + 0xa25dd010, + 0x430cb780, + 0xc0980109, + 0xd0080918, + 0x9ea2a941, + 0xc0980244, + 0xd2080a00, + 0xc096aa1d, + 0xd0100d50, + 0xb740a241, + 0xc101430c, + 0xc0980220, + 0xd2080a00, + 0xc096aa1d, + 0xd0080954, + 0xb720a241, + 0xb748430c, + 0xb7846e29, + 0x9e8a75ad, + 0xc0967104, + 0xc0000d58, + 0x02109134, + 0x0a00c098, + 0xaa1dd208, + 0x90e0c000, + 0xc0960203, + 0xd2080a54, + 0xd010aa01, + 0xb740a241, + 0x9e69430c, + 0xc0980d02, + 0xb7850910, + 0xd0087e29, + 0xb720a142, + 0x75004314, + 0x6ccdb764, + 0x90c2c000, + 0x6acdb781, + 0x9080c000, + 0x6b4db781, + 0xb58474c6, + 0xc00063cd, + 0x74c29182, + 0x9222c000, + 0x430cb720, + 0x63adb784, + 0xc0003a04, + 0xb7209100, + 0xb784430c, + 0xc00863ad, + 0xb5843a00, + 0xb78063ad, + 0xc096430c, + 0xd2080a50, + 0xc200aa01, + 0xc1015a08, + 0xf2080242, + 0x7480a911, + 0x9222c000, + 0xa111f248, + 0x430cb780, + 0xc0960902, + 0xd2080a50, + 0xc200aa01, + 0xc1015a08, + 0xf2080242, + 0xb742a111, + 0x76804555, + 0x9102c002, + 0x430cb780, + 0x6cc9b748, + 0x0a50c096, + 0xaa01d208, + 0xc2007480, + 0xc1015a08, + 0xf2480242, + 0xf208a911, + 0xb780a111, + 0x0902430c, + 0x454db542, + 0x0a50c096, + 0xaa01d208, + 0x5a08c200, + 0x0242c101, + 0xa112f248, + 0x90c2c001, + 0x430cb720, + 0x7ea9b785, + 0xc0007500, + 0x74c29202, + 0x91a2c000, + 0xc0980203, + 0xd2080a00, + 0x0103aa01, + 0x0904c098, + 0xc0004a04, + 0x010390c0, + 0x0904c098, + 0xd0080a02, + 0xb720a241, + 0x0a02430c, + 0x6cc9b588, + 0x75adb784, + 0x6fadb584, + 0x430cb720, + 0x45cdb742, + 0xc0960203, + 0xd2080a50, + 0x7480aa01, + 0x5a08c200, + 0x0242c101, + 0xaa11f248, + 0x642db584, + 0x430cb720, + 0x9262c000, + 0xb5440a02, + 0xb582652d, + 0xb74045cd, + 0xb748430c, + 0xb5886d51, + 0xc0986d49, + 0xd0080908, + 0xc001a142, + 0x020390c0, + 0x0a54c096, + 0xaa01d208, + 0x5a08c200, + 0x0242c101, + 0xaa11f248, + 0x652db584, + 0x430cb720, + 0xc0960203, + 0xd2080a54, + 0xb744aa01, + 0x9e8a6f2d, + 0x5a08c200, + 0xb72400c2, + 0x0a02722d, + 0x0d08c098, + 0xd0017082, + 0xd0100a42, + 0xb742a241, + 0xb720464d, + 0x7480430c, + 0x9262c000, + 0xb5440a02, + 0xb582662d, + 0xb740464d, + 0xb748430c, + 0xb5886dd1, + 0xc0986dc9, + 0xd008090c, + 0xc001a142, + 0x020390c0, + 0x0a58c096, + 0xaa01d208, + 0x5a08c200, + 0x0242c101, + 0xaa11f248, + 0x662db584, + 0x430cb720, + 0xc0960203, + 0xd2080a58, + 0xb744aa01, + 0x9e8a6f2d, + 0x5a08c200, + 0xb72400c2, + 0x0a02722d, + 0x0d0cc098, + 0xd0017082, + 0xd0100a42, + 0xb720a241, + 0xb740430c, + 0x048b4155, + 0x6badb744, + 0xb7845d10, + 0x5910642d, + 0xcffe097c, + 0xc1012901, + 0xb7426125, + 0x02285b53, + 0x64adb584, + 0x430cb720, + 0x5bcbb762, + 0x652db784, + 0x61b4c101, + 0x193ed012, + 0xb5840228, + 0xb72065ad, + 0x769c430c, + 0x662db784, + 0xb5440128, + 0xc00066ad, + 0x0d3a90bc, + 0x90a0c000, + 0xc8120d82, + 0xb7207286, + 0xb780430c, + 0xd031500c, + 0xb70a2dae, + 0x0b0246a9, + 0xba000f06, + 0x02884000, + 0x5a30c000, + 0xc0219ea3, + 0xc1c80d80, + 0xc0050d90, + 0x9e699140, + 0xa322d208, + 0x4f35b702, + 0xc0037600, + 0xb7429022, + 0xd2114dad, + 0x9dcd1e38, + 0xa161f000, + 0x4e2db742, + 0x1e38d1f1, + 0xf0009dcd, + 0xb742a161, + 0xd2114ead, + 0x9dcd1e30, + 0xa161f000, + 0x4018b960, + 0xd152048b, + 0xb7821d38, + 0x9dae4fad, + 0xa261f000, + 0x0d20d051, + 0xf0009dad, + 0xb742a261, + 0xd09151ad, + 0x9dcd1e20, + 0xa161f000, + 0x1e20d051, + 0xf0009dcd, + 0x0890a161, + 0xffff0d10, + 0x9e699101, + 0x1e38d071, + 0x4d2db742, + 0xf0009dcd, + 0x7e04a161, + 0x9042c001, + 0x1d30d072, + 0x53cdb782, + 0xf0009dae, + 0xb742a261, + 0xd011564d, + 0x9dcd0e28, + 0xa161f000, + 0x72860d20, + 0xffff0c90, + 0x048b9224, + 0x554db782, + 0xf0009dbe, + 0xb742a261, + 0xd01155cd, + 0x9dcd0e38, + 0xa161f000, + 0x1e30d091, + 0xf0009dcd, + 0xb720a062, + 0xf210430c, + 0x0507a929, + 0x6badb724, + 0xc00e1904, + 0xd011297c, + 0xc2001a12, + 0xc00f5a40, + 0xcf4a2a7c, + 0x32440d01, + 0xf0009dae, + 0x9e69a261, + 0xb7419e5c, + 0xcf8068ad, + 0x9dcd0a01, + 0xa161f000, + 0x692db741, + 0x9dcd0a20, + 0xa161f000, + 0x682db741, + 0x1a30c008, + 0xf0009dcd, + 0xb781a161, + 0x1a0c5dad, + 0xc0007504, + 0x9e5c9112, + 0x0a41cf6a, + 0xf0009dcd, + 0x0b04a1e1, + 0xc1000a84, + 0xb7200d80, + 0x0260430c, + 0x4729b72a, + 0xfffa7102, + 0x77c0925c, + 0x9182c000, + 0xb785048b, + 0x75007e49, + 0x9024c001, + 0x59cdb582, + 0x93a0c000, + 0x048b9e69, + 0x67a9b786, + 0x6829b746, + 0x68b1b746, + 0x7529b589, + 0x75a9b549, + 0x7631b549, + 0x430cb720, + 0x5bcbb782, + 0x5b4bb742, + 0x6c2db724, + 0xb5246244, + 0xb582714b, + 0xb740584d, + 0xf048478c, + 0x7508aa41, + 0x90a4c000, + 0xf0481a0c, + 0xb720a241, + 0x9d8f430c, + 0x68b5b7c4, + 0x672db7c4, + 0x0da1cfee, + 0x9360c000, + 0x9b08ff34, + 0x430cb720, + 0x02039d8f, + 0x0a10c0a4, + 0xaa01d208, + 0x0da1cfee, + 0xc0107500, + 0xb78493c4, + 0x7188672d, + 0x90e4c000, + 0x68adb784, + 0xc0007399, + 0xffd49082, + 0xb7809ad0, + 0xc098430c, + 0xd2080a70, + 0xff94a982, + 0xc01c99d3, + 0x0281743e, + 0xfffe0d92, + 0xb7809322, + 0x0906430c, + 0x0a18c09c, + 0xa101d208, + 0x430cb720, + 0xc0a20203, + 0xd2080a70, + 0x7500aa01, + 0x9082c000, + 0x532bb54c, + 0x430cb720, + 0x2bfcc00e, + 0x6e29b728, + 0xd01210f2, + 0xcffe1992, + 0xff942dfc, + 0x9e699b29, + 0x7e29b785, + 0xc0007500, + 0xb7209162, + 0x0a16430c, + 0x5eadb583, + 0x430cb720, + 0x422db584, + 0x048b0d02, + 0x7e51b545, + 0x430cb720, + 0x532bb78c, + 0xc0007500, + 0x010392e2, + 0x0970c0a2, + 0xaa41d008, + 0xc0007500, + 0xd00891e2, + 0xb740a142, + 0x0205430c, + 0x0a74c0a2, + 0xaa01d208, + 0x0918c09c, + 0xa241d008, + 0x430cb720, + 0x532bb78c, + 0xc0007500, + 0xb78191a2, + 0x75065dad, + 0x9104c000, + 0xc09c0103, + 0x1a0c0918, + 0xa241d008, + 0x430cb720, + 0xc09c0203, + 0xd2080a18, + 0x7500aa01, + 0x9302c001, + 0x67adb784, + 0xc0007500, + 0x9ea190a4, + 0x9080c000, + 0x6135b721, + 0x4badb786, + 0x61adb741, + 0x46b1b74a, + 0x2a7cc00e, + 0x4731b70a, + 0x31c4d020, + 0xc0000802, + 0x9dcd9260, + 0xa0e2f000, + 0xc0210205, + 0xc0320a00, + 0x9dcd0a70, + 0xa1e2f000, + 0x9dcd0a10, + 0xa061f000, + 0x293ed3f2, + 0x0da2d011, + 0xc1807280, + 0x02055930, + 0x0a00c021, + 0x0a60c032, + 0x90b4ffff, + 0xb5840a02, + 0xb72079ad, + 0x9ea3430c, + 0xb76a9ea1, + 0xba1b46a9, + 0xc0004000, + 0xf0089080, + 0xb720a0c2, + 0xd110430c, + 0x0d840637, + 0x4731b74a, + 0x5908c200, + 0xc09c0122, + 0x71150940, + 0x923cffff, + 0x47b1b76a, + 0x9a50ff54, + 0xb7809e69, + 0xb743430c, + 0xc09a572b, + 0x74800a44, + 0xa281d208, + 0x9262c008, + 0x4314b7c0, + 0xc09c9e74, + 0xd2080a18, + 0x7500aa01, + 0x9182c007, + 0x048d9e74, + 0x0a44c09a, + 0xaa01d208, + 0xb76a9d17, + 0xc20046d1, + 0xc1015a08, + 0xb74000cc, + 0xcfee7035, + 0xc0100ab1, + 0x010b0986, + 0x9905fef4, + 0x430cb780, + 0xa921d208, + 0xc0a4030b, + 0xd2080a1c, + 0xb720a101, + 0xd229430c, + 0xb785a945, + 0xc0a47ea9, + 0x750008a0, + 0xd0020a02, + 0x51500a42, + 0x4029b540, + 0x430cb720, + 0x582db762, + 0x4135b760, + 0x995fc274, + 0x9ea80982, + 0xc00e9e9b, + 0xc001287c, + 0xd20893c0, + 0x7480aac1, + 0xc10100da, + 0xb56a0092, + 0xd030532b, + 0xb720a90a, + 0xc000430c, + 0xb78a9164, + 0xba2452ab, + 0x62094002, + 0x0629d110, + 0x9240c000, + 0xc0a40203, + 0xd2080a1c, + 0x1a04aa01, + 0xc0007088, + 0xb78a9184, + 0xba2452ab, + 0x62094002, + 0x1629d110, + 0xc00e9ea2, + 0x9e712d7c, + 0x430cb780, + 0x5babb742, + 0x29fccffe, + 0x009800da, + 0x0627d110, + 0x6124c101, + 0x532bb54b, + 0x0d840189, + 0xb7200b08, + 0xd3f14314, + 0x9e4c2d3e, + 0x0a1cc0a4, + 0xaa01d208, + 0xfffd7104, + 0xb7829352, + 0xb7425bcb, + 0x61465c53, + 0xc0007295, + 0x00da9188, + 0x0092c101, + 0x532bb78b, + 0x1124c101, + 0xb58b1244, + 0x048b532b, + 0x5dcdb781, + 0xc0007508, + 0x750a90a2, + 0x91e4c000, + 0x430cb720, + 0x532bb78c, + 0xc0007500, + 0x9e8b9102, + 0xc06e098e, + 0xc0000d90, + 0xb7209120, + 0x9e8b430c, + 0x6cadb764, + 0x0d90c06e, + 0x9931ffb4, + 0xb7859e69, + 0x75007ea9, + 0x9082c001, + 0x430cb720, + 0xc09c0203, + 0xd2080a14, + 0x9e8baa01, + 0xc0807500, + 0xc0000d80, + 0xb7649102, + 0xffb46cad, + 0xc000991a, + 0x9e8a9220, + 0xc06e0109, + 0xb9600d10, + 0x9e5348d8, + 0xc0209e91, + 0x0904aa65, + 0xa269c010, + 0x9321ffff, + 0x430cb720, + 0x09020a7f, + 0x6c29b588, + 0x430cb780, + 0x0a3cc0a2, + 0xa101d208, + 0x430cb720, + 0x47b1b76a, + 0x99d7ff54, + 0x430cb720, + 0xb78a0902, + 0xc1364729, + 0xb58008a0, + 0xb7804029, + 0xc136430c, + 0xd2080a24, + 0xb780a101, + 0xc0a2430c, + 0xd2080a38, + 0xc001a101, + 0x9e929080, + 0x4030b960, + 0x430cb720, + 0x5c8cc100, + 0xd1100d04, + 0xb5430493, + 0xb7207bab, + 0xd110430c, + 0xb5430493, + 0xb7207c2b, + 0xd110430c, + 0xb5430493, + 0xb7807cab, + 0xc101430c, + 0xb5430498, + 0xffff7d4b, + 0xfff690a1, + 0xb79f91e0, + 0xb7bf6cee, + 0xb7df6d6e, + 0xb7ff6dee, + 0xcfec6e6e, + 0x9c228421, + 0xa205f839, + 0x430cb720, + 0x0d860902, + 0x6e29b788, + 0x0898c098, + 0xb5800a08, + 0xb7804029, + 0xc098430c, + 0xd2080a1c, + 0xc000a101, + 0xd0089320, + 0xb780a1de, + 0xc101430c, + 0x01850146, + 0x0a18c098, + 0x0980c098, + 0xa901d208, + 0xaa7dd008, + 0x71040d84, + 0x1144d020, + 0x9086c000, + 0xa17ed008, + 0x430cb720, + 0x0216c101, + 0x02030109, + 0x0a18c098, + 0xaa01d208, + 0x0900c098, + 0xfffe72d9, + 0xb7819398, + 0x01034bad, + 0x0934c096, + 0xd0080a04, + 0xb780a241, + 0x097f430c, + 0xc09609c2, + 0xd2080a30, + 0xb720a101, + 0xb76a430c, + 0xb74046b1, + 0x010374b5, + 0x0930c094, + 0x9b91fed4, + 0x430cb720, + 0x4badb781, + 0x532bb749, + 0x5a0cc200, + 0x291dcff0, + 0x2a60c00e, + 0xb5893244, + 0xb720532b, + 0x0a02430c, + 0x68adb584, + 0xaa1df9f8, + 0xa61d9c22, + 0x8420c006, + 0x430cb720, + 0x0a060902, + 0x4829b54a, + 0x4314b720, + 0x5ab5b703, + 0x5b35b7a3, + 0xb54aa01a, + 0xb72048c9, + 0x9e6b4314, + 0x5bb5b7e3, + 0x44cbb545, + 0x520cc200, + 0xb7800389, + 0xb723430c, + 0xc00e5c2d, + 0xc0a42bfc, + 0x9ebb0a10, + 0xa101d208, + 0xff54a09d, + 0xb720986f, + 0x9e4c4314, + 0x0a10c0a4, + 0xaa01d208, + 0xc0157500, + 0xf1b192a4, + 0xb7cd8c80, + 0xb7cd6049, + 0x9e4960d1, + 0xb960010b, + 0xb7834028, + 0xf0084c2d, + 0xb783a241, + 0xf0084cad, + 0xb783a245, + 0xf0084d2d, + 0xb783a249, + 0x08c04dad, + 0xa24df008, + 0xffff0940, + 0xb74391c1, + 0x9e4a5bd5, + 0x2dded3f2, + 0x0992c144, + 0x9b1ffed4, + 0x430cb720, + 0xb9600d02, + 0xb5cd4028, + 0xb7206029, + 0xb5cd430c, + 0xb72060b1, + 0xf208430c, + 0xc100aa21, + 0xd1105c90, + 0x0d040493, + 0x4c2db583, + 0x430cb720, + 0xaa25f208, + 0x0493d110, + 0x4cadb583, + 0x430cb720, + 0xaa29f208, + 0x0493d110, + 0x4d2db583, + 0x430cb780, + 0xa92df208, + 0xc1010ac0, + 0xb5430498, + 0xfffe4dcd, + 0xb7209381, + 0xaa19430c, + 0x5a0cb700, + 0x5a94b700, + 0x5aadb583, + 0x430cb720, + 0x0c82c021, + 0x0d82c021, + 0x5b35b5a3, + 0x430cb720, + 0x0c80c014, + 0x0d90c014, + 0x5bb5b5e3, + 0x430cb720, + 0xb960a99d, + 0xb5634038, + 0xb7a05c2d, + 0x9e694314, + 0x0dd8d2f1, + 0x7029b788, + 0x6fa9b748, + 0x6f31b748, + 0x47a9b58a, + 0x430cb7a0, + 0x6ea9b788, + 0xb54a008b, + 0xb7204729, + 0xb54a430c, + 0xb72046b1, + 0xb58a430c, + 0xb7204629, + 0xb562430c, + 0xb720612d, + 0xd9e9430c, + 0x9d9eaa1d, + 0xa261f000, + 0xaa1dd9f1, + 0xf0009dbe, + 0xffffa261, + 0x048b92c1, + 0x7e49b785, + 0xc0067500, + 0xc00e91c2, + 0xb5850a7e, + 0xb720482d, + 0x0a82430c, + 0xb5a40906, + 0xb72067ad, + 0xb5a4430c, + 0xb720682d, + 0xb5a4430c, + 0xb720692d, + 0xb544430c, + 0xb7206cad, + 0xb5a4430c, + 0xb7806d2d, + 0xc09c430c, + 0xd2080a10, + 0xb780a281, + 0xc09c430c, + 0xd2080a18, + 0xfff4a101, + 0xb7809a8d, + 0x0109430c, + 0x0918c098, + 0xa941d008, + 0xc0980244, + 0xd2080a00, + 0xb720a11d, + 0xb781430c, + 0x75004bad, + 0x9262c000, + 0x5c35b741, + 0x46b1b76a, + 0x5a1cc200, + 0x1d00c008, + 0xc1010103, + 0xc09c0528, + 0xc0040960, + 0xfed409d2, + 0xb7809a4e, + 0x090a430c, + 0xa9aef210, + 0x0a40c09a, + 0xa281d208, + 0x430cb720, + 0xb762040b, + 0xb5a65b4a, + 0xb7804aad, + 0xb742430c, + 0xf2105bd2, + 0xc0c4a8aa, + 0xd2080a60, + 0xb720a101, + 0xd111430c, + 0xb56465a7, + 0xb7a06bb5, + 0x008b430c, + 0x6bb5b764, + 0xc25465b3, + 0x048b9aa0, + 0x5f4bb781, + 0x75009ea8, + 0x6c4cb504, + 0x4314b740, + 0x9142c000, + 0x0a6a9e52, + 0x091cc09c, + 0xa241d008, + 0x9140c000, + 0x9e549e69, + 0x67a9b746, + 0x0a1cc09c, + 0xa101d208, + 0x430cb720, + 0x087f0d82, + 0x67a9b786, + 0x08a0c09c, + 0x0c7ec00e, + 0x4029b580, + 0x430cb720, + 0x46b1b74a, + 0x4001ba12, + 0x91e0c000, + 0x6fcdb789, + 0x02426a32, + 0x0a20c138, + 0xa011d208, + 0x430cb720, + 0xb50900b2, + 0xb7206fb5, + 0xd010430c, + 0x0d840534, + 0x4729b78a, + 0x5988c100, + 0x00b2d020, + 0xffff7088, + 0x090290fc, + 0xb9600d7f, + 0xb7804040, + 0x0228430c, + 0x0a60c098, + 0xa116d208, + 0xffff0904, + 0x048b9301, + 0x5e55b741, + 0x430cb780, + 0x76800902, + 0x0a18c0a4, + 0x0922d002, + 0xa101d208, + 0x430cb720, + 0x76860a02, + 0x52abb58a, + 0x9102c009, + 0xc0017684, + 0xb7819304, + 0x1a0c5dcd, + 0xc0017504, + 0xb7209252, + 0xc809430c, + 0xccf00a42, + 0xb5890a20, + 0xb7206aad, + 0xc809430c, + 0xc9be0a46, + 0xb5890a60, + 0xb7206b2d, + 0xc809430c, + 0xccf80a42, + 0xb5890a50, + 0xb7206a2d, + 0xc809430c, + 0xc5f80a46, + 0xb5890a20, + 0xb7206c2d, + 0xc809430c, + 0xc40a0a46, + 0xb5890a10, + 0xb7206bad, + 0xc809430c, + 0xcd0a0a42, + 0xb5890a10, + 0x040b6cad, + 0x5dccb781, + 0xc0017504, + 0xb78192e4, + 0x75045e4c, + 0x9244c001, + 0x430cb720, + 0x0a42c809, + 0x0a70cf1a, + 0x6aadb589, + 0x430cb720, + 0x0a46c809, + 0x0a30cac6, + 0x6b2db589, + 0x430cb720, + 0x0a42c809, + 0x0a70cd0c, + 0x6a2db589, + 0x430cb720, + 0x0a46c809, + 0x0a30cb92, + 0x6c2db589, + 0x430cb720, + 0x0a46c809, + 0x0a50cc94, + 0x6badb589, + 0x430cb720, + 0x0a42c809, + 0x0a20cd2e, + 0x6cadb589, + 0xb7819e69, + 0x75025e2d, + 0x92e4c001, + 0x5dadb781, + 0xc0017504, + 0xb7209244, + 0xc809430c, + 0xcf1a0a42, + 0xb5890a70, + 0xb7206aad, + 0xc809430c, + 0xcb360a46, + 0xb5890a00, + 0xb7206b2d, + 0xc809430c, + 0xc9dc0a46, + 0xb5890a10, + 0xb7206a2d, + 0xc809430c, + 0xcb0a0a46, + 0xb5890a30, + 0xb7206c2d, + 0xc809430c, + 0xcc940a46, + 0xb5890a50, + 0xb7206bad, + 0xc809430c, + 0xcd2e0a42, + 0xb5890a20, + 0x048b6cad, + 0x5e4db781, + 0xc0017508, + 0xb78192e4, + 0x75045dcd, + 0x9244c001, + 0x430cb720, + 0x0a46c809, + 0x0a40caca, + 0x6aadb589, + 0x430cb720, + 0x0a46c809, + 0x0a00cb36, + 0x6b2db589, + 0x430cb720, + 0x0a46c809, + 0x0a10c9dc, + 0x6a2db589, + 0x430cb720, + 0x0a46c809, + 0x0a30cb0a, + 0x6c2db589, + 0x430cb720, + 0x0a46c809, + 0x0a50cc94, + 0x6badb589, + 0x430cb720, + 0x0a46c809, + 0x0a70cab8, + 0x6cadb589, + 0xb781040b, + 0x75025e4c, + 0x9304c001, + 0x5dccb781, + 0x75041a0c, + 0x9252c001, + 0x430cb720, + 0x0a46c809, + 0x0a30c050, + 0x6aadb589, + 0x430cb720, + 0x0a46c809, + 0x0a50c9c8, + 0x6b2db589, + 0x430cb720, + 0x0a42c809, + 0x0a10cd38, + 0x6a2db589, + 0x430cb720, + 0x0a46c809, + 0x0a60c1a0, + 0x6c2db589, + 0x430cb720, + 0x0a46c809, + 0x0a00c0aa, + 0x6badb589, + 0x430cb720, + 0x0a42c809, + 0x0a20cd68, + 0x6cadb589, + 0x430cb720, + 0xb5ea9ebb, + 0xb7204829, + 0xb5ea430c, + 0xb79f48a9, + 0xb7bf77ee, + 0xb7df786e, + 0xb7ff78ee, + 0xc008796e, + 0xff318c20, + 0xa60d9140, + 0xb7208440, + 0x0203430c, + 0x0a10c0a4, + 0xaa01d208, + 0xc0107500, + 0x02039104, + 0x0a18c09c, + 0xaa01d208, + 0xc0017500, + 0xb7aa90c2, + 0xb7c046a9, + 0xc000478c, + 0xd0319320, + 0xc00e0a54, + 0x6a522a7c, + 0xd032024c, + 0xc0000ac8, + 0xff149080, + 0xf2109945, + 0x0d92aa21, + 0xffff750c, + 0xd0119344, + 0x02890a52, + 0x2afcc00e, + 0x430cb720, + 0x4729b78a, + 0xffff710a, + 0xb7209088, + 0x0203430c, + 0x0a18c09c, + 0xaa01d208, + 0xc0027500, + 0x9ea39304, + 0x4731b7ca, + 0x98b8ff74, + 0x430cb780, + 0x0a10c0a4, + 0xab01d208, + 0xc00e7580, + 0x0a869084, + 0x56d89eaa, + 0x2efcc00e, + 0xff34058b, + 0x9eb29945, + 0x018d9eb1, + 0x010d008b, + 0xa31d058d, + 0xa295a319, + 0x9af4ff94, + 0x4314b700, + 0x5e88d322, + 0x4490b760, + 0x00d0c101, + 0x6fb5b729, + 0x6c50b74d, + 0xd0319e73, + 0xc00e6d19, + 0xc1012cfc, + 0xc1380120, + 0xd0080920, + 0x1b04a951, + 0x99ebff14, + 0x430cb740, + 0x00d4058b, + 0x6fadb789, + 0x02446a32, + 0x0a20c138, + 0xa311d208, + 0x430cb780, + 0x008b02d8, + 0x0a7ec00e, + 0x6fadb589, + 0x9997ff34, + 0x560cb780, + 0xc2209ea2, + 0xb720a349, + 0x0203430c, + 0x0a18c0a4, + 0xaa01d208, + 0xc0007500, + 0xb78991a2, + 0xb7646bad, + 0x018379b5, + 0x09b0c0c4, + 0x9c629ea4, + 0x9080c000, + 0x532bb58c, + 0x430cb720, + 0x67adb784, + 0xb5840a04, + 0xb7a067ad, + 0x008b430c, + 0x612db781, + 0xc0017500, + 0xb7849122, + 0x750067ad, + 0x9082c001, + 0x46a9b74a, + 0x4729b76a, + 0xc0000d82, + 0x9d4d9100, + 0xd3f1aa61, + 0xc1012d2e, + 0xd01205b8, + 0x70860922, + 0x5e30d122, + 0x0a00c021, + 0x0a00c034, + 0x9234ffff, + 0xaa2df208, + 0xa9a9f208, + 0xc25461c7, + 0x008b9816, + 0x4badb506, + 0x430cb720, + 0xb5a50a82, + 0xb72044ab, + 0xb76a430c, + 0xff344831, + 0xb720993a, + 0x0203430c, + 0x0a18c09c, + 0xaa01d208, + 0xc0027500, + 0xb7849162, + 0x0a04682d, + 0x682db584, + 0x430cb740, + 0x0914c09c, + 0xaa41d008, + 0xc0007500, + 0xd0089322, + 0xb720a2c1, + 0x097f430c, + 0x6e29b788, + 0xc0980218, + 0xd2080a04, + 0x0098aa1d, + 0x08a0c098, + 0x41a9b5a0, + 0x430cb780, + 0x0a30c096, + 0xa101d208, + 0x430cb720, + 0x6cadb784, + 0xc0007506, + 0xb7849182, + 0xb584782d, + 0xb720772d, + 0xb784430c, + 0xb58477ad, + 0xb720782d, + 0xb784430c, + 0xb5846fad, + 0xb7406f2d, + 0x0205430c, + 0x0a04c098, + 0xaa01d208, + 0x0900c098, + 0xa241d008, + 0x9180c000, + 0xc0c40203, + 0xd2080a60, + 0xb786a901, + 0x02444aad, + 0x4aadb586, + 0x430cb780, + 0xc09c0a82, + 0xd2080a10, + 0xb720a281, + 0xb744430c, + 0x748068ad, + 0x91a2c000, + 0x67adb784, + 0xc0007088, + 0xffd49104, + 0xb7209b37, + 0xb5a5430c, + 0xb720482d, + 0xb743430c, + 0x76805733, + 0x93c4c004, + 0x5eadb783, + 0x09020e82, + 0xd00a7502, + 0x75040ed2, + 0x0922d001, + 0x0a027508, + 0x0a42d001, + 0x31287740, + 0x90a2c000, + 0xc0009e55, + 0x748090c0, + 0xd0010a86, + 0x08020ad2, + 0x5b14b7c0, + 0x49049e83, + 0x0b7ec03e, + 0x4018b960, + 0x9000c004, + 0xaa49c060, + 0x430cb760, + 0x5c88c100, + 0x4000ba24, + 0x0058d020, + 0xd0220496, + 0x04925c88, + 0x0096c101, + 0x0496c101, + 0xb7830092, + 0x00967bcb, + 0x57abb723, + 0x4002ba24, + 0x0242e000, + 0x90acc000, + 0xc0000201, + 0xd0109080, + 0xd022710c, + 0xc1015c88, + 0x00920096, + 0xb5830096, + 0xd12257ab, + 0xb7805c88, + 0xc101430c, + 0x00920096, + 0x00980d04, + 0xb5037688, + 0xfffe7bab, + 0x77409124, + 0x9364c001, + 0xaa49c060, + 0x430cb760, + 0x5c88c100, + 0x4000ba24, + 0x0028d020, + 0xd0220496, + 0x04925c88, + 0x0096c101, + 0x0496c101, + 0xb7830092, + 0x00967bcb, + 0x57abb723, + 0x4002ba24, + 0x0242e000, + 0x90acc000, + 0xc0000201, + 0xd0109080, + 0xd022710c, + 0xc1015c88, + 0x00920096, + 0xb5830096, + 0xd12257ab, + 0xb7805c88, + 0xc101430c, + 0x00920096, + 0x00980d04, + 0xb503768e, + 0xfffe7bab, + 0x0d849124, + 0x9101c000, + 0x4490b760, + 0x9a60fef4, + 0x90a0c000, + 0xfffc9e82, + 0xb79f9020, + 0xb7bf7dee, + 0xb7df7e6e, + 0xc0027eee, + 0x9c228c20, + 0x430cb720, + 0x5dadb781, + 0x7eadb742, + 0xc1007504, + 0xc000580c, + 0xcd8e90a4, + 0x9c220841, + 0x0850cfff, + 0x0800c480, + 0x9e589c22, + 0xe0000c86, + 0xd0054030, + 0x9e511c94, + 0x72c0c814, + 0x70c0c814, + 0x7480c008, + 0x483cb323, + 0x445cb323, + 0x927cc000, + 0x5a1be100, + 0x90ccc000, + 0x0a02c002, + 0x90e0c000, + 0x7100d002, + 0xba240a04, + 0x9e504000, + 0x51d3d024, + 0xc0055193, + 0xc00074c0, + 0xe180923c, + 0xc0005a57, + 0xc00290cc, + 0xc0000a02, + 0xd00290e0, + 0x0a047100, + 0x4000ba24, + 0x51f30098, + 0x470cb740, + 0xd0109e58, + 0xc0200636, + 0xb740a906, + 0x7293468c, + 0xaa05ca20, + 0xd11d6039, + 0xd11e1623, + 0xd01a1215, + 0xd01c5013, + 0xc1015010, + 0x9c226003, + 0xb7a0a60d, + 0x0707430c, + 0xb781008b, + 0x75045dad, + 0x93c4c007, + 0x9b94fff4, + 0x5c88d322, + 0x009a0d06, + 0x7f2db722, + 0x10029e52, + 0x7080d812, + 0xb781008b, + 0x9eb55e2d, + 0xc0007502, + 0x750890a2, + 0x93a4c000, + 0xb787008b, + 0x75004e2d, + 0x92e2c000, + 0x402db743, + 0xb3207104, + 0xc0004834, + 0x008b9154, + 0x4629b76a, + 0x11c4d020, + 0xfff40d02, + 0x0a069b7e, + 0x7100d012, + 0x7188c810, + 0xb7209e95, + 0xb7804314, + 0x0b1e448c, + 0x75009e4a, + 0x0938c0a2, + 0xa941d008, + 0x4422b346, + 0xc0007480, + 0xb7809282, + 0xb764414d, + 0xb7436bd5, + 0xb76a41cd, + 0xc1014649, + 0x0d0265b9, + 0x15b4c101, + 0x9b59fff4, + 0xc0009e82, + 0xd3229180, + 0xc1015c88, + 0xb7830092, + 0xb72540ad, + 0x109846ad, + 0xcffe9e8a, + 0x76802d7c, + 0x9382c004, + 0x7740c080, + 0x9094c000, + 0x0e81cfc0, + 0x4314b720, + 0x5e4db781, + 0xc0007508, + 0xb78192c4, + 0x1a045ecd, + 0xc0007502, + 0xb7449192, + 0x0a1a6ccd, + 0x74829e69, + 0x1a46d009, + 0xc00052b1, + 0xd2a29140, + 0xc0005e85, + 0xd2a290c0, + 0xd1105e0d, + 0x9e5316d9, + 0x15dac101, + 0xfff40d02, + 0x09869b1c, + 0x70c0c812, + 0x4314b700, + 0x5c88d322, + 0x5e4cb781, + 0x0090c101, + 0x7f35b742, + 0xd0127508, + 0xc0001248, + 0xc10090c4, + 0xd0125a05, + 0xc03e70c8, + 0xc41009fe, + 0x0a067106, + 0x9ea39e89, + 0x7246c412, + 0x7086cc10, + 0x70c8cc12, + 0x0a7ec00f, + 0x0a7ccffe, + 0x0155d120, + 0xc8109ea3, + 0x0d827286, + 0x7286c812, + 0x5c88d322, + 0x5918c300, + 0x0090c101, + 0x40adb723, + 0x9e4c5cd8, + 0x2900c01c, + 0x04877440, + 0xd0013904, + 0x31280cb4, + 0xc0037440, + 0xcf000a7e, + 0xd0010a00, + 0x59b00db8, + 0x9e5c21b8, + 0xd0113126, + 0x31280de2, + 0x59b09e4c, + 0x02073128, + 0x0a00c021, + 0x0a40c01e, + 0xf0009dcd, + 0xc0ffa161, + 0xcffe0a7e, + 0x9ea30a40, + 0x0a0a7440, + 0x1a44d002, + 0x25265d10, + 0x3d049ea3, + 0x0980c021, + 0xc01e3526, + 0x9dbd09d0, + 0xa162f000, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x8440a61d, + 0x0a32d011, + 0x5b30c200, + 0x6931d0d1, + 0x540cb780, + 0xa1969e9e, + 0x03a8d020, + 0xc021010d, + 0xa1120900, + 0x0930c03c, + 0xa9619d2d, + 0xda109e92, + 0xcff0aa61, + 0x5d212d00, + 0x0629d110, + 0x5e88d322, + 0xa261da10, + 0x430cb720, + 0x297cc00e, + 0x00d2018d, + 0x40adb783, + 0x0980c021, + 0x0980c03a, + 0x0629d110, + 0x40adb583, + 0x430cb720, + 0x41adb783, + 0x0244c101, + 0x41adb583, + 0xaa65da10, + 0xda100228, + 0xb720a265, + 0x00d2430c, + 0x40adb783, + 0xb5830228, + 0xb72040ad, + 0xb783430c, + 0x024441ad, + 0x41adb583, + 0xa9e19d3d, + 0xaa65f210, + 0x29fec7ff, + 0x0246010d, + 0x0900c021, + 0x0920c03a, + 0xa265f210, + 0xa9619d2d, + 0xaa69f210, + 0xc7ff9eb2, + 0x0244297e, + 0x0d00c021, + 0x0d10c03a, + 0xa269f210, + 0xa9629d2e, + 0xaa61f250, + 0xc021010d, + 0xc1010900, + 0xc03a0244, + 0xf2500930, + 0x9d2da261, + 0xf250a961, + 0x9eb3aa65, + 0xc040000d, + 0x02440800, + 0x0d80c021, + 0xc042a019, + 0xf2500dc0, + 0x9d3ea265, + 0xf210a9e2, + 0xc00faa71, + 0x018d2dfe, + 0x0246c101, + 0x0980c021, + 0x09d0c042, + 0xa271f210, + 0xa9e19d3d, + 0xaa75f210, + 0x29fec00f, + 0x0246038d, + 0x0b80c021, + 0x0ba0c042, + 0xa275f210, + 0xa8e19d7d, + 0xaa7df210, + 0x28fccffe, + 0x0242010d, + 0x0900c021, + 0x0930c042, + 0xa27df210, + 0xa9619d2d, + 0xaa79f210, + 0xc00f9eb2, + 0x0244297e, + 0x0d00c021, + 0x0d40c048, + 0xa279f210, + 0xa9629d2e, + 0x01b6c101, + 0x020d01b2, + 0xc00f01b4, + 0xc0212d7e, + 0xc1010a00, + 0xc04601b4, + 0x9d4d0a60, + 0x9ea2aa61, + 0xa979da10, + 0x2d7cc3ff, + 0xda105d41, + 0xc101a9fe, + 0xb7400524, + 0xc3fe430c, + 0xc1012a7c, + 0x01540246, + 0xa17ada10, + 0xa27dda10, + 0x0960c05e, + 0xaa41f008, + 0x0238058d, + 0xa241f008, + 0x430cb720, + 0x402db783, + 0xb5830246, + 0xb7a0402d, + 0xfff44314, + 0xc1019999, + 0xc05e02da, + 0xf2080ae0, + 0x7140aaa1, + 0x90e6c000, + 0x04dcd010, + 0xb58c0a06, + 0xb7204829, + 0xb747430c, + 0x74804e2d, + 0x9222c000, + 0x402db783, + 0xc0007104, + 0xb78c9186, + 0x750065ab, + 0x90e4c000, + 0x009cc101, + 0xb58c0a04, + 0xb7204929, + 0xb781430c, + 0x75045dad, + 0x92c4c002, + 0xc021020d, + 0xc01e0a00, + 0x9d4d0a60, + 0x9e79aa61, + 0xb7009e78, + 0x9ea25431, + 0x54b1b760, + 0x55b0b720, + 0x5529b720, + 0xc2000109, + 0xcff059e1, + 0xc1012900, + 0xc00f0240, + 0xb5802d7c, + 0x59215428, + 0x5d41020d, + 0x0126c101, + 0x0522c101, + 0x01b2c101, + 0x0a00c021, + 0x54a8b540, + 0x5530b540, + 0x55a8b560, + 0x0a70c01e, + 0xaa619d4d, + 0xb7009ea2, + 0xb7605630, + 0xb72056b0, + 0xb7205728, + 0x010957b0, + 0x2900cff0, + 0x2d7cc00f, + 0x59e1c200, + 0x5d415921, + 0x0126c101, + 0x0522c101, + 0x01b2c101, + 0x0240c101, + 0x5628b580, + 0x56a8b540, + 0x5730b540, + 0x57a8b560, + 0x2deed3f2, + 0x9978fff4, + 0x7600a812, + 0x9322c005, + 0xb780a919, + 0xc300590c, + 0xc0215d18, + 0xc0020900, + 0xd1300910, + 0x9d2d0529, + 0x9e92a961, + 0xa9cad858, + 0xa8cdd858, + 0xa8d2d858, + 0xa856d858, + 0xcff00205, + 0xc00f2a00, + 0xc1002d7c, + 0xc20059e1, + 0x5d415a21, + 0x297cc00e, + 0x0246c101, + 0x05220122, + 0x01b0c101, + 0xa249d858, + 0xa14dd858, + 0xa152d858, + 0xa1d5d858, + 0xaa619d7d, + 0xabf9da10, + 0xa87ef210, + 0xa941f058, + 0xa879f210, + 0xaaf1f210, + 0xaaf6f210, + 0xab7dda10, + 0xa9dbd858, + 0xa946f058, + 0xa9c9f058, + 0xa9cef058, + 0xa8d5f058, + 0xa8d2f058, + 0xc101a39d, + 0xf0580120, + 0xa81eabd9, + 0xa964f212, + 0x5a61c200, + 0x9eb80490, + 0x0246c301, + 0xc1014c7d, + 0x01ba0520, + 0x009c05ba, + 0xd8587205, + 0xf058a259, + 0xf058a141, + 0xf058a146, + 0xf058a1c9, + 0xf058a1ce, + 0xf058a0d5, + 0xc000a0d2, + 0xd0d891b2, + 0xe010aa59, + 0x0a048b21, + 0xa159f058, + 0xa259d0d8, + 0x90c0c000, + 0x0274c001, + 0xa259f058, + 0xaa5df058, + 0xa969f210, + 0x4d7d9ea2, + 0xc0007295, + 0xd0d891b2, + 0xc101aa5d, + 0xf0581124, + 0x0a04a15d, + 0xa25dd0d8, + 0x90a0c000, + 0xf0580244, + 0xb720a25d, + 0x0203430c, + 0x0a18c0a4, + 0xaa01d208, + 0xc0007500, + 0xb7899122, + 0xa9966a2d, + 0x050f9e73, + 0x9c629ea4, + 0xc002058f, + 0xb79f09c2, + 0xb7bf7d6e, + 0xb7df7dee, + 0xb7ff7e6e, + 0xc0027eee, + 0xfe698c40, + 0xb79f9200, + 0xb7bf7d6e, + 0xb7df7dee, + 0xb7ff7e6e, + 0xc0027eee, + 0x9c228c40, + 0x430cb740, + 0x0d020487, + 0xc0c60185, + 0xb9600980, + 0xc0204258, + 0x9e9baa29, + 0xa269c020, + 0xffff0d04, + 0xc0d29341, + 0x0a060960, + 0xa241d008, + 0x430cb720, + 0x0a70c002, + 0x792bb58c, + 0x5749b780, + 0x430cb720, + 0x4aabb58d, + 0xb7209c22, + 0x9e53430c, + 0xa95df010, + 0xa956f010, + 0x712db786, + 0x0124c101, + 0xb5860244, + 0xb720712d, + 0xf008430c, + 0xf008a972, + 0xb786a979, + 0xc10170ad, + 0x02440124, + 0x70adb586, + 0x430cb720, + 0xa966f008, + 0xa969f008, + 0x71adb786, + 0x0124c101, + 0xb5860244, + 0xb72071ad, + 0xd808430c, + 0xd808a961, + 0xb786a966, + 0xc101702d, + 0x02440124, + 0x702db586, + 0x430cb720, + 0x64adb786, + 0xb3407502, + 0xd80848a4, + 0xb786a965, + 0x02446fad, + 0x6fadb586, + 0xf0109c22, + 0xb720a9e2, + 0x0906430c, + 0x6435b566, + 0x430cb780, + 0x0a70c0d0, + 0xa101d208, + 0xa6059c22, + 0x430cb7a0, + 0xaa41d810, + 0xa945d810, + 0x0685008b, + 0x5e35b741, + 0x00890244, + 0xf2107684, + 0xcffea9b1, + 0xc00028fc, + 0x9ea99224, + 0x5a4bb78d, + 0x6dcdb746, + 0xb58d0238, + 0xf2105a4b, + 0x0128aa35, + 0x6dcdb546, + 0x9120c000, + 0xb56d9ea9, + 0xf2105a4b, + 0xb586aa35, + 0x9ea96dcd, + 0xa9a1f250, + 0x6fcdb786, + 0x704db746, + 0x6ed5b746, + 0xb5860246, + 0xf2506fcd, + 0xb786a9a5, + 0xb7666f4d, + 0x01265ad5, + 0x704db546, + 0xa925da10, + 0xb5860242, + 0xc1016f4d, + 0xb5460524, + 0xf2106ed5, + 0xf210a931, + 0xd0a4aa35, + 0x01835905, + 0xc1010244, + 0xb56605b8, + 0xda105ad5, + 0xda10aa39, + 0xc101a9be, + 0x05b405b8, + 0x9a5ec214, + 0xb786008b, + 0xb74c69ad, + 0x02406d2b, + 0xb5860904, + 0xb54c69ad, + 0xb7806d2b, + 0x7500448c, + 0x90a4c000, + 0xc0000109, + 0x9e699180, + 0xb9600902, + 0xb7804030, + 0x088454a9, + 0xffff0128, + 0x9ea99381, + 0x614bb54d, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xaa61f010, + 0xb7209e5a, + 0x7500430c, + 0x9082c000, + 0x76adb586, + 0xa956d008, + 0x76bec01c, + 0x90e2c000, + 0xc0da0203, + 0xd2080a64, + 0xd008a102, + 0xc01ca959, + 0xc00074be, + 0x020390e2, + 0x0a68c0da, + 0xa101d208, + 0xc0da0103, + 0x0a060940, + 0xa241d008, + 0xa6059c22, + 0xf0109e55, + 0xf010a955, + 0xf010aa51, + 0xf208a95a, + 0xb720a9bd, + 0x0244430c, + 0x0244c101, + 0xb5860246, + 0xf208752d, + 0xf208a929, + 0xda08a9a6, + 0xda08aa25, + 0xb7a0a9a1, + 0xd1204314, + 0x01c601a7, + 0xffd40d02, + 0x9e699b22, + 0x712db506, + 0x430cb720, + 0xa935f208, + 0x702db786, + 0xb5860244, + 0xb720702d, + 0xf208430c, + 0xb786a935, + 0x0244722d, + 0x722db586, + 0x430cb720, + 0xa925f208, + 0x6fadb786, + 0xb5860244, + 0xb7206fad, + 0xda08430c, + 0xb78da939, + 0x0244612b, + 0x612bb58d, + 0x430cb720, + 0xa921da08, + 0x61abb78d, + 0xb58d0244, + 0xb72061ab, + 0xf208430c, + 0xb786a929, + 0x0244732d, + 0x732db586, + 0x430cb720, + 0xa93dda08, + 0x652bb78d, + 0xb58d0244, + 0xb720652b, + 0xda08430c, + 0xb78da925, + 0x024465ab, + 0x65abb58d, + 0x430cb720, + 0xaa25f208, + 0xa92af208, + 0x73adb746, + 0xc1010244, + 0xb5860244, + 0xb72073ad, + 0xf208430c, + 0xb786a939, + 0x0244742d, + 0x742db586, + 0x430cb720, + 0xa93df208, + 0x74adb786, + 0xb5860244, + 0xb72074ad, + 0xda08430c, + 0xda08a93a, + 0xb786a93d, + 0xc10175ad, + 0x02440124, + 0x75adb586, + 0x430cb720, + 0xaa21da08, + 0xa926da08, + 0x632bb74d, + 0xc1010244, + 0xb58d0244, + 0xb720632b, + 0xb787430c, + 0xb746462d, + 0x0244752d, + 0x462db587, + 0x430cb720, + 0xa939da08, + 0xaabdda08, + 0x492db787, + 0x024a02d4, + 0x492db587, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xa9e2f010, + 0x430cb720, + 0xb5660906, + 0xb7806435, + 0xc0d0430c, + 0xd2080a70, + 0x9c22a101, + 0x430cb720, + 0xb78e9e58, + 0x11095bab, + 0x9060c000, + 0x70080004, + 0x93d6ffff, + 0x9060c000, + 0x74000008, + 0x93d8ffff, + 0x287ccffe, + 0xa6059c22, + 0x430cb7a0, + 0xb78e008b, + 0xc2005b2b, + 0x009a5888, + 0x7eb5b7a6, + 0x7eb5b566, + 0x09c2d012, + 0x9bdcfff4, + 0xb50e008b, + 0x9e685b2b, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0x430cb720, + 0x5e2db781, + 0xc0007502, + 0xb78691c4, + 0x750040a9, + 0x9122c000, + 0x5aabb78c, + 0x75020802, + 0x0802d002, + 0x08029c22, + 0xa60d9c22, + 0x4314b7a0, + 0xb7669e69, + 0xfff45ab5, + 0x03019bca, + 0x9be2fff4, + 0xc0007400, + 0x9e6991c2, + 0x6cadb786, + 0x5aadb746, + 0x4e35b746, + 0xc1010128, + 0xb5461124, + 0xb7a06cad, + 0x008b430c, + 0x75adb786, + 0xc0017500, + 0xfff49264, + 0x74009bc9, + 0x91a2c000, + 0xb78e008b, + 0xb7445bab, + 0x708867ad, + 0xd0040a02, + 0xc0000a42, + 0x008b9360, + 0x5e2db781, + 0xc0007502, + 0x750890a2, + 0x9204c000, + 0xb784008b, + 0xb74667ad, + 0xb7464e2d, + 0x624550b5, + 0x0a027115, + 0x0a42d00b, + 0x9080c000, + 0x008b0a06, + 0x75adb586, + 0xb706008b, + 0x740075ad, + 0x93a2c000, + 0x9b98fff4, + 0xc0007400, + 0x9e6992a2, + 0x532db786, + 0x7188c010, + 0x5b2bb78e, + 0x5a08c200, + 0x00cac101, + 0x7eadb786, + 0x12400268, + 0x7eadb586, + 0x90a0c000, + 0xb7069e69, + 0xb79f4e2d, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xf8399c22, + 0xfff4a205, + 0x74009b73, + 0x430cb700, + 0x91e2c000, + 0x5b2ab78e, + 0x532cb746, + 0x5a08c200, + 0xb78600c0, + 0xc0107ead, + 0xc0017104, + 0xb74192a0, + 0x74885e2c, + 0x9164c000, + 0x5b2ab78e, + 0x5a08c200, + 0xb70600c0, + 0xc0017ead, + 0xb78690e0, + 0x750075ac, + 0x9004c001, + 0xc0007482, + 0x748890a2, + 0x91e4c000, + 0x67acb784, + 0x4e2cb746, + 0x50b4b746, + 0x71156245, + 0xd00b0a02, + 0xc0000a42, + 0x0a069060, + 0x75acb586, + 0x75acb786, + 0xc0007500, + 0x000990a4, + 0x9080c000, + 0x4e2cb706, + 0xaa1df9f8, + 0xf0109c22, + 0xf010a966, + 0x9e5ca9e2, + 0x7100c814, + 0x7080c802, + 0x0e22d011, + 0x753c0244, + 0x915cc000, + 0x087ec7ff, + 0xcffe76c0, + 0xd00e087c, + 0x9c220804, + 0xc0007680, + 0xd02290bc, + 0x9c225468, + 0xc1019e5a, + 0x50531205, + 0x76e69c22, + 0x1000d01e, + 0x48bcb340, + 0x76c0c380, + 0x1524d02e, + 0x913cc000, + 0x084ec002, + 0xd0119c22, + 0xd2240e32, + 0x9e525987, + 0x76c8c004, + 0x4002ba12, + 0xffff0d04, + 0xb78092da, + 0x9e5b5d8c, + 0xc040691a, + 0x0244aa65, + 0xba000009, + 0x9c224002, + 0xa205f839, + 0x1cb0f011, + 0x9e539e99, + 0x90d6c000, + 0x0902c002, + 0x9100c000, + 0x7040d002, + 0x01090a04, + 0x4000ba12, + 0x19400d02, + 0xc8129e50, + 0x0a067004, + 0xcc1251eb, + 0xc00270c8, + 0xcc100dce, + 0xcc127246, + 0xba1b72c4, + 0xb7804003, + 0x05b65d0c, + 0x512b9e58, + 0xa986c840, + 0x4003ba1b, + 0x65b5c101, + 0x9bc4c1f4, + 0xfff49e83, + 0xf9f89baa, + 0x9c22aa1d, + 0x0e30f011, + 0x4314b760, + 0x92e2c000, + 0xc10100c8, + 0xb74c0096, + 0x750273b3, + 0xba120912, + 0xd0024003, + 0xd0311926, + 0xd0100e20, + 0xc10170c8, + 0xd0120124, + 0xba1c7104, + 0x9e5c4002, + 0x0a48c0c8, + 0xa801d208, + 0x70c0c010, + 0xaa1dd3e8, + 0x7008c012, + 0x287ccffe, + 0xf8399c22, + 0xb720a205, + 0x0a02430c, + 0x9e8b0992, + 0x702bb58c, + 0x0db0c0d4, + 0x6d2bb58c, + 0x5aadb586, + 0x542bb58d, + 0x672db586, + 0x67adb586, + 0x692db586, + 0x64adb586, + 0x996ffe54, + 0x4314b760, + 0x0982c002, + 0x0da0c0d6, + 0xaa1df9f8, + 0x90a0fe4b, + 0x430cb780, + 0xd11005b6, + 0xb78c04b9, + 0x01b87cab, + 0x4002ba1b, + 0x70c0d014, + 0x753ec7fc, + 0x48bab340, + 0x7cabb56c, + 0xa6059c22, + 0x430cb7a0, + 0xb781008b, + 0xb7a65e2d, + 0x75026bb5, + 0x9184c000, + 0x9a40fff4, + 0xc0007400, + 0x008b90e4, + 0x50adb706, + 0x9060c000, + 0xd1100802, + 0xb79f1451, + 0xb7bf7f6e, + 0x8c407fee, + 0xa6059c22, + 0x430cb7a0, + 0xb766008b, + 0xfff45ab5, + 0x008b9a0c, + 0x6cadb746, + 0x5aadb786, + 0x12400244, + 0x6cadb586, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xb7a0a60d, + 0x097a430c, + 0xb786008b, + 0xd010502d, + 0x196c7104, + 0x7104cc12, + 0x4e2db786, + 0xb76c9e9d, + 0x9ea35a2b, + 0x6a7ac006, + 0x5bb3b5ae, + 0x0d025988, + 0x5b1fc200, + 0x983fffd4, + 0x7740008b, + 0x4eadb506, + 0x9c87c820, + 0x9116c000, + 0x4000b960, + 0x9080c000, + 0x7e2db5c6, + 0xffff0890, + 0x008b93a1, + 0x4e2db786, + 0x655dd131, + 0x6659d131, + 0x6cadb546, + 0x4fadb586, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x8420a61d, + 0x430cb7a0, + 0x0787a199, + 0xb7869ea8, + 0x7502714c, + 0x1092d019, + 0x9132c000, + 0x08829ea8, + 0x634ab78d, + 0xd00a7502, + 0x9ea80892, + 0xb786a09d, + 0x7504564c, + 0x9364c001, + 0x5e4cb781, + 0x734ab7cc, + 0x594cb7e6, + 0xc0007502, + 0xfff49284, + 0x740099b1, + 0x91e4c000, + 0xb786008b, + 0xb74650ad, + 0xc2006bad, + 0x70885a08, + 0x90bcc000, + 0xc0000f32, + 0x0f129060, + 0xb7819ea8, + 0x75025e4c, + 0x9104c006, + 0x4e4cb786, + 0x4fccb746, + 0x70886a1e, + 0x9016c006, + 0x5e05d322, + 0x9ea50268, + 0x2efccffe, + 0x9180c001, + 0xc0c8020b, + 0xd2080a38, + 0xc001aa82, + 0x77c090a0, + 0x91e4c000, + 0xb78c008b, + 0x75006e2b, + 0x9122c000, + 0xc0c8020b, + 0xd2080a38, + 0xc003ab01, + 0xd01092e0, + 0x9ea804fe, + 0xc380009a, + 0xb78c5c88, + 0xb7cc654a, + 0xc101742b, + 0xb7e6049a, + 0x752a5b4d, + 0x9eb50f0e, + 0x1f62d009, + 0x7440a89d, + 0x9004c003, + 0xc00075c0, + 0x020b9124, + 0x0a48c0c8, + 0xa902d208, + 0x9100c004, + 0x9ebba91a, + 0x4002ba1e, + 0x9a6afff4, + 0x430cb720, + 0xb7819e82, + 0xcffe5e2d, + 0x75042d7c, + 0x9304c003, + 0xb7869ea8, + 0x7504564c, + 0x9242c003, + 0x5c88d3a2, + 0xb746009a, + 0xb7865f2d, + 0xb726602d, + 0xb7666135, + 0xe0006235, + 0xc0030244, + 0xc2009062, + 0x01a85905, + 0x6525d131, + 0xc18005b2, + 0xc1015a05, + 0xc10105b8, + 0xc1f405b4, + 0x9e829a2a, + 0x2d7ccffe, + 0x9200c002, + 0x5eadb781, + 0x75021a04, + 0x9192c000, + 0x066cd010, + 0xc1019e51, + 0xd010024a, + 0x9e6a7048, + 0x9120c000, + 0x066ad010, + 0xd0109e51, + 0xd0107048, + 0xd012155c, + 0x03097104, + 0x2b7ccffe, + 0x4314b7a0, + 0xb781040b, + 0x75025e4c, + 0x92a4c001, + 0x98f4fff4, + 0xc0017400, + 0x008b9204, + 0x562db786, + 0xc0017504, + 0x040b9144, + 0x64cab78d, + 0x7510c004, + 0x9074c001, + 0x50adb786, + 0x6badb746, + 0x5a08c200, + 0xc0007088, + 0x020b9356, + 0x0a38c0c8, + 0xab01d208, + 0x9260c000, + 0xb7ac008b, + 0x774073b3, + 0x9204fffb, + 0x9080fffa, + 0x430cb720, + 0x5e2db781, + 0xfffd7508, + 0xfffd93a4, + 0x058f9160, + 0x4002ba1e, + 0x9a12fff4, + 0x7deeb79f, + 0x7e6eb7bf, + 0x7eeeb7df, + 0x7f6eb7ff, + 0x8c20c002, + 0xa60d9c22, + 0x430cb7a0, + 0xb786008b, + 0x750075ad, + 0x9264c001, + 0x98a8fff4, + 0xc0007400, + 0x008b91a2, + 0x5babb78e, + 0x67adb744, + 0x0a027088, + 0x0a42d004, + 0x9360c000, + 0xb781008b, + 0x75025e2d, + 0x90a2c000, + 0xc0007508, + 0x008b9204, + 0x67adb784, + 0x4e2db746, + 0x50b5b746, + 0x71156245, + 0xd00b0a02, + 0xc0000a42, + 0x0a069080, + 0xb586008b, + 0x008b75ad, + 0x75adb786, + 0xc0037500, + 0x008b9042, + 0x5e2db781, + 0xc0027508, + 0x75029382, + 0x9224c000, + 0x986efff4, + 0x0a80f012, + 0x9164c000, + 0xb786008b, + 0xb7464e2d, + 0x6a1e4fad, + 0xc0017088, + 0x008b93f6, + 0x53abb78c, + 0xc0027500, + 0x008b9082, + 0x7433b7ac, + 0x6badb786, + 0xd8120906, + 0xb7817104, + 0x75025e2d, + 0x9184c000, + 0x984cfff4, + 0xc0007400, + 0x008b90e4, + 0x50adb786, + 0x9060c000, + 0xba2d0a02, + 0x9ea24003, + 0x9eb39e6b, + 0x995afff4, + 0x15d1d110, + 0x59870a0e, + 0x70c8cc10, + 0xcc121a08, + 0x008b70c8, + 0x6db3b76c, + 0xba1b1187, + 0xb79f4002, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0x90c0ffee, + 0x99d7fff4, + 0x9e6c1001, + 0x7008c012, + 0xfffe7400, + 0xfffd905a, + 0xb79f9340, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xa6059c22, + 0x4314b7a0, + 0xb78c9e69, + 0x75025aab, + 0x1a42d001, + 0x90c2c000, + 0xb78c9e69, + 0x0a045b2b, + 0x652bb58d, + 0xb74c9e69, + 0xd0115aab, + 0xcffe1a22, + 0xc07c2a7c, + 0xc000753e, + 0x097f91f4, + 0x0a7ecffe, + 0x63abb54d, + 0x732db586, + 0x642bb54d, + 0x64abb54d, + 0x9120c001, + 0x9e939e69, + 0x5b2bb7ac, + 0x09d2d011, + 0x98f0c1f4, + 0x75401804, + 0x09029e69, + 0x287ccffe, + 0x0922d002, + 0xb50d0104, + 0x0a06642b, + 0x7088c812, + 0x652bb78d, + 0x4e35b746, + 0x62400a84, + 0xb5ad02d8, + 0xcffe64ab, + 0xc1012afc, + 0xb54d62d5, + 0xb5a663ab, + 0x9e69732d, + 0x652bb78d, + 0x4e2db746, + 0xb5866245, + 0xb79f73ad, + 0xb7bf7f6e, + 0x8c407fee, + 0xa61d9c22, + 0xb7c08440, + 0x008d430c, + 0x5e2db781, + 0x76adb726, + 0xc0087508, + 0x744092e4, + 0x90c2c006, + 0x0d029eb1, + 0x654bb76d, + 0x64cbb78d, + 0x4e4db746, + 0x5985d1a4, + 0xc1011094, + 0xa09905b8, + 0x99d7ffb4, + 0x008da015, + 0x652bb7ed, + 0x68abb78d, + 0x76b5b7c6, + 0x6333b7ed, + 0x1a041278, + 0x7780c005, + 0xc0009ea5, + 0xb7669152, + 0xc0054e2d, + 0xd31474c0, + 0xc0005da8, + 0x9eb190f4, + 0xb766058d, + 0x59ab4e4d, + 0x987fc1f4, + 0xcffea916, + 0xa9992efc, + 0x162ed010, + 0xba249e6a, + 0x624f4002, + 0x0659d110, + 0xb766008d, + 0xb7466ab5, + 0xba246b35, + 0xba124002, + 0x62474002, + 0xb76c6127, + 0xc1015a2b, + 0xc10105b8, + 0xb5660524, + 0xb5466ab5, + 0xb5c66b35, + 0x058d4e35, + 0x02815988, + 0x985cc1f4, + 0xb786008d, + 0xb506532d, + 0xc0054ead, + 0xc0007500, + 0xc20090f4, + 0x61595a2b, + 0x90a0c000, + 0xc2006259, + 0x9eb15929, + 0x4fcdb786, + 0x534db546, + 0x7500c005, + 0x90f4c000, + 0x5a2bc200, + 0xc0006159, + 0x625990a0, + 0x5929c200, + 0xb786008d, + 0xb5466bad, + 0xc0054fad, + 0xc0007500, + 0xc20090f4, + 0x62595a2b, + 0x90a0c000, + 0xc2006259, + 0x9eb15a29, + 0x6ccdb706, + 0x6bcdb586, + 0x7400c005, + 0x90f4c000, + 0x5a2bc000, + 0xc0006059, + 0x625190a0, + 0x5829c200, + 0xb7a6008d, + 0xb7a64e2d, + 0xb5064fb5, + 0xd2a46cad, + 0xd01059fd, + 0xc101065a, + 0xc10105ba, + 0xc200024a, + 0x5d875a13, + 0x01c7d120, + 0x50adb586, + 0xc1d4018b, + 0x600b9bff, + 0xb5069eb1, + 0xc01250cd, + 0x0a02700a, + 0x16d0c101, + 0x5155b5a6, + 0x76cdb586, + 0x50cdb506, + 0xc0da9eb2, + 0xd0100d60, + 0x7480a941, + 0x9142c000, + 0xc0c8020d, + 0xd2080a38, + 0x0a02a101, + 0xa241d010, + 0xc0da9eb2, + 0xd0100d68, + 0xc01ca941, + 0xc00074be, + 0xc0029222, + 0xd0100a4e, + 0x09027088, + 0x7104d012, + 0xc0c8010d, + 0xd0080948, + 0x0a7fa241, + 0xa241d010, + 0xc0da9eb2, + 0xd0100d64, + 0xc01ca941, + 0xc00074be, + 0xc0029222, + 0xd0100a4e, + 0x09027088, + 0x7104d012, + 0xc0c8010d, + 0xd0080944, + 0x0a7fa241, + 0xa241d010, + 0x9a8ffff4, + 0x7d6eb79f, + 0x7deeb7bf, + 0x7e6eb7df, + 0x7eeeb7ff, + 0x8c40c002, + 0x9380ffe3, + 0xc0067440, + 0x9eb190c2, + 0xb76d0d02, + 0xb78d654b, + 0xb74664cb, + 0xd1a44e4d, + 0x10945985, + 0x05b8c101, + 0xffb4a09d, + 0xa01198c2, + 0xb7ed008d, + 0xb78d652b, + 0xb7c668ab, + 0xb7ed76b5, + 0x12786333, + 0xc0051a04, + 0x9ea57780, + 0x9152c000, + 0x4e2db766, + 0x74c0c005, + 0x5da8d314, + 0x90f4c000, + 0x058d9eb1, + 0x4e4db766, + 0xc1d459ab, + 0xa9129b6a, + 0x2efccffe, + 0xd010a99d, + 0x9e6a162e, + 0x4002ba24, + 0xd110624f, + 0x008d0659, + 0x6ab5b766, + 0x6b35b746, + 0x4002ba24, + 0x4002ba12, + 0x61276247, + 0x5a2bb76c, + 0x05b8c101, + 0x0524c101, + 0x6ab5b566, + 0x6b35b546, + 0x4e35b5c6, + 0x5988058d, + 0xc1d40281, + 0x008d9b47, + 0x532db786, + 0x4eadb506, + 0x7500c005, + 0x90f4c000, + 0x5a2bc200, + 0xc0006159, + 0x625990a0, + 0x5929c200, + 0xb7869eb1, + 0xb5464fcd, + 0xc005534d, + 0xc0007500, + 0xc20090f4, + 0x61595a2b, + 0x90a0c000, + 0xc2006259, + 0x008d5929, + 0x6badb786, + 0x4fadb546, + 0x7500c005, + 0x90f4c000, + 0x5a2bc200, + 0xc0006259, + 0x625990a0, + 0x5a29c200, + 0xb7069eb1, + 0xb5866ccd, + 0xc0056bcd, + 0xc0007400, + 0xc00090f4, + 0x60595a2b, + 0x90a0c000, + 0xc2006251, + 0x008d5829, + 0x4e2db7a6, + 0x4fb5b7a6, + 0x6cadb506, + 0x59fdd2a4, + 0x065ad010, + 0x05bac101, + 0x024ac101, + 0x5a13c200, + 0xd1205d87, + 0xb58601c7, + 0x018b50ad, + 0x9aeac1d4, + 0x9eb1600b, + 0x50cdb506, + 0x700ac012, + 0xc1010a02, + 0xb5a616d0, + 0xb5865155, + 0xb50676cd, + 0x9eb250cd, + 0x0d60c0da, + 0xa941d010, + 0xc0007480, + 0x020d9142, + 0x0a38c0c8, + 0xa101d208, + 0xd0100a02, + 0x9eb2a241, + 0x0d68c0da, + 0xa941d010, + 0x74bec01c, + 0x9222c000, + 0x0a4ec002, + 0x7088d010, + 0xd0120902, + 0x010d7104, + 0x0948c0c8, + 0xa241d008, + 0xd0100a7f, + 0x9eb2a241, + 0x0d64c0da, + 0xa941d010, + 0x74bec01c, + 0x9222c000, + 0x0a4ec002, + 0x7088d010, + 0xd0120902, + 0x010d7104, + 0x0944c0c8, + 0xa241d008, + 0xd0100a7f, + 0xb79fa241, + 0xb7bf7d6e, + 0xb7df7dee, + 0xb7ff7e6e, + 0xc0027eee, + 0xffeb8c40, + 0x9e5a9200, + 0x70c0d014, + 0x1a0476c0, + 0x0244d01b, + 0x1228d01c, + 0xff9d9ea3, + 0xa60d92c0, + 0x4314b7a0, + 0x03079e5d, + 0xc0d8058b, + 0xc00e0da0, + 0xfe3409b2, + 0xb760986c, + 0xc00a4314, + 0xc0ca0992, + 0xfe340dc0, + 0xb7609864, + 0xc0024314, + 0xc0d60982, + 0xfe340da0, + 0xb760985c, + 0x09d24314, + 0x0dd0c0d4, + 0x9855fe34, + 0x4314b760, + 0x09f2c04c, + 0x0dd0c0e6, + 0x984dfe34, + 0x018b058b, + 0x0d80c0c6, + 0x0d32c004, + 0x9870fe34, + 0x9e699e68, + 0x4facb786, + 0x040b097f, + 0x7520c001, + 0xc0c89e6c, + 0xc0da0c38, + 0xc0de0a64, + 0xd20808a0, + 0x0a22a101, + 0x4029b580, + 0x4050b760, + 0xd00d9e6b, + 0x0c821a48, + 0xb421050b, + 0x9e69443a, + 0x09e8c0da, + 0x0d60c0da, + 0xd0089e4d, + 0x9d0aa161, + 0xb56c018d, + 0xd0107332, + 0xb526a0c2, + 0x040b76b4, + 0x08e0c12a, + 0x0b02c010, + 0xa961d008, + 0x9e689e44, + 0x0a6cc0da, + 0xa101d208, + 0xaa61d008, + 0x6fb2b74d, + 0xa9c2d018, + 0x0a027500, + 0x0a42d002, + 0x0244c101, + 0x6faab58d, + 0x7e7edffc, + 0x0d38d012, + 0x430cb740, + 0x05a2d022, + 0xc0c80205, + 0xd2080a48, + 0x9e58aa01, + 0x7008d010, + 0x0944c0c8, + 0xa941d008, + 0x7104d012, + 0x76440c84, + 0x462bb59a, + 0x402bb5c0, + 0x412bb5a0, + 0x0c040984, + 0xfffe0888, + 0xb7209124, + 0xb78c430c, + 0x752a652b, + 0x9112c000, + 0x0a169e69, + 0x7b2bb58d, + 0x90a0c000, + 0xb52d9e68, + 0x9e697b32, + 0xb7660d02, + 0xb76650b5, + 0xfff44e2d, + 0xcffe9b40, + 0x0a7a287c, + 0x7008c010, + 0xc0121a74, + 0x9e697008, + 0x5babb50e, + 0x98a1fff4, + 0xb78c9e68, + 0x750065aa, + 0xd0010a02, + 0xb5800a42, + 0xb79f448c, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xb7209c22, + 0x0a02430c, + 0x4aabb72d, + 0xa271f010, + 0xa269f010, + 0xa0e1f010, + 0xa265f010, + 0xa27dd810, + 0xa279d810, + 0xa279f010, + 0xa271f050, + 0xa261f050, + 0xa27dd850, + 0xa279d850, + 0xa269d850, + 0xa26df050, + 0xa269f050, + 0xa265f050, + 0xa275f050, + 0x430cb720, + 0x4aabb74d, + 0x6f2db546, + 0x430cb720, + 0x712db586, + 0x430cb720, + 0x702db586, + 0x430cb720, + 0x6fadb586, + 0x430cb720, + 0x61abb58d, + 0x430cb720, + 0x612bb58d, + 0x430cb720, + 0x722db586, + 0x430cb720, + 0x732db586, + 0x430cb720, + 0x65abb58d, + 0x430cb720, + 0x652bb58d, + 0x430cb720, + 0x632bb58d, + 0x430cb720, + 0x74adb586, + 0x430cb720, + 0x742db586, + 0x430cb720, + 0x73adb586, + 0x430cb720, + 0x75adb586, + 0x430cb720, + 0x752db586, + 0x74c29c22, + 0xc0019e98, + 0xb78091a4, + 0x0902430c, + 0x0a00c0e2, + 0xa101d208, + 0x4314b720, + 0x684bb78c, + 0xc0017500, + 0xb7269104, + 0xb74d54cd, + 0xb7876353, + 0xb766464d, + 0xb7466dcd, + 0xb766514d, + 0xc10150d5, + 0x02466523, + 0x1244c101, + 0x1126c101, + 0xc0007104, + 0x9e4c925c, + 0x0a00c0e2, + 0xa002d208, + 0x9160c000, + 0xc00074c0, + 0xf0109104, + 0xb720aa61, + 0xb58d430c, + 0xb7804aab, + 0xc0e2430c, + 0xd2080a00, + 0x9c22a801, + 0x8420a61d, + 0xa1199e57, + 0x9e4a9e9f, + 0xcc120d02, + 0xd0317286, + 0xaa1966be, + 0x7540d001, + 0x020b134e, + 0x4828b334, + 0xc0007580, + 0x9ea3935c, + 0xff94018d, + 0xaa199a22, + 0xc2009e86, + 0x71885a0b, + 0x931ac000, + 0x5990d3a4, + 0x01fec101, + 0xff940d02, + 0xc1019a14, + 0xd024600d, + 0xc0005b13, + 0xb7209160, + 0x0d02430c, + 0x4c2db726, + 0xff941194, + 0x9e869a06, + 0x77400e82, + 0x913ac000, + 0xc0007740, + 0xcfff90e4, + 0xc000757e, + 0x676f9074, + 0xb79f9e70, + 0xb7bf7dee, + 0xb7df7e6e, + 0xb7ff7eee, + 0xc0027f6e, + 0x9c228c20, + 0xb720a61d, + 0x0d02430c, + 0x4c2db766, + 0x45b5b767, + 0x5ab3b7cc, + 0x632bb7ad, + 0x712db7e6, + 0x73b5b7e6, + 0x99dbff94, + 0x430cb720, + 0xb7860301, + 0xb70764ad, + 0x7504462d, + 0x9264c001, + 0x45adb787, + 0xd0207540, + 0xc00012c0, + 0xb7669282, + 0x018b74b5, + 0xff940d02, + 0xb72099c4, + 0x0a02430c, + 0x4c2db726, + 0x6003109a, + 0x1451d110, + 0x7100c812, + 0xb7209e95, + 0xb786430c, + 0x750265ad, + 0x91b4c000, + 0xc00077ba, + 0x9ea99152, + 0x49adb747, + 0x4003ba09, + 0xc001058b, + 0xb76690c0, + 0x058b4c2d, + 0xc0010d02, + 0xb7879120, + 0x754044ad, + 0x11c0d020, + 0x9142c000, + 0x4c2db786, + 0x4aadb747, + 0x6245124a, + 0x15b8c101, + 0x65adb786, + 0xc0007502, + 0xb78d90f2, + 0x75004a2b, + 0x91a2c000, + 0xb7479ea9, + 0xba094a2d, + 0x018f4003, + 0xfff4050f, + 0xc0009b45, + 0xb7669100, + 0x9ea24c2d, + 0xff9411ba, + 0x0a029976, + 0x7100d012, + 0x000c006c, + 0x7008c010, + 0x7e6eb79f, + 0x7eeeb7bf, + 0x7f6eb7df, + 0x7feeb7ff, + 0x8c00c002, + 0xf8399c22, + 0xb740a205, + 0xd0105c8c, + 0x02440624, + 0xaa1ddbc8, + 0x9e5b0107, + 0x70886a0c, + 0x4834b320, + 0x91b4c001, + 0xc00076c0, + 0xb760927c, + 0xc18040d5, + 0x0d025a07, + 0x61a7d132, + 0xc1015d99, + 0xff9405b8, + 0x582b9944, + 0x4002ba00, + 0x90c0c000, + 0x0e22d011, + 0x4002ba04, + 0x430cb740, + 0xc0e40205, + 0xd2080a14, + 0x7008aa01, + 0x443ab340, + 0x913ac000, + 0xc0e40205, + 0xd2080a10, + 0xc012aa01, + 0xf9f87008, + 0x9c22aa1d, + 0xa205f839, + 0x430cb720, + 0x64adb786, + 0xc0007504, + 0xc0e690c4, + 0xc0000890, + 0xc0e69080, + 0xb78008d0, + 0xdffc43ab, + 0xc0007d3e, + 0x9e5090a4, + 0x90a0c000, + 0xfff49e89, + 0xb7209ba8, + 0xb78d430c, + 0x75024dab, + 0x91c4c000, + 0xc0c80203, + 0xd2080a60, + 0x7500aa01, + 0x90c4c000, + 0x0a04d011, + 0x4002ba04, + 0xc0d20203, + 0xd2080a60, + 0x7502aa01, + 0x90c4c000, + 0x0a02d011, + 0x4002ba04, + 0x522bb74d, + 0x0a28d011, + 0xc0007008, + 0xd01190fa, + 0x70081a28, + 0x9096c000, + 0x4002ba04, + 0xc0e40203, + 0xd2080a0c, + 0x097ba902, + 0xc1010183, + 0xba241204, + 0xd0124002, + 0x09107104, + 0x7104d010, + 0x0994c0e4, + 0x0528c101, + 0xa961d008, + 0xba009e50, + 0x70044002, + 0x443ab320, + 0x913ac000, + 0xc0e40203, + 0xd2080a10, + 0xc012aa01, + 0xf9f87008, + 0x9c22aa1d, + 0x76c0a61d, + 0x430cb720, + 0x9264c000, + 0x65abb74d, + 0x61abb78d, + 0xb7a69e8d, + 0xb766702d, + 0xb72d6fad, + 0xd0206133, + 0xc0e60128, + 0xc0000ed0, + 0x9e8d91a0, + 0x65b3b74d, + 0x722db7a6, + 0x732db766, + 0x6533b72d, + 0x0e90c0e6, + 0x7680c008, + 0x92bcc000, + 0x5e1bf122, + 0x90ccc000, + 0x0a02c002, + 0x90e0c000, + 0x7100d002, + 0xba240a04, + 0x9ea34000, + 0x52b3c200, + 0x554f54af, + 0x768051f3, + 0x93a2c002, + 0x468cb780, + 0x0524d010, + 0xc8409e51, + 0xb780ab46, + 0xda10470c, + 0xc100a931, + 0xc0405d87, + 0xd131aba5, + 0x09046667, + 0xda109e53, + 0xc200a131, + 0x05b2531f, + 0xff940d02, + 0xc00c985e, + 0x9e877588, + 0xa935da10, + 0x937cc001, + 0x4002ba12, + 0x12440a6e, + 0x65ebc101, + 0x02899eba, + 0x4002ba2d, + 0xc07e55eb, + 0xcc100d7e, + 0x9eaa72c4, + 0x5a07c300, + 0xc10155e8, + 0x018d05b8, + 0xff940d02, + 0x9e83983e, + 0xaa21f210, + 0xa9b1da10, + 0xc10165bf, + 0xf21005b8, + 0xba1ba1a2, + 0x0d024002, + 0x982fff94, + 0xf210755e, + 0xd04da025, + 0xd01a1a50, + 0xc0005213, + 0x0a4290ba, + 0x5210124a, + 0xa225f210, + 0xda100a06, + 0xb79fa23d, + 0xb7bf7e6e, + 0xb7df7eee, + 0xb7ff7f6e, + 0xc0027fee, + 0x9c228c00, + 0x76c2a605, + 0x91e4c001, + 0x430cb720, + 0x64adb786, + 0xd0227504, + 0xfff415b6, + 0xb7209b53, + 0xb74d430c, + 0xb786632b, + 0x70884c2d, + 0x93a2c000, + 0x712db7a6, + 0x9a0efff4, + 0x430cb780, + 0x018b9e83, + 0xc0c80109, + 0xd0080938, + 0xc0e4a941, + 0x9e920a0c, + 0xa101d208, + 0xfff40916, + 0xb7809ac5, + 0xc0c8430c, + 0xd2080a38, + 0xb780a001, + 0xc0c8430c, + 0xd2080a38, + 0xb79fa801, + 0xb7bf7f6e, + 0x8c407fee, + 0xa6059c22, + 0x4314b720, + 0x05839e5b, + 0x0d88c0e2, + 0xa961d010, + 0x4655b747, + 0x6dcdb786, + 0xd1107480, + 0xc0000529, + 0xb7869102, + 0x02484e4d, + 0xc00110c4, + 0xb7809060, + 0xc101440c, + 0xb5400528, + 0xb7864414, + 0x729950cd, + 0x91b8c000, + 0xd0100a06, + 0xb720a261, + 0xb726430c, + 0x00924e2d, + 0xc0001094, + 0xb78d91a0, + 0xb7464a4b, + 0x0a084e4d, + 0x729360c5, + 0x9216c000, + 0x1094c101, + 0xc0007440, + 0xd071917c, + 0xc2000a1e, + 0xf0085a17, + 0x0a06a265, + 0x90a0c000, + 0xf0080a02, + 0xd808a265, + 0xb7a0a265, + 0x0d02430c, + 0xb78d008b, + 0xb76d65ab, + 0xb74761ab, + 0x01b8492d, + 0x5985d1a4, + 0x05b4c101, + 0x9b7bff74, + 0xb50c008b, + 0xb7206a2b, + 0xb78c430c, + 0xc0c86a2b, + 0xb58008b8, + 0xb7204029, + 0xb787430c, + 0xb587452d, + 0xb72044ad, + 0xb786430c, + 0x750464ad, + 0x9024c001, + 0x65adb786, + 0xc0007502, + 0xb7879284, + 0x09964b2d, + 0xc2000d02, + 0xb5875a0b, + 0xb7a04bad, + 0x008b430c, + 0x7d35b766, + 0x9b4fff74, + 0xb506008b, + 0xb7207d2d, + 0xb786430c, + 0xb58773ad, + 0xc00149ad, + 0xb7869040, + 0xb587742d, + 0xb7204bad, + 0xb786430c, + 0xb58773ad, + 0xb7204a2d, + 0xb787430c, + 0xb58644ad, + 0xb7207d2d, + 0xb78d430c, + 0xb74c52ab, + 0x02446a2b, + 0x52abb58d, + 0x430cb720, + 0x4a2bb78d, + 0xb58d0a04, + 0xb79f4a2b, + 0xb7bf7f6e, + 0x8c407fee, + 0x9e9a9c22, + 0xd1220906, + 0x52445c87, + 0x72d91a04, + 0x933cc000, + 0x1a32d011, + 0x19045250, + 0x9160c000, + 0xc0007917, + 0xd11090c2, + 0xc0001627, + 0xc2009360, + 0xba1a5a07, + 0x70c24002, + 0xffff0904, + 0x08029278, + 0xd0119c22, + 0x75001a12, + 0xc8100d02, + 0x51509c83, + 0x4818b323, + 0x9160c000, + 0xc0007897, + 0x121890e2, + 0xba000009, + 0x9c224002, + 0x9e545907, + 0xba240d04, + 0xffff4002, + 0x08069261, + 0xa6059c22, + 0x430cb7a0, + 0x09020687, + 0xb9609eaa, + 0xc0c64258, + 0x9e690d00, + 0xc0209e93, + 0x0904aa25, + 0xa249c030, + 0x9321ffff, + 0xc0020d82, + 0xfff40982, + 0x74009bae, + 0x0a02010b, + 0x0a42d002, + 0x0964c0c8, + 0xa241d008, + 0x430cb7a0, + 0x008b09ba, + 0x4c35b766, + 0x9b9dfff4, + 0xb50e008b, + 0xb7205bab, + 0x0902430c, + 0x5babb78e, + 0x5fabb58e, + 0x430cb720, + 0x0a06c001, + 0x5cabb54e, + 0x430cb720, + 0x60abb54e, + 0x430cb720, + 0x4d2db587, + 0x430cb720, + 0x4e2db786, + 0x6e2db586, + 0xb7809e69, + 0xb7205729, + 0xb58d430c, + 0xb79f4aab, + 0xb7bf7f6e, + 0x8c407fee, + 0x0a029c22, + 0xa261d810, + 0x430cb720, + 0x632bb78c, + 0xb3407500, + 0xb74648a4, + 0xb7664e2d, + 0xb7464fad, + 0x02246db5, + 0xc1010244, + 0xc20011b4, + 0xb7465a07, + 0xd0126e2d, + 0xb7477104, + 0xb74c46b5, + 0x70c85aab, + 0x1124c101, + 0xc0001904, + 0x0a0690b6, + 0xa261d810, + 0x430cb720, + 0x6035b746, + 0xc0007680, + 0x748691e2, + 0x919ac000, + 0x4e2db786, + 0x02466229, + 0xc0007115, + 0x0a0690b6, + 0xa261d810, + 0x430cb720, + 0x4e2db786, + 0x6dadb746, + 0x70880248, + 0x1248d01c, + 0x4459b423, + 0xa6059c22, + 0x430cb720, + 0xb7870687, + 0x1a04472d, + 0x472db587, + 0xa961d810, + 0x430cb720, + 0xc0007482, + 0x0a029164, + 0x44adb587, + 0x430cb720, + 0x4dabb54d, + 0x9160c000, + 0xb58d0a02, + 0xb7204dab, + 0xb787430c, + 0xb58746ad, + 0xb72047ad, + 0x9e8b430c, + 0x4e35b746, + 0x0d84c0e2, + 0xa9e1d010, + 0xc00074c0, + 0x058590a2, + 0x9280c000, + 0x67adb784, + 0x50adb746, + 0x6629d131, + 0xb3337104, + 0xc0004458, + 0x0a069138, + 0xa261d010, + 0x430cb720, + 0x4e35b766, + 0x430cb720, + 0x0d02091e, + 0x6dadb766, + 0x462db787, + 0xc1010246, + 0xb5861246, + 0xb7206dad, + 0xb786430c, + 0xb76776ad, + 0x1246462d, + 0x76adb586, + 0x430cb720, + 0x4e35b726, + 0x462db787, + 0x5e2fb763, + 0x1419d110, + 0x587fd024, + 0x01b0e000, + 0xd00305b0, + 0xb5630db2, + 0xb7205e2f, + 0xb787430c, + 0xb726462d, + 0xb7634e35, + 0xd1105d2f, + 0xd0241419, + 0xe000587f, + 0x05b001b0, + 0x0db2d003, + 0x5d2fb563, + 0x430cb7a0, + 0xb763008b, + 0xc1b45d2f, + 0xd022984e, + 0x01015e74, + 0x3144590d, + 0x597fd124, + 0xb543008b, + 0xb7205daf, + 0xb786430c, + 0xb74764ad, + 0x75044635, + 0x9344c000, + 0x6035b546, + 0x430cb720, + 0xb7860d02, + 0xb5866dad, + 0xb7a0762d, + 0x008b430c, + 0x5aabb76c, + 0x6db5b766, + 0xff741984, + 0x008b999a, + 0x4c2db507, + 0x9180c000, + 0x6e2db746, + 0x02440224, + 0x0244c101, + 0x5a09c200, + 0x6e2db586, + 0xfff4058b, + 0xb7209afc, + 0x0a02430c, + 0x462db587, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xb7a0a60d, + 0x0687430c, + 0x03070d86, + 0x9964fff4, + 0xb506008b, + 0xb7606f2d, + 0x09864314, + 0x0de0c0d6, + 0x9af3ffd4, + 0x430cb720, + 0x4635b5a7, + 0xaa41da08, + 0xc0007500, + 0x9eb390a4, + 0x998bfff4, + 0x4314b700, + 0xc0d09e44, + 0xd2080a70, + 0x7502aa01, + 0x9044c002, + 0x6454b746, + 0x4e54b726, + 0x5acab74c, + 0x4e54b546, + 0x430cb720, + 0x4a52b76d, + 0xb7661522, + 0xb78c642d, + 0x19045aab, + 0x1126c101, + 0xc1016247, + 0xb5866125, + 0xb72053ad, + 0xb786430c, + 0x024476ad, + 0x76adb586, + 0x430cb7a0, + 0xb76c008b, + 0xb7665a2b, + 0x59884e35, + 0x9bf8c194, + 0xb506008b, + 0xb7a04ead, + 0x008b430c, + 0x4eb5b766, + 0x4cadb766, + 0x9be7c194, + 0xb506008b, + 0xb78054ad, + 0x0902430c, + 0x0a70c0d0, + 0xa101d208, + 0xb79f9eb3, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0x90e0fff6, + 0x6e3bd331, + 0xd2249e5a, + 0xd0115921, + 0x72991a26, + 0x90bcc000, + 0xba129ea2, + 0xd0114003, + 0x0d8a09a6, + 0xc8120124, + 0x70867286, + 0x9096c000, + 0x4002ba13, + 0xd0100a7e, + 0xb7407088, + 0xc0e4430c, + 0xd0080914, + 0xb780a241, + 0xc0e4430c, + 0xd2080a10, + 0x9c22a102, + 0xb720a60d, + 0x76c2430c, + 0x46adb787, + 0xb5870a04, + 0xb72046ad, + 0xb566430c, + 0xc00a64b5, + 0xb7209364, + 0x0d02430c, + 0xb7860a82, + 0xb747762d, + 0xb7674c2d, + 0xb76746b5, + 0x124447ad, + 0x762db586, + 0x4314b720, + 0x1737d110, + 0x4bb5b7a7, + 0x474db767, + 0x76d5b766, + 0x98b9ff74, + 0x430cb720, + 0x6dadb746, + 0x7635b746, + 0x4e2db786, + 0x1124c101, + 0x12445907, + 0x7148d012, + 0x08040008, + 0xb5075807, + 0xb72045ad, + 0xb787430c, + 0xb74645ad, + 0xd0127c2d, + 0xb5877104, + 0xb72045ad, + 0xb747430c, + 0xb78645ad, + 0xc8107b2d, + 0x75827088, + 0x45adb547, + 0x93dcc000, + 0x430cb720, + 0x058b9eaa, + 0x4c2db766, + 0x9887ff74, + 0x430cb720, + 0x02030936, + 0x0a64c0c8, + 0xa902d208, + 0x4c2db726, + 0xc1011d04, + 0x02401264, + 0x7104d010, + 0x0244c101, + 0x62c2d032, + 0x430cb720, + 0x45adb787, + 0x124ac101, + 0x44adb587, + 0x430cb720, + 0x4b2db787, + 0x44adb747, + 0x5a1bc200, + 0xc0007088, + 0xb5879096, + 0xb72044ad, + 0x9eaa430c, + 0x4c2db786, + 0x5a05c200, + 0x0659d110, + 0x4aadb587, + 0x430cb7a0, + 0xb767008b, + 0xb7664ab5, + 0xff744c2d, + 0x008b984a, + 0x4aadb507, + 0x430cb720, + 0x44adb787, + 0x0659d110, + 0x45adb587, + 0x430cb720, + 0x65adb786, + 0xc0007502, + 0xb78d90f2, + 0x75004a2b, + 0x92a2c001, + 0x4d2bb74d, + 0x6a2bb70c, + 0xd0027480, + 0xc0010a02, + 0xb7869264, + 0xcffe7d2d, + 0x7500287c, + 0x919cc000, + 0x44adb767, + 0x9e926209, + 0x5987d1a4, + 0x01c7d120, + 0x9819ff74, + 0x430cb720, + 0x6a33b74c, + 0x1204c101, + 0x7100d014, + 0xc0007504, + 0xb50c90da, + 0xc0006a2b, + 0x729192a0, + 0x0a7acffe, + 0xb324090a, + 0xc101443c, + 0xc0000244, + 0xb74c9120, + 0xb78c6a2b, + 0x02446aab, + 0x5a07c200, + 0x6a2bb58c, + 0x430cb720, + 0x4d2bb78d, + 0xc0007500, + 0xb78c9204, + 0x753e6a2b, + 0x90b4c000, + 0xc0000a7e, + 0x750090c0, + 0x90a4c000, + 0xb58c0a04, + 0xb7206a2b, + 0xb786430c, + 0x750265ad, + 0x90f2c000, + 0x4a2bb78d, + 0xc0007500, + 0xb78d91e2, + 0x75004d2b, + 0x92e4c000, + 0x6a33b76c, + 0x2dfcc00e, + 0x9ac2fff4, + 0x91e0c000, + 0xc0e40103, + 0x0a0a0910, + 0xa241d008, + 0x430cb780, + 0xc0e4097e, + 0xd2080a14, + 0xb720a101, + 0xb78e430c, + 0x7502502b, + 0x91e4c000, + 0xc0c80203, + 0xd2080a60, + 0x7500aa01, + 0x90e4c000, + 0x6a2bb78c, + 0xb58c0a08, + 0xb7206a2b, + 0xb78c430c, + 0xc0c86a2b, + 0x0a0408b8, + 0x4029b580, + 0x430cb740, + 0x02059e92, + 0x0d38c0c8, + 0x0a14c0e4, + 0xaa01d208, + 0xa9c1d010, + 0xc00070c8, + 0x02059152, + 0x0a10c0e4, + 0xaa01d208, + 0xc00070c8, + 0xd0109088, + 0xb740a241, + 0x0205430c, + 0x0a38c0c8, + 0xaa01d208, + 0x090cc0e4, + 0xa241d008, + 0x430cb720, + 0xb5870a02, + 0xb7204cad, + 0xb58e430c, + 0xb7205b2b, + 0xb58e430c, + 0xb7205c2b, + 0xb587430c, + 0xb7204ead, + 0xb58e430c, + 0xb7205f2b, + 0xb58e430c, + 0xb720602b, + 0xb587430c, + 0xb720462d, + 0xb587430c, + 0xb720452d, + 0xb587430c, + 0xb720492d, + 0x0203430c, + 0x0a0cc0e4, + 0xaa01d208, + 0x522bb58d, + 0x430cb720, + 0xc0c80203, + 0xd2080a38, + 0xb58daa01, + 0xb79f4aab, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xa6059c22, + 0x430cb720, + 0xb7860d02, + 0x0a0465ad, + 0x65adb586, + 0x430cb720, + 0x46b5b547, + 0x430cb720, + 0x5aabb78c, + 0x472db587, + 0x430cb720, + 0x5033b54e, + 0x430cb780, + 0x5aabb76c, + 0x0a60c0d2, + 0xa102d208, + 0x430cb720, + 0x65adb786, + 0xc0007502, + 0xb5469184, + 0xb72076b5, + 0xb78c430c, + 0xb58d6aab, + 0xc00052ab, + 0xb7869120, + 0xb74650ad, + 0x12446dad, + 0x76adb586, + 0x430cb720, + 0x76adb746, + 0x4e2db786, + 0xd1240144, + 0xb543597f, + 0xb7205e2f, + 0xb746430c, + 0xb78676ad, + 0x0144512d, + 0x597fd124, + 0x5d2fb543, + 0x430cb720, + 0x5d2fb743, + 0x5dafb543, + 0x430cb720, + 0x76adb786, + 0x53adb746, + 0xb5860244, + 0xb72076ad, + 0xb786430c, + 0xb74653ad, + 0xc20076ad, + 0x70885a07, + 0x93f6c000, + 0xc0d20203, + 0x0d060a60, + 0xa102d208, + 0x430cb720, + 0x53adb786, + 0x76adb746, + 0xc2006a16, + 0x70885a13, + 0x91b6c000, + 0x5033b54e, + 0x430cb720, + 0x53adb786, + 0xc2006a16, + 0xb5865a13, + 0xb7a076ad, + 0x0990430c, + 0x9ea80d02, + 0x76d4b766, + 0x9aafff54, + 0xb507008b, + 0xb7204b2d, + 0xb787430c, + 0x6a164b2d, + 0x45adb587, + 0x430cb720, + 0x76adb786, + 0xc0007500, + 0x0a0290b6, + 0x45adb587, + 0x430cb7a0, + 0x094ec002, + 0x9ea80d02, + 0x4f4cb766, + 0x59ffd1a4, + 0x991dc194, + 0xb767008b, + 0xd02245ad, + 0xc0005e64, + 0x30c2589d, + 0xc00070c2, + 0x9ea890bc, + 0x45ccb527, + 0x430cb720, + 0x45adb787, + 0x7c2db746, + 0x7104d012, + 0x45adb587, + 0x430cb720, + 0x45adb787, + 0x7b2db746, + 0x7104d010, + 0x45adb587, + 0x430cb720, + 0x45adb787, + 0x44adb587, + 0x430cb7a0, + 0xb76d008b, + 0x74c04a2b, + 0x9142c000, + 0x52b3b76d, + 0xff540d02, + 0x9ea89a5a, + 0x52cab50d, + 0x430cb720, + 0x65adb786, + 0xc0007502, + 0x018393b4, + 0x09e0c0c8, + 0xa962d008, + 0xc0007680, + 0xb78d92a2, + 0x753052ab, + 0x9212c000, + 0x53adb786, + 0x76adb746, + 0xc2006a1e, + 0x70885a0f, + 0x90dcc000, + 0x1e22d011, + 0xa261d008, + 0x430cb720, + 0x6a2bb78c, + 0xd0097506, + 0xd3e91a44, + 0xc0002ace, + 0x0a8a9072, + 0x65adb786, + 0xc0007502, + 0x757a9194, + 0xb3540a72, + 0x9ea34426, + 0xfff40289, + 0xc0009913, + 0x010391e0, + 0x0910c0e4, + 0xd0080a0a, + 0xb780a241, + 0x097e430c, + 0x0a14c0e4, + 0xa101d208, + 0x430cb720, + 0x502bb78e, + 0xc0007502, + 0x020391c4, + 0x0a60c0c8, + 0xaa01d208, + 0xd0017500, + 0xd3e10a54, + 0xc000294e, + 0x010b9062, + 0xc0d20203, + 0xd2080a60, + 0x7502aa01, + 0x0a24d001, + 0x294ed3e1, + 0xc0e40203, + 0xd2080a14, + 0x7097a982, + 0x4832b332, + 0x91f2c000, + 0xc0e40203, + 0xd2080a10, + 0x9e92a981, + 0x2a3ed3f1, + 0xb3327088, + 0xd3f14446, + 0xb78c2d2e, + 0xd0206a2b, + 0x76bd1128, + 0x1a44d00c, + 0x90d8c000, + 0xc0007684, + 0x0a0890bc, + 0x294ed3f1, + 0xc0007097, + 0x020391f2, + 0x0a10c0e4, + 0xa981d208, + 0xd3f19e92, + 0x70882a3e, + 0x4446b332, + 0x2daed3f2, + 0xc0e40203, + 0xd2080a0c, + 0xb780a182, + 0x0902430c, + 0x0a38c0c8, + 0xa182d208, + 0x430cb720, + 0x4a2bb54d, + 0x430cb720, + 0x52abb54d, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xa205f839, + 0x430cb720, + 0xb56d76c2, + 0xc0004d2b, + 0xb72091f2, + 0x0982430c, + 0xb72c9e9a, + 0xd0125aab, + 0xfff41992, + 0x0d8a9a52, + 0x90c0c000, + 0xc00076c4, + 0x0d8690a4, + 0x98a2fff4, + 0x4314b760, + 0x0de0c0d6, + 0x9a9dffb4, + 0x430cb700, + 0x6f2cb706, + 0xaa1df9f8, + 0xa60d9c22, + 0x4314b740, + 0x09760687, + 0x0d48c0c8, + 0xaa41d010, + 0x7104d010, + 0xa241d010, + 0x430cb720, + 0xd0110a02, + 0xb5860932, + 0xd01158ad, + 0xb7200e32, + 0x6129430c, + 0x77adb546, + 0x430cb720, + 0x6a2db786, + 0x5d2db586, + 0x430cb720, + 0x5b2bb56c, + 0x430cb720, + 0x65adb786, + 0xc0007500, + 0xb78690e2, + 0xb7466dad, + 0x124450ad, + 0x59adb586, + 0x430cb720, + 0x76adb786, + 0x512db746, + 0xd1240128, + 0xb543597f, + 0xb7205d2f, + 0xb786430c, + 0xc20065ad, + 0x749c5908, + 0x90f2c000, + 0xc0ca0103, + 0xc000094c, + 0x02489260, + 0xc000751c, + 0x01039112, + 0x094cc0ca, + 0xc0000a0e, + 0xc00491a0, + 0x01037498, + 0x094cc0ca, + 0x90b2c000, + 0xc0000a0a, + 0x0a069060, + 0xa241d008, + 0x430cb720, + 0x4f35b766, + 0xc0ca0203, + 0xd2080a4c, + 0xc101a902, + 0xb7661207, + 0xd13159ad, + 0xc1806629, + 0x70885910, + 0x91f6c000, + 0xc0ca0203, + 0xd2080a50, + 0x7500aa01, + 0x90e4c000, + 0x792bb78c, + 0xc0000a04, + 0xd0319280, + 0xc1806526, + 0x71045a0c, + 0x931cc000, + 0xc0ca0203, + 0xd2080a50, + 0x7500aa01, + 0x9204c000, + 0x792bb78c, + 0xb58c1a04, + 0xb780792b, + 0x0906430c, + 0x0a50c0ca, + 0xa101d208, + 0x9100c000, + 0xc0ca0103, + 0x0a020950, + 0xa241d008, + 0x430cb720, + 0x792bb78c, + 0x4002ba24, + 0x752ac004, + 0x90dac000, + 0x0a5ac002, + 0x9100c000, + 0x7502c008, + 0x90dcc000, + 0x0a06c004, + 0x792bb58c, + 0x430cb720, + 0xb78c0902, + 0xb546792b, + 0xb74676ad, + 0xb7204e2d, + 0xba244314, + 0xb7264002, + 0x624577ad, + 0x40cbb74e, + 0x74826243, + 0x0a00c002, + 0x5999c200, + 0x9184c000, + 0x76cdb746, + 0x59cdb786, + 0xd2187480, + 0x12285a07, + 0x93e0c000, + 0x59d5b746, + 0x4f4db786, + 0x5d10d122, + 0x70881209, + 0x90b8c000, + 0xc0007680, + 0xd010921c, + 0xb7460624, + 0xc10176cd, + 0xc2000244, + 0x11285a13, + 0xb5460126, + 0xc00076cd, + 0xb78690e0, + 0x024676cd, + 0x76cdb586, + 0x430cb720, + 0x7735b5a6, + 0x430cb720, + 0x65adb786, + 0xc000751c, + 0x0a0490b2, + 0x65adb586, + 0x430cb720, + 0x65adb786, + 0xc0007502, + 0x02039224, + 0x0a38c0c8, + 0xaa01d208, + 0x6aabb58c, + 0x430cb720, + 0x6aabb78c, + 0x512bb58d, + 0x9380c007, + 0x50abb78d, + 0xc0007500, + 0xb78e9184, + 0x750040ab, + 0x9142c000, + 0x532bb78d, + 0xc0007504, + 0x0a0690b4, + 0x9060c000, + 0xb58d0a02, + 0xb7204f2b, + 0x0b02430c, + 0x662db746, + 0x4a2bb76d, + 0xc0c80203, + 0x11b40a4c, + 0xa902d208, + 0x5fadb726, + 0x5987d1a4, + 0xc1010524, + 0x55eb05b2, + 0x9825ff54, + 0x430cb720, + 0x5fadb5c6, + 0x430cb720, + 0x5db5b746, + 0x65adb786, + 0x1944d011, + 0x6125c101, + 0xc2001a04, + 0x02445a05, + 0xb5860240, + 0xb7a05dad, + 0x020b430c, + 0x0a4cc0c8, + 0xaa01d208, + 0xb766008b, + 0xb7665db5, + 0x9ea265ad, + 0x55eb1984, + 0x9bffff34, + 0xb5069ea9, + 0xb7005dcd, + 0xb74d430c, + 0x748050aa, + 0x9342c000, + 0x51aab78d, + 0xc0007500, + 0xb76d90e2, + 0x74c0532a, + 0x90c4c000, + 0x4aaab70d, + 0x92e0c000, + 0x5985d1a4, + 0xc1019eb2, + 0xff3405b8, + 0x18049be0, + 0x91a0c000, + 0x4a2ab76d, + 0x422ab78e, + 0xd1a49e92, + 0xc1015985, + 0xff3405b8, + 0xb7209bd2, + 0xb766430c, + 0x74c465ad, + 0x93d4c000, + 0x512bb74d, + 0x0a04d011, + 0xc0007088, + 0xb74c913a, + 0xd0116f33, + 0x72991a24, + 0x90b6c000, + 0xc0001804, + 0xd01191a0, + 0x70881a04, + 0x90f8c000, + 0x0a24d011, + 0xc0007299, + 0x0804907c, + 0x0e52d031, + 0x5a13c200, + 0x0a027502, + 0x0a42d00d, + 0x1108d020, + 0xc0007696, + 0xb74c921c, + 0xd0116f2b, + 0x72991a24, + 0x0d22d00e, + 0x90fcc000, + 0x0a24d011, + 0xd00d7299, + 0x74c41d22, + 0x9334c000, + 0x652bb78c, + 0xc000752c, + 0xb76c9292, + 0xb78d6f2b, + 0xd01152ab, + 0xba241938, + 0x71044002, + 0x9136c000, + 0x6a2bb74c, + 0x1a34d011, + 0xd00d7088, + 0x02031d24, + 0x0a48c0c8, + 0xa901d208, + 0xc8109e54, + 0x02037088, + 0x0a44c0c8, + 0xaa01d208, + 0x7104d012, + 0x6a2bb58c, + 0x430cb720, + 0x6a2bb78c, + 0x6aabb58c, + 0x430cb720, + 0x6aabb78c, + 0x512bb58d, + 0x430cb720, + 0x6a2bb78c, + 0x4aabb58d, + 0x430cb720, + 0xb78c0902, + 0xb58d6aab, + 0xb72052ab, + 0xb546430c, + 0xb720662d, + 0xb78c430c, + 0xc0d25b2b, + 0x0a0408e0, + 0x4029b580, + 0x430cb720, + 0x6aabb78c, + 0x59abb58d, + 0x430cb720, + 0x6aabb78c, + 0x592bb58d, + 0x430cb720, + 0x40abb54e, + 0x430cb720, + 0x502bb54d, + 0x430cb720, + 0x402bb54e, + 0x430cb720, + 0x50abb54d, + 0x430cb720, + 0x422bb54e, + 0x430cb720, + 0x4a2bb54d, + 0x430cb720, + 0x51abb54d, + 0x430cb720, + 0x532bb54d, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0xb720a605, + 0xb566430c, + 0xb72064b5, + 0xb786430c, + 0x750264ad, + 0x9004c006, + 0xb5460902, + 0xb7206fad, + 0xb786430c, + 0xc00477ad, + 0xc0037510, + 0xb7669092, + 0xb76659b5, + 0x9e9258ad, + 0xff3405b6, + 0x0a229af0, + 0x7008c010, + 0xcc121a40, + 0xb7207008, + 0xb78d430c, + 0x75004a2b, + 0x92e2c000, + 0x58adb786, + 0x6badb586, + 0x430cb720, + 0x4e35b746, + 0x6badb746, + 0x02246d7e, + 0x0a080244, + 0xc1015d17, + 0xc2000244, + 0xb5865a0b, + 0xb7206bad, + 0xb746430c, + 0xb74658ad, + 0x02246bb5, + 0xc2000244, + 0x9e525a0b, + 0x7104d012, + 0x6badb586, + 0x430cb720, + 0x50abb78d, + 0xc0007500, + 0xb78e91c2, + 0x750040ab, + 0x9124c000, + 0x6badb786, + 0xc2006a26, + 0xb5865a0f, + 0xb7206bad, + 0xb746430c, + 0x62356bad, + 0x5a17c200, + 0xb5461128, + 0xb7206bad, + 0xb786430c, + 0xb7466bad, + 0xd0107b2d, + 0xb5867104, + 0xc0006bad, + 0xb7469280, + 0xb78650ad, + 0x12446dad, + 0x59adb586, + 0x430cb720, + 0x59adb786, + 0x4e2db746, + 0x5a17c200, + 0xb5461128, + 0xb7206bad, + 0xb78c430c, + 0x75005b2b, + 0x9342c001, + 0x78adb786, + 0xc0007500, + 0xb7669302, + 0x74c0792d, + 0x9262c000, + 0x0d029ea3, + 0x9a6fff34, + 0x9e857420, + 0x90b4c000, + 0xc0000ec2, + 0x74029120, + 0x90d2c000, + 0xc0000e8a, + 0x0e969060, + 0x430cb7a0, + 0x008b0d02, + 0x6bb5b766, + 0x5b2bb76c, + 0xd11065d7, + 0xff3405d7, + 0x008b9a54, + 0x6badb506, + 0x430cb7a0, + 0x0d029e6b, + 0xb766008b, + 0xff346bb5, + 0x008b9a48, + 0x6c2db506, + 0x430cb720, + 0xb5860a02, + 0xb72070ad, + 0xb586430c, + 0xb720712d, + 0xb586430c, + 0xb7206fad, + 0xb586430c, + 0xb720712d, + 0xb586430c, + 0xb72070ad, + 0xb586430c, + 0xb79f7e2d, + 0xb7bf7f6e, + 0x8c407fee, + 0xa6059c22, + 0xaa61d810, + 0x430cb720, + 0xb58d0687, + 0xb7204dab, + 0xb786430c, + 0xb74676ad, + 0x12447e2d, + 0x76adb586, + 0x430cb720, + 0xb7469e8b, + 0xc0e24e35, + 0xd0100d84, + 0x74c0a9e1, + 0x9284c000, + 0x67adb784, + 0x50adb746, + 0x6629d131, + 0xb3327104, + 0xc0004458, + 0x0a069138, + 0xa261d010, + 0x430cb720, + 0x4e35b746, + 0x430cb720, + 0x6dadb746, + 0x7e2db786, + 0xc1010244, + 0xb5861244, + 0xb7206dad, + 0xb746430c, + 0xb78659b5, + 0xb7467e2d, + 0xc1014e2d, + 0x12440244, + 0x59adb586, + 0x430cb720, + 0x59adb786, + 0x7500de01, + 0x90d6c000, + 0x0a02cf01, + 0x59adb586, + 0x430cb720, + 0x0d02091e, + 0x7e2db786, + 0x4e35b726, + 0x5d2fb763, + 0x1419d110, + 0x587fd024, + 0x01b0e000, + 0xd00305b0, + 0xb5630db2, + 0xb7a05d2f, + 0x008b430c, + 0x5d2fb763, + 0x9845c174, + 0x5e74d022, + 0x590d0101, + 0xd1243144, + 0x008b597f, + 0x5dafb543, + 0x430cb720, + 0x64adb786, + 0xc0027504, + 0xb7869124, + 0xc00477ad, + 0xc0017510, + 0xb74693b2, + 0x768076b5, + 0x909cc001, + 0x772db786, + 0xc0c80103, + 0xd008094c, + 0xc200a941, + 0xc1015a05, + 0xc2000244, + 0xb5865209, + 0xb7a058ad, + 0x020b430c, + 0xc0c8008b, + 0xb7660a4c, + 0xb76658b5, + 0xd208772d, + 0xff34a902, + 0x008b997e, + 0x58adb506, + 0x9100c000, + 0x4e2db786, + 0x5a0bc200, + 0x58adb586, + 0x430cb720, + 0x4e2db786, + 0x58adb746, + 0x5a0bc200, + 0x7088c812, + 0x58adb546, + 0x9300c001, + 0x4e2db786, + 0x58adb586, + 0x9240c001, + 0xb58d0a02, + 0xb7204eab, + 0xb746430c, + 0x768264b5, + 0x9024c001, + 0x772db786, + 0xb5861a04, + 0xb720772d, + 0xb74c430c, + 0xb7865aab, + 0x7104772d, + 0x9094c000, + 0x7735b546, + 0x430cb720, + 0x4a2bb78d, + 0xb58d0a04, + 0xb7204a2b, + 0xb786430c, + 0xb5867e2d, + 0xc00078ad, + 0x76809120, + 0x90c4c000, + 0x7e2db786, + 0x792db586, + 0x430cb720, + 0x6dadb746, + 0xc0007480, + 0xb7869196, + 0x024476ad, + 0x76adb586, + 0x430cb720, + 0xb5860a02, + 0xb7206dad, + 0xb78c430c, + 0x7500632b, + 0x9184c001, + 0x6dadb746, + 0x4fadb786, + 0xc0017088, + 0x0203905c, + 0x0a48c0c8, + 0x4aabb74d, + 0xaa01d208, + 0xc0007088, + 0x0a069306, + 0xa221da10, + 0x430cb720, + 0x772db786, + 0xc0007500, + 0x02039222, + 0x0a48c0c8, + 0x422bb74e, + 0xaa01d208, + 0xb58e0244, + 0xc000422b, + 0x0a0290a0, + 0xa221da10, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0x430cb720, + 0x64adb786, + 0xc0087502, + 0xb78d9284, + 0x75004a2b, + 0x90e2c000, + 0x4eb3b76d, + 0xc00076c0, + 0xb7869102, + 0xb5866fad, + 0xc0015c2d, + 0xb74691c0, + 0xb7664c35, + 0xd0106fad, + 0xd0110624, + 0xc101693b, + 0x70880244, + 0x9154c000, + 0x5c2db786, + 0x5908c180, + 0x70886a16, + 0x9132c000, + 0x6e27d3f1, + 0x5a21c200, + 0xc00070c8, + 0x0a069214, + 0x402bb58e, + 0x430cb720, + 0x5b2bb78c, + 0x08e0c0d2, + 0xb5800a0c, + 0xc0004029, + 0xb56e9080, + 0xb7204033, + 0xb786430c, + 0xc00477ad, + 0xc0047510, + 0xb78693b2, + 0x750265ad, + 0x9364c000, + 0x4a2bb74d, + 0xc0007480, + 0xb54d92c4, + 0xb7204fab, + 0xb786430c, + 0xb5867e2d, + 0xb7205a2d, + 0xb786430c, + 0xb5866fad, + 0xb720582d, + 0xb54d430c, + 0xc003502b, + 0xb76d93e0, + 0xb746502b, + 0x74c06fb5, + 0x93e2c000, + 0x582db746, + 0x0624d010, + 0x0244c101, + 0xc2000244, + 0xb5865a09, + 0xb720582d, + 0xb786430c, + 0xb5867e2d, + 0xb7205a2d, + 0x0a02430c, + 0x502bb58d, + 0x430cb720, + 0xb58d0a04, + 0xc0024fab, + 0xb7869360, + 0xd0314c2d, + 0x6a266d25, + 0xc0017088, + 0xb78691f4, + 0xb7465a2d, + 0x01487e35, + 0x05240128, + 0xc0017295, + 0x0d0690bc, + 0x5033b54d, + 0x430cb720, + 0x50b3b54d, + 0x430cb720, + 0x51abb56d, + 0x430cb720, + 0x532bb56d, + 0x430cb720, + 0x5aabb78c, + 0x4a2bb74d, + 0xc2006a14, + 0x70885a0f, + 0x90dac000, + 0x40abb56e, + 0x9140c001, + 0x40b3b54e, + 0x90c0c001, + 0xb58d0a02, + 0xb720502b, + 0xb78e430c, + 0x7500402b, + 0x9384c000, + 0x582db746, + 0x6fb5b746, + 0x02440224, + 0x0244c101, + 0x5a09c200, + 0x582db586, + 0x430cb720, + 0x5a2db746, + 0x7e35b746, + 0x02440224, + 0x0244c101, + 0x5a0bc200, + 0x5a2db586, + 0x430cb720, + 0x6fadb786, + 0x5c2db586, + 0x430cb720, + 0x50abb78d, + 0xc0007500, + 0xb78d9382, + 0x7500502b, + 0x91e2c000, + 0x40abb78e, + 0xc0007500, + 0xb78c9144, + 0x01035b2b, + 0x0960c0d2, + 0xc0000a0c, + 0xb78c9100, + 0x01035b2b, + 0x0960c0d2, + 0xd0080a08, + 0xb720a241, + 0xb78d430c, + 0x75004a2b, + 0x48a4b340, + 0x70adb786, + 0x5eadb586, + 0x75049c22, + 0x48a4b340, + 0x7e2db766, + 0x5a4be180, + 0x90ccc000, + 0x0d82c002, + 0x9100c000, + 0x7100d002, + 0x9ea30a04, + 0x4001ba1b, + 0x6aabb78c, + 0xba24097e, + 0xd0104002, + 0x0c867104, + 0xd0129e48, + 0xba247100, + 0x9e584002, + 0x5a18c200, + 0x65b5b746, + 0xba245163, + 0x62454002, + 0xc1010936, + 0x76841126, + 0x518bc200, + 0x9054c006, + 0xb58d0a02, + 0xb7204eab, + 0xb78d430c, + 0x75004f2b, + 0x92c4c005, + 0x592db746, + 0x590cd1a4, + 0x6a2bd031, + 0xc0007299, + 0xd01190f6, + 0x72996a2b, + 0x911ac005, + 0x4eb3b52d, + 0x430cb720, + 0x592db746, + 0x022470c4, + 0x921cc002, + 0xc00070c8, + 0x023690d6, + 0xc0017104, + 0xb78c90ba, + 0x0a18792b, + 0x792bb58c, + 0x430cb720, + 0x77b5b746, + 0x4e2db786, + 0x76adb746, + 0x6245c101, + 0xc2000a20, + 0x01285a11, + 0x76adb546, + 0x430cb780, + 0xc0d2091a, + 0xd2080a60, + 0xb780a101, + 0x190c430c, + 0x0a48c0ca, + 0x9240c003, + 0x792bb78c, + 0xb58c0a08, + 0xb720792b, + 0xb746430c, + 0xb78677b5, + 0xb7464e2d, + 0xc10176ad, + 0x0a406245, + 0x5a15c200, + 0xb5460128, + 0xb78076ad, + 0x090e430c, + 0x0a60c0d2, + 0xa101d208, + 0x430cb780, + 0x0a48c0ca, + 0xa082d208, + 0x9200c002, + 0xc00070c8, + 0x023690d6, + 0xc0017104, + 0xb78c90ba, + 0x1a08792b, + 0x792bb58c, + 0x430cb720, + 0x77b5b746, + 0x4e2db786, + 0x76adb746, + 0x6245c101, + 0xc2000a40, + 0x11285a15, + 0x76adb546, + 0x430cb780, + 0xc0d2091a, + 0xd2080a60, + 0xb780a101, + 0x1920430c, + 0x0a48c0ca, + 0x9080c001, + 0x792bb78c, + 0xb58c1a04, + 0xb720792b, + 0xb746430c, + 0xb78677b5, + 0xb7464e2d, + 0xc10176ad, + 0xc0026245, + 0xc2000a00, + 0x11285a19, + 0x76adb546, + 0x430cb780, + 0xc0d2090e, + 0xd2080a60, + 0xb780a101, + 0x1910430c, + 0x0a48c0ca, + 0xa101d208, + 0x430cb720, + 0x592db566, + 0x430cb720, + 0x4eabb78d, + 0x50abb58d, + 0xa60d9c22, + 0x430cb720, + 0xb7460307, + 0x768464b5, + 0x90c4c000, + 0x6ab3b74c, + 0x9020c00a, + 0xc0077682, + 0xb78d9364, + 0x75004a2b, + 0x9224c000, + 0x4eabb78d, + 0xc0007500, + 0xb78c9184, + 0xb74e6aab, + 0x9ea2422b, + 0xb58e0244, + 0xc009422b, + 0xb78d9140, + 0x75004eab, + 0x90c2c001, + 0x01030203, + 0x0a48c0ca, + 0xaa01d208, + 0x0938c0c8, + 0xa942d008, + 0x4000ba24, + 0xc1010910, + 0xd0080244, + 0xd010a941, + 0x01037104, + 0x0944c0c8, + 0xa941d008, + 0x7104c812, + 0x4dabb78d, + 0x4233b74e, + 0xd0027500, + 0x75000922, + 0x9000c001, + 0x502bb78d, + 0xc0017500, + 0x020392c2, + 0x0a38c0c8, + 0xaa01d208, + 0xc0c80103, + 0xd0080948, + 0x0a04a941, + 0x7104d010, + 0xc0c80103, + 0xd0080944, + 0xc812a941, + 0xb78d7104, + 0xb74e4dab, + 0x75004233, + 0x0a28d002, + 0x4422b324, + 0x2a7ccffe, + 0x0244c101, + 0x422bb58e, + 0x430cb720, + 0xb78d9e92, + 0x022851ab, + 0x51abb58d, + 0x430cb720, + 0x532bb78d, + 0xb58d0a04, + 0xc006532b, + 0xb7869100, + 0xb5865f2d, + 0xb7a05e2d, + 0x9e694314, + 0x6badb746, + 0xc0007480, + 0xb78e9316, + 0x750040ab, + 0x09e2d002, + 0x9104c000, + 0xc0d29e6c, + 0xd2080a60, + 0x01e8aa01, + 0xc0c89e6c, + 0xd2080a48, + 0xcc10aa01, + 0xc00170c8, + 0x9e6990e0, + 0x5e2db766, + 0x4e2db786, + 0x6a35b746, + 0xc20011a6, + 0xcc125a0b, + 0x9e6c70c8, + 0xc0c89e6a, + 0xd2080a4c, + 0xc0d2aa01, + 0xd0080960, + 0x0a14aac1, + 0x554c9ea3, + 0x5987d1a4, + 0xc15405b4, + 0x581998d6, + 0xc010026a, + 0x12ea7008, + 0x700acc12, + 0xb78d9e69, + 0x9e694dab, + 0xb74e7500, + 0xd002422b, + 0x750009b2, + 0x0a38d002, + 0x4422b334, + 0xcffe9e69, + 0x02442a7c, + 0x422bb58e, + 0x430cb720, + 0x50abb78d, + 0xc0007500, + 0xb78d91c2, + 0x023851ab, + 0x51abb58d, + 0x430cb720, + 0x532bb78d, + 0xb58d0a04, + 0xb720532b, + 0x0203430c, + 0x0a48c0c8, + 0xaa01d208, + 0x70c8d010, + 0xc0c80103, + 0xd0080944, + 0xd012a941, + 0x9ea27104, + 0x6f2bb58c, + 0x90a0c002, + 0xc0007680, + 0x000790a2, + 0x93c0c001, + 0x6c2db766, + 0x5aadb746, + 0x4e2db786, + 0xc20011b4, + 0xcc125a0b, + 0x020370c8, + 0x0a4cc0c8, + 0xaa01d208, + 0x6aadb746, + 0x5a05c200, + 0x515051f3, + 0x5987d1a4, + 0xc1015914, + 0xff1405b4, + 0xb72099a8, + 0x5819430c, + 0xb74d0810, + 0xb78d59ab, + 0x0244592b, + 0x5a07c200, + 0x094ed011, + 0x7004c010, + 0xc0120a04, + 0x02037008, + 0x0a48c0c8, + 0xaa01d208, + 0x7008c010, + 0x08c4c0c8, + 0x4029b780, + 0x7008c012, + 0x9e509e82, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0xb720a605, + 0x0687430c, + 0x64adb786, + 0xc00a7502, + 0xb7469144, + 0xb76d712d, + 0xe1004a33, + 0xc0005a4b, + 0xc00290cc, + 0xc0000982, + 0xd0029100, + 0x0a047100, + 0xba1b0189, + 0x9e6c4000, + 0x514fd024, + 0x4002ba24, + 0xd010097e, + 0x19787104, + 0x7104d012, + 0xc0c80103, + 0xba24094c, + 0xd0084002, + 0xc200a941, + 0xba245a18, + 0xc1014002, + 0x09146245, + 0x76c01126, + 0x510bd224, + 0x90e2c000, + 0x4eabb78d, + 0xc0007500, + 0xb78693c2, + 0xc1015fad, + 0xb5860244, + 0xb7205fad, + 0xb546430c, + 0xb7206a35, + 0xb546430c, + 0xb7206ab5, + 0xb786430c, + 0xb58670ad, + 0xb7205ead, + 0xb786430c, + 0xb5865ead, + 0xc0065f2d, + 0xb78d9260, + 0x7500502b, + 0x9124c006, + 0x402bb78e, + 0xc0067500, + 0xb7869084, + 0xc1015fad, + 0xb5860244, + 0xb7205fad, + 0xb78d430c, + 0xb7664fab, + 0x750070ad, + 0x90c2c000, + 0x5eadb566, + 0x9180c000, + 0x5eadb746, + 0x02460236, + 0x02446916, + 0x5a0dc200, + 0x5eadb586, + 0x430cb720, + 0x5eadb786, + 0x5f2db586, + 0x430cb720, + 0x4fabb78d, + 0xc0007500, + 0xb54690c2, + 0xc0016a35, + 0xb78d91e0, + 0xb74650ab, + 0x75006a2d, + 0x9182c001, + 0x532bb78d, + 0x62451a04, + 0x0629d110, + 0x6a2db586, + 0x430cb720, + 0x532bb78d, + 0x6a2db746, + 0x5a05c200, + 0xb5460128, + 0xb7a06a2d, + 0x020b430c, + 0x0a4cc0c8, + 0xaa01d208, + 0xb766008b, + 0x9ea26a35, + 0x532bb76d, + 0xff1455eb, + 0x9ea998c2, + 0x6a4db506, + 0x430cb720, + 0x9100c001, + 0x662db786, + 0x4a2bb76d, + 0xd01111b8, + 0x62451a32, + 0x0629d110, + 0x5907c180, + 0x6a2db586, + 0x430cb720, + 0x6a2db786, + 0xb5860244, + 0xb7a06a2d, + 0x008b430c, + 0x6a35b766, + 0x9b6cc134, + 0xb5069ea9, + 0xb7206a4d, + 0xb786430c, + 0x750265ad, + 0x90c4c000, + 0x6a2db786, + 0x9280c000, + 0x4a33b74d, + 0x5aabb74c, + 0x6a35b766, + 0x0624d010, + 0x71041904, + 0x5d2db766, + 0x9156c000, + 0x0236c101, + 0x5a07c200, + 0x6aadb586, + 0x9140c001, + 0x1224c101, + 0x6526d031, + 0x02446247, + 0x6aadb586, + 0x430cb720, + 0x5aabb78c, + 0x6aadb746, + 0xc2001a04, + 0x01285a07, + 0x6aadb546, + 0x430cb7a0, + 0xb76c008b, + 0xb7665aab, + 0x19846ab5, + 0x9b2ac134, + 0xb5069ea9, + 0xc0006acd, + 0xb78690e0, + 0x0a04662d, + 0x662db586, + 0x430cb720, + 0x6ab5b746, + 0x5dadb746, + 0x6b35b546, + 0x430cb720, + 0x65adb786, + 0xc0007502, + 0x022491d4, + 0xb7460244, + 0xc1016aad, + 0xc2000244, + 0xc8105a0b, + 0xb5467088, + 0xb7206aad, + 0xb78d430c, + 0xb58d592b, + 0xb72059ab, + 0xb5ad430c, + 0xb7205933, + 0xb786430c, + 0x750064ad, + 0x90c4c000, + 0x70adb786, + 0x5aadb586, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0x430cb720, + 0x6435b746, + 0x4e2db746, + 0x5aabb78c, + 0x4e35b546, + 0x4314b720, + 0x4a2bb72d, + 0x1524c101, + 0xb7461a04, + 0x124276cd, + 0x6245c101, + 0xb5460128, + 0xb78076cd, + 0x0902430c, + 0x0a70c0d0, + 0xa101d208, + 0xa6059c22, + 0x430cb720, + 0xb5660287, + 0xd8087e35, + 0x7500aa61, + 0x9144c000, + 0x430cb720, + 0x6f35b766, + 0x9a6efff4, + 0x9b04ffd4, + 0x430cb780, + 0x0a70c0d0, + 0xaa01d208, + 0xc0007502, + 0xfff49084, + 0x9eab9bc3, + 0x7f6eb79f, + 0x7feeb7bf, + 0xffcd8c40, + 0x01079240, + 0xa9e1f010, + 0x70c0d014, + 0x753ec03f, + 0x90dcc000, + 0x4226e000, + 0x48acb340, + 0xf0100226, + 0x9c22a261, + 0xb7c0a60d, + 0x008d430c, + 0x5b33b7ae, + 0x1dd2d012, + 0x9948ff34, + 0x028c5808, + 0xd0129ea9, + 0xb7a61dd4, + 0xff347ecd, + 0x5808993f, + 0xb726008c, + 0x9eb17ead, + 0xc2000252, + 0xb7665a05, + 0x9ea25ad5, + 0x72c4cc12, + 0x4e4db766, + 0x0d025d9c, + 0x9b9ffef4, + 0xc0dc020d, + 0xd2080a50, + 0xc006a901, + 0x11c00a7a, + 0xc0007480, + 0x9e9390a4, + 0x90e0c000, + 0xb786008d, + 0xd0207a2d, + 0x9eb111b8, + 0xb7460a02, + 0x592779cd, + 0x7088c810, + 0xc0dc020d, + 0xd2080a60, + 0xc180aa01, + 0x9eb25a8f, + 0xd01102d8, + 0x9eb36e3f, + 0x0d50c0dc, + 0x02d40e86, + 0x5a23c200, + 0xa2c2d010, + 0x7a4db566, + 0x0db0c0dc, + 0xfff402d8, + 0xc0089b96, + 0xd4100a02, + 0x9e6c7148, + 0x7148d412, + 0x2afcc00e, + 0xb79f000b, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xa60d9c22, + 0x430cb7a0, + 0xb786008b, + 0xb741562d, + 0x75045e2d, + 0x92a4c005, + 0xc0007488, + 0xb78190e4, + 0x75045ead, + 0x9322c005, + 0xb746008b, + 0xb7464fad, + 0xb7616bb5, + 0xd1205e2d, + 0xc02111a5, + 0xc00076c0, + 0x74c29354, + 0x93e4c009, + 0x4e2db786, + 0x70886a1e, + 0x9336c009, + 0x5e15d1a2, + 0x6ac5d072, + 0x9060c001, + 0xc00074c2, + 0xd1a293e2, + 0xd0325e15, + 0xc0006acd, + 0x74c29340, + 0x9184c009, + 0xb786008b, + 0x6a1e4e2d, + 0xc0097088, + 0xd07190b6, + 0xc0006e35, + 0x74c290e0, + 0x9102c000, + 0x6e3dd031, + 0x5a97d224, + 0x9060c000, + 0x008b0e82, + 0x5ab3b7cc, + 0xc0027782, + 0xff3491c4, + 0x008b9a72, + 0x4e35b7a6, + 0xc0109e6b, + 0xc1017006, + 0xd812120b, + 0xb7867008, + 0x750075ad, + 0x92e4c001, + 0x98acff34, + 0xc0007400, + 0x008b91a2, + 0x5babb78e, + 0x67adb744, + 0x0a027088, + 0x0a42d004, + 0x92c0c000, + 0xb781008b, + 0x75025e2d, + 0x90a2c000, + 0xc0007508, + 0x008b9224, + 0x67adb784, + 0x50adb746, + 0x6659d131, + 0x0a027104, + 0x0a42d00b, + 0x75adb586, + 0x90a0c000, + 0xb5c6008b, + 0x008b75b5, + 0x75adb786, + 0xc0007500, + 0xc81290c4, + 0xc000710c, + 0x010d9060, + 0xb786008b, + 0xd0204e2d, + 0x008b12c4, + 0x5e2db781, + 0xc0007502, + 0xff349264, + 0x7400986b, + 0x91c4c000, + 0xb786008b, + 0xb74650ad, + 0xc2006bad, + 0x70885a08, + 0x909cc000, + 0x73b5b7a6, + 0xd4120d06, + 0xc0047344, + 0x748291e0, + 0x92c2c001, + 0xc0047488, + 0x008b9024, + 0x5eadb781, + 0xc0007500, + 0xb7a690c2, + 0xc0034e35, + 0x008b93e0, + 0x65abb78c, + 0x75009ead, + 0xd0010a02, + 0xb5800a42, + 0xb78c448c, + 0xc0dc71ab, + 0x75000ee0, + 0x90c2c000, + 0xa922d210, + 0x90e0c000, + 0x9ab0fff4, + 0xd2109e82, + 0x008ba021, + 0x4e2db786, + 0xd131092a, + 0xc2006629, + 0xcc125a1f, + 0x9e9d7104, + 0x9300c002, + 0xb78c008b, + 0xc2006dab, + 0x00ca5a08, + 0x5cb5b7a6, + 0x9816ff34, + 0x0b00f011, + 0x90e4c001, + 0xb786008b, + 0x750075ad, + 0x9244c000, + 0x67adb784, + 0x4e2db746, + 0x50b5b746, + 0x61450802, + 0xd00b7095, + 0x74000802, + 0x75adb506, + 0x9122c000, + 0x99afff34, + 0xc0121001, + 0xc000700c, + 0x008b9100, + 0x6badb786, + 0xc0121228, + 0xc1017100, + 0x008b06d0, + 0x5e2db781, + 0xc0007502, + 0xff1493e4, + 0x74009be5, + 0x9344c000, + 0xb78d008b, + 0xc00464ab, + 0xc0007510, + 0xb7869274, + 0xc2004e2d, + 0x9ea25a0f, + 0x7344d412, + 0x9140c000, + 0xb78c008b, + 0xc2006dab, + 0x00ca5a08, + 0x5cb5b7a6, + 0x430cb720, + 0x5e2db781, + 0xc0007508, + 0x750290a2, + 0x92e4c000, + 0xb787008b, + 0x75004e2d, + 0x9222c000, + 0xd4109ea2, + 0xc0007344, + 0x72959180, + 0x9238fff6, + 0x9180fff6, + 0xfff77295, + 0xfff69078, + 0x9e6893c0, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0xb7a0a60d, + 0x9e5e430c, + 0xb781008b, + 0x75025e2d, + 0x91a6c004, + 0xc0007504, + 0x750890f4, + 0x90e4c004, + 0x93a0c001, + 0x76c2008b, + 0x5635b566, + 0x6db3b7ac, + 0x9084c000, + 0x9ad5ff34, + 0x9a71fff4, + 0xb506008b, + 0x01815dad, + 0xff34058b, + 0x9e8299a7, + 0x0902008b, + 0x9100c000, + 0x7c2bb78c, + 0x4002ba24, + 0x0528c101, + 0x0888709b, + 0xffff0904, + 0xb74092fc, + 0x9e53430c, + 0xc0c80205, + 0xd2080a48, + 0xd010aa01, + 0xc0c870c8, + 0xd0080944, + 0xc412a941, + 0x9e8d7104, + 0xb5ac008b, + 0xff347c33, + 0xc00298d4, + 0x008b9140, + 0xb56676c2, + 0xb7ac5635, + 0xc0006db3, + 0xff349084, + 0xfff49a9a, + 0x008b9a36, + 0x5dadb506, + 0x058b0181, + 0x996cff34, + 0x008b9e82, + 0xc0000902, + 0xb78c9100, + 0xba247c2b, + 0xc1014002, + 0x709b0528, + 0x09040888, + 0x92fcffff, + 0x430cb740, + 0x02059e53, + 0x0a48c0c8, + 0xaa01d208, + 0x70c8d010, + 0x0944c0c8, + 0xa941d008, + 0x7104c412, + 0x008b9e8d, + 0x7c33b5ac, + 0x9899ff34, + 0xc0007584, + 0xb72091c4, + 0xb781430c, + 0x75045ead, + 0x90e2c000, + 0xc0c80203, + 0xd2080a38, + 0x9e68aa82, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x8440a61d, + 0x4314b7a0, + 0x1236c101, + 0x9e699ea2, + 0xb72c9e6a, + 0xc0de712b, + 0xd0080920, + 0xa095ab42, + 0x55d89e69, + 0x73adb726, + 0xc114a099, + 0x9e6c9bfd, + 0xc0de0301, + 0xd2080a10, + 0x7480a901, + 0x90c2c000, + 0xb7869e69, + 0x11087c2d, + 0xb78d9e69, + 0xb7ac64ab, + 0xc0046e2b, + 0xc0007510, + 0xd3249114, + 0x0b825b93, + 0xc000a391, + 0x9e699180, + 0xb7866916, + 0x590f7bad, + 0x5b87d324, + 0x5b97c200, + 0x9e69a111, + 0xb7467780, + 0x0a22712d, + 0x4824b364, + 0x9ea67482, + 0x9332c003, + 0x632bb78d, + 0xc0037502, + 0xc0019292, + 0xa91593c0, + 0x3154e000, + 0x9324c001, + 0xb7869e69, + 0x750075ad, + 0x9144c002, + 0x9aa8ff14, + 0xc0007400, + 0x9e6991a2, + 0x5babb78e, + 0x67adb744, + 0x0a027088, + 0x0a42d004, + 0x9360c000, + 0xb7819e69, + 0x75025e2d, + 0x90a2c000, + 0xc0007508, + 0x9e699204, + 0x67adb784, + 0x4e2db746, + 0x50b5b746, + 0x71156245, + 0xd00b0a02, + 0xc0000a42, + 0x9e699080, + 0xb5860a06, + 0x9e6975ad, + 0x75adb786, + 0xc0007500, + 0x75409324, + 0x9104c000, + 0xb78d9e69, + 0x7500632b, + 0x9104c000, + 0x0a029e6a, + 0x0910c0de, + 0xa241d008, + 0xaa19a915, + 0x74800802, + 0x4422b340, + 0x90e0c001, + 0x9e699e6c, + 0x0a10c0de, + 0x058b0a86, + 0xa281d208, + 0x7c2db5c6, + 0x0df0c0dc, + 0xfff4018d, + 0xa91298c2, + 0xa9199e71, + 0x0674d010, + 0x52a4c200, + 0x024a024e, + 0xc2006245, + 0xc0005007, + 0x9e699140, + 0x632bb78d, + 0xfffe7500, + 0xfffc9102, + 0xb79f9140, + 0xb7bf7d6e, + 0xb7df7dee, + 0xb7ff7e6e, + 0xc0027eee, + 0x9c228c40, + 0xb7a0a60d, + 0x008b430c, + 0x5e2db781, + 0xc0047502, + 0xff149004, + 0xf0119a27, + 0xc0000880, + 0x9ea99184, + 0x4e55b766, + 0x4fcdb746, + 0x6e3fd011, + 0xc0017088, + 0x008b91d6, + 0x64abb78d, + 0x6cadb7c6, + 0x73b5b7a6, + 0x7510c004, + 0x9332c002, + 0x63b3b76d, + 0x6333b74d, + 0x4e2db786, + 0x592db746, + 0x15b49e5b, + 0xba1b7088, + 0x1d844003, + 0x1248d01a, + 0x1228d019, + 0x61c7d132, + 0x4002ba1b, + 0x5a07c180, + 0x05b8c101, + 0xfef40d02, + 0xc1019834, + 0xc00106d0, + 0x9ea99300, + 0x64cbb78d, + 0x6bcdb7c6, + 0x50d5b7a6, + 0x7510c004, + 0x9012c001, + 0x594db706, + 0x63cbb74d, + 0x6353b74d, + 0x01857017, + 0x1007d119, + 0x11a5d120, + 0x4434b310, + 0x4003ba1b, + 0xba1b1d84, + 0xd1324002, + 0xc1806187, + 0xc1015a07, + 0x0d0205b8, + 0x9809fef4, + 0x06d0c101, + 0xb78c008b, + 0x75006eab, + 0x9122c000, + 0x065ad010, + 0x024ac101, + 0xd224024c, + 0x9eb35a8b, + 0xfff49e6b, + 0x0a029ab1, + 0x7100d012, + 0xb5869ea9, + 0xc0016b4d, + 0x008b90a0, + 0x64abb78d, + 0x63b3b74d, + 0x632bb74d, + 0x7510c004, + 0x6aadb706, + 0x90d4c000, + 0x73adb706, + 0x9220c000, + 0x1625d110, + 0xba1b0189, + 0x74c24002, + 0x913cc000, + 0x5987d1a4, + 0xc1010d02, + 0xfed405b0, + 0x9ea99bce, + 0x6b4db506, + 0x430cb7a0, + 0x008b0902, + 0x632bb78d, + 0x7135b746, + 0x68abb54d, + 0x76820a04, + 0x632bb58d, + 0x5b2bb74c, + 0x9192c005, + 0x2a7ccffe, + 0xc0057502, + 0xb74d90f2, + 0x76806833, + 0x9144c005, + 0xc0017480, + 0xb78693c2, + 0x74826b2d, + 0x5907c200, + 0x5cadb546, + 0x9104c000, + 0x02440224, + 0x5a07c200, + 0x5cadb586, + 0x0e869ea9, + 0x6b55b766, + 0x5ccdb786, + 0x6fcbb76d, + 0x15b8c101, + 0xfed41984, + 0x03019b90, + 0x9200c000, + 0xa9cdd008, + 0x9b89fed4, + 0x0e52d011, + 0x5c88d2a2, + 0x009a9ea5, + 0x5cadb506, + 0x2efccffe, + 0x025ac101, + 0x0109008b, + 0x6fabb78d, + 0x0960c0da, + 0x711b9eb3, + 0xffff0d02, + 0xc00390f2, + 0x9ea99160, + 0xb76d9e92, + 0xb78664cb, + 0x19846acd, + 0x29fccffe, + 0x5985d1a4, + 0x05b8c101, + 0x9b61fed4, + 0xb506008b, + 0xc0025cad, + 0x9ea992e0, + 0x64cbb78d, + 0x7510c004, + 0x9194c000, + 0x4e4db786, + 0xb7460904, + 0x61296c55, + 0x1124c101, + 0x6b4db546, + 0x0902008b, + 0x6b2db786, + 0x7088d812, + 0x5b33b7ac, + 0xc0017740, + 0xb78691c2, + 0x75005b2d, + 0x9262c000, + 0x5badb766, + 0xc00074c0, + 0x9ea391c2, + 0xfed49e92, + 0x0a429b32, + 0x7008c010, + 0xcc121a38, + 0xc0007008, + 0x09969060, + 0x61bcd032, + 0xc1010d02, + 0xfed401ba, + 0x9ea99b22, + 0x11e0d020, + 0xb5069e6b, + 0x0d025ccd, + 0x9b19fed4, + 0xb506008b, + 0xc0005d2d, + 0x9ea991e0, + 0x5ccdb5c6, + 0x9140c000, + 0xb781008b, + 0x75085e2d, + 0x9004fffe, + 0x91a0fffd, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0xb7a0a605, + 0x008b430c, + 0x752db766, + 0xc00074c0, + 0x008b91c2, + 0x74adb786, + 0x5985d1a4, + 0x05b8c101, + 0x99b5c114, + 0xcffe0181, + 0x008b29fc, + 0x5e2db781, + 0x73abb56c, + 0xc0007504, + 0x75089142, + 0x9302c001, + 0xc0047502, + 0xc00193c4, + 0xff149260, + 0x74009895, + 0xb746008b, + 0xc0007335, + 0xb74690c2, + 0xc0006cad, + 0x008b90a0, + 0x6badb746, + 0xb781008b, + 0x75025e2d, + 0x9124c000, + 0xc0007400, + 0xb78690c4, + 0xc00050ad, + 0x0a029060, + 0x9e521228, + 0x7104d010, + 0x1105c101, + 0x7104d012, + 0x5a07c200, + 0x1629d110, + 0xb586008b, + 0x0a0a6aad, + 0x7b2bb58d, + 0x9160c003, + 0x9864ff14, + 0x008b7400, + 0x7335b746, + 0x9222c000, + 0x6cadb786, + 0xd0109e52, + 0xc1017104, + 0xd0121105, + 0xc2007104, + 0xd1105a07, + 0xc0011429, + 0x008b92e0, + 0x5e2db781, + 0xc0007502, + 0x008b90a4, + 0x50adb706, + 0xb786008b, + 0xb746712d, + 0x75006bad, + 0x90e4c000, + 0x50adb786, + 0xc0001040, + 0x008b93e0, + 0x64abb78d, + 0xc0041020, + 0xc0007510, + 0x740092f2, + 0x5807d018, + 0x9258c000, + 0xb786008b, + 0xc2004fad, + 0x70885a07, + 0x1000d01e, + 0x911cc000, + 0xb786008b, + 0x6209542d, + 0x5833c200, + 0x1421d110, + 0xb781008b, + 0x75025e2d, + 0x90a4c000, + 0xb5871a04, + 0x008b4e2d, + 0x5e2db781, + 0xc0007508, + 0xb7819144, + 0x75045ead, + 0x90a2c000, + 0xb5870a02, + 0x0a024e2d, + 0x7100d012, + 0xb586008b, + 0x008b6aad, + 0x712db786, + 0x09b29eab, + 0xb5860a04, + 0xc0ca712d, + 0xfd540de0, + 0x9eab9af6, + 0xc0cc0992, + 0xfd540d90, + 0x9eab9af0, + 0xc0ce0992, + 0xfd540dc8, + 0x9eab9aea, + 0xc0da09a2, + 0xfd540d90, + 0x0a029ae4, + 0xb58d008b, + 0xb58d68ab, + 0xb58d632b, + 0xb79f682b, + 0xb7bf7f6e, + 0x8c407fee, + 0x9000ffed, + 0xd0149e52, + 0xd00270c0, + 0xf0127100, + 0xc0000942, + 0x9e5290a4, + 0x9120c000, + 0xc1010a7a, + 0xd0101244, + 0x11287104, + 0xf01051f0, + 0xf010a165, + 0x9c22a1e1, + 0xaa65f010, + 0xa961f010, + 0xc4129e50, + 0xd1207100, + 0x12181115, + 0x9e545153, + 0x51f39e8a, + 0xfffe11a6, + 0x0d029320, + 0x91c0ffff, + 0xa966f008, + 0xa9e1f008, + 0x9100ffff, + 0xaa65f010, + 0xa8e1f010, + 0xc4129ea0, + 0xd1107204, + 0x15141619, + 0x9e5450b3, + 0xc81451f3, + 0xc8027040, + 0xd0147080, + 0xd00270c0, + 0x09047100, + 0xc8120a04, + 0x19747088, + 0x9e900d02, + 0x7280c812, + 0x5d070d04, + 0x05249e54, + 0x51f350b3, + 0x052201b2, + 0x9100fffd, + 0xfffe0d02, + 0xf01092e0, + 0xf010aa65, + 0x9ea0a8e1, + 0x7204c412, + 0x1619d110, + 0x50b31514, + 0x51f39e54, + 0x7040c814, + 0x7080c802, + 0x70c0d014, + 0x7100d002, + 0x196c0128, + 0x9e900d02, + 0x7280c812, + 0x5d070d04, + 0x05249e54, + 0x51f350b3, + 0x052261b3, + 0x9000fffc, + 0xa966f008, + 0xa9e1f008, + 0x92e0fffe, + 0xfffe0d02, + 0xa60d9280, + 0xaae5f010, + 0x9e5e0685, + 0xf0107155, + 0xd11ca962, + 0x0107165b, + 0x4458b343, + 0x4838b355, + 0x554fd018, + 0xd0120986, + 0x9ea370c4, + 0x70c4cc12, + 0x05b45d87, + 0x983ac114, + 0xd1209eb3, + 0x0181115b, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0xfffa8c60, + 0xf00891e0, + 0xf008a966, + 0xfffea9e1, + 0x0d029280, + 0x9220fffe, + 0xa966f010, + 0xaa61f010, + 0xc8109e51, + 0x11b47046, + 0x518fc200, + 0x1524c101, + 0x9300fff9, + 0xc002a61d, + 0xd1718420, + 0xb7c06e35, + 0x0246430c, + 0x06b6d010, + 0x5a08c200, + 0x00cca199, + 0xb7e702dc, + 0xc12a502d, + 0xda080ac0, + 0xc180aab1, + 0xc1015e88, + 0x9e6906dc, + 0xc00f0707, + 0xe0b22bfe, + 0x09028d80, + 0x4002ba2d, + 0xb7a6018f, + 0xb55f6835, + 0xb5bf7bec, + 0xfff47b6c, + 0xe0b29b95, + 0x09a28d80, + 0x9bc4fff4, + 0x0e62d011, + 0x8d80f092, + 0x5a0cc200, + 0xa21d01e8, + 0x0980c0d2, + 0x058f0a02, + 0xa2e1f210, + 0x7cecb59f, + 0x9b78fff4, + 0x09a2058f, + 0x9baefff4, + 0xb786008d, + 0xf0515dad, + 0x9e6b8c80, + 0x09029eab, + 0xa221f208, + 0xfff4a115, + 0x008d9b6d, + 0x5a2bb78c, + 0x6d2bb76c, + 0x11c69eab, + 0x9b95fff4, + 0x09a29eab, + 0x9b94fff4, + 0x7b6cb79f, + 0xc0007500, + 0xf21090fc, + 0x7500aa61, + 0x923ac000, + 0x0902aa1d, + 0x7becb55f, + 0xb5ff00cc, + 0xb7867b6c, + 0xb72664ad, + 0xb59f642d, + 0xf2107cec, + 0xb79fa0e1, + 0x75007b6c, + 0x913cc000, + 0x8c80f092, + 0xaa21f210, + 0xc0007500, + 0x008d919a, + 0x4eadb746, + 0xb59f0a06, + 0xb55f7dec, + 0xc0007d6c, + 0x008d9340, + 0x5dadb786, + 0x8c80f071, + 0x8d80e051, + 0xf2089eab, + 0x0a02a221, + 0x7decb59f, + 0x9ac6fff4, + 0xe0b19eab, + 0xfff48d80, + 0x9eab9b17, + 0xfff49e6b, + 0xa9199b44, + 0x6e65d171, + 0x04ecd010, + 0x02480244, + 0xc114024c, + 0x009c0a20, + 0xa991da08, + 0x5f2bb72e, + 0xd02a7046, + 0xd02901b6, + 0xd0090192, + 0xd00a0a32, + 0xd1200a12, + 0x098e01c7, + 0x9b4dc0f4, + 0x8d80e0b2, + 0xfff40181, + 0xe0719af7, + 0xe0b28d80, + 0xfff48d80, + 0xe0b29b1c, + 0xfef48d80, + 0x9e839b0a, + 0x9b29fef4, + 0x7beeb79f, + 0x7c6eb7bf, + 0x7ceeb7df, + 0x7d6eb7ff, + 0x8c20c004, + 0xa6059c22, + 0x430cb720, + 0x0d829e5d, + 0x4aabb76d, + 0x998dffb4, + 0xa021f208, + 0x430cb720, + 0x64adb786, + 0xc0007500, + 0xb50d9082, + 0x0a024aab, + 0xa225f208, + 0xa22df208, + 0xa231f208, + 0xa235f208, + 0xa229f208, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0xb720a605, + 0x76c2430c, + 0xb56d9e5c, + 0xc0004d2b, + 0xb7209292, + 0xb7ac430c, + 0xb76c5b2b, + 0xd0115ab3, + 0xc0f409d2, + 0x9e839afd, + 0x1d84018b, + 0x9ab7ff74, + 0xc0000d8a, + 0x76c69160, + 0x15b6d021, + 0x90c2c000, + 0xc0007504, + 0x0d8690a4, + 0x9910ff94, + 0x4314b760, + 0x0de0c0d6, + 0x9bb1fff4, + 0x430cb700, + 0x6f2cb706, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0x8420a60d, + 0x430cb720, + 0xb5850a06, + 0xb72044ab, + 0xb76a430c, + 0xfdd447b1, + 0xb7209b6d, + 0x0982430c, + 0x46b1b76a, + 0x4731b74a, + 0x5e30d1a2, + 0xc0219e5a, + 0xc1120a00, + 0xc0000a10, + 0x9dcd90e0, + 0xa1e1f000, + 0x0a00c100, + 0x09047095, + 0x9334ffff, + 0x5badb7c3, + 0x8d00e031, + 0x9eb209a2, + 0x9a2dfd74, + 0xb720aa19, + 0x09024314, + 0x2ac6d011, + 0x68d1d131, + 0x46d1b7aa, + 0x0092c101, + 0x602db540, + 0x430cb720, + 0x9e889d29, + 0xb76a9e89, + 0xb70a46b1, + 0xc0184729, + 0xc01c0c30, + 0xc0010cb0, + 0xd1a29020, + 0x9dcb5e30, + 0xc0210189, + 0xc1280980, + 0xe0200980, + 0x9dbdaa09, + 0xa261f000, + 0xa929e020, + 0x0a30d111, + 0xf0009dcd, + 0x0d10a161, + 0x7680c008, + 0xffff0990, + 0xd0119204, + 0x9ea30e32, + 0x2dfcc00e, + 0xffff72d1, + 0xaa1d9014, + 0xc0017d02, + 0xd1319342, + 0xa99a69d1, + 0x5d2db780, + 0x5d890196, + 0x0990c010, + 0x9c629ea4, + 0x025ac101, + 0xc2000a04, + 0x01095a30, + 0x0900c021, + 0xc0260d02, + 0x9dad0970, + 0xa162f000, + 0x0a00c021, + 0x0a60c026, + 0xf0009dcd, + 0xb720a162, + 0x098e430c, + 0x46a9b74a, + 0x4731b74a, + 0x5a30c100, + 0x0a00c021, + 0x0a50c114, + 0x90e0c000, + 0xf0009dcd, + 0xc100a1e1, + 0x70950a00, + 0xffff0904, + 0xb7209334, + 0xd132430c, + 0xd05168d1, + 0xd1100a60, + 0x9eab0493, + 0x5fadb580, + 0x430cb780, + 0x0498c101, + 0xc200aa1d, + 0xb5805a05, + 0xfdd45f4d, + 0x75409802, + 0x90c4c000, + 0x430cb720, + 0x44abb5a5, + 0x430cb720, + 0x47b1b76a, + 0x9b3bfdd4, + 0xfd340d8e, + 0x0d8a9b9d, + 0x9b9afd34, + 0x7e6eb79f, + 0x7eeeb7bf, + 0x7f6eb7df, + 0x8c00c002, + 0xa60d9c22, + 0x430cb720, + 0xc0a40203, + 0xd2080a10, + 0xb7caaa82, + 0x77404729, + 0x92a4c002, + 0xb5a50a86, + 0xb72044ab, + 0xb76a430c, + 0xfdd447b1, + 0x9eab9a89, + 0x99e8fe14, + 0x430cb720, + 0x5dadb781, + 0x75061a08, + 0x4832b353, + 0x92d2c000, + 0x5908d226, + 0xe0508520, + 0x94088044, + 0x940c940a, + 0xc1019401, + 0xc0000982, + 0x09869120, + 0x90c0c000, + 0xc000098e, + 0x098a9060, + 0x7eb1b745, + 0x0a02c020, + 0x5930c300, + 0xb3247680, + 0xc1404822, + 0x32460900, + 0x0900c021, + 0xf0009dad, + 0x0203a261, + 0x0a20c09c, + 0x5badb763, + 0xa902d208, + 0x09069eb3, + 0x98acfdb4, + 0x430cb720, + 0x47b1b76a, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0xfdd68c60, + 0xb79f9180, + 0xb7bf7eee, + 0xb7df7f6e, + 0x8c607fee, + 0xa61d9c22, + 0x8420c016, + 0x6f74b57f, + 0xaa61d050, + 0xa9e6d050, + 0x03091a28, + 0xc00e1d88, + 0xc00e2b7c, + 0xfd742dfc, + 0xd031987d, + 0xd0116869, + 0xd2240a62, + 0x02015b30, + 0x0a20c014, + 0x5808d324, + 0x0a00c021, + 0x6aecb59f, + 0x568cb780, + 0x6e6cb51f, + 0x9e400101, + 0x020801ec, + 0x57ecb59f, + 0x4f8cb780, + 0x0930c014, + 0x0900c021, + 0xb59f0208, + 0xb7806b6c, + 0xb55f558c, + 0x9e716a6c, + 0xb59f0268, + 0xb780576c, + 0xd0d1500c, + 0x02686961, + 0x7c6cb59f, + 0x518cb780, + 0x0880c040, + 0x59ecb53f, + 0xb59f0238, + 0xb78071ec, + 0xb51f540c, + 0xb7207b74, + 0x0128430c, + 0x596cb55f, + 0x59f4b71f, + 0xb7800906, + 0x9e91570c, + 0x7becb57f, + 0xc24000e2, + 0xc021a0c6, + 0x0a020c00, + 0xb51f9e72, + 0x9e737974, + 0x4c29b58a, + 0x9e749e70, + 0xc0219e71, + 0x050d0900, + 0x078d058d, + 0x048d040d, + 0x0900c01e, + 0x0d00c021, + 0x0980c021, + 0x0d80c021, + 0x0a00c021, + 0x0f80c021, + 0x0800c021, + 0x0c00c021, + 0x0880c021, + 0x0c80c021, + 0x756cb55f, + 0x0d10c01e, + 0x09a0c01e, + 0x0df0c014, + 0x0a00c010, + 0x0f90c010, + 0x0820c010, + 0x0c30c010, + 0x0880c004, + 0x0ca0c012, + 0x1950c00a, + 0x75f4b55f, + 0x776cb57f, + 0x5a74b57f, + 0x5cecb59f, + 0x5bf4b5ff, + 0x5b6cb51f, + 0x5af4b51f, + 0x61ecb53f, + 0x6174b53f, + 0x60ecb55f, + 0x1d50c00c, + 0x19d0c00c, + 0x1d90c004, + 0xc0080a70, + 0xc0020f90, + 0xc0020830, + 0xc00e0c30, + 0x0cf008f0, + 0xb55f0950, + 0xb57f72f4, + 0xb57f726c, + 0xb59f7174, + 0xb5ff70ec, + 0xc0026074, + 0xb51f0d60, + 0x09f05fec, + 0x7074b51f, + 0x78ecb53f, + 0x5ef4b53f, + 0x5e6cb55f, + 0x0db0c034, + 0x0a50c004, + 0xc0041fd0, + 0xc00c0810, + 0xc0240c20, + 0xc00e08d0, + 0xc0121cd0, + 0xb55f0900, + 0xb57f5df4, + 0xb57f5d6c, + 0xb59f7374, + 0xb5ff73ec, + 0xb51f69f4, + 0xb51f696c, + 0xb53f68f4, + 0xb53f746c, + 0xb55f6874, + 0xc03074ec, + 0xc0320d50, + 0xcfd009c0, + 0xc01e0da1, + 0x1ff00a40, + 0x0850c06a, + 0x0c50c026, + 0x08e1cfe0, + 0x0cf0c014, + 0x0931cfee, + 0x67f4b55f, + 0x676cb57f, + 0x66f4b57f, + 0x666cb59f, + 0x65f4b5ff, + 0x656cb51f, + 0x64f4b51f, + 0x646cb53f, + 0x63f4b53f, + 0x766cb55f, + 0x0d31cfd0, + 0x09c1cfce, + 0xcfe21de0, + 0xcfea0a01, + 0xcf7c0fe1, + 0x0c200871, + 0x08d0c01e, + 0x0cf0c024, + 0x0910c032, + 0x76f4b55f, + 0x77ecb57f, + 0x62f4b57f, + 0x626cb59f, + 0x7a74b5ff, + 0x79ecb51f, + 0x6df4b51f, + 0x6d6cb53f, + 0x6cf4b53f, + 0x6c6cb55f, + 0x9060c059, + 0x09829eb3, + 0x9b91fdf4, + 0x4314b740, + 0xb55f9e50, + 0xb7437af4, + 0x7480572a, + 0x9364c003, + 0x6cacb784, + 0x0e820b82, + 0xd00a7502, + 0x75040bf2, + 0x0ed2d001, + 0xc00075c0, + 0x774090c4, + 0xd0010906, + 0xb73f0922, + 0xc1007aec, + 0x69635a0c, + 0x0118d020, + 0xc0010802, + 0x04050abe, + 0x03a8d020, + 0xcfc09d09, + 0xc0340a80, + 0xb9600c78, + 0xd0114018, + 0xb75f4e52, + 0xc2007aec, + 0xd0105a08, + 0x024004f0, + 0xd0200248, + 0xb78000c4, + 0xb7405a2b, + 0x75c0502b, + 0x4002ba24, + 0x5a28c200, + 0xc03e224a, + 0x9dcb297c, + 0xb76031c4, + 0xb7204052, + 0xc000462b, + 0xb78392a4, + 0xb7436dcb, + 0xb74377cb, + 0xba2463d3, + 0xc2004002, + 0x224a5a28, + 0x9ea15950, + 0x2940c3ff, + 0x2d7cc03e, + 0x351431b4, + 0x2a0ed3f1, + 0xc0007506, + 0xb75f90e2, + 0x9dad756c, + 0xa1e1f000, + 0x4002ba21, + 0x2dfcc03e, + 0xc2009e5b, + 0x224a5a28, + 0xb77f3246, + 0x9dbe75f4, + 0xa261f000, + 0x776cb79f, + 0xf0009dcd, + 0x0804a162, + 0xfffd0c08, + 0x9eb391c1, + 0x9be6fe34, + 0x430cb720, + 0x4cadb742, + 0x7c80c020, + 0x90e2c000, + 0x438cb780, + 0xc0005158, + 0x7c8291e0, + 0x438cb740, + 0x91a2c000, + 0x0a02c010, + 0x5218c200, + 0x22444a7d, + 0x51580906, + 0xc0003244, + 0xc0109100, + 0xc2000a06, + 0x4a7d5218, + 0xb5802228, + 0xc021438c, + 0xb7400a02, + 0xc020438c, + 0x9dcd0a00, + 0xa161f000, + 0x430cb780, + 0x7b6cb71f, + 0x9ea80288, + 0x6fccb789, + 0x753ec01c, + 0x9164c001, + 0xfdf49eb3, + 0x008b9b5e, + 0x6fadb509, + 0x91e0c000, + 0x9becfd94, + 0xb7a09eb3, + 0xfdf4430c, + 0xb75f9b52, + 0x02aa7b6c, + 0xb5099ea8, + 0xb7806fcc, + 0xb75f430c, + 0x0d927b6c, + 0xb72900a8, + 0xc01c6fad, + 0xffff747e, + 0x68b29122, + 0x57f4b75f, + 0xb7290098, + 0xf010712d, + 0xb740a0c1, + 0xb77f430c, + 0x00b47b6c, + 0x6fadb789, + 0x02446a32, + 0x0a20c138, + 0xaa11d208, + 0x753ec01c, + 0x9084c001, + 0xfdf49eb3, + 0xb7209ac7, + 0xb77f4314, + 0xd0107b74, + 0xb74904b2, + 0xd0316fb5, + 0xd0106d29, + 0xc1010624, + 0xc1380122, + 0xd0080920, + 0xc101a951, + 0x02440244, + 0x5a08c200, + 0x00c3d120, + 0x6b6cb79f, + 0x70d5b729, + 0xa082f208, + 0xfe349eb3, + 0x74009ac8, + 0x90c4c000, + 0x097ec00e, + 0x9020c004, + 0x430cb740, + 0xc0a20205, + 0xd2080a38, + 0x7680a902, + 0x90e2c001, + 0x01859e93, + 0x09c0c0a2, + 0x0d9cc0a4, + 0xa962d008, + 0xaa61d010, + 0xc0007115, + 0xb7ff91f2, + 0x0a027c74, + 0xa261d210, + 0xaa61d008, + 0xa961d010, + 0x9ea21244, + 0x2d7cc00e, + 0x430cb740, + 0x0940c0a2, + 0xaa41d008, + 0xd0080a04, + 0xc001a241, + 0x00ac9380, + 0x02030103, + 0x0940c0a2, + 0x0a20c0a2, + 0xa945d008, + 0xaa11d208, + 0xc0017088, + 0xb71f9064, + 0xb5407c6c, + 0xb54a4030, + 0xb7a04c31, + 0x9ea8430c, + 0xc0a4020b, + 0xb76a0a1c, + 0xd2084750, + 0xb76aaa01, + 0xc1014648, + 0x02dc15bc, + 0x65b9c101, + 0x1a32d011, + 0x01c7d120, + 0x0ac0c0a2, + 0x9ac6c0d4, + 0xa025d208, + 0x430cb780, + 0xc0a2024c, + 0xd2080a40, + 0xd011a906, + 0xd2080d22, + 0xb780a105, + 0xc09c430c, + 0xd2080a14, + 0x7500aa01, + 0x9142c000, + 0x7c6cb73f, + 0x4029b780, + 0x0a027500, + 0x0a42d002, + 0x5774b73f, + 0xb5809e52, + 0xb7804049, + 0xc00e430c, + 0xb760297c, + 0x9e93560c, + 0xc23000cc, + 0xb78aa1c6, + 0x75004c29, + 0x9304c000, + 0x4b29b54a, + 0x7c74b7ff, + 0xaa61d210, + 0xc0007500, + 0xb72091e4, + 0x0103430c, + 0x091cc0a4, + 0xb78a009c, + 0xd0084b29, + 0x0244a941, + 0x4b29b58a, + 0x560cb700, + 0xaa45c200, + 0xc0007500, + 0x9eb390a4, + 0x9957fe14, + 0x7c74b71f, + 0x430cb7a0, + 0x4048b780, + 0xc0007500, + 0x9eaf90e2, + 0x0f90c06e, + 0x90a0c000, + 0xc0809eaf, + 0x9eb30f80, + 0x9be2fe94, + 0x7aecb73f, + 0xa9e1f210, + 0x6835b741, + 0x2d4d7cc2, + 0x9202c000, + 0x430cb780, + 0xc0983d10, + 0xd2080a10, + 0x7500aa01, + 0x92c2c000, + 0x3d00c200, + 0x9240c000, + 0x7cc0c010, + 0x91c2c000, + 0x430cb780, + 0xc0983d20, + 0xd2080a14, + 0x7500aa01, + 0x9084c000, + 0x3d00c100, + 0x430cb720, + 0x5dadb781, + 0xc001750a, + 0xb7859084, + 0x75007ea9, + 0x93e2c000, + 0x7f29b785, + 0xc0007500, + 0xb73f9202, + 0xc8007c74, + 0xb7800902, + 0x75004049, + 0xb3420a02, + 0x9e934424, + 0xc0003526, + 0xb71f9180, + 0xb7807c6c, + 0xc2004028, + 0xc8005a3c, + 0x9ea02a00, + 0xb73f3520, + 0x9d9d796c, + 0xa162f000, + 0x560cb740, + 0x7cc2c010, + 0x508cb780, + 0xb55f0164, + 0xd008636c, + 0x0902aac2, + 0x0922d001, + 0xb71f9e92, + 0xc2407aec, + 0xb780a146, + 0x06da430c, + 0x5baab742, + 0x06d8c101, + 0xb74a040b, + 0xb7825352, + 0xb7205b2a, + 0xb7a0412c, + 0x6244510c, + 0x63c5c101, + 0x01839ebb, + 0x6fecb53f, + 0x99d9c0d4, + 0x71f4b73f, + 0x6fecb77f, + 0xb5009ebb, + 0xb75f404b, + 0x02aa7bec, + 0x9a29c0d4, + 0xa021da08, + 0xb73f9e68, + 0xb74b7bec, + 0xb780532a, + 0x0d02520c, + 0xb7409e91, + 0xc840538c, + 0xb780a0a6, + 0xb77f528c, + 0x01145974, + 0xa126c840, + 0x5332b7aa, + 0x530cb780, + 0x09c2c002, + 0xa142d808, + 0xa2a6c840, + 0x9ba5fd14, + 0x4314b720, + 0xc0a49e4c, + 0xd2080a18, + 0x7500aa01, + 0x9202c001, + 0x636cb75f, + 0x7c74b75f, + 0xa8c1d008, + 0xaa41d010, + 0xc1010092, + 0x75000092, + 0x532bb74a, + 0x9104c000, + 0x6c4db784, + 0x01090228, + 0x297ccffe, + 0x6b4db709, + 0x018d9e93, + 0x9c629e84, + 0x430cb720, + 0x5dadb781, + 0x75021a10, + 0x90f2c000, + 0x532bb78c, + 0xc0007500, + 0xb78090e4, + 0x9e83550c, + 0xa1c6c240, + 0x7aecb71f, + 0x5dacb781, + 0xc0007506, + 0xb7209204, + 0x00e2430c, + 0x4829b78c, + 0xc0007500, + 0x097e9102, + 0x550cb780, + 0xc2409e90, + 0xb73fa046, + 0xb7817aec, + 0x1a085dad, + 0xc0037506, + 0xd22691f2, + 0x8520590c, + 0x8044e050, + 0x9100c000, + 0x9240c002, + 0x9260c001, + 0x9280c001, + 0x7af4b73f, + 0x4149b786, + 0xc0007500, + 0xb7409202, + 0xc220560c, + 0x7500aa45, + 0x9124c000, + 0x7c74b75f, + 0xaa41d010, + 0xc0007500, + 0x9eb390c4, + 0xfe14018f, + 0xb77f992a, + 0xb7407c6c, + 0x9d07550c, + 0xa8e2d008, + 0xa945c220, + 0x0a029eb3, + 0xd052018f, + 0xcfe80d78, + 0xb59e08e1, + 0xfd74666a, + 0xc0019aef, + 0x0d829260, + 0x9080c001, + 0x560cb700, + 0xaa45c200, + 0xc0007500, + 0x9ea390a4, + 0x9853fe14, + 0x430cb720, + 0xb76a018f, + 0xfe1446b1, + 0xb71f9900, + 0x9ebb7c74, + 0x0df8d051, + 0x4050b740, + 0x9280c000, + 0x560cb720, + 0xaa45c210, + 0xc0007500, + 0x9ea390e4, + 0x9839fe14, + 0x9140c000, + 0x430cb760, + 0x0d060d82, + 0x09e0c06e, + 0x9b5cfdf4, + 0xfe349eb3, + 0xb7a09925, + 0xb780430c, + 0x9ea9550c, + 0xaa45c240, + 0x70c9b744, + 0x2a7cc002, + 0xc0305920, + 0x32442900, + 0x5a6cb75f, + 0xf0009dad, + 0xb75fa261, + 0x020b7c74, + 0x5dc5b741, + 0xabc1d010, + 0x0a44c09a, + 0xc00075c0, + 0xd20891e2, + 0xc200aa01, + 0x00ca5a08, + 0x6cb5b742, + 0x63adb742, + 0x682db782, + 0x91a0c000, + 0xaa01d208, + 0x5a08c200, + 0xb74200ca, + 0xb7427a35, + 0xb782712d, + 0xb77f75ad, + 0x9dbd5cec, + 0xa161f000, + 0x5bf4b77f, + 0xf0009dbe, + 0xb79fa261, + 0x9dcd5b6c, + 0xa162f000, + 0xb781000b, + 0xb71f482c, + 0x9d8e5af4, + 0xa261f000, + 0xa8a9f208, + 0xb53f75c0, + 0xb7496eec, + 0x0203573a, + 0x5a10c200, + 0xc0029d4b, + 0xcffc0a7c, + 0x59102a01, + 0x0148d020, + 0xcffe097c, + 0xc1012901, + 0x02030528, + 0x6525c101, + 0x2a710a0c, + 0x5890c200, + 0xc0005d05, + 0xb78391a2, + 0x0e825e2c, + 0xe003048b, + 0xb3152a00, + 0x05834442, + 0x92e0c000, + 0xc3019ea9, + 0xb7846115, + 0xd0f241cd, + 0xc00508ae, + 0xcffc7d00, + 0xc0002c81, + 0x068390a2, + 0x9080c000, + 0x0692c101, + 0x0092c101, + 0x020b0585, + 0x0a10c098, + 0xd2089ea8, + 0xb781a801, + 0x09c24b4c, + 0xd0217400, + 0x750209b8, + 0x1124d011, + 0x9102c000, + 0xc1800207, + 0xc0815920, + 0x31443a20, + 0x61ecb77f, + 0xf0009dbd, + 0x9ea8a161, + 0x7ec8b785, + 0x664cb764, + 0x66ccb744, + 0xc0007500, + 0x740092e2, + 0x9282c000, + 0xc00075c0, + 0xb7649102, + 0xb744654c, + 0xc00065cc, + 0x9ea89100, + 0xb7649eba, + 0xb744644c, + 0x858664cc, + 0x9080c000, + 0x05078582, + 0xb7849ea8, + 0xb71f644c, + 0xd1106174, + 0x9d8e0639, + 0xa261f000, + 0xb7849ea8, + 0xb71f64cc, + 0xd11060f4, + 0x9d8e0639, + 0xa261f000, + 0xb7849ea8, + 0xb71f654c, + 0xd11072f4, + 0x9d8e0639, + 0xa261f000, + 0xb7849ea8, + 0xd11065cc, + 0xb77f0639, + 0x9dbe7274, + 0xa261f000, + 0x0627d110, + 0x7174b71f, + 0xf0009d8e, + 0xd110a261, + 0xb75f0625, + 0x9dad70ec, + 0xa261f000, + 0x75c09ea8, + 0x7f48b785, + 0x9102c000, + 0x7d3ec01c, + 0x9104c000, + 0x9140c000, + 0x7d3ec01c, + 0x90c4c000, + 0x0a060d02, + 0x9080c000, + 0x0a020d0a, + 0x74809d6b, + 0x9102c000, + 0xc0007500, + 0x090690a2, + 0x9100c000, + 0x09029d6f, + 0x31c6e000, + 0x0922d001, + 0x4a049e53, + 0x59083238, + 0xb77f3244, + 0x9dbe6074, + 0xa261f000, + 0xc096020b, + 0xd2080a58, + 0xb71fa901, + 0xc1005ff4, + 0x024a5a08, + 0xaa11f288, + 0x0619d110, + 0xf0009d8e, + 0x590ca261, + 0xf0c8012a, + 0x0114a951, + 0x706cb73f, + 0xf0009d9d, + 0xb75fa161, + 0x9dae78f4, + 0xa161f000, + 0xc096020b, + 0xd2080a50, + 0xb77fa901, + 0xd0205eec, + 0xd1220124, + 0x024a5e08, + 0xaa11f2c8, + 0x0659d110, + 0xf0009dbd, + 0x5908a261, + 0xf088012a, + 0xb77fa951, + 0xd1105e74, + 0x9dbe0515, + 0xa161f000, + 0x5d080d04, + 0x052ac101, + 0xa952f0d0, + 0x5decb79f, + 0x9dcd0554, + 0xa162f000, + 0x7b6cb75f, + 0x5d74b75f, + 0xb72100aa, + 0x9dae48ad, + 0xa0e1f000, + 0xb7849ea8, + 0xf21163cc, + 0xc00128c0, + 0x74009004, + 0x93a4c000, + 0x72f4b73f, + 0xaa619d1e, + 0x716cb75f, + 0xf0009dad, + 0xb75fa261, + 0x9d2e7274, + 0xb77faa61, + 0x9dbd70ec, + 0xa261f000, + 0x7074b77f, + 0xaa619d3e, + 0x78ecb71f, + 0xf0009d8d, + 0xb71fa261, + 0xb75f7aec, + 0xb7807bec, + 0xb722510c, + 0x000b5b32, + 0x622cb701, + 0xa9c7c840, + 0x520cb780, + 0xb51f9dc0, + 0x7604566c, + 0x71f4b75f, + 0xa846c840, + 0xa965f210, + 0xaac2d810, + 0x56ecb55f, + 0x9104c001, + 0x5b8cb760, + 0x5c14b740, + 0xb9600902, + 0xe0304018, + 0xb77faa45, + 0x9dbe6af4, + 0xa261f000, + 0xb77f9e50, + 0xe0006a74, + 0x9dbeaa45, + 0xa261f000, + 0xffff0910, + 0xb79f9201, + 0xc0746e6c, + 0xc0140976, + 0xc0210a40, + 0x9dcd0a00, + 0xa161f000, + 0x9060c001, + 0x75069d50, + 0x93e4c000, + 0x0906c001, + 0x0a02c021, + 0x0900c4d0, + 0x0a20c014, + 0xf0009dcd, + 0xc02ca161, + 0x0a100932, + 0xf0009dcd, + 0xc002a161, + 0x0a10097e, + 0xf0009dcd, + 0x0a02a161, + 0x736cb71f, + 0xf0009d8d, + 0xf011a261, + 0x75028e28, + 0x9072c001, + 0x0906c001, + 0x0a02c021, + 0x0900cca0, + 0x0a20c014, + 0xf0009dcd, + 0xc040a161, + 0x0a10091a, + 0xf0009dcd, + 0xc002a161, + 0x0a10097e, + 0xf0009dcd, + 0x000ba161, + 0x60acb781, + 0x736cb75f, + 0x2a1c1a04, + 0xf0009dad, + 0x9e78a261, + 0x73f4b75f, + 0x622cb740, + 0xc1010205, + 0x9dae3a00, + 0xa261f000, + 0xf0009dae, + 0xb780a161, + 0xb77f62ac, + 0x9dbd69ec, + 0xa261f000, + 0x632cb780, + 0x6974b77f, + 0xf0009dbe, + 0xf210a261, + 0xb71faa69, + 0x9d8d68ec, + 0xa261f000, + 0xc098020b, + 0xd2080a0c, + 0x7500aa01, + 0x9222c000, + 0xc0007440, + 0x008b91c2, + 0x62adb781, + 0x746cb75f, + 0x3a00c020, + 0xf0009dad, + 0xc000a261, + 0x000b9140, + 0x62acb781, + 0x746cb73f, + 0xf0009d9d, + 0x000ba261, + 0x632cb781, + 0x686cb73f, + 0xf0009d9d, + 0xb78ca261, + 0xb741532a, + 0x750063ac, + 0x90c2c001, + 0x7c82c001, + 0x90c4c000, + 0x9e92097e, + 0x9220c000, + 0x292ed3f2, + 0x9ea30a7e, + 0x2900cff0, + 0x592176be, + 0x4846b323, + 0xb32474be, + 0x05074426, + 0xc0010109, + 0x9e503d04, + 0x5a20c100, + 0xb73f3208, + 0x9d9d74ec, + 0xa261f000, + 0x90e0c000, + 0x74f4b75f, + 0xf0009dae, + 0x000ba161, + 0x652cb781, + 0x67ecb73f, + 0xf0009d9d, + 0xb781a261, + 0xb75f65ac, + 0x9dad676c, + 0xa261f000, + 0x6602d011, + 0x8db2e012, + 0x6fecb77f, + 0x05b8c101, + 0x7d7cb55f, + 0x7de4b55f, + 0x9a17c0b4, + 0x0451d110, + 0x566cb77f, + 0xcff05820, + 0x30062800, + 0x66f4b77f, + 0xf0009dbe, + 0x000ba061, + 0x662cb781, + 0x6674b71f, + 0xf0009d8e, + 0xd2a2a261, + 0xb73f5e20, + 0xcff056ec, + 0xb73f2a00, + 0x324265f4, + 0xf0009d9e, + 0xb781a261, + 0xb75f672c, + 0x9dad656c, + 0xa261f000, + 0x532ab783, + 0x64f4b75f, + 0xf0009dae, + 0xc021a261, + 0xc0120902, + 0x9dad0950, + 0xa261f000, + 0x7decb77f, + 0x7d7cb75f, + 0xc00074c4, + 0xb7819204, + 0xb77f642c, + 0x9dbe6474, + 0xa261f000, + 0x64acb781, + 0x63ecb71f, + 0xf0009d8d, + 0x020ba261, + 0xc09a75c0, + 0xc0000a44, + 0xd20893c2, + 0xb71faa01, + 0xc2007674, + 0x00ca5a08, + 0x7aadb780, + 0xf0009d8e, + 0xb780a261, + 0xb73f7f2d, + 0x9d9e76f4, + 0xa261f000, + 0x43adb721, + 0x77ecb75f, + 0xf0009dad, + 0xc001a0e1, + 0xd2089380, + 0xb77ea902, + 0xb77f7a68, + 0x5d087674, + 0x293ed3f1, + 0x5a0cc100, + 0x04abd110, + 0x0a7cc002, + 0x7ab5b740, + 0x2a01cffc, + 0x6245c301, + 0x0244c101, + 0xf0009dbe, + 0xd012a261, + 0x9d53692b, + 0x5d0b0d0c, + 0x7f2db760, + 0x5a18c200, + 0x6245c101, + 0x76ecb71f, + 0x9d8d0246, + 0xa261f000, + 0x6125c301, + 0x43adb721, + 0xc0020124, + 0xcffc097c, + 0x01222901, + 0x77f4b71f, + 0xf0009d8e, + 0xf210a161, + 0xb73fa96d, + 0x9d9d62ec, + 0xa161f000, + 0xb75f9ea9, + 0xb7816274, + 0x9dae66cd, + 0xa261f000, + 0x7aecb71f, + 0x7fa8b785, + 0xc0007500, + 0xb78a9164, + 0x75024649, + 0x90c4c000, + 0x3900c080, + 0xa16df210, + 0x0a7ec7fe, + 0x7a74b71f, + 0xf0009d8e, + 0xc401a261, + 0xc5000a02, + 0xb73f0a00, + 0x9d9d79ec, + 0xa261f000, + 0x991afe14, + 0x7becb75f, + 0x528cb780, + 0x7b6cb77f, + 0xb59f0228, + 0xb780786c, + 0x0228520c, + 0x5c6cb59f, + 0x510cb780, + 0x03a8d020, + 0x548cb780, + 0xb59f0238, + 0xb780586c, + 0x03a8538c, + 0x560cb780, + 0xb59f0268, + 0xc0095f6c, + 0x74809380, + 0x08021a04, + 0x0802d001, + 0x70880c02, + 0x0c02d001, + 0xb51f7400, + 0xc0006bec, + 0x008b9142, + 0x5b2bb762, + 0x5333b74c, + 0xc0020906, + 0xb73f9080, + 0x020b5f74, + 0x0a1cc0a4, + 0xa901d208, + 0x4049b780, + 0xc0017104, + 0x00dc93c8, + 0x4929b78c, + 0x08c0c060, + 0xc0007500, + 0xb7809204, + 0x75004029, + 0x9164c000, + 0xb78c000b, + 0x0d02532a, + 0xd0027500, + 0xc0000d22, + 0x0d069060, + 0xda10008b, + 0xb742aa61, + 0x02445b2b, + 0xa261da10, + 0x9160c000, + 0x71f4b73f, + 0xa1e1da10, + 0x404bb780, + 0xb5800a04, + 0xf208404b, + 0xda10a929, + 0x7104aa61, + 0xffff11c4, + 0x000b9228, + 0x5b2ab762, + 0x9eb30902, + 0x9e419e91, + 0xfdf4a01e, + 0xb71f9ada, + 0x9e745874, + 0x0a00c021, + 0x404cb740, + 0x0a00c016, + 0xf0009dcd, + 0x9eb3a161, + 0x98dafe14, + 0x0992058d, + 0x0d02c400, + 0x9a57fd74, + 0x0a02c400, + 0x7a6cb73f, + 0xf0009d9d, + 0xb73fa261, + 0xda087874, + 0x000baa61, + 0x404bb740, + 0xda080a04, + 0x0904a261, + 0x404bb540, + 0x5baab742, + 0x2a7ccffe, + 0xc0007088, + 0x0a0291e4, + 0xa261da08, + 0x7becb73f, + 0x530cb740, + 0xd8080114, + 0x0a04aa41, + 0xa241d808, + 0x7c74b73f, + 0x530cb780, + 0x7becb75f, + 0x4051b760, + 0xaa45c840, + 0xc00076c0, + 0x9ea590a2, + 0x9100c000, + 0x430cb720, + 0x6c2db724, + 0x02c2d020, + 0x6bf4b75f, + 0xc0017680, + 0xda089024, + 0x000ba961, + 0x5baab762, + 0x0d027482, + 0x0d22d001, + 0xd00174c2, + 0xb3321a44, + 0xc0004442, + 0x748290a2, + 0x1a42d00a, + 0xc00076c0, + 0xb72090e4, + 0xb724430c, + 0x02426c2d, + 0x018d9ea3, + 0x9adefe74, + 0x430cb720, + 0xc0a40203, + 0xd2080a18, + 0x7500aa01, + 0x9322c001, + 0x5f74b71f, + 0xc0a40203, + 0xd2080a1c, + 0xb780a901, + 0x71044048, + 0x91e8c002, + 0x7874b73f, + 0x5c74b75f, + 0x404bb740, + 0xaa41d810, + 0xc0027088, + 0xda089088, + 0x7500aa61, + 0x93e4c001, + 0x6b2db709, + 0x018d058b, + 0x9c629e84, + 0x430cb720, + 0x5dadb781, + 0x75021a10, + 0x90f2c000, + 0x532bb78c, + 0xc0017500, + 0xb7809184, + 0x9e80550c, + 0xc2409eb3, + 0xc001a046, + 0x009c9040, + 0x4829b78c, + 0xc0007500, + 0x008b93c2, + 0x5dadb781, + 0x550cb740, + 0xc0007504, + 0xc0029124, + 0x9ea10a44, + 0xa0c6c220, + 0x9180c000, + 0xd2080264, + 0x0d7ea901, + 0x9e930908, + 0x7286c810, + 0xa102d208, + 0xfe149eb3, + 0xfdf49825, + 0xb79f9bbf, + 0xb71f786c, + 0xb7a05c6c, + 0xda08430c, + 0xb780a901, + 0x7088402a, + 0x9366fff5, + 0xc0a2020b, + 0xd2080a38, + 0x7500aa01, + 0x90a2c000, + 0xc0000a06, + 0x025c9200, + 0xc0a20109, + 0xc0a20920, + 0xd2080a40, + 0xd008aa05, + 0x7104a951, + 0xd0010a02, + 0x9eb30a42, + 0xfdf40389, + 0x058d9be3, + 0xc1000992, + 0xfd740d02, + 0xcffe9960, + 0xc1002bfc, + 0xb71f0a02, + 0x9d8e7a74, + 0xa261f000, + 0xb73f0a86, + 0x9d9d6dec, + 0xa2e1f000, + 0x6d74b73f, + 0xf0009d9e, + 0xb77fa2e1, + 0xc00659f4, + 0x0d0209f2, + 0xfcf4010b, + 0xc0389958, + 0x0d0209f2, + 0x058d010b, + 0x9951fcf4, + 0x7c74b75f, + 0x530cb780, + 0x7becb77f, + 0xa941d010, + 0xaa65c840, + 0x1a047480, + 0x9144c000, + 0x430cb720, + 0x2a7ccffe, + 0x6c2db724, + 0x020300c2, + 0x2a7ccffe, + 0x9eaa9ea3, + 0xfe74018d, + 0x75c09a09, + 0x90a4c000, + 0xc000010f, + 0xb7209280, + 0xb785430c, + 0x75007ea9, + 0x90a4c000, + 0xc000010b, + 0xb77f9140, + 0x09027c74, + 0xaa61d010, + 0xd0017500, + 0x9e950922, + 0xcffe75c0, + 0xc0002efc, + 0xb7209124, + 0xb786430c, + 0x75006729, + 0x9042c002, + 0x0a82c008, + 0x7a6cb79f, + 0xf0009dcd, + 0xb7ffa2e1, + 0x9d7e7974, + 0xc002aa61, + 0x9dfe3a40, + 0xa261f000, + 0x0a02c401, + 0x0a00c008, + 0x79ecb71f, + 0xf0009d8d, + 0x9eaaa261, + 0x058d0992, + 0x98ddfd74, + 0xb71f0a02, + 0x9d8e79f4, + 0xa261f000, + 0x7a6cb73f, + 0xf0009d9d, + 0xb73fa2e1, + 0x9d1e6cf4, + 0xb75faa61, + 0x9d2d6c6c, + 0x7680a962, + 0x91c2c000, + 0x6b6cb77f, + 0x5a35c200, + 0xf0082a7c, + 0xd110a961, + 0x12a81525, + 0x9060c000, + 0xb7400a82, + 0x9eb3430c, + 0xc0a20986, + 0xd008093c, + 0x0a04aa41, + 0xa241d008, + 0x430cb740, + 0xc050012c, + 0xd0080960, + 0x0a04aa41, + 0xa241d008, + 0x9a45fdb4, + 0xc00075c0, + 0xb7209322, + 0xd2a4430c, + 0xb784590c, + 0xc10179ad, + 0xb5840244, + 0xb78079ad, + 0xb71f430c, + 0x02087b6c, + 0x0a40c09c, + 0xa901f208, + 0x0524c101, + 0xa102f208, + 0xc0007740, + 0xb7809242, + 0x9ea2430c, + 0x0a3cc0a2, + 0x0d20c0a4, + 0xa901d208, + 0xaa41d010, + 0x0a027088, + 0x4828b354, + 0xb7009ea5, + 0xb7814314, + 0x1a105dcc, + 0xc0007502, + 0xb78c9132, + 0x7500534a, + 0x9082c000, + 0x76ccb7a4, + 0x560cb740, + 0x7bf4b75f, + 0x7c6cb77f, + 0xa8c5c220, + 0xb7209e44, + 0x00925314, + 0x0a50c096, + 0x0090c101, + 0x532bb72a, + 0xa8cac810, + 0xa902d208, + 0xaa61d008, + 0x040cc101, + 0x4c50b70a, + 0xc1017500, + 0x01031492, + 0x9e699eb3, + 0xd0010a02, + 0xcffe0a42, + 0x018b2cfc, + 0xa219a01e, + 0xfdf4a395, + 0x75c098ed, + 0x9042c001, + 0x4314b720, + 0x7b74b7ff, + 0x9eb39d0b, + 0x04f2d010, + 0xb7290992, + 0xcfe86fad, + 0xd0320971, + 0x02126919, + 0xc1380522, + 0xd0100d20, + 0x0242a952, + 0x0244c101, + 0x5a08c200, + 0x00c3d120, + 0x70d5b749, + 0x99f1fd14, + 0x430cb720, + 0x5733b743, + 0xc0067680, + 0xb7839184, + 0x87825ead, + 0x75020902, + 0x87b2e00a, + 0x75049d60, + 0x0922d001, + 0x0a027508, + 0x0a42d001, + 0xd0407400, + 0xc0003128, + 0x9e5490a2, + 0x90e0c000, + 0x0a069dc0, + 0xd0017600, + 0xc0010a42, + 0xc200853e, + 0x0e825a08, + 0x8500cfc0, + 0x58ecb59f, + 0x4018b960, + 0x2e5ed3f1, + 0xd0117506, + 0xc00012da, + 0xb73f90c2, + 0x9d1d756c, + 0xb73faae1, + 0x9d1e75f4, + 0xb75fa9e1, + 0x9d2d776c, + 0xb75fa9e2, + 0xb72058f4, + 0xd0104314, + 0x042a045a, + 0xd0100400, + 0xd1200482, + 0xb7230083, + 0xb78357ab, + 0x9e9a7bcb, + 0xc03e9d48, + 0x12422d7c, + 0x0244c101, + 0x7bcbb583, + 0x4314b720, + 0x59084904, + 0x0482d010, + 0x0083d120, + 0x012ac101, + 0x7dcbb783, + 0x5dabb723, + 0x0124d020, + 0x12429d4b, + 0x59a921b4, + 0xb5830246, + 0xb7207dcb, + 0x010b4314, + 0x297cc03e, + 0x0482d010, + 0x0083d120, + 0x67abb723, + 0x7fcbb783, + 0xc03e0787, + 0x12422ffc, + 0xb5830244, + 0xb7207fcb, + 0x9d4f430c, + 0xc1019d7e, + 0x00820402, + 0xb7849ddf, + 0xb70341ab, + 0x215671d2, + 0x9dfc25be, + 0xc1015929, + 0xc3ff1240, + 0x02442ac0, + 0x5da977c0, + 0xc2800e84, + 0xb58459d1, + 0xc00141ab, + 0xb72091c4, + 0xd0104314, + 0xd12004a2, + 0xb7230083, + 0xb78463ab, + 0x124243cb, + 0x0246c301, + 0x43cbb584, + 0x4314b720, + 0x04a2d010, + 0x0083d120, + 0x6dabb723, + 0x45cbb784, + 0xc1011242, + 0xb5840246, + 0xb72045cb, + 0xd0104314, + 0xd12004a2, + 0xb7230083, + 0xb78477ab, + 0x124247cb, + 0xb5840246, + 0xfffa47cb, + 0xb70093e1, + 0x9e444314, + 0x0a38c0a2, + 0xaa01d208, + 0xc0007500, + 0xd1109204, + 0xb700060d, + 0xc0a2560c, + 0xd2080a20, + 0xc200aa11, + 0x1a04a945, + 0xc0027088, + 0x75c09038, + 0x9202c001, + 0x7b74b73f, + 0x6c50b74d, + 0x4490b760, + 0x0490d010, + 0xb729018d, + 0xd0316fb5, + 0xc00e6d19, + 0xc1012cfc, + 0xc1380120, + 0xd0080920, + 0xfd54a951, + 0xb7409ad8, + 0xb77f430c, + 0x00b47b6c, + 0x6fadb789, + 0x02446a32, + 0x0a20c138, + 0xd208097f, + 0xb720a111, + 0xc00e430c, + 0x00b20a7e, + 0x6fadb589, + 0x9200c000, + 0x7b74b77f, + 0xd010097f, + 0xb78904b0, + 0x6a326fad, + 0x0240c101, + 0x0a20c138, + 0xa111d208, + 0x430cb740, + 0xb7000a7f, + 0x9ea0560c, + 0xc2000205, + 0xc0a2a046, + 0xd2080a38, + 0x7500aa01, + 0x92a4c000, + 0x02059e92, + 0x0a3cc0a2, + 0x0d20c0a4, + 0xa901d208, + 0xaa41d010, + 0xc0007088, + 0x9eb39108, + 0x98c7fdf4, + 0xffa67400, + 0xb73f9364, + 0x0a1a6f6c, + 0x41adb580, + 0x430cb720, + 0xc0a20203, + 0xd2080a38, + 0x7500aa01, + 0x9044c001, + 0x4729b76a, + 0x46a9b74a, + 0x4794b720, + 0x0d069ea3, + 0x9120c000, + 0x5aadb780, + 0x750c0787, + 0x4842b327, + 0xd051050f, + 0x70866a29, + 0x00c2c101, + 0x0a22d011, + 0xc00e0109, + 0xffff297c, + 0x768091f4, + 0x9162c000, + 0x4488b780, + 0x478cb740, + 0x02446a52, + 0xf2480906, + 0x9e72a115, + 0x0a02c002, + 0x0900c021, + 0xf0009dad, + 0xc002a261, + 0x9dad1a00, + 0xa261f000, + 0x430cb740, + 0xc2000a86, + 0xc13652b8, + 0xd0080924, + 0x0a7fa942, + 0x9e50424a, + 0xd0082240, + 0xfdf4a241, + 0xd3f298a5, + 0xfd7429de, + 0xb79f99f8, + 0xb7bf67ee, + 0xb7df686e, + 0xb7ff68ee, + 0xcfe6696e, + 0x9c228461, + 0xfcd4a605, + 0xc0219a45, + 0x09060a02, + 0x0a00c010, + 0xf0009dcd, + 0x0d82a161, + 0x9baafcf4, + 0xfcd40d8a, + 0x09029a41, + 0x0a02c021, + 0x438cb540, + 0x0a00c020, + 0xf0009dcd, + 0xc01ca161, + 0x9d4d0a40, + 0xc021aa61, + 0xc00f0a82, + 0xb5802a7e, + 0xc004450c, + 0x9d5d0ae0, + 0x2afcaae1, + 0xfd349eab, + 0xb7809bbc, + 0xb9604e0c, + 0xd2084038, + 0xc146a281, + 0xffff0a20, + 0xc0219381, + 0xc0060a02, + 0x9d4d0a50, + 0x7d1eaa61, + 0x92c2c000, + 0x0a02c021, + 0x0a40c006, + 0xa9629d4d, + 0xc008090a, + 0x9dcd0a40, + 0xa161f000, + 0xc00076bf, + 0xc00c90e2, + 0x9dcd1a70, + 0xa162f000, + 0x0a02c401, + 0x0902c021, + 0x0a00cff0, + 0x9dad0920, + 0xa261f000, + 0x578cb720, + 0xb7400a02, + 0x9ea34a14, + 0x4588b580, + 0x4688b580, + 0x018928e1, + 0xc03c851a, + 0xc0580ac2, + 0xc01c0802, + 0xc0140c02, + 0xb9600c82, + 0xd3f14058, + 0xf0102d3e, + 0x7480a147, + 0xa141d010, + 0x90e4c000, + 0xf1d09e4b, + 0xc000a0dd, + 0xf1d09240, + 0x7482aa49, + 0x02460a1c, + 0xf1d02a61, + 0xb303a25d, + 0xc0004822, + 0x749290c2, + 0xb303018b, + 0x0d844432, + 0xffff0d50, + 0xc0219001, + 0x0a820a02, + 0x0a10c010, + 0xf0009dcd, + 0x0a10a2e1, + 0xf0009dcd, + 0x090aa2e1, + 0x0a30c104, + 0xf0009dcd, + 0xc123a161, + 0xc5660952, + 0xcefa0960, + 0x9dcd0a31, + 0xa161f000, + 0x99fffcd4, + 0x0926c829, + 0x0900cc3e, + 0xaa41f008, + 0xc0007500, + 0x0a0490a2, + 0xa241f008, + 0x9b06fd94, + 0xfcd49eab, + 0x000b998d, + 0x7f6eb79f, + 0x7feeb7bf, + 0x9c228c40, + 0x4314b720, + 0xd0109e9a, + 0xb78c0494, + 0x75004829, + 0x9342c000, + 0x6f4db746, + 0xc0c89e4c, + 0xd2080a48, + 0x0908aa01, + 0x7104d010, + 0x6f4db586, + 0x430cb720, + 0x550cb780, + 0x6f2db746, + 0xaa65c040, + 0x7104d012, + 0x6f2db586, + 0x430cb700, + 0x6f2cb706, + 0xa6059c22, + 0x430cb7a0, + 0x9e9d0d86, + 0x98e8fed4, + 0xb506008b, + 0xb7206f2d, + 0xd0104314, + 0xb78c049a, + 0x75004829, + 0x9082c001, + 0x6f4db786, + 0xc0c89e4a, + 0xd0080948, + 0x0a08a941, + 0x7104d010, + 0x6f4db586, + 0x430cb720, + 0x550cb740, + 0xb7869e6b, + 0xc0206f2d, + 0xd012a965, + 0xb5867088, + 0xb7206f2d, + 0x0908430c, + 0x6f2db786, + 0x7104d010, + 0x6f2db586, + 0x4314b760, + 0xc0d60986, + 0xfeb40de0, + 0xb7209a4c, + 0xb786430c, + 0xb58d6f2d, + 0xb7004aab, + 0xb706430c, + 0xb79f6f2c, + 0xb7bf7f6e, + 0x8c407fee, + 0xa61d9c22, + 0x8420c002, + 0x430cb7e0, + 0xaa41d810, + 0xa945d810, + 0x9eb9008f, + 0xb7419e55, + 0xb57f5e35, + 0xb7267c6c, + 0x024469d5, + 0x00897684, + 0x7d74b53f, + 0xcffe9e5e, + 0xf20828fc, + 0xc000a932, + 0x9eb99244, + 0x5a4bb78d, + 0x6dcdb746, + 0x0629d110, + 0x5a4bb58d, + 0xaa35f208, + 0xb5460128, + 0xc0006dcd, + 0x9eb99120, + 0x5a53b54d, + 0xaa35f208, + 0x6dcdb586, + 0xf2489eb9, + 0xb786a9a1, + 0xb7466fcd, + 0xb746704d, + 0x02466ed5, + 0x6fcdb586, + 0xa9a5f248, + 0x6f4db786, + 0x5ad5b766, + 0xb5460126, + 0xda08704d, + 0x0242a925, + 0x6f4db586, + 0x0524c101, + 0x6ed5b546, + 0xa931f208, + 0xaa35f208, + 0x5905d0a4, + 0x02440183, + 0x05b8c101, + 0x5ad5b566, + 0xaa39da08, + 0xa9beda08, + 0x05b8c101, + 0xc07405b4, + 0x008f9b19, + 0x69adb786, + 0x6d2bb74c, + 0x09040240, + 0x69adb586, + 0x6d2bb54c, + 0x448cb780, + 0x4314b720, + 0xc0007500, + 0x010990a4, + 0x9180c000, + 0x0902008b, + 0x4030b960, + 0x54a9b780, + 0x01280884, + 0x9381ffff, + 0x614bb54d, + 0xf2089eb9, + 0xb786a935, + 0xf2085dcd, + 0xb766a932, + 0x1244564d, + 0x1244c101, + 0x5dcdb586, + 0x69d5b726, + 0xb53f74c4, + 0xc0167cf4, + 0x008f91e2, + 0x71abb78c, + 0xc0167500, + 0xb7a09124, + 0xf2084314, + 0xf208a925, + 0x048baa29, + 0x4ccdb766, + 0x6dd3b7cc, + 0xd1a40244, + 0x9eb75985, + 0x01c7d120, + 0x2ffccffe, + 0x9ac3c074, + 0x6e65d171, + 0x5c88d322, + 0x024ec101, + 0x5a08c200, + 0x024ac101, + 0xc101a01d, + 0xc0e6009a, + 0xd2480a60, + 0xb746ab0d, + 0x048b682d, + 0x5a4bb76d, + 0x5c53b74d, + 0x6d53b72c, + 0xba12112c, + 0xa0924003, + 0x682db546, + 0x9e69a892, + 0x066cd010, + 0xa11a1514, + 0xb72d9e9b, + 0xc101612b, + 0xba1b024a, + 0xc12a4003, + 0x76c00a40, + 0xaa99da08, + 0x7decb53f, + 0x917cc001, + 0xb786048b, + 0x75006dcd, + 0x90bcc001, + 0x05b9d110, + 0xc0745da0, + 0x75409a87, + 0x90a4c000, + 0xc0000101, + 0x025a90e0, + 0x0240024a, + 0x590bc200, + 0x066cd010, + 0xc1017580, + 0xc12a024a, + 0xda080a40, + 0xc000a119, + 0x028190a4, + 0x90e0c000, + 0x02400200, + 0xc200024c, + 0xc00e5a8b, + 0xc810097e, + 0xd1717144, + 0x9e696e65, + 0x01cec101, + 0x5a08c180, + 0x024ac101, + 0x0a60c0e6, + 0xa10dd248, + 0x7df4b73f, + 0x5433b74d, + 0x76400a02, + 0x0a42d002, + 0x692db746, + 0x0244c101, + 0x012aa91e, + 0x692db546, + 0xb58d7680, + 0xc000542b, + 0xb7869122, + 0x9e494cad, + 0x70481a04, + 0x9326c000, + 0x04ecd010, + 0xb74d048b, + 0xc1015c4b, + 0xb72e009a, + 0x02365f2b, + 0x024ac101, + 0xc1140904, + 0x0d060a20, + 0xda08a116, + 0xb54da091, + 0xc0045c4b, + 0x9e6990e0, + 0x6dadb746, + 0xc0007480, + 0x0a169204, + 0x7b6cb59f, + 0x7becb55f, + 0x5c2bb78d, + 0xa0960c86, + 0xb58d0a04, + 0xc0005c2b, + 0x0a029100, + 0x7b6cb55f, + 0x7becb59f, + 0xb79fa215, + 0xb75f7d6c, + 0x11a87cec, + 0x4002ba1b, + 0x0a4ec002, + 0x70c8cc10, + 0x1a4cc002, + 0x70c8cc12, + 0x5d0cb780, + 0x4002ba1b, + 0xc84001b6, + 0xe0b2a9e5, + 0xba1b8d80, + 0xff744002, + 0xa99d9999, + 0x8d80e0b2, + 0x99c5ff74, + 0x8d80e0b2, + 0x99adfe74, + 0xc0120a16, + 0xd1717008, + 0xcffe6e65, + 0xc101287c, + 0x0248024e, + 0x024ac101, + 0xc1140289, + 0xda080aa0, + 0x7580ab31, + 0x9142c000, + 0x01ecd020, + 0x0d88098e, + 0x05b0c101, + 0x99cac074, + 0x6de5d172, + 0x5c88d322, + 0x05b605be, + 0xda0805ba, + 0xc101a031, + 0xc114009a, + 0xd8100da0, + 0xb787a9f2, + 0x048b4ead, + 0x5a4bb76c, + 0x05b8c101, + 0x15bcc101, + 0xcc129e9a, + 0xd01072c4, + 0xc18006ec, + 0xb5675a05, + 0xc1014eb5, + 0xc10102da, + 0xc07405b8, + 0x008b999e, + 0x5f2bb50e, + 0x6e65d171, + 0x5d8cc300, + 0x024ec101, + 0x5a08c200, + 0x00cac101, + 0x502db7a7, + 0xc0d205d6, + 0xc00f0da0, + 0x018b2afe, + 0x98d7ff74, + 0x7640a896, + 0x93e4c000, + 0x6e69d091, + 0x4038b960, + 0x0659d110, + 0xc12c9ea2, + 0xf0100d70, + 0xf090aa41, + 0xf010a949, + 0xf090a245, + 0x1d10a14d, + 0x92e1ffff, + 0x6ce9d091, + 0x009ac101, + 0x612db5a9, + 0xb549a91d, + 0xd171582d, + 0xa91d6e65, + 0x024ec101, + 0x5a08c200, + 0x00cac101, + 0x502db787, + 0x297ec00f, + 0xcff1a99d, + 0x32442a00, + 0xb587058b, + 0xc0d2502d, + 0xff740de0, + 0xa91a98cf, + 0xc0027680, + 0x9e69917c, + 0x682bb74d, + 0x5a2bb78c, + 0xa9116244, + 0x75080244, + 0x901cc002, + 0x0a040205, + 0x75062a0c, + 0x9344c001, + 0x9e510a18, + 0x7102d010, + 0x6d69d091, + 0x4002ba24, + 0x0555d110, + 0x75020982, + 0xd0109e92, + 0x9e9b19c2, + 0x0d30c130, + 0x4418b333, + 0x9140c000, + 0xaa41f010, + 0xa959f150, + 0x01b80d10, + 0x05b4c101, + 0x9301ffff, + 0xd01074c0, + 0xc00004ec, + 0xc1019184, + 0xc12a021a, + 0xc0100a40, + 0xda080902, + 0xc000a111, + 0xc1019160, + 0x5da0029a, + 0x0ac0c12a, + 0x9903c074, + 0xa031da08, + 0xb7869eb9, + 0x7502564d, + 0x9184c007, + 0x430cb7a0, + 0xb7cc0a10, + 0x008b6d4b, + 0x652bb74c, + 0x6f2db726, + 0xd00374ae, + 0x61441a44, + 0xd0137044, + 0xc0001124, + 0x9ea99146, + 0x4ccdb786, + 0x70480228, + 0xd0030902, + 0x9ea90922, + 0x674bb78c, + 0x6dd3b7ac, + 0xc0067500, + 0x74809124, + 0x9222c003, + 0x6f53b76c, + 0xc00276c0, + 0xb7669324, + 0xb746704d, + 0xb7866fd5, + 0xd110644d, + 0x02480527, + 0xc0027104, + 0xd05191a8, + 0x70c86e29, + 0x9114c002, + 0x6ecdb786, + 0x6917d051, + 0x71046a52, + 0x9006c002, + 0x7c4bb78c, + 0xc0c8010b, + 0xd0080938, + 0x0a08a941, + 0x19149ea2, + 0x2d7ccffe, + 0x72950a06, + 0x6ecbb58c, + 0x704bb58c, + 0x90f6c000, + 0x0e28d011, + 0xcffe9ea2, + 0xb79f2d7c, + 0x9e52786a, + 0x2a7ccffe, + 0x00ca0248, + 0x7c2bb72d, + 0x0992d011, + 0x7086cc12, + 0x430cb740, + 0xc0c80205, + 0xd2080a48, + 0xcc10aa01, + 0xc0c870c8, + 0xd0080944, + 0xcc12aa41, + 0xba1b70c8, + 0xfe744002, + 0x008b98c7, + 0x472bb50d, + 0x7c2bb50c, + 0xb7469ea9, + 0xb7866455, + 0xb7666fcd, + 0xd010704d, + 0x02460524, + 0x0124c101, + 0xc2000244, + 0xb5865a09, + 0xc002644d, + 0x008b92e0, + 0x702bb78c, + 0xc0027500, + 0xd2a29222, + 0x9ea95c88, + 0xb766009a, + 0xb76c5cb5, + 0xc0745a4b, + 0x008b985b, + 0xb766600d, + 0xc0005aad, + 0xb74d5a08, + 0x70c84733, + 0x91fcc000, + 0x04dad010, + 0xb72c009a, + 0x08a0742b, + 0xc0017293, + 0xd0119036, + 0xc0000e22, + 0x02009340, + 0xc00070c8, + 0x9ea99336, + 0x6bcdb786, + 0x514db746, + 0x71040238, + 0x9216c000, + 0x04dad010, + 0xb72c009a, + 0x1890742b, + 0xc0007293, + 0xd01190fc, + 0x9ea21e22, + 0x2d7ccffe, + 0xc0c8020b, + 0xd2080a48, + 0x9e52aa01, + 0x7088d010, + 0xc0c8010b, + 0xd0080944, + 0xd012a941, + 0x008b7104, + 0x472bb58d, + 0x7beeb79f, + 0x7c6eb7bf, + 0x7ceeb7df, + 0x7d6eb7ff, + 0x8c20c004, + 0xf0109c22, + 0x9e5baa61, + 0x430cb720, + 0xc0007500, + 0xb5869082, + 0xd00876ad, + 0xc01ca975, + 0xc00074be, + 0x020390e2, + 0x0a64c0da, + 0xa101d208, + 0xa979d008, + 0x74bec01c, + 0x90e2c000, + 0xc0da0203, + 0xd2080a68, + 0x0103a101, + 0xc0da9e8a, + 0x0a060940, + 0xa241d008, + 0x0d50c0dc, + 0xd0101a04, + 0xd008a241, + 0x0203a971, + 0x0a60c0da, + 0xa101d208, + 0xb7209c22, + 0xd1104314, + 0xb70c0497, + 0xb78c7c4b, + 0x75004829, + 0x48a2b340, + 0xc0c89e4c, + 0xd2080a48, + 0x9c22a801, + 0xb7a0a60d, + 0x9e5d4314, + 0x058b0307, + 0x0da0c0d8, + 0x09b2c00e, + 0x99affcb4, + 0x4314b760, + 0x0992c00a, + 0x0dc0c0ca, + 0x99a7fcb4, + 0x4314b760, + 0x0982c002, + 0x0da0c0d6, + 0x999ffcb4, + 0x4314b760, + 0xc0d409d2, + 0xfcb40dd0, + 0xb7609998, + 0xc04c4314, + 0xc0e609f2, + 0xfcb40dd0, + 0x058b9990, + 0xc0c6018b, + 0xc0040d80, + 0xfcb40d32, + 0x9e6899b3, + 0xb7869e69, + 0x097f4fac, + 0xc001040b, + 0x9e6c7520, + 0x0c38c0c8, + 0x0a64c0da, + 0x08a0c0de, + 0xa101d208, + 0xb5800a22, + 0xb7604029, + 0x9e6b4050, + 0x1a48d00d, + 0x050b0c82, + 0x443ab421, + 0xc0da9e69, + 0xc0da09e8, + 0x9e4d0d60, + 0xa161d008, + 0x018d9d0a, + 0x7332b56c, + 0xa0c2d010, + 0x76b4b526, + 0xc12a040b, + 0xc01008e0, + 0xd0080b02, + 0x9e44a961, + 0xc0da9e68, + 0xd2080a6c, + 0xd008a101, + 0xb74daa61, + 0xd0186fb2, + 0x7500a9c2, + 0xd0020a02, + 0xc1010a42, + 0xb58d0244, + 0xdffc6faa, + 0xd0127e7e, + 0xb7400d38, + 0xd022430c, + 0x020505a2, + 0x0a48c0c8, + 0xaa01d208, + 0xd0109e58, + 0xc0c87008, + 0xd0080944, + 0xd012a941, + 0x0c847104, + 0xb59a7644, + 0xb5c0462b, + 0xb5a0402b, + 0x0984412b, + 0x08880c04, + 0x9124fffe, + 0x430cb720, + 0x652bb78c, + 0xc000752a, + 0x9e699112, + 0xb58d0a16, + 0xc0007b2b, + 0x9e6890a0, + 0x7b32b52d, + 0x0d029e69, + 0x50b5b766, + 0x4e2db766, + 0x9883fe94, + 0x287ccffe, + 0xc0100a7a, + 0x1a747008, + 0x7008c012, + 0xb50e9e69, + 0xfe745bab, + 0x9e6899e4, + 0xb78c9e6a, + 0xc0dc65aa, + 0x75000960, + 0xd0010a02, + 0xb5800a42, + 0xc006448c, + 0xd0080a02, + 0xb720a241, + 0xb781430c, + 0x75045ead, + 0x93c4c000, + 0x5f35b761, + 0x4e2cb7a6, + 0x0992c006, + 0xd1320d02, + 0xb5a761d7, + 0xc0024e2c, + 0xfe340dc8, + 0x9e699810, + 0xb746000a, + 0xb5074e2d, + 0x02244e2d, + 0x7100d010, + 0x7088c812, + 0x4e2db547, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0xfe5d8c60, + 0xa60d9020, + 0x430cb7a0, + 0x07079e9d, + 0x030b008b, + 0x712bb78c, + 0x0b40c0da, + 0xa941d208, + 0x31c6e000, + 0xd0020a02, + 0x74800a42, + 0x6db3b54c, + 0x712bb58c, + 0x71b3b5ac, + 0x90e2c000, + 0x99e9fe74, + 0xd2080a02, + 0x7786a241, + 0x92d2c001, + 0x5d08d326, + 0xe0508520, + 0x94298044, + 0x94059428, + 0x0d829401, + 0x90e0c001, + 0xb78c008b, + 0xb5ac5b2b, + 0x75007133, + 0x9162c000, + 0x430cb720, + 0x67adb784, + 0xc0007502, + 0xc0009204, + 0x9ea99100, + 0x684bb78d, + 0xc0007500, + 0x008b9104, + 0x632bb78d, + 0xb58d1a04, + 0xff34632b, + 0x0d869b39, + 0x90a0c000, + 0x98aaff54, + 0xff340d8a, + 0x9e8599c1, + 0x430cb720, + 0x02039e6a, + 0x0a48c0c8, + 0xaa01d208, + 0x7088d010, + 0xc0c80103, + 0xd0080944, + 0xcc12a941, + 0x9ea97104, + 0xb74c0207, + 0xcffe694b, + 0x9e9a2a7c, + 0xb58d7480, + 0xb58d7ccb, + 0xc0017c4b, + 0x020b91a2, + 0x0a1cc0ca, + 0xaa01d208, + 0xc0017299, + 0x020390bc, + 0x0a1cc0a4, + 0xa901d208, + 0xc0007482, + 0xb78a93b4, + 0x71044629, + 0x9304c000, + 0x52abb78a, + 0xc0ca010b, + 0x0a040918, + 0x52abb58a, + 0x430cb720, + 0xa941d008, + 0x52abb78a, + 0x4002ba24, + 0xc0007104, + 0x1205911c, + 0x9060c000, + 0xb58a0a02, + 0x9e5052ab, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x8420a61d, + 0x4314b7a0, + 0x00b6a19d, + 0xb78c040b, + 0xc10171ca, + 0x9e5b009a, + 0xb7cd7500, + 0xb76c7c2b, + 0xb72c6dd2, + 0xc0005a4a, + 0xb7869184, + 0x7502714c, + 0x91b2c000, + 0x634ab78d, + 0xc0007502, + 0x048b9112, + 0x564db786, + 0xc0087504, + 0x040b92a4, + 0x564cb786, + 0xc0007502, + 0x750490f4, + 0x9184c008, + 0x9360c005, + 0xc00074c0, + 0x9e6991c4, + 0x5dadb786, + 0x7c2bb7cc, + 0xd00e7500, + 0xc0070a62, + 0xc007935c, + 0x048b9360, + 0x704bb78c, + 0xc0007500, + 0xb7cd90c2, + 0xc0014753, + 0x040b93e0, + 0x5dccb786, + 0xd00e7500, + 0xc0010a62, + 0xba1b929c, + 0xff544003, + 0xb72099b1, + 0x9e82430c, + 0x5e2db781, + 0xc0007508, + 0xb7819344, + 0x1a045ead, + 0xc0007502, + 0xcffe9292, + 0xd0d12d7c, + 0x9e510964, + 0x7044c810, + 0xb78d048b, + 0xba247b4b, + 0x12684002, + 0x7088c812, + 0xc0009e96, + 0x040b9220, + 0x7b4ab74d, + 0x2d7ccffe, + 0xba129e51, + 0x02644002, + 0x7048d010, + 0xd0121164, + 0x9ea67104, + 0x2f7ccffe, + 0x430cb720, + 0xb78c048b, + 0xb741654b, + 0x0b9a5e2d, + 0xd009752a, + 0x74881bf6, + 0x92c4c000, + 0x5eadb781, + 0x75021a04, + 0x9212c000, + 0x7c4bb78c, + 0x03fed020, + 0xd019753c, + 0xb374027e, + 0xd0194434, + 0x0389024e, + 0x9060c000, + 0x040b9ebf, + 0x4e4cb7a7, + 0xc0017540, + 0xb72c9222, + 0xa09a5a52, + 0x6d4ab76c, + 0xc2009e4c, + 0x70c85a0d, + 0x90d4c001, + 0x402db743, + 0x5a0cc100, + 0xc000710a, + 0xd1a493f4, + 0xc1015985, + 0xc05405b4, + 0xa919994e, + 0x700a6005, + 0x9286c000, + 0xb74c040b, + 0xd0117c4a, + 0x9ea60e68, + 0xcffe7184, + 0xc0002f7c, + 0xc00290d2, + 0xc0000fca, + 0x126490a0, + 0x0bc4d0d2, + 0x9e739e69, + 0x7c2bb74c, + 0x0675d110, + 0x70c8d010, + 0xc002112e, + 0x040b9180, + 0x5e4cb781, + 0xc0027508, + 0xb78d9162, + 0xb7465a4a, + 0xb74c6dcc, + 0xba246d52, + 0xe0004002, + 0xc10103c4, + 0xc0011194, + 0x74c093a2, + 0x9342c001, + 0x5dd4b766, + 0xcc120f06, + 0xc18072cc, + 0xc1015a05, + 0xc05405b8, + 0x9e82990a, + 0x4002ba2e, + 0x018b9ebb, + 0x9916fe54, + 0x6a67d011, + 0xc2000208, + 0x9ea25a0b, + 0x2d7ccffe, + 0xc000729d, + 0x700c91e4, + 0x9182c000, + 0x0a7ecffe, + 0xb346700c, + 0xd110444a, + 0x9ea2066d, + 0x2d7ccffe, + 0x9e539e69, + 0x7c2bb74c, + 0x0a26d011, + 0x70c8d010, + 0xd012190c, + 0x03097104, + 0x2b7ccffe, + 0x430cb760, + 0xc0c80207, + 0xd2080a48, + 0xd010aa01, + 0x01077188, + 0x0944c0c8, + 0xa941d008, + 0x7104d012, + 0x040baa9d, + 0x64ccb746, + 0x00bad020, + 0xb74c0009, + 0xcffe4851, + 0x00da287c, + 0x009ac101, + 0x76800120, + 0x64ccb546, + 0x7c2bb50d, + 0x90e4c000, + 0x4949b78c, + 0xc0007500, + 0x020790e2, + 0x0a48c0c8, + 0xa801d208, + 0x7deeb79f, + 0x7e6eb7bf, + 0x7eeeb7df, + 0x7f6eb7ff, + 0x8c20c002, + 0xa61d9c22, + 0x4314b7c0, + 0x07850287, + 0x9e769e71, + 0x712bb78c, + 0x0b40c0da, + 0x04a4d010, + 0xa941d208, + 0x009cc101, + 0x3246e000, + 0x742bb7ec, + 0x9e710a02, + 0x0a42d002, + 0xb58c7480, + 0x0687712b, + 0x71abb56c, + 0x6db3b54c, + 0x90e2c000, + 0x9bc1fe54, + 0xd2080a02, + 0x7746a241, + 0x92d2c001, + 0x5d08d2a6, + 0xe0508520, + 0x94298044, + 0x94059428, + 0x0d829401, + 0x90e0c001, + 0xb78c9e71, + 0xb5ac5b2b, + 0x7500712b, + 0x9162c000, + 0x430cb720, + 0x67adb784, + 0xc0007502, + 0xc0009204, + 0x9e719100, + 0x682bb78d, + 0xc0007500, + 0x9e719104, + 0x632bb78d, + 0xb58d1a04, + 0xff34632b, + 0x0d869911, + 0x90a0c000, + 0x9a82ff34, + 0xff140d8a, + 0x02819b99, + 0x4314b7a0, + 0xc0c89e6c, + 0xd2080a48, + 0xd010aa01, + 0x9e6a7148, + 0x0944c0c8, + 0xa941d008, + 0x7104d012, + 0xba349e69, + 0xb7464002, + 0x7482712d, + 0x9172c003, + 0x632bb78d, + 0xc0037502, + 0xc00390d2, + 0x9e6992c0, + 0x6cadb766, + 0x90a0c000, + 0xb7669e69, + 0x9e696bad, + 0x5e2db781, + 0xc0007502, + 0x76809124, + 0x90c4c000, + 0x50adb746, + 0x9060c000, + 0x11340902, + 0xcc120a02, + 0x9e697088, + 0xb78c9e9b, + 0xb7466dab, + 0xb766562d, + 0xc2004fad, + 0xc1015a08, + 0x748200ca, + 0x5b35b746, + 0x9172c002, + 0xb7869e69, + 0x75005dad, + 0x92dac000, + 0x4e2db746, + 0xc0007295, + 0x7540923a, + 0x90e2c000, + 0x5a07c180, + 0xc00072d9, + 0xc10090fc, + 0x71485a07, + 0x9094c000, + 0x4002ba37, + 0xb78c9e69, + 0x752a652b, + 0x9152c000, + 0x0a78d011, + 0x7188d010, + 0x1978d011, + 0x9160c000, + 0xb74d9e69, + 0xba127b2b, + 0x02744002, + 0x7188d010, + 0xd0121174, + 0xba347104, + 0xc0004002, + 0x9e699280, + 0xb7860902, + 0xb7466c2d, + 0xc1016bb5, + 0xd4121244, + 0xfe347088, + 0xf0129aab, + 0xfffc0900, + 0xfffc9204, + 0x9e6c9260, + 0x0a48c0c8, + 0xaa81d208, + 0x718ad410, + 0xaa1dd3e8, + 0x7148d412, + 0x4002ba2d, + 0x058f018b, + 0x9be8fe34, + 0x0a029e71, + 0x6dadb586, + 0x5a2bb58d, + 0x7c2bb50c, + 0x430cb740, + 0x2afccffe, + 0xc0c80205, + 0xd2080a48, + 0xd410aa01, + 0xc0c87148, + 0xd0080944, + 0xd412aa41, + 0x000b7148, + 0x7e6eb79f, + 0x7eeeb7bf, + 0x7f6eb7df, + 0x7feeb7ff, + 0x8c00c002, + 0xa61d9c22, + 0xb7808460, + 0x09024d8c, + 0x4038b960, + 0xa101d208, + 0xa105d208, + 0x0a20c146, + 0x9341ffff, + 0x0882c021, + 0x08d0c006, + 0xaa619d1d, + 0x2a7cc00e, + 0xc0007506, + 0x0d8a90f2, + 0x9bd0fcf4, + 0x9240ffff, + 0x0902c021, + 0x0940c006, + 0xab629d2d, + 0xabe29d2d, + 0xa9629d2d, + 0x9d2da11a, + 0x9e74a9e2, + 0x478cb720, + 0x2a00c7f0, + 0x5aa1d224, + 0xd011a196, + 0x6a520e54, + 0xd0310242, + 0xf2080b48, + 0x750caa41, + 0x9204c000, + 0x6e59d051, + 0x498cb740, + 0x0dc2c809, + 0x02440d02, + 0x0db0c5c2, + 0xa10ef208, + 0xa182f208, + 0x7f80c010, + 0x9322c003, + 0xfcb4058b, + 0xa91a9a23, + 0xe0719e7b, + 0xd1f28d00, + 0xfd342dee, + 0xb79f98d2, + 0xc01c7468, + 0xc000753e, + 0xfcf490c4, + 0xb51f9b9e, + 0xb77f7468, + 0x0d067470, + 0xc00e55cc, + 0xfd142dfc, + 0x9e6c9bb1, + 0x4c8cb720, + 0x6a22c146, + 0x7468b77f, + 0x008b02c2, + 0x5aadb723, + 0x9ead058b, + 0xc06ca091, + 0x008b0ee0, + 0x5ab5b5c3, + 0xaba1f210, + 0xa3a2f210, + 0xc300a919, + 0xb7c35d3d, + 0xb5435bb5, + 0xaa155bad, + 0xc06e030b, + 0x9e5a0b00, + 0xabc2f208, + 0xf2082d04, + 0xfd14a241, + 0x008b9867, + 0x7470b77f, + 0xa3a1f210, + 0x5bb5b5c3, + 0xa3c2f208, + 0x0d06aa11, + 0xc00e55cc, + 0xb5832dfc, + 0xfd345aad, + 0xb7409802, + 0xb740490c, + 0xf0084d14, + 0x7508aa41, + 0x9142c000, + 0xc0007504, + 0xd0109144, + 0x7500aa41, + 0x90a2c000, + 0xf0080886, + 0xb760a0c1, + 0x09504894, + 0x0d20c146, + 0xfffa7097, + 0xffff9162, + 0x058b9100, + 0x998ffcb4, + 0xc0017400, + 0x9e6c9044, + 0x4c8cb720, + 0x6a22c146, + 0x4c8cb740, + 0xc06c02c2, + 0xd2080a80, + 0xa916a9a2, + 0xd1a2099a, + 0x0d845c90, + 0x00940098, + 0x4db5b543, + 0x4c35b5c3, + 0x4cb5b5e3, + 0xb583aa19, + 0xc0344d2d, + 0xd2089adb, + 0xf208a021, + 0x7508aa41, + 0x9124c000, + 0xf2080886, + 0xc000a0c1, + 0xfcf49080, + 0x058b9af3, + 0x995dfcb4, + 0x0d927400, + 0x9324ffff, + 0x91c0fff8, + 0xb740a60d, + 0x0205430c, + 0x0a10c0a4, + 0xaa01d208, + 0xc0037500, + 0x9e9292a4, + 0x0d38c0a2, + 0xa941d010, + 0xc0007480, + 0x0a0492e4, + 0xa241d010, + 0x430cb780, + 0xc0a29e93, + 0xd2080a40, + 0xfd54a101, + 0xb7809a5f, + 0xc0a4430c, + 0xd2080a10, + 0x7500aa01, + 0x9304c002, + 0x430cb720, + 0xc09c0203, + 0xd2080a18, + 0x7500aa01, + 0x91c2c002, + 0x4729b7aa, + 0x4814b7c0, + 0x9220c001, + 0xaa2df210, + 0xc000750c, + 0x9eaa93e4, + 0x55e80d86, + 0x2dfcc00e, + 0x9ad8fd14, + 0x478cb720, + 0x4508b780, + 0x29ded3f2, + 0xb58100e2, + 0xfd9468a9, + 0x0a029a33, + 0xa22df210, + 0x0a46c809, + 0x0a30c656, + 0xa221f210, + 0x90c0c001, + 0x46a9b78a, + 0x02857148, + 0x9124c000, + 0xfcf40d92, + 0xb7209a87, + 0xb7aa430c, + 0xb7204729, + 0xba2d430c, + 0xd0514000, + 0x02036b59, + 0xc0a20103, + 0xc0a40a40, + 0xd2080920, + 0xd008a902, + 0xd120aa41, + 0xd01102ed, + 0x72991952, + 0x9386fffd, + 0x7eeeb79f, + 0x7f6eb7bf, + 0x7feeb7df, + 0x9c228c60, + 0x8440a61d, + 0x430cb780, + 0x0a10c0a4, + 0xaa01d208, + 0xc00a7500, + 0xfd9490a4, + 0xb7209b00, + 0x0203430c, + 0x0a10c0a4, + 0xa901d208, + 0xc0097480, + 0xc0069324, + 0xd2081a78, + 0x7500aa01, + 0x91a2c005, + 0xc0a40203, + 0xb7ca0a1c, + 0xd2084629, + 0x9e97ab81, + 0x1962d011, + 0xd020627d, + 0x068f0328, + 0xba26110f, + 0xa1194000, + 0xc002a211, + 0xb78a9000, + 0xc10146a9, + 0x02d8029a, + 0x99bcc034, + 0x0e72d011, + 0x0aa0c0a2, + 0x75889ea7, + 0x4001ba3f, + 0xa031d208, + 0x9324c000, + 0xba249e6c, + 0x75044000, + 0x9264c000, + 0xc00075c4, + 0xb7209204, + 0xb78a430c, + 0xd11046a9, + 0x02420659, + 0x0a20c0a2, + 0xa911d208, + 0xd2080904, + 0xb720a111, + 0xb78a430c, + 0xf11046a9, + 0x0e840659, + 0x9ea20242, + 0xc0a20109, + 0xc0a20d20, + 0xc000093c, + 0xd01090c2, + 0xd008aa51, + 0xa911a245, + 0x058da91a, + 0x73d5018d, + 0xb7200764, + 0xfffd430c, + 0xb78a9344, + 0x09024729, + 0x02189e93, + 0x0a40c0a2, + 0x9c8bc810, + 0xd2080005, + 0xc002a105, + 0xb7209000, + 0xb78a430c, + 0xd11046a9, + 0xc2000639, + 0x00c25a08, + 0x46adb505, + 0x430cb720, + 0x46a9b78a, + 0x0096c101, + 0xc0a20098, + 0xb76008c0, + 0xc00040a9, + 0xb78b91a0, + 0xb742534a, + 0xb7455b2b, + 0x624446d5, + 0x0244c101, + 0x46cdb585, + 0x430cb720, + 0xd0120136, + 0xb78a0932, + 0xd02046a9, + 0xd1100022, + 0xc2000639, + 0x02425908, + 0x0a20c0a2, + 0xaa11d208, + 0x00a2d020, + 0x9e537106, + 0x29fcc00e, + 0x93b2fffe, + 0xfffe0d84, + 0x0d829041, + 0x992cfd54, + 0x430cb720, + 0xc0a40203, + 0xd2080a10, + 0x7680a902, + 0x9344c003, + 0x1a78c006, + 0xaa01d208, + 0xc0037500, + 0x020390e2, + 0x0a1cc0a4, + 0xaa01d208, + 0x4629b74a, + 0x4731b7aa, + 0x71040705, + 0x4001ba2d, + 0x0d7ecfff, + 0xcffe0189, + 0xd0510d30, + 0xb3236e59, + 0x65554432, + 0x478cb740, + 0x0528c101, + 0x01449e6e, + 0x1fa2d031, + 0x29fcc00e, + 0x0aa2d351, + 0x0fa6d012, + 0xc000a195, + 0xfd149360, + 0xb780998f, + 0xd3f24508, + 0xd20829ee, + 0xfd94a221, + 0xc80998ed, + 0xc6560d46, + 0x9e7a0d30, + 0xa166e250, + 0x1a62d011, + 0xe0500d02, + 0xba34a146, + 0x1ad04000, + 0x430cb720, + 0x165cd010, + 0xd0240906, + 0xb72a51d0, + 0xa9154729, + 0x2dfcc00e, + 0x10940f04, + 0xfffe7102, + 0xc000931a, + 0xfcf49080, + 0xb7809931, + 0x0d92430c, + 0xc0a29ea2, + 0xc0a40a3c, + 0xd2080d1c, + 0xd010a901, + 0x7088aa41, + 0x9226ffff, + 0x7d6eb79f, + 0x7deeb7bf, + 0x7e6eb7df, + 0x7eeeb7ff, + 0x8c40c002, + 0x91a0fddd, + 0x7d6eb79f, + 0x7deeb7bf, + 0x7e6eb7df, + 0x7eeeb7ff, + 0x8c40c002, + 0xa61d9c22, + 0xb7c08460, + 0xa1954314, + 0xaae1d808, + 0xb706040d, + 0x9e715654, + 0xa012048d, + 0x6dabb72c, + 0x7540068d, + 0xb72da099, + 0x9e5e5453, + 0x0ec8c0c8, + 0x7d74b53f, + 0x9102c000, + 0xaaa2d210, + 0x7df4b5bf, + 0x9380c000, + 0xb76c040d, + 0xb7866d4a, + 0xb75f69cc, + 0xd1a47d6c, + 0x59085985, + 0x05b8c101, + 0x05b4c101, + 0x9867c034, + 0xaa21d210, + 0x7008c010, + 0x700ac012, + 0x287ccffe, + 0x7decb51f, + 0xb5c69e71, + 0xb7205aad, + 0xb781430c, + 0x75025e2d, + 0x90c4c000, + 0x9b5bfe14, + 0x90e0c000, + 0xc0007508, + 0xfe3490e4, + 0x9e839912, + 0x90a0c000, + 0xb766048d, + 0x040d4e55, + 0x5accb746, + 0x6accb786, + 0x6b54b746, + 0x6bccb766, + 0xc1011244, + 0x01261524, + 0x6accb586, + 0x6b54b546, + 0x1126c101, + 0x6c4cb566, + 0x0a02c201, + 0x7088c810, + 0x0a02ce01, + 0x7088c812, + 0x6bccb546, + 0xb740a895, + 0x7480402b, + 0x92a4c009, + 0x430cb7e0, + 0xb7869eb9, + 0x7502564d, + 0x9024c005, + 0x6e4bb78c, + 0x6e4bb54c, + 0x7d3edffc, + 0x6f4bb58c, + 0x9144c004, + 0x674bb78c, + 0xc0047500, + 0xb76690a4, + 0xb78c5acd, + 0xd1a4744b, + 0xc8025949, + 0x0d047280, + 0x4002ba24, + 0x094cc002, + 0x7104d010, + 0x194cc002, + 0x7104d012, + 0x5d0cb740, + 0x4002ba24, + 0xca200248, + 0xba12aa05, + 0x9e524001, + 0x7053b76c, + 0xba2451e9, + 0x62474002, + 0xc1010936, + 0x76c01124, + 0x5389d224, + 0x9084c001, + 0x6fcbb78c, + 0xc0027506, + 0xb74692d4, + 0xb7666fd5, + 0xd051704d, + 0x70c86e27, + 0x90f4c000, + 0x5c4db786, + 0xc00073d9, + 0x9eb89232, + 0x5e05d3a2, + 0x5c4cb746, + 0xc0027104, + 0xd0109014, + 0xc1010624, + 0x70c80244, + 0x9334c001, + 0x430cb7c0, + 0xc0020a06, + 0x008d09b2, + 0x6eabb58c, + 0x6e2bb58c, + 0x430cb7a0, + 0xc0de9eab, + 0xc0dc0aa0, + 0xd2080d80, + 0xfc74aaa2, + 0x9eb199a0, + 0xb7869eb3, + 0xb74c5acd, + 0xd208744b, + 0xb586a2a2, + 0x0a02594d, + 0x6fcbb58c, + 0x734bb54c, + 0x0d90c0da, + 0xfc7409a2, + 0xb720998c, + 0xb781430c, + 0x75045e2d, + 0x9124c000, + 0x0a049eb0, + 0x7b4ab58d, + 0x9060c000, + 0x008f0f82, + 0x5c2db786, + 0x6d7bd012, + 0x01280148, + 0x0524c101, + 0xb5465d0d, + 0xa9195c35, + 0x9e9b0982, + 0xd1100224, + 0x00870669, + 0xc0ce9ea2, + 0xc0000d48, + 0xd8109120, + 0xd831aa41, + 0xba24a0c5, + 0x01b84002, + 0x040da89a, + 0x6fcab74d, + 0x0632d010, + 0x71040d84, + 0x9238ffff, + 0xc0007640, + 0x05839142, + 0xcffe1d84, + 0xba1b2dfc, + 0xfe144002, + 0x040d9be7, + 0x6e4ab78c, + 0xc0037500, + 0xa8919002, + 0xc0027442, + 0xfe149384, + 0x74009a39, + 0x430cb720, + 0x90c2c000, + 0x6cadb746, + 0x9080c000, + 0x6badb746, + 0x5e2db781, + 0xc0007502, + 0x74009124, + 0x90c4c000, + 0x50adb786, + 0x9060c000, + 0x12440a02, + 0xd0120a82, + 0x7500710a, + 0x93fcc001, + 0xb766048d, + 0xb7664fcd, + 0xb78d6bd5, + 0xb746654b, + 0xd1204e55, + 0xb74d11b7, + 0xc101634b, + 0xb78d65b9, + 0x65b563cb, + 0x12449eaa, + 0x65b9c101, + 0x9845fdf4, + 0xb786040d, + 0x9e836acc, + 0xc0007500, + 0xb5a69096, + 0x9e716acc, + 0x6aadb786, + 0x632bb74d, + 0x6b35b746, + 0x5aadb766, + 0x0246c101, + 0xc1011904, + 0xb5861526, + 0xb54d6aad, + 0xb546632b, + 0xfef46b35, + 0xc0009bb3, + 0xa89290c0, + 0xc0017640, + 0x040d90c2, + 0xb77fa891, + 0xb7867dec, + 0xb74674cc, + 0x7442754c, + 0x09040246, + 0x74ccb586, + 0x754cb546, + 0x9142c000, + 0xc0007442, + 0x74449246, + 0x9184c007, + 0x9300c004, + 0xb78d040d, + 0xb74c684a, + 0x0a046fca, + 0xb58d0904, + 0xb54c684a, + 0xaa196fca, + 0x048d9e71, + 0x67adb746, + 0x6735b746, + 0xc2000a04, + 0xc1015a0c, + 0xb54600cc, + 0xb54664ad, + 0xaa196435, + 0x694db746, + 0xc200040d, + 0xd1205a08, + 0x0a0200cd, + 0x684db546, + 0x694cb586, + 0x674cb586, + 0x67ccb586, + 0xb780a895, + 0x7500402b, + 0x9344c002, + 0x5accb746, + 0xb546a999, + 0xb79f5b4d, + 0x00b67b6a, + 0x009cc101, + 0x742bb58c, + 0x430cb720, + 0x5e2db781, + 0xc0027504, + 0xb76690e4, + 0x74c05dcc, + 0x90c4c000, + 0x0d82c010, + 0x9080c001, + 0xc1807086, + 0xc0005a07, + 0xd02091bc, + 0x5da811a6, + 0x05b8c101, + 0x9a7ac014, + 0x0800c040, + 0x9180c000, + 0x11b4d020, + 0xc1015da8, + 0xc01405b8, + 0xc0409a6f, + 0x10400a02, + 0x0a02c100, + 0x7008d010, + 0xc4120902, + 0x9e8b7104, + 0xb71fa89a, + 0x05127df4, + 0xc0d0052c, + 0xd8100d60, + 0x9e49a941, + 0x65b0d031, + 0x02420224, + 0x5a08c200, + 0xc1010904, + 0x290400cc, + 0x612db566, + 0xa141d810, + 0x5f35b566, + 0x7640a892, + 0x9384c002, + 0xb78d040d, + 0x0a0468ca, + 0x68cab58d, + 0x9280c002, + 0x7d6cb73f, + 0xc0007440, + 0xb7209282, + 0xb781430c, + 0x75025e2d, + 0x91a4c000, + 0xb746048d, + 0xc1005acd, + 0x02445a7d, + 0x5a07c200, + 0xb5860244, + 0x040d5acd, + 0x7b6ab73f, + 0x5ad4b7a6, + 0x734ab52c, + 0x5954b5a6, + 0x430cb7a0, + 0xb7819ea9, + 0x75025e4d, + 0x92a4c000, + 0x9904fe14, + 0xc0007400, + 0x9ea89204, + 0x64cab78d, + 0x7510c004, + 0x9134c000, + 0xb5a69e71, + 0xb73f5b35, + 0xb52c7b72, + 0x040d7433, + 0x714cb786, + 0xc0007502, + 0xb7669264, + 0xb7665ad4, + 0xc0144e4c, + 0x741099f7, + 0x915cc000, + 0x1980d031, + 0x5987a99a, + 0x4002ba1b, + 0x9a7efe14, + 0x430cb720, + 0x5e2db781, + 0xc0017508, + 0x9e719302, + 0x712db786, + 0xc0017502, + 0xb7869244, + 0x7504562d, + 0x91a2c001, + 0x542bb78d, + 0xc0007500, + 0xa89992a4, + 0xb746048d, + 0x58885acd, + 0x009cc101, + 0x5cadb726, + 0x02125908, + 0x70880242, + 0x90d6c000, + 0x09ffa99a, + 0x9260c000, + 0x040da899, + 0x5accb746, + 0xc1015888, + 0xb726009c, + 0x01245cad, + 0x02420212, + 0xc0007088, + 0xa99a90dc, + 0xfe140986, + 0xa8959a41, + 0xb5800a02, + 0xb58040ab, + 0xb7a040ad, + 0x9ea9430c, + 0x5e55b7a1, + 0xc0037744, + 0x774893a2, + 0x9342c003, + 0x75cdb786, + 0xc0017500, + 0xfe149284, + 0x74009887, + 0x91e2c000, + 0xb78e9ea8, + 0xb7445bca, + 0x708867cc, + 0xd0040a02, + 0xb5860a42, + 0xc00075cc, + 0x77429380, + 0x90a2c000, + 0xc0007748, + 0x008b9244, + 0x67adb784, + 0x4e2db746, + 0x50b5b746, + 0x71156245, + 0xd00b0a02, + 0xb5860a42, + 0xc00075ad, + 0x9ea990c0, + 0xb5860a06, + 0x9ea875cd, + 0x75ccb786, + 0xc0027500, + 0xb7209062, + 0xb781430c, + 0x75025e2d, + 0x91a4c008, + 0x4e35b746, + 0x4fadb746, + 0x6e2fd011, + 0xc0087088, + 0xd0119096, + 0x76800e2e, + 0x4458b342, + 0x5d0fd122, + 0x9140c000, + 0x5b2bb78c, + 0xc0007500, + 0x02249082, + 0x59070144, + 0x50adb786, + 0x5a07c200, + 0x7088c810, + 0x6badb786, + 0xc0007104, + 0xa89590d6, + 0xb5801228, + 0xa89640ad, + 0x40cdb740, + 0xc0007480, + 0x0a0691a2, + 0x40cbb580, + 0xd0121a04, + 0x0a7c7088, + 0x5a17c200, + 0x40cdb580, + 0x0902a916, + 0x4314b7c0, + 0xa141d810, + 0x4314b7a0, + 0xb721040b, + 0xb7cc5e54, + 0x76486dca, + 0x9084c002, + 0x5eccb781, + 0x75021a04, + 0x9032c001, + 0x4e4cb7a6, + 0xc0069e92, + 0xb5a70992, + 0xb7204e4c, + 0xb721430c, + 0x60d35f2d, + 0x0994d0d2, + 0x9a33fdd4, + 0x000a9e69, + 0x4e2db746, + 0x4e2db507, + 0xd0100224, + 0xc8127100, + 0xb5477088, + 0xc0044e2d, + 0x048b9100, + 0x6ccdb786, + 0x4fcdb7a6, + 0xfe1412d8, + 0x000a9864, + 0x0a02c040, + 0x7008c012, + 0xb786040b, + 0xb5074e4c, + 0xc2004e4c, + 0x70085a07, + 0x9228c003, + 0x0a06a895, + 0x402bb580, + 0x9160c003, + 0xb746040b, + 0xb74c4e54, + 0xba2e6e4a, + 0xd0104002, + 0x74800624, + 0x0244c101, + 0x5987c200, + 0x9144c000, + 0x5888c280, + 0x009ac101, + 0x5b2db786, + 0x70c8cc12, + 0x76429e69, + 0x4fb5b766, + 0x6badb786, + 0x1539d110, + 0x92e4c000, + 0x6e2fd011, + 0xc00072d9, + 0xd0119136, + 0x76800e26, + 0x4458b342, + 0x5d8bd122, + 0x0a02c040, + 0x7088d012, + 0xb587048b, + 0xc0004e4d, + 0xc1019080, + 0x70860124, + 0x91d6c000, + 0x448cb780, + 0xc0007500, + 0x748090a2, + 0x90dac000, + 0x0a06a915, + 0xa241d808, + 0x992ffe14, + 0x10010d02, + 0xc0129e53, + 0x74007006, + 0x92dac000, + 0x430cb720, + 0x5e2db781, + 0xc0007504, + 0x9e6c9244, + 0xc10100da, + 0xc0c8009a, + 0xb74c0a48, + 0xd208742b, + 0x7088aa01, + 0x90a8c000, + 0xda08aa15, + 0xa816a102, + 0x404ab780, + 0xc0007500, + 0x9eb390c2, + 0xfe140992, + 0x9e7198f3, + 0x632bb78c, + 0xc0007500, + 0xa8969222, + 0xb5800a02, + 0xc000404b, + 0xb7869160, + 0xb746562d, + 0x75024e2d, + 0x9124fff8, + 0x9000fff8, + 0x7ceeb79f, + 0x7d6eb7bf, + 0x7deeb7df, + 0x7e6eb7ff, + 0x8c60c002, + 0xa68d9c22, + 0x02059e9d, + 0x29fccffe, + 0x2a7ccffe, + 0xd1240705, + 0xd0325941, + 0x07876046, + 0x5dc1c280, + 0xd1310285, + 0xc1016527, + 0xd02260c7, + 0x01145e41, + 0x704601a8, + 0x6426d031, + 0x9094c000, + 0x0804c001, + 0x665cd031, + 0x615fd132, + 0x5941c180, + 0xc1010104, + 0xcffe0244, + 0xcffe29fc, + 0x02442c7c, + 0xc10159c0, + 0x9ea001b0, + 0xb7bf0007, + 0xb7df7eee, + 0xb7ff7f6e, + 0x8c607fee, + 0x9e989c22, + 0x09020802, + 0x9140c000, + 0x08029e98, + 0x4530d010, + 0x72c0cc14, + 0x7200c014, + 0xc18072c0, + 0xe0095d09, + 0xf0127204, + 0xc0005d04, + 0x72c49254, + 0x0804d004, + 0x15b4d024, + 0xd00472c0, + 0xd0240802, + 0xe00015b0, + 0x11813124, + 0x442ab330, + 0x72c09c22, + 0xffff0886, + 0xc40293a6, + 0xd06572c0, + 0xd0652c9e, + 0xc8023c9e, + 0xd0657200, + 0xd0652d2e, + 0xe0003d2e, + 0x9e531514, + 0x5408d01a, + 0x50acd01a, + 0xd01472c0, + 0xd0240002, + 0xe08015b0, + 0x5c055885, + 0x9304ffff, + 0x3124e000, + 0xb3301181, + 0x9c22442a, + 0x9d3a9e64, + 0x0e46c809, + 0x1e06b09b, + 0x9dc39ea4, + 0xe0009e58, + 0x15873400, + 0x482ab330, + 0x9e649c22, + 0x0e46c809, + 0x1d66b09b, + 0x9e589ea4, + 0x00009c22, + 0x87c2c809, + 0x0e60b060, + 0x87c2c809, + 0x0b80b060, + 0x87c2c809, + 0x0ac0b060, +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_data[] = { + 0x00000000, + 0x00000000, + 0x0000ff00, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x809000b0, + 0x809198c8, + 0x8288f08a, + 0x8288f10c, + 0x8288f1e0, + 0x8288f2a8, + 0x8288f2b4, + 0x8288f214, + 0x8288f208, + 0x8288f1e8, + 0x8288f2c8, + 0x8288f1ec, + 0x8288f1f0, + 0x8288f2d0, + 0x8288f2d8, + 0x8288fd1c, + 0x8288f998, + 0x8288f7e4, + 0x8288f7e7, + 0x8288fd1e, + 0x82899618, + 0x82899620, + 0x82899624, + 0x82899626, + 0x8289962a, + 0x8289962e, + 0x82899632, + 0x82899636, + 0x8289963a, + 0x82899640, + 0x828996a0, + 0x828996a8, + 0x828996ac, + 0x828996b0, + 0x828996b4, + 0x828996bc, + 0x828996c7, + 0x8289c35b, + 0x8289c350, + 0x8289c360, + 0x8288efa8, + 0x8288f012, + 0x8288f022, + 0x8288f024, + 0x8288f02c, + 0x8288f03c, + 0x8288f04c, + 0x8288f14e, + 0x8288f1b8, + 0x80901728, + 0x80901728, + 0x809163cc, + 0x8091c3f0, + 0x8090901c, + 0x8091c1d8, + 0x8090b7d4, + 0x8090abe4, + 0x80901cc0, + 0x8090515c, + 0x809018e0, + 0x80901728, + 0x80901728, + 0x80901728, + 0x80901728, + 0x80901728, + 0x80901728, + 0x80906738, + 0x80906678, + 0x80916060, + 0x80906010, + 0x80901728, + 0x80901728, + 0x01030004, + 0x01010102, + 0x02010101, + 0x04000301, + 0x01030004, + 0x01010102, + 0x02010101, + 0x04000301, + 0x0d080300, + 0x00100b06, + 0x00584aaf, + 0x007f1410, + 0x005030c6, + 0x007f500c, + 0x00027dac, + 0x000021f5, + 0x000308d1, + 0x0000057a, + 0x00260019, + 0x003f0032, + 0x0058004b, + 0x00710064, + 0x008a007d, + 0x00a30096, + 0x00bc00af, + 0x00d500c8, + 0x00ee00e1, + 0x010700fa, + 0x01070107, + 0x01070107, + 0x01070107, + 0x01070107, + 0x01070107, + 0x00000107, + 0x00200040, + 0x001002ab, + 0x015500cd, + 0x00080249, + 0x00cd01c7, + 0x0155005d, + 0x0249013b, + 0x00040111, + 0x01c700f1, + 0x00cd01af, + 0x005d00c3, + 0x01550059, + 0x013b0029, + 0x0249025f, + 0x01110235, + 0x00020021, + 0x00f1001f, + 0x01c70075, + 0x01af006f, + 0x00cd0069, + 0x00c30019, + 0x005d017d, + 0x0059005b, + 0x015502b9, + 0x002900a7, + 0x013b0283, + 0x025f0135, + 0x02490095, + 0x0235023f, + 0x0111008b, + 0x00210219, + 0x00010041, + 0x0b060600, + 0x0c0b0a06, + 0x0a0b0c06, + 0x0c0d0c0c, + 0x0d0d0c06, + 0x0b0b0c0c, + 0x0e0d0a0d, + 0x0a0d0e0e, + 0x0c0d0a06, + 0x0c0e0c0e, + 0x0e0d0a0d, + 0x0f0c0c0c, + 0x0f0b0d0e, + 0x0d0f0e0e, + 0x0d0f0f0f, + 0x0c0b0f0e, + 0x00140006, + 0x001a0016, + 0x0020001c, + 0x00280024, + 0x0034002c, + 0x00400038, + 0x00500048, + 0x00680058, + 0x00800070, + 0x00a00090, + 0x00d000b0, + 0x010000e0, + 0x01400120, + 0x01a00160, + 0x020001c0, + 0x02800240, + 0x034002c0, + 0x04000380, + 0x05000480, + 0x06800580, + 0x08000700, + 0x0a000900, + 0x0d000b00, + 0x10000e00, + 0x14001200, + 0x1a001600, + 0x00001c00, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x01010000, + 0x02020201, + 0x04030303, + 0x05040404, + 0x00000005, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x1234baac, +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_textreloc[] = { + 0x00000000, +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_datareloc[] = { + 0x00000000, +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_textrelocfulladdr[] = { + 0x00000000, +}; + +unsigned int all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_textreloctype[] = { + 0x00000000, +}; + +struct IMG_COMPILED_FW_BIN_RECORD simg_compiled_all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1 = { + /* unsigned int ui32TextSize, ui32DataSize; */ + 15278, 13643, + /* unsigned int ui32DataOrigin, ui32TextOrigin; */ + 0x8288eeb8, 0x80900000, + /* unsigned int ui32TextRelocSize, ui32DataRelocSize; */ + 0, 0, + + /* + * unsigned int ui32Pipes; + * unsigned char *sFormat, *rcMode; + * unsigned int ui32FormatsMask, ui32HwConfig; + */ + 2, "ALL_CODECS", "ALL", 31, 1, + + /* unsigned int ui32IntDefineCount; */ + 5, + /* unsigned char **pscIntDefineNames; */ + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_define_names_array, + /* unsigned int *pui32IntDefines; */ + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_define_values_array, + + /* + * unsigned int *pui32Text, *pui32Data; + * unsigned int *pui32TextReloc, *pui32DataDeloc; + * unsigned int *pui32TextRelocFullAddr, *pui32TextRelocType; + */ + + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_text, + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_data, + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_textreloc, + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_datareloc, + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_textrelocfulladdr, + all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1_textreloctype, +}; + +/* Py_Return simg_compiled_all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1 */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_binaries/include_all_fw_variants.h b/drivers/media/platform/vxe-vxd/encoder/fw_binaries/include_all_fw_variants.h --- a/drivers/media/platform/vxe-vxd/encoder/fw_binaries/include_all_fw_variants.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_binaries/include_all_fw_variants.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef __INCLUDE_ALL_VARIANTS_INC_INCLUDED__ +#define __INCLUDE_ALL_VARIANTS_INC_INCLUDED__ + +#define INCLUDE_ALL_VARIANTS_TEMPLATE_VERSION (1) + +#define FW_BIN_FORMAT_VERSION (2) + +struct IMG_COMPILED_FW_BIN_RECORD { + unsigned int text_size, data_size; + unsigned int data_origin, text_origin; + unsigned int text_reloc_size, data_reloc_size; + + unsigned int pipes; + unsigned char *fmt, *rc_mode; + unsigned int formats_mask, hw_config; + + unsigned int int_define_cnt; + unsigned char **int_define_names; + unsigned int *int_defines; + + unsigned int *text, *data; + unsigned int *text_reloc, *data_reloc; + unsigned int *text_reloc_full_addr, *text_reloc_type; +}; + +#include "ALL_CODECS_FW_ALL_pipes_2_contexts_8_hwconfig_1_bin.c" + +unsigned int all_fw_binaries_cnt = 1; +struct IMG_COMPILED_FW_BIN_RECORD *all_fw_binaries[] = { + &simg_compiled_all_codecs_fw_all_pipes_2_contexts_8_hwconfig_1, +}; + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_headers/coreflags.h b/drivers/media/platform/vxe-vxd/encoder/fw_headers/coreflags.h --- a/drivers/media/platform/vxe-vxd/encoder/fw_headers/coreflags.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_headers/coreflags.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _COREFLAGS_H_ +#define _COREFLAGS_H_ + +#define SERIALIZED_PIPES (1) + +/* The number of TOPAZ cores present in the system */ +#define TOPAZHP_MAX_NUM_PIPES (4) + +#define TOPAZHP_MAX_POSSIBLE_STREAMS (8) +#define TOPAZHP_MAX_BU_SUPPORT_HD 90 +#define TOPAZHP_MAX_BU_SUPPORT_4K 128 + +#define USE_VCM_HW_SUPPORT (1) +/* controls the firmwares ability to support the optional hardware input scaler */ +#define INPUT_SCALER_SUPPORTED (1) +/* controls the firmwares ability to support secure mode firmware upload */ +#define SECURE_MODE_POSSIBLE (1) + +/* controls the firmwares ability to support secure input/output ports */ +#define SECURE_IO_PORTS (1) + +/* Line counter feature is not ready for Onyx yet + * (comment the define to remove the feature from builds) + */ +#define LINE_COUNTER_SUPPORTED (1) + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_headers/defs.h b/drivers/media/platform/vxe-vxd/encoder/fw_headers/defs.h --- a/drivers/media/platform/vxe-vxd/encoder/fw_headers/defs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_headers/defs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#if !defined DEFS_H_ +#define DEFS_H_ + +#include + +/* + * MACROS to insert values into fields within a word. The basename of the + * field must have MASK_BASENAME and SHIFT_BASENAME constants. + */ +#define F_MASK(basename) (MASK_##basename) +#define F_SHIFT(basename) (SHIFT_##basename) +/* + * Extract a value from an instruction word. + */ +#define F_EXTRACT(val, basename) (((val) & (F_MASK(basename))) >> (F_SHIFT(basename))) + +/* + * Mask and shift a value to the position of a particular field. + */ +#define F_ENCODE(val, basename) (((val) << (F_SHIFT(basename))) & (F_MASK(basename))) +#define F_DECODE(val, basename) (((val) & (F_MASK(basename))) >> (F_SHIFT(basename))) + +/* + * Insert a value into a word. + */ +#define F_INSERT(word, val, basename) (((word) & ~(F_MASK(basename))) | (F_ENCODE((val), basename))) + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_headers/mtx_fwif.h b/drivers/media/platform/vxe-vxd/encoder/fw_headers/mtx_fwif.h --- a/drivers/media/platform/vxe-vxd/encoder/fw_headers/mtx_fwif.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_headers/mtx_fwif.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _MTX_FWIF_H_ +#define _MTX_FWIF_H_ + +#include "vxe_common.h" +#include "topazscfwif.h" + +//#define VXE_MEASURE_MTX_CLK_FREQ + +/* + * enum describing the MTX load method + */ +enum mtx_load_method { + MTX_LOADMETHOD_NONE = 0, /* don't load MTX code */ + MTX_LOADMETHOD_BACKDOOR, /* backdoor - writes MTX load data direct to out.res */ + MTX_LOADMETHOD_REGIF, /* load mtx code via register interface */ + MTX_LOADMETHOD_DMA, /* load mtx code via DMA */ + MTX_LOADMETHOD_FORCE32BITS = 0x7FFFFFFFU + +}; + +/* + * defines that should come from auto generated headers + */ +#define MTX_DMA_MEMORY_BASE (0x82880000) +#define PC_START_ADDRESS (0x80900000) + +#define MTX_CORE_CODE_MEM (0x10) +#define MTX_CORE_DATA_MEM (0x18) + +#define MTX_PC (0x05) + +/* + * MTX Firmware Context Structure + */ + +/* + * struct img_fw_int_defines_table - contains info for the fw int defines + * + * @length: length of the table + * @names: array of names of entries + * @values: array of values of entries + */ +struct img_fw_int_defines_table { + unsigned int length; + unsigned char **names; + unsigned int *values; +}; + +/* + * struct img_fw_context - contains info for the context of the loaded firmware + * + * @initialized: TRUE if MTX core is initialized + * @populated: TRUE if MTX firmware context had been populated with data + * @active_ctx_mask: A bit mask of active encode contexts in the firmware + * @dev_ctx: Pointer to the device context + * @load_method: method used to load this MTX + * @supported_codecs: Codec mask + * @mtx_debug_val: Value in MTX Debug register (for RAM config) + * @mtx_ram_size: Size of MTX RAM + * @mtx_bank_size: Size of MTX RAM banks + * @mtx_reg_mem_space_addr: Memspace ID for MTX registers + * @topaz_reg_mem_space_addr: Memspace ID for TOPAZ registers + * @topaz_multicore_reg_addr: Memspace ID for TOPAZ multicore control registers + * @core_rev: Hardware core revision ID + * @core_des1: Hardware core designer (feature bits) + * @drv_has_mtx_ctrl: TRUE if driver (not DASH) has control of MTX + * @access_control: Use to get read/write access to MTX + * @hw_num_pipes: Number of pipes available in hardware + * @num_pipes: Number of pipes supported by firmware + * @num_contexts: Number of contexts supported by firmware + * @mtx_context_data_copy: Copy of MTX Context Data during hibernate + * @mtx_reg_copy: Copy of MTX Register block during hibernate + * @mtx_topaz_fw_text_size: Size of MTX Firmware Text Section in words + * @mtx_topaz_fw_text: Pointer to MTX Firmware Text Section + * @mtx_topaz_fw_data_size: Size of MTX Firmware Data Section in words + * @mtx_topaz_fw_data: Pointer to MTX Firmware Data Section + * @mtx_topaz_fw_data_origin: Offset to location of Data section + * @int_defines: table of int defines + */ +struct img_fw_context { + unsigned short initialized; + unsigned short populated; + unsigned char active_ctx_mask; + + void *dev_ctx; + + enum mtx_load_method load_method; + + unsigned int supported_codecs; + + unsigned int mtx_debug_val; + unsigned int mtx_ram_size; + unsigned int mtx_bank_size; + + void *mtx_reg_mem_space_addr; + void *topaz_reg_mem_space_addr[TOPAZHP_MAX_NUM_PIPES]; + void *topaz_multicore_reg_addr; + unsigned int core_rev; + unsigned int core_des1; + + unsigned short drv_has_mtx_ctrl; + unsigned int access_control; + + unsigned int hw_num_pipes; + unsigned int num_pipes; + unsigned int num_contexts; + + struct vidio_ddbufinfo *mtx_context_data_copy[TOPAZHP_MAX_POSSIBLE_STREAMS]; + unsigned int *mtx_reg_copy; + + unsigned int mtx_topaz_fw_text_size; + unsigned int *mtx_topaz_fw_text; + + unsigned int mtx_topaz_fw_data_size; + unsigned int *mtx_topaz_fw_data; + + unsigned int mtx_topaz_fw_data_origin; + + struct img_fw_int_defines_table int_defines; +}; + +/* + * Populates MTX context structure + * @param codec : version of codec specific firmware to associate with this MTX + * @param fw_ctx : Output context + * @return int : Standard IMG_ERRORCODE + */ +int mtx_populate_fw_ctx(enum img_codec codec, + struct img_fw_context *fw_ctx); + +/* + * Initialise the hardware using given (populated) MTX context structure + * @param fw_ctx : Pointer to the context of the target MTX + * @return None + */ +void mtx_initialize(void *dev_ctx, struct img_fw_context *fw_ctx); + +/* + * Return the integer define used to compile given version of firmware. + * @param fw_ctx : Pointer to the context of the target MTX + * @param name : Name of a define (string) + * @return Value of define or -1 if not found. + */ +int mtx_get_fw_config_int(struct img_fw_context const * const fw_ctx, + unsigned char const * const name); + +/* + * Load text and data sections onto an MTX. + * @param fw_ctx : Pointer to the context of the target MTX + * @param load_method : Method to use for loading code + * @return None + */ +void mtx_load(void *dev_ctx, struct img_fw_context *fw_ctx, + enum mtx_load_method load_method); + +/* + * Deinitialises MTX and MTX control structure + */ +void mtx_deinitialize(struct img_fw_context *fw_ctx); + +/* + * Saves MTX State -- Registers and Data Memory + */ +void mtx_save_state(struct img_fw_context *fw_ctx); + +/* + * Restores MTX State -- Registers and Data Memory + */ +void mtx_restore_state(void *ctx, struct img_fw_context *fw_ctx); + +/* + * mtx_start + */ +void mtx_start(struct img_fw_context *fw_ctx); + +/* + * mtx_stop + */ +void mtx_stop(struct img_fw_context *fw_ctx); + +/* + * Kicks MTX + */ +void mtx_kick(struct img_fw_context *fw_ctx, unsigned int kick_count); + +/* + * Waits for MTX to halt + */ +void mtx_wait_for_completion(struct img_fw_context *fw_ctx); + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_headers/topazscfwif.h b/drivers/media/platform/vxe-vxd/encoder/fw_headers/topazscfwif.h --- a/drivers/media/platform/vxe-vxd/encoder/fw_headers/topazscfwif.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_headers/topazscfwif.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _TOPAZSCFWIF_H_ +#define _TOPAZSCFWIF_H_ + +#include "coreflags.h" +#include + +#define MAX_QP_H264 (51) + +/* + * The number of bytes used by each MVEA MV param & above param region + */ +#define MVEA_MV_PARAM_REGION_SIZE 16 +#define MVEA_ABOVE_PARAM_REGION_SIZE 96 + +/* + * Macros to align to the correct number of bytes + */ +#define ALIGN_4(X) (((X) + 3) & ~3) +#define ALIGN_16(X) (((X) + 15) & ~15) +#define ALIGN_64(X) (((X) + 63) & ~63) +#define ALIGN_128(X) (((X) + 127) & ~127) +#define ALIGN_1024(X) (((X) + 1023) & ~1023) + +/* + * Context size allocated from host application + */ +#define MTX_CONTEXT_SIZE (13 * 1024) + +/* + * SEI (Buffering Period and Picture Timing) Constants shared + * between host and firmware + */ +#define BPH_SEI_NAL_INITIAL_CPB_REMOVAL_DELAY_SIZE 23 +#define PTH_SEI_NAL_CPB_REMOVAL_DELAY_SIZE 23 +#define PTH_SEI_NAL_DPB_OUTPUT_DELAY_SIZE 7 + +/* + * Size of the header in output coded buffer. This varies based on + * whether data logging is enabled/disabled + */ +#if defined(INCLUDE_CRC_REGISTER_CHECKS) +#define CRC_REGISTER_FEEDBACK_SIZE (80 * 4) +#else +#define CRC_REGISTER_FEEDBACK_SIZE 0 +#endif + +/* MUST be aligned to the DMA 64 byte boundary condition + * (CRC data is DMA'd after the coded buffer header) + */ +#define CODED_BUFFER_HEADER_SIZE 64 +#define CODED_BUFFER_INFO_SECTION_SIZE (CODED_BUFFER_HEADER_SIZE + CRC_REGISTER_FEEDBACK_SIZE) + +/* + * Mask defines for the -ui8EnableSelStatsFlags variable + */ +#define ESF_FIRST_STAGE_STATS 1 +#define ESF_MP_BEST_MB_DECISION_STATS 2 +#define ESF_MP_BEST_MOTION_VECTOR_STATS 4 + +#define CUSTOM_QUANT_PARAMSIZE_8x8 2 + +/* + * Combined size of H.264 quantization lists (6 * 16 + {2 or 6} * 64) + */ +#define QUANT_LISTS_SIZE (6 * 16 + CUSTOM_QUANT_PARAMSIZE_8x8 * 64) + +/* + * Size in bytes and words of memory to transfer partially coded header data + */ +#define MAX_HEADERSIZEBYTES (128) +#define MAX_HEADERSIZEWORDS (32) + +/* + * Maximum number of slices per field + */ +#define MAX_SLICESPERPIC (128) + +/* + * Picture parameter flags used in the PIC_PARAM structure + */ +#define ISINTERP_FLAGS (0x00000001) +#define ISRC_FLAGS (0x00000010) +#define ISRC_I16BIAS (0x00000020) +#define ISINTERB_FLAGS (0x00000080) +#define ISSCENE_DISABLED (0x00000100) +#define ISMULTIREF_FLAGS (0x00000200) +#define SPATIALDIRECT_FLAGS (0x00000400) + +/* + * Enum describing contents of scratch registers + */ +enum mtx_scratch_regdata { + MTX_SCRATCHREG_BOOTSTATUS = 0, + MTX_SCRATCHREG_UNUSED = 0, + MTX_SCRATCHREG_TOHOST, //!< Reg for MTX->Host data + MTX_SCRATCHREG_TOMTX, //!< Reg for Host->MTX data + + MTX_SCRATCHREG_SIZE, //!< End marker for enum + MTX_SCRATCHREG_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * `MTX_SCRATCHREG_IDLE` register that is used for synchronous communication and debug. + * + * Current register usage: + * : + * 2-10 : Number of executed commands (mod 255) + * 0-1 : FW idle status + */ +#define MTX_SCRATCHREG_IDLE TOPAZHP_TOP_CR_FIRMWARE_REG_4 //!< Reg for firmware IDLE status + +/* Flags relating to MTX_SCRATCHREG_IDLE */ +/* Bits [10-22] are used for the line information */ +/* TOPAZHP_LINE_COUNTER (see TRM 8.1.1) uses 12 bits for the line count */ +#define SHIFT_FW_IDLE_REG_STATUS (0) +#define MASK_FW_IDLE_REG_STATUS (3) +#define FW_IDLE_STATUS_IDLE (1) + +/* + * In secure FW mode the first value written to the command FIFO is copied to MMU_CONTROL_0 + * by the firmware. When we don't want that to happen we can write this value instead. + * The firmware will know to ignore it as + * long as it is written BEFORE the firmware starts up + */ +#define TOPAZHP_NON_SECURE_FW_MARKER (0xffffffff) + +/* + * This value is an arbitrary value that the firmware will write to TOPAZHP_TOP_CR_FIRMWARE_REG_1 + * (MTX_SCRATCHREG_BOOTSTATUS) + * when it has completed the boot process to indicate that it is ready + */ +#define TOPAZHP_FW_BOOT_SIGNAL (0x12345678) + +/* + * Sizes for arrays that depend on reference usage pattern + */ +#define MAX_REF_B_LEVELS 3 +#define MAX_REF_SPACING 1 +#define MAX_REF_I_OR_P_LEVELS (MAX_REF_SPACING + 2) +#define MAX_REF_LEVELS (MAX_REF_B_LEVELS + MAX_REF_I_OR_P_LEVELS) +#define MAX_PIC_NODES (MAX_REF_LEVELS + 2) +#define MAX_MV (MAX_PIC_NODES * 2) + +#define MAX_BFRAMES 7 //B-frame count limit for Hierarchical mode +#define MAX_GOP_SIZE (MAX_BFRAMES + 1) +#define MAX_SOURCE_SLOTS_SL (MAX_GOP_SIZE + 1) + +#define MV_ROW_STRIDE (ALIGN_64(sizeof(struct img_mv_settings) * MAX_BFRAMES)) + +/* + * MTX -> host message FIFO + */ +#define LOG2_WB_FIFO_SIZE (5) + +#define WB_FIFO_SIZE (1 << (LOG2_WB_FIFO_SIZE)) + +#define SHIFT_WB_PRODUCER (0) +#define MASK_WB_PRODUCER (((1 << LOG2_WB_FIFO_SIZE) - 1) << SHIFT_WB_PRODUCER) + +#define SHIFT_WB_CONSUMER (0) +#define MASK_WB_CONSUMER (((1 << LOG2_WB_FIFO_SIZE) - 1) << SHIFT_WB_CONSUMER) + +/* + * Number of buffers per encode task (default: 2 - double bufferring) + */ +#define CODED_BUFFERING_CNT 2 //default to double-buffering + +/* + * Calculates the ideal minimun coded buffers for a frame level encode + */ +#define CALC_OPTIMAL_CODED_PACKAGES_FRAME_ENCODE(numcores, isinterlaced) \ + ((((isinterlaced) ? 2 : 1) * (numcores)) * CODED_BUFFERING_CNT) + +/* + * Calculates the ideal minimum coded buffers for a slice level encode + */ +#define CALC_OPTIMAL_CODED_PACKAGES_SLICE_ENCODE(slicesperpic) \ + ((slicesperpic) * CODED_BUFFERING_CNT) + +/* + * Calculates the ideal minimum coded buffers for an encode + */ +#define CALC_OPTIMAL_CODED_PACKAGES_ENCODE(bis_slice_level, slicesperpic, numcores, isinterlaced) \ + (bis_slice_level ? CALC_OPTIMAL_CODED_PACKAGES_SLICE_ENCODE(slicesperpic) \ + : CALC_OPTIMAL_CODED_PACKAGES_FRAME_ENCODE(numcores, isinterlaced)) + +/* + * Calculates the actual number of coded buffers that can be used for an encode + */ +#define CALC_NUM_CODED_PACKAGES_ENCODE(bis_slice_level, slicesperpic, numcores, isinterlaced) \ + (CALC_OPTIMAL_CODED_PACKAGES_ENCODE(bis_slice_level, slicesperpic, numcores, isinterlaced)) + +/* + * Maximum number of coded packages + */ +#define MAX_CODED_PACKAGES CALC_NUM_CODED_PACKAGES_ENCODE(0, 0, TOPAZHP_MAX_NUM_PIPES, 1) + +/* + * DMA configuration parameters + */ +#define MTX_DMA_BURSTSIZE_BYTES 32 + +/* + * types that should be in DMAC header file + */ +enum dmac_acc_del { + DMAC_ACC_DEL_0 = 0x0, //!< Access delay zero clock cycles + DMAC_ACC_DEL_256 = 0x1, //!< Access delay 256 clock cycles + DMAC_ACC_DEL_512 = 0x2, //!< Access delay 512 clock cycles + DMAC_ACC_DEL_768 = 0x3, //!< Access delay 768 clock cycles + DMAC_ACC_DEL_1024 = 0x4, //!< Access delay 1024 clock cycles + DMAC_ACC_DEL_1280 = 0x5, //!< Access delay 1280 clock cycles + DMAC_ACC_DEL_1536 = 0x6, //!< Access delay 1536 clock cycles + DMAC_ACC_DEL_1792 = 0x7, //!< Access delay 1792 clock cycles + DMAC_ACC_FORCE32BITS = 0x7FFFFFFFU +}; + +enum dmac_bswap { + DMAC_BSWAP_NO_SWAP = 0x0, //!< No byte swapping will be performed. + DMAC_BSWAP_REVERSE = 0x1, //!< Byte order will be reversed. + DMAC_BSWAP_FORCE32BITS = 0x7FFFFFFFU +}; + +enum dmac_burst { + DMAC_BURST_0 = 0x0, //!< burst size of 0 + DMAC_BURST_1 = 0x1, //!< burst size of 1 + DMAC_BURST_2 = 0x2, //!< burst size of 2 + DMAC_BURST_3 = 0x3, //!< burst size of 3 + DMAC_BURST_4 = 0x4, //!< burst size of 4 + DMAC_BURST_5 = 0x5, //!< burst size of 5 + DMAC_BURST_6 = 0x6, //!< burst size of 6 + DMAC_BURST_7 = 0x7, //!< burst size of 7 + DMAC_BURST_FORCE32BITS = 0x7FFFFFFFU +}; + +#define DMAC_VALUE_COUNT(BSWAP, PW, DIR, PERIPH_INCR, COUNT) \ + ((((BSWAP) << SHIFT_IMG_SOC_BSWAP) & MASK_IMG_SOC_BSWAP) |\ + (((PW) << SHIFT_IMG_SOC_PW) & MASK_IMG_SOC_PW) |\ + (((DIR) << SHIFT_IMG_SOC_DIR) & MASK_IMG_SOC_DIR) |\ + (((PERIPH_INCR) << SHIFT_IMG_SOC_PI) & MASK_IMG_SOC_PI) |\ + (((COUNT) << SHIFT_IMG_SOC_CNT) & MASK_IMG_SOC_CNT)) + +#define DMAC_VALUE_PERIPH_PARAM(ACC_DEL, INCR, BURST) \ + ((((ACC_DEL) << SHIFT_IMG_SOC_ACC_DEL) & MASK_IMG_SOC_ACC_DEL) |\ + (((INCR) << SHIFT_IMG_SOC_INCR) & MASK_IMG_SOC_INCR) |\ + (((BURST) << SHIFT_IMG_SOC_BURST) & MASK_IMG_SOC_BURST)) + +enum dmac_pw { + DMAC_PWIDTH_32_BIT = 0x0, //!< Peripheral width 32-bit. + DMAC_PWIDTH_16_BIT = 0x1, //!< Peripheral width 16-bit. + DMAC_PWIDTH_8_BIT = 0x2, //!< Peripheral width 8-bit. + DMAC_PWIDTH_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Enum describing Command IDs. Some commands require data to be DMA'd in + * from the Host, with the base address of the data specified in the Command + * Data Address word of the command. The data required is specific to each + * command type. + */ +enum mtx_cmd_id { + // Common Commands + MTX_CMDID_NULL, //!< (no data)\n Null command does nothing\n + MTX_CMDID_SHUTDOWN, //!< (no data)\n shutdown the MTX\n + + // Video Commands + /* !< (extra data: #MTX_HEADER_PARAMS) Command for Sequence, Picture and Slice headers */ + MTX_CMDID_DO_HEADER, + /* !< (data: low latency encode activation, HBI usage) Encode frame data*/ + MTX_CMDID_ENCODE_FRAME, + MTX_CMDID_START_FRAME, //!< (no data)\n Prepare to encode frame\n + MTX_CMDID_ENCODE_SLICE, //!< (no data)\n Encode slice data\n + MTX_CMDID_END_FRAME, //!< (no data)\n Complete frame encoding\n + /* !< (data: pipe number, extra data: #IMG_MTX_VIDEO_CONTEXT)\n Set MTX Video Context */ + MTX_CMDID_SETVIDEO, + /* !< (data: pipe number, extra data: #IMG_MTX_VIDEO_CONTEXT) + * Get MTX Video Context + */ + MTX_CMDID_GETVIDEO, + /* !< (data: new pipe allocations for the context) + * Change pipe allocation for a Video Context + */ + MTX_CMDID_DO_CHANGE_PIPEWORK, +#if SECURE_IO_PORTS + MTX_CMDID_SECUREIO, //!< (data: )\n Change IO security\n +#endif + /* !< (data: subtype and parameters, extra data: #IMG_PICMGMT_CUSTOM_QUANT_DATA + * (optional))\n Change encoding parameters + */ + MTX_CMDID_PICMGMT, + /* !< (data: QP and bitrate)\n Change encoding parameters */ + MTX_CMDID_RC_UPDATE, + /* !< (extra data: #IMG_SOURCE_BUFFER_PARAMS) + * Transfer source buffer from host + */ + MTX_CMDID_PROVIDE_SOURCE_BUFFER, + /* !< (data: buffer parameters, extra data: reference buffer) + * Transfer reference buffer from host + */ + MTX_CMDID_PROVIDE_REF_BUFFER, + /* !< (data: slot and size, extra data: coded package)\n Transfer coded package from host + *(coded package contains addresses of header and coded output buffers/1st linked list node) + */ + MTX_CMDID_PROVIDE_CODEDPACKAGE_BUFFER, + MTX_CMDID_ABORT, //!< (no data)\n Stop encoding and release all buffers\n + + // JPEG commands + MTX_CMDID_SETQUANT, //!< (extra data: #JPEG_MTX_QUANT_TABLE)\n + MTX_CMDID_SETUP_INTERFACE, //!< (extra data: #JPEG WRITEBACK POINTERS)\n + MTX_CMDID_ISSUEBUFF, //!< (extra data: #MTX_ISSUE_BUFFERS)\n + MTX_CMDID_SETUP, //!< (extra data: #JPEG_MTX_DMA_SETUP)\n\n + /* !< (extra data: #IMG_VXE_SCALER_SETUP)\nChange source + * pixel format after context creation\ + */ + MTX_CMDID_UPDATE_SOURCE_FORMAT, + /* !< (extra data: #IMG_VXE_CSC_SETUP)\nChange Colour Space Conversion setup dynamically */ + MTX_CMDID_UPDATE_CSC, + + MTX_CMDID_ENDMARKER, //!< end marker for enum + MTX_CMDID_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Priority for the command. + * Each Command ID will only work with the correct priority. + */ +#define MTX_CMDID_PRIORITY 0x80 + +/* + * Indicates whether or not to issue an interrupt when the firmware sends the + * command's writeback message. + */ +#define MTX_CMDID_WB_INTERRUPT 0x8000 + +/* + * Enum describing response IDs + */ +enum mtx_message_id { + MTX_MESSAGE_ACK, + MTX_MESSAGE_CODED, + MTX_MESSAGE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Mask and shift values for command word + */ +#define SHIFT_MTX_MSG_CMD_ID (0) +#define MASK_MTX_MSG_CMD_ID (0x7f << SHIFT_MTX_MSG_CMD_ID) +#define SHIFT_MTX_MSG_PRIORITY (7) +#define MASK_MTX_MSG_PRIORITY (0x1 << SHIFT_MTX_MSG_PRIORITY) +#define SHIFT_MTX_MSG_CORE (8) +#define MASK_MTX_MSG_CORE (0x7f << SHIFT_MTX_MSG_CORE) +#define SHIFT_MTX_MSG_COUNT (16) +#define MASK_MTX_MSG_COUNT (0xffffU << SHIFT_MTX_MSG_COUNT) +#define SHIFT_MTX_MSG_MESSAGE_ID (16) +#define MASK_MTX_MSG_MESSAGE_ID (0xff << SHIFT_MTX_MSG_MESSAGE_ID) + +/* + * Mask and shift values for data word + */ +#define SHIFT_MTX_MSG_ENCODE_CODED_INTERRUPT (0) +#define MASK_MTX_MSG_ENCODE_CODED_INTERRUPT \ + (0xff << SHIFT_MTX_MSG_ENCODE_CODED_INTERRUPT) +#define SHIFT_MTX_MSG_ENCODE_USE_LINE_COUNTER (20) +#define MASK_MTX_MSG_ENCODE_USE_LINE_COUNTER \ + (0x1 << SHIFT_MTX_MSG_ENCODE_USE_LINE_COUNTER) + +#define SHIFT_MTX_MSG_PICMGMT_SUBTYPE (0) +#define MASK_MTX_MSG_PICMGMT_SUBTYPE (0xff << SHIFT_MTX_MSG_PICMGMT_SUBTYPE) +#define SHIFT_MTX_MSG_PICMGMT_DATA (8) +#define MASK_MTX_MSG_PICMGMT_DATA (0xffffffU << SHIFT_MTX_MSG_PICMGMT_DATA) +#define SHIFT_MTX_MSG_PICMGMT_STRIDE_Y (0) +#define MASK_MTX_MSG_PICMGMT_STRIDE_Y (0x3ff << SHIFT_MTX_MSG_PICMGMT_STRIDE_Y) +#define SHIFT_MTX_MSG_PICMGMT_STRIDE_UV (10) +#define MASK_MTX_MSG_PICMGMT_STRIDE_UV (0x3ff << SHIFT_MTX_MSG_PICMGMT_STRIDE_UV) + +/*Values for updating static Qp values when Rate Control is disabled*/ +#define SHIFT_MTX_MSG_NUM_CODED_BUFFERS_PER_HEADER (5) +#define MASK_MTX_MSG_NUM_CODED_BUFFERS_PER_HEADER \ + (0xf << SHIFT_MTX_MSG_NUM_CODED_BUFFERS_PER_HEADER) + +#define SHIFT_MTX_MSG_PROVIDE_CODEDPACKAGE_BUFFER_SLOT (0) +#define MASK_MTX_MSG_PROVIDE_CODEDPACKAGE_BUFFER_SLOT \ + (0x0f << SHIFT_MTX_MSG_PROVIDE_CODEDPACKAGE_BUFFER_SLOT) +#define SHIFT_MTX_MSG_PROVIDE_CODED_BUFFER_SIZE (4) +#define MASK_MTX_MSG_PROVIDE_CODED_BUFFER_SIZE \ + (0x3fffff << SHIFT_MTX_MSG_PROVIDE_CODED_BUFFER_SIZE) + +/* + * Enum describing partially coded header element types + */ +enum header_element_type { + ELEMENT_STARTCODE_RAWDATA = 0, //!< Raw data that includes a start code + /*!< Raw data that includes a start code in the middle of the header */ + ELEMENT_STARTCODE_MIDHDR, + ELEMENT_RAWDATA, //!< Raw data + ELEMENT_QP, //!< Insert the H264 Picture Header QP parameter + ELEMENT_SQP, //!< Insert the H264 Slice Header QP parameter + /* Insert the H263/MPEG4 Frame Q_scale parameter (vob_quant field) */ + ELEMENT_FRAMEQSCALE, + /* !< Insert the H263/MPEG4 Slice Q_scale parameter (quant_scale field) */ + ELEMENT_SLICEQSCALE, + ELEMENT_INSERTBYTEALIGN_H264, //!< Insert the byte alignment bits for H264 + ELEMENT_INSERTBYTEALIGN_MPG4, //!< Insert the byte alignment bits for MPEG4 + ELEMENT_INSERTBYTEALIGN_MPG2, //!< Insert the byte alignment bits for MPEG2 + ELEMENT_VBV_MPG2, + ELEMENT_TEMPORAL_REF_MPG2, + ELEMENT_CURRMBNR, //!< Insert the current macrloblock number for a slice. + + /* !< Insert frame_num field (used as ID for ref. pictures in H264) */ + ELEMENT_FRAME_NUM, //!< Insert frame_num field (used as ID for ref. pictures in H264) + /* !< Insert Temporal Reference field (used as ID for ref. pictures in H263) */ + ELEMENT_TEMPORAL_REFERENCE, + ELEMENT_EXTENDED_TR, //!< Insert Extended Temporal Reference field + /*//!< Insert idr_pic_id field (used to distinguish consecutive IDR frames) */ + ELEMENT_IDR_PIC_ID, + /* !< Insert pic_order_cnt_lsb field (used for display ordering in H264) */ + ELEMENT_PIC_ORDER_CNT, + /* !< Insert gob_frame_id field (used for display ordering in H263) */ + ELEMENT_GOB_FRAME_ID, + /* !< Insert vop_time_increment field (used for display ordering in MPEG4) */ + ELEMENT_VOP_TIME_INCREMENT, + /* !< Insert modulo_time_base used in MPEG4 (depends on vop_time_increment_resolution) */ + ELEMENT_MODULO_TIME_BASE, + + ELEMENT_BOTTOM_FIELD, //!< Insert bottom_field flag + ELEMENT_SLICE_NUM, //!< Insert slice num (used for GOB headers in H263) + ELEMENT_MPEG2_SLICE_VERTICAL_POS, //!< Insert slice vertical pos (MPEG2 slice header) + /* !< Insert 1 bit flag indicating if slice is Intra or not (MPEG2 slice header) */ + ELEMENT_MPEG2_IS_INTRA_SLICE, + /* !< Insert 2 bit field indicating if the current header is for a frame picture (11), + * top field (01) or bottom field (10) - (MPEG2 picture header + */ + ELEMENT_MPEG2_PICTURE_STRUCTURE, + /* !< Insert flag indicating whether or not this picture is a reference */ + ELEMENT_REFERENCE, + ELEMENT_ADAPTIVE, //!< Insert reference picture marking + ELEMENT_DIRECT_SPATIAL_MV_FLAG, //!< Insert spatial direct mode flag + ELEMENT_NUM_REF_IDX_ACTIVE, //!< Insert number of active references + ELEMENT_REORDER_L0, //!< Insert reference list 0 reordering + ELEMENT_REORDER_L1, //!< Insert reference list 1 reordering + ELEMENT_TEMPORAL_ID, //!< Insert temporal ID of the picture, used for MVC header + /*!< Insert flag indicating whether or not this picture is an anchor picture */ + ELEMENT_ANCHOR_PIC_FLAG, + + BPH_SEI_NAL_INITIAL_CPB_REMOVAL_DELAY, //!< Insert nal_initial_cpb_removal_delay + /* !< Insert nal_initial_cpb_removal_delay_offset */ + BPH_SEI_NAL_INITIAL_CPB_REMOVAL_DELAY_OFFSET, + PTH_SEI_NAL_CPB_REMOVAL_DELAY, //!< Insert cpb_removal_delay + PTH_SEI_NAL_DPB_OUTPUT_DELAY, //!< Insert dpb_output_delay + + ELEMENT_SLICEWEIGHTEDPREDICTIONSTRUCT, //!< Insert weighted prediciton parameters + ELEMENT_CUSTOM_QUANT, //!< Insert custom quantization values + ELEMENT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Struct describing a partially coded header element + */ +struct mtx_header_element { + enum header_element_type element_type; //!< Element type + /* !< Number of bits of coded data to be inserted */ + unsigned char size; + unsigned char bits; //!< Raw data to be inserted. +}; + +/* + * Struct describing partially coded header parameters + */ +struct mtx_header_params { + unsigned int elements; //!< Number of header elements + /*!< array of element data */ + struct mtx_header_element element_stream[MAX_HEADERSIZEWORDS - 1]; +}; + +/* + * Enum describing threshold values for skipped MB biasing + */ +enum th_skip_scale { + TH_SKIP_0 = 0, //!< Bias threshold for QP 0 to 12 + TH_SKIP_12 = 1, //!< Bias threshold for QP 12 to 24 + TH_SKIP_24 = 2, //!< Bias threshold for QP 24 and above + TH_SKIP_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Struct describing rate control input parameters + */ +struct in_rc_params { + unsigned int mb_per_frm; //!< Number of MBs Per Frame + unsigned int mb_per_bu; //!< Number of MBs Per BU + unsigned short bu_per_frm; //!< Number of BUs Per Frame + + unsigned short intra_period; //!< Intra frame frequency + unsigned short bframes; //!< B frame frequency + + int bits_per_frm; //!< Bits Per Frame + int bits_per_bu; //!< Bits Per BU + + int bit_rate; //!< Bit Rate (bps) + int buffer_size; //!< Size of Buffer in bits + int buffer_size_frames;//!< Size of Buffer in frames, to be used in VCM + int initial_level; //!< Initial Level of Buffer + int initial_delay; //!< Initial Delay of Buffer + + unsigned short frm_skip_disable; //!< Disable Frame skipping + + unsigned char se_init_qp_i; //!< Initial QP for sequence (I frames) + unsigned char se_init_qp_p; //!< Initial QP for sequence (P frames) + unsigned char se_init_qp_b; //!< Initial QP for sequence (B frames) + + unsigned char min_qp; //!< Minimum QP value to use + unsigned char max_qp; //!< Maximum QP value to use + + /* !< Scale Factor used to limit the range + * of arithmetic with high resolutions and bitrates + */ + unsigned char scale_factor; + unsigned short mb_per_row; //!< Number of MBs Per Row + + unsigned short disable_vcm_hardware; //!< Disable using vcm hardware in RC modes. + + union { + struct { + /* !< Rate at which bits are sent from encoder + * to the output after each frame finished encoding + */ + int transfer_rate; + /* !< Disable Scene Change detection */ + unsigned short sc_detect_disable; + /* !< Flag indicating Hierarchical B Pic or Flat mode rate control */ + unsigned short hierarchical_mode; + /* !< Constant used in rate control = + * (GopSize/(BufferSize-InitialLevel))*256 + */ + unsigned int rc_scale_factor; + /* !< Enable movement of slice boundary when Qp is high */ + unsigned short enable_slice_bob; + /* !< Maximum number of rows the slice boundary can be moved */ + unsigned char max_slice_bob; + /* !< Minimum Qp at which slice bobbing should take place */ + unsigned char slice_bob_qp; + } h264; + struct { + unsigned char half_framerate; //!< Half Frame Rate (MP4 only) + unsigned char f_code; //!< F Code (MP4 only) + int bits_pergop; //!< Bits Per GOP (MP4 only) + unsigned short bu_skip_disable; //!< Disable BU skipping + int bits_per_mb; //!< Bits Per MB + unsigned short avg_qp_val; //!< Average QP in Current Picture + unsigned short initial_qp; //!< Initial Quantizer + } other; + } mode; +}; + +/* + * Enum describing MTX firmware version (codec and rate control) + */ +enum img_codec { + IMG_CODEC_NONE = 0, //!< There is no FW in MTX memory + IMG_CODEC_JPEG, //!< JPEG + IMG_CODEC_H264_NO_RC, //!< H264 with no rate control + IMG_CODEC_H264_VBR, //!< H264 variable bitrate + IMG_CODEC_H264_CBR, //!< H264 constant bitrate + IMG_CODEC_H264_VCM, //!< H264 video conferance mode + IMG_CODEC_H263_NO_RC, //!< H263 with no rate control + IMG_CODEC_H263_VBR, //!< H263 variable bitrate + IMG_CODEC_H263_CBR, //!< H263 constant bitrate + IMG_CODEC_MPEG4_NO_RC, //!< MPEG4 with no rate control + IMG_CODEC_MPEG4_VBR, //!< MPEG4 variable bitrate + IMG_CODEC_MPEG4_CBR, //!< MPEG4 constant bitrate + IMG_CODEC_MPEG2_NO_RC, //!< MPEG2 with no rate control + IMG_CODEC_MPEG2_VBR, //!< MPEG2 variable bitrate + IMG_CODEC_MPEG2_CBR, //!< MPEG2 constant bitrate + IMG_CODEC_H264_ERC, //!< H264 example rate control + IMG_CODEC_H263_ERC, //!< H263 example rate control + IMG_CODEC_MPEG4_ERC, //!< MPEG4 example rate control + IMG_CODEC_MPEG2_ERC, //!< MPEG2 example rate control + IMG_CODEC_H264MVC_NO_RC, //!< MVC H264 with no rate control + IMG_CODEC_H264MVC_CBR, //!< MVC H264 constant bitrate + IMG_CODEC_H264MVC_VBR, //!< MVC H264 variable bitrate + IMG_CODEC_H264MVC_ERC, //!< MVC H264 example rate control + IMG_CODEC_H264_ALL_RC, //!< H264 with multiple rate control modes + IMG_CODEC_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Enum describing encoding standard (codec) + */ +enum img_standard { + IMG_STANDARD_NONE = 0, //!< There is no FW in MTX memory + IMG_STANDARD_JPEG, //!< JPEG + IMG_STANDARD_H264, //!< H264 with no rate control + IMG_STANDARD_H263, //!< H263 with no rate control + IMG_STANDARD_MPEG4, //!< MPEG4 with no rate control + IMG_STANDARD_MPEG2, //!< MPEG2 with no rate control + IMG_STANDARD_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Enum describing image surface format types + */ +enum img_format { + IMG_CODEC_420_YUV = 100, //!< Planar Y U V + IMG_CODEC_420_YV12 = 44, //!< YV12 format Data + IMG_CODEC_420_IMC2 = 36, //!< IMC2 format Data + IMG_CODEC_420_PL8 = 47, //!< PL8 format YUV data + IMG_CODEC_420_PL12 = 101, //!< PL12 format YUV data + /* |< PL12 format packed into a single plane (not currently supported by JPEG) */ + IMG_CODEC_420_PL12_PACKED = 25, + /* !< PL21 format packed into a single plane (not currently supported by JPEG) */ + IMG_CODEC_420_PL21_PACKED = 26, + /* !< YUV format 4:2:2 data; start the incrementing auto enumeration + * values after the last ones we have used. + */ + IMG_CODEC_422_YUV = 102, + IMG_CODEC_422_YV12, //!< YV12 format 4:2:2 data + IMG_CODEC_422_PL8, //!< PL8 format 4:2:2 data + IMG_CODEC_422_IMC2, //!< IMC2 format 4:2:2 data + IMG_CODEC_422_PL12, //!< PL12 format 4:2:2 data + IMG_CODEC_Y0UY1V_8888, //!< 4:2:2 YUYV data + IMG_CODEC_Y0VY1U_8888, //!< 4:2:2 YVYU data + IMG_CODEC_UY0VY1_8888, //!< 4:2:2 UYVY data + IMG_CODEC_VY0UY1_8888, //!< 4:2:2 VYUY data + IMG_CODEC_444_YUV, //!< YUV format 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_444_YV12, //!< YV12 format 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_444_PL8, //!< PL8 format 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_444_IMC2, //!< PL8 format 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_444_PL12, //!< PL12 format 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_ABCX, //!< Interleaved 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_XBCA, //!< Interleaved 4:4:4 data (not currently supported by JPEG) + IMG_CODEC_ABC565, //!< Packed 4:4:4 data (not currently supported by JPEG) + + IMG_CODEC_420_PL21, //!< PL21 format YUV data + IMG_CODEC_422_PL21, //!< 4:2:2 PL21 format YUV data + /* !< 4:4:4 PL21 format YUV data (not currently supported by JPEG) */ + IMG_CODEC_444_PL21, + + PVR_SURF_UNSPECIFIED, //!< End of the enum + IMG_CODEC_FORMAT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Enum describing presets for source image colour space conversion + */ +enum img_csc_preset { + IMG_CSC_NONE, //!< No colour space conversion + IMG_CSC_709_TO_601, //!< ITU BT.709 YUV to be converted to ITU BT.601 YUV + IMG_CSC_601_TO_709, //!< ITU BT.601 YUV to be converted to ITU BT.709 YUV + IMG_CSC_RGB_TO_601_ANALOG, //!< RGB to be converted to ITU BT.601 YUV + /* !< RGB to be converted to ITU BT.601 YCbCr for SDTV (reduced scale - 16-235) */ + IMG_CSC_RGB_TO_601_DIGITAL, + /* !< RGB to be converted to ITU BT.601 YCbCr for HDTV (full range - 0-255) */ + IMG_CSC_RGB_TO_601_DIGITAL_FS, + IMG_CSC_RGB_TO_709, //!< RGB to be converted to ITU BT.709 YUV + IMG_CSC_YIQ_TO_601, //!< YIQ to be converted to ITU BT.601 YUV + IMG_CSC_YIQ_TO_709, //!< YIQ to be converted to ITU BT.709 YUV + IMG_CSC_BRG_TO_601, //!< BRG to be converted to ITU BT.601 YUV (for XRGB format) + IMG_CSC_RBG_TO_601, //!< RBG to be converted to ITU BT.601 YUV (for XBGR format) + IMG_CSC_BGR_TO_601, //!< BGR to be converted to ITU BT.601 YUV (for BGRX format) + IMG_CSC_UYV_TO_YUV, //!< UYV to be converted to YUV (BT.601 or BT.709) + IMG_CSC_PRESETS, //!< End of the enum + IMG_CSC_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * GOP structure information + */ +#define SHIFT_GOP_FRAMETYPE (0) +#define MASK_GOP_FRAMETYPE (0x3 << SHIFT_GOP_FRAMETYPE) +#define SHIFT_GOP_REFERENCE (2) +#define MASK_GOP_REFERENCE (0x1 << SHIFT_GOP_REFERENCE) +#define SHIFT_GOP_POS (3) +#define MASK_GOP_POS (0x1f << SHIFT_GOP_POS) +#define SHIFT_GOP_REF0 (0 + 8) +#define MASK_GOP_REF0 (0xf << SHIFT_GOP_REF0) +#define SHIFT_GOP_REF1 (4 + 8) +#define MASK_GOP_REF1 (0xf << SHIFT_GOP_REF1) + +/* + * Frame types + */ +enum img_frame_type { + IMG_INTRA_IDR = 0, + IMG_INTRA_FRAME, + IMG_INTER_P, + IMG_INTER_B, + IMG_INTER_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Motion vector calculation register settings + */ +struct img_mv_settings { + unsigned int mv_calc_below; + unsigned int mv_calc_colocated; + unsigned int mv_calc_config; +}; + +/* + * Frame template types + */ +enum img_frame_template_type { + IMG_FRAME_IDR = 0, + IMG_FRAME_INTRA, + IMG_FRAME_INTER_P, + IMG_FRAME_INTER_B, + IMG_FRAME_INTER_P_IDR, + IMG_FRAME_UNDEFINED, + IMG_FRAME_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Rate control modes + */ +enum img_rcmode { + IMG_RCMODE_NONE = 0, + IMG_RCMODE_CBR, + IMG_RCMODE_VBR, + IMG_RCMODE_ERC, // Example Rate Control + IMG_RCMODE_VCM, + IMG_RCMODE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Video Conferencing Mode (VCM) rate control method's sub modes + */ +enum img_rc_vcm_mode { + IMG_RC_VCM_MODE_DEFAULT = 0, + IMG_RC_VCM_MODE_CFS_NONIFRAMES, + IMG_RC_VCM_MODE_CFS_ALLFRAMES, + IMG_RC_VCM_MODE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Weighted prediction values + */ +struct weighted_prediction_values { + unsigned char frame_type; + unsigned char weighted_pred_flag; // Corresponds to field in the pps + unsigned char weighted_bipred_idc; + unsigned int luma_log2_weight_denom; + unsigned int chroma_log2_weight_denom; + /* Y, Cb, Cr Support for 2 ref pictures on P, or 1 pic in each direction on B. */ + unsigned char weight_flag[3][2]; + int weight[3][2]; + int offset[3][2]; +}; + +enum weighted_bipred_idc { + WBI_NONE = 0x0, + WBI_EXPLICIT, + WBI_IMPLICIT, + WBI_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Registers required to configure input scaler + */ +struct img_vxe_scaler_setup { + unsigned int input_scaler_control; + unsigned int scaler_input_size_reg; + unsigned int scaler_crop_reg; + unsigned int scaler_pitch_reg; + unsigned int scaler_control; + unsigned int hor_scaler_coeff_regs[4]; + unsigned int ver_scaler_coeff_regs[4]; +}; + +/* + * Registers required to configure input Colour Space conversion + */ +struct img_vxe_csc_setup { + unsigned int csc_source_y[3]; + unsigned int csc_output_clip[2]; + unsigned int csc_source_cbcr[3]; +}; + +/* + * SETVIDEO & GETVIDEO - Video encode context + */ +struct img_mtx_video_context { + /* // keep this at the top as it has alignment issues */ + unsigned long long clock_div_bitrate; + unsigned int width_in_mbs; //!< target output width + unsigned int picture_height_in_mbs; //!< target output height + unsigned int tmp_reconstructed[MAX_PIC_NODES]; + unsigned int reconstructed[MAX_PIC_NODES]; + unsigned int colocated[MAX_PIC_NODES]; + unsigned int mv[MAX_MV]; + unsigned int inter_view_mv[2]; + /* !< Send debug information from Register CRCs to Host with the coded buffer */ + unsigned int debug_crcs; + unsigned int writeback_regions[WB_FIFO_SIZE]; //!< Data section + unsigned int initial_cpb_removal_delayoffset; + unsigned int max_buffer_mult_clock_div_bitrate; + unsigned int sei_buffering_period_template; + unsigned int sei_picture_timing_template; + unsigned short enable_mvc; + unsigned short mvc_view_idx; + unsigned int slice_params_templates[5]; + unsigned int pichdr_templates[4]; + unsigned int seq_header; + unsigned int subset_seq_header; + unsigned short no_sequence_headers; + + /* !< Slice map of the source picture */ + unsigned int slice_map[MAX_SOURCE_SLOTS_SL]; + unsigned int flat_gop_struct; //!< Address of Flat MiniGop structure + unsigned char weighted_prediction_enabled; + unsigned char mtx_weighted_implicit_bi_pred; + unsigned int weighted_prediction_virt_addr[MAX_SOURCE_SLOTS_SL]; + /* !< Address of hierarchical MiniGop structure */ + unsigned int hierar_gop_struct; + /* Output Parameters of the First Pass */ + unsigned int firstpass_out_param_addr[MAX_SOURCE_SLOTS_SL]; + /* !< Selectable Output Best MV Parameters data of the First Pass */ + unsigned int firstpass_out_best_multipass_param_addr[MAX_SOURCE_SLOTS_SL]; + /* !< Input Parameters to the second pass */ + unsigned int mb_ctrl_in_params_addr[MAX_SOURCE_SLOTS_SL]; + /* !< Strides of source Y data and chroma data */ + unsigned int pic_row_stride_bytes; + /* !< Picture level parameters (supplied by driver) */ + unsigned int above_params[TOPAZHP_MAX_NUM_PIPES]; + unsigned int idr_period; + unsigned int intra_loop_cnt; + unsigned int bframe_count; + unsigned char hierarchical; + /* !< Only used in MPEG2, 2 bit field (0 = 8 bit, 1 = 9 bit, 2 = 10 bit and 3=11 bit + * precision). Set to zero for other encode standards. + */ + unsigned char mpeg2_intra_dc_precision; + unsigned char pic_on_level[MAX_REF_LEVELS]; + unsigned int vop_time_resolution; + unsigned short kick_size; //!< Number of Macroblocks per kick + unsigned short kicks_per_bu; //!< Number of kicks per BU + unsigned short kicks_per_picture; //!< Number of kicks per picture + struct img_mv_settings mv_settings_idr; + struct img_mv_settings mv_settings_non_b[MAX_BFRAMES + 1]; + unsigned int mv_settings_b_table; + unsigned int mv_settings_hierarchical; + enum img_format format; //!< Pixel format of the source surface + enum img_standard standard; //!< Encoder standard (H264 / H263 / MPEG4 / JPEG) + enum img_rcmode rc_mode; //!< RC flavour + enum img_rc_vcm_mode rc_vcm_mode; //!< RC VCM flavour + /* !< RC VCM maximum frame size percentage allowed to exceed in CFS */ + unsigned int rc_cfs_max_margin_perc; + unsigned char first_pic; + unsigned char is_interlaced; + unsigned char top_field_first; + unsigned char arbitrary_so; + unsigned char output_reconstructed; + unsigned char disable_bit_stuffing; + unsigned char insert_hrd_params; + unsigned char max_slices_per_picture; + unsigned int f_code; + /* Contents Adaptive Rate Control parameters*/ + unsigned int jmcomp_rc_reg0; + unsigned int jmcomp_rc_reg1; + /* !< Value to use for MVClip_Config register */ + unsigned int mv_clip_config; + /* !< Value to use for Predictor combiner register */ + unsigned int pred_comb_control; + /* !< Value to use for LRITC_Cache_Chunk_Config register */ + unsigned int lritc_cache_chunk_config; + /* !< Value to use for IPEVectorClipping register */ + unsigned int ipe_vector_clipping; + /* !< Value to use for H264CompControl register */ + unsigned int h264_comp_control; + /* !< Value to use for H264CompIntraPredMode register */ + unsigned int h264_comp_intra_pred_modes; + /* !< Value to use for IPCM_0 Config register */ + unsigned int ipcm_0_config; + /* !< Value to use for IPCM_1 Config register */ + unsigned int ipcm_1_config; + /* !< Value to use for SPEMvdClipRange register */ + unsigned int spe_mvd_clip_range; + /* !< Value to use for MB_HOST_CONTROL register */ + unsigned int mb_host_ctrl; + /* !< Value for the CR_DB_DISABLE_DEBLOCK_IDC register */ + unsigned int deblock_ctrl; + /* !< Value for the CR_DB_DISABLE_DEBLOCK_IDC register */ + unsigned int skip_coded_inter_intra; + unsigned int vlc_control; + /* !< Slice control register value. Configures the size of a slice */ + unsigned int vlc_slice_control; + /* !< Slice control register value. Configures the size of a slice */ + unsigned int vlc_slice_mb_control; + /* !< Chroma QP offset to use (when PPS id = 0)*/ + unsigned short cqp_offset; + unsigned char coded_header_per_slice; + unsigned char initial_qp_i; //!< Initial QP I frames + unsigned char initial_qp_p; //!< Initial QP P frames + unsigned char initial_qp_b; //!< Initial QP B frames + unsigned int first_pic_flags; + unsigned int non_first_pic_flags; + unsigned char mc_adaptive_rounding_disable; +#define AR_REG_SIZE 18 +#define AR_DELTA_SIZE 7 + unsigned short mc_adaptive_rounding_offsets[AR_REG_SIZE][4]; + short mc_adaptive_rounding_offsets_delta[AR_DELTA_SIZE][4]; + /* !< Reconstructed address to allow host picture management */ + unsigned int patched_recon_address; + /* !< Reference 0 address to allow host picture management */ + unsigned int patched_ref0_address; + /* !< Reference 1 address to allow host picture management */ + unsigned int patched_ref1_address; + unsigned int ltref_header[MAX_SOURCE_SLOTS_SL]; + signed char slice_header_slot_num; + unsigned char recon_is_longterm; + unsigned char ref0_is_longterm; + unsigned char ref1_is_longterm; + unsigned char ref_spacing; + unsigned char fw_num_pipes; + unsigned char fw_first_pipe; + unsigned char fw_last_pipe; + unsigned char fw_pipes_to_use_flags; +#if SECURE_IO_PORTS + unsigned int secure_io_control; +#endif + struct img_vxe_scaler_setup scaler_setup; + struct img_vxe_csc_setup csc_setup; + + struct in_rc_params in_params; +}; + +/* + * PICMGMT - Command sub-type + */ +enum img_picmgmt_type { + IMG_PICMGMT_REF_TYPE = 0, + IMG_PICMGMT_GOP_STRUCT, + IMG_PICMGMT_SKIP_FRAME, + IMG_PICMGMT_EOS, + IMG_PICMGMT_FLUSH, + IMG_PICMGMT_QUANT, + IMG_PICMGMT_STRIDE, + IMG_PICMGMT_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * MTX- > host message structure + */ +struct img_writeback_msg { + unsigned int cmd_word; + union { + struct { + unsigned int data; + unsigned int extra_data; + unsigned int writeback_val; + }; + unsigned int coded_package_consumed_idx; + }; +}; + +/* + * PROVIDE_SOURCE_BUFFER - Details of the source picture buffer + */ +struct img_source_buffer_params { + /* !< Host context value. Keep at start for alignment. */ + unsigned long long host_context; + unsigned int phys_addr_y_plane_field_0; //!< Source pic phys addr (Y plane, Field 0) + unsigned int phys_addr_u_plane_field_0; //!< Source pic phys addr (U plane, Field 0) + unsigned int phys_addr_v_plane_field_0; //!< Source pic phys addr (V plane, Field 0) + unsigned int phys_addr_y_plane_field_1; //!< Source pic phys addr (Y plane, Field 1) + unsigned int phys_addr_u_plane_field_1; //!< Source pic phys addr (U plane, Field 1) + unsigned int phys_addr_v_plane_field_1; //!< Source pic phys addr (V plane, Field 1) + /* !< Number of frames in the stream (incl. skipped) */ + unsigned char display_order_num; + unsigned char slot_num; //!< Source slot number + unsigned char reserved1; + unsigned char reserved2; +}; + +/* + * Struct describing input parameters to encode a video slice + */ +struct slice_params { + unsigned int flags; //!< Flags for slice encode + + /* Config registers. These are passed straight + * through from drivers to hardware. + */ + unsigned int slice_config; //!< Value to use for Slice Config register + unsigned int ipe_control; //!< Value to use for IPEControl register + /* !MTX = IsLinkedList, list segment + * (CB memory) size, number of list segments per coded buffer + */ + unsigned int coded_buffer_info; + + // PAD TO 64 BYTES + unsigned int padding[16 - MAX_CODED_BUFFERS_PER_PACKAGE_FW - 2]; +}; + +/* + * Contents of the coded data buffer header feedback word + */ +#define SHIFT_CODED_FIRST_BU (24) +#define MASK_CODED_FIRST_BU (0xFFU << SHIFT_CODED_FIRST_BU) +#define SHIFT_CODED_SLICE_NUM (16) +#define MASK_CODED_SLICE_NUM (0xFF << SHIFT_CODED_SLICE_NUM) +#define SHIFT_CODED_STORAGE_FRAME_NUM (14) +#define MASK_CODED_STORAGE_FRAME_NUM (0x03 << SHIFT_CODED_STORAGE_FRAME_NUM) +#define SHIFT_CODED_ENTIRE_FRAME (12) +#define MASK_CODED_ENTIRE_FRAME (0x01 << SHIFT_CODED_ENTIRE_FRAME) +#define SHIFT_CODED_IS_SKIPPED (11) +#define MASK_CODED_IS_SKIPPED (0x01 << SHIFT_CODED_IS_SKIPPED) +#define SHIFT_CODED_IS_CODED (10) +#define MASK_CODED_IS_CODED (0x01 << SHIFT_CODED_IS_CODED) +#define SHIFT_CODED_RECON_IDX (6) +#define MASK_CODED_RECON_IDX (0x0F << SHIFT_CODED_RECON_IDX) +#define SHIFT_CODED_SOURCE_SLOT (2) +#define MASK_CODED_SOURCE_SLOT (0x0F << SHIFT_CODED_SOURCE_SLOT) +#define SHIFT_CODED_FRAME_TYPE (0) +#define MASK_CODED_FRAME_TYPE (0x03 << SHIFT_CODED_FRAME_TYPE) + +/* + * Contents of the coded data buffer header extra feedback word + */ +#define SHIFT_CODED_SLICES_SO_FAR (24) +#define MASK_CODED_SLICES_SO_FAR (0xFFU << SHIFT_CODED_SLICES_SO_FAR) + +#define SHIFT_CODED_SLICES_IN_BUFFER (16) +#define MASK_CODED_SLICES_IN_BUFFER (0xFF << SHIFT_CODED_SLICES_IN_BUFFER) + +#define SHIFT_CODED_BUFFER_NUMBER_USED (2) +#define MASK_CODED_BUFFER_NUMBER_USED (0xFF << SHIFT_CODED_BUFFER_NUMBER_USED) + +#define SHIFT_CODED_FIELD (1) +#define MASK_CODED_FIELD (0x01 << SHIFT_CODED_FIELD) + +#define SHIFT_CODED_PATCHED_RECON (0) +#define MASK_CODED_PATCHED_RECON (0x01 << SHIFT_CODED_PATCHED_RECON) + +#endif /* _TOPAZSCFWIF_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/fw_headers/vxe_common.h b/drivers/media/platform/vxe-vxd/encoder/fw_headers/vxe_common.h --- a/drivers/media/platform/vxe-vxd/encoder/fw_headers/vxe_common.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/fw_headers/vxe_common.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _VXECOMMON_H_ +#define _VXECOMMON_H_ + +#include "topazscfwif.h" +#include "../common/vid_buf.h" + +/* + * Enum describing buffer lock status + */ +enum lock_status { + BUFFER_FREE = 1, //!< Buffer is not locked + HW_LOCK, //!< Buffer is locked by hardware + SW_LOCK, //!< Buffer is locked by software + NOTDEVICEMEMORY, //!< Buffer is not a device memory buffer + LOCK_ST_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Struct describing a data buffer + */ +struct img_buffer { + struct vidio_ddbufinfo mem_info; //!< Pointer to the memory handle for the buffer + enum lock_status lock; //!< Lock status for the buffer + unsigned int size; //!< Size in bytes of the buffer + unsigned int bytes_written; //!< Number of bytes written into buffer +}; + +/* + * Struct describing a coded data buffer + */ +struct img_coded_buffer { + struct vidio_ddbufinfo mem_info; //!< Pointer to the memory handle for the buffer + enum lock_status lock; //!< Lock status for the buffer + unsigned int size; //!< Size in bytes of the buffer + unsigned int bytes_written; //!< Number of bytes written into buffer +}; + +struct coded_info { + struct img_buffer *code_package_fw_buffer; + struct coded_package_dma_info *coded_package_fw; +}; + +// This structure is used by the Drivers +struct coded_package_host { + struct coded_info mtx_info; + /* Array of pointers to buffers */ + struct img_coded_buffer *coded_buffer[MAX_CODED_BUFFERS_PER_PACKAGE]; + struct img_buffer *header_buffer; + unsigned char num_coded_buffers; + unsigned char busy; +}; + +/* + * Struct describing surface component info + */ +struct img_surf_component_info { + unsigned int step; + unsigned int width; + unsigned int height; + unsigned int phys_width; + unsigned int phys_height; +}; + +/* + * Struct describing a frame + */ +struct img_frame { + struct img_buffer *y_plane_buffer; //!< pointer to the image buffer + struct img_buffer *u_plane_buffer; //!< pointer to the image buffer + struct img_buffer *v_plane_buffer; //!< pointer to the image buffer + unsigned int width_bytes; //!< stride of pBuffer + unsigned int height; //!< height of picture in pBuffer + + unsigned int component_count; //!< number of colour components used + enum img_format format; + unsigned int component_offset[3]; + unsigned int bottom_component_offset[3]; + struct img_surf_component_info component_info[3]; + int y_component_offset; + int u_component_offset; + int v_component_offset; + int field0_y_offset, field1_y_offset; + int field0_u_offset, field1_u_offset; + int field0_v_offset, field1_v_offset; + unsigned short src_y_stride_bytes, src_uv_stride_bytes; + unsigned char imported; +}; + +/* + * Struct describing an array of frames + */ +struct img_frame_array { + unsigned int array_size; //!< Number of frames in array + struct img_frame *frame; //!< Pointer to start of frame array +}; + +/* + * Struct describing list items + */ +struct list_item { + struct list_item *next; //!< Next item in the list + void *data; //!< pointer to list item data +}; + +/* + * Struct describing rate control params + */ +struct img_rc_params { + unsigned int bits_per_second; //!< Bit rate + /* !< Transfer rate of encoded data from encoder to the output */ + unsigned int transfer_bits_per_second; + unsigned int initial_qp_i; //!< Initial QP I frames (only field used by JPEG) + unsigned int initial_qp_p; //!< Initial QP P frames (only field used by JPEG) + unsigned int initial_qp_b; //!< Initial QP B frames (only field used by JPEG) + unsigned int bu_size; //!< Basic unit size + unsigned int frame_rate; + unsigned int buffer_size; + unsigned int intra_freq; + short min_qp; + short max_qp; + unsigned char rc_enable; + int initial_level; + int initial_delay; + unsigned short bframes; + unsigned char hierarchical; + + /* !< Enable movement of slice boundary when Qp is high */ + unsigned char enable_slice_bob; + /* !< Maximum number of rows the slice boundary can be moved */ + unsigned char max_slice_bob; + /* !< Minimum Qp at which slice bobbing should take place */ + unsigned char slice_bob_qp; + + signed char qcp_offset; + unsigned char sc_detect_disable; + unsigned int slice_byte_limit; + unsigned int slice_mb_limit; + enum img_rcmode rc_mode; + enum img_rc_vcm_mode rc_vcm_mode; + unsigned int rc_cfs_max_margin_perc; + unsigned char disable_frame_skipping; + unsigned char disable_vcm_hardware; +}; + +/* + * Bit fields for ui32MmuFlags + */ +#define MMU_USE_MMU_FLAG 0x00000001 +#define MMU_EXTENDED_ADDR_FLAG 0x00000004 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/header_gen.c b/drivers/media/platform/vxe-vxd/encoder/header_gen.c --- a/drivers/media/platform/vxe-vxd/encoder/header_gen.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/header_gen.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,1751 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Encoder coded header generation function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include "fw_headers/topazscfwif.h" +#include "fw_headers/defs.h" +#include "header_gen.h" +#include "img_errors.h" +#include "reg_headers/topazhp_core_regs.h" +#include "topaz_api.h" + +#define ELEMENTS_EMPTY 9999 +#define MAXNUMBERELEMENTS 32 +#define _1080P_30FPS (((1920 * 1088) / 256) * 30) + +void insert_element_token(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + enum header_element_type token) +{ + unsigned char offset; + unsigned char *p; + + if (mtx_header->elements != ELEMENTS_EMPTY) { + if (element_pointers[mtx_header->elements]->element_type == + ELEMENT_STARTCODE_RAWDATA || + element_pointers[mtx_header->elements]->element_type == ELEMENT_RAWDATA || + element_pointers[mtx_header->elements]->element_type == + ELEMENT_STARTCODE_MIDHDR) { + /* + * Add a new element aligned to word boundary + * Find RAWBit size in bytes (rounded to word boundary)) + * NumberofRawbits (excluding size of bit count field)+ + * size of the bitcount field + */ + offset = element_pointers[mtx_header->elements]->size + 8 + 31; + offset /= 32; /*Now contains rawbits size in words */ + offset += 1; /*Now contains rawbits+element_type size in words */ + /* Convert to number of bytes (total size of structure + * in bytes, aligned to word boundary). + */ + offset *= 4; + } else { + offset = 4; + } + + mtx_header->elements++; + p = (unsigned char *)element_pointers[mtx_header->elements - 1]; + p += offset; + element_pointers[mtx_header->elements] = (struct mtx_header_element *)p; + } else { + mtx_header->elements = 0; + } + + element_pointers[mtx_header->elements]->element_type = token; + element_pointers[mtx_header->elements]->size = 0; +} + +unsigned int write_upto_8bits_to_elements(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + unsigned int write_bits, unsigned short bit_cnt) +{ + /* This is the core function to write bits/bytes to a header stream, + * it writes them directly to ELEMENT structures. + */ + unsigned char *write_bytes; + unsigned char *size_bits; + union input_value { + unsigned int input16; + unsigned char input8[2]; + } input_val; + + unsigned char out_byte_index; + short shift; + + if (bit_cnt == 0) + return 0; + + /* First ensure that unused bits in ui32WriteBits are zeroed */ + write_bits &= (0x00ff >> (8 - bit_cnt)); + input_val.input16 = 0; + /*Pointer to the bit count field */ + size_bits = &element_pointers[mtx_header->elements]->size; + /*Pointer to the space where header bits are to be written */ + write_bytes = &element_pointers[mtx_header->elements]->bits; + out_byte_index = (size_bits[0] / 8); + + if (!(size_bits[0] & 7)) { + if (size_bits[0] >= 120) { + /*Element maximum bits send to element, time to start a new one */ + mtx_header->elements++; /* Increment element index */ + /*Element pointer set to position of next element (120/8 = 15 bytes) */ + element_pointers[mtx_header->elements] = + (struct mtx_header_element *)&write_bytes[15]; + /*Write ELEMENT_TYPE */ + element_pointers[mtx_header->elements]->element_type = ELEMENT_RAWDATA; + /* Set new element size (bits) to zero */ + element_pointers[mtx_header->elements]->size = 0; + /* Begin writing to the new element */ + write_upto_8bits_to_elements(mtx_header, element_pointers, write_bits, + bit_cnt); + return (unsigned int)bit_cnt; + } + write_bytes[out_byte_index] = 0; /* Beginning a new byte, clear byte */ + } + + shift = (short)((8 - bit_cnt) - (size_bits[0] & 7)); + + if (shift >= 0) { + write_bits <<= shift; + write_bytes[out_byte_index] |= write_bits; + size_bits[0] = size_bits[0] + bit_cnt; + } else { + input_val.input8[1] = (unsigned char)write_bits + 256; + input_val.input16 >>= -shift; + write_bytes[out_byte_index] |= input_val.input8[1]; + + size_bits[0] = size_bits[0] + bit_cnt; + size_bits[0] = size_bits[0] - ((unsigned char)-shift); + input_val.input8[0] = input_val.input8[0] >> (8 + shift); + write_upto_8bits_to_elements(mtx_header, element_pointers, input_val.input8[0], + (unsigned short)-shift); + } + + return (unsigned int)bit_cnt; +} + +unsigned int write_upto_32bits_to_elements(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + unsigned int write_bits, unsigned int bit_cnt) +{ + unsigned int bit_lp; + unsigned int end_byte; + unsigned char bytes[4]; + + for (bit_lp = 0; bit_lp < 4; bit_lp++) { + bytes[bit_lp] = (unsigned char)(write_bits & 255); + write_bits = write_bits >> 8; + } + + end_byte = ((bit_cnt + 7) / 8); + if ((bit_cnt) % 8) + write_upto_8bits_to_elements(mtx_header, element_pointers, bytes[end_byte - 1], + (unsigned char)((bit_cnt) % 8)); + else + write_upto_8bits_to_elements(mtx_header, element_pointers, bytes[end_byte - 1], 8); + + if (end_byte > 1) + for (bit_lp = end_byte - 1; bit_lp > 0; bit_lp--) + write_upto_8bits_to_elements(mtx_header, element_pointers, + bytes[bit_lp - 1], 8); + + return bit_cnt; +} + +void h264_write_bits_startcode_prefix_element(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + unsigned int byte_size) +{ + /* GENERATES THE FIRST ELEMENT OF THE H264_SEQUENCE_HEADER() STRUCTURE */ + unsigned int lp; + /* + * Byte aligned (bit 0) + * (3 bytes in slice header when slice is first in + * a picture without sequence/picture_header before picture + */ + for (lp = 0; lp < byte_size - 1; lp++) + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 8); + /* Byte aligned (bit 32 or 24) */ +} + +unsigned int generate_ue(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, unsigned int val) +{ + unsigned int lp; + unsigned char zeros; + unsigned int chunk; + unsigned int bit_cnter = 0; + + for (lp = 1, zeros = 0; (lp - 1) < val; lp = lp + lp, zeros++) + val = val - lp; + + /* + * zeros = number of preceding zeros required + * Val = value to append after zeros and 1 bit + * Write preceding zeros + */ + for (lp = (unsigned int)zeros; lp + 1 > 8; lp -= 8) + bit_cnter += write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + + /* Write zeros and 1 bit set */ + bit_cnter += + write_upto_8bits_to_elements(mtx_header, element_pointers, (unsigned char)1, + (unsigned char)(lp + 1)); + + /* Write Numeric part */ + while (zeros > 8) { + zeros -= 8; + chunk = (val >> zeros); + bit_cnter += write_upto_8bits_to_elements(mtx_header, element_pointers, + (unsigned char)chunk, 8); + val = val - (chunk << zeros); + } + + bit_cnter += write_upto_8bits_to_elements(mtx_header, + element_pointers, (unsigned char)val, zeros); + + return bit_cnter; +} + +unsigned int generate_se(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, int val) +{ + unsigned int bit_cnter; + unsigned int code_num; + + bit_cnter = 0; + + if (val > 0) + code_num = (unsigned int)(val + val - 1); + else + code_num = (unsigned int)(-val - val); + + bit_cnter = generate_ue(mtx_header, element_pointers, code_num); + + return bit_cnter; +} + +void h264_write_bits_scaling_lists(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_scaling_matrix_params *scaling_matrix, + unsigned char write_8x8) +{ + /* Used by H264_WriteBits_SequenceHeader and H264_WriteBits_PictureHeader */ + unsigned int list, index; + int cur_scale, delta_scale; + + if (!scaling_matrix) { + insert_element_token(mtx_header, element_pointers, ELEMENT_CUSTOM_QUANT); + return; + } + + for (list = 0; list < 6; list++) { + /* seq_scaling_list_present_flag[ui32List] = 1 */ + if (scaling_matrix->list_mask & (1 << list)) { + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + + cur_scale = 8; + for (index = 0; index < 16; index++) { + delta_scale = + ((int)scaling_matrix->scaling_lists4x4[list][index]) - + cur_scale; + cur_scale += delta_scale; + /* delta_scale */ + generate_se(mtx_header, element_pointers, delta_scale); + } + } else { + /* seq_scaling_list_present_flag[ui32List] = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + } + + if (!write_8x8) + return; + + for (; list < 8; list++) { + /* seq_scaling_list_present_flag[ui32List] = 1 */ + if (scaling_matrix->list_mask & (1 << list)) { + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + + cur_scale = 8; + for (index = 0; index < 64; index++) { + delta_scale = + ((int)scaling_matrix->scaling_lists8x8[list - 6][index]) - + cur_scale; + cur_scale += delta_scale; + /* delta_scale */ + generate_se(mtx_header, element_pointers, delta_scale); + } + } else { + /* seq_scaling_list_present_flag[ui32List] = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + } +} + +void h264_write_bits_vui_params(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_vui_params *vui_params) +{ + /* Builds VUI Params for the Sequence Header (only present in the 1st sequence of stream) */ + + if (vui_params->aspect_ratio_info_present_flag == 1) { + /* aspect_ratio_info_present_flag = 1 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->aspect_ratio_info_present_flag, 1); + /* aspect_ratio_idc (8 bits) = vui_params->aspect_ratio_idc in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->aspect_ratio_idc, 8); + + if (vui_params->aspect_ratio_idc == 255) { + write_upto_8bits_to_elements(mtx_header, element_pointers, + (vui_params->sar_width >> 8), 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->sar_width, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, + (vui_params->sar_height >> 8), 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->sar_height, 8); + } + } else { + /* aspect_ratio_info_present_flag = 0 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* overscan_info_present_flag (1 bit) = 0 in Topaz */ + (0 << 3) | + /* video_signal_type_present_flag (1 bit) = 0 in Topaz */ + (0 << 2) | + /* chroma_loc_info_present_flag (1 bit) = 0 in Topaz */ + (0 << 1) | + /* timing_info_present_flag (1 bit) = 1 in Topaz */ + (1), + /* num_units_in_tick (32 bits) = 1 in Topaz */ + 4); + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 8); + + /* time_scale (32 bits) = frame rate */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + write_upto_8bits_to_elements(mtx_header, element_pointers, + (unsigned char)vui_params->time_scale, 8); + /* fixed_frame_rate_flag (1 bit) = 1 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* nal_hrd_parameters_present_flag (1 bit) = 1 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* Definitions for nal_hrd_parameters() contained in VUI structure for Topaz + * cpb_cnt_minus1 ue(v) = 0 in Topaz = 1b + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* bit_rate_scale (4 bits) = 0 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 4); + /* cpb_size_scale (4 bits) = 2 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 2, 4); + /* bit_rate_value_minus1[0] ue(v) = (Bitrate/64)-1 [RANGE:0 to (2^32)-2] */ + generate_ue(mtx_header, element_pointers, vui_params->bit_rate_value_minus1); + /* cpb_size_value_minus1[0] ue(v) = (CPB_Bits_Size/16)- + * 1 where CPB_Bits_Size = 1.5 * Bitrate [RANGE:0 to (2^32)-2] + */ + generate_ue(mtx_header, element_pointers, vui_params->cbp_size_value_minus1); + /* cbr_flag[0] (1 bit) = 0 for VBR, 1 for CBR */ + write_upto_8bits_to_elements(mtx_header, element_pointers, vui_params->cbr, 1); + /*initial_cpb_removal_delay_length_minus1 (5 bits) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->initial_cpb_removal_delay_length_minus1, 5); + /* cpb_removal_delay_length_minus1 (5 bits) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->cpb_removal_delay_length_minus1, 5); + /* dpb_output_delay_length_minus1 (5 bits) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + vui_params->dpb_output_delay_length_minus1, 5); + /* time_offst_length (5 bits) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, vui_params->time_offset_length, + 5); + + /* End of nal_hrd_parameters() */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /* low_delay_hrd_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /* pic_struct_present_flag (1 bit) = 0 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* bitstream_restriction_flag (1 bit) = 1 in Topaz */ + (1 << 1) | + /* motion_vectors_over_pic_boundaries_flag (1 bit) = 1 + * in Topaz; + */ + (1 << 0), + 2); + /* max_bytes_per_pic_denom ue(v) = 0 */ + generate_ue(mtx_header, element_pointers, 0); + /* max_bits_per_mb_denom ue(v) = 0 */ + generate_ue(mtx_header, element_pointers, 0); + /* log2_max_mv_length_horizontal ue(v) = 9(max horizontal vector is 128 integer samples) */ + generate_ue(mtx_header, element_pointers, 9); + /* log2_max_mv_length_vertical ue(v) = 9 (max vertical vecotr is 103 integer samples) */ + generate_ue(mtx_header, element_pointers, 9); + /* num_reorder_frames ue(v) = 0 */ + generate_ue(mtx_header, element_pointers, vui_params->num_reorder_frames); + /* max_dec_frame_buffering ue(v) = 0 */ + generate_ue(mtx_header, element_pointers, vui_params->max_dec_frame_buffering); +} + +void h264_write_bits_sequence_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_sequence_header_params *sh_params, + struct h264_crop_params *crop, + struct h264_scaling_matrix_params *scaling_matrix, + unsigned char aso) +{ + /* calculate some of the VUI parameters here */ + if (sh_params->profile == SH_PROFILE_BP) { + /* for Baseline profile we never re-roder frames */ + sh_params->vui_params.num_reorder_frames = 0; + sh_params->vui_params.max_dec_frame_buffering = sh_params->max_num_ref_frames; + } else { + /* in higher profiles we can do up to 3 level hierarchical B frames */ + if (!sh_params->vui_params.num_reorder_frames) + sh_params->vui_params.num_reorder_frames = sh_params->max_num_ref_frames; + sh_params->vui_params.max_dec_frame_buffering = + max(sh_params->max_num_ref_frames, + sh_params->vui_params.num_reorder_frames); + } + + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, 4); + + /* GENERATES THE FIRST ELEMENT OF THE H264_SEQUENCE_HEADER() STRUCTURE */ + /* + * 4 Byte StartCodePrefix Pregenerated in: H264_WriteBits_StartCodePrefix_Element() + * Byte aligned (bit 32) + * forbidden_zero_bit=0 + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, (0 << 7) | + (0x3 << 5) | /* nal_ref_idc=01 (may be 11) */ + (7), /* nal_unit_type=00111 */ + 8); + + /* Byte aligned (bit 40) */ + switch (sh_params->profile) { + case SH_PROFILE_BP: + /* profile_idc = 8 bits = 66 for BP (PROFILE_IDC_BP) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 66, 8); + + /* Byte aligned (bit 48) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* constraint_set0_flag = 1 for BP constra ints */ + (1 << 7) | + /* constraint_set1_flag = 1 for MP constraints */ + ((aso ? 0 : 1) << 6) | + /* constraint_set2_flag = 1 for EP constra ints */ + (1 << 5) | + /* constraint_set3_flag = 1 + * for level 1b, 0 for others + */ + ((sh_params->level == SH_LEVEL_1B ? 1 : 0) << 4), + /* reserved_zero_4bits = 0 */ + 8); + break; + + case SH_PROFILE_MP: + /* profile_idc = 8 bits = 77 for MP (PROFILE_IDC_MP) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 77, 8); + + /* Byte aligned (bit 48) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* constraint_set0_flag = 0 for no BP constraints */ + (0 << 7) | + /* constraint_set1_flag = 1 for MP constraints */ + (1 << 6) | + /* constraint_set2_flag = 1 for EP constraints */ + (1 << 5) | + /* constraint_set3_flag = 1 + * for level 1b, 0 for others + */ + ((sh_params->level == SH_LEVEL_1B ? 1 : 0) << 4), + /* reserved_zero_4bits = 0 */ + 8); + break; + + case SH_PROFILE_HP: + /* profile_idc = 8 bits = 100 for HP (PROFILE_IDC_HP) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 100, 8); + + /* Byte aligned (bit 48) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* constraint_set0_flag = 0 for no BP constraints */ + (0 << 7) | + /* constraint_set1_flag = 0 for no MP constraints */ + (0 << 6) | + /* constraint_set2_flag = 0 for no EP constraints */ + (0 << 5) | + /* constraint_set3_flag = 0 */ + (0 << 4), + /* reserved_zero_4bits = 0 */ + 8); + break; + + case SH_PROFILE_H444P: + /* profile_idc = 8 bits = 244 for H444P (PROFILE_IDC_H444P) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 244, 8); + + /* Byte aligned (bit 48) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* constraint_set0_flag = 0 for no BP constraints */ + (0 << 7) | + /* constraint_set1_flag = 0 for no MP constraints */ + (0 << 6) | + /* constraint_set2_flag = 0 for no EP constraints */ + (0 << 5) | + /* constraint_set3_flag = 0 */ + (0 << 4), + /* reserved_zero_4bits = 0 */ + 8); + break; + + default: + break; + } + + /* + * Byte aligned (bit 56) + * level_idc should be set to 9 in the sps in case of + * level is Level 1B and the profile is High or High 4:4:4 Profile + */ + if (sh_params->profile == SH_PROFILE_HP || sh_params->profile == SH_PROFILE_H444P) + /* level_idc (8 bits) = 9 for 1b, 10xlevel for others */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->level == SH_LEVEL_1B) ? 9 : + (unsigned char)sh_params->level, 8); + + else + /* level_idc (8 bits) = 11 for 1b, 10xlevel for others */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->level == SH_LEVEL_1B) ? 11 : + (unsigned char)sh_params->level, 8); + + generate_ue(mtx_header, element_pointers, 0); /* seq_parameter_set_id = 0 */ + + if (sh_params->profile == SH_PROFILE_HP || sh_params->profile == SH_PROFILE_H444P) { + generate_ue(mtx_header, element_pointers, 1); /* chroma_format_idc = 1 */ + /* bit_depth_luma_minus8 = 0 */ + generate_ue(mtx_header, element_pointers, 0); + /* bit_depth_chroma_minus8 = 0 */ + generate_ue(mtx_header, element_pointers, 0); + + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* qpprime_y_zero_transform_bypass_flag = 1 + * if lossless + */ + sh_params->is_lossless ? 1 : 0, 1); + + if (sh_params->use_default_scaling_list || + sh_params->seq_scaling_matrix_present_flag) { + /* seq_scaling_matrix_present_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + if (!sh_params->use_default_scaling_list) { + h264_write_bits_scaling_lists(mtx_header, element_pointers, + scaling_matrix, TRUE); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + } else { + /* seq_scaling_list_present_flag[i] = 0; 0 < i < 8 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + } + } else { + /* seq_scaling_matrix_present_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + } + + generate_ue(mtx_header, element_pointers, 1); /* log2_max_frame_num_minus4 = 1 */ + generate_ue(mtx_header, element_pointers, 0); /* pic_order_cnt_type = 0 */ + /* log2_max_pic_order_cnt_Isb_minus4 = 2 */ + generate_ue(mtx_header, element_pointers, sh_params->log2_max_pic_order_cnt - 4); + /*num_ref_frames ue(2), typically 2 */ + generate_ue(mtx_header, element_pointers, sh_params->max_num_ref_frames); + + /* Bytes aligned (bit 72) */ + /* gaps_in_frame_num_value_allowed_Flag - (1 bit) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->gaps_in_frame_num_value), 1); + + /* + * GENERATES THE SECOND, VARIABLE LENGTH, ELEMENT OF THE H264_SEQUENCE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: xx + */ + /*pic_width_in_mbs_minus1: ue(v) from 10 to 44 (176 to 720 pixel per row) */ + generate_ue(mtx_header, element_pointers, sh_params->width_in_mbs_minus1); + /* pic_height_in_maps_units_minus1: + * ue(v) Value from 8 to 35 (144 to 576 pixels per column) + */ + generate_ue(mtx_header, element_pointers, sh_params->height_in_maps_units_minus1); + /* We don't know the alignment at this point, so will have to use bit writing functions */ + /* frame_mb_only_flag 1=frame encoding, 0=field encoding */ + write_upto_8bits_to_elements(mtx_header, element_pointers, sh_params->frame_mbs_only_flag, + 1); + + if (!sh_params->frame_mbs_only_flag) /* in the case of interlaced encoding */ + /* mb_adaptive_frame_field_flag = 0 in Topaz(field encoding at the sequence level) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + + /* direct_8x8_inference_flag=1 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + + if (crop->clip) { + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + generate_ue(mtx_header, element_pointers, crop->left_crop_offset); + generate_ue(mtx_header, element_pointers, crop->right_crop_offset); + generate_ue(mtx_header, element_pointers, crop->top_crop_offset); + generate_ue(mtx_header, element_pointers, crop->bottom_crop_offset); + } else { + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + /* + * GENERATES THE THIRD ELEMENT OF THE H264_SEQUENCE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: xx + */ + /* vui_parameters_present_flag (VUI only in 1st sequence of stream) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->vui_params_present), + 1); + if (sh_params->vui_params_present > 0) + h264_write_bits_vui_params(mtx_header, element_pointers, &sh_params->vui_params); + + /* Finally we need to align to the next byte */ + /* Tell MTX to insert the byte align field (we don't know + * final stream size for alignment at this point) + */ + insert_element_token(mtx_header, element_pointers, ELEMENT_INSERTBYTEALIGN_H264); +} + +/* + * Prepare an H264 SPS in a form for the MTX to encode into a bitstream. + */ +void h264_prepare_sequence_header(struct mtx_header_params *mtx_header, + unsigned int pic_width_in_mbs, + unsigned int pic_height_in_mbs, + unsigned char vui_params_present, + struct h264_vui_params *params, + struct h264_crop_params *crop, + struct h264_sequence_header_params *sh_params, + unsigned char aso) +{ + struct mtx_header_element *this_element; + struct mtx_header_element *element_pointers[MAXNUMBERELEMENTS]; + + /* + * Builds a sequence, picture and slice header with from the given inputs + * parameters (start of new frame) Essential we initialise our header + * structures before building + */ + mtx_header->elements = ELEMENTS_EMPTY; + this_element = (struct mtx_header_element *)mtx_header->element_stream; + element_pointers[0] = this_element; + + h264_write_bits_sequence_header(mtx_header, element_pointers, sh_params, crop, NULL, aso); + /*Has been used as an index, so need to add 1 for a valid element count */ + mtx_header->elements++; +} + +void h264_write_bits_picture_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_picture_header_params *ph_params, + struct h264_scaling_matrix_params *scaling_matrix) +{ + /* Begin building the picture header element */ + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, 4); + + /* GENERATES THE FIRST (STATIC) ELEMENT OF THE H264_PICTURE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: 18 + * 4 Byte StartCodePrefix Pregenerated in: H264_WriteBits_StartCodePrefix_Element() + * Byte aligned (bit 32) + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (0 << 7) | /* forbidden_zero_bit */ + (1 << 5) | /* nal_ref_idc (2 bits) = 1 */ + (8), /* nal_unit_tpye (5 bits) = 8 */ + 8); + + /* Byte aligned (bit 40) */ + /* pic_parameter_set_id ue(v) */ + generate_ue(mtx_header, element_pointers, ph_params->pic_parameter_set_id); + /* seq_parameter_set_id ue(v) */ + generate_ue(mtx_header, element_pointers, ph_params->seq_parameter_set_id); + + /* entropy_coding_mode_flag (1 bit) 0 for CAVLC */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (ph_params->entropy_coding_mode_flag << 4) | + (0 << 3) | /* pic_order_present_flag (1 bit) = 0 */ + (1 << 2) | /* num_slice_group_minus1 ue(v) = 0 in Topaz */ + (1 << 1) | /* num_ref_idx_l0_active_minus1 ue(v) = 0 in Topaz*/ + (1),/* num_ref_idx_l1_active_minus1 ue(v) = 0 in Topaz */ + 5); + + /* WEIGHTED PREDICTION */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* weighted_pred_flag (1 bit) */ + (ph_params->weighted_pred_flag << 2) | + /* weighted_bipred_flag (2 bits) */ + (ph_params->weighted_bipred_idc), 3); + + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_QP); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + /* + * GENERATES THE SECOND ELEMENT OF THE H264_PICTURE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: 5 + * The following field will be generated as a special case by MTX - so not here + * Generate_se(mtx_header, ph_params->pic_init_qp_minus26); pic_int_qp_minus26 + * se(v) = -26 to 25 in Topaz + */ + generate_se(mtx_header, element_pointers, 0); /* pic_int_qs_minus26 se(v) = 0 in Topaz */ + /* chroma_qp_index_offset se(v) = 0 in Topaz */ + generate_se(mtx_header, element_pointers, ph_params->chroma_qp_index_offset); + /* deblocking_filter_control_present_flag (1 bit) = 1 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, (1 << 2) | + /* constrained_intra_pred_Flag (1 bit) = 0 in Topaz */ + (ph_params->constrained_intra_pred_flag << 1) | + /* redundant_pic_cnt_present_flag (1 bit) = 0 in Topaz */ + (0), + 3); + + if (ph_params->transform_8x8_mode_flag || + ph_params->second_chroma_qp_index_offset != ph_params->chroma_qp_index_offset || + ph_params->pic_scaling_matrix_present_flag) { + /* 8x8 transform flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + ph_params->transform_8x8_mode_flag, 1); + if (ph_params->pic_scaling_matrix_present_flag) { + /* pic_scaling_matrix_present_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + if (!ph_params->use_default_scaling_list) { + h264_write_bits_scaling_lists(mtx_header, element_pointers, + scaling_matrix, + ph_params->transform_8x8_mode_flag); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + } else { + unsigned char scaling_list_size = + ph_params->transform_8x8_mode_flag ? 8 : 6; + + /* pic_scaling_list_present_flag[i] = 0; + * 0 < i < 6 (+ 2 ( +4 for chroma444) for 8x8) + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, + scaling_list_size); + } + } else { + /* pic_scaling_matrix_present_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + /* second_chroma_qp_index_offset se(v) = 0 in Topaz */ + generate_se(mtx_header, element_pointers, ph_params->second_chroma_qp_index_offset); + } + /* Tell MTX to insert the byte align field (we don't know final + * stream size for alignment at this point) + */ + insert_element_token(mtx_header, element_pointers, ELEMENT_INSERTBYTEALIGN_H264); +} + +/* + * Prepare an H264 PPS in a form for the MTX to encode into a bitstream + */ +void h264_prepare_picture_header(struct mtx_header_params *mtx_header, + unsigned char cabac_enabled, + unsigned char transform_8x8, + unsigned char intra_constrained, + signed char cqp_offset, + unsigned char weighted_prediction, + unsigned char weighted_bi_pred, + unsigned char mvc_pps, + unsigned char scaling_matrix, + unsigned char scaling_lists) +{ + /* + * Builds a picture header with from the given inputs parameters (start of new frame) + * Essential we initialise our header structures before building + */ + struct h264_picture_header_params ph_params; + struct mtx_header_element *this_element; + struct mtx_header_element *element_pointers[MAXNUMBERELEMENTS]; + + mtx_header->elements = ELEMENTS_EMPTY; + this_element = (struct mtx_header_element *)mtx_header->element_stream; + element_pointers[0] = this_element; + + ph_params.pic_parameter_set_id = mvc_pps ? MVC_PPS_ID : 0; + ph_params.seq_parameter_set_id = mvc_pps ? MVC_SPS_ID : 0; + ph_params.entropy_coding_mode_flag = cabac_enabled ? 1 : 0; + ph_params.weighted_pred_flag = weighted_prediction; + ph_params.weighted_bipred_idc = weighted_bi_pred; + ph_params.chroma_qp_index_offset = cqp_offset; + ph_params.constrained_intra_pred_flag = intra_constrained ? 1 : 0; + ph_params.transform_8x8_mode_flag = transform_8x8 ? 1 : 0; + ph_params.pic_scaling_matrix_present_flag = scaling_matrix ? 1 : 0; + ph_params.use_default_scaling_list = !scaling_lists; + ph_params.second_chroma_qp_index_offset = cqp_offset; + + h264_write_bits_picture_header(mtx_header, element_pointers, &ph_params, NULL); + /*Has been used as an index, so need to add 1 for a valid element count */ + mtx_header->elements++; +} + +/* SEI_INSERTION */ +void h264_write_bits_aud_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers) +{ + /* Essential we insert the element before we try to fill it! */ + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + /* 00 00 00 01 start code prefix */ + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, 4); + /* AUD nal_unit_type = 09 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 9, 8); + + /* primary_pic_type u(3) 0=I slice, 1=P or I slice, 2=P,B or I slice */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 2, 3); + /* rbsp_trailing_bits */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1 << 4, 5); + + /* Write terminator */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0x80, 8); +} + +void h264_prepare_aud_header(struct mtx_header_params *mtx_header) +{ + /* Essential we initialise our header structures before building */ + struct mtx_header_element *this_element; + struct mtx_header_element *element_pointers[MAXNUMBERELEMENTS]; + + mtx_header->elements = ELEMENTS_EMPTY; + this_element = (struct mtx_header_element *)mtx_header->element_stream; + element_pointers[0] = this_element; + + h264_write_bits_aud_header(mtx_header, element_pointers); + /*Has been used as an index, so need to add 1 for a valid element count */ + mtx_header->elements++; +} + +static void insert_prefix_nal_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_slice_header_params *slh_params, + unsigned char cabac_enabled) +{ + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + /*Can be 3 or 4 bytes - always 4 bytes in our implementations */ + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, + slh_params->startcode_prefix_size_bytes); + + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* forbidden_zero_bit */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_REFERENCE); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* nal unit type */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 14, 5); + /* SVC extension flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /* non_idr_flag flag */ + if (slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE) + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + else + /* non_idr_flag flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* priority_id flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 6); + /* view_id flag */ + write_upto_32bits_to_elements(mtx_header, element_pointers, 0, 10); + /* temporal_id flag */ + insert_element_token(mtx_header, element_pointers, ELEMENT_TEMPORAL_ID); + /* anchor_pic_flag */ + insert_element_token(mtx_header, element_pointers, ELEMENT_ANCHOR_PIC_FLAG); + + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* interview flag */ + if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + else + /* interview flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* reserved one bit */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); +} + +/* helper function to start new raw data block */ +static unsigned char start_next_rawdata_element = FALSE; +static void check_start_rawdata_element(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers) +{ + if (start_next_rawdata_element) { + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + start_next_rawdata_element = FALSE; + } +} + +void h264_write_bits_extension_slice_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_slice_header_params *slh_params, + unsigned char cabac_enabled, + unsigned char is_idr) +{ + start_next_rawdata_element = FALSE; + + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + /*Can be 3 or 4 bytes - always 4 bytes in our implementations */ + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, + slh_params->startcode_prefix_size_bytes); + + /* GENERATES THE FIRST ELEMENT OF THE H264_SLICE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: 8 + + * StartCodePrefix Pregenerated in: Build_H264_4Byte_StartCodePrefix_Element() + * (4 or 3 bytes) (3 bytes when slice is first in a picture without + * sequence/picture_header before picture Byte aligned (bit 32 or 24) + * NOTE: Slice_Type and Frame_Type are always the same, hence slice_frame_type + */ + /* forbidden_zero_bit */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_REFERENCE); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* nal_unit_type for coded_slice_extension */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 20, 5); + /* SVC extension flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + + if (slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE) + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /* non_idr_flag flag */ + else if ((slh_params->slice_frame_type == SLHP_P_SLICEFRAME_TYPE) && is_idr) + /* non_idr_flag flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + else + /* non_idr_flag flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* priority_id flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 6); + /* view_id = hardcoded to 1 for dependent view */ + write_upto_32bits_to_elements(mtx_header, element_pointers, 1, 10); + /* temporal_id flag */ + insert_element_token(mtx_header, element_pointers, ELEMENT_TEMPORAL_ID); + /* anchor_pic_flag */ + insert_element_token(mtx_header, element_pointers, ELEMENT_ANCHOR_PIC_FLAG); + + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* interview flag is always FALSE for dependent frames */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /* reserved one bit */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + + /* slice header */ + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_CURRMBNR); + + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + /* GENERATES THE SECOND ELEMENT OF THE H264_SLICE_HEADER() STRUCTURE */ + + generate_ue(mtx_header, element_pointers, + (unsigned int)((slh_params->slice_frame_type == + SLHP_IDR_SLICEFRAME_TYPE) ? SLHP_I_SLICEFRAME_TYPE : + slh_params->slice_frame_type)); + /*slice_type ue(v): 0 for P-slice, 1 for B-slice, 2 for I-slice */ + + /* pic_parameter_set_id = 1 for dependent view */ + generate_ue(mtx_header, element_pointers, 1); + + /* Insert token to tell MTX to insert frame_num */ + insert_element_token(mtx_header, element_pointers, ELEMENT_FRAME_NUM); + start_next_rawdata_element = TRUE; + + if (slh_params->pic_interlace || + slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE) { + /* interlaced encoding */ + if (slh_params->pic_interlace) { + check_start_rawdata_element(mtx_header, element_pointers); + /* field_pic_flag = 1 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* Insert token to tell MTX to insert BOTTOM_FIELD flag if required */ + insert_element_token(mtx_header, element_pointers, ELEMENT_BOTTOM_FIELD); + start_next_rawdata_element = TRUE; + } + } + + if (slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE || (is_idr)) { + check_start_rawdata_element(mtx_header, element_pointers); + /* idr_pic_id ue(v) = 0 (1b) in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + } + /* Insert token to tell MTX to insert pic_order_cnt_lsb */ + insert_element_token(mtx_header, element_pointers, ELEMENT_PIC_ORDER_CNT); + start_next_rawdata_element = TRUE; + + if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) + /* Insert token to tell MTX to insert direct_spatial_mv_pred_flag */ + insert_element_token(mtx_header, element_pointers, ELEMENT_DIRECT_SPATIAL_MV_FLAG); + + if (slh_params->slice_frame_type == SLHP_P_SLICEFRAME_TYPE) { + insert_element_token(mtx_header, element_pointers, ELEMENT_NUM_REF_IDX_ACTIVE); + start_next_rawdata_element = TRUE; + } else if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) { + check_start_rawdata_element(mtx_header, element_pointers); + /* num_ref_idx_active_override_flag (1 bit) = 0 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + /* reference picture list modification */ + if (slh_params->slice_frame_type != SLHP_I_SLICEFRAME_TYPE && + slh_params->slice_frame_type != SLHP_IDR_SLICEFRAME_TYPE) { + /* Insert token to tell MTX to insert BOTTOM_FIELD flag if required */ + insert_element_token(mtx_header, element_pointers, ELEMENT_REORDER_L0); + start_next_rawdata_element = TRUE; + } + + if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) { + check_start_rawdata_element(mtx_header, element_pointers); + /* ref_pic_list_ordering_flag_l1 (1 bit) = 0, no reference + * picture ordering in Topaz + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + if (slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE || (is_idr)) { + check_start_rawdata_element(mtx_header, element_pointers); + /* no_output_of_prior_pics_flag (1 bit) = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + /* long_term_reference_flag (1 bit) = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } else { + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_ADAPTIVE); + start_next_rawdata_element = TRUE; + } + + if (cabac_enabled && (slh_params->slice_frame_type == SLHP_P_SLICEFRAME_TYPE || + slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE)) { + check_start_rawdata_element(mtx_header, element_pointers); + /* hard code cabac_init_idc value of 0 */ + generate_ue(mtx_header, element_pointers, 0); + } + + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_SQP); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + /* GENERATES ELEMENT OF THE H264_SLICE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: 11 + */ + /*disable_deblocking_filter_idc ue(v) = 2? */ + generate_ue(mtx_header, element_pointers, slh_params->disable_deblocking_filter_idc); + if (slh_params->disable_deblocking_filter_idc != 1) { + /*slice_alpha_c0_offset_div2 se(v) = 0 (1b) in Topaz */ + generate_se(mtx_header, element_pointers, slh_params->deb_alpha_offset_div2); + /*slice_beta_offset_div2 se(v) = 0 (1b) in Topaz */ + generate_se(mtx_header, element_pointers, slh_params->deb_beta_offset_div2); + } + /* + * num_slice_groups_minus1 ==0 in Topaz, so no slice_group_change_cycle field here + * no byte alignment at end of slice headers + */ +} + +void h264_write_bits_slice_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_slice_header_params *slh_params, + unsigned char cabac_enabled, unsigned char is_idr) +{ + start_next_rawdata_element = FALSE; + if (slh_params->mvc_view_idx == (unsigned short)(NON_MVC_VIEW)) { + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + } else if (slh_params->mvc_view_idx == MVC_BASE_VIEW_IDX) { + insert_prefix_nal_header(mtx_header, element_pointers, slh_params, cabac_enabled); + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_MIDHDR); + } else { + /*Insert */ + h264_write_bits_extension_slice_header(mtx_header, element_pointers, + slh_params, cabac_enabled, is_idr); + return; + } + + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, + /*Can be 3 or 4 bytes - always 4 + * bytes in our implementations + */ + slh_params->startcode_prefix_size_bytes); + + /* GENERATES THE FIRST ELEMENT OF THE H264_SLICE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: 8 + + * StartCodePrefix Pregenerated in: Build_H264_4Byte_StartCodePrefix_Element() + * (4 or 3 bytes) (3 bytes when slice is first in a picture without + * sequence/picture_header before picture Byte aligned (bit 32 or 24) + * NOTE: Slice_Type and Frame_Type are always the same, hence slice_frame_type + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); /* forbidden_zero_bit */ + + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_REFERENCE); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + write_upto_8bits_to_elements(mtx_header, element_pointers, + /* nal_unit_tpye (5 bits) = I-frame IDR, and 1 for rest */ + ((slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE ? + 5 : 1)), 5); + + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_CURRMBNR); + + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + /* GENERATES THE SECOND ELEMENT OF THE H264_SLICE_HEADER() STRUCTURE */ + + generate_ue(mtx_header, element_pointers, + (unsigned int)((slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE) ? + /*slice_type ue(v): 0 for P-slice, 1 for B-slice, 2 for I-slice */ + SLHP_I_SLICEFRAME_TYPE : slh_params->slice_frame_type)); + + if (slh_params->mvc_view_idx != (unsigned short)(NON_MVC_VIEW)) + /* pic_parameter_set_id = 0 */ + generate_ue(mtx_header, element_pointers, slh_params->mvc_view_idx); + else + generate_ue(mtx_header, element_pointers, 0); /* pic_parameter_set_id = 0 */ + /* Insert token to tell MTX to insert frame_num */ + insert_element_token(mtx_header, element_pointers, ELEMENT_FRAME_NUM); + + if (slh_params->pic_interlace || + slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE) { + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* interlaced encoding */ + if (slh_params->pic_interlace) { + /* field_pic_flag = 1 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + /* Insert token to tell MTX to insert BOTTOM_FIELD flag if required */ + insert_element_token(mtx_header, element_pointers, ELEMENT_BOTTOM_FIELD); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + } + + if (slh_params->slice_frame_type == SLHP_IDR_SLICEFRAME_TYPE) + /* idr_pic_id ue(v) */ + insert_element_token(mtx_header, element_pointers, ELEMENT_IDR_PIC_ID); + } + /* Insert token to tell MTX to insert pic_order_cnt_lsb */ + insert_element_token(mtx_header, element_pointers, ELEMENT_PIC_ORDER_CNT); + + if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) + /* Insert token to tell MTX to insert direct_spatial_mv_pred_flag */ + insert_element_token(mtx_header, element_pointers, ELEMENT_DIRECT_SPATIAL_MV_FLAG); + + if (slh_params->slice_frame_type == SLHP_P_SLICEFRAME_TYPE) { + /* Insert token to tell MTX to insert override for number of active references */ + insert_element_token(mtx_header, element_pointers, ELEMENT_NUM_REF_IDX_ACTIVE); + } else if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) { + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + /* num_ref_idx_active_override_flag (1 bit) = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + if (slh_params->slice_frame_type != SLHP_I_SLICEFRAME_TYPE && + slh_params->slice_frame_type != SLHP_IDR_SLICEFRAME_TYPE) { + /* Insert token to tell MTX to insert reference list 0 reordering */ + insert_element_token(mtx_header, element_pointers, ELEMENT_REORDER_L0); + + if (slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE) + /* Insert token to tell MTX to insert reference list 1 reordering */ + insert_element_token(mtx_header, element_pointers, ELEMENT_REORDER_L1); + } + + /* WEIGHTED PREDICTION */ + insert_element_token(mtx_header, element_pointers, ELEMENT_SLICEWEIGHTEDPREDICTIONSTRUCT); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + if (slh_params->reference_picture && slh_params->is_longterm_ref) { + /* adaptive_ref_pic_marking_mode_flag (1 bit) = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + + /* Clear any existing long-term reference */ + /* memory_management_control_operation */ + generate_ue(mtx_header, element_pointers, 5); + + /* Allow a single long-term reference */ + /* memory_management_control_operation */ + generate_ue(mtx_header, element_pointers, 4); + /* max_long_term_frame_idx_plus1 */ + generate_ue(mtx_header, element_pointers, 1); + + /* Set current picture as the long-term reference */ + /* memory_management_control_operation */ + generate_ue(mtx_header, element_pointers, 6); + /* long_term_frame_idx */ + generate_ue(mtx_header, element_pointers, 0); + + /* End */ + /* memory_management_control_operation */ + generate_ue(mtx_header, element_pointers, 0); + } else { + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_ADAPTIVE); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + } + + if (cabac_enabled && (slh_params->slice_frame_type == SLHP_P_SLICEFRAME_TYPE || + slh_params->slice_frame_type == SLHP_B_SLICEFRAME_TYPE)) + /* hard code cabac_init_idc value of 0 */ + generate_ue(mtx_header, element_pointers, 0); + + /*MTX fills this value in */ + insert_element_token(mtx_header, element_pointers, ELEMENT_SQP); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + + /* GENERATES ELEMENT OF THE H264_SLICE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: 11 + */ + /*disable_deblocking_filter_idc ue(v) = 2? */ + generate_ue(mtx_header, element_pointers, slh_params->disable_deblocking_filter_idc); + if (slh_params->disable_deblocking_filter_idc != 1) { + /*slice_alpha_c0_offset_div2 se(v) = 0 (1b) in Topaz */ + generate_se(mtx_header, element_pointers, slh_params->deb_alpha_offset_div2); + /*slice_beta_offset_div2 se(v) = 0 (1b) in Topaz */ + generate_se(mtx_header, element_pointers, slh_params->deb_beta_offset_div2); + } + + /* + * num_slice_groups_minus1 ==0 in Topaz, so no slice_group_change_cycle field here + * no byte alignment at end of slice headers + */ +} + +/* + * Prepare an H264 slice header in a form for the MTX to encode into a + * bitstream. + */ +void h264_prepare_slice_header(struct mtx_header_params *mtx_header, + unsigned char intra_slice, unsigned char inter_b_slice, + unsigned char disable_deblocking_filter_idc, + unsigned int first_mb_address, unsigned int mb_skip_run, + unsigned char cabac_enabled, unsigned char is_interlaced, + unsigned char is_idr, unsigned short mvc_view_idx, + unsigned char is_longterm_ref) +{ + struct h264_slice_header_params slh_params; + struct mtx_header_element *this_element; + struct mtx_header_element *element_pointers[MAXNUMBERELEMENTS]; + + slh_params.startcode_prefix_size_bytes = 4; + /* pcb - I think that this is more correct now -- This should also + * work for IDR-P frames which will be marked as SLHP_P_SLICEFRAME_TYPE + */ + slh_params.slice_frame_type = intra_slice ? (is_idr ? SLHP_IDR_SLICEFRAME_TYPE : + SLHP_I_SLICEFRAME_TYPE) : + (inter_b_slice ? SLHP_B_SLICEFRAME_TYPE : + SLHP_P_SLICEFRAME_TYPE); + + slh_params.first_mb_address = first_mb_address; + slh_params.disable_deblocking_filter_idc = (unsigned char)disable_deblocking_filter_idc; + slh_params.pic_interlace = is_interlaced; + slh_params.deb_alpha_offset_div2 = 0; + slh_params.deb_beta_offset_div2 = 0; + /* setup the new flags used for B frame as reference */ + slh_params.reference_picture = inter_b_slice ? 0 : 1; + slh_params.mvc_view_idx = mvc_view_idx; + slh_params.is_longterm_ref = is_longterm_ref; + slh_params.log2_max_pic_order_cnt = 2; + slh_params.longterm_ref_num = 0; + slh_params.ref_is_longterm_ref[0] = 0; + slh_params.ref_longterm_ref_num[0] = 0; + slh_params.ref_is_longterm_ref[1] = 0; + slh_params.ref_longterm_ref_num[1] = 0; + /* + * Builds a single slice header from the given parameters (mid frame) + * Essential we initialise our header structures before building + */ + mtx_header->elements = ELEMENTS_EMPTY; + this_element = (struct mtx_header_element *)mtx_header->element_stream; + element_pointers[0] = this_element; + + h264_write_bits_slice_header(mtx_header, element_pointers, &slh_params, cabac_enabled, + is_idr); + /*Has been used as an index, so need to add 1 for a valid element count */ + mtx_header->elements++; +} + +/* + * PrepareEncodeSliceParams + */ +unsigned int prepare_encode_slice_params(void *enc_ctx, struct slice_params *slice_params, + unsigned char is_intra, unsigned short current_row, + unsigned char deblock_idc, unsigned short slice_height, + unsigned char is_bpicture, unsigned char field_mode, + int fine_y_search_size) +{ + struct img_enc_context *enc; + struct img_video_context *video; + unsigned int frame_store_format; + unsigned char swap_chromas; + unsigned int mbs_per_kick, kicks_per_slice; + unsigned int ipe_control; + enum img_ipe_minblock_size blk_sz; + struct img_mtx_video_context *mtx_enc_context = NULL; + unsigned char restrict_4x4_search_size; + unsigned int lritc_boundary; + + if (!enc_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + enc = (struct img_enc_context *)enc_ctx; + video = enc->video; + + if (video->mtx_enc_ctx_mem.cpu_virt) + mtx_enc_context = (struct img_mtx_video_context *)(&video->mtx_enc_ctx_mem); + + /* We want multiple ones of these so we can submit multiple + * slices without having to wait for the next + */ + slice_params->flags = 0; + ipe_control = video->ipe_control; + + /* extract block size */ + blk_sz = (enum img_ipe_minblock_size)F_EXTRACT(ipe_control, TOPAZHP_CR_IPE_BLOCKSIZE); + + /* mask-out the block size bits from ipe_control */ + ipe_control &= ~(F_MASK(TOPAZHP_CR_IPE_BLOCKSIZE)); + + switch (video->standard) { + case IMG_STANDARD_H264: + if (blk_sz > 2) + blk_sz = (enum img_ipe_minblock_size)2; + if (is_bpicture && blk_sz > 1) + blk_sz = (enum img_ipe_minblock_size)1; + + if (video->mbps >= _1080P_30FPS) + ipe_control |= F_ENCODE(fine_y_search_size, TOPAZHP_CR_IPE_LRITC_BOUNDARY) | + F_ENCODE(fine_y_search_size, TOPAZHP_CR_IPE_Y_FINE_SEARCH); + else + ipe_control |= F_ENCODE(fine_y_search_size + 1, + TOPAZHP_CR_IPE_LRITC_BOUNDARY) | + F_ENCODE(fine_y_search_size, TOPAZHP_CR_IPE_Y_FINE_SEARCH); + + if (video->limit_num_vectors) + ipe_control |= F_ENCODE(1, TOPAZHP_CR_IPE_MV_NUMBER_RESTRICTION); + break; + default: + break; + } + + if (video->mbps >= _1080P_30FPS) + restrict_4x4_search_size = 1; + else + restrict_4x4_search_size = 0; + + ipe_control |= F_ENCODE(blk_sz, TOPAZHP_CR_IPE_BLOCKSIZE); + + lritc_boundary = + (blk_sz != + BLK_SZ_16x16) ? (fine_y_search_size + (restrict_4x4_search_size ? 0 : 1)) : 1; + if (lritc_boundary > 3) + IMG_DBG_ASSERT(0); + + /* Minimum sub block size to calculate motion vectors for. 0=16x16, 1=8x8, 2=4x4 */ + ipe_control = F_INSERT(ipe_control, blk_sz, TOPAZHP_CR_IPE_BLOCKSIZE); + ipe_control = F_INSERT(ipe_control, fine_y_search_size, TOPAZHP_CR_IPE_Y_FINE_SEARCH); + ipe_control = F_INSERT(ipe_control, video->limit_num_vectors, + TOPAZHP_CR_IPE_MV_NUMBER_RESTRICTION); + + /* 8x8 search */ + ipe_control = F_INSERT(ipe_control, lritc_boundary, TOPAZHP_CR_IPE_LRITC_BOUNDARY); + ipe_control = F_INSERT(ipe_control, restrict_4x4_search_size ? 0 : 1, + TOPAZHP_CR_IPE_4X4_SEARCH); + + ipe_control = F_INSERT(ipe_control, video->high_latency, TOPAZHP_CR_IPE_HIGH_LATENCY); + + slice_params->ipe_control = ipe_control; + + if (!is_intra) { + if (is_bpicture) + slice_params->flags |= ISINTERB_FLAGS; + else + slice_params->flags |= ISINTERP_FLAGS; + } + + if (video->multi_reference_p && !(is_intra || is_bpicture)) + slice_params->flags |= ISMULTIREF_FLAGS; + + if (video->spatial_direct && is_bpicture) + slice_params->flags |= SPATIALDIRECT_FLAGS; + + if (is_intra) { + slice_params->slice_config = F_ENCODE(TOPAZHP_CR_SLICE_TYPE_I_SLICE, + TOPAZHP_CR_SLICE_TYPE); + } else { + if (is_bpicture) + slice_params->slice_config = F_ENCODE(TOPAZHP_CR_SLICE_TYPE_B_SLICE, + TOPAZHP_CR_SLICE_TYPE); + else /* p frame */ + slice_params->slice_config = F_ENCODE(TOPAZHP_CR_SLICE_TYPE_P_SLICE, + TOPAZHP_CR_SLICE_TYPE); + } + + mbs_per_kick = video->kick_size; + + /* + * we need to figure out the number of kicks and mb's per kick to use. + * on H.264 we will use a MB's per kick of basic unit + * on other rc varients we will use mb's per kick of width + */ + kicks_per_slice = ((slice_height / 16) * (video->width / 16)) / mbs_per_kick; + + IMG_DBG_ASSERT((kicks_per_slice * mbs_per_kick) == + ((slice_height / 16) * (video->width / 16))); + + /* + * need some sensible ones don't look to be implemented yet... + * change per stream + */ + if (video->format == IMG_CODEC_UY0VY1_8888 || video->format == IMG_CODEC_VY0UY1_8888) + frame_store_format = 3; + else if ((video->format == IMG_CODEC_Y0UY1V_8888) || + (video->format == IMG_CODEC_Y0VY1U_8888)) + frame_store_format = 2; + else if (video->format == IMG_CODEC_420_PL12 || video->format == IMG_CODEC_422_PL12 || + video->format == IMG_CODEC_420_PL12_PACKED || + video->format == IMG_CODEC_420_PL21_PACKED || + video->format == IMG_CODEC_420_PL21 || video->format == IMG_CODEC_422_PL21) + frame_store_format = 1; + else + frame_store_format = 0; + + if (video->format == IMG_CODEC_VY0UY1_8888 || video->format == IMG_CODEC_Y0VY1U_8888 || + ((video->format == IMG_CODEC_420_PL21 || + video->format == IMG_CODEC_420_PL21_PACKED) && mtx_enc_context && + mtx_enc_context->scaler_setup.scaler_control == 0)) + swap_chromas = 1; + else + swap_chromas = 0; + + switch (video->standard) { + case IMG_STANDARD_H264: + /* H264 */ + slice_params->seq_config = F_ENCODE(0, TOPAZHP_CR_TEMPORAL_PIC0_BELOW_IN_VALID) | + F_ENCODE(0, TOPAZHP_CR_TEMPORAL_PIC1_BELOW_IN_VALID) | + F_ENCODE(0, TOPAZHP_CR_ABOVE_OUT_OF_SLICE_VALID) | + F_ENCODE(1, TOPAZHP_CR_WRITE_TEMPORAL_PIC0_BELOW_VALID) | + F_ENCODE(0, TOPAZHP_CR_REF_PIC0_VALID) | + F_ENCODE(0, TOPAZHP_CR_REF_PIC1_VALID) | + F_ENCODE(!is_bpicture, TOPAZHP_CR_REF_PIC1_EQUAL_PIC0) | + F_ENCODE(field_mode ? 1 : 0, TOPAZHP_CR_FIELD_MODE) | + F_ENCODE(swap_chromas, TOPAZHP_CR_FRAME_STORE_CHROMA_SWAP) | + F_ENCODE(frame_store_format, TOPAZHP_CR_FRAME_STORE_FORMAT) | + F_ENCODE(TOPAZHP_CR_ENCODER_STANDARD_H264, TOPAZHP_CR_ENCODER_STANDARD) | + F_ENCODE(deblock_idc == 1 ? 0 : 1, TOPAZHP_CR_DEBLOCK_ENABLE); + + if (video->rc_params.bframes) { + slice_params->seq_config |= + F_ENCODE(1, TOPAZHP_CR_WRITE_TEMPORAL_COL_VALID); + if ((slice_params->flags & ISINTERB_FLAGS) == ISINTERB_FLAGS) + slice_params->seq_config |= F_ENCODE(1, + TOPAZHP_CR_TEMPORAL_COL_IN_VALID); + } + if (!is_bpicture) + slice_params->seq_config |= + F_ENCODE(1, TOPAZHP_CR_WRITE_TEMPORAL_COL_VALID); + break; + + default: + break; + } + + if (is_bpicture) { + slice_params->seq_config |= F_ENCODE(0, TOPAZHP_CR_TEMPORAL_PIC1_BELOW_IN_VALID) | + F_ENCODE(0, TOPAZHP_CR_WRITE_TEMPORAL_PIC1_BELOW_VALID) | + F_ENCODE(1, TOPAZHP_CR_REF_PIC1_VALID) | + F_ENCODE(1, TOPAZHP_CR_TEMPORAL_COL_IN_VALID); + } + + if (video->enable_sel_stats_flags & ESF_FIRST_STAGE_STATS) + slice_params->seq_config |= F_ENCODE(1, TOPAZHP_CR_WRITE_MB_FIRST_STAGE_VALID); + + if (video->enable_sel_stats_flags & ESF_MP_BEST_MB_DECISION_STATS || + video->enable_sel_stats_flags & ESF_MP_BEST_MOTION_VECTOR_STATS) { + slice_params->seq_config |= F_ENCODE(1, TOPAZHP_CR_BEST_MULTIPASS_OUT_VALID); + + if (!(video->enable_sel_stats_flags & ESF_MP_BEST_MOTION_VECTOR_STATS)) + /* 64 Byte Best Multipass Motion Vector output disabled by default */ + slice_params->seq_config |= F_ENCODE(1, TOPAZHP_CR_BEST_MVS_OUT_DISABLE); + } + + if (video->enable_inp_ctrl) + slice_params->seq_config |= F_ENCODE(1, TOPAZHP_CR_MB_CONTROL_IN_VALID); + + return 0; +} + +/* + * Generates the slice params template + */ +void generate_slice_params_template(struct img_enc_context *enc, + struct vidio_ddbufinfo *mem_info, + enum img_frame_template_type slice_type, + unsigned char is_interlaced, int fine_y_search_size) +{ + unsigned char is_intra = ((slice_type == IMG_FRAME_IDR) || (slice_type == IMG_FRAME_INTRA)); + unsigned char is_bframe = (slice_type == IMG_FRAME_INTER_B); + unsigned char is_idr = ((slice_type == IMG_FRAME_IDR) || + (slice_type == IMG_FRAME_INTER_P_IDR)); + struct img_video_context *video = enc->video; + unsigned short mvc_view_idx = (unsigned short)(NON_MVC_VIEW); + /* Initialize Slice Params */ + struct slice_params *slice_params_dest; + unsigned int slice_height = video->picture_height / video->slices_per_picture; + + slice_height &= ~15; + + slice_params_dest = (struct slice_params *)(mem_info->cpu_virt); + + mvc_view_idx = video->mvc_view_idx; + + prepare_encode_slice_params(enc, slice_params_dest, is_intra, + 0, video->deblock_idc, slice_height, is_bframe, + is_interlaced, fine_y_search_size); + + slice_params_dest->template_type = slice_type; + + /* Prepare Slice Header Template */ + switch (video->standard) { + case IMG_STANDARD_H264: + h264_prepare_slice_header(&slice_params_dest->slice_hdr_tmpl, is_intra, + is_bframe, video->deblock_idc, 0, 0, video->cabac_enabled, + is_interlaced, is_idr, mvc_view_idx, FALSE); + break; + + default: + break; + } +} + +void h264_write_bits_mvc_sequence_header(struct mtx_header_params *mtx_header, + struct mtx_header_element **element_pointers, + struct h264_sequence_header_params *sh_params, + struct h264_crop_params *crop, + struct h264_scaling_matrix_params *scaling_matrix) +{ + int view_idx = 0; + int num_views = MAX_MVC_VIEWS; + + insert_element_token(mtx_header, element_pointers, ELEMENT_STARTCODE_RAWDATA); + h264_write_bits_startcode_prefix_element(mtx_header, element_pointers, 4); + + /* + * 4 Byte StartCodePrefix Pregenerated in: H264_WriteBits_StartCodePrefix_Element() + * Byte aligned (bit 32) + */ + /* forbidden_zero_bit=0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, (0 << 7) | + (0x3 << 5) | /* nal_ref_idc=01 (may be 11) */ + (15), /* nal_unit_type=15 */ + 8); + + /* + * Byte aligned (bit 40) + * profile_idc = 8 bits = 66 for BP (PROFILE_IDC_BP), 77 for MP (PROFILE_IDC_MP) + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 118, 8); + + /* Byte aligned (bit 48) */ + /* constrain_set0_flag = 1 for MP + BP constraints */ + write_upto_8bits_to_elements(mtx_header, element_pointers, (0 << 7) | + (0 << 6) | /* constrain_set1_flag = 1 for MP + BP constraints */ + (0 << 5) | /* constrain_set2_flag = always 0 in BP/MP */ + (0 << 4), /* constrain_set3_flag = 1 for level 1b, 0 for others */ + /* reserved_zero_4bits = 0 */ + 8); + + /* + * Byte aligned (bit 56) + * level_idc should be set to 9 in the sps in case of level is Level 1B and the profile + * is Multiview High or Stereo High profiles + */ + /* level_idc (8 bits) = 9 for 1b, 10xlevel for others */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->level == SH_LEVEL_1B) ? 9 : + (unsigned char)sh_params->level, 8); + + /* seq_parameter_Set_id = 1 FOR subset-SPS */ + generate_ue(mtx_header, element_pointers, MVC_SPS_ID); + generate_ue(mtx_header, element_pointers, 1); /* chroma_format_idc = 1 */ + generate_ue(mtx_header, element_pointers, 0); /* bit_depth_luma_minus8 = 0 */ + generate_ue(mtx_header, element_pointers, 0); /* bit_depth_chroma_minus8 = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, sh_params->is_lossless ? 1 : 0, + 1); /* qpprime_y_zero_transform_bypass_flag = 0 */ + + if (sh_params->use_default_scaling_list || sh_params->seq_scaling_matrix_present_flag) { + /* seq_scaling_matrix_present_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + if (!sh_params->use_default_scaling_list) { + h264_write_bits_scaling_lists(mtx_header, element_pointers, scaling_matrix, + TRUE); + insert_element_token(mtx_header, element_pointers, ELEMENT_RAWDATA); + } else { + /* seq_scaling_list_present_flag[i] = 0; 0 < i < 8 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 8); + } + } else { + /* seq_scaling_matrix_present_flag */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + generate_ue(mtx_header, element_pointers, 1); /* log2_max_frame_num_minus4 = 1 */ + generate_ue(mtx_header, element_pointers, 0); /* pic_order_cnt_type = 0 */ + /* log2_max_pic_order_cnt_Isb_minus4 = 2 */ + generate_ue(mtx_header, element_pointers, 2); + + /*num_ref_frames ue(2), typically 2 */ + generate_ue(mtx_header, element_pointers, sh_params->max_num_ref_frames); + /* Bytes aligned (bit 72) */ + /* gaps_in_frame_num_value_allowed_Flag - (1 bit) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + sh_params->gaps_in_frame_num_value, 1); + + /* + * GENERATES THE SECOND, VARIABLE LENGTH, ELEMENT OF THE H264_SEQUENCE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: xx + */ + /*pic_width_in_mbs_minus1: ue(v) from 10 to 44 (176 to 720 pixel per row) */ + generate_ue(mtx_header, element_pointers, sh_params->width_in_mbs_minus1); + /*pic_height_in_maps_units_minus1: ue(v) Value from 8 to 35 (144 to 576 pixels per column) + */ + generate_ue(mtx_header, element_pointers, sh_params->height_in_maps_units_minus1); + /* We don't know the alignment at this point, so will have to use bit writing functions */ + /* frame_mb_only_flag 1=frame encoding, 0=field encoding */ + write_upto_8bits_to_elements(mtx_header, element_pointers, sh_params->frame_mbs_only_flag, + 1); + + if (!sh_params->frame_mbs_only_flag) /* in the case of interlaced encoding */ + /* mb_adaptive_frame_field_flag = 0 in Topaz(field encoding at the sequence level) */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + + /* direct_8x8_inference_flag=1 in Topaz */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + + if (crop->clip) { + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); + generate_ue(mtx_header, element_pointers, crop->left_crop_offset); + generate_ue(mtx_header, element_pointers, crop->right_crop_offset); + generate_ue(mtx_header, element_pointers, crop->top_crop_offset); + generate_ue(mtx_header, element_pointers, crop->bottom_crop_offset); + + } else { + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 1); + } + + /* + * GENERATES THE THIRD ELEMENT OF THE H264_SEQUENCE_HEADER() STRUCTURE + * ELEMENT BITCOUNT: xx + * vui_parameters_present_flag (VUI only in 1st sequence of stream) + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->vui_params_present), 1); + if (sh_params->vui_params_present > 0) + h264_write_bits_vui_params(mtx_header, element_pointers, &sh_params->vui_params); + + write_upto_8bits_to_elements(mtx_header, element_pointers, 1, 1); /*bit_equal_to_one */ + + /* sequence parameter set MVC extension */ + generate_ue(mtx_header, element_pointers, (num_views - 1)); /*num_views_minus1 */ + for (view_idx = 0; view_idx < num_views; view_idx++) + generate_ue(mtx_header, element_pointers, view_idx); + + /* anchor references */ + for (view_idx = 1; view_idx < num_views; view_idx++) { + /* num_anchor_refs_l0 = 1; view-1 refers to view-0 */ + generate_ue(mtx_header, element_pointers, 1); + generate_ue(mtx_header, element_pointers, 0); /* anchor_ref_l0 = 0 */ + generate_ue(mtx_header, element_pointers, 0); /* num_anchor_refs_l1 = 0 */ + } + + /* non-anchor references */ + for (view_idx = 1; view_idx < num_views; view_idx++) { + generate_ue(mtx_header, element_pointers, 1); /* num_non_anchor_refs_l0 = 0 */ + generate_ue(mtx_header, element_pointers, 0); /* non_anchor_refs_l0 = 0 */ + generate_ue(mtx_header, element_pointers, 0); /* num_non_anchor_refs_l1 = 0 */ + } + + generate_ue(mtx_header, element_pointers, 0);/* num_level_values_signaled_minus1 = 0 */ + + /* level_idc should be set to 9 in the sps in case of level is + * Level 1B and the profile is Multiview High or Stereo High profiles + */ + write_upto_8bits_to_elements(mtx_header, element_pointers, + (sh_params->level == SH_LEVEL_1B) ? 9 : + (unsigned char)sh_params->level, + 8);/* level_idc (8 bits) = 9 for 1b, 10xlevel for others */ + generate_ue(mtx_header, element_pointers, 0);/* num_applicable_ops_minus1 = 0 */ + /* applicable_ops_temporal_id = 0 */ + write_upto_8bits_to_elements(mtx_header, element_pointers, 0, 3); + /* applicable_op_num_target_views_minus1 = 0 */ + generate_ue(mtx_header, element_pointers, 0); + generate_ue(mtx_header, element_pointers, 0); /* applicable_op_target_view_id = 0 */ + generate_ue(mtx_header, element_pointers, 0); /* applicable_op_num_views_minus1 = 0 */ + + write_upto_8bits_to_elements(mtx_header, element_pointers, + 0, /* mvc_vui_parameters_present_flag =0 */ + 1); + + write_upto_8bits_to_elements(mtx_header, element_pointers, + 0, /* additional_extension2_flag =0 */ + 1); + + /* Finally we need to align to the next byte */ + /* Tell MTX to insert the byte align field + * (we don't know final stream size for alignment at this point) + */ + insert_element_token(mtx_header, element_pointers, ELEMENT_INSERTBYTEALIGN_H264); +} + +/* + * Prepare an H264 SPS in a form for the MTX to encode into a bitstream. + */ +void h264_prepare_mvc_sequence_header(struct mtx_header_params *mtx_header, + unsigned int pic_width_in_mbs, + unsigned int pic_height_in_mbs, + unsigned char vui_params_present, + struct h264_vui_params *params, + struct h264_crop_params *crop, + struct h264_sequence_header_params *sh_params) +{ + struct mtx_header_element *this_element; + struct mtx_header_element *element_pointers[MAXNUMBERELEMENTS]; + + /* + * Builds a sequence, picture and slice header with from the given inputs + * parameters (start of new frame) Essential we initialise our header + * structures before building + */ + mtx_header->elements = ELEMENTS_EMPTY; + this_element = (struct mtx_header_element *)mtx_header->element_stream; + element_pointers[0] = this_element; + + h264_write_bits_mvc_sequence_header(mtx_header, element_pointers, sh_params, crop, NULL); + /* Has been used as an index, so need to add 1 for a valid element count */ + mtx_header->elements++; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/header_gen.h b/drivers/media/platform/vxe-vxd/encoder/header_gen.h --- a/drivers/media/platform/vxe-vxd/encoder/header_gen.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/header_gen.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * encoder header generation interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include "fw_headers/topazscfwif.h" +#include +#include "topaz_api.h" +#include "vid_buf.h" + +/* + * enum describing slice/frame type (H264) + */ +enum slhp_sliceframe_type { + SLHP_P_SLICEFRAME_TYPE, + SLHP_B_SLICEFRAME_TYPE, + SLHP_I_SLICEFRAME_TYPE, + SLHP_SP_SLICEFRAME_TYPE, + SLHP_SI_SLICEFRAME_TYPE, + SLHP_IDR_SLICEFRAME_TYPE, + SLHP_SLICE_FRAME_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * struct describing scaling lists (H264) + */ +struct h264_scaling_matrix_params { + unsigned char scaling_lists4x4[6][16]; + unsigned char scaling_lists8x8[2][64]; + unsigned int list_mask; +}; + +/* + * struct describing picture parameter set (H264) + */ +struct h264_picture_header_params { + unsigned char pic_parameter_set_id; + unsigned char seq_parameter_set_id; + unsigned char entropy_coding_mode_flag; + unsigned char weighted_pred_flag; + unsigned char weighted_bipred_idc; + signed char chroma_qp_index_offset; + unsigned char constrained_intra_pred_flag; + unsigned char transform_8x8_mode_flag; + unsigned char pic_scaling_matrix_present_flag; + unsigned char use_default_scaling_list; + signed char second_chroma_qp_index_offset; +}; + +/* + * struct describing slice header (H264) + */ +struct h264_slice_header_params { + unsigned char startcode_prefix_size_bytes; + enum slhp_sliceframe_type slice_frame_type; + unsigned int first_mb_address; + unsigned char log2_max_pic_order_cnt; + unsigned char disable_deblocking_filter_idc; + unsigned char pic_interlace; + unsigned char reference_picture; + signed char deb_alpha_offset_div2; + signed char deb_beta_offset_div2; + unsigned short mvc_view_idx; + unsigned char is_longterm_ref; + unsigned char longterm_ref_num; + /* Long term reference info for reference frames */ + unsigned char ref_is_longterm_ref[2]; + unsigned char ref_longterm_ref_num[2]; +}; + +void generate_slice_params_template(struct img_enc_context *enc, + struct vidio_ddbufinfo *mem_info, + enum img_frame_template_type slice_type, + unsigned char is_interlaced, int fine_y_search_size); + +void h264_prepare_sequence_header(struct mtx_header_params *mtx_header, + unsigned int pic_width_in_mbs, + unsigned int pic_height_in_mbs, unsigned char vui_params_present, + struct h264_vui_params *params, + struct h264_crop_params *crop, + struct h264_sequence_header_params *sh_params, + unsigned char aso); + +void h264_prepare_mvc_sequence_header(struct mtx_header_params *mtx_header, + unsigned int pic_width_in_mbs, unsigned int pic_height_in_mbs, + unsigned char vui_params_present, + struct h264_vui_params *params, + struct h264_crop_params *crop, + struct h264_sequence_header_params *sh_params); + +void h264_prepare_aud_header(struct mtx_header_params *mtx_header); + +void h264_prepare_picture_header(struct mtx_header_params *mtx_header, + unsigned char cabac_enabled, + unsigned char transform_8x8, + unsigned char intra_constrained, + signed char cqp_offset, + unsigned char weighted_prediction, + unsigned char weighted_bi_pred, + unsigned char mvc_pps, + unsigned char scaling_matrix, + unsigned char scaling_lists); diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/mtx_fwif.c b/drivers/media/platform/vxe-vxd/encoder/mtx_fwif.c --- a/drivers/media/platform/vxe-vxd/encoder/mtx_fwif.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/mtx_fwif.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,990 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MTX Firmware Interface + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include +#include + +#include "fw_headers/mtx_fwif.h" +#include "fw_headers/defs.h" +#include "fw_binaries/include_all_fw_variants.h" +#include "img_errors.h" +#include "reg_headers/mtx_regs.h" +/* still used for DMAC regs */ +#include "reg_headers/img_soc_dmac_regs.h" +#include "target_config.h" +#include "topaz_device.h" +#include "topazmmu.h" +#include "vxe_public_regdefs.h" + +extern struct mem_space topaz_mem_space[]; + +/* + * Static Function Decl + */ +static void mtx_get_mtx_ctrl_from_dash(struct img_fw_context *fw_ctx); + +static unsigned int mtx_read_core_reg(struct img_fw_context *fw_ctx, + const unsigned int reg); + +static void mtx_write_core_reg(struct img_fw_context *fw_ctx, + const unsigned int reg, + const unsigned int val); + +static int mtx_select_fw_build(struct img_fw_context *fw_ctx, enum img_codec codec); + +static void mtx_reg_if_upload(struct img_fw_context *fw_ctx, + const unsigned int data_mem, unsigned int addr, + const unsigned int words, const unsigned int *const data); + +/* + * Polling Configuration for TAL + */ +#define TAL_REG_RD_WR_TRIES 1000 /* => try 1000 times before giving up */ + +/* + * defines that should come from auto generated headers + */ +#define MTX_DMA_MEMORY_BASE (0x82880000) +#define PC_START_ADDRESS (0x80900000) + +#define MTX_CORE_CODE_MEM (0x10) +#define MTX_CORE_DATA_MEM (0x18) + +#define MTX_PC (0x05) + +/* + * Get control of the MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Return None + */ +static void mtx_get_mtx_ctrl_from_dash(struct img_fw_context *fw_ctx) +{ + unsigned int reg = 0; + + IMG_DBG_ASSERT(!fw_ctx->drv_has_mtx_ctrl); + + /* Request the bus from the Dash...*/ + reg = F_ENCODE(1, TOPAZHP_TOP_CR_MTX_MSTR_DBG_IS_SLAVE) | + F_ENCODE(0x2, TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_IN); + VXE_WR_REG32(fw_ctx->topaz_multicore_reg_addr, TOPAZHP_TOP_CR_MTX_DEBUG_MSTR, reg); + + do { + reg = VXE_RD_REG32(fw_ctx->topaz_multicore_reg_addr, TOPAZHP_TOP_CR_MTX_DEBUG_MSTR); + + } while ((reg & 0x18) != 0); + + /* Save the access control register...*/ + fw_ctx->drv_has_mtx_ctrl = VXE_RD_REG32(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_RAM_ACCESS_CONTROL); + + fw_ctx->drv_has_mtx_ctrl = TRUE; +} + +/* + * Release control of the MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Return None + */ +static void mtx_release_mtx_ctrl_from_dash(struct img_fw_context *fw_ctx) +{ + unsigned int reg = 0; + + IMG_DBG_ASSERT(fw_ctx->drv_has_mtx_ctrl); + + /* Restore the access control register...*/ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_RAM_ACCESS_CONTROL, + fw_ctx->access_control); + + /* Release the bus...*/ + reg = F_ENCODE(1, TOPAZHP_TOP_CR_MTX_MSTR_DBG_IS_SLAVE); + VXE_WR_REG32(fw_ctx->topaz_multicore_reg_addr, TOPAZHP_TOP_CR_MTX_DEBUG_MSTR, reg); + + fw_ctx->drv_has_mtx_ctrl = FALSE; +} + +/* + * Read an MTX register. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input reg : Offset of register to read + * @Return unsigned int : Register value + */ +static unsigned int mtx_read_core_reg(struct img_fw_context *fw_ctx, const unsigned int reg) +{ + unsigned int ret = 0; + + mtx_get_mtx_ctrl_from_dash(fw_ctx); + + /* Issue read request */ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_REGISTER_READ_WRITE_REQUEST, + MASK_MTX_MTX_RNW | (reg & ~MASK_MTX_MTX_DREADY)); + + /* Wait for done */ + VXE_POLL_REG32_ISEQ(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_REGISTER_READ_WRITE_REQUEST, + MASK_MTX_MTX_DREADY, + MASK_MTX_MTX_DREADY, + TAL_REG_RD_WR_TRIES); + + /* Read */ + ret = VXE_RD_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_REGISTER_READ_WRITE_DATA); + + mtx_release_mtx_ctrl_from_dash(fw_ctx); + + return ret; +} + +/* + * Write an MTX register. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input reg : Offset of register to write + * @Input val : Value to write to register + */ +static void mtx_write_core_reg(struct img_fw_context *fw_ctx, + const unsigned int reg, const unsigned int val) +{ + mtx_get_mtx_ctrl_from_dash(fw_ctx); + + /* Put data in MTX_RW_DATA */ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_REGISTER_READ_WRITE_DATA, val); + + /* DREADY is set to 0 and request a write*/ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_REGISTER_READ_WRITE_REQUEST, + (reg & ~MASK_MTX_MTX_DREADY)); + + /* Wait for DREADY to become set*/ + VXE_POLL_REG32_ISEQ(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_REGISTER_READ_WRITE_REQUEST, + MASK_MTX_MTX_DREADY, + MASK_MTX_MTX_DREADY, + TAL_REG_RD_WR_TRIES); + + mtx_release_mtx_ctrl_from_dash(fw_ctx); +} + +/* ****** Utility macroses for `mtx_select_fw_build` ************** */ + +#if FW_BIN_FORMAT_VERSION != 2 +# error Unsupported firmware format version +#endif + +/* + * Assign a firmware binary to an MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input codec : Firmware version to use + */ +static int mtx_select_fw_build(struct img_fw_context *fw_ctx, enum img_codec codec) +{ + unsigned char *fmt, *rc_mode; + unsigned int target_fw_pipes = 0; + unsigned int codec_mask = 0; + unsigned int cur_hw_config; + unsigned char force_specific_pipe_cnt = FALSE; + +# define HW_CONFIG_ALL_FEATURES 0 +# define HW_CONFIG_8CONTEXT 1 + +#define CORE_REV_CONFIG_1_MIN 0x00030906 +#define CORE_REV_CONFIG_1_MAX 0x0003090a + +# define CODEC_MASK_JPEG 0x0001 +# define CODEC_MASK_MPEG2 0x0002 +# define CODEC_MASK_MPEG4 0x0004 +# define CODEC_MASK_H263 0x0008 +# define CODEC_MASK_H264 0x0010 +# define CODEC_MASK_H264MVC 0x0020 +# define CODEC_MASK_VP8 0x0040 +# define CODEC_MASK_H265 0x0080 +# define CODEC_MASK_FAKE 0x007F + +#define _MVC_CODEC_CASE(RC) { case IMG_CODEC_H264MVC_ ## RC: fmt = "H264MVC"; rc_mode = #RC; \ + force_specific_pipe_cnt = TRUE; codec_mask = CODEC_MASK_H264MVC; break; } + + switch (codec) { + case IMG_CODEC_H264_NO_RC: + case IMG_CODEC_H264_VBR: + case IMG_CODEC_H264_CBR: + + case IMG_CODEC_H264_VCM: + fmt = "H264"; + rc_mode = "ALL"; + force_specific_pipe_cnt = TRUE; + codec_mask = CODEC_MASK_H264; + break; + case IMG_CODEC_H263_NO_RC: + case IMG_CODEC_H263_VBR: + case IMG_CODEC_H263_CBR: + fmt = "LEGACY_VIDEO"; + rc_mode = "ALL"; + codec_mask = CODEC_MASK_H263; + break; + case IMG_CODEC_MPEG2_NO_RC: + case IMG_CODEC_MPEG2_VBR: + case IMG_CODEC_MPEG2_CBR: + fmt = "LEGACY_VIDEO"; + rc_mode = "ALL"; + codec_mask = CODEC_MASK_MPEG2; + break; + case IMG_CODEC_MPEG4_NO_RC: + case IMG_CODEC_MPEG4_VBR: + case IMG_CODEC_MPEG4_CBR: + fmt = "LEGACY_VIDEO"; + rc_mode = "ALL"; + codec_mask = CODEC_MASK_MPEG4; + break; + _MVC_CODEC_CASE(NO_RC); + _MVC_CODEC_CASE(VBR); + _MVC_CODEC_CASE(CBR); + _MVC_CODEC_CASE(ERC); + case IMG_CODEC_JPEG: + fmt = "JPEG"; + rc_mode = "NO_RC"; + codec_mask = CODEC_MASK_JPEG; + break; + default: + pr_err("Failed to locate firmware for codec %d\n", codec); + return IMG_ERROR_UNDEFINED; + } +#undef _MVC_CODEC_CASE + + /* rc mode name fix */ + if (strcmp(rc_mode, "NO_RC") == 0) + rc_mode = "NONE"; + + { + /* + * Pick firmware type (done implicitly via determining number + * of pipes given firmware is expected to have + */ + const unsigned int core_id = fw_ctx->core_rev; +#define IS_REV(name) ((core_id >= MIN_ ## name ## _REV) && \ + (core_id <= MAX_ ## name ## _REV)) + + if (core_id >= CORE_REV_CONFIG_1_MIN && core_id <= CORE_REV_CONFIG_1_MAX) { + /* + * For now, it is assumed that this revision ID means 8 + * context 2 pipe variant + */ + cur_hw_config = HW_CONFIG_8CONTEXT; + target_fw_pipes = 2; + } else { + cur_hw_config = HW_CONFIG_ALL_FEATURES; + if (fw_ctx->hw_num_pipes < 3 && force_specific_pipe_cnt) + target_fw_pipes = 2; + else + target_fw_pipes = 4; + } +#undef IS_REV + } + + { + /* Search for matching firmwares */ + + unsigned int fmts_included = 0; + unsigned int ii; + unsigned char preferred_fw_located = FALSE; + unsigned int req_size = 0; + struct IMG_COMPILED_FW_BIN_RECORD *selected, *iter; + + selected = NULL; + + for (ii = 0; ii < all_fw_binaries_cnt; ii++) { + iter = all_fw_binaries[ii]; + /* + * With HW_3_6, we want to allow 3 pipes if it was + * required, this is mainly for test purposes + */ + if ((strcmp("JPEG_H264", iter->fmt) == 0) && target_fw_pipes != 3) { + preferred_fw_located = TRUE; + req_size = (4 * iter->data_size + (iter->data_origin - + MTX_DMA_MEMORY_BASE)); + break; + } + } + + if (preferred_fw_located && req_size <= fw_ctx->mtx_ram_size && + cur_hw_config == iter->hw_config && iter->pipes >= target_fw_pipes && + (codec_mask == CODEC_MASK_JPEG || codec_mask == CODEC_MASK_H264) && + ((iter->formats_mask & codec_mask) != 0)) { + selected = iter; + } else { + for (ii = 0; ii < all_fw_binaries_cnt; ii++) { + iter = all_fw_binaries[ii]; + /* The hardware config modes need to match */ + if (cur_hw_config != iter->hw_config) { + pr_err("cur_hw_config %x iter->hw_config %x mismatch\n", + cur_hw_config, iter->hw_config); + continue; + } + + fmts_included = iter->formats_mask; + + if (((fmts_included & codec_mask) != 0) && + (codec_mask == CODEC_MASK_JPEG || + /* no need to match RC for JPEG */ + strcmp(rc_mode, iter->rc_mode) == 0)) { + /* + * This firmware matches by format/mode + * combination, now to check if it fits + * better than current best + */ + if (!selected && iter->pipes >= target_fw_pipes) { + /* + * Select firmware ether if it + * is first matchin one we've + * encountered or if it better + * matches desired number of + * pipes. + */ + selected = iter; + } + + if (iter->pipes == target_fw_pipes) { + /* Found ideal firmware version */ + selected = iter; + break; + } + } + } + } + + if (!selected) { + pr_err("Failed to locate firmware for format '%s' and RC mode '%s'.\n", + fmt, rc_mode); + return IMG_ERROR_UNDEFINED; + } +#ifdef DEBUG_ENCODER_DRIVER + pr_info("Using firmware: %s with %i pipes, hwconfig=%i (text size = %i, data size = %i) for requested codec: %s RC mode %s\n", + selected->fmt, selected->pipes, + selected->hw_config, selected->text_size, + selected->data_size, fmt, rc_mode); +#endif + + /* Export selected firmware to the fw context */ + fw_ctx->mtx_topaz_fw_text_size = selected->text_size; + fw_ctx->mtx_topaz_fw_data_size = selected->data_size; + fw_ctx->mtx_topaz_fw_text = selected->text; + fw_ctx->mtx_topaz_fw_data = selected->data; + fw_ctx->mtx_topaz_fw_data_origin = selected->data_origin; + fw_ctx->num_pipes = selected->pipes; + fw_ctx->int_defines.length = selected->int_define_cnt; + fw_ctx->int_defines.names = selected->int_define_names; + fw_ctx->int_defines.values = selected->int_defines; + fw_ctx->supported_codecs = selected->formats_mask; + fw_ctx->num_contexts = mtx_get_fw_config_int(fw_ctx, "TOPAZHP_MAX_NUM_STREAMS"); + } + return IMG_SUCCESS; +} + +/* + * Upload MTX text and data sections via register interface + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input data_mem : RAM ID for text/data section + * @Input address : Address to upload data to + * @Input words : Number of words of data to upload + * @Input data : Pointer to data to upload + */ +static void mtx_reg_if_upload(struct img_fw_context *fw_ctx, const unsigned int data_mem, + unsigned int address, const unsigned int words, + const unsigned int *const data) +{ + unsigned int loop; + unsigned int ctrl; + unsigned int ram_id; + unsigned int addr; + unsigned int curr_bank = ~0; + unsigned int uploaded = 0; + + mtx_get_mtx_ctrl_from_dash(fw_ctx); + + VXE_POLL_REG32_ISEQ(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_RAM_ACCESS_STATUS, + MASK_MTX_MTX_MTX_MCM_STAT, + MASK_MTX_MTX_MTX_MCM_STAT, + TAL_REG_RD_WR_TRIES); + + for (loop = 0; loop < words; loop++) { + ram_id = data_mem + (address / fw_ctx->mtx_bank_size); + if (ram_id != curr_bank) { + addr = address >> 2; + ctrl = 0; + ctrl = F_ENCODE(ram_id, MTX_MTX_MCMID) | + F_ENCODE(addr, MTX_MTX_MCM_ADDR) | + F_ENCODE(1, MTX_MTX_MCMAI); + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_RAM_ACCESS_CONTROL, ctrl); + curr_bank = ram_id; + } + address += 4; + + if (uploaded > (1024 * 24)) /* should this be RAM bank size?? */ + break; + uploaded += 4; + + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_RAM_ACCESS_DATA_TRANSFER, + data[loop]); + + VXE_POLL_REG32_ISEQ(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_RAM_ACCESS_STATUS, + MASK_MTX_MTX_MTX_MCM_STAT, + MASK_MTX_MTX_MTX_MCM_STAT, + TAL_REG_RD_WR_TRIES); + } + + mtx_release_mtx_ctrl_from_dash(fw_ctx); +} + +/* + * Transfer memory between the Host and MTX via DMA. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input channel : DMAC channel to use (0 for TopazSC) + * @Input hHostMemTransfer : void * for the host memory + * @Input hostMemOffset : offset into the host memory + * @Input mtx_addr : Address on MTX + * @Input numWords : size of transfer in 32-bit words (PW units) + * @Input bRNW : Read not Write (FALSE to write to the MTX) + */ +void mtx_dmac_transfer(struct img_fw_context *fw_ctx, unsigned int channel, + struct vidio_ddbufinfo *host_mem_transfer, + unsigned int host_mem_offset, unsigned int mtx_addr, + unsigned int words, unsigned char rnw) +{ + unsigned int irq_stat; + unsigned int count_reg; + void *dmac_reg_addr; + void *reg_addr; + unsigned int config_reg; + unsigned int mmu_status = 0; + + unsigned int dmac_burst_size = DMAC_BURST_2; /* 2 * 128 bits = 32 bytes */ + unsigned int mtx_burst_size = 4; /* 4 * 2 * 32 bits = 32 bytes */ + + /* check the burst sizes */ + IMG_DBG_ASSERT(dmac_burst_size * 16 == MTX_DMA_BURSTSIZE_BYTES); + IMG_DBG_ASSERT(mtx_burst_size * 8 == MTX_DMA_BURSTSIZE_BYTES); + + /* check transfer size matches burst width */ + IMG_DBG_ASSERT(0 == (words & ((MTX_DMA_BURSTSIZE_BYTES >> 2) - 1))); + + /* check DMA channel */ + IMG_DBG_ASSERT(channel < DMAC_MAX_CHANNELS); + + /* check that no transfer is currently in progress */ + dmac_reg_addr = (void *)topaz_mem_space[REG_DMAC].cpu_addr; + count_reg = VXE_RD_REG32(dmac_reg_addr, IMG_SOC_DMAC_COUNT(channel)); + IMG_DBG_ASSERT(0 == (count_reg & + (MASK_IMG_SOC_EN | MASK_IMG_SOC_LIST_EN))); + + /* check we don't already have a page fault condition */ + reg_addr = (void *)topaz_mem_space[REG_TOPAZHP_MULTICORE].cpu_addr; + mmu_status = VXE_RD_REG32(reg_addr, TOPAZHP_TOP_CR_MMU_STATUS); + + IMG_DBG_ASSERT(mmu_status == 0); + + if (mmu_status || (count_reg & (MASK_IMG_SOC_EN | + MASK_IMG_SOC_LIST_EN))) { + /* DMA engine not idle or pre-existing page fault condition */ + pr_err("DMA engine not idle or pre-existing page fault condition!\n"); + fw_ctx->initialized = FALSE; + return; + } + + /* clear status of any previous interrupts */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_IRQ_STAT(channel), 0); + + /* and that no interrupts are outstanding */ + irq_stat = VXE_RD_REG32(dmac_reg_addr, IMG_SOC_DMAC_IRQ_STAT(channel)); + IMG_DBG_ASSERT(irq_stat == 0); + + /* Write MTX DMAC registers (for current MTX) */ + /* MTX Address */ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_SYSC_CDMAA, + mtx_addr); + + /* MTX DMAC Config */ + config_reg = F_ENCODE(mtx_burst_size, MTX_BURSTSIZE) | + F_ENCODE((rnw ? 1 : 0), MTX_RNW) | + F_ENCODE(1, MTX_ENABLE) | + F_ENCODE(words, MTX_LENGTH); + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_SYSC_CDMAC, + config_reg); + + /* Write System DMAC registers */ + /* per hold - allow HW to sort itself out */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_PER_HOLD(channel), 16); + + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_SETUP(channel), + host_mem_transfer->dev_virt + host_mem_offset); + + /* count reg */ + count_reg = DMAC_VALUE_COUNT(DMAC_BSWAP_NO_SWAP, DMAC_PWIDTH_32_BIT, + rnw, DMAC_PWIDTH_32_BIT, words); + count_reg |= MASK_IMG_SOC_TRANSFER_IEN; /* generate an interrupt at end of transfer */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_COUNT(channel), count_reg); + + /* don't inc address, set burst size */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_PERIPH(channel), + DMAC_VALUE_PERIPH_PARAM(DMAC_ACC_DEL_0, FALSE, dmac_burst_size)); + + /* Target correct MTX DMAC port */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_PERIPHERAL_ADDR(channel), + MTX_CR_MTX_SYSC_CDMAT + REG_START_TOPAZ_MTX_HOST); + + /* + * Finally, rewrite the count register with the enable bit set to kick + * off the transfer + */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_COUNT(channel), + (count_reg | MASK_IMG_SOC_EN)); + + /* Wait for it to finish */ + VXE_POLL_REG32_ISEQ(dmac_reg_addr, IMG_SOC_DMAC_IRQ_STAT(channel), + F_ENCODE(1, IMG_SOC_TRANSFER_FIN), + F_ENCODE(1, IMG_SOC_TRANSFER_FIN), + TAL_REG_RD_WR_TRIES); + count_reg = VXE_RD_REG32(dmac_reg_addr, IMG_SOC_DMAC_COUNT(channel)); + mmu_status = VXE_RD_REG32(reg_addr, TOPAZHP_TOP_CR_MMU_STATUS); + if (mmu_status || (count_reg & + (MASK_IMG_SOC_EN | MASK_IMG_SOC_LIST_EN))) { + pr_err("DMA has failed or page faulted\n"); + /* DMA has failed or page faulted */ + fw_ctx->initialized = FALSE; + } + + /* Clear the interrupt */ + VXE_WR_REG32(dmac_reg_addr, IMG_SOC_DMAC_IRQ_STAT(channel), 0); +} + +/* + * Sets target MTX for DMA and register writes + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input bTargetAll : TRUE indicates register and DMA writes go to all MTX + */ +void mtx_set_target(struct img_fw_context *fw_ctx) +{ + unsigned int reg = 0; + + reg = F_ENCODE(0, TOPAZHP_TOP_CR_WRITES_CORE_ALL); + VXE_WR_REG32(fw_ctx->topaz_multicore_reg_addr, + TOPAZHP_TOP_CR_MULTICORE_CORE_SEL_0, reg); +} + +/* + * Upload text and data sections via DMA + * @Input fw_ctx : Pointer to the context of the target MTX + */ +static void mtx_uploadfw(void *dev_ctx, struct img_fw_context *fw_ctx) +{ + struct topaz_dev_ctx *ctx = (struct topaz_dev_ctx *)dev_ctx; + struct vidio_ddbufinfo text, data; + void *add_lin_text, *add_lin_data; + unsigned int text_size = fw_ctx->mtx_topaz_fw_text_size; + unsigned int data_size = fw_ctx->mtx_topaz_fw_data_size; + + if (topaz_mmu_alloc(ctx->topaz_mmu_ctx.mmu_context_handle, + ctx->vxe_arg, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + text_size * 4 + MTX_DMA_BURSTSIZE_BYTES, 64, &text)) { + pr_err("mmu_alloc for text failed!\n"); + fw_ctx->initialized = FALSE; + return; + } + if (topaz_mmu_alloc(ctx->topaz_mmu_ctx.mmu_context_handle, + ctx->vxe_arg, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + data_size * 4 + MTX_DMA_BURSTSIZE_BYTES, 64, &data)) { + pr_err("mmu_alloc for data failed!\n"); + topaz_mmu_free(ctx->vxe_arg, &text); + fw_ctx->initialized = FALSE; + } + + add_lin_text = text.cpu_virt; + memcpy((void *)add_lin_text, fw_ctx->mtx_topaz_fw_text, text_size * 4); + add_lin_data = data.cpu_virt; + memcpy((void *)add_lin_data, fw_ctx->mtx_topaz_fw_data, data_size * 4); + + topaz_update_device_mem(ctx->vxe_arg, &text); + topaz_update_device_mem(ctx->vxe_arg, &data); + + /* adjust transfer sizes of text and data sections to match burst size */ + text_size = + ((text_size * 4 + (MTX_DMA_BURSTSIZE_BYTES - 1)) & ~(MTX_DMA_BURSTSIZE_BYTES - 1)) / + 4; + data_size = + ((data_size * 4 + (MTX_DMA_BURSTSIZE_BYTES - 1)) & ~(MTX_DMA_BURSTSIZE_BYTES - 1)) / + 4; + + /* ensure that data section (+stack) will not wrap in memory */ + IMG_DBG_ASSERT(fw_ctx->mtx_ram_size >= + (fw_ctx->mtx_topaz_fw_data_origin + (data_size * 4) - MTX_DMA_MEMORY_BASE)); + if (fw_ctx->mtx_ram_size < + (fw_ctx->mtx_topaz_fw_data_origin + (data_size * 4) - MTX_DMA_MEMORY_BASE)) + fw_ctx->initialized = FALSE; + + /* data section is already prepared/cached */ + /* Transfer the text section */ + if (fw_ctx->initialized) { + mtx_dmac_transfer(fw_ctx, 0, &text, 0, MTX_DMA_MEMORY_BASE, + text_size, FALSE); + } + /* Transfer the data section */ + if (fw_ctx->initialized) { + mtx_dmac_transfer(fw_ctx, 0, &data, 0, + fw_ctx->mtx_topaz_fw_data_origin, data_size, + FALSE); + } + + topaz_mmu_free(ctx->vxe_arg, &text); + topaz_mmu_free(ctx->vxe_arg, &data); + + /* Flush the MMU table cache used during code download */ + topaz_core_mmu_flush_cache(); +#ifdef DEBUG_ENCODER_DRIVER + if (fw_ctx->initialized) + pr_info("%s complete!\n", __func__); +#endif +} + +/* + * Load text and data sections onto an MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input load_method : Method to use for loading code + * @Input bTargetAll : Load to one (FALSE) or all (TRUE) MTX + */ +void mtx_load(void *dev_ctx, struct img_fw_context *fw_ctx, + enum mtx_load_method load_method) +{ + struct topaz_dev_ctx *ctx = (struct topaz_dev_ctx *)dev_ctx; + unsigned int reg; + unsigned short i; + + IMG_DBG_ASSERT(fw_ctx->initialized); + if (!fw_ctx->initialized) + return; + + fw_ctx->load_method = load_method; + + /* set target to current or all MTXs */ + mtx_set_target(fw_ctx); + + /* MTX Reset */ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_SOFT_RESET, + MASK_MTX_MTX_RESET); + ndelay(300); + + switch (load_method) { + case MTX_LOADMETHOD_REGIF: + /* Code Upload */ + mtx_reg_if_upload(fw_ctx, MTX_CORE_CODE_MEM, 0, + fw_ctx->mtx_topaz_fw_text_size, + fw_ctx->mtx_topaz_fw_text); + + /* Data Upload */ + mtx_reg_if_upload(fw_ctx, MTX_CORE_DATA_MEM, + fw_ctx->mtx_topaz_fw_data_origin - MTX_DMA_MEMORY_BASE, + fw_ctx->mtx_topaz_fw_data_size, + fw_ctx->mtx_topaz_fw_data); + break; + + case MTX_LOADMETHOD_DMA: + mtx_uploadfw(ctx, fw_ctx); + break; + + case MTX_LOADMETHOD_NONE: + break; + + default: + IMG_DBG_ASSERT(FALSE); + } + + /* if we have had any failures up to this point then return now */ + if (!fw_ctx->initialized) + return; + + if (load_method != MTX_LOADMETHOD_NONE) { + for (i = 5; i < 8; i++) + mtx_write_core_reg(fw_ctx, 0x1 | (i << 4), 0); + + /* Restore 8 Registers of D1 Bank */ + /* D1Re0, D1Ar5, D1Ar3, D1Ar1, D1RtP, D1.5, D1.6 and D1.7 */ + for (i = 5; i < 8; i++) + mtx_write_core_reg(fw_ctx, 0x2 | (i << 4), 0); + + /* Set Starting PC address */ + mtx_write_core_reg(fw_ctx, MTX_PC, PC_START_ADDRESS); + + /* Verify Starting PC */ + reg = mtx_read_core_reg(fw_ctx, MTX_PC); + +#ifdef DEBUG_ENCODER_DRIVER + pr_info("PC_START_ADDRESS = 0x%08X\n", reg); +#endif + IMG_DBG_ASSERT(reg == PC_START_ADDRESS); + } +} + +/* + * Deinitialise the given MTX context structure + * @Input fw_ctx : Pointer to the context of the target MTX + */ +void mtx_deinitialize(struct img_fw_context *fw_ctx) +{ + struct topaz_dev_ctx *ctx = (struct topaz_dev_ctx *)fw_ctx->dev_ctx; + unsigned int i; + + if (!fw_ctx->initialized) + pr_warn("Warning detected multi de-initialiseations\n"); + + for (i = 0; i < TOPAZHP_MAX_POSSIBLE_STREAMS; i++) { + if (fw_ctx->mtx_context_data_copy[i]) + topaz_mmu_free(ctx->vxe_arg, fw_ctx->mtx_context_data_copy[i]); + fw_ctx->mtx_context_data_copy[i] = NULL; + } + + kfree(fw_ctx->mtx_reg_copy); + fw_ctx->mtx_reg_copy = NULL; + fw_ctx->initialized = FALSE; +} + +/* + * Initialise the given MTX context structure + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input core_num : Core number of the MTX to target + * @Input codec : version of codec specific firmware to associate with this MTX + */ +int mtx_populate_fw_ctx(enum img_codec codec, struct img_fw_context *fw_ctx) +{ + unsigned int pipe_cnt; + unsigned int size; + unsigned int i; + + if (fw_ctx->initialized || fw_ctx->populated) + return IMG_ERROR_INVALID_CONTEXT; + + /* initialise Context structure */ + fw_ctx->mtx_reg_mem_space_addr = (void *)topaz_mem_space[REG_MTX].cpu_addr; + fw_ctx->topaz_multicore_reg_addr = (void *)topaz_mem_space[REG_TOPAZHP_MULTICORE].cpu_addr; + + fw_ctx->core_rev = VXE_RD_REG32(fw_ctx->topaz_multicore_reg_addr, + TOPAZHP_TOP_CR_TOPAZHP_CORE_REV); + fw_ctx->core_rev &= (MASK_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV | + MASK_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV | + MASK_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV); + fw_ctx->core_des1 = VXE_RD_REG32(fw_ctx->topaz_multicore_reg_addr, + TOPAZHP_TOP_CR_TOPAZHP_CORE_DES1); + + /* Number of hw pipes */ + pipe_cnt = VXE_RD_REG32(fw_ctx->topaz_multicore_reg_addr, TOPAZHP_TOP_CR_MULTICORE_HW_CFG); + pipe_cnt = (pipe_cnt & MASK_TOPAZHP_TOP_CR_NUM_CORES_SUPPORTED); + fw_ctx->hw_num_pipes = pipe_cnt; + + IMG_DBG_ASSERT(fw_ctx->hw_num_pipes > 0 && fw_ctx->hw_num_pipes <= TOPAZHP_MAX_NUM_PIPES); + + if (fw_ctx->hw_num_pipes <= 0 || fw_ctx->hw_num_pipes > TOPAZHP_MAX_NUM_PIPES) + return IMG_ERROR_INVALID_ID; + + for (i = 0; i < fw_ctx->hw_num_pipes; i++) + fw_ctx->topaz_reg_mem_space_addr[i] = + (void *)topaz_mem_space[REG_TOPAZHP_CORE_0 + (4 * i)].cpu_addr; + + fw_ctx->mtx_debug_val = VXE_RD_REG32(fw_ctx->topaz_multicore_reg_addr, + TOPAZHP_TOP_CR_MTX_DEBUG_MSTR); + + /* last bank size */ + size = 0x1 << + (F_EXTRACT(fw_ctx->mtx_debug_val, TOPAZHP_TOP_CR_MTX_MSTR_LAST_RAM_BANK_SIZE) + 2); + /* all other banks */ + fw_ctx->mtx_bank_size = 0x1 << + (F_EXTRACT(fw_ctx->mtx_debug_val, TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANK_SIZE) + 2); + /* total RAM size */ + fw_ctx->mtx_ram_size = size + + (fw_ctx->mtx_bank_size * + (F_EXTRACT(fw_ctx->mtx_debug_val, TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANKS) - 1)); + + fw_ctx->drv_has_mtx_ctrl = FALSE; + fw_ctx->access_control = 0; + + fw_ctx->active_ctx_mask = 0; + + if (mtx_select_fw_build(fw_ctx, codec) != IMG_SUCCESS) { + fw_ctx->populated = FALSE; + fw_ctx->initialized = FALSE; + return IMG_ERROR_UNDEFINED; + } + + if (fw_ctx->mtx_topaz_fw_data_size != 0) { + /* check FW fits in memory */ + /* could also add stack size estimate */ + size = 4 * fw_ctx->mtx_topaz_fw_data_size; + size += (fw_ctx->mtx_topaz_fw_data_origin - MTX_DMA_MEMORY_BASE); + if (size > fw_ctx->mtx_ram_size) { + IMG_DBG_ASSERT(fw_ctx->mtx_ram_size > size); + return IMG_ERROR_OUT_OF_MEMORY; + } + } + + fw_ctx->populated = TRUE; + return IMG_SUCCESS; +} + +void mtx_initialize(void *dev_ctx, struct img_fw_context *fw_ctx) +{ + struct topaz_dev_ctx *ctx = (struct topaz_dev_ctx *)dev_ctx; + unsigned int i = 0; + + if (fw_ctx->initialized) + return; + + if (fw_ctx->mtx_topaz_fw_data_size != 0) { + fw_ctx->mtx_reg_copy = kmalloc((53 * 4), GFP_KERNEL); + for (i = 0; i < TOPAZHP_MAX_POSSIBLE_STREAMS; i++) { + fw_ctx->mtx_context_data_copy[i] = kmalloc + (sizeof(*fw_ctx->mtx_context_data_copy[i]), + GFP_KERNEL); + if (!fw_ctx->mtx_context_data_copy[i]) + goto alloc_failed; + + if (topaz_mmu_alloc(ctx->topaz_mmu_ctx.mmu_context_handle, + ctx->vxe_arg, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + MTX_CONTEXT_SIZE, 64, + fw_ctx->mtx_context_data_copy[i])) { + pr_err("mmu_alloc for data copy failed!\n"); + kfree(fw_ctx->mtx_context_data_copy[i]); + fw_ctx->mtx_context_data_copy[i] = NULL; + goto alloc_failed; + } + } + + fw_ctx->dev_ctx = dev_ctx; + fw_ctx->initialized = TRUE; + } + + return; + +alloc_failed: + while (i > 0) { + topaz_mmu_free(ctx->vxe_arg, fw_ctx->mtx_context_data_copy[i - 1]); + kfree(fw_ctx->mtx_context_data_copy[i - 1]); + fw_ctx->mtx_context_data_copy[i - 1] = NULL; + i--; + } +} + +int mtx_get_fw_config_int(struct img_fw_context const * const fw_ctx, + unsigned char const * const name) +{ + const unsigned long max_len = 1024; + unsigned int ii; + + if (fw_ctx->mtx_topaz_fw_data_size == 0) { + IMG_DBG_ASSERT("FW context structure is not initialised!" == NULL); + return -1; + } + + for (ii = 0; ii < fw_ctx->int_defines.length; ii++) { + if (strncmp(fw_ctx->int_defines.names[ii], name, max_len) == 0) + return fw_ctx->int_defines.values[ii]; + } + + return -1; +} + +/* + * Start an MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + */ +void mtx_start(struct img_fw_context *fw_ctx) +{ + IMG_DBG_ASSERT(fw_ctx->initialized); + if (!fw_ctx->initialized) + return; + + /* target only the current MTX */ + mtx_set_target(fw_ctx); + + /* Turn on the thread */ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_ENABLE, + MASK_MTX_MTX_ENABLE); +} + +/* + * Stop an MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + */ +void mtx_stop(struct img_fw_context *fw_ctx) +{ + IMG_DBG_ASSERT(fw_ctx->initialized); + + /* target only the current MTX */ + mtx_set_target(fw_ctx); + + /* + * Turn off the thread by writing one to the MTX_TOFF field of the MTX_ENABLE + * register. + */ + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_ENABLE, + MASK_MTX_MTX_TOFF); +} + +/* + * Kick an MTX. + * @Input fw_ctx : Pointer to the context of the target MTX + * @Input kick_count : The number of kicks to register + */ +void mtx_kick(struct img_fw_context *fw_ctx, unsigned int kick_count) +{ + IMG_DBG_ASSERT(fw_ctx->initialized); + if (!fw_ctx->initialized) + return; + + /* target only the current MTX */ + mtx_set_target(fw_ctx); + + VXE_WR_REG32(fw_ctx->mtx_reg_mem_space_addr, MTX_CR_MTX_KICK, + kick_count); +} + +/* + * Wait for MTX to halt + * @Input fw_ctx : Pointer to the MTX context + */ +void mtx_wait_for_completion(struct img_fw_context *fw_ctx) +{ + IMG_DBG_ASSERT(fw_ctx->initialized); + + if (fw_ctx->load_method != MTX_LOADMETHOD_NONE) { + /* target only the current MTX */ + mtx_set_target(fw_ctx); + + /* Wait for the Completion */ + VXE_POLL_REG32_ISEQ(fw_ctx->mtx_reg_mem_space_addr, + MTX_CR_MTX_ENABLE, MASK_MTX_MTX_TOFF, + (MASK_MTX_MTX_TOFF | MASK_MTX_MTX_ENABLE), + TAL_REG_RD_WR_TRIES); + } +} + +unsigned int poll_hw_inactive(struct img_fw_context *fw_ctx) +{ + return VXE_POLL_REG32_ISEQ(fw_ctx->topaz_multicore_reg_addr, + MTX_SCRATCHREG_IDLE, + F_ENCODE(FW_IDLE_STATUS_IDLE, FW_IDLE_REG_STATUS), + MASK_FW_IDLE_REG_STATUS, + TAL_REG_RD_WR_TRIES); +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/img_soc_dmac_regs.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/img_soc_dmac_regs.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/img_soc_dmac_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/img_soc_dmac_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_img_soc_dmac_regs_h +#define _REGCONV_H_img_soc_dmac_regs_h + +/* Register DMAC_COUNT */ +#define IMG_SOC_DMAC_COUNT(X) (0x0004 + (32 * (X))) +#define MASK_IMG_SOC_BSWAP 0x40000000 +#define SHIFT_IMG_SOC_BSWAP 30 +#define SHIFT_IMG_SOC_PW 27 +#define MASK_IMG_SOC_PW 0x18000000 +#define MASK_IMG_SOC_DIR 0x04000000 +#define SHIFT_IMG_SOC_DIR 26 + +/* Register DMAC_COUNT */ +#define MASK_IMG_SOC_EN 0x00010000 +#define MASK_IMG_SOC_LIST_EN 0x00040000 + +/* Register DMAC_COUNT */ +#define MASK_IMG_SOC_PI 0x03000000 +#define SHIFT_IMG_SOC_PI 24 +#define MASK_IMG_SOC_CNT 0x0000FFFF +#define SHIFT_IMG_SOC_CNT 0 +#define MASK_IMG_SOC_TRANSFER_IEN 0x20000000 + +/* Register DMAC_IRQ_STAT */ +#define IMG_SOC_DMAC_IRQ_STAT(X) (0x000C + (32 * (X))) +#define MASK_IMG_SOC_TRANSFER_FIN 0x00020000 +#define SHIFT_IMG_SOC_TRANSFER_FIN 17 + +/* Register DMAC_PER_HOLD */ +#define IMG_SOC_DMAC_PER_HOLD(X) (0x0018 + (32 * (X))) + +/* Register DMAC_SETUP */ +#define IMG_SOC_DMAC_SETUP(X) (0x0000 + (32 * (X))) + +/* Register DMAC_PERIPH */ +#define IMG_SOC_DMAC_PERIPH(X) (0x0008 + (32 * (X))) +#define MASK_IMG_SOC_ACC_DEL 0xE0000000 +#define SHIFT_IMG_SOC_ACC_DEL 29 +#define MASK_IMG_SOC_INCR 0x08000000 +#define SHIFT_IMG_SOC_INCR 27 +#define MASK_IMG_SOC_BURST 0x07000000 +#define SHIFT_IMG_SOC_BURST 24 + +/* Register DMAC_PERIPHERAL_ADDR */ +#define IMG_SOC_DMAC_PERIPHERAL_ADDR(X) (0x0014 + (32 * (X))) + +#endif + diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/mtx_regs.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/mtx_regs.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/mtx_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/mtx_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_mtx_regs_h +#define _REGCONV_H_mtx_regs_h + +/* Register CR_MTX_ENABLE */ +#define MTX_CR_MTX_ENABLE 0x0000 +#define MASK_MTX_MTX_ENABLE 0x00000001 +#define MASK_MTX_MTX_TOFF 0x00000002 + +/* Register CR_MTX_KICK */ +#define MTX_CR_MTX_KICK 0x0080 + +/* Register CR_MTX_REGISTER_READ_WRITE_DATA */ +#define MTX_CR_MTX_REGISTER_READ_WRITE_DATA 0x00F8 + +/* Register CR_MTX_REGISTER_READ_WRITE_REQUEST */ +#define MTX_CR_MTX_REGISTER_READ_WRITE_REQUEST 0x00FC +#define MASK_MTX_MTX_RNW 0x00010000 +#define MASK_MTX_MTX_DREADY 0x80000000 + +/* Register CR_MTX_RAM_ACCESS_DATA_TRANSFER */ +#define MTX_CR_MTX_RAM_ACCESS_DATA_TRANSFER 0x0104 + +/* Register CR_MTX_RAM_ACCESS_CONTROL */ +#define MTX_CR_MTX_RAM_ACCESS_CONTROL 0x0108 +#define MASK_MTX_MTX_MCMR 0x00000001 +#define MASK_MTX_MTX_MCMAI 0x00000002 +#define SHIFT_MTX_MTX_MCMAI 1 +#define MASK_MTX_MTX_MCM_ADDR 0x000FFFFC +#define SHIFT_MTX_MTX_MCM_ADDR 2 +#define MASK_MTX_MTX_MCMID 0x0FF00000 +#define SHIFT_MTX_MTX_MCMID 20 + +/* Register CR_MTX_RAM_ACCESS_STATUS */ +#define MTX_CR_MTX_RAM_ACCESS_STATUS 0x010C +#define MASK_MTX_MTX_MTX_MCM_STAT 0x00000001 + +/* Register CR_MTX_SOFT_RESET */ +#define MTX_CR_MTX_SOFT_RESET 0x0200 +#define MASK_MTX_MTX_RESET 0x00000001 + +/* Register CR_MTX_SYSC_CDMAC */ +#define MTX_CR_MTX_SYSC_CDMAC 0x0340 +#define MASK_MTX_LENGTH 0x0000FFFF +#define SHIFT_MTX_LENGTH 0 +#define MASK_MTX_ENABLE 0x00010000 +#define SHIFT_MTX_ENABLE 16 +#define MASK_MTX_RNW 0x00020000 +#define SHIFT_MTX_RNW 17 +#define MASK_MTX_BURSTSIZE 0x07000000 +#define SHIFT_MTX_BURSTSIZE 24 + +/* Register CR_MTX_SYSC_CDMAA */ +#define MTX_CR_MTX_SYSC_CDMAA 0x0344 + +/* Register CR_MTX_SYSC_CDMAT */ +#define MTX_CR_MTX_SYSC_CDMAT 0x0350 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_coreext_regs.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_coreext_regs.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_coreext_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_coreext_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_topazhp_coreext_regs_h +#define _REGCONV_H_topazhp_coreext_regs_h + +/* Register CR_SCALER_INPUT_SIZE */ +#define MASK_TOPAZHP_EXT_CR_SCALER_INPUT_WIDTH_MIN1 0x00000FFF +#define SHIFT_TOPAZHP_EXT_CR_SCALER_INPUT_WIDTH_MIN1 0 +#define MASK_TOPAZHP_EXT_CR_SCALER_INPUT_HEIGHT_MIN1 0x0FFF0000 +#define SHIFT_TOPAZHP_EXT_CR_SCALER_INPUT_HEIGHT_MIN1 16 + +/* Register CR_SCALER_PITCH */ +#define MASK_TOPAZHP_EXT_CR_SCALER_INPUT_HOR_PITCH 0x00007FFF +#define SHIFT_TOPAZHP_EXT_CR_SCALER_INPUT_HOR_PITCH 0 +#define MASK_TOPAZHP_EXT_CR_SCALER_HOR_BILINEAR_FILTER 0x00008000 +#define SHIFT_TOPAZHP_EXT_CR_SCALER_HOR_BILINEAR_FILTER 15 +#define MASK_TOPAZHP_EXT_CR_SCALER_INPUT_VER_PITCH 0x7FFF0000 +#define SHIFT_TOPAZHP_EXT_CR_SCALER_INPUT_VER_PITCH 16 +#define MASK_TOPAZHP_EXT_CR_SCALER_VER_BILINEAR_FILTER 0x80000000 +#define SHIFT_TOPAZHP_EXT_CR_SCALER_VER_BILINEAR_FILTER 31 + +/* Register CR_SCALER_CROP */ +#define MASK_TOPAZHP_EXT_CR_SCALER_INPUT_CROP_VER 0x000000FF +#define SHIFT_TOPAZHP_EXT_CR_SCALER_INPUT_CROP_VER 0 +#define MASK_TOPAZHP_EXT_CR_SCALER_INPUT_CROP_HOR 0x0000FF00 +#define SHIFT_TOPAZHP_EXT_CR_SCALER_INPUT_CROP_HOR 8 + +/* Register CR_SCALER_CONTROL */ +#define MASK_TOPAZHP_EXT_CR_SCALER_ENABLE 0x00000001 +#define SHIFT_TOPAZHP_EXT_CR_SCALER_ENABLE 0 +#define MASK_TOPAZHP_EXT_CR_ENABLE_COLOUR_SPACE_CONVERSION 0x00000002 +#define SHIFT_TOPAZHP_EXT_CR_ENABLE_COLOUR_SPACE_CONVERSION 1 +#define MASK_TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT 0x007F0000 +#define SHIFT_TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT 16 + +/* 4:4:4, Any 3 colour space components plus reserved byte (e.g. + * RGB), 8-bit components, packed 32-bit per pixel in a single plane, 8 LSBits not used + */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444IL4XBCA8 0x0000007E + +/* 4:4:4, Any 3 colour space components plus reserved byte (e.g. + * RGB), 8-bit components, packed 32-bit per pixel in a single plane, 8 MSBits not used + */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444IL4ABCX8 0x0000007C + +/* RGB with 5 bits for R, 6 bits for G and 5 bits for B */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444IL3RGB565 0x00000070 + +/* 4:4:4, Y in 1 plane, CrCb interleaved in 2nd plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444PL12YCRCB8 0x0000006A + +/* 4:4:4, Y in 1 plane, CbCr interleaved in 2nd plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444PL12YCBCR8 0x00000068 + +/* 4:4:4, Y Cb Cr in 3 separate planes, 8-bit components + * (could also be ABC, but colour space conversion is not supported by input scaler + */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444PL111YCBCR8 0x00000060 + +/* 4:2:2, CrYCbY interleaved in a single plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3CRYCBY8 0x00000056 + +/* 4:2:2, CbYCrY interleaved in a single plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3CBYCRY8 0x00000054 + +/* 4:2:2, YCrYCb interleaved in a single plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3YCRYCB8 0x00000052 + +/* 4:2:2, YCbYCr interleaved in a single plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3YCBYCR8 0x00000050 + +/* 4:2:2, Y in 1 plane, CrCb interleaved in 2nd plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422PL12YCRCB8 0x0000004A + +/* 4:2:2, Y in 1 plane, CbCr interleaved in 2nd plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422PL12YCBCR8 0x00000048 + +/* 4:2:2, Y Cb Cr in 3 separate planes, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422PL111YCBCR8 0x00000040 + +/* 4:2:0, Y in 1 plane, CrCb interleaved in 2nd plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_420PL12YCRCB8 0x0000002A + +/* 4:2:0, Y in 1 plane, CbCr interleaved in 2nd plane, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_420PL12YCBCR8 0x00000028 + +/* 4:2:0, Y Cb Cr in 3 separate planes, 8-bit components */ +#define TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_420PL111YCBCR8 0x00000020 + +/* Register CR_CSC_SOURCE_MOD_Y_0 */ +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_MOD_00 0x00000003 +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_MOD_00 0 + +/* Subtract 1/2 maximum value from unsigned pixel component */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_00_MINUS_1_2 0x00000003 + +/* Subtract 1/16th maximum value from unsigned pixel component */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_00_MINUS_1_16 0x00000002 + +/* Source pixel component is unsigned */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_00_UNSIGNED 0x00000000 + +/* Register CR_CSC_SOURCE_MOD_Y_1 */ +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_MOD_01 0x00000003 +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_MOD_01 0 + +/* Subtract 1/2 maximum value from unsigned pixel component */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_01_MINUS_1_2 0x00000003 + +/* Subtract 1/16th maximum value from unsigned pixel component */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_01_MINUS_1_16 0x00000002 + +/* Source pixel component is unsigned */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_01_UNSIGNED 0x00000000 + +/* Register CR_CSC_SOURCE_CB_CR_1 */ +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CB_01 0x00000FFF +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CB_01 0 +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CR_01 16 + +/* Register CR_CSC_SOURCE_MOD_Y_2 */ +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_MOD_02 0x00000003 +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_MOD_02 0 + +/* Subtract 1/2 maximum value from unsigned pixel component */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_02_MINUS_1_2 0x00000003 + +/* Subtract 1/16th maximum value from unsigned pixel component */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_02_MINUS_1_16 0x00000002 + +/* Source pixel component is unsigned */ +#define TOPAZHP_EXT_CR_CSC_SOURCE_MOD_02_UNSIGNED 0x00000000 + +/* Register CR_CSC_SOURCE_CB_CR_2 */ +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CB_02 0x00000FFF +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CR_02 16 + +/* Register CR_CSC_OUTPUT_COEFF_0 */ +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP_00 0 +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MAX_CLIP_00 16 +#define MASK_TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_00 0x30000000 +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_00 28 + +/* Add 1/16th maximum value prior to applying unsigned clamping */ +#define TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_00_ADD_1_16 0x00000002 + +/* Register CR_CSC_OUTPUT_COEFF_1 */ +#define MASK_TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP_01 0x000003FF +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP_01 0 +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MAX_CLIP_01 16 +#define MASK_TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_01 0x30000000 +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_01 28 + +/* Add 1/2 maximum value prior to applying unsigned clamping */ +#define TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_01_ADD_1_2 0x00000003 +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_Y 0x0FFF0000 +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_Y 16 + +/* Register CR_CSC_SOURCE_CB_CR */ +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CB 0x00000FFF +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CB 0 +#define MASK_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CR 0x0FFF0000 +#define SHIFT_TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CR 16 + +/* Register CR_CSC_OUTPUT_COEFF */ +#define MASK_TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP 0x000003FF +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP 0 +#define MASK_TOPAZHP_EXT_CR_CSC_OUTPUT_MAX_CLIP 0x03FF0000 +#define SHIFT_TOPAZHP_EXT_CR_CSC_OUTPUT_MAX_CLIP 16 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_db_regs.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_db_regs.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_db_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_db_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_topaz_db_regs_h +#define _REGCONV_H_topaz_db_regs_h + +/* Register CR_DB_DISABLE_DEBLOCK_IDC */ +#define MASK_TOPAZ_DB_CR_DISABLE_DEBLOCK_IDC 0x00000003 +#define SHIFT_TOPAZ_DB_CR_DISABLE_DEBLOCK_IDC 0 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_core_regs.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_core_regs.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_core_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_core_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_topazhp_core_regs_h +#define _REGCONV_H_topazhp_core_regs_h + +/* Register CR_LRITC_CACHE_CHUNK_CONFIG */ +#define MASK_TOPAZHP_CR_CACHE_CHUNKS_PRIORITY 0x000000FF +#define SHIFT_TOPAZHP_CR_CACHE_CHUNKS_PRIORITY 0 +#define MASK_TOPAZHP_CR_CACHE_CHUNKS_MAX 0x0000FF00 +#define SHIFT_TOPAZHP_CR_CACHE_CHUNKS_MAX 8 +#define MASK_TOPAZHP_CR_CACHE_CHUNKS_PER_MB 0x00FF0000 +#define SHIFT_TOPAZHP_CR_CACHE_CHUNKS_PER_MB 16 + +/* Register CR_SEQ_CUR_PIC_ROW_STRIDE */ +#define MASK_TOPAZHP_CR_CUR_PIC_LUMA_STRIDE 0x0000FFC0 +#define SHIFT_TOPAZHP_CR_CUR_PIC_LUMA_STRIDE 6 +#define MASK_TOPAZHP_CR_CUR_PIC_CHROMA_STRIDE 0xFFC00000 +#define SHIFT_TOPAZHP_CR_CUR_PIC_CHROMA_STRIDE 22 + +/* Register CR_SEQUENCER_CONFIG */ +#define MASK_TOPAZHP_CR_ENCODER_STANDARD 0x00000007 +#define SHIFT_TOPAZHP_CR_ENCODER_STANDARD 0 +#define TOPAZHP_CR_ENCODER_STANDARD_H264 0x00000002 /* H264 encode */ +#define MASK_TOPAZHP_CR_FRAME_STORE_FORMAT 0x00000030 +#define SHIFT_TOPAZHP_CR_FRAME_STORE_FORMAT 4 + +/* 4:2:0 frame, with Luma, Cb and Cr all in separate planes (if the frame + * store actually contains 4:2:2 chroma, the chroma stride can be doubled + * so that it is read as 4:2:0) + */ +#define MASK_TOPAZHP_CR_FRAME_STORE_CHROMA_SWAP 0x00000040 +#define SHIFT_TOPAZHP_CR_FRAME_STORE_CHROMA_SWAP 6 +#define MASK_TOPAZHP_CR_FIELD_MODE 0x00000080 +#define SHIFT_TOPAZHP_CR_FIELD_MODE 7 +#define MASK_TOPAZHP_CR_REF_PIC0_VALID 0x00000100 +#define SHIFT_TOPAZHP_CR_REF_PIC0_VALID 8 +#define MASK_TOPAZHP_CR_REF_PIC1_VALID 0x00000200 +#define SHIFT_TOPAZHP_CR_REF_PIC1_VALID 9 +#define MASK_TOPAZHP_CR_REF_PIC1_EQUAL_PIC0 0x00000400 +#define SHIFT_TOPAZHP_CR_REF_PIC1_EQUAL_PIC0 10 +#define MASK_TOPAZHP_CR_ABOVE_OUT_OF_SLICE_VALID 0x00000800 +#define SHIFT_TOPAZHP_CR_ABOVE_OUT_OF_SLICE_VALID 11 +#define MASK_TOPAZHP_CR_TEMPORAL_COL_IN_VALID 0x00001000 +#define SHIFT_TOPAZHP_CR_TEMPORAL_COL_IN_VALID 12 +#define MASK_TOPAZHP_CR_TEMPORAL_PIC0_BELOW_IN_VALID 0x00002000 +#define SHIFT_TOPAZHP_CR_TEMPORAL_PIC0_BELOW_IN_VALID 13 +#define MASK_TOPAZHP_CR_TEMPORAL_PIC1_BELOW_IN_VALID 0x00004000 +#define SHIFT_TOPAZHP_CR_TEMPORAL_PIC1_BELOW_IN_VALID 14 +#define MASK_TOPAZHP_CR_DEBLOCK_ENABLE 0x00008000 +#define SHIFT_TOPAZHP_CR_DEBLOCK_ENABLE 15 +#define MASK_TOPAZHP_CR_WRITE_TEMPORAL_COL_VALID 0x00010000 +#define SHIFT_TOPAZHP_CR_WRITE_TEMPORAL_COL_VALID 16 +#define MASK_TOPAZHP_CR_WRITE_TEMPORAL_PIC0_BELOW_VALID 0x00020000 +#define SHIFT_TOPAZHP_CR_WRITE_TEMPORAL_PIC0_BELOW_VALID 17 +#define MASK_TOPAZHP_CR_WRITE_TEMPORAL_PIC1_BELOW_VALID 0x00040000 +#define SHIFT_TOPAZHP_CR_WRITE_TEMPORAL_PIC1_BELOW_VALID 18 +#define MASK_TOPAZHP_CR_WRITE_MB_FIRST_STAGE_VALID 0x00200000 +#define SHIFT_TOPAZHP_CR_WRITE_MB_FIRST_STAGE_VALID 21 +#define MASK_TOPAZHP_CR_MB_CONTROL_IN_VALID 0x00800000 +#define SHIFT_TOPAZHP_CR_MB_CONTROL_IN_VALID 23 +#define MASK_TOPAZHP_CR_BEST_MULTIPASS_OUT_VALID 0x10000000 +#define SHIFT_TOPAZHP_CR_BEST_MULTIPASS_OUT_VALID 28 +#define MASK_TOPAZHP_CR_BEST_MVS_OUT_DISABLE 0x40000000 +#define SHIFT_TOPAZHP_CR_BEST_MVS_OUT_DISABLE 30 +#define MASK_TOPAZHP_CR_SLICE_TYPE 0x00030000 +#define SHIFT_TOPAZHP_CR_SLICE_TYPE 16 +#define TOPAZHP_CR_SLICE_TYPE_B_SLICE 0x00000002 /* B-slice */ +#define TOPAZHP_CR_SLICE_TYPE_P_SLICE 0x00000001 /* P-slice */ +#define TOPAZHP_CR_SLICE_TYPE_I_SLICE 0x00000000 /* I-slice */ +#define MASK_TOPAZHP_CR_MVCALC_RESTRICT_PICTURE 0x00010000 +#define SHIFT_TOPAZHP_CR_MVCALC_RESTRICT_PICTURE 16 + +/* Register CR_MVCALC_CONFIG */ +#define MASK_TOPAZHP_CR_MVCALC_GRID_MB_X_STEP 0x0000000F +#define SHIFT_TOPAZHP_CR_MVCALC_GRID_MB_X_STEP 0 +#define MASK_TOPAZHP_CR_MVCALC_GRID_MB_Y_STEP 0x00000F00 +#define SHIFT_TOPAZHP_CR_MVCALC_GRID_MB_Y_STEP 8 +#define MASK_TOPAZHP_CR_MVCALC_GRID_SUB_STEP 0x000F0000 +#define SHIFT_TOPAZHP_CR_MVCALC_GRID_SUB_STEP 16 +#define MASK_TOPAZHP_CR_MVCALC_GRID_DISABLE 0x00800000 +#define SHIFT_TOPAZHP_CR_MVCALC_GRID_DISABLE 23 +#define MASK_TOPAZHP_CR_MVCALC_IPE0_JITTER_FACTOR 0x03000000 +#define SHIFT_TOPAZHP_CR_MVCALC_IPE0_JITTER_FACTOR 24 +#define MASK_TOPAZHP_CR_MVCALC_IPE1_JITTER_FACTOR 0x0C000000 +#define SHIFT_TOPAZHP_CR_MVCALC_IPE1_JITTER_FACTOR 26 +#define MASK_TOPAZHP_CR_MVCALC_JITTER_POINTER_RST 0x10000000 +#define MASK_TOPAZHP_CR_MVCALC_NO_PSEUDO_DUPLICATES 0x20000000 +#define SHIFT_TOPAZHP_CR_MVCALC_NO_PSEUDO_DUPLICATES 29 +#define MASK_TOPAZHP_CR_MVCALC_DUP_VEC_MARGIN 0xC0000000 +#define SHIFT_TOPAZHP_CR_MVCALC_DUP_VEC_MARGIN 30 + +/* Register CR_MVCALC_COLOCATED */ +#define MASK_TOPAZHP_CR_COL_DIST_SCALE_FACT 0x000007FF +#define SHIFT_TOPAZHP_CR_COL_DIST_SCALE_FACT 0 + +/* Register CR_MVCALC_BELOW */ +#define MASK_TOPAZHP_CR_PIC0_DIST_SCALE_FACTOR 0x000007FF +#define SHIFT_TOPAZHP_CR_PIC0_DIST_SCALE_FACTOR 0 +#define MASK_TOPAZHP_CR_PIC1_DIST_SCALE_FACTOR 0x07FF0000 +#define SHIFT_TOPAZHP_CR_PIC1_DIST_SCALE_FACTOR 16 + +/* Register CR_PREFETCH_QP */ +#define MASK_TOPAZHP_CR_SKIPPED_CODED_SCALE_IDX 0x00007000 +#define SHIFT_TOPAZHP_CR_SKIPPED_CODED_SCALE_IDX 12 +#define MASK_TOPAZHP_CR_INTER_INTRA_SCALE_IDX 0x00000700 +#define SHIFT_TOPAZHP_CR_INTER_INTRA_SCALE_IDX 8 + +/* Register CR_MB_HOST_CONTROL */ +#define MASK_TOPAZHP_CR_MB_HOST_QP 0x00000001 +#define SHIFT_TOPAZHP_CR_MB_HOST_QP 0 +#define MASK_TOPAZHP_CR_MB_HOST_SKIPPED_CODED_SCALE 0x00000002 +#define SHIFT_TOPAZHP_CR_MB_HOST_SKIPPED_CODED_SCALE 1 +#define MASK_TOPAZHP_CR_MB_HOST_INTER_INTRA_SCALE 0x00000004 +#define SHIFT_TOPAZHP_CR_MB_HOST_INTER_INTRA_SCALE 2 +#define MASK_TOPAZHP_CR_H264COMP_8X8_TRANSFORM 0x00000001 +#define SHIFT_TOPAZHP_CR_H264COMP_8X8_TRANSFORM 0 +#define MASK_TOPAZHP_CR_H264COMP_CONSTRAINED_INTRA 0x00000002 +#define SHIFT_TOPAZHP_CR_H264COMP_CONSTRAINED_INTRA 1 +#define MASK_TOPAZHP_CR_H264COMP_8X8_CAVLC 0x00000004 +#define SHIFT_TOPAZHP_CR_H264COMP_8X8_CAVLC 2 +#define MASK_TOPAZHP_CR_H264COMP_DEFAULT_SCALING_LIST 0x00000008 +#define SHIFT_TOPAZHP_CR_H264COMP_DEFAULT_SCALING_LIST 3 +#define MASK_TOPAZHP_CR_H264COMP_ADAPT_ROUND_ENABLE 0x00000010 +#define SHIFT_TOPAZHP_CR_H264COMP_ADAPT_ROUND_ENABLE 4 +#define MASK_TOPAZHP_CR_H264COMP_VIDEO_CONF_ENABLE 0x00000020 +#define SHIFT_TOPAZHP_CR_H264COMP_VIDEO_CONF_ENABLE 5 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_8X8_INTER_LUMA_ENABLE 0x00000080 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_8X8_INTER_LUMA_ENABLE 7 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_CR_ENABLE 0x00000100 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_CR_ENABLE 8 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_CB_ENABLE 0x00000200 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_CB_ENABLE 9 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_LUMA_ENABLE 0x00000400 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_LUMA_ENABLE 10 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_8X8_INTRA_LUMA_ENABLE 0x00000800 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_8X8_INTRA_LUMA_ENABLE 11 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_CR_ENABLE 0x00001000 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_CR_ENABLE 12 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_CB_ENABLE 0x00002000 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_CB_ENABLE 13 +#define MASK_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_LUMA_ENABLE 0x00004000 +#define SHIFT_TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_LUMA_ENABLE 14 +#define MASK_TOPAZHP_CR_H264COMP_LOSSLESS 0x00010000 +#define SHIFT_TOPAZHP_CR_H264COMP_LOSSLESS 16 +#define MASK_TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER 0x00020000 +#define SHIFT_TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER 17 + +/* The Intra8x8 Pre-filter is performed in Lossless Mode. H.264 standard lossless. */ +#define TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER_FILTER 0x00000001 + +/* The Intra8x8 Pre-filter is bypassed in Lossless Mode. x264 compatibility mode for lossless. */ +#define TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER_BYPASS 0x00000000 + +/* Register CR_IPE_CONTROL */ +#define MASK_TOPAZHP_CR_IPE_BLOCKSIZE 0x00000003 +#define SHIFT_TOPAZHP_CR_IPE_BLOCKSIZE 0 +#define MASK_TOPAZHP_CR_IPE_16X8_ENABLE 0x00000004 +#define SHIFT_TOPAZHP_CR_IPE_16X8_ENABLE 2 +#define MASK_TOPAZHP_CR_IPE_8X16_ENABLE 0x00000008 +#define SHIFT_TOPAZHP_CR_IPE_8X16_ENABLE 3 +#define MASK_TOPAZHP_CR_IPE_Y_FINE_SEARCH 0x00000030 +#define SHIFT_TOPAZHP_CR_IPE_Y_FINE_SEARCH 4 +#define MASK_TOPAZHP_CR_IPE_4X4_SEARCH 0x00000040 +#define SHIFT_TOPAZHP_CR_IPE_4X4_SEARCH 6 +#define MASK_TOPAZHP_CR_IPE_LRITC_BOUNDARY 0x00000300 +#define SHIFT_TOPAZHP_CR_IPE_LRITC_BOUNDARY 8 +#define MASK_TOPAZHP_CR_IPE_HIGH_LATENCY 0x00001000 +#define SHIFT_TOPAZHP_CR_IPE_HIGH_LATENCY 12 +#define MASK_TOPAZHP_CR_IPE_MV_NUMBER_RESTRICTION 0x00004000 +#define SHIFT_TOPAZHP_CR_IPE_MV_NUMBER_RESTRICTION 14 + +/* Register CR_IPE_VECTOR_CLIPPING */ +#define MASK_TOPAZHP_CR_IPE_VECTOR_CLIPPING_X 0x000000FF +#define SHIFT_TOPAZHP_CR_IPE_VECTOR_CLIPPING_X 0 +#define MASK_TOPAZHP_CR_IPE_VECTOR_CLIPPING_Y 0x0000FF00 +#define SHIFT_TOPAZHP_CR_IPE_VECTOR_CLIPPING_Y 8 +#define MASK_TOPAZHP_CR_IPE_VECTOR_CLIPPING_ENABLED 0x00010000 +#define SHIFT_TOPAZHP_CR_IPE_VECTOR_CLIPPING_ENABLED 16 + +/* Register CR_JMCOMP_CARC_CONTROL_0 */ +#define MASK_TOPAZHP_CR_CARC_NEG_SCALE 0x3F000000 +#define SHIFT_TOPAZHP_CR_CARC_NEG_SCALE 24 +#define MASK_TOPAZHP_CR_CARC_NEG_RANGE 0x001F0000 +#define SHIFT_TOPAZHP_CR_CARC_NEG_RANGE 16 +#define MASK_TOPAZHP_CR_CARC_POS_SCALE 0x00003F00 +#define SHIFT_TOPAZHP_CR_CARC_POS_SCALE 8 +#define MASK_TOPAZHP_CR_CARC_POS_RANGE 0x0000001F +#define SHIFT_TOPAZHP_CR_CARC_POS_RANGE 0 + +/* Register CR_JMCOMP_CARC_CONTROL_1 */ +#define MASK_TOPAZHP_CR_CARC_SHIFT 0x03000000 +#define SHIFT_TOPAZHP_CR_CARC_SHIFT 24 +#define MASK_TOPAZHP_CR_CARC_CUTOFF 0x00F00000 +#define SHIFT_TOPAZHP_CR_CARC_CUTOFF 20 +#define MASK_TOPAZHP_CR_CARC_THRESHOLD 0x0007FF00 +#define SHIFT_TOPAZHP_CR_CARC_THRESHOLD 8 +#define MASK_TOPAZHP_CR_SPE_MVD_CLIP_ENABLE 0x80000000 +#define SHIFT_TOPAZHP_CR_SPE_MVD_CLIP_ENABLE 31 + +/* Register CR_PRED_COMB_CONTROL */ +#define MASK_TOPAZHP_CR_INTRA4X4_DISABLE 0x00000001 +#define SHIFT_TOPAZHP_CR_INTRA4X4_DISABLE 0 +#define MASK_TOPAZHP_CR_INTRA8X8_DISABLE 0x00000002 +#define SHIFT_TOPAZHP_CR_INTRA8X8_DISABLE 1 +#define MASK_TOPAZHP_CR_INTRA16X16_DISABLE 0x00000004 +#define SHIFT_TOPAZHP_CR_INTRA16X16_DISABLE 2 +#define MASK_TOPAZHP_CR_INTER8X8_DISABLE 0x00000010 +#define SHIFT_TOPAZHP_CR_INTER8X8_DISABLE 4 +#define MASK_TOPAZHP_CR_B_PIC0_DISABLE 0x00000100 +#define SHIFT_TOPAZHP_CR_B_PIC0_DISABLE 8 +#define MASK_TOPAZHP_CR_B_PIC1_DISABLE 0x00000200 +#define SHIFT_TOPAZHP_CR_B_PIC1_DISABLE 9 +#define MASK_TOPAZHP_CR_INTER_INTRA_SCALE_ENABLE 0x00001000 +#define SHIFT_TOPAZHP_CR_INTER_INTRA_SCALE_ENABLE 12 +#define MASK_TOPAZHP_CR_CUMULATIVE_BIASES_ENABLE 0x00000800 +#define SHIFT_TOPAZHP_CR_CUMULATIVE_BIASES_ENABLE 11 +#define MASK_TOPAZHP_CR_SKIPPED_CODED_SCALE_ENABLE 0x00002000 +#define SHIFT_TOPAZHP_CR_SKIPPED_CODED_SCALE_ENABLE 13 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_multicore_regs_old.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_multicore_regs_old.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_multicore_regs_old.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topazhp_multicore_regs_old.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_topazhp_multicore_regs_old_h +#define _REGCONV_H_topazhp_multicore_regs_old_h + +///* Register CR_LAMBDA_DC_TABLE */ +#define MASK_TOPAZHP_CR_TEMPORAL_BLEND 0x001F0000 +#define SHIFT_TOPAZHP_CR_TEMPORAL_BLEND 16 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_vlc_regs.h b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_vlc_regs.h --- a/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_vlc_regs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/reg_headers/topaz_vlc_regs.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * firmware header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _REGCONV_H_topaz_vlc_regs_h +#define _REGCONV_H_topaz_vlc_regs_h + +///* Register CR_VLC_CONTROL */ +#define MASK_TOPAZ_VLC_CR_CODEC 0x00000003 +#define SHIFT_TOPAZ_VLC_CR_CODEC 0 +#define MASK_TOPAZ_VLC_CR_CABAC_ENABLE 0x00000100 +#define SHIFT_TOPAZ_VLC_CR_CABAC_ENABLE 8 +#define MASK_TOPAZ_VLC_CR_VLC_FIELD_CODED 0x00000200 +#define SHIFT_TOPAZ_VLC_CR_VLC_FIELD_CODED 9 +#define MASK_TOPAZ_VLC_CR_VLC_8X8_TRANSFORM 0x00000400 +#define SHIFT_TOPAZ_VLC_CR_VLC_8X8_TRANSFORM 10 +#define MASK_TOPAZ_VLC_CR_VLC_CONSTRAINED_INTRA 0x00000800 +#define SHIFT_TOPAZ_VLC_CR_VLC_CONSTRAINED_INTRA 11 +#define MASK_TOPAZ_VLC_CR_CODEC_EXTEND 0x10000000 +#define SHIFT_TOPAZ_VLC_CR_CODEC_EXTEND 28 + +///* Register CR_VLC_IPCM_0 */ +#define MASK_TOPAZ_VLC_CR_CABAC_DB_MARGIN 0x03FF0000 +#define SHIFT_TOPAZ_VLC_CR_CABAC_DB_MARGIN 16 +#define MASK_TOPAZ_VLC_CR_CABAC_BIN_FLEX 0x00001FFF +#define SHIFT_TOPAZ_VLC_CR_CABAC_BIN_FLEX 0 +#define MASK_TOPAZ_VLC_CR_IPCM_THRESHOLD 0x00000FFF +#define SHIFT_TOPAZ_VLC_CR_IPCM_THRESHOLD 0 +#define MASK_TOPAZ_VLC_CR_CABAC_BIN_LIMIT 0x1FFF0000 +#define SHIFT_TOPAZ_VLC_CR_CABAC_BIN_LIMIT 16 +#define MASK_TOPAZ_VLC_CR_SLICE_SIZE_LIMIT 0x00FFFFFF +#define SHIFT_TOPAZ_VLC_CR_SLICE_SIZE_LIMIT 0 +#define MASK_TOPAZ_VLC_CR_SLICE_MBS_LIMIT 0x00003FFF +#define SHIFT_TOPAZ_VLC_CR_SLICE_MBS_LIMIT 0 + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/target_config.h b/drivers/media/platform/vxe-vxd/encoder/target_config.h --- a/drivers/media/platform/vxe-vxd/encoder/target_config.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/target_config.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Device specific memory configuration + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef __TARGET_CONFIG_H__ +#define __TARGET_CONFIG_H__ + +#include "target.h" + +/* Order MUST match with topaz_mem_space definition */ +enum topaz_mem_space_idx { + REG_TOPAZHP_MULTICORE = 0, + REG_DMAC, + REG_COMMS, + REG_MTX, + REG_MMU, + REG_TOPAZHP_TEST, + REG_MTX_RAM, + REG_TOPAZHP_CORE_0, + REG_TOPAZHP_VLC_CORE_0, + REG_TOPAZHP_DEBLOCKER_CORE_0, + REG_TOPAZHP_COREEXT_0, + REG_TOPAZHP_CORE_1, + REG_TOPAZHP_VLC_CORE_1, + REG_TOPAZHP_DEBLOCKER_CORE_1, + REG_TOPAZHP_COREEXT_1, + REG_TOPAZHP_CORE_2, + REG_TOPAZHP_VLC_CORE_2, + REG_TOPAZHP_DEBLOCKER_CORE_2, + REG_TOPAZHP_COREEXT_2, + REG_TOPAZHP_CORE_3, + REG_TOPAZHP_VLC_CORE_3, + REG_TOPAZHP_DEBLOCKER_CORE_3, + REG_TOPAZHP_COREEXT_3, + FW, + SYSMEM, + MEMSYSMEM, + MEM, + FB, + MEMDMAC_00, + MEMDMAC_01, + MEMDMAC_02, + MEM_SPACE_FORCE32BITS = 0x7FFFFFFFU +}; + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/target.h b/drivers/media/platform/vxe-vxd/encoder/target.h --- a/drivers/media/platform/vxe-vxd/encoder/target.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/target.h 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * target interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#if !defined(__TARGET_H__) +#define __TARGET_H__ + +#include + +#define TARGET_NO_IRQ (999) /* Interrupt number when no interrupt exists */ + +/* + * The memory space types + */ +enum mem_space_type { + MEMSPACE_REGISTER, /* Memory space is mapped to device registers */ + MEMSPACE_MEMORY, /* Memory space is mapped to device memory */ + MEMSPACE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * This structure contains all information about a device register + */ +struct mem_space_reg { + unsigned long long addr; /* Base address of device registers */ + unsigned int size; /* Size of device register block */ + unsigned int intr_num; /* The interrupt number */ +}; + +/* + * This structure contains all information about a device memory region + */ +struct mem_space_mem { + unsigned long long addr; /* Base address of memory region */ + unsigned long long size; /* Size of memory region */ + unsigned long long guard_band; /* Memory guard band */ +}; + +/* + * This structure contains all information about the device memory space + */ +struct mem_space { + unsigned char *name; /* Memory space name */ + enum mem_space_type type; /* Memory space type */ + union { + struct mem_space_reg reg; /* Device register info */ + struct mem_space_mem mem; /* Device memory region info */ + }; + + unsigned long long cpu_addr; /* Cpu KM address for the mem space */ +}; + +struct target_config { + unsigned int num_mem_spaces; + struct mem_space *mem_spaces; +}; + +#endif /* __TARGET_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topaz_api.c b/drivers/media/platform/vxe-vxd/encoder/topaz_api.c --- a/drivers/media/platform/vxe-vxd/encoder/topaz_api.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topaz_api.c 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,3875 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Encoder Core API function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include + +#include "fw_headers/coreflags.h" +#include "fw_headers/topazscfwif.h" +#include "header_gen.h" +#include "img_errors.h" +#include "img_mem_man.h" +#include "lst.h" +#include "reg_headers/topaz_coreext_regs.h" +#include "reg_headers/topazhp_core_regs.h" +#include "reg_headers/topaz_vlc_regs.h" +#include "reg_headers/topaz_db_regs.h" +#include "topaz_color_formats.h" +#include "topaz_device.h" +#include "topaz_api.h" +#include "topaz_api_utils.h" +#include "topazmmu.h" +#include "vxe_public_regdefs.h" +#include "img_errors.h" + +#define TOPAZ_TIMEOUT_RETRIES (5000000) +#define TOPAZ_TIMEOUT_WAIT_FOR_SPACE (500) + +#define COMM_WB_DATA_BUF_SIZE (64) + +/* + * All contexts should be able to send as many commands as possible before waiting for a response. + * There must be enough command memory buffers for all applicable commands, that is: + * -To fill all source slots + * -To supply custom quant data + */ +#define TOPAZ_CMD_DATA_BUF_NUM ((MAX_SOURCE_SLOTS_SL + 1) * TOPAZHP_MAX_POSSIBLE_STREAMS) +#define TOPAZ_CMD_DATA_BUF_SIZE (64) +#define COMM_CMD_DATA_BUF_SLOT_NONE 0xFF + +struct topaz_core_context *global_topaz_core_context; + +static unsigned char global_cmd_data_busy[TOPAZ_CMD_DATA_BUF_NUM]; +struct vidio_ddbufinfo global_cmd_data_dev_addr; /* Data section */ +struct vidio_ddbufinfo global_cmd_data_info[TOPAZ_CMD_DATA_BUF_NUM]; /* Data section */ +static unsigned char global_pipe_usage[TOPAZHP_MAX_NUM_PIPES] = { 0 }; + +struct vidio_ddbufinfo *global_wb_data_info; +static unsigned char is_topaz_core_initialized; + +/* + * Get a buffer reference + */ +static int topaz_get_buffer(struct topaz_stream_context *str_ctx, + struct img_buffer *buffer, void **lin_address, + unsigned char update_host_memory) +{ + if (buffer->lock == NOTDEVICEMEMORY) { + *lin_address = buffer->mem_info.cpu_virt; + return IMG_SUCCESS; + } + + if (buffer->lock == SW_LOCK) + return IMG_ERROR_SURFACE_LOCKED; + + if (update_host_memory) + topaz_update_host_mem(str_ctx->vxe_ctx, &buffer->mem_info); + + *lin_address = buffer->mem_info.cpu_virt; + buffer->lock = SW_LOCK; + + return IMG_SUCCESS; +} + +static int topaz_release_buffer(struct topaz_stream_context *str_ctx, + struct img_buffer *buffer, unsigned char update_device_memory) +{ + if (buffer->lock == NOTDEVICEMEMORY) + return IMG_SUCCESS; + + if (buffer->lock == HW_LOCK) + return IMG_ERROR_SURFACE_LOCKED; + + buffer->lock = BUFFER_FREE; + + if (update_device_memory) + topaz_update_device_mem(str_ctx->vxe_ctx, &buffer->mem_info); + + return IMG_SUCCESS; +} + +static int topaz_get_cmd_data_buffer(struct vidio_ddbufinfo **mem_info) +{ + int index = 0; + int res = IMG_SUCCESS; + + mutex_lock_nested(global_topaz_core_context->mutex, SUBCLASS_TOPAZ_API); + + do { + if (!global_cmd_data_busy[index]) + break; + index++; + } while (index < ARRAY_SIZE(global_cmd_data_info)); + + if (index == ARRAY_SIZE(global_cmd_data_info)) { + res = IMG_ERROR_UNEXPECTED_STATE; + } else { + global_cmd_data_busy[index] = TRUE; + *mem_info = &global_cmd_data_info[index]; + } + + mutex_unlock((struct mutex *)global_topaz_core_context->mutex); + + return res; +} + +static int topaz_release_cmd_data_buffer(struct vidio_ddbufinfo *mem_info) +{ + int index = 0; + int res = IMG_ERROR_UNEXPECTED_STATE; + + mutex_lock_nested(global_topaz_core_context->mutex, SUBCLASS_TOPAZ_API); + + do { + if (mem_info == &global_cmd_data_info[index]) { + global_cmd_data_busy[index] = FALSE; + res = IMG_SUCCESS; + break; + } + index++; + } while (index < ARRAY_SIZE(global_cmd_data_info)); + + mutex_unlock((struct mutex *)global_topaz_core_context->mutex); + + return res; +} + +/* + * Get a buffer reference + */ +static int get_coded_buffer(struct topaz_stream_context *str_ctx, void **lin_address, + unsigned char update_host_memory, unsigned char coded_package_idx) +{ + struct img_enc_context *enc; + struct img_video_context *video; + unsigned char coded_buffer_idx; + unsigned char found = FALSE; + unsigned int *address; + struct coded_data_hdr *coded_datahdr = NULL; + unsigned int offset_buffer_header = 0, offset_coded_buffer = 0; + /* Tells if all the slices have been retrieved */ + unsigned char all_slice_retrieved = FALSE; + /* Tells if we have reach the last coded buffer used or not */ + unsigned char slice_break = FALSE; + /* Tells if we are at the beginning of a slice or not */ + unsigned char new_coded_header = TRUE; + /* Tells the number of bytes remaining to be retrieved */ + unsigned int total_byte_written = 0; + unsigned int coded_slices_so_far = 0; + unsigned int coded_slices_in_buffer = 0; + + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->coded_package[coded_package_idx]->header_buffer->lock == SW_LOCK) + return IMG_ERROR_UNDEFINED; + + /* Retrieve the FW Package memory. Get linear address */ + video->coded_package[coded_package_idx]->mtx_info.coded_package_fw = + (struct coded_package_dma_info *)(&video->coded_package[coded_package_idx]->mtx_info + .code_package_fw_buffer->mem_info); + + if (update_host_memory) { + /* Go through all the coded buffers */ + for (coded_buffer_idx = 0; coded_buffer_idx < MAX_CODED_BUFFERS_PER_PACKAGE; + coded_buffer_idx++) { + /* Reset the Offset */ + offset_coded_buffer = 0; + do { + if (new_coded_header) { // beginning of a slice + slice_break = FALSE; + /* Get the coded header information */ + *lin_address = video->coded_package + [coded_package_idx]->header_buffer->mem_info.cpu_virt; + address = *lin_address; + /* Getting the nth buffer header */ + coded_datahdr = (struct coded_data_hdr *)(address + + (offset_buffer_header / 4)); + total_byte_written = coded_datahdr->bytes_written; + coded_slices_so_far = + F_DECODE(coded_datahdr->extra_feedback, + CODED_SLICES_SO_FAR); + coded_slices_in_buffer = + F_DECODE(coded_datahdr->extra_feedback, + CODED_SLICES_IN_BUFFER); + + /* Increment the offset in the coded header information + * buffer in order to point on the next header + */ + offset_buffer_header += CODED_BUFFER_INFO_SECTION_SIZE; + } + + if (!new_coded_header) { + /* Retrieve the last coded data */ + offset_coded_buffer = ALIGN_16(offset_coded_buffer + + total_byte_written); + slice_break = TRUE; + /* On next loop we will be at the start of a new slice */ + new_coded_header = TRUE; + } else { + /* + * New slice : Read all the bytes written for this slice + * Go after what we read, next 16bit align address + */ + offset_coded_buffer = + ALIGN_16(offset_coded_buffer + + coded_datahdr->bytes_written); + if (F_DECODE(coded_datahdr->extra_feedback, + CODED_SLICES_SO_FAR) == + F_DECODE(coded_datahdr->extra_feedback, + CODED_SLICES_IN_BUFFER)) { + /* We now have all the slices for this coded buffer, + * we should not try to read further. + */ + all_slice_retrieved = TRUE; + break; + } + } + } while (coded_slices_so_far != coded_slices_in_buffer); + + if (all_slice_retrieved || slice_break) { + /* If we are NOT in the middle of a slice */ + found = TRUE; + /* We lock this last buffer */ + video->coded_package[coded_package_idx]->coded_buffer + [coded_buffer_idx]->lock = SW_LOCK; + /* This function will do nothing if -debugCRCs (1 or 2) has not + * been specified on the command line + */ + break; + } + } + + if (!found) + topaz_update_host_mem(str_ctx->vxe_ctx, &video->coded_package + [coded_package_idx]->header_buffer->mem_info); + } + + /* address of first header if all buffer finish in middle of + * slice or !bUpdateHostMemory, last red header otherwise + */ + *lin_address = video->coded_package[coded_package_idx]->header_buffer->mem_info.cpu_virt; + /* Lock-it */ + video->coded_package[coded_package_idx]->header_buffer->lock = SW_LOCK; + + return IMG_SUCCESS; +} + +static void combine_feedback(struct topaz_stream_context *str_ctx, + unsigned char active_coded_package_idx, unsigned int *feedback, + unsigned int *extra_feedback, unsigned int *bytes_coded) +{ + struct img_enc_context *enc = str_ctx->enc_ctx; + struct coded_data_hdr *coded_datahdr; + unsigned int offset = 0; + unsigned int min_bu = 0xFFFFFFFF; + unsigned int coded_bytes = 0; + unsigned int bu; + unsigned int coded_slices_so_far; + unsigned int coded_slices_in_buffer; + + do { + /* we should be able to rely on the linear pointer here + * as the coded data header should have been updated. + */ + coded_datahdr = (struct coded_data_hdr *)((unsigned long)(enc->video->coded_package + [active_coded_package_idx]->header_buffer->mem_info.cpu_virt) + + offset); + + IMG_DBG_ASSERT(coded_datahdr); + if (!coded_datahdr) + return; + + bu = F_DECODE(coded_datahdr->feedback, CODED_FIRST_BU); + coded_slices_so_far = F_DECODE(coded_datahdr->extra_feedback, CODED_SLICES_SO_FAR); + coded_slices_in_buffer = F_DECODE(coded_datahdr->extra_feedback, + CODED_SLICES_IN_BUFFER); + + if (bu < min_bu) + min_bu = bu; + + coded_bytes += coded_datahdr->bytes_written; + offset += CODED_BUFFER_INFO_SECTION_SIZE; + } while (coded_slices_so_far != coded_slices_in_buffer); + + *bytes_coded = coded_bytes; + *feedback = F_INSERT(coded_datahdr->feedback, min_bu, CODED_FIRST_BU); + *extra_feedback = coded_datahdr->extra_feedback; +} + +/* + * Move around the reconstructed data and handle the list for frame reordering + */ +static void process_reconstructed(struct topaz_stream_context *str_ctx, unsigned char is_coded, + enum img_frame_type frame_type, struct list_item **recon_list) +{ + struct img_video_context *video = str_ctx->enc_ctx->video; + unsigned char *tmp_buffer; + unsigned short width, height; + struct list_item *new_item; + struct img_recon_node *new_node; + struct list_item *current_item; + + *recon_list = NULL; + + if (!video->output_reconstructed) + return; + + /* Create new reconstructed node */ + new_item = kzalloc(sizeof(*new_item), GFP_KERNEL); + if (!new_item) + return; + + new_item->data = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_item->data) { + kfree(new_item); + new_item = NULL; + return; + } + + new_node = (struct img_recon_node *)new_item->data; + + if (is_coded) { + topaz_update_host_mem(str_ctx->vxe_ctx, video->recon_buffer); + tmp_buffer = (unsigned char *)video->recon_buffer->cpu_virt; + width = ALIGN_64(video->width); + height = ALIGN_64(video->frame_height); + + new_node->buffer = kzalloc(width * height * 3 / 2, GFP_KERNEL); + if (!new_node->buffer) { + kfree(new_item->data); + kfree(new_item); + new_item = NULL; + new_node = NULL; + return; + } + memcpy(new_node->buffer, tmp_buffer, width * height * 3 / 2); + + } else { + new_node->buffer = NULL; + } + new_node->poc = video->recon_poc; + + /* Add new node to the queue */ + if (!video->ref_frame) { + /* First element */ + new_item->next = NULL; + video->ref_frame = new_item; + } else if (new_node->poc == 0) { + /* First element after aborted sequence */ + current_item = video->ref_frame; + + while (current_item->next) + current_item = current_item->next; + + /* Insert at end */ + new_item->next = NULL; + current_item->next = new_item; + } else { + struct img_recon_node *head_node = (struct img_recon_node *)video->ref_frame->data; + + if (head_node->poc > new_node->poc) { + /* Insert at start */ + new_item->next = video->ref_frame; + video->ref_frame = new_item; + } else { + struct img_recon_node *next_node = NULL; + + current_item = video->ref_frame; + while (current_item->next) { + next_node = (struct img_recon_node *)current_item->next->data; + + if (next_node->poc > new_node->poc) { + /* Insert between current and next */ + new_item->next = current_item->next; + current_item->next = new_item; + break; + } + current_item = current_item->next; + } + + if (!current_item->next) { + /* Insert at end */ + new_item->next = NULL; + current_item->next = new_item; + } + } + } + + if (video->next_recon == 0) { + video->next_recon++; + /* Flush all frames */ + *recon_list = video->ref_frame; + video->ref_frame = NULL; + } else if (new_node->poc == video->next_recon) { + struct list_item *flush_tail = video->ref_frame; + struct img_recon_node *next_node; + + video->next_recon++; + + /* Find all flushable frames */ + while (flush_tail->next) { + next_node = (struct img_recon_node *)flush_tail->next->data; + + /* Flushing sequence ends when POCs no longer match */ + if (next_node->poc != video->next_recon) + break; + + video->next_recon++; + + flush_tail = flush_tail->next; + } + + /* Flush consecutive sequence */ + *recon_list = video->ref_frame; + + /* Set new head */ + video->ref_frame = flush_tail->next; + + /* Separate sequences */ + flush_tail->next = NULL; + } +} + +int topaz_process_message(struct topaz_stream_context *str_ctx, struct mtx_tohost_msg tohost_msg) +{ + struct driver_tohost_msg *driver_msg; + struct list_item *current_el = NULL; + struct img_enc_context *enc; + struct img_video_context *video; + struct list_item *message_list = NULL; + unsigned int index = 0; + + enc = str_ctx->enc_ctx; + video = enc->video; + + /* add a new element */ + current_el = kzalloc(sizeof(*current_el), GFP_KERNEL); + if (!current_el) + return IMG_ERROR_OUT_OF_MEMORY; + + current_el->data = kzalloc(sizeof(*driver_msg), GFP_KERNEL); + if (!current_el->data) { + kfree(current_el); + current_el = NULL; + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* adding to head */ + current_el->next = message_list; + message_list = current_el; + + driver_msg = (struct driver_tohost_msg *)current_el->data; + driver_msg->cmd_id = tohost_msg.cmd_id; + driver_msg->data = tohost_msg.data; + driver_msg->command_data_buf = tohost_msg.command_data_buf; + + switch (tohost_msg.cmd_id) { + case MTX_MESSAGE_ACK: + driver_msg->input_cmd_id = (enum mtx_cmd_id)F_DECODE(tohost_msg.input_cmd_word, + MTX_MSG_CMD_ID); + break; + + case MTX_MESSAGE_CODED: + { + struct coded_data_hdr *coded_datahdr = NULL; + unsigned int feedback, extra_feedback; + unsigned char active_coded_package_idx; + struct img_feedback_element *feedback_struct; + + active_coded_package_idx = tohost_msg.coded_pkg_idx; + + get_coded_buffer(str_ctx, (void **)&coded_datahdr, TRUE, + active_coded_package_idx); + + feedback = coded_datahdr->feedback; + extra_feedback = coded_datahdr->extra_feedback; + + /* detect the FrameNum of the coded buffer */ + feedback_struct = (struct img_feedback_element *)&driver_msg->feedback; + + combine_feedback(str_ctx, active_coded_package_idx, &feedback, &extra_feedback, + &feedback_struct->bytes_coded); + + feedback_struct->coded_buffer_count = F_DECODE(extra_feedback, + CODED_BUFFER_NUMBER_USED); + + /* Give the header buffer to the feedback structure */ + feedback_struct->coded_package = video->coded_package[active_coded_package_idx]; + feedback_struct->active_coded_package_idx = active_coded_package_idx; + /* update this frame, using the info from the coded buffer */ + feedback_struct->coded_package->coded_buffer[feedback_struct->coded_slot_num] = + video->coded_package[active_coded_package_idx]->coded_buffer[feedback_struct + ->coded_slot_num]; + + feedback_struct->first_bu = F_DECODE(feedback, CODED_FIRST_BU); + feedback_struct->storage_frame_num = F_DECODE(feedback, CODED_STORAGE_FRAME_NUM); + feedback_struct->entire_frame = F_DECODE(feedback, CODED_ENTIRE_FRAME); + feedback_struct->is_skipped = F_DECODE(feedback, CODED_IS_SKIPPED); + feedback_struct->is_coded = F_DECODE(feedback, CODED_IS_CODED); + feedback_struct->recon_idx = F_DECODE(feedback, CODED_RECON_IDX); + feedback_struct->source_slot = F_DECODE(feedback, CODED_SOURCE_SLOT); + feedback_struct->frame_type = (enum img_frame_type)F_DECODE + (feedback, CODED_FRAME_TYPE); + feedback_struct->slice_num = F_DECODE(feedback, CODED_SLICE_NUM); + feedback_struct->poc = video->source_slot_poc[feedback_struct->source_slot]; + + feedback_struct->slices_in_buffer = F_DECODE(extra_feedback, + CODED_SLICES_IN_BUFFER); + feedback_struct->field = F_DECODE(extra_feedback, CODED_FIELD); + feedback_struct->patched_recon = F_DECODE(extra_feedback, + CODED_PATCHED_RECON); + feedback_struct->bytes_coded = coded_datahdr->bytes_written; + feedback_struct->host_ctx = coded_datahdr->host_ctx; + + if (video->highest_storage_number != feedback_struct->storage_frame_num && + video->standard != IMG_STANDARD_H263) { + if (feedback_struct->storage_frame_num == + ((video->highest_storage_number + 1) & 0x03)) { + /* it is piece of the next frame */ + video->highest_storage_number = feedback_struct->storage_frame_num; + /* retrieve next WB */ + video->encode_pic_processing--; + video->extra_wb_retrieved++; + } else if (feedback_struct->storage_frame_num == + ((video->highest_storage_number + 2) & 0x03)) { + /* it is piece of the next frame */ + video->highest_storage_number = feedback_struct->storage_frame_num; + + video->encode_pic_processing -= 2; + video->extra_wb_retrieved += 2; + } + } + + while (index < feedback_struct->coded_buffer_count) { + if (video->coded_package + [active_coded_package_idx]->coded_buffer[index]->lock == SW_LOCK) + /* Unlock coded buffers used*/ + topaz_release_buffer(str_ctx, + (struct img_buffer *)(video->coded_package + [active_coded_package_idx]->coded_buffer[index]), + FALSE); + index++; + } + + /* Unlock header buffer */ + topaz_release_buffer(str_ctx, video->coded_package + [feedback_struct->active_coded_package_idx]->header_buffer, + FALSE); + + /* Release the coded slot */ + video->coded_package[feedback_struct->active_coded_package_idx]->busy = FALSE; + + feedback_struct->src_frame = video->source_slot_buff[feedback_struct->source_slot]; + + /* Detect the slice number based on the Slice Map and the first BU in a slice */ + if (feedback_struct->bytes_coded) { + struct img_buffer *output_slice_map; + unsigned char *src_buffer = NULL; + unsigned char slices_per_picture; + unsigned short first_bu_in_slice; + unsigned char slice_number; + unsigned char index; + unsigned char slice_size_in_bu[MAX_SLICESPERPIC]; + + /* postion the start of the slice map */ + output_slice_map = &video->slice_map[feedback_struct->source_slot]; + + topaz_get_buffer(str_ctx, output_slice_map, (void **)&src_buffer, FALSE); + + /* retrieve slices per field */ + slices_per_picture = *src_buffer; + src_buffer++; + + /* retrieve first BU in slices and Slice sizes in BUs */ + first_bu_in_slice = 0; + + for (index = 0; index < slices_per_picture; index++) { + slice_number = src_buffer[index * 2]; + slice_size_in_bu[slice_number] = src_buffer[index * 2 + 1]; + + first_bu_in_slice += slice_size_in_bu[slice_number]; + } + topaz_release_buffer(str_ctx, output_slice_map, FALSE); + + feedback_struct->slices_per_picture = slices_per_picture; + } + + if (feedback_struct->entire_frame) { + /* we encoded the entire frame */ + video->frames_encoded++; +#ifdef DEBUG_ENCODER_DRIVER + pr_info("FRAMES_CODED[%d]\n", video->frames_encoded); +#endif + + if (feedback_struct->coded_package->coded_buffer[0]) + /* Send callback for coded_buffer ready */ + global_topaz_core_context->vxe_str_processed_cb(str_ctx->vxe_ctx, + VXE_CB_CODED_BUFF_READY, + (void *)(feedback_struct->coded_package->coded_buffer[0]), + feedback_struct->bytes_coded, video->frames_encoded); + + if (feedback_struct->src_frame) + /* Send callback for coded_buffer ready */ + global_topaz_core_context->vxe_str_processed_cb(str_ctx->vxe_ctx, + VXE_CB_SRC_FRAME_RELEASE, + (void *)(feedback_struct->src_frame), + 0, 0); + + if (video->flush_at_frame > 0 && + video->frames_encoded >= video->flush_at_frame) + feedback_struct->last_frame_encoded = TRUE; + + if (feedback_struct->patched_recon && video->patched_recon_buffer) { + video->recon_buffer = video->patched_recon_buffer; + video->patched_recon_buffer = NULL; + } else { + video->recon_buffer = + &video->recon_pictures[feedback_struct->recon_idx]; + } + video->recon_poc = feedback_struct->poc; + + video->frame_type = feedback_struct->frame_type; + + process_reconstructed(str_ctx, feedback_struct->is_coded, video->frame_type, + &feedback_struct->recon_list); + + /* If there are more frames to be encoded, release the source slot */ + if (video->frame_count == 0 || + video->encode_requested < video->frame_count) + video->source_slot_buff[feedback_struct->source_slot] = NULL; + + if (!video->extra_wb_retrieved) { + video->encode_pic_processing--; + video->highest_storage_number = + (video->highest_storage_number + 1) & 0x03; + } else { + video->extra_wb_retrieved--; + } + } else { + if (feedback_struct->coded_package->coded_buffer[0]) + /* Send callback for coded_buffer ready */ + global_topaz_core_context->vxe_str_processed_cb(str_ctx->vxe_ctx, + VXE_CB_CODED_BUFF_READY, + (void *)(feedback_struct->coded_package->coded_buffer[0]), + feedback_struct->bytes_coded, video->frames_encoded); + } + + if (feedback_struct->entire_frame && + (video->enable_sel_stats_flags & ESF_FIRST_STAGE_STATS)) + feedback_struct->motion_search_statistics_buf = + &video->firstpass_out_param_buf[feedback_struct->source_slot]; + else + feedback_struct->motion_search_statistics_buf = NULL; + + if (video->frame_count > 0 && video->frames_encoded >= video->frame_count) + feedback_struct->last_frame_encoded = TRUE; + + if (feedback_struct->entire_frame && + (video->enable_sel_stats_flags & ESF_MP_BEST_MB_DECISION_STATS || + video->enable_sel_stats_flags & ESF_MP_BEST_MOTION_VECTOR_STATS)) + feedback_struct->best_multipass_statistics_buf = + &video->firstpass_out_best_multipass_param_buf + [feedback_struct->source_slot]; + else + feedback_struct->best_multipass_statistics_buf = NULL; + break; + } + default: + break; + } + + kfree(current_el->data); + kfree(current_el); + + return IMG_SUCCESS; +} + +void handle_encoder_firmware_response(struct img_writeback_msg *wb_msg, void *priv) +{ + struct topaz_stream_context *str_ctx; + struct mtx_tohost_msg tohost_msg; + int index; + unsigned int cmd_buf_slot = COMM_CMD_DATA_BUF_SLOT_NONE; + unsigned int *cmdbuf_devaddr; + + str_ctx = (struct topaz_stream_context *)priv; + + if (!str_ctx) + return; + + memset(&tohost_msg, 0, sizeof(tohost_msg)); + tohost_msg.cmd_id = (enum mtx_message_id)F_DECODE(wb_msg->cmd_word, MTX_MSG_MESSAGE_ID); + + switch (tohost_msg.cmd_id) { + case MTX_MESSAGE_ACK: +#ifdef DEBUG_ENCODER_DRIVER + pr_info("MTX_MESSAGE_ACK received\n"); +#endif + + tohost_msg.wb_val = wb_msg->writeback_val; + tohost_msg.input_cmd_word = wb_msg->cmd_word; + tohost_msg.data = wb_msg->data; + break; + case MTX_MESSAGE_CODED: +#ifdef DEBUG_ENCODER_DRIVER + pr_info("MTX_MESSAGE_CODED Received\n"); +#endif + tohost_msg.input_cmd_word = wb_msg->cmd_word; + tohost_msg.coded_pkg_idx = wb_msg->coded_package_consumed_idx; + break; + default: + break; + } + + cmdbuf_devaddr = global_cmd_data_dev_addr.cpu_virt; + + for (index = 0; index < TOPAZ_CMD_DATA_BUF_NUM; index++) { + if (*cmdbuf_devaddr == wb_msg->extra_data) { + /* Input cmd buffer found */ + cmd_buf_slot = index; + break; + } + cmdbuf_devaddr++; + } + + if (cmd_buf_slot != COMM_CMD_DATA_BUF_SLOT_NONE) { + tohost_msg.command_data_buf = &global_cmd_data_info[cmd_buf_slot]; + topaz_release_cmd_data_buffer(tohost_msg.command_data_buf); + + } else { + tohost_msg.command_data_buf = NULL; + } + + topaz_process_message(str_ctx, tohost_msg); +} + +static inline void populate_firmware_message(struct vidio_ddbufinfo *dest, unsigned int dest_offset, + struct vidio_ddbufinfo *src, unsigned int src_offset) +{ + *(unsigned int *)((unsigned long long)dest->cpu_virt + dest_offset) = + src->dev_virt + src_offset; +} + +/* + * init_hardware + */ +int init_topaz_core(void *device_handle, unsigned int *num_pipes, + unsigned int mmu_flags, void *callback) +{ + unsigned int index; + + if (is_topaz_core_initialized) + return IMG_ERROR_INVALID_PARAMETERS; + + is_topaz_core_initialized = TRUE; + + global_topaz_core_context = kzalloc(sizeof(*global_topaz_core_context), GFP_KERNEL); + if (!global_topaz_core_context) { + is_topaz_core_initialized = FALSE; + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* Initialise device context. */ + global_topaz_core_context->dev_handle = (struct topaz_dev_ctx *)device_handle; + global_topaz_core_context->vxe_str_processed_cb = (vxe_cb)callback; + + lst_init(&global_topaz_core_context->topaz_stream_list); + + *num_pipes = topazdd_get_num_pipes(device_handle); + + /* allocate memory for HighCmd FIFO data section */ + if (topaz_mmu_alloc(global_topaz_core_context->dev_handle->topaz_mmu_ctx.mmu_context_handle, + global_topaz_core_context->dev_handle->vxe_arg, MMU_GENERAL_HEAP_ID, + 1, (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + 4 * TOPAZ_CMD_DATA_BUF_NUM, 64, &global_cmd_data_dev_addr)) { + IMG_DBG_ASSERT("Global command data info buff alloc failed\n" != NULL); + kfree(global_topaz_core_context); + return IMG_ERROR_OUT_OF_MEMORY; + } + + for (index = 0; index < ARRAY_SIZE(global_cmd_data_info); index++) { + if (topaz_mmu_alloc + (global_topaz_core_context->dev_handle->topaz_mmu_ctx.mmu_context_handle, + global_topaz_core_context->dev_handle->vxe_arg, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + TOPAZ_CMD_DATA_BUF_SIZE, 64, &global_cmd_data_info[index])) { + IMG_DBG_ASSERT("Global command data info buff alloc failed\n" != NULL); + topaz_mmu_free(global_topaz_core_context->dev_handle->vxe_arg, + &global_cmd_data_dev_addr); + kfree(global_topaz_core_context); + return IMG_ERROR_OUT_OF_MEMORY; + } + populate_firmware_message(&global_cmd_data_dev_addr, 4 * index, + &global_cmd_data_info[index], 0); + global_cmd_data_busy[index] = FALSE; + } + + /*Lock for locking critical section in TopazAPI*/ + global_topaz_core_context->mutex = kzalloc(sizeof(*global_topaz_core_context->mutex), + GFP_KERNEL); + if (!global_topaz_core_context->mutex) + return IMG_ERROR_OUT_OF_MEMORY; + + mutex_init(global_topaz_core_context->mutex); + return IMG_SUCCESS; +} + +/* + * deinit_topaz_core + */ +int deinit_topaz_core(void) +{ + unsigned int index; + + mutex_destroy(global_topaz_core_context->mutex); + kfree(global_topaz_core_context->mutex); + global_topaz_core_context->mutex = NULL; + + if (topaz_mmu_free(global_topaz_core_context->dev_handle->vxe_arg, + &global_cmd_data_dev_addr)) + IMG_DBG_ASSERT("Free failed" == NULL); + + for (index = 0; index < ARRAY_SIZE(global_cmd_data_info); index++) + if (topaz_mmu_free(global_topaz_core_context->dev_handle->vxe_arg, + &global_cmd_data_info[index])) + IMG_DBG_ASSERT("Free failed" == NULL); + + return IMG_SUCCESS; +} + +static unsigned short create_gop_frame(unsigned char *level, unsigned char reference, + unsigned char pos, unsigned char ref0_level, + unsigned char ref1_level, enum img_frame_type frame_type) +{ + *level = max(ref0_level, ref1_level) + 1; + + return F_ENCODE(reference, GOP_REFERENCE) | + F_ENCODE(pos, GOP_POS) | + F_ENCODE(ref0_level, GOP_REF0) | + F_ENCODE(ref1_level, GOP_REF1) | + F_ENCODE(frame_type, GOP_FRAMETYPE); +} + +static void gop_split(unsigned short **gop_structure, signed char ref0, + signed char ref1, unsigned char ref0_level, + unsigned char ref1_level, unsigned char pic_on_level[]) +{ + unsigned char distance = ref1 - ref0; + unsigned char position = ref0 + (distance >> 1); + unsigned char level; + + if (distance == 1) + return; + + /* mark middle as this level */ + (*gop_structure)++; + **gop_structure = create_gop_frame(&level, distance >= 3, position, ref0_level, ref1_level, + IMG_INTER_B); + pic_on_level[level]++; + + if (distance >= 4) + gop_split(gop_structure, ref0, position, ref0_level, level, pic_on_level); + + if (distance >= 3) + gop_split(gop_structure, position, ref1, level, ref1_level, pic_on_level); +} + +static void mini_gop_generate_hierarchical(unsigned short gop_structure[], + unsigned int bframe_count, + unsigned int ref_spacing, + unsigned char pic_on_level[]) +{ + unsigned char level; + + gop_structure[0] = create_gop_frame(&level, TRUE, bframe_count, ref_spacing, 0, + IMG_INTER_P); + pic_on_level[level]++; + + gop_split(&gop_structure, -1, bframe_count, ref_spacing, ref_spacing + 1, pic_on_level); +} + +static void mini_gop_generate_flat(unsigned short gop_structure[], + unsigned int bframe_count, + unsigned int ref_spacing, + unsigned char pic_on_level[]) +{ + /* B B B B P */ + unsigned char encode_order_pos; + unsigned char level; + + gop_structure[0] = create_gop_frame(&level, TRUE, MAX_BFRAMES, ref_spacing, 0, + IMG_INTER_P); + pic_on_level[level]++; + + for (encode_order_pos = 1; encode_order_pos < MAX_GOP_SIZE; encode_order_pos++) { + gop_structure[encode_order_pos] = create_gop_frame(&level, + FALSE, encode_order_pos - 1, + ref_spacing, ref_spacing + 1, + IMG_INTER_B); + pic_on_level[level] = bframe_count; + } +} + +/* + * Create the MTX-side encoder context + */ +static int topaz_video_create_mtx_context(struct topaz_stream_context *str_ctx, + struct img_video_params *video_params) +{ + struct img_video_context *video; + struct img_enc_context *enc; + int index, i, j; + void *mtx_enc_context_mem; + struct img_mtx_video_context *mtx_enc_context; + unsigned char flag; + unsigned int max_cores; + unsigned int bit_limit; + unsigned int vert_mv_limit; + unsigned int packed_strides; + unsigned short *gop_structure; + + max_cores = topazdd_get_num_pipes(global_topaz_core_context->dev_handle); + + enc = str_ctx->enc_ctx; + video = enc->video; + + mtx_enc_context = (struct img_mtx_video_context *)(video->mtx_enc_ctx_mem.cpu_virt); + + /* clear the context region */ + memset(mtx_enc_context, 0x00, MTX_CONTEXT_SIZE); + + mtx_enc_context_mem = (void *)(&enc->video->mtx_enc_ctx_mem); + + mtx_enc_context->initial_qp_i = video->rc_params.initial_qp_i; + mtx_enc_context->initial_qp_p = video->rc_params.initial_qp_p; + mtx_enc_context->initial_qp_b = video->rc_params.initial_qp_b; + + mtx_enc_context->cqp_offset = (video->rc_params.qcp_offset & 0x1f) | + ((video->rc_params.qcp_offset & 0x1f) << 8); + mtx_enc_context->standard = video->standard; + mtx_enc_context->width_in_mbs = video->width >> 4; + mtx_enc_context->picture_height_in_mbs = video->picture_height >> 4; + + mtx_enc_context->kick_size = video->kick_size; + mtx_enc_context->kicks_per_bu = video->kicks_per_bu; + mtx_enc_context->kicks_per_picture = (mtx_enc_context->width_in_mbs * + mtx_enc_context->picture_height_in_mbs) / video->kick_size; + + mtx_enc_context->output_reconstructed = video->output_reconstructed; + + mtx_enc_context->vop_time_resolution = video->vop_time_resolution; + + mtx_enc_context->max_slices_per_picture = video->slices_per_picture; + + mtx_enc_context->is_interlaced = video->is_interlaced; + mtx_enc_context->top_field_first = video->top_field_first; + mtx_enc_context->arbitrary_so = video->arbitrary_so; + + mtx_enc_context->idr_period = video->idr_period; + mtx_enc_context->bframe_count = video->rc_params.bframes; + mtx_enc_context->hierarchical = (unsigned char)video->rc_params.hierarchical; + mtx_enc_context->intra_loop_cnt = video->intra_cnt; + mtx_enc_context->ref_spacing = video_params->ref_spacing; + + mtx_enc_context->debug_crcs = video_params->debug_crcs; + + mtx_enc_context->fw_num_pipes = enc->pipes_to_use; + mtx_enc_context->fw_first_pipe = enc->base_pipe; + mtx_enc_context->fw_last_pipe = enc->base_pipe + enc->pipes_to_use - 1; + mtx_enc_context->fw_pipes_to_use_flags = 0; + + flag = 0x1 << mtx_enc_context->fw_first_pipe; + /* Pipes used MUST be contiguous from the BasePipe offset */ + for (index = 0; index < mtx_enc_context->fw_num_pipes; index++, flag <<= 1) + mtx_enc_context->fw_pipes_to_use_flags |= flag; + + mtx_enc_context->format = video_params->format; + + /* copy scaler values to context in case we need them later */ + video->enable_scaler = video_params->enable_scaler; + video->crop_left = video_params->crop_left; + video->crop_right = video_params->crop_right; + video->crop_top = video_params->crop_top; + video->crop_bottom = video_params->crop_bottom; + video->source_width = video_params->source_width; + video->source_frame_height = video_params->source_frame_height; + video->intra_pred_modes = video_params->intra_pred_modes; + + topaz_setup_input_format(video, &mtx_enc_context->scaler_setup); + topaz_setup_input_csc(video, &mtx_enc_context->scaler_setup, &mtx_enc_context->csc_setup, + video_params->csc_preset); + + mtx_enc_context->enable_mvc = video->enable_mvc; + mtx_enc_context->mvc_view_idx = video->mvc_view_idx; + + if (video->standard == IMG_STANDARD_H264) + mtx_enc_context->no_sequence_headers = video->no_sequence_headers; + + mtx_enc_context->coded_header_per_slice = video->coded_header_per_slice; + + packed_strides = topaz_get_packed_buffer_strides + (video->buffer_stride_bytes, video->format, video_params->enable_scaler, + video_params->is_interlaced, video_params->is_interleaved); + + mtx_enc_context->pic_row_stride_bytes = + F_ENCODE(F_DECODE(packed_strides, MTX_MSG_PICMGMT_STRIDE_Y), + TOPAZHP_CR_CUR_PIC_LUMA_STRIDE) | + F_ENCODE(F_DECODE(packed_strides, MTX_MSG_PICMGMT_STRIDE_UV), + TOPAZHP_CR_CUR_PIC_CHROMA_STRIDE); + + mtx_enc_context->rc_mode = video->rc_params.rc_mode; + if (mtx_enc_context->rc_mode == IMG_RCMODE_VCM) { + mtx_enc_context->rc_vcm_mode = video->rc_params.rc_vcm_mode; + mtx_enc_context->rc_cfs_max_margin_perc = video->rc_params.rc_cfs_max_margin_perc; + } + + mtx_enc_context->disable_bit_stuffing = (unsigned char)video_params->disable_bit_stuffing; + + mtx_enc_context->first_pic = TRUE; + + /*Content Adaptive Rate Control Parameters*/ + if (video_params->carc) { + mtx_enc_context->jmcomp_rc_reg0 = + F_ENCODE(video_params->carc_pos_range, TOPAZHP_CR_CARC_POS_RANGE) | + F_ENCODE(video_params->carc_pos_scale, TOPAZHP_CR_CARC_POS_SCALE) | + F_ENCODE(video_params->carc_neg_range, TOPAZHP_CR_CARC_NEG_RANGE) | + F_ENCODE(video_params->carc_neg_scale, TOPAZHP_CR_CARC_NEG_SCALE); + + mtx_enc_context->jmcomp_rc_reg1 = + F_ENCODE(video_params->carc_threshold, TOPAZHP_CR_CARC_THRESHOLD) | + F_ENCODE(video_params->carc_cutoff, TOPAZHP_CR_CARC_CUTOFF) | + F_ENCODE(video_params->carc_shift, TOPAZHP_CR_CARC_SHIFT); + } else { + mtx_enc_context->jmcomp_rc_reg0 = 0; + mtx_enc_context->jmcomp_rc_reg1 = 0; + } + + mtx_enc_context->mv_clip_config = + F_ENCODE(video_params->no_offscreen_mv, TOPAZHP_CR_MVCALC_RESTRICT_PICTURE); + + mtx_enc_context->lritc_cache_chunk_config = 0; + + mtx_enc_context->ipcm_0_config = + F_ENCODE(enc->video->cabac_bin_flex, TOPAZ_VLC_CR_CABAC_BIN_FLEX) | + F_ENCODE(DEFAULT_CABAC_DB_MARGIN, TOPAZ_VLC_CR_CABAC_DB_MARGIN); + + bit_limit = 3100; + + mtx_enc_context->ipcm_1_config = F_ENCODE(bit_limit, TOPAZ_VLC_CR_IPCM_THRESHOLD) | + F_ENCODE(enc->video->cabac_bin_limit, TOPAZ_VLC_CR_CABAC_BIN_LIMIT); + + /* leave alone until high profile and constrained modes are defined. */ + mtx_enc_context->h264_comp_control = F_ENCODE((video->cabac_enabled ? 0 : 1), + TOPAZHP_CR_H264COMP_8X8_CAVLC); + mtx_enc_context->h264_comp_control |= + F_ENCODE(video_params->use_default_scaling_list ? 1 : 0, + TOPAZHP_CR_H264COMP_DEFAULT_SCALING_LIST); + mtx_enc_context->h264_comp_control |= F_ENCODE(video->h264_8x8_transform ? 1 : 0, + TOPAZHP_CR_H264COMP_8X8_TRANSFORM); + mtx_enc_context->h264_comp_control |= F_ENCODE(video->h264_intra_constrained ? 1 : 0, + TOPAZHP_CR_H264COMP_CONSTRAINED_INTRA); + + mtx_enc_context->mc_adaptive_rounding_disable = video_params->vp_adaptive_rounding_disable; + mtx_enc_context->h264_comp_control |= + F_ENCODE(mtx_enc_context->mc_adaptive_rounding_disable ? 0 : 1, + TOPAZHP_CR_H264COMP_ADAPT_ROUND_ENABLE); + + if (!mtx_enc_context->mc_adaptive_rounding_disable) + for (i = 0; i < 4; i++) + for (j = 0; j < AR_REG_SIZE; j++) + mtx_enc_context->mc_adaptive_rounding_offsets[j][i] = + video_params->vp_adaptive_rounding_offsets[j][i]; + + if (video->standard == IMG_STANDARD_H264) + mtx_enc_context->h264_comp_control |= + F_ENCODE(USE_VCM_HW_SUPPORT, TOPAZHP_CR_H264COMP_VIDEO_CONF_ENABLE); + + mtx_enc_context->h264_comp_control |= + F_ENCODE(video_params->use_custom_scaling_lists & 0x01 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_LUMA_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x02 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_CB_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x04 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTRA_CR_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x08 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_LUMA_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x10 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_CB_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x20 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_4X4_INTER_CR_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x40 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_8X8_INTRA_LUMA_ENABLE) | + F_ENCODE(video_params->use_custom_scaling_lists & 0x80 ? 1 : 0, + TOPAZHP_CR_H264COMP_CUSTOM_QUANT_8X8_INTER_LUMA_ENABLE); + + mtx_enc_context->h264_comp_control |= + F_ENCODE(video_params->enable_lossless ? 1 : 0, TOPAZHP_CR_H264COMP_LOSSLESS) | + F_ENCODE(video_params->lossless_8x8_prefilter ? + TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER_BYPASS : + TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER_FILTER, + TOPAZHP_CR_H264COMP_LOSSLESS_8X8_PREFILTER); + + mtx_enc_context->h264_comp_intra_pred_modes = 0x3ffff;// leave at default for now. + + if (video->intra_pred_modes != 0) + mtx_enc_context->h264_comp_intra_pred_modes = video->intra_pred_modes; + + mtx_enc_context->pred_comb_control = video->pred_comb_control; + + mtx_enc_context->skip_coded_inter_intra = + F_ENCODE(video->inter_intra_index, TOPAZHP_CR_INTER_INTRA_SCALE_IDX) | + F_ENCODE(video->coded_skipped_index, TOPAZHP_CR_SKIPPED_CODED_SCALE_IDX); + + if (video->enable_inp_ctrl) { + mtx_enc_context->mb_host_ctrl = + F_ENCODE(video->enable_host_qp, TOPAZHP_CR_MB_HOST_QP) | + F_ENCODE(video->enable_host_bias, TOPAZHP_CR_MB_HOST_SKIPPED_CODED_SCALE) | + F_ENCODE(video->enable_host_bias, TOPAZHP_CR_MB_HOST_INTER_INTRA_SCALE); + mtx_enc_context->pred_comb_control |= F_ENCODE(1, + TOPAZHP_CR_INTER_INTRA_SCALE_ENABLE) + | F_ENCODE(1, TOPAZHP_CR_SKIPPED_CODED_SCALE_ENABLE); + } + + if (video_params->enable_cumulative_biases) + mtx_enc_context->pred_comb_control |= + F_ENCODE(1, TOPAZHP_CR_CUMULATIVE_BIASES_ENABLE); + + mtx_enc_context->pred_comb_control |= + F_ENCODE((((video->inter_intra_index == 3) && (video->coded_skipped_index == 3)) + ? 0 : 1), TOPAZHP_CR_INTER_INTRA_SCALE_ENABLE) | + F_ENCODE((video->coded_skipped_index == 3 ? 0 : 1), + TOPAZHP_CR_SKIPPED_CODED_SCALE_ENABLE); + + mtx_enc_context->deblock_ctrl = + F_ENCODE(video->deblock_idc, TOPAZ_DB_CR_DISABLE_DEBLOCK_IDC); + + /* Set up VLC Control Register */ + mtx_enc_context->vlc_control = 0; + + switch (video->standard) { + case IMG_STANDARD_H264: + /* 1 for H.264 note this is inconsistent with the sequencer value */ + mtx_enc_context->vlc_control |= F_ENCODE(1, TOPAZ_VLC_CR_CODEC); + mtx_enc_context->vlc_control |= F_ENCODE(0, TOPAZ_VLC_CR_CODEC_EXTEND); + break; + + default: + break; + } + + if (video->cabac_enabled) + /* 2 for Mpeg4 note this is inconsistent with the sequencer value */ + mtx_enc_context->vlc_control |= F_ENCODE(1, TOPAZ_VLC_CR_CABAC_ENABLE); + + mtx_enc_context->vlc_control |= F_ENCODE(video->is_interlaced ? 1 : 0, + TOPAZ_VLC_CR_VLC_FIELD_CODED); + mtx_enc_context->vlc_control |= F_ENCODE(video->h264_8x8_transform ? 1 : 0, + TOPAZ_VLC_CR_VLC_8X8_TRANSFORM); + mtx_enc_context->vlc_control |= F_ENCODE(video->h264_intra_constrained ? 1 : 0, + TOPAZ_VLC_CR_VLC_CONSTRAINED_INTRA); + + mtx_enc_context->vlc_slice_control = F_ENCODE(video->rc_params.slice_byte_limit, + TOPAZ_VLC_CR_SLICE_SIZE_LIMIT); + mtx_enc_context->vlc_slice_mb_control = F_ENCODE(video->rc_params.slice_mb_limit, + TOPAZ_VLC_CR_SLICE_MBS_LIMIT); + + switch (video->standard) { + case IMG_STANDARD_H264: + vert_mv_limit = 255; /* default to no clipping */ + if (video->vert_mv_limit) + vert_mv_limit = enc->video->vert_mv_limit; + + /* as topaz can only cope with at most 255 (in the register field) */ + vert_mv_limit = min(255U, vert_mv_limit); + mtx_enc_context->ipe_vector_clipping = + F_ENCODE(1, TOPAZHP_CR_IPE_VECTOR_CLIPPING_ENABLED) | + F_ENCODE(255, TOPAZHP_CR_IPE_VECTOR_CLIPPING_X) | + F_ENCODE(vert_mv_limit, TOPAZHP_CR_IPE_VECTOR_CLIPPING_Y); + + mtx_enc_context->spe_mvd_clip_range = F_ENCODE(0, TOPAZHP_CR_SPE_MVD_CLIP_ENABLE); + break; + default: + break; + } + + /* Update MV Scaling settings: IDR */ + memcpy(&mtx_enc_context->mv_settings_idr, &video->mv_settings_idr, + sizeof(struct img_mv_settings)); + + /* NonB (I or P) */ + for (i = 0; i <= MAX_BFRAMES; i++) + memcpy(&mtx_enc_context->mv_settings_non_b[i], &video->mv_settings_non_b[i], + sizeof(struct img_mv_settings)); + + /* WEIGHTED PREDICTION */ + mtx_enc_context->weighted_prediction_enabled = video_params->weighted_prediction; + mtx_enc_context->mtx_weighted_implicit_bi_pred = video_params->vp_weighted_implicit_bi_pred; + + /* SEI_INSERTION */ + mtx_enc_context->insert_hrd_params = video_params->insert_hrd_params; + if (mtx_enc_context->insert_hrd_params & enc->video->rc_params.bits_per_second) + /* HRD parameters are meaningless without a bitrate */ + mtx_enc_context->insert_hrd_params = FALSE; + + if (mtx_enc_context->insert_hrd_params) { + mtx_enc_context->clock_div_bitrate = (90000 * 0x100000000LL); + mtx_enc_context->clock_div_bitrate /= enc->video->rc_params.bits_per_second; + mtx_enc_context->max_buffer_mult_clock_div_bitrate = + (unsigned int)(((unsigned long long)(video->rc_params.buffer_size) * + 90000ULL) / + (unsigned long long)enc->video->rc_params.bits_per_second); + } + + memcpy(&mtx_enc_context->in_params, &video->pic_params.in_params, + sizeof(struct in_rc_params)); + + mtx_enc_context->lritc_cache_chunk_config = + F_ENCODE(enc->video->chunks_per_mb, + TOPAZHP_CR_CACHE_CHUNKS_PER_MB) + | F_ENCODE(enc->video->max_chunks, TOPAZHP_CR_CACHE_CHUNKS_MAX) + | F_ENCODE(enc->video->max_chunks - enc->video->priority_chunks, + TOPAZHP_CR_CACHE_CHUNKS_PRIORITY); + + mtx_enc_context->first_pic_flags = video->first_pic_flags; + mtx_enc_context->non_first_pic_flags = video->non_first_pic_flags; + + mtx_enc_context->slice_header_slot_num = -1; + + memset(mtx_enc_context->pic_on_level, 0, sizeof(mtx_enc_context->pic_on_level)); + + gop_structure = (unsigned short *)(video->flat_gop_struct.cpu_virt); + + mini_gop_generate_flat(gop_structure, mtx_enc_context->bframe_count, + mtx_enc_context->ref_spacing, mtx_enc_context->pic_on_level); + topaz_update_device_mem(str_ctx->vxe_ctx, &video->flat_gop_struct); + + if (video->rc_params.hierarchical) { + memset(mtx_enc_context->pic_on_level, 0, sizeof(mtx_enc_context->pic_on_level)); + gop_structure = (unsigned short *)(video->hierar_gop_struct.cpu_virt); + + mini_gop_generate_hierarchical(gop_structure, mtx_enc_context->bframe_count, + mtx_enc_context->ref_spacing, + mtx_enc_context->pic_on_level); + topaz_update_device_mem(str_ctx->vxe_ctx, &video->hierar_gop_struct); + } + + topaz_update_device_mem(str_ctx->vxe_ctx, &video->mtx_enc_ctx_mem); + + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->mv_settings_b_table - + (unsigned char *)mtx_enc_context), + &video->mv_settings_btable, 0); + + if (video->rc_params.hierarchical) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->mv_settings_hierarchical - + (unsigned char *)mtx_enc_context), + &video->mv_settings_hierarchical, 0); + + for (i = 0; i < video->pic_nodes; i++) { + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->reconstructed[i] - + (unsigned char *)mtx_enc_context), + &video->recon_pictures[i], 0); + + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->colocated[i] - + (unsigned char *)mtx_enc_context), + &video->colocated[i], 0); + } + + for (i = 0; i < WB_FIFO_SIZE; i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->writeback_regions[i] - + (unsigned char *)mtx_enc_context), + &global_wb_data_info[i], 0); + + for (i = 0; i < video->mv_stores; i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->mv[i] - (unsigned char *)mtx_enc_context), + &video->mv[i], 0); + + if (video->enable_mvc) { + for (i = 0; i < 2; i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->inter_view_mv[i] - + (unsigned char *)mtx_enc_context), + &video->inter_view_mv[i], 0); + } + + for (i = 0; i < (int)max_cores; i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->above_params[i] - + (unsigned char *)mtx_enc_context), + &video->above_params[i], 0); + + /* SEI insertion */ + if (video_params->insert_hrd_params) { + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->sei_buffering_period_template - + (unsigned char *)mtx_enc_context), + &video->sei_buffering_period_header_mem, 0); + + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->sei_picture_timing_template - + (unsigned char *)mtx_enc_context), + &video->sei_picture_timing_header_mem, 0); + } + + for (i = 0; i < ARRAY_SIZE(video->slice_params_template_mem); i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->slice_params_templates[i] - + (unsigned char *)mtx_enc_context), + &video->slice_params_template_mem[i], 0); + + for (i = 0; i < video->slots_in_use; i++) { + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->slice_map[i] - + (unsigned char *)mtx_enc_context), + &video->slice_map[i].mem_info, 0); + + /* WEIGHTED PREDICTION */ + if (video_params->weighted_prediction || + video_params->vp_weighted_implicit_bi_pred == WBI_EXPLICIT) { + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->weighted_prediction_virt_addr[i] - + (unsigned char *)mtx_enc_context), + &video->weighted_prediction_mem[i], 0); + } + } + + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->flat_gop_struct - + (unsigned char *)mtx_enc_context), &video->flat_gop_struct, 0); + + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->flat_gop_struct - + (unsigned char *)mtx_enc_context), + &video->flat_gop_struct, 0); + + for (i = 0; i < video->slots_in_use; i++) { + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->ltref_header[i] - + (unsigned char *)mtx_enc_context), + &video->ltref_header[i], 0); + } + + if (mtx_enc_context->hierarchical) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->hierar_gop_struct - + (unsigned char *)mtx_enc_context), + &video->hierar_gop_struct, 0); + + for (i = 0; i < ARRAY_SIZE(video->pichdr_template_mem); i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->pichdr_templates[i] - + (unsigned char *)mtx_enc_context), + &video->pichdr_template_mem[i], 0); + + if (video->standard == IMG_STANDARD_H264) { + populate_firmware_message(mtx_enc_context_mem, (unsigned int)((unsigned char *) + &mtx_enc_context->seq_header - (unsigned char *)mtx_enc_context), + &video->seq_header_mem, 0); + + if (video->enable_mvc) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->subset_seq_header - + (unsigned char *)mtx_enc_context), + &video->subset_seq_header_mem, 0); + } + + /* Store the feedback memory address for all "5" slots in the context */ + if (video->enable_sel_stats_flags & ESF_FIRST_STAGE_STATS) { + for (i = 0; i < video->slots_in_use; i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->firstpass_out_param_addr[i] - + (unsigned char *)mtx_enc_context), + &video->firstpass_out_param_buf[i].mem_info, 0); + } + + /* Store the feedback memory address for all "5" slots in the context */ + if (video->enable_sel_stats_flags & ESF_MP_BEST_MB_DECISION_STATS || + video->enable_sel_stats_flags & ESF_MP_BEST_MOTION_VECTOR_STATS) { + for (i = 0; i < video->slots_in_use; i++) { + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *) + &mtx_enc_context->firstpass_out_best_multipass_param_addr[i] - + (unsigned char *)mtx_enc_context), + &video->firstpass_out_best_multipass_param_buf[i].mem_info, 0); + } + } + + /* Store the MB-Input control parameter memory for all the 5-slots in the context */ + if (video->enable_inp_ctrl) { + for (i = 0; i < video->slots_in_use; i++) + populate_firmware_message + (mtx_enc_context_mem, + (unsigned int)((unsigned char *)&mtx_enc_context->mb_ctrl_in_params_addr[i] - + (unsigned char *)mtx_enc_context), + &video->mb_ctrl_in_params_buf[i].mem_info, 0); + } + + topaz_update_device_mem(str_ctx->vxe_ctx, &video->mtx_enc_ctx_mem); + + return IMG_SUCCESS; +} + +/* + * Prepares the header templates for the encode for H.264 + */ +static int h264_prepare_templates(struct topaz_stream_context *str_ctx, + struct img_rc_params *rc_params, + int fine_y_search_size) +{ + struct img_enc_context *enc; + struct img_video_context *video_ctx; + struct pic_params *pic_params; + + enc = str_ctx->enc_ctx; + video_ctx = enc->video; + + prepare_mv_estimates(enc); + + pic_params = &enc->video->pic_params; + + pic_params->flags = 0; + + if (rc_params->rc_enable) { + pic_params->flags |= ISRC_FLAGS; + setup_rc_data(enc->video, pic_params, rc_params); + } else { + pic_params->in_params.se_init_qp_i = rc_params->initial_qp_i; + pic_params->in_params.mb_per_row = (enc->video->width >> 4); + pic_params->in_params.mb_per_bu = rc_params->bu_size; + pic_params->in_params.mb_per_frm = ((unsigned int)(enc->video->width >> 4)) * + (enc->video->frame_height >> 4); + pic_params->in_params.bu_per_frm = (pic_params->in_params.mb_per_frm) / + rc_params->bu_size; + } + + /* Prepare Slice header templates */ + generate_slice_params_template(enc, &enc->video->slice_params_template_mem[IMG_FRAME_IDR], + IMG_FRAME_IDR, enc->video->is_interlaced, + fine_y_search_size); + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->slice_params_template_mem + [IMG_FRAME_IDR]); + + generate_slice_params_template(enc, &enc->video->slice_params_template_mem[IMG_FRAME_INTRA], + IMG_FRAME_INTRA, enc->video->is_interlaced, + fine_y_search_size); + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->slice_params_template_mem + [IMG_FRAME_INTRA]); + + generate_slice_params_template(enc, + &enc->video->slice_params_template_mem[IMG_FRAME_INTER_P], + IMG_FRAME_INTER_P, enc->video->is_interlaced, + fine_y_search_size); + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->slice_params_template_mem + [IMG_FRAME_INTER_P]); + + generate_slice_params_template(enc, &enc->video->slice_params_template_mem + [IMG_FRAME_INTER_B], + IMG_FRAME_INTER_B, enc->video->is_interlaced, + fine_y_search_size); + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->slice_params_template_mem + [IMG_FRAME_INTER_B]); + + if (video_ctx->enable_mvc) { + generate_slice_params_template(enc, &enc->video->slice_params_template_mem + [IMG_FRAME_INTER_P_IDR], + IMG_FRAME_INTER_P_IDR, enc->video->is_interlaced, fine_y_search_size); + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->slice_params_template_mem + [IMG_FRAME_INTER_P_IDR]); + } + + /* Prepare Pic Params Templates */ + adjust_pic_flags(enc, rc_params, TRUE, &video_ctx->first_pic_flags); + adjust_pic_flags(enc, rc_params, FALSE, &video_ctx->non_first_pic_flags); + + return IMG_SUCCESS; +} + +/* + * Prepares the header templates for the encode. + */ +static int topaz_video_prepare_templates(struct topaz_stream_context *str_ctx, + unsigned char search_range, + int fine_y_search_size) +{ + struct img_enc_context *enc = str_ctx->enc_ctx; + struct img_video_context *video = enc->video; + int err_value = IMG_ERROR_UNEXPECTED_STATE; + + switch (video->standard) { + case IMG_STANDARD_H264: + err_value = h264_prepare_templates(str_ctx, &video->rc_params, fine_y_search_size); + break; + default: + break; + } + + return err_value; +} + +/* + * Prepare the sequence header for h.264 + */ +int topaz_h264_prepare_sequence_header(void *topaz_str_ctx, unsigned int mb_width, + unsigned int mb_height, + unsigned char vui_params_present, + struct h264_vui_params *params, + struct h264_crop_params *crop_params, + struct h264_sequence_header_params *sh_params, + unsigned char mvc_sps) +{ + struct mtx_header_params *seq_header; + struct img_enc_context *enc; + struct topaz_stream_context *str_ctx; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + enc = str_ctx->enc_ctx; + + /* Ensure parameters are consistent with context */ + if (!enc->video->custom_scaling) + sh_params->seq_scaling_matrix_present_flag = FALSE; + + /* Get a pointer to the memory the header will be written to */ + seq_header = (struct mtx_header_params *)(enc->video->seq_header_mem.cpu_virt); + h264_prepare_sequence_header(seq_header, mb_width, mb_height, vui_params_present, + params, crop_params, sh_params, enc->video->arbitrary_so); + + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->seq_header_mem); + + if (mvc_sps) { + /* prepare subset sequence parameter header */ + struct mtx_header_params *subset_seq_header; + + subset_seq_header = + (struct mtx_header_params *)(enc->video->subset_seq_header_mem.cpu_virt); + h264_prepare_mvc_sequence_header(subset_seq_header, mb_width, mb_height, + vui_params_present, params, crop_params, + sh_params); + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->subset_seq_header_mem); + } + + return IMG_SUCCESS; +} + +/* + * Prepare the picture header for h.264 + */ +int topaz_h264_prepare_picture_header(void *topaz_str_ctx, signed char cqp_offset) +{ + struct mtx_header_params *pic_header; + struct topaz_stream_context *str_ctx; + struct img_enc_context *enc; + unsigned char dep_view_pps = FALSE; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + + /* Get a pointer to the memory the header will be written to */ + pic_header = (struct mtx_header_params *)(enc->video->pichdr_template_mem[0].cpu_virt); + + if (enc->video->enable_mvc && enc->video->mvc_view_idx != 0 && + (enc->video->mvc_view_idx != (unsigned short)(NON_MVC_VIEW))) + dep_view_pps = TRUE; + + h264_prepare_picture_header(pic_header, enc->video->cabac_enabled, + enc->video->h264_8x8_transform, + enc->video->h264_intra_constrained, + cqp_offset, enc->video->weighted_prediction, + enc->video->weighted_bi_pred, + dep_view_pps, enc->video->pps_scaling, + enc->video->pps_scaling && enc->video->custom_scaling); + + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->pichdr_template_mem[0]); + + return IMG_SUCCESS; +} + +/* + * Prepare the AUD header for H264 + */ +int topaz_h264_prepare_aud_header(void *str_context) +{ + struct mtx_header_params *aud_header; + struct img_enc_context *enc; + struct topaz_stream_context *str_ctx; + + str_ctx = (struct topaz_stream_context *)str_context; + if (!str_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + enc = str_ctx->enc_ctx; + + /* Get a pointer to the memory the header will be written to */ + aud_header = (struct mtx_header_params *)(&enc->video->aud_header_mem); + + h264_prepare_aud_header(aud_header); + + topaz_update_device_mem(str_ctx->vxe_ctx, &enc->video->aud_header_mem); + + return IMG_SUCCESS; +} + +static unsigned int topaz_get_max_coded_data_size(enum img_standard standard, unsigned short width, + unsigned short height, unsigned int initial_qp_i) +{ + unsigned int worst_qp_size; + + if (standard == IMG_STANDARD_H264) { + /* allocate based on worst case qp size */ + worst_qp_size = 400; + return ((unsigned int)(width / 16) * (unsigned int)(height / 16) * worst_qp_size); + } + + if (initial_qp_i <= 5) + return ((unsigned int)width * (unsigned int)height * 1600) / (16 * 16); + + return ((unsigned int)width * (unsigned int)height * 900) / (16 * 16); +} + +static int topaz_get_context_coded_buffer_size(struct img_enc_context *enc, + struct img_rc_params *rc_params, + unsigned int *coded_buffer_size) +{ + struct img_video_context *video; + + video = enc->video; + + *coded_buffer_size = topaz_get_max_coded_data_size(video->standard, video->width, + video->picture_height, + rc_params->initial_qp_i); + + if (!video->disable_bit_stuffing && rc_params->rc_mode == IMG_RCMODE_CBR) + *coded_buffer_size = max(*coded_buffer_size, + ((rc_params->bits_per_second + rc_params->frame_rate / 2) / + rc_params->frame_rate) * 2); + + if (video->coded_header_per_slice) + *coded_buffer_size += CODED_BUFFER_INFO_SECTION_SIZE * video->slices_per_picture; + else + *coded_buffer_size += CODED_BUFFER_INFO_SECTION_SIZE; + /* Ensure coded buffer sizes are always aligned to 1024 */ + *coded_buffer_size = ALIGN_1024(*coded_buffer_size); + + return IMG_SUCCESS; +} + +/* + * Description: Allocate a coded package + */ +static int topaz_allocate_coded_package(struct topaz_stream_context *str_ctx, + unsigned int coded_buffersize_bytes, + struct coded_package_host **package) +{ + struct coded_package_host *this_package; + struct img_video_context *video = str_ctx->enc_ctx->video; + + *package = kzalloc(sizeof(*package), GFP_KERNEL); + + this_package = *package; + + if (!this_package) + return IMG_ERROR_OUT_OF_MEMORY; + + this_package->busy = 0; + + this_package->num_coded_buffers = 1; + + /* Allocate FW Buffer IMG_BUFFER memory */ + this_package->mtx_info.code_package_fw_buffer = + kzalloc(sizeof(struct img_buffer), GFP_KERNEL); + + if (!this_package->mtx_info.code_package_fw_buffer) + goto error_handling; + + /* Allocate header IMG_BUFFER memory */ + this_package->header_buffer = kzalloc(sizeof(*this_package->header_buffer), GFP_KERNEL); + + if (!this_package->header_buffer) + goto error_handling; + + /* Allocate the FW Package (this will provide addresses + * of header and the coded buffer array) + */ + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + sizeof(struct coded_package_dma_info), 64, + &this_package->mtx_info.code_package_fw_buffer->mem_info)) + goto error_handling; + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE), + (video->coded_header_per_slice ? video->slices_per_picture : 1) * + CODED_BUFFER_INFO_SECTION_SIZE, + 64, &this_package->header_buffer->mem_info)) + goto error_handling; + + this_package->header_buffer->size = + (video->coded_header_per_slice ? video->slices_per_picture : 1) * + CODED_BUFFER_INFO_SECTION_SIZE; + + return IMG_SUCCESS; + +error_handling: + if (*package) { + kfree(*package); + *package = NULL; + } + + if (this_package->mtx_info.code_package_fw_buffer) { + if (this_package->mtx_info.code_package_fw_buffer->mem_info.dev_virt) + topaz_mmu_stream_free + (str_ctx->mmu_ctx, + &this_package->mtx_info.code_package_fw_buffer->mem_info); + + kfree(this_package->mtx_info.code_package_fw_buffer); + this_package->mtx_info.code_package_fw_buffer = NULL; + } + + kfree(this_package->header_buffer); + this_package->header_buffer = NULL; + + return IMG_ERROR_OUT_OF_MEMORY; +} + +/* + * Create the Video Encoder context + */ +static int topaz_video_create_context(struct topaz_stream_context *str_ctx, + struct img_video_params *video_params, + struct img_rc_params *rc_params) +{ + struct img_enc_context *enc; + struct img_video_context *video; + unsigned int alloc_size; + int index, i; + unsigned short picture_height; + unsigned int coded_buffer_size; + unsigned short width_in_mbs; + unsigned short frame_height_in_mbs; + unsigned char pipes_to_use; + unsigned int max_cores; + unsigned int min_slice_height; + unsigned int factor = 1; + unsigned int kick_size, kicks_per_bu; + int ret; + + max_cores = topazdd_get_num_pipes(str_ctx->core_ctx->dev_handle); + + enc = str_ctx->enc_ctx; + + picture_height = + ((video_params->frame_height >> (video_params->is_interlaced ? 1 : 0)) + 15) & ~15; + width_in_mbs = (video_params->width + 15) >> 4; + frame_height_in_mbs = ((picture_height + 15) >> 4) << + (video_params->is_interlaced ? 1 : 0); + + if (topaz_get_encoder_caps(video_params->standard, video_params->width, picture_height, + &enc->caps) != IMG_SUCCESS) { + pr_err("\nERROR: Unable to encode the size %dx%d with current hardware version\n\n", + video_params->width, picture_height); + return IMG_ERROR_NOT_SUPPORTED; + } + + /*scaler input W/H limit is 4K*/ + if (video_params->source_width > 4096) { + pr_err("\nERROR: Source Width is bigger than the maximum supported Source Width(4096)\n"); + return IMG_ERROR_NOT_SUPPORTED; + } + + if (video_params->source_frame_height > 4096) { + pr_err("\nERROR: Source Height is bigger than the maximum supported Source Height(4096)\n"); + return IMG_ERROR_NOT_SUPPORTED; + } + + if (video_params->width > enc->caps.max_width) { + pr_err("\n ERROR: Width too big for given core revision 0x%x. Maximum width is %d.\n", + enc->caps.core_revision, enc->caps.max_width); + return IMG_ERROR_NOT_SUPPORTED; + } + + if (picture_height > enc->caps.max_height) { + pr_err("\n ERROR: Height too big for given core revision 0x%x. Maximum height is %d.\n", + enc->caps.core_revision, enc->caps.max_height); + return IMG_ERROR_NOT_SUPPORTED; + } + + if (video_params->width < enc->caps.min_width) { + pr_err("\n ERROR: Width too small for given core revision 0x%x. Minimum width is %d.\n", + enc->caps.core_revision, enc->caps.min_width); + return IMG_ERROR_NOT_SUPPORTED; + } + + if (video_params->standard == IMG_STANDARD_H264) { + if (video_params->slices_per_picture < enc->caps.min_slices) { + pr_err("WARNING: Minimum slices supported for this resolution is %d. Increasing slices per frame to %d\n", + enc->caps.min_slices, video_params->slices_per_picture); + video_params->slices_per_picture = (unsigned char)enc->caps.min_slices; + } + factor = min(enc->pipes_to_use, video_params->slices_per_picture); + } + + if (video_params->standard == IMG_STANDARD_H264) + pipes_to_use = min(enc->pipes_to_use, video_params->slices_per_picture); + else + pipes_to_use = 1; + + if (picture_height < (enc->caps.min_height * factor)) { + pr_err("\n ERROR: Height too small for given core revision 0x%x. Minimum height is %d.\n", + enc->caps.core_revision, enc->caps.min_height * factor); + return IMG_ERROR_NOT_SUPPORTED; + } + + if ((unsigned int)((width_in_mbs) * (picture_height >> 4)) > enc->caps.max_mb_num) { + pr_err("\n ERROR: Number of macroblocks too high. It should not be bigger than %d.\n", + enc->caps.max_mb_num); + return IMG_ERROR_NOT_SUPPORTED; + } + + calculate_kick_and_bu_size(width_in_mbs, picture_height / 16, video_params->is_interlaced, + enc->caps.max_bu_per_frame, &kick_size, &kicks_per_bu, + &min_slice_height); + + if (enc->caps.min_slice_height > min_slice_height) + min_slice_height = enc->caps.min_slice_height; + + if ((unsigned int)(video_params->slices_per_picture * min_slice_height) > + (unsigned int)(picture_height / 16)) { + /* we have too many slices for this resolution */ + pr_err("\n ERROR: Too many slices for this resolution.\n"); + return IMG_ERROR_NOT_SUPPORTED; + } + + video = kzalloc(sizeof(*video), GFP_KERNEL); + if (!video) + return IMG_ERROR_OUT_OF_MEMORY; + + enc->video = video; + + memcpy(&video->rc_params, rc_params, sizeof(*rc_params)); + + /* Setup BU size for rate control */ + video->rc_params.bu_size = kick_size * kicks_per_bu; + rc_params->bu_size = video->rc_params.bu_size; + + video->kick_size = kick_size; + video->kicks_per_bu = kicks_per_bu; + + video->debug_crcs = video_params->debug_crcs; + + /* stream level params */ + video->standard = video_params->standard; + video->format = video_params->format; + video->csc_preset = video_params->csc_preset; + video->width = width_in_mbs << 4; + video->frame_height = frame_height_in_mbs << 4; + video->unrounded_width = video_params->width; + video->unrounded_frame_height = video_params->frame_height; + + video->picture_height = picture_height; + video->is_interlaced = video_params->is_interlaced; + video->is_interleaved = video_params->is_interleaved; + video->top_field_first = !(video_params->bottom_field_first); + video->encode_requested = 0; + video->limit_num_vectors = video_params->limit_num_vectors; + video->disable_bit_stuffing = video_params->disable_bit_stuffing; + video->vert_mv_limit = video_params->vert_mv_limit; + /* Cabac Parameters */ + video->cabac_enabled = video_params->cabac_enabled; + video->cabac_bin_limit = video_params->cabac_bin_limit; + video->cabac_bin_flex = video_params->cabac_bin_flex; + + video->frame_count = 0; + video->flush_at_frame = 0; + video->flushed_at_frame = 0; + video->encoder_idle = TRUE; + video->high_latency = video_params->high_latency; + video->slices_per_picture = (unsigned char)video_params->slices_per_picture; + video->deblock_idc = video_params->deblock_idc; + video->output_reconstructed = video_params->output_reconstructed; + video->arbitrary_so = video_params->arbitrary_so; + video->f_code = video_params->f_code; + + /* Default f_code is 4 */ + if (!video->f_code) + video->f_code = 4; + + video->vop_time_resolution = video_params->vop_time_resolution; + video->frames_encoded = 0; + video->idr_period = video_params->idr_period; + + video->intra_cnt = video_params->intra_cnt; + video->multi_reference_p = video_params->multi_reference_p; + video->spatial_direct = video_params->spatial_direct; + video->enable_sel_stats_flags = video_params->enable_sel_stats_flags; + video->enable_inp_ctrl = video_params->enable_inp_ctrl; + video->enable_host_bias = video_params->enable_host_bias; + video->enable_host_qp = video_params->enable_host_qp; + /* Line counter */ + video->line_counter = video_params->line_counter_enabled; + + video->enable_air = video_params->enable_air; + video->num_air_mbs = video_params->num_air_mbs; + video->air_threshold = video_params->air_threshold; + video->air_skip_cnt = video_params->air_skip_cnt; + + video->extra_wb_retrieved = 0; + video->highest_storage_number = 0; + + video->buffer_stride_bytes = calculate_stride(video_params->format, + video_params->buffer_stride_bytes, + video_params->source_width); + video->buffer_height = ((video_params->buffer_height ? video_params->buffer_height : + video_params->source_frame_height)); + + if (!video_params->disable_bh_rounding) + video->buffer_height = + (((video->buffer_height >> (video_params->is_interlaced ? 1 : 0)) + 15) & + ~15) << (video_params->is_interlaced ? 1 : 0); + + video_params->buffer_stride_bytes = video->buffer_stride_bytes; + video_params->buffer_height = video->buffer_height; + + video->next_recon = 0; + + video->enable_mvc = video_params->enable_mvc; + video->mvc_view_idx = video_params->mvc_view_idx; + + enc->pipes_to_use = pipes_to_use; + + enc->requested_pipes_to_use = pipes_to_use; + video->slots_in_use = rc_params->bframes + 2; + enc->video->slots_required = enc->video->slots_in_use; + + video->h264_8x8_transform = video_params->h264_8x8; + video->h264_intra_constrained = video_params->constrained_intra; + video->custom_scaling = (video_params->use_custom_scaling_lists != 0); + video->pps_scaling = + (video_params->pps_scaling && + (video_params->use_default_scaling_list || video->custom_scaling)); + + video->encode_pic_processing = 0; + video->next_slice = 0; + video->ref_frame = NULL; + + /* create topaz device context */ + ret = topazdd_create_stream_context(global_topaz_core_context->dev_handle, + str_ctx->enc_ctx->codec, + handle_encoder_firmware_response, str_ctx, + &str_ctx->enc_ctx->video->dd_str_ctx, + &global_wb_data_info); + + if (ret != IMG_SUCCESS) + return ret; + + ret = topazdd_setup_stream_ctx + (str_ctx->enc_ctx->video->dd_str_ctx, video->frame_height, + video->width, (unsigned char *)&video->dd_ctx_num, &video->dd_ctx_num); + + if (ret != IMG_SUCCESS) + return ret; + + /* Create MMU stream context */ + ret = topaz_mmu_stream_create(&global_topaz_core_context->dev_handle->topaz_mmu_ctx, + 0x1 /*stream_id*/, str_ctx->vxe_ctx, &str_ctx->mmu_ctx); + if (ret) + return ret; + + /* WEIGHTED PREDICTION */ + if (video_params->weighted_prediction || + video_params->vp_weighted_implicit_bi_pred == WBI_EXPLICIT) { + video->weighted_prediction = TRUE; + + for (i = 0; i < video->slots_in_use; i++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + sizeof(struct weighted_prediction_values), 64, + &video->weighted_prediction_mem[i])) + IMG_DBG_ASSERT("Allocation failed (A)" == NULL); + } + } else { + video->weighted_prediction = FALSE; + } + + video->weighted_bi_pred = video_params->vp_weighted_implicit_bi_pred; + + video->coded_skipped_index = video_params->coded_skipped_index; + video->inter_intra_index = video_params->inter_intra_index; + + /* + * patch video parameters is the user has specified a profile + * calculate the number of macroblocks per second + */ + video->mbps = width_in_mbs * frame_height_in_mbs * video->rc_params.frame_rate; + + patch_hw_profile(video_params, video); + + enc->auto_expand_pipes = video_params->auto_expand_pipes; + + /* As ui32Vp8RefStructMode is not in use the worst case + * would have to be taken and hence 5 pic nodes + */ + video->pic_nodes = (rc_params->hierarchical ? MAX_REF_B_LEVELS : 0) + + video_params->ref_spacing + 4; + video->mv_stores = (video->pic_nodes * 2); + + /* We're using a common MACRO here so we can guarantee the same calculation + * when managing buffers either from host or within drivers + */ + video->coded_package_max_num = CALC_NUM_CODED_PACKAGES_ENCODE + (video_params->slice_level, + video_params->slices_per_picture, pipes_to_use, + video->is_interlaced); + + alloc_size = MVEA_ABOVE_PARAM_REGION_SIZE * (ALIGN_64(width_in_mbs)); + + for (index = 0; index < (int)max_cores; index++) { + if (str_ctx->vxe_ctx->above_mb_params_sgt[index].sgl) { + video->above_params[index].buf_size = alloc_size; + + topaz_mmu_stream_map_ext_sg + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, + &str_ctx->vxe_ctx->above_mb_params_sgt[index], + video->above_params[index].buf_size, + 64, (enum sys_emem_attrib)0, video->above_params[index].cpu_virt, + &video->above_params[index], + &video->above_params[index].buff_id); + } else { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(alloc_size), 64, &video->above_params[index])) + IMG_DBG_ASSERT("Allocation failed (C)" == NULL); + } + } + + alloc_size = MVEA_MV_PARAM_REGION_SIZE * ALIGN_4(width_in_mbs) * frame_height_in_mbs; + + for (index = 0; index < video->pic_nodes; index++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 64, &video->colocated[index])) + IMG_DBG_ASSERT("Allocation failed (D)" == NULL); + } + + alloc_size = (ALIGN_64(video->width)) * (ALIGN_64(video->frame_height)) * 3 / 2; + + for (index = 0; index < video->pic_nodes; index++) { + void *data; + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 256, &video->recon_pictures[index])) + IMG_DBG_ASSERT("Allocation failed (E)" == NULL); + + data = video->recon_pictures[index].cpu_virt; + memset(data, 0, alloc_size); + + topaz_update_device_mem(str_ctx->vxe_ctx, &video->recon_pictures[index]); + } + + video->patched_recon_buffer = NULL; + + alloc_size = MVEA_MV_PARAM_REGION_SIZE * ALIGN_4(width_in_mbs) * frame_height_in_mbs; + for (i = 0; i < video->mv_stores; i++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 64, &video->mv[i])) + IMG_DBG_ASSERT("Allocation failed (F)" == NULL); + topaz_update_device_mem(str_ctx->vxe_ctx, &video->mv[i]); + } + + if (video->enable_mvc) { + for (i = 0; i < 2; i++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 64, &video->inter_view_mv[i])) + IMG_DBG_ASSERT("Allocation failed (G)" == NULL); + } + } + + /* memory for encoder context */ + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(MTX_CONTEXT_SIZE), 64, &video->mtx_enc_ctx_mem)) + IMG_DBG_ASSERT("Allocation failed (H)" == NULL); + + video->no_sequence_headers = video_params->no_sequence_headers; + video->auto_encode = video_params->auto_encode; + video->slice_level = video_params->slice_level; + video->coded_header_per_slice = video_params->coded_header_per_slice; + + /* partially coded headers supplied to HW */ + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + MAX_HEADERSIZEBYTES, 64, &video->seq_header_mem)) + IMG_DBG_ASSERT("Allocation failed (I)\n" == NULL); + + /* partially coded subset sequence parameter headers supplied to HW */ + if (video->enable_mvc) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + MAX_HEADERSIZEBYTES, 64, &video->subset_seq_header_mem)) + IMG_DBG_ASSERT("Allocation failed (J)" == NULL); + } + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + MAX_BFRAMES * MV_ROW_STRIDE, 64, &video->mv_settings_btable)) + + IMG_DBG_ASSERT("Allocation failed (K)" == NULL); + + if (video->rc_params.hierarchical) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + MAX_BFRAMES * sizeof(struct img_mv_settings), 64, + &video->mv_settings_hierarchical)) + IMG_DBG_ASSERT("Allocation failed (L)" == NULL); + } else { + video->mv_settings_hierarchical.cpu_virt = NULL; + } + + video->insert_hrd_params = video_params->insert_hrd_params; + if (video_params->insert_hrd_params) { + alloc_size = 64; + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 64, &video->aud_header_mem)) + IMG_DBG_ASSERT("Allocation failed (M)" == NULL); + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 64, &video->sei_buffering_period_header_mem)) + IMG_DBG_ASSERT("Allocation failed (N)" == NULL); + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + alloc_size, 64, &video->sei_picture_timing_header_mem)) + IMG_DBG_ASSERT("Allocation failed (O)" == NULL); + } + + for (index = 0; index < ARRAY_SIZE(video->pichdr_template_mem); index++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + MAX_HEADERSIZEBYTES, 64, &video->pichdr_template_mem[index])) + IMG_DBG_ASSERT("Allocation failed (P)" == NULL); + } + + for (index = 0; index < ARRAY_SIZE(video->slice_params_template_mem); index++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(sizeof(struct slice_params)), 64, + &video->slice_params_template_mem[index])) + IMG_DBG_ASSERT("Allocation failed (Q)" == NULL); + } + + for (index = 0; index < video->slots_in_use; index++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(sizeof(struct mtx_header_params)), 64, + &video->ltref_header[index])) + IMG_DBG_ASSERT("Allocation failed (R)" == NULL); + } + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(4), 64, &video->src_phys_addr)) + IMG_DBG_ASSERT("Allocation failed (S)" == NULL); + + for (index = 0; index < video->slots_in_use; index++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + (1 + MAX_SLICESPERPIC * 2 + 15) & ~15, 64, + &video->slice_map[index].mem_info) != IMG_SUCCESS) + IMG_DBG_ASSERT("Allocation failed (T)" == NULL); + } + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(sizeof(unsigned short) * MAX_GOP_SIZE), 64, &video->flat_gop_struct)) + IMG_DBG_ASSERT("Allocation failed (U)" == NULL); + + if (video->rc_params.hierarchical) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(sizeof(unsigned short) * MAX_GOP_SIZE), 64, + &video->hierar_gop_struct)) + IMG_DBG_ASSERT("Allocation failed (V)" == NULL); + } + + if (video->custom_scaling) { + for (index = 0; index < 2; index++) { + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + ALIGN_64(QUANT_LISTS_SIZE), 64, &video->custom_quant[index])) + IMG_DBG_ASSERT("Allocation failed (W)" == NULL); + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + 192, 64, &video->custom_quant_regs4x4_sp[index])) + IMG_DBG_ASSERT("Allocation failed (X)" == NULL); + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + 128 * CUSTOM_QUANT_PARAMSIZE_8x8, 64, + &video->custom_quant_regs8x8_sp[index])) + IMG_DBG_ASSERT("Allocation failed (Y)" == NULL); + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + 128, 64, &video->custom_quant_regs4x4_q[index])) + IMG_DBG_ASSERT("Allocation failed (Z)" == NULL); + + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + 64 * CUSTOM_QUANT_PARAMSIZE_8x8, 64, + &video->custom_quant_regs8x8_q[index])) + IMG_DBG_ASSERT("Allocation failed (0)" == NULL); + } + video->custom_quant_slot = 0; + } + + /* Allocate device memory for storing feedback information for all "5" slots */ + if (video->enable_sel_stats_flags & ESF_FIRST_STAGE_STATS) { + for (index = 0; index < video->slots_in_use; index++) { + unsigned int row_size = + ALIGN_64(width_in_mbs * sizeof(struct img_first_stage_mb_params)); + + /* Allocate memory padding size of each row to be multiple of 64-bytes */ + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + frame_height_in_mbs * row_size, 64, + &video->firstpass_out_param_buf[index].mem_info)) + IMG_DBG_ASSERT("Allocation failed (1)" == NULL); + + video->firstpass_out_param_buf[index].lock = BUFFER_FREE; + video->firstpass_out_param_buf[index].bytes_written = 0; + video->firstpass_out_param_buf[index].size = + frame_height_in_mbs * row_size; + } + } else { + /* Set buffer pointers to NULL */ + for (index = 0; index < video->slots_in_use; index++) { + video->firstpass_out_param_buf[index].mem_info.cpu_virt = NULL; + video->firstpass_out_param_buf[index].lock = BUFFER_FREE; + video->firstpass_out_param_buf[index].bytes_written = 0; + video->firstpass_out_param_buf[index].size = 0; + } + } + + /* Allocate device memory for storing feedback information for all "5" slots */ + if (video->enable_sel_stats_flags & ESF_MP_BEST_MB_DECISION_STATS || + video->enable_sel_stats_flags & ESF_MP_BEST_MOTION_VECTOR_STATS) { + for (index = 0; index < video->slots_in_use; index++) { + unsigned int best_multipass_size = frame_height_in_mbs * + //From TRM (4.5.2) + (((5 * width_in_mbs) + 3) >> 2) * 64; + + /* Allocate memory padding size of each row to be multiple of 64-bytes */ + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), + best_multipass_size, 64, + &video->firstpass_out_best_multipass_param_buf[index].mem_info)) + IMG_DBG_ASSERT("Allocation failed (2)" == NULL); + + video->firstpass_out_best_multipass_param_buf[index].lock = + BUFFER_FREE; + video->firstpass_out_best_multipass_param_buf[index].bytes_written = 0; + video->firstpass_out_best_multipass_param_buf[index].size = + best_multipass_size; + } + } else { + /* Set buffer pointers to NULL */ + for (index = 0; index < video->slots_in_use; index++) { + video->firstpass_out_best_multipass_param_buf[index].mem_info.cpu_virt = + NULL; + video->firstpass_out_best_multipass_param_buf[index].lock = + BUFFER_FREE; + video->firstpass_out_best_multipass_param_buf[index].bytes_written = 0; + video->firstpass_out_best_multipass_param_buf[index].size = 0; + } + } + + if (video->enable_inp_ctrl) { + for (index = 0; index < video->slots_in_use; index++) { + alloc_size = frame_height_in_mbs * width_in_mbs * 2; + + /* + * Allocate memory for worst case slice structure + * i.e. assume number-of-slices == number-of-rows + */ + if (topaz_mmu_stream_alloc + (str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | + SYS_MEMATTRIB_WRITECOMBINE), alloc_size + 64, 64, + &video->mb_ctrl_in_params_buf[index].mem_info)) + IMG_DBG_ASSERT("Allocation failed (3)" == NULL); + + video->mb_ctrl_in_params_buf[index].lock = BUFFER_FREE; + video->mb_ctrl_in_params_buf[index].bytes_written = 0; + video->mb_ctrl_in_params_buf[index].size = alloc_size; + } + } else { + for (index = 0; index < video->slots_in_use; index++) { + video->mb_ctrl_in_params_buf[index].mem_info.cpu_virt = NULL; + video->mb_ctrl_in_params_buf[index].lock = BUFFER_FREE; + video->mb_ctrl_in_params_buf[index].bytes_written = 0; + video->mb_ctrl_in_params_buf[index].size = 0; + } + } + + for (index = 0; index < video->slots_in_use; index++) + video->source_slot_buff[index] = NULL; + + /* Allocate coded package */ + topaz_get_context_coded_buffer_size(enc, rc_params, &coded_buffer_size); + + video->coded_buffer_max_size = coded_buffer_size; + + for (i = 0; i < video->coded_package_max_num; i++) { + if (topaz_allocate_coded_package(str_ctx, coded_buffer_size, + &video->coded_package[i]) != IMG_SUCCESS) + IMG_DBG_ASSERT("Coded package Allocation failed\n" == NULL); + } + + video->encode_sent = 0; + + topaz_video_prepare_templates(str_ctx, video_params->f_code, + video_params->fine_y_search_size); + + enc->video->max_chunks = video_params->max_chunks; + enc->video->chunks_per_mb = video_params->chunks_per_mb; + enc->video->priority_chunks = video_params->priority_chunks; + + return topaz_video_create_mtx_context(str_ctx, video_params); +} + +unsigned char topaz_validate_params(struct img_video_params *video_params, + struct img_rc_params *rc_params) +{ + unsigned char modified = FALSE; + unsigned int required_core_des1 = 0; + unsigned int core_des1 = topazdd_get_core_des1(); + + if (video_params) { + /* Validate video params */ + if (video_params->standard == IMG_STANDARD_H264) { + if (video_params->is_interlaced) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_INTERLACED_SUPPORTED)) == 0) { + video_params->is_interlaced = FALSE; + + if (!video_params->is_interleaved) { + /* Non-interleaved source. + * Encode field pictures as frames. + */ + video_params->frame_height >>= 1; + video_params->buffer_height >>= 1; + video_params->source_frame_height >>= 1; + } else { + /* Interleaved source. Unite fields into single picture. */ + video_params->is_interleaved = FALSE; + } + + video_params->bottom_field_first = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_INTERLACED_SUPPORTED); + } + } + + if (video_params->h264_8x8) { + if ((core_des1 & + F_ENCODE(1, TOPAZHP_TOP_CR_TOPAZHP_H264_8X8_TRANSFORM_SUPPORTED)) == + 0) { + video_params->h264_8x8 = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_8X8_TRANSFORM_SUPPORTED); + } + } + + if (video_params->cabac_enabled) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_CABAC_SUPPORTED)) == 0) { + video_params->cabac_enabled = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_CABAC_SUPPORTED); + } + } + + if (!video_params->enc_features.disable_bframes) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED)) == 0) { + video_params->enc_features.disable_bframes = FALSE; + modified = TRUE; + } + } + + if (video_params->enable_sel_stats_flags) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_MULTIPASS_SUPPORTED)) == 0) { + video_params->enable_sel_stats_flags = 0; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_MULTIPASS_SUPPORTED); + } + } + + if (video_params->use_default_scaling_list) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_DEFAULT_TABLES_SUPPORTED)) == + 0) { + video_params->use_default_scaling_list = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_DEFAULT_TABLES_SUPPORTED); + } + } + + if (video_params->use_custom_scaling_lists) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_CUSTOM_QUANT_SUPPORTED)) == 0) { + video_params->use_custom_scaling_lists = 0; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_CUSTOM_QUANT_SUPPORTED); + } + } + + if ((video_params->weighted_prediction || + video_params->vp_weighted_implicit_bi_pred)) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_SUPPORTED)) == + 0) { + video_params->weighted_prediction = FALSE; + video_params->vp_weighted_implicit_bi_pred = 0; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_SUPPORTED); + } + } + + if (video_params->multi_reference_p || video_params->enable_mvc) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_2_REF_ON_P_PIC_SUPPORTED)) == + 0) { + video_params->multi_reference_p = FALSE; + video_params->enable_mvc = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_2_REF_ON_P_PIC_SUPPORTED); + } + } + + if (video_params->spatial_direct) { + if ((core_des1 & + F_ENCODE + (1, + TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED)) == + 0) { + video_params->spatial_direct = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED); + } + } + + if (video_params->enable_lossless) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_LOSSLESS_SUPPORTED)) == 0) { + video_params->enable_lossless = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_LOSSLESS_SUPPORTED); + } + } + } + + if (video_params->enable_scaler) { + if ((core_des1 & + F_ENCODE(1, TOPAZHP_TOP_CR_TOPAZHP_SCALER_SUPPORTED)) == 0) { + video_params->enable_scaler = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_SCALER_SUPPORTED); + } + } + + if (rc_params) { + /* Validate RC params */ + if (video_params->standard == IMG_STANDARD_H264) { + if (rc_params->bframes) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED)) == + 0) { + rc_params->bframes = 0; + rc_params->hierarchical = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED); + } + } + + if (rc_params->hierarchical && rc_params->bframes > 1) { + if ((core_des1 & + F_ENCODE + (1, TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED)) + == 0) { + rc_params->hierarchical = FALSE; + modified = TRUE; + } else { + required_core_des1 |= F_ENCODE(1, + TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED); + } + } + } + } + } + + return modified; +} + +/* + * Creat an encoder context + */ +int topaz_stream_create(void *vxe_ctx, struct img_video_params *video_params, + unsigned char base_pipe, unsigned char pipes_to_use, + struct img_rc_params *rc_params, void **topaz_str_context) +{ + struct img_enc_context *enc; + struct topaz_stream_context *str_ctx; + + if (!is_topaz_core_initialized) + return IMG_ERROR_NOT_INITIALISED; + + str_ctx = kzalloc(sizeof(*str_ctx), GFP_KERNEL); + if (!str_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + enc = kzalloc(sizeof(*enc), GFP_KERNEL); + if (!enc) { + kfree(str_ctx); + return IMG_ERROR_OUT_OF_MEMORY; + } + + *topaz_str_context = str_ctx; + str_ctx->enc_ctx = enc; + str_ctx->core_ctx = global_topaz_core_context; + str_ctx->vxe_ctx = (struct vxe_enc_ctx *)vxe_ctx; + + enc->core_rev = topazdd_get_core_rev(); + enc->sync_first_pass = true; + + enc->requested_base_pipe = base_pipe; + enc->base_pipe = base_pipe; + enc->requested_pipes_to_use = pipes_to_use; + enc->pipes_to_use = pipes_to_use; + + topaz_validate_params(video_params, rc_params); + + switch (video_params->standard) { + case IMG_STANDARD_H264: + if (video_params->enable_mvc) { + switch (rc_params->rc_mode) { + case IMG_RCMODE_NONE: + enc->codec = IMG_CODEC_H264MVC_NO_RC; + break; + case IMG_RCMODE_CBR: + enc->codec = IMG_CODEC_H264MVC_CBR; + break; + case IMG_RCMODE_VBR: + enc->codec = IMG_CODEC_H264MVC_VBR; + break; + case IMG_RCMODE_ERC: + enc->codec = IMG_CODEC_H264MVC_ERC; + break; + case IMG_RCMODE_VCM: + IMG_DBG_ASSERT("VCM mode is not supported for MVC" == NULL); + break; + default: + break; + } + } else { + switch (rc_params->rc_mode) { + case IMG_RCMODE_NONE: + enc->codec = IMG_CODEC_H264_NO_RC; + break; + case IMG_RCMODE_CBR: + enc->codec = IMG_CODEC_H264_CBR; + break; + case IMG_RCMODE_VBR: + enc->codec = IMG_CODEC_H264_VBR; + break; + case IMG_RCMODE_VCM: + enc->codec = IMG_CODEC_H264_VCM; + break; + case IMG_RCMODE_ERC: + enc->codec = IMG_CODEC_H264_ERC; + break; + default: + break; + } + } + break; + default: + IMG_DBG_ASSERT("Only H264 encode is supported" == NULL); + } + + /* initialise video context structure */ + return (topaz_video_create_context(str_ctx, video_params, rc_params)); +} + +/* + * Sends a command to the specified core. + * The function returns a writeback value. This is a unique value that will be + * written back by the target core after it completes its command. + */ +unsigned int topaz_insert_command(struct img_enc_context *enc_ctx, + enum mtx_cmd_id cmd_id, unsigned int data) +{ + unsigned int writeback_val; + + if (enc_ctx->debug_settings && + enc_ctx->debug_settings->serialized_communication_mode == + VXE_SERIALIZED_MODE_SERIAL) + /* in serial mode do not use the priority bit */ + cmd_id &= ~MTX_CMDID_PRIORITY; + + topazdd_send_msg(enc_ctx->video->dd_str_ctx, cmd_id, data, NULL, &writeback_val); + + return writeback_val; +} + +/* + * Sends a command to the specified core. + */ +unsigned int topaz_insert_command_with_sync(struct img_enc_context *enc_ctx, + enum mtx_cmd_id cmd_id, unsigned int data) +{ + int ret; + + if (enc_ctx->debug_settings && + enc_ctx->debug_settings->serialized_communication_mode == + VXE_SERIALIZED_MODE_SERIAL) + /* in serial mode do not use the priority bit */ + cmd_id &= ~MTX_CMDID_PRIORITY; + + ret = topazdd_send_msg_with_sync(enc_ctx->video->dd_str_ctx, cmd_id, data, NULL); + + return ret; +} + +/* + * Sends a command to the specified core. + * The data specified in psCommandData will be read via DMA by the MTX, + * so this memory must remain in scope for the duration of the execution + * of the command. + * The function returns a writeback value. This is a unique value that will be + * written back by the target core after it completes its command. + */ +unsigned int topaz_insert_mem_command(struct img_enc_context *enc_ctx, + enum mtx_cmd_id cmd_id, + unsigned int data, + struct vidio_ddbufinfo *command_data) +{ + unsigned int writeback_val; + + /* Priority bit is not supported for MEM commands */ + cmd_id &= ~MTX_CMDID_PRIORITY; + + topazdd_send_msg(enc_ctx->video->dd_str_ctx, cmd_id, data, command_data, &writeback_val); + + return writeback_val; +} + +/* + * Sends a command to the specified core. + * The data specified in psCommandData will be read via DMA by the MTX, + * so this memory must remain in scope for the duration of the execution + * of the command. + */ +unsigned int topaz_insert_mem_command_with_sync(struct img_enc_context *enc_ctx, + enum mtx_cmd_id cmd_id, + unsigned int data, + struct vidio_ddbufinfo *command_data) +{ + int ret; + + /* Priority bit is not supported for MEM commands */ + cmd_id &= ~MTX_CMDID_PRIORITY; + + ret = topazdd_send_msg_with_sync(enc_ctx->video->dd_str_ctx, cmd_id, + data, command_data); + return ret; +} + +/* + * Send the Access Unit Delimiter to the stream + */ +static int topaz_send_aud_header(struct img_enc_context *enc) +{ + if (enc->video->aborted) + return IMG_ERROR_UNDEFINED; + + /* must use unique writeback word */ + topaz_insert_mem_command(enc, MTX_CMDID_DO_HEADER, 0, + enc->video->aud_header_mem.cpu_virt); + + return IMG_SUCCESS; +} + +/* + * Transmit the picture headerts to MTX + */ +static int topaz_send_picture_headers(struct img_enc_context *enc) +{ + /* send Seqence headers only for IDR (I-frames) and only once in the beginning */ + struct img_video_context *video = enc->video; + + /* SEI_INSERTION */ + if (video->insert_hrd_params) { + /* Access unit delimiter */ + if (!video->enable_mvc || (video->enable_mvc && video->mvc_view_idx == 0)) + /* in case of MVC, both views are a single access unit. + * delimiter should be inserted by view 0 only. + */ + topaz_send_aud_header(enc); + } + + if (video->insert_seq_header && !video->no_sequence_headers) { + switch (video->standard) { + case IMG_STANDARD_H264: + IMG_DBG_ASSERT("SPS and PPS will be send from firmware." != NULL); + break; + default: + IMG_DBG_ASSERT("only H264 encode is supported." == NULL); + break; + } + } + + return IMG_SUCCESS; +} + +/* + * Encode a frame + */ +int topaz_encode_frame(void *topaz_str_ctx) +{ + struct img_enc_context *enc; + struct img_video_context *video; + struct topaz_stream_context *str_ctx; + /* If line counter is enabled, we add one more bit in the command data + * to inform the firmware context whether it should proceed + */ + unsigned int encode_cmd_data; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return IMG_ERROR_UNEXPECTED_STATE; + + video->insert_seq_header = (video->encode_sent == 0); + + topaz_send_picture_headers(enc); + + encode_cmd_data = F_ENCODE(1, MTX_MSG_ENCODE_CODED_INTERRUPT); + + if (video->line_counter) + /* Set bit 20 to 1 to inform FW that we are using the line counter feature */ + encode_cmd_data |= F_ENCODE(1, MTX_MSG_ENCODE_USE_LINE_COUNTER); + + topaz_insert_command_with_sync(enc, (enum mtx_cmd_id) + (MTX_CMDID_ENCODE_FRAME | MTX_CMDID_WB_INTERRUPT), + encode_cmd_data); + + video->encode_pic_processing++; + video->encode_sent++; + + return IMG_SUCCESS; +} + +int topaz_get_pipe_usage(unsigned char pipe, unsigned char *ctx_id) +{ + IMG_DBG_ASSERT(pipe < TOPAZHP_MAX_NUM_PIPES); + + if (pipe >= TOPAZHP_MAX_NUM_PIPES) + return 0; + + return global_pipe_usage[pipe]; +} + +void topaz_set_pipe_usage(unsigned char pipe, unsigned char val) +{ + IMG_DBG_ASSERT(pipe < TOPAZHP_MAX_NUM_PIPES); + + if (pipe < TOPAZHP_MAX_NUM_PIPES) + global_pipe_usage[pipe] = val; +} + +/* + * Set the mtx context to the one implicit in the encoder context + */ +static int topaz_video_setup_mtx_context(struct img_enc_context *enc) +{ + struct img_video_context *video_context; + unsigned char index; + + video_context = enc->video; + + for (index = 0; index < enc->pipes_to_use; index++) + topaz_set_pipe_usage(enc->base_pipe + index, enc->ctx_num); + + if (topaz_insert_mem_command_with_sync(enc, (enum mtx_cmd_id) + (MTX_CMDID_SETVIDEO | MTX_CMDID_WB_INTERRUPT), + enc->base_pipe, &video_context->mtx_enc_ctx_mem)) { + pr_err("topaz mtx context setup command failed\n"); + return IMG_ERROR_UNDEFINED; + } + + video_context->aborted = FALSE; + + return IMG_SUCCESS; +} + +/* + * Load the encoder and MTX context + */ +int topaz_load_context(void *topaz_str_ctx) +{ + struct topaz_stream_context *str_ctx; + struct img_enc_context *enc; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + + enc->video->vid_ctx_num = 0; + + enc->ctx_num++; + + return topaz_video_setup_mtx_context(enc); +} + +/* + * Store the encoder and MTX context + */ +int topaz_store_context(void *topaz_str_ctx) +{ + struct img_enc_context *enc; + struct topaz_stream_context *str_ctx; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + + /* Update Globals */ + if (enc->codec != IMG_CODEC_NONE && enc->codec != IMG_CODEC_JPEG) { + struct img_video_context *video_context; + + video_context = enc->video; + + if (!topaz_insert_mem_command_with_sync(enc, (enum mtx_cmd_id) + (MTX_CMDID_GETVIDEO | + MTX_CMDID_WB_INTERRUPT), + enc->base_pipe, &video_context->mtx_enc_ctx_mem)) { + pr_err("MTX message for GETVIDEO failed\n"); + return IMG_ERROR_UNDEFINED; + } + } + + return IMG_SUCCESS; +} + +/* + * Flush video stream + */ +int topaz_flush_stream(void *topaz_str_ctx, unsigned int frame_cnt) +{ + struct topaz_stream_context *str_ctx; + struct img_enc_context *enc; + int index; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + enc = str_ctx->enc_ctx; + + if (enc->video->aborted) + return IMG_ERROR_UNDEFINED; + + /* flush the internal queues */ + /* Check source slots */ + for (index = 0; index < enc->video->slots_in_use; index++) { + if (enc->video->source_slot_buff[index]) { + /* Found a valid src_frame, so signal callback for the same. */ + global_topaz_core_context->vxe_str_processed_cb(str_ctx->vxe_ctx, + VXE_CB_SRC_FRAME_RELEASE, + (void *)(enc->video->source_slot_buff[index]), + 0, 0); + enc->video->source_slot_buff[index] = NULL; + } + } + + /* Check coded package slots */ + for (index = 0; index < enc->video->coded_package_max_num; index++) { + if (enc->video->coded_package[index]->busy) { + /* Found a valid coded package, so, signal callback for the same */ + global_topaz_core_context->vxe_str_processed_cb(str_ctx->vxe_ctx, + VXE_CB_CODED_BUFF_READY, + (void *)(enc->video->coded_package[index]->coded_buffer[0]), + 0, 0); + enc->video->coded_package[index]->busy = FALSE; + } + } + + return IMG_SUCCESS; +} + +/* + * Destroy the Video Encoder context + */ +static int topaz_video_destroy_context(struct topaz_stream_context *str_ctx) +{ + struct img_enc_context *enc; + struct img_video_context *video; + int i; + unsigned int max_cores; + + max_cores = topazdd_get_num_pipes(str_ctx->core_ctx->dev_handle); + enc = str_ctx->enc_ctx; + video = enc->video; + + for (i = 0; i < enc->pipes_to_use; i++) + if (topaz_get_pipe_usage(enc->base_pipe + i, NULL) == enc->ctx_num) + topaz_set_pipe_usage(enc->base_pipe + i, 0); + + if (video->standard == IMG_STANDARD_H264 && video->weighted_prediction) { + for (i = 0; i < video->slots_in_use; i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->weighted_prediction_mem[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + } + + for (i = 0; i < video->coded_package_max_num; i++) { + if (topaz_mmu_stream_free + (str_ctx->mmu_ctx, + &video->coded_package[i]->mtx_info.code_package_fw_buffer->mem_info)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->coded_package[i]->header_buffer->mem_info)) + IMG_DBG_ASSERT("Free failed" == NULL); + + kfree(video->coded_package[i]); + video->coded_package[i] = NULL; + } + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->flat_gop_struct)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (video->rc_params.hierarchical) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->hierar_gop_struct)) + IMG_DBG_ASSERT("Free failed" == NULL); + + for (i = 0; i < video->slots_in_use; i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->slice_map[i].mem_info)) + IMG_DBG_ASSERT("slice map free failed" == NULL); + } + + for (i = 0; i < (int)max_cores; i++) { + if (str_ctx->vxe_ctx->above_mb_params_sgt[i].sgl) { + topaz_mmu_stream_free_sg(str_ctx->mmu_ctx, &video->above_params[i]); + } else { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->above_params[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + } + + for (i = 0; i < video->pic_nodes; i++) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->colocated[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + for (i = 0; i < video->pic_nodes; i++) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->recon_pictures[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + for (i = 0; i < video->mv_stores; i++) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->mv[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (video->enable_mvc) { + for (i = 0; i < 2; i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->inter_view_mv[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + } + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->mtx_enc_ctx_mem)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->mv_settings_btable)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (video->mv_settings_hierarchical.cpu_virt) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->mv_settings_hierarchical)) + IMG_DBG_ASSERT("Free failed" == NULL); + + /* partially coded headers supplied to HW */ + /* SEI_INSERTION */ + if (video->insert_hrd_params) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->aud_header_mem)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->sei_buffering_period_header_mem)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->sei_picture_timing_header_mem)) + IMG_DBG_ASSERT("Free failed" == NULL); + } + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->seq_header_mem)) + IMG_DBG_ASSERT("Free failed" == NULL); + + /* FREE subset sequence parameter header */ + if (video->enable_mvc) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->subset_seq_header_mem)) + IMG_DBG_ASSERT("Free failed" == NULL); + + for (i = 0; i < ARRAY_SIZE(video->pichdr_template_mem); i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->pichdr_template_mem[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + + for (i = 0; i < ARRAY_SIZE(video->slice_params_template_mem); i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->slice_params_template_mem[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->src_phys_addr)) + IMG_DBG_ASSERT("Free failed" == NULL); + + /* de-allocate memory corresponding to the output parameters */ + for (i = 0; i < video->slots_in_use; i++) { + if (video->firstpass_out_param_buf[i].mem_info.cpu_virt) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->firstpass_out_param_buf[i].mem_info)) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (video->mb_ctrl_in_params_buf[i].mem_info.cpu_virt) + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->mb_ctrl_in_params_buf[i].mem_info)) + IMG_DBG_ASSERT("Free failed" == NULL); + } + + /* de-allocate memory corresponding to the selectable best MV parameters */ + for (i = 0; i < video->slots_in_use; i++) { + if (video->firstpass_out_best_multipass_param_buf[i].mem_info.cpu_virt) + if (topaz_mmu_stream_free + (str_ctx->mmu_ctx, + &video->firstpass_out_best_multipass_param_buf[i].mem_info)) + IMG_DBG_ASSERT("Free failed" == NULL); + } + + for (i = 0; i < video->slots_in_use; i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->ltref_header[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + + if (video->custom_scaling) { + for (i = 0; i < 2; i++) { + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, &video->custom_quant[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->custom_quant_regs4x4_sp[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->custom_quant_regs8x8_sp[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->custom_quant_regs4x4_q[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + + if (topaz_mmu_stream_free(str_ctx->mmu_ctx, + &video->custom_quant_regs8x8_q[i])) + IMG_DBG_ASSERT("Free failed" == NULL); + } + } + + topazdd_destroy_stream_ctx(video->dd_str_ctx); + + topaz_mmu_stream_destroy(&global_topaz_core_context->dev_handle->topaz_mmu_ctx, + str_ctx->mmu_ctx); + + /* free the video encoder structure itself */ + kfree(video); + + return IMG_SUCCESS; +} + +/* + * Destroy an Encoder Context + */ +int topaz_stream_destroy(void *str_context) +{ + struct img_enc_context *enc; + struct topaz_stream_context *str_ctx; + int ret; + + str_ctx = (struct topaz_stream_context *)str_context; + if (!str_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + enc = str_ctx->enc_ctx; + + ret = topaz_video_destroy_context(str_ctx); + + kfree(enc->debug_settings); + enc->debug_settings = NULL; + + kfree(enc); + kfree(str_context); + + return ret; +} + +/* + * Get the capabilities of the encoder for the given codec + */ +int topaz_get_encoder_caps(enum img_standard standard, + unsigned short width, unsigned short height, + struct img_enc_caps *caps) +{ + unsigned int width_in_mbs, height_in_mbs, kick_size, kicks_per_bu, min_slice_height, mbs; + + /* get the actual number of cores */ + caps->num_cores = topazdd_get_num_pipes(global_topaz_core_context->dev_handle); + + if (caps->num_cores < 3) + caps->max_bu_per_frame = TOPAZHP_MAX_BU_SUPPORT_HD; + else + caps->max_bu_per_frame = TOPAZHP_MAX_BU_SUPPORT_4K; + + caps->core_features = topazdd_get_core_des1(); + caps->core_revision = topazdd_get_core_rev(); + + width_in_mbs = (width + 15) / 16; + height_in_mbs = (height + 15) / 16; + + switch (standard) { + case IMG_STANDARD_H264: + /* Assume progressive video for now as we don't know either way */ + calculate_kick_and_bu_size(width_in_mbs, height_in_mbs, FALSE, + caps->max_bu_per_frame, &kick_size, &kicks_per_bu, + &min_slice_height); + caps->max_slices = height_in_mbs / min_slice_height; + + /* + * Limit for number of MBs in slices is 32K-2 = 32766 + * Here we will limit it to 16K per slice = 16384 + */ + caps->min_slices = 1; + mbs = width_in_mbs * height_in_mbs; + if (mbs >= 32768) + caps->min_slices = 3; + else if (mbs >= 16384) + caps->min_slices = 2; + + /* if height is bigger or equal to 4000, use at least two slices */ + if (height_in_mbs >= 250 && caps->min_slices == 1) + caps->min_slices = 2; + + caps->recommended_slices = min(caps->num_cores, caps->max_slices); + caps->min_slice_height = min_slice_height; + + caps->max_height = 2048; + caps->max_width = 2048; + caps->min_height = 48; + caps->min_width = 144; + caps->max_mb_num = (2048 * 2048) >> 8; + break; + default: + IMG_DBG_ASSERT("Only H264 encoder is supported" == NULL); + } + + if (caps->recommended_slices < caps->min_slices) + caps->recommended_slices = caps->min_slices; + if (caps->recommended_slices > caps->max_slices) + caps->recommended_slices = caps->max_slices; + + return IMG_SUCCESS; +} + +/* + * Supply a source frame to the encode process + */ +int topaz_send_source_frame(void *topaz_str_ctx, struct img_frame *src_frame, + unsigned int frame_num, unsigned long long ctx) +{ + struct topaz_stream_context *str_ctx; + struct img_source_buffer_params *buffer_params; + + struct img_enc_context *enc; + struct img_video_context *video; + unsigned char slot_number; + void *data; + unsigned int y_plane_base = 0; + unsigned int u_plane_base = 0; + unsigned int v_plane_base = 0; + struct vidio_ddbufinfo *cmd_data_mem_info = NULL; + unsigned char *slice_map_addr = NULL; + unsigned char index; + unsigned char round; + unsigned char slice_number; + unsigned char first_bu_in_slice; + unsigned char size_in_bus; + unsigned int slice_height; + unsigned char halfway_slice; + unsigned int halfway_bu; + unsigned char slices_per_picture; + unsigned int picture_height_remaining; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + /* if source slot is NULL then it's just a next portion of slices */ + if (!src_frame) + return IMG_ERROR_UNEXPECTED_STATE; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return IMG_ERROR_UNEXPECTED_STATE; + + slot_number = video->source_slot_reserved; + + /* mark the appropriate slot as filled */ + video->source_slot_buff[slot_number] = src_frame; + video->source_slot_poc[slot_number] = frame_num; + + topaz_get_cmd_data_buffer(&cmd_data_mem_info); + + if (!cmd_data_mem_info) + return IMG_ERROR_UNEXPECTED_STATE; + + data = cmd_data_mem_info->cpu_virt; + buffer_params = (struct img_source_buffer_params *)data; + + /* Prepare data */ + if (src_frame->y_plane_buffer) { + populate_firmware_message(&video->src_phys_addr, 0, + &src_frame->y_plane_buffer->mem_info, 0); + + data = video->src_phys_addr.cpu_virt; + y_plane_base = *((unsigned int *)data); + } + + if (src_frame->u_plane_buffer) { + populate_firmware_message(&video->src_phys_addr, 0, + &src_frame->u_plane_buffer->mem_info, 0); + + data = video->src_phys_addr.cpu_virt; + u_plane_base = *((unsigned int *)data); + } else { + u_plane_base = y_plane_base; + } + + if (src_frame->v_plane_buffer) { + populate_firmware_message(&video->src_phys_addr, 0, + &src_frame->v_plane_buffer->mem_info, 0); + + data = video->src_phys_addr.cpu_virt; + v_plane_base = *((unsigned int *)data); + } else { + v_plane_base = u_plane_base; + } + + buffer_params->slot_num = slot_number; + buffer_params->display_order_num = (unsigned char)(frame_num & 0xFF); + buffer_params->host_context = ctx; + + buffer_params->phys_addr_y_plane_field_0 = y_plane_base + src_frame->y_component_offset + + src_frame->field0_y_offset; + buffer_params->phys_addr_u_plane_field_0 = u_plane_base + src_frame->u_component_offset + + src_frame->field0_u_offset; + buffer_params->phys_addr_v_plane_field_0 = v_plane_base + src_frame->v_component_offset + + src_frame->field0_v_offset; + + buffer_params->phys_addr_y_plane_field_1 = y_plane_base + src_frame->y_component_offset + + src_frame->field1_y_offset; + buffer_params->phys_addr_u_plane_field_1 = u_plane_base + src_frame->u_component_offset + + src_frame->field1_u_offset; + buffer_params->phys_addr_v_plane_field_1 = v_plane_base + src_frame->v_component_offset + + src_frame->field1_v_offset; + + topaz_update_device_mem(str_ctx->vxe_ctx, cmd_data_mem_info); + + topaz_get_buffer(str_ctx, &video->slice_map[slot_number], (void **)&slice_map_addr, + FALSE); + + /* Fill standard Slice Map (non arbitrary) */ + halfway_bu = 0; + first_bu_in_slice = 0; + slice_number = 0; + slices_per_picture = video->slices_per_picture; + picture_height_remaining = video->picture_height; + halfway_slice = slices_per_picture / 2; + *slice_map_addr = slices_per_picture; + slice_map_addr++; + round = 16 * enc->caps.min_slice_height - 1; + + for (index = 0; index < slices_per_picture - 1; index++) { + if (index == halfway_slice) + halfway_bu = first_bu_in_slice; + + slice_height = (picture_height_remaining / (video->slices_per_picture - index)) & + ~round; + picture_height_remaining -= slice_height; + size_in_bus = ((slice_height / 16) * (video->width / 16)) / + video->rc_params.bu_size; + + /* slice number */ + *slice_map_addr = slice_number; + slice_map_addr++; + + /* SizeInKicks BU */ + *slice_map_addr = size_in_bus; + slice_map_addr++; + + slice_number++; + + first_bu_in_slice += (unsigned int)size_in_bus; + } + + slice_height = picture_height_remaining; + if (index == halfway_slice) + halfway_bu = first_bu_in_slice; + + /* round up for case where the last BU is smaller */ + size_in_bus = ((slice_height / 16) * (video->width / 16) + video->rc_params.bu_size - 1) / + video->rc_params.bu_size; + + /* slice number */ + *slice_map_addr = slice_number; + slice_map_addr++; + + /* last BU */ + *slice_map_addr = size_in_bus; + slice_map_addr++; + + topaz_release_buffer(str_ctx, &video->slice_map[slot_number], TRUE); + +#ifdef DEBUG_ENCODER_DRIVER + pr_info("\n\nAPI - IMG_V_SendSourceFrame - Sending a source slot %i to FW\n\n", + slot_number); +#endif + + /* Send command */ + topaz_insert_mem_command(enc, MTX_CMDID_PROVIDE_SOURCE_BUFFER, 0, cmd_data_mem_info); + + video->encode_requested++; + + return IMG_SUCCESS; +} + +/* + * Supply a header buffer and an optional number of coded data buffers as part of a package + */ +int topaz_send_coded_package(void *topaz_str_ctx, struct img_coded_buffer *coded_buffer) +{ + struct img_enc_context *enc; + struct img_video_context *video; + struct topaz_stream_context *str_ctx; + unsigned char coded_buffer_idx; + unsigned int *address = NULL; + struct coded_package_dma_info *this_coded_header_node; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + if (!coded_buffer) + return IMG_ERROR_INVALID_PARAMETERS; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return IMG_ERROR_UNEXPECTED_STATE; + + video->coded_package[video->coded_package_slot_reserved]->coded_buffer[0] = coded_buffer; + +#ifdef DEBUG_ENCODER_DRIVER + pr_info("\n\nEncode Context [%i] sending coded package [%i]\n", enc->ctx_num, + video->coded_package_slot_reserved); +#endif + + /* Get the FW buffer */ + topaz_get_buffer + (str_ctx, + video->coded_package[video->coded_package_slot_reserved]->mtx_info.code_package_fw_buffer, + (void **)&address, FALSE); + + this_coded_header_node = + video->coded_package[video->coded_package_slot_reserved]->mtx_info.coded_package_fw = + (struct coded_package_dma_info *)address; + + this_coded_header_node->coded_buffer_info = + F_ENCODE + (video->coded_package[video->coded_package_slot_reserved]->num_coded_buffers, + MTX_MSG_NUM_CODED_BUFFERS_PER_HEADER); + + /* Inverted function: From host to MTX */ + populate_firmware_message(&(video->coded_package + [video->coded_package_slot_reserved]->mtx_info.code_package_fw_buffer->mem_info), + (unsigned char *)&this_coded_header_node->coded_header_addr - + (unsigned char *)this_coded_header_node, + (struct vidio_ddbufinfo *) + (&(video->coded_package[video->coded_package_slot_reserved]->header_buffer->mem_info + )), 0); + + /* Normal mode - An array of consecutive memory addresses */ + for (coded_buffer_idx = 0; coded_buffer_idx < + video->coded_package[video->coded_package_slot_reserved]->num_coded_buffers; + coded_buffer_idx++) { + if (video->coded_package[video->coded_package_slot_reserved]->coded_buffer + [coded_buffer_idx]) { + /* Write coded buffer memory address into the structure (host to MTX) */ + populate_firmware_message(&(video->coded_package + [video->coded_package_slot_reserved]->mtx_info.code_package_fw_buffer->mem_info), + (unsigned char *)&this_coded_header_node->coded_mem_addr[coded_buffer_idx] - + (unsigned char *)this_coded_header_node, (struct vidio_ddbufinfo *) + (&(video->coded_package + [video->coded_package_slot_reserved]->coded_buffer[coded_buffer_idx]->mem_info)), + 0); + } else { + this_coded_header_node->coded_mem_addr[coded_buffer_idx] = 0; + break; + } + } + + /* Release the FW buffer */ + topaz_release_buffer(str_ctx, video->coded_package + [video->coded_package_slot_reserved]->mtx_info.code_package_fw_buffer, TRUE); + + /* Send header buffers to the MTX */ + topaz_insert_mem_command(enc, (enum mtx_cmd_id)(MTX_CMDID_PROVIDE_CODEDPACKAGE_BUFFER | + MTX_CMDID_WB_INTERRUPT), + F_ENCODE(video->coded_package[video->coded_package_slot_reserved]->coded_buffer[0]->size >> + 10, MTX_MSG_PROVIDE_CODED_BUFFER_SIZE) | + F_ENCODE(video->coded_package_slot_reserved, MTX_MSG_PROVIDE_CODEDPACKAGE_BUFFER_SLOT), + &(video->coded_package + [video->coded_package_slot_reserved]->mtx_info.code_package_fw_buffer->mem_info)); + + return IMG_SUCCESS; +} + +unsigned int topaz_get_coded_buffer_max_size(void *topaz_str_ctx, enum img_standard standard, + unsigned short width, unsigned short height, + struct img_rc_params *rc_params) +{ + /* TODO: Determine if we want to make this api str_ctx dependent + * struct topaz_stream_context *str_ctx; + * if (!topaz_str_ctx) + * return IMG_ERROR_INVALID_CONTEXT; + */ + /* Worst-case coded buffer size: All MBs maximum size, + * and a coded buffer header for each row + */ + return topaz_get_max_coded_data_size(standard, width, height, rc_params->initial_qp_i) + + ((height >> 4) * CODED_BUFFER_INFO_SECTION_SIZE); +} + +unsigned int topaz_get_coded_package_max_num(void *topaz_str_ctx, enum img_standard standard, + unsigned short width, unsigned short height, + struct img_rc_params *rc_params) +{ + struct topaz_stream_context *str_ctx; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + return str_ctx->enc_ctx->video->coded_package_max_num; +} + +/* + * Get a source slot to fill + */ +int topaz_reserve_source_slot(void *topaz_str_ctx, unsigned char *src_slot_num) +{ + struct img_enc_context *enc; + struct img_video_context *video; + struct topaz_stream_context *str_ctx; + signed char index; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return IMG_ERROR_UNEXPECTED_STATE; + + for (index = 0; index < video->slots_in_use; index++) { + if (!video->source_slot_buff[index]) { + /* Found an empty slot, Mark the slot as reserved */ + video->source_slot_reserved = index; + *src_slot_num = index; + return IMG_SUCCESS; + } + } + + return IMG_ERROR_UNEXPECTED_STATE; +} + +/* + * Get a coded slot to fill + */ +int topaz_reserve_coded_package_slot(void *topaz_str_ctx) +{ + struct img_enc_context *enc; + struct img_video_context *video; + struct topaz_stream_context *str_ctx; + signed char index; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return IMG_ERROR_UNEXPECTED_STATE; + + for (index = 0; index < video->coded_package_max_num; index++) { + if (!video->coded_package[index]->busy) { + /* Found an empty slot, Mark the slot as reserved */ + video->coded_package_slot_reserved = index; + video->coded_package[index]->busy = TRUE; + return IMG_SUCCESS; + } + } + + return IMG_ERROR_UNEXPECTED_STATE; +} + +/* + * Returns number of empty source slots + */ +signed char topaz_query_empty_source_slots(void *topaz_str_ctx) +{ + struct topaz_stream_context *str_ctx; + struct img_enc_context *enc; + struct img_video_context *video; + + unsigned char slot_number; + unsigned char empty_source_slots = 0; + + if (!topaz_str_ctx) { + pr_err("ERROR: Invalid context handle provides to IMG_V_QueryEmptySourceSlots\n"); + return IMG_ERROR_INVALID_CONTEXT; + } + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return -2; + + for (slot_number = 0; slot_number < video->slots_in_use; slot_number++) { + if (!video->source_slot_buff[slot_number]) + empty_source_slots++; + } + + return empty_source_slots; +} + +/* + * Returns number of empty coded buffer slots + */ +signed char topaz_query_empty_coded_slots(void *topaz_str_ctx) +{ + struct topaz_stream_context *str_ctx; + struct img_enc_context *enc; + struct img_video_context *video; + + unsigned char slot_number; + unsigned char empty_coded_slots = 0; + + if (!topaz_str_ctx) { + pr_err("ERROR: Invalid context handle provides to IMG_V_QueryEmptyCodedSlots\n"); + return IMG_ERROR_INVALID_CONTEXT; + } + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + video = enc->video; + + if (video->aborted) + return -2; + + for (slot_number = 0; slot_number < video->coded_package_max_num; slot_number++) { + if (!video->coded_package[slot_number]->busy) + empty_coded_slots++; + } + + return empty_coded_slots; +} + +/* + * topaz_stream_map_buf_sg + */ +int topaz_stream_map_buf_sg(void *topaz_str_ctx, enum venc_buf_type buf_type, + struct vidio_ddbufinfo *buf_info, void *sgt) +{ + int ret; + struct topaz_stream_context *str_ctx; + + /* + * Resource stream ID cannot be zero. If zero just warning and + * proceeding further will break the code. Return IMG_ERROR_INVALID_ID. + */ + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + IMG_DBG_ASSERT(buf_type < VENC_BUFTYPE_MAX); + IMG_DBG_ASSERT(buf_info); + IMG_DBG_ASSERT(sgt); + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + /* Map heap from VENC to MMU. Currently only one heap is used for all buffer types */ + switch (buf_type) { + case VENC_BUFTYPE_BITSTREAM: + case VENC_BUFTYPE_PICTURE: + /* TODO: add logic to cache these buffers into str context list */ + break; + + default: + IMG_DBG_ASSERT(FALSE); + } + + /* Map this buffer into the MMU. */ + ret = topaz_mmu_stream_map_ext_sg(str_ctx->mmu_ctx, MMU_GENERAL_HEAP_ID, sgt, + buf_info->buf_size, 64, + (enum sys_emem_attrib)0, buf_info->cpu_virt, buf_info, + &buf_info->buff_id); + IMG_DBG_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return IMG_ERROR_OUT_OF_MEMORY; + + return IMG_SUCCESS; +} + +/* + * core_stream_unmap_buf_sg + */ +int topaz_stream_unmap_buf_sg(void *topaz_str_ctx, struct vidio_ddbufinfo *buf_info) +{ + int ret; + struct topaz_stream_context *str_ctx; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + + /* Unmap this buffer from the MMU. */ + ret = topaz_mmu_stream_free_sg(str_ctx->mmu_ctx, buf_info); + + IMG_DBG_ASSERT(ret == IMG_SUCCESS); + if (ret != IMG_SUCCESS) + return ret; + + return IMG_SUCCESS; +} + +/* + * End Of Video stream + */ +int topaz_end_of_stream(void *topaz_str_ctx, unsigned int frame_cnt) +{ + struct topaz_stream_context *str_ctx; + struct img_enc_context *enc; + + if (!topaz_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + str_ctx = (struct topaz_stream_context *)topaz_str_ctx; + enc = str_ctx->enc_ctx; + + if (enc->video->aborted) + return IMG_ERROR_UNDEFINED; + + enc->video->frame_count = frame_cnt; + + if (frame_cnt - enc->video->flushed_at_frame < enc->video->slots_in_use) + enc->video->slots_required = frame_cnt - enc->video->flushed_at_frame; + + /* Send PicMgmt Command */ + topaz_insert_command(enc, (enum mtx_cmd_id)(MTX_CMDID_PICMGMT | MTX_CMDID_PRIORITY), + F_ENCODE(IMG_PICMGMT_EOS, MTX_MSG_PICMGMT_SUBTYPE) | + F_ENCODE(frame_cnt, MTX_MSG_PICMGMT_DATA)); + + return IMG_SUCCESS; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topaz_api.h b/drivers/media/platform/vxe-vxd/encoder/topaz_api.h --- a/drivers/media/platform/vxe-vxd/encoder/topaz_api.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topaz_api.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1047 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Encoder core interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef __TOPAZ_API_H__ +#define __TOPAZ_API_H__ + +#include +#include +#include +#include +#include + +#include "fw_headers/topazscfwif.h" +#include "fw_headers/vxe_common.h" +#include "vid_buf.h" +#include "lst.h" + +#define MAX_MVC_VIEWS 2 +#define MVC_BASE_VIEW_IDX 0 +#define NON_MVC_VIEW (~0x0) + +#define MVC_SPS_ID 1 +#define MVC_PPS_ID 1 + +#define NUM_SLICE_TYPES 5 +#define MAX_PLANES 3 + +/* + * This type defines the buffer type categories. + * @brief Buffer Types + */ +enum venc_buf_type { + VENC_BUFTYPE_BITSTREAM = 0, + VENC_BUFTYPE_PICTURE, + VENC_BUFTYPE_ALL, + VENC_BUFTYPE_MAX, + VENC_BUFTYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * VXE callback type definitions + */ +enum vxe_cb_type { + VXE_CB_CODED_BUFF_READY, + VXE_CB_SRC_FRAME_RELEASE, + VXE_CB_STR_END, + VXE_CB_ERROR_FATAL, + VXE_CB_FORCE32BITS = 0x7FFFFFFFU +}; + +typedef void (*vxe_cb)(void *ctx, enum vxe_cb_type type, void *buf_ref, unsigned int size, + unsigned int coded_frm_cnt); + +/* + * Enum specifying video encode profile + */ +enum img_video_enc_profile { + ENC_PROFILE_DEFAULT = 0, + ENC_PROFILE_LOWCOMPLEXITY, + ENC_PROFILE_HIGHCOMPLEXITY, + ENC_PROFILE_REDUCEDMODE, + ENC_PROFILE_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Enum describing smallest blocksize used during motion search + */ +enum img_ipe_minblock_size { + BLK_SZ_16x16 = 0, + BLK_SZ_8x8 = 1, + BLK_SZ_4x4 = 2, + BLK_SZ_DEFAULT = 3, + BLK_SZ_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * Struct specifying flags to enable/disable encode features. + * All boolean flags are FALSE by default + */ +struct img_encode_features { + unsigned short disable_intra4x4; + unsigned short disable_intra8x8; + unsigned short disable_intra16x16; + unsigned short disable_inter8x8; + unsigned short restrict_inter4x4; + unsigned short disable_bpic_ref1; + unsigned short disable_bpic_ref0; + unsigned short enable_8x16_mv_detect; + unsigned short enable_16x8_mv_detect; + unsigned short disable_bframes; + enum img_ipe_minblock_size min_blk_sz; + unsigned short restricted_intra_pred; +}; + +/* + * + * Struct describing Macro-block params generated by first stage. + * Refer T.R.M. for details + */ +struct img_first_stage_mb_params { + unsigned short ipe0_sad; + unsigned short ipe1_sad; + unsigned char ipe0_blks; + unsigned char ipe1_blks; + unsigned char carc_cmplx_val; + unsigned char reserved; +}; + +/* + * Size of Inter/Intra & Coded/Skipped tables + */ +#define TOPAZHP_SCALE_TBL_SZ (8) +#define DEFAULT_CABAC_DB_MARGIN (0x190) + +/* + *Struct describing params for video encoding + *@enable_sel_stats_flags: Flags to enable selective first-pass statistics gathering by the + *hardware. Bit 1 - First Stage Motion Search Data, Bit 2 - Best + * Multipass MB Decision Data, Bit 3 - Best Multipass Motion Vectors. + * (First stage Table 2 motion vectors are always switched on) + *@enable_inp_ctrl: Enable Macro-block input control + *@enable_air: Enable Adaptive Intra Refresh + *@num_air_mbs: n = Max number of AIR MBs per frame, 0 = _ALL_ MBs over threshold will be marked + * as AIR Intras, -1 = Auto 10% + *@air_threshold: n = SAD Threshold above which a MB is a AIR MB candidate, -1 = Auto adjusting + * threshold + *@air_skip_cnt: n = Number of MBs to skip in AIR Table between frames, -1 = Random + * (0 - NumAIRMbs) skip between frames in AIR table + *@disable_bit_stuffing: Disabling bitstuffing to maintain bitrate + *@mpeg2_intra_dc_precision: Only used in MPEG2, 2 bit field (0 = 8 bit, 1 = 9 bit, 2 = 10 bit + * and 3=11 bit precision). Set to zero for other encode standards. + *@enable_mvc: True if MVC is enabled. False by default + *@mvc_view_idx: MVC view index + *@disable_bh_rounding: True if we wish to disable the buffer height rounding to 16 pixels + * (enables contiguous YU memory for non-aligned image heights) + *@auto_expand_pipes: Automatically expand a context pipe allocations when new pipes become + * available + *@line_counter_enabled: + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include + +#include "fw_headers/defs.h" +#include "img_errors.h" +#include "reg_headers/topazhp_core_regs.h" +#include "reg_headers/topaz_coreext_regs.h" +#include "reg_headers/topazhp_multicore_regs_old.h" +#include "topaz_api.h" + +#define MV_OFFSET_IN_TABLE(distance, \ + position) ((distance) * MV_ROW_STRIDE + (position) * sizeof(struct img_mv_settings)) +#define DEFAULT_MVCALC_CONFIG ((0x00040303) | (MASK_TOPAZHP_CR_MVCALC_JITTER_POINTER_RST)) + +/* + * Calculates the correct number of macroblocks per kick and kicks per BU + */ +void calculate_kick_and_bu_size(unsigned int width_in_mbs, + unsigned int height_in_mbs, + unsigned char is_interlaced, + unsigned int max_bu_per_frame, + unsigned int *kick_size, + unsigned int *kicks_per_bu, + unsigned int *min_slice_height) +{ + unsigned int kick_size_local, kicks_per_bu_local, bu_per_frame, min_slice_height_local; + + /* + * Basic unit is either an integer number of rows or an integer number of + * basic units fit in a row We calculate the ideal kick size first then decide + * how many kicks there will be for each basic unit + */ + + /* Default to 1 kick per row */ + kick_size_local = width_in_mbs; + kicks_per_bu_local = 1; + min_slice_height_local = 1; + + /* See if we can use a smaller kick size */ + if (!(kick_size_local % 3) && kick_size_local > 30) { + kick_size_local /= 3; + kicks_per_bu_local = 3; + } else if (!(kick_size_local % 2) && (kick_size_local > 20)) { + kick_size_local /= 2; + kicks_per_bu_local = 2; + } + + IMG_DBG_ASSERT((kick_size_local < 256) && ("Kick Size can't be bigger than 255" != NULL)); + + /* Now calculate how many kicks we do per BU */ + bu_per_frame = height_in_mbs * (is_interlaced ? 2 : 1); + + while (bu_per_frame > max_bu_per_frame) { + /* we have too many BUs so double up the number + * of rows per BU so we can half the number of BUs + */ + kicks_per_bu_local *= 2; + /* if we had an odd number of rows then the last BU will be half height */ + bu_per_frame = (bu_per_frame + 1) / 2; + min_slice_height_local *= 2; + } + + /* if we can afford to have 2 BUs per row then do it */ + if ((bu_per_frame < (max_bu_per_frame / 2)) && kicks_per_bu_local == 2) { + kicks_per_bu_local = 1; + bu_per_frame *= 2; + } + + /* if we can afford to have 3 BUs per row then do it */ + if ((bu_per_frame < (max_bu_per_frame / 3)) && kicks_per_bu_local == 3) { + kicks_per_bu_local = 1; + bu_per_frame += 2; + } + + *kick_size = kick_size_local; + *kicks_per_bu = kicks_per_bu_local; + *min_slice_height = min_slice_height_local; +} + +/* + * Calculates the stride based on the input format and width + */ +unsigned int calculate_stride(enum img_format format, ushort requested_stride_bytes, ushort width) +{ + ushort stride_bytes; + + if (requested_stride_bytes) { + stride_bytes = requested_stride_bytes; + } else { + switch (format) { + case IMG_CODEC_Y0UY1V_8888: + case IMG_CODEC_Y0VY1U_8888: + case IMG_CODEC_UY0VY1_8888: + case IMG_CODEC_VY0UY1_8888: + stride_bytes = width << 1; + break; + case IMG_CODEC_ABCX: + case IMG_CODEC_XBCA: + stride_bytes = width << 2; + break; + case IMG_CODEC_ABC565: + stride_bytes = width << 1; + break; + default: + stride_bytes = width; + break; + } + } + + switch (format) { + case IMG_CODEC_420_YUV: + case IMG_CODEC_420_YV12: + case IMG_CODEC_420_PL8: + case IMG_CODEC_422_YUV: + case IMG_CODEC_422_YV12: + case IMG_CODEC_422_PL8: + /* although luma stride is same as chroma stride, + * start address is half the stride. so we need 128-byte alignment + */ + case IMG_CODEC_420_IMC2: + /* although luma stride is same as chroma stride, + * start address is half the stride. so we need 128-byte alignment + */ + case IMG_CODEC_422_IMC2: + + /* + * All strides need to be 64-byte aligned + * Chroma stride is half luma stride, so (luma) stride needs + * to be 64-byte aligned when divided by 2 + */ + return ALIGN_128(stride_bytes); + default: + /* Stride needs to be 64-byte aligned */ + return ALIGN_64(stride_bytes); + } +} + +/* + * Patch HW profile based on the profile specified by the user + */ +void patch_hw_profile(struct img_video_params *video_params, struct img_video_context *video) +{ + unsigned int ipe_control = 0; + unsigned int pred_comb_control = 0; + struct img_encode_features *enc_features = &video_params->enc_features; + + /* disable_intra4x4 */ + if (enc_features->disable_intra4x4) + pred_comb_control |= F_ENCODE(1, TOPAZHP_CR_INTRA4X4_DISABLE); + + /* disable_intra8x8 */ + if (enc_features->disable_intra8x8) + pred_comb_control |= F_ENCODE(1, TOPAZHP_CR_INTRA8X8_DISABLE); + + /* disable_intra16x16, check if at least one of the other Intra mode is enabled */ + if (enc_features->disable_intra16x16 && + (!(enc_features->disable_intra8x8) || !(enc_features->disable_intra4x4))) + pred_comb_control |= F_ENCODE(1, TOPAZHP_CR_INTRA16X16_DISABLE); + + if (video_params->mbps) + video->mbps = video_params->mbps; + + if (enc_features->restrict_inter4x4) + ipe_control |= F_ENCODE(1, TOPAZHP_CR_IPE_MV_NUMBER_RESTRICTION); + + if (enc_features->disable_inter8x8) + pred_comb_control |= F_ENCODE(1, TOPAZHP_CR_INTER8X8_DISABLE); + + if (enc_features->disable_bpic_ref1) + pred_comb_control |= F_ENCODE(1, TOPAZHP_CR_B_PIC1_DISABLE); + else if (enc_features->disable_bpic_ref0) + pred_comb_control |= F_ENCODE(1, TOPAZHP_CR_B_PIC0_DISABLE); + + /* save predictor combiner control in video encode parameter set */ + video->pred_comb_control = pred_comb_control; + + /* set blocksize */ + ipe_control |= F_ENCODE(enc_features->min_blk_sz, TOPAZHP_CR_IPE_BLOCKSIZE); + + if (enc_features->enable_8x16_mv_detect) + ipe_control |= F_ENCODE(1, TOPAZHP_CR_IPE_8X16_ENABLE); + + if (enc_features->enable_16x8_mv_detect) + ipe_control |= F_ENCODE(1, TOPAZHP_CR_IPE_16X8_ENABLE); + + if (enc_features->disable_bframes) + video->rc_params.bframes = 0; + + if (enc_features->restricted_intra_pred) + video->intra_pred_modes = 0xff0f; + + /* save IPE-control register */ + video->ipe_control = ipe_control; +} + +/* + * Set offsets and strides for YUV components of source picture + */ +int topaz_set_component_offsets(void *enc_ctx_handle, struct img_frame *frame) +{ + struct img_enc_context *enc; + struct img_video_context *video; + enum img_format format; + ushort stride_bytes; + ushort picture_height; + + if (!enc_ctx_handle) + return IMG_ERROR_INVALID_CONTEXT; + + /* if source slot is NULL then it's just a next portion of slices */ + if (!frame) + return IMG_ERROR_UNDEFINED; + + enc = (struct img_enc_context *)enc_ctx_handle; + video = enc->video; + + format = video->format; + picture_height = video->buffer_height >> (video->is_interlaced ? 1 : 0); + stride_bytes = video->buffer_stride_bytes; + + /* + * 3 Components: Y, U, V + * Y component is always at the beginning + */ + frame->y_component_offset = 0; + frame->src_y_stride_bytes = stride_bytes; + + /* Assume for now that field 0 comes first */ + frame->field0_y_offset = 0; + frame->field0_u_offset = 0; + frame->field0_v_offset = 0; + + switch (format) { + case IMG_CODEC_420_YUV: + frame->src_uv_stride_bytes = stride_bytes / 2; + + frame->u_component_offset = stride_bytes * picture_height; + frame->v_component_offset = stride_bytes * picture_height + (stride_bytes / 2) * + (picture_height / 2); + break; + + case IMG_CODEC_420_PL8: + frame->src_uv_stride_bytes = stride_bytes / 2; + + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + case IMG_CODEC_420_PL12: + case IMG_CODEC_420_PL21: + frame->src_uv_stride_bytes = stride_bytes; + + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + case IMG_CODEC_420_YV12: + frame->src_uv_stride_bytes = stride_bytes / 2; + frame->u_component_offset = stride_bytes * picture_height + (stride_bytes / 2) * + (picture_height / 2); + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_420_PL12_PACKED: + case IMG_CODEC_420_PL21_PACKED: + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = stride_bytes * picture_height; + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_420_IMC2: /* IMC2 */ + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = stride_bytes * picture_height + (stride_bytes / 2); + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_422_YUV: + frame->src_uv_stride_bytes = stride_bytes / 2; + frame->u_component_offset = stride_bytes * picture_height; + frame->v_component_offset = stride_bytes * picture_height + (stride_bytes / 2) * + picture_height; + break; + + case IMG_CODEC_422_YV12: /* YV16 */ + frame->src_uv_stride_bytes = stride_bytes / 2; + frame->u_component_offset = stride_bytes * picture_height + (stride_bytes / 2) * + picture_height; + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_422_PL8: + frame->src_uv_stride_bytes = stride_bytes / 2; + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + case IMG_CODEC_422_IMC2: /* IMC2 */ + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = stride_bytes * picture_height + (stride_bytes / 2); + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_422_PL12: + case IMG_CODEC_422_PL21: + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + case IMG_CODEC_444_YUV: + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = stride_bytes * picture_height; + frame->v_component_offset = stride_bytes * picture_height + stride_bytes * + picture_height; + break; + + case IMG_CODEC_444_YV12: /* YV16 */ + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = stride_bytes * picture_height + stride_bytes * + picture_height; + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_444_PL8: + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + case IMG_CODEC_444_IMC2: /* IMC2 */ + frame->src_uv_stride_bytes = stride_bytes * 2; + frame->u_component_offset = stride_bytes * picture_height + stride_bytes; + frame->v_component_offset = stride_bytes * picture_height; + break; + + case IMG_CODEC_444_PL12: + case IMG_CODEC_444_PL21: + frame->src_uv_stride_bytes = stride_bytes * 2; + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + case IMG_CODEC_Y0UY1V_8888: + case IMG_CODEC_Y0VY1U_8888: + case IMG_CODEC_UY0VY1_8888: + case IMG_CODEC_VY0UY1_8888: + case IMG_CODEC_ABCX: + case IMG_CODEC_XBCA: + case IMG_CODEC_ABC565: + frame->src_uv_stride_bytes = stride_bytes; + frame->u_component_offset = 0; + frame->v_component_offset = 0; + break; + + default: + break; + } + + if (video->is_interlaced) { + if (video->is_interleaved) { + switch (format) { + case IMG_CODEC_420_IMC2: + case IMG_CODEC_422_IMC2: + frame->v_component_offset *= 2; + frame->u_component_offset = frame->v_component_offset + + (stride_bytes / 2); + break; + case IMG_CODEC_444_IMC2: + frame->v_component_offset *= 2; + frame->u_component_offset = frame->v_component_offset + + stride_bytes; + break; + + default: + frame->u_component_offset *= 2; + frame->v_component_offset *= 2; + break; + } + + frame->field1_y_offset = frame->field0_y_offset + frame->src_y_stride_bytes; + frame->field1_u_offset = frame->field0_u_offset + + frame->src_uv_stride_bytes; + frame->field1_v_offset = frame->field0_v_offset + + frame->src_uv_stride_bytes; + + frame->src_y_stride_bytes *= 2; + frame->src_uv_stride_bytes *= 2; + } else { + unsigned int y_field_size, c_field_size; + + switch (format) { + case IMG_CODEC_420_YUV: + case IMG_CODEC_420_YV12: + case IMG_CODEC_420_IMC2: + case IMG_CODEC_420_PL12_PACKED: + case IMG_CODEC_420_PL21_PACKED: + /* In Packed formats including PL12 packed the field offsets + * should be calculated in the following manner + */ + y_field_size = picture_height * stride_bytes * 3 / 2; + c_field_size = y_field_size; + break; + case IMG_CODEC_420_PL8: + y_field_size = picture_height * stride_bytes; + c_field_size = picture_height * stride_bytes / 4; + break; + case IMG_CODEC_420_PL12: + case IMG_CODEC_420_PL21: + y_field_size = picture_height * stride_bytes; + c_field_size = picture_height * stride_bytes / 2; + break; + case IMG_CODEC_422_YUV: + case IMG_CODEC_422_YV12: + case IMG_CODEC_422_IMC2: + y_field_size = picture_height * stride_bytes * 2; + c_field_size = y_field_size; + break; + case IMG_CODEC_422_PL8: + y_field_size = picture_height * stride_bytes; + c_field_size = picture_height * stride_bytes / 2; + break; + case IMG_CODEC_422_PL12: + case IMG_CODEC_422_PL21: + y_field_size = picture_height * stride_bytes; + c_field_size = picture_height * stride_bytes; + break; + case IMG_CODEC_Y0UY1V_8888: + case IMG_CODEC_UY0VY1_8888: + case IMG_CODEC_Y0VY1U_8888: + case IMG_CODEC_VY0UY1_8888: + y_field_size = picture_height * stride_bytes; + c_field_size = y_field_size; + break; + case IMG_CODEC_444_YUV: + case IMG_CODEC_444_YV12: + case IMG_CODEC_444_IMC2: + y_field_size = picture_height * stride_bytes * 3; + c_field_size = y_field_size; + break; + case IMG_CODEC_444_PL8: + y_field_size = picture_height * stride_bytes; + c_field_size = picture_height * stride_bytes; + break; + case IMG_CODEC_444_PL12: + case IMG_CODEC_444_PL21: + y_field_size = picture_height * stride_bytes; + c_field_size = picture_height * stride_bytes * 2; + break; + case IMG_CODEC_ABCX: + case IMG_CODEC_XBCA: + case IMG_CODEC_ABC565: + y_field_size = picture_height * stride_bytes; + c_field_size = y_field_size; + break; + default: + y_field_size = picture_height * stride_bytes * 3 / 2; + c_field_size = y_field_size; + break; + } + + frame->field1_y_offset = y_field_size; + frame->field1_u_offset = c_field_size; + frame->field1_v_offset = c_field_size; + } + } else { + frame->field1_y_offset = frame->field0_y_offset; + frame->field1_u_offset = frame->field0_u_offset; + frame->field1_v_offset = frame->field0_v_offset; + } + return IMG_SUCCESS; +} + +void topaz_setup_input_csc(struct img_video_context *video, + struct img_vxe_scaler_setup *scaler_setup, + struct img_vxe_csc_setup *csc_setup, + enum img_csc_preset csc_preset) +{ +#define CSC_MINUS_1_16(X) TOPAZHP_EXT_CR_CSC_SOURCE_MOD_0 ## X ## _MINUS_1_16 +#define CSC_MINUS_1_2(X) TOPAZHP_EXT_CR_CSC_SOURCE_MOD_0 ## X ## _MINUS_1_2 +#define CSC_UNSIGNED(X) TOPAZHP_EXT_CR_CSC_SOURCE_MOD_0 ## X ## _UNSIGNED + + if (csc_preset != IMG_CSC_NONE && + (video->format == IMG_CODEC_ABCX || + video->format == IMG_CODEC_XBCA || video->format == IMG_CODEC_ABC565)) { + unsigned char source_mode[IMG_CSC_PRESETS][3] = { + /* IMG_CSC_NONE - No colour-space conversion */ + {CSC_MINUS_1_16(0), CSC_MINUS_1_2(1), CSC_MINUS_1_16(2)}, + + /* IMG_CSC_709_TO_601 - ITU BT.709 YUV to be converted to ITU BT.601 YUV */ + {CSC_MINUS_1_16(0), CSC_MINUS_1_2(1), CSC_MINUS_1_16(2)}, + + /* IMG_CSC_601_TO_709 - ITU BT.601 YUV to be + * converted to ITU BT.709 YUV + */ + {CSC_MINUS_1_16(0), CSC_MINUS_1_2(1), CSC_MINUS_1_16(2)}, + + /* IMG_CSC_RGB_TO_601_ANALOG - RGB to be + * converted to ITU BT.601 YUV + */ + { CSC_UNSIGNED(0), CSC_UNSIGNED(1), CSC_UNSIGNED(2)}, + + /* IMG_CSC_RGB_TO_601_DIGITAL - RGB to be + * converted to ITU BT.601 YCbCr RS + */ + { CSC_UNSIGNED(0), CSC_UNSIGNED(1), CSC_UNSIGNED(2)}, + + /* IMG_CSC_RGB_TO_601_DIGITAL_FS - RGB to be + * converted to ITU BT.601 YCbCr FS + */ + { CSC_UNSIGNED(0), CSC_UNSIGNED(1), CSC_UNSIGNED(2)}, + + /* IMG_CSC_RGB_TO_709 - RGB to be converted to ITU BT.709 YUV */ + { CSC_UNSIGNED(0), CSC_UNSIGNED(1), CSC_UNSIGNED(2)}, + + /* IMG_CSC_YIQ_TO_601 - YIQ to be converted to ITU BT.601 YUV */ + {CSC_MINUS_1_16(0), CSC_MINUS_1_2(1), CSC_MINUS_1_16(2)}, + + /* IMG_CSC_YIQ_TO_709 - YIQ to be converted to ITU BT.709 YUV */ + {CSC_MINUS_1_16(0), CSC_MINUS_1_2(1), CSC_MINUS_1_16(2)}, + + /* IMG_CSC_BRG_TO_601 - RGB to be converted to ITU BT.601 YUV */ + {0, 0, 0}, + + /* IMG_CSC_RBG_TO_601 - RGB to be converted to ITU BT.709 YUV */ + {0, 0, 0}, + + /* IMG_CSC_BGR_TO_601 - RGB to be converted to ITU BT.601 YUV */ + {0, 0, 0}, + + /* IMG_CSC_UYV_TO_YUV - UYV to be converted to YUV */ + {CSC_MINUS_1_2(0), CSC_MINUS_1_16(1), CSC_MINUS_1_2(2)}, + /*{ CSC_UNSIGNED(0), CSC_UNSIGNED(1), CSC_UNSIGNED(2)}, */ + }; + + int coeffs[IMG_CSC_PRESETS][3][3] = { + /* IMG_CSC_NONE - No colour-space conversion */ + { + { 1024, 0, 0 }, + { 0, 1024, 0 }, + { 0, 0, 1024 } + }, + + /* IMG_CSC_709_TO_601 - ITU BT.709 YUV to be converted to ITU BT.601 YUV */ + { + { 1024, (int)(0.15941 * 1024), (int)(0.11649 * 1024) }, + { 0, (int)(-0.07844 * 1024), (int)(0.98985 * 1024) }, + { 0, (int)(0.9834 * 1024), (int)(-0.10219 * 1024) } + }, + + /* IMG_CSC_601_TO_709 - ITU BT.601 YUV to be converted to ITU BT.709 YUV */ + { + { 1024, (int)(-0.17292 * 1024), (int)(-0.13554 * 1024) }, + { 0, (int)(0.08125 * 1024), (int)(1.01864 * 1024) }, + { 0, (int)(1.02532 * 1024), (int)(0.10586 * 1024) } + }, + + /* IMG_CSC_RGB_TO_601_ANALOG - RGB to be converted to ITU BT.601 YUV */ + { /* R G B */ + { (int)(219 * 0.299 * 4.0157), + (int)(219 * 0.587 * 4.0157), + (int)(219 * 0.114 * 4.0157) }, + { (int)(224 * -0.14713 * 4.0157), + (int)(224 * -0.28886 * 4.0157), + (int)(224 * 0.446 * 4.0157) }, + { (int)(224 * 0.615 * 4.0157), + (int)(224 * -0.51499 * 4.0157), + (int)(224 * -0.10001 * 4.0157) } + }, /* A B C */ + + /* IMG_CSC_RGB_TO_601_DIGITAL - RGB to be + * converted to ITU BT.601 YCbCr reduced scale + */ + { /* R G B */ + { (int)(219 * 0.299 * 4.0157), + (int)(219 * 0.587 * 4.0157), + (int)(219 * 0.114 * 4.0157) }, + { (int)(224 * -0.172 * 4.0157), + (int)(224 * -0.339 * 4.0157), + (int)(224 * 0.511 * 4.0157) }, + { (int)(224 * 0.511 * 4.0157), + (int)(224 * -0.428 * 4.0157), + (int)(224 * -0.083 * 4.0157) } + }, /* A B C */ + + /* IMG_CSC_RGB_TO_601_DIGITAL_FS - RGB to be + * converted to ITU BT.601 YCbCr full scale + */ + { /* R G B */ + { (int)(219 * 0.257 * 4.0157), + (int)(219 * 0.504 * 4.0157), + (int)(219 * 0.098 * 4.0157) }, + { (int)(224 * -0.148 * 4.0157), + (int)(224 * -0.291 * 4.0157), + (int)(224 * 0.439 * 4.0157) }, + { (int)(224 * 0.439 * 4.0157), + (int)(224 * -0.368 * 4.0157), + (int)(224 * -0.071 * 4.0157) } + }, /* A B C */ + + /* IMG_CSC_RGB_TO_709 - RGB to be converted to ITU BT.709 YUV */ + { + { (int)(219 * 0.2215 * 4.0157), (int)(219 * 0.7154 * 4.0157), + (int)(219 * 0.0721 * 4.0157) }, + { (int)(224 * -0.1145 * 4.0157), (int)(224 * -0.3855 * 4.0157), + (int)(224 * 0.5 * 4.0157) }, + { (int)(224 * 0.5016 * 4.0157), (int)(224 * -0.4556 * 4.0157), + (int)(224 * -0.0459 * 4.0157) } + }, + + /* IMG_CSC_YIQ_TO_601 - YIQ to be converted to ITU BT.601 YUV */ + { + { 1024, 0, 0 }, + { 0, (int)(0.83885 * 1024), (int)(-0.54475 * 1024) }, + { 0, (int)(0.54484 * 1024), (int)(0.83896 * 1024) } + }, + + /* IMG_CSC_YIQ_TO_709 - YIQ to be converted to ITU BT.709 YUV */ + { + { 1024, (int)(-0.20792 * 1024), (int)(0.07122 * 1024) }, + { 0, (int)(0.89875 * 1024), (int)(-0.48675 * 1024) }, + { 0, (int)(0.64744 * 1024), (int)(0.80255 * 1024) } + }, + + /* + * IMG_CSC_BRG_TO_601 - RGB to be converted to ITU BT.601 YUV + * Entries have been reordered to provide support for xRGB format + */ + { /* B R G */ + { (int)(219 * 0.114 * 4.0157), + (int)(219 * 0.299 * 4.0157), + (int)(219 * 0.587 * 4.0157)}, + { (int)(224 * 0.446 * 4.0157), + (int)(224 * -0.14713 * 4.0157), + (int)(224 * -0.28886 * 4.0157)}, + { (int)(224 * -0.10001 * 4.0157), + (int)(224 * 0.615 * 4.0157), + (int)(224 * -0.51499 * 4.0157)} + }, /* A B C */ + + /* + * IMG_CSC_RBG_TO_601 - RGB to be converted to ITU BT.601 YUV + * Entries have been reordered to provide support for xBGR format + */ + { /* R B G */ + { (int)(219 * 0.299 * 4.0157), + (int)(219 * 0.114 * 4.0157), + (int)(219 * 0.587 * 4.0157)}, + { (int)(224 * -0.14713 * 4.0157), + (int)(224 * 0.446 * 4.0157), + (int)(224 * -0.28886 * 4.0157)}, + { (int)(224 * 0.615 * 4.0157), + (int)(224 * -0.10001 * 4.0157), + (int)(224 * -0.51499 * 4.0157)} + }, /* A B C */ + + /* + * IMG_CSC_BGR_TO_601 - RGB to be converted to ITU BT.601 YUV + * Entries have been reordered to provide support for BGRx format + */ + { /* B G R */ + { (int)(219 * 0.114 * 4.0157), + (int)(219 * 0.587 * 4.0157), + (int)(219 * 0.299 * 4.0157)}, + { (int)(224 * 0.446 * 4.0157), + (int)(224 * -0.28886 * 4.0157), + (int)(224 * -0.14713 * 4.0157)}, + { (int)(224 * -0.10001 * 4.0157), + (int)(224 * -0.51499 * 4.0157), + (int)(224 * 0.615 * 4.0157)}, + }, /* A B C */ + + /* IMG_CSC_UYV_TO_YUV - UYV to YUV */ + { + { 0, 1024, 0 }, + { 1024, 0, 0 }, + { 0, 0, 1024 } + }, + }; + + unsigned int index = csc_preset; + + IMG_DBG_ASSERT(index < IMG_CSC_PRESETS); + + if (index >= IMG_CSC_PRESETS) + return; + +#define SRC_MOD(X) TOPAZHP_EXT_CR_CSC_SOURCE_MOD_0 ## X +#define OUT_MOD(X) TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_0 ## X + +#define SOURCE_Y_ARRAY csc_setup->csc_source_y +#define SRC_Y_PARAM(X) TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_Y + +#define SOURCE_CBCR_ARRAY csc_setup->csc_source_cbcr +#define SRC_CB_PARAM(X) TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CB +#define SRC_CR_PARAM(X) TOPAZHP_EXT_CR_CSC_SOURCE_SRC_TO_CR + +#define CLIP_VALUE 255 + + scaler_setup->scaler_control |= F_ENCODE(1, + TOPAZHP_EXT_CR_ENABLE_COLOUR_SPACE_CONVERSION); + + csc_setup->csc_output_clip[0] = + F_ENCODE(TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_00_ADD_1_16, OUT_MOD(0)) | + F_ENCODE(CLIP_VALUE, TOPAZHP_EXT_CR_CSC_OUTPUT_MAX_CLIP) | + F_ENCODE(0, TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP); + + csc_setup->csc_output_clip[1] = + F_ENCODE(TOPAZHP_EXT_CR_CSC_OUTPUT_MOD_01_ADD_1_2, OUT_MOD(1)) | + F_ENCODE(CLIP_VALUE, TOPAZHP_EXT_CR_CSC_OUTPUT_MAX_CLIP) | + F_ENCODE(0, TOPAZHP_EXT_CR_CSC_OUTPUT_MIN_CLIP); + + SOURCE_Y_ARRAY[0] = F_ENCODE(source_mode[index][0], SRC_MOD(0)) | + F_ENCODE(coeffs[index][0][0], SRC_Y_PARAM(0)); + SOURCE_CBCR_ARRAY[0] = F_ENCODE(coeffs[index][1][0], SRC_CB_PARAM(0)) | + F_ENCODE(coeffs[index][2][0], SRC_CR_PARAM(0)); + + SOURCE_Y_ARRAY[1] = F_ENCODE(source_mode[index][1], SRC_MOD(1)) | + F_ENCODE(coeffs[index][0][1], SRC_Y_PARAM(1)); + SOURCE_CBCR_ARRAY[1] = F_ENCODE(coeffs[index][1][1], SRC_CB_PARAM(1)) | + F_ENCODE(coeffs[index][2][1], SRC_CR_PARAM(1)); + + SOURCE_Y_ARRAY[2] = F_ENCODE(source_mode[index][2], SRC_MOD(2)) | + F_ENCODE(coeffs[index][0][2], SRC_Y_PARAM(2)); + SOURCE_CBCR_ARRAY[2] = F_ENCODE(coeffs[index][1][2], SRC_CB_PARAM(2)) | + F_ENCODE(coeffs[index][2][2], SRC_CR_PARAM(2)); + } +} + +/* + * Calculate buffer strides + */ +unsigned int topaz_get_packed_buffer_strides(ushort buffer_stride_bytes, + enum img_format format, + unsigned char enable_scaler, + unsigned char is_interlaced, + unsigned char is_interleaved) +{ + ushort src_y_stride_bytes; + ushort src_uv_stride_bytes = 0; + + /* 3 Components: Y, U, V */ + src_y_stride_bytes = buffer_stride_bytes; + + switch (format) { + case IMG_CODEC_420_YUV: + case IMG_CODEC_420_PL8: + case IMG_CODEC_420_YV12: + src_uv_stride_bytes = src_y_stride_bytes / 2; + break; + + case IMG_CODEC_422_YUV: /* Odd-numbered chroma rows unused if scaler not present */ + case IMG_CODEC_422_YV12: /* Odd-numbered chroma rows unused if scaler not present */ + case IMG_CODEC_422_PL8: /* Odd-numbered chroma rows unused if scaler not present */ + if (!enable_scaler) + /* Skip alternate lines of chroma for 4:2:2 if scaler disabled/not present */ + src_uv_stride_bytes = src_y_stride_bytes; + else + src_uv_stride_bytes = src_y_stride_bytes / 2; + break; + /* Interleaved chroma pixels (and unused odd-numbered chroma rows if scaler not present) */ + case IMG_CODEC_422_IMC2: + /* Interleaved chroma rows (and unused odd-numbered chroma rows if scaler not present) */ + case IMG_CODEC_422_PL12: + /* Interleaved chroma rows (and unused odd-numbered chroma rows if scaler not present) */ + case IMG_CODEC_422_PL21: + if (!enable_scaler) + /* Skip alternate lines of chroma for 4:2:2 if scaler disabled/not present */ + src_uv_stride_bytes = src_y_stride_bytes * 2; + else + src_uv_stride_bytes = src_y_stride_bytes; + break; + + case IMG_CODEC_420_PL12: /* Interleaved chroma pixels */ + case IMG_CODEC_420_PL21: + + case IMG_CODEC_420_PL12_PACKED: /* Interleaved chroma pixels */ + case IMG_CODEC_420_PL21_PACKED: /* Interleaved chroma pixels */ + case IMG_CODEC_420_IMC2: /* Interleaved chroma rows */ + case IMG_CODEC_Y0UY1V_8888: /* Interleaved luma and chroma pixels */ + case IMG_CODEC_Y0VY1U_8888: /* Interleaved luma and chroma pixels */ + case IMG_CODEC_UY0VY1_8888: /* Interleaved luma and chroma pixels */ + case IMG_CODEC_VY0UY1_8888: /* Interleaved luma and chroma pixels */ + case IMG_CODEC_ABCX: /* Interleaved pixels of unknown colour space */ + case IMG_CODEC_XBCA: /* Interleaved pixels of unknown colour space */ + case IMG_CODEC_ABC565: /* Packed pixels of unknown coloour space */ + src_uv_stride_bytes = src_y_stride_bytes; + break; + + case IMG_CODEC_444_YUV: /* Unusable if scaler not present */ + case IMG_CODEC_444_YV12: /* Unusable if scaler not present */ + case IMG_CODEC_444_PL8: /* Unusable if scaler not present */ + src_uv_stride_bytes = src_y_stride_bytes; + break; + + /* Interleaved chroma pixels (unusable if scaler not present) */ + case IMG_CODEC_444_IMC2: + /* Interleaved chroma rows (unusable if scaler not present) */ + case IMG_CODEC_444_PL12: + /* Interleaved chroma rows (unusable if scaler not present) */ + case IMG_CODEC_444_PL21: + src_uv_stride_bytes = src_y_stride_bytes * 2; + break; + + default: + break; + } + + if (is_interlaced && is_interleaved) { + src_y_stride_bytes *= 2; + src_uv_stride_bytes *= 2; + } + return F_ENCODE(src_y_stride_bytes >> 6, MTX_MSG_PICMGMT_STRIDE_Y) | + F_ENCODE(src_uv_stride_bytes >> 6, MTX_MSG_PICMGMT_STRIDE_UV); +} + +/* + * Setup the registers for scaling candidate motion vectors to take into account + * how far away (temporally) the reference pictures are + */ +#define RESTRICT16x16_FLAGS (0x1) +#define RESTRICT8x8_FLAGS (0x2) + +void update_driver_mv_scaling(unsigned int frame_num, unsigned int ref0_num, unsigned int ref1_num, + unsigned int pic_flags, unsigned int *mv_calc_below_handle, + unsigned int *mv_calc_colocated_handle, + unsigned int *mv_calc_config_handle) +{ + unsigned int mv_calc_config = 0; + unsigned int mv_calc_colocated = F_ENCODE(0x10, TOPAZHP_CR_TEMPORAL_BLEND); + unsigned int mv_calc_below = 0; + + /* If b picture calculate scaling factor for colocated motion vectors */ + if (pic_flags & ISINTERB_FLAGS) { + int tb, td, tx; + int dist_scale; + + /* calculation taken from H264 spec */ + tb = (frame_num * 2) - (ref1_num * 2); + td = (ref0_num * 2) - (ref1_num * 2); + tx = (16384 + abs(td / 2)) / td; + dist_scale = (tb * tx + 32) >> 6; + if (dist_scale > 1023) + dist_scale = 1023; + + if (dist_scale < -1024) + dist_scale = -1024; + + mv_calc_colocated |= F_ENCODE(dist_scale, TOPAZHP_CR_COL_DIST_SCALE_FACT); + + /* + * We assume the below temporal mvs are from the latest reference frame + * rather then the most recently encoded B frame (as Bs aren't reference) + * Fwd temporal is same as colocated mv scale + */ + mv_calc_below |= F_ENCODE(dist_scale, TOPAZHP_CR_PIC0_DIST_SCALE_FACTOR); + + /* Bkwd temporal needs to be scaled by the recipricol + * amount in the other direction + */ + tb = (frame_num * 2) - (ref0_num * 2); + td = (ref0_num * 2) - (ref1_num * 2); + tx = (16384 + abs(td / 2)) / td; + dist_scale = (tb * tx + 32) >> 6; + if (dist_scale > 1023) + dist_scale = 1023; + + if (dist_scale < -1024) + dist_scale = -1024; + + mv_calc_below |= F_ENCODE(dist_scale, TOPAZHP_CR_PIC1_DIST_SCALE_FACTOR); + } else { + /* Don't scale the temporal below mvs */ + mv_calc_below |= F_ENCODE(1 << 8, TOPAZHP_CR_PIC0_DIST_SCALE_FACTOR); + + if (ref0_num != ref1_num) { + int ref0_dist, ref1_dist; + int scale; + + /* + * Distance to second reference picture may be different when + * using multiple reference frames on P. Scale based on difference + * in temporal distance to ref pic 1 compared to distance to ref pic 0 + */ + ref0_dist = (frame_num - ref0_num); + ref1_dist = (frame_num - ref1_num); + scale = (ref1_dist << 8) / ref0_dist; + + if (scale > 1023) + scale = 1023; + if (scale < -1024) + scale = -1024; + + mv_calc_below |= F_ENCODE(scale, TOPAZHP_CR_PIC1_DIST_SCALE_FACTOR); + } else { + mv_calc_below |= F_ENCODE(1 << 8, TOPAZHP_CR_PIC1_DIST_SCALE_FACTOR); + } + } + + if (frame_num > 0) { + int ref0_distance, ref1_distance; + int jitter0, jitter1; + + ref0_distance = abs((int)frame_num - (int)ref0_num); + ref1_distance = abs((int)frame_num - (int)ref1_num); + + if (!(pic_flags & ISINTERB_FLAGS)) { + jitter0 = ref0_distance * 1; + jitter1 = jitter0 > 1 ? 1 : 2; + } else { + jitter0 = ref1_distance * 1; + jitter1 = ref0_distance * 1; + } + + /* Hardware can only cope with 1 - 4 jitter factors */ + jitter0 = (jitter0 > 4) ? 4 : (jitter0 < 1) ? 1 : jitter0; + jitter1 = (jitter1 > 4) ? 4 : (jitter1 < 1) ? 1 : jitter1; + + /* Hardware can only cope with 1 - 4 jitter factors */ + IMG_DBG_ASSERT(jitter0 > 0 && jitter0 <= 4 && jitter1 > 0 && jitter1 <= 4); + + mv_calc_config |= F_ENCODE(jitter0 - 1, TOPAZHP_CR_MVCALC_IPE0_JITTER_FACTOR) | + F_ENCODE(jitter1 - 1, TOPAZHP_CR_MVCALC_IPE1_JITTER_FACTOR); + } + + mv_calc_config |= F_ENCODE(1, TOPAZHP_CR_MVCALC_DUP_VEC_MARGIN); + mv_calc_config |= F_ENCODE(7, TOPAZHP_CR_MVCALC_GRID_MB_X_STEP); + mv_calc_config |= F_ENCODE(13, TOPAZHP_CR_MVCALC_GRID_MB_Y_STEP); + mv_calc_config |= F_ENCODE(3, TOPAZHP_CR_MVCALC_GRID_SUB_STEP); + mv_calc_config |= F_ENCODE(1, TOPAZHP_CR_MVCALC_GRID_DISABLE); + + mv_calc_config |= F_ENCODE(1, TOPAZHP_CR_MVCALC_NO_PSEUDO_DUPLICATES); + + *mv_calc_below_handle = mv_calc_below; + *mv_calc_colocated_handle = mv_calc_colocated; + *mv_calc_config_handle = mv_calc_config; +} + +void prepare_mv_estimates(struct img_enc_context *enc) +{ + struct img_video_context *vid_ctx = enc->video; + unsigned int distance; + unsigned int distance_b; + unsigned int position; + struct img_mv_settings *host_mv_settings_b_table; + struct img_mv_settings *host_mv_settings_hierarchical; + unsigned char hierarchical; + + /* IDR */ + vid_ctx->mv_settings_idr.mv_calc_config = DEFAULT_MVCALC_CONFIG; /* default based on TRM */ + vid_ctx->mv_settings_idr.mv_calc_colocated = 0x00100100; /* default based on TRM */ + vid_ctx->mv_settings_idr.mv_calc_below = 0x01000100; /* default based on TRM */ + + update_driver_mv_scaling(0, 0, 0, 0, &vid_ctx->mv_settings_idr.mv_calc_below, + &vid_ctx->mv_settings_idr.mv_calc_colocated, + &vid_ctx->mv_settings_idr.mv_calc_config); + + /* NonB (I or P) */ + for (distance = 1; distance <= MAX_BFRAMES + 1; distance++) { + /* default based on TRM */ + vid_ctx->mv_settings_non_b[distance - 1].mv_calc_config = DEFAULT_MVCALC_CONFIG; + /* default based on TRM */ + vid_ctx->mv_settings_non_b[distance - 1].mv_calc_colocated = 0x00100100; + /* default based on TRM */ + vid_ctx->mv_settings_non_b[distance - 1].mv_calc_below = 0x01000100; + + update_driver_mv_scaling + (distance, 0, 0, 0, + &vid_ctx->mv_settings_non_b[distance - 1].mv_calc_below, + &vid_ctx->mv_settings_non_b[distance - 1].mv_calc_colocated, + &vid_ctx->mv_settings_non_b[distance - 1].mv_calc_config); + } + + hierarchical = (bool)(vid_ctx->mv_settings_hierarchical.cpu_virt); + + host_mv_settings_b_table = (struct img_mv_settings *)(vid_ctx->mv_settings_btable.cpu_virt); + + if (hierarchical) + host_mv_settings_hierarchical = + (struct img_mv_settings *)(vid_ctx->mv_settings_hierarchical.cpu_virt); + + for (distance_b = 0; distance_b < MAX_BFRAMES; distance_b++) { + for (position = 1; position <= distance_b + 1; position++) { + struct img_mv_settings *mv_element = + (struct img_mv_settings *)((unsigned char *)host_mv_settings_b_table + + MV_OFFSET_IN_TABLE(distance_b, position - 1)); + + mv_element->mv_calc_config = + /* default based on TRM */ + (DEFAULT_MVCALC_CONFIG | MASK_TOPAZHP_CR_MVCALC_GRID_DISABLE); + + mv_element->mv_calc_colocated = 0x00100100;/* default based on TRM */ + mv_element->mv_calc_below = 0x01000100; /* default based on TRM */ + + update_driver_mv_scaling(position, distance_b + 2, 0, ISINTERB_FLAGS, + &mv_element->mv_calc_below, + &mv_element->mv_calc_colocated, + &mv_element->mv_calc_config); + } + } + + if (hierarchical) { + for (distance_b = 0; distance_b < MAX_BFRAMES; distance_b++) + memcpy(host_mv_settings_hierarchical + distance_b, + (unsigned char *)host_mv_settings_b_table + + MV_OFFSET_IN_TABLE(distance_b, distance_b >> 1), + sizeof(struct img_mv_settings)); + } +} + +/* + * Generates the video pic params template + */ +void adjust_pic_flags(struct img_enc_context *enc, struct img_rc_params *rc_params, + unsigned char first_pic, unsigned int *flags) +{ + unsigned int flags_local; + struct pic_params *pic_params = &enc->video->pic_params; + + flags_local = pic_params->flags; + + if (!rc_params->rc_enable || !first_pic) + flags_local = 0; + + *flags = flags_local; +} + +/* + * Sets up RC Data + */ +void setup_rc_data(struct img_video_context *video, struct pic_params *pic_params, + struct img_rc_params *rc_params) +{ + int tmp_qp = 0; + int buffer_size_in_frames; + short max_qp = MAX_QP_H264; + short min_qp = 0; + int mul_of_8mbits; + int framerate, scale = 1; + int l1, l2, l3, l4, l5, scaled_bpp; + + /* If Bit Rate and Basic Units are not specified then set to default values. */ + if (rc_params->bits_per_second == 0 && !video->enable_mvc) + rc_params->bits_per_second = 640000; /* kbps */ + + if (!rc_params->bu_size) + /* BU = 1 Frame */ + rc_params->bu_size = (video->picture_height >> 4) * (video->width >> 4); + + if (!rc_params->frame_rate) + rc_params->frame_rate = 30; /* fps */ + + /* Calculate Bits per Pixel */ + if (video->width <= 176) + framerate = 30; + else + framerate = rc_params->frame_rate; + + mul_of_8mbits = rc_params->bits_per_second / 8000000; + + if (mul_of_8mbits == 0) + scale = 256; + else if (mul_of_8mbits > 127) + scale = 1; + else + scale = 128 / mul_of_8mbits; + + scaled_bpp = (scale * rc_params->bits_per_second) / + (framerate * video->width * video->frame_height); + + pic_params->in_params.se_init_qp_i = rc_params->initial_qp_i; + + pic_params->in_params.mb_per_row = (video->width >> 4); + pic_params->in_params.mb_per_bu = rc_params->bu_size; + pic_params->in_params.mb_per_frm = ((unsigned int)(video->width >> 4)) * + (video->frame_height >> 4); + pic_params->in_params.bu_per_frm = (pic_params->in_params.mb_per_frm) / + rc_params->bu_size; + + pic_params->in_params.intra_period = rc_params->intra_freq; + pic_params->in_params.bframes = rc_params->bframes; + pic_params->in_params.bit_rate = rc_params->bits_per_second; + + pic_params->in_params.frm_skip_disable = rc_params->disable_frame_skipping; + + pic_params->in_params.bits_per_frm = + (rc_params->bits_per_second + rc_params->frame_rate / 2) / rc_params->frame_rate; + + pic_params->in_params.bits_per_bu = pic_params->in_params.bits_per_frm / + (4 * pic_params->in_params.bu_per_frm); + + /*Disable Vcm Hardware*/ + pic_params->in_params.disable_vcm_hardware = rc_params->disable_vcm_hardware; + /* Codec-dependent fields */ + if (video->standard == IMG_STANDARD_H264) { + pic_params->in_params.mode.h264.transfer_rate = + (rc_params->transfer_bits_per_second + rc_params->frame_rate / 2) / + rc_params->frame_rate; + pic_params->in_params.mode.h264.hierarchical_mode = rc_params->hierarchical; + + pic_params->in_params.mode.h264.enable_slice_bob = + (unsigned char)rc_params->enable_slice_bob; + pic_params->in_params.mode.h264.max_slice_bob = + (unsigned char)rc_params->max_slice_bob; + pic_params->in_params.mode.h264.slice_bob_qp = + (unsigned char)rc_params->slice_bob_qp; + } + + if (pic_params->in_params.bits_per_frm) { + buffer_size_in_frames = + (rc_params->buffer_size + (pic_params->in_params.bits_per_frm / 2)) / + pic_params->in_params.bits_per_frm; + } else { + IMG_DBG_ASSERT(video->enable_mvc && ("Can happen only in MVC mode" != NULL)); + /* Asigning more or less `normal` value. To be overridden by MVC RC module */ + buffer_size_in_frames = 30; + } + + /* select thresholds and initial Qps etc that are codec dependent */ + switch (video->standard) { + case IMG_STANDARD_H264: + /* Setup MAX and MIN Quant Values */ + pic_params->in_params.max_qp = (rc_params->max_qp > 0) && + (rc_params->max_qp < max_qp) ? rc_params->max_qp : max_qp; + + if (rc_params->min_qp == 0) { + if (scaled_bpp >= (scale >> 1)) { + tmp_qp = 4; + } else if (scaled_bpp > ((scale << 1) / 15)) { + tmp_qp = (22 * scale) - (40 * scaled_bpp); + tmp_qp = tmp_qp / scale; + } else { + tmp_qp = (30 * scale) - (100 * scaled_bpp); + tmp_qp = tmp_qp / scale; + } + + /* Adjust minQp up for small buffer size and down for large buffer size */ + if (buffer_size_in_frames < 5) { + tmp_qp += 2; + } else if (buffer_size_in_frames > 40) { + if (tmp_qp >= 1) + tmp_qp -= 1; + } + /* for HD content allow a lower minQp as bitrate is + * more easily controlled in this case + */ + if (pic_params->in_params.mb_per_frm > 2000) + tmp_qp -= 6; + } else { + tmp_qp = rc_params->min_qp; + } + + min_qp = 2; + + if (tmp_qp < min_qp) + pic_params->in_params.min_qp = min_qp; + else + pic_params->in_params.min_qp = tmp_qp; + + /* Calculate Initial QP if it has not been specified */ + tmp_qp = pic_params->in_params.se_init_qp_i; + if (pic_params->in_params.se_init_qp_i == 0) { + l1 = scale / 20; + l2 = scale / 5; + l3 = (scale * 2) / 5; + l4 = (scale * 4) / 5; + l5 = (scale * 1011) / 1000; + + tmp_qp = pic_params->in_params.min_qp; + + pic_params->in_params.se_init_qp_i = tmp_qp; + if (scaled_bpp < l1) + tmp_qp = (45 * scale) - (78 * scaled_bpp); + else if (scaled_bpp < l2) + tmp_qp = (44 * scale) - (73 * scaled_bpp); + else if (scaled_bpp < l3) + tmp_qp = (34 * scale) - (25 * scaled_bpp); + else if (scaled_bpp < l4) + tmp_qp = (32 * scale) - (20 * scaled_bpp); + else if (scaled_bpp < l5) + tmp_qp = (25 * scale) - (10 * scaled_bpp); + else + tmp_qp = (18 * scale) - (5 * scaled_bpp); + + /* Adjust ui8SeInitQP up for small buffer size or small fps */ + /* Adjust ui8SeInitQP up for small gop size */ + if (buffer_size_in_frames < 20 || rc_params->intra_freq < 20) + tmp_qp += 2 * scale; + + /* for very small buffers increase initial Qp even more */ + if (buffer_size_in_frames < 5) + tmp_qp += 8 * scale; + + /* start on a lower initial Qp for HD content + * as the coding is more efficient + */ + if (pic_params->in_params.mb_per_frm > 2000) + tmp_qp -= 2 * scale; + + if (pic_params->in_params.intra_period == 1) { + /* for very small GOPS start with a much higher initial Qp */ + tmp_qp += 12 * scale; + } else if (pic_params->in_params.intra_period < 5) { + tmp_qp += 6 * scale; + } + + tmp_qp = tmp_qp / scale; + } + + max_qp = 49; + + if (tmp_qp > max_qp) + tmp_qp = max_qp; + + if (tmp_qp < pic_params->in_params.min_qp) + tmp_qp = pic_params->in_params.min_qp; + + pic_params->in_params.se_init_qp_i = tmp_qp; + + if (scaled_bpp <= ((3 * scale) / 10)) + pic_params->flags |= ISRC_I16BIAS; + break; + + default: + /* the NO RC cases will fall here */ + break; + } + + if (video->rc_params.rc_mode == IMG_RCMODE_VBR) { + pic_params->in_params.mb_per_bu = pic_params->in_params.mb_per_frm; + pic_params->in_params.bu_per_frm = 1; + + /* Initialize the parameters of fluid flow traffic model. */ + pic_params->in_params.buffer_size = rc_params->buffer_size; + + /* VBR shouldn't skip frames */ + pic_params->in_params.frm_skip_disable = TRUE; + + /* + * These scale factor are used only for rate control to avoid overflow + * in fixed-point calculation these scale factors are decided by bit rate + */ + if (rc_params->bits_per_second < 640000) + pic_params->in_params.scale_factor = 2; /* related to complexity */ + else if (rc_params->bits_per_second < 2000000) /* 2 Mbits */ + pic_params->in_params.scale_factor = 4; + else if (rc_params->bits_per_second < 8000000) /* 8 Mbits */ + pic_params->in_params.scale_factor = 6; + else + pic_params->in_params.scale_factor = 8; + } else { + /* Set up Input Parameters that are mode dependent */ + switch (video->standard) { + case IMG_STANDARD_H264: + /* + * H264 CBR RC: Initialize the parameters of fluid flow traffic model. + */ + pic_params->in_params.buffer_size = rc_params->buffer_size; + + /* HRD consideration - These values are used by H.264 reference code. */ + if (rc_params->bits_per_second < 1000000) /* 1 Mbits/s */ + pic_params->in_params.scale_factor = 0; + else if (rc_params->bits_per_second < 2000000) /* 2 Mbits/s */ + pic_params->in_params.scale_factor = 1; + else if (rc_params->bits_per_second < 4000000) /* 4 Mbits/s */ + pic_params->in_params.scale_factor = 2; + else if (rc_params->bits_per_second < 8000000) /* 8 Mbits/s */ + pic_params->in_params.scale_factor = 3; + else + pic_params->in_params.scale_factor = 4; + + if (video->rc_params.rc_mode == IMG_RCMODE_VCM) + pic_params->in_params.buffer_size_frames = buffer_size_in_frames; + break; + + default: + break; + } + } + + if (rc_params->sc_detect_disable) + pic_params->flags |= ISSCENE_DISABLED; + + pic_params->in_params.initial_delay = rc_params->initial_delay; + pic_params->in_params.initial_level = rc_params->initial_level; + rc_params->initial_qp_i = pic_params->in_params.se_init_qp_i; + + /* The rate control uses this value to adjust + * the reaction rate to larger than expected frames + */ + if (video->standard == IMG_STANDARD_H264) { + if (pic_params->in_params.bits_per_frm) { + const int bits_per_gop = + (rc_params->bits_per_second / rc_params->frame_rate) * + rc_params->intra_freq; + + pic_params->in_params.mode.h264.rc_scale_factor = (bits_per_gop * 256) / + (pic_params->in_params.buffer_size - + pic_params->in_params.initial_level); + } else { + pic_params->in_params.mode.h264.rc_scale_factor = 0; + } + } +} + +void topaz_setup_input_format(struct img_video_context *video, + struct img_vxe_scaler_setup *scaler_setup) +{ + const unsigned int scaler_coeff_regs_no_crop[] = {4261951490, 4178589440, + 4078580480, 4045614080}; + + if (video->enable_scaler) { + unsigned int pitch_x, pitch_y; + int phase; + + pitch_x = (((unsigned int)(video->source_width - video->crop_left - + video->crop_right)) << 13) / video->unrounded_width; + + pitch_y = (((unsigned int)(video->source_frame_height - video->crop_top - + video->crop_bottom)) << 13) / video->unrounded_frame_height; + + /* Input size */ + scaler_setup->scaler_input_size_reg = + F_ENCODE(video->source_width - 1, + TOPAZHP_EXT_CR_SCALER_INPUT_WIDTH_MIN1) | + F_ENCODE((video->source_frame_height >> + (video->is_interlaced ? 1 : 0)) - 1, + TOPAZHP_EXT_CR_SCALER_INPUT_HEIGHT_MIN1); + + scaler_setup->scaler_crop_reg = F_ENCODE(video->crop_left, + TOPAZHP_EXT_CR_SCALER_INPUT_CROP_HOR) | + F_ENCODE(video->crop_top, + TOPAZHP_EXT_CR_SCALER_INPUT_CROP_VER); + + /* Scale factors */ + scaler_setup->scaler_pitch_reg = 0; + + if (pitch_x > 0x7FFF) { + scaler_setup->scaler_pitch_reg |= F_ENCODE(1, + TOPAZHP_EXT_CR_SCALER_HOR_BILINEAR_FILTER); + pitch_x >>= 1; + } + + if (pitch_x > 0x7FFF) + pitch_x = 0x7FFF; + + if (pitch_y > 0x7FFF) { + scaler_setup->scaler_pitch_reg |= F_ENCODE(1U, + TOPAZHP_EXT_CR_SCALER_VER_BILINEAR_FILTER); + pitch_y >>= 1; + } + + if (pitch_y > 0x7FFF) + pitch_y = 0x7FFF; + + scaler_setup->scaler_pitch_reg |= + F_ENCODE(pitch_x, TOPAZHP_EXT_CR_SCALER_INPUT_HOR_PITCH) | + F_ENCODE(pitch_y, TOPAZHP_EXT_CR_SCALER_INPUT_VER_PITCH); + + /* + * Coefficients + * With no crop, the coefficients remain the same. + * If crop is desired, new values will need to be calculated. + */ + for (phase = 0; phase < 4; phase++) + scaler_setup->hor_scaler_coeff_regs[phase] = + scaler_coeff_regs_no_crop[phase]; + + for (phase = 0; phase < 4; phase++) + scaler_setup->ver_scaler_coeff_regs[phase] = + scaler_coeff_regs_no_crop[phase]; + + scaler_setup->scaler_control = F_ENCODE(1, TOPAZHP_EXT_CR_SCALER_ENABLE); + + switch (video->format) { + case IMG_CODEC_420_YUV: + case IMG_CODEC_420_PL8: + case IMG_CODEC_420_YV12: + case IMG_CODEC_420_IMC2: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_420PL111YCBCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_420_PL12: + case IMG_CODEC_420_PL12_PACKED: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_420PL12YCBCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_420_PL21: + case IMG_CODEC_420_PL21_PACKED: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_420PL12YCRCB8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_422_YUV: + case IMG_CODEC_422_PL8: + case IMG_CODEC_422_YV12: + case IMG_CODEC_422_IMC2: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422PL111YCBCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_422_PL12: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422PL12YCBCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_422_PL21: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422PL12YCRCB8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_Y0UY1V_8888: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3YCBYCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_Y0VY1U_8888: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3YCRYCB8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_UY0VY1_8888: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3CBYCRY8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_VY0UY1_8888: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_422IL3CRYCBY8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_444_YUV: + case IMG_CODEC_444_PL8: + case IMG_CODEC_444_YV12: + case IMG_CODEC_444_IMC2: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444PL111YCBCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_444_PL12: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444PL12YCBCR8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_444_PL21: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444PL12YCRCB8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_ABCX: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444IL4ABCX8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_XBCA: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444IL4XBCA8, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + case IMG_CODEC_ABC565: + scaler_setup->input_scaler_control = + F_ENCODE(TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT_444IL3RGB565, + TOPAZHP_EXT_CR_INPUT_FRAME_STORE_FORMAT); + break; + default: + break; + } + } else { + /* Disable Scaling */ + scaler_setup->scaler_control = 0; + } +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topaz_api_utils.h b/drivers/media/platform/vxe-vxd/encoder/topaz_api_utils.h --- a/drivers/media/platform/vxe-vxd/encoder/topaz_api_utils.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topaz_api_utils.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * topaz utility header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include "topaz_api.h" + +/* + * Calculates the correct number of macroblocks per kick and kicks per BU + */ +void calculate_kick_and_bu_size(unsigned int width_in_mbs, + unsigned int height_in_mbs, + unsigned char is_interlaced, + unsigned int max_bu_per_frame, + unsigned int *kick_size, + unsigned int *kicks_per_bu, + unsigned int *min_slice_height); + +unsigned int calculate_stride(enum img_format format, + unsigned short requested_stride_bytes, + unsigned short width); + +void topaz_setup_input_format(struct img_video_context *video, + struct img_vxe_scaler_setup *scaler_setup); + +void topaz_setup_input_csc(struct img_video_context *video, + struct img_vxe_scaler_setup *scaler_setup, + struct img_vxe_csc_setup *csc_setup, + enum img_csc_preset csc_preset); + +unsigned int topaz_get_packed_buffer_strides(unsigned short buffer_stride_bytes, + enum img_format format, + unsigned char enable_scaler, + unsigned char is_interlaced, + unsigned char is_interleaved); + +void prepare_mv_estimates(struct img_enc_context *enc); + +void adjust_pic_flags(struct img_enc_context *enc, struct img_rc_params *prc_params, + unsigned char first_pic, unsigned int *flags); + +void setup_rc_data(struct img_video_context *video, struct pic_params *pic_params, + struct img_rc_params *rc_params); + +void patch_hw_profile(struct img_video_params *video_params, struct img_video_context *video); diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topaz_color_formats.h b/drivers/media/platform/vxe-vxd/encoder/topaz_color_formats.h --- a/drivers/media/platform/vxe-vxd/encoder/topaz_color_formats.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topaz_color_formats.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * buffer sizes calculation + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include "topaz_api.h" +#include "fw_headers/defs.h" + +void plane_size(enum img_format color_format, unsigned int stride, + unsigned int height, unsigned int *y_size, unsigned int *u_size, + unsigned int *v_size) +{ + *y_size = *u_size = *v_size = 0; + + switch (color_format) { + case IMG_CODEC_420_PL8: + /* allocate frame for 4:2:0 planar format */ + *y_size = stride * height; + *u_size = stride * height / 4; + *v_size = stride * height / 4; + break; + case IMG_CODEC_420_PL12: + /* allocate frame for 4:2:0 planar format (chroma interleaved) */ + *y_size = stride * height; + *u_size = stride * height / 2; + break; + case IMG_CODEC_422_YUV: + case IMG_CODEC_422_YV12: + case IMG_CODEC_422_IMC2: + /* allocate frame for 4:2:2 format */ + *y_size = stride * height * 2; + break; + case IMG_CODEC_422_PL8: + /* allocate frame for 4:2:2 planar format */ + *y_size = stride * height; + *u_size = stride * height / 2; + *v_size = stride * height / 2; + break; + case IMG_CODEC_422_PL12: + /* allocate frame for 4:2:2 planar format (chroma interleaved) */ + *y_size = stride * height; + *u_size = stride * height; + break; + case IMG_CODEC_Y0UY1V_8888: + case IMG_CODEC_UY0VY1_8888: + case IMG_CODEC_Y0VY1U_8888: + case IMG_CODEC_VY0UY1_8888: + /* allocate frame for 4:2:2 format */ + *y_size = stride * height; + break; + case IMG_CODEC_444_YUV: + case IMG_CODEC_444_YV12: + case IMG_CODEC_444_IMC2: + /* allocate frame for 4:2:2 format */ + *y_size = stride * height * 3; + break; + case IMG_CODEC_444_PL8: + /* allocate frame for 4:2:2 planar format */ + *y_size = stride * height; + *u_size = stride * height; + *v_size = stride * height; + break; + case IMG_CODEC_444_PL12: + /* allocate frame for 4:2:2 planar format (chroma interleaved) */ + *y_size = stride * height; + *u_size = stride * height * 2; + break; + case IMG_CODEC_ABCX: + case IMG_CODEC_XBCA: + case IMG_CODEC_ABC565: + /* allocate frame for RGB interleaved format */ + *y_size = stride * height; + break; + case IMG_CODEC_420_YUV: + case IMG_CODEC_420_YV12: + case IMG_CODEC_420_IMC2: + case IMG_CODEC_420_PL12_PACKED: + case IMG_CODEC_420_PL21_PACKED: + /* allocate frame for 4:2:0 format */ + *y_size = stride * height * 3 / 2; + break; + default: + *y_size = 0; + *u_size = 0; + *v_size = 0; + break; + } +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topaz_device.c b/drivers/media/platform/vxe-vxd/encoder/topaz_device.c --- a/drivers/media/platform/vxe-vxd/encoder/topaz_device.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topaz_device.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1671 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Encoder device function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "work_queue.h" +#include "fw_headers/defs.h" +#include "fw_headers/vxe_common.h" +#include "target.h" +#include "target_config.h" +#include "topaz_device.h" +#include "topazmmu.h" +#include "vid_buf.h" +#include "vxe_public_regdefs.h" +#include "img_errors.h" + +#ifdef DEBUG_ENCODER_DRIVER +static char command_string[][38] = { + "MTX_CMDID_NULL", + "MTX_CMDID_SHUTDOWN", + "MTX_CMDID_DO_HEADER", + "MTX_CMDID_ENCODE_FRAME", + "MTX_CMDID_START_FRAME", + "MTX_CMDID_ENCODE_SLICE", + "MTX_CMDID_END_FRAME", + "MTX_CMDID_SETVIDEO", + "MTX_CMDID_GETVIDEO", + "MTX_CMDID_DO_CHANGE_PIPEWORK", +#if SECURE_IO_PORTS + "MTX_CMDID_SECUREIO", +#endif + "MTX_CMDID_PICMGMT", + "MTX_CMDID_RC_UPDATE", + "MTX_CMDID_PROVIDE_SOURCE_BUFFER", + "MTX_CMDID_PROVIDE_REF_BUFFER", + "MTX_CMDID_PROVIDE_CODEDPACKAGE_BUFFER", + "MTX_CMDID_ABORT", + "MTX_CMDID_SETQUANT", + "MTX_CMDID_SETUP_INTERFACE", + "MTX_CMDID_ISSUEBUFF", + "MTX_CMDID_SETUP", + "MTX_CMDID_UPDATE_SOURCE_FORMAT", + "MTX_CMDID_UPDATE_CSC", + "MTX_CMDID_ENDMARKER" +}; +#endif + +DECLARE_WAIT_QUEUE_HEAD(event_wait_queue); + +#define TOPAZ_DEV_SPIN_LOCK_NAME "topaz_dev" +/* max syncStatus value used (at least 4 * MAX_TOPAZ_CMDS_QUEUED) */ +#define MAX_TOPAZ_CMD_COUNT (0x1000) + +#define COMM_WB_DATA_BUF_SIZE (64) + +/* Sempahore locks */ +#define COMM_LOCK_TX 0x01 +#define COMM_LOCK_RX 0x02 +#define COMM_LOCK_BOTH (COMM_LOCK_TX | COMM_LOCK_RX) + +static unsigned int topaz_timeout_retries = 817000; + +#define TOPAZ_TIMEOUT_JPEG (50000) +#define TOPAZ_TIMEOUT_RETRIES (topaz_timeout_retries) + +unsigned short g_load_method = MTX_LOADMETHOD_DMA; /* This is the load method used */ + +unsigned int g_core_rev; +unsigned int g_core_des1; +void *g_lock; + +struct vidio_ddbufinfo *g_aps_wb_data_info; + +static unsigned char g_pipe_usage[TOPAZHP_MAX_NUM_PIPES] = { 0 }; + +/* Order MUST match with topaz_mem_space_idx enum */ +struct mem_space topaz_mem_space[] = { + /* Multicore sync RAM */ + { "REG_TOPAZHP_MULTICORE", MEMSPACE_REGISTER, + {{0x00000000, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_DMAC", MEMSPACE_REGISTER, + {{0x00000400, 0x000000ff, TARGET_NO_IRQ}}}, + { "REG_COMMS", MEMSPACE_REGISTER, + {{0x00000500, 0x000000ff, TARGET_NO_IRQ}}}, + { "REG_MTX", MEMSPACE_REGISTER, + {{0x00000800, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_MMU", MEMSPACE_REGISTER, + {{0x00000C00, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_TEST", MEMSPACE_REGISTER, + {{0xFFFF0000, 0x000001ff, TARGET_NO_IRQ}}}, + { "REGMTXRAM", MEMSPACE_REGISTER, + {{0x80000000, 0x0000ffff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_CORE_0", MEMSPACE_REGISTER, + {{0x00001000, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_VLC_CORE_0", MEMSPACE_REGISTER, + {{0x00001400, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_DEBLOCKER_CORE_0", MEMSPACE_REGISTER, + {{0x00001800, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_COREEXT_0", MEMSPACE_REGISTER, + {{0x00001C00, 0x000003ff, TARGET_NO_IRQ}}}, + + { "REG_TOPAZHP_CORE_1", MEMSPACE_REGISTER, + {{0x00002000, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_VLC_CORE_1", MEMSPACE_REGISTER, + {{0x00002400, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_DEBLOCKER_CORE_1", MEMSPACE_REGISTER, + {{0x00002800, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_COREEXT_1", MEMSPACE_REGISTER, + {{0x00002C00, 0x000003ff, TARGET_NO_IRQ}}}, + + { "REG_TOPAZHP_CORE_2", MEMSPACE_REGISTER, + {{0x00003000, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_VLC_CORE_2", MEMSPACE_REGISTER, + {{0x00003400, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_DEBLOCKER_CORE_2", MEMSPACE_REGISTER, + {{0x00003800, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_COREEXT_2", MEMSPACE_REGISTER, + {{0x00003C00, 0x000003ff, TARGET_NO_IRQ}}}, + + { "REG_TOPAZHP_CORE_3", MEMSPACE_REGISTER, + {{0x00004000, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_VLC_CORE_3", MEMSPACE_REGISTER, + {{0x00004400, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_DEBLOCKER_CORE_3", MEMSPACE_REGISTER, + {{0x00004800, 0x000003ff, TARGET_NO_IRQ}}}, + { "REG_TOPAZHP_COREEXT_3", MEMSPACE_REGISTER, + {{0x00004C00, 0x000003ff, TARGET_NO_IRQ}}}, + + { "FW", MEMSPACE_MEMORY, + {{0x00000000, 0x00800000, 0 }}}, + { "SYSMEM", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, + { "MEMSYSMEM", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, + { "MEM", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, + { "FB", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, + { "MEMDMAC_00", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, + { "MEMDMAC_01", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, + { "MEMDMAC_02", MEMSPACE_MEMORY, + {{0x00000000, 0, 0 }}}, +}; + +#define MEMORYSPACES_NUM (sizeof(topaz_mem_space) / sizeof(struct mem_space)) + +static struct target_config topaz_target_config = { + MEMORYSPACES_NUM, + &topaz_mem_space[0] +}; + +/* + * topazdd_int_enable + */ +static void topazdd_int_enable(struct topaz_dev_ctx *ctx, unsigned int mask) +{ + unsigned int reg; + unsigned long flags; + + spin_lock_irqsave(ctx->lock, flags); + + /* config interrupts on Topaz core */ + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB); + + /* set enable interrupt bits */ + reg |= mask; + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB, reg); + + spin_unlock_irqrestore(ctx->lock, (unsigned long)flags); +} + +/* + * topazdd_int_disable + */ +static void topazdd_int_disable(struct topaz_dev_ctx *ctx, unsigned int mask) +{ + unsigned int reg; + unsigned long flags; + + spin_lock_irqsave(ctx->lock, flags); + + /* config interrupts on Topaz core */ + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB); + + /* clear enable interrupt bits */ + reg &= ~mask; + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB, reg); + + spin_unlock_irqrestore(ctx->lock, (unsigned long)flags); +} + +/* + * Get the number of pipes present + */ +unsigned int topazdd_get_num_pipes(struct topaz_dev_ctx *ctx) +{ + static unsigned int g_pipes_avail; + + if (!ctx->multi_core_mem_addr) + return 0; + + if (g_pipes_avail == 0) { + /* get the actual number of cores */ + g_pipes_avail = VXE_RD_REG32(ctx->multi_core_mem_addr, + TOPAZHP_TOP_CR_MULTICORE_HW_CFG); + g_pipes_avail = (g_pipes_avail & MASK_TOPAZHP_TOP_CR_NUM_CORES_SUPPORTED); + IMG_DBG_ASSERT(g_pipes_avail != 0); + } + + return g_pipes_avail; +} + +unsigned int topazdd_get_core_rev(void) +{ + return g_core_rev; +} + +unsigned int topazdd_get_core_des1(void) +{ + return g_core_des1; +} + +static void wbfifo_clear(struct img_comm_socket *sock) +{ + sock->in_fifo_producer = 0; + sock->in_fifo_consumer = 0; +} + +static unsigned char wbfifo_add(struct img_comm_socket *sock, struct img_writeback_msg *msg) +{ + unsigned int new_producer = sock->in_fifo_producer + 1; + + if (new_producer == COMM_INCOMING_FIFO_SIZE) + new_producer = 0; + + if (new_producer == sock->in_fifo_consumer) + return FALSE; + + memcpy(&sock->in_fifo[sock->in_fifo_producer], msg, sizeof(struct img_writeback_msg)); + + sock->in_fifo_producer = new_producer; + + return TRUE; +} + +static unsigned char wbfifo_is_empty(struct img_comm_socket *sock) +{ + return (sock->in_fifo_producer == sock->in_fifo_consumer); +} + +static unsigned char wbfifo_get(struct img_comm_socket *sock, struct img_writeback_msg *msg) +{ + if (wbfifo_is_empty(sock)) + return FALSE; + + memcpy(msg, &sock->in_fifo[sock->in_fifo_consumer], sizeof(struct img_writeback_msg)); + + sock->in_fifo_consumer++; + + if (sock->in_fifo_consumer == COMM_INCOMING_FIFO_SIZE) + sock->in_fifo_consumer = 0; + + return TRUE; +} + +unsigned char topazdd_is_idle(struct img_comm_socket *sock) +{ + if (sock->msgs_sent == sock->ack_recv && wbfifo_is_empty(sock)) + return TRUE; + + return FALSE; +} + +static void set_auto_clock_gating(struct topaz_dev_ctx *ctx, struct img_fw_context *fw_ctx, + unsigned char gating) +{ + unsigned int reg; + + reg = F_ENCODE(1U, TOPAZHP_TOP_CR_WRITES_CORE_ALL); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CORE_SEL_0, reg); + + reg = F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_IPE0_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_IPE1_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_SPE0_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_SPE1_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_H264COMP4X4_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_H264COMP8X8_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_H264COMP16X16_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_JMCOMP_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_VLC_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_DEB_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_PC_DM_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_PC_DMS_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_CABAC_AUTO_CLK_GATE) | + F_ENCODE(gating, TOPAZHP_CR_TOPAZHP_INPUT_SCALER_AUTO_CLK_GATE); + + VXE_WR_REG32(ctx->hp_core_reg_addr[0], TOPAZHP_CR_TOPAZHP_AUTO_CLOCK_GATING, reg); + + reg = 0; + reg = VXE_RD_REG32(ctx->hp_core_reg_addr[0], TOPAZHP_CR_TOPAZHP_MAN_CLOCK_GATING); + + /* Disable LRITC clocks */ + reg = F_INSERT(reg, 1, TOPAZHP_CR_TOPAZHP_LRITC_MAN_CLK_GATE); + + VXE_WR_REG32(ctx->hp_core_reg_addr[0], TOPAZHP_CR_TOPAZHP_MAN_CLOCK_GATING, reg); + + reg = F_ENCODE(0, TOPAZHP_TOP_CR_WRITES_CORE_ALL); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CORE_SEL_0, reg); +} + +static void comm_lock(struct topaz_dev_ctx *ctx, unsigned int flags) +{ + if (flags & COMM_LOCK_TX) + mutex_lock_nested(ctx->comm_tx_mutex, SUBCLASS_TOPAZDD_TX); +} + +static void comm_unlock(struct topaz_dev_ctx *ctx, unsigned int flags) +{ + if (flags & COMM_LOCK_TX) + mutex_unlock((struct mutex *)ctx->comm_tx_mutex); +} + +int comm_prepare_fw(struct img_fw_context *fw_ctx, enum img_codec codec) +{ + if (fw_ctx->populated || fw_ctx->initialized) + return IMG_SUCCESS; + + return mtx_populate_fw_ctx(codec, fw_ctx); +} + +static unsigned int H264_RCCONFIG_TABLE_5[27] = { + 0x00000007, 0x00000006, 0x00000006, 0x00000006, 0x00000006, 0x00000005, 0x00000005, + 0x00000005, 0x00000005, + 0x00000005, 0x00000005, 0x00000004, 0x00000004, + 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000005, + 0x00000005, 0x00000005, + 0x00000005, 0x00000005, 0x00000005, 0x00000006, + 0x00000006, +}; + +static unsigned int H264_RCCONFIG_TABLE_6[27] = { + 0x00000018, 0x00000018, 0x00000018, 0x00000018, 0x00000018, 0x00000018, 0x00000018, + 0x00000018, 0x00000024, + 0x00000030, 0x00000030, 0x0000003c, 0x0000003c, + 0x00000048, 0x00000048, 0x00000054, 0x00000060, 0x0000006c, 0x000000c8, 0x00000144, + 0x00000180, 0x00000210, + 0x000002a0, 0x00000324, 0x0000039c, 0x00000414, + 0x00000450, +}; + +static unsigned int H264_RCCONFIG_TABLE_7[27] = { + 0x00000014, 0x00000014, 0x00000014, 0x00000014, 0x00000014, 0x00000014, 0x00000032, + 0x00000064, 0x000000d2, + 0x000001a4, 0x000001a4, 0x000001bd, 0x000001d6, + 0x000001ef, 0x00000208, 0x00000217, 0x00000226, 0x0000023a, 0x000002cb, 0x0000035c, + 0x00000384, 0x000003e8, + 0x000004b0, 0x00000578, 0x00000640, 0x00000708, + 0x000007d0, +}; + +static unsigned int MPEG_RCCONFIG_TABLE_7[17] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000003c, + 0x000000b4, 0x0000012c, + 0x000001a4, 0x0000021c, 0x00000294, 0x0000030c, + 0x00000384, 0x000003fc, 0x00000474, 0x000004ec, +}; + +/* + * Load the tables for H.264 + */ +void comm_load_h264_tables(struct topaz_dev_ctx *ctx) +{ + int n; + unsigned int pipe, pipe_cnt; + + pipe_cnt = topazdd_get_num_pipes(ctx); + + for (n = 26; n >= 0; n--) { + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE4, 0); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE5, + H264_RCCONFIG_TABLE_5[n]); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE6, + H264_RCCONFIG_TABLE_6[n]); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE7, + H264_RCCONFIG_TABLE_7[n]); + } + + for (pipe = 0; pipe < pipe_cnt; pipe++) { + VXE_WR_REG32(ctx->hp_core_reg_addr[pipe], TOPAZHP_CR_RC_CONFIG_REG8, 0x00000006); + VXE_WR_REG32(ctx->hp_core_reg_addr[pipe], TOPAZHP_CR_RC_CONFIG_REG9, 0x00000406); + } +} + +/* + * Load the tables for mpeg4 + */ +void comm_load_tables(struct topaz_dev_ctx *ctx) +{ + int n; + unsigned int pipe; + + for (n = 16; n > 0; n--) { + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE4, 0); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE6, 0); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_RC_CONFIG_TABLE7, + MPEG_RCCONFIG_TABLE_7[n]); + } + + for (pipe = 0; pipe < topazdd_get_num_pipes(ctx); pipe++) + VXE_WR_REG32(ctx->hp_core_reg_addr[pipe], TOPAZHP_CR_RC_CONFIG_REG8, 0x00000006); +} + +/* + * Load bias tables + */ +static int comm_load_bias(struct topaz_dev_ctx *ctx, unsigned int codec_mask) +{ + if ((codec_mask & CODEC_MASK_H263) || (codec_mask & CODEC_MASK_MPEG2) || + (codec_mask & CODEC_MASK_MPEG4)) + comm_load_tables(ctx); + + if ((codec_mask & CODEC_MASK_H264) || (codec_mask & CODEC_MASK_H264MVC)) + comm_load_h264_tables(ctx); + + return IMG_SUCCESS; +} + +/* + * Loads MTX firmware + */ +void topaz_setup_firmware(struct topaz_dev_ctx *ctx, + struct img_fw_context *fw_ctx, + enum mtx_load_method load_method, + enum img_codec codec, unsigned char num_pipes) +{ + unsigned int reg; + unsigned int secure_reg; + int ret; + + fw_ctx->initialized = FALSE; + + /* Reset the MTXs and Upload the code. */ + /* start each MTX in turn MUST start with master to enable comms to other cores */ + +#if SECURE_IO_PORTS + /* reset SECURE_CONFIG register to allow loading FW without security. + * Default option is secure. + */ + + secure_reg = 0x000F0F0F; + + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_SECURE_CONFIG, secure_reg); +#endif + + ret = comm_prepare_fw(fw_ctx, codec); + + if (ret != IMG_SUCCESS) { + pr_err("Failed to populate firmware context. Error code: %i\n", ret); + return; + } + + /* initialise the MTX */ + mtx_initialize(ctx, fw_ctx); + + /* clear TOHOST register now so that our ISR doesn't see any + * intermediate value before the FW has output anything + */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOHOST << 2), 0); + + /* clear BOOTSTATUS register. Firmware will write to + * this to indicate firmware boot progress + */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_BOOTSTATUS << 2), 0); + + /* Soft reset of MTX */ + reg = 0; + reg = F_ENCODE(1, TOPAZHP_TOP_CR_IMG_TOPAZ_MTX_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_TOP_CR_IMG_TOPAZ_CORE_SOFT_RESET); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_SRST, reg); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_SRST, 0x0); + + if (fw_ctx->initialized) { + set_auto_clock_gating(ctx, fw_ctx, 1); + mtx_load(ctx, fw_ctx, load_method); + + /* flush the command FIFO */ + reg = 0; + reg = F_ENCODE(1, TOPAZHP_TOP_CR_CMD_FIFO_FLUSH); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_TOPAZ_CMD_FIFO_FLUSH, reg); + + /* we do not want to run in secre FW mode so write a place holder + * to the FIFO that the firmware will know to ignore + */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, + TOPAZHP_NON_SECURE_FW_MARKER); + + /* Clear FW_IDLE_STATUS register */ + VXE_WR_REG32(ctx->multi_core_mem_addr, MTX_SCRATCHREG_IDLE, 0); + + /* turn on MTX */ + mtx_start(fw_ctx); + /* get MTX Clk Freq */ + + mtx_kick(fw_ctx, 1); + + /* + * We do not need to do this POLL here as it is safe to continue without it. + * We do it because it serves to warn us that there is a problem if the + * firmware doesn't start for some reason + */ + VXE_POLL_REG32_ISEQ(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_BOOTSTATUS << 2), TOPAZHP_FW_BOOT_SIGNAL, + 0xffffffff, TOPAZ_TIMEOUT_RETRIES); + } +} + +static int comm_send(struct img_comm_socket *sock, struct mtx_tomtx_msg *msg, unsigned int *wb_val) +{ + struct topaz_dev_ctx *ctx; + struct img_fw_context *fw_ctx; + unsigned int space_avail; + unsigned int cmd_word; + unsigned int writeback_val; + enum mtx_cmd_id cmd_id = (enum mtx_cmd_id)(msg->cmd_id & 0x7F); + + ctx = sock->ctx; + fw_ctx = &ctx->fw_ctx; + + /* mark the context as active in case we need to save its state later */ + fw_ctx->active_ctx_mask |= (1 << sock->id); + + space_avail = VXE_RD_REG32(ctx->multi_core_mem_addr, + TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE_SPACE); + + space_avail = F_DECODE(space_avail, TOPAZHP_TOP_CR_CMD_FIFO_SPACE); + + if (space_avail < 4) + return IMG_ERROR_RETRY; + + /* Write command to FIFO */ + cmd_word = F_ENCODE(sock->id, MTX_MSG_CORE) | msg->cmd_id; + + if (msg->cmd_id & MTX_CMDID_PRIORITY) { + /* increment the command counter */ + sock->high_cmd_cnt++; + + /* Prepare high priority command */ + cmd_word |= F_ENCODE(1, MTX_MSG_PRIORITY) | + F_ENCODE(((sock->low_cmd_cnt - 1) & 0xff) | (sock->high_cmd_cnt << 8), + MTX_MSG_COUNT); + } else { + /* Prepare low priority command */ + cmd_word |= F_ENCODE(sock->low_cmd_cnt & 0xff, MTX_MSG_COUNT); + } + + /* write command into FIFO */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, cmd_word); + + /* Write data to FIFO */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, msg->data); + + if (msg->command_data_buf) { + /* Write address */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, + msg->command_data_buf->dev_virt); + } else { + /* Write nothing */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, 0); + } + + /* Write writeback value to FIFO */ + + /* prepare Writeback value */ + + /* We don't actually use this value, but it may be useful to customers */ + if (msg->cmd_id & MTX_CMDID_PRIORITY) { + /* HIGH priority command */ + + writeback_val = sock->high_cmd_cnt << 24; + + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, + writeback_val); + } else { + /* LOW priority command */ + writeback_val = sock->low_cmd_cnt << 16; + + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE, + writeback_val); + + /* increment the command counter */ + sock->low_cmd_cnt++; + } + + if (wb_val) + *wb_val = writeback_val; + + sock->last_sync = writeback_val; + + switch (cmd_id) { + case MTX_CMDID_PROVIDE_CODEDPACKAGE_BUFFER: + { +#ifdef DEBUG_ENCODER_DRIVER + unsigned int slot; + + slot = F_DECODE(msg->data, MTX_MSG_PROVIDE_CODEDPACKAGE_BUFFER_SLOT); + pr_debug("MSG_TX[%d]: %s(%d) %s %s cmd: %#08x cmd_word: %#08x data: %#08x: addr: 0x%p writeback_val: %#08x\n", + sock->id, command_string[cmd_id], slot, + (msg->cmd_id & MTX_CMDID_PRIORITY ? "(PRIORITY)" : "(NORMAL)"), + (msg->cmd_id & MTX_CMDID_WB_INTERRUPT ? "(Interrupt)" : "(NO Interrupt)"), + (msg->cmd_id), cmd_word, (msg->data), msg->command_data_buf, + writeback_val); +#endif + break; + } +#ifdef ENABLE_PROFILING + case MTX_CMDID_ENCODE_FRAME: + { + struct timespec64 time; + + ktime_get_real_ts64(&time); + + sock->fw_lat.start_time = timespec64_to_ns((const struct timespec64 *)&time); + } +#endif + default: +#ifdef DEBUG_ENCODER_DRIVER + pr_debug("MSG_TX[%d]: %s %s %s cmd: %#08x cmd_word: %#08x data: %#08x addr: 0x%p writeback_val: %#08x\n", + sock->id, command_string[cmd_id], + (msg->cmd_id & MTX_CMDID_PRIORITY ? "(PRIORITY)" : "(NORMAL)"), + (msg->cmd_id & MTX_CMDID_WB_INTERRUPT ? "(Interrupt)" : "(NO Interrupt)"), + (msg->cmd_id), cmd_word, (msg->data), msg->command_data_buf, + writeback_val); +#endif + break; + } +#ifdef DEBUG_ENCODER_DRIVER + if (msg->command_data_buf) { + int i; + + pr_debug("Has msg->command_data_buf cpu_virt=0x%p dev_virt=%#08x\n", + msg->command_data_buf->cpu_virt, msg->command_data_buf->dev_virt); + + for (i = 0; i < 350; i++) { + pr_debug("MSG_TX %03d %#08x\n", i, + ((unsigned int *)msg->command_data_buf->cpu_virt)[i]); + } + } +#endif + + /* kick the master MTX */ + mtx_kick(fw_ctx, 1); + + sock->msgs_sent++; + + return IMG_SUCCESS; +} + +int topazdd_send_msg(void *dd_str_ctx, enum mtx_cmd_id cmd_id, + unsigned int data, struct vidio_ddbufinfo *cmd_data_buf, + unsigned int *wb_val) +{ + struct mtx_tomtx_msg *msg; + struct img_comm_socket *sock; + int err; + + if (!dd_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + sock = (struct img_comm_socket *)dd_str_ctx; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + IMG_DBG_ASSERT(msg); + if (!msg) + return IMG_ERROR_UNDEFINED; + + msg->command_data_buf = cmd_data_buf; + msg->cmd_id = cmd_id; + msg->data = data; + + if (!wb_val) { + comm_lock(sock->ctx, COMM_LOCK_TX); + err = comm_send(sock, msg, NULL); + comm_unlock(sock->ctx, COMM_LOCK_TX); + } else { + unsigned int ret_wb_val; + + comm_lock(sock->ctx, COMM_LOCK_TX); + err = comm_send(sock, msg, &ret_wb_val); + comm_unlock(sock->ctx, COMM_LOCK_TX); + + if (err == IMG_SUCCESS) + *wb_val = ret_wb_val; + } + + kfree(msg); + return err; +} + +#define WAIT_FOR_SYNC_RETRIES 1200 +#define WAIT_FOR_SYNC_TIMEOUT 1 + +static int wait_event_obj(void *event, unsigned char uninterruptible, unsigned int timeout) +{ + struct event *p_event = (struct event *)event; + int ret; + + IMG_DBG_ASSERT(event); + if (!event) + return IMG_ERROR_GENERIC_FAILURE; + + if (uninterruptible) { + if (timeout == (unsigned int)(-1)) { + ret = 0; + wait_event(event_wait_queue, p_event->signalled); + } else { + ret = wait_event_timeout(event_wait_queue, p_event->signalled, timeout); + if (!ret) + return IMG_ERROR_TIMEOUT; + } + } else { + if (timeout == (unsigned int)(-1)) { + ret = wait_event_interruptible(event_wait_queue, p_event->signalled); + } else { + ret = wait_event_interruptible_timeout(event_wait_queue, + p_event->signalled, timeout); + if (!ret) + return IMG_ERROR_TIMEOUT; + } + } + + /* If there are signals pending... */ + if (ret == -ERESTARTSYS) + return IMG_ERROR_INTERRUPTED; + + /* If there was no signal...*/ + IMG_DBG_ASSERT(p_event->signalled); + + /* Clear signal pending...*/ + p_event->signalled = FALSE; + + return IMG_SUCCESS; +} + +static int topazdd_wait_on_sync(struct img_comm_socket *sock, unsigned int wb_val) +{ + unsigned int retries = 0; + + if (!sock) + return IMG_ERROR_INVALID_CONTEXT; + + while (wait_event_obj(sock->event, TRUE, WAIT_FOR_SYNC_TIMEOUT) != IMG_SUCCESS) { + if (retries == WAIT_FOR_SYNC_RETRIES) { + /* + * We shouldn't wait any longer than that! + * If the hardware locked up, we will get stuck otherwise. + */ + pr_err("TIMEOUT: %s timed out waiting for writeback 0x%08x.\n", + __func__, sock->sync_wb_val); + return IMG_ERROR_TIMEOUT; + } + + msleep(WAIT_FOR_SYNC_TIMEOUT); + retries++; + continue; + } + + return IMG_SUCCESS; +} + +int topazdd_send_msg_with_sync(void *dd_str_ctx, enum mtx_cmd_id cmd_id, + unsigned int data, + struct vidio_ddbufinfo *cmd_data_buf) +{ + struct img_comm_socket *sock; + unsigned int wb_val = 0; + + if (!dd_str_ctx) + return IMG_ERROR_INVALID_CONTEXT; + + sock = (struct img_comm_socket *)dd_str_ctx; + + mutex_lock_nested(sock->sync_wb_mutex, SUBCLASS_TOPAZDD); + topazdd_send_msg(dd_str_ctx, cmd_id, data, cmd_data_buf, &wb_val); + sock->sync_waiting = TRUE; + sock->sync_wb_val = wb_val; + mutex_unlock((struct mutex *)sock->sync_wb_mutex); + + return topazdd_wait_on_sync(sock, wb_val); +} + +static void stream_worker(void *work) +{ + struct img_comm_socket *sock = NULL; + struct img_writeback_msg msg; + struct event *p_event; + + work = get_work_buff(work, FALSE); + sock = container_of(work, struct img_comm_socket, work); + + while (wbfifo_get(sock, &msg)) { + if (F_DECODE(msg.cmd_word, MTX_MSG_MESSAGE_ID) == MTX_MESSAGE_ACK) + sock->ack_recv++; + + mutex_lock_nested(sock->sync_wb_mutex, SUBCLASS_TOPAZDD); + if (sock->sync_waiting && msg.writeback_val == sock->sync_wb_val) { + sock->sync_waiting = FALSE; + mutex_unlock((struct mutex *)sock->sync_wb_mutex); + /* signal the waiting sync event */ + p_event = (struct event *)sock->event; + + IMG_DBG_ASSERT(sock->event); + if (!sock->event) + return; + + p_event->signalled = TRUE; + wake_up(&event_wait_queue); + return; + } + mutex_unlock((struct mutex *)sock->sync_wb_mutex); + + if (sock->cb) + sock->cb(&msg, sock->str_ctx); + } +} + +int topazdd_create_stream_context(struct topaz_dev_ctx *ctx, enum img_codec codec, + enc_cb cb, void *cb_priv, + void **dd_str_ctx, struct vidio_ddbufinfo **wb_data_info) +{ + struct img_comm_socket *p_sock; + struct event *p_event; + + p_sock = kmalloc(sizeof(*p_sock), GFP_KERNEL); + IMG_DBG_ASSERT(p_sock); + if (!p_sock) + return IMG_ERROR_OUT_OF_MEMORY; + + p_sock->sync_wb_mutex = kzalloc(sizeof(*p_sock->sync_wb_mutex), GFP_KERNEL); + if (!p_sock->sync_wb_mutex) { + kfree(p_sock); + return IMG_ERROR_OUT_OF_MEMORY; + } + mutex_init(p_sock->sync_wb_mutex); + + /* Allocate a Sync structure...*/ + p_event = kmalloc(sizeof(struct event *), GFP_KERNEL); + IMG_DBG_ASSERT(p_event); + if (!p_event) + return IMG_ERROR_OUT_OF_MEMORY; + + memset(p_event, 0, sizeof(struct event)); + + p_sock->event = (void *)p_event; + + if (!p_sock->event) { + mutex_destroy(p_sock->sync_wb_mutex); + kfree(p_sock->sync_wb_mutex); + p_sock->sync_wb_mutex = NULL; + kfree(p_sock); + return IMG_ERROR_OUT_OF_MEMORY; + } + + p_sock->low_cmd_cnt = 0xa5a5a5a5 % MAX_TOPAZ_CMD_COUNT; + p_sock->high_cmd_cnt = 0; + p_sock->msgs_sent = 0; + p_sock->ack_recv = 0; + p_sock->codec = codec; + p_sock->ctx = ctx; + p_sock->cb = cb; + p_sock->str_ctx = (struct topaz_stream_context *)cb_priv; + + init_work(&p_sock->work, stream_worker, HWA_ENCODER); + if (!p_sock->work) { + mutex_destroy(p_sock->sync_wb_mutex); + kfree(p_sock->sync_wb_mutex); + p_sock->sync_wb_mutex = NULL; + kfree(p_sock); + return IMG_ERROR_OUT_OF_MEMORY; + } + + wbfifo_clear(p_sock); + + *wb_data_info = g_aps_wb_data_info; + + *dd_str_ctx = (void *)p_sock; + +#ifdef DEBUG_ENCODER_DRIVE + pr_info("topazdd context created with codec %d\n", codec); +#endif + + return IMG_SUCCESS; +} + +static int topaz_upload_firmware(struct topaz_dev_ctx *ctx, enum img_codec codec) +{ +#ifdef DEBUG_ENCODER_DRIVE + pr_info("Loading firmware.\n"); +#endif + /* Upload FW */ + /* load and start MTX cores */ + ctx->fw_ctx.load_method = (enum mtx_load_method)g_load_method; + + topaz_setup_firmware(ctx, &ctx->fw_ctx, ctx->fw_ctx.load_method, + codec, topazdd_get_num_pipes(ctx)); + + if (!ctx->fw_ctx.initialized) { + pr_err("\nERROR: Firmware cannot be loaded!\n"); + return IMG_ERROR_UNDEFINED; + } + + comm_load_bias(ctx, ctx->fw_ctx.supported_codecs); + /* initialise read offset of firmware output fifo */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOMTX << 2), 0); + + ctx->fw_uploaded = codec; + +#ifdef DEBUG_ENCODER_DRIVE + pr_info("firmware uploaded!\n"); +#endif + return IMG_SUCCESS; +} + +int topazdd_setup_stream_ctx(void *dd_str_ctx, unsigned short height, + unsigned short width, unsigned char *ctx_num, + unsigned int *used_sock) +{ + unsigned char idx; + struct img_fw_context *fw_ctx; + struct img_comm_socket *sock; + int res = IMG_ERROR_UNDEFINED; + unsigned int codec_mask = 0; + + sock = (struct img_comm_socket *)dd_str_ctx; + + comm_lock(sock->ctx, COMM_LOCK_BOTH); + + fw_ctx = &sock->ctx->fw_ctx; + + switch (sock->codec) { + case IMG_CODEC_JPEG: + codec_mask = CODEC_MASK_JPEG; + break; + case IMG_CODEC_H264_NO_RC: + case IMG_CODEC_H264_VBR: + case IMG_CODEC_H264_CBR: + case IMG_CODEC_H264_VCM: + case IMG_CODEC_H264_ERC: + codec_mask = CODEC_MASK_H264; + break; + case IMG_CODEC_H263_NO_RC: + case IMG_CODEC_H263_VBR: + case IMG_CODEC_H263_CBR: + case IMG_CODEC_H263_ERC: + codec_mask = CODEC_MASK_H263; + break; + case IMG_CODEC_MPEG4_NO_RC: + case IMG_CODEC_MPEG4_VBR: + case IMG_CODEC_MPEG4_CBR: + case IMG_CODEC_MPEG4_ERC: + codec_mask = CODEC_MASK_MPEG4; + break; + case IMG_CODEC_MPEG2_NO_RC: + case IMG_CODEC_MPEG2_VBR: + case IMG_CODEC_MPEG2_CBR: + case IMG_CODEC_MPEG2_ERC: + codec_mask = CODEC_MASK_MPEG2; + break; + + case IMG_CODEC_H264MVC_NO_RC: + case IMG_CODEC_H264MVC_VBR: + case IMG_CODEC_H264MVC_CBR: + case IMG_CODEC_H264MVC_ERC: + codec_mask = CODEC_MASK_H264MVC; + break; + default: + IMG_DBG_ASSERT("Impossible use case!\n" == NULL); + break; + } + /* Only do the following checks if some other firmware is loaded */ + if (sock->ctx->fw_uploaded != IMG_CODEC_NONE && + (sock->ctx->fw_uploaded != sock->codec || /* Different firmware is uploaded */ + /* We currently only support one JPEG context to be encoded at the same time */ + (sock->ctx->fw_uploaded == IMG_CODEC_JPEG && sock->ctx->used_socks))) { + if (!(fw_ctx->supported_codecs & codec_mask)) { + comm_unlock(sock->ctx, COMM_LOCK_BOTH); + res = IMG_ERROR_UNDEFINED; + pr_err("\nERROR: Incompatible firmware context types!. Required codec: 0x%x Loaded FW : 0x%x\n", + codec_mask, fw_ctx->supported_codecs); + return res; + } + } + + if (fw_ctx->initialized && sock->ctx->used_socks >= fw_ctx->num_contexts) { + /* the firmware can't support any more contexts */ + comm_unlock(sock->ctx, COMM_LOCK_BOTH); + pr_err("\nERROR: Firmware context limit reached!\n"); + return IMG_ERROR_UNDEFINED; + } + + /* Search for an Available socket. */ + IMG_DBG_ASSERT(TOPAZHP_MAX_POSSIBLE_STREAMS < (1 << 8)); + for (idx = 0; idx < TOPAZHP_MAX_POSSIBLE_STREAMS; idx++) { + if (!(sock->ctx->socks[idx])) { + unsigned int index = idx; + + sock->id = idx; + *ctx_num = idx; + *used_sock = index; + sock->ctx->socks[idx] = sock; + sock->ctx->used_socks++; + break; + } + } + + if (idx == TOPAZHP_MAX_POSSIBLE_STREAMS) { + comm_unlock(sock->ctx, COMM_LOCK_BOTH); + return IMG_ERROR_INVALID_SIZE; + } + + if (sock->codec == IMG_CODEC_JPEG) { + topaz_timeout_retries = TOPAZ_TIMEOUT_JPEG; + } else { + unsigned int mbs_per_pic = (height * width) / 256; + + if (topaz_timeout_retries < (mbs_per_pic + 10) * 100) + topaz_timeout_retries = (mbs_per_pic + 10) * 100; + } + + if (sock->ctx->fw_uploaded == IMG_CODEC_NONE) { +#ifdef DEBUG_ENCODER_DRIVE + pr_info("Loading a different firmware.\n"); +#endif + res = topaz_upload_firmware(sock->ctx, (enum img_codec)sock->codec); + if (!res) { + comm_unlock(sock->ctx, COMM_LOCK_BOTH); + res = IMG_ERROR_UNDEFINED; + pr_err("\nERROR: Firmware cannot be loaded!\n"); + return res; + } + } + + res = IMG_SUCCESS; + + comm_unlock(sock->ctx, COMM_LOCK_BOTH); + + return res; +} + +void topazdd_destroy_stream_ctx(void *dd_str_ctx) +{ + unsigned int idx; + struct img_comm_socket *sock; + + sock = (struct img_comm_socket *)dd_str_ctx; + + WARN_ON((!sock)); + if (!sock) { + pr_err("topazdd_destroy_sock: invalid sock\n"); + return; + } + + flush_work(sock->work); + + mutex_lock_nested(sock->sync_wb_mutex, SUBCLASS_TOPAZDD); + comm_lock(sock->ctx, COMM_LOCK_BOTH); + for (idx = 0; idx < TOPAZHP_MAX_POSSIBLE_STREAMS; idx++) { + if (sock->ctx->socks[idx] == sock) { + sock->ctx->used_socks--; + break; + } + } + +#ifdef DEBUG_ENCODER_DRIVE + pr_info("topazdd sock context closed\n"); +#endif + + /* Flush the MMU table cache (so it we can't accidentally access + * the freed device memory due to cache/table mismatch.) + */ + topaz_core_mmu_flush_cache(); + + /* + * if nIndex == TOPAZHP_MAX_POSSIBLE_STREAMS then OpenSocket succeeded + * and SetupSocket failed (maybe incompatible firmware) + */ + if (idx != TOPAZHP_MAX_POSSIBLE_STREAMS) { + /* + * Abort the stream first. + * This function can be called as a result of abnormal process + * exit, and since the hardware might be encoding some frame it + * means that the hardware still needs the context resources + * (buffers mapped to the hardware, etc), so we need to make + * sure that hardware encoding is aborted first before releasing + * the resources. + * This is important if you're doing several encodes + * simultaneously because releasing the resources too early will + * cause a page-fault that will halt all simultaneous encodes + * not just the one that caused the page-fault. + */ + struct mtx_tomtx_msg msg; + unsigned int wb_val = 0; + + wbfifo_clear(sock); + + msg.cmd_id = (enum mtx_cmd_id)(MTX_CMDID_ABORT | MTX_CMDID_PRIORITY | + MTX_CMDID_WB_INTERRUPT); + msg.data = 0; + msg.command_data_buf = NULL; + comm_send(sock, &msg, &wb_val); + sock->sync_waiting = TRUE; + sock->sync_wb_val = wb_val; + mutex_unlock((struct mutex *)sock->sync_wb_mutex); + + topazdd_wait_on_sync(sock, wb_val); + /* + * Set it to NULL here -not any time sooner-, we need it in case + * we had to abort the stream. + */ + sock->ctx->socks[idx] = NULL; + } + + comm_unlock(sock->ctx, COMM_LOCK_BOTH); + kfree(sock->event); + mutex_destroy(sock->sync_wb_mutex); + kfree(sock->sync_wb_mutex); + sock->sync_wb_mutex = NULL; + kfree(sock->work); + kfree(sock); +} + +/* + * topazdd_int_clear + */ +static void topazdd_int_clear(struct topaz_dev_ctx *ctx, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(ctx->lock, flags); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_INT_CLEAR, mask); + + spin_unlock_irqrestore(ctx->lock, (unsigned long)flags); +} + +unsigned char topazdd_get_pipe_usage(unsigned char pipe) +{ + IMG_DBG_ASSERT(pipe < TOPAZHP_MAX_NUM_PIPES); + if (pipe >= TOPAZHP_MAX_NUM_PIPES) + return 0; + + return g_pipe_usage[pipe]; +} + +void topazdd_set_pipe_usage(unsigned char pipe, unsigned char val) +{ + IMG_DBG_ASSERT(pipe < TOPAZHP_MAX_NUM_PIPES); + if (pipe < TOPAZHP_MAX_NUM_PIPES) + g_pipe_usage[pipe] = val; +} + +static unsigned int comm_get_consumer(struct topaz_dev_ctx *ctx) +{ + unsigned int reg; + + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOMTX << 2)); + + return F_DECODE(reg, WB_CONSUMER); +} + +static void comm_set_consumer(struct topaz_dev_ctx *ctx, unsigned int consumer) +{ + unsigned int reg; + + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOMTX << 2)); + + reg = F_INSERT(reg, consumer, WB_CONSUMER); + + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOMTX << 2), reg); +} + +static unsigned int comm_get_producer(struct topaz_dev_ctx *ctx) +{ + unsigned int reg; + + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOHOST << 2)); + + return F_DECODE(reg, WB_PRODUCER); +} + +static void comm_set_producer(struct topaz_dev_ctx *ctx, unsigned int producer) +{ + unsigned int reg; + + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOHOST << 2)); + + reg = F_INSERT(reg, producer, WB_PRODUCER); + + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_FIRMWARE_REG_1 + + (MTX_SCRATCHREG_TOHOST << 2), reg); +} + +static int topazdd_init_comms(struct topaz_dev_ctx *ctx, unsigned int mmu_flags) +{ + unsigned int num_cores; + unsigned int i; + unsigned int reg; + + num_cores = topazdd_get_num_pipes(ctx); + + for (i = 0; i < num_cores; i++) { + unsigned int offset = REG_TOPAZHP_CORE_0 + (i * 4); + + ctx->hp_core_reg_addr[i] = (void *)topaz_mem_space[offset].cpu_addr; + + offset = REG_TOPAZHP_VLC_CORE_0 + (i * 4); + ctx->vlc_reg_addr[i] = (void *)topaz_mem_space[offset].cpu_addr; + } + + if (topaz_mmu_device_create(&ctx->topaz_mmu_ctx, mmu_flags) != IMG_SUCCESS) { + pr_err("\nERROR: Could not initialize MMU with selected parameters!\n"); + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* Start up MMU support for each core (if MMU is switched on) */ + reg = (F_ENCODE(1, TOPAZHP_TOP_CR_IMG_TOPAZ_MTX_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_TOP_CR_IMG_TOPAZ_CORE_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_TOP_CR_IMG_TOPAZ_IO_SOFT_RESET)); + + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_SRST, reg); + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_SRST, 0x0); + + for (i = 0; i < num_cores; i++) { + unsigned int reset_bits = F_ENCODE(1, TOPAZHP_CR_TOPAZHP_IPE_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_SPE_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_PC_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_H264COMP_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_JMCOMP_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_PREFETCH_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_VLC_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_LTRITC_SOFT_RESET) | + F_ENCODE(1, TOPAZHP_CR_TOPAZHP_DB_SOFT_RESET); + +#ifdef TOPAZHP // TODO: strangely, this doesn't seem defined in the build... but we ARE topazhp... + reset_bits |= F_ENCODE(1, MVEA_CR_IMG_MVEA_SPE_SOFT_RESET(1)) | + F_ENCODE(1, MVEA_CR_IMG_MVEA_IPE_SOFT_RESET(1)); +#endif + + VXE_WR_REG32(ctx->hp_core_reg_addr[i], TOPAZHP_CR_TOPAZHP_SRST, reset_bits); + + VXE_WR_REG32(ctx->hp_core_reg_addr[i], TOPAZHP_CR_TOPAZHP_SRST, 0); + } + + ctx->topaz_mmu_ctx.ptd_phys_addr = ctx->ptd; + topaz_core_mmu_hw_setup(&ctx->topaz_mmu_ctx, ctx->multi_core_mem_addr); + + ctx->fw_uploaded = IMG_CODEC_NONE; + + g_core_rev = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_TOPAZHP_CORE_REV); + g_core_rev &= + (MASK_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV | MASK_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV | + MASK_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV); + g_core_des1 = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_TOPAZHP_CORE_DES1); + + ctx->comm_tx_mutex = kzalloc(sizeof(*ctx->comm_tx_mutex), GFP_KERNEL); + if (!(ctx->comm_tx_mutex)) + return IMG_ERROR_OUT_OF_MEMORY; + + mutex_init(ctx->comm_tx_mutex); + + ctx->comm_rx_mutex = kzalloc(sizeof(*ctx->comm_rx_mutex), GFP_KERNEL); + if (!ctx->comm_rx_mutex) { + mutex_destroy(ctx->comm_tx_mutex); + kfree(ctx->comm_tx_mutex); + ctx->comm_tx_mutex = NULL; + pr_err("Memory allocation failed for mutex\n"); + return IMG_ERROR_OUT_OF_MEMORY; + } + mutex_init(ctx->comm_rx_mutex); + + g_aps_wb_data_info = kmalloc(sizeof(*g_aps_wb_data_info) * WB_FIFO_SIZE, GFP_KERNEL); + if (!g_aps_wb_data_info) { + mutex_destroy(ctx->comm_rx_mutex); + kfree(ctx->comm_rx_mutex); + ctx->comm_rx_mutex = NULL; + + mutex_destroy(ctx->comm_tx_mutex); + kfree(ctx->comm_tx_mutex); + ctx->comm_tx_mutex = NULL; + return IMG_ERROR_OUT_OF_MEMORY; + } + + /* Allocate WB buffers */ + for (i = 0; i < WB_FIFO_SIZE; i++) { + struct vidio_ddbufinfo *mem_info = &g_aps_wb_data_info[i]; + + if (topaz_mmu_alloc(ctx->topaz_mmu_ctx.mmu_context_handle, + ctx->vxe_arg, MMU_GENERAL_HEAP_ID, 1, + (enum sys_emem_attrib)(SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE), + COMM_WB_DATA_BUF_SIZE, 64, mem_info)) { + pr_err("mmu_alloc failed!\n"); + kfree(g_aps_wb_data_info); + return IMG_ERROR_OUT_OF_MEMORY; + } + } + + /* Initialise the COMM registers */ + comm_set_producer(ctx, 0); + + /* Must reset the Consumer register too, + * otherwise the COMM stack may be initialised incorrectly + */ + comm_set_consumer(ctx, 0); + + for (i = 0; i < TOPAZHP_MAX_POSSIBLE_STREAMS; i++) + ctx->socks[i] = NULL; + + ctx->used_socks = 0; + ctx->initialized = TRUE; + + return 0; +} + +static void topazdd_deinit_comms(struct topaz_dev_ctx *ctx) +{ + unsigned int idx; + struct img_fw_context *fw_ctx; + + fw_ctx = &ctx->fw_ctx; + + if (fw_ctx && fw_ctx->initialized) { + /* Stop the MTX */ + mtx_stop(fw_ctx); + mtx_wait_for_completion(fw_ctx); + } + + if (g_aps_wb_data_info) { + for (idx = 0; idx < WB_FIFO_SIZE; idx++) { + struct vidio_ddbufinfo *mem_info = &g_aps_wb_data_info[idx]; + + topaz_mmu_free(ctx->vxe_arg, mem_info); + } + kfree(g_aps_wb_data_info); + } + + /* Close all of the opened sockets */ + for (idx = 0; idx < TOPAZHP_MAX_POSSIBLE_STREAMS; idx++) { + if (ctx->socks[idx]) + topazdd_destroy_stream_ctx(ctx->socks[idx]); + } + + mutex_destroy(ctx->comm_tx_mutex); + kfree(ctx->comm_tx_mutex); + ctx->comm_tx_mutex = NULL; + + mutex_destroy(ctx->comm_rx_mutex); + kfree(ctx->comm_rx_mutex); + ctx->comm_rx_mutex = NULL; + + if (fw_ctx && fw_ctx->initialized) + mtx_deinitialize(fw_ctx); + + topaz_mmu_device_destroy(&ctx->topaz_mmu_ctx); + + ctx->fw_uploaded = IMG_CODEC_NONE; + ctx->initialized = FALSE; +} + +static void setup_topaz_mem(unsigned long long reg_base, unsigned int reg_size) +{ + unsigned int idx; + + /* set up the kernel virtual address for mem space access */ + for (idx = 0; idx < topaz_target_config.num_mem_spaces; idx++) { + unsigned long long offset = topaz_target_config.mem_spaces[idx].reg.addr; + + topaz_target_config.mem_spaces[idx].cpu_addr = reg_base + offset; + } +} + +/* + * topazdd_init + */ +int topazdd_init(unsigned long long reg_base, unsigned int reg_size, unsigned int mmu_flags, + void *vxe_arg, unsigned int ptd, void **data) +{ + struct topaz_dev_ctx *ctx; + int ret; + spinlock_t **lock; /* spinlock */ + + setup_topaz_mem(reg_base, reg_size); + + /* Allocate device structure...*/ + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + IMG_DBG_ASSERT(ctx); + if (!ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + memset(ctx, 0, sizeof(*ctx)); + + lock = (spinlock_t **)&ctx->lock; + *lock = kzalloc(sizeof(spinlock_t), GFP_KERNEL); + + if (!(*lock)) { + pr_err("Memory allocation failed for spin-lock\n"); + kfree(ctx); + return IMG_ERROR_OUT_OF_MEMORY; + } + spin_lock_init(*lock); + g_lock = ctx->lock; + + *data = ctx; + ctx->initialized = FALSE; + + ctx->multi_core_mem_addr = (void *)topaz_mem_space[REG_TOPAZHP_MULTICORE].cpu_addr; + + if (!ctx->multi_core_mem_addr) { + kfree(&ctx->lock); + kfree(ctx); + return IMG_ERROR_DEVICE_NOT_FOUND; + } + + /* Now enabled interrupts */ + topazdd_int_enable(ctx, (MASK_TOPAZHP_TOP_CR_HOST_INTEN_MTX | + MASK_TOPAZHP_TOP_CR_HOST_TOPAZHP_MAS_INTEN | + MASK_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT | + MASK_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT_B)); + + ctx->vxe_arg = vxe_arg; + ctx->ptd = ptd; + + ret = topazdd_init_comms(ctx, mmu_flags); + if (ret) { + topazdd_int_disable(ctx, ~0); + kfree(&ctx->lock); + kfree(ctx); + return ret; + } + + comm_lock(ctx, COMM_LOCK_BOTH); + ret = topaz_upload_firmware(ctx, IMG_CODEC_H264_NO_RC); + comm_unlock(ctx, COMM_LOCK_BOTH); + + if (ret) { + topazdd_deinit_comms(ctx); + topazdd_int_disable(ctx, ~0); + kfree(&ctx->lock); + kfree(ctx); + return ret; + } + + /* Device now initailised...*/ + ctx->initialized = TRUE; + + /* Return success...*/ + return IMG_SUCCESS; +} + +/* + * topazdd_deinit + */ +void topazdd_deinit(void *data) +{ + struct topaz_dev_ctx *ctx = data; + unsigned int reg; + + /* If the interrupt was defined then it is also safe to clear interrupts + * and reset the core.... + */ + if (ctx->initialized) { + topazdd_deinit_comms(ctx); + + /* Disable interrupts...*/ + topazdd_int_disable(ctx, ~0); + + /* disable interrupts on Topaz core */ + reg = + VXE_RD_REG32(ctx->multi_core_mem_addr, + TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB); + + reg &= ~MASK_TOPAZHP_TOP_CR_HOST_INTEN_MTX; + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB, reg); + + /* clear interrupt - just in case */ + VXE_WR_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_INT_CLEAR, + MASK_TOPAZHP_TOP_CR_INTCLR_MTX); + + g_lock = NULL; + kfree(&ctx->lock); + } + + kfree(data); +} + +static int comm_dispatch_in_msg(struct topaz_dev_ctx *ctx) +{ + unsigned int hw_fifo_producer; + unsigned int hw_fifo_consumer; + + hw_fifo_consumer = comm_get_consumer(ctx); + hw_fifo_producer = comm_get_producer(ctx); + + while (hw_fifo_consumer != hw_fifo_producer) { + struct img_writeback_msg *wb_msg; + unsigned char conn_id; + struct vidio_ddbufinfo *mem_info = &g_aps_wb_data_info[hw_fifo_consumer]; + enum mtx_cmd_id cmd_id; + + /* Update corresponding memory region */ + topaz_update_host_mem(ctx->vxe_arg, mem_info); + wb_msg = (struct img_writeback_msg *)(mem_info->cpu_virt); + + /* Copy to the corresponding SW fifo */ + conn_id = F_DECODE(wb_msg->cmd_word, MTX_MSG_CORE); + + /* Find corresponding Buffer Addr */ + cmd_id = (enum mtx_cmd_id)F_DECODE(wb_msg->cmd_word, MTX_MSG_MESSAGE_ID); +#ifdef DEBUG_ENCODER_DRIVER + if ((unsigned int)cmd_id == (unsigned int)MTX_MESSAGE_ACK) { + pr_debug("MSG_RX[%d]: 0x%03X %s (ACK) cmd_word: %#08x data: %#08x extra_data: %#08x writeback_val: %#08x\n", + F_DECODE(wb_msg->cmd_word, MTX_MSG_CORE), + hw_fifo_producer & 0x1f, + command_string[wb_msg->cmd_word & 0x1f], + wb_msg->cmd_word, wb_msg->data, + wb_msg->extra_data, wb_msg->writeback_val); + } else { +#ifdef ENABLE_PROFILING + struct timespec64 time; + + ktime_get_real_ts64(&time); + ctx->socks[conn_id]->fw_lat.end_time = + timespec64_to_ns((const struct timespec64 *)&time); + pr_err("fw encode time is %llu us for msg_id x%0x\n", + div_s64(ctx->socks[conn_id]->fw_lat.end_time - + ctx->socks[conn_id]->fw_lat.start_time, 1000), + wb_msg->writeback_val); +#endif + pr_debug("MSG_RX[%d]: 0x%03X CODED_BUFFER cmd_word: %#08x coded_package_consumed: %d\n", + F_DECODE(wb_msg->cmd_word, MTX_MSG_CORE), + hw_fifo_producer & 0x1f, + wb_msg->cmd_word, + wb_msg->coded_package_consumed_idx); + } +#endif + + /* If corresponding socket still exists, call the callback */ + if (ctx->socks[conn_id]) { + wbfifo_add(ctx->socks[conn_id], wb_msg); + schedule_work(ctx->socks[conn_id]->work); + } + + /* Activate corresponding FIFO + * proceed to the next one + */ + hw_fifo_consumer++; + + if (hw_fifo_consumer == WB_FIFO_SIZE) + hw_fifo_consumer = 0; + + comm_set_consumer(ctx, hw_fifo_consumer); + + /* + * We need to update the producer because we might have received a new + * message meanwhile. This new message won't trigger an interrupt and + * consequently will be lost till another message arrives + */ + hw_fifo_producer = comm_get_producer(ctx); + } + + return IMG_SUCCESS; +} + +/* + * topazdd_threaded_isr + */ +unsigned char topazdd_threaded_isr(void *inst_data) +{ + struct topaz_dev_ctx *ctx = *(struct topaz_dev_ctx **)inst_data; + + /* If interrupts not defined then...*/ + if (!ctx || !ctx->initialized) + return FALSE; + + /* Now dispatch the messages */ + comm_dispatch_in_msg(ctx); + + /* Signal this interrupt has been handled...*/ + return TRUE; +} + +/* + * topazdd_isr + */ +irqreturn_t topazdd_isr(void *inst_data) +{ + unsigned int reg; + unsigned int mmu_fault_mask = MASK_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT; + + struct topaz_dev_ctx *ctx = *(struct topaz_dev_ctx **)inst_data; + + /* More requesters with topaz hp */ + mmu_fault_mask |= MASK_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT_B; + + /* If interrupts not defined then...*/ + if (!ctx || !ctx->initialized) + return IRQ_NONE; + + /* read device interrupt status */ + reg = VXE_RD_REG32(ctx->multi_core_mem_addr, TOPAZHP_TOP_CR_MULTICORE_INT_STAT); + + /* if interrupts enabled and fired...*/ + if (((reg & MASK_TOPAZHP_TOP_CR_INT_STAT_MTX) == (MASK_TOPAZHP_TOP_CR_INT_STAT_MTX))) { + /* Clear interrupt source...*/ + topazdd_int_clear(ctx, MASK_TOPAZHP_TOP_CR_INTCLR_MTX); + + /* Signal this interrupt has been handled...*/ + return IRQ_WAKE_THREAD; + } + + /* if page fault ever happenned */ + if (reg & (mmu_fault_mask)) { + static unsigned char dump_once = TRUE; + + if (dump_once) { + VXE_WR_REG32(ctx->multi_core_mem_addr, + TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB, 0); + + dump_once = FALSE; /* only on first page fault for readability */ + } + + /* Clear interrupt source...*/ + topazdd_int_clear(ctx, mmu_fault_mask); + + /* IT served, we might never reach that point on kernel crashes */ + return IRQ_HANDLED; + } + + /* Signal not this device...*/ + return IRQ_NONE; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topaz_device.h b/drivers/media/platform/vxe-vxd/encoder/topaz_device.h --- a/drivers/media/platform/vxe-vxd/encoder/topaz_device.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topaz_device.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * topaz driver data strcutures + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#if !defined(__TOPAZ_DEVICE_H__) +#define __TOPAZ_DEVICE_H__ + +#include + +#include "fw_headers/topazscfwif.h" +#include "fw_headers/mtx_fwif.h" +#include "topazmmu.h" +#include "vid_buf.h" +#include "topaz_api.h" + +# define CODEC_MASK_JPEG 0x0001 +# define CODEC_MASK_MPEG2 0x0002 +# define CODEC_MASK_MPEG4 0x0004 +# define CODEC_MASK_H263 0x0008 +# define CODEC_MASK_H264 0x0010 +# define CODEC_MASK_H264MVC 0x0020 +# define CODEC_MASK_VP8 0x0040 +# define CODEC_MASK_H265 0x0080 +# define CODEC_MASK_FAKE 0x007F + +struct img_comm_socket; + +/*! + **************************************************************************** + Event object structure + **************************************************************************** + */ +struct event { + unsigned char signalled; +}; + +/* prototype for callback for incoming message */ +typedef void (*enc_cb)(struct img_writeback_msg *msg, void *priv); + +#ifdef ENABLE_PROFILING +struct enc_fw_latency { + unsigned int start_time; + unsigned int end_time; +}; +#endif + +struct mtx_tohost_msg { + enum mtx_message_id cmd_id; + unsigned int input_cmd_word; + unsigned char coded_pkg_idx; + unsigned int wb_val; + unsigned int data; + struct vidio_ddbufinfo *command_data_buf; +}; + +struct mtx_tomtx_msg { + enum mtx_cmd_id cmd_id; + unsigned int data; + struct vidio_ddbufinfo *command_data_buf; +}; + +/* + * This structure contains the device context. + */ +struct topaz_dev_ctx { + /* Parent context, needed to pass to mmu_alloc */ + void *vxe_arg; + + /* KM addresses for mem spaces */ + void *multi_core_mem_addr; + void *hp_core_reg_addr[TOPAZHP_MAX_NUM_PIPES]; + void *vlc_reg_addr[TOPAZHP_MAX_NUM_PIPES]; + + unsigned char initialized; /*!< Indicates that the device driver has been initialised */ + + unsigned int used_socks; + struct img_comm_socket *socks[TOPAZHP_MAX_POSSIBLE_STREAMS]; + + unsigned int fw_uploaded; + struct img_fw_context fw_ctx; + + void *lock; /* basic device level spinlock */ + struct mutex *comm_tx_mutex; + struct mutex *comm_rx_mutex; + + unsigned int ptd; + struct topaz_mmu_context topaz_mmu_ctx; +}; + +#define COMM_INCOMING_FIFO_SIZE (WB_FIFO_SIZE * 2) +struct img_comm_socket { + unsigned char id; + unsigned int low_cmd_cnt; /* count of low-priority commands sent to TOPAZ */ + unsigned int high_cmd_cnt; /* count of high-priority commands sent to TOPAZ */ + unsigned int last_sync; /* Last sync value sent */ + struct img_writeback_msg in_fifo[COMM_INCOMING_FIFO_SIZE]; + unsigned int in_fifo_consumer; + unsigned int in_fifo_producer; + void *work; + + enc_cb cb; /* User-provided callback function */ + struct topaz_stream_context *str_ctx; /* User-provided callback data */ + + void *event; + unsigned char sync_waiting; + unsigned int sync_wb_val; + struct mutex *sync_wb_mutex; + + unsigned int msgs_sent; + unsigned int ack_recv; + unsigned char is_serialized; + + unsigned int codec; + + struct topaz_dev_ctx *ctx; +#ifdef ENABLE_PROFILING + struct enc_fw_latency fw_lat; +#endif +}; + +unsigned char topazdd_threaded_isr(void *data); +irqreturn_t topazdd_isr(void *data); + +int topazdd_init(unsigned long long reg_base, unsigned int reg_size, + unsigned int mmu_flags, + void *vxe_arg, unsigned int ptd, void **data); +void topazdd_deinit(void *data); +unsigned int topazdd_get_num_pipes(struct topaz_dev_ctx *ctx); +unsigned int topazdd_get_core_rev(void); +unsigned int topazdd_get_core_des1(void); +unsigned char topazdd_is_idle(struct img_comm_socket *sock); + +int topazdd_upload_firmware(struct topaz_dev_ctx *ctx, enum img_codec codec); +int topazdd_create_stream_context(struct topaz_dev_ctx *ctx, enum img_codec codec, enc_cb cb, + void *cb_priv, void **dd_str_ctx, + struct vidio_ddbufinfo **wb_data_info); +void topazdd_destroy_stream_ctx(void *dd_str_ctx); +int topazdd_setup_stream_ctx(void *dd_str_ctx, unsigned short height, + unsigned short width, unsigned char *ctx_num, + unsigned int *used_sock); +int topazdd_send_msg(void *dd_str_ctx, enum mtx_cmd_id cmd_id, + unsigned int data, struct vidio_ddbufinfo *cmd_data_buf, + unsigned int *wb_val); +int topazdd_send_msg_with_sync(void *dd_str_ctx, enum mtx_cmd_id cmd_id, + unsigned int data, + struct vidio_ddbufinfo *cmd_data_buf); + +extern unsigned int mmu_control_val; + +#endif /* __TOPAZ_DEVICE_H__ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topazmmu.c b/drivers/media/platform/vxe-vxd/encoder/topazmmu.c --- a/drivers/media/platform/vxe-vxd/encoder/topazmmu.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topazmmu.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,741 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * topaz mmu function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "fw_headers/defs.h" +#include "img_errors.h" +#include "img_mem.h" +#include "img_mem_man.h" +#include "talmmu_api.h" +#include "topazmmu.h" +#include "vxe_public_regdefs.h" + +int use_extended_addressing; +unsigned int mmu_control_val; +unsigned char device_initialized = FALSE; + +/* + * These determine the sizes of the MMU heaps we are using. + * The tiled heap is set arbitrarily large at present. + */ +#define GENERALMMUHEAPLENGTH 0x40000000 + +/* + * This describes the heaps - the separate areas mapped by the MMU + * Currently we only use a single large heap as Topaz Core has no + * MMU specific memory features. + */ +struct talmmu_heap_info mmu_heap_info[HEAP_ID_NO_OF_HEAPS] = { + { MMU_GENERAL_HEAP_ID, TALMMU_HEAP_PERCONTEXT, TALMMU_HEAPFLAGS_NONE, "MEMSYSMEM", + 0x00400000, GENERALMMUHEAPLENGTH } +}; + +/* This describes the memory being mapped by the MMU */ +struct talmmu_devmem_info mmu_device_memory_info = { + /* ui32DeviceId */ + 1, + /* eMMUType */ + TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR, + /* eDevFlags */ + TALMMU_DEVFLAGS_NONE, + /* pszPageDirMemSpaceName */ + "MEMSYSMEM", + /* pszPageTableMemSpaceName */ + "MEMSYSMEM", + /* ui32PageSize */ + 4096, + /* ui32PageTableDirAlignment */ + 0 +}; + +/* + * mmu template is global. so we don't need to worry about maintaining device + * context + */ +void *mmu_template; + +/* + * Stream context is global. Can be modified in future to handle list of streams. + */ +struct mmu_str_context *str_ctx; + +/* + * Called once during initialization to initialize the MMU hardware, create + * the template and define the MMU heap. + * This is where talmmu initialization and template will be created. + * + * NOTE : We are not taking care of alignment here, need to be updated in + * mmu_device_memory_info. + */ +int topaz_mmu_device_create(struct topaz_mmu_context *mmu_context, unsigned int mmu_flags) +{ + void *topaz_multi_core_regid; + unsigned int hw_rev; + int result, i; + + use_extended_addressing = (mmu_flags & MMU_EXTENDED_ADDR_FLAG); + + /* Initialize TALMMU API and create a template */ + result = talmmu_init(); + IMG_DBG_ASSERT(result == 0); + + if (result != 0) { + pr_err("talmmu_init failed!\n"); + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + + /* + * We are reading the register and finding the mmu type, if needed this + * can be passed from the upper layers directly. + */ + + topaz_multi_core_regid = (void *)topaz_mem_space[REG_TOPAZHP_MULTICORE].cpu_addr; + + hw_rev = VXE_RD_REG32(topaz_multi_core_regid, TOPAZHP_TOP_CR_TOPAZHP_CORE_REV); + hw_rev &= + (MASK_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV | MASK_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV | + MASK_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV); + + if (use_extended_addressing) { + unsigned int reg_val; + + /* Versions 3.6 and above may be 32-bit, 36-bit or 40-bit */ + reg_val = VXE_RD_REG32(topaz_multi_core_regid, TOPAZHP_TOP_CR_MULTICORE_HW_CFG); + + switch (F_DECODE(reg_val, TOPAZHP_TOP_CR_EXTENDED_ADDR_RANGE)) { + case 0: + mmu_device_memory_info.mmu_type = TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR; + break; + case 4: + mmu_device_memory_info.mmu_type = TALMMU_MMUTYPE_4K_PAGES_36BIT_ADDR; + break; + case 8: + mmu_device_memory_info.mmu_type = TALMMU_MMUTYPE_4K_PAGES_40BIT_ADDR; + break; + default: + pr_err("Unsupported MMU mode requested\n"); + return IMG_ERROR_NOT_SUPPORTED; + } + } + + result = talmmu_devmem_template_create(&mmu_device_memory_info, &mmu_template); + IMG_DBG_ASSERT(result == 0); + if (result != 0) { + pr_err("talmmu_devmem_template_create failed!\n"); + talmmu_deinit(); + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + + /* Add heaps to the template */ + for (i = 0; i < HEAP_ID_NO_OF_HEAPS; i++) { + result = talmmu_devmem_heap_add(mmu_template, &mmu_heap_info[i]); + IMG_DBG_ASSERT(result == 0); + if (result != 0) { + pr_err("talmmu_devmem_heap_add failed!\n"); + talmmu_devmem_template_destroy(mmu_template); + talmmu_deinit(); + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + } + + /* Create a context from the template */ + /* (Template, User allocated user ID) */ + result = talmmu_devmem_ctx_create(mmu_template, 1, &mmu_context->mmu_context_handle); + IMG_DBG_ASSERT(result == 0); + if (result != 0) { + pr_err("talmmu_devmem_ctx_create failed!\n"); + talmmu_devmem_template_destroy(mmu_template); + talmmu_deinit(); + return IMG_ERROR_COULD_NOT_OBTAIN_RESOURCE; + } + + topaz_core_mmu_flush_cache(); + + /* Initialise stream list. */ + lst_init(&mmu_context->str_list); + + device_initialized = TRUE; + + return IMG_SUCCESS; +} + +/* + * This function is used to destroy the MMU device context. + * NOTE: Destroy device automatically destroys any streams and frees and + * memory allocated using MMU_StreamMalloc(). + */ +int topaz_mmu_device_destroy(struct topaz_mmu_context *mmu_context) +{ + unsigned int result = 0; + struct mmu_str_context *str_ctx; + + /* Destroy all streams associated with the device. */ + str_ctx = lst_first(&mmu_context->str_list); + while (str_ctx) { + /* remove stream to list. */ + lst_remove(&mmu_context->str_list, str_ctx); + topaz_mmu_stream_destroy(mmu_context, str_ctx); + + /* See if there are more streams. */ + str_ctx = lst_first(&mmu_context->str_list); + } + + /* Destroy the device context */ + result = talmmu_devmem_ctx_destroy(mmu_context->mmu_context_handle); + if (result != IMG_SUCCESS) + return result; + + /* Destroy the template. */ + return talmmu_devmem_template_destroy(mmu_template); +} + +/* + * This function is used to create and initialize the MMU stream context. + */ +int topaz_mmu_stream_create(struct topaz_mmu_context *mmu_context, unsigned int km_str_id, + void *vxe_enc_ctx_arg, void **mmu_str_ctx) +{ + struct mmu_str_context *str_ctx; + + /* Validate inputs. */ + if (!device_initialized) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Allocate a stream context structure */ + str_ctx = kzalloc(sizeof(*str_ctx), GFP_KERNEL); + if (!str_ctx) + return IMG_ERROR_OUT_OF_MEMORY; + + str_ctx->km_str_id = km_str_id; + str_ctx->int_reg_num = 32; + str_ctx->vxe_enc_context = (struct vxe_enc_ctx *)vxe_enc_ctx_arg; + + /* copy the mmu context created earlier */ + str_ctx->mmu_context_handle = mmu_context->mmu_context_handle; + + *mmu_str_ctx = str_ctx; + + /* Add stream to list. */ + lst_add(&mmu_context->str_list, str_ctx); + + return IMG_SUCCESS; +} + +/* + * This function is used to destroy the MMU stream context. + * NOTE: Destroy automatically frees and memory allocated using + * mmu_stream_malloc(). + */ +int topaz_mmu_stream_destroy(struct topaz_mmu_context *mmu_context, + struct mmu_str_context *str_ctx) +{ + /* Validate inputs. */ + if (!str_ctx) + return IMG_ERROR_INVALID_PARAMETERS; + + /* remove stream to list. */ + lst_remove(&mmu_context->str_list, str_ctx); + + kfree(str_ctx); + + return IMG_SUCCESS; +} + +static unsigned int set_attributes(enum sys_emem_attrib mem_attrib) +{ + unsigned int attrib = 0; + + if (mem_attrib & SYS_MEMATTRIB_CACHED) + attrib |= MEM_ATTR_CACHED; + + if (mem_attrib & SYS_MEMATTRIB_UNCACHED) + attrib |= MEM_ATTR_UNCACHED; + + if (mem_attrib & SYS_MEMATTRIB_WRITECOMBINE) + attrib |= MEM_ATTR_WRITECOMBINE; + + if (mem_attrib & SYS_MEMATTRIB_SECURE) + attrib |= MEM_ATTR_SECURE; + + return attrib; +} + +int topaz_mmu_alloc(void *mmu_context_handle, struct vxe_enc_ctx *vxe_enc_ctx_arg, + enum topaz_mmu_eheap_id heap_id, unsigned int mem_heap_id, + enum sys_emem_attrib mem_attrib, unsigned int size, unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info) +{ + int result = 0; + void *devmem_heap_hndl; + struct vxe_enc_ctx *ctx; + struct vxe_dev *vxe; + unsigned int flags = 0; + unsigned int attributes = 0; + + if (!mmu_context_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Round size up to next multiple of physical pages */ + if ((size % HOST_MMU_PAGE_SIZE) != 0) + size = ((size / HOST_MMU_PAGE_SIZE) + 1) * HOST_MMU_PAGE_SIZE; + + /* Allocate memory */ + ctx = vxe_enc_ctx_arg; + vxe = ctx->dev; + + attributes = set_attributes(mem_attrib); + + result = img_mem_alloc(vxe->dev, ctx->mem_ctx, mem_heap_id, + size, (enum mem_attr)attributes, (int *)&ddbuf_info->buff_id); + if (result != IMG_SUCCESS) + goto error_alloc; + + ddbuf_info->is_internal = 1; + + /* TODO need to check more on attributes from memmgr_km */ + if (mem_attrib & SYS_MEMATTRIB_SECURE) { + ddbuf_info->cpu_virt = NULL; + } else { + /* Map the buffer to CPU */ + result = img_mem_map_km(ctx->mem_ctx, ddbuf_info->buff_id); + if (result) { + dev_err(vxe->dev, "%s: failed to map buf to cpu!(%d)\n", + __func__, result); + goto error_get_heap_handle; + } + ddbuf_info->cpu_virt = img_mem_get_kptr(ctx->mem_ctx, ddbuf_info->buff_id); + } + + /* Get heap handle */ + result = talmmu_get_heap_handle(heap_id, mmu_context_handle, &devmem_heap_hndl); + if (result != IMG_SUCCESS) + goto error_get_heap_handle; + + /* Allocate device "virtual" memory. */ + result = talmmu_devmem_addr_alloc(mmu_context_handle, devmem_heap_hndl, + size, alignment, &ddbuf_info->hndl_memory); + if (result != IMG_SUCCESS) + goto error_mem_map_ext_mem; + + /* Get the device virtual address. */ + result = talmmu_get_dev_virt_addr(ddbuf_info->hndl_memory, &ddbuf_info->dev_virt); + if (result != IMG_SUCCESS) + goto error_get_dev_virt_addr; + + result = img_mmu_map(ctx->mmu_ctx, ctx->mem_ctx, ddbuf_info->buff_id, ddbuf_info->dev_virt, + flags); + if (result != IMG_SUCCESS) + goto error_map_dev; + + return IMG_SUCCESS; + +error_map_dev: +error_get_dev_virt_addr: + talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + ddbuf_info->hndl_memory = NULL; +error_mem_map_ext_mem: +error_get_heap_handle: + img_mem_free(ctx->mem_ctx, ddbuf_info->buff_id); +error_alloc: + return result; +} + +/* + * mmu_stream_malloc + */ +int topaz_mmu_stream_alloc(void *mmu_str_hndl, enum topaz_mmu_eheap_id heap_id, + unsigned int mem_heap_id, enum sys_emem_attrib mem_attrib, + unsigned int size, unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info) +{ + struct mmu_str_context *str_ctx; + + /* Validate inputs. */ + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + str_ctx = (struct mmu_str_context *)mmu_str_hndl; + + /* Check if device level heap. */ + switch (heap_id) { + case MMU_GENERAL_HEAP_ID: + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + ddbuf_info->kmstr_id = str_ctx->km_str_id; + + /* Allocate device memory. */ + return (topaz_mmu_alloc(str_ctx->mmu_context_handle, str_ctx->vxe_enc_context, + heap_id, mem_heap_id, mem_attrib, size, alignment, ddbuf_info)); +} + +/* + * mmu_stream_map_ext_sg + */ +int topaz_mmu_stream_map_ext_sg(void *mmu_str_hndl, enum topaz_mmu_eheap_id heap_id, + void *sgt, unsigned int size, unsigned int alignment, + enum sys_emem_attrib mem_attrib, void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info, unsigned int *buff_id) +{ + int result; + void *devmem_heap_hndl; + struct mmu_str_context *str_ctx; + struct vxe_enc_ctx *ctx; + struct vxe_dev *vxe; + + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + str_ctx = (struct mmu_str_context *)mmu_str_hndl; + + ctx = str_ctx->vxe_enc_context; + vxe = ctx->dev; + + /* Check if device level heap. */ + switch (heap_id) { + case MMU_GENERAL_HEAP_ID: + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + if (!str_ctx->mmu_context_handle) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Round size up to next multiple of physical pages */ + if ((size % HOST_MMU_PAGE_SIZE) != 0) + size = ((size / HOST_MMU_PAGE_SIZE) + 1) * HOST_MMU_PAGE_SIZE; + + result = img_mem_import(vxe->dev, ctx->mem_ctx, ddbuf_info->buf_size, + (enum mem_attr)set_attributes(mem_attrib), (int *)buff_id); + if (result != IMG_SUCCESS) + return result; + + if (mem_attrib & SYS_MEMATTRIB_SECURE) + ddbuf_info->cpu_virt = NULL; + + ddbuf_info->buff_id = *buff_id; + ddbuf_info->is_internal = 0; + + ddbuf_info->kmstr_id = str_ctx->km_str_id; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Ensure the address of the buffer is at least page aligned. */ + ddbuf_info->cpu_virt = cpu_linear_addr; + + /* Get heap handle */ + result = talmmu_get_heap_handle(heap_id, str_ctx->mmu_context_handle, &devmem_heap_hndl); + if (result != IMG_SUCCESS) + return result; + + /* Allocate device "virtual" memory. */ + result = talmmu_devmem_addr_alloc(str_ctx->mmu_context_handle, devmem_heap_hndl, + size, alignment, &ddbuf_info->hndl_memory); + if (result != IMG_SUCCESS) + return result; + + /* Get the device virtual address. */ + result = talmmu_get_dev_virt_addr(ddbuf_info->hndl_memory, &ddbuf_info->dev_virt); + if (result != IMG_SUCCESS) + goto error_get_dev_virt_addr; + + result = img_mmu_map_sg(ctx->mmu_ctx, ctx->mem_ctx, ddbuf_info->buff_id, sgt, + ddbuf_info->dev_virt, mem_attrib); + if (result != IMG_SUCCESS) + goto error_map_dev; + + return IMG_SUCCESS; + +error_map_dev: +error_get_dev_virt_addr: + talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + ddbuf_info->hndl_memory = NULL; + return result; +} + +/* + * topaz_mmu_stream_map_ext + */ +int topaz_mmu_stream_map_ext(void *mmu_str_hndl, enum topaz_mmu_eheap_id heap_id, + unsigned int buff_id, unsigned int size, unsigned int alignment, + enum sys_emem_attrib mem_attrib, void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info) +{ + int result = 0; + void *devmem_heap_hndl; + struct vxe_enc_ctx *ctx; + struct mmu_str_context *str_ctx; + + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + str_ctx = (struct mmu_str_context *)mmu_str_hndl; + + /* Check if device level heap. */ + switch (heap_id) { + case MMU_GENERAL_HEAP_ID: + break; + + default: + return IMG_ERROR_INVALID_PARAMETERS; + } + + /* Round size up to next multiple of physical pages */ + if ((size % HOST_MMU_PAGE_SIZE) != 0) + size = ((size / HOST_MMU_PAGE_SIZE) + 1) * HOST_MMU_PAGE_SIZE; + + ddbuf_info->buff_id = buff_id; + ddbuf_info->is_internal = 0; + + ddbuf_info->kmstr_id = str_ctx->km_str_id; + + /* Set buffer size. */ + ddbuf_info->buf_size = size; + + /* Ensure the address of the buffer is at least page aligned. */ + ddbuf_info->cpu_virt = cpu_linear_addr; + + /* Get heap handle */ + result = talmmu_get_heap_handle(heap_id, str_ctx->mmu_context_handle, + &devmem_heap_hndl); + if (result != IMG_SUCCESS) + return result; + + /* Allocate device "virtual" memory. */ + result = talmmu_devmem_addr_alloc(str_ctx->mmu_context_handle, + devmem_heap_hndl, size, alignment, + &ddbuf_info->hndl_memory); + if (result != IMG_SUCCESS) + return result; + + /* Get the device virtual address. */ + result = talmmu_get_dev_virt_addr(ddbuf_info->hndl_memory, &ddbuf_info->dev_virt); + if (result != IMG_SUCCESS) + return result; + + /* + * Map device memory (allocated from outside VDEC) + * into the stream PTD. + */ + ctx = str_ctx->vxe_enc_context; + + return img_mmu_map(ctx->mmu_ctx, ctx->mem_ctx, ddbuf_info->buff_id, ddbuf_info->dev_virt, + mem_attrib); +} + +/* + * topaz_mmu_free + */ +int topaz_mmu_free(struct vxe_enc_ctx *vxe_enc_ctx_arg, struct vidio_ddbufinfo *ddbuf_info) +{ + int result = 0; + struct vxe_enc_ctx *ctx; + + /* Validate inputs. */ + if (!ddbuf_info) + return IMG_ERROR_INVALID_PARAMETERS; + + /* Unmap the memory mapped to the device */ + ctx = vxe_enc_ctx_arg; + result = img_mmu_unmap(ctx->mmu_ctx, ctx->mem_ctx, ddbuf_info->buff_id); + + /* + * Unmapping the memory mapped to the device - done + * Free the memory. + */ + result = talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + + if (ddbuf_info->is_internal) + img_mem_free(ctx->mem_ctx, ddbuf_info->buff_id); + + return result; +} + +/* + * topaz_mmu_free_mem. + * This should be used only to free the stream memory. + */ +int topaz_mmu_stream_free(void *mmu_str_hndl, struct vidio_ddbufinfo *ddbuf_info) +{ + struct mmu_str_context *str_ctx; + + if (!mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + str_ctx = (struct mmu_str_context *)mmu_str_hndl; + + return topaz_mmu_free(str_ctx->vxe_enc_context, ddbuf_info); +} + +/* + * topaz_mmu_free_mem_sg. + * This should be used only to free the stream memory. + */ +int topaz_mmu_stream_free_sg(void *mmu_str_hndl, struct vidio_ddbufinfo *ddbuf_info) +{ + int result = 0; + struct vxe_enc_ctx *ctx; + struct mmu_str_context *str_ctx; + + /* Validate inputs. */ + if (!ddbuf_info || !mmu_str_hndl) + return IMG_ERROR_INVALID_PARAMETERS; + + str_ctx = (struct mmu_str_context *)mmu_str_hndl; + + /* Unmap the memory mapped to the device */ + ctx = str_ctx->vxe_enc_context; + + result = img_mmu_unmap(ctx->mmu_ctx, ctx->mem_ctx, ddbuf_info->buff_id); + + /* + * Unmapping the memory mapped to the device - done + * Free the memory. + */ + result = talmmu_devmem_addr_free(ddbuf_info->hndl_memory); + + /* + * for external mem manager buffers, just cleanup the idr list and + * buffer objects + */ + img_mem_free_bufid(ctx->mem_ctx, ddbuf_info->buff_id); + + return result; +} + +int topaz_update_device_mem(struct vxe_enc_ctx *vxe_enc_ctx_arg, + struct vidio_ddbufinfo *ddbuf_info) +{ + struct vxe_enc_ctx *ctx = vxe_enc_ctx_arg; + + return img_mem_sync_cpu_to_device(ctx->mem_ctx, + ddbuf_info->buff_id); +} + +int topaz_update_host_mem(struct vxe_enc_ctx *vxe_enc_ctx_arg, + struct vidio_ddbufinfo *ddbuf_info) +{ + struct vxe_enc_ctx *ctx = vxe_enc_ctx_arg; + + return img_mem_sync_device_to_cpu(ctx->mem_ctx, + ddbuf_info->buff_id); +} + +/* + * Called for each Topaz core when MMU support is activated, sets up the MMU + * hardware for the specified core. + */ +int topaz_core_mmu_hw_setup(struct topaz_mmu_context *mmu_context, void *core_reg) +{ + unsigned int cmd; + + /* Bypass all requesters while MMU is being configured */ + cmd = F_ENCODE(1, TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ); + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0, cmd); + + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE(0), mmu_context->ptd_phys_addr); + + cmd = VXE_RD_REG32(core_reg, TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE(0)); + +#ifdef DEBUG_ENCODER_DRIVER + pr_info("Page table directory at physical address 0x%08x\n", cmd); +#endif + /* + * Set up the Index Register (to point to the base register) + * We're setting all fields to zero (all flags pointing to directory bank 0) + */ + cmd = 0; + + /* Now enable MMU access for all requesters + * 36-bit actually means "not 32-bit" + */ + cmd = F_ENCODE(use_extended_addressing ? 1 : 0, TOPAZHP_TOP_CR_MMU_ENABLE_36BIT_ADDRESSING); + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL2, cmd); + + mmu_control_val = F_ENCODE(0, TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ); + cmd = F_ENCODE(0, TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ); + + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0, cmd); + + return 0; +} + +/* + * topaz_core_mmu_flush_cache + */ +int topaz_core_mmu_flush_cache(void) +{ + static void *core_reg; + unsigned int reg_value; + unsigned long flags; + + if (!core_reg) + core_reg = (void *)topaz_mem_space[REG_TOPAZHP_MULTICORE].cpu_addr; + + /* TODO we can have global mutex or local based on need */ + spin_lock_irqsave(g_lock, flags); + + reg_value = VXE_RD_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0); + + /* PAUSE */ + reg_value |= F_ENCODE(1, TOPAZHP_TOP_CR_MMU_PAUSE); + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0, reg_value); + + { + unsigned int i, mem_req_reg; + +wait_till_idle: + for (i = 0; i < 10; i++) { + mem_req_reg = VXE_RD_REG32(core_reg, TOPAZHP_TOP_CR_MMU_MEM_REQ); + if (mem_req_reg != 0) + goto wait_till_idle; + } + } + + /* Set invalidate */ + reg_value |= F_ENCODE(1, TOPAZHP_TOP_CR_MMU_INVALDC); + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0, reg_value); + + /* Clear invalidate */ + reg_value &= ~((unsigned int)F_ENCODE(1, TOPAZHP_TOP_CR_MMU_INVALDC)); + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0, reg_value); + + /* UNPAUSE */ + reg_value &= ~((unsigned int)F_ENCODE(1, TOPAZHP_TOP_CR_MMU_PAUSE)); + VXE_WR_REG32(core_reg, TOPAZHP_TOP_CR_MMU_CONTROL0, reg_value); + + /* TODO we can have global mutex or local based on need */ + spin_unlock_irqrestore(g_lock, flags); + + return 0; +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/topazmmu.h b/drivers/media/platform/vxe-vxd/encoder/topazmmu.h --- a/drivers/media/platform/vxe-vxd/encoder/topazmmu.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/topazmmu.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * topaz mmu header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef TOPAZZ_MMU_H_ +#define TOPAZZ_MMU_H_ + +#include +#include +#include +#include + +#include "talmmu_api.h" +#include "vxe_enc.h" +#include "img_mem.h" +#include "target_config.h" + +/* Page size of the device MMU */ +#define DEV_MMU_PAGE_SIZE (0x1000) +/* Page alignment of the device MMU */ +#define DEV_MMU_PAGE_ALIGNMENT (0x1000) + +#define HOST_MMU_PAGE_SIZE PAGE_SIZE + +/* + * This structure contains the stream context. + * @brief MMU Stream Context + * @devmem_ctx_hndl: Handle for MMU context. + * @dev_ctx: Pointer to device context. + * @ctx_id: MMU context Id. + * km_str_id: Stream ID used in communication with new KM interface + */ +struct mmu_str_context { + void **link; // to be able to maintain in single linked list. + void *mmu_context_handle; + unsigned int int_reg_num; + unsigned int km_str_id; + /* vxe encoder context. Need in stream context to access mem_ctx. */ + struct vxe_enc_ctx *vxe_enc_context; + struct lst_t ddbuf_list; +}; + +struct topaz_mmu_context { + void *mmu_context_handle; + unsigned int ptd_phys_addr; + struct lst_t str_list; +}; + +/* + * This type defines the MMU heaps. + * @0: General heap ID. + */ +enum topaz_mmu_eheap_id { + MMU_GENERAL_HEAP_ID = 0x00, + /* Do not remove - keeps count of size */ + HEAP_ID_NO_OF_HEAPS +}; + +/* Function definitions */ + +/* + * Called once during initialization to initialize the MMU hardware, create + * the template and define the MMU heap. + * This is where talmmu initialization and template will be created. + * + * NOTE : We are not taking care of alignment here, need to be updated in + * mmu_device_memory_info. + */ +int topaz_mmu_device_create(struct topaz_mmu_context *mmu_context, unsigned int mmu_flags); + +/* + * @Function mmu_device_destroy + * @Description + * This function is used to destroy the MMU device context. + * NOTE: Destroy device automatically destroys any streams and frees and + * memory allocated using MMU_StreamMalloc(). + * @Return IMG_SUCCESS or an error code. + */ +int topaz_mmu_device_destroy(struct topaz_mmu_context *mmu_context); + +/* + * @Function mmu_stream_create + * @Description + * This function is used to create and initialize the MMU stream context. + * @Input km_str_id : Stream Id used in communication with KM driver. + * @Return IMG_SUCCESS or an error code. + * + * Context ID is 1, since we are creating single stream. + */ +int topaz_mmu_stream_create(struct topaz_mmu_context *mmu_context, unsigned int km_str_id, + void *vxe_enc_ctx_arg, void **mmu_str_ctx); + +/* + * @Function mmu_stream_destroy + * @Description + * This function is used to destroy the MMU stream context. + * NOTE: Destroy automatically frees and memory allocated using + * mmu_stream_malloc(). + * @Input str_ctx : The MMU stream handle. + * @Return IMG_SUCCESS or an error code. + */ +int topaz_mmu_stream_destroy(struct topaz_mmu_context *mmu_context, + struct mmu_str_context *str_ctx); + +int topaz_mmu_alloc(void *mmu_context_handle, struct vxe_enc_ctx *vxe_enc_ctx_arg, + enum topaz_mmu_eheap_id heap_id, unsigned int mem_heap_id, + enum sys_emem_attrib mem_attrib, unsigned int size, unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info); +/* + * @Function mmu_stream_malloc + */ +int topaz_mmu_stream_alloc(void *mmu_str_hndl, enum topaz_mmu_eheap_id heap_id, + unsigned int mem_heap_id, + enum sys_emem_attrib mem_attrib, + unsigned int size, + unsigned int alignment, + struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function mmu_stream_map_ext_sg + */ +int topaz_mmu_stream_map_ext_sg(void *mmu_str_hndl, enum topaz_mmu_eheap_id heap_id, + void *sgt, + unsigned int size, + unsigned int alignment, + enum sys_emem_attrib mem_attrib, + void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info, + unsigned int *buff_id); + +/* + * @Function mmu_stream_map_ext + */ +int topaz_mmu_stream_map_ext(void *mmu_str_hndl, enum topaz_mmu_eheap_id heap_id, + unsigned int buff_id, unsigned int size, + unsigned int alignment, + enum sys_emem_attrib mem_attrib, + void *cpu_linear_addr, + struct vidio_ddbufinfo *ddbuf_info); + +/* topaz core mmu hardware setup */ +int topaz_core_mmu_hw_setup(struct topaz_mmu_context *mmu_context, void *core_reg); + +/* topaz core mmu flush cache */ +int topaz_core_mmu_flush_cache(void); + +/* + * @Function mmu_free + * + * Free memory allocated with mmu_alloc + */ +int topaz_mmu_free(struct vxe_enc_ctx *vxe_enc_ctx_arg, + struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function mmu_free_mem. + * + * NOTE : This should be used only to free the stream memory. + */ +int topaz_mmu_stream_free(void *mmu_str_hndl, struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function mmu_free_mem_sg. + * + * NOTE : This should be used only to free the stream memory. + */ +int topaz_mmu_stream_free_sg(void *mmu_str_hndl, struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function update_device_mem + * + * Update the memory to the device + */ +int topaz_update_device_mem(struct vxe_enc_ctx *vxe_enc_ctx_arg, + struct vidio_ddbufinfo *ddbuf_info); + +/* + * @Function update_host_mem + * + * Update the memory to the host + */ +int topaz_update_host_mem(struct vxe_enc_ctx *vxe_enc_ctx_arg, + struct vidio_ddbufinfo *ddbuf_info); + +/* Global */ +extern struct mem_space topaz_mem_space[]; +extern void *g_lock; + +#endif /* TOPAZZ_MMU_H_ */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/vxe_enc.c b/drivers/media/platform/vxe-vxd/encoder/vxe_enc.c --- a/drivers/media/platform/vxe-vxd/encoder/vxe_enc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/vxe_enc.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Encoder Interface API function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include + +#include "img_mem_man.h" +#include "topazmmu.h" +#include "vxe_enc.h" + +#define MAX(a, b, type) ({ \ + type __a = a; \ + type __b = b; \ + (((__a) >= (__b)) ? (__a) : (__b)); }) + +void mmu_callback(enum mmu_callback_type callback_type, + int buff_id, void *data) +{ + topaz_core_mmu_flush_cache(); +} + +int vxe_init_mem(struct vxe_dev *vxe) +{ + int ret; + + /* Create memory management context for HW buffers */ + ret = img_mem_create_ctx(&vxe->drv_ctx.mem_ctx); + if (ret) { + dev_err(vxe->dev, "%s: failed to create mem context (err:%d)!\n", + __func__, ret); + goto create_mem_context_failed; + } + + ret = img_mmu_ctx_create(vxe->dev, 40 /* mmu_addr_width is 40 */, + vxe->drv_ctx.mem_ctx, vxe->drv_ctx.internal_heap_id, + mmu_callback, vxe, &vxe->drv_ctx.mmu_ctx); + if (ret) { + dev_err(vxe->dev, "%s:%d: failed to create mmu ctx\n", + __func__, __LINE__); + goto create_mmu_context_failed; + } + + ret = img_mmu_get_ptd(vxe->drv_ctx.mmu_ctx, &vxe->drv_ctx.ptd); + if (ret) { + dev_err(vxe->dev, "%s:%d: failed to get PTD\n", + __func__, __LINE__); + goto get_ptd_failed; + } + + return 0; + +get_ptd_failed: + img_mmu_ctx_destroy(vxe->drv_ctx.mmu_ctx); +create_mmu_context_failed: + img_mem_destroy_ctx(vxe->drv_ctx.mem_ctx); +create_mem_context_failed: + return ret; +} + +void vxe_deinit_mem(struct vxe_dev *vxe) +{ + if (vxe->drv_ctx.mmu_ctx) { + img_mmu_ctx_destroy(vxe->drv_ctx.mmu_ctx); + vxe->drv_ctx.mmu_ctx = NULL; + } + + if (vxe->drv_ctx.mem_ctx) { + img_mem_destroy_ctx(vxe->drv_ctx.mem_ctx); + vxe->drv_ctx.mem_ctx = NULL; + } + + /* Deinitialize memory management component */ + while (!list_empty(&vxe->drv_ctx.heaps)) { + struct vxe_heap *heap; + + heap = list_first_entry(&vxe->drv_ctx.heaps, struct vxe_heap, list); + __list_del_entry(&heap->list); + img_mem_del_heap(heap->id); + kfree(heap); + } + + vxe->drv_ctx.internal_heap_id = VXE_INVALID_ID; + + img_mem_exit(); +} + +void vxe_create_ctx(struct vxe_dev *vxe, struct vxe_enc_ctx *ctx) +{ + ctx->mem_ctx = vxe->drv_ctx.mem_ctx; + ctx->mmu_ctx = vxe->drv_ctx.mmu_ctx; +} + +int calculate_h264_level(unsigned int width, unsigned int height, unsigned int framerate, + unsigned char rc_enable, unsigned int bitrate, + unsigned char lossless, + enum sh_profile_type profile_type, + unsigned int max_num_ref_frames) +{ + unsigned int level = 0, mbf = 0, mbs = 0, temp_level = 0, dpb_mbs; + unsigned int num = 1, den = 1; + unsigned int lossless_min_level = 320; + + mbf = (width * height) / 256; + mbs = mbf * framerate; + + if (mbf > 36864) { + pr_warn("WARNING: Frame size is too high for maximum supported level!\n"); + level = 520; + } else if (mbf > 22080) { + level = 510; + } else if (mbf > 8704) { + level = 500; + } else if (mbf > 8192) { + level = 420; + } else if (mbf > 5120) { + level = 400; + } else if (mbf > 3600) { + level = 320; + } else if (mbf > 1620) { + level = 310; + } else if (mbf > 792) { + level = 220; + } else if (mbf > 396) { + level = 210; + } else if (mbf > 99) { + level = 110; + } else { + level = 100; + } + + dpb_mbs = mbf * max_num_ref_frames; + + if (dpb_mbs > 184320) { + pr_warn("ERROR: Decoded picture buffer is too high for supported level!\n"); + return -1; + } else if (dpb_mbs > 110400) { + temp_level = 510; + } else if (dpb_mbs > 34816) { + temp_level = 500; + } else if (dpb_mbs > 32768) { + temp_level = 420; + } else if (dpb_mbs > 20480) { + temp_level = 400; + } else if (dpb_mbs > 18000) { + temp_level = 320; + } else if (dpb_mbs > 8100) { + temp_level = 310; + } else if (dpb_mbs > 4752) { + temp_level = 220; + } else if (dpb_mbs > 2376) { + temp_level = 210; + } else if (dpb_mbs > 900) { + temp_level = 120; + } else if (dpb_mbs > 396) { + temp_level = 110; + } else { + temp_level = 100; + } + + level = MAX(level, temp_level, unsigned int); + + /* now restrict based on the number of macroblocks per second */ + if (mbs > 2073600) { + pr_err("ERROR: Macroblock processing rate is too high for supported level!\n"); + return -1; + } else if (mbs > 983040) { + temp_level = 520; + } else if (mbs > 589824) { + temp_level = 510; + } else if (mbs > 522240) { + temp_level = 500; + } else if (mbs > 245760) { + temp_level = 420; + } else if (mbs > 216000) { + temp_level = 400; + } else if (mbs > 108000) { + temp_level = 320; + } else if (mbs > 40500) { + temp_level = 310; + } else if (mbs > 20250) { + temp_level = 300; + } else if (mbs > 19800) { + temp_level = 220; + } else if (mbs > 11880) { + temp_level = 210; + } else if (mbs > 6000) { + temp_level = 130; + } else if (mbs > 3000) { + temp_level = 120; + } else if (mbs > 1485) { + temp_level = 110; + } else { + temp_level = 100; + } + + level = MAX(level, temp_level, unsigned int); + + if (rc_enable) { + /* + * SH_PROFILE_H10P and SH_PROFILE_H422P are + * not valid choices for HW_3_X, skipping + */ + if (profile_type == SH_PROFILE_HP) { + num = 5; + den = 4; + } else if (profile_type == SH_PROFILE_H444P) { + num = 4; + den = 1; + } + + if (bitrate > ((135000000 * num) / den)) + temp_level = 510; + else if (bitrate > ((50000000 * num) / den)) + temp_level = 500; + else if (bitrate > ((20000000 * num) / den)) + temp_level = 410; + else if (bitrate > ((14000000 * num) / den)) + temp_level = 320; + else if (bitrate > ((10000000 * num) / den)) + temp_level = 310; + else if (bitrate > ((4000000 * num) / den)) + temp_level = 300; + else if (bitrate > ((2000000 * num) / den)) + temp_level = 210; + else if (bitrate > ((768000 * num) / den)) + temp_level = 200; + else if (bitrate > ((384000 * num) / den)) + temp_level = 130; + else if (bitrate > ((192000 * num) / den)) + temp_level = 120; + else if (bitrate > ((128000 * num) / den)) + temp_level = 110; + else if (bitrate > ((64000 * num) / den)) + temp_level = 101; + else + temp_level = 100; + + level = MAX(level, temp_level, unsigned int); + } else { + level = 510; + } + + if (lossless) + level = MAX(level, lossless_min_level, unsigned int); + + return level; +} + +enum sh_profile_type find_h264_profile(unsigned char lossless, + unsigned char h264_use_default_scaling_list, + unsigned int custom_quant_mask, + unsigned char h264_8x8_transform, + unsigned char enable_mvc, + unsigned int b_frame_count, + unsigned char interlaced, + unsigned char h264_cabac, + unsigned int weighted_prediction_mode, + unsigned int weighted_implicit_bi_pred) +{ + enum sh_profile_type profile = SH_PROFILE_BP; + + if (lossless) + profile = SH_PROFILE_H444P; + else if (h264_use_default_scaling_list || custom_quant_mask || + h264_8x8_transform || enable_mvc) + profile = SH_PROFILE_HP; + else if ((b_frame_count > 0) || interlaced || h264_cabac || + weighted_prediction_mode || weighted_implicit_bi_pred) + profile = SH_PROFILE_MP; + + return profile; +} + +void vxe_fill_default_src_frame_params(struct vxe_buffer *buf) +{ + buf->src_frame.component_count = 0; /* Unset in IMG */ + buf->src_frame.format = IMG_CODEC_420_YUV; /* Unset in IMG */ + buf->src_frame.component_offset[0] = 0; + buf->src_frame.component_offset[1] = 0; + buf->src_frame.component_offset[2] = 0; + buf->src_frame.bottom_component_offset[0] = 0; /* Unset in IMG */ + buf->src_frame.bottom_component_offset[1] = 0; /* Unset in IMG */ + buf->src_frame.bottom_component_offset[2] = 0; /* Unset in IMG */ + buf->src_frame.component_info[0].step = 0; + buf->src_frame.component_info[0].width = 0; + buf->src_frame.component_info[0].height = 0; + buf->src_frame.component_info[0].phys_width = 0; + buf->src_frame.component_info[0].phys_height = 0; + buf->src_frame.component_info[1].step = 0; + buf->src_frame.component_info[1].width = 0; + buf->src_frame.component_info[1].height = 0; + buf->src_frame.component_info[1].phys_width = 0; + buf->src_frame.component_info[1].phys_height = 0; + buf->src_frame.component_info[2].step = 0; + buf->src_frame.component_info[2].width = 0; + buf->src_frame.component_info[2].height = 0; + buf->src_frame.component_info[2].phys_width = 0; + buf->src_frame.component_info[2].phys_height = 0; + buf->src_frame.field0_y_offset = 0; + buf->src_frame.field1_y_offset = 0; + buf->src_frame.field0_u_offset = 0; + buf->src_frame.field1_u_offset = 0; + buf->src_frame.field0_v_offset = 0; + buf->src_frame.field1_v_offset = 0; + buf->src_frame.imported = FALSE; +} + +void vxe_fill_default_params(struct vxe_enc_ctx *ctx) +{ + int i, j; + unsigned short h264_rounding_offsets[18][4] = { + {683, 683, 683, 683}, /* 0 I-Slice - INTRA4 LUMA */ + {683, 683, 683, 683}, /* 1 P-Slice - INTRA4 LUMA */ + {683, 683, 683, 683}, /* 2 B-Slice - INTRA4 LUMA */ + + {683, 683, 683, 683}, /* 3 I-Slice - INTRA8 LUMA */ + {683, 683, 683, 683}, /* 4 P-Slice - INTRA8 LUMA */ + {683, 683, 683, 683}, /* 5 B-Slice - INTRA8 LUMA */ + + {341, 341, 341, 341}, /* 6 P-Slice - INTER8 LUMA */ + {341, 341, 341, 341}, /* 7 B-Slice - INTER8 LUMA */ + + {683, 683, 683, 000}, /* 8 I-Slice - INTRA16 LUMA */ + {683, 683, 683, 000}, /* 9 P-Slice - INTRA16 LUMA */ + {683, 683, 683, 000}, /* 10 B-Slice - INTRA16 LUMA */ + + {341, 341, 341, 341}, /* 11 P-Slice - INTER16 LUMA */ + {341, 341, 341, 341}, /* 12 B-Slice - INTER16 LUMA */ + + {683, 683, 683, 000}, /* 13 I-Slice - INTRA16 CR */ + {683, 683, 683, 000}, /* 14 P-Slice - INTRA16 CR */ + {683, 683, 683, 000}, /* 15 B-Slice - INTRA16 CR */ + + {341, 341, 341, 000 }, /* 16 P-Slice - INTER16 CHROMA */ + {341, 341, 341, 000 } /* 17 B-Slice - INTER16 CHROMA */ + }; + + ctx->vparams.csc_preset = IMG_CSC_NONE; + ctx->vparams.slices_per_picture = 1; + ctx->vparams.is_interleaved = FALSE; + ctx->vparams.constrained_intra = FALSE; + ctx->vparams.h264_8x8 = TRUE; + ctx->vparams.bottom_field_first = FALSE; + ctx->vparams.arbitrary_so = FALSE; + ctx->vparams.cabac_enabled = TRUE; + ctx->vparams.cabac_bin_limit = 2800; + ctx->vparams.cabac_bin_flex = 2800; + ctx->vparams.deblock_idc = 0; + ctx->vparams.output_reconstructed = FALSE; + ctx->vparams.f_code = 4; + ctx->vparams.fine_y_search_size = 2; + ctx->vparams.no_offscreen_mv = FALSE; + ctx->vparams.idr_period = 1800; /* 60 * 30fps */ + ctx->vparams.intra_cnt = 30; + ctx->vparams.vop_time_resolution = 15; + ctx->vparams.enc_features.disable_bpic_ref1 = FALSE; + ctx->vparams.enc_features.disable_bpic_ref0 = FALSE; + ctx->vparams.enc_features.disable_bframes = FALSE; + ctx->vparams.enc_features.restricted_intra_pred = FALSE; + ctx->vparams.enable_sel_stats_flags = 0; + ctx->vparams.enable_inp_ctrl = FALSE; + ctx->vparams.enable_air = FALSE; + ctx->vparams.num_air_mbs = -1; + ctx->vparams.air_threshold = -1; + ctx->vparams.air_skip_cnt = -1; + ctx->vparams.enable_cumulative_biases = FALSE; + ctx->vparams.enable_host_bias = TRUE; + ctx->vparams.enable_host_qp = FALSE; + ctx->vparams.use_default_scaling_list = FALSE; + ctx->vparams.use_custom_scaling_lists = 0; + ctx->vparams.pps_scaling = 0; + ctx->vparams.disable_bit_stuffing = TRUE; + ctx->vparams.coded_skipped_index = 3; + ctx->vparams.inter_intra_index = 3; + ctx->vparams.mpeg2_intra_dc_precision = 0; + ctx->vparams.carc = 0; + ctx->vparams.carc_baseline = 0; + ctx->vparams.carc_threshold = 1; + ctx->vparams.carc_cutoff = 15; + ctx->vparams.carc_neg_range = 5; + ctx->vparams.carc_neg_scale = 12; + ctx->vparams.carc_pos_range = 5; + ctx->vparams.carc_pos_scale = 12; + ctx->vparams.carc_shift = 3; + ctx->vparams.weighted_prediction = FALSE; + ctx->vparams.vp_weighted_implicit_bi_pred = 0; + ctx->vparams.insert_hrd_params = FALSE; + ctx->vparams.intra_refresh = 0; + ctx->vparams.chunks_per_mb = 64; + ctx->vparams.max_chunks = 160; + ctx->vparams.priority_chunks = 64; + ctx->vparams.mbps = 0; + ctx->vparams.multi_reference_p = FALSE; + ctx->vparams.ref_spacing = 0; + ctx->vparams.spatial_direct = FALSE; + ctx->vparams.vp_adaptive_rounding_disable = 0; + + for (i = 0; i < 18; i++) { + for (j = 0; j < 4; j++) { + ctx->vparams.vp_adaptive_rounding_offsets[i][j] = + h264_rounding_offsets[i][j]; + } + } + + ctx->vparams.debug_crcs = 0; + ctx->vparams.enable_mvc = FALSE; + ctx->vparams.mvc_view_idx = 65535; + ctx->vparams.high_latency = TRUE; + ctx->vparams.disable_bh_rounding = FALSE; + ctx->vparams.no_sequence_headers = FALSE; + ctx->vparams.auto_encode = FALSE; + ctx->vparams.slice_level = FALSE; + ctx->vparams.coded_header_per_slice = FALSE; + ctx->vparams.auto_expand_pipes = FALSE; + ctx->vparams.enable_lossless = FALSE; + ctx->vparams.lossless_8x8_prefilter = FALSE; + ctx->vparams.enable_scaler = FALSE; + ctx->vparams.line_counter_enabled = FALSE; + + ctx->rc.initial_qp_i = 0; + ctx->rc.initial_qp_p = 0; + ctx->rc.initial_qp_b = 0; + + ctx->rc.min_qp = 0; + ctx->rc.max_qp = 0; + ctx->rc.rc_enable = TRUE; + + ctx->rc.hierarchical = FALSE; + + ctx->rc.enable_slice_bob = FALSE; + ctx->rc.max_slice_bob = 2; + ctx->rc.slice_bob_qp = 44; + + ctx->rc.qcp_offset = 0; + ctx->rc.sc_detect_disable = FALSE; + ctx->rc.slice_byte_limit = 0; + ctx->rc.slice_mb_limit = 0; + ctx->rc.rc_mode = IMG_RCMODE_VBR; + ctx->rc.rc_vcm_mode = IMG_RC_VCM_MODE_DEFAULT; + ctx->rc.rc_cfs_max_margin_perc = 9; + ctx->rc.disable_frame_skipping = FALSE; + ctx->rc.disable_vcm_hardware = FALSE; + + ctx->s_fmt_flags = 0; + + ctx->above_mb_params_sgt[0].sgl = NULL; + ctx->above_mb_params_sgt[1].sgl = NULL; +} + +unsigned int vxe_get_sizeimage(int w, int h, struct vxe_enc_fmt *fmt, unsigned char plane_id) +{ + return (w * h * fmt->size_num[plane_id] / fmt->size_den[plane_id]); +} + +unsigned int vxe_get_stride(int w, struct vxe_enc_fmt *fmt) +{ + return ALIGN(w * fmt->bytes_pp, HW_ALIGN); +} diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/vxe_enc.h b/drivers/media/platform/vxe-vxd/encoder/vxe_enc.h --- a/drivers/media/platform/vxe-vxd/encoder/vxe_enc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/vxe_enc.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * encoder interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _VXE_ENC_H +#define _VXE_ENC_H + +#include +#include +#include +#include +#include "topaz_api.h" + +#define HW_ALIGN 64 +#define MB_SIZE 16 +#define VXE_INVALID_ID (-1) +#define OCM_RAM_POOL_CHUNK_SIZE (32 * 1024) + +enum { + Q_ENC_DATA_SRC = 0, + Q_ENC_DATA_DST = 1, + Q_ENC_DATA_FORCE32BITS = 0x7FFFFFFFU +}; + +enum { + IMG_ENC_FMT_TYPE_CAPTURE = 0x01, + IMG_ENC_FMT_TYPE_OUTPUT = 0x10, + IMG_ENC_FMT_TYPE_FORCE32BITS = 0x7FFFFFFFU +}; + +enum vxe_map_flags { + VXE_MAP_FLAG_NONE = 0x0, + VXE_MAP_FLAG_READ_ONLY = 0x1, + VXE_MAP_FLAG_WRITE_ONLY = 0x2, + VXE_MAP_FLAG_FORCE32BITS = 0x7FFFFFFFU +}; + +/* + * struct vxe_enc_fmt - contains info for each supported video format + */ +struct vxe_enc_fmt { + unsigned int fourcc; + unsigned int num_planes; + unsigned int type; + union { + enum img_standard std; + enum img_format fmt; + }; + unsigned int min_bufs; + unsigned int size_num[MAX_PLANES]; + unsigned int size_den[MAX_PLANES]; + unsigned int bytes_pp; + enum img_csc_preset csc_preset; +}; + +/* + * struct vxe_buffer - contains info for all buffers + */ +struct vxe_buffer { + struct v4l2_m2m_buffer buffer; + unsigned int index; + unsigned int buf_map_id; + struct vidio_ddbufinfo buf_info; + union { + struct img_frame src_frame; + struct img_coded_buffer coded_buffer; + }; + struct img_buffer y_buffer; + struct img_buffer u_buffer; + struct img_buffer v_buffer; + unsigned char src_slot_num; + unsigned char mapped; +}; + +/* + * struct vxe_heap - node for heaps list + * @id: heap id + * @list: Entry in + */ +struct vxe_heap { + int id; + struct list_head list; +}; + +/* Driver context */ +struct vxe_drv_ctx { + /* Available memory heaps. List of */ + struct list_head heaps; + /* heap id for all internal allocations */ + int internal_heap_id; + /* Memory Management context for driver */ + struct mem_ctx *mem_ctx; + /* MMU context for driver */ + struct mmu_ctx *mmu_ctx; + /* PTD */ + unsigned int ptd; +}; + +/* + * struct vxe_dev - The struct containing encoder driver internal parameters. + */ +struct vxe_dev { + void *dev; + struct video_device *vfd; + struct v4l2_device ti_vxe_dev; + struct platform_device *plat_dev; + struct v4l2_m2m_dev *m2m_dev; + struct mutex *mutex; + int module_irq; + struct idr *streams; + void __iomem *reg_base; + void *topaz_dev_ctx; + struct vxe_drv_ctx drv_ctx; + /* dummy context for MMU mappings and allocations */ + struct vxe_enc_ctx *ctx; + unsigned int num_pipes; + + /* The variables defined below are used in RTOS only. */ + /* This variable holds queue handler */ + void *vxe_worker_queue_handle; + void *vxe_worker_queue_sem_handle; + + /* On Chip Memory Pool for above MB params struct */ + /* Supporting only 2 max instances (upto 1080p resolutions) to make use of this */ + void *ocm_ram_chunk[2]; //each chunk of 32KB + void *ram_chunk_owner[2]; + +}; + +#define S_FMT_FLAG_OUT_RECV 0x1 +#define S_FMT_FLAG_CAP_RECV 0x2 +#define S_FMT_FLAG_STREAM_CREATED 0x4 + +/* + * struct vxe_enc_q_data - contains queue data information + * + * @fmt: format info + * @width: frame width + * @height: frame height + * @bytesperline: bytes per line in memory + * @size_image: image size in memory + */ +struct vxe_enc_q_data { + struct vxe_enc_fmt *fmt; + unsigned int width; + unsigned int height; + unsigned int bytesperline[MAX_PLANES]; + unsigned int size_image[MAX_PLANES]; + unsigned char streaming; +}; + +#ifdef ENABLE_PROFILING +struct enc_drv_latency { + unsigned int start_time; + unsigned int end_time; +}; +#endif + +/* + * struct vxe_ctx - The struct containing stream context parameters. + */ +struct vxe_enc_ctx { + struct v4l2_fh fh; + struct vxe_dev *dev; + void **enc_context; + void *topaz_str_context; + struct mutex *mutex; + struct img_enc_caps caps; + struct img_rc_params rc; + struct img_video_params vparams; + struct vxe_enc_q_data out_queue; + struct vxe_enc_q_data cap_queue; + struct mem_ctx *mem_ctx; + struct mmu_ctx *mmu_ctx; + /* list open_slots*/ + unsigned char s_fmt_flags; + struct h264_vui_params vui_params; + struct h264_crop_params crop_params; + struct h264_sequence_header_params sh_params; + unsigned char eos; + unsigned char flag_last; + unsigned int coded_packages_per_frame; /* How many slices per frame */ + unsigned int available_coded_packages; + unsigned int available_source_frames; + unsigned int frames_encoding; + unsigned int frame_num; + unsigned int last_frame_num; + + /* The below variable used only in Rtos */ + void *mm_return_resource; /* Place holder for CB to application */ + void *stream_worker_queue_handle; + void *stream_worker_queue_sem_handle; + void *work; + struct vxe_enc_q_data q_data[2]; + + struct sg_table above_mb_params_sgt[2]; + +#ifdef ENABLE_PROFILING + struct enc_drv_latency drv_lat; +#endif +}; + +int vxe_init_mem(struct vxe_dev *vxe); +void vxe_deinit_mem(struct vxe_dev *vxe); +void vxe_create_ctx(struct vxe_dev *vxe, struct vxe_enc_ctx *ctx); +int calculate_h264_level(unsigned int width, unsigned int height, unsigned int framerate, + unsigned char rc_enable, unsigned int bitrate, + unsigned char lossless, + enum sh_profile_type profile_type, + unsigned int max_num_ref_frames); +enum sh_profile_type find_h264_profile(unsigned char lossless, + unsigned char h264_use_default_scaling_list, + unsigned int custom_quant_mask, + unsigned char h264_8x8_transform, + unsigned char enable_mvc, + unsigned int b_frame_count, + unsigned char interlaced, + unsigned char h264_cabac, + unsigned int weighted_prediction_mode, + unsigned int weighted_implicit_bi_pred); +void vxe_fill_default_src_frame_params(struct vxe_buffer *buf); +void vxe_fill_default_params(struct vxe_enc_ctx *ctx); +unsigned int vxe_get_sizeimage(int w, int h, struct vxe_enc_fmt *fmt, unsigned char plane_id); +unsigned int vxe_get_stride(int w, struct vxe_enc_fmt *fmt); + +#endif /* _VXE_ENC_H */ diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/vxe_public_regdefs.h b/drivers/media/platform/vxe-vxd/encoder/vxe_public_regdefs.h --- a/drivers/media/platform/vxe-vxd/encoder/vxe_public_regdefs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/vxe_public_regdefs.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,926 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * encoder public register definitions + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef __VXE_PUBLIC_REGDEFS_H__ +#define __VXE_PUBLIC_REGDEFS_H__ + +#include +#include +#include +#include + +/* Write to the register */ +#define VXE_WR_REG32(base, offs, val) \ + (iowrite32((val), (void *)((offs) + (unsigned long long)(base)))) + +/* Read the register */ +#define VXE_RD_REG32(base, offs) \ + (ioread32((void *)((base) + (offs)))) + +#define VXE_POLL_REG32_ISEQ(base, offs, val, mask, cnt) \ + (ioreg32_poll_iseq((unsigned long long)(base) + (offs), val, mask, cnt)) + +#define REG_BASE_HOST 0x00000000 +#define REG_OFFSET_TOPAZ_MTX 0x00000800 +#define REG_START_TOPAZ_MTX_HOST (REG_BASE_HOST + REG_OFFSET_TOPAZ_MTX) + +static inline int ioreg32_poll_iseq(unsigned long long addr, + unsigned int req_val, unsigned int mask, unsigned int cnt) +{ + unsigned int count, val; + unsigned int res = 0; + + /* Add high-frequency poll loops. */ + cnt += 10; + + /* + * High-frequency loop (designed for shorter hardware latency such as + * reset). + */ + for (count = 0; count < cnt; count++) { + /* Read from the device */ + val = ioread32((void *)addr); + val = (val & mask); + + if (val == req_val) { + res = 0; + break; + } + + /* + * Sleep to wait for hardware. + * Period is selected to allow for high-frequency polling + * (5us, e.g. reset) over the first 10 iterations, then + * reverting to a lower-frequency (100us, e.g. DMA) for the + * remainder. + */ + if (count < 10) + usleep_range(5, 5); + else + usleep_range(100, 100); + } + + if (res || count >= cnt) { + pr_info("Poll failed!\n"); + res = -1; + } + + return res; +} + +/* + * DMAC configuration values: + */ +/*! The maximum number of channels in the SoC */ +#define DMAC_MAX_CHANNELS (1) + +/* Register CR_TOPAZHP_CORE_REV */ +#define TOPAZHP_TOP_CR_TOPAZHP_CORE_REV 0x03D0 +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV 0x000000FF +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV 0 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV 0x03D0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MAINT_REV 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV 0x0000FF00 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV 8 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV 0x03D0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MINOR_REV 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV 0x00FF0000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV 16 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV 0x03D0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MAJOR_REV 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_DESIGNER 0xFF000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_DESIGNER 24 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_DESIGNER 0x03D0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_DESIGNER 0 + +/* Register CR_TOPAZHP_CORE_DES1 */ +#define TOPAZHP_TOP_CR_TOPAZHP_CORE_DES1 0x03E0 + +/* Register CR_MULTICORE_HW_CFG */ +#define TOPAZHP_TOP_CR_MULTICORE_HW_CFG 0x0058 +#define MASK_TOPAZHP_TOP_CR_NUM_CORES_SUPPORTED 0x0000001F +#define SHIFT_TOPAZHP_TOP_CR_NUM_CORES_SUPPORTED 0 +#define REGNUM_TOPAZHP_TOP_CR_NUM_CORES_SUPPORTED 0x0058 +#define SIGNED_TOPAZHP_TOP_CR_NUM_CORES_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_NUM_MTX_SUPPORTED 0x00000700 +#define SHIFT_TOPAZHP_TOP_CR_NUM_MTX_SUPPORTED 8 +#define REGNUM_TOPAZHP_TOP_CR_NUM_MTX_SUPPORTED 0x0058 +#define SIGNED_TOPAZHP_TOP_CR_NUM_MTX_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_NUM_CORES_PER_MTX 0x00070000 +#define SHIFT_TOPAZHP_TOP_CR_NUM_CORES_PER_MTX 16 +#define REGNUM_TOPAZHP_TOP_CR_NUM_CORES_PER_MTX 0x0058 +#define SIGNED_TOPAZHP_TOP_CR_NUM_CORES_PER_MTX 0 + +#define MASK_TOPAZHP_TOP_CR_EXTENDED_ADDR_RANGE 0x0F000000 +#define SHIFT_TOPAZHP_TOP_CR_EXTENDED_ADDR_RANGE 24 +#define REGNUM_TOPAZHP_TOP_CR_EXTENDED_ADDR_RANGE 0x0058 +#define SIGNED_TOPAZHP_TOP_CR_EXTENDED_ADDR_RANGE 0 + +/* Register CR_MULTICORE_SRST */ +#define TOPAZHP_TOP_CR_MULTICORE_SRST 0x0000 +#define MASK_TOPAZHP_TOP_CR_IMG_TOPAZ_MTX_SOFT_RESET 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_IMG_TOPAZ_MTX_SOFT_RESET 0 +#define REGNUM_TOPAZHP_TOP_CR_IMG_TOPAZ_MTX_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_TOP_CR_IMG_TOPAZ_MTX_SOFT_RESET 0 + +#define MASK_TOPAZHP_TOP_CR_IMG_TOPAZ_IO_SOFT_RESET 0x00000002 +#define SHIFT_TOPAZHP_TOP_CR_IMG_TOPAZ_IO_SOFT_RESET 1 +#define REGNUM_TOPAZHP_TOP_CR_IMG_TOPAZ_IO_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_TOP_CR_IMG_TOPAZ_IO_SOFT_RESET 0 + +#define MASK_TOPAZHP_TOP_CR_IMG_TOPAZ_CORE_SOFT_RESET 0x00000004 +#define SHIFT_TOPAZHP_TOP_CR_IMG_TOPAZ_CORE_SOFT_RESET 2 +#define REGNUM_TOPAZHP_TOP_CR_IMG_TOPAZ_CORE_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_TOP_CR_IMG_TOPAZ_CORE_SOFT_RESET 0 + +/* Register CR_MULTICORE_INT_STAT */ +#define TOPAZHP_TOP_CR_MULTICORE_INT_STAT 0x0004 +#define MASK_TOPAZHP_TOP_CR_INT_STAT_DMAC 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_DMAC 0 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_DMAC 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_DMAC 0 + +#define MASK_TOPAZHP_TOP_CR_INT_STAT_MTX 0x00000002 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_MTX 1 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_MTX 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_MTX 0 + +#define MASK_TOPAZHP_TOP_CR_INT_STAT_MTX_HALT 0x00000004 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_MTX_HALT 2 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_MTX_HALT 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_MTX_HALT 0 + +#define MASK_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT 0x00000078 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT 3 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT 0 + +#define MASK_TOPAZHP_TOP_CR_INT_STAT_MTX_CORES 0x0000FF00 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_MTX_CORES 8 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_MTX_CORES 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_MTX_CORES 0 + +#define MASK_TOPAZHP_TOP_CR_INT_STAT_HOST_CORES 0x00FF0000 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_HOST_CORES 16 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_HOST_CORES 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_HOST_CORES 0 + +#define MASK_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT_B 0x1E000000 +#define SHIFT_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT_B 25 +#define REGNUM_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT_B 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_INT_STAT_MMU_FAULT_B 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MAS_MTX_INTS 0x40000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MAS_MTX_INTS 30 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MAS_MTX_INTS 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MAS_MTX_INTS 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MAS_HOST_INTS 0x80000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MAS_HOST_INTS 31 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MAS_HOST_INTS 0x0004 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MAS_HOST_INTS 0 + +/* Register CR_MULTICORE_HOST_INT_ENAB */ +#define TOPAZHP_TOP_CR_MULTICORE_HOST_INT_ENAB 0x000C +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_DMAC 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_DMAC 0 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_DMAC 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_DMAC 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_MTX 0x00000002 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_MTX 1 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_MTX 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_MTX 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_MTX_HALT 0x00000004 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_MTX_HALT 2 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_MTX_HALT 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_MTX_HALT 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT 0x00000078 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT 3 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_MTX_CORES 0x0000FF00 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_MTX_CORES 8 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_MTX_CORES 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_MTX_CORES 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_HOST_CORES 0x00FF0000 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_HOST_CORES 16 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_HOST_CORES 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_HOST_CORES 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT_B 0x1E000000 +#define SHIFT_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT_B 25 +#define REGNUM_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT_B 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_INTEN_MMU_FAULT_B 0 + +#define MASK_TOPAZHP_TOP_CR_HOST_TOPAZHP_MAS_INTEN 0x80000000 +#define SHIFT_TOPAZHP_TOP_CR_HOST_TOPAZHP_MAS_INTEN 31 +#define REGNUM_TOPAZHP_TOP_CR_HOST_TOPAZHP_MAS_INTEN 0x000C +#define SIGNED_TOPAZHP_TOP_CR_HOST_TOPAZHP_MAS_INTEN 0 + +/* Register CR_MULTICORE_INT_CLEAR */ +#define TOPAZHP_TOP_CR_MULTICORE_INT_CLEAR 0x0010 +#define MASK_TOPAZHP_TOP_CR_INTCLR_DMAC 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_INTCLR_DMAC 0 +#define REGNUM_TOPAZHP_TOP_CR_INTCLR_DMAC 0x0010 +#define SIGNED_TOPAZHP_TOP_CR_INTCLR_DMAC 0 + +#define MASK_TOPAZHP_TOP_CR_INTCLR_MTX 0x00000002 +#define SHIFT_TOPAZHP_TOP_CR_INTCLR_MTX 1 +#define REGNUM_TOPAZHP_TOP_CR_INTCLR_MTX 0x0010 +#define SIGNED_TOPAZHP_TOP_CR_INTCLR_MTX 0 + +#define MASK_TOPAZHP_TOP_CR_INTCLR_MTX_HALT 0x00000004 +#define SHIFT_TOPAZHP_TOP_CR_INTCLR_MTX_HALT 2 +#define REGNUM_TOPAZHP_TOP_CR_INTCLR_MTX_HALT 0x0010 +#define SIGNED_TOPAZHP_TOP_CR_INTCLR_MTX_HALT 0 + +#define MASK_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT 0x00000078 +#define SHIFT_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT 3 +#define REGNUM_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT 0x0010 +#define SIGNED_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT 0 + +#define MASK_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT_B 0x1E000000 +#define SHIFT_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT_B 25 +#define REGNUM_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT_B 0x0010 +#define SIGNED_TOPAZHP_TOP_CR_INTCLR_MMU_FAULT_B 0 + +/* Register CR_TOPAZ_CMD_FIFO_FLUSH */ +#define TOPAZHP_TOP_CR_TOPAZ_CMD_FIFO_FLUSH 0x0078 +#define MASK_TOPAZHP_TOP_CR_CMD_FIFO_FLUSH 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_CMD_FIFO_FLUSH 0 +#define REGNUM_TOPAZHP_TOP_CR_CMD_FIFO_FLUSH 0x0078 +#define SIGNED_TOPAZHP_TOP_CR_CMD_FIFO_FLUSH 0 + +/* Register CR_MULTICORE_CMD_FIFO_WRITE */ +#define TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE 0x0060 +#define MASK_TOPAZHP_TOP_CR_CMD_FIFO_WDATA 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_CMD_FIFO_WDATA 0 +#define REGNUM_TOPAZHP_TOP_CR_CMD_FIFO_WDATA 0x0060 +#define SIGNED_TOPAZHP_TOP_CR_CMD_FIFO_WDATA 0 + +/* Register CR_MULTICORE_CMD_FIFO_WRITE_SPACE */ +#define TOPAZHP_TOP_CR_MULTICORE_CMD_FIFO_WRITE_SPACE 0x0064 +#define MASK_TOPAZHP_TOP_CR_CMD_FIFO_SPACE 0x000000FF +#define SHIFT_TOPAZHP_TOP_CR_CMD_FIFO_SPACE 0 +#define REGNUM_TOPAZHP_TOP_CR_CMD_FIFO_SPACE 0x0064 +#define SIGNED_TOPAZHP_TOP_CR_CMD_FIFO_SPACE 0 + +#define MASK_TOPAZHP_TOP_CR_CMD_FIFO_FULL 0x00000100 +#define SHIFT_TOPAZHP_TOP_CR_CMD_FIFO_FULL 8 +#define REGNUM_TOPAZHP_TOP_CR_CMD_FIFO_FULL 0x0064 +#define SIGNED_TOPAZHP_TOP_CR_CMD_FIFO_FULL 0 + +/* Register CR_MULTICORE_IDLE_PWR_MAN */ +#define TOPAZHP_TOP_CR_MULTICORE_IDLE_PWR_MAN 0x0118 +#define MASK_TOPAZHP_TOP_CR_TOPAZ_IDLE_DISABLE 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZ_IDLE_DISABLE 0 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZ_IDLE_DISABLE 0x0118 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZ_IDLE_DISABLE 0 + +/* Register CR_FIRMWARE_REG_1 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_1 0x0100 +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_1 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_1 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_1 0x0100 +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_1 0 + +/* Register CR_FIRMWARE_REG_2 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_2 0x0104 +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_2 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_2 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_2 0x0104 +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_2 0 + +/* Register CR_FIRMWARE_REG_3 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_3 0x0108 +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_3 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_3 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_3 0x0108 +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_3 0 + +/* Register CR_FIRMWARE_REG_4 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_4 0x0300 +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_4 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_4 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_4 0x0300 +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_4 0 + +/* Register CR_FIRMWARE_REG_5 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_5 0x0304 +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_5 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_5 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_5 0x0304 +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_5 0 + +/* Register CR_FIRMWARE_REG_6 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_6 0x0308 +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_6 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_6 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_6 0x0308 +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_6 0 + +/* Register CR_FIRMWARE_REG_7 */ +#define TOPAZHP_TOP_CR_FIRMWARE_REG_7 0x030C +#define MASK_TOPAZHP_TOP_CR_FIRMWARE_REG_7 0xFFFFFFFF +#define SHIFT_TOPAZHP_TOP_CR_FIRMWARE_REG_7 0 +#define REGNUM_TOPAZHP_TOP_CR_FIRMWARE_REG_7 0x030C +#define SIGNED_TOPAZHP_TOP_CR_FIRMWARE_REG_7 0 + +/* Register CR_MTX_DEBUG_MSTR */ +#define TOPAZHP_TOP_CR_MTX_DEBUG_MSTR 0x0044 +#define MASK_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_IN 0x00000003 +#define SHIFT_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_IN 0 +#define REGNUM_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_IN 0x0044 +#define SIGNED_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_IN 0 + +#define MASK_TOPAZHP_TOP_CR_MTX_MSTR_DBG_IS_SLAVE 0x00000004 +#define SHIFT_TOPAZHP_TOP_CR_MTX_MSTR_DBG_IS_SLAVE 2 +#define REGNUM_TOPAZHP_TOP_CR_MTX_MSTR_DBG_IS_SLAVE 0x0044 +#define SIGNED_TOPAZHP_TOP_CR_MTX_MSTR_DBG_IS_SLAVE 0 + +#define MASK_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_OUT 0x00000018 +#define SHIFT_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_OUT 3 +#define REGNUM_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_OUT 0x0044 +#define SIGNED_TOPAZHP_TOP_CR_MTX_MSTR_DBG_GPIO_OUT 0 + +#define MASK_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANKS 0x00000F00 +#define SHIFT_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANKS 8 +#define REGNUM_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANKS 0x0044 +#define SIGNED_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANKS 0 + +#define MASK_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANK_SIZE 0x000F0000 +#define SHIFT_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANK_SIZE 16 +#define REGNUM_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANK_SIZE 0x0044 +#define SIGNED_TOPAZHP_TOP_CR_MTX_MSTR_RAM_BANK_SIZE 0 + +#define MASK_TOPAZHP_TOP_CR_MTX_MSTR_LAST_RAM_BANK_SIZE 0x0F000000 +#define SHIFT_TOPAZHP_TOP_CR_MTX_MSTR_LAST_RAM_BANK_SIZE 24 +#define REGNUM_TOPAZHP_TOP_CR_MTX_MSTR_LAST_RAM_BANK_SIZE 0x0044 +#define SIGNED_TOPAZHP_TOP_CR_MTX_MSTR_LAST_RAM_BANK_SIZE 0 + +/* Register CR_MULTICORE_CORE_SEL_0 */ +#define TOPAZHP_TOP_CR_MULTICORE_CORE_SEL_0 0x0050 +#define MASK_TOPAZHP_TOP_CR_DMAC_MTX_SELECT 0x00000007 +#define SHIFT_TOPAZHP_TOP_CR_DMAC_MTX_SELECT 0 +#define REGNUM_TOPAZHP_TOP_CR_DMAC_MTX_SELECT 0x0050 +#define SIGNED_TOPAZHP_TOP_CR_DMAC_MTX_SELECT 0 + +#define MASK_TOPAZHP_TOP_CR_WRITES_MTX_ALL 0x40000000 +#define SHIFT_TOPAZHP_TOP_CR_WRITES_MTX_ALL 30 +#define REGNUM_TOPAZHP_TOP_CR_WRITES_MTX_ALL 0x0050 +#define SIGNED_TOPAZHP_TOP_CR_WRITES_MTX_ALL 0 + +#define MASK_TOPAZHP_TOP_CR_WRITES_CORE_ALL 0x80000000 +#define SHIFT_TOPAZHP_TOP_CR_WRITES_CORE_ALL 31 +#define REGNUM_TOPAZHP_TOP_CR_WRITES_CORE_ALL 0x0050 +#define SIGNED_TOPAZHP_TOP_CR_WRITES_CORE_ALL 0 + +/* Register CR_TOPAZHP_AUTO_CLOCK_GATING */ +#define TOPAZHP_CR_TOPAZHP_AUTO_CLOCK_GATING 0x0024 +#define MASK_TOPAZHP_CR_TOPAZHP_IPE0_AUTO_CLK_GATE 0x00000001 +#define SHIFT_TOPAZHP_CR_TOPAZHP_IPE0_AUTO_CLK_GATE 0 +#define REGNUM_TOPAZHP_CR_TOPAZHP_IPE0_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_IPE0_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_IPE1_AUTO_CLK_GATE 0x00000002 +#define SHIFT_TOPAZHP_CR_TOPAZHP_IPE1_AUTO_CLK_GATE 1 +#define REGNUM_TOPAZHP_CR_TOPAZHP_IPE1_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_IPE1_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_SPE0_AUTO_CLK_GATE 0x00000004 +#define SHIFT_TOPAZHP_CR_TOPAZHP_SPE0_AUTO_CLK_GATE 2 +#define REGNUM_TOPAZHP_CR_TOPAZHP_SPE0_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_SPE0_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_SPE1_AUTO_CLK_GATE 0x00000008 +#define SHIFT_TOPAZHP_CR_TOPAZHP_SPE1_AUTO_CLK_GATE 3 +#define REGNUM_TOPAZHP_CR_TOPAZHP_SPE1_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_SPE1_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP4X4_AUTO_CLK_GATE 0x00000010 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP4X4_AUTO_CLK_GATE 4 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP4X4_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP4X4_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP8X8_AUTO_CLK_GATE 0x00000020 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP8X8_AUTO_CLK_GATE 5 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP8X8_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP8X8_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP16X16_AUTO_CLK_GATE 0x00000040 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP16X16_AUTO_CLK_GATE 6 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP16X16_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP16X16_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_JMCOMP_AUTO_CLK_GATE 0x00000080 +#define SHIFT_TOPAZHP_CR_TOPAZHP_JMCOMP_AUTO_CLK_GATE 7 +#define REGNUM_TOPAZHP_CR_TOPAZHP_JMCOMP_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_JMCOMP_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_VLC_AUTO_CLK_GATE 0x00000200 +#define SHIFT_TOPAZHP_CR_TOPAZHP_VLC_AUTO_CLK_GATE 9 +#define REGNUM_TOPAZHP_CR_TOPAZHP_VLC_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_VLC_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_DEB_AUTO_CLK_GATE 0x00000400 +#define SHIFT_TOPAZHP_CR_TOPAZHP_DEB_AUTO_CLK_GATE 10 +#define REGNUM_TOPAZHP_CR_TOPAZHP_DEB_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_DEB_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PC_DM_AUTO_CLK_GATE 0x00000800 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PC_DM_AUTO_CLK_GATE 11 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PC_DM_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PC_DM_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PC_DMS_AUTO_CLK_GATE 0x00001000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PC_DMS_AUTO_CLK_GATE 12 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PC_DMS_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PC_DMS_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_CABAC_AUTO_CLK_GATE 0x00002000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_CABAC_AUTO_CLK_GATE 13 +#define REGNUM_TOPAZHP_CR_TOPAZHP_CABAC_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_CABAC_AUTO_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_AUTO_CLK_GATE 0x00008000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_AUTO_CLK_GATE 15 +#define REGNUM_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_AUTO_CLK_GATE 0x0024 +#define SIGNED_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_AUTO_CLK_GATE 0 + +/* Register CR_TOPAZHP_MAN_CLOCK_GATING */ +#define TOPAZHP_CR_TOPAZHP_MAN_CLOCK_GATING 0x0028 +#define MASK_TOPAZHP_CR_TOPAZHP_IPE0_MAN_CLK_GATE 0x00000001 +#define SHIFT_TOPAZHP_CR_TOPAZHP_IPE0_MAN_CLK_GATE 0 +#define REGNUM_TOPAZHP_CR_TOPAZHP_IPE0_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_IPE0_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_IPE1_MAN_CLK_GATE 0x00000002 +#define SHIFT_TOPAZHP_CR_TOPAZHP_IPE1_MAN_CLK_GATE 1 +#define REGNUM_TOPAZHP_CR_TOPAZHP_IPE1_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_IPE1_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_SPE0_MAN_CLK_GATE 0x00000004 +#define SHIFT_TOPAZHP_CR_TOPAZHP_SPE0_MAN_CLK_GATE 2 +#define REGNUM_TOPAZHP_CR_TOPAZHP_SPE0_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_SPE0_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_SPE1_MAN_CLK_GATE 0x00000008 +#define SHIFT_TOPAZHP_CR_TOPAZHP_SPE1_MAN_CLK_GATE 3 +#define REGNUM_TOPAZHP_CR_TOPAZHP_SPE1_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_SPE1_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP4X4_MAN_CLK_GATE 0x00000010 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP4X4_MAN_CLK_GATE 4 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP4X4_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP4X4_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP8X8_MAN_CLK_GATE 0x00000020 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP8X8_MAN_CLK_GATE 5 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP8X8_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP8X8_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP16X16_MAN_CLK_GATE 0x00000040 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP16X16_MAN_CLK_GATE 6 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP16X16_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP16X16_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_JMCOMP_MAN_CLK_GATE 0x00000080 +#define SHIFT_TOPAZHP_CR_TOPAZHP_JMCOMP_MAN_CLK_GATE 7 +#define REGNUM_TOPAZHP_CR_TOPAZHP_JMCOMP_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_JMCOMP_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PREFETCH_MAN_CLK_GATE 0x00000100 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PREFETCH_MAN_CLK_GATE 8 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PREFETCH_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PREFETCH_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_VLC_MAN_CLK_GATE 0x00000200 +#define SHIFT_TOPAZHP_CR_TOPAZHP_VLC_MAN_CLK_GATE 9 +#define REGNUM_TOPAZHP_CR_TOPAZHP_VLC_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_VLC_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_DEB_MAN_CLK_GATE 0x00000400 +#define SHIFT_TOPAZHP_CR_TOPAZHP_DEB_MAN_CLK_GATE 10 +#define REGNUM_TOPAZHP_CR_TOPAZHP_DEB_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_DEB_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PC_DM_MAN_CLK_GATE 0x00000800 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PC_DM_MAN_CLK_GATE 11 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PC_DM_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PC_DM_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PC_DMS_MAN_CLK_GATE 0x00001000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PC_DMS_MAN_CLK_GATE 12 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PC_DMS_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PC_DMS_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_CABAC_MAN_CLK_GATE 0x00002000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_CABAC_MAN_CLK_GATE 13 +#define REGNUM_TOPAZHP_CR_TOPAZHP_CABAC_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_CABAC_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_LRITC_MAN_CLK_GATE 0x00004000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_LRITC_MAN_CLK_GATE 14 +#define REGNUM_TOPAZHP_CR_TOPAZHP_LRITC_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_LRITC_MAN_CLK_GATE 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_MAN_CLK_GATE 0x00008000 +#define SHIFT_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_MAN_CLK_GATE 15 +#define REGNUM_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_MAN_CLK_GATE 0x0028 +#define SIGNED_TOPAZHP_CR_TOPAZHP_INPUT_SCALER_MAN_CLK_GATE 0 + +/* Register CR_TOPAZHP_SRST */ +#define TOPAZHP_CR_TOPAZHP_SRST 0x0000 +#define MASK_TOPAZHP_CR_TOPAZHP_IPE_SOFT_RESET 0x00000001 +#define SHIFT_TOPAZHP_CR_TOPAZHP_IPE_SOFT_RESET 0 +#define REGNUM_TOPAZHP_CR_TOPAZHP_IPE_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_IPE_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_SPE_SOFT_RESET 0x00000002 +#define SHIFT_TOPAZHP_CR_TOPAZHP_SPE_SOFT_RESET 1 +#define REGNUM_TOPAZHP_CR_TOPAZHP_SPE_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_SPE_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PC_SOFT_RESET 0x00000004 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PC_SOFT_RESET 2 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PC_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PC_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_H264COMP_SOFT_RESET 0x00000008 +#define SHIFT_TOPAZHP_CR_TOPAZHP_H264COMP_SOFT_RESET 3 +#define REGNUM_TOPAZHP_CR_TOPAZHP_H264COMP_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_H264COMP_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_JMCOMP_SOFT_RESET 0x00000010 +#define SHIFT_TOPAZHP_CR_TOPAZHP_JMCOMP_SOFT_RESET 4 +#define REGNUM_TOPAZHP_CR_TOPAZHP_JMCOMP_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_JMCOMP_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_PREFETCH_SOFT_RESET 0x00000020 +#define SHIFT_TOPAZHP_CR_TOPAZHP_PREFETCH_SOFT_RESET 5 +#define REGNUM_TOPAZHP_CR_TOPAZHP_PREFETCH_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_PREFETCH_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_VLC_SOFT_RESET 0x00000040 +#define SHIFT_TOPAZHP_CR_TOPAZHP_VLC_SOFT_RESET 6 +#define REGNUM_TOPAZHP_CR_TOPAZHP_VLC_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_VLC_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_DB_SOFT_RESET 0x00000080 +#define SHIFT_TOPAZHP_CR_TOPAZHP_DB_SOFT_RESET 7 +#define REGNUM_TOPAZHP_CR_TOPAZHP_DB_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_DB_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_LTRITC_SOFT_RESET 0x00000100 +#define SHIFT_TOPAZHP_CR_TOPAZHP_LTRITC_SOFT_RESET 8 +#define REGNUM_TOPAZHP_CR_TOPAZHP_LTRITC_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_LTRITC_SOFT_RESET 0 + +#define MASK_TOPAZHP_CR_TOPAZHP_SCALER_SOFT_RESET 0x00000200 +#define SHIFT_TOPAZHP_CR_TOPAZHP_SCALER_SOFT_RESET 9 +#define REGNUM_TOPAZHP_CR_TOPAZHP_SCALER_SOFT_RESET 0x0000 +#define SIGNED_TOPAZHP_CR_TOPAZHP_SCALER_SOFT_RESET 0 + +/* Register CR_MMU_STATUS */ +#define TOPAZHP_TOP_CR_MMU_STATUS 0x001C +#define MASK_TOPAZHP_TOP_CR_MMU_PF_N_RW 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_MMU_PF_N_RW 0 +#define REGNUM_TOPAZHP_TOP_CR_MMU_PF_N_RW 0x001C +#define SIGNED_TOPAZHP_TOP_CR_MMU_PF_N_RW 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_FAULT_ADDR 0xFFFFF000 +#define SHIFT_TOPAZHP_TOP_CR_MMU_FAULT_ADDR 12 +#define REGNUM_TOPAZHP_TOP_CR_MMU_FAULT_ADDR 0x001C +#define SIGNED_TOPAZHP_TOP_CR_MMU_FAULT_ADDR 0 + +/* Register CR_MMU_MEM_REQ */ +#define TOPAZHP_TOP_CR_MMU_MEM_REQ 0x0020 +#define MASK_TOPAZHP_TOP_CR_MEM_REQ_STAT_READS 0x000000FF +#define SHIFT_TOPAZHP_TOP_CR_MEM_REQ_STAT_READS 0 +#define REGNUM_TOPAZHP_TOP_CR_MEM_REQ_STAT_READS 0x0020 +#define SIGNED_TOPAZHP_TOP_CR_MEM_REQ_STAT_READS 0 + +/* Register CR_MMU_CONTROL0 */ +#define TOPAZHP_TOP_CR_MMU_CONTROL0 0x0024 +#define MASK_TOPAZHP_TOP_CR_MMU_NOREORDER 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_MMU_NOREORDER 0 +#define REGNUM_TOPAZHP_TOP_CR_MMU_NOREORDER 0x0024 +#define SIGNED_TOPAZHP_TOP_CR_MMU_NOREORDER 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_PAUSE 0x00000002 +#define SHIFT_TOPAZHP_TOP_CR_MMU_PAUSE 1 +#define REGNUM_TOPAZHP_TOP_CR_MMU_PAUSE 0x0024 +#define SIGNED_TOPAZHP_TOP_CR_MMU_PAUSE 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_FLUSH 0x00000004 +#define SHIFT_TOPAZHP_TOP_CR_MMU_FLUSH 2 +#define REGNUM_TOPAZHP_TOP_CR_MMU_FLUSH 0x0024 +#define SIGNED_TOPAZHP_TOP_CR_MMU_FLUSH 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_INVALDC 0x00000008 +#define SHIFT_TOPAZHP_TOP_CR_MMU_INVALDC 3 +#define REGNUM_TOPAZHP_TOP_CR_MMU_INVALDC 0x0024 +#define SIGNED_TOPAZHP_TOP_CR_MMU_INVALDC 0 + +#define MASK_TOPAZHP_TOP_CR_FLOWRATE_TOPAZ 0x00000700 +#define SHIFT_TOPAZHP_TOP_CR_FLOWRATE_TOPAZ 8 +#define REGNUM_TOPAZHP_TOP_CR_FLOWRATE_TOPAZ 0x0024 +#define SIGNED_TOPAZHP_TOP_CR_FLOWRATE_TOPAZ 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ 0x00010000 +#define SHIFT_TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ 16 +#define REGNUM_TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ 0x0024 +#define SIGNED_TOPAZHP_TOP_CR_MMU_BYPASS_TOPAZ 0 + +/* Register CR_MMU_CONTROL1 */ +#define TOPAZHP_TOP_CR_MMU_CONTROL1 0x0028 +#define MASK_TOPAZHP_TOP_CR_MMU_TTE_THRESHOLD 0x00000FFF +#define SHIFT_TOPAZHP_TOP_CR_MMU_TTE_THRESHOLD 0 +#define REGNUM_TOPAZHP_TOP_CR_MMU_TTE_THRESHOLD 0x0028 +#define SIGNED_TOPAZHP_TOP_CR_MMU_TTE_THRESHOLD 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_ADT_TTE 0x000FF000 +#define SHIFT_TOPAZHP_TOP_CR_MMU_ADT_TTE 12 +#define REGNUM_TOPAZHP_TOP_CR_MMU_ADT_TTE 0x0028 +#define SIGNED_TOPAZHP_TOP_CR_MMU_ADT_TTE 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_BEST_COUNT 0x0FF00000 +#define SHIFT_TOPAZHP_TOP_CR_MMU_BEST_COUNT 20 +#define REGNUM_TOPAZHP_TOP_CR_MMU_BEST_COUNT 0x0028 +#define SIGNED_TOPAZHP_TOP_CR_MMU_BEST_COUNT 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_PAGE_SIZE 0xF0000000 +#define SHIFT_TOPAZHP_TOP_CR_MMU_PAGE_SIZE 28 +#define REGNUM_TOPAZHP_TOP_CR_MMU_PAGE_SIZE 0x0028 +#define SIGNED_TOPAZHP_TOP_CR_MMU_PAGE_SIZE 0 + +/* Register CR_MMU_CONTROL2 */ +#define TOPAZHP_TOP_CR_MMU_CONTROL2 0x002C +#define MASK_TOPAZHP_TOP_CR_MMU_ENABLE_36BIT_ADDRESSING 0x00000001 +#define SHIFT_TOPAZHP_TOP_CR_MMU_ENABLE_36BIT_ADDRESSING 0 +#define REGNUM_TOPAZHP_TOP_CR_MMU_ENABLE_36BIT_ADDRESSING 0x002C +#define SIGNED_TOPAZHP_TOP_CR_MMU_ENABLE_36BIT_ADDRESSING 0 + +#define MASK_TOPAZHP_TOP_CR_MMU_TILING_SCHEME 0x00000008 +#define SHIFT_TOPAZHP_TOP_CR_MMU_TILING_SCHEME 3 +#define REGNUM_TOPAZHP_TOP_CR_MMU_TILING_SCHEME 0x002C +#define SIGNED_TOPAZHP_TOP_CR_MMU_TILING_SCHEME 0 + +/* Table MMU_DIR_LIST_BASE */ + +/* Register CR_MMU_DIR_LIST_BASE */ +#define TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE(X) (0x0030 + (4 * (X))) +#define MASK_TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE_ADDR 0xFFFFFFF0 +#define SHIFT_TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE_ADDR 4 +#define REGNUM_TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE_ADDR 0x0030 +#define SIGNED_TOPAZHP_TOP_CR_MMU_DIR_LIST_BASE_ADDR 0 + +/* Number of entries in table MMU_DIR_LIST_BASE */ + +#define TOPAZHP_TOP_MMU_DIR_LIST_BASE_SIZE_UINT32 1 +#define TOPAZHP_TOP_MMU_DIR_LIST_BASE_NUM_ENTRIES 1 + +/* Table MMU_TILE */ + +/* Register CR_MMU_TILE */ +#define TOPAZHP_TOP_CR_MMU_TILE(X) (0x0038 + (4 * (X))) +#define MASK_TOPAZHP_TOP_CR_TILE_MIN_ADDR 0x00000FFF +#define SHIFT_TOPAZHP_TOP_CR_TILE_MIN_ADDR 0 +#define REGNUM_TOPAZHP_TOP_CR_TILE_MIN_ADDR 0x0038 +#define SIGNED_TOPAZHP_TOP_CR_TILE_MIN_ADDR 0 + +#define MASK_TOPAZHP_TOP_CR_TILE_MAX_ADDR 0x00FFF000 +#define SHIFT_TOPAZHP_TOP_CR_TILE_MAX_ADDR 12 +#define REGNUM_TOPAZHP_TOP_CR_TILE_MAX_ADDR 0x0038 +#define SIGNED_TOPAZHP_TOP_CR_TILE_MAX_ADDR 0 + +#define MASK_TOPAZHP_TOP_CR_TILE_STRIDE 0x07000000 +#define SHIFT_TOPAZHP_TOP_CR_TILE_STRIDE 24 +#define REGNUM_TOPAZHP_TOP_CR_TILE_STRIDE 0x0038 +#define SIGNED_TOPAZHP_TOP_CR_TILE_STRIDE 0 + +#define MASK_TOPAZHP_TOP_CR_TILE_ENABLE 0x10000000 +#define SHIFT_TOPAZHP_TOP_CR_TILE_ENABLE 28 +#define REGNUM_TOPAZHP_TOP_CR_TILE_ENABLE 0x0038 +#define SIGNED_TOPAZHP_TOP_CR_TILE_ENABLE 0 + +#define MASK_TOPAZHP_TOP_CR_TILE_128BYTE_INTERLEAVE 0x20000000 +#define SHIFT_TOPAZHP_TOP_CR_TILE_128BYTE_INTERLEAVE 29 +#define REGNUM_TOPAZHP_TOP_CR_TILE_128BYTE_INTERLEAVE 0x0038 +#define SIGNED_TOPAZHP_TOP_CR_TILE_128BYTE_INTERLEAVE 0 + +/* Number of entries in table MMU_TILE */ + +#define TOPAZHP_TOP_MMU_TILE_SIZE_UINT32 2 +#define TOPAZHP_TOP_MMU_TILE_NUM_ENTRIES 2 + +/* Table MMU_TILE_EXT */ + +/* Register CR_MMU_TILE_EXT */ +#define TOPAZHP_TOP_CR_MMU_TILE_EXT(X) (0x0080 + (4 * (X))) +#define MASK_TOPAZHP_TOP_CR_TILE_MIN_ADDR_EXT 0x000000FF +#define SHIFT_TOPAZHP_TOP_CR_TILE_MIN_ADDR_EXT 0 +#define REGNUM_TOPAZHP_TOP_CR_TILE_MIN_ADDR_EXT 0x0080 +#define SIGNED_TOPAZHP_TOP_CR_TILE_MIN_ADDR_EXT 0 + +#define MASK_TOPAZHP_TOP_CR_TILE_MAX_ADDR_EXT 0x0000FF00 +#define SHIFT_TOPAZHP_TOP_CR_TILE_MAX_ADDR_EXT 8 +#define REGNUM_TOPAZHP_TOP_CR_TILE_MAX_ADDR_EXT 0x0080 +#define SIGNED_TOPAZHP_TOP_CR_TILE_MAX_ADDR_EXT 0 + +/* Number of entries in table MMU_TILE_EXT */ + +#define TOPAZHP_TOP_MMU_TILE_EXT_SIZE_UINT32 2 +#define TOPAZHP_TOP_MMU_TILE_EXT_NUM_ENTRIES 2 + +#define TOPAZHP_CR_PROC_ESB_ACCESS_WORD0 0x00F0 + +/* Register CR_PROC_ESB_ACCESS_CONTROL */ +#define TOPAZHP_CR_PROC_ESB_ACCESS_CONTROL 0x00EC +#define MASK_TOPAZHP_CR_PROC_ESB_ADDR 0x00003FF0 +#define SHIFT_TOPAZHP_CR_PROC_ESB_ADDR 4 +#define REGNUM_TOPAZHP_CR_PROC_ESB_ADDR 0x00EC +#define SIGNED_TOPAZHP_CR_PROC_ESB_ADDR 0 + +#define MASK_TOPAZHP_CR_PROC_ESB_READ_N_WRITE 0x00010000 +#define SHIFT_TOPAZHP_CR_PROC_ESB_READ_N_WRITE 16 +#define REGNUM_TOPAZHP_CR_PROC_ESB_READ_N_WRITE 0x00EC +#define SIGNED_TOPAZHP_CR_PROC_ESB_READ_N_WRITE 0 + +#define MASK_TOPAZHP_CR_PROC_ESB_OP_VALID 0x00020000 +#define SHIFT_TOPAZHP_CR_PROC_ESB_OP_VALID 17 +#define REGNUM_TOPAZHP_CR_PROC_ESB_OP_VALID 0x00EC +#define SIGNED_TOPAZHP_CR_PROC_ESB_OP_VALID 0 + +#define MASK_TOPAZHP_CR_PROC_ACCESS_FLAG 0x03000000 +#define SHIFT_TOPAZHP_CR_PROC_ACCESS_FLAG 24 +#define REGNUM_TOPAZHP_CR_PROC_ACCESS_FLAG 0x00EC +#define SIGNED_TOPAZHP_CR_PROC_ACCESS_FLAG 0 + +/* Register CR_SECURE_CONFIG */ +#define TOPAZHP_TOP_CR_SECURE_CONFIG 0x0200 + +/* Register CR_VLC_MPEG4_CFG */ +#define TOPAZ_VLC_CR_VLC_MPEG4_CFG 0x0064 +#define MASK_TOPAZ_VLC_CR_RSIZE 0x00000007 +#define SHIFT_TOPAZ_VLC_CR_RSIZE 0 +#define REGNUM_TOPAZ_VLC_CR_RSIZE 0x0064 +#define SIGNED_TOPAZ_VLC_CR_RSIZE 0 + +/* RC Config registers and tables */ +#define TOPAZHP_TOP_CR_RC_CONFIG_TABLE7 0x012C +#define TOPAZHP_TOP_CR_RC_CONFIG_TABLE6 0x0124 +#define TOPAZHP_TOP_CR_RC_CONFIG_TABLE4 0x0128 +#define TOPAZHP_TOP_CR_RC_CONFIG_TABLE5 0x0130 +#define TOPAZHP_CR_RC_CONFIG_REG8 0x0344 +#define TOPAZHP_CR_RC_CONFIG_REG9 0x0184 +#define TOPAZHP_CR_JMCOMP_RC_STATS 0x0340 + +/* Register CR_TOPAZHP_CORE_DES1 */ +#define TOPAZHP_TOP_CR_TOPAZHP_CORE_DES1 0x03E0 +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_SCALER_SUPPORTED 0x00000080 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_SCALER_SUPPORTED 7 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_SCALER_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_SCALER_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_GENERATE_PERFORMANCE_STORE 0x00000100 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_GENERATE_PERFORMANCE_STORE 8 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_GENERATE_PERFORMANCE_STORE 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_GENERATE_PERFORMANCE_STORE 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_LOSSLESS_SUPPORTED 0x00000200 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_LOSSLESS_SUPPORTED 9 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_LOSSLESS_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_LOSSLESS_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_CUSTOM_QUANT_SUPPORTED 0x00000400 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_CUSTOM_QUANT_SUPPORTED 10 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_CUSTOM_QUANT_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_CUSTOM_QUANT_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MPEG2_SUPPORTED 0x00000800 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MPEG2_SUPPORTED 11 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MPEG2_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MPEG2_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_SUBSET 0x00001000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_SUBSET 12 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_SUBSET 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_SUBSET 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_ALL 0x00002000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_ALL 13 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_ALL 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_SIGNATURES_SUPPORTED_ALL 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_ME_SUPPORTED 0x00004000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_ME_SUPPORTED 14 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_ME_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_ME_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_SUPPORTED 0x00008000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_SUPPORTED 15 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_WEIGHTED_PRED_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_2_REF_ON_P_PIC_SUPPORTED 0x00010000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_2_REF_ON_P_PIC_SUPPORTED 16 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_2_REF_ON_P_PIC_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_2_REF_ON_P_PIC_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED 0x00020000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED 17 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_SPATIAL_DIRECT_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_MULTIPASS_SUPPORTED 0x00040000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_MULTIPASS_SUPPORTED 18 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_MULTIPASS_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_MULTIPASS_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_DEFAULT_TABLES_SUPPORTED 0x00080000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_DEFAULT_TABLES_SUPPORTED 19 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_DEFAULT_TABLES_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_DEFAULT_TABLES_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_8X8_TRANSFORM_SUPPORTED 0x00100000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_8X8_TRANSFORM_SUPPORTED 20 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_8X8_TRANSFORM_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_8X8_TRANSFORM_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_INTERLACED_SUPPORTED 0x00200000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_INTERLACED_SUPPORTED 21 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_INTERLACED_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_INTERLACED_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED 0x00400000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED 22 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_B_PIC_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_16X8_8X16_SUPPORTED 0x00800000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_16X8_8X16_SUPPORTED 23 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_16X8_8X16_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_16X8_8X16_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_CABAC_SUPPORTED 0x01000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_CABAC_SUPPORTED 24 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_CABAC_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_CABAC_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_SLAVE_JPEG_SUPPORTED 0x02000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_SLAVE_JPEG_SUPPORTED 25 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_SLAVE_JPEG_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_SLAVE_JPEG_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_JPEG_SUPPORTED 0x04000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_JPEG_SUPPORTED 26 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_JPEG_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_JPEG_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H263_SUPPORTED 0x08000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H263_SUPPORTED 27 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H263_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H263_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MPEG4_SUPPORTED 0x10000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MPEG4_SUPPORTED 28 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MPEG4_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MPEG4_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_H264_SUPPORTED 0x20000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_H264_SUPPORTED 29 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_H264_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_H264_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_DMAC_SUPPORTED 0x40000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_DMAC_SUPPORTED 30 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_DMAC_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_DMAC_SUPPORTED 0 + +#define MASK_TOPAZHP_TOP_CR_TOPAZHP_MMU_SUPPORTED 0x80000000 +#define SHIFT_TOPAZHP_TOP_CR_TOPAZHP_MMU_SUPPORTED 31 +#define REGNUM_TOPAZHP_TOP_CR_TOPAZHP_MMU_SUPPORTED 0x03E0 +#define SIGNED_TOPAZHP_TOP_CR_TOPAZHP_MMU_SUPPORTED 0 +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.c b/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.c --- a/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1936 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMG Encoder v4l2 Driver Interface function implementations + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * David Huang + * + * Re-written for upstreaming + * Sidraya Jayagond + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fw_headers/vxe_common.h" +#include "img_mem_man.h" +#include "target_config.h" +#include "topaz_device.h" +#include "vxe_enc.h" +#include "vxe_v4l2.h" +#include "img_errors.h" + +#define IMG_VXE_ENC_MODULE_NAME "vxe-enc" + +static struct heap_config vxe_enc_heap_configs[] = { + { + .type = MEM_HEAP_TYPE_UNIFIED, + .options.unified = { + .gfp_type = __GFP_DMA32 | __GFP_ZERO, + }, + .to_dev_addr = NULL, + }, +}; + +static struct vxe_enc_fmt vxe_enc_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .fmt = IMG_CODEC_420_PL12, + .min_bufs = 2, + .size_num[0] = 3, + .size_den[0] = 2, + .bytes_pp = 1, + .csc_preset = IMG_CSC_NONE, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .fmt = IMG_CODEC_ABCX, + .min_bufs = 2, + .size_num[0] = 1, + .size_den[0] = 1, + .bytes_pp = 4, + .csc_preset = IMG_CSC_RGB_TO_601_ANALOG, + }, + { + .fourcc = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .std = IMG_STANDARD_H264, + .min_bufs = 1, + .size_num[0] = 1, + .size_den[0] = 1, + .bytes_pp = 1, + .csc_preset = IMG_CSC_NONE, + }, +}; + +/* Note: Arrange in order of ascending CID # to simplify QUERYCTRL */ +static struct vxe_ctrl controls[] = { + { + /* + * idr_period + * + * Period between IDR frames. Default to 60 * framerate. + * Since default framerate is 30fps, default to 1800 frames + * between IDR frames. IDR frames are a special I frame in + * H.264 that specifies no frame after the IDR frame can + * reference any frame before the IDR frame. + * + * This period is in number of frames. + * ex. Default: 1800 + * Every 1800 frames is an IDR frame. At 30fps this means there + * is an IDR frame every 60 seconds. + */ + .cid = V4L2_CID_MPEG_VIDEO_GOP_SIZE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "GOP size", + .minimum = 1, + .maximum = 7200, + .step = 1, + .default_value = 1800, + .compound = FALSE, + }, + { + /* + * bits_per_second + * + * Bits per second for the encode. This will be the final + * bitrate of the encoded stream. Warning, setting this too + * low results in extreme loss of quality and choppy output. + * + * This is specified in bits per second + */ + .cid = V4L2_CID_MPEG_VIDEO_BITRATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Video Bitrate", + .minimum = 50000, + .maximum = 100000000, + .step = 1, + .default_value = 500000, + .compound = FALSE, + }, + { + /* + * intra_freq + * + * Period between I-frames. I-frames are complete frames that + * do not need to reference any other frames to decode. Named + * intra_freq instead of intra_period due to naming in + * underlying topaz_api layers. + * + * This frequency is actually the period between I-frames. + * ex. Default: 30 + * This means there is an I-frame every 30 frames. At 30fps + * this would mean one I-frame every second. + */ + .cid = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "H264 I period", + .minimum = 1, + .maximum = 600, + .step = 1, + .default_value = 30, + .compound = FALSE, + }, +}; + +static struct v4l2_fract frmivals[] = { + { + .numerator = 1, + .denominator = 15, + }, + { + .numerator = 1, + .denominator = 30, + }, + { + .numerator = 1, + .denominator = 45, + }, + { + .numerator = 1, + .denominator = 60, + }, +}; + +static struct vxe_enc_ctx *file2ctx(struct file *file) +{ + return container_of(file->private_data, struct vxe_enc_ctx, fh); +} + +static void vxe_eos(struct vxe_enc_ctx *ctx) +{ + struct v4l2_event event = {}; + struct vb2_v4l2_buffer *vb; + + event.type = V4L2_EVENT_EOS; + v4l2_event_queue_fh(&ctx->fh, &event); + /* + * If a capture buffer is available, dequeue with FLAG_LAST + * else, mark for next qbuf to handle + */ + if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0) { + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + vb->flags |= V4L2_BUF_FLAG_LAST; + vb2_set_plane_payload(&vb->vb2_buf, 0, 0); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE); + } else { + ctx->flag_last = TRUE; + } + + topaz_flush_stream(ctx->topaz_str_context, ctx->last_frame_num); +} + +static void vxe_return_resource(void *ctx_handle, enum vxe_cb_type type, + void *img_buf_ref, unsigned int size, + unsigned int coded_frm_cnt) +{ + struct vxe_enc_ctx *ctx = ctx_handle; + struct device *dev = ctx->dev->dev; + struct vxe_buffer *buf; +#ifdef ENABLE_PROFILING + struct timespec64 time; +#endif + + switch (type) { + case VXE_CB_CODED_BUFF_READY: + if (!img_buf_ref) + dev_err(dev, "VXE_CB_STRUNIT_PROCESSED had no buffer\n"); + + buf = container_of((struct img_coded_buffer *)img_buf_ref, + struct vxe_buffer, coded_buffer); + vb2_set_plane_payload(&buf->buffer.vb.vb2_buf, 0, size); +#ifdef ENABLE_PROFILING + ktime_get_real_ts64(&time); + ctx->drv_lat.end_time = timespec64_to_ns((const struct timespec64 *)&time); + + pr_err("driver encode time is %llu us\n", div_s64(ctx->drv_lat.end_time - + ctx->drv_lat.start_time, 1000)); +#endif + + v4l2_m2m_buf_done(&buf->buffer.vb, VB2_BUF_STATE_DONE); + + if (coded_frm_cnt == ctx->last_frame_num) + vxe_eos(ctx); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + break; + case VXE_CB_SRC_FRAME_RELEASE: + if (!img_buf_ref) + dev_err(dev, "VXE_CB_PICT_RELEASE had no buffer\n"); + + buf = container_of((struct img_frame *)img_buf_ref, struct vxe_buffer, src_frame); + vb2_set_plane_payload(&buf->buffer.vb.vb2_buf, 0, size); + v4l2_m2m_buf_done(&buf->buffer.vb, VB2_BUF_STATE_DONE); + ctx->frames_encoding--; + break; + case VXE_CB_ERROR_FATAL: + break; + default: + break; + } +} + +static void device_run(void *priv) +{ + struct vxe_enc_ctx *ctx = priv; + struct device *dev = ctx->dev->dev; + struct vb2_v4l2_buffer *dst_vbuf, *src_vbuf; + struct vxe_buffer *buf; + int ret = 0; +#ifdef ENABLE_PROFILING + struct timespec64 time; +#endif + + mutex_lock_nested(ctx->mutex, SUBCLASS_VXE_V4L2); + while (((topaz_query_empty_coded_slots(ctx->topaz_str_context) > 0) && + (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0)) && + ((topaz_query_empty_source_slots(ctx->topaz_str_context) > 0) && + (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0))) { +#ifdef ENABLE_PROFILING + ktime_get_real_ts64(&time); + ctx->drv_lat.start_time = timespec64_to_ns((const struct timespec64 *)&time); +#endif + /* + * Submit src and dst buffers one to one + * Note: Will have to revisit for B frame support + */ + dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!dst_vbuf) + dev_err(dev, "Next src buffer is null\n"); + + src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (!src_vbuf) + dev_err(dev, "Next src buffer is null\n"); + + /* Handle EOS */ + if (ctx->eos && (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0)) { + pr_debug("%s eos found\n", __func__); + ret = topaz_end_of_stream(ctx->topaz_str_context, ctx->frame_num + 1); + if (ret) + dev_err(dev, "Failed to send EOS to topaz %d\n", + ret); + ctx->last_frame_num = ctx->frame_num + 1; + } + + /* Submit coded package */ + buf = container_of(dst_vbuf, struct vxe_buffer, buffer.vb); + ret = topaz_reserve_coded_package_slot(ctx->topaz_str_context); + if (ret) + dev_err(dev, "Failed to reserve coded package slot %d\n", ret); + ret = topaz_send_coded_package(ctx->topaz_str_context, &buf->coded_buffer); + if (ret) + dev_err(dev, "Failed to send coded package %d\n", + ret); + if (!ret) + ctx->available_coded_packages++; + + /* Submit source frame */ + buf = container_of(src_vbuf, struct vxe_buffer, buffer.vb); + ret = topaz_reserve_source_slot(ctx->topaz_str_context, &buf->src_slot_num); + if (ret) + dev_err(dev, "Failed to reserve source slot %d\n", + ret); + ret = topaz_send_source_frame(ctx->topaz_str_context, &buf->src_frame, + ctx->frame_num, (unsigned long long)ctx); + if (ret) + dev_err(dev, "Failed to send source frame %d\n", + ret); + ctx->frame_num++; + if (!ret) + ctx->available_source_frames++; + } + + while ((ctx->available_source_frames > 0) && (ctx->available_coded_packages > 0)) { + pr_debug("Calling topaz_encode_frame #src=%d #coded=%d frames_encoding=%d\n", + ctx->available_source_frames, + ctx->available_coded_packages, + ctx->frames_encoding); + ret = topaz_encode_frame(ctx->topaz_str_context); + if (ret) { + dev_err(dev, "Failed to send encode_frame command %d\n", + ret); + } else { + /* TODO: Account for scenarios where these are not 1 */ + ctx->available_source_frames--; + ctx->available_coded_packages--; + ctx->frames_encoding++; + } + } + + mutex_unlock((struct mutex *)ctx->mutex); +} + +static int job_ready(void *priv) +{ + struct vxe_enc_ctx *ctx = priv; + + /* + * In normal play, check if we can + * submit any source or coded buffers + */ + if (((topaz_query_empty_source_slots(ctx->topaz_str_context) > 0) && + (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0)) && + ((topaz_query_empty_coded_slots(ctx->topaz_str_context) > 0) && + (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0))) + return 1; + + /* + * In EOS state, we only need to know + * that coded buffers are available + */ + if (ctx->eos && (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0) && + (topaz_query_empty_coded_slots(ctx->topaz_str_context) > 0)) + return 1; + + /* + * Since we're allowing device_run for both submissions and actual + * encodes, say job ready if buffers are ready in fw + */ + if (ctx->available_source_frames > 0 && ctx->available_coded_packages > 0) + return 1; + + return 0; +} + +static void job_abort(void *priv) +{ + /* TODO: stub */ +} + +static const struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_ready = job_ready, + .job_abort = job_abort, +}; + +static struct vxe_enc_q_data *get_queue(struct vxe_enc_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return &ctx->out_queue; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return &ctx->cap_queue; + default: + return NULL; + } + return NULL; +} + +static int vxe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + int i; + struct vxe_enc_ctx *ctx = vb2_get_drv_priv(vq); + struct vxe_enc_q_data *queue; + + queue = get_queue(ctx, vq->type); + if (!queue) + return -EINVAL; + + if (*nplanes) { + /* This is being called from CREATEBUFS, perform validation */ + if (*nplanes != queue->fmt->num_planes) + return -EINVAL; + + for (i = 0; i < *nplanes; i++) { + if (sizes[i] != queue->size_image[i]) + return -EINVAL; + } + + return 0; + } + + *nplanes = queue->fmt->num_planes; + + if (V4L2_TYPE_IS_OUTPUT(queue->fmt->type)) { + *nbuffers = max(*nbuffers, queue->fmt->min_bufs); + } else { + *nbuffers = topaz_get_coded_package_max_num(ctx->topaz_str_context, + queue->fmt->std, + queue->width, + queue->height, + &ctx->rc); + for (i = 0; i < *nplanes; i++) { + queue->size_image[i] = + topaz_get_coded_buffer_max_size(ctx->topaz_str_context, + queue->fmt->std, + queue->width, + queue->height, + &ctx->rc); + } + } + + for (i = 0; i < *nplanes; i++) + sizes[i] = queue->size_image[i]; + + return 0; +} + +static int vxe_buf_init(struct vb2_buffer *vb) +{ + struct vxe_enc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct device *dev = ctx->dev->dev; + struct vxe_enc_q_data *queue; + void *sgt; + int i, num_planes, ret; + struct vxe_buffer *buf = container_of(vb, struct vxe_buffer, + buffer.vb.vb2_buf); + + queue = get_queue(ctx, vb->vb2_queue->type); + if (!queue) { + dev_err(dev, "Invalid queue type %d\n", + vb->vb2_queue->type); + return -EINVAL; + } + + num_planes = queue->fmt->num_planes; + + for (i = 0; i < num_planes; i++) { + if (vb2_plane_size(vb, i) < queue->size_image[i]) { + dev_err(dev, "data will not fit into plane(%lu < %lu)\n", + vb2_plane_size(vb, i), + (long)queue->size_image[i]); + return -EINVAL; + } + } + + buf->buf_info.cpu_virt = vb2_plane_vaddr(vb, 0); + buf->buf_info.buf_size = vb2_plane_size(vb, 0); + + sgt = vb2_dma_sg_plane_desc(vb, 0); + if (!sgt) { + dev_err(dev, "Could not get sg_table from plane 0\n"); + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) { + ret = topaz_stream_map_buf_sg(ctx->topaz_str_context, VENC_BUFTYPE_PICTURE, + &buf->buf_info, sgt); + if (ret) { + dev_err(dev, "OUTPUT core_stream_map_buf_sg failed\n"); + return ret; + } + pr_debug("Picture buffer mapped successfully, buf_id[%d], dev_virt[%x]\n", + buf->buf_info.buff_id, buf->buf_info.dev_virt); + + vxe_fill_default_src_frame_params(buf); + + buf->y_buffer.mem_info = buf->buf_info; + buf->y_buffer.lock = BUFFER_FREE; + buf->y_buffer.size = 0; /* IMG has 0 */ + buf->y_buffer.bytes_written = 0; + + /* TODO Fill U/V img buffers if necessary */ + buf->src_frame.y_plane_buffer = &buf->y_buffer; + buf->src_frame.u_plane_buffer = NULL; + buf->src_frame.v_plane_buffer = NULL; + buf->src_frame.y_component_offset = 0; + buf->src_frame.u_component_offset = queue->bytesperline[0] * queue->height; + buf->src_frame.v_component_offset = queue->bytesperline[0] * queue->height; + + buf->src_frame.width_bytes = queue->bytesperline[0]; + buf->src_frame.height = queue->height; + buf->src_frame.src_y_stride_bytes = queue->bytesperline[0]; + buf->src_frame.src_uv_stride_bytes = queue->bytesperline[0]; + } else { + ret = topaz_stream_map_buf_sg(ctx->topaz_str_context, + VENC_BUFTYPE_BITSTREAM, + &buf->buf_info, sgt); + if (ret) { + dev_err(dev, "CAPTURE core_stream_map_buf_sg failed\n"); + return ret; + } + pr_debug("Bit-stream buffer mapped successfully, buf_id[%d], dev_virt[%x]\n", + buf->buf_info.buff_id, buf->buf_info.dev_virt); + + buf->coded_buffer.mem_info = buf->buf_info; + buf->coded_buffer.lock = BUFFER_FREE; + buf->coded_buffer.size = vb2_plane_size(vb, 0); + buf->coded_buffer.bytes_written = 0; + } + + return 0; +} + +static int vxe_buf_prepare(struct vb2_buffer *vb) +{ +#ifdef DEBUG_ENCODER_DRIVER + int i; + struct vxe_buffer *buf = container_of(vb, struct vxe_buffer, + buffer.vb.vb2_buf); + + pr_info("%s printing contents of buffer %d at 0x%p\n", + __func__, vb->index, buf->buf_info.cpu_virt); + for (i = 0; i < 1536; i = i + 8) { + pr_info("[%d] 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x,\n", + ((i + 1) / 8), + ((char *)buf->buf_info.cpu_virt)[i + 0], + ((char *)buf->buf_info.cpu_virt)[i + 1], + ((char *)buf->buf_info.cpu_virt)[i + 2], + ((char *)buf->buf_info.cpu_virt)[i + 3], + ((char *)buf->buf_info.cpu_virt)[i + 4], + ((char *)buf->buf_info.cpu_virt)[i + 5], + ((char *)buf->buf_info.cpu_virt)[i + 6], + ((char *)buf->buf_info.cpu_virt)[i + 7]); + } +#endif + return 0; +} + +static void vxe_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vxe_enc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + mutex_lock_nested(ctx->mutex, SUBCLASS_VXE_V4L2); + if (ctx->flag_last && (!V4L2_TYPE_IS_OUTPUT(vb->type))) { + /* + * If EOS came and we did not have a buffer ready + * to service it, service now that we have a buffer + */ + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + } else { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + } + mutex_unlock((struct mutex *)ctx->mutex); +} + +static void vxe_buf_cleanup(struct vb2_buffer *vb) +{ + struct vxe_enc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vxe_buffer *buf = container_of(vb, struct vxe_buffer, + buffer.vb.vb2_buf); + + pr_debug("%s Unmapping buffer %d\n", __func__, buf->index); + topaz_stream_unmap_buf_sg(ctx->topaz_str_context, &buf->buf_info); +} + +static int vxe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vxe_enc_ctx *ctx = vb2_get_drv_priv(vq); + struct vxe_enc_q_data *queue; + + queue = get_queue(ctx, vq->type); + queue->streaming = TRUE; + + return 0; +} + +static void vxe_stop_streaming(struct vb2_queue *vq) +{ + struct vxe_enc_ctx *ctx = vb2_get_drv_priv(vq); + struct device *dev = ctx->dev->dev; + struct vb2_v4l2_buffer *vb; + + /* Unmap all buffers in v4l2 from mmu */ + while (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vb) + dev_err(dev, "Next dst buffer is null\n"); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + } + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) { + vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (!vb) + dev_err(dev, "Next dst buffer is null\n"); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + } +} + +static struct vb2_ops vxe_video_ops = { + .queue_setup = vxe_queue_setup, + .buf_init = vxe_buf_init, + .buf_prepare = vxe_buf_prepare, + .buf_queue = vxe_buf_queue, + .buf_cleanup = vxe_buf_cleanup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vxe_start_streaming, + .stop_streaming = vxe_stop_streaming, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct vxe_enc_ctx *ctx = priv; + struct vxe_dev *vxe = ctx->dev; + int ret = 0; + + /* src_vq */ + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct vxe_buffer); + src_vq->ops = &vxe_video_ops; + src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = vxe->mutex; + src_vq->dev = vxe->ti_vxe_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + /* dst_vq */ + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct vxe_buffer); + dst_vq->ops = &vxe_video_ops; + dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = vxe->mutex; + dst_vq->dev = vxe->ti_vxe_dev.dev; + + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int vxe_open(struct file *file) +{ + struct vxe_dev *vxe = video_drvdata(file); + struct vxe_enc_ctx *ctx; + int i, ret = 0; + + dev_dbg(vxe->dev, "%s:%d vxe %p\n", __func__, __LINE__, vxe); + + mutex_lock((struct mutex *)vxe->mutex); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock((struct mutex *)vxe->mutex); + return -ENOMEM; + } + + ctx->mutex = kzalloc(sizeof(*ctx->mutex), GFP_KERNEL); + if (!ctx->mutex) + return -ENOMEM; + + mutex_init(ctx->mutex); + + ctx->dev = vxe; + ctx->s_fmt_flags = 0; + ctx->eos = FALSE; + ctx->flag_last = FALSE; + ctx->available_coded_packages = 0; + ctx->available_source_frames = 0; + ctx->frames_encoding = 0; + ctx->frame_num = 0; + ctx->out_queue.streaming = FALSE; + ctx->cap_queue.streaming = FALSE; + + for (i = 0; i < ARRAY_SIZE(vxe_enc_formats); i++) { + if (vxe_enc_formats[i].type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ctx->out_queue.fmt = &vxe_enc_formats[i]; + break; + } + } + for (i = 0; i < ARRAY_SIZE(vxe_enc_formats); i++) { + if (vxe_enc_formats[i].type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ctx->cap_queue.fmt = &vxe_enc_formats[i]; + break; + } + } + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vxe->m2m_dev, ctx, &queue_init); + if (IS_ERR_VALUE((unsigned long)ctx->fh.m2m_ctx)) { + ret = (long)(ctx->fh.m2m_ctx); + goto exit; + } + + vxe_fill_default_params(ctx); + + v4l2_fh_add(&ctx->fh); + + vxe_create_ctx(vxe, ctx); + + /* TODO: Add stream id creation */ +exit: + mutex_unlock((struct mutex *)vxe->mutex); + return ret; +} + +static int vxe_release(struct file *file) +{ + struct vxe_dev *vxe = video_drvdata(file); + struct vxe_enc_ctx *ctx = file2ctx(file); + /* TODO Need correct API */ + + mutex_lock((struct mutex *)vxe->mutex); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + if (ctx->s_fmt_flags & S_FMT_FLAG_STREAM_CREATED) + topaz_stream_destroy(ctx->topaz_str_context); + ctx->topaz_str_context = NULL; + + mutex_destroy(ctx->mutex); + kfree(ctx->mutex); + ctx->mutex = NULL; + kfree(ctx); + + mutex_unlock((struct mutex *)vxe->mutex); + + return 0; +} + +static const struct v4l2_file_operations vxe_enc_fops = { + .owner = THIS_MODULE, + .open = vxe_open, + .release = vxe_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int vxe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, IMG_VXE_ENC_MODULE_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, IMG_VXE_ENC_MODULE_NAME, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", IMG_VXE_ENC_MODULE_NAME); + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static struct vxe_enc_fmt *find_format(struct v4l2_format *f) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vxe_enc_formats); ++i) { + if (vxe_enc_formats[i].fourcc == f->fmt.pix_mp.pixelformat && + vxe_enc_formats[i].type == f->type) + return &vxe_enc_formats[i]; + } + return NULL; +} + +static int vxe_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + int i, index = 0; + struct vxe_enc_fmt *fmt = NULL; + + for (i = 0; i < ARRAY_SIZE(vxe_enc_formats); ++i) { + if (vxe_enc_formats[i].type == f->type) { + if (index == f->index) { + fmt = &vxe_enc_formats[i]; + break; + } + index++; + } + } + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static int vxe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct vxe_enc_q_data *queue; + int i; + + pix_mp = &f->fmt.pix_mp; + + queue = get_queue(ctx, f->type); + if (!queue) + return -EINVAL; + + pix_mp->width = queue->width; + pix_mp->height = queue->height; + pix_mp->pixelformat = queue->fmt->fourcc; + pix_mp->field = V4L2_FIELD_NONE; + + for (i = 0; i < queue->fmt->num_planes; i++) { + pix_mp->plane_fmt[i].sizeimage = queue->size_image[i]; + pix_mp->plane_fmt[i].bytesperline = queue->bytesperline[i]; + } + pix_mp->num_planes = queue->fmt->num_planes; + + return 0; +} + +static int vxe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + struct vxe_enc_fmt *fmt; + struct vxe_enc_q_data *queue; + int i; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + struct img_rc_params rc; + + fmt = find_format(f); + if (!fmt) + return -EINVAL; + + queue = get_queue(ctx, f->type); + if (!queue) + return -EINVAL; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + pix_mp->num_planes = fmt->num_planes; + pix_mp->flags = 0; + for (i = 0; i < fmt->num_planes; i++) { + plane_fmt[i].bytesperline = vxe_get_stride(pix_mp->width, fmt); + plane_fmt[i].sizeimage = vxe_get_sizeimage(plane_fmt[i].bytesperline, + pix_mp->height, fmt, i); + } + } else { + pix_mp->flags = 0; + /* Worst case estimation of sizeimage + *plane_fmt[0].sizeimage = ALIGN(pix_mp->width, HW_ALIGN) * + * ALIGN(pix_mp->height, HW_ALIGN) * 2; + */ + /* TODO: This is the only thing that matters here, make sure this is correct */ + rc.initial_qp_i = 18; + plane_fmt[0].bytesperline = 0; + plane_fmt[0].sizeimage = topaz_get_coded_buffer_max_size(NULL, fmt->std, + pix_mp->width, + pix_mp->height, + &rc); + } + + if (pix_mp->field == V4L2_FIELD_ANY) + pix_mp->field = V4L2_FIELD_NONE; + + return 0; +} + +static int vxe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct vxe_enc_fmt *fmt; + struct vxe_enc_q_data *queue; + int i, ret = 0; + unsigned int level_h264; + + ret = vxe_try_fmt(file, priv, f); + if (ret) + return ret; + + fmt = find_format(f); + if (!fmt) + return -EINVAL; + + queue = get_queue(ctx, f->type); + if (!queue) + return -EINVAL; + + pix_mp = &f->fmt.pix_mp; + + queue->fmt = fmt; + queue->width = pix_mp->width; + queue->height = pix_mp->height; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + ctx->vparams.format = fmt->fmt; + ctx->vparams.source_width = pix_mp->width; + ctx->vparams.source_frame_height = pix_mp->height; + ctx->vparams.csc_preset = fmt->csc_preset; + if (ctx->vparams.csc_preset != IMG_CSC_NONE) + ctx->vparams.enable_scaler = TRUE; + + pr_debug("img_video_params: format=%d\n", ctx->vparams.format); + pr_debug("img_video_params: source_width=%d\n", ctx->vparams.source_width); + pr_debug("img_video_params: source_frame_height=%d\n", + ctx->vparams.source_frame_height); + pr_debug("img_video_params: csc_preset=%d\n", ctx->vparams.csc_preset); + pr_debug("img_video_params: enable_scaler=%s\n", + ctx->vparams.enable_scaler ? "true" : "false"); + + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = vxe_get_stride(queue->width, fmt); + queue->size_image[i] = vxe_get_sizeimage(pix_mp->plane_fmt[i].bytesperline, + queue->height, fmt, i); + } + + /* Rate Control parameters */ + ctx->rc.transfer_bits_per_second = ctx->rc.bits_per_second; + ctx->rc.bu_size = -1414812757; /* Pretty sure uninitialized */ + ctx->rc.buffer_size = ctx->rc.transfer_bits_per_second; + + ctx->rc.initial_level = (3 * ctx->rc.buffer_size) >> 4; + ctx->rc.initial_level = ((ctx->rc.initial_level + + ((ctx->rc.bits_per_second / + ctx->rc.frame_rate) / 2)) / + (ctx->rc.bits_per_second / + ctx->rc.frame_rate)) * + (ctx->rc.bits_per_second / ctx->rc.frame_rate); + ctx->rc.initial_level = max((unsigned int)ctx->rc.initial_level, + (unsigned int)(ctx->rc.bits_per_second / + ctx->rc.frame_rate)); + ctx->rc.initial_delay = ctx->rc.buffer_size - ctx->rc.initial_level; + ctx->rc.bframes = 0; + + pr_debug("img_rc_params: initial_level=%d\n", ctx->rc.initial_level); + pr_debug("img_rc_params: initial_delay=%d\n", ctx->rc.initial_delay); + /* TODO Figure out which lossless to use */ + ctx->sh_params.profile = find_h264_profile + (FALSE, + ctx->vparams.use_default_scaling_list, + FALSE, + ctx->vparams.h264_8x8, + ctx->vparams.enable_mvc, + ctx->rc.bframes, + ctx->vparams.is_interlaced, + ctx->vparams.cabac_enabled, + ctx->vparams.weighted_prediction, + ctx->vparams.vp_weighted_implicit_bi_pred); + ctx->sh_params.max_num_ref_frames = 1; //TODO Need more logic + + level_h264 = calculate_h264_level(pix_mp->width, pix_mp->height, + ctx->rc.frame_rate, + ctx->rc.rc_enable, + ctx->rc.bits_per_second, + /* TODO Figure out which lossless to use */ + FALSE, + ctx->sh_params.profile, + ctx->sh_params.max_num_ref_frames); + pr_debug("level_h264=%d\n", level_h264); + + ctx->vparams.vert_mv_limit = 255; + if (level_h264 >= 110) + ctx->vparams.vert_mv_limit = 511; + if (level_h264 >= 210) + ctx->vparams.vert_mv_limit = 1023; + if (level_h264 >= 310) + ctx->vparams.vert_mv_limit = 2047; + + if (level_h264 >= 300) + ctx->vparams.limit_num_vectors = TRUE; + else + ctx->vparams.limit_num_vectors = FALSE; + + pr_debug("ctx->vparams.vert_mv_limit=%d\n", ctx->vparams.vert_mv_limit); + pr_debug("ctx->vparams.limit_num_vectors=%d\n", ctx->vparams.limit_num_vectors); + + /* VUI parameters */ + ctx->vui_params.time_scale = ctx->rc.frame_rate * 2; + ctx->vui_params.bit_rate_value_minus1 = (ctx->rc.bits_per_second / 64) + - 1; + ctx->vui_params.cbp_size_value_minus1 = (ctx->rc.buffer_size / 64) - 1; + ctx->vui_params.aspect_ratio_info_present_flag = FALSE; //unset + ctx->vui_params.aspect_ratio_idc = 0; //unset + ctx->vui_params.sar_width = 0; //unset + ctx->vui_params.sar_height = 0; //unset + ctx->vui_params.cbr = (ctx->rc.rc_mode == IMG_RCMODE_CBR) ? + TRUE : FALSE; + ctx->vui_params.initial_cpb_removal_delay_length_minus1 = + BPH_SEI_NAL_INITIAL_CPB_REMOVAL_DELAY_SIZE - 1; + ctx->vui_params.cpb_removal_delay_length_minus1 = + PTH_SEI_NAL_CPB_REMOVAL_DELAY_SIZE - 1; + ctx->vui_params.dpb_output_delay_length_minus1 = + PTH_SEI_NAL_DPB_OUTPUT_DELAY_SIZE - 1; + ctx->vui_params.time_offset_length = 24; //hard coded + ctx->vui_params.num_reorder_frames = 0; //TODO + ctx->vui_params.max_dec_frame_buffering = 0; //unset + + pr_debug("h264_vui_params: time_scale=%d\n", ctx->vui_params.time_scale); + pr_debug("h264_vui_params: bit_rate_value_minus1=%d\n", + ctx->vui_params.bit_rate_value_minus1); + pr_debug("h264_vui_params: cbp_size_value_minus1=%d\n", + ctx->vui_params.cbp_size_value_minus1); + pr_debug("h264_vui_params: cbr=%d\n", ctx->vui_params.cbr); + pr_debug("h264_vui_params: initial_cpb_removal_delay_length_minus1=%d\n", + ctx->vui_params.initial_cpb_removal_delay_length_minus1); + pr_debug("h264_vui_params: cpb_removal_delay_length_minus1=%d\n", + ctx->vui_params.cpb_removal_delay_length_minus1); + pr_debug("h264_vui_params: dpb_output_delay_length_minus1=%d\n", + ctx->vui_params.dpb_output_delay_length_minus1); + + /* Sequence Header parameters */ + switch (level_h264) { + case 100: + ctx->sh_params.level = SH_LEVEL_1; + break; + case 101: + ctx->sh_params.level = SH_LEVEL_1B; + break; + case 110: + ctx->sh_params.level = SH_LEVEL_11; + break; + case 120: + ctx->sh_params.level = SH_LEVEL_12; + break; + case 130: + ctx->sh_params.level = SH_LEVEL_13; + break; + case 200: + ctx->sh_params.level = SH_LEVEL_2; + break; + case 210: + ctx->sh_params.level = SH_LEVEL_21; + break; + case 220: + ctx->sh_params.level = SH_LEVEL_22; + break; + case 300: + ctx->sh_params.level = SH_LEVEL_3; + break; + case 310: + ctx->sh_params.level = SH_LEVEL_31; + break; + case 320: + ctx->sh_params.level = SH_LEVEL_32; + break; + case 400: + ctx->sh_params.level = SH_LEVEL_4; + break; + case 410: + ctx->sh_params.level = SH_LEVEL_41; + break; + case 420: + ctx->sh_params.level = SH_LEVEL_42; + break; + case 500: + ctx->sh_params.level = SH_LEVEL_5; + break; + case 510: + ctx->sh_params.level = SH_LEVEL_51; + break; + case 520: + ctx->sh_params.level = SH_LEVEL_52; + break; + default: + pr_err("Error invalid h264 level %d\n", level_h264); + return -EINVAL; + } + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + ctx->sh_params.width_in_mbs_minus1 = (queue->width / MB_SIZE) - 1; + ctx->sh_params.height_in_maps_units_minus1 = (queue->height / MB_SIZE) - 1; + pr_debug("h264_sequence_header_params: width_in_mbs_minus1=%d\n", + ctx->sh_params.width_in_mbs_minus1); + pr_debug("h264_sequence_header_params: height_in_maps_units_minus1=%d\n", + ctx->sh_params.height_in_maps_units_minus1); + } + ctx->sh_params.log2_max_pic_order_cnt = 6; //hard coded + ctx->sh_params.gaps_in_frame_num_value = FALSE; + ctx->sh_params.frame_mbs_only_flag = ctx->vparams.is_interlaced ? + FALSE : TRUE; + ctx->sh_params.vui_params_present = (ctx->rc.rc_mode == IMG_RCMODE_NONE) + ? FALSE : TRUE; + ctx->sh_params.seq_scaling_matrix_present_flag = FALSE; + ctx->sh_params.use_default_scaling_list = FALSE; + ctx->sh_params.is_lossless = FALSE; + ctx->sh_params.vui_params = ctx->vui_params; + + pr_debug("h264_sequence_header_params: frame_mbs_only_flag=%d\n", + ctx->sh_params.frame_mbs_only_flag); + pr_debug("h264_sequence_header_params: vui_params_present=%d\n", + ctx->sh_params.vui_params_present); + + ctx->s_fmt_flags |= S_FMT_FLAG_OUT_RECV; + } else { + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = 0; + queue->size_image[i] = + topaz_get_coded_buffer_max_size(ctx->topaz_str_context, + queue->fmt->std, + queue->width, + queue->height, + &ctx->rc); + } + ctx->vparams.standard = fmt->std; + ctx->vparams.width = pix_mp->width; + /* + * Note: Do not halve height for interlaced. + * App should take care of this. + */ + ctx->vparams.frame_height = pix_mp->height; + + pr_debug("img_video_params: standard=%d\n", ctx->vparams.standard); + pr_debug("img_video_params: width=%d\n", ctx->vparams.width); + pr_debug("img_video_params: frame_height=%d\n", ctx->vparams.frame_height); + + ctx->s_fmt_flags |= S_FMT_FLAG_CAP_RECV; + } + ctx->vparams.is_interlaced = FALSE; + + ctx->vparams.intra_pred_modes = -1414812757; /* Pretty sure uninitialized */ + + ctx->vparams.buffer_stride_bytes = 0; + ctx->vparams.buffer_height = 0; + + ctx->vparams.crop_left = 0; + ctx->vparams.crop_right = 0; + ctx->vparams.crop_top = 0; + ctx->vparams.crop_bottom = 0; + + ctx->vparams.slices_per_picture = 1; + + /* Crop parameters */ + ctx->crop_params.clip = FALSE; + ctx->crop_params.left_crop_offset = 0; + ctx->crop_params.right_crop_offset = 0; + ctx->crop_params.top_crop_offset = 0; + ctx->crop_params.bottom_crop_offset = 0; + + pr_debug("s_fmt_flags=%#08x\n", ctx->s_fmt_flags); + if ((ctx->s_fmt_flags & S_FMT_FLAG_OUT_RECV) && + (ctx->s_fmt_flags & S_FMT_FLAG_CAP_RECV)) { + pr_debug("Calling topaz_stream_create()\n"); + topaz_stream_create(ctx, &ctx->vparams, 0, 2, &ctx->rc, + &ctx->topaz_str_context); + + topaz_h264_prepare_sequence_header(ctx->topaz_str_context, + ctx->sh_params.width_in_mbs_minus1 + 1, + ctx->sh_params.height_in_maps_units_minus1 + 1, + TRUE, &ctx->vui_params, + &ctx->crop_params, + &ctx->sh_params, FALSE); + /* Note: cqp_offset looks unset in img */ + topaz_h264_prepare_picture_header(ctx->topaz_str_context, 0); + + topaz_load_context(ctx->topaz_str_context); + + ctx->s_fmt_flags |= S_FMT_FLAG_STREAM_CREATED; + } + + return 0; +} + +static int vxe_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type != V4L2_EVENT_EOS) + return -EINVAL; + + v4l2_event_subscribe(fh, sub, 0, NULL); + return 0; +} + +static int vxe_try_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *cmd) +{ + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + return 0; +} + +static int vxe_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + mutex_lock_nested(ctx->mutex, SUBCLASS_VXE_V4L2); + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0 || + !ctx->out_queue.streaming || !ctx->cap_queue.streaming) { + /* Buffers are still in queue for encode, set eos flag */ + ctx->eos = TRUE; + mutex_unlock((struct mutex *)ctx->mutex); + } else if ((ctx->available_source_frames > 0) || + (ctx->frames_encoding) > 0) { + /* + * Buffers are still in firmware for encode. Tell topaz + * that last frame sent is last frame in stream + */ + topaz_end_of_stream(ctx->topaz_str_context, ctx->frame_num + 1); + ctx->last_frame_num = ctx->frame_num + 1; + } else { + /* All buffers are encoded, so issue dummy stream end */ + mutex_unlock((struct mutex *)ctx->mutex); + vxe_eos(ctx); + } + return 0; +} + +static int vxe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *query) +{ + int i; + + query->reserved[0] = 0; + query->reserved[1] = 0; + + /* Enumerate controls */ + if (query->id & V4L2_CTRL_FLAG_NEXT_CTRL) { + query->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; + for (i = 0; i < ARRAY_SIZE(controls); i++) { + if (!controls[i].compound && controls[i].cid > query->id) { + query->id = controls[i].cid; + query->type = controls[i].type; + strncpy(query->name, controls[i].name, sizeof(query->name)); + query->minimum = controls[i].minimum; + query->maximum = controls[i].maximum; + query->step = controls[i].step; + query->default_value = controls[i].default_value; + query->flags = 0; + return 0; + } + } + return -EINVAL; + } + + /* Return info on requested control */ + for (i = 0; i < ARRAY_SIZE(controls); i++) { + if (controls[i].cid == query->id) { + query->id = controls[i].cid; + query->type = controls[i].type; + strncpy(query->name, controls[i].name, sizeof(query->name)); + query->minimum = controls[i].minimum; + query->maximum = controls[i].maximum; + query->step = controls[i].step; + query->default_value = controls[i].default_value; + query->flags = 0; + return 0; + } + } + + return -EINVAL; +} + +static int vxe_query_ext_ctrl(struct file *file, void *priv, + struct v4l2_query_ext_ctrl *query) +{ + unsigned int queryid; + int i, j; + + query->reserved[0] = 0; + query->reserved[1] = 0; + + /* Enumerate controls */ + if ((query->id & V4L2_CTRL_FLAG_NEXT_CTRL) || + (query->id & V4L2_CTRL_FLAG_NEXT_COMPOUND)) { + queryid = query->id; + queryid &= ~V4L2_CTRL_FLAG_NEXT_CTRL; + queryid &= ~V4L2_CTRL_FLAG_NEXT_COMPOUND; + for (i = 0; i < ARRAY_SIZE(controls); i++) { + if (((!controls[i].compound && (query->id & V4L2_CTRL_FLAG_NEXT_CTRL)) || + (controls[i].compound && + (query->id & V4L2_CTRL_FLAG_NEXT_COMPOUND))) && + controls[i].cid > queryid) { + query->id = controls[i].cid; + query->type = controls[i].type; + strncpy(query->name, controls[i].name, sizeof(query->name)); + query->minimum = controls[i].minimum; + query->maximum = controls[i].maximum; + query->step = controls[i].step; + query->default_value = controls[i].default_value; + /* Our supported controls use int values */ + query->elem_size = 4; + query->elems = 1; + query->nr_of_dims = 0; + for (j = 0; j < V4L2_CTRL_MAX_DIMS; j++) + query->dims[j] = 0; + query->flags = 0; + return 0; + } + } + return -EINVAL; + } + + /* Return info on requested control */ + for (i = 0; i < ARRAY_SIZE(controls); i++) { + if (controls[i].cid == query->id) { + query->id = controls[i].cid; + query->type = controls[i].type; + strncpy(query->name, controls[i].name, sizeof(query->name)); + query->minimum = controls[i].minimum; + query->maximum = controls[i].maximum; + query->step = controls[i].step; + query->default_value = controls[i].default_value; + /* Our supported controls use int values */ + query->elem_size = 4; + query->elems = 1; + query->nr_of_dims = 0; + for (j = 0; j < V4L2_CTRL_MAX_DIMS; j++) + query->dims[j] = 0; + query->flags = 0; + return 0; + } + } + + return -EINVAL; +} + +static int vxe_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + struct device *dev = ctx->dev->dev; + struct v4l2_queryctrl query; + int i; + + ctrls->reserved[0] = 0; + ctrls->reserved[1] = 0; + + if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) { + for (i = 0; i < ctrls->count; i++) { + query.id = ctrls->controls[i].id; + if (vxe_queryctrl(NULL, NULL, &query)) { + dev_err(dev, "%s could not find default value for id=%#08x\n", + __func__, ctrls->controls[i].id); + return -EINVAL; + } + ctrls->controls[i].value = query.default_value; + } + } + + for (i = 0; i < ctrls->count; i++) { + ctrls->controls[i].reserved2[0] = 0; + + switch (ctrls->controls[i].id) { + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrls->controls[i].size = 0; + ctrls->controls[i].value = ctx->vparams.idr_period; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrls->controls[i].size = 0; + ctrls->controls[i].value = ctx->rc.bits_per_second; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + ctrls->controls[i].size = 0; + ctrls->controls[i].value = ctx->rc.intra_freq; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + ctrls->controls[i].size = 0; + ctrls->controls[i].value = ctx->sh_params.profile; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + ctrls->controls[i].size = 0; + ctrls->controls[i].value = ctx->sh_params.level; + break; + default: + dev_err(dev, "%s Invalid control id %#08x\n", + __func__, ctrls->controls[i].id); + ctrls->error_idx = ctrls->count; + return -EINVAL; + } + } + + return 0; +} + +static int vxe_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + struct device *dev = ctx->dev->dev; + struct v4l2_queryctrl query; + int i; + + ctrls->reserved[0] = 0; + ctrls->reserved[1] = 0; + + /* Can't write default values or support requests */ + if (ctrls->which != V4L2_CTRL_WHICH_CUR_VAL) + return -EINVAL; + + /* Cannot change values once context is created */ + /* TODO: Handle controls after stream is created but before streamon */ + if (ctx->s_fmt_flags & S_FMT_FLAG_STREAM_CREATED) + return -EBUSY; + + for (i = 0; i < ctrls->count; i++) { + ctrls->controls[i].reserved2[0] = 0; + + query.id = ctrls->controls[i].id; + if (vxe_queryctrl(NULL, NULL, &query)) { + dev_err(dev, "%s could not find control id=%#08x\n", + __func__, ctrls->controls[i].id); + ctrls->error_idx = i; + return -EINVAL; + } + if (ctrls->controls[i].value < query.minimum) { + dev_err(dev, "%s control id=%#08x value=%d less than minimum=%d\n", + __func__, ctrls->controls[i].id, + ctrls->controls[i].value, query.minimum); + ctrls->error_idx = i; + return -ERANGE; + } + if (ctrls->controls[i].value > query.maximum) { + dev_err(dev, "%s control id=%#08x value=%d greater than maximum=%d\n", + __func__, ctrls->controls[i].id, + ctrls->controls[i].value, query.maximum); + ctrls->error_idx = i; + return -ERANGE; + } + } + + return 0; +} + +static int vxe_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + struct device *dev = ctx->dev->dev; + int i; + int ret; + + ctrls->reserved[0] = 0; + ctrls->reserved[1] = 0; + + if (ctrls->which != V4L2_CTRL_WHICH_CUR_VAL) + return -EINVAL; + + /* Verify first with try_ext_ctrls */ + ret = vxe_try_ext_ctrls(file, priv, ctrls); + if (ret) { + /* Indicate verification stage error */ + ctrls->error_idx = ctrls->count; + return ret; + } + + /* Set all values in this set of commands */ + for (i = 0; i < ctrls->count; i++) { + ctrls->controls[i].reserved2[0] = 0; + + switch (ctrls->controls[i].id) { + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrls->controls[i].size = 0; + ctx->vparams.idr_period = ctrls->controls[i].value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrls->controls[i].size = 0; + ctx->rc.bits_per_second = ctrls->controls[i].value; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + ctrls->controls[i].size = 0; + ctx->rc.intra_freq = ctrls->controls[i].value; + ctx->vparams.intra_cnt = ctrls->controls[i].value; + break; + default: + dev_err(dev, "%s Invalid control id %#08x\n", + __func__, ctrls->controls[i].id); + ctrls->error_idx = i; + return -EINVAL; + } + } + + return 0; +} + +static int vxe_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index != 0) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = 1920; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = 1; + fsize->stepwise.max_height = 1080; + fsize->stepwise.step_height = 1; + + fsize->reserved[0] = 0; + fsize->reserved[1] = 0; + + return 0; +} + +static int vxe_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + if (fival->index > (ARRAY_SIZE(frmivals))) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = frmivals[fival->index]; + + fival->reserved[0] = 0; + fival->reserved[1] = 1; + + return 0; +} + +static int vxe_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + + if (V4L2_TYPE_IS_OUTPUT(parm->type)) { + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.output.timeperframe.numerator = 1; + parm->parm.output.timeperframe.denominator = ctx->rc.frame_rate; + } else { + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.numerator = 1; + parm->parm.capture.timeperframe.denominator = ctx->rc.frame_rate; + } + + return 0; +} + +static int vxe_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vxe_enc_ctx *ctx = file2ctx(file); + unsigned int num, den; + int i; + + /* Cannot change values once context is created */ + /* TODO: Handle controls after stream is created but before streamon */ + if (ctx->s_fmt_flags & S_FMT_FLAG_STREAM_CREATED) + return -EBUSY; + + if (V4L2_TYPE_IS_OUTPUT(parm->type)) { + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + num = parm->parm.output.timeperframe.numerator; + den = parm->parm.output.timeperframe.denominator; + } else { + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + num = parm->parm.capture.timeperframe.numerator; + den = parm->parm.capture.timeperframe.denominator; + } + + for (i = 0; i < (ARRAY_SIZE(frmivals)); i++) { + if (num == frmivals[i].numerator && + den == frmivals[i].denominator) { + /* Switch from frame interval to frame rate */ + ctx->rc.frame_rate = den / num; + } + } + + if (V4L2_TYPE_IS_OUTPUT(parm->type)) { + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.output.timeperframe.numerator = 1; + parm->parm.output.timeperframe.denominator = ctx->rc.frame_rate; + } else { + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.numerator = 1; + parm->parm.capture.timeperframe.denominator = ctx->rc.frame_rate; + } + + return 0; +} + +static const struct v4l2_ioctl_ops vxe_enc_ioctl_ops = { + .vidioc_querycap = vxe_querycap, + + .vidioc_enum_fmt_vid_cap = vxe_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = vxe_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vxe_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vxe_s_fmt, + + .vidioc_enum_fmt_vid_out = vxe_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = vxe_g_fmt, + .vidioc_try_fmt_vid_out_mplane = vxe_try_fmt, + .vidioc_s_fmt_vid_out_mplane = vxe_s_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + + .vidioc_subscribe_event = vxe_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_encoder_cmd = vxe_try_cmd, + .vidioc_encoder_cmd = vxe_cmd, + + .vidioc_queryctrl = vxe_queryctrl, + .vidioc_query_ext_ctrl = vxe_query_ext_ctrl, + .vidioc_g_ext_ctrls = vxe_g_ext_ctrls, + .vidioc_s_ext_ctrls = vxe_s_ext_ctrls, + .vidioc_try_ext_ctrls = vxe_try_ext_ctrls, + + .vidioc_enum_framesizes = vxe_enum_framesizes, + .vidioc_enum_frameintervals = vxe_enum_frameintervals, + + .vidioc_g_parm = vxe_g_parm, + .vidioc_s_parm = vxe_s_parm, +}; + +static const struct of_device_id vxe_enc_of_match[] = { + {.compatible = "img,vxe384"}, { /* end */}, +}; +MODULE_DEVICE_TABLE(of, vxe_enc_of_match); + +static irqreturn_t soft_thread_irq(int irq, void *dev_data) +{ + unsigned char handled; + + if (!dev_data) + return IRQ_NONE; + + handled = topazdd_threaded_isr(dev_data); + if (handled) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static irqreturn_t hard_isrcb(int irq, void *dev_data) +{ + if (!dev_data) + return IRQ_NONE; + + return topazdd_isr(dev_data); +} + +static int vxe_enc_probe(struct platform_device *pdev) +{ + struct vxe_dev *vxe; + struct resource *res; + const struct of_device_id *of_dev_id; + struct video_device *vfd; + int ret; + int module_irq; + struct vxe_enc_ctx *ctx; + struct heap_config *heap_configs = vxe_enc_heap_configs; + int num_heaps = ARRAY_SIZE(vxe_enc_heap_configs); + unsigned int i; + + of_dev_id = of_match_device(vxe_enc_of_match, &pdev->dev); + if (!of_dev_id) { + dev_err(&pdev->dev, "%s: Unable to match device\n", __func__); + return -ENODEV; + } + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + + vxe = devm_kzalloc(&pdev->dev, sizeof(*vxe), GFP_KERNEL); + if (!vxe) + return -ENOMEM; + + vxe->dev = &pdev->dev; + vxe->plat_dev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vxe->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vxe->reg_base)) + return PTR_ERR(vxe->reg_base); + + module_irq = platform_get_irq(pdev, 0); + if (module_irq < 0) + return -ENXIO; + vxe->module_irq = module_irq; + + ret = img_mem_init(vxe->dev); + if (ret) { + dev_err(vxe->dev, "Failed to initialize memory\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&vxe->drv_ctx.heaps); + vxe->drv_ctx.internal_heap_id = VXE_INVALID_ID; + + /* Initialise memory management component */ + for (i = 0; i < num_heaps; i++) { + struct vxe_heap *heap; +#ifdef DEBUG_ENCODER_DRIVER + dev_info(vxe->dev, "%s: adding heap of type %d\n", + __func__, heap_configs[i].type); +#endif + + heap = kzalloc(sizeof(*heap), GFP_KERNEL); + if (!heap) { + ret = -ENOMEM; + goto heap_add_failed; + } + + ret = img_mem_add_heap(&heap_configs[i], &heap->id); + if (ret < 0) { + dev_err(vxe->dev, "%s: failed to init heap (type %d)!\n", + __func__, heap_configs[i].type); + kfree(heap); + goto heap_add_failed; + } + list_add(&heap->list, &vxe->drv_ctx.heaps); + + /* Implicitly, first heap is used for internal allocations */ + if (vxe->drv_ctx.internal_heap_id < 0) { + vxe->drv_ctx.internal_heap_id = heap->id; + dev_err(vxe->dev, "%s: using heap %d for internal alloc\n", + __func__, vxe->drv_ctx.internal_heap_id); + } + } + + /* Do not proceed if internal heap not defined */ + if (vxe->drv_ctx.internal_heap_id < 0) { + dev_err(vxe->dev, "%s: failed to locate heap for internal alloc\n", + __func__); + ret = -EINVAL; + /* Loop registered heaps just for sanity */ + goto heap_add_failed; + } + + ret = vxe_init_mem(vxe); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize memory\n"); + return -ENOMEM; + } + + vxe->mutex = kzalloc(sizeof(*vxe->mutex), GFP_KERNEL); + if (!vxe->mutex) + return -ENOMEM; + + mutex_init(vxe->mutex); + + platform_set_drvdata(pdev, vxe); + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "%s: failed to enable clock, status = %d\n", + __func__, ret); + goto exit; + } + + ret = devm_request_threaded_irq(&pdev->dev, module_irq, (irq_handler_t)hard_isrcb, + (irq_handler_t)soft_thread_irq, IRQF_SHARED, + IMG_VXE_ENC_MODULE_NAME, &vxe->topaz_dev_ctx); + if (ret) { + dev_err(&pdev->dev, "Failed to get IRQ\n"); + goto out_put_sync; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + free_irq(module_irq, &vxe->topaz_dev_ctx); + return -ENOMEM; + } + ctx->dev = vxe; + + vxe_fill_default_params(ctx); + + ctx->mem_ctx = vxe->drv_ctx.mem_ctx; + ctx->mmu_ctx = vxe->drv_ctx.mmu_ctx; + + vxe->ctx = ctx; + + ret = topazdd_init((unsigned long long)vxe->reg_base, res->end - res->start + 1, + (MMU_USE_MMU_FLAG | MMU_EXTENDED_ADDR_FLAG), + ctx, vxe->drv_ctx.ptd, &vxe->topaz_dev_ctx); + if (ret) + goto out_free_irq; + + vxe->streams = kzalloc(sizeof(*vxe->streams), GFP_KERNEL); + if (!vxe->streams) { + ret = -ENOMEM; + goto topazdd_deinit; + } + idr_init(vxe->streams); + + ret = init_topaz_core(vxe->topaz_dev_ctx, &vxe->num_pipes, + (MMU_USE_MMU_FLAG | MMU_EXTENDED_ADDR_FLAG), + vxe_return_resource); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize topaz core\n"); + goto topazdd_deinit; + } + + ret = v4l2_device_register(&pdev->dev, &vxe->ti_vxe_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2 device\n"); + goto topaz_core_deinit; + } + + vfd = video_device_alloc(); + if (!vfd) { + dev_err(&pdev->dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto out_v4l2_device; + } + + snprintf(vfd->name, sizeof(vfd->name), "%s", IMG_VXE_ENC_MODULE_NAME); + vfd->fops = &vxe_enc_fops; + vfd->ioctl_ops = &vxe_enc_ioctl_ops; + vfd->minor = -1; + vfd->release = video_device_release; + vfd->vfl_dir = VFL_DIR_M2M; + vfd->v4l2_dev = &vxe->ti_vxe_dev; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vfd->lock = vxe->mutex; + + vxe->vfd = vfd; + video_set_drvdata(vfd, vxe); + + vxe->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR_VALUE((unsigned long)vxe->m2m_dev)) { + dev_err(&pdev->dev, "Failed to init mem2mem device\n"); + ret = -EINVAL; + goto out_vid_dev; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to register video device\n"); + goto out_vid_reg; + } + v4l2_info(&vxe->ti_vxe_dev, "encoder registered as /dev/video%d\n", + vfd->num); + + return 0; + +out_vid_reg: + v4l2_m2m_release(vxe->m2m_dev); +out_vid_dev: + video_device_release(vfd); +out_v4l2_device: + v4l2_device_unregister(&vxe->ti_vxe_dev); +topaz_core_deinit: + deinit_topaz_core(); +topazdd_deinit: + topazdd_deinit(vxe->topaz_dev_ctx); +out_free_irq: + kfree(vxe->ctx); + free_irq(module_irq, &vxe->topaz_dev_ctx); +out_put_sync: + pm_runtime_put_sync(&pdev->dev); +heap_add_failed: + while (!list_empty(&vxe->drv_ctx.heaps)) { + struct vxe_heap *heap; + + heap = list_first_entry(&vxe->drv_ctx.heaps, struct vxe_heap, list); + __list_del_entry(&heap->list); + img_mem_del_heap(heap->id); + kfree(heap); + } + vxe->drv_ctx.internal_heap_id = VXE_INVALID_ID; + +exit: + pm_runtime_disable(&pdev->dev); + vxe_deinit_mem(vxe); + + return ret; +} + +static int vxe_enc_remove(struct platform_device *pdev) +{ + struct vxe_dev *vxe = platform_get_drvdata(pdev); + + topazdd_deinit(vxe->topaz_dev_ctx); + + kfree(vxe->ctx); + vxe_deinit_mem(vxe); + + free_irq(vxe->module_irq, &vxe->topaz_dev_ctx); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver vxe_enc_driver = { + .probe = vxe_enc_probe, + .remove = vxe_enc_remove, + .driver = { + .name = "img_enc", + .of_match_table = vxe_enc_of_match, + }, +}; +module_platform_driver(vxe_enc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("IMG VXE384 video encoder driver"); diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.h b/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.h --- a/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/encoder/vxe_v4l2.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * V4L2 interface header + * + * Copyright (c) Imagination Technologies Ltd. + * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * + * Authors: + * Sunita Nadampalli + * + * Re-written for upstreming + * Sidraya Jayagond + */ + +#ifndef _VXE_V4L2_H +#define _VXE_V4L2_H + +#include +#include +#include +#include + +/* + * struct vxe_ctrl - contains info for each supported v4l2 control + */ +struct vxe_ctrl { + unsigned int cid; + enum v4l2_ctrl_type type; + unsigned char name[32]; + int minimum; + int maximum; + int step; + int default_value; + unsigned char compound; +}; + +extern struct mem_space topaz_mem_space[]; + +#endif diff -Naur --no-dereference a/drivers/media/platform/vxe-vxd/Makefile b/drivers/media/platform/vxe-vxd/Makefile --- a/drivers/media/platform/vxe-vxd/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/media/platform/vxe-vxd/Makefile 2022-01-06 12:45:53.818318123 -0500 @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Optional Video feature configuration control + +# (1) +# This config allows enabling or disabling of HEVC/H265 video +# decoding functionality with IMG VXD Video decoder. If you +# do not want HEVC decode capability, select N. +# If unsure, select Y +HAS_HEVC ?=y + +# (2) +# This config enables error concealment with gray pattern. +# Disable if you do not want error concealment capability. +# If unsure, say Y +ERROR_CONCEALMENT ?=y + +# (3) +# This config, if enabled, configures H264 video decoder to +# output frames in the decode order with no buffering and +# picture reordering inside codec. +# If unsure, say N +REDUCED_DPB_NO_PIC_REORDERING ?=n + +# (4) +# This config, if enabled, enables all the debug traces in +# decoder driver. Enable it only for debug purpose +# Keep it always disabled for release codebase +DEBUG_DECODER_DRIVER ?=n + +# (5) +# This config allows enabling or disabling of MJPEG video +# decoding functionality with IMG VXD Video decoder. If you +# do not want MJPEG decode capability, select N. +# If unsure, select Y +HAS_JPEG ?=y + +# (6) +# This config allows simulation of Error recovery. +# This config is only for testing, never enable it for release build. +ERROR_RECOVERY_SIMULATION ?=n + +# (7) +# This config enables allocation of capture buffers from +# dma contiguous memory. +# If unsure, say Y +CAPTURE_CONTIG_ALLOC ?=y + +vxd-dec-y += common/img_mem_man.o \ + common/img_mem_unified.o \ + common/imgmmu.o \ + common/pool_api.o \ + common/idgen_api.o \ + common/talmmu_api.o \ + common/pool.o \ + common/hash.o \ + common/ra.o \ + common/addr_alloc.o \ + common/work_queue.o \ + common/lst.o \ + common/dq.o \ + common/resource.o \ + common/rman_api.o \ + +vxd-dec-y += decoder/vxd_core.o \ + decoder/vxd_pvdec.o \ + decoder/dec_resources.o \ + decoder/pixel_api.o \ + decoder/vdecdd_utils_buf.o \ + decoder/vdecdd_utils.o \ + decoder/vdec_mmu_wrapper.o \ + decoder/hw_control.o \ + decoder/vxd_int.o \ + decoder/translation_api.o \ + decoder/decoder.o \ + decoder/core.o \ + decoder/swsr.o \ + decoder/h264_secure_parser.o \ + decoder/bspp.o \ + decoder/vxd_dec.o \ + decoder/vxd_v4l2.o \ + + +ifeq ($(HAS_HEVC),y) +ccflags-y += -DHAS_HEVC +vxd-dec-y += decoder/hevc_secure_parser.o +endif + +ifeq ($(HAS_JPEG),y) +ccflags-y += -DHAS_JPEG +vxd-dec-y += decoder/jpeg_secure_parser.o +endif + +ifeq ($(DEBUG_DECODER_DRIVER), y) +ccflags-y += -DDEBUG_DECODER_DRIVER +ccflags-y += -DDEBUG +endif + +ifeq ($(ERROR_CONCEALMENT),y) +ccflags-y += -DERROR_CONCEALMENT +endif + +ifeq ($(REDUCED_DPB_NO_PIC_REORDERING),y) +ccflags-y += -DREDUCED_DPB_NO_PIC_REORDERING +endif + +ifeq ($(ERROR_RECOVERY_SIMULATION),y) +ccflags-y += -DERROR_RECOVERY_SIMULATION +endif + +ifeq ($(CAPTURE_CONTIG_ALLOC),y) +ccflags-y += -DCAPTURE_CONTIG_ALLOC +endif + +obj-$(CONFIG_VIDEO_IMG_VXD_DEC) += vxd-dec.o + +# (1) +# This config, if enabled, enables all the debug traces in +# encoder driver. Enable it only for debug purpose +# Keep it always disabled for release codebase +DEBUG_ENCODER_DRIVER ?=n + +# (3) +# This config enables encoder performance profiling +# keep it always disabled. Enable it only for profiling in development +# environments. +ENABLE_PROFILING ?=n + +vxe-enc-y += common/img_mem_man.o \ + common/img_mem_unified.o \ + common/talmmu_api.o \ + common/addr_alloc.o \ + common/lst.o \ + common/hash.o \ + common/ra.o \ + common/pool.o \ + common/rman_api.o \ + common/dq.o \ + common/idgen_api.o \ + common/imgmmu.o \ + common/work_queue.o \ + +vxe-enc-y += encoder/vxe_v4l2.o \ + encoder/vxe_enc.o \ + encoder/topaz_device.o \ + encoder/topazmmu.o \ + encoder/topaz_api.o \ + encoder/topaz_api_utils.o \ + encoder/header_gen.o \ + encoder/mtx_fwif.o \ + +obj-$(CONFIG_VIDEO_IMG_VXE_ENC) += vxe-enc.o + +ifeq ($(DEBUG_ENCODER_DRIVER), y) +ccflags-y += -DDEBUG_ENCODER_DRIVER +ccflags-y += -DDEBUG +endif + +ifeq ($(ENABLE_PROFILING),y) +ccflags-y += -DENABLE_PROFILING +endif + +ccflags-y += -I$(srctree)/drivers/media/platform/vxe-vxd/common/ diff -Naur --no-dereference a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c 2022-01-06 12:45:53.822318139 -0500 @@ -681,12 +681,13 @@ static struct v4l2_mbus_framefmt * __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, + sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xcsi2rxss->format; default: @@ -705,7 +706,7 @@ * Return: 0 on success */ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); struct v4l2_mbus_framefmt *format; @@ -713,7 +714,7 @@ mutex_lock(&xcsi2rxss->lock); for (i = 0; i < XCSI_MEDIA_PADS; i++) { - format = v4l2_subdev_get_try_format(sd, cfg, i); + format = v4l2_subdev_get_try_format(sd, sd_state, i); *format = xcsi2rxss->default_format; } mutex_unlock(&xcsi2rxss->lock); @@ -732,13 +733,14 @@ * Return: 0 on success */ static int xcsi2rxss_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); mutex_lock(&xcsi2rxss->lock); - fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, fmt->pad, + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, sd_state, + fmt->pad, fmt->which); mutex_unlock(&xcsi2rxss->lock); @@ -759,7 +761,7 @@ * Return: 0 on success */ static int xcsi2rxss_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); @@ -773,7 +775,7 @@ * CSI format cannot be changed at runtime. * Ensure that format to set is copied to over to CSI pad format */ - __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg, + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, sd_state, fmt->pad, fmt->which); /* only sink pad format can be updated */ @@ -811,7 +813,7 @@ * Return: -EINVAL or zero on success */ static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct xcsi2rxss_state *state = to_xcsi2rxssstate(sd); diff -Naur --no-dereference a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c --- a/drivers/media/platform/xilinx/xilinx-dma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/xilinx/xilinx-dma.c 2022-01-06 12:45:53.822318139 -0500 @@ -175,8 +175,8 @@ struct xvip_dma *start) { struct media_graph graph; - struct media_entity *entity = &start->video.entity; - struct media_device *mdev = entity->graph_obj.mdev; + struct media_pad *pad = start->video.entity.pads; + struct media_device *mdev = pad->entity->graph_obj.mdev; unsigned int num_inputs = 0; unsigned int num_outputs = 0; int ret; @@ -190,15 +190,15 @@ return ret; } - media_graph_walk_start(&graph, entity); + media_graph_walk_start(&graph, pad); - while ((entity = media_graph_walk_next(&graph))) { + while ((pad = media_graph_walk_next(&graph))) { struct xvip_dma *dma; - if (entity->function != MEDIA_ENT_F_IO_V4L) + if (pad->entity->function != MEDIA_ENT_F_IO_V4L) continue; - dma = to_xvip_dma(media_entity_to_video_device(entity)); + dma = to_xvip_dma(media_entity_to_video_device(pad->entity)); if (dma->pad.flags & MEDIA_PAD_FL_SINK) { pipe->output = dma; @@ -403,10 +403,10 @@ * Use the pipeline object embedded in the first DMA object that starts * streaming. */ - pipe = dma->video.entity.pipe + pipe = dma->video.entity.pads->pipe ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe; - ret = media_pipeline_start(&dma->video.entity, &pipe->pipe); + ret = media_pipeline_start(dma->video.entity.pads, &pipe->pipe); if (ret < 0) goto error; @@ -432,7 +432,7 @@ return 0; error_stop: - media_pipeline_stop(&dma->video.entity); + media_pipeline_stop(dma->video.entity.pads); error: /* Give back all queued buffers to videobuf2. */ @@ -460,7 +460,7 @@ /* Cleanup the pipeline and mark it as being stopped. */ xvip_pipeline_cleanup(pipe); - media_pipeline_stop(&dma->video.entity); + media_pipeline_stop(dma->video.entity.pads); /* Give back all queued buffers to videobuf2. */ spin_lock_irq(&dma->queued_lock); diff -Naur --no-dereference a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h --- a/drivers/media/platform/xilinx/xilinx-dma.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/xilinx/xilinx-dma.h 2022-01-06 12:45:53.822318139 -0500 @@ -47,7 +47,7 @@ static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) { - return container_of(e->pipe, struct xvip_pipeline, pipe); + return container_of(e->pads->pipe, struct xvip_pipeline, pipe); } /** diff -Naur --no-dereference a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c --- a/drivers/media/platform/xilinx/xilinx-tpg.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/xilinx/xilinx-tpg.c 2022-01-06 12:45:53.822318139 -0500 @@ -251,12 +251,13 @@ static struct v4l2_mbus_framefmt * __xtpg_get_pad_format(struct xtpg_device *xtpg, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad); + return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, + sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xtpg->formats[pad]; default: @@ -265,25 +266,26 @@ } static int xtpg_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct xtpg_device *xtpg = to_tpg(subdev); - fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); + fmt->format = *__xtpg_get_pad_format(xtpg, sd_state, fmt->pad, + fmt->which); return 0; } static int xtpg_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct xtpg_device *xtpg = to_tpg(subdev); struct v4l2_mbus_framefmt *__format; u32 bayer_phase; - __format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); + __format = __xtpg_get_pad_format(xtpg, sd_state, fmt->pad, fmt->which); /* In two pads mode the source pad format is always identical to the * sink pad format. @@ -306,7 +308,8 @@ /* Propagate the format to the source pad. */ if (xtpg->npads == 2) { - __format = __xtpg_get_pad_format(xtpg, cfg, 1, fmt->which); + __format = __xtpg_get_pad_format(xtpg, sd_state, 1, + fmt->which); *__format = fmt->format; } @@ -318,12 +321,12 @@ */ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -351,11 +354,11 @@ struct xtpg_device *xtpg = to_tpg(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); + format = v4l2_subdev_get_try_format(subdev, fh->state, 0); *format = xtpg->default_format; if (xtpg->npads == 2) { - format = v4l2_subdev_get_try_format(subdev, fh->pad, 1); + format = v4l2_subdev_get_try_format(subdev, fh->state, 1); *format = xtpg->default_format; } diff -Naur --no-dereference a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c --- a/drivers/media/platform/xilinx/xilinx-vip.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/xilinx/xilinx-vip.c 2022-01-06 12:45:53.822318139 -0500 @@ -246,7 +246,7 @@ * is not valid. */ int xvip_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct v4l2_mbus_framefmt *format; @@ -260,7 +260,7 @@ if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, cfg, code->pad); + format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); code->code = format->code; @@ -284,7 +284,7 @@ * if the index or the code is not valid. */ int xvip_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; @@ -295,7 +295,7 @@ if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; diff -Naur --no-dereference a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h --- a/drivers/media/platform/xilinx/xilinx-vip.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/platform/xilinx/xilinx-vip.h 2022-01-06 12:45:53.822318139 -0500 @@ -125,10 +125,10 @@ void xvip_set_format_size(struct v4l2_mbus_framefmt *format, const struct v4l2_subdev_format *fmt); int xvip_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code); int xvip_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse); static inline u32 xvip_read(struct xvip_device *xvip, u32 addr) diff -Naur --no-dereference a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c --- a/drivers/media/test-drivers/vimc/vimc-capture.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/test-drivers/vimc/vimc-capture.c 2022-01-06 12:45:53.822318139 -0500 @@ -246,7 +246,7 @@ vcap->sequence = 0; /* Start the media pipeline */ - ret = media_pipeline_start(entity, &vcap->stream.pipe); + ret = media_pipeline_start(entity->pads, &vcap->stream.pipe); if (ret) { vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); return ret; @@ -254,7 +254,7 @@ ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1); if (ret) { - media_pipeline_stop(entity); + media_pipeline_stop(entity->pads); vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); return ret; } @@ -273,7 +273,7 @@ vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0); /* Stop the media pipeline */ - media_pipeline_stop(&vcap->vdev.entity); + media_pipeline_stop(vcap->vdev.entity.pads); /* Release all active buffers */ vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR); diff -Naur --no-dereference a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c --- a/drivers/media/test-drivers/vimc/vimc-debayer.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c 2022-01-06 12:45:53.822318139 -0500 @@ -150,17 +150,17 @@ } static int vimc_deb_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; unsigned int i; - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); *mf = sink_fmt_default; for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, cfg, i); + mf = v4l2_subdev_get_try_format(sd, sd_state, i); *mf = sink_fmt_default; mf->code = vdeb->src_code; } @@ -169,7 +169,7 @@ } static int vimc_deb_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (VIMC_IS_SRC(code->pad)) { @@ -188,7 +188,7 @@ } static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index) @@ -213,14 +213,14 @@ } static int vimc_deb_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); /* Get the current sink format */ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, cfg, 0) : + *v4l2_subdev_get_try_format(sd, sd_state, 0) : vdeb->sink_fmt; /* Set the right code for the source pad */ @@ -251,7 +251,7 @@ } static int vimc_deb_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); @@ -266,8 +266,8 @@ sink_fmt = &vdeb->sink_fmt; src_code = &vdeb->src_code; } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - src_code = &v4l2_subdev_get_try_format(sd, cfg, 1)->code; + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + src_code = &v4l2_subdev_get_try_format(sd, sd_state, 1)->code; } /* diff -Naur --no-dereference a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c --- a/drivers/media/test-drivers/vimc/vimc-scaler.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c 2022-01-06 12:45:53.822318139 -0500 @@ -84,20 +84,20 @@ } static int vimc_sca_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf; struct v4l2_rect *r; unsigned int i; - mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf = v4l2_subdev_get_try_format(sd, sd_state, 0); *mf = sink_fmt_default; - r = v4l2_subdev_get_try_crop(sd, cfg, 0); + r = v4l2_subdev_get_try_crop(sd, sd_state, 0); *r = crop_rect_default; for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, cfg, i); + mf = v4l2_subdev_get_try_format(sd, sd_state, i); *mf = sink_fmt_default; mf->width = mf->width * sca_mult; mf->height = mf->height * sca_mult; @@ -107,7 +107,7 @@ } static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { u32 mbus_code = vimc_mbus_code_by_index(code->index); @@ -128,7 +128,7 @@ } static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { const struct vimc_pix_map *vpix; @@ -156,7 +156,7 @@ } static int vimc_sca_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); @@ -164,8 +164,8 @@ /* Get the current sink format */ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, sd_state, 0); } else { format->format = vsca->sink_fmt; crop_rect = &vsca->crop_rect; @@ -201,7 +201,7 @@ } static int vimc_sca_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); @@ -216,8 +216,8 @@ sink_fmt = &vsca->sink_fmt; crop_rect = &vsca->crop_rect; } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, sd_state, 0); } /* @@ -254,7 +254,7 @@ } static int vimc_sca_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); @@ -268,8 +268,8 @@ sink_fmt = &vsca->sink_fmt; crop_rect = &vsca->crop_rect; } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, sd_state, 0); } switch (sel->target) { @@ -287,7 +287,7 @@ } static int vimc_sca_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); @@ -305,8 +305,8 @@ crop_rect = &vsca->crop_rect; sink_fmt = &vsca->sink_fmt; } else { - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, sd_state, 0); + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); } switch (sel->target) { diff -Naur --no-dereference a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c --- a/drivers/media/test-drivers/vimc/vimc-sensor.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c 2022-01-06 12:45:53.822318139 -0500 @@ -42,14 +42,14 @@ }; static int vimc_sen_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { unsigned int i; for (i = 0; i < sd->entity.num_pads; i++) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, cfg, i); + mf = v4l2_subdev_get_try_format(sd, sd_state, i); *mf = fmt_default; } @@ -57,7 +57,7 @@ } static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { u32 mbus_code = vimc_mbus_code_by_index(code->index); @@ -71,7 +71,7 @@ } static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { const struct vimc_pix_map *vpix; @@ -93,14 +93,14 @@ } static int vimc_sen_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vimc_sen_device *vsen = container_of(sd, struct vimc_sen_device, sd); fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) : + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) : vsen->mbus_format; return 0; @@ -146,7 +146,7 @@ } static int vimc_sen_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd); @@ -159,7 +159,7 @@ mf = &vsen->mbus_format; } else { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); } /* Set the new format */ diff -Naur --no-dereference a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c --- a/drivers/media/usb/au0828/au0828-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/usb/au0828/au0828-core.c 2022-01-06 12:45:53.822318139 -0500 @@ -410,7 +410,7 @@ goto end; } - ret = __media_pipeline_start(entity, pipe); + ret = __media_pipeline_start(entity->pads, pipe); if (ret) { pr_err("Start Pipeline: %s->%s Error %d\n", source->name, entity->name, ret); @@ -501,12 +501,12 @@ return; /* stop pipeline */ - __media_pipeline_stop(dev->active_link_owner); + __media_pipeline_stop(dev->active_link_owner->pads); pr_debug("Pipeline stop for %s\n", dev->active_link_owner->name); ret = __media_pipeline_start( - dev->active_link_user, + dev->active_link_user->pads, dev->active_link_user_pipe); if (ret) { pr_err("Start Pipeline: %s->%s %d\n", @@ -532,7 +532,7 @@ return; /* stop pipeline */ - __media_pipeline_stop(dev->active_link_owner); + __media_pipeline_stop(dev->active_link_owner->pads); pr_debug("Pipeline stop for %s\n", dev->active_link_owner->name); diff -Naur --no-dereference a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c --- a/drivers/media/usb/go7007/s2250-board.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/usb/go7007/s2250-board.c 2022-01-06 12:45:53.822318139 -0500 @@ -398,7 +398,7 @@ } static int s2250_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; diff -Naur --no-dereference a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c --- a/drivers/media/v4l2-core/v4l2-common.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/v4l2-core/v4l2-common.c 2022-01-06 12:45:53.822318139 -0500 @@ -441,3 +441,36 @@ return 0; } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); + +s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul, + unsigned int div) +{ + struct v4l2_ctrl *ctrl; + s64 freq; + + ctrl = v4l2_ctrl_find(handler, V4L2_CID_LINK_FREQ); + if (ctrl) { + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; + int ret; + + qm.index = v4l2_ctrl_g_ctrl(ctrl); + + ret = v4l2_querymenu(handler, &qm); + if (ret) + return -ENOENT; + + freq = qm.value; + } else { + if (!mul || !div) + return -ENOENT; + + ctrl = v4l2_ctrl_find(handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) + return -ENOENT; + + freq = div_u64(v4l2_ctrl_g_ctrl_int64(ctrl) * mul, div); + } + + return freq > 0 ? freq : -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_get_link_freq); diff -Naur --no-dereference a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c --- a/drivers/media/v4l2-core/v4l2-ioctl.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/v4l2-core/v4l2-ioctl.c 2022-01-06 12:45:53.822318139 -0500 @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -1420,6 +1421,8 @@ case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; + case V4L2_PIX_FMT_TI1210: descr = "10-bit YUV 4:2:0 (NV12)"; break; + case V4L2_PIX_FMT_TI1610: descr = "10-bit YUV 4:2:2 (NV16)"; break; default: /* Compressed formats */ @@ -3104,6 +3107,21 @@ } break; } + + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *routing = parg; + + if (routing->num_routes > 256) + return -EINVAL; + + *user_ptr = u64_to_user_ptr(routing->routes); + *kernel_ptr = (void **)&routing->routes; + *array_size = sizeof(struct v4l2_subdev_route) + * routing->num_routes; + ret = 1; + break; + } } return ret; @@ -3340,8 +3358,15 @@ /* * Some ioctls can return an error, but still have valid * results that must be returned. + * + * FIXME: subdev IOCTLS are partially handled here and partially in + * v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS + * defined here as part of the 'v4l2_ioctls' array. As + * VIDIOC_SUBDEV_G_ROUTING needs to return results to applications even + * in case of failure, but it is not defined here as part of the + * 'v4l2_ioctls' array, insert an ad-hoc check to address that. */ - if (err < 0 && !always_copy) + if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING) goto out; out_array_args: diff -Naur --no-dereference a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c --- a/drivers/media/v4l2-core/v4l2-mc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/v4l2-core/v4l2-mc.c 2022-01-06 12:45:53.822318139 -0500 @@ -427,20 +427,20 @@ /* * pipeline_pm_use_count - Count the number of users of a pipeline - * @entity: The entity + * @pad: Any pad along the pipeline * * Return the total number of users of all video device nodes in the pipeline. */ -static int pipeline_pm_use_count(struct media_entity *entity, - struct media_graph *graph) +static int pipeline_pm_use_count(struct media_pad *pad, + struct media_graph *graph) { int use = 0; - media_graph_walk_start(graph, entity); + media_graph_walk_start(graph, pad); - while ((entity = media_graph_walk_next(graph))) { - if (is_media_entity_v4l2_video_device(entity)) - use += entity->use_count; + while ((pad = media_graph_walk_next(graph))) { + if (is_media_entity_v4l2_video_device(pad->entity)) + use += pad->entity->use_count; } return use; @@ -482,7 +482,7 @@ /* * pipeline_pm_power - Apply power change to all entities in a pipeline - * @entity: The entity + * @pad: Any pad along the pipeline * @change: Use count change * * Walk the pipeline to update the use count and the power state of all non-node @@ -490,30 +490,29 @@ * * Return 0 on success or a negative error code on failure. */ -static int pipeline_pm_power(struct media_entity *entity, int change, - struct media_graph *graph) +static int pipeline_pm_power(struct media_pad *pad, int change, + struct media_graph *graph) { - struct media_entity *first = entity; + struct media_pad *tmp_pad, *first = pad; int ret = 0; if (!change) return 0; - media_graph_walk_start(graph, entity); + media_graph_walk_start(graph, pad); - while (!ret && (entity = media_graph_walk_next(graph))) - if (is_media_entity_v4l2_subdev(entity)) - ret = pipeline_pm_power_one(entity, change); + while (!ret && (pad = media_graph_walk_next(graph))) + if (is_media_entity_v4l2_subdev(pad->entity)) + ret = pipeline_pm_power_one(pad->entity, change); if (!ret) return ret; media_graph_walk_start(graph, first); - while ((first = media_graph_walk_next(graph)) - && first != entity) - if (is_media_entity_v4l2_subdev(first)) - pipeline_pm_power_one(first, -change); + while ((tmp_pad = media_graph_walk_next(graph)) && tmp_pad != pad) + if (is_media_entity_v4l2_subdev(tmp_pad->entity)) + pipeline_pm_power_one(tmp_pad->entity, -change); return ret; } @@ -531,7 +530,7 @@ WARN_ON(entity->use_count < 0); /* Apply power change to connected non-nodes. */ - ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk); + ret = pipeline_pm_power(entity->pads, change, &mdev->pm_count_walk); if (ret < 0) entity->use_count -= change; @@ -557,8 +556,8 @@ unsigned int notification) { struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk; - struct media_entity *source = link->source->entity; - struct media_entity *sink = link->sink->entity; + struct media_pad *source = link->source; + struct media_pad *sink = link->sink; int source_use; int sink_use; int ret = 0; diff -Naur --no-dereference a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c --- a/drivers/media/v4l2-core/v4l2-subdev.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/media/v4l2-core/v4l2-subdev.c 2022-01-06 12:45:53.822318139 -0500 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -26,19 +27,21 @@ #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { - if (sd->entity.num_pads) { - fh->pad = v4l2_subdev_alloc_pad_config(sd); - if (fh->pad == NULL) - return -ENOMEM; - } + struct v4l2_subdev_state *state; + + state = v4l2_subdev_alloc_state(sd); + if (IS_ERR(state)) + return PTR_ERR(state); + + fh->state = state; return 0; } static void subdev_fh_free(struct v4l2_subdev_fh *fh) { - v4l2_subdev_free_pad_config(fh->pad); - fh->pad = NULL; + v4l2_subdev_free_state(fh->state); + fh->state = NULL; } static int subdev_open(struct file *file) @@ -146,63 +149,63 @@ return 0; } -static int check_cfg(u32 which, struct v4l2_subdev_pad_config *cfg) +static int check_cfg(u32 which, struct v4l2_subdev_state *state) { - if (which == V4L2_SUBDEV_FORMAT_TRY && !cfg) + if (which == V4L2_SUBDEV_FORMAT_TRY && !state) return -EINVAL; return 0; } static inline int check_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { if (!format) return -EINVAL; return check_which(format->which) ? : check_pad(sd, format->pad) ? : - check_cfg(format->which, cfg); + check_cfg(format->which, state); } static int call_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { - return check_format(sd, cfg, format) ? : - sd->ops->pad->get_fmt(sd, cfg, format); + return check_format(sd, state, format) ? : + sd->ops->pad->get_fmt(sd, state, format); } static int call_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { - return check_format(sd, cfg, format) ? : - sd->ops->pad->set_fmt(sd, cfg, format); + return check_format(sd, state, format) ? : + sd->ops->pad->set_fmt(sd, state, format); } static int call_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { if (!code) return -EINVAL; return check_which(code->which) ? : check_pad(sd, code->pad) ? : - check_cfg(code->which, cfg) ? : - sd->ops->pad->enum_mbus_code(sd, cfg, code); + check_cfg(code->which, state) ? : + sd->ops->pad->enum_mbus_code(sd, state, code); } static int call_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { if (!fse) return -EINVAL; return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : - check_cfg(fse->which, cfg) ? : - sd->ops->pad->enum_frame_size(sd, cfg, fse); + check_cfg(fse->which, state) ? : + sd->ops->pad->enum_frame_size(sd, state, fse); } static inline int check_frame_interval(struct v4l2_subdev *sd, @@ -229,42 +232,42 @@ } static int call_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_interval_enum *fie) { if (!fie) return -EINVAL; return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : - check_cfg(fie->which, cfg) ? : - sd->ops->pad->enum_frame_interval(sd, cfg, fie); + check_cfg(fie->which, state) ? : + sd->ops->pad->enum_frame_interval(sd, state, fie); } static inline int check_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { if (!sel) return -EINVAL; return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : - check_cfg(sel->which, cfg); + check_cfg(sel->which, state); } static int call_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { - return check_selection(sd, cfg, sel) ? : - sd->ops->pad->get_selection(sd, cfg, sel); + return check_selection(sd, state, sel) ? : + sd->ops->pad->get_selection(sd, state, sel); } static int call_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { - return check_selection(sd, cfg, sel) ? : - sd->ops->pad->set_selection(sd, cfg, sel); + return check_selection(sd, state, sel) ? : + sd->ops->pad->set_selection(sd, state, sel); } static inline int check_edid(struct v4l2_subdev *sd, @@ -482,7 +485,7 @@ memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); - return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->pad, format); + return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->state, format); } case VIDIOC_SUBDEV_S_FMT: { @@ -493,7 +496,7 @@ memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); - return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format); + return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->state, format); } case VIDIOC_SUBDEV_G_CROP: { @@ -507,7 +510,7 @@ sel.target = V4L2_SEL_TGT_CROP; rval = v4l2_subdev_call( - sd, pad, get_selection, subdev_fh->pad, &sel); + sd, pad, get_selection, subdev_fh->state, &sel); crop->rect = sel.r; @@ -529,7 +532,7 @@ sel.r = crop->rect; rval = v4l2_subdev_call( - sd, pad, set_selection, subdev_fh->pad, &sel); + sd, pad, set_selection, subdev_fh->state, &sel); crop->rect = sel.r; @@ -540,7 +543,7 @@ struct v4l2_subdev_mbus_code_enum *code = arg; memset(code->reserved, 0, sizeof(code->reserved)); - return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad, + return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->state, code); } @@ -548,7 +551,7 @@ struct v4l2_subdev_frame_size_enum *fse = arg; memset(fse->reserved, 0, sizeof(fse->reserved)); - return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad, + return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->state, fse); } @@ -573,7 +576,7 @@ struct v4l2_subdev_frame_interval_enum *fie = arg; memset(fie->reserved, 0, sizeof(fie->reserved)); - return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad, + return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->state, fie); } @@ -582,7 +585,7 @@ memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( - sd, pad, get_selection, subdev_fh->pad, sel); + sd, pad, get_selection, subdev_fh->state, sel); } case VIDIOC_SUBDEV_S_SELECTION: { @@ -593,7 +596,7 @@ memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( - sd, pad, set_selection, subdev_fh->pad, sel); + sd, pad, set_selection, subdev_fh->state, sel); } case VIDIOC_G_EDID: { @@ -657,6 +660,52 @@ case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); + case VIDIOC_SUBDEV_G_ROUTING: { + struct v4l2_subdev_routing *routing = arg; + struct v4l2_subdev_krouting krouting = { + .which = routing->which, + .num_routes = routing->num_routes, + .routes = (struct v4l2_subdev_route *)(uintptr_t) + routing->routes, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, get_routing, subdev_fh->state, &krouting); + + routing->num_routes = krouting.num_routes; + + return ret; + } + + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *routing = arg; + struct v4l2_subdev_route *routes = + (struct v4l2_subdev_route *)(uintptr_t)routing->routes; + struct v4l2_subdev_krouting krouting = {}; + unsigned int i; + + if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + + for (i = 0; i < routing->num_routes; ++i) { + if (routes[i].sink_pad >= sd->entity.num_pads || + routes[i].source_pad >= sd->entity.num_pads) + return -EINVAL; + + if (!(sd->entity.pads[routes[i].sink_pad].flags & + MEDIA_PAD_FL_SINK) || + !(sd->entity.pads[routes[i].source_pad].flags & + MEDIA_PAD_FL_SOURCE)) + return -EINVAL; + } + + krouting.which = routing->which; + krouting.num_routes = routing->num_routes; + krouting.routes = routes; + + return v4l2_subdev_call(sd, pad, set_routing, subdev_fh->state, &krouting); + } + default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } @@ -788,6 +837,7 @@ static int v4l2_subdev_link_validate_get_format(struct media_pad *pad, + u32 stream, struct v4l2_subdev_format *fmt) { if (is_media_entity_v4l2_subdev(pad->entity)) { @@ -796,6 +846,7 @@ fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; + fmt->stream = stream; return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); } @@ -806,63 +857,352 @@ return -EINVAL; } -int v4l2_subdev_link_validate(struct media_link *link) +int v4l2_subdev_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) { - struct v4l2_subdev *sink; - struct v4l2_subdev_format sink_fmt, source_fmt; - int rval; + int ret; - rval = v4l2_subdev_link_validate_get_format( - link->source, &source_fmt); - if (rval < 0) + routing->which = state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + routing->routes = NULL; + routing->num_routes = 0; + + ret = v4l2_subdev_call(sd, pad, get_routing, state, routing); + if (ret == 0) return 0; + if (ret != -ENOSPC) + return ret; + + routing->routes = kvmalloc_array(routing->num_routes, + sizeof(*routing->routes), GFP_KERNEL); + if (!routing->routes) + return -ENOMEM; + + ret = v4l2_subdev_call(sd, pad, get_routing, state, routing); + if (ret) { + kvfree(routing->routes); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_get_routing); + +void v4l2_subdev_free_routing(struct v4l2_subdev_krouting *routing) +{ + kvfree(routing->routes); + routing->routes = NULL; + routing->num_routes = 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_free_routing); + +int v4l2_subdev_cpy_routing(struct v4l2_subdev_krouting *dst, + const struct v4l2_subdev_krouting *src) +{ + if (dst->num_routes < src->num_routes) { + dst->num_routes = src->num_routes; + return -ENOSPC; + } - rval = v4l2_subdev_link_validate_get_format( - link->sink, &sink_fmt); - if (rval < 0) + memcpy(dst->routes, src->routes, + src->num_routes * sizeof(*src->routes)); + dst->num_routes = src->num_routes; + dst->which = src->which; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_cpy_routing); + +int v4l2_subdev_dup_routing(struct v4l2_subdev_krouting *dst, + const struct v4l2_subdev_krouting *src) +{ + v4l2_subdev_free_routing(dst); + + if (src->num_routes == 0) { + dst->which = src->which; return 0; + } - sink = media_entity_to_v4l2_subdev(link->sink->entity); + dst->routes = kvmalloc_array(src->num_routes, sizeof(*src->routes), + GFP_KERNEL); + if (!dst->routes) + return -ENOMEM; - rval = v4l2_subdev_call(sink, pad, link_validate, link, - &source_fmt, &sink_fmt); - if (rval != -ENOIOCTLCMD) - return rval; + memcpy(dst->routes, src->routes, + src->num_routes * sizeof(*src->routes)); + dst->num_routes = src->num_routes; + dst->which = src->which; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_dup_routing); + +bool v4l2_subdev_has_route(struct v4l2_subdev_krouting *routing, + unsigned int pad0, unsigned int pad1) +{ + unsigned int i; + + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; - return v4l2_subdev_link_validate_default( - sink, link, &source_fmt, &sink_fmt); + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if ((route->sink_pad == pad0 && route->source_pad == pad1) || + (route->source_pad == pad0 && route->sink_pad == pad1)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_has_route); + +static int cmp_u32(const void *a, const void *b) +{ + u32 a32 = *(u32 *)a; + u32 b32 = *(u32 *)b; + + return a32 > b32 ? 1 : (a32 < b32 ? -1 : 0); } -EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); -struct v4l2_subdev_pad_config * -v4l2_subdev_alloc_pad_config(struct v4l2_subdev *sd) +int v4l2_subdev_link_validate(struct media_link *link) { - struct v4l2_subdev_pad_config *cfg; int ret; + unsigned int i; + + struct v4l2_subdev *source_subdev = + media_entity_to_v4l2_subdev(link->source->entity); + struct v4l2_subdev *sink_subdev = + media_entity_to_v4l2_subdev(link->sink->entity); + struct device *dev = sink_subdev->entity.graph_obj.mdev->dev; + + struct v4l2_subdev_krouting routing; + + static const u32 default_streams[] = { 0 }; + + u32 num_source_streams = 0; + const u32 *source_streams = NULL; + u32 num_sink_streams = 0; + const u32 *sink_streams = NULL; + + dev_dbg(dev, "validating link \"%s\":%u -> \"%s\":%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + /* Get source streams */ - if (!sd->entity.num_pads) - return NULL; + memset(&routing, 0, sizeof(routing)); - cfg = kvmalloc_array(sd->entity.num_pads, sizeof(*cfg), - GFP_KERNEL | __GFP_ZERO); - if (!cfg) - return NULL; + ret = v4l2_subdev_get_routing(source_subdev, NULL, &routing); - ret = v4l2_subdev_call(sd, pad, init_cfg, cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - kvfree(cfg); - return NULL; + if (ret && ret != -ENOIOCTLCMD) + return ret; + + if (ret == -ENOIOCTLCMD) { + num_source_streams = 1; + source_streams = default_streams; + } else { + u32 *streams; + + streams = kmalloc_array(routing.num_routes, sizeof(u32), + GFP_KERNEL); + + for (i = 0; i < routing.num_routes; ++i) { + int j; + struct v4l2_subdev_route *route = &routing.routes[i]; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (route->source_pad != link->source->index) + continue; + + for (j = 0; j < num_source_streams; ++j) { + if (streams[j] == route->source_stream) + break; + } + + if (j != num_source_streams) + continue; + + streams[num_source_streams++] = route->source_stream; + + } + + sort(streams, num_source_streams, sizeof(u32), &cmp_u32, NULL); + + source_streams = streams; + + v4l2_subdev_free_routing(&routing); } - return cfg; + /* Get sink streams */ + + memset(&routing, 0, sizeof(routing)); + + ret = v4l2_subdev_get_routing(sink_subdev, NULL, &routing); + + if (ret && ret != -ENOIOCTLCMD) + goto out; + + if (ret == -ENOIOCTLCMD) { + num_sink_streams = 1; + sink_streams = default_streams; + } else { + u32 *streams; + + streams = kmalloc_array(routing.num_routes, sizeof(u32), + GFP_KERNEL); + + for (i = 0; i < routing.num_routes; ++i) { + struct v4l2_subdev_route *route = &routing.routes[i]; + int j; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (route->sink_pad != link->sink->index) + continue; + + for (j = 0; j < num_sink_streams; ++j) { + if (streams[j] == route->sink_stream) + break; + } + + if (j != num_sink_streams) + continue; + + streams[num_sink_streams++] = route->sink_stream; + } + + sort(streams, num_sink_streams, sizeof(u32), &cmp_u32, NULL); + + sink_streams = streams; + + v4l2_subdev_free_routing(&routing); + } + + if (num_source_streams != num_sink_streams) { + dev_err(dev, + "Sink and source stream count mismatch: %d vs %d\n", + num_source_streams, num_sink_streams); + ret = -EINVAL; + goto out; + } + + /* Validate source and sink stream formats */ + + for (i = 0; i < num_source_streams; ++i) { + struct v4l2_subdev_format sink_fmt, source_fmt; + u32 stream; + + if (source_streams[i] != sink_streams[i]) { + dev_err(dev, "Sink and source streams do not match\n"); + ret = -EINVAL; + goto out; + } + + stream = source_streams[i]; + + dev_dbg(dev, "validating stream \"%s\":%u:%u -> \"%s\":%u:%u\n", + link->source->entity->name, link->source->index, stream, + link->sink->entity->name, link->sink->index, stream); + + ret = v4l2_subdev_link_validate_get_format(link->source, stream, + &source_fmt); + if (ret < 0) { + dev_dbg(dev, "Failed to get format for \"%s\":%u:%u (but that's ok)\n", + link->source->entity->name, link->source->index, + stream); + ret = 0; + continue; + } + + ret = v4l2_subdev_link_validate_get_format(link->sink, stream, + &sink_fmt); + if (ret < 0) { + dev_dbg(dev, "Failed to get format for \"%s\":%u:%u (but that's ok)\n", + link->sink->entity->name, link->sink->index, + stream); + ret = 0; + continue; + } + + /* TODO: add stream number to link_validate() */ + ret = v4l2_subdev_call(sink_subdev, pad, link_validate, link, + &source_fmt, &sink_fmt); + if (!ret) + continue; + + if (ret != -ENOIOCTLCMD) + goto out; + + ret = v4l2_subdev_link_validate_default(sink_subdev, link, + &source_fmt, &sink_fmt); + + if (ret) + goto out; + } + +out: + if (source_streams != default_streams) + kfree(source_streams); + + if (sink_streams != default_streams) + kfree(sink_streams); + + return ret; } -EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_pad_config); +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); -void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg) +struct v4l2_subdev_state *v4l2_subdev_alloc_state(struct v4l2_subdev *sd) { - kvfree(cfg); + struct v4l2_subdev_state *state; + int ret; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto err; + } + + /* Drivers that support streams do not need the legacy pad config */ + if (!(sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED) && sd->entity.num_pads) { + state->pads = kvmalloc_array(sd->entity.num_pads, + sizeof(*state->pads), + GFP_KERNEL | __GFP_ZERO); + if (!state->pads) { + ret = -ENOMEM; + goto err; + } + } + + ret = v4l2_subdev_call(sd, pad, init_cfg, state); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto err; + + return state; + +err: + if (state && state->pads) + kvfree(state->pads); + + kfree(state); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_state); + +void v4l2_subdev_free_state(struct v4l2_subdev_state *state) +{ + v4l2_subdev_free_routing(&state->routing); + v4l2_uninit_stream_configs(&state->stream_configs); + + kvfree(state->pads); + kvfree(state); } -EXPORT_SYMBOL_GPL(v4l2_subdev_free_pad_config); +EXPORT_SYMBOL_GPL(v4l2_subdev_free_state); + #endif /* CONFIG_MEDIA_CONTROLLER */ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) @@ -891,3 +1231,65 @@ v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev); } EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); + +int v4l2_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, + const struct v4l2_subdev_krouting *routing) +{ + u32 num_configs = 0; + unsigned int i; + u32 format_idx = 0; + + v4l2_uninit_stream_configs(stream_configs); + + /* Count number of formats needed */ + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + /* Each route needs a format on both ends of the route */ + num_configs += 2; + } + + if (num_configs) { + stream_configs->configs = + kvcalloc(num_configs, sizeof(*stream_configs->configs), + GFP_KERNEL); + + if (!stream_configs->configs) + return -ENOMEM; + + stream_configs->num_configs = num_configs; + } + + /* Fill in the 'pad' and stream' value for each item in the array from the routing table */ + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + u32 idx; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + idx = format_idx++; + + stream_configs->configs[idx].pad = route->sink_pad; + stream_configs->configs[idx].stream = route->sink_stream; + + idx = format_idx++; + + stream_configs->configs[idx].pad = route->source_pad; + stream_configs->configs[idx].stream = route->source_stream; + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_init_stream_configs); + +void v4l2_uninit_stream_configs(struct v4l2_subdev_stream_configs *stream_configs) +{ + kvfree(stream_configs->configs); + stream_configs->configs = NULL; + stream_configs->num_configs = 0; +} +EXPORT_SYMBOL_GPL(v4l2_uninit_stream_configs); diff -Naur --no-dereference a/drivers/memory/Kconfig b/drivers/memory/Kconfig --- a/drivers/memory/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/memory/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -103,8 +103,8 @@ temperature changes config OMAP_GPMC - bool "Texas Instruments OMAP SoC GPMC driver" if COMPILE_TEST - depends on OF_ADDRESS + bool "Texas Instruments OMAP SoC GPMC driver" + depends on OF_ADDRESS || COMPILE_TEST select GPIOLIB help This driver is for the General Purpose Memory Controller (GPMC) diff -Naur --no-dereference a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c --- a/drivers/memory/omap-gpmc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/memory/omap-gpmc.c 2022-01-06 12:45:53.822318139 -0500 @@ -233,6 +233,7 @@ struct irq_chip irq_chip; struct gpio_chip gpio_chip; int nirqs; + struct resource *data; }; static struct irq_domain *gpmc_irq_domain; @@ -1452,12 +1453,18 @@ } } -static void gpmc_mem_init(void) +static void gpmc_mem_init(struct gpmc_device *gpmc) { int cs; - gpmc_mem_root.start = GPMC_MEM_START; - gpmc_mem_root.end = GPMC_MEM_END; + if (!gpmc->data) { + /* All legacy devices have same data IO window */ + gpmc_mem_root.start = GPMC_MEM_START; + gpmc_mem_root.end = GPMC_MEM_END; + } else { + gpmc_mem_root.start = gpmc->data->start; + gpmc_mem_root.end = gpmc->data->end; + } /* Reserve all regions that has been set up by bootloader */ for (cs = 0; cs < gpmc_cs_num; cs++) { @@ -1884,6 +1891,7 @@ { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ + { .compatible = "ti,am64-gpmc" }, { } }; @@ -2171,7 +2179,8 @@ } } - if (of_device_is_compatible(child, "ti,omap2-nand")) { + if (of_device_is_compatible(child, "ti,omap2-nand") || + of_node_name_eq(child, "nand")) { /* NAND specific setup */ val = 8; of_property_read_u32(child, "nand-bus-width", &val); @@ -2398,13 +2407,25 @@ gpmc->dev = &pdev->dev; platform_set_drvdata(pdev, gpmc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (!res) { + /* legacy DT */ + gpmc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gpmc_base)) + return PTR_ERR(gpmc_base); + } else { + gpmc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpmc_base)) + return PTR_ERR(gpmc_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data"); + if (!res) { + dev_err(&pdev->dev, "couldn't get data reg resource\n"); + return -ENOENT; + } - gpmc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(gpmc_base)) - return PTR_ERR(gpmc_base); + gpmc->data = res; + } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -2458,7 +2479,7 @@ dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), GPMC_REVISION_MINOR(l)); - gpmc_mem_init(); + gpmc_mem_init(gpmc); rc = gpmc_gpio_init(gpmc); if (rc) goto gpio_init_failed; diff -Naur --no-dereference a/drivers/misc/cape/beaglebone/bone-pinmux-helper.c b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c --- a/drivers/misc/cape/beaglebone/bone-pinmux-helper.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,242 @@ +/* + * Pinmux helper driver + * + * Copyright (C) 2013 Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct of_device_id bone_pinmux_helper_of_match[] = { + { + .compatible = "bone-pinmux-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, bone_pinmux_helper_of_match); + +struct pinmux_helper_data { + struct pinctrl *pinctrl; + char *selected_state_name; +}; + +static ssize_t pinmux_helper_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + const char *name; + + name = data->selected_state_name; + if (name == NULL || strlen(name) == 0) + name = "none"; + return sprintf(buf, "%s\n", name); +} + +static ssize_t pinmux_helper_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + struct pinctrl_state *state; + char *state_name; + char *s; + int err; + + /* duplicate (as a null terminated string) */ + state_name = kmalloc(count + 1, GFP_KERNEL); + if (state_name == NULL) + return -ENOMEM; + memcpy(state_name, buf, count); + state_name[count] = '\0'; + + /* and chop off newline */ + s = strchr(state_name, '\n'); + if (s != NULL) + *s = '\0'; + + /* try to select default state at first (if it exists) */ + state = pinctrl_lookup_state(data->pinctrl, state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) + dev_err(dev, "Failed to select state %s\n", + state_name); + } else { + dev_err(dev, "Failed to find state %s\n", state_name); + err = PTR_ERR_OR_ZERO(state); + } + + if (err == 0) { + kfree(data->selected_state_name); + data->selected_state_name = state_name; + } + + return err ? err : count; +} + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, + pinmux_helper_show_state, pinmux_helper_store_state); + +static struct attribute *pinmux_helper_attributes[] = { + &dev_attr_state.attr, + NULL +}; + +static const struct attribute_group pinmux_helper_attr_group = { + .attrs = pinmux_helper_attributes, +}; + +static int bone_pinmux_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinmux_helper_data *data; + struct pinctrl_state *state; + char *state_name; + const char *mode_name; + int mode_len; + int err; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Failed to allocate data\n"); + err = -ENOMEM; + goto err_no_mem; + } + + state_name = kmalloc(strlen(PINCTRL_STATE_DEFAULT) + 1, + GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_state_mem; + } + data->selected_state_name = state_name; + strcpy(data->selected_state_name, PINCTRL_STATE_DEFAULT); + + platform_set_drvdata(pdev, data); + + data->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(data->pinctrl)) { + dev_err(dev, "Failed to get pinctrl\n"); + err = PTR_ERR_OR_ZERO(data->pinctrl); + goto err_no_pinctrl; + } + + /* See if an initial mode is specified in the device tree */ + mode_name = of_get_property(dev->of_node, "mode", &mode_len); + + err = -1; + if (mode_name != NULL ) { + state_name = kmalloc(mode_len + 1, GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_mode_mem; + } + strncpy(state_name, mode_name, mode_len); + + /* try to select requested mode */ + state = pinctrl_lookup_state(data->pinctrl, state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) { + dev_warn(dev, "Unable to select requested mode %s\n", state_name); + kfree(state_name); + } else { + kfree(data->selected_state_name); + data->selected_state_name = state_name; + dev_notice(dev, "Set initial pinmux mode to %s\n", state_name); + } + } + } + + /* try to select default state if mode_name failed */ + if ( err != 0) { + state = pinctrl_lookup_state(data->pinctrl, + data->selected_state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) { + dev_err(dev, "Failed to select default state\n"); + goto err_no_state; + } + } else { + data->selected_state_name = '\0'; + } + } + + /* Register sysfs hooks */ + err = sysfs_create_group(&dev->kobj, &pinmux_helper_attr_group); + if (err) { + dev_err(dev, "Failed to create sysfs group\n"); + goto err_no_sysfs; + } + + return 0; + +err_no_sysfs: +err_no_state: +err_no_mode_mem: + devm_pinctrl_put(data->pinctrl); +err_no_pinctrl: + devm_kfree(dev, data->selected_state_name); +err_no_state_mem: + devm_kfree(dev, data); +err_no_mem: + return err; +} + +static int bone_pinmux_helper_remove(struct platform_device *pdev) +{ + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + sysfs_remove_group(&dev->kobj, &pinmux_helper_attr_group); + kfree(data->selected_state_name); + devm_pinctrl_put(data->pinctrl); + devm_kfree(dev, data); + + return 0; +} + +struct platform_driver bone_pinmux_helper_driver = { + .probe = bone_pinmux_helper_probe, + .remove = bone_pinmux_helper_remove, + .driver = { + .name = "bone-pinmux-helper", + .owner = THIS_MODULE, + .of_match_table = bone_pinmux_helper_of_match, + }, +}; + +module_platform_driver(bone_pinmux_helper_driver); + +MODULE_AUTHOR("Pantelis Antoniou"); +MODULE_DESCRIPTION("Beaglebone pinmux helper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bone-pinmux-helper"); diff -Naur --no-dereference a/drivers/misc/cape/beaglebone/Kconfig b/drivers/misc/cape/beaglebone/Kconfig --- a/drivers/misc/cape/beaglebone/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/misc/cape/beaglebone/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,10 @@ +# +# Beaglebone capes +# + +config BEAGLEBONE_PINMUX_HELPER + tristate "Beaglebone Pinmux Helper" + depends on ARCH_OMAP2PLUS && OF + default n + help + Say Y here to include support for the pinmux helper diff -Naur --no-dereference a/drivers/misc/cape/beaglebone/Makefile b/drivers/misc/cape/beaglebone/Makefile --- a/drivers/misc/cape/beaglebone/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/misc/cape/beaglebone/Makefile 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,5 @@ +# +# Makefile for beaglebone capes +# + +obj-$(CONFIG_BEAGLEBONE_PINMUX_HELPER) += bone-pinmux-helper.o diff -Naur --no-dereference a/drivers/misc/cape/Kconfig b/drivers/misc/cape/Kconfig --- a/drivers/misc/cape/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/misc/cape/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,5 @@ +# +# Capes +# + +source "drivers/misc/cape/beaglebone/Kconfig" diff -Naur --no-dereference a/drivers/misc/cape/Makefile b/drivers/misc/cape/Makefile --- a/drivers/misc/cape/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/misc/cape/Makefile 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,5 @@ +# +# Makefile for cape like devices +# + +obj-y += beaglebone/ diff -Naur --no-dereference a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c --- a/drivers/misc/eeprom/eeprom_93xx46.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/misc/eeprom/eeprom_93xx46.c 2022-01-06 12:45:53.822318139 -0500 @@ -528,3 +528,4 @@ MODULE_AUTHOR("Anatolij Gustschin "); MODULE_ALIAS("spi:93xx46"); MODULE_ALIAS("spi:eeprom-93xx46"); +MODULE_ALIAS("spi:93lc46b"); diff -Naur --no-dereference a/drivers/misc/Kconfig b/drivers/misc/Kconfig --- a/drivers/misc/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/misc/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -423,6 +423,13 @@ config SRAM_EXEC bool +config SRAM_DMA_HEAP + bool "Export on-chip SRAM pools using DMA-Heaps" + depends on SRAM + help + This driver allows the export of on-chip SRAM marked as exportable + to userspace using the DMA-Heaps interface. + config PCI_ENDPOINT_TEST depends on PCI select CRC32 @@ -475,6 +482,7 @@ source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" source "drivers/misc/genwqe/Kconfig" +source "drivers/misc/cape/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" source "drivers/misc/ocxl/Kconfig" diff -Naur --no-dereference a/drivers/misc/Makefile b/drivers/misc/Makefile --- a/drivers/misc/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/misc/Makefile 2022-01-06 12:45:53.822318139 -0500 @@ -46,7 +46,9 @@ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_SRAM_EXEC) += sram-exec.o +obj-$(CONFIG_SRAM_DMA_HEAP) += sram-dma-heap.o obj-$(CONFIG_GENWQE) += genwqe/ +obj-y += cape/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o diff -Naur --no-dereference a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c --- a/drivers/misc/pci_endpoint_test.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/misc/pci_endpoint_test.c 2022-01-06 12:45:53.822318139 -0500 @@ -68,8 +68,9 @@ #define PCI_ENDPOINT_TEST_FLAGS 0x2c #define FLAG_USE_DMA BIT(0) -#define PCI_DEVICE_ID_TI_J721E 0xb00d #define PCI_DEVICE_ID_TI_AM654 0xb00c +#define PCI_DEVICE_ID_TI_J7200 0xb00f +#define PCI_DEVICE_ID_TI_AM64 0xb010 #define PCI_DEVICE_ID_LS1088A 0x80c0 #define is_am654_pci_dev(pdev) \ @@ -80,6 +81,9 @@ #define PCI_DEVICE_ID_RENESAS_R8A774C0 0x002d #define PCI_DEVICE_ID_RENESAS_R8A774E1 0x0025 +#define is_j721e_pci_dev(pdev) \ + ((pdev)->device == PCI_DEVICE_ID_TI_J721E) + static DEFINE_IDA(pci_endpoint_test_ida); #define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \ @@ -811,9 +815,11 @@ pci_set_master(pdev); - if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) { - err = -EINVAL; - goto err_disable_irq; + if (!(is_am654_pci_dev(pdev) || is_j721e_pci_dev(pdev))) { + if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) { + err = -EINVAL; + goto err_disable_irq; + } } for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { @@ -851,9 +857,11 @@ goto err_ida_remove; } - if (!pci_endpoint_test_request_irq(test)) { - err = -EINVAL; - goto err_kfree_test_name; + if (!(is_am654_pci_dev(pdev) || is_j721e_pci_dev(pdev))) { + if (!pci_endpoint_test_request_irq(test)) { + err = -EINVAL; + goto err_kfree_test_name; + } } misc_device = &test->miscdev; @@ -970,6 +978,12 @@ { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E), .driver_data = (kernel_ulong_t)&j721e_data, }, + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J7200), + .driver_data = (kernel_ulong_t)&j721e_data, + }, + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM64), + .driver_data = (kernel_ulong_t)&j721e_data, + }, { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); @@ -979,6 +993,7 @@ .id_table = pci_endpoint_test_tbl, .probe = pci_endpoint_test_probe, .remove = pci_endpoint_test_remove, + .sriov_configure = pci_sriov_configure_simple, }; module_pci_driver(pci_endpoint_test_driver); diff -Naur --no-dereference a/drivers/misc/sram.c b/drivers/misc/sram.c --- a/drivers/misc/sram.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/misc/sram.c 2022-01-06 12:45:53.822318139 -0500 @@ -109,6 +109,15 @@ if (ret) return ret; } + if (block->dma_heap_export) { + ret = sram_add_pool(sram, block, start, part); + if (ret) + return ret; + + ret = sram_dma_heap_export(sram, block, start, part); + if (ret) + return ret; + } if (block->protect_exec) { ret = sram_check_protect_exec(sram, block, part); if (ret) @@ -209,8 +218,11 @@ if (of_find_property(child, "protect-exec", NULL)) block->protect_exec = true; - if ((block->export || block->pool || block->protect_exec) && - block->size) { + if (of_find_property(child, "dma-heap-export", NULL)) + block->dma_heap_export = true; + + if ((block->export || block->pool || block->protect_exec || + block->dma_heap_export) && block->size) { exports++; label = NULL; @@ -272,8 +284,8 @@ goto err_chunks; } - if ((block->export || block->pool || block->protect_exec) && - block->size) { + if ((block->export || block->pool || block->protect_exec || + block->dma_heap_export) && block->size) { ret = sram_add_partition(sram, block, res->start + block->start); if (ret) { diff -Naur --no-dereference a/drivers/misc/sram-dma-heap.c b/drivers/misc/sram-dma-heap.c --- a/drivers/misc/sram-dma-heap.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/misc/sram-dma-heap.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SRAM DMA-Heap userspace exporter + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sram.h" + +struct sram_dma_heap { + struct dma_heap *heap; + struct gen_pool *pool; +}; + +struct sram_dma_heap_buffer { + struct gen_pool *pool; + struct list_head attachments; + struct mutex attachments_lock; + unsigned long len; + void *vaddr; + phys_addr_t paddr; +}; + +struct dma_heap_attachment { + struct device *dev; + struct sg_table *table; + struct list_head list; +}; + +static int dma_heap_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a; + struct sg_table *table; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(a); + return -ENOMEM; + } + if (sg_alloc_table(table, 1, GFP_KERNEL)) { + kfree(table); + kfree(a); + return -ENOMEM; + } + sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(buffer->paddr)), buffer->len, 0); + + a->table = table; + a->dev = attachment->dev; + INIT_LIST_HEAD(&a->list); + + attachment->priv = a; + + mutex_lock(&buffer->attachments_lock); + list_add(&a->list, &buffer->attachments); + mutex_unlock(&buffer->attachments_lock); + + return 0; +} + +static void dma_heap_detatch(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a = attachment->priv; + + mutex_lock(&buffer->attachments_lock); + list_del(&a->list); + mutex_unlock(&buffer->attachments_lock); + + sg_free_table(a->table); + kfree(a->table); + kfree(a); +} + +static struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct dma_heap_attachment *a = attachment->priv; + struct sg_table *table = a->table; + + if (!dma_map_sg_attrs(attachment->dev, table->sgl, table->nents, + direction, DMA_ATTR_SKIP_CPU_SYNC)) + return ERR_PTR(-ENOMEM); + + return table; +} + +static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + dma_unmap_sg_attrs(attachment->dev, table->sgl, table->nents, + direction, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void dma_heap_dma_buf_release(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); + kfree(buffer); +} + +static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + int ret; + + /* SRAM mappings are not cached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret = vm_iomap_memory(vma, buffer->paddr, buffer->len); + if (ret) + pr_err("Could not map buffer to userspace\n"); + + return ret; +} + +static void *dma_heap_vmap(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->vaddr; +} + +const struct dma_buf_ops sram_dma_heap_buf_ops = { + .attach = dma_heap_attach, + .detach = dma_heap_detatch, + .map_dma_buf = dma_heap_map_dma_buf, + .unmap_dma_buf = dma_heap_unmap_dma_buf, + .release = dma_heap_dma_buf_release, + .mmap = dma_heap_mmap, + .vmap = dma_heap_vmap, +}; + +static int sram_dma_heap_allocate(struct dma_heap *heap, + unsigned long len, + unsigned long fd_flags, + unsigned long heap_flags) +{ + struct sram_dma_heap *sram_dma_heap = dma_heap_get_drvdata(heap); + struct sram_dma_heap_buffer *buffer; + + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dmabuf; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + buffer->pool = sram_dma_heap->pool; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->attachments_lock); + buffer->len = len; + + buffer->vaddr = (void *)gen_pool_alloc(buffer->pool, buffer->len); + if (!buffer->vaddr) { + ret = -ENOMEM; + goto free_buffer; + } + + buffer->paddr = gen_pool_virt_to_phys(buffer->pool, (unsigned long)buffer->vaddr); + if (buffer->paddr == -1) { + ret = -ENOMEM; + goto free_pool; + } + + /* create the dmabuf */ + exp_info.ops = &sram_dma_heap_buf_ops; + exp_info.size = buffer->len; + exp_info.flags = fd_flags; + exp_info.priv = buffer; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto free_pool; + } + + ret = dma_buf_fd(dmabuf, fd_flags); + if (ret < 0) { + dma_buf_put(dmabuf); + /* just return, as put will call release and that will free */ + return ret; + } + + return ret; + +free_pool: + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); +free_buffer: + kfree(buffer); + + return ret; +} + +static struct dma_heap_ops sram_dma_heap_ops = { + .allocate = sram_dma_heap_allocate, +}; + +int sram_dma_heap_export(struct sram_dev *sram, + struct sram_reserve *block, + phys_addr_t start, + struct sram_partition *part) +{ + struct sram_dma_heap *sram_dma_heap; + struct dma_heap_export_info exp_info; + + dev_info(sram->dev, "Exporting SRAM pool '%s'\n", block->label); + + sram_dma_heap = kzalloc(sizeof(*sram_dma_heap), GFP_KERNEL); + if (!sram_dma_heap) + return -ENOMEM; + sram_dma_heap->pool = part->pool; + + exp_info.name = block->label; + exp_info.ops = &sram_dma_heap_ops; + exp_info.priv = sram_dma_heap; + sram_dma_heap->heap = dma_heap_add(&exp_info); + if (IS_ERR(sram_dma_heap->heap)) { + int ret = PTR_ERR(sram_dma_heap->heap); + kfree(sram_dma_heap); + return ret; + } + + return 0; +} diff -Naur --no-dereference a/drivers/misc/sram.h b/drivers/misc/sram.h --- a/drivers/misc/sram.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/misc/sram.h 2022-01-06 12:45:53.822318139 -0500 @@ -32,6 +32,7 @@ bool export; bool pool; bool protect_exec; + bool dma_heap_export; const char *label; }; @@ -52,4 +53,20 @@ return -ENODEV; } #endif /* CONFIG_SRAM_EXEC */ + +#ifdef CONFIG_SRAM_DMA_HEAP +int sram_dma_heap_export(struct sram_dev *sram, + struct sram_reserve *block, + phys_addr_t start, + struct sram_partition *part); +#else +static inline int sram_dma_heap_export(struct sram_dev *sram, + struct sram_reserve *block, + phys_addr_t start, + struct sram_partition *part) +{ + return 0; +} +#endif /* CONFIG_SRAM_DMA_HEAP */ + #endif /* __SRAM_H */ diff -Naur --no-dereference a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c --- a/drivers/mmc/host/sdhci_am654.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mmc/host/sdhci_am654.c 2022-01-06 12:45:53.822318139 -0500 @@ -514,6 +514,26 @@ .flags = IOMUX_PRESENT, }; +static const struct sdhci_pltfm_data sdhci_am64_8bit_pdata = { + .ops = &sdhci_j721e_8bit_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static const struct sdhci_am654_driver_data sdhci_am64_8bit_drvdata = { + .pdata = &sdhci_am64_8bit_pdata, + .flags = DLL_PRESENT | DLL_CALIB, +}; + +static const struct sdhci_pltfm_data sdhci_am64_4bit_pdata = { + .ops = &sdhci_j721e_4bit_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static const struct sdhci_am654_driver_data sdhci_am64_4bit_drvdata = { + .pdata = &sdhci_am64_4bit_pdata, + .flags = IOMUX_PRESENT, +}; + static const struct soc_device_attribute sdhci_am654_devices[] = { { .family = "AM65X", .revision = "SR1.0", @@ -737,6 +757,14 @@ .compatible = "ti,j721e-sdhci-4bit", .data = &sdhci_j721e_4bit_drvdata, }, + { + .compatible = "ti,am64-sdhci-8bit", + .data = &sdhci_am64_8bit_drvdata, + }, + { + .compatible = "ti,am64-sdhci-4bit", + .data = &sdhci_am64_4bit_drvdata, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sdhci_am654_of_match); diff -Naur --no-dereference a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig --- a/drivers/mtd/nand/raw/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/nand/raw/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -62,7 +62,7 @@ config MTD_NAND_OMAP2 tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller" - depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST + depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST depends on HAS_IOMEM help Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 diff -Naur --no-dereference a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c --- a/drivers/mtd/nand/raw/omap2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/nand/raw/omap2.c 2022-01-06 12:45:53.822318139 -0500 @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -28,6 +28,7 @@ #include #include +#include #define DRIVER_NAME "omap2-nand" #define OMAP_NAND_TIMEOUT_MS 5000 @@ -164,6 +165,7 @@ u_char *buf; int buf_len; /* Interface to GPMC */ + void __iomem *fifo; struct gpmc_nand_regs reg; struct gpmc_nand_ops *ops; bool flash_bbt; @@ -171,6 +173,13 @@ struct device *elm_dev; /* NAND ready gpio */ struct gpio_desc *ready_gpiod; + + void (*data_in)(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit); + void (*data_out)(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit); + bool force_32bit; }; static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) @@ -178,6 +187,13 @@ return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); } +static void omap_nand_data_in(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit); + +static void omap_nand_data_out(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit); + /** * omap_prefetch_enable - configures and starts prefetch transfer * @cs: cs (chip select) number @@ -236,169 +252,70 @@ } /** - * omap_hwcontrol - hardware specific access to control-lines - * @chip: NAND chip object - * @cmd: command to device - * @ctrl: - * NAND_NCE: bit 0 -> don't care - * NAND_CLE: bit 1 -> Command Latch - * NAND_ALE: bit 2 -> Address Latch - * - * NOTE: boards may use different bits for these!! + * omap_nand_data_in_pref - NAND data in using prefetch Prefetch engine */ -static void omap_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl) +static void omap_nand_data_in_pref(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) { struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); - - if (cmd != NAND_CMD_NONE) { - if (ctrl & NAND_CLE) - writeb(cmd, info->reg.gpmc_nand_command); - - else if (ctrl & NAND_ALE) - writeb(cmd, info->reg.gpmc_nand_address); - - else /* NAND_NCE */ - writeb(cmd, info->reg.gpmc_nand_data); - } -} - -/** - * omap_read_buf8 - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - - ioread8_rep(nand->legacy.IO_ADDR_R, buf, len); -} - -/** - * omap_write_buf8 - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - u_char *p = (u_char *)buf; - bool status; - - while (len--) { - iowrite8(*p++, info->nand.legacy.IO_ADDR_W); - /* wait until buffer is available for write */ - do { - status = info->ops->nand_writebuffer_empty(); - } while (!status); - } -} - -/** - * omap_read_buf16 - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - - ioread16_rep(nand->legacy.IO_ADDR_R, buf, len / 2); -} - -/** - * omap_write_buf16 - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - u16 *p = (u16 *) buf; - bool status; - /* FIXME try bursts of writesw() or DMA ... */ - len >>= 1; - - while (len--) { - iowrite16(*p++, info->nand.legacy.IO_ADDR_W); - /* wait until buffer is available for write */ - do { - status = info->ops->nand_writebuffer_empty(); - } while (!status); - } -} - -/** - * omap_read_buf_pref - read data from NAND controller into buffer - * @chip: NAND chip object - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf_pref(struct nand_chip *chip, u_char *buf, int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); uint32_t r_count = 0; int ret = 0; u32 *p = (u32 *)buf; + unsigned int pref_len; - /* take care of subpage reads */ - if (len % 4) { - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len % 4); - else - omap_read_buf8(mtd, buf, len % 4); - p = (u32 *) (buf + len % 4); - len -= len % 4; + if (force_8bit) { + omap_nand_data_in(chip, buf, len, force_8bit); + return; } + /* read 32-bit words using prefetch and remaining bytes normally */ + /* configure and start prefetch transfer */ + pref_len = len - (len & 3); ret = omap_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info); if (ret) { - /* PFPW engine is busy, use cpu copy method */ - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, (u_char *)p, len); - else - omap_read_buf8(mtd, (u_char *)p, len); + /* prefetch engine is busy, use CPU copy method */ + omap_nand_data_in(chip, buf, len, false); } else { do { r_count = readl(info->reg.gpmc_prefetch_status); r_count = PREFETCH_STATUS_FIFO_CNT(r_count); r_count = r_count >> 2; - ioread32_rep(info->nand.legacy.IO_ADDR_R, p, r_count); + ioread32_rep(info->fifo, p, r_count); p += r_count; - len -= r_count << 2; - } while (len); - /* disable and stop the PFPW engine */ + pref_len -= r_count << 2; + } while (pref_len); + /* disable and stop the Prefetch engine */ omap_prefetch_reset(info->gpmc_cs, info); + /* fetch any remaining bytes */ + if (len & 3) + omap_nand_data_in(chip, p, len & 3, false); } } /** - * omap_write_buf_pref - write buffer to NAND controller - * @chip: NAND chip object - * @buf: data buffer - * @len: number of bytes to write + * omap_nand_data_out_pref - NAND data out using Write Posting engine */ -static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf, - int len) +static void omap_nand_data_out_pref(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); uint32_t w_count = 0; int i = 0, ret = 0; u16 *p = (u16 *)buf; unsigned long tim, limit; u32 val; + if (force_8bit) { + omap_nand_data_out(chip, buf, len, force_8bit); + return; + } + /* take care of subpage writes */ if (len % 2 != 0) { - writeb(*buf, info->nand.legacy.IO_ADDR_W); + writeb(*(u8 *)buf, info->fifo); p = (u16 *)(buf + 1); len--; } @@ -407,18 +324,15 @@ ret = omap_prefetch_enable(info->gpmc_cs, PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); if (ret) { - /* PFPW engine is busy, use cpu copy method */ - if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, (u_char *)p, len); - else - omap_write_buf8(mtd, (u_char *)p, len); + /* write posting engine is busy, use CPU copy method */ + omap_nand_data_out(chip, buf, len, false); } else { while (len) { w_count = readl(info->reg.gpmc_prefetch_status); w_count = PREFETCH_STATUS_FIFO_CNT(w_count); w_count = w_count >> 1; for (i = 0; (i < w_count) && len; i++, len -= 2) - iowrite16(*p++, info->nand.legacy.IO_ADDR_W); + iowrite16(*p++, info->fifo); } /* wait for data to flushed-out before reset the prefetch */ tim = 0; @@ -446,15 +360,16 @@ /* * omap_nand_dma_transfer: configure and start dma transfer - * @mtd: MTD device structure + * @chip: nand chip structure * @addr: virtual address in RAM of source/destination * @len: number of data bytes to be transferred * @is_write: flag for read/write operation */ -static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, - unsigned int len, int is_write) +static inline int omap_nand_dma_transfer(struct nand_chip *chip, + const void *addr, unsigned int len, + int is_write) { - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); struct dma_async_tx_descriptor *tx; enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; @@ -516,49 +431,41 @@ out_copy_unmap: dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) - : omap_write_buf16(mtd, (u_char *) addr, len); - else - is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) - : omap_write_buf8(mtd, (u_char *) addr, len); + is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false) + : omap_nand_data_out(chip, addr, len, false); + return 0; } /** - * omap_read_buf_dma_pref - read data from NAND controller into buffer - * @chip: NAND chip object - * @buf: buffer to store date - * @len: number of bytes to read + * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch */ -static void omap_read_buf_dma_pref(struct nand_chip *chip, u_char *buf, - int len) +static void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) { struct mtd_info *mtd = nand_to_mtd(chip); if (len <= mtd->oobsize) - omap_read_buf_pref(chip, buf, len); + omap_nand_data_in_pref(chip, buf, len, false); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, buf, len, 0x0); + omap_nand_dma_transfer(chip, buf, len, 0x0); } /** - * omap_write_buf_dma_pref - write buffer to NAND controller - * @chip: NAND chip object - * @buf: data buffer - * @len: number of bytes to write + * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting */ -static void omap_write_buf_dma_pref(struct nand_chip *chip, const u_char *buf, - int len) +static void omap_nand_data_out_dma_pref(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) { struct mtd_info *mtd = nand_to_mtd(chip); if (len <= mtd->oobsize) - omap_write_buf_pref(chip, buf, len); + omap_nand_data_out_pref(chip, buf, len, false); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1); + omap_nand_dma_transfer(chip, buf, len, 0x1); } /* @@ -582,13 +489,13 @@ bytes = info->buf_len; else if (!info->buf_len) bytes = 0; - iowrite32_rep(info->nand.legacy.IO_ADDR_W, (u32 *)info->buf, + iowrite32_rep(info->fifo, (u32 *)info->buf, bytes >> 2); info->buf = info->buf + bytes; info->buf_len -= bytes; } else { - ioread32_rep(info->nand.legacy.IO_ADDR_R, (u32 *)info->buf, + ioread32_rep(info->fifo, (u32 *)info->buf, bytes >> 2); info->buf = info->buf + bytes; @@ -608,20 +515,17 @@ } /* - * omap_read_buf_irq_pref - read data from NAND controller into buffer - * @chip: NAND chip object - * @buf: buffer to store date - * @len: number of bytes to read + * omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ */ -static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf, - int len) +static void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + struct mtd_info *mtd = nand_to_mtd(&info->nand); int ret = 0; - if (len <= mtd->oobsize) { - omap_read_buf_pref(chip, buf, len); + if (len <= mtd->oobsize || force_8bit) { + omap_nand_data_in(chip, buf, len, force_8bit); return; } @@ -632,9 +536,11 @@ /* configure and start prefetch transfer */ ret = omap_prefetch_enable(info->gpmc_cs, PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); - if (ret) + if (ret) { /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + omap_nand_data_in(chip, buf, len, false); + return; + } info->buf_len = len; @@ -647,31 +553,23 @@ /* disable and stop the PFPW engine */ omap_prefetch_reset(info->gpmc_cs, info); return; - -out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len); - else - omap_read_buf8(mtd, buf, len); } /* - * omap_write_buf_irq_pref - write buffer to NAND controller - * @chip: NAND chip object - * @buf: data buffer - * @len: number of bytes to write + * omap_nand_data_out_irq_pref - NAND out using write posting and IRQ */ -static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf, - int len) +static void omap_nand_data_out_irq_pref(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + struct mtd_info *mtd = nand_to_mtd(&info->nand); int ret = 0; unsigned long tim, limit; u32 val; - if (len <= mtd->oobsize) { - omap_write_buf_pref(chip, buf, len); + if (len <= mtd->oobsize || force_8bit) { + omap_nand_data_out(chip, buf, len, force_8bit); return; } @@ -682,9 +580,11 @@ /* configure and start prefetch transfer : size=24 */ ret = omap_prefetch_enable(info->gpmc_cs, (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); - if (ret) + if (ret) { /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + omap_nand_data_out(chip, buf, len, false); + return; + } info->buf_len = len; @@ -706,12 +606,6 @@ /* disable and stop the PFPW engine */ omap_prefetch_reset(info->gpmc_cs, info); return; - -out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, buf, len); - else - omap_write_buf8(mtd, buf, len); } /** @@ -977,50 +871,6 @@ } /** - * omap_wait - wait until the command is done - * @this: NAND Chip structure - * - * Wait function is called during Program and erase operations and - * the way it is called from MTD layer, we should wait till the NAND - * chip is ready after the programming/erase operation has completed. - * - * Erase can take up to 400ms and program up to 20ms according to - * general NAND and SmartMedia specs - */ -static int omap_wait(struct nand_chip *this) -{ - struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(this)); - unsigned long timeo = jiffies; - int status; - - timeo += msecs_to_jiffies(400); - - writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); - while (time_before(jiffies, timeo)) { - status = readb(info->reg.gpmc_nand_data); - if (status & NAND_STATUS_READY) - break; - cond_resched(); - } - - status = readb(info->reg.gpmc_nand_data); - return status; -} - -/** - * omap_dev_ready - checks the NAND Ready GPIO line - * @mtd: MTD device structure - * - * Returns true if ready and false if busy. - */ -static int omap_dev_ready(struct nand_chip *chip) -{ - struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); - - return gpiod_get_value(info->ready_gpiod); -} - -/** * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation * @mtd: MTD device structure * @mode: Read/Write mode @@ -1524,6 +1374,7 @@ int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); int ret; uint8_t *ecc_calc = chip->ecc.calc_buf; @@ -1533,7 +1384,7 @@ chip->ecc.hwctl(chip, NAND_ECC_WRITE); /* Write data */ - chip->legacy.write_buf(chip, buf, mtd->writesize); + info->data_out(chip, buf, mtd->writesize, false); /* Update ecc vector from GPMC result registers */ omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); @@ -1544,7 +1395,7 @@ return ret; /* Write ecc vector to OOB area */ - chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); + info->data_out(chip, chip->oob_poi, mtd->oobsize, false); return nand_prog_page_end_op(chip); } @@ -1565,6 +1416,7 @@ int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); u8 *ecc_calc = chip->ecc.calc_buf; int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes; @@ -1585,7 +1437,7 @@ chip->ecc.hwctl(chip, NAND_ECC_WRITE); /* Write data */ - chip->legacy.write_buf(chip, buf, mtd->writesize); + info->data_out(chip, buf, mtd->writesize, false); for (step = 0; step < ecc_steps; step++) { /* mask ECC of un-touched subpages by padding 0xFF */ @@ -1610,7 +1462,7 @@ return ret; /* write OOB buffer to NAND device */ - chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); + info->data_out(chip, chip->oob_poi, mtd->oobsize, false); return nand_prog_page_end_op(chip); } @@ -1633,6 +1485,7 @@ int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); uint8_t *ecc_calc = chip->ecc.calc_buf; uint8_t *ecc_code = chip->ecc.code_buf; int stat, ret; @@ -1644,7 +1497,7 @@ chip->ecc.hwctl(chip, NAND_ECC_READ); /* Read data */ - chip->legacy.read_buf(chip, buf, mtd->writesize); + info->data_in(chip, buf, mtd->writesize, false); /* Read oob bytes */ nand_change_read_column_op(chip, @@ -1927,8 +1780,9 @@ /* Re-populate low-level callbacks based on xfer modes */ switch (info->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: - chip->legacy.read_buf = omap_read_buf_pref; - chip->legacy.write_buf = omap_write_buf_pref; + dev_info(dev, "using prefetch polled xfer mode\n"); + info->data_in = omap_nand_data_in_pref; + info->data_out = omap_nand_data_out_pref; break; case NAND_OMAP_POLLED: @@ -1960,12 +1814,14 @@ err); return err; } - chip->legacy.read_buf = omap_read_buf_dma_pref; - chip->legacy.write_buf = omap_write_buf_dma_pref; + + info->data_in = omap_nand_data_in_dma_pref; + info->data_out = omap_nand_data_out_dma_pref; } break; case NAND_OMAP_PREFETCH_IRQ: + dev_info(dev, "using prefetch irq xfer mode\n"); info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); if (info->gpmc_irq_fifo <= 0) return -ENODEV; @@ -1992,9 +1848,8 @@ return err; } - chip->legacy.read_buf = omap_read_buf_irq_pref; - chip->legacy.write_buf = omap_write_buf_irq_pref; - + info->data_in = omap_nand_data_in_irq_pref; + info->data_out = omap_nand_data_out_irq_pref; break; default: @@ -2158,22 +2013,146 @@ return 0; } +static void omap_nand_data_in(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) +{ + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + u32 alignment = ((uintptr_t)buf | len) & 3; + + if (info->force_32bit) { + u32 val; + int left; + u8 *ptr; + + ioread32_rep(info->fifo, buf, len >> 2); + left = len & 0x3; + if (left) { + val = ioread32(info->fifo); + ptr = (u8 *)(buf + (len - left)); + while (left--) { + *ptr++ = val & 0xff; + val >>= 8; + } + } + + return; + } + + if (force_8bit || (alignment & 1)) + ioread8_rep(info->fifo, buf, len); + else if (alignment & 3) + ioread16_rep(info->fifo, buf, len >> 1); + else + ioread32_rep(info->fifo, buf, len >> 2); +} + +static void omap_nand_data_out(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) +{ + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + u32 alignment = ((uintptr_t)buf | len) & 3; + + if (force_8bit || (alignment & 1)) + iowrite8_rep(info->fifo, buf, len); + else if (alignment & 3) + iowrite16_rep(info->fifo, buf, len >> 1); + else + iowrite32_rep(info->fifo, buf, len >> 2); +} + +static int omap_nand_exec_instr(struct nand_chip *chip, + const struct nand_op_instr *instr) +{ + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + unsigned int i; + int ret; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + iowrite8(instr->ctx.cmd.opcode, + info->reg.gpmc_nand_command); + break; + + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) { + iowrite8(instr->ctx.addr.addrs[i], + info->reg.gpmc_nand_address); + } + break; + + case NAND_OP_DATA_IN_INSTR: + info->data_in(chip, instr->ctx.data.buf.in, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + break; + + case NAND_OP_DATA_OUT_INSTR: + info->data_out(chip, instr->ctx.data.buf.out, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + break; + + case NAND_OP_WAITRDY_INSTR: + ret = info->ready_gpiod ? + nand_gpio_waitrdy(chip, info->ready_gpiod, instr->ctx.waitrdy.timeout_ms) : + nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); + if (ret) + return ret; + break; + } + + if (instr->delay_ns) + ndelay(instr->delay_ns); + + return 0; +} + +static int omap_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + unsigned int i; + + if (check_only) + return 0; + + for (i = 0; i < op->ninstrs; i++) { + int ret; + + ret = omap_nand_exec_instr(chip, &op->instrs[i]); + if (ret) + return ret; + } + + return 0; +} + static const struct nand_controller_ops omap_nand_controller_ops = { .attach_chip = omap_nand_attach_chip, + .exec_op = omap_nand_exec_op, }; /* Shared among all NAND instances to synchronize access to the ECC Engine */ static struct nand_controller omap_gpmc_controller; static bool omap_gpmc_controller_initialized; +static const struct of_device_id omap_nand_ids[]; + static int omap_nand_probe(struct platform_device *pdev) { + const struct soc_device_attribute k3_soc_devices[] = { + { .family = "AM64X", .revision = "SR1.0" }, + { /* sentinel */ } + }; + struct omap_nand_info *info; struct mtd_info *mtd; struct nand_chip *nand_chip; int err; struct resource *res; struct device *dev = &pdev->dev; + void __iomem *vaddr; info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), GFP_KERNEL); @@ -2182,6 +2161,12 @@ info->pdev = pdev; + /* Some SoC's have 32-bit at least, read limitation */ + if (soc_device_match(k3_soc_devices)) { + dev_info(&pdev->dev, "force 32-bit\n"); + info->force_32bit = true; + } + err = omap_get_dt_info(dev, info); if (err) return err; @@ -2208,10 +2193,11 @@ } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nand_chip->legacy.IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nand_chip->legacy.IO_ADDR_R)) - return PTR_ERR(nand_chip->legacy.IO_ADDR_R); + vaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + info->fifo = vaddr; info->phys_base = res->start; if (!omap_gpmc_controller_initialized) { @@ -2222,9 +2208,6 @@ nand_chip->controller = &omap_gpmc_controller; - nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R; - nand_chip->legacy.cmd_ctrl = omap_hwcontrol; - info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", GPIOD_IN); if (IS_ERR(info->ready_gpiod)) { @@ -2232,27 +2215,16 @@ return PTR_ERR(info->ready_gpiod); } - /* - * If RDY/BSY line is connected to OMAP then use the omap ready - * function and the generic nand_wait function which reads the status - * register after monitoring the RDY/BSY line. Otherwise use a standard - * chip delay which is slightly more than tR (AC Timing) of the NAND - * device and read status register until you get a failure or success - */ - if (info->ready_gpiod) { - nand_chip->legacy.dev_ready = omap_dev_ready; - nand_chip->legacy.chip_delay = 0; - } else { - nand_chip->legacy.waitfunc = omap_wait; - nand_chip->legacy.chip_delay = 50; - } - if (info->flash_bbt) nand_chip->bbt_options |= NAND_BBT_USE_FLASH; /* scan NAND device connected to chip controller */ nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; + /* default operations */ + info->data_in = omap_nand_data_in; + info->data_out = omap_nand_data_out; + err = nand_scan(nand_chip, 1); if (err) goto return_error; @@ -2299,6 +2271,7 @@ static const struct of_device_id omap_nand_ids[] = { { .compatible = "ti,omap2-nand", }, + { .compatible = "ti,am64-nand", }, {}, }; MODULE_DEVICE_TABLE(of, omap_nand_ids); diff -Naur --no-dereference a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c --- a/drivers/mtd/spi-nor/core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/spi-nor/core.c 2022-01-06 12:45:53.822318139 -0500 @@ -40,6 +40,81 @@ #define SPI_NOR_MAX_ADDR_WIDTH 4 +#define SPI_NOR_SRST_SLEEP_MIN 200 +#define SPI_NOR_SRST_SLEEP_MAX 400 + +/** + * spi_nor_get_cmd_ext() - Get the command opcode extension based on the + * extension type. + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to the 'struct spi_mem_op' whose properties + * need to be initialized. + * + * Right now, only "repeat" and "invert" are supported. + * + * Return: The opcode extension. + */ +static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor, + const struct spi_mem_op *op) +{ + switch (nor->cmd_ext_type) { + case SPI_NOR_EXT_INVERT: + return ~op->cmd.opcode; + + case SPI_NOR_EXT_REPEAT: + return op->cmd.opcode; + + default: + dev_err(nor->dev, "Unknown command extension type\n"); + return 0; + } +} + +/** + * spi_nor_spimem_setup_op() - Set up common properties of a spi-mem op. + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to the 'struct spi_mem_op' whose properties + * need to be initialized. + * @proto: the protocol from which the properties need to be set. + */ +void spi_nor_spimem_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto) +{ + u8 ext; + + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto); + + if (op->addr.nbytes) + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto); + + if (op->dummy.nbytes) + op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto); + + if (op->data.nbytes) + op->data.buswidth = spi_nor_get_protocol_data_nbits(proto); + + if (spi_nor_protocol_is_dtr(proto)) { + /* + * SPIMEM supports mixed DTR modes, but right now we can only + * have all phases either DTR or STR. IOW, SPIMEM can have + * something like 4S-4D-4D, but SPI NOR can't. So, set all 4 + * phases to either DTR or STR. + */ + op->cmd.dtr = true; + op->addr.dtr = true; + op->dummy.dtr = true; + op->data.dtr = true; + + /* 2 bytes per clock cycle in DTR mode. */ + op->dummy.nbytes *= 2; + + ext = spi_nor_get_cmd_ext(nor, op); + op->cmd.opcode = (op->cmd.opcode << 8) | ext; + op->cmd.nbytes = 2; + } +} + /** * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data * transfer @@ -82,6 +157,59 @@ return spi_mem_exec_op(nor->spimem, op); } +static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode, + u8 *buf, size_t len) +{ + if (spi_nor_protocol_is_dtr(nor->reg_proto)) + return -EOPNOTSUPP; + + return nor->controller_ops->read_reg(nor, opcode, buf, len); +} + +static int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, + const u8 *buf, size_t len) +{ + if (spi_nor_protocol_is_dtr(nor->reg_proto)) + return -EOPNOTSUPP; + + return nor->controller_ops->write_reg(nor, opcode, buf, len); +} + +static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs) +{ + if (spi_nor_protocol_is_dtr(nor->write_proto)) + return -EOPNOTSUPP; + + return nor->controller_ops->erase(nor, offs); +} + +/** + * spi_nor_spimem_get_read_op() - return a template for the spi_mem_op used for + * reading data from the flash via spi-mem. + * @nor: pointer to 'struct spi_nor' + * + * Return: A template of the 'struct spi_mem_op' for used for reading data from + * the flash. The caller is expected to fill in the address, data length, and + * the data buffer. + */ +static struct spi_mem_op spi_nor_spimem_get_read_op(struct spi_nor *nor) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), + SPI_MEM_OP_DATA_IN(2, NULL, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->read_proto); + + /* convert the dummy cycles to the number of bytes */ + op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op.dummy.nbytes *= 2; + + return op; +} + /** * spi_nor_spimem_read_data() - read data from flash's memory region via * spi-mem @@ -95,23 +223,14 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, from, 1), - SPI_MEM_OP_DUMMY(nor->read_dummy, 1), - SPI_MEM_OP_DATA_IN(len, buf, 1)); + struct spi_mem_op op = spi_nor_spimem_get_read_op(nor); bool usebouncebuf; ssize_t nbytes; int error; - /* get transfer protocols. */ - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); - op.dummy.buswidth = op.addr.buswidth; - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); - - /* convert the dummy cycles to the number of bytes */ - op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + op.addr.val = from; + op.data.nbytes = len; + op.data.buf.in = buf; usebouncebuf = spi_nor_spimem_bounce(nor, &op); @@ -162,20 +281,18 @@ size_t len, const u8 *buf) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, to, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, to, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, buf, 1)); + SPI_MEM_OP_DATA_OUT(len, buf, 0)); ssize_t nbytes; int error; - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op.addr.nbytes = 0; + spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + if (spi_nor_spimem_bounce(nor, &op)) memcpy(nor->bouncebuf, buf, op.data.nbytes); @@ -222,15 +339,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREN, + NULL, 0); } if (ret) @@ -251,15 +370,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRDI, + NULL, 0); } if (ret) @@ -272,7 +393,7 @@ * spi_nor_read_sr() - Read the Status Register. * @nor: pointer to 'struct spi_nor'. * @sr: pointer to a DMA-able buffer where the value of the - * Status Register will be written. + * Status Register will be written. Should be at least 2 bytes. * * Return: 0 on success, -errno otherwise. */ @@ -282,15 +403,27 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr, 1)); + SPI_MEM_OP_DATA_IN(1, sr, 0)); + + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; + } + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR, - sr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr, + 1); } if (ret) @@ -303,7 +436,8 @@ * spi_nor_read_fsr() - Read the Flag Status Register. * @nor: pointer to 'struct spi_nor' * @fsr: pointer to a DMA-able buffer where the value of the - * Flag Status Register will be written. + * Flag Status Register will be written. Should be at least 2 + * bytes. * * Return: 0 on success, -errno otherwise. */ @@ -313,15 +447,27 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, fsr, 1)); + SPI_MEM_OP_DATA_IN(1, fsr, 0)); + + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; + } + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDFSR, - fsr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, fsr, + 1); } if (ret) @@ -345,14 +491,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, cr, 1)); + SPI_MEM_OP_DATA_IN(1, cr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDCR, cr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDCR, cr, + 1); } if (ret) @@ -378,17 +527,19 @@ SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, - 1), + 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, - enable ? SPINOR_OP_EN4B : - SPINOR_OP_EX4B, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, + enable ? SPINOR_OP_EN4B : + SPINOR_OP_EX4B, + NULL, 0); } if (ret) @@ -414,15 +565,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_BRWR, - nor->bouncebuf, 1); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_BRWR, + nor->bouncebuf, 1); } if (ret) @@ -446,15 +599,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREAR, - nor->bouncebuf, 1); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREAR, + nor->bouncebuf, 1); } if (ret) @@ -477,15 +632,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr, 1)); + SPI_MEM_OP_DATA_IN(1, sr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_XRDSR, - sr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_XRDSR, sr, + 1); } if (ret) @@ -522,15 +679,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLSR, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR, + NULL, 0); } if (ret) @@ -586,15 +745,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLFSR, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR, + NULL, 0); } if (ret) @@ -730,15 +891,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, sr, 1)); + SPI_MEM_OP_DATA_OUT(len, sr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR, - sr, len); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR, sr, + len); } if (ret) { @@ -932,15 +1095,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, sr2, 1)); + SPI_MEM_OP_DATA_OUT(1, sr2, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR2, - sr2, 1); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR2, + sr2, 1); } if (ret) { @@ -966,15 +1131,17 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr2, 1)); + SPI_MEM_OP_DATA_IN(1, sr2, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR2, - sr2, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR2, sr2, + 1); } if (ret) @@ -997,15 +1164,18 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, + SPINOR_OP_CHIP_ERASE, + NULL, 0); } if (ret) @@ -1139,14 +1309,16 @@ if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, addr, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, addr, 0), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + return spi_mem_exec_op(nor->spimem, &op); } else if (nor->controller_ops->erase) { - return nor->controller_ops->erase(nor, addr); + return spi_nor_controller_ops_erase(nor, addr); } /* @@ -1158,8 +1330,8 @@ addr >>= 8; } - return nor->controller_ops->write_reg(nor, nor->erase_opcode, - nor->bouncebuf, nor->addr_width); + return spi_nor_controller_ops_write_reg(nor, nor->erase_opcode, + nor->bouncebuf, nor->addr_width); } /** @@ -2082,6 +2254,82 @@ return ERR_PTR(-ENODEV); } +/* + * On Octal DTR capable flashes like Micron Xcella reads cannot start or + * end at an odd address in Octal DTR mode. Extra bytes need to be read + * at the start or end to make sure both the start address and length + * remain even. + */ +static int spi_nor_octal_dtr_read(struct spi_nor *nor, loff_t from, size_t len, + u_char *buf) +{ + u_char *tmp_buf; + size_t tmp_len; + loff_t start, end; + int ret, bytes_read; + + if (IS_ALIGNED(from, 2) && IS_ALIGNED(len, 2)) + return spi_nor_read_data(nor, from, len, buf); + else if (IS_ALIGNED(from, 2) && len > PAGE_SIZE) + return spi_nor_read_data(nor, from, round_down(len, PAGE_SIZE), + buf); + + tmp_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + start = round_down(from, 2); + end = round_up(from + len, 2); + + /* + * Avoid allocating too much memory. The requested read length might be + * quite large. Allocating a buffer just as large (slightly bigger, in + * fact) would put unnecessary memory pressure on the system. + * + * For example if the read is from 3 to 1M, then this will read from 2 + * to 4098. The reads from 4098 to 1M will then not need a temporary + * buffer so they can proceed as normal. + */ + tmp_len = min_t(size_t, end - start, PAGE_SIZE); + + ret = spi_nor_read_data(nor, start, tmp_len, tmp_buf); + if (ret == 0) { + ret = -EIO; + goto out; + } + if (ret < 0) + goto out; + + /* + * More bytes are read than actually requested, but that number can't be + * reported to the calling function or it will confuse its calculations. + * Calculate how many of the _requested_ bytes were read. + */ + bytes_read = ret; + + if (from != start) + ret -= from - start; + + /* + * Only account for extra bytes at the end if they were actually read. + * For example, if the total length was truncated because of temporary + * buffer size limit then the adjustment for the extra bytes at the end + * is not needed. + */ + if (start + bytes_read == end) + ret -= end - (from + len); + + if (ret < 0) { + ret = -EIO; + goto out; + } + + memcpy(buf, tmp_buf + (from - start), ret); +out: + kfree(tmp_buf); + return ret; +} + static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -2099,7 +2347,10 @@ addr = spi_nor_convert_addr(nor, addr); - ret = spi_nor_read_data(nor, addr, len, buf); + if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) + ret = spi_nor_octal_dtr_read(nor, addr, len, buf); + else + ret = spi_nor_read_data(nor, addr, len, buf); if (ret == 0) { /* We shouldn't see 0-length reads */ ret = -EIO; @@ -2122,6 +2373,71 @@ } /* + * On Octal DTR capable flashes like Micron Xcella the writes cannot start or + * end at an odd address in Octal DTR mode. Extra 0xff bytes need to be appended + * or prepended to make sure the start address and end address are even. 0xff is + * used because on NOR flashes a program operation can only flip bits from 1 to + * 0, not the other way round. 0 to 1 flip needs to happen via erases. + */ +static int spi_nor_octal_dtr_write(struct spi_nor *nor, loff_t to, size_t len, + const u8 *buf) +{ + u8 *tmp_buf; + size_t bytes_written; + loff_t start, end; + int ret; + + if (IS_ALIGNED(to, 2) && IS_ALIGNED(len, 2)) + return spi_nor_write_data(nor, to, len, buf); + + tmp_buf = kmalloc(nor->page_size, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + memset(tmp_buf, 0xff, nor->page_size); + + start = round_down(to, 2); + end = round_up(to + len, 2); + + memcpy(tmp_buf + (to - start), buf, len); + + ret = spi_nor_write_data(nor, start, end - start, tmp_buf); + if (ret == 0) { + ret = -EIO; + goto out; + } + if (ret < 0) + goto out; + + /* + * More bytes are written than actually requested, but that number can't + * be reported to the calling function or it will confuse its + * calculations. Calculate how many of the _requested_ bytes were + * written. + */ + bytes_written = ret; + + if (to != start) + ret -= to - start; + + /* + * Only account for extra bytes at the end if they were actually + * written. For example, if for some reason the controller could only + * complete a partial write then the adjustment for the extra bytes at + * the end is not needed. + */ + if (start + bytes_written == end) + ret -= end - (to + len); + + if (ret < 0) + ret = -EIO; + +out: + kfree(tmp_buf); + return ret; +} + +/* * Write an address range to the nor chip. Data must be written in * FLASH_PAGESIZE chunks. The address range may be any size provided * it is within the physical boundaries. @@ -2168,7 +2484,12 @@ if (ret) goto write_err; - ret = spi_nor_write_data(nor, addr, page_remain, buf + i); + if (nor->write_proto == SNOR_PROTO_8_8_8_DTR) + ret = spi_nor_octal_dtr_write(nor, addr, page_remain, + buf + i); + else + ret = spi_nor_write_data(nor, addr, page_remain, + buf + i); if (ret < 0) goto write_err; written = ret; @@ -2206,7 +2527,7 @@ return 0; } -static void +void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, u8 num_wait_states, @@ -2255,6 +2576,7 @@ { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 }, { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 }, { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR }, + { SNOR_HWCAPS_READ_8_8_8_DTR, SNOR_CMD_READ_8_8_8_DTR }, }; return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd, @@ -2271,6 +2593,7 @@ { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 }, { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 }, { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 }, + { SNOR_HWCAPS_PP_8_8_8_DTR, SNOR_CMD_PP_8_8_8_DTR }, }; return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd, @@ -2283,7 +2606,7 @@ *@nor: pointer to a 'struct spi_nor' *@op: pointer to op template to be checked * - * Returns 0 if operation is supported, -ENOTSUPP otherwise. + * Returns 0 if operation is supported, -EOPNOTSUPP otherwise. */ static int spi_nor_spimem_check_op(struct spi_nor *nor, struct spi_mem_op *op) @@ -2297,12 +2620,12 @@ op->addr.nbytes = 4; if (!spi_mem_supports_op(nor->spimem, op)) { if (nor->mtd.size > SZ_16M) - return -ENOTSUPP; + return -EOPNOTSUPP; /* If flash size <= 16MB, 3 address bytes are sufficient */ op->addr.nbytes = 3; if (!spi_mem_supports_op(nor->spimem, op)) - return -ENOTSUPP; + return -EOPNOTSUPP; } return 0; @@ -2314,22 +2637,22 @@ *@nor: pointer to a 'struct spi_nor' *@read: pointer to op template to be checked * - * Returns 0 if operation is supported, -ENOTSUPP otherwise. + * Returns 0 if operation is supported, -EOPNOTSUPP otherwise. */ static int spi_nor_spimem_check_readop(struct spi_nor *nor, const struct spi_nor_read_command *read) { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1), - SPI_MEM_OP_ADDR(3, 0, 1), - SPI_MEM_OP_DUMMY(0, 1), - SPI_MEM_OP_DATA_IN(0, NULL, 1)); - - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto); - op.dummy.buswidth = op.addr.buswidth; - op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * - op.dummy.buswidth / 8; + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0), + SPI_MEM_OP_ADDR(3, 0, 0), + SPI_MEM_OP_DUMMY(1, 0), + SPI_MEM_OP_DATA_IN(2, NULL, 0)); + + spi_nor_spimem_setup_op(nor, &op, read->proto); + + /* convert the dummy cycles to the number of bytes */ + op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op.dummy.nbytes *= 2; return spi_nor_spimem_check_op(nor, &op); } @@ -2340,19 +2663,17 @@ *@nor: pointer to a 'struct spi_nor' *@pp: pointer to op template to be checked * - * Returns 0 if operation is supported, -ENOTSUPP otherwise. + * Returns 0 if operation is supported, -EOPNOTSUPP otherwise. */ static int spi_nor_spimem_check_pp(struct spi_nor *nor, const struct spi_nor_pp_command *pp) { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1), - SPI_MEM_OP_ADDR(3, 0, 1), + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0), + SPI_MEM_OP_ADDR(3, 0, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 1)); + SPI_MEM_OP_DATA_OUT(2, NULL, 0)); - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto); + spi_nor_spimem_setup_op(nor, &op, pp->proto); return spi_nor_spimem_check_op(nor, &op); } @@ -2370,12 +2691,16 @@ struct spi_nor_flash_parameter *params = nor->params; unsigned int cap; - /* DTR modes are not supported yet, mask them all. */ - *hwcaps &= ~SNOR_HWCAPS_DTR; - /* X-X-X modes are not supported yet, mask them all. */ *hwcaps &= ~SNOR_HWCAPS_X_X_X; + /* + * If the reset line is broken, we do not want to enter a stateful + * mode. + */ + if (nor->flags & SNOR_F_BROKEN_RESET) + *hwcaps &= ~(SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR); + for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { int rdidx, ppidx; @@ -2630,7 +2955,7 @@ * controller directly implements the spi_nor interface. * Yet another reason to switch to spi-mem. */ - ignored_mask = SNOR_HWCAPS_X_X_X; + ignored_mask = SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR; if (shared_mask & ignored_mask) { dev_dbg(nor->dev, "SPI n-n-n protocols are not supported.\n"); @@ -2731,6 +3056,7 @@ nor->flags |= SNOR_F_HAS_16BIT_SR; /* Set SPI NOR sizes. */ + params->writesize = 1; params->size = (u64)info->sector_size * info->n_sectors; params->page_size = info->page_size; @@ -2775,11 +3101,28 @@ SNOR_PROTO_1_1_8); } + if (info->flags & SPI_NOR_OCTAL_DTR_READ) { + params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_READ_FAST, + SNOR_PROTO_8_8_8_DTR); + } + /* Page Program settings. */ params->hwcaps.mask |= SNOR_HWCAPS_PP; spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP, SNOR_PROTO_1_1_1); + if (info->flags & SPI_NOR_OCTAL_DTR_PP) { + params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; + /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR); + } + /* * Sector Erase settings. Sort Erase Types in ascending order, with the * smallest erase size starting at BIT(0). @@ -2887,7 +3230,8 @@ spi_nor_manufacturer_init_params(nor); - if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && + if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ)) && !(nor->info->flags & SPI_NOR_SKIP_SFDP)) spi_nor_sfdp_init_params(nor); @@ -2898,6 +3242,38 @@ return 0; } +/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable or disable Octal DTR + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + int ret; + + if (!nor->params->octal_dtr_enable) + return 0; + + if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR && + nor->write_proto == SNOR_PROTO_8_8_8_DTR)) + return 0; + + if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE)) + return 0; + + ret = nor->params->octal_dtr_enable(nor, enable); + if (ret) + return ret; + + if (enable) + nor->reg_proto = SNOR_PROTO_8_8_8_DTR; + else + nor->reg_proto = SNOR_PROTO_1_1_1; + + return 0; +} + /** * spi_nor_quad_enable() - enable Quad I/O if needed. * @nor: pointer to a 'struct spi_nor' @@ -2944,6 +3320,12 @@ { int err; + err = spi_nor_octal_dtr_enable(nor, true); + if (err) { + dev_dbg(nor->dev, "octal mode not supported\n"); + return err; + } + err = spi_nor_quad_enable(nor); if (err) { dev_dbg(nor->dev, "quad mode not supported\n"); @@ -2952,7 +3334,9 @@ spi_nor_try_unlock_all(nor); - if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) { + if (nor->addr_width == 4 && + nor->read_proto != SNOR_PROTO_8_8_8_DTR && + !(nor->flags & SNOR_F_4B_OPCODES)) { /* * If the RESET# pin isn't hooked up properly, or the system * otherwise doesn't perform a reset command in the boot @@ -2968,6 +3352,59 @@ return 0; } +static void spi_nor_soft_reset(struct spi_nor *nor) +{ + struct spi_mem_op op; + int ret; + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DATA); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) { + dev_warn(nor->dev, "Software reset failed: %d\n", ret); + return; + } + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DATA); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) { + dev_warn(nor->dev, "Software reset failed: %d\n", ret); + return; + } + + /* + * Software Reset is not instant, and the delay varies from flash to + * flash. Looking at a few flashes, most range somewhere below 100 + * microseconds. So, sleep for a range of 200-400 us. + */ + usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX); +} + +/* mtd suspend handler */ +static int spi_nor_suspend(struct mtd_info *mtd) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + /* Disable octal DTR mode if we enabled it. */ + ret = spi_nor_octal_dtr_enable(nor, false); + if (ret) + dev_err(nor->dev, "suspend() failed\n"); + + return ret; +} + /* mtd resume handler */ static void spi_nor_resume(struct mtd_info *mtd) { @@ -3018,6 +3455,9 @@ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) nor->params->set_4byte_addr_mode(nor, false); + + if (nor->flags & SNOR_F_SOFT_RESET) + spi_nor_soft_reset(nor); } EXPORT_SYMBOL_GPL(spi_nor_restore); @@ -3042,6 +3482,20 @@ { if (nor->addr_width) { /* already configured from SFDP */ + } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { + /* + * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So + * in this protocol an odd address width cannot be used because + * then the address phase would only span a cycle and a half. + * Half a cycle would be left over. We would then have to start + * the dummy phase in the middle of a cycle and so too the data + * phase, and we will end the transaction with half a cycle left + * over. + * + * Force all 8D-8D-8D flashes to use an address width of 4 to + * avoid this situation. + */ + nor->addr_width = 4; } else if (nor->info->addr_width) { nor->addr_width = nor->info->addr_width; } else { @@ -3182,11 +3636,12 @@ mtd->name = dev_name(dev); mtd->priv = nor; mtd->type = MTD_NORFLASH; - mtd->writesize = 1; + mtd->writesize = nor->params->writesize; mtd->flags = MTD_CAP_NORFLASH; mtd->size = nor->params->size; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; + mtd->_suspend = spi_nor_suspend; mtd->_resume = spi_nor_resume; mtd->_get_device = spi_nor_get_device; mtd->_put_device = spi_nor_put_device; @@ -3239,6 +3694,9 @@ if (info->flags & SPI_NOR_4B_OPCODES) nor->flags |= SNOR_F_4B_OPCODES; + if (info->flags & SPI_NOR_IO_MODE_EN_VOLATILE) + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + ret = spi_nor_set_addr_width(nor); if (ret) return ret; @@ -3274,23 +3732,10 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), - SPI_MEM_OP_DUMMY(nor->read_dummy, 1), - SPI_MEM_OP_DATA_IN(0, NULL, 1)), + .op_tmpl = spi_nor_spimem_get_read_op(nor), .offset = 0, .length = nor->mtd.size, }; - struct spi_mem_op *op = &info.op_tmpl; - - /* get transfer protocols. */ - op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); - op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); - op->dummy.buswidth = op->addr.buswidth; - op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); - - /* convert the dummy cycles to the number of bytes */ - op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, &info); @@ -3300,24 +3745,27 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 1)), + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), .offset = 0, .length = nor->mtd.size, }; struct spi_mem_op *op = &info.op_tmpl; - /* get transfer protocols. */ - op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); - op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); - op->dummy.buswidth = op->addr.buswidth; - op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op->addr.nbytes = 0; + spi_nor_spimem_setup_op(nor, op, nor->write_proto); + + /* + * Since spi_nor_spimem_setup_op() only sets buswidth when the number + * of data bytes is non-zero, the data buswidth won't be set here. So, + * do it explicitly. + */ + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); + nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, &info); return PTR_ERR_OR_ZERO(nor->dirmap.wdesc); @@ -3333,6 +3781,7 @@ * checking what's really supported using spi_mem_supports_op(). */ const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL }; + struct mtd_part *part; char *flash_name; int ret; @@ -3392,8 +3841,25 @@ if (ret) return ret; - return mtd_device_register(&nor->mtd, data ? data->parts : NULL, - data ? data->nr_parts : 0); + ret = mtd_device_register(&nor->mtd, data ? data->parts : NULL, + data ? data->nr_parts : 0); + if (ret) + return ret; + + list_for_each_entry(part, &nor->mtd.partitions, node) { + struct spi_mem_op op; + struct mtd_info *part_info = container_of(part, + struct mtd_info, part); + + if (part_info->name && + !strcmp(part_info->name, "ospi.phypattern")) { + op = spi_nor_spimem_get_read_op(nor); + op.addr.val = part->offset; + spi_mem_do_calibration(nor->spimem, &op); + } + } + + return 0; } static int spi_nor_remove(struct spi_mem *spimem) diff -Naur --no-dereference a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h --- a/drivers/mtd/spi-nor/core.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/spi-nor/core.h 2022-01-06 12:45:53.822318139 -0500 @@ -26,6 +26,8 @@ SNOR_F_HAS_SR_TB_BIT6 = BIT(11), SNOR_F_HAS_4BIT_BP = BIT(12), SNOR_F_HAS_SR_BP3_BIT6 = BIT(13), + SNOR_F_IO_MODE_EN_VOLATILE = BIT(14), + SNOR_F_SOFT_RESET = BIT(15), }; struct spi_nor_read_command { @@ -62,6 +64,7 @@ SNOR_CMD_READ_1_8_8, SNOR_CMD_READ_8_8_8, SNOR_CMD_READ_1_8_8_DTR, + SNOR_CMD_READ_8_8_8_DTR, SNOR_CMD_READ_MAX }; @@ -78,6 +81,7 @@ SNOR_CMD_PP_1_1_8, SNOR_CMD_PP_1_8_8, SNOR_CMD_PP_8_8_8, + SNOR_CMD_PP_8_8_8_DTR, SNOR_CMD_PP_MAX }; @@ -189,7 +193,12 @@ * Serial Flash Discoverable Parameters (SFDP) tables. * * @size: the flash memory density in bytes. + * @writesize Minimal writable flash unit size. Defaults to 1. Set to + * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. + * @rdsr_dummy: dummy cycles needed for Read Status Register command. + * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register + * command. * @hwcaps: describes the read and page program hardware * capabilities. * @reads: read capabilities ordered by priority: the higher index @@ -198,6 +207,7 @@ * higher index in the array, the higher priority. * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. + * @octal_dtr_enable: enables SPI NOR octal DTR mode. * @quad_enable: enables SPI NOR quad mode. * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @convert_addr: converts an absolute address into something the flash @@ -211,7 +221,10 @@ */ struct spi_nor_flash_parameter { u64 size; + u32 writesize; u32 page_size; + u8 rdsr_dummy; + u8 rdsr_addr_nbytes; struct spi_nor_hwcaps hwcaps; struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; @@ -219,6 +232,7 @@ struct spi_nor_erase_map erase_map; + int (*octal_dtr_enable)(struct spi_nor *nor, bool enable); int (*quad_enable)(struct spi_nor *nor); int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); u32 (*convert_addr)(struct spi_nor *nor, u32 addr); @@ -311,6 +325,13 @@ * BP3 is bit 6 of status register. * Must be used with SPI_NOR_4BIT_BP. */ +#define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR Read. */ +#define SPI_NOR_OCTAL_DTR_PP BIT(20) /* Flash supports Octal DTR Page Program */ +#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(21) /* + * Flash enables the best + * available I/O mode via a + * volatile bit. + */ /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; @@ -399,6 +420,9 @@ extern const struct spi_nor_manufacturer spi_nor_xilinx; extern const struct spi_nor_manufacturer spi_nor_xmc; +void spi_nor_spimem_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto); int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); @@ -419,6 +443,11 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps); u8 spi_nor_convert_3to4_read(u8 opcode); +void spi_nor_set_read_settings(struct spi_nor_read_command *read, + u8 num_mode_clocks, + u8 num_wait_states, + u8 opcode, + enum spi_nor_protocol proto); void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, enum spi_nor_protocol proto); diff -Naur --no-dereference a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c --- a/drivers/mtd/spi-nor/micron-st.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/spi-nor/micron-st.c 2022-01-06 12:45:53.822318139 -0500 @@ -8,10 +8,123 @@ #include "core.h" +#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */ +#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */ +#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */ +#define SPINOR_REG_MT_CFR0V 0x00 /* For setting octal DTR mode */ +#define SPINOR_REG_MT_CFR1V 0x01 /* For setting dummy cycles */ +#define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */ +#define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */ + +static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + if (enable) { + /* Use 20 dummy cycles for memory array reads. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + *buf = 20; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + } + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (enable) + *buf = SPINOR_MT_OCT_DTR; + else + *buf = SPINOR_MT_EXSPI; + + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(enable ? 3 : 4, + SPINOR_REG_MT_CFR0V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + if (!enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + /* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1), + SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), + buf, 1)); + + if (enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; + + return 0; +} + +static void mt35xu512aba_default_init(struct spi_nor *nor) +{ + nor->params->octal_dtr_enable = spi_nor_micron_octal_dtr_enable; +} + +static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) +{ + /* Set the Fast Read settings. */ + nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_MT_DTR_RD, + SNOR_PROTO_8_8_8_DTR); + + nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + nor->params->rdsr_dummy = 8; + nor->params->rdsr_addr_nbytes = 0; + + /* + * The BFPT quad enable field is set to a reserved value so the quad + * enable function is ignored by spi_nor_parse_bfpt(). Make sure we + * disable it. + */ + nor->params->quad_enable = NULL; +} + +static struct spi_nor_fixups mt35xu512aba_fixups = { + .default_init = mt35xu512aba_default_init, + .post_sfdp = mt35xu512aba_post_sfdp_fixup, +}; + static const struct flash_info micron_parts[] = { { "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512, SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | - SPI_NOR_4B_OPCODES) }, + SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | + SPI_NOR_IO_MODE_EN_VOLATILE) + .fixups = &mt35xu512aba_fixups}, { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, diff -Naur --no-dereference a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c --- a/drivers/mtd/spi-nor/sfdp.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/spi-nor/sfdp.c 2022-01-06 12:45:53.822318139 -0500 @@ -4,6 +4,7 @@ * Copyright (C) 2014, Freescale Semiconductor, Inc. */ +#include #include #include #include @@ -19,6 +20,11 @@ #define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ #define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ #define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ +#define SFDP_PROFILE1_ID 0xff05 /* xSPI Profile 1.0 table. */ +#define SFDP_SCCR_MAP_ID 0xff87 /* + * Status, Control and Configuration + * Register Map. + */ #define SFDP_SIGNATURE 0x50444653U @@ -602,10 +608,32 @@ break; } + /* Soft Reset support. */ + if (bfpt.dwords[BFPT_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST) + nor->flags |= SNOR_F_SOFT_RESET; + /* Stop here if not JESD216 rev C or later. */ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); + /* 8D-8D-8D command extension. */ + switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { + case BFPT_DWORD18_CMD_EXT_REP: + nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + break; + + case BFPT_DWORD18_CMD_EXT_INV: + nor->cmd_ext_type = SPI_NOR_EXT_INVERT; + break; + + case BFPT_DWORD18_CMD_EXT_RES: + dev_dbg(nor->dev, "Reserved command extension used\n"); + break; + + case BFPT_DWORD18_CMD_EXT_16B: + dev_dbg(nor->dev, "16-bit opcodes not supported\n"); + return -EOPNOTSUPP; + } return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); } @@ -1046,9 +1074,16 @@ } /* 4BAIT is the only SFDP table that indicates page program support. */ - if (pp_hwcaps & SNOR_HWCAPS_PP) + if (pp_hwcaps & SNOR_HWCAPS_PP) { spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP], SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); + } if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4], SPINOR_OP_PP_1_1_4_4B, @@ -1082,6 +1117,131 @@ return ret; } +#define PROFILE1_DWORD1_RDSR_ADDR_BYTES BIT(29) +#define PROFILE1_DWORD1_RDSR_DUMMY BIT(28) +#define PROFILE1_DWORD1_RD_FAST_CMD GENMASK(15, 8) +#define PROFILE1_DWORD4_DUMMY_200MHZ GENMASK(11, 7) +#define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27) +#define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17) +#define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7) + +/** + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table + * @nor: pointer to a 'struct spi_nor' + * @profile1_header: pointer to the 'struct sfdp_parameter_header' describing + * the Profile 1.0 Table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_profile1(struct spi_nor *nor, + const struct sfdp_parameter_header *profile1_header, + struct spi_nor_flash_parameter *params) +{ + u32 *dwords, addr; + size_t len; + int ret; + u8 dummy, opcode; + + len = profile1_header->length * sizeof(*dwords); + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(profile1_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + goto out; + + le32_to_cpu_array(dwords, profile1_header->length); + + /* Get 8D-8D-8D fast read opcode and dummy cycles. */ + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, dwords[0]); + + /* Set the Read Status Register dummy cycles and dummy address bytes. */ + if (dwords[0] & PROFILE1_DWORD1_RDSR_DUMMY) + params->rdsr_dummy = 8; + else + params->rdsr_dummy = 4; + + if (dwords[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES) + params->rdsr_addr_nbytes = 4; + else + params->rdsr_addr_nbytes = 0; + + /* + * We don't know what speed the controller is running at. Find the + * dummy cycles for the fastest frequency the flash can run at to be + * sure we are never short of dummy cycles. A value of 0 means the + * frequency is not supported. + * + * Default to PROFILE1_DUMMY_DEFAULT if we don't find anything, and let + * flashes set the correct value if needed in their fixup hooks. + */ + dummy = FIELD_GET(PROFILE1_DWORD4_DUMMY_200MHZ, dwords[3]); + if (!dummy) + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_166MHZ, dwords[4]); + if (!dummy) + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_133MHZ, dwords[4]); + if (!dummy) + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_100MHZ, dwords[4]); + if (!dummy) + dev_dbg(nor->dev, + "Can't find dummy cycles from Profile 1.0 table\n"); + + /* Round up to an even value to avoid tripping controllers up. */ + dummy = round_up(dummy, 2); + + /* Update the fast read settings. */ + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, dummy, opcode, + SNOR_PROTO_8_8_8_DTR); + +out: + kfree(dwords); + return ret; +} + +#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31) + +/** + * spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register + * Map. + * @nor: pointer to a 'struct spi_nor' + * @sccr_header: pointer to the 'struct sfdp_parameter_header' describing + * the SCCR Map table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_sccr(struct spi_nor *nor, + const struct sfdp_parameter_header *sccr_header, + struct spi_nor_flash_parameter *params) +{ + u32 *dwords, addr; + size_t len; + int ret; + + len = sccr_header->length * sizeof(*dwords); + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(sccr_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + goto out; + + le32_to_cpu_array(dwords, sccr_header->length); + + if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[22])) + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + +out: + kfree(dwords); + return ret; +} + /** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' @@ -1183,6 +1343,14 @@ err = spi_nor_parse_4bait(nor, param_header, params); break; + case SFDP_PROFILE1_ID: + err = spi_nor_parse_profile1(nor, param_header, params); + break; + + case SFDP_SCCR_MAP_ID: + err = spi_nor_parse_sccr(nor, param_header, params); + break; + default: break; } diff -Naur --no-dereference a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h --- a/drivers/mtd/spi-nor/sfdp.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/spi-nor/sfdp.h 2022-01-06 12:45:53.822318139 -0500 @@ -90,6 +90,14 @@ #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD16_SWRST_EN_RST BIT(12) + +#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29) +#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */ +#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */ +#define BFPT_DWORD18_CMD_EXT_RES (0x2UL << 29) /* Reserved */ +#define BFPT_DWORD18_CMD_EXT_16B (0x3UL << 29) /* 16-bit opcode */ + struct sfdp_parameter_header { u8 id_lsb; u8 minor; diff -Naur --no-dereference a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c --- a/drivers/mtd/spi-nor/spansion.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/spi-nor/spansion.c 2022-01-06 12:45:53.822318139 -0500 @@ -8,6 +8,173 @@ #include "core.h" +#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ +#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ +#define SPINOR_REG_CYPRESS_CFR2V 0x00800003 +#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb +#define SPINOR_REG_CYPRESS_CFR3V 0x00800004 +#define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */ +#define SPINOR_REG_CYPRESS_CFR5V 0x00800006 +#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3 +#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0 +#define SPINOR_OP_CYPRESS_RD_FAST 0xee + +/** + * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable or disable Octal DTR + * + * This also sets the memory access latency cycles to 24 to allow the flash to + * run at up to 200MHz. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + if (enable) { + /* Use 24 dummy cycles for memory array reads. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V, + 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + nor->read_dummy = 24; + } + + /* Set/unset the octal and DTR enable bits. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (enable) + *buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; + else + *buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; + + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(enable ? 3 : 4, + SPINOR_REG_CYPRESS_CFR5V, + 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + if (!enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + /* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), + SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1), + SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1), + SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), + buf, 1)); + + if (enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; + + return 0; +} + +static void s28hs512t_default_init(struct spi_nor *nor) +{ + nor->params->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable; + nor->params->writesize = 16; +} + +static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor) +{ + /* + * On older versions of the flash the xSPI Profile 1.0 table has the + * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE. + */ + if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) + nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = + SPINOR_OP_CYPRESS_RD_FAST; + + /* This flash is also missing the 4-byte Page Program opcode bit. */ + spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP], + SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); + + /* + * The xSPI Profile 1.0 table advertises the number of additional + * address bytes needed for Read Status Register command as 0 but the + * actual value for that is 4. + */ + nor->params->rdsr_addr_nbytes = 4; +} + +static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* + * The BFPT table advertises a 512B page size but the page size is + * actually configurable (with the default being 256B). Read from + * CFR3V[4] and set the correct size. + */ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); + int ret; + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) + params->page_size = 512; + else + params->page_size = 256; + + return 0; +} + +static struct spi_nor_fixups s28hs512t_fixups = { + .default_init = s28hs512t_default_init, + .post_sfdp = s28hs512t_post_sfdp_fixup, + .post_bfpt = s28hs512t_post_bfpt_fixup, +}; + static int s25fs_s_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -104,6 +271,11 @@ SPI_NOR_4B_OPCODES) }, { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1, SPI_NOR_NO_ERASE) }, + { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP) + .fixups = &s28hs512t_fixups, + }, }; static void spansion_post_sfdp_fixups(struct spi_nor *nor) diff -Naur --no-dereference a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c --- a/drivers/mtd/ubi/build.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/ubi/build.c 2022-01-06 12:45:53.822318139 -0500 @@ -628,10 +628,8 @@ ubi->bad_peb_limit = get_bad_peb_limit(ubi, max_beb_per1024); } - if (ubi->mtd->type == MTD_NORFLASH) { - ubi_assert(ubi->mtd->writesize == 1); + if (ubi->mtd->type == MTD_NORFLASH) ubi->nor_flash = 1; - } ubi->min_io_size = ubi->mtd->writesize; ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; diff -Naur --no-dereference a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c --- a/drivers/mtd/ubi/io.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/mtd/ubi/io.c 2022-01-06 12:45:53.822318139 -0500 @@ -535,7 +535,14 @@ return -EROFS; } - if (ubi->nor_flash) { + /* + * If the flash is ECC-ed then we have to erase the ECC block before we + * can write to it. But the write is in preparation to an erase in the + * first place. This means we cannot zero out EC and VID before the + * erase and we just have to hope the flash starts erasing from the + * start of the page. + */ + if (ubi->nor_flash && ubi->mtd->writesize == 1) { err = nor_erase_prepare(ubi, pnum); if (err) return err; diff -Naur --no-dereference a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c --- a/drivers/net/can/m_can/m_can.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/can/m_can/m_can.c 2022-01-06 12:45:53.822318139 -0500 @@ -21,6 +21,7 @@ #include #include #include +#include #include "m_can.h" @@ -1419,6 +1420,7 @@ static int m_can_close(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); + int err; netif_stop_queue(dev); @@ -1438,6 +1440,10 @@ close_candev(dev); can_led_event(dev, CAN_LED_EVENT_STOP); + err = phy_power_off(cdev->transceiver); + if (err) + return err; + return 0; } @@ -1623,6 +1629,10 @@ struct m_can_classdev *cdev = netdev_priv(dev); int err; + err = phy_power_on(cdev->transceiver); + if (err) + return err; + err = m_can_clk_start(cdev); if (err) return err; @@ -1678,6 +1688,7 @@ close_candev(dev); exit_disable_clks: m_can_clk_stop(cdev); + phy_power_off(cdev->transceiver); return err; } diff -Naur --no-dereference a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h --- a/drivers/net/can/m_can/m_can.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/can/m_can/m_can.h 2022-01-06 12:45:53.822318139 -0500 @@ -27,6 +27,7 @@ #include #include #include +#include /* m_can lec values */ enum m_can_lec_type { @@ -80,6 +81,7 @@ struct workqueue_struct *tx_wq; struct work_struct tx_work; struct sk_buff *tx_skb; + struct phy *transceiver; struct can_bittiming_const *bit_timing; struct can_bittiming_const *data_timing; diff -Naur --no-dereference a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c --- a/drivers/net/can/m_can/m_can_platform.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/can/m_can/m_can_platform.c 2022-01-06 12:45:53.822318139 -0500 @@ -6,6 +6,7 @@ // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ #include +#include #include "m_can.h" @@ -60,6 +61,7 @@ struct resource *res; void __iomem *addr; void __iomem *mram_addr; + struct phy *transceiver; int irq, ret = 0; mcan_class = m_can_class_allocate_dev(&pdev->dev); @@ -99,6 +101,16 @@ goto probe_fail; } + transceiver = devm_phy_optional_get(&pdev->dev, NULL); + if (IS_ERR(transceiver)) { + ret = PTR_ERR(transceiver); + dev_err_probe(&pdev->dev, ret, "failed to get phy\n"); + return ret; + } + + if (transceiver) + mcan_class->can.bitrate_max = transceiver->attrs.max_link_rate; + priv->base = addr; priv->mram_base = mram_addr; @@ -106,6 +118,7 @@ mcan_class->pm_clock_support = 1; mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk); mcan_class->dev = &pdev->dev; + mcan_class->transceiver = transceiver; mcan_class->ops = &m_can_plat_ops; diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c --- a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c 2022-01-06 12:45:53.822318139 -0500 @@ -372,7 +372,15 @@ /* Ethtool priv_flags */ static const char am65_cpsw_ethtool_priv_flags[][ETH_GSTRING_LEN] = { #define AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN BIT(0) +/* common flags */ "p0-rx-ptype-rrobin", +/* port specific flags */ +#define AM65_CPSW_PRIV_IET_FRAME_PREEMPTION BIT(1) + "iet-frame-preemption", +#define AM65_CPSW_PRIV_IET_MAC_VERIFY BIT(2) + "iet-mac-verify", +#define AM65_CPSW_PRIV_CUT_THRU BIT(3) + "cut-thru", }; static int am65_cpsw_ethtool_op_begin(struct net_device *ndev) @@ -721,10 +729,19 @@ static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev) { struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_iet *iet = &port->qos.iet; u32 priv_flags = 0; if (common->pf_p0_rx_ptype_rrobin) priv_flags |= AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN; + /* Port specific flags */ + if (iet->fpe_configured) + priv_flags |= AM65_CPSW_PRIV_IET_FRAME_PREEMPTION; + if (iet->mac_verify_configured) + priv_flags |= AM65_CPSW_PRIV_IET_MAC_VERIFY; + if (port->qos.cut_thru.enable) + priv_flags |= AM65_CPSW_PRIV_CUT_THRU; return priv_flags; } @@ -732,20 +749,115 @@ static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags) { struct am65_cpsw_common *common = am65_ndev_to_common(ndev); - int rrobin; + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_iet *iet = &port->qos.iet; + int rrobin, iet_fpe, mac_verify, cut_thru; rrobin = !!(flags & AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN); + iet_fpe = !!(flags & AM65_CPSW_PRIV_IET_FRAME_PREEMPTION); + mac_verify = !!(flags & AM65_CPSW_PRIV_IET_MAC_VERIFY); + cut_thru = !!(flags & AM65_CPSW_PRIV_CUT_THRU); if (common->usage_count) return -EBUSY; - if (common->est_enabled && rrobin) { + if ((common->est_enabled || common->iet_enabled || iet_fpe) && rrobin) { netdev_err(ndev, "p0-rx-ptype-rrobin flag conflicts with QOS\n"); return -EINVAL; } + if (common->tx_ch_num < 2 && iet_fpe) { + netdev_err(ndev, "IET fpe needs at least 2 h/w queues\n"); + return -EINVAL; + } + + if (mac_verify && (!iet->fpe_configured && !iet_fpe)) { + netdev_err(ndev, "Enable IET FPE for IET MAC verify\n"); + return -EINVAL; + } + + if (cut_thru && !(common->pdata.quirks & AM64_CPSW_QUIRK_CUT_THRU)) { + netdev_err(ndev, "Cut-Thru not supported\n"); + return -EOPNOTSUPP; + } + + if (cut_thru && common->is_emac_mode) { + netdev_err(ndev, "Enable switch mode for cut-thru\n"); + return -EINVAL; + } + common->pf_p0_rx_ptype_rrobin = rrobin; + iet->fpe_configured = iet_fpe; + iet->mac_verify_configured = mac_verify; + port->qos.cut_thru.enable = cut_thru; + + return 0; +} + +static int am65_cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_tx_chn *tx_chn; + + tx_chn = &common->tx_chns[0]; + + coal->rx_coalesce_usecs = common->rx_pace_timeout / 1000; + coal->tx_coalesce_usecs = tx_chn->tx_pace_timeout / 1000; + + return 0; +} + +static int am65_cpsw_get_per_queue_coalesce(struct net_device *ndev, u32 queue, + struct ethtool_coalesce *coal) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_tx_chn *tx_chn; + + if (queue >= AM65_CPSW_MAX_TX_QUEUES) + return -EINVAL; + + tx_chn = &common->tx_chns[queue]; + + coal->tx_coalesce_usecs = tx_chn->tx_pace_timeout / 1000; + + return 0; +} + +static int am65_cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_tx_chn *tx_chn; + + tx_chn = &common->tx_chns[0]; + + if (coal->rx_coalesce_usecs && coal->rx_coalesce_usecs < 20) + coal->rx_coalesce_usecs = 20; + + if (coal->tx_coalesce_usecs && coal->tx_coalesce_usecs < 20) + coal->tx_coalesce_usecs = 20; + + common->rx_pace_timeout = coal->rx_coalesce_usecs * 1000; + tx_chn->tx_pace_timeout = coal->tx_coalesce_usecs * 1000; + + return 0; +} + +static int am65_cpsw_set_per_queue_coalesce(struct net_device *ndev, u32 queue, + struct ethtool_coalesce *coal) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_tx_chn *tx_chn; + + if (queue >= AM65_CPSW_MAX_TX_QUEUES) + return -EINVAL; + + tx_chn = &common->tx_chns[queue]; + + if (coal->tx_coalesce_usecs && coal->tx_coalesce_usecs < 20) + coal->tx_coalesce_usecs = 20; + + tx_chn->tx_pace_timeout = coal->tx_coalesce_usecs * 1000; return 0; } @@ -767,6 +879,11 @@ .get_ts_info = am65_cpsw_get_ethtool_ts_info, .get_priv_flags = am65_cpsw_get_ethtool_priv_flags, .set_priv_flags = am65_cpsw_set_ethtool_priv_flags, + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS, + .get_coalesce = am65_cpsw_get_coalesce, + .set_coalesce = am65_cpsw_set_coalesce, + .get_per_queue_coalesce = am65_cpsw_get_per_queue_coalesce, + .set_per_queue_coalesce = am65_cpsw_set_per_queue_coalesce, .get_link = ethtool_op_get_link, .get_link_ksettings = am65_cpsw_get_link_ksettings, diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c 2022-01-06 12:45:53.822318139 -0500 @@ -27,10 +27,12 @@ #include #include #include +#include #include "cpsw_ale.h" #include "cpsw_sl.h" #include "am65-cpsw-nuss.h" +#include "am65-cpsw-switchdev.h" #include "k3-cppi-desc-pool.h" #include "am65-cpts.h" @@ -78,6 +80,7 @@ /* AM65_CPSW_P0_REG_CTL */ #define AM65_CPSW_P0_REG_CTL_RX_CHECKSUM_EN BIT(0) +#define AM65_CPSW_P0_REG_CTL_RX_REMAP_VLAN BIT(16) /* AM65_CPSW_PORT_REG_PRI_CTL */ #define AM65_CPSW_PORT_REG_PRI_CTL_RX_PTYPE_RROBIN BIT(8) @@ -191,11 +194,11 @@ cpsw_sl_ctl_set(port->slave.mac_sl, mac_control); + am65_cpsw_qos_link_up(ndev, phy->speed, phy->duplex); + /* enable forwarding */ cpsw_ale_control_set(common->ale, port->port_id, ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); - - am65_cpsw_qos_link_up(ndev, phy->speed); netif_tx_wake_all_queues(ndev); } else { int tmo; @@ -228,6 +231,9 @@ u32 port_mask, unreg_mcast = 0; int ret; + if (!common->is_emac_mode) + return 0; + if (!netif_running(ndev) || !vid) return 0; @@ -241,8 +247,8 @@ if (!vid) unreg_mcast = port_mask; dev_info(common->dev, "Adding vlan %d to vlan filter\n", vid); - ret = cpsw_ale_add_vlan(common->ale, vid, port_mask, - unreg_mcast, port_mask, 0); + ret = cpsw_ale_vlan_add_modify(common->ale, vid, port_mask, + unreg_mcast, port_mask, 0); pm_runtime_put(common->dev); return ret; @@ -252,8 +258,12 @@ __be16 proto, u16 vid) { struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); int ret; + if (!common->is_emac_mode) + return 0; + if (!netif_running(ndev) || !vid) return 0; @@ -264,17 +274,23 @@ } dev_info(common->dev, "Removing vlan %d from vlan filter\n", vid); - ret = cpsw_ale_del_vlan(common->ale, vid, 0); + ret = cpsw_ale_del_vlan(common->ale, vid, + BIT(port->port_id) | ALE_PORT_HOST); pm_runtime_put(common->dev); return ret; } -static void am65_cpsw_slave_set_promisc_2g(struct am65_cpsw_port *port, - bool promisc) +static void am65_cpsw_slave_set_promisc(struct am65_cpsw_port *port, + bool promisc) { struct am65_cpsw_common *common = port->common; + if (promisc && !common->is_emac_mode) { + dev_dbg(common->dev, "promisc mode requested in switch mode"); + return; + } + if (promisc) { /* Enable promiscuous mode */ cpsw_ale_control_set(common->ale, port->port_id, @@ -296,7 +312,7 @@ bool promisc; promisc = !!(ndev->flags & IFF_PROMISC); - am65_cpsw_slave_set_promisc_2g(port, promisc); + am65_cpsw_slave_set_promisc(port, promisc); if (promisc) return; @@ -364,8 +380,9 @@ } desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx); - buf_dma = dma_map_single(dev, skb->data, pkt_len, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_dma))) { + buf_dma = dma_map_single(rx_chn->dma_dev, skb->data, pkt_len, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(rx_chn->dma_dev, buf_dma))) { k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); dev_err(dev, "Failed to map rx skb buffer\n"); return -EINVAL; @@ -373,7 +390,8 @@ cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT, AM65_CPSW_NAV_PS_DATA_SIZE); - cppi5_hdesc_attach_buf(desc_rx, 0, 0, buf_dma, skb_tailroom(skb)); + k3_udma_glue_rx_dma_to_cppi5_addr(rx_chn->rx_chn, &buf_dma); + cppi5_hdesc_attach_buf(desc_rx, buf_dma, skb_tailroom(skb), buf_dma, skb_tailroom(skb)); swdata = cppi5_hdesc_get_swdata(desc_rx); *((void **)swdata) = skb; @@ -404,6 +422,11 @@ writel(val, host_p->port_base + AM65_CPSW_PORT_REG_PRI_CTL); } +static void am65_cpsw_init_host_port_switch(struct am65_cpsw_common *common); +static void am65_cpsw_init_host_port_emac(struct am65_cpsw_common *common); +static void am65_cpsw_init_port_switch_ale(struct am65_cpsw_port *port); +static void am65_cpsw_init_port_emac_ale(struct am65_cpsw_port *port); + static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common, netdev_features_t features) { @@ -425,10 +448,9 @@ /* set base flow_id */ writel(common->rx_flow_id_base, host_p->port_base + AM65_CPSW_PORT0_REG_FLOW_ID_OFFSET); - /* en tx crc offload */ - if (features & NETIF_F_HW_CSUM) - writel(AM65_CPSW_P0_REG_CTL_RX_CHECKSUM_EN, - host_p->port_base + AM65_CPSW_P0_REG_CTL); + writel(AM65_CPSW_P0_REG_CTL_RX_CHECKSUM_EN | + AM65_CPSW_P0_REG_CTL_RX_REMAP_VLAN, + host_p->port_base + AM65_CPSW_P0_REG_CTL); am65_cpsw_nuss_set_p0_ptype(common); @@ -452,9 +474,6 @@ ALE_DEFAULT_THREAD_ID, 0); cpsw_ale_control_set(common->ale, HOST_PORT_NUM, ALE_DEFAULT_THREAD_ENABLE, 1); - if (AM65_CPSW_IS_CPSW2G(common)) - cpsw_ale_control_set(common->ale, HOST_PORT_NUM, - ALE_PORT_NOLEARN, 1); /* switch to vlan unaware mode */ cpsw_ale_control_set(common->ale, HOST_PORT_NUM, ALE_VLAN_AWARE, 1); cpsw_ale_control_set(common->ale, HOST_PORT_NUM, @@ -468,6 +487,13 @@ port_mask, port_mask, port_mask & ~ALE_PORT_HOST); + if (common->is_emac_mode) + am65_cpsw_init_host_port_emac(common); + else + am65_cpsw_init_host_port_switch(common); + + am65_cpsw_qos_tx_p0_rate_init(common); + for (i = 0; i < common->rx_chns.descs_num; i++) { skb = __netdev_alloc_skb_ip_align(NULL, AM65_CPSW_MAX_PACKET_SIZE, @@ -497,6 +523,10 @@ } napi_enable(&common->napi_rx); + if (common->rx_irq_disabled) { + common->rx_irq_disabled = false; + enable_irq(common->rx_chns.irq); + } dev_dbg(common->dev, "cpsw_nuss started\n"); return 0; @@ -528,8 +558,10 @@ msecs_to_jiffies(1000)); if (!i) dev_err(common->dev, "tx timeout\n"); - for (i = 0; i < common->tx_ch_num; i++) + for (i = 0; i < common->tx_ch_num; i++) { napi_disable(&common->tx_chns[i].napi_tx); + hrtimer_cancel(&common->tx_chns[i].tx_hrtimer); + } for (i = 0; i < common->tx_ch_num; i++) { k3_udma_glue_reset_tx_chn(common->tx_chns[i].tx_chn, @@ -540,6 +572,7 @@ k3_udma_glue_tdown_rx_chn(common->rx_chns.rx_chn, true); napi_disable(&common->napi_rx); + hrtimer_cancel(&common->rx_hrtimer); for (i = 0; i < AM65_CPSW_MAX_RX_FLOWS; i++) k3_udma_glue_reset_rx_chn(common->rx_chns.rx_chn, i, @@ -573,6 +606,10 @@ port->slave.phy = NULL; } + /* Clean up IET */ + am65_cpsw_qos_iet_cleanup(ndev); + am65_cpsw_qos_cut_thru_cleanup(port); + ret = am65_cpsw_nuss_common_stop(common); if (ret) return ret; @@ -596,7 +633,6 @@ { struct am65_cpsw_common *common = am65_ndev_to_common(ndev); struct am65_cpsw_port *port = am65_ndev_to_port(ndev); - u32 port_mask; int ret, i; ret = pm_runtime_get_sync(common->dev); @@ -618,8 +654,12 @@ return ret; } - for (i = 0; i < common->tx_ch_num; i++) - netdev_tx_reset_queue(netdev_get_tx_queue(ndev, i)); + for (i = 0; i < common->tx_ch_num; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(ndev, i); + + netdev_tx_reset_queue(txq); + txq->tx_maxrate = common->tx_chns[i].rate_mbps; + } ret = am65_cpsw_nuss_common_open(common, ndev->features); if (ret) @@ -629,19 +669,10 @@ am65_cpsw_port_set_sl_mac(port, ndev->dev_addr); - if (port->slave.mac_only) - /* enable mac-only mode on port */ - cpsw_ale_control_set(common->ale, port->port_id, - ALE_PORT_MACONLY, 1); - if (AM65_CPSW_IS_CPSW2G(common)) - cpsw_ale_control_set(common->ale, port->port_id, - ALE_PORT_NOLEARN, 1); - - port_mask = BIT(port->port_id) | ALE_PORT_HOST; - cpsw_ale_add_ucast(common->ale, ndev->dev_addr, - HOST_PORT_NUM, ALE_SECURE, 0); - cpsw_ale_add_mcast(common->ale, ndev->broadcast, - port_mask, 0, 0, ALE_MCAST_FWD_2); + if (common->is_emac_mode) + am65_cpsw_init_port_emac_ale(port); + else + am65_cpsw_init_port_switch_ale(port); /* mac_sl should be configured via phy-link interface */ am65_cpsw_sl_ctl_reset(port); @@ -668,6 +699,11 @@ /* restore vlan configurations */ vlan_for_each(ndev, cpsw_restore_vlans, port); + /* Initialize IET */ + am65_cpsw_qos_iet_init(ndev); + am65_cpsw_qos_cut_thru_init(port); + am65_cpsw_qos_mqprio_init(port); + phy_attached_info(port->slave.phy); phy_start(port->slave.phy); @@ -691,8 +727,9 @@ swdata = cppi5_hdesc_get_swdata(desc_rx); skb = *swdata; cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); - dma_unmap_single(rx_chn->dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); dev_kfree_skb_any(skb); @@ -767,7 +804,7 @@ return ret; } - if (desc_dma & 0x1) { + if (cppi5_desc_is_tdcm(desc_dma)) { dev_dbg(dev, "%s RX tdown flow: %u\n", __func__, flow_idx); return 0; } @@ -779,6 +816,7 @@ swdata = cppi5_hdesc_get_swdata(desc_rx); skb = *swdata; cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); pkt_len = cppi5_hdesc_get_pktlen(desc_rx); cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id); @@ -793,18 +831,19 @@ csum_info = psdata[2]; dev_dbg(dev, "%s rx csum_info:%#x\n", __func__, csum_info); - dma_unmap_single(dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); new_skb = netdev_alloc_skb_ip_align(ndev, AM65_CPSW_MAX_PACKET_SIZE); if (new_skb) { + ndev_priv = netdev_priv(ndev); + am65_cpsw_nuss_set_offload_fwd_mark(skb, ndev_priv->offload_fwd_mark); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); am65_cpsw_nuss_rx_csum(skb, csum_info); napi_gro_receive(&common->napi_rx, skb); - ndev_priv = netdev_priv(ndev); stats = this_cpu_ptr(ndev_priv->stats); u64_stats_update_begin(&stats->syncp); @@ -833,6 +872,15 @@ return ret; } +static enum hrtimer_restart am65_cpsw_nuss_rx_timer_callback(struct hrtimer *timer) +{ + struct am65_cpsw_common *common = + container_of(timer, struct am65_cpsw_common, rx_hrtimer); + + enable_irq(common->rx_chns.irq); + return HRTIMER_NORESTART; +} + static int am65_cpsw_nuss_rx_poll(struct napi_struct *napi_rx, int budget) { struct am65_cpsw_common *common = am65_cpsw_napi_to_common(napi_rx); @@ -857,14 +905,23 @@ dev_dbg(common->dev, "%s num_rx:%d %d\n", __func__, num_rx, budget); - if (num_rx < budget && napi_complete_done(napi_rx, num_rx)) - enable_irq(common->rx_chns.irq); + if (num_rx < budget && napi_complete_done(napi_rx, num_rx)) { + if (common->rx_irq_disabled) { + common->rx_irq_disabled = false; + if (unlikely(common->rx_pace_timeout)) { + hrtimer_start(&common->rx_hrtimer, + ns_to_ktime(common->rx_pace_timeout), + HRTIMER_MODE_REL_PINNED); + } else { + enable_irq(common->rx_chns.irq); + } + } + } return num_rx; } static void am65_cpsw_nuss_xmit_free(struct am65_cpsw_tx_chn *tx_chn, - struct device *dev, struct cppi5_host_desc_t *desc) { struct cppi5_host_desc_t *first_desc, *next_desc; @@ -875,20 +932,23 @@ next_desc = first_desc; cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); - dma_unmap_single(dev, buf_dma, buf_dma_len, - DMA_TO_DEVICE); + dma_unmap_single(tx_chn->dma_dev, buf_dma, buf_dma_len, DMA_TO_DEVICE); next_desc_dma = cppi5_hdesc_get_next_hbdesc(first_desc); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma); while (next_desc_dma) { next_desc = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, next_desc_dma); cppi5_hdesc_get_obuf(next_desc, &buf_dma, &buf_dma_len); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); - dma_unmap_page(dev, buf_dma, buf_dma_len, + dma_unmap_page(tx_chn->dma_dev, buf_dma, buf_dma_len, DMA_TO_DEVICE); next_desc_dma = cppi5_hdesc_get_next_hbdesc(next_desc); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma); k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); } @@ -906,15 +966,62 @@ desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, desc_dma); swdata = cppi5_hdesc_get_swdata(desc_tx); skb = *(swdata); - am65_cpsw_nuss_xmit_free(tx_chn, tx_chn->common->dev, desc_tx); + am65_cpsw_nuss_xmit_free(tx_chn, desc_tx); dev_kfree_skb_any(skb); } -static int am65_cpsw_nuss_tx_compl_packets(struct am65_cpsw_common *common, - int chn, unsigned int budget) +static struct sk_buff * +am65_cpsw_nuss_tx_compl_packet(struct am65_cpsw_tx_chn *tx_chn, + dma_addr_t desc_dma) { + struct am65_cpsw_ndev_priv *ndev_priv; + struct am65_cpsw_ndev_stats *stats; struct cppi5_host_desc_t *desc_tx; + struct net_device *ndev; + struct sk_buff *skb; + void **swdata; + + desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, + desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_tx); + skb = *(swdata); + am65_cpsw_nuss_xmit_free(tx_chn, desc_tx); + + ndev = skb->dev; + + am65_cpts_tx_timestamp(tx_chn->common->cpts, skb); + + ndev_priv = netdev_priv(ndev); + stats = this_cpu_ptr(ndev_priv->stats); + u64_stats_update_begin(&stats->syncp); + stats->tx_packets++; + stats->tx_bytes += skb->len; + u64_stats_update_end(&stats->syncp); + + return skb; +} + +static void am65_cpsw_nuss_tx_wake(struct am65_cpsw_tx_chn *tx_chn, struct net_device *ndev, + struct netdev_queue *netif_txq) +{ + if (netif_tx_queue_stopped(netif_txq)) { + /* Check whether the queue is stopped due to stalled + * tx dma, if the queue is stopped then wake the queue + * as we have free desc for tx + */ + __netif_tx_lock(netif_txq, smp_processor_id()); + if (netif_running(ndev) && + (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >= MAX_SKB_FRAGS)) + netif_tx_wake_queue(netif_txq); + + __netif_tx_unlock(netif_txq); + } +} + +static int am65_cpsw_nuss_tx_compl_packets(struct am65_cpsw_common *common, + int chn, unsigned int budget, bool *tdown) +{ struct device *dev = common->dev; struct am65_cpsw_tx_chn *tx_chn; struct netdev_queue *netif_txq; @@ -923,41 +1030,70 @@ struct sk_buff *skb; dma_addr_t desc_dma; int res, num_tx = 0; - void **swdata; tx_chn = &common->tx_chns[chn]; while (true) { - struct am65_cpsw_ndev_priv *ndev_priv; - struct am65_cpsw_ndev_stats *stats; - + spin_lock(&tx_chn->lock); res = k3_udma_glue_pop_tx_chn(tx_chn->tx_chn, &desc_dma); + spin_unlock(&tx_chn->lock); if (res == -ENODATA) break; - if (desc_dma & 0x1) { + if (cppi5_desc_is_tdcm(desc_dma)) { if (atomic_dec_and_test(&common->tdown_cnt)) complete(&common->tdown_complete); + *tdown = true; break; } - desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, - desc_dma); - swdata = cppi5_hdesc_get_swdata(desc_tx); - skb = *(swdata); - am65_cpsw_nuss_xmit_free(tx_chn, dev, desc_tx); - + skb = am65_cpsw_nuss_tx_compl_packet(tx_chn, desc_dma); + total_bytes = skb->len; ndev = skb->dev; + napi_consume_skb(skb, budget); + num_tx++; - am65_cpts_tx_timestamp(common->cpts, skb); + netif_txq = netdev_get_tx_queue(ndev, chn); - ndev_priv = netdev_priv(ndev); - stats = this_cpu_ptr(ndev_priv->stats); - u64_stats_update_begin(&stats->syncp); - stats->tx_packets++; - stats->tx_bytes += skb->len; - u64_stats_update_end(&stats->syncp); + netdev_tx_completed_queue(netif_txq, num_tx, total_bytes); + + am65_cpsw_nuss_tx_wake(tx_chn, ndev, netif_txq); + } + + dev_dbg(dev, "%s:%u pkt:%d\n", __func__, chn, num_tx); + + return num_tx; +} + +static int am65_cpsw_nuss_tx_compl_packets_2g(struct am65_cpsw_common *common, + int chn, unsigned int budget, bool *tdown) +{ + struct device *dev = common->dev; + struct am65_cpsw_tx_chn *tx_chn; + struct netdev_queue *netif_txq; + unsigned int total_bytes = 0; + struct net_device *ndev; + struct sk_buff *skb; + dma_addr_t desc_dma; + int res, num_tx = 0; + + tx_chn = &common->tx_chns[chn]; + + while (true) { + res = k3_udma_glue_pop_tx_chn(tx_chn->tx_chn, &desc_dma); + if (res == -ENODATA) + break; + if (cppi5_desc_is_tdcm(desc_dma)) { + if (atomic_dec_and_test(&common->tdown_cnt)) + complete(&common->tdown_complete); + *tdown = true; + break; + } + + skb = am65_cpsw_nuss_tx_compl_packet(tx_chn, desc_dma); + + ndev = skb->dev; total_bytes += skb->len; napi_consume_skb(skb, budget); num_tx++; @@ -970,44 +1106,56 @@ netdev_tx_completed_queue(netif_txq, num_tx, total_bytes); - if (netif_tx_queue_stopped(netif_txq)) { - /* Check whether the queue is stopped due to stalled tx dma, - * if the queue is stopped then wake the queue as - * we have free desc for tx - */ - __netif_tx_lock(netif_txq, smp_processor_id()); - if (netif_running(ndev) && - (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >= - MAX_SKB_FRAGS)) - netif_tx_wake_queue(netif_txq); + am65_cpsw_nuss_tx_wake(tx_chn, ndev, netif_txq); - __netif_tx_unlock(netif_txq); - } dev_dbg(dev, "%s:%u pkt:%d\n", __func__, chn, num_tx); return num_tx; } +static enum hrtimer_restart am65_cpsw_nuss_tx_timer_callback(struct hrtimer *timer) +{ + struct am65_cpsw_tx_chn *tx_chns = + container_of(timer, struct am65_cpsw_tx_chn, tx_hrtimer); + + enable_irq(tx_chns->irq); + return HRTIMER_NORESTART; +} + static int am65_cpsw_nuss_tx_poll(struct napi_struct *napi_tx, int budget) { struct am65_cpsw_tx_chn *tx_chn = am65_cpsw_napi_to_tx_chn(napi_tx); + bool tdown = false; int num_tx; - num_tx = am65_cpsw_nuss_tx_compl_packets(tx_chn->common, tx_chn->id, - budget); - num_tx = min(num_tx, budget); - if (num_tx < budget) { - napi_complete(napi_tx); - enable_irq(tx_chn->irq); + if (AM65_CPSW_IS_CPSW2G(tx_chn->common)) + num_tx = am65_cpsw_nuss_tx_compl_packets_2g(tx_chn->common, tx_chn->id, + budget, &tdown); + else + num_tx = am65_cpsw_nuss_tx_compl_packets(tx_chn->common, + tx_chn->id, budget, &tdown); + + if (num_tx >= budget) + return budget; + + if (napi_complete_done(napi_tx, num_tx)) { + if (unlikely(tx_chn->tx_pace_timeout && !tdown)) { + hrtimer_start(&tx_chn->tx_hrtimer, + ns_to_ktime(tx_chn->tx_pace_timeout), + HRTIMER_MODE_REL_PINNED); + } else { + enable_irq(tx_chn->irq); + } } - return num_tx; + return 0; } static irqreturn_t am65_cpsw_nuss_rx_irq(int irq, void *dev_id) { struct am65_cpsw_common *common = dev_id; + common->rx_irq_disabled = true; disable_irq_nosync(irq); napi_schedule(&common->napi_rx); @@ -1053,9 +1201,9 @@ netif_txq = netdev_get_tx_queue(ndev, q_idx); /* Map the linear buffer */ - buf_dma = dma_map_single(dev, skb->data, pkt_len, + buf_dma = dma_map_single(tx_chn->dma_dev, skb->data, pkt_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_dma))) { + if (unlikely(dma_mapping_error(tx_chn->dma_dev, buf_dma))) { dev_err(dev, "Failed to map tx skb buffer\n"); ndev->stats.tx_errors++; goto err_free_skb; @@ -1064,7 +1212,8 @@ first_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); if (!first_desc) { dev_dbg(dev, "Failed to allocate descriptor\n"); - dma_unmap_single(dev, buf_dma, pkt_len, DMA_TO_DEVICE); + dma_unmap_single(tx_chn->dma_dev, buf_dma, pkt_len, + DMA_TO_DEVICE); goto busy_stop_q; } @@ -1074,6 +1223,7 @@ cppi5_hdesc_set_pkttype(first_desc, 0x7); cppi5_desc_set_tags_ids(&first_desc->hdr, 0, port->port_id); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len); swdata = cppi5_hdesc_get_swdata(first_desc); *(swdata) = skb; @@ -1109,9 +1259,9 @@ goto busy_free_descs; } - buf_dma = skb_frag_dma_map(dev, frag, 0, frag_size, + buf_dma = skb_frag_dma_map(tx_chn->dma_dev, frag, 0, frag_size, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_dma))) { + if (unlikely(dma_mapping_error(tx_chn->dma_dev, buf_dma))) { dev_err(dev, "Failed to map tx skb page\n"); k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); ndev->stats.tx_errors++; @@ -1119,11 +1269,13 @@ } cppi5_hdesc_reset_hbdesc(next_desc); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); cppi5_hdesc_attach_buf(next_desc, buf_dma, frag_size, buf_dma, frag_size); desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, next_desc); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &desc_dma); cppi5_hdesc_link_hbdesc(cur_desc, desc_dma); pkt_len += frag_size; @@ -1139,7 +1291,13 @@ cppi5_hdesc_set_pktlen(first_desc, pkt_len); desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, first_desc); - ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + if (AM65_CPSW_IS_CPSW2G(common)) { + ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + } else { + spin_lock_bh(&tx_chn->lock); + ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + spin_unlock_bh(&tx_chn->lock); + } if (ret) { dev_err(dev, "can't push desc %d\n", ret); /* inform bql */ @@ -1165,14 +1323,14 @@ return NETDEV_TX_OK; err_free_descs: - am65_cpsw_nuss_xmit_free(tx_chn, dev, first_desc); + am65_cpsw_nuss_xmit_free(tx_chn, first_desc); err_free_skb: ndev->stats.tx_dropped++; dev_kfree_skb_any(skb); return NETDEV_TX_OK; busy_free_descs: - am65_cpsw_nuss_xmit_free(tx_chn, dev, first_desc); + am65_cpsw_nuss_xmit_free(tx_chn, first_desc); busy_stop_q: netif_tx_stop_queue(netif_txq); return NETDEV_TX_BUSY; @@ -1314,6 +1472,43 @@ return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } +static int am65_cpsw_switch_config_ioctl(struct net_device *ndev, + struct ifreq *ifrq, int cmd) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct net_switch_config config; + int ret = -EINVAL; + + /* Only SIOCSWITCHCONFIG is used as cmd argument and hence, there is no + * switch statement required. + * Function calls are based on switch_config.cmd + */ + + if (copy_from_user(&config, ifrq->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.cmd) { + case SWITCH_RATELIMIT: + { + ret = cpsw_ale_rx_ratelimit_mc(common->ale, port->port_id, config.mcast_rate_limit); + if (ret) + dev_err(common->dev, "CPSW_ALE set MC ratelimit failed"); + + ret = cpsw_ale_rx_ratelimit_bc(common->ale, port->port_id, config.bcast_rate_limit); + if (ret) + dev_err(common->dev, "CPSW_ALE set BC ratelimit failed"); + + break; + } + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev, struct ifreq *req, int cmd) { @@ -1327,6 +1522,8 @@ return am65_cpsw_nuss_hwtstamp_set(ndev, req); case SIOCGHWTSTAMP: return am65_cpsw_nuss_hwtstamp_get(ndev, req); + case SIOCSWITCHCONFIG: + return am65_cpsw_switch_config_ioctl(ndev, req, cmd); } if (!port->slave.phy) @@ -1369,32 +1566,14 @@ stats->tx_dropped = dev->stats.tx_dropped; } -static int am65_cpsw_nuss_ndo_slave_set_features(struct net_device *ndev, - netdev_features_t features) +static struct devlink_port *am65_cpsw_ndo_get_devlink_port(struct net_device *ndev) { - struct am65_cpsw_common *common = am65_ndev_to_common(ndev); - netdev_features_t changes = features ^ ndev->features; - struct am65_cpsw_host *host_p; - - host_p = am65_common_get_host(common); - - if (changes & NETIF_F_HW_CSUM) { - bool enable = !!(features & NETIF_F_HW_CSUM); - - dev_info(common->dev, "Turn %s tx-checksum-ip-generic\n", - enable ? "ON" : "OFF"); - if (enable) - writel(AM65_CPSW_P0_REG_CTL_RX_CHECKSUM_EN, - host_p->port_base + AM65_CPSW_P0_REG_CTL); - else - writel(0, - host_p->port_base + AM65_CPSW_P0_REG_CTL); - } + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); - return 0; + return &port->devlink_port; } -static const struct net_device_ops am65_cpsw_nuss_netdev_ops_2g = { +static const struct net_device_ops am65_cpsw_nuss_netdev_ops = { .ndo_open = am65_cpsw_nuss_ndo_slave_open, .ndo_stop = am65_cpsw_nuss_ndo_slave_stop, .ndo_start_xmit = am65_cpsw_nuss_ndo_slave_xmit, @@ -1406,8 +1585,9 @@ .ndo_vlan_rx_add_vid = am65_cpsw_nuss_ndo_slave_add_vid, .ndo_vlan_rx_kill_vid = am65_cpsw_nuss_ndo_slave_kill_vid, .ndo_do_ioctl = am65_cpsw_nuss_ndo_slave_ioctl, - .ndo_set_features = am65_cpsw_nuss_ndo_slave_set_features, .ndo_setup_tc = am65_cpsw_qos_ndo_setup_tc, + .ndo_set_tx_maxrate = am65_cpsw_qos_ndo_tx_p0_set_maxrate, + .ndo_get_devlink_port = am65_cpsw_ndo_get_devlink_port, }; static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port) @@ -1417,7 +1597,6 @@ if (!port->disabled) return; - common->disabled_ports_mask |= BIT(port->port_id); cpsw_ale_control_set(common->ale, port->port_id, ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); @@ -1450,6 +1629,7 @@ devm_remove_action(dev, am65_cpsw_nuss_free_tx_chns, common); + common->tx_ch_rate_msk = 0; for (i = 0; i < common->tx_ch_num; i++) { struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; @@ -1496,28 +1676,29 @@ snprintf(tx_chn->tx_chn_name, sizeof(tx_chn->tx_chn_name), "tx%d", i); + spin_lock_init(&tx_chn->lock); tx_chn->common = common; tx_chn->id = i; tx_chn->descs_num = max_desc_num; - tx_chn->desc_pool = - k3_cppi_desc_pool_create_name(dev, - tx_chn->descs_num, - hdesc_size, - tx_chn->tx_chn_name); - if (IS_ERR(tx_chn->desc_pool)) { - ret = PTR_ERR(tx_chn->desc_pool); - dev_err(dev, "Failed to create poll %d\n", ret); - goto err; - } tx_chn->tx_chn = k3_udma_glue_request_tx_chn(dev, tx_chn->tx_chn_name, &tx_cfg); if (IS_ERR(tx_chn->tx_chn)) { - ret = PTR_ERR(tx_chn->tx_chn); - dev_err(dev, "Failed to request tx dma channel %d\n", - ret); + ret = dev_err_probe(dev, PTR_ERR(tx_chn->tx_chn), + "Failed to request tx dma channel\n"); + goto err; + } + tx_chn->dma_dev = k3_udma_glue_tx_get_dma_device(tx_chn->tx_chn); + + tx_chn->desc_pool = k3_cppi_desc_pool_create_name(tx_chn->dma_dev, + tx_chn->descs_num, + hdesc_size, + tx_chn->tx_chn_name); + if (IS_ERR(tx_chn->desc_pool)) { + ret = PTR_ERR(tx_chn->desc_pool); + dev_err(dev, "Failed to create poll %d\n", ret); goto err; } @@ -1577,7 +1758,16 @@ /* init all flows */ rx_chn->dev = dev; rx_chn->descs_num = max_desc_num; - rx_chn->desc_pool = k3_cppi_desc_pool_create_name(dev, + + rx_chn->rx_chn = k3_udma_glue_request_rx_chn(dev, "rx", &rx_cfg); + if (IS_ERR(rx_chn->rx_chn)) { + ret = dev_err_probe(dev, PTR_ERR(rx_chn->rx_chn), + "Failed to request rx dma channel\n"); + goto err; + } + rx_chn->dma_dev = k3_udma_glue_rx_get_dma_device(rx_chn->rx_chn); + + rx_chn->desc_pool = k3_cppi_desc_pool_create_name(rx_chn->dma_dev, rx_chn->descs_num, hdesc_size, "rx"); if (IS_ERR(rx_chn->desc_pool)) { @@ -1586,13 +1776,6 @@ goto err; } - rx_chn->rx_chn = k3_udma_glue_request_rx_chn(dev, "rx", &rx_cfg); - if (IS_ERR(rx_chn->rx_chn)) { - ret = PTR_ERR(rx_chn->rx_chn); - dev_err(dev, "Failed to request rx dma channel %d\n", ret); - goto err; - } - common->rx_flow_id_base = k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn); dev_info(dev, "set new flow-id-base %u\n", common->rx_flow_id_base); @@ -1606,7 +1789,6 @@ }; struct k3_ring_cfg fdqring_cfg = { .elm_size = K3_RINGACC_RING_ELSIZE_8, - .mode = K3_RINGACC_RING_MODE_MESSAGE, .flags = K3_RINGACC_RING_SHARED, }; struct k3_udma_glue_rx_flow_cfg rx_flow_cfg = { @@ -1620,6 +1802,7 @@ rx_flow_cfg.ring_rxfdq0_id = fdqring_id; rx_flow_cfg.rx_cfg.size = max_desc_num; rx_flow_cfg.rxfdq_cfg.size = max_desc_num; + rx_flow_cfg.rxfdq_cfg.mode = common->pdata.fdqring_mode; ret = k3_udma_glue_rx_flow_init(rx_chn->rx_chn, i, &rx_flow_cfg); @@ -1725,6 +1908,13 @@ return ret; } common->cpts = cpts; + /* Forbid PM runtime if CPTS is running. + * K3 CPSWxG modules may completely lose context during ON->OFF + * transitions depending on integration. + * AM65x/J721E MCU CPSW2G: false + * J721E MAIN_CPSW9G: true + */ + pm_runtime_forbid(dev); return 0; } @@ -1772,14 +1962,17 @@ port->fetch_ram_base = common->cpsw_base + AM65_CPSW_NU_FRAM_BASE + (AM65_CPSW_NU_FRAM_PORT_OFFSET * (port_id - 1)); + port->qos.iet.addfragsize = 1; port->slave.mac_sl = cpsw_sl_get("am65", dev, port->port_base); if (IS_ERR(port->slave.mac_sl)) return PTR_ERR(port->slave.mac_sl); port->disabled = !of_device_is_available(port_np); - if (port->disabled) + if (port->disabled) { + common->disabled_ports_mask |= BIT(port->port_id); continue; + } port->slave.ifphy = devm_of_phy_get(dev, port_np, NULL); if (IS_ERR(port->slave.ifphy)) { @@ -1795,12 +1988,10 @@ /* get phy/link info */ if (of_phy_is_fixed_link(port_np)) { ret = of_phy_register_fixed_link(port_np); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "%pOF failed to register fixed-link phy: %d\n", - port_np, ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to register fixed-link phy %pOF\n", + port_np); port->slave.phy_node = of_node_get(port_np); } else { port->slave.phy_node = @@ -1833,6 +2024,12 @@ } of_node_put(node); + /* is there at least one ext.port */ + if (!(~common->disabled_ports_mask & GENMASK(common->port_num, 1))) { + dev_err(dev, "No Ext. port are available\n"); + return -ENODEV; + } + return 0; } @@ -1843,14 +2040,18 @@ free_percpu(stats); } -static int am65_cpsw_nuss_init_ndev_2g(struct am65_cpsw_common *common) +static int +am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) { struct am65_cpsw_ndev_priv *ndev_priv; struct device *dev = common->dev; struct am65_cpsw_port *port; int ret; - port = am65_common_get_port(common, 1); + port = &common->ports[port_idx]; + + if (port->disabled) + return 0; /* alloc netdev */ port->ndev = devm_alloc_etherdev_mqs(common->dev, @@ -1879,7 +2080,7 @@ port->ndev->features = port->ndev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; port->ndev->vlan_features |= NETIF_F_SG; - port->ndev->netdev_ops = &am65_cpsw_nuss_netdev_ops_2g; + port->ndev->netdev_ops = &am65_cpsw_nuss_netdev_ops; port->ndev->ethtool_ops = &am65_cpsw_ethtool_ops_slave; /* Disable TX checksum offload by default due to HW bug */ @@ -1892,30 +2093,46 @@ ret = devm_add_action_or_reset(dev, am65_cpsw_pcpu_stats_free, ndev_priv->stats); - if (ret) { - dev_err(dev, "Failed to add percpu stat free action %d\n", ret); - return ret; + if (ret) + dev_err(dev, "failed to add percpu stat free action %d\n", ret); + + if (!common->dma_ndev) + common->dma_ndev = port->ndev; + + return ret; +} + +static int am65_cpsw_nuss_init_ndevs(struct am65_cpsw_common *common) +{ + int ret; + int i; + + for (i = 0; i < common->port_num; i++) { + ret = am65_cpsw_nuss_init_port_ndev(common, i); + if (ret) + return ret; } - netif_napi_add(port->ndev, &common->napi_rx, + netif_napi_add(common->dma_ndev, &common->napi_rx, am65_cpsw_nuss_rx_poll, NAPI_POLL_WEIGHT); + hrtimer_init(&common->rx_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + common->rx_hrtimer.function = &am65_cpsw_nuss_rx_timer_callback; return ret; } -static int am65_cpsw_nuss_ndev_add_napi_2g(struct am65_cpsw_common *common) +static int am65_cpsw_nuss_ndev_add_tx_napi(struct am65_cpsw_common *common) { struct device *dev = common->dev; - struct am65_cpsw_port *port; int i, ret = 0; - port = am65_common_get_port(common, 1); - for (i = 0; i < common->tx_ch_num; i++) { struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; - netif_tx_napi_add(port->ndev, &tx_chn->napi_tx, + netif_tx_napi_add(common->dma_ndev, &tx_chn->napi_tx, am65_cpsw_nuss_tx_poll, NAPI_POLL_WEIGHT); + hrtimer_init(&tx_chn->tx_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + tx_chn->tx_hrtimer.function = &am65_cpsw_nuss_tx_timer_callback; ret = devm_request_irq(dev, tx_chn->irq, am65_cpsw_nuss_tx_irq, @@ -1932,16 +2149,468 @@ return ret; } -static int am65_cpsw_nuss_ndev_reg_2g(struct am65_cpsw_common *common) +static void am65_cpsw_nuss_cleanup_ndev(struct am65_cpsw_common *common) +{ + struct am65_cpsw_port *port; + int i; + + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + if (port->ndev) + unregister_netdev(port->ndev); + } +} + +static void am65_cpsw_port_offload_fwd_mark_update(struct am65_cpsw_common *common) +{ + int set_val = 0; + int i; + + if (common->br_members == (GENMASK(common->port_num, 1) & ~common->disabled_ports_mask)) + set_val = 1; + + dev_dbg(common->dev, "set offload_fwd_mark %d\n", set_val); + + for (i = 1; i <= common->port_num; i++) { + struct am65_cpsw_port *port = am65_common_get_port(common, i); + struct am65_cpsw_ndev_priv *priv; + + if (!port->ndev) + continue; + + priv = am65_ndev_to_priv(port->ndev); + priv->offload_fwd_mark = set_val; + } +} + +bool am65_cpsw_port_dev_check(const struct net_device *ndev) +{ + if (ndev->netdev_ops == &am65_cpsw_nuss_netdev_ops) { + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + + return !common->is_emac_mode; + } + + return false; +} + +static int am65_cpsw_netdevice_port_link(struct net_device *ndev, struct net_device *br_ndev) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev); + + if (!common->br_members) { + common->hw_bridge_dev = br_ndev; + } else { + /* This is adding the port to a second bridge, this is + * unsupported + */ + if (common->hw_bridge_dev != br_ndev) + return -EOPNOTSUPP; + } + + common->br_members |= BIT(priv->port->port_id); + + am65_cpsw_port_offload_fwd_mark_update(common); + + return NOTIFY_DONE; +} + +static void am65_cpsw_netdevice_port_unlink(struct net_device *ndev) +{ + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev); + + common->br_members &= ~BIT(priv->port->port_id); + + am65_cpsw_port_offload_fwd_mark_update(common); + + if (!common->br_members) + common->hw_bridge_dev = NULL; +} + +/* netdev notifier */ +static int am65_cpsw_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + int ret = NOTIFY_DONE; + + if (!am65_cpsw_port_dev_check(ndev)) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_CHANGEUPPER: + info = ptr; + + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) + ret = am65_cpsw_netdevice_port_link(ndev, info->upper_dev); + else + am65_cpsw_netdevice_port_unlink(ndev); + } + break; + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(ret); +} + +static int am65_cpsw_register_notifiers(struct am65_cpsw_common *cpsw) +{ + int ret = 0; + + if (AM65_CPSW_IS_CPSW2G(cpsw) || + !IS_REACHABLE(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV)) + return 0; + + cpsw->am65_cpsw_netdevice_nb.notifier_call = &am65_cpsw_netdevice_event; + ret = register_netdevice_notifier(&cpsw->am65_cpsw_netdevice_nb); + if (ret) { + dev_err(cpsw->dev, "can't register netdevice notifier\n"); + return ret; + } + + ret = am65_cpsw_switchdev_register_notifiers(cpsw); + if (ret) + unregister_netdevice_notifier(&cpsw->am65_cpsw_netdevice_nb); + + return ret; +} + +static void am65_cpsw_unregister_notifiers(struct am65_cpsw_common *cpsw) { + if (AM65_CPSW_IS_CPSW2G(cpsw) || + !IS_REACHABLE(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV)) + return; + + am65_cpsw_switchdev_unregister_notifiers(cpsw); + unregister_netdevice_notifier(&cpsw->am65_cpsw_netdevice_nb); +} + +static const struct devlink_ops am65_cpsw_devlink_ops = {}; + +static void am65_cpsw_init_stp_ale_entry(struct am65_cpsw_common *cpsw) +{ + cpsw_ale_add_mcast(cpsw->ale, eth_stp_addr, ALE_PORT_HOST, ALE_SUPER, 0, + ALE_MCAST_BLOCK_LEARN_FWD); +} + +static void am65_cpsw_init_host_port_switch(struct am65_cpsw_common *common) +{ + struct am65_cpsw_host *host = am65_common_get_host(common); + + writel(common->default_vlan, host->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + + am65_cpsw_init_stp_ale_entry(common); + + cpsw_ale_control_set(common->ale, HOST_PORT_NUM, ALE_P0_UNI_FLOOD, 1); + dev_dbg(common->dev, "Set P0_UNI_FLOOD\n"); + cpsw_ale_control_set(common->ale, HOST_PORT_NUM, ALE_PORT_NOLEARN, 0); +} + +static void am65_cpsw_init_host_port_emac(struct am65_cpsw_common *common) +{ + struct am65_cpsw_host *host = am65_common_get_host(common); + + writel(0, host->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + + cpsw_ale_control_set(common->ale, HOST_PORT_NUM, ALE_P0_UNI_FLOOD, 0); + dev_dbg(common->dev, "unset P0_UNI_FLOOD\n"); + + /* learning make no sense in multi-mac mode */ + cpsw_ale_control_set(common->ale, HOST_PORT_NUM, ALE_PORT_NOLEARN, 1); +} + +static int am65_cpsw_dl_switch_mode_get(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct am65_cpsw_devlink *dl_priv = devlink_priv(dl); + struct am65_cpsw_common *common = dl_priv->common; + + dev_dbg(common->dev, "%s id:%u\n", __func__, id); + + if (id != AM65_CPSW_DL_PARAM_SWITCH_MODE) + return -EOPNOTSUPP; + + ctx->val.vbool = !common->is_emac_mode; + + return 0; +} + +static void am65_cpsw_init_port_emac_ale(struct am65_cpsw_port *port) +{ + struct am65_cpsw_slave_data *slave = &port->slave; + struct am65_cpsw_common *common = port->common; + u32 port_mask; + + writel(slave->port_vlan, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + + if (slave->mac_only) + /* enable mac-only mode on port */ + cpsw_ale_control_set(common->ale, port->port_id, + ALE_PORT_MACONLY, 1); + + cpsw_ale_control_set(common->ale, port->port_id, ALE_PORT_NOLEARN, 1); + + port_mask = BIT(port->port_id) | ALE_PORT_HOST; + + cpsw_ale_add_ucast(common->ale, port->ndev->dev_addr, + HOST_PORT_NUM, ALE_SECURE, slave->port_vlan); + cpsw_ale_add_mcast(common->ale, port->ndev->broadcast, + port_mask, ALE_VLAN, slave->port_vlan, ALE_MCAST_FWD_2); +} + +static void am65_cpsw_init_port_switch_ale(struct am65_cpsw_port *port) +{ + struct am65_cpsw_slave_data *slave = &port->slave; + struct am65_cpsw_common *cpsw = port->common; + u32 port_mask; + + cpsw_ale_control_set(cpsw->ale, port->port_id, + ALE_PORT_NOLEARN, 0); + + cpsw_ale_add_ucast(cpsw->ale, port->ndev->dev_addr, + HOST_PORT_NUM, ALE_SECURE | ALE_BLOCKED | ALE_VLAN, + slave->port_vlan); + + port_mask = BIT(port->port_id) | ALE_PORT_HOST; + + cpsw_ale_add_mcast(cpsw->ale, port->ndev->broadcast, + port_mask, ALE_VLAN, slave->port_vlan, + ALE_MCAST_FWD_2); + + writel(slave->port_vlan, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + + cpsw_ale_control_set(cpsw->ale, port->port_id, + ALE_PORT_MACONLY, 0); +} + +static int am65_cpsw_dl_switch_mode_set(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct am65_cpsw_devlink *dl_priv = devlink_priv(dl); + struct am65_cpsw_common *cpsw = dl_priv->common; + bool switch_en = ctx->val.vbool; + bool if_running = false; + int i; + + dev_dbg(cpsw->dev, "%s id:%u\n", __func__, id); + + if (id != AM65_CPSW_DL_PARAM_SWITCH_MODE) + return -EOPNOTSUPP; + + if (switch_en == !cpsw->is_emac_mode) + return 0; + + if (!switch_en && cpsw->br_members) { + dev_err(cpsw->dev, "Remove ports from bridge before disabling switch mode\n"); + return -EINVAL; + } + + rtnl_lock(); + + cpsw->is_emac_mode = !switch_en; + + for (i = 0; i < cpsw->port_num; i++) { + struct net_device *sl_ndev = cpsw->ports[i].ndev; + + if (!sl_ndev || !netif_running(sl_ndev)) + continue; + + if_running = true; + } + + if (!if_running) { + /* all ndevs are down */ + for (i = 0; i < cpsw->port_num; i++) { + struct net_device *sl_ndev = cpsw->ports[i].ndev; + struct am65_cpsw_slave_data *slave; + + if (!sl_ndev) + continue; + + slave = am65_ndev_to_slave(sl_ndev); + if (switch_en) + slave->port_vlan = cpsw->default_vlan; + else + slave->port_vlan = 0; + } + + goto exit; + } + + cpsw_ale_control_set(cpsw->ale, 0, ALE_BYPASS, 1); + /* clean up ALE table */ + cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_CLEAR, 1); + cpsw_ale_control_get(cpsw->ale, HOST_PORT_NUM, ALE_AGEOUT); + + if (switch_en) { + dev_info(cpsw->dev, "Enable switch mode\n"); + + am65_cpsw_init_host_port_switch(cpsw); + + for (i = 0; i < cpsw->port_num; i++) { + struct net_device *sl_ndev = cpsw->ports[i].ndev; + struct am65_cpsw_slave_data *slave; + struct am65_cpsw_port *port; + + if (!sl_ndev) + continue; + + port = am65_ndev_to_port(sl_ndev); + slave = am65_ndev_to_slave(sl_ndev); + slave->port_vlan = cpsw->default_vlan; + + if (netif_running(sl_ndev)) + am65_cpsw_init_port_switch_ale(port); + } + + } else { + dev_info(cpsw->dev, "Disable switch mode\n"); + + am65_cpsw_init_host_port_emac(cpsw); + + for (i = 0; i < cpsw->port_num; i++) { + struct net_device *sl_ndev = cpsw->ports[i].ndev; + struct am65_cpsw_port *port; + + if (!sl_ndev) + continue; + + port = am65_ndev_to_port(sl_ndev); + port->slave.port_vlan = 0; + if (netif_running(sl_ndev)) { + am65_cpsw_init_port_emac_ale(port); + am65_cpsw_qos_cut_thru_cleanup(port); + } + } + } + cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_BYPASS, 0); +exit: + rtnl_unlock(); + + return 0; +} + +static const struct devlink_param am65_cpsw_devlink_params[] = { + DEVLINK_PARAM_DRIVER(AM65_CPSW_DL_PARAM_SWITCH_MODE, "switch_mode", + DEVLINK_PARAM_TYPE_BOOL, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + am65_cpsw_dl_switch_mode_get, + am65_cpsw_dl_switch_mode_set, NULL), +}; + +static void am65_cpsw_unregister_devlink_ports(struct am65_cpsw_common *common) +{ + struct devlink_port *dl_port; + struct am65_cpsw_port *port; + int i; + + for (i = 1; i <= common->port_num; i++) { + port = am65_common_get_port(common, i); + dl_port = &port->devlink_port; + + if (dl_port->registered) + devlink_port_unregister(dl_port); + } +} + +static int am65_cpsw_nuss_register_devlink(struct am65_cpsw_common *common) +{ + struct devlink_port_attrs attrs = {}; + struct am65_cpsw_devlink *dl_priv; struct device *dev = common->dev; + struct devlink_port *dl_port; struct am65_cpsw_port *port; int ret = 0; + int i; + + common->devlink = + devlink_alloc(&am65_cpsw_devlink_ops, sizeof(*dl_priv)); + if (!common->devlink) + return -ENOMEM; + + dl_priv = devlink_priv(common->devlink); + dl_priv->common = common; + + ret = devlink_register(common->devlink, dev); + if (ret) { + dev_err(dev, "devlink reg fail ret:%d\n", ret); + goto dl_free; + } + + /* Provide devlink hook to switch mode when multiple external ports + * are present NUSS switchdev driver is enabled. + */ + if (!AM65_CPSW_IS_CPSW2G(common) && + IS_ENABLED(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV)) { + ret = devlink_params_register(common->devlink, + am65_cpsw_devlink_params, + ARRAY_SIZE(am65_cpsw_devlink_params)); + if (ret) { + dev_err(dev, "devlink params reg fail ret:%d\n", ret); + goto dl_unreg; + } + devlink_params_publish(common->devlink); + } + + for (i = 1; i <= common->port_num; i++) { + port = am65_common_get_port(common, i); + dl_port = &port->devlink_port; + + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + attrs.phys.port_number = port->port_id; + attrs.switch_id.id_len = sizeof(resource_size_t); + memcpy(attrs.switch_id.id, common->switch_id, attrs.switch_id.id_len); + devlink_port_attrs_set(dl_port, &attrs); + + ret = devlink_port_register(common->devlink, dl_port, port->port_id); + if (ret) { + dev_err(dev, "devlink_port reg fail for port %d, ret:%d\n", + port->port_id, ret); + goto dl_port_unreg; + } + devlink_port_type_eth_set(dl_port, port->ndev); + } + + return ret; + +dl_port_unreg: + am65_cpsw_unregister_devlink_ports(common); +dl_unreg: + devlink_unregister(common->devlink); +dl_free: + devlink_free(common->devlink); + + return ret; +} + +static void am65_cpsw_unregister_devlink(struct am65_cpsw_common *common) +{ + if (!AM65_CPSW_IS_CPSW2G(common) && + IS_ENABLED(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV)) { + devlink_params_unpublish(common->devlink); + devlink_params_unregister(common->devlink, am65_cpsw_devlink_params, + ARRAY_SIZE(am65_cpsw_devlink_params)); + } + + am65_cpsw_unregister_devlink_ports(common); + devlink_unregister(common->devlink); + devlink_free(common->devlink); +} + +static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) +{ + struct device *dev = common->dev; + struct am65_cpsw_port *port; + int ret = 0, i; - port = am65_common_get_port(common, 1); - ret = am65_cpsw_nuss_ndev_add_napi_2g(common); + ret = am65_cpsw_nuss_ndev_add_tx_napi(common); if (ret) - goto err; + return ret; ret = devm_request_irq(dev, common->rx_chns.irq, am65_cpsw_nuss_rx_irq, @@ -1949,17 +2618,45 @@ if (ret) { dev_err(dev, "failure requesting rx irq %u, %d\n", common->rx_chns.irq, ret); - goto err; + return ret; } - ret = register_netdev(port->ndev); + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + + ret = am65_cpsw_nuss_register_port_debugfs(port); + if (ret) + goto err_cleanup_ndev; + + if (!port->ndev) + continue; + + ret = register_netdev(port->ndev); + if (ret) { + dev_err(dev, "error registering slave net device%i %d\n", + i, ret); + goto err_cleanup_ndev; + } + } + + ret = am65_cpsw_register_notifiers(common); + if (ret) + goto err_cleanup_ndev; + + ret = am65_cpsw_nuss_register_devlink(common); if (ret) - dev_err(dev, "error registering slave net device %d\n", ret); + goto clean_unregister_notifiers; /* can't auto unregister ndev using devm_add_action() due to * devres release sequence in DD core for DMA */ -err: + + return 0; +clean_unregister_notifiers: + am65_cpsw_unregister_notifiers(common); +err_cleanup_ndev: + am65_cpsw_nuss_cleanup_ndev(common); + return ret; } @@ -1972,19 +2669,7 @@ if (ret) return ret; - return am65_cpsw_nuss_ndev_add_napi_2g(common); -} - -static void am65_cpsw_nuss_cleanup_ndev(struct am65_cpsw_common *common) -{ - struct am65_cpsw_port *port; - int i; - - for (i = 0; i < common->port_num; i++) { - port = &common->ports[i]; - if (port->ndev) - unregister_netdev(port->ndev); - } + return am65_cpsw_nuss_ndev_add_tx_napi(common); } struct am65_cpsw_soc_pdata { @@ -2005,15 +2690,26 @@ static const struct am65_cpsw_pdata am65x_sr1_0 = { .quirks = AM65_CPSW_QUIRK_I2027_NO_TX_CSUM, + .ale_dev_id = "am65x-cpsw2g", + .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE, }; static const struct am65_cpsw_pdata j721e_pdata = { .quirks = 0, + .ale_dev_id = "am65x-cpsw2g", + .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE, +}; + +static const struct am65_cpsw_pdata am64x_cpswxg_pdata = { + .quirks = AM64_CPSW_QUIRK_CUT_THRU, + .ale_dev_id = "am64-cpswxg", + .fdqring_mode = K3_RINGACC_RING_MODE_RING, }; static const struct of_device_id am65_cpsw_nuss_of_mtable[] = { { .compatible = "ti,am654-cpsw-nuss", .data = &am65x_sr1_0}, { .compatible = "ti,j721e-cpsw-nuss", .data = &j721e_pdata}, + { .compatible = "ti,am642-cpsw-nuss", .data = &am64x_cpswxg_pdata}, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, am65_cpsw_nuss_of_mtable); @@ -2040,6 +2736,7 @@ struct device_node *node; struct resource *res; struct clk *clk; + u64 id_temp; int ret, i; common = devm_kzalloc(dev, sizeof(struct am65_cpsw_common), GFP_KERNEL); @@ -2059,6 +2756,9 @@ if (IS_ERR(common->ss_base)) return PTR_ERR(common->ss_base); common->cpsw_base = common->ss_base + AM65_CPSW_CPSW_NU_BASE; + /* Use device's physical base address as switch id */ + id_temp = cpu_to_be64(res->start); + memcpy(common->switch_id, &id_temp, sizeof(res->start)); node = of_get_child_by_name(dev->of_node, "ethernet-ports"); if (!node) @@ -2068,19 +2768,11 @@ return -ENOENT; of_node_put(node); - if (common->port_num != 1) - return -EOPNOTSUPP; - common->rx_flow_id_base = -1; init_completion(&common->tdown_complete); common->tx_ch_num = 1; common->pf_p0_rx_ptype_rrobin = false; - - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(48)); - if (ret) { - dev_err(dev, "error setting dma mask: %d\n", ret); - return ret; - } + common->default_vlan = 1; common->ports = devm_kcalloc(dev, common->port_num, sizeof(*common->ports), @@ -2089,13 +2781,8 @@ return -ENOMEM; clk = devm_clk_get(dev, "fck"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - - if (ret != -EPROBE_DEFER) - dev_err(dev, "error getting fck clock %d\n", ret); - return ret; - } + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "getting fck clock\n"); common->bus_freq = clk_get_rate(clk); pm_runtime_enable(dev); @@ -2145,7 +2832,7 @@ ale_params.ale_ageout = AM65_CPSW_ALE_AGEOUT_DEFAULT; ale_params.ale_ports = common->port_num + 1; ale_params.ale_regs = common->cpsw_base + AM65_CPSW_NU_ALE_BASE; - ale_params.dev_id = "am65x-cpsw2g"; + ale_params.dev_id = common->pdata.ale_dev_id; ale_params.bus_freq = common->bus_freq; common->ale = cpsw_ale_create(&ale_params); @@ -2165,14 +2852,22 @@ dev_set_drvdata(dev, common); - ret = am65_cpsw_nuss_init_ndev_2g(common); + common->is_emac_mode = true; + + ret = am65_cpsw_nuss_init_ndevs(common); if (ret) goto err_of_clear; - ret = am65_cpsw_nuss_ndev_reg_2g(common); + ret = am65_cpsw_nuss_register_debugfs(common); if (ret) goto err_of_clear; + ret = am65_cpsw_nuss_register_ndevs(common); + if (ret) { + am65_cpsw_nuss_unregister_debugfs(common); + goto err_of_clear; + } + pm_runtime_put(dev); return 0; @@ -2198,6 +2893,9 @@ return ret; } + am65_cpsw_unregister_devlink(common); + am65_cpsw_unregister_notifiers(common); + /* must unregister ndevs here because DD release_driver routine calls * dma_deconfigure(dev) before devres_release_all(dev) */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h 2022-01-06 12:45:53.822318139 -0500 @@ -6,11 +6,15 @@ #ifndef AM65_CPSW_NUSS_H_ #define AM65_CPSW_NUSS_H_ +#include +#include #include #include #include #include #include +#include +#include #include "am65-cpsw-qos.h" struct am65_cpts; @@ -21,6 +25,8 @@ #define AM65_CPSW_MAX_RX_QUEUES 1 #define AM65_CPSW_MAX_RX_FLOWS 1 +#define AM65_CPSW_PORT_VLAN_REG_OFFSET 0x014 + struct am65_cpsw_slave_data { bool mac_only; struct cpsw_sl *mac_sl; @@ -31,6 +37,7 @@ bool rx_pause; bool tx_pause; u8 mac_addr[ETH_ALEN]; + int port_vlan; }; struct am65_cpsw_port { @@ -46,6 +53,8 @@ bool tx_ts_enabled; bool rx_ts_enabled; struct am65_cpsw_qos qos; + struct devlink_port devlink_port; + struct dentry *debugfs_port; }; struct am65_cpsw_host { @@ -55,18 +64,24 @@ }; struct am65_cpsw_tx_chn { + struct device *dma_dev; struct napi_struct napi_tx; struct am65_cpsw_common *common; struct k3_cppi_desc_pool *desc_pool; struct k3_udma_glue_tx_channel *tx_chn; + spinlock_t lock; /* protect TX rings in multi-port mode */ + struct hrtimer tx_hrtimer; + unsigned long tx_pace_timeout; int irq; u32 id; u32 descs_num; char tx_chn_name[128]; + u32 rate_mbps; }; struct am65_cpsw_rx_chn { struct device *dev; + struct device *dma_dev; struct k3_cppi_desc_pool *desc_pool; struct k3_udma_glue_rx_channel *rx_chn; u32 descs_num; @@ -74,9 +89,21 @@ }; #define AM65_CPSW_QUIRK_I2027_NO_TX_CSUM BIT(0) +#define AM64_CPSW_QUIRK_CUT_THRU BIT(1) struct am65_cpsw_pdata { u32 quirks; + enum k3_ring_mode fdqring_mode; + const char *ale_dev_id; +}; + +enum cpsw_devlink_param_id { + AM65_CPSW_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, + AM65_CPSW_DL_PARAM_SWITCH_MODE, +}; + +struct am65_cpsw_devlink { + struct am65_cpsw_common *common; }; struct am65_cpsw_common { @@ -91,10 +118,12 @@ struct am65_cpsw_host host; struct am65_cpsw_port *ports; u32 disabled_ports_mask; + struct net_device *dma_ndev; int usage_count; /* number of opened ports */ struct cpsw_ale *ale; int tx_ch_num; + u32 tx_ch_rate_msk; u32 rx_flow_id_base; struct am65_cpsw_tx_chn tx_chns[AM65_CPSW_MAX_TX_QUEUES]; @@ -103,6 +132,9 @@ struct am65_cpsw_rx_chn rx_chns; struct napi_struct napi_rx; + bool rx_irq_disabled; + struct hrtimer rx_hrtimer; + unsigned long rx_pace_timeout; u32 nuss_ver; u32 cpsw_ver; @@ -110,6 +142,18 @@ bool pf_p0_rx_ptype_rrobin; struct am65_cpts *cpts; int est_enabled; + int iet_enabled; + unsigned int cut_thru_enabled; + + bool is_emac_mode; + u16 br_members; + int default_vlan; + struct devlink *devlink; + struct net_device *hw_bridge_dev; + struct notifier_block am65_cpsw_netdevice_nb; + unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; + + struct dentry *debugfs_root; }; struct am65_cpsw_ndev_stats { @@ -124,6 +168,7 @@ u32 msg_enable; struct am65_cpsw_port *port; struct am65_cpsw_ndev_stats __percpu *stats; + bool offload_fwd_mark; }; #define am65_ndev_to_priv(ndev) \ @@ -151,4 +196,10 @@ void am65_cpsw_nuss_remove_tx_chns(struct am65_cpsw_common *common); int am65_cpsw_nuss_update_tx_chns(struct am65_cpsw_common *common, int num_tx); +bool am65_cpsw_port_dev_check(const struct net_device *dev); + +int am65_cpsw_nuss_register_port_debugfs(struct am65_cpsw_port *port); +int am65_cpsw_nuss_register_debugfs(struct am65_cpsw_common *common); +void am65_cpsw_nuss_unregister_debugfs(struct am65_cpsw_common *common); + #endif /* AM65_CPSW_NUSS_H_ */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-qos.c b/drivers/net/ethernet/ti/am65-cpsw-qos.c --- a/drivers/net/ethernet/ti/am65-cpsw-qos.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-qos.c 2022-01-06 12:45:53.822318139 -0500 @@ -4,8 +4,10 @@ * * quality of service module includes: * Enhanced Scheduler Traffic (EST - P802.1Qbv/D2.2) + * Interspersed Express Traffic (IET - P802.3br/D2.0) */ +#include #include #include @@ -14,14 +16,29 @@ #include "am65-cpts.h" #define AM65_CPSW_REG_CTL 0x004 +#define AM65_CPSW_REG_FREQ 0x05c #define AM65_CPSW_PN_REG_CTL 0x004 +#define AM65_CPSW_PN_REG_MAX_BLKS 0x008 +#define AM65_CPSW_PN_REG_TX_PRI_MAP 0x018 +#define AM65_CPSW_PN_REG_RX_PRI_MAP 0x020 +#define AM65_CPSW_PN_REG_IET_CTRL 0x040 +#define AM65_CPSW_PN_REG_IET_STATUS 0x044 +#define AM65_CPSW_PN_REG_IET_VERIFY 0x048 #define AM65_CPSW_PN_REG_FIFO_STATUS 0x050 #define AM65_CPSW_PN_REG_EST_CTL 0x060 +#define AM65_CPSW_PN_REG_PRI_CIR(pri) (0x140 + 4 * (pri)) +#define AM65_CPSW_PN_REG_PRI_EIR(pri) (0x160 + 4 * (pri)) + +#define AM64_CPSW_PN_CUT_THRU 0x3C0 +#define AM64_CPSW_PN_SPEED 0x3C4 /* AM65_CPSW_REG_CTL register fields */ +#define AM65_CPSW_CTL_IET_EN BIT(17) #define AM65_CPSW_CTL_EST_EN BIT(18) +#define AM64_CPSW_CTL_CUT_THRU_EN BIT(19) /* AM65_CPSW_PN_REG_CTL register fields */ +#define AM65_CPSW_PN_CTL_IET_PORT_EN BIT(16) #define AM65_CPSW_PN_CTL_EST_PORT_EN BIT(17) /* AM65_CPSW_PN_REG_EST_CTL register fields */ @@ -32,6 +49,27 @@ #define AM65_CPSW_PN_EST_ONEPRI BIT(4) #define AM65_CPSW_PN_EST_TS_PRI_MSK GENMASK(7, 5) +/* AM65_CPSW_PN_REG_IET_CTRL register fields */ +#define AM65_CPSW_PN_IET_MAC_PENABLE BIT(0) +#define AM65_CPSW_PN_IET_MAC_DISABLEVERIFY BIT(2) +#define AM65_CPSW_PN_IET_MAC_LINKFAIL BIT(3) +#define AM65_CPSW_PN_IET_MAC_MAC_ADDFRAGSIZE_MASK GENMASK(10, 8) +#define AM65_CPSW_PN_IET_MAC_MAC_ADDFRAGSIZE_OFFSET 8 +#define AM65_CPSW_PN_IET_PREMPT_MASK GENMASK(23, 16) +#define AM65_CPSW_PN_IET_PREMPT_OFFSET 16 + +/* AM65_CPSW_PN_REG_IET_STATUS register fields */ +#define AM65_CPSW_PN_MAC_VERIFIED BIT(0) +#define AM65_CPSW_PN_MAC_VERIFY_FAIL BIT(1) +#define AM65_CPSW_PN_MAC_RESPOND_ERR BIT(2) +#define AM65_CPSW_PN_MAC_VERIFY_ERR BIT(3) + +/* AM65_CPSW_PN_REG_IET_VERIFY register fields */ +/* 10 msec converted to NSEC */ +#define AM65_CPSW_IET_VERIFY_CNT_MS (10) +#define AM65_CPSW_IET_VERIFY_CNT_NS (AM65_CPSW_IET_VERIFY_CNT_MS * \ + NSEC_PER_MSEC) + /* AM65_CPSW_PN_REG_FIFO_STATUS register fields */ #define AM65_CPSW_PN_FST_TX_PRI_ACTIVE_MSK GENMASK(7, 0) #define AM65_CPSW_PN_FST_TX_E_MAC_ALLOW_MSK GENMASK(15, 8) @@ -47,12 +85,258 @@ #define AM65_CPSW_FETCH_ALLOW_MSK GENMASK(7, 0) #define AM65_CPSW_FETCH_ALLOW_MAX AM65_CPSW_FETCH_ALLOW_MSK +/* Cut-Thru AM64_CPSW_PN_CUT_THRU */ +#define AM64_PN_CUT_THRU_TX_PRI GENMASK(7, 0) +#define AM64_PN_CUT_THRU_RX_PRI GENMASK(15, 8) + +/* Cut-Thru AM64_CPSW_PN_SPEED */ +#define AM64_PN_SPEED_VAL GENMASK(3, 0) +#define AM64_PN_SPEED_AUTO_EN BIT(8) +#define AM64_PN_AUTO_SPEED GENMASK(15, 12) + +/* AM65_CPSW_PN_REG_MAX_BLKS fields for IET and No IET cases */ +/* 7 blocks for pn_rx_max_blks, 13 for pn_tx_max_blks*/ +#define AM65_CPSW_PN_TX_RX_MAX_BLKS_IET 0xD07 +#define AM65_CPSW_PN_TX_RX_MAX_BLKS_DEFAULT 0x1004 + enum timer_act { TACT_PROG, /* need program timer */ TACT_NEED_STOP, /* need stop first */ TACT_SKIP_PROG, /* just buffer can be updated */ }; +/* number of traffic classes (fifos) per port */ +#define AM65_CPSW_PN_TC_NUM 8 +#define AM65_CPSW_PN_TX_PRI_MAP_DEF 0x76543210 + +static int am65_cpsw_mqprio_setup(struct net_device *ndev, void *type_data); + +/* Fetch command count it's number of bytes in Gigabit mode or nibbles in + * 10/100Mb mode. So, having speed and time in ns, recalculate ns to number of + * bytes/nibbles that can be sent while transmission on given speed. + */ +static int am65_est_cmd_ns_to_cnt(u64 ns, int link_speed) +{ + u64 temp; + + temp = ns * link_speed; + if (link_speed < SPEED_1000) + temp <<= 1; + + return DIV_ROUND_UP(temp, 8 * 1000); +} + +/* IET */ + +static void am65_cpsw_iet_enable(struct am65_cpsw_common *common) +{ + int common_enable = 0; + u32 val; + int i; + + for (i = 0; i < common->port_num; i++) + common_enable |= !!common->ports[i].qos.iet.mask; + + val = readl(common->cpsw_base + AM65_CPSW_REG_CTL); + + if (common_enable) + val |= AM65_CPSW_CTL_IET_EN; + else + val &= ~AM65_CPSW_CTL_IET_EN; + + writel(val, common->cpsw_base + AM65_CPSW_REG_CTL); + common->iet_enabled = common_enable; +} + +static void am65_cpsw_port_iet_enable(struct am65_cpsw_port *port, + u32 mask) +{ + u32 val; + + val = readl(port->port_base + AM65_CPSW_PN_REG_CTL); + if (mask) + val |= AM65_CPSW_PN_CTL_IET_PORT_EN; + else + val &= ~AM65_CPSW_PN_CTL_IET_PORT_EN; + + writel(val, port->port_base + AM65_CPSW_PN_REG_CTL); + port->qos.iet.mask = mask; +} + +static int am65_cpsw_iet_verify(struct am65_cpsw_port *port) +{ + int try; + u32 val; + + netdev_info(port->ndev, "Starting IET/FPE MAC Verify\n"); + /* Set verify timeout depending on link speed. It is 10 msec + * in wireside clocks + */ + val = am65_est_cmd_ns_to_cnt(AM65_CPSW_IET_VERIFY_CNT_NS, + port->qos.link_speed); + writel(val, port->port_base + AM65_CPSW_PN_REG_IET_VERIFY); + + /* By experiment, keep this about 20 * 50 msec = 1000 msec. + * Usually succeeds in one try. But at times it takes more + * attempts especially at initial boot. Try for 20 times + * before give up + */ + try = 20; + do { + /* Enable IET Preemption for the port and + * reset LINKFAIL bit to start Verify. + */ + writel(AM65_CPSW_PN_IET_MAC_PENABLE, + port->port_base + AM65_CPSW_PN_REG_IET_CTRL); + + /* Takes 10 msec to complete this in h/w assuming other + * side is already ready. However since both side might + * take variable setup/config time, need to Wait for + * additional time. Chose 50 msec through trials + */ + msleep(50); + + val = readl(port->port_base + AM65_CPSW_PN_REG_IET_STATUS); + if (val & AM65_CPSW_PN_MAC_VERIFIED) + break; + + if (val & AM65_CPSW_PN_MAC_VERIFY_FAIL) { + netdev_dbg(port->ndev, + "IET MAC verify failed, trying again"); + /* Reset the verify state machine by writing 1 + * to LINKFAIL + */ + writel(AM65_CPSW_PN_IET_MAC_LINKFAIL, + port->port_base + AM65_CPSW_PN_REG_IET_CTRL); + } + + if (val & AM65_CPSW_PN_MAC_RESPOND_ERR) { + netdev_err(port->ndev, "IET MAC respond error"); + return -ENODEV; + } + + if (val & AM65_CPSW_PN_MAC_VERIFY_ERR) { + netdev_err(port->ndev, "IET MAC verify error"); + return -ENODEV; + } + + } while (try-- > 0); + + if (try <= 0) { + netdev_err(port->ndev, "IET MAC Verify/Response timeout"); + return -ENODEV; + } + + netdev_info(port->ndev, "IET/FPE MAC Verify Success\n"); + return 0; +} + +static void am65_cpsw_iet_config_mac_preempt(struct am65_cpsw_port *port, + bool enable, bool force) +{ + struct am65_cpsw_iet *iet = &port->qos.iet; + u32 val; + + /* Enable pre-emption queues and force mode if no mac verify */ + val = 0; + if (enable) { + if (!force) { + /* AM65_CPSW_PN_IET_MAC_PENABLE already + * set as part of MAC Verify. So read + * modify + */ + val = readl(port->port_base + + AM65_CPSW_PN_REG_IET_CTRL); + } else { + val |= AM65_CPSW_PN_IET_MAC_PENABLE; + val |= AM65_CPSW_PN_IET_MAC_DISABLEVERIFY; + } + val |= ((iet->fpe_mask_configured << + AM65_CPSW_PN_IET_PREMPT_OFFSET) & + AM65_CPSW_PN_IET_PREMPT_MASK); + val |= ((iet->addfragsize << + AM65_CPSW_PN_IET_MAC_MAC_ADDFRAGSIZE_OFFSET) & + AM65_CPSW_PN_IET_MAC_MAC_ADDFRAGSIZE_MASK); + } + writel(val, port->port_base + AM65_CPSW_PN_REG_IET_CTRL); + iet->fpe_enabled = enable; +} + +static void am65_cpsw_iet_set(struct net_device *ndev) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_common *common = port->common; + struct am65_cpsw_iet *iet = &port->qos.iet; + + /* For IET, Change MAX_BLKS */ + writel(AM65_CPSW_PN_TX_RX_MAX_BLKS_IET, + port->port_base + AM65_CPSW_PN_REG_MAX_BLKS); + + am65_cpsw_port_iet_enable(port, iet->fpe_mask_configured); + am65_cpsw_iet_enable(common); +} + +static int am65_cpsw_iet_fpe_enable(struct am65_cpsw_port *port, bool verify) +{ + int ret; + + if (verify) { + ret = am65_cpsw_iet_verify(port); + if (ret) + return ret; + } + + am65_cpsw_iet_config_mac_preempt(port, true, !verify); + + return 0; +} + +void am65_cpsw_qos_iet_init(struct net_device *ndev) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_common *common = port->common; + struct am65_cpsw_iet *iet = &port->qos.iet; + + /* Enable IET FPE only if user has enabled priv flag for iet frame + * preemption. + */ + if (!iet->fpe_configured) { + iet->fpe_mask_configured = 0; + return; + } + /* Use highest priority queue as express queue and others + * as preemptible queues. + */ + iet->fpe_mask_configured = GENMASK(common->tx_ch_num - 2, 0); + + /* Init work queue for IET MAC verify process */ + iet->ndev = ndev; + + am65_cpsw_iet_set(ndev); +} + +static void am65_cpsw_iet_fpe_disable(struct am65_cpsw_port *port) +{ + struct am65_cpsw_iet *iet = &port->qos.iet; + + am65_cpsw_iet_config_mac_preempt(port, false, + !iet->mac_verify_configured); +} + +void am65_cpsw_qos_iet_cleanup(struct net_device *ndev) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_common *common = am65_ndev_to_common(ndev); + + /* restore MAX_BLKS to default */ + writel(AM65_CPSW_PN_TX_RX_MAX_BLKS_DEFAULT, + port->port_base + AM65_CPSW_PN_REG_MAX_BLKS); + + am65_cpsw_iet_fpe_disable(port); + am65_cpsw_port_iet_enable(port, 0); + am65_cpsw_iet_enable(common); +} + static int am65_cpsw_port_est_enabled(struct am65_cpsw_port *port) { return port->qos.est_oper || port->qos.est_admin; @@ -223,21 +507,6 @@ am65_cpsw_admin_to_oper(ndev); } -/* Fetch command count it's number of bytes in Gigabit mode or nibbles in - * 10/100Mb mode. So, having speed and time in ns, recalculate ns to number of - * bytes/nibbles that can be sent while transmission on given speed. - */ -static int am65_est_cmd_ns_to_cnt(u64 ns, int link_speed) -{ - u64 temp; - - temp = ns * link_speed; - if (link_speed < SPEED_1000) - temp <<= 1; - - return DIV_ROUND_UP(temp, 8 * 1000); -} - static void __iomem *am65_cpsw_est_set_sched_cmds(void __iomem *addr, int fetch_cnt, int fetch_allow) @@ -356,7 +625,7 @@ writel(~all_fetch_allow & AM65_CPSW_FETCH_ALLOW_MSK, ram_addr); } -/** +/* * Enable ESTf periodic output, set cycle start time and interval. */ static int am65_cpsw_timer_set(struct net_device *ndev, @@ -594,18 +863,46 @@ switch (type) { case TC_SETUP_QDISC_TAPRIO: return am65_cpsw_setup_taprio(ndev, type_data); + case TC_SETUP_QDISC_MQPRIO: + return am65_cpsw_mqprio_setup(ndev, type_data); default: return -EOPNOTSUPP; } } -void am65_cpsw_qos_link_up(struct net_device *ndev, int link_speed) +static void am65_cpsw_iet_link_up(struct net_device *ndev) { struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_iet *iet = &port->qos.iet; - if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_TAS)) + if (!iet->fpe_configured) return; + /* Schedule MAC Verify and enable IET FPE if configured */ + if (iet->mac_verify_configured) { + am65_cpsw_iet_fpe_enable(port, true); + } else { + /* Force IET FPE here */ + netdev_info(ndev, "IET Enable Force mode\n"); + am65_cpsw_iet_fpe_enable(port, false); + } +} + +static void am65_cpsw_cut_thru_link_up(struct am65_cpsw_port *port); +static void am65_cpsw_tx_pn_shaper_link_up(struct am65_cpsw_port *port); + +void am65_cpsw_qos_link_up(struct net_device *ndev, int link_speed, int duplex) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + + port->qos.link_speed = link_speed; + port->qos.duplex = duplex; + am65_cpsw_iet_link_up(ndev); + am65_cpsw_cut_thru_link_up(port); + am65_cpsw_tx_pn_shaper_link_up(port); + + if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_TAS)) + return; am65_cpsw_est_link_up(ndev, link_speed); port->qos.link_down_time = 0; } @@ -614,6 +911,8 @@ { struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + am65_cpsw_iet_fpe_disable(port); + if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_TAS)) return; @@ -622,3 +921,536 @@ port->qos.link_speed = SPEED_UNKNOWN; } + +static void am65_cpsw_cut_thru_dump(struct am65_cpsw_port *port) +{ + struct am65_cpsw_common *common = port->common; + u32 contro, cut_thru, speed; + + contro = readl(common->cpsw_base + AM65_CPSW_REG_CTL); + cut_thru = readl(port->port_base + AM64_CPSW_PN_CUT_THRU); + speed = readl(port->port_base + AM64_CPSW_PN_SPEED); + dev_dbg(common->dev, "Port%u: cut_thru dump control:%08x cut_thru:%08x hwspeed:%08x\n", + port->port_id, contro, cut_thru, speed); +} + +static void am65_cpsw_cut_thru_enable(struct am65_cpsw_common *common) +{ + u32 val; + + if (common->cut_thru_enabled) { + common->cut_thru_enabled++; + return; + } + + /* Populate CPSW VBUS freq for auto speed detection */ + writel(common->bus_freq / 1000000, + common->cpsw_base + AM65_CPSW_REG_FREQ); + + val = readl(common->cpsw_base + AM65_CPSW_REG_CTL); + val |= AM64_CPSW_CTL_CUT_THRU_EN; + writel(val, common->cpsw_base + AM65_CPSW_REG_CTL); + common->cut_thru_enabled++; +} + +void am65_cpsw_qos_cut_thru_init(struct am65_cpsw_port *port) +{ + struct am65_cpsw_cut_thru *cut_thru = &port->qos.cut_thru; + struct am65_cpsw_common *common = port->common; + + /* Enable cut_thr only if user has enabled priv flag */ + if (!cut_thru->enable) + return; + + if (common->is_emac_mode) { + cut_thru->enable = false; + dev_info(common->dev, "Disable cut-thru, need Switch mode\n"); + return; + } + + am65_cpsw_cut_thru_enable(common); + + /* en auto speed */ + writel(AM64_PN_SPEED_AUTO_EN, port->port_base + AM64_CPSW_PN_SPEED); + dev_info(common->dev, "Init cut_thru\n"); + am65_cpsw_cut_thru_dump(port); +} + +static void am65_cpsw_cut_thru_disable(struct am65_cpsw_common *common) +{ + u32 val; + + if (--common->cut_thru_enabled) + return; + + val = readl(common->cpsw_base + AM65_CPSW_REG_CTL); + val &= ~AM64_CPSW_CTL_CUT_THRU_EN; + writel(val, common->cpsw_base + AM65_CPSW_REG_CTL); +} + +void am65_cpsw_qos_cut_thru_cleanup(struct am65_cpsw_port *port) +{ + struct am65_cpsw_cut_thru *cut_thru = &port->qos.cut_thru; + struct am65_cpsw_common *common = port->common; + + if (!cut_thru->enable) + return; + + writel(0, port->port_base + AM64_CPSW_PN_CUT_THRU); + writel(0, port->port_base + AM64_CPSW_PN_SPEED); + + am65_cpsw_cut_thru_disable(common); + dev_info(common->dev, "Cleanup cut_thru\n"); + am65_cpsw_cut_thru_dump(port); +} + +static u32 am65_cpsw_cut_thru_speed2hw(int link_speed) +{ + switch (link_speed) { + case SPEED_10: + return 1; + case SPEED_100: + return 2; + case SPEED_1000: + return 3; + default: + return 0; + } +} + +static void am65_cpsw_cut_thru_link_up(struct am65_cpsw_port *port) +{ + struct am65_cpsw_cut_thru *cut_thru = &port->qos.cut_thru; + struct am65_cpsw_common *common = port->common; + u32 val, speed; + + if (!cut_thru->enable) + return; + + writel(AM64_PN_SPEED_AUTO_EN, port->port_base + AM64_CPSW_PN_SPEED); + /* barrier */ + readl(port->port_base + AM64_CPSW_PN_SPEED); + /* HW need 15us in 10/100 mode and 3us in 1G mode auto speed detection + * add delay with some margin + */ + usleep_range(40, 50); + val = readl(port->port_base + AM64_CPSW_PN_SPEED); + speed = FIELD_GET(AM64_PN_AUTO_SPEED, val); + if (!speed) { + dev_warn(common->dev, + "Port%u: cut_thru no speed auto detected switch to manual\n", + port->port_id); + speed = am65_cpsw_cut_thru_speed2hw(port->qos.link_speed); + if (!speed) { + dev_err(common->dev, + "Port%u: cut_thru speed configuration failed\n", + port->port_id); + return; + } + val = FIELD_PREP(AM64_PN_SPEED_VAL, speed); + writel(val, port->port_base + AM64_CPSW_PN_SPEED); + } + + val = FIELD_PREP(AM64_PN_CUT_THRU_TX_PRI, cut_thru->tx_pri_mask) | + FIELD_PREP(AM64_PN_CUT_THRU_RX_PRI, cut_thru->rx_pri_mask); + + if (port->qos.duplex) { + writel(val, port->port_base + AM64_CPSW_PN_CUT_THRU); + dev_info(common->dev, "Port%u: Enable cut_thru rx:%08x tx:%08x hwspeed:%u (%08x)\n", + port->port_id, + cut_thru->rx_pri_mask, cut_thru->tx_pri_mask, + speed, val); + } else { + writel(0, port->port_base + AM64_CPSW_PN_CUT_THRU); + dev_info(common->dev, "Port%u: Disable cut_thru duplex=%d\n", + port->port_id, port->qos.duplex); + } + am65_cpsw_cut_thru_dump(port); +} + +static u32 +am65_cpsw_qos_tx_rate_calc(u32 rate_mbps, unsigned long bus_freq) +{ + u32 ir; + + bus_freq /= 1000000; + ir = DIV_ROUND_UP(((u64)rate_mbps * 32768), bus_freq); + return ir; +} + +static void +am65_cpsw_qos_tx_p0_rate_apply(struct am65_cpsw_common *common, + int tx_ch, u32 rate_mbps) +{ + struct am65_cpsw_host *host = am65_common_get_host(common); + u32 ch_cir; + int i; + + ch_cir = am65_cpsw_qos_tx_rate_calc(rate_mbps, common->bus_freq); + writel(ch_cir, host->port_base + AM65_CPSW_PN_REG_PRI_CIR(tx_ch)); + + /* update rates for every port tx queues */ + for (i = 0; i < common->port_num; i++) { + struct net_device *ndev = common->ports[i].ndev; + + if (!ndev) + continue; + netdev_get_tx_queue(ndev, tx_ch)->tx_maxrate = rate_mbps; + } +} + +int am65_cpsw_qos_ndo_tx_p0_set_maxrate(struct net_device *ndev, + int queue, u32 rate_mbps) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct am65_cpsw_common *common = port->common; + struct am65_cpsw_tx_chn *tx_chn; + u32 ch_rate, tx_ch_rate_msk_new; + u32 ch_msk = 0; + int ret; + + dev_dbg(common->dev, "apply TX%d rate limiting %uMbps tx_rate_msk%x\n", + queue, rate_mbps, common->tx_ch_rate_msk); + + if (common->pf_p0_rx_ptype_rrobin) { + dev_err(common->dev, "TX Rate Limiting failed - rrobin mode\n"); + return -EINVAL; + } + + ch_rate = netdev_get_tx_queue(ndev, queue)->tx_maxrate; + if (ch_rate == rate_mbps) + return 0; + + ret = pm_runtime_get_sync(common->dev); + if (ret < 0) { + pm_runtime_put_noidle(common->dev); + return ret; + } + ret = 0; + + tx_ch_rate_msk_new = common->tx_ch_rate_msk; + if (rate_mbps && !(tx_ch_rate_msk_new & BIT(queue))) { + tx_ch_rate_msk_new |= BIT(queue); + ch_msk = GENMASK(common->tx_ch_num - 1, queue); + ch_msk = tx_ch_rate_msk_new ^ ch_msk; + } else if (!rate_mbps) { + tx_ch_rate_msk_new &= ~BIT(queue); + ch_msk = queue ? GENMASK(queue - 1, 0) : 0; + ch_msk = tx_ch_rate_msk_new & ch_msk; + } + + if (ch_msk) { + dev_err(common->dev, "TX rate limiting has to be enabled sequentially hi->lo tx_rate_msk:%x tx_rate_msk_new:%x\n", + common->tx_ch_rate_msk, tx_ch_rate_msk_new); + ret = -EINVAL; + goto exit_put; + } + + tx_chn = &common->tx_chns[queue]; + tx_chn->rate_mbps = rate_mbps; + common->tx_ch_rate_msk = tx_ch_rate_msk_new; + + if (!common->usage_count) + /* will be applied on next netif up */ + goto exit_put; + + am65_cpsw_qos_tx_p0_rate_apply(common, queue, rate_mbps); + +exit_put: + pm_runtime_put(common->dev); + return ret; +} + +void am65_cpsw_qos_tx_p0_rate_init(struct am65_cpsw_common *common) +{ + struct am65_cpsw_host *host = am65_common_get_host(common); + int tx_ch; + + for (tx_ch = 0; tx_ch < common->tx_ch_num; tx_ch++) { + struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[tx_ch]; + u32 ch_cir; + + if (!tx_chn->rate_mbps) + continue; + + ch_cir = am65_cpsw_qos_tx_rate_calc(tx_chn->rate_mbps, + common->bus_freq); + writel(ch_cir, + host->port_base + AM65_CPSW_PN_REG_PRI_CIR(tx_ch)); + } +} + +static void am65_cpsw_tx_pn_shaper_apply(struct am65_cpsw_port *port) +{ + struct am65_cpsw_mqprio *p_mqprio = &port->qos.mqprio; + struct am65_cpsw_common *common = port->common; + struct tc_mqprio_qopt_offload *mqprio; + bool shaper_en; + u32 rate_mbps; + int i; + + mqprio = &p_mqprio->mqprio_hw; + shaper_en = p_mqprio->shaper_en && !p_mqprio->shaper_susp; + + for (i = 0; i < mqprio->qopt.num_tc; i++) { + rate_mbps = 0; + if (shaper_en) { + rate_mbps = mqprio->min_rate[i] * 8 / 1000000; + rate_mbps = am65_cpsw_qos_tx_rate_calc(rate_mbps, + common->bus_freq); + } + + writel(rate_mbps, + port->port_base + AM65_CPSW_PN_REG_PRI_CIR(i)); + } + + for (i = 0; i < mqprio->qopt.num_tc; i++) { + rate_mbps = 0; + if (shaper_en && mqprio->max_rate[i]) { + rate_mbps = mqprio->max_rate[i] - mqprio->min_rate[i]; + rate_mbps = rate_mbps * 8 / 1000000; + rate_mbps = am65_cpsw_qos_tx_rate_calc(rate_mbps, + common->bus_freq); + } + + writel(rate_mbps, + port->port_base + AM65_CPSW_PN_REG_PRI_EIR(i)); + } +} + +static void am65_cpsw_tx_pn_shaper_link_up(struct am65_cpsw_port *port) +{ + struct am65_cpsw_mqprio *p_mqprio = &port->qos.mqprio; + struct am65_cpsw_common *common = port->common; + bool shaper_susp = false; + + if (!p_mqprio->enable || !p_mqprio->shaper_en) + return; + + if (p_mqprio->max_rate_total > port->qos.link_speed) + shaper_susp = true; + + if (p_mqprio->shaper_susp == shaper_susp) + return; + + if (shaper_susp) + dev_info(common->dev, + "Port%u: total shaper tx rate > link speed - suspend shaper\n", + port->port_id); + else + dev_info(common->dev, + "Port%u: link recover - resume shaper\n", + port->port_id); + + p_mqprio->shaper_susp = shaper_susp; + am65_cpsw_tx_pn_shaper_apply(port); +} + +void am65_cpsw_qos_mqprio_init(struct am65_cpsw_port *port) +{ + struct am65_cpsw_host *host = am65_common_get_host(port->common); + struct am65_cpsw_mqprio *p_mqprio = &port->qos.mqprio; + struct tc_mqprio_qopt_offload *mqprio = &p_mqprio->mqprio_hw; + int i, fifo, rx_prio_map; + + rx_prio_map = readl(host->port_base + AM65_CPSW_PN_REG_RX_PRI_MAP); + + if (p_mqprio->enable) { + for (i = 0; i < AM65_CPSW_PN_TC_NUM; i++) { + fifo = mqprio->qopt.prio_tc_map[i]; + p_mqprio->tx_prio_map |= fifo << (4 * i); + } + + netdev_set_num_tc(port->ndev, mqprio->qopt.num_tc); + for (i = 0; i < mqprio->qopt.num_tc; i++) { + netdev_set_tc_queue(port->ndev, i, + mqprio->qopt.count[i], + mqprio->qopt.offset[i]); + if (!i) { + p_mqprio->tc0_q = mqprio->qopt.offset[i]; + rx_prio_map &= ~(0x7 << (4 * p_mqprio->tc0_q)); + } + } + } else { + /* restore default configuration */ + netdev_reset_tc(port->ndev); + p_mqprio->tx_prio_map = AM65_CPSW_PN_TX_PRI_MAP_DEF; + rx_prio_map |= p_mqprio->tc0_q << (4 * p_mqprio->tc0_q); + p_mqprio->tc0_q = 0; + } + + writel(p_mqprio->tx_prio_map, + port->port_base + AM65_CPSW_PN_REG_TX_PRI_MAP); + writel(rx_prio_map, + host->port_base + AM65_CPSW_PN_REG_RX_PRI_MAP); + + am65_cpsw_tx_pn_shaper_apply(port); +} + +static int am65_cpsw_mqprio_verify(struct am65_cpsw_port *port, + struct tc_mqprio_qopt_offload *mqprio) +{ + int i; + + for (i = 0; i < mqprio->qopt.num_tc; i++) { + unsigned int last = mqprio->qopt.offset[i] + + mqprio->qopt.count[i]; + + if (mqprio->qopt.offset[i] >= port->ndev->real_num_tx_queues || + !mqprio->qopt.count[i] || + last > port->ndev->real_num_tx_queues) + return -EINVAL; + } + + return 0; +} + +static int am65_cpsw_mqprio_verify_shaper(struct am65_cpsw_port *port, + struct tc_mqprio_qopt_offload *mqprio, + u64 *max_rate) +{ + struct am65_cpsw_common *common = port->common; + bool has_min_rate, has_max_rate; + u64 min_rate_total = 0, max_rate_total = 0; + u32 min_rate_msk = 0, max_rate_msk = 0; + int num_tc, i; + + has_min_rate = !!(mqprio->flags & TC_MQPRIO_F_MIN_RATE); + has_max_rate = !!(mqprio->flags & TC_MQPRIO_F_MAX_RATE); + + if (!has_min_rate && has_max_rate) + return -EOPNOTSUPP; + + if (!has_min_rate) + return 0; + + num_tc = mqprio->qopt.num_tc; + + for (i = num_tc - 1; i >= 0; i--) { + u32 ch_msk; + + if (mqprio->min_rate[i]) + min_rate_msk |= BIT(i); + min_rate_total += mqprio->min_rate[i]; + + if (has_max_rate) { + if (mqprio->max_rate[i]) + max_rate_msk |= BIT(i); + max_rate_total += mqprio->max_rate[i]; + + if (!mqprio->min_rate[i] && mqprio->max_rate[i]) { + dev_err(common->dev, "TX tc%d rate max>0 but min=0\n", + i); + return -EINVAL; + } + + if (mqprio->max_rate[i] && + mqprio->max_rate[i] < mqprio->min_rate[i]) { + dev_err(common->dev, "TX tc%d rate min(%llu)>max(%llu)\n", + i, mqprio->min_rate[i], + mqprio->max_rate[i]); + return -EINVAL; + } + } + + ch_msk = GENMASK(num_tc - 1, i); + if ((min_rate_msk & BIT(i)) && (min_rate_msk ^ ch_msk)) { + dev_err(common->dev, "TX Min rate limiting has to be enabled sequentially hi->lo tx_rate_msk%x\n", + min_rate_msk); + return -EINVAL; + } + + if ((max_rate_msk & BIT(i)) && (max_rate_msk ^ ch_msk)) { + dev_err(common->dev, "TX max rate limiting has to be enabled sequentially hi->lo tx_rate_msk%x\n", + max_rate_msk); + return -EINVAL; + } + } + min_rate_total *= 8; + min_rate_total /= 1000 * 1000; + max_rate_total *= 8; + max_rate_total /= 1000 * 1000; + + if (port->qos.link_speed != SPEED_UNKNOWN) { + if (min_rate_total > port->qos.link_speed) { + dev_err(common->dev, "TX rate min exceed %llu link speed %d\n", + min_rate_total, port->qos.link_speed); + return -EINVAL; + } + + if (max_rate_total > port->qos.link_speed) { + dev_err(common->dev, "TX rate max exceed %llu link speed %d\n", + max_rate_total, port->qos.link_speed); + return -EINVAL; + } + } + + *max_rate = max_t(u64, min_rate_total, max_rate_total); + + return 0; +} + +static int am65_cpsw_mqprio_setup(struct net_device *ndev, void *type_data) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct tc_mqprio_qopt_offload *mqprio = type_data; + struct am65_cpsw_common *common = port->common; + struct am65_cpsw_mqprio *p_mqprio = &port->qos.mqprio; + bool has_min_rate; + int num_tc, ret; + u64 max_rate; + + if (!mqprio->qopt.hw) + goto skip_check; + + if (mqprio->mode != TC_MQPRIO_MODE_CHANNEL) + return -EOPNOTSUPP; + + num_tc = mqprio->qopt.num_tc; + if (num_tc > AM65_CPSW_PN_TC_NUM) + return -ERANGE; + + if ((mqprio->flags & TC_MQPRIO_F_SHAPER) && + mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -EOPNOTSUPP; + + ret = am65_cpsw_mqprio_verify(port, mqprio); + if (ret) + return ret; + + ret = am65_cpsw_mqprio_verify_shaper(port, mqprio, &max_rate); + if (ret) + return ret; + +skip_check: + ret = pm_runtime_get_sync(common->dev); + if (ret < 0) { + pm_runtime_put_noidle(common->dev); + return ret; + } + + if (mqprio->qopt.hw) { + memcpy(&p_mqprio->mqprio_hw, mqprio, sizeof(*mqprio)); + has_min_rate = !!(mqprio->flags & TC_MQPRIO_F_MIN_RATE); + p_mqprio->enable = 1; + p_mqprio->shaper_en = has_min_rate; + p_mqprio->shaper_susp = !has_min_rate; + p_mqprio->max_rate_total = max_rate; + p_mqprio->tx_prio_map = 0; + } else { + unsigned int tc0_q = p_mqprio->tc0_q; + + memset(p_mqprio, 0, sizeof(*p_mqprio)); + p_mqprio->mqprio_hw.qopt.num_tc = AM65_CPSW_PN_TC_NUM; + p_mqprio->tc0_q = tc0_q; + } + + if (!netif_running(ndev)) + goto exit_put; + + am65_cpsw_qos_mqprio_init(port); + +exit_put: + pm_runtime_put(common->dev); + return 0; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-qos.h b/drivers/net/ethernet/ti/am65-cpsw-qos.h --- a/drivers/net/ethernet/ti/am65-cpsw-qos.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-qos.h 2022-01-06 12:45:53.822318139 -0500 @@ -7,6 +7,10 @@ #include #include +#include + +struct am65_cpsw_port; +struct am65_cpsw_common; struct am65_cpsw_est { int buf; @@ -14,16 +18,59 @@ struct tc_taprio_qopt_offload taprio; }; +struct am65_cpsw_iet { + struct net_device *ndev; + /* Set through priv flags */ + bool fpe_configured; + bool mac_verify_configured; + /* frame preemption enabled */ + bool fpe_enabled; + /* configured mask */ + u32 fpe_mask_configured; + /* current mask */ + u32 mask; + u32 addfragsize; +}; + +struct am65_cpsw_mqprio { + struct tc_mqprio_qopt_offload mqprio_hw; + u64 max_rate_total; + u32 tx_prio_map; + + unsigned enable:1; + unsigned shaper_en:1; + unsigned shaper_susp:1; + unsigned tc0_q:3; +}; + +struct am65_cpsw_cut_thru { + unsigned int rx_pri_mask; + unsigned int tx_pri_mask; + bool enable; +}; + struct am65_cpsw_qos { struct am65_cpsw_est *est_admin; struct am65_cpsw_est *est_oper; ktime_t link_down_time; int link_speed; + int duplex; + struct am65_cpsw_iet iet; + struct am65_cpsw_mqprio mqprio; + struct am65_cpsw_cut_thru cut_thru; }; int am65_cpsw_qos_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data); -void am65_cpsw_qos_link_up(struct net_device *ndev, int link_speed); +void am65_cpsw_qos_link_up(struct net_device *ndev, int link_speed, int duplex); void am65_cpsw_qos_link_down(struct net_device *ndev); +void am65_cpsw_qos_iet_init(struct net_device *ndev); +void am65_cpsw_qos_iet_cleanup(struct net_device *ndev); +void am65_cpsw_qos_cut_thru_init(struct am65_cpsw_port *port); +void am65_cpsw_qos_cut_thru_cleanup(struct am65_cpsw_port *port); +int am65_cpsw_qos_ndo_tx_p0_set_maxrate(struct net_device *ndev, + int queue, u32 rate_mbps); +void am65_cpsw_qos_tx_p0_rate_init(struct am65_cpsw_common *common); +void am65_cpsw_qos_mqprio_init(struct am65_cpsw_port *port); #endif /* AM65_CPSW_QOS_H_ */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c --- a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,552 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments K3 AM65 Ethernet Switchdev Driver + * + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include +#include +#include + +#include "am65-cpsw-nuss.h" +#include "am65-cpsw-switchdev.h" +#include "cpsw_ale.h" + +struct am65_cpsw_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct am65_cpsw_port *port; + unsigned long event; +}; + +static int am65_cpsw_port_stp_state_set(struct am65_cpsw_port *port, + struct switchdev_trans *trans, u8 state) +{ + struct am65_cpsw_common *cpsw = port->common; + u8 cpsw_state; + int ret = 0; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + switch (state) { + case BR_STATE_FORWARDING: + cpsw_state = ALE_PORT_STATE_FORWARD; + break; + case BR_STATE_LEARNING: + cpsw_state = ALE_PORT_STATE_LEARN; + break; + case BR_STATE_DISABLED: + cpsw_state = ALE_PORT_STATE_DISABLE; + break; + case BR_STATE_LISTENING: + case BR_STATE_BLOCKING: + cpsw_state = ALE_PORT_STATE_BLOCK; + break; + default: + return -EOPNOTSUPP; + } + + ret = cpsw_ale_control_set(cpsw->ale, port->port_id, + ALE_PORT_STATE, cpsw_state); + netdev_dbg(port->ndev, "ale state: %u\n", cpsw_state); + + return ret; +} + +static int am65_cpsw_port_attr_br_flags_set(struct am65_cpsw_port *port, + struct switchdev_trans *trans, + struct net_device *orig_dev, + unsigned long brport_flags) +{ + struct am65_cpsw_common *cpsw = port->common; + bool unreg_mcast_add = false; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + if (brport_flags & BR_MCAST_FLOOD) + unreg_mcast_add = true; + netdev_dbg(port->ndev, "BR_MCAST_FLOOD: %d port %u\n", + unreg_mcast_add, port->port_id); + + cpsw_ale_set_unreg_mcast(cpsw->ale, BIT(port->port_id), + unreg_mcast_add); + + return 0; +} + +static int am65_cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, + struct switchdev_trans *trans, + unsigned long flags) +{ + if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD)) + return -EINVAL; + + return 0; +} + +static int am65_cpsw_port_attr_set(struct net_device *ndev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + int ret; + + netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, port->port_id); + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + ret = am65_cpsw_port_attr_br_flags_pre_set(ndev, trans, + attr->u.brport_flags); + break; + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + ret = am65_cpsw_port_stp_state_set(port, trans, attr->u.stp_state); + netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + ret = am65_cpsw_port_attr_br_flags_set(port, trans, attr->orig_dev, + attr->u.brport_flags); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static u16 am65_cpsw_get_pvid(struct am65_cpsw_port *port) +{ + struct am65_cpsw_common *cpsw = port->common; + struct am65_cpsw_host *host_p = am65_common_get_host(cpsw); + u32 pvid; + + if (port->port_id) + pvid = readl(port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + else + pvid = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + + pvid = pvid & 0xfff; + + return pvid; +} + +static void am65_cpsw_set_pvid(struct am65_cpsw_port *port, u16 vid, bool cfi, u32 cos) +{ + struct am65_cpsw_common *cpsw = port->common; + struct am65_cpsw_host *host_p = am65_common_get_host(cpsw); + u32 pvid; + + pvid = vid; + pvid |= cfi ? BIT(12) : 0; + pvid |= (cos & 0x7) << 13; + + if (port->port_id) + writel(pvid, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + else + writel(pvid, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); +} + +static int am65_cpsw_port_vlan_add(struct am65_cpsw_port *port, bool untag, bool pvid, + u16 vid, struct net_device *orig_dev) +{ + bool cpu_port = netif_is_bridge_master(orig_dev); + struct am65_cpsw_common *cpsw = port->common; + int unreg_mcast_mask = 0; + int reg_mcast_mask = 0; + int untag_mask = 0; + int port_mask; + int ret = 0; + u32 flags; + + if (cpu_port) { + port_mask = BIT(HOST_PORT_NUM); + flags = orig_dev->flags; + unreg_mcast_mask = port_mask; + } else { + port_mask = BIT(port->port_id); + flags = port->ndev->flags; + } + + if (flags & IFF_MULTICAST) + reg_mcast_mask = port_mask; + + if (untag) + untag_mask = port_mask; + + ret = cpsw_ale_vlan_add_modify(cpsw->ale, vid, port_mask, untag_mask, + reg_mcast_mask, unreg_mcast_mask); + if (ret) { + netdev_err(port->ndev, "Unable to add vlan\n"); + return ret; + } + + if (cpu_port) + cpsw_ale_add_ucast(cpsw->ale, port->slave.mac_addr, + HOST_PORT_NUM, ALE_VLAN | ALE_SECURE, vid); + if (!pvid) + return ret; + + am65_cpsw_set_pvid(port, vid, 0, 0); + + netdev_dbg(port->ndev, "VID add: %s: vid:%u ports:%X\n", + port->ndev->name, vid, port_mask); + + return ret; +} + +static int am65_cpsw_port_vlan_del(struct am65_cpsw_port *port, u16 vid, + struct net_device *orig_dev) +{ + bool cpu_port = netif_is_bridge_master(orig_dev); + struct am65_cpsw_common *cpsw = port->common; + int port_mask; + int ret = 0; + + if (cpu_port) + port_mask = BIT(HOST_PORT_NUM); + else + port_mask = BIT(port->port_id); + + ret = cpsw_ale_del_vlan(cpsw->ale, vid, port_mask); + if (ret != 0) + return ret; + + /* We don't care for the return value here, error is returned only if + * the unicast entry is not present + */ + if (cpu_port) + cpsw_ale_del_ucast(cpsw->ale, port->slave.mac_addr, + HOST_PORT_NUM, ALE_VLAN, vid); + + if (vid == am65_cpsw_get_pvid(port)) + am65_cpsw_set_pvid(port, 0, 0, 0); + + /* We don't care for the return value here, error is returned only if + * the multicast entry is not present + */ + cpsw_ale_del_mcast(cpsw->ale, port->ndev->broadcast, port_mask, + ALE_VLAN, vid); + netdev_dbg(port->ndev, "VID del: %s: vid:%u ports:%X\n", + port->ndev->name, vid, port_mask); + + return ret; +} + +static int am65_cpsw_port_vlans_add(struct am65_cpsw_port *port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + struct net_device *orig_dev = vlan->obj.orig_dev; + bool cpu_port = netif_is_bridge_master(orig_dev); + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + + netdev_dbg(port->ndev, "VID add: %s: vid:%u flags:%X\n", + port->ndev->name, vlan->vid_begin, vlan->flags); + + if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY)) + return 0; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return am65_cpsw_port_vlan_add(port, untag, pvid, vlan->vid_begin, orig_dev); +} + +static int am65_cpsw_port_vlans_del(struct am65_cpsw_port *port, + const struct switchdev_obj_port_vlan *vlan) + +{ + return am65_cpsw_port_vlan_del(port, vlan->vid_begin, vlan->obj.orig_dev); +} + +static int am65_cpsw_port_mdb_add(struct am65_cpsw_port *port, + struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) + +{ + struct net_device *orig_dev = mdb->obj.orig_dev; + bool cpu_port = netif_is_bridge_master(orig_dev); + struct am65_cpsw_common *cpsw = port->common; + int port_mask; + int err; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + if (cpu_port) + port_mask = BIT(HOST_PORT_NUM); + else + port_mask = BIT(port->port_id); + + err = cpsw_ale_add_mcast(cpsw->ale, mdb->addr, port_mask, + ALE_VLAN, mdb->vid, 0); + netdev_dbg(port->ndev, "MDB add: %s: vid %u:%pM ports: %X\n", + port->ndev->name, mdb->vid, mdb->addr, port_mask); + + return err; +} + +static int am65_cpsw_port_mdb_del(struct am65_cpsw_port *port, + struct switchdev_obj_port_mdb *mdb) + +{ + struct net_device *orig_dev = mdb->obj.orig_dev; + bool cpu_port = netif_is_bridge_master(orig_dev); + struct am65_cpsw_common *cpsw = port->common; + int del_mask; + + if (cpu_port) + del_mask = BIT(HOST_PORT_NUM); + else + del_mask = BIT(port->port_id); + + /* Ignore error as error code is returned only when entry is already removed */ + cpsw_ale_del_mcast(cpsw->ale, mdb->addr, del_mask, + ALE_VLAN, mdb->vid); + netdev_dbg(port->ndev, "MDB del: %s: vid %u:%pM ports: %X\n", + port->ndev->name, mdb->vid, mdb->addr, del_mask); + + return 0; +} + +static int am65_cpsw_port_obj_add(struct net_device *ndev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans, + struct netlink_ext_ack *extack) +{ + struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + int err = 0; + + netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, port->port_id); + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = am65_cpsw_port_vlans_add(port, vlan, trans); + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + case SWITCHDEV_OBJ_ID_HOST_MDB: + err = am65_cpsw_port_mdb_add(port, mdb, trans); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int am65_cpsw_port_obj_del(struct net_device *ndev, + const struct switchdev_obj *obj) +{ + struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + int err = 0; + + netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, port->port_id); + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = am65_cpsw_port_vlans_del(port, vlan); + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + case SWITCHDEV_OBJ_ID_HOST_MDB: + err = am65_cpsw_port_mdb_del(port, mdb); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static void am65_cpsw_fdb_offload_notify(struct net_device *ndev, + struct switchdev_notifier_fdb_info *rcv) +{ + struct switchdev_notifier_fdb_info info; + + info.addr = rcv->addr; + info.vid = rcv->vid; + info.offloaded = true; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, + ndev, &info.info, NULL); +} + +static void am65_cpsw_switchdev_event_work(struct work_struct *work) +{ + struct am65_cpsw_switchdev_event_work *switchdev_work = + container_of(work, struct am65_cpsw_switchdev_event_work, work); + struct am65_cpsw_port *port = switchdev_work->port; + struct switchdev_notifier_fdb_info *fdb; + struct am65_cpsw_common *cpsw = port->common; + int port_id = port->port_id; + + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb = &switchdev_work->fdb_info; + + netdev_dbg(port->ndev, "cpsw_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, + fdb->offloaded, port_id); + + if (!fdb->added_by_user) + break; + if (memcmp(port->slave.mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0) + port_id = HOST_PORT_NUM; + + cpsw_ale_add_ucast(cpsw->ale, (u8 *)fdb->addr, port_id, + fdb->vid ? ALE_VLAN : 0, fdb->vid); + am65_cpsw_fdb_offload_notify(port->ndev, fdb); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb = &switchdev_work->fdb_info; + + netdev_dbg(port->ndev, "cpsw_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, + fdb->offloaded, port_id); + + if (!fdb->added_by_user) + break; + if (memcmp(port->slave.mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0) + port_id = HOST_PORT_NUM; + + cpsw_ale_del_ucast(cpsw->ale, (u8 *)fdb->addr, port_id, + fdb->vid ? ALE_VLAN : 0, fdb->vid); + break; + default: + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(port->ndev); +} + +/* called under rcu_read_lock() */ +static int am65_cpsw_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); + struct am65_cpsw_switchdev_event_work *switchdev_work; + struct am65_cpsw_port *port = am65_ndev_to_port(ndev); + struct switchdev_notifier_fdb_info *fdb_info = ptr; + int err; + + if (event == SWITCHDEV_PORT_ATTR_SET) { + err = switchdev_handle_port_attr_set(ndev, ptr, + am65_cpsw_port_dev_check, + am65_cpsw_port_attr_set); + return notifier_from_errno(err); + } + + if (!am65_cpsw_port_dev_check(ndev)) + return NOTIFY_DONE; + + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, am65_cpsw_switchdev_event_work); + switchdev_work->port = port; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + if (!switchdev_work->fdb_info.addr) + goto err_addr_alloc; + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + dev_hold(ndev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(system_long_wq, &switchdev_work->work); + + return NOTIFY_DONE; + +err_addr_alloc: + kfree(switchdev_work); + return NOTIFY_BAD; +} + +static struct notifier_block cpsw_switchdev_notifier = { + .notifier_call = am65_cpsw_switchdev_event, +}; + +static int am65_cpsw_switchdev_blocking_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + int err; + + switch (event) { + case SWITCHDEV_PORT_OBJ_ADD: + err = switchdev_handle_port_obj_add(dev, ptr, + am65_cpsw_port_dev_check, + am65_cpsw_port_obj_add); + return notifier_from_errno(err); + case SWITCHDEV_PORT_OBJ_DEL: + err = switchdev_handle_port_obj_del(dev, ptr, + am65_cpsw_port_dev_check, + am65_cpsw_port_obj_del); + return notifier_from_errno(err); + case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(dev, ptr, + am65_cpsw_port_dev_check, + am65_cpsw_port_attr_set); + return notifier_from_errno(err); + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block cpsw_switchdev_bl_notifier = { + .notifier_call = am65_cpsw_switchdev_blocking_event, +}; + +int am65_cpsw_switchdev_register_notifiers(struct am65_cpsw_common *cpsw) +{ + int ret = 0; + + ret = register_switchdev_notifier(&cpsw_switchdev_notifier); + if (ret) { + dev_err(cpsw->dev, "register switchdev notifier fail ret:%d\n", + ret); + return ret; + } + + ret = register_switchdev_blocking_notifier(&cpsw_switchdev_bl_notifier); + if (ret) { + dev_err(cpsw->dev, "register switchdev blocking notifier ret:%d\n", + ret); + unregister_switchdev_notifier(&cpsw_switchdev_notifier); + } + + return ret; +} + +void am65_cpsw_switchdev_unregister_notifiers(struct am65_cpsw_common *cpsw) +{ + unregister_switchdev_blocking_notifier(&cpsw_switchdev_bl_notifier); + unregister_switchdev_notifier(&cpsw_switchdev_notifier); +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpsw-switchdev.h b/drivers/net/ethernet/ti/am65-cpsw-switchdev.h --- a/drivers/net/ethernet/ti/am65-cpsw-switchdev.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpsw-switchdev.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef DRIVERS_NET_ETHERNET_TI_AM65_CPSW_SWITCHDEV_H_ +#define DRIVERS_NET_ETHERNET_TI_AM65_CPSW_SWITCHDEV_H_ + +#include + +#if IS_ENABLED(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV) +static inline void am65_cpsw_nuss_set_offload_fwd_mark(struct sk_buff *skb, bool val) +{ + skb->offload_fwd_mark = val; +} + +int am65_cpsw_switchdev_register_notifiers(struct am65_cpsw_common *cpsw); +void am65_cpsw_switchdev_unregister_notifiers(struct am65_cpsw_common *cpsw); +#else +static inline int am65_cpsw_switchdev_register_notifiers(struct am65_cpsw_common *cpsw) +{ + return -EOPNOTSUPP; +} + +static inline void am65_cpsw_switchdev_unregister_notifiers(struct am65_cpsw_common *cpsw) +{ +} + +static inline void am65_cpsw_nuss_set_offload_fwd_mark(struct sk_buff *skb, bool val) +{ +} + +#endif + +#endif /* DRIVERS_NET_ETHERNET_TI_AM65_CPSW_SWITCHDEV_H_ */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c --- a/drivers/net/ethernet/ti/am65-cpts.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-cpts.c 2022-01-06 12:45:53.822318139 -0500 @@ -175,7 +175,12 @@ u64 timestamp; u32 genf_enable; u32 hw_ts_enable; + u32 estf_enable; struct sk_buff_head txq; + bool pps_enabled; + bool pps_present; + u32 pps_hw_ts_idx; + u32 pps_genf_idx; }; struct am65_cpts_skb_cb_data { @@ -309,8 +314,17 @@ case AM65_CPTS_EV_HW: pevent.index = am65_cpts_event_get_port(event) - 1; pevent.timestamp = event->timestamp; - pevent.type = PTP_CLOCK_EXTTS; - dev_dbg(cpts->dev, "AM65_CPTS_EV_HW p:%d t:%llu\n", + if (cpts->pps_enabled && + pevent.index == cpts->pps_hw_ts_idx) { + pevent.type = PTP_CLOCK_PPSUSR; + pevent.pps_times.ts_real = + ns_to_timespec64(pevent.timestamp); + } else { + pevent.type = PTP_CLOCK_EXTTS; + } + dev_dbg(cpts->dev, "AM65_CPTS_EV_HW:%s p:%d t:%llu\n", + pevent.type == PTP_CLOCK_EXTTS ? + "extts" : "pps", pevent.index, event->timestamp); ptp_clock_event(cpts->ptp_clock, &pevent); @@ -384,9 +398,12 @@ static int am65_cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) { struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info); - int neg_adj = 0; - u64 adj_period; - u32 val; + u32 estf_ctrl_val = 0, estf_ppm_hi = 0, estf_ppm_low = 0; + int pps_index = cpts->pps_genf_idx; + u64 adj_period, pps_adj_period; + u32 ctrl_val, ppm_hi, ppm_low; + unsigned long flags; + int neg_adj = 0, i; if (ppb < 0) { neg_adj = 1; @@ -406,17 +423,60 @@ mutex_lock(&cpts->ptp_clk_lock); - val = am65_cpts_read32(cpts, control); + ctrl_val = am65_cpts_read32(cpts, control); if (neg_adj) - val |= AM65_CPTS_CONTROL_TS_PPM_DIR; + ctrl_val |= AM65_CPTS_CONTROL_TS_PPM_DIR; else - val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR; - am65_cpts_write32(cpts, val, control); + ctrl_val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR; + + ppm_hi = upper_32_bits(adj_period) & 0x3FF; + ppm_low = lower_32_bits(adj_period); + + if (cpts->pps_enabled) { + estf_ctrl_val = am65_cpts_read32(cpts, genf[pps_index].control); + if (neg_adj) + estf_ctrl_val &= ~BIT(1); + else + estf_ctrl_val |= BIT(1); + + /* GenF PPM will do correction using cpts refclk tick which is + * (cpts->ts_add_val + 1) ns, so GenF length PPM adj period + * need to be corrected. + */ + pps_adj_period = adj_period * (cpts->ts_add_val + 1); + estf_ppm_hi = upper_32_bits(pps_adj_period) & 0x3FF; + estf_ppm_low = lower_32_bits(pps_adj_period); + } + + spin_lock_irqsave(&cpts->lock, flags); + + /* All below writes must be done extremely fast: + * - delay between PPM dir and PPM value changes can cause err due old + * PPM correction applied in wrong direction + * - delay between CPTS-clock PPM cfg and GenF PPM cfg can cause err + * due CPTS-clock PPM working with new cfg while GenF PPM cfg still + * with old for short period of time + */ - val = upper_32_bits(adj_period) & 0x3FF; - am65_cpts_write32(cpts, val, ts_ppm_hi); - val = lower_32_bits(adj_period); - am65_cpts_write32(cpts, val, ts_ppm_low); + am65_cpts_write32(cpts, ctrl_val, control); + am65_cpts_write32(cpts, ppm_hi, ts_ppm_hi); + am65_cpts_write32(cpts, ppm_low, ts_ppm_low); + + if (cpts->pps_enabled) { + am65_cpts_write32(cpts, estf_ctrl_val, genf[pps_index].control); + am65_cpts_write32(cpts, estf_ppm_hi, genf[pps_index].ppm_hi); + am65_cpts_write32(cpts, estf_ppm_low, genf[pps_index].ppm_low); + } + + for (i = 0; i < AM65_CPTS_ESTF_MAX_NUM; i++) { + if (cpts->estf_enable & BIT(i)) { + am65_cpts_write32(cpts, estf_ctrl_val, estf[i].control); + am65_cpts_write32(cpts, estf_ppm_hi, estf[i].ppm_hi); + am65_cpts_write32(cpts, estf_ppm_low, estf[i].ppm_low); + } + } + /* All GenF/EstF can be updated here the same way */ + spin_unlock_irqrestore(&cpts->lock, flags); mutex_unlock(&cpts->ptp_clk_lock); @@ -496,6 +556,10 @@ static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on) { + + if (cpts->pps_present && index == cpts->pps_hw_ts_idx) + return -EINVAL; + if (!!(cpts->hw_ts_enable & BIT(index)) == !!on) return 0; @@ -529,6 +593,11 @@ am65_cpts_write32(cpts, val, estf[idx].comp_lo); val = lower_32_bits(cycles); am65_cpts_write32(cpts, val, estf[idx].length); + am65_cpts_write32(cpts, 0, estf[idx].control); + am65_cpts_write32(cpts, 0, estf[idx].ppm_hi); + am65_cpts_write32(cpts, 0, estf[idx].ppm_low); + + cpts->estf_enable |= BIT(idx); dev_dbg(cpts->dev, "%s: ESTF:%u enabled\n", __func__, idx); @@ -539,6 +608,7 @@ void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx) { am65_cpts_write32(cpts, 0, estf[idx].length); + cpts->estf_enable &= ~BIT(idx); dev_dbg(cpts->dev, "%s: ESTF:%u disabled\n", __func__, idx); } @@ -569,6 +639,10 @@ val = lower_32_bits(cycles); am65_cpts_write32(cpts, val, genf[req->index].length); + am65_cpts_write32(cpts, 0, genf[req->index].control); + am65_cpts_write32(cpts, 0, genf[req->index].ppm_hi); + am65_cpts_write32(cpts, 0, genf[req->index].ppm_low); + cpts->genf_enable |= BIT(req->index); } else { am65_cpts_write32(cpts, 0, genf[req->index].length); @@ -580,6 +654,9 @@ static int am65_cpts_perout_enable(struct am65_cpts *cpts, struct ptp_perout_request *req, int on) { + if (cpts->pps_present && req->index == cpts->pps_genf_idx) + return -EINVAL; + if (!!(cpts->genf_enable & BIT(req->index)) == !!on) return 0; @@ -593,6 +670,48 @@ return 0; } +static int am65_cpts_pps_enable(struct am65_cpts *cpts, int on) +{ + struct ptp_clock_request rq; + struct timespec64 ts; + int ret = 0; + u64 ns; + + if (!cpts->pps_present) + return -EINVAL; + + if (cpts->pps_enabled == !!on) + return 0; + + mutex_lock(&cpts->ptp_clk_lock); + + if (on) { + am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on); + + ns = am65_cpts_gettime(cpts, NULL); + ts = ns_to_timespec64(ns); + rq.perout.period.sec = 1; + rq.perout.period.nsec = 0; + rq.perout.start.sec = ts.tv_sec + 2; + rq.perout.start.nsec = 0; + rq.perout.index = cpts->pps_genf_idx; + + am65_cpts_perout_enable_hw(cpts, &rq.perout, on); + cpts->pps_enabled = true; + } else { + rq.perout.index = cpts->pps_genf_idx; + am65_cpts_perout_enable_hw(cpts, &rq.perout, on); + am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on); + cpts->pps_enabled = false; + } + + mutex_unlock(&cpts->ptp_clk_lock); + + dev_dbg(cpts->dev, "%s: pps: %s\n", + __func__, on ? "enabled" : "disabled"); + return ret; +} + static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on) { @@ -603,6 +722,8 @@ return am65_cpts_extts_enable(cpts, rq->extts.index, on); case PTP_CLK_REQ_PEROUT: return am65_cpts_perout_enable(cpts, &rq->perout, on); + case PTP_CLK_REQ_PPS: + return am65_cpts_pps_enable(cpts, on); default: break; } @@ -727,7 +848,7 @@ /** * am65_cpts_rx_enable - enable rx timestamping * @cpts: cpts handle - * @skb: packet + * @en: enable * * This functions enables rx packets timestamping. The CPTS can timestamp all * rx packets. @@ -915,6 +1036,12 @@ if (!of_property_read_u32(node, "ti,cpts-periodic-outputs", &prop[0])) cpts->genf_num = prop[0]; + if (!of_property_read_u32_array(node, "ti,pps", prop, 2)) { + cpts->pps_present = true; + cpts->pps_hw_ts_idx = prop[0]; + cpts->pps_genf_idx = prop[1]; + } + return cpts_of_mux_clk_setup(cpts, node); } @@ -985,6 +1112,8 @@ cpts->ptp_info.n_ext_ts = cpts->ext_ts_inputs; if (cpts->genf_num) cpts->ptp_info.n_per_out = cpts->genf_num; + if (cpts->pps_present) + cpts->ptp_info.pps = 1; am65_cpts_set_add_val(cpts); @@ -1020,9 +1149,9 @@ return ERR_PTR(ret); } - dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u\n", + dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u pps:%d\n", am65_cpts_read32(cpts, idver), - cpts->refclk_freq, cpts->ts_add_val); + cpts->refclk_freq, cpts->ts_add_val, cpts->pps_present); return cpts; diff -Naur --no-dereference a/drivers/net/ethernet/ti/am65-debugfs.c b/drivers/net/ethernet/ti/am65-debugfs.c --- a/drivers/net/ethernet/ti/am65-debugfs.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/am65-debugfs.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments K3 AM65 Ethernet debugfs submodule + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include +#include + +#include "am65-cpsw-nuss.h" + +int am65_cpsw_nuss_register_debugfs(struct am65_cpsw_common *common) +{ + common->debugfs_root = debugfs_create_dir(dev_name(common->dev), NULL); + if (IS_ERR(common->debugfs_root)) + return PTR_ERR(common->debugfs_root); + + return 0; +} + +void am65_cpsw_nuss_unregister_debugfs(struct am65_cpsw_common *common) +{ + debugfs_remove_recursive(common->debugfs_root); +} + +static int +cut_thru_tx_pri_mask_get(void *data, u64 *val) +{ + struct am65_cpsw_port *port = data; + struct am65_cpsw_cut_thru *cut_thru; + int ret = -EINVAL; + + read_lock(&dev_base_lock); + cut_thru = &port->qos.cut_thru; + if (port->ndev->reg_state == NETREG_REGISTERED) { + *val = cut_thru->tx_pri_mask; + ret = 0; + } + read_unlock(&dev_base_lock); + + return ret; +} + +static int +cut_thru_tx_pri_mask_set(void *data, u64 val) +{ + struct am65_cpsw_cut_thru *cut_thru; + struct am65_cpsw_port *port = data; + struct am65_cpsw_common *common; + int ret = 0; + + if (val & ~GENMASK(7, 0)) + return -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + common = port->common; + cut_thru = &port->qos.cut_thru; + + if (cut_thru->enable) { + dev_err(common->dev, "Port%u: can't set cut-thru tx_pri_mask while cut-thru enabled\n", + port->port_id); + ret = -EINVAL; + goto err; + } + cut_thru->tx_pri_mask = val; + +err: + rtnl_unlock(); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_cut_thru_tx_pri_mask, cut_thru_tx_pri_mask_get, + cut_thru_tx_pri_mask_set, "%llx\n"); + +static int +cut_thru_rx_pri_mask_get(void *data, u64 *val) +{ + struct am65_cpsw_port *port = data; + struct am65_cpsw_cut_thru *cut_thru; + int ret = -EINVAL; + + read_lock(&dev_base_lock); + cut_thru = &port->qos.cut_thru; + if (port->ndev->reg_state == NETREG_REGISTERED) { + *val = cut_thru->rx_pri_mask; + ret = 0; + } + read_unlock(&dev_base_lock); + + return ret; +} + +static int +cut_thru_rx_pri_mask_set(void *data, u64 val) +{ + struct am65_cpsw_cut_thru *cut_thru; + struct am65_cpsw_port *port = data; + struct am65_cpsw_common *common; + int ret = 0; + + if (val & ~GENMASK(7, 0)) + return -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + common = port->common; + cut_thru = &port->qos.cut_thru; + + if (cut_thru->enable) { + dev_err(common->dev, "Port%u: can't set cut-thru rx_pri_mask while cut-thru enabled\n", + port->port_id); + ret = -EINVAL; + goto err; + } + cut_thru->rx_pri_mask = val; + +err: + rtnl_unlock(); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_cut_thru_rx_pri_mask, cut_thru_rx_pri_mask_get, + cut_thru_rx_pri_mask_set, "%llx\n"); + +static int +iet_addfragsize_get(void *data, u64 *val) +{ + struct am65_cpsw_port *port = data; + struct am65_cpsw_iet *iet; + int ret = -EINVAL; + + read_lock(&dev_base_lock); + iet = &port->qos.iet; + if (port->ndev->reg_state == NETREG_REGISTERED) { + *val = (iet->addfragsize + 1) << 6; + ret = 0; + } + read_unlock(&dev_base_lock); + + return ret; +} + +static int +iet_addfragsize_set(void *data, u64 val) +{ + struct am65_cpsw_iet *iet; + struct am65_cpsw_port *port = data; + struct am65_cpsw_common *common; + int ret = 0; + + if (val > 512) + return -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + common = port->common; + iet = &port->qos.iet; + + /* hw addfragsize is in 64 octet units*/ + iet->addfragsize = (val >> 6) - 1; + + rtnl_unlock(); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_iet_addfragsize, iet_addfragsize_get, iet_addfragsize_set, "%llu\n"); + +int am65_cpsw_nuss_register_port_debugfs(struct am65_cpsw_port *port) +{ + struct am65_cpsw_common *common = port->common; + char dirn[32]; + + scnprintf(dirn, sizeof(dirn), "Port%x", port->port_id); + port->debugfs_port = debugfs_create_dir(dirn, common->debugfs_root); + if (IS_ERR(port->debugfs_port)) + return PTR_ERR(port->debugfs_port); + + debugfs_create_bool("disabled", 0400, + port->debugfs_port, &port->disabled); + if (port->disabled) + return 0; + + if (common->pdata.quirks & AM64_CPSW_QUIRK_CUT_THRU) { + debugfs_create_file("cut_thru_tx_pri_mask", 0600, + port->debugfs_port, + port, &fops_cut_thru_tx_pri_mask); + debugfs_create_file("cut_thru_rx_pri_mask", 0600, + port->debugfs_port, + port, &fops_cut_thru_rx_pri_mask); + } + + debugfs_create_file("iet_addfragsize", 0600, + port->debugfs_port, + port, &fops_iet_addfragsize); + + return 0; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c --- a/drivers/net/ethernet/ti/cpsw_ale.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_ale.c 2022-01-06 12:45:53.822318139 -0500 @@ -50,6 +50,8 @@ /* ALE_AGING_TIMER */ #define ALE_AGING_TIMER_MASK GENMASK(23, 0) +#define ALE_RATE_LIMIT_MIN_PPS 1000 + /** * struct ale_entry_fld - The ALE tbl entry field description * @start_bit: field start bit @@ -634,8 +636,8 @@ return 0; } -static void cpsw_ale_del_vlan_modify(struct cpsw_ale *ale, u32 *ale_entry, - u16 vid, int port_mask) +static void cpsw_ale_vlan_del_modify_int(struct cpsw_ale *ale, u32 *ale_entry, + u16 vid, int port_mask) { int reg_mcast, unreg_mcast; int members, untag; @@ -644,6 +646,7 @@ ALE_ENT_VID_MEMBER_LIST); members &= ~port_mask; if (!members) { + cpsw_ale_set_vlan_untag(ale, ale_entry, vid, 0); cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); return; } @@ -673,7 +676,7 @@ ALE_ENT_VID_MEMBER_LIST, members); } -int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask) +int cpsw_ale_vlan_del_modify(struct cpsw_ale *ale, u16 vid, int port_mask) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; @@ -684,11 +687,39 @@ cpsw_ale_read(ale, idx, ale_entry); - if (port_mask) { - cpsw_ale_del_vlan_modify(ale, ale_entry, vid, port_mask); - } else { + cpsw_ale_vlan_del_modify_int(ale, ale_entry, vid, port_mask); + cpsw_ale_write(ale, idx, ale_entry); + + return 0; +} + +int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int members, idx; + + idx = cpsw_ale_match_vlan(ale, vid); + if (idx < 0) + return -ENOENT; + + cpsw_ale_read(ale, idx, ale_entry); + + /* if !port_mask - force remove VLAN (legacy). + * Check if there are other VLAN members ports + * if no - remove VLAN. + * if yes it means same VLAN was added to >1 port in multi port mode, so + * remove port_mask ports from VLAN ALE entry excluding Host port. + */ + members = cpsw_ale_vlan_get_fld(ale, ale_entry, ALE_ENT_VID_MEMBER_LIST); + members &= ~port_mask; + + if (!port_mask || !members) { + /* last port or force remove - remove VLAN */ cpsw_ale_set_vlan_untag(ale, ale_entry, vid, 0); cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); + } else { + port_mask &= ~ALE_PORT_HOST; + cpsw_ale_vlan_del_modify_int(ale, ale_entry, vid, port_mask); } cpsw_ale_write(ale, idx, ale_entry); @@ -1107,6 +1138,50 @@ return tmp & BITMASK(info->bits); } +int cpsw_ale_rx_ratelimit_mc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps) + +{ + int val = ratelimit_pps / ALE_RATE_LIMIT_MIN_PPS; + u32 remainder = ratelimit_pps % ALE_RATE_LIMIT_MIN_PPS; + + if (ratelimit_pps && !val) { + dev_err(ale->params.dev, "ALE MC port:%d ratelimit min value 1000pps\n", port); + return -EINVAL; + } + + if (remainder) + dev_info(ale->params.dev, "ALE port:%d MC ratelimit set to %dpps (requested %d)\n", + port, ratelimit_pps - remainder, ratelimit_pps); + + cpsw_ale_control_set(ale, port, ALE_PORT_MCAST_LIMIT, val); + + dev_dbg(ale->params.dev, "ALE port:%d MC ratelimit set %d\n", + port, val * ALE_RATE_LIMIT_MIN_PPS); + return 0; +} + +int cpsw_ale_rx_ratelimit_bc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps) + +{ + int val = ratelimit_pps / ALE_RATE_LIMIT_MIN_PPS; + u32 remainder = ratelimit_pps % ALE_RATE_LIMIT_MIN_PPS; + + if (ratelimit_pps && !val) { + dev_err(ale->params.dev, "ALE port:%d BC ratelimit min value 1000pps\n", port); + return -EINVAL; + } + + if (remainder) + dev_info(ale->params.dev, "ALE port:%d BC ratelimit set to %dpps (requested %d)\n", + port, ratelimit_pps - remainder, ratelimit_pps); + + cpsw_ale_control_set(ale, port, ALE_PORT_BCAST_LIMIT, val); + + dev_dbg(ale->params.dev, "ALE port:%d BC ratelimit set %d\n", + port, val * ALE_RATE_LIMIT_MIN_PPS); + return 0; +} + static void cpsw_ale_timer(struct timer_list *t) { struct cpsw_ale *ale = from_timer(ale, t, timer); @@ -1170,6 +1245,26 @@ void cpsw_ale_start(struct cpsw_ale *ale) { + unsigned long ale_prescale; + + /* configure Broadcast and Multicast Rate Limit + * number_of_packets = (Fclk / ALE_PRESCALE) * port.BCAST/MCAST_LIMIT + * ALE_PRESCALE width is 19bit and min value 0x10 + * port.BCAST/MCAST_LIMIT is 8bit + * + * For multi port configuration support the ALE_PRESCALE is configured to 1ms interval, + * which allows to configure port.BCAST/MCAST_LIMIT per port and achieve: + * min number_of_packets = 1000 when port.BCAST/MCAST_LIMIT = 1 + * max number_of_packets = 1000 * 255 = 255000 when port.BCAST/MCAST_LIMIT = 0xFF + */ + ale_prescale = ale->params.bus_freq / ALE_RATE_LIMIT_MIN_PPS; + writel((u32)ale_prescale, ale->params.ale_regs + ALE_PRESCALE); + + /* Allow MC/BC rate limiting globally. + * The actual Rate Limit cfg enabled per-port by port.BCAST/MCAST_LIMIT + */ + cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT, 1); + cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1); cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); @@ -1227,6 +1322,13 @@ .major_ver_mask = 0x7, .vlan_entry_tbl = vlan_entry_k3_cpswxg, }, + { + .dev_id = "am64-cpswxg", + .features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING, + .major_ver_mask = 0x7, + .vlan_entry_tbl = vlan_entry_k3_cpswxg, + .tbl_entries = 512, + }, { }, }; diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h --- a/drivers/net/ethernet/ti/cpsw_ale.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_ale.h 2022-01-06 12:45:53.822318139 -0500 @@ -120,6 +120,8 @@ int reg_mcast, int unreg_mcast); int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port); void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti, int port); +int cpsw_ale_rx_ratelimit_bc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps); +int cpsw_ale_rx_ratelimit_mc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps); int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control); int cpsw_ale_control_set(struct cpsw_ale *ale, int port, @@ -134,6 +136,7 @@ int cpsw_ale_vlan_add_modify(struct cpsw_ale *ale, u16 vid, int port_mask, int untag_mask, int reg_mcast, int unreg_mcast); +int cpsw_ale_vlan_del_modify(struct cpsw_ale *ale, u16 vid, int port_mask); void cpsw_ale_set_unreg_mcast(struct cpsw_ale *ale, int unreg_mcast_mask, bool add); diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c --- a/drivers/net/ethernet/ti/cpsw.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw.c 2022-01-06 12:45:53.822318139 -0500 @@ -34,6 +34,8 @@ #include #include #include +//#include +#include #include #include @@ -59,6 +61,10 @@ module_param(rx_packet_max, int, 0); MODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)"); +static int tx_packet_min = CPSW_MIN_PACKET_SIZE; +module_param(tx_packet_min, int, 0444); +MODULE_PARM_DESC(tx_packet_min, "minimum tx packet size (bytes)"); + static int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT; module_param(descs_pool_size, int, 0444); MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool"); @@ -409,12 +415,10 @@ xdp.frame_sz = PAGE_SIZE; port = priv->emac_port + cpsw->data.dual_emac; - ret = cpsw_run_xdp(priv, ch, &xdp, page, port); + ret = cpsw_run_xdp(priv, ch, &xdp, page, port, &len); if (ret != CPSW_XDP_PASS) goto requeue; - /* XDP prog might have changed packet data and boundaries */ - len = xdp.data_end - xdp.data; headroom = xdp.data - xdp.data_hard_start; /* XDP prog can modify vlan tag, so can't use encap header */ @@ -498,7 +502,8 @@ /* enable forwarding */ cpsw_ale_control_set(cpsw->ale, slave_port, - ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); + ALE_PORT_STATE, + priv->port_state[slave_port]); *link = true; @@ -611,6 +616,7 @@ slave->mac_control = 0; /* no link yet */ slave_port = cpsw_get_slave_port(slave->slave_num); + priv->port_state[slave_port] = ALE_PORT_STATE_FORWARD; if (cpsw->data.dual_emac) cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port); @@ -911,14 +917,17 @@ struct cpts *cpts = cpsw->cpts; struct netdev_queue *txq; struct cpdma_chan *txch; + unsigned int len; int ret, q_idx; - if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) { + if (skb_padto(skb, tx_packet_min)) { cpsw_err(priv, tx_err, "packet pad failed\n"); ndev->stats.tx_dropped++; return NET_XMIT_DROP; } + len = skb->len < tx_packet_min ? tx_packet_min : skb->len; + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb)) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; @@ -1138,7 +1147,7 @@ for (i = 0; i < n; i++) { xdpf = frames[i]; - if (xdpf->len < CPSW_MIN_PACKET_SIZE) { + if (xdpf->len < tx_packet_min) { xdp_return_frame_rx_napi(xdpf); drops++; continue; @@ -1164,12 +1173,37 @@ } #endif +#include "cpsw_switch_ioctl.c" + +static int cpsw_ndo_ioctl_legacy(struct net_device *dev, struct ifreq *req, int cmd) +{ + struct cpsw_priv *priv = netdev_priv(dev); + struct cpsw_common *cpsw = priv->cpsw; + int slave_no = cpsw_slave_index(cpsw, priv); + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCSHWTSTAMP: + return cpsw_hwtstamp_set(dev, req); + case SIOCGHWTSTAMP: + return cpsw_hwtstamp_get(dev, req); + case SIOCSWITCHCONFIG: + return cpsw_switch_config_ioctl(dev, req, cmd); + } + + if (!cpsw->slaves[slave_no].phy) + return -EOPNOTSUPP; + return phy_mii_ioctl(cpsw->slaves[slave_no].phy, req, cmd); +} + static const struct net_device_ops cpsw_netdev_ops = { .ndo_open = cpsw_ndo_open, .ndo_stop = cpsw_ndo_stop, .ndo_start_xmit = cpsw_ndo_start_xmit, .ndo_set_mac_address = cpsw_ndo_set_mac_address, - .ndo_do_ioctl = cpsw_ndo_ioctl, + .ndo_do_ioctl = cpsw_ndo_ioctl_legacy, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = cpsw_ndo_tx_timeout, .ndo_set_rx_mode = cpsw_ndo_set_rx_mode, diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c --- a/drivers/net/ethernet/ti/cpsw_new.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_new.c 2022-01-06 12:45:53.822318139 -0500 @@ -46,6 +46,8 @@ static int ale_ageout = CPSW_ALE_AGEOUT_DEFAULT; static int rx_packet_max = CPSW_MAX_PACKET_SIZE; static int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT; +module_param(descs_pool_size, int, 0444); +MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool"); struct cpsw_devlink { struct cpsw_common *cpsw; @@ -351,12 +353,10 @@ xdp.rxq = &priv->xdp_rxq[ch]; xdp.frame_sz = PAGE_SIZE; - ret = cpsw_run_xdp(priv, ch, &xdp, page, priv->emac_port); + ret = cpsw_run_xdp(priv, ch, &xdp, page, priv->emac_port, &len); if (ret != CPSW_XDP_PASS) goto requeue; - /* XDP prog might have changed packet data and boundaries */ - len = xdp.data_end - xdp.data; headroom = xdp.data - xdp.data_hard_start; /* XDP prog can modify vlan tag, so can't use encap header */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_priv.c b/drivers/net/ethernet/ti/cpsw_priv.c --- a/drivers/net/ethernet/ti/cpsw_priv.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_priv.c 2022-01-06 12:45:53.822318139 -0500 @@ -502,6 +502,7 @@ ale_params.ale_ageout = ale_ageout; ale_params.ale_ports = CPSW_ALE_PORTS_NUM; ale_params.dev_id = "cpsw"; + ale_params.bus_freq = cpsw->bus_freq_mhz * 1000000; cpsw->ale = cpsw_ale_create(&ale_params); if (IS_ERR(cpsw->ale)) { @@ -612,7 +613,7 @@ writel_relaxed(ETH_P_8021Q, &cpsw->regs->vlan_ltype); } -static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) +int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) { struct cpsw_priv *priv = netdev_priv(dev); struct cpsw_common *cpsw = priv->cpsw; @@ -676,7 +677,7 @@ return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } -static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) +int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) { struct cpsw_common *cpsw = ndev_to_cpsw(dev); struct cpsw_priv *priv = netdev_priv(dev); @@ -693,16 +694,6 @@ return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } -#else -static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) -{ - return -EOPNOTSUPP; -} - -static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) -{ - return -EOPNOTSUPP; -} #endif /*CONFIG_TI_CPTS*/ int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) @@ -1323,7 +1314,7 @@ } int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp, - struct page *page, int port) + struct page *page, int port, int *len) { struct cpsw_common *cpsw = priv->cpsw; struct net_device *ndev = priv->ndev; @@ -1341,10 +1332,13 @@ } act = bpf_prog_run_xdp(prog, xdp); + /* XDP prog might have changed packet data and boundaries */ + *len = xdp->data_end - xdp->data; + switch (act) { case XDP_PASS: ret = CPSW_XDP_PASS; - break; + goto out; case XDP_TX: xdpf = xdp_convert_buff_to_frame(xdp); if (unlikely(!xdpf)) @@ -1370,8 +1364,13 @@ trace_xdp_exception(ndev, prog, act); fallthrough; /* handle aborts by dropping packet */ case XDP_DROP: + ndev->stats.rx_bytes += *len; + ndev->stats.rx_packets++; goto drop; } + + ndev->stats.rx_bytes += *len; + ndev->stats.rx_packets++; out: rcu_read_unlock(); return ret; diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_priv.h b/drivers/net/ethernet/ti/cpsw_priv.h --- a/drivers/net/ethernet/ti/cpsw_priv.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_priv.h 2022-01-06 12:45:53.822318139 -0500 @@ -381,6 +381,7 @@ u32 emac_port; struct cpsw_common *cpsw; int offload_fwd_mark; + u8 port_state[3]; u32 tx_packet_min; }; @@ -440,7 +441,7 @@ int cpsw_xdp_tx_frame(struct cpsw_priv *priv, struct xdp_frame *xdpf, struct page *page, int port); int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp, - struct page *page, int port); + struct page *page, int port, int *len); irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id); irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id); irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id); @@ -495,4 +496,19 @@ cpdma_handler_fn rx_handler); int cpsw_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *info); +#if IS_ENABLED(CONFIG_TI_CPTS) +int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr); +int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr); +#else +static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) +{ + return -EOPNOTSUPP; +} + +static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) +{ + return -EOPNOTSUPP; +} +#endif /*CONFIG_TI_CPTS*/ + #endif /* DRIVERS_NET_ETHERNET_TI_CPSW_PRIV_H_ */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c --- a/drivers/net/ethernet/ti/cpsw_switchdev.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_switchdev.c 2022-01-06 12:45:53.822318139 -0500 @@ -227,7 +227,7 @@ else port_mask = BIT(priv->emac_port); - ret = cpsw_ale_del_vlan(cpsw->ale, vid, port_mask); + ret = cpsw_ale_vlan_del_modify(cpsw->ale, vid, port_mask); if (ret != 0) return ret; diff -Naur --no-dereference a/drivers/net/ethernet/ti/cpsw_switch_ioctl.c b/drivers/net/ethernet/ti/cpsw_switch_ioctl.c --- a/drivers/net/ethernet/ti/cpsw_switch_ioctl.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/cpsw_switch_ioctl.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* CPSW switch-configuration using non-standard private ioctl SIOCSWITCHCONFIG + * Grygorii Strashko : + * moved code in separate file to minimize merge conflicts with LKML + */ + +static int cpsw_set_port_state(struct cpsw_priv *priv, int port, + int port_state) +{ + switch (port_state) { + case PORT_STATE_DISABLED: + priv->port_state[port] = ALE_PORT_STATE_DISABLE; + break; + case PORT_STATE_BLOCKED: + priv->port_state[port] = ALE_PORT_STATE_BLOCK; + break; + case PORT_STATE_LEARN: + priv->port_state[port] = ALE_PORT_STATE_LEARN; + break; + case PORT_STATE_FORWARD: + priv->port_state[port] = ALE_PORT_STATE_FORWARD; + break; + default: + dev_err(priv->dev, "Switch config: Invalid port state\n"); + return -EINVAL; + } + return cpsw_ale_control_set(priv->cpsw->ale, port, ALE_PORT_STATE, + priv->port_state[port]); +} + +static int cpsw_switch_config_ioctl(struct net_device *ndev, + struct ifreq *ifrq, int cmd) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + struct net_switch_config config; + int ret = -EINVAL; + + if (cpsw->data.dual_emac) { + dev_err(priv->dev, "CPSW not in switch mode\n"); + return -EOPNOTSUPP; + } + + /* Only SIOCSWITCHCONFIG is used as cmd argument and hence, there is no + * switch statement required. + * Function calls are based on switch_config.cmd + */ + + if (copy_from_user(&config, ifrq->ifr_data, sizeof(config))) + return -EFAULT; + + if (config.vid > 4095) { + dev_err(priv->dev, "Invalid VLAN id Arguments for cmd %d\n", + config.cmd); + return ret; + } + + switch (config.cmd) { + case SWITCH_ADD_MULTICAST: + if (config.port > 0 && config.port <= 7 && + is_multicast_ether_addr(config.addr)) { + ret = cpsw_ale_add_mcast(cpsw->ale, config.addr, + config.port, ALE_VLAN, + config.vid, 0); + } else { + dev_err(priv->dev, "Invalid Arguments for cmd %d\n", + config.cmd); + } + break; + case SWITCH_DEL_MULTICAST: + if (is_multicast_ether_addr(config.addr)) { + ret = cpsw_ale_del_mcast(cpsw->ale, config.addr, + 0, ALE_VLAN, config.vid); + } else { + dev_err(priv->dev, "Invalid Arguments for cmd %d\n", + config.cmd); + } + break; + case SWITCH_ADD_VLAN: + if (config.port > 0 && config.port <= 7) { + ret = cpsw_ale_add_vlan(cpsw->ale, config.vid, + config.port, + config.untag_port, + config.reg_multi, + config.unreg_multi); + } else { + dev_err(priv->dev, "Invalid Arguments for cmd %d\n", + config.cmd); + } + break; + case SWITCH_DEL_VLAN: + ret = cpsw_ale_del_vlan(cpsw->ale, config.vid, 0); + break; + case SWITCH_SET_PORT_CONFIG: + { + struct phy_device *phy = NULL; + struct ethtool_link_ksettings cmd; + + if (config.port == 1 || config.port == 2) + phy = cpsw->slaves[config.port - 1].phy; + + if (!phy) { + dev_err(priv->dev, "Phy not Found\n"); + break; + } + + convert_legacy_settings_to_link_ksettings(&cmd, &config.ecmd); + cmd.base.phy_address = phy->mdio.addr; + ret = phy_ethtool_ksettings_set(phy, &cmd); + break; + } + case SWITCH_GET_PORT_CONFIG: + { + struct phy_device *phy = NULL; + struct ethtool_link_ksettings cmd; + + if (config.port == 1 || config.port == 2) + phy = cpsw->slaves[config.port - 1].phy; + + if (!phy) { + dev_err(priv->dev, "Phy not Found\n"); + break; + } + + cmd.base.phy_address = phy->mdio.addr; + phy_ethtool_ksettings_get(phy, &cmd); + convert_link_ksettings_to_legacy_settings(&config.ecmd, &cmd); + + ret = copy_to_user(ifrq->ifr_data, &config, sizeof(config)); + break; + } + case SWITCH_ADD_UNKNOWN_VLAN_INFO: + if (config.unknown_vlan_member <= 7 && + config.unknown_vlan_untag <= 7 && + config.unknown_vlan_unreg_multi <= 7 && + config.unknown_vlan_reg_multi <= 7) { + cpsw_ale_control_set(cpsw->ale, 0, + ALE_PORT_UNTAGGED_EGRESS, + config.unknown_vlan_untag); + cpsw_ale_control_set(cpsw->ale, 0, + ALE_PORT_UNKNOWN_REG_MCAST_FLOOD, + config.unknown_vlan_reg_multi); + cpsw_ale_control_set(cpsw->ale, 0, + ALE_PORT_UNKNOWN_MCAST_FLOOD, + config.unknown_vlan_unreg_multi); + cpsw_ale_control_set(cpsw->ale, 0, + ALE_PORT_UNKNOWN_VLAN_MEMBER, + config.unknown_vlan_member); + ret = 0; + } else { + dev_err(priv->dev, "Invalid Unknown VLAN Arguments\n"); + } + break; + case SWITCH_GET_PORT_STATE: + if (config.port == 1 || config.port == 2) { + config.port_state = priv->port_state[config.port]; + ret = copy_to_user(ifrq->ifr_data, &config, + sizeof(config)); + } else { + dev_err(priv->dev, "Invalid Port number\n"); + } + break; + case SWITCH_SET_PORT_STATE: + if (config.port == 1 || config.port == 2) { + ret = cpsw_set_port_state(priv, config.port, + config.port_state); + } else { + dev_err(priv->dev, "Invalid Port number\n"); + } + break; + case SWITCH_GET_PORT_VLAN_CONFIG: + { + u32 __iomem *port_vlan_reg; + u32 port_vlan; + + switch (config.port) { + case 0: + port_vlan_reg = &cpsw->host_port_regs->port_vlan; + port_vlan = readl(port_vlan_reg); + ret = 0; + + break; + case 1: + case 2: + { + int slave = config.port - 1; + int reg = CPSW2_PORT_VLAN; + + if (cpsw->version == CPSW_VERSION_1) + reg = CPSW1_PORT_VLAN; + + port_vlan = slave_read(cpsw->slaves + slave, reg); + ret = 0; + + break; + } + default: + dev_err(priv->dev, "Invalid Port number\n"); + break; + } + + if (!ret) { + config.vid = port_vlan & 0xfff; + config.vlan_cfi = port_vlan & BIT(12) ? true : false; + config.prio = (port_vlan >> 13) & 0x7; + ret = copy_to_user(ifrq->ifr_data, &config, + sizeof(config)); + } + break; + } + case SWITCH_SET_PORT_VLAN_CONFIG: + { + void __iomem *port_vlan_reg; + u32 port_vlan; + + port_vlan = config.vid; + port_vlan |= config.vlan_cfi ? BIT(12) : 0; + port_vlan |= (config.prio & 0x7) << 13; + + switch (config.port) { + case 0: + port_vlan_reg = &cpsw->host_port_regs->port_vlan; + writel(port_vlan, port_vlan_reg); + ret = 0; + + break; + case 1: + case 2: + { + int slave = config.port - 1; + int reg = CPSW2_PORT_VLAN; + + if (cpsw->version == CPSW_VERSION_1) + reg = CPSW1_PORT_VLAN; + + slave_write(cpsw->slaves + slave, port_vlan, reg); + ret = 0; + + break; + } + default: + dev_err(priv->dev, "Invalid Port number\n"); + break; + } + + break; + } + case SWITCH_RATELIMIT: + { + if (config.port > 2) { + dev_err(priv->dev, "Invalid Port number\n"); + break; + } + + ret = cpsw_ale_rx_ratelimit_mc(cpsw->ale, config.port, config.mcast_rate_limit); + if (ret) + dev_err(priv->dev, "CPSW_ALE set MC ratelimit failed"); + + ret = cpsw_ale_rx_ratelimit_bc(cpsw->ale, config.port, config.bcast_rate_limit); + if (ret) + dev_err(priv->dev, "CPSW_ALE set BC ratelimit failed"); + + break; + } + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c --- a/drivers/net/ethernet/ti/davinci_mdio.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/davinci_mdio.c 2022-01-06 12:45:53.822318139 -0500 @@ -92,6 +92,10 @@ u32 clk_div; }; +#if IS_ENABLED(CONFIG_OF) +static void davinci_mdio_update_dt_from_phymask(u32 phy_mask); +#endif + static void davinci_mdio_init_clk(struct davinci_mdio_data *data) { u32 mdio_in, div, mdio_out_khz, access_time; @@ -159,6 +163,12 @@ /* restrict mdio bus to live phys only */ dev_info(data->dev, "detected phy mask %x\n", ~phy_mask); phy_mask = ~phy_mask; + + #if IS_ENABLED(CONFIG_OF) + if (of_machine_is_compatible("ti,am335x-bone")) + davinci_mdio_update_dt_from_phymask(phy_mask); + #endif + } else { /* desperately scan all phys */ dev_warn(data->dev, "no live phy, scanning all\n"); @@ -358,20 +368,16 @@ } if (IS_ENABLED(CONFIG_OF) && dev->of_node) { - const struct of_device_id *of_id; + const struct davinci_mdio_of_param *of_mdio_data; ret = davinci_mdio_probe_dt(&data->pdata, pdev); if (ret) return ret; snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); - of_id = of_match_device(davinci_mdio_of_mtable, &pdev->dev); - if (of_id) { - const struct davinci_mdio_of_param *of_mdio_data; - - of_mdio_data = of_id->data; - if (of_mdio_data) - autosuspend_delay_ms = + of_mdio_data = of_device_get_match_data(&pdev->dev); + if (of_mdio_data) { + autosuspend_delay_ms = of_mdio_data->autosuspend_delay_ms; } } else { @@ -381,9 +387,9 @@ } data->bus->name = dev_name(dev); - data->bus->read = davinci_mdio_read, - data->bus->write = davinci_mdio_write, - data->bus->reset = davinci_mdio_reset, + data->bus->read = davinci_mdio_read; + data->bus->write = davinci_mdio_write; + data->bus->reset = davinci_mdio_reset; data->bus->parent = dev; data->bus->priv = data; @@ -476,6 +482,94 @@ } #endif +static void davinci_mdio_update_dt_from_phymask(u32 phy_mask) +{ + int i, len, skip; + u32 addr; + __be32 *old_phy_p, *phy_id_p; + struct property *phy_id_property = NULL; + struct device_node *node_p, *slave_p; + + addr = 0; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if ((phy_mask & (1 << i)) == 0) { + addr = (u32) i; + break; + } + } + + for_each_compatible_node(node_p, NULL, "ti,cpsw") { + for_each_node_by_name(slave_p, "slave") { + +#if IS_ENABLED(CONFIG_OF_OVERLAY) + skip = 1; + // Hack, the overlay fixup "slave" doesn't have phy-mode... + old_phy_p = (__be32 *) of_get_property(slave_p, "phy-mode", &len); + + if (len != (sizeof(__be32 *) * 1)) + { + skip = 0; + } + + if (skip) { +#endif + + old_phy_p = (__be32 *) of_get_property(slave_p, "phy_id", &len); + + if (len != (sizeof(__be32 *) * 2)) + goto err_out; + + if (old_phy_p) { + + phy_id_property = kzalloc(sizeof(*phy_id_property), GFP_KERNEL); + + if (! phy_id_property) + goto err_out; + + phy_id_property->length = len; + phy_id_property->name = kstrdup("phy_id", GFP_KERNEL); + phy_id_property->value = kzalloc(len, GFP_KERNEL); + + if (! phy_id_property->name) + goto err_out; + + if (! phy_id_property->value) + goto err_out; + + memcpy(phy_id_property->value, old_phy_p, len); + + phy_id_p = (__be32 *) phy_id_property->value + 1; + + *phy_id_p = cpu_to_be32(addr); + + of_update_property(slave_p, phy_id_property); + pr_info("davinci_mdio: dt: updated phy_id[%d] from phy_mask[%x]\n", addr, phy_mask); + + ++addr; + } +#if IS_ENABLED(CONFIG_OF_OVERLAY) + } +#endif + } + } + + return; + +err_out: + + if (phy_id_property) { + if (phy_id_property->name) + kfree(phy_id_property->name); + + if (phy_id_property->value) + kfree(phy_id_property->value); + + if (phy_id_property) + kfree(phy_id_property); + } +} + #ifdef CONFIG_PM_SLEEP static int davinci_mdio_suspend(struct device *dev) { diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_classifier.c b/drivers/net/ethernet/ti/icssg_classifier.c --- a/drivers/net/ethernet/ti/icssg_classifier.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_classifier.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG Ethernet Driver + * + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include + +#include "icssg_prueth.h" + +#define ICSSG_NUM_CLASSIFIERS 16 +#define ICSSG_NUM_FT1_SLOTS 8 +#define ICSSG_NUM_FT3_SLOTS 16 + +#define ICSSG_NUM_CLASSIFIERS_IN_USE 5 + +/* Filter 1 - FT1 */ +#define FT1_NUM_SLOTS 8 +#define FT1_SLOT_SIZE 0x10 /* bytes */ + +/* offsets from FT1 slot base i.e. slot 1 start */ +#define FT1_DA0 0x0 +#define FT1_DA1 0x4 +#define FT1_DA0_MASK 0x8 +#define FT1_DA1_MASK 0xc + +#define FT1_N_REG(slize, n, reg) (offs[slice].ft1_slot_base + FT1_SLOT_SIZE * (n) + (reg)) + +#define FT1_LEN_MASK GENMASK(19, 16) +#define FT1_LEN_SHIFT 16 +#define FT1_LEN(len) (((len) << FT1_LEN_SHIFT) & FT1_LEN_MASK) + +#define FT1_START_MASK GENMASK(14, 0) +#define FT1_START(start) ((start) & FT1_START_MASK) + +#define FT1_MATCH_SLOT(n) (GENMASK(23, 16) & (BIT(n) << 16)) + +enum ft1_cfg_type { + FT1_CFG_TYPE_DISABLED = 0, + FT1_CFG_TYPE_EQ, + FT1_CFG_TYPE_GT, + FT1_CFG_TYPE_LT, +}; + +#define FT1_CFG_SHIFT(n) (2 * (n)) +#define FT1_CFG_MASK(n) (0x3 << FT1_CFG_SHIFT((n))) + +/* Filter 3 - FT3 */ +#define FT3_NUM_SLOTS 16 +#define FT3_SLOT_SIZE 0x20 /* bytes */ + +/* offsets from FT3 slot n's base */ +#define FT3_START 0 +#define FT3_START_AUTO 0x4 +#define FT3_START_OFFSET 0x8 +#define FT3_JUMP_OFFSET 0xc +#define FT3_LEN 0x10 +#define FT3_CFG 0x14 +#define FT3_T 0x18 +#define FT3_T_MASK 0x1c + +#define FT3_N_REG(slize, n, reg) (offs[slice].ft3_slot_base + FT3_SLOT_SIZE * (n) + (reg)) + +/* offsets from rx_class n's base */ +#define RX_CLASS_AND_EN 0 +#define RX_CLASS_OR_EN 0x4 + +#define RX_CLASS_NUM_SLOTS 16 +#define RX_CLASS_EN_SIZE 0x8 /* bytes */ + +#define RX_CLASS_N_REG(slice, n, reg) (offs[slice].rx_class_base + RX_CLASS_EN_SIZE * (n) + (reg)) + +/* RX Class Gates */ +#define RX_CLASS_GATES_SIZE 0x4 /* bytes */ + +#define RX_CLASS_GATES_N_REG(slice, n) \ + (offs[slice].rx_class_gates_base + RX_CLASS_GATES_SIZE * (n)) + +#define RX_CLASS_GATES_ALLOW_MASK BIT(6) +#define RX_CLASS_GATES_RAW_MASK BIT(5) +#define RX_CLASS_GATES_PHASE_MASK BIT(4) + +/* RX Class traffic data matching bits */ +#define RX_CLASS_FT_UC BIT(31) +#define RX_CLASS_FT_MC BIT(30) +#define RX_CLASS_FT_BC BIT(29) +#define RX_CLASS_FT_FW BIT(28) +#define RX_CLASS_FT_RCV BIT(27) +#define RX_CLASS_FT_VLAN BIT(26) +#define RX_CLASS_FT_DA_P BIT(25) +#define RX_CLASS_FT_DA_I BIT(24) +#define RX_CLASS_FT_FT1_MATCH_MASK GENMASK(23, 16) +#define RX_CLASS_FT_FT1_MATCH_SHIFT 16 +#define RX_CLASS_FT_FT3_MATCH_MASK GENMASK(15, 0) +#define RX_CLASS_FT_FT3_MATCH_SHIFT 0 + +#define RX_CLASS_FT_FT1_MATCH(slot) \ + ((BIT(slot) << RX_CLASS_FT_FT1_MATCH_SHIFT) & RX_CLASS_FT_FT1_MATCH_MASK) + +enum rx_class_sel_type { + RX_CLASS_SEL_TYPE_OR = 0, + RX_CLASS_SEL_TYPE_AND = 1, + RX_CLASS_SEL_TYPE_OR_AND_AND = 2, + RX_CLASS_SEL_TYPE_OR_OR_AND = 3, +}; + +#define FT1_CFG_SHIFT(n) (2 * (n)) +#define FT1_CFG_MASK(n) (0x3 << FT1_CFG_SHIFT((n))) + +#define RX_CLASS_SEL_SHIFT(n) (2 * (n)) +#define RX_CLASS_SEL_MASK(n) (0x3 << RX_CLASS_SEL_SHIFT((n))) + +#define ICSSG_CFG_OFFSET 0 +#define MAC_INTERFACE_0 0x18 +#define MAC_INTERFACE_1 0x1c + +#define ICSSG_CFG_RX_L2_G_EN BIT(2) + +/* these are register offsets per PRU */ +struct miig_rt_offsets { + u32 mac0; + u32 mac1; + u32 ft1_start_len; + u32 ft1_cfg; + u32 ft1_slot_base; + u32 ft3_slot_base; + u32 ft3_p_base; + u32 ft_rx_ptr; + u32 rx_class_base; + u32 rx_class_cfg1; + u32 rx_class_cfg2; + u32 rx_class_gates_base; + u32 rx_green; + u32 rx_rate_cfg_base; + u32 rx_rate_src_sel0; + u32 rx_rate_src_sel1; + u32 tx_rate_cfg_base; + u32 stat_base; + u32 tx_hsr_tag; + u32 tx_hsr_seq; + u32 tx_vlan_type; + u32 tx_vlan_ins; +}; + +static struct miig_rt_offsets offs[] = { + /* PRU0 */ + { + 0x8, + 0xc, + 0x80, + 0x84, + 0x88, + 0x108, + 0x308, + 0x408, + 0x40c, + 0x48c, + 0x490, + 0x494, + 0x4d4, + 0x4e4, + 0x504, + 0x508, + 0x50c, + 0x54c, + 0x63c, + 0x640, + 0x644, + 0x648, + }, + /* PRU1 */ + { + 0x10, + 0x14, + 0x64c, + 0x650, + 0x654, + 0x6d4, + 0x8d4, + 0x9d4, + 0x9d8, + 0xa58, + 0xa5c, + 0xa60, + 0xaa0, + 0xab0, + 0xad0, + 0xad4, + 0xad8, + 0xb18, + 0xc08, + 0xc0c, + 0xc10, + 0xc14, + }, +}; + +static inline u32 addr_to_da0(const u8 *addr) +{ + return (u32)(addr[0] | addr[1] << 8 | + addr[2] << 16 | addr[3] << 24); +}; + +static inline u32 addr_to_da1(const u8 *addr) +{ + return (u32)(addr[4] | addr[5] << 8); +}; + +static void rx_class_ft1_set_start_len(struct regmap *miig_rt, int slice, + u16 start, u8 len) +{ + u32 offset, val; + + offset = offs[slice].ft1_start_len; + val = FT1_LEN(len) | FT1_START(start); + regmap_write(miig_rt, offset, val); +} + +static void rx_class_ft1_set_da(struct regmap *miig_rt, int slice, + int n, const u8 *addr) +{ + u32 offset; + + offset = FT1_N_REG(slice, n, FT1_DA0); + regmap_write(miig_rt, offset, addr_to_da0(addr)); + offset = FT1_N_REG(slice, n, FT1_DA1); + regmap_write(miig_rt, offset, addr_to_da1(addr)); +} + +static void rx_class_ft1_set_da_mask(struct regmap *miig_rt, int slice, + int n, const u8 *addr) +{ + u32 offset; + + offset = FT1_N_REG(slice, n, FT1_DA0_MASK); + regmap_write(miig_rt, offset, addr_to_da0(addr)); + offset = FT1_N_REG(slice, n, FT1_DA1_MASK); + regmap_write(miig_rt, offset, addr_to_da1(addr)); +} + +static void rx_class_ft1_cfg_set_type(struct regmap *miig_rt, int slice, int n, + enum ft1_cfg_type type) +{ + u32 offset; + + offset = offs[slice].ft1_cfg; + regmap_update_bits(miig_rt, offset, FT1_CFG_MASK(n), + type << FT1_CFG_SHIFT(n)); +} + +static void rx_class_sel_set_type(struct regmap *miig_rt, int slice, int n, + enum rx_class_sel_type type) +{ + u32 offset; + + offset = offs[slice].rx_class_cfg1; + regmap_update_bits(miig_rt, offset, RX_CLASS_SEL_MASK(n), + type << RX_CLASS_SEL_SHIFT(n)); +} + +static void rx_class_set_and(struct regmap *miig_rt, int slice, int n, + u32 data) +{ + u32 offset; + + offset = RX_CLASS_N_REG(slice, n, RX_CLASS_AND_EN); + regmap_write(miig_rt, offset, data); +} + +static void rx_class_set_or(struct regmap *miig_rt, int slice, int n, + u32 data) +{ + u32 offset; + + offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN); + regmap_write(miig_rt, offset, data); +} + +static u32 rx_class_get_or(struct regmap *miig_rt, int slice, int n) +{ + u32 offset, val; + + offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN); + regmap_read(miig_rt, offset, &val); + + return val; +} + +void icssg_class_set_host_mac_addr(struct regmap *miig_rt, u8 *mac) +{ + regmap_write(miig_rt, MAC_INTERFACE_0, addr_to_da0(mac)); + regmap_write(miig_rt, MAC_INTERFACE_1, addr_to_da1(mac)); +} + +void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac) +{ + regmap_write(miig_rt, offs[slice].mac0, addr_to_da0(mac)); + regmap_write(miig_rt, offs[slice].mac1, addr_to_da1(mac)); +} + +static void icssg_class_ft1_add_mcast(struct regmap *miig_rt, int slice, + int slot, const u8 *addr, const u8 *mask) +{ + int i; + u32 val; + + WARN(slot >= FT1_NUM_SLOTS, "invalid slot: %d\n", slot); + + rx_class_ft1_set_da(miig_rt, slice, slot, addr); + rx_class_ft1_set_da_mask(miig_rt, slice, slot, mask); + rx_class_ft1_cfg_set_type(miig_rt, slice, slot, FT1_CFG_TYPE_EQ); + + /* Enable the FT1 slot in OR enable for all classifiers */ + for (i = 0; i < ICSSG_NUM_CLASSIFIERS_IN_USE; i++) { + val = rx_class_get_or(miig_rt, slice, i); + val |= RX_CLASS_FT_FT1_MATCH(slot); + rx_class_set_or(miig_rt, slice, i, val); + } +} + +/* disable all RX traffic */ +void icssg_class_disable(struct regmap *miig_rt, int slice) +{ + u32 data, offset; + int n; + + /* Enable RX_L2_G */ + regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, ICSSG_CFG_RX_L2_G_EN, + ICSSG_CFG_RX_L2_G_EN); + + for (n = 0; n < ICSSG_NUM_CLASSIFIERS; n++) { + /* AND_EN = 0 */ + rx_class_set_and(miig_rt, slice, n, 0); + /* OR_EN = 0 */ + rx_class_set_or(miig_rt, slice, n, 0); + + /* set CFG1 to OR */ + rx_class_sel_set_type(miig_rt, slice, n, RX_CLASS_SEL_TYPE_OR); + + /* configure gate */ + offset = RX_CLASS_GATES_N_REG(slice, n); + regmap_read(miig_rt, offset, &data); + /* clear class_raw so we go through filters */ + data &= ~RX_CLASS_GATES_RAW_MASK; + /* set allow and phase mask */ + data |= RX_CLASS_GATES_ALLOW_MASK | RX_CLASS_GATES_PHASE_MASK; + regmap_write(miig_rt, offset, data); + } + + /* FT1 Disabled */ + for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++) { + u8 addr[] = { 0, 0, 0, 0, 0, 0, }; + + rx_class_ft1_cfg_set_type(miig_rt, slice, n, + FT1_CFG_TYPE_DISABLED); + rx_class_ft1_set_da(miig_rt, slice, n, addr); + rx_class_ft1_set_da_mask(miig_rt, slice, n, addr); + } + + /* clear CFG2 */ + regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0); +} + +void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti, + bool is_sr1) +{ + u32 data; + int n; + int classifiers_in_use = ICSSG_NUM_CLASSIFIERS_IN_USE; + + if (!is_sr1) + classifiers_in_use = 1; + + /* defaults */ + icssg_class_disable(miig_rt, slice); + + /* Setup Classifier */ + for (n = 0; n < classifiers_in_use; n++) { + /* match on Broadcast or MAC_PRU address */ + data = RX_CLASS_FT_BC | RX_CLASS_FT_DA_P; + + /* multicast? */ + if (allmulti) + data |= RX_CLASS_FT_MC; + + rx_class_set_or(miig_rt, slice, n, data); + + /* set CFG1 for OR_OR_AND for classifier */ + rx_class_sel_set_type(miig_rt, slice, n, + RX_CLASS_SEL_TYPE_OR_OR_AND); + } + + /* clear CFG2 */ + regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0); +} + +void icssg_class_promiscuous_sr1(struct regmap *miig_rt, int slice) +{ + u32 data; + u32 offset; + int n; + + /* defaults */ + icssg_class_disable(miig_rt, slice); + + /* Setup Classifier */ + for (n = 0; n < ICSSG_NUM_CLASSIFIERS_IN_USE; n++) { + /* set RAW_MASK to bypass filters */ + offset = RX_CLASS_GATES_N_REG(slice, n); + regmap_read(miig_rt, offset, &data); + data |= RX_CLASS_GATES_RAW_MASK; + regmap_write(miig_rt, offset, data); + } +} + +void icssg_class_add_mcast_sr1(struct regmap *miig_rt, int slice, + struct net_device *ndev) +{ + int slot; + struct netdev_hw_addr *ha; + u8 sr_addr[] = { 0x01, 0x80, 0xC2, 0, 0, 0, }; + u8 cb_addr[] = { 0x01, 0x00, 0x5e, 0, 0, 0, }; + u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, }; + + rx_class_ft1_set_start_len(miig_rt, slice, 0, 6); + /* reserve first 2 slots for + * 1) 01-80-C2-00-00-XX Known Service Ethernet Multicast addresses + * 2) 01-00-5e-00-00-XX Local Network Control Block + * (224.0.0.0 - 224.0.0.255 (224.0.0/24)) + */ + mask_addr[5] = 0xff; + icssg_class_ft1_add_mcast(miig_rt, slice, 0, sr_addr, mask_addr); + icssg_class_ft1_add_mcast(miig_rt, slice, 1, cb_addr, mask_addr); + mask_addr[5] = 0; + slot = 2; + netdev_for_each_mc_addr(ha, ndev) { + /* skip addresses matching reserved slots */ + if (!memcmp(sr_addr, ha->addr, 5) || + !memcmp(cb_addr, ha->addr, 5)) { + netdev_dbg(ndev, "mcast skip %pM\n", ha->addr); + continue; + } + + if (slot >= FT1_NUM_SLOTS) { + netdev_dbg(ndev, + "can't add more than %d MC addresses, enabling allmulti\n", + FT1_NUM_SLOTS); + icssg_class_default(miig_rt, slice, 1, 1); + break; + } + + netdev_dbg(ndev, "mcast add %pM\n", ha->addr); + icssg_class_ft1_add_mcast(miig_rt, slice, slot, + ha->addr, mask_addr); + slot++; + } +} + +/* required for SR2 for SAV check */ +void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr) +{ + u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, }; + + rx_class_ft1_set_start_len(miig_rt, slice, 0, 6); + rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr); + rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr); + rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ); +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_config.c b/drivers/net/ethernet/ti/icssg_config.c --- a/drivers/net/ethernet/ti/icssg_config.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_config.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,862 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ICSSG Ethernet driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include +#include "icssg_config.h" +#include "icssg_prueth.h" +#include "icssg_switch_map.h" +#include "icss_mii_rt.h" + +/* TX IPG Values to be set for 100M and 1G link speeds. These values are + * in ocp_clk cycles. So need change if ocp_clk is changed for a specific + * h/w design. + */ + +/* IPG is in core_clk cycles */ +#define MII_RT_TX_IPG_100M_SR1 0x166 +#define MII_RT_TX_IPG_1G_SR1 0x1a +#define MII_RT_TX_IPG_100M 0x17 +#define MII_RT_TX_IPG_1G 0xb + +#define ICSSG_QUEUES_MAX 64 +#define ICSSG_QUEUE_OFFSET 0xd00 +#define ICSSG_QUEUE_PEEK_OFFSET 0xe00 +#define ICSSG_QUEUE_CNT_OFFSET 0xe40 +#define ICSSG_QUEUE_RESET_OFFSET 0xf40 + +#define ICSSG_NUM_TX_QUEUES 8 + +#define RECYCLE_Q_SLICE0 16 +#define RECYCLE_Q_SLICE1 17 + +#define ICSSG_NUM_OTHER_QUEUES 5 /* port, host and special queues */ + +#define PORT_HI_Q_SLICE0 32 +#define PORT_LO_Q_SLICE0 33 +#define HOST_HI_Q_SLICE0 34 +#define HOST_LO_Q_SLICE0 35 +#define HOST_SPL_Q_SLICE0 40 /* Special Queue */ + +#define PORT_HI_Q_SLICE1 36 +#define PORT_LO_Q_SLICE1 37 +#define HOST_HI_Q_SLICE1 38 +#define HOST_LO_Q_SLICE1 39 +#define HOST_SPL_Q_SLICE1 41 /* Special Queue */ + +#define MII_RXCFG_DEFAULT (PRUSS_MII_RT_RXCFG_RX_ENABLE | \ + PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | \ + PRUSS_MII_RT_RXCFG_RX_L2_EN | \ + PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS) + +#define MII_TXCFG_DEFAULT (PRUSS_MII_RT_TXCFG_TX_ENABLE | \ + PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | \ + PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | \ + PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN) + +#define ICSSG_CFG_DEFAULT (ICSSG_CFG_TX_L1_EN | \ + ICSSG_CFG_TX_L2_EN | ICSSG_CFG_RX_L2_G_EN | \ + ICSSG_CFG_TX_PRU_EN | /* SR2.0 only */ \ + ICSSG_CFG_SGMII_MODE) + +#define FDB_GEN_CFG1 0x60 +#define SMEM_VLAN_OFFSET 8 +#define SMEM_VLAN_OFFSET_MASK GENMASK(25, 8) + +#define FDB_GEN_CFG2 0x64 +#define FDB_VLAN_EN BIT(6) +#define FDB_HOST_EN BIT(2) +#define FDB_PRU1_EN BIT(1) +#define FDB_PRU0_EN BIT(0) +#define FDB_EN_ALL (FDB_PRU0_EN | FDB_PRU1_EN | \ + FDB_HOST_EN | FDB_VLAN_EN) + +struct map { + int queue; + u32 pd_addr_start; + u32 flags; + bool special; +}; + +struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = { + { + { PORT_HI_Q_SLICE0, PORT_DESC0_HI, 0x200000, 0 }, + { PORT_LO_Q_SLICE0, PORT_DESC0_LO, 0, 0 }, + { HOST_HI_Q_SLICE0, HOST_DESC0_HI, 0x200000, 0 }, + { HOST_LO_Q_SLICE0, HOST_DESC0_LO, 0, 0 }, + { HOST_SPL_Q_SLICE0, HOST_SPPD0, 0x400000, 1 }, + }, + { + { PORT_HI_Q_SLICE1, PORT_DESC1_HI, 0xa00000, 0 }, + { PORT_LO_Q_SLICE1, PORT_DESC1_LO, 0x800000, 0 }, + { HOST_HI_Q_SLICE1, HOST_DESC1_HI, 0xa00000, 0 }, + { HOST_LO_Q_SLICE1, HOST_DESC1_LO, 0x800000, 0 }, + { HOST_SPL_Q_SLICE1, HOST_SPPD1, 0xc00000, 1 }, + }, +}; + +static void icssg_config_mii_init_switch(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + struct regmap *mii_rt = prueth->mii_rt; + int mii = prueth_emac_slice(emac); + u32 rxcfg_reg, txcfg_reg, pcnt_reg; + u32 rxcfg, txcfg; + + rxcfg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_RXCFG0 : + PRUSS_MII_RT_RXCFG1; + txcfg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 : + PRUSS_MII_RT_TXCFG1; + pcnt_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 : + PRUSS_MII_RT_RX_PCNT1; + + rxcfg = PRUSS_MII_RT_RXCFG_RX_ENABLE | + PRUSS_MII_RT_RXCFG_RX_L2_EN | + PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS; + + txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE | + PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | + PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN; + + if (mii == ICSS_MII1) + rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL; + + if (emac->phy_if == PHY_INTERFACE_MODE_MII && mii == ICSS_MII1) + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + else if (emac->phy_if != PHY_INTERFACE_MODE_MII && mii == ICSS_MII0) + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + + regmap_write(mii_rt, rxcfg_reg, rxcfg); + regmap_write(mii_rt, txcfg_reg, txcfg); + regmap_write(mii_rt, pcnt_reg, 0x1); +} + +static void icssg_config_mii_init(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + struct regmap *mii_rt = prueth->mii_rt; + int slice = prueth_emac_slice(emac); + u32 rxcfg_reg, txcfg_reg, pcnt_reg; + u32 rxcfg, txcfg; + + rxcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RXCFG0 : + PRUSS_MII_RT_RXCFG1; + txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 : + PRUSS_MII_RT_TXCFG1; + pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 : + PRUSS_MII_RT_RX_PCNT1; + + rxcfg = MII_RXCFG_DEFAULT; + txcfg = MII_TXCFG_DEFAULT; + + if (slice == ICSS_MII1) + rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL; + + /* In MII mode TX lines swapped inside ICSSG, so TX_MUX_SEL cfg need + * to be swapped also comparing to RGMII mode. TODO: errata? + */ + if (emac->phy_if == PHY_INTERFACE_MODE_MII && slice == ICSS_MII0) + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + else if (emac->phy_if != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1) + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + + regmap_write(mii_rt, rxcfg_reg, rxcfg); + regmap_write(mii_rt, txcfg_reg, txcfg); + regmap_write(mii_rt, pcnt_reg, 0x1); +} + +static void icssg_miig_queues_init(struct prueth *prueth, int slice) +{ + struct regmap *miig_rt = prueth->miig_rt; + void __iomem *smem = prueth->shram.va; + u8 pd[ICSSG_SPECIAL_PD_SIZE]; + int queue = 0, i, j; + u32 *pdword; + + /* reset hwqueues */ + if (slice) + queue = ICSSG_NUM_TX_QUEUES; + + for (i = 0; i < ICSSG_NUM_TX_QUEUES; i++) { + regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue); + queue++; + } + + queue = slice ? RECYCLE_Q_SLICE1 : RECYCLE_Q_SLICE0; + regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue); + + for (i = 0; i < ICSSG_NUM_OTHER_QUEUES; i++) { + regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, + hwq_map[slice][i].queue); + } + + /* initialize packet descriptors in SMEM */ + /* push pakcet descriptors to hwqueues */ + + pdword = (u32 *)pd; + for (j = 0; j < ICSSG_NUM_OTHER_QUEUES; j++) { + struct map *mp; + int pd_size, num_pds; + u32 pdaddr; + + mp = &hwq_map[slice][j]; + if (mp->special) { + pd_size = ICSSG_SPECIAL_PD_SIZE; + num_pds = ICSSG_NUM_SPECIAL_PDS; + } else { + pd_size = ICSSG_NORMAL_PD_SIZE; + num_pds = ICSSG_NUM_NORMAL_PDS; + } + + for (i = 0; i < num_pds; i++) { + memset(pd, 0, pd_size); + + pdword[0] &= cpu_to_le32(ICSSG_FLAG_MASK); + pdword[0] |= cpu_to_le32(mp->flags); + pdaddr = mp->pd_addr_start + i * pd_size; + + memcpy_toio(smem + pdaddr, pd, pd_size); + queue = mp->queue; + regmap_write(miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, + pdaddr); + } + } +} + +void icssg_config_ipg(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + + switch (emac->speed) { + case SPEED_1000: + icssg_mii_update_ipg(prueth->mii_rt, slice, prueth->is_sr1 ? + MII_RT_TX_IPG_1G_SR1 : MII_RT_TX_IPG_1G); + break; + case SPEED_100: + icssg_mii_update_ipg(prueth->mii_rt, slice, prueth->is_sr1 ? + MII_RT_TX_IPG_100M_SR1 : MII_RT_TX_IPG_100M); + break; + case SPEED_10: + /* Firmware hardcodes IPG for PG1. PG2 same as 100M */ + if (!prueth->is_sr1) + icssg_mii_update_ipg(prueth->mii_rt, slice, + MII_RT_TX_IPG_100M); + break; + default: + /* Other links speeds not supported */ + pr_err("Unsupported link speed\n"); + return; + } +} + +/* SR1: Set buffer sizes for the pools. There are 8 internal queues + * implemented in firmware, but only 4 tx channels/threads in the Egress + * direction to firmware. Need a high priority queue for management + * messages since they shouldn't be blocked even during high traffic + * situation. So use Q0-Q2 as data queues and Q3 as management queue + * in the max case. However for ease of configuration, use the max + * data queue + 1 for management message if we are not using max + * case. + * + * Allocate 4 MTU buffers per data queue. Firmware requires + * pool sizes to be set for internal queues. Set the upper 5 queue + * pool size to min size of 128 bytes since there are only 3 tx + * data channels and management queue requires only minimum buffer. + * i.e lower queues are used by driver and highest priority queue + * from that is used for management message. + */ + +static int emac_egress_buf_pool_size[] = { + PRUETH_EMAC_BUF_POOL_SIZE_SR1, PRUETH_EMAC_BUF_POOL_SIZE_SR1, + PRUETH_EMAC_BUF_POOL_SIZE_SR1, PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1, + PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1, PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1, + PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1, PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1}; + +void icssg_config_sr1(struct prueth *prueth, struct prueth_emac *emac, + int slice) +{ + void __iomem *va; + struct icssg_config_sr1 *config; + int i, index; + + va = prueth->shram.va + slice * ICSSG_CONFIG_OFFSET_SLICE1; + config = &prueth->config[slice]; + memset(config, 0, sizeof(*config)); + config->addr_lo = cpu_to_le32(lower_32_bits(prueth->msmcram.pa)); + config->addr_hi = cpu_to_le32(upper_32_bits(prueth->msmcram.pa)); + config->num_tx_threads = 0; + config->rx_flow_id = emac->rx_flow_id_base; /* flow id for host port */ + config->rx_mgr_flow_id = emac->rx_mgm_flow_id_base; /* for mgm ch */ + config->rand_seed = get_random_int(); + + for (i = PRUETH_EMAC_BUF_POOL_START_SR1; i < PRUETH_NUM_BUF_POOLS_SR1; + i++) { + index = i - PRUETH_EMAC_BUF_POOL_START_SR1; + config->tx_buf_sz[i] = + cpu_to_le32(emac_egress_buf_pool_size[index]); + } + + memcpy_toio(va, &prueth->config[slice], sizeof(prueth->config[slice])); +} + +static void emac_r30_cmd_init(struct prueth_emac *emac) +{ + int i; + struct icssg_r30_cmd *p; + + p = emac->dram.va + MGR_R30_CMD_OFFSET; + + for (i = 0; i < 4; i++) + writel(EMAC_NONE, &p->cmd[i]); +} + +static int emac_r30_is_done(struct prueth_emac *emac) +{ + const struct icssg_r30_cmd *p; + int i; + u32 cmd; + + p = emac->dram.va + MGR_R30_CMD_OFFSET; + + for (i = 0; i < 4; i++) { + cmd = readl(&p->cmd[i]); + if (cmd != EMAC_NONE) + return 0; + } + + return 1; +} + +static int prueth_switch_buffer_setup(struct prueth_emac *emac) +{ + struct icssg_buffer_pool_cfg *bpool_cfg; + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + struct icssg_rxq_ctx *rxq_ctx; + u32 addr; + int i; + + addr = lower_32_bits(prueth->msmcram.pa); + if (slice) + addr += PRUETH_NUM_BUF_POOLS_SR2 * PRUETH_EMAC_BUF_POOL_SIZE_SR2; + + if (addr % SZ_64K) { + dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n"); + return -EINVAL; + } + + bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET; + /* workaround for f/w bug. bpool 0 needs to be initilalized */ + for (i = 0; + i < PRUETH_NUM_BUF_POOLS_SR2; + i++) { + bpool_cfg[i].addr = cpu_to_le32(addr); + bpool_cfg[i].len = cpu_to_le32(PRUETH_EMAC_BUF_POOL_SIZE_SR2); + addr += PRUETH_EMAC_BUF_POOL_SIZE_SR2; + } + + if (!slice) + addr += PRUETH_NUM_BUF_POOLS_SR2 * PRUETH_EMAC_BUF_POOL_SIZE_SR2; + else + addr += PRUETH_SW_NUM_BUF_POOLS_HOST_SR2 * PRUETH_SW_BUF_POOL_SIZE_HOST_SR2; + + for (i = PRUETH_NUM_BUF_POOLS_SR2; + i < PRUETH_SW_NUM_BUF_POOLS_HOST_SR2 + PRUETH_NUM_BUF_POOLS_SR2; + i++) { + bpool_cfg[i].addr = cpu_to_le32(addr); + bpool_cfg[i].len = cpu_to_le32(PRUETH_SW_BUF_POOL_SIZE_HOST_SR2); + addr += PRUETH_SW_BUF_POOL_SIZE_HOST_SR2; + } + + if (!slice) + addr += PRUETH_SW_NUM_BUF_POOLS_HOST_SR2 * PRUETH_SW_BUF_POOL_SIZE_HOST_SR2; + else + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE * 2; + + /* Pre-emptible RX buffer queue */ + rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET; + for (i = 0; i < 3; i++) + rxq_ctx->start[i] = cpu_to_le32(addr); + + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; + rxq_ctx->end = cpu_to_le32(addr); + + /* Express RX buffer queue */ + rxq_ctx = emac->dram.va + HOST_RX_Q_EXP_CONTEXT_OFFSET; + for (i = 0; i < 3; i++) + rxq_ctx->start[i] = cpu_to_le32(addr); + + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; + rxq_ctx->end = cpu_to_le32(addr); + + return 0; +} + +static int prueth_emac_buffer_setup(struct prueth_emac *emac) +{ + struct icssg_buffer_pool_cfg *bpool_cfg; + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + struct icssg_rxq_ctx *rxq_ctx; + u32 addr; + int i; + + /* Layout to have 64KB aligned buffer pool + * |BPOOL0|BPOOL1|RX_CTX0|RX_CTX1| + */ + + addr = lower_32_bits(prueth->msmcram.pa); + if (slice) + addr += PRUETH_NUM_BUF_POOLS_SR2 * PRUETH_EMAC_BUF_POOL_SIZE_SR2; + + if (addr % SZ_64K) { + dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n"); + return -EINVAL; + } + + bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET; + /* workaround for f/w bug. bpool 0 needs to be initilalized */ + bpool_cfg[0].addr = cpu_to_le32(addr); + bpool_cfg[0].len = 0; + + for (i = PRUETH_EMAC_BUF_POOL_START_SR2; + i < (PRUETH_EMAC_BUF_POOL_START_SR2 + PRUETH_NUM_BUF_POOLS_SR2); + i++) { + bpool_cfg[i].addr = cpu_to_le32(addr); + bpool_cfg[i].len = cpu_to_le32(PRUETH_EMAC_BUF_POOL_SIZE_SR2); + addr += PRUETH_EMAC_BUF_POOL_SIZE_SR2; + } + + addr += PRUETH_NUM_BUF_POOLS_SR2 * PRUETH_EMAC_BUF_POOL_SIZE_SR2; + if (slice) + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE * 2; + + /* Pre-emptible RX buffer queue */ + rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET; + for (i = 0; i < 3; i++) + rxq_ctx->start[i] = cpu_to_le32(addr); + + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; + rxq_ctx->end = cpu_to_le32(addr); + + /* Express RX buffer queue */ + rxq_ctx = emac->dram.va + HOST_RX_Q_EXP_CONTEXT_OFFSET; + for (i = 0; i < 3; i++) + rxq_ctx->start[i] = cpu_to_le32(addr); + + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; + rxq_ctx->end = cpu_to_le32(addr); + + return 0; +} + +static void icssg_init_emac_mode(struct prueth *prueth) +{ + u8 mac[ETH_ALEN] = { 0 }; + + if (prueth->emacs_initialized) + return; + + regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, 0); + regmap_write(prueth->miig_rt, FDB_GEN_CFG2, 0); + /* Clear host MAC address */ + icssg_class_set_host_mac_addr(prueth->miig_rt, mac); +} + +static void icssg_init_switch_mode(struct prueth *prueth) +{ + int i; + u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET; + + if (prueth->emacs_initialized) + return; + + /* Set VLAN TABLE address base */ + regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, + addr << SMEM_VLAN_OFFSET); + /* Set enable VLAN aware mode, and FDBs for all PRUs */ + regmap_write(prueth->miig_rt, FDB_GEN_CFG2, FDB_EN_ALL); + prueth->vlan_tbl = prueth->shram.va + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET; + for (i = 0; i < SZ_4K - 1; i++) { + prueth->vlan_tbl[i].fid = i; + prueth->vlan_tbl[i].fid_c1 = 0; + } + + icssg_class_set_host_mac_addr(prueth->miig_rt, prueth->hw_bridge_dev->dev_addr); + icssg_set_pvid(prueth, prueth->default_vlan, PRUETH_PORT_HOST); +} + +int icssg_config_sr2(struct prueth *prueth, struct prueth_emac *emac, int slice) +{ + void *config = emac->dram.va + ICSSG_CONFIG_OFFSET; + u8 *cfg_byte_ptr = config; + struct icssg_flow_cfg *flow_cfg; + u32 mask; + int ret; + + if (prueth->is_switch_mode) + icssg_init_switch_mode(prueth); + else + icssg_init_emac_mode(prueth); + + memset_io(config, 0, TAS_GATE_MASK_LIST0); + icssg_miig_queues_init(prueth, slice); + + emac->speed = SPEED_1000; + emac->duplex = DUPLEX_FULL; + if (!phy_interface_mode_is_rgmii(emac->phy_if)) { + emac->speed = SPEED_100; + emac->duplex = DUPLEX_FULL; + } + regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET, ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT); + icssg_miig_set_interface_mode(prueth->miig_rt, slice, emac->phy_if); + if (prueth->is_switch_mode) + icssg_config_mii_init_switch(emac); + else + icssg_config_mii_init(emac); + icssg_config_ipg(emac); + icssg_update_rgmii_cfg(prueth->miig_rt, emac); + + /* set GPI mode */ + pruss_cfg_gpimode(prueth->pruss, prueth->pru_id[slice], + PRUSS_GPI_MODE_MII); + + /* enable XFR shift for PRU and RTU */ + mask = PRUSS_SPP_XFER_SHIFT_EN | PRUSS_SPP_RTU_XFR_SHIFT_EN; + pruss_cfg_update(prueth->pruss, PRUSS_CFG_SPP, mask, mask); + + /* set C28 to 0x100 */ + pru_rproc_set_ctable(prueth->pru[slice], PRU_C28, 0x100 << 8); + pru_rproc_set_ctable(prueth->rtu[slice], PRU_C28, 0x100 << 8); + pru_rproc_set_ctable(prueth->txpru[slice], PRU_C28, 0x100 << 8); + + flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET; + flow_cfg->rx_base_flow = cpu_to_le32(emac->rx_flow_id_base); + flow_cfg->mgm_base_flow = 0; + *(cfg_byte_ptr + SPL_PKT_DEFAULT_PRIORITY) = 0; + *(cfg_byte_ptr + QUEUE_NUM_UNTAGGED) = 0x0; + + if (prueth->is_switch_mode) + ret = prueth_switch_buffer_setup(emac); + else + ret = prueth_emac_buffer_setup(emac); + if (ret) + return ret; + + emac_r30_cmd_init(emac); + + return 0; +} + +/* commands to program ICSSG R30 registers */ +/* FIXME: fix hex magic numbers with macros */ +static struct icssg_r30_cmd emac_r32_bitmask[] = { + {{0xffff0004, 0xffff0100, 0xffff0100, EMAC_NONE}}, /* EMAC_PORT_DISABLE */ + {{0xfffb0040, 0xfeff0200, 0xfeff0200, EMAC_NONE}}, /* EMAC_PORT_BLOCK */ + {{0xffbb0000, 0xfcff0000, 0xdcff0000, EMAC_NONE}}, /* EMAC_PORT_FORWARD */ + {{0xffbb0000, 0xfcff0000, 0xfcff2000, EMAC_NONE}}, /* EMAC_PORT_FORWARD_WO_LEARNING */ + {{0xffff0001, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT ALL */ + {{0xfffe0002, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT TAGGED */ + {{0xfffc0000, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT UNTAGGED and PRIO */ + {{EMAC_NONE, 0xffff0020, EMAC_NONE, EMAC_NONE}}, /* TAS Trigger List change */ + {{EMAC_NONE, 0xdfff1000, EMAC_NONE, EMAC_NONE}}, /* TAS set state ENABLE*/ + {{EMAC_NONE, 0xefff2000, EMAC_NONE, EMAC_NONE}}, /* TAS set state RESET*/ + {{EMAC_NONE, 0xcfff0000, EMAC_NONE, EMAC_NONE}}, /* TAS set state DISABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xffff0400, EMAC_NONE}}, /* UC flooding ENABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xfbff0000, EMAC_NONE}}, /* UC flooding DISABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xffff0800, EMAC_NONE}}, /* MC flooding ENABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xf7ff0000, EMAC_NONE}}, /* MC flooding DISABLE*/ + {{EMAC_NONE, 0xffff4000, EMAC_NONE, EMAC_NONE}}, /* Preemption on Tx ENABLE*/ + {{EMAC_NONE, 0xbfff0000, EMAC_NONE, EMAC_NONE}}, /* Preemption on Tx DISABLE*/ + {{0xffff0010, EMAC_NONE, 0xffff0010, EMAC_NONE}}, /* VLAN AWARE*/ + {{0xffef0000, EMAC_NONE, 0xffef0000, EMAC_NONE}} /* VLAN UNWARE*/ +}; + +int emac_set_port_state(struct prueth_emac *emac, + enum icssg_port_state_cmd cmd) +{ + struct icssg_r30_cmd *p; + int ret = -ETIMEDOUT; + int timeout = 10; + int i; + + p = emac->dram.va + MGR_R30_CMD_OFFSET; + + if (cmd >= ICSSG_EMAC_PORT_MAX_COMMANDS) { + netdev_err(emac->ndev, "invalid port command\n"); + return -EINVAL; + } + + /* only one command at a time allowed to firmware */ + mutex_lock(&emac->cmd_lock); + + for (i = 0; i < 4; i++) + writel(emac_r32_bitmask[cmd].cmd[i], &p->cmd[i]); + + /* wait for done */ + while (timeout) { + if (emac_r30_is_done(emac)) { + ret = 0; + break; + } + + usleep_range(1000, 2000); + timeout--; + } + + if (ret == -ETIMEDOUT) + netdev_err(emac->ndev, "timeout waiting for command done\n"); + + mutex_unlock(&emac->cmd_lock); + + return ret; +} + +void icssg_config_set_speed(struct prueth_emac *emac) +{ + u8 fw_speed; + + if (emac->is_sr1) + return; + + switch (emac->speed) { + case SPEED_1000: + fw_speed = FW_LINK_SPEED_1G; + break; + case SPEED_100: + fw_speed = FW_LINK_SPEED_100M; + break; + case SPEED_10: + fw_speed = FW_LINK_SPEED_10M; + break; + default: + /* Other links speeds not supported */ + pr_err("Unsupported link speed\n"); + return; + } + + if (emac->duplex == DUPLEX_HALF) + fw_speed |= FW_LINK_SPEED_HD; + + writeb(fw_speed, emac->dram.va + PORT_LINK_SPEED_OFFSET); +} + +static void icssg_config_half_duplex_sr1(struct prueth_emac *emac) +{ + int slice = prueth_emac_slice(emac); + struct icssg_config_sr1 *config; + u32 val = get_random_int(); + void __iomem *va; + + va = emac->prueth->shram.va + slice * ICSSG_CONFIG_OFFSET_SLICE1; + config = (struct icssg_config_sr1 *)va; + + writel(val, &config->rand_seed); +} + +void icssg_config_half_duplex(struct prueth_emac *emac) +{ + u32 val; + + if (emac->is_sr1) + icssg_config_half_duplex_sr1(emac); + + val = get_random_int(); + writel(val, emac->dram.va + HD_RAND_SEED_OFFSET); +} + +int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd, + struct mgmt_cmd_rsp *rsp) +{ + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + int addr; + int i = 10000; + + addr = icssg_queue_pop(prueth, slice == 0 ? + ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1); + if (addr < 0) + return addr; + + /* First 4 bytes have FW owned buffer linking info which should + * not be touched + */ + memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd)); + icssg_queue_push(prueth, slice == 0 ? + ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr); + while (i--) { + addr = icssg_queue_pop(prueth, slice == 0 ? + ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1); + if (addr < 0) { + usleep_range(1000, 2000); + continue; + } + + memcpy_fromio(rsp, prueth->shram.va + addr, sizeof(*rsp)); + /* Return buffer back for to pool */ + icssg_queue_push(prueth, slice == 0 ? + ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr); + break; + } + if (i <= 0) { + netdev_err(emac->ndev, "Timedout sending HWQ message\n"); + return -EINVAL; + } + + return 0; +} + +int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr, + u8 vid, u8 fid_c2, bool add) +{ + struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 }; + struct mgmt_cmd fdb_cmd = { 0 }; + int slice = prueth_emac_slice(emac); + u8 mac_fid[ETH_ALEN + 2]; + u8 fid = vid; + int ret, i; + u16 fdb_slot; + + for (i = 0; i < ETH_ALEN; i++) + mac_fid[i] = addr[i]; + + /* 1-1 VID-FID mapping is already setup */ + mac_fid[ETH_ALEN] = fid; + mac_fid[ETH_ALEN + 1] = 0; + + fdb_slot = bitrev32(crc32_le(0, mac_fid, 8)) & PRUETH_SWITCH_FDB_MASK; + + fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER; + fdb_cmd.type = ICSSG_FW_MGMT_FDB_CMD_TYPE; + fdb_cmd.seqnum = ++(emac->prueth->icssg_hwcmdseq); + if (add) + fdb_cmd.param = ICSS_CMD_ADD_FDB; + else + fdb_cmd.param = ICSS_CMD_DEL_FDB; + + fdb_cmd.param |= (slice << 4); + + fid_c2 |= ICSSG_FDB_ENTRY_VALID; + memcpy(&fdb_cmd.cmd_args[0], addr, 4); + memcpy(&fdb_cmd.cmd_args[1], &addr[4], 2); + fdb_cmd.cmd_args[1] |= ((fid << 16) | (fid_c2 << 24)); + fdb_cmd.cmd_args[2] = fdb_slot; + + netdev_dbg(emac->ndev, "MAC %pM slot %X vlan %X FID %X\n", + addr, fdb_slot, vid, fid); + + ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp); + if (ret) + return ret; + + WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum); + if (fdb_cmd_rsp.status == 1) + return 0; + + return -EINVAL; +} + +int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr, + u8 vid) +{ + struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 }; + struct mgmt_cmd fdb_cmd = { 0 }; + int slice = prueth_emac_slice(emac); + struct prueth_fdb_slot *slot; + u8 mac_fid[ETH_ALEN + 2]; + u8 fid = vid; + int ret, i; + u16 fdb_slot; + + for (i = 0; i < ETH_ALEN; i++) + mac_fid[i] = addr[i]; + + /* 1-1 VID-FID mapping is already setup */ + mac_fid[ETH_ALEN] = fid; + mac_fid[ETH_ALEN + 1] = 0; + + fdb_slot = bitrev32(crc32_le(0, mac_fid, 8)) & PRUETH_SWITCH_FDB_MASK; + + fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER; + fdb_cmd.type = ICSSG_FW_MGMT_FDB_CMD_TYPE; + fdb_cmd.seqnum = ++(emac->prueth->icssg_hwcmdseq); + fdb_cmd.param = ICSS_CMD_GET_FDB_SLOT; + + fdb_cmd.param |= (slice << 4); + + memcpy(&fdb_cmd.cmd_args[0], addr, 4); + memcpy(&fdb_cmd.cmd_args[1], &addr[4], 2); + fdb_cmd.cmd_args[1] |= fid << 16; + fdb_cmd.cmd_args[2] = fdb_slot; + + ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp); + if (ret) + return ret; + + WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum); + + slot = emac->dram.va + FDB_CMD_BUFFER; + for (i = 0; i < 4; i++) { + if (ether_addr_equal(addr, slot->mac) && vid == slot->fid) + return (slot->fid_c2 & ~ICSSG_FDB_ENTRY_VALID); + slot++; + } + + return 0; +} + +void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask, + u8 untag_mask, bool add) +{ + struct prueth *prueth = emac->prueth; + struct prueth_vlan_tbl *tbl = prueth->vlan_tbl; + u8 fid_c1 = tbl[vid].fid_c1; + + /* FID_C1: bit0..2 port membership mask, + * bit3..5 tagging mask for each port + * bit6 Stream VID (not handled currently) + * bit7 MC flood (not handled currently) + */ + if (add) { + fid_c1 |= (port_mask | port_mask << 3); + fid_c1 &= ~(untag_mask << 3); + } else { + fid_c1 &= ~(port_mask | port_mask << 3); + } + + tbl[vid].fid_c1 = fid_c1; +} + +u16 icssg_get_pvid(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + u32 pvid; + + if (emac->port_id == PRUETH_PORT_MII0) + pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET); + else + pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET); + + pvid = pvid >> 24; + + return pvid; +} + +void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port) +{ + u32 pvid; + + /* only 256 VLANs are supported */ + pvid = cpu_to_be32((ETH_P_8021Q << 16) | (vid & 0xff)); + + if (port == PRUETH_PORT_MII0) + writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET); + else if (port == PRUETH_PORT_MII1) + writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET); + else + writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET); +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_config.h b/drivers/net/ethernet/ti/icssg_config.h --- a/drivers/net/ethernet/ti/icssg_config.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_config.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSG Ethernet driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#ifndef __NET_TI_ICSSG_CONFIG_H +#define __NET_TI_ICSSG_CONFIG_H + +struct icssg_buffer_pool_cfg { + __le32 addr; + __le32 len; +} __packed; + +struct icssg_flow_cfg { + __le16 rx_base_flow; + __le16 mgm_base_flow; +} __packed; + +/*------------------------ SR1.0 related --------------------------*/ + +/* Port queue size in MSMC from firmware + * PORTQSZ_HP .set (0x1800) + * PORTQSZ_HP2 .set (PORTQSZ_HP+128) ;include barrier area + * 0x1880 x 8 bytes per slice (port) + */ + +#define MSMC_RAM_SIZE_SR1 (SZ_64K + SZ_32K + SZ_2K) /* 0x1880 x 8 x 2 */ + +#define PRUETH_MAX_RX_MGM_DESC 8 +#define PRUETH_MAX_RX_FLOWS_SR1 4 /* excluding default flow */ +#define PRUETH_RX_FLOW_DATA_SR1 3 /* highest priority flow */ +#define PRUETH_MAX_RX_MGM_FLOWS 2 /* excluding default flow */ +#define PRUETH_RX_MGM_FLOW_RESPONSE 0 +#define PRUETH_RX_MGM_FLOW_TIMESTAMP 1 +#define PRUETH_RX_MGM_FLOW_OTHER 2 + +#define PRUETH_NUM_BUF_POOLS_SR1 16 +#define PRUETH_EMAC_BUF_POOL_START_SR1 8 +#define PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1 128 +#define PRUETH_EMAC_BUF_SIZE_SR1 1536 +#define PRUETH_EMAC_NUM_BUF_SR1 4 +#define PRUETH_EMAC_BUF_POOL_SIZE_SR1 (PRUETH_EMAC_NUM_BUF_SR1 * \ + PRUETH_EMAC_BUF_SIZE_SR1) +/* Config area lies in shared RAM */ +#define ICSSG_CONFIG_OFFSET_SLICE0 0 +#define ICSSG_CONFIG_OFFSET_SLICE1 0x8000 + +struct icssg_config_sr1 { + __le32 status; /* Firmware status */ + __le32 addr_lo; /* MSMC Buffer pool base address low. */ + __le32 addr_hi; /* MSMC Buffer pool base address high. Must be 0 */ + __le32 tx_buf_sz[16]; /* Array of buffer pool sizes */ + __le32 num_tx_threads; /* Number of active egress threads, 1 to 4 */ + __le32 tx_rate_lim_en; /* Bitmask: Egress rate limit en per thread */ + __le32 rx_flow_id; /* RX flow id for first rx ring */ + __le32 rx_mgr_flow_id; /* RX flow id for the first management ring */ + __le32 flags; /* TBD */ + __le32 n_burst; /* for debug */ + __le32 rtu_status; /* RTU status */ + __le32 info; /* reserved */ + __le32 reserve; + __le32 rand_seed; /* Used for the random number generation at fw */ +} __packed; + +/* Shutdown command to stop processing at firmware. + * Command format : 0x8101ss00. ss - sequence number. Currently not used + * by driver. + */ +#define ICSSG_SHUTDOWN_CMD 0x81010000 + +/* pstate speed/duplex command to set speed and duplex settings + * in firmware. + * Command format : 0x8102ssPN. ss - sequence number: currently not + * used by driver, P - port number: For switch, N - Speed/Duplex state + * - Possible values of N: + * 0x0 - 10Mbps/Half duplex ; + * 0x8 - 10Mbps/Full duplex ; + * 0x2 - 100Mbps/Half duplex; + * 0xa - 100Mbps/Full duplex; + * 0xc - 1Gbps/Full duplex; + * NOTE: The above are same as bits [3..1](slice 0) or bits [8..6](slice 1) of + * RGMII CFG register. So suggested to read the register to populate the command + * bits. + */ +#define ICSSG_PSTATE_SPEED_DUPLEX_CMD 0x81020000 + +/*------------------------ SR2.0 related --------------------------*/ + +#define PRUETH_PKT_TYPE_CMD 0x10 +#define PRUETH_NAV_PS_DATA_SIZE 16 /* Protocol specific data size */ +#define PRUETH_NAV_SW_DATA_SIZE 16 /* SW related data size */ +#define PRUETH_MAX_TX_DESC 512 +#define PRUETH_MAX_RX_DESC 512 +#define PRUETH_MAX_RX_FLOWS_SR2 1 /* excluding default flow */ +#define PRUETH_RX_FLOW_DATA_SR2 0 /* FIXME: f/w bug to change to highest priority flow */ + +#define PRUETH_EMAC_BUF_POOL_SIZE_SR2 SZ_8K +#define PRUETH_EMAC_POOLS_PER_SLICE 24 +#define PRUETH_EMAC_BUF_POOL_START_SR2 8 +#define PRUETH_NUM_BUF_POOLS_SR2 8 +#define PRUETH_EMAC_RX_CTX_BUF_SIZE SZ_16K /* per slice */ +#define MSMC_RAM_SIZE_SR2 \ + (2 * (PRUETH_EMAC_BUF_POOL_SIZE_SR2 * PRUETH_NUM_BUF_POOLS_SR2 + \ + PRUETH_EMAC_RX_CTX_BUF_SIZE * 2)) + +#define PRUETH_SW_BUF_POOL_SIZE_HOST_SR2 SZ_2K +#define PRUETH_SW_NUM_BUF_POOLS_HOST_SR2 16 +#define MSMC_RAM_SIZE_SR2_SWITCH_MODE \ + (MSMC_RAM_SIZE_SR2 + \ + (2 * PRUETH_SW_BUF_POOL_SIZE_HOST_SR2 * PRUETH_SW_NUM_BUF_POOLS_HOST_SR2)) + +#define PRUETH_SWITCH_FDB_MASK ((SIZE_OF_FDB / NUMBER_OF_FDB_BUCKET_ENTRIES) - 1) + +struct icssg_rxq_ctx { + __le32 start[3]; + __le32 end; +} __packed; + +/* Load time Fiwmware Configuration */ + +#define ICSSG_FW_MGMT_CMD_HEADER 0x81 +#define ICSSG_FW_MGMT_FDB_CMD_TYPE 0x03 +#define ICSSG_FW_MGMT_CMD_TYPE 0x04 +#define ICSSG_FW_MGMT_PKT 0x80000000 + +struct icssg_r30_cmd { + u32 cmd[4]; +} __packed; + +enum icssg_port_state_cmd { + ICSSG_EMAC_PORT_DISABLE = 0, + ICSSG_EMAC_PORT_BLOCK, + ICSSG_EMAC_PORT_FORWARD, + ICSSG_EMAC_PORT_FORWARD_WO_LEARNING, + ICSSG_EMAC_PORT_ACCEPT_ALL, + ICSSG_EMAC_PORT_ACCEPT_TAGGED, + ICSSG_EMAC_PORT_ACCEPT_UNTAGGED_N_PRIO, + ICSSG_EMAC_PORT_TAS_TRIGGER, + ICSSG_EMAC_PORT_TAS_ENABLE, + ICSSG_EMAC_PORT_TAS_RESET, + ICSSG_EMAC_PORT_TAS_DISABLE, + ICSSG_EMAC_PORT_UC_FLOODING_ENABLE, + ICSSG_EMAC_PORT_UC_FLOODING_DISABLE, + ICSSG_EMAC_PORT_MC_FLOODING_ENABLE, + ICSSG_EMAC_PORT_MC_FLOODING_DISABLE, + ICSSG_EMAC_PORT_PREMPT_TX_ENABLE, + ICSSG_EMAC_PORT_PREMPT_TX_DISABLE, + ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE, + ICSSG_EMAC_PORT_VLAN_AWARE_DISABLE, + ICSSG_EMAC_PORT_MAX_COMMANDS +}; + +#define EMAC_NONE 0xffff0000 +#define EMAC_PRU0_P_DI 0xffff0004 +#define EMAC_PRU1_P_DI 0xffff0040 +#define EMAC_TX_P_DI 0xffff0100 + +#define EMAC_PRU0_P_EN 0xfffb0000 +#define EMAC_PRU1_P_EN 0xffbf0000 +#define EMAC_TX_P_EN 0xfeff0000 + +#define EMAC_P_BLOCK 0xffff0040 +#define EMAC_TX_P_BLOCK 0xffff0200 +#define EMAC_P_UNBLOCK 0xffbf0000 +#define EMAC_TX_P_UNBLOCK 0xfdff0000 +#define EMAC_LEAN_EN 0xfff70000 +#define EMAC_LEAN_DI 0xffff0008 + +#define EMAC_ACCEPT_ALL 0xffff0001 +#define EMAC_ACCEPT_TAG 0xfffe0002 +#define EMAC_ACCEPT_PRIOR 0xfffc0000 + +/* Config area lies in DRAM */ +#define ICSSG_CONFIG_OFFSET 0x0 + +#define ICSSG_NUM_NORMAL_PDS 64 +#define ICSSG_NUM_SPECIAL_PDS 16 + +#define ICSSG_NORMAL_PD_SIZE 8 +#define ICSSG_SPECIAL_PD_SIZE 20 + +#define ICSSG_FLAG_MASK 0xff00ffff + +struct icssg_setclock_desc { + u8 request; + u8 restore; + u8 acknowledgment; + u8 cmp_status; + u32 margin; + u32 cyclecounter0_set; + u32 cyclecounter1_set; + u32 iepcount_set; + u32 rsvd1; + u32 rsvd2; + u32 CMP0_current; + u32 iepcount_current; + u32 difference; + u32 cyclecounter0_new; + u32 cyclecounter1_new; + u32 CMP0_new; +} __packed; + +#define ICSSG_CMD_POP_SLICE0 56 +#define ICSSG_CMD_POP_SLICE1 60 + +#define ICSSG_CMD_PUSH_SLICE0 57 +#define ICSSG_CMD_PUSH_SLICE1 61 + +#define ICSSG_RSP_POP_SLICE0 58 +#define ICSSG_RSP_POP_SLICE1 62 + +#define ICSSG_RSP_PUSH_SLICE0 56 +#define ICSSG_RSP_PUSH_SLICE1 60 + +#define ICSSG_TS_POP_SLICE0 59 +#define ICSSG_TS_POP_SLICE1 63 + +#define ICSSG_TS_PUSH_SLICE0 40 +#define ICSSG_TS_PUSH_SLICE1 41 + +struct mgmt_cmd { + u8 param; + u8 seqnum; + u8 type; + u8 header; + u32 cmd_args[3]; +} __packed; + +struct mgmt_cmd_rsp { + u32 reserved; + u8 status; + u8 seqnum; + u8 type; + u8 header; + u32 cmd_args[3]; +} __packed; + +/* FDB FID_C2 flag definitions */ +/* Indicates host port membership.*/ +#define ICSSG_FDB_ENTRY_P0_MEMBERSHIP BIT(0) +/* Indicates that MAC ID is connected to physical port 1 */ +#define ICSSG_FDB_ENTRY_P1_MEMBERSHIP BIT(1) +/* Indicates that MAC ID is connected to physical port 2 */ +#define ICSSG_FDB_ENTRY_P2_MEMBERSHIP BIT(2) +/* Ageable bit is set for learned entries and cleared for static entries */ +#define ICSSG_FDB_ENTRY_AGEABLE BIT(3) +/* If set for DA then packet is determined to be a special packet */ +#define ICSSG_FDB_ENTRY_BLOCK BIT(4) +/* If set for DA then the SA from the packet is not learned */ +#define ICSSG_FDB_ENTRY_SECURE BIT(5) +/* If set, it means packet has been seen recently with source address + FID + * matching MAC address/FID of entry + */ +#define ICSSG_FDB_ENTRY_TOUCHED BIT(6) +/* Set if entry is valid */ +#define ICSSG_FDB_ENTRY_VALID BIT(7) + +/** + * struct prueth_vlan_tbl - VLAN table entries struct in ICSSG SMEM + * @fid_c1: membership and forwarding rules flag to this table. See + * above to defines for bit definitions + * @fid: FDB index for this VID (there is 1-1 mapping b/w VID and FID) + */ +struct prueth_vlan_tbl { + u8 fid_c1; + u8 fid; +} __packed; + +/** + * struct prueth_fdb_slot - Result of FDB slot lookup + * @mac: MAC address + * @fid: fid to be associated with MAC + * @fid_c2: FID_C2 entry for this MAC + */ +struct prueth_fdb_slot { + u8 mac[ETH_ALEN]; + u8 fid; + u8 fid_c2; +} __packed; + +enum icssg_ietfpe_verify_states { + ICSSG_IETFPE_STATE_UNKNOWN = 0, + ICSSG_IETFPE_STATE_INITIAL, + ICSSG_IETFPE_STATE_VERIFYING, + ICSSG_IETFPE_STATE_SUCCEEDED, + ICSSG_IETFPE_STATE_FAILED, + ICSSG_IETFPE_STATE_DISABLED +}; +#endif /* __NET_TI_ICSSG_CONFIG_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_ethtool.c b/drivers/net/ethernet/ti/icssg_ethtool.c --- a/drivers/net/ethernet/ti/icssg_ethtool.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_ethtool.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG Ethernet driver + * + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include "icssg_prueth.h" +#include + +static u32 stats_base[] = { 0x54c, /* Slice 0 stats start */ + 0xb18, /* Slice 1 stats start */ +}; + +struct miig_stats_regs { + /* Rx */ + u32 rx_good_frames; + u32 rx_broadcast_frames; + u32 rx_multicast_frames; + u32 rx_crc_error_frames; + u32 rx_mii_error_frames; + u32 rx_odd_nibble_frames; + u32 rx_frame_max_size; + u32 rx_max_size_error_frames; + u32 rx_frame_min_size; + u32 rx_min_size_error_frames; + u32 rx_overrun_frames; + u32 rx_class0_hits; + u32 rx_class1_hits; + u32 rx_class2_hits; + u32 rx_class3_hits; + u32 rx_class4_hits; + u32 rx_class5_hits; + u32 rx_class6_hits; + u32 rx_class7_hits; + u32 rx_class8_hits; + u32 rx_class9_hits; + u32 rx_class10_hits; + u32 rx_class11_hits; + u32 rx_class12_hits; + u32 rx_class13_hits; + u32 rx_class14_hits; + u32 rx_class15_hits; + u32 rx_smd_frags; + u32 rx_bucket1_size; + u32 rx_bucket2_size; + u32 rx_bucket3_size; + u32 rx_bucket4_size; + u32 rx_64B_frames; + u32 rx_bucket1_frames; + u32 rx_bucket2_frames; + u32 rx_bucket3_frames; + u32 rx_bucket4_frames; + u32 rx_bucket5_frames; + u32 rx_total_bytes; + u32 rx_tx_total_bytes; + /* Tx */ + u32 tx_good_frames; + u32 tx_broadcast_frames; + u32 tx_multicast_frames; + u32 tx_odd_nibble_frames; + u32 tx_underflow_errors; + u32 tx_frame_max_size; + u32 tx_max_size_error_frames; + u32 tx_frame_min_size; + u32 tx_min_size_error_frames; + u32 tx_bucket1_size; + u32 tx_bucket2_size; + u32 tx_bucket3_size; + u32 tx_bucket4_size; + u32 tx_64B_frames; + u32 tx_bucket1_frames; + u32 tx_bucket2_frames; + u32 tx_bucket3_frames; + u32 tx_bucket4_frames; + u32 tx_bucket5_frames; + u32 tx_total_bytes; +}; + +#define ICSSG_STATS(field) \ +{ \ + #field, \ + offsetof(struct miig_stats_regs, field), \ +} + +struct icssg_stats { + char name[ETH_GSTRING_LEN]; + u32 offset; +}; + +static const struct icssg_stats icssg_ethtool_stats[] = { + /* Rx */ + ICSSG_STATS(rx_good_frames), + ICSSG_STATS(rx_broadcast_frames), + ICSSG_STATS(rx_multicast_frames), + ICSSG_STATS(rx_crc_error_frames), + ICSSG_STATS(rx_mii_error_frames), + ICSSG_STATS(rx_odd_nibble_frames), + ICSSG_STATS(rx_frame_max_size), + ICSSG_STATS(rx_max_size_error_frames), + ICSSG_STATS(rx_frame_min_size), + ICSSG_STATS(rx_min_size_error_frames), + ICSSG_STATS(rx_overrun_frames), + ICSSG_STATS(rx_class0_hits), + ICSSG_STATS(rx_class1_hits), + ICSSG_STATS(rx_class2_hits), + ICSSG_STATS(rx_class3_hits), + ICSSG_STATS(rx_class4_hits), + ICSSG_STATS(rx_class5_hits), + ICSSG_STATS(rx_class6_hits), + ICSSG_STATS(rx_class7_hits), + ICSSG_STATS(rx_class8_hits), + ICSSG_STATS(rx_class9_hits), + ICSSG_STATS(rx_class10_hits), + ICSSG_STATS(rx_class11_hits), + ICSSG_STATS(rx_class12_hits), + ICSSG_STATS(rx_class13_hits), + ICSSG_STATS(rx_class14_hits), + ICSSG_STATS(rx_class15_hits), + ICSSG_STATS(rx_smd_frags), + ICSSG_STATS(rx_bucket1_size), + ICSSG_STATS(rx_bucket2_size), + ICSSG_STATS(rx_bucket3_size), + ICSSG_STATS(rx_bucket4_size), + ICSSG_STATS(rx_64B_frames), + ICSSG_STATS(rx_bucket1_frames), + ICSSG_STATS(rx_bucket2_frames), + ICSSG_STATS(rx_bucket3_frames), + ICSSG_STATS(rx_bucket4_frames), + ICSSG_STATS(rx_bucket5_frames), + ICSSG_STATS(rx_total_bytes), + ICSSG_STATS(rx_tx_total_bytes), + /* Tx */ + ICSSG_STATS(tx_good_frames), + ICSSG_STATS(tx_broadcast_frames), + ICSSG_STATS(tx_multicast_frames), + ICSSG_STATS(tx_odd_nibble_frames), + ICSSG_STATS(tx_underflow_errors), + ICSSG_STATS(tx_frame_max_size), + ICSSG_STATS(tx_max_size_error_frames), + ICSSG_STATS(tx_frame_min_size), + ICSSG_STATS(tx_min_size_error_frames), + ICSSG_STATS(tx_bucket1_size), + ICSSG_STATS(tx_bucket2_size), + ICSSG_STATS(tx_bucket3_size), + ICSSG_STATS(tx_bucket4_size), + ICSSG_STATS(tx_64B_frames), + ICSSG_STATS(tx_bucket1_frames), + ICSSG_STATS(tx_bucket2_frames), + ICSSG_STATS(tx_bucket3_frames), + ICSSG_STATS(tx_bucket4_frames), + ICSSG_STATS(tx_bucket5_frames), + ICSSG_STATS(tx_total_bytes), +}; + +static void emac_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + strlcpy(info->driver, dev_driver_string(prueth->dev), + sizeof(info->driver)); + /* TODO: info->fw_version */ + strlcpy(info->bus_info, dev_name(prueth->dev), sizeof(info->bus_info)); +} + +static u32 emac_get_msglevel(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + return emac->msg_enable; +} + +static void emac_set_msglevel(struct net_device *ndev, u32 value) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + emac->msg_enable = value; +} + +static int emac_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *ecmd) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev) + return -EOPNOTSUPP; + + phy_ethtool_ksettings_get(emac->phydev, ecmd); + return 0; +} + +static int emac_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *ecmd) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev || phy_is_pseudo_fixed_link(emac->phydev)) + return -EOPNOTSUPP; + + return phy_ethtool_ksettings_set(emac->phydev, ecmd); +} + +static int emac_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev || phy_is_pseudo_fixed_link(emac->phydev)) + return -EOPNOTSUPP; + + return phy_ethtool_get_eee(emac->phydev, edata); +} + +static int emac_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev || phy_is_pseudo_fixed_link(emac->phydev)) + return -EOPNOTSUPP; + + return phy_ethtool_set_eee(emac->phydev, edata); +} + +static int emac_nway_reset(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev || phy_is_pseudo_fixed_link(emac->phydev)) + return -EOPNOTSUPP; + + return genphy_restart_aneg(emac->phydev); +} + +/* Ethtool priv_flags for IET/Frame Preemption configuration. + * TODO: This is a temporary solution until upstream interface + * is available. + */ +static const char emac_ethtool_priv_flags[][ETH_GSTRING_LEN] = { +#define EMAC_PRIV_IET_FRAME_PREEMPTION BIT(0) + "iet-frame-preemption", +#define EMAC_PRIV_IET_MAC_VERIFY BIT(1) + "iet-mac-verify", +}; + +static int emac_get_sset_count(struct net_device *ndev, int stringset) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + switch (stringset) { + case ETH_SS_STATS: + return ARRAY_SIZE(icssg_ethtool_stats); + case ETH_SS_PRIV_FLAGS: + if (!prueth->is_sr1) + return ARRAY_SIZE(emac_ethtool_priv_flags); + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } +} + +static void emac_get_strings(struct net_device *ndev, u32 stringset, u8 *data) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(icssg_ethtool_stats); i++) { + memcpy(p, icssg_ethtool_stats[i].name, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + case ETH_SS_PRIV_FLAGS: + if (prueth->is_sr1) + return; + + for (i = 0; i < ARRAY_SIZE(emac_ethtool_priv_flags); i++) { + memcpy(p, emac_ethtool_priv_flags[i], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + default: + break; + } +} + +static void emac_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, u64 *data) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int i; + int slice = prueth_emac_slice(emac); + u32 base = stats_base[slice]; + u32 val; + + for (i = 0; i < ARRAY_SIZE(icssg_ethtool_stats); i++) { + regmap_read(prueth->miig_rt, + base + icssg_ethtool_stats[i].offset, + &val); + data[i] = val; + } +} + +static int emac_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->phc_index = icss_iep_get_ptp_clock_idx(emac->iep); + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); + + return 0; +} + +static void emac_get_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + ch->max_rx = 1; + /* SR1 use high priority channel for management messages */ + ch->max_tx = emac->is_sr1 ? PRUETH_MAX_TX_QUEUES - 1 : + PRUETH_MAX_TX_QUEUES; + ch->rx_count = 1; + ch->tx_count = emac->is_sr1 ? emac->tx_ch_num - 1 : + emac->tx_ch_num; +} + +static int emac_set_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + /* verify we have at least one channel in each direction */ + /* TODO: remove below check before sending to LKML */ + if (!ch->rx_count || !ch->tx_count) + return -EINVAL; + + /* Check if interface is up. Can change the num queues when + * the interface is down. + */ + if (netif_running(emac->ndev)) + return -EBUSY; + + emac->tx_ch_num = ch->tx_count; + /* highest channel number for management messaging on SR1 */ + if (emac->is_sr1) + emac->tx_ch_num++; + + return 0; +} + +/* TODO : This is temporary until a formal ethtool interface become available + * in LKML to configure IET FPE. + */ +static u32 emac_get_ethtool_priv_flags(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth_qos_iet *iet = &emac->qos.iet; + u32 priv_flags = 0; + + if (emac->is_sr1) + return priv_flags; + + /* Port specific flags */ + if (iet->fpe_configured) + priv_flags |= EMAC_PRIV_IET_FRAME_PREEMPTION; + if (iet->mac_verify_configured) + priv_flags |= EMAC_PRIV_IET_MAC_VERIFY; + + return priv_flags; +} + +static int emac_set_ethtool_priv_flags(struct net_device *ndev, u32 flags) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth_qos_iet *iet = &emac->qos.iet; + int iet_fpe, mac_verify; + + if (emac->is_sr1) + return -EOPNOTSUPP; + + iet_fpe = !!(flags & EMAC_PRIV_IET_FRAME_PREEMPTION); + mac_verify = !!(flags & EMAC_PRIV_IET_MAC_VERIFY); + + if (netif_running(ndev)) + return -EBUSY; + + if (emac->tx_ch_num < 2 && iet_fpe) { + netdev_err(ndev, "IET fpe needs at least 2 h/w queues\n"); + return -EINVAL; + } + + if (mac_verify && (!iet->fpe_configured && !iet_fpe)) { + netdev_err(ndev, "Enable IET FPE for IET MAC verify\n"); + return -EINVAL; + } + + iet->fpe_configured = iet_fpe; + iet->mac_verify_configured = mac_verify; + + return 0; +} + +const struct ethtool_ops icssg_ethtool_ops = { + .get_drvinfo = emac_get_drvinfo, + .get_msglevel = emac_get_msglevel, + .set_msglevel = emac_set_msglevel, + .get_sset_count = emac_get_sset_count, + .get_strings = emac_get_strings, + .get_ethtool_stats = emac_get_ethtool_stats, + .get_ts_info = emac_get_ts_info, + .get_priv_flags = emac_get_ethtool_priv_flags, + .set_priv_flags = emac_set_ethtool_priv_flags, + + .get_channels = emac_get_channels, + .set_channels = emac_set_channels, + .get_link_ksettings = emac_get_link_ksettings, + .set_link_ksettings = emac_set_link_ksettings, + .get_link = ethtool_op_get_link, + .get_eee = emac_get_eee, + .set_eee = emac_set_eee, + .nway_reset = emac_nway_reset, +}; diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_mii_cfg.c b/drivers/net/ethernet/ti/icssg_mii_cfg.c --- a/drivers/net/ethernet/ti/icssg_mii_cfg.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_mii_cfg.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG Ethernet Driver + * + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include + +#include "icss_mii_rt.h" +#include "icssg_prueth.h" + +void icssg_mii_update_ipg(struct regmap *mii_rt, int mii, u32 ipg) +{ + u32 val; + + if (mii == ICSS_MII0) { + regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG0, ipg); + } else { + /* Errata workaround: IEP1 is not read by h/w unless IEP0 is written */ + regmap_read(mii_rt, PRUSS_MII_RT_TX_IPG0, &val); + regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG1, ipg); + regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG0, val); + } +} + +void icssg_update_rgmii_cfg(struct regmap *miig_rt, struct prueth_emac *emac) +{ + u32 gig_en_mask, gig_val = 0, full_duplex_mask, full_duplex_val = 0; + int slice = prueth_emac_slice(emac); + u32 inband_en_mask, inband_val = 0; + + gig_en_mask = (slice == ICSS_MII0) ? RGMII_CFG_GIG_EN_MII0 : + RGMII_CFG_GIG_EN_MII1; + if (emac->speed == SPEED_1000) + gig_val = gig_en_mask; + regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, gig_en_mask, gig_val); + + inband_en_mask = (slice == ICSS_MII0) ? RGMII_CFG_INBAND_EN_MII0 : + RGMII_CFG_INBAND_EN_MII1; + if (emac->speed == SPEED_10 && phy_interface_mode_is_rgmii(emac->phy_if)) + inband_val = inband_en_mask; + regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, inband_en_mask, inband_val); + + full_duplex_mask = (slice == ICSS_MII0) ? RGMII_CFG_FULL_DUPLEX_MII0 : + RGMII_CFG_FULL_DUPLEX_MII1; + if (emac->duplex == DUPLEX_FULL) + full_duplex_val = full_duplex_mask; + regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, full_duplex_mask, + full_duplex_val); +} + +void icssg_miig_set_interface_mode(struct regmap *miig_rt, int mii, phy_interface_t phy_if) +{ + u32 val, mask, shift; + + mask = mii == ICSS_MII0 ? ICSSG_CFG_MII0_MODE : ICSSG_CFG_MII1_MODE; + shift = mii == ICSS_MII0 ? ICSSG_CFG_MII0_MODE_SHIFT : ICSSG_CFG_MII1_MODE_SHIFT; + + val = MII_MODE_RGMII; + if (phy_if == PHY_INTERFACE_MODE_MII) + val = MII_MODE_MII; + + val <<= shift; + regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, mask, val); + regmap_read(miig_rt, ICSSG_CFG_OFFSET, &val); +} + +u32 icssg_rgmii_cfg_get_bitfield(struct regmap *miig_rt, u32 mask, u32 shift) +{ + u32 val; + + regmap_read(miig_rt, RGMII_CFG_OFFSET, &val); + val &= mask; + val >>= shift; + + return val; +} + +u32 icssg_rgmii_get_speed(struct regmap *miig_rt, int mii) +{ + u32 shift = RGMII_CFG_SPEED_MII0_SHIFT, mask = RGMII_CFG_SPEED_MII0; + + if (mii == ICSS_MII1) { + shift = RGMII_CFG_SPEED_MII1_SHIFT; + mask = RGMII_CFG_SPEED_MII1; + } + + return icssg_rgmii_cfg_get_bitfield(miig_rt, mask, shift); +} + +u32 icssg_rgmii_get_fullduplex(struct regmap *miig_rt, int mii) +{ + u32 shift = RGMII_CFG_FULLDUPLEX_MII0_SHIFT; + u32 mask = RGMII_CFG_FULLDUPLEX_MII0; + + if (mii == ICSS_MII1) { + shift = RGMII_CFG_FULLDUPLEX_MII1_SHIFT; + mask = RGMII_CFG_FULLDUPLEX_MII1; + } + + return icssg_rgmii_cfg_get_bitfield(miig_rt, mask, shift); +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_prueth.c b/drivers/net/ethernet/ti/icssg_prueth.c --- a/drivers/net/ethernet/ti/icssg_prueth.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_prueth.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,3173 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Texas Instruments ICSSG Ethernet Driver + * + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icssg_prueth.h" +#include "icssg_switchdev.h" +#include "icss_mii_rt.h" +#include "k3-cppi-desc-pool.h" + +#define PRUETH_MODULE_VERSION "0.1" +#define PRUETH_MODULE_DESCRIPTION "PRUSS ICSSG Ethernet driver" + +#define PRUETH_MIN_PKT_SIZE (VLAN_ETH_ZLEN) +#define PRUETH_MAX_PKT_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) + +/* Netif debug messages possible */ +#define PRUETH_EMAC_DEBUG (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | \ + NETIF_MSG_TX_QUEUED | \ + NETIF_MSG_INTR | \ + NETIF_MSG_TX_DONE | \ + NETIF_MSG_RX_STATUS | \ + NETIF_MSG_PKTDATA | \ + NETIF_MSG_HW | \ + NETIF_MSG_WOL) + +#define prueth_napi_to_emac(napi) container_of(napi, struct prueth_emac, napi) + +/* CTRLMMR_ICSSG_RGMII_CTRL register bits */ +#define ICSSG_CTRL_RGMII_ID_MODE BIT(24) + +#define IEP_DEFAULT_CYCLE_TIME_NS 1000000 /* 1 ms */ + +static int debug_level = -1; +module_param(debug_level, int, 0644); +MODULE_PARM_DESC(debug_level, "PRUETH debug level (NETIF_MSG bits)"); + +static void prueth_cleanup_rx_chns(struct prueth_emac *emac, + struct prueth_rx_chn *rx_chn, + int max_rflows) +{ + if (rx_chn->desc_pool) + k3_cppi_desc_pool_destroy(rx_chn->desc_pool); + + if (rx_chn->rx_chn) + k3_udma_glue_release_rx_chn(rx_chn->rx_chn); +} + +static void prueth_cleanup_tx_chns(struct prueth_emac *emac) +{ + int i; + + for (i = 0; i < emac->tx_ch_num; i++) { + struct prueth_tx_chn *tx_chn = &emac->tx_chns[i]; + + if (tx_chn->desc_pool) + k3_cppi_desc_pool_destroy(tx_chn->desc_pool); + + if (tx_chn->tx_chn) + k3_udma_glue_release_tx_chn(tx_chn->tx_chn); + + /* Assume prueth_cleanup_tx_chns() is called at the + * end after all channel resources are freed + */ + memset(tx_chn, 0, sizeof(*tx_chn)); + } +} + +static void prueth_ndev_del_tx_napi(struct prueth_emac *emac, int num) +{ + int i; + + for (i = 0; i < num; i++) { + struct prueth_tx_chn *tx_chn = &emac->tx_chns[i]; + + if (tx_chn->irq) + free_irq(tx_chn->irq, tx_chn); + netif_napi_del(&tx_chn->napi_tx); + } +} + +static void prueth_xmit_free(struct prueth_tx_chn *tx_chn, + struct cppi5_host_desc_t *desc) +{ + struct cppi5_host_desc_t *first_desc, *next_desc; + dma_addr_t buf_dma, next_desc_dma; + u32 buf_dma_len; + + first_desc = desc; + next_desc = first_desc; + + cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); + + dma_unmap_single(tx_chn->dma_dev, buf_dma, buf_dma_len, + DMA_TO_DEVICE); + + next_desc_dma = cppi5_hdesc_get_next_hbdesc(first_desc); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma); + while (next_desc_dma) { + next_desc = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, + next_desc_dma); + cppi5_hdesc_get_obuf(next_desc, &buf_dma, &buf_dma_len); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); + + dma_unmap_page(tx_chn->dma_dev, buf_dma, buf_dma_len, + DMA_TO_DEVICE); + + next_desc_dma = cppi5_hdesc_get_next_hbdesc(next_desc); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma); + + k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); + } + + k3_cppi_desc_pool_free(tx_chn->desc_pool, first_desc); +} + +static int emac_tx_complete_packets(struct prueth_emac *emac, int chn, + int budget) +{ + struct net_device *ndev = emac->ndev; + struct cppi5_host_desc_t *desc_tx; + struct netdev_queue *netif_txq; + struct prueth_tx_chn *tx_chn; + unsigned int total_bytes = 0; + struct sk_buff *skb; + dma_addr_t desc_dma; + int res, num_tx = 0; + void **swdata; + + tx_chn = &emac->tx_chns[chn]; + + while (budget--) { + res = k3_udma_glue_pop_tx_chn(tx_chn->tx_chn, &desc_dma); + if (res == -ENODATA) + break; + + /* teardown completion */ + if (cppi5_desc_is_tdcm(desc_dma)) { + if (atomic_dec_and_test(&emac->tdown_cnt)) + complete(&emac->tdown_complete); + break; + } + + desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, + desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_tx); + + /* was this command's TX complete? */ + if (emac->is_sr1 && *(swdata) == emac->cmd_data) { + prueth_xmit_free(tx_chn, desc_tx); + budget++; /* not a data packet */ + continue; + } + + skb = *(swdata); + prueth_xmit_free(tx_chn, desc_tx); + + ndev = skb->dev; + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + total_bytes += skb->len; + napi_consume_skb(skb, budget); + num_tx++; + } + + if (!num_tx) + return 0; + + netif_txq = netdev_get_tx_queue(ndev, chn); + netdev_tx_completed_queue(netif_txq, num_tx, total_bytes); + + if (netif_tx_queue_stopped(netif_txq)) { + /* If the TX queue was stopped, wake it now + * if we have enough room. + */ + __netif_tx_lock(netif_txq, smp_processor_id()); + if (netif_running(ndev) && + (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >= + MAX_SKB_FRAGS)) + netif_tx_wake_queue(netif_txq); + __netif_tx_unlock(netif_txq); + } + + return num_tx; +} + +static int emac_napi_tx_poll(struct napi_struct *napi_tx, int budget) +{ + struct prueth_tx_chn *tx_chn = prueth_napi_to_tx_chn(napi_tx); + struct prueth_emac *emac = tx_chn->emac; + int num_tx_packets; + + num_tx_packets = emac_tx_complete_packets(emac, tx_chn->id, budget); + + if (num_tx_packets < budget) { + napi_complete(napi_tx); + enable_irq(tx_chn->irq); + } + + return num_tx_packets; +} + +static irqreturn_t prueth_tx_irq(int irq, void *dev_id) +{ + struct prueth_tx_chn *tx_chn = dev_id; + + disable_irq_nosync(irq); + napi_schedule(&tx_chn->napi_tx); + + return IRQ_HANDLED; +} + +static int prueth_ndev_add_tx_napi(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + int i, ret; + + for (i = 0; i < emac->tx_ch_num; i++) { + struct prueth_tx_chn *tx_chn = &emac->tx_chns[i]; + + netif_tx_napi_add(emac->ndev, &tx_chn->napi_tx, + emac_napi_tx_poll, NAPI_POLL_WEIGHT); + ret = request_irq(tx_chn->irq, prueth_tx_irq, + IRQF_TRIGGER_HIGH, tx_chn->name, + tx_chn); + if (ret) { + netif_napi_del(&tx_chn->napi_tx); + dev_err(prueth->dev, "unable to request TX IRQ %d\n", + tx_chn->irq); + goto fail; + } + } + + return 0; +fail: + prueth_ndev_del_tx_napi(emac, i); + return ret; +} + +static int prueth_init_tx_chns(struct prueth_emac *emac) +{ + struct net_device *ndev = emac->ndev; + struct device *dev = emac->prueth->dev; + struct k3_udma_glue_tx_channel_cfg tx_cfg; + static const struct k3_ring_cfg ring_cfg = { + .elm_size = K3_RINGACC_RING_ELSIZE_8, + .mode = K3_RINGACC_RING_MODE_RING, + .flags = 0, + .size = PRUETH_MAX_TX_DESC, + }; + int ret, slice, i; + u32 hdesc_size; + + slice = prueth_emac_slice(emac); + if (slice < 0) + return slice; + + init_completion(&emac->tdown_complete); + + hdesc_size = cppi5_hdesc_calc_size(true, PRUETH_NAV_PS_DATA_SIZE, + PRUETH_NAV_SW_DATA_SIZE); + memset(&tx_cfg, 0, sizeof(tx_cfg)); + tx_cfg.swdata_size = PRUETH_NAV_SW_DATA_SIZE; + tx_cfg.tx_cfg = ring_cfg; + tx_cfg.txcq_cfg = ring_cfg; + + for (i = 0; i < emac->tx_ch_num; i++) { + struct prueth_tx_chn *tx_chn = &emac->tx_chns[i]; + + /* To differentiate channels for SLICE0 vs SLICE1 */ + snprintf(tx_chn->name, sizeof(tx_chn->name), + "tx%d-%d", slice, i); + + tx_chn->emac = emac; + tx_chn->id = i; + tx_chn->descs_num = PRUETH_MAX_TX_DESC; + + tx_chn->tx_chn = + k3_udma_glue_request_tx_chn(dev, tx_chn->name, + &tx_cfg); + if (IS_ERR(tx_chn->tx_chn)) { + ret = PTR_ERR(tx_chn->tx_chn); + tx_chn->tx_chn = NULL; + netdev_err(ndev, + "Failed to request tx dma ch: %d\n", ret); + goto fail; + } + + tx_chn->dma_dev = k3_udma_glue_tx_get_dma_device(tx_chn->tx_chn); + tx_chn->desc_pool = + k3_cppi_desc_pool_create_name(tx_chn->dma_dev, + tx_chn->descs_num, + hdesc_size, + tx_chn->name); + if (IS_ERR(tx_chn->desc_pool)) { + ret = PTR_ERR(tx_chn->desc_pool); + tx_chn->desc_pool = NULL; + netdev_err(ndev, "Failed to create tx pool: %d\n", ret); + goto fail; + } + + tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); + if (tx_chn->irq <= 0) { + ret = -EINVAL; + netdev_err(ndev, "failed to get tx irq\n"); + goto fail; + } + + snprintf(tx_chn->name, sizeof(tx_chn->name), "%s-tx%d", + dev_name(dev), tx_chn->id); + } + + return 0; + +fail: + prueth_cleanup_tx_chns(emac); + return ret; +} + +static int prueth_init_rx_chns(struct prueth_emac *emac, + struct prueth_rx_chn *rx_chn, + char *name, u32 max_rflows, + u32 max_desc_num) +{ + struct net_device *ndev = emac->ndev; + struct device *dev = emac->prueth->dev; + struct k3_udma_glue_rx_channel_cfg rx_cfg; + u32 fdqring_id; + u32 hdesc_size; + int i, ret = 0, slice; + + slice = prueth_emac_slice(emac); + if (slice < 0) + return slice; + + /* To differentiate channels for SLICE0 vs SLICE1 */ + snprintf(rx_chn->name, sizeof(rx_chn->name), "%s%d", name, slice); + + hdesc_size = cppi5_hdesc_calc_size(true, PRUETH_NAV_PS_DATA_SIZE, + PRUETH_NAV_SW_DATA_SIZE); + memset(&rx_cfg, 0, sizeof(rx_cfg)); + rx_cfg.swdata_size = PRUETH_NAV_SW_DATA_SIZE; + rx_cfg.flow_id_num = max_rflows; + rx_cfg.flow_id_base = -1; /* udmax will auto select flow id base */ + + /* init all flows */ + rx_chn->dev = dev; + rx_chn->descs_num = max_desc_num; + + rx_chn->rx_chn = k3_udma_glue_request_rx_chn(dev, rx_chn->name, + &rx_cfg); + if (IS_ERR(rx_chn->rx_chn)) { + ret = PTR_ERR(rx_chn->rx_chn); + rx_chn->rx_chn = NULL; + netdev_err(ndev, "Failed to request rx dma ch: %d\n", ret); + goto fail; + } + + rx_chn->dma_dev = k3_udma_glue_rx_get_dma_device(rx_chn->rx_chn); + rx_chn->desc_pool = k3_cppi_desc_pool_create_name(rx_chn->dma_dev, + rx_chn->descs_num, + hdesc_size, + rx_chn->name); + if (IS_ERR(rx_chn->desc_pool)) { + ret = PTR_ERR(rx_chn->desc_pool); + rx_chn->desc_pool = NULL; + netdev_err(ndev, "Failed to create rx pool: %d\n", ret); + goto fail; + } + + if (!strncmp(name, "rxmgm", 5)) { + emac->rx_mgm_flow_id_base = k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn); + netdev_dbg(ndev, "mgm flow id base = %d\n", + emac->rx_mgm_flow_id_base); + } else { + emac->rx_flow_id_base = k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn); + netdev_dbg(ndev, "flow id base = %d\n", + emac->rx_flow_id_base); + } + + fdqring_id = K3_RINGACC_RING_ID_ANY; + for (i = 0; i < rx_cfg.flow_id_num; i++) { + struct k3_ring_cfg rxring_cfg = { + .elm_size = K3_RINGACC_RING_ELSIZE_8, + .mode = K3_RINGACC_RING_MODE_RING, + .flags = 0, + }; + struct k3_ring_cfg fdqring_cfg = { + .elm_size = K3_RINGACC_RING_ELSIZE_8, + .flags = K3_RINGACC_RING_SHARED, + }; + struct k3_udma_glue_rx_flow_cfg rx_flow_cfg = { + .rx_cfg = rxring_cfg, + .rxfdq_cfg = fdqring_cfg, + .ring_rxq_id = K3_RINGACC_RING_ID_ANY, + .src_tag_lo_sel = + K3_UDMA_GLUE_SRC_TAG_LO_USE_REMOTE_SRC_TAG, + }; + + rx_flow_cfg.ring_rxfdq0_id = fdqring_id; + rx_flow_cfg.rx_cfg.size = max_desc_num; + rx_flow_cfg.rxfdq_cfg.size = max_desc_num; + rx_flow_cfg.rxfdq_cfg.mode = emac->prueth->pdata.fdqring_mode; + + ret = k3_udma_glue_rx_flow_init(rx_chn->rx_chn, + i, &rx_flow_cfg); + if (ret) { + netdev_err(ndev, "Failed to init rx flow%d %d\n", + i, ret); + goto fail; + } + if (!i) + fdqring_id = k3_udma_glue_rx_flow_get_fdq_id(rx_chn->rx_chn, + i); + rx_chn->irq[i] = k3_udma_glue_rx_get_irq(rx_chn->rx_chn, i); + if (rx_chn->irq[i] <= 0) { + netdev_err(ndev, "Failed to get rx dma irq"); + goto fail; + } + } + + return 0; + +fail: + prueth_cleanup_rx_chns(emac, rx_chn, max_rflows); + return ret; +} + +static int prueth_dma_rx_push(struct prueth_emac *emac, + struct sk_buff *skb, + struct prueth_rx_chn *rx_chn) +{ + struct cppi5_host_desc_t *desc_rx; + struct net_device *ndev = emac->ndev; + dma_addr_t desc_dma; + dma_addr_t buf_dma; + u32 pkt_len = skb_tailroom(skb); + void **swdata; + + desc_rx = k3_cppi_desc_pool_alloc(rx_chn->desc_pool); + if (!desc_rx) { + netdev_err(ndev, "rx push: failed to allocate descriptor\n"); + return -ENOMEM; + } + desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx); + + buf_dma = dma_map_single(rx_chn->dma_dev, skb->data, pkt_len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(rx_chn->dma_dev, buf_dma))) { + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + netdev_err(ndev, "rx push: failed to map rx pkt buffer\n"); + return -EINVAL; + } + + cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT, + PRUETH_NAV_PS_DATA_SIZE); + k3_udma_glue_rx_dma_to_cppi5_addr(rx_chn->rx_chn, &buf_dma); + cppi5_hdesc_attach_buf(desc_rx, buf_dma, skb_tailroom(skb), buf_dma, skb_tailroom(skb)); + + swdata = cppi5_hdesc_get_swdata(desc_rx); + *swdata = skb; + + return k3_udma_glue_push_rx_chn(rx_chn->rx_chn, 0, + desc_rx, desc_dma); +} + +static u64 icssg_ts_to_ns(u32 hi_sw, u32 hi, u32 lo, u32 cycle_time_ns) +{ + u32 iepcount_lo, iepcount_hi, hi_rollover_count; + u64 ns; + + iepcount_lo = lo & GENMASK(19, 0); + iepcount_hi = (hi & GENMASK(11, 0)) << 12 | lo >> 20; + hi_rollover_count = hi >> 11; + + ns = ((u64)hi_rollover_count) << 23 | (iepcount_hi + hi_sw); + ns = ns * cycle_time_ns + iepcount_lo; + + return ns; +} + +static void emac_rx_timestamp(struct prueth_emac *emac, + struct sk_buff *skb, u32 *psdata) +{ + struct skb_shared_hwtstamps *ssh; + u64 ns; + + if (emac->is_sr1) { + ns = (u64)psdata[1] << 32 | psdata[0]; + } else { + u32 hi_sw = readl(emac->prueth->shram.va + + TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET); + ns = icssg_ts_to_ns(hi_sw, psdata[1], psdata[0], + IEP_DEFAULT_CYCLE_TIME_NS); + } + + ssh = skb_hwtstamps(skb); + memset(ssh, 0, sizeof(*ssh)); + ssh->hwtstamp = ns_to_ktime(ns); +} + +static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id) +{ + struct prueth_rx_chn *rx_chn = &emac->rx_chns; + struct net_device *ndev = emac->ndev; + struct cppi5_host_desc_t *desc_rx; + dma_addr_t desc_dma, buf_dma; + u32 buf_dma_len, pkt_len, port_id = 0; + int ret; + void **swdata; + struct sk_buff *skb, *new_skb; + u32 *psdata; + + ret = k3_udma_glue_pop_rx_chn(rx_chn->rx_chn, flow_id, &desc_dma); + if (ret) { + if (ret != -ENODATA) + netdev_err(ndev, "rx pop: failed: %d\n", ret); + return ret; + } + + if (cppi5_desc_is_tdcm(desc_dma)) /* Teardown ? */ + return 0; + + desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma); + + swdata = cppi5_hdesc_get_swdata(desc_rx); + skb = *swdata; + + psdata = cppi5_hdesc_get_psdata(desc_rx); + /* RX HW timestamp */ + if (emac->rx_ts_enabled) + emac_rx_timestamp(emac, skb, psdata); + + cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); + pkt_len = cppi5_hdesc_get_pktlen(desc_rx); + /* firmware adds 4 CRC bytes, strip them */ + pkt_len -= 4; + cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); + + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + + skb->dev = ndev; + if (!netif_running(skb->dev)) { + dev_kfree_skb_any(skb); + return 0; + } + + new_skb = netdev_alloc_skb_ip_align(ndev, PRUETH_MAX_PKT_SIZE); + /* if allocation fails we drop the packet but push the + * descriptor back to the ring with old skb to prevent a stall + */ + if (!new_skb) { + ndev->stats.rx_dropped++; + new_skb = skb; + } else { + /* send the filled skb up the n/w stack */ + if (emac->prueth->is_switch_mode) + skb->offload_fwd_mark = emac->offload_fwd_mark; + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, ndev); + netif_receive_skb(skb); + ndev->stats.rx_bytes += pkt_len; + ndev->stats.rx_packets++; + } + + /* queue another RX DMA */ + ret = prueth_dma_rx_push(emac, new_skb, &emac->rx_chns); + if (WARN_ON(ret < 0)) { + dev_kfree_skb_any(new_skb); + ndev->stats.rx_errors++; + ndev->stats.rx_dropped++; + } + + return ret; +} + +static void prueth_rx_cleanup(void *data, dma_addr_t desc_dma) +{ + struct prueth_rx_chn *rx_chn = data; + struct cppi5_host_desc_t *desc_rx; + struct sk_buff *skb; + dma_addr_t buf_dma; + u32 buf_dma_len; + void **swdata; + + desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_rx); + skb = *swdata; + cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); + + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, + DMA_FROM_DEVICE); + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + + dev_kfree_skb_any(skb); +} + +static int emac_get_tx_ts(struct prueth_emac *emac, + struct emac_tx_ts_response *rsp) +{ + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + int addr; + + addr = icssg_queue_pop(prueth, slice == 0 ? + ICSSG_TS_POP_SLICE0 : ICSSG_TS_POP_SLICE1); + if (addr < 0) + return addr; + + memcpy_fromio(rsp, prueth->shram.va + addr, sizeof(*rsp)); + /* return buffer back for to pool */ + icssg_queue_push(prueth, slice == 0 ? + ICSSG_TS_PUSH_SLICE0 : ICSSG_TS_PUSH_SLICE1, addr); + + return 0; +} + +/* TODO: Convert this to use worker/workqueue mechanism to serialize the + * request to firmware + */ +static int emac_send_command_sr1(struct prueth_emac *emac, u32 cmd) +{ + dma_addr_t desc_dma, buf_dma; + struct prueth_tx_chn *tx_chn; + struct cppi5_host_desc_t *first_desc; + int ret = 0; + u32 *epib; + u32 *data = emac->cmd_data; + u32 pkt_len = sizeof(emac->cmd_data); + void **swdata; + + netdev_dbg(emac->ndev, "Sending cmd %x\n", cmd); + + /* only one command at a time allowed to firmware */ + mutex_lock(&emac->cmd_lock); + data[0] = cpu_to_le32(cmd); + + /* highest priority channel for management messages */ + tx_chn = &emac->tx_chns[emac->tx_ch_num - 1]; + + /* Map the linear buffer */ + buf_dma = dma_map_single(tx_chn->dma_dev, data, pkt_len, DMA_TO_DEVICE); + if (dma_mapping_error(tx_chn->dma_dev, buf_dma)) { + netdev_err(emac->ndev, "cmd %x: failed to map cmd buffer\n", cmd); + ret = -EINVAL; + goto err_unlock; + } + + first_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); + if (!first_desc) { + netdev_err(emac->ndev, "cmd %x: failed to allocate descriptor\n", cmd); + dma_unmap_single(tx_chn->dma_dev, buf_dma, pkt_len, DMA_TO_DEVICE); + ret = -ENOMEM; + goto err_unlock; + } + + cppi5_hdesc_init(first_desc, CPPI5_INFO0_HDESC_EPIB_PRESENT, + PRUETH_NAV_PS_DATA_SIZE); + cppi5_hdesc_set_pkttype(first_desc, PRUETH_PKT_TYPE_CMD); + epib = first_desc->epib; + epib[0] = 0; + epib[1] = 0; + + cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len); + swdata = cppi5_hdesc_get_swdata(first_desc); + *swdata = data; + + cppi5_hdesc_set_pktlen(first_desc, pkt_len); + desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, first_desc); + + /* send command */ + reinit_completion(&emac->cmd_complete); + ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + if (ret) { + netdev_err(emac->ndev, "cmd %x: push failed: %d\n", cmd, ret); + goto free_desc; + } + ret = wait_for_completion_timeout(&emac->cmd_complete, msecs_to_jiffies(100)); + if (!ret) + netdev_err(emac->ndev, "cmd %x: completion timeout\n", cmd); + + mutex_unlock(&emac->cmd_lock); + + return ret; +free_desc: + prueth_xmit_free(tx_chn, first_desc); +err_unlock: + mutex_unlock(&emac->cmd_lock); + + return ret; +} + +static void emac_change_port_speed_duplex(struct prueth_emac *emac) +{ + u32 cmd = ICSSG_PSTATE_SPEED_DUPLEX_CMD, val; + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + + /* only full duplex supported for now */ + if (emac->duplex != DUPLEX_FULL) + return; + + if (!emac->is_sr1) + return; + + val = icssg_rgmii_get_speed(prueth->miig_rt, slice); + /* firmware expects full duplex settings in bit 2-1 */ + val <<= 1; + cmd |= val; + + val = icssg_rgmii_get_fullduplex(prueth->miig_rt, slice); + /* firmware expects full duplex settings in bit 3 */ + val <<= 3; + cmd |= val; + + emac_send_command_sr1(emac, cmd); +} + +static int emac_shutdown(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + /* FIXME for SR2.0 */ + if (!emac->is_sr1) + return 0; + + return emac_send_command_sr1(emac, ICSSG_SHUTDOWN_CMD); +} + +static void tx_ts_work(struct prueth_emac *emac) +{ + u64 ns; + struct skb_shared_hwtstamps ssh; + struct sk_buff *skb; + int timeout = 10; + int ret = 0; + struct emac_tx_ts_response tsr; + u32 hi_sw; + + if (!test_bit(__STATE_TX_TS_IN_PROGRESS, &emac->state)) { + netdev_err(emac->ndev, "unexpected TS response\n"); + return; + } + + skb = emac->tx_ts_skb; + while (timeout-- > 0) { + /* wait for response or timeout */ + ret = emac_get_tx_ts(emac, &tsr); + if (!ret) + break; + usleep_range(10, 20); + } + + if (ret) { + netdev_err(emac->ndev, "TX timestamp timeout\n"); + goto error; + } + + if (tsr.cookie != emac->tx_ts_cookie) { + netdev_err(emac->ndev, "TX TS cookie mismatch 0x%x:0x%x\n", + tsr.cookie, emac->tx_ts_cookie); + goto error; + } + + hi_sw = readl(emac->prueth->shram.va + + TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET); + ns = icssg_ts_to_ns(hi_sw, tsr.hi_ts, tsr.lo_ts, + IEP_DEFAULT_CYCLE_TIME_NS); + + emac->tx_ts_cookie++; + memset(&ssh, 0, sizeof(ssh)); + ssh.hwtstamp = ns_to_ktime(ns); + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); + + skb_tstamp_tx(skb, &ssh); + dev_consume_skb_any(skb); + + return; + +error: + dev_kfree_skb_any(skb); + emac->tx_ts_skb = NULL; + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); +} + +/** + * emac_ndo_start_xmit - EMAC Transmit function + * @skb: SKB pointer + * @ndev: EMAC network adapter + * + * Called by the system to transmit a packet - we queue the packet in + * EMAC hardware transmit queue + * Doesn't wait for completion we'll check for TX completion in + * emac_tx_complete_packets(). + * + * Returns success(NETDEV_TX_OK) or error code (typically out of descs) + */ +static int emac_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct cppi5_host_desc_t *first_desc, *next_desc, *cur_desc; + struct netdev_queue *netif_txq; + struct prueth_tx_chn *tx_chn; + dma_addr_t desc_dma, buf_dma; + int i, ret = 0, q_idx; + bool in_tx_ts = 0; + void **swdata; + u32 pkt_len; + u32 *epib; + + pkt_len = skb_headlen(skb); + q_idx = skb_get_queue_mapping(skb); + + tx_chn = &emac->tx_chns[q_idx]; + netif_txq = netdev_get_tx_queue(ndev, q_idx); + + /* Map the linear buffer */ + buf_dma = dma_map_single(tx_chn->dma_dev, skb->data, pkt_len, DMA_TO_DEVICE); + if (dma_mapping_error(tx_chn->dma_dev, buf_dma)) { + netdev_err(ndev, "tx: failed to map skb buffer\n"); + ret = -EINVAL; + goto drop_stop_q; + } + + first_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); + if (!first_desc) { + netdev_dbg(ndev, "tx: failed to allocate descriptor\n"); + dma_unmap_single(tx_chn->dma_dev, buf_dma, pkt_len, DMA_TO_DEVICE); + ret = -ENOMEM; + goto drop_stop_q_busy; + } + + cppi5_hdesc_init(first_desc, CPPI5_INFO0_HDESC_EPIB_PRESENT, + PRUETH_NAV_PS_DATA_SIZE); + cppi5_hdesc_set_pkttype(first_desc, 0); + epib = first_desc->epib; + epib[0] = 0; + epib[1] = 0; + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + emac->tx_ts_enabled) { + /* We currently support only one TX HW timestamp at a time */ + if (!test_and_set_bit_lock(__STATE_TX_TS_IN_PROGRESS, + &emac->state)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + /* Request TX timestamp */ + epib[0] = emac->tx_ts_cookie; + epib[1] = 0x80000000; /* TX TS request */ + emac->tx_ts_skb = skb_get(skb); + in_tx_ts = 1; + } + } + + /* set dst tag to indicate internal qid at the firmware which is at + * bit8..bit15. bit0..bit7 indicates port num for directed + * packets in case of switch mode operation + */ + cppi5_desc_set_tags_ids(&first_desc->hdr, 0, (emac->port_id | (q_idx << 8))); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); + cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len); + swdata = cppi5_hdesc_get_swdata(first_desc); + *swdata = skb; + + if (!skb_is_nonlinear(skb)) + goto tx_push; + + /* Handle the case where skb is fragmented in pages */ + cur_desc = first_desc; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + u32 frag_size = skb_frag_size(frag); + + next_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); + if (!next_desc) { + netdev_err(ndev, + "tx: failed to allocate frag. descriptor\n"); + ret = -ENOMEM; + goto cleanup_tx_ts; + } + + buf_dma = skb_frag_dma_map(tx_chn->dma_dev, frag, 0, frag_size, + DMA_TO_DEVICE); + if (dma_mapping_error(tx_chn->dma_dev, buf_dma)) { + netdev_err(ndev, "tx: Failed to map skb page\n"); + k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); + ret = -EINVAL; + goto cleanup_tx_ts; + } + + cppi5_hdesc_reset_hbdesc(next_desc); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); + cppi5_hdesc_attach_buf(next_desc, + buf_dma, frag_size, buf_dma, frag_size); + + desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, + next_desc); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &desc_dma); + cppi5_hdesc_link_hbdesc(cur_desc, desc_dma); + + pkt_len += frag_size; + cur_desc = next_desc; + } + WARN_ON(pkt_len != skb->len); + +tx_push: + /* report bql before sending packet */ + netdev_tx_sent_queue(netif_txq, pkt_len); + + cppi5_hdesc_set_pktlen(first_desc, pkt_len); + desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, first_desc); + /* cppi5_desc_dump(first_desc, 64); */ + + skb_tx_timestamp(skb); /* SW timestamp if SKBTX_IN_PROGRESS not set */ + ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + if (ret) { + netdev_err(ndev, "tx: push failed: %d\n", ret); + goto drop_free_descs; + } + + if (k3_cppi_desc_pool_avail(tx_chn->desc_pool) < MAX_SKB_FRAGS) { + netif_tx_stop_queue(netif_txq); + /* Barrier, so that stop_queue visible to other cpus */ + smp_mb__after_atomic(); + + if (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >= + MAX_SKB_FRAGS) + netif_tx_wake_queue(netif_txq); + } + + return NETDEV_TX_OK; + +cleanup_tx_ts: + if (in_tx_ts) { + dev_kfree_skb_any(emac->tx_ts_skb); + emac->tx_ts_skb = NULL; + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); + } + +drop_free_descs: + prueth_xmit_free(tx_chn, first_desc); +drop_stop_q: + netif_tx_stop_queue(netif_txq); + dev_kfree_skb_any(skb); + + /* error */ + ndev->stats.tx_dropped++; + netdev_err(ndev, "tx: error: %d\n", ret); + + return ret; + +drop_stop_q_busy: + netif_tx_stop_queue(netif_txq); + return NETDEV_TX_BUSY; +} + +static void prueth_tx_cleanup(void *data, dma_addr_t desc_dma) +{ + struct prueth_tx_chn *tx_chn = data; + struct cppi5_host_desc_t *desc_tx; + struct sk_buff *skb; + void **swdata; + + desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_tx); + skb = *(swdata); + prueth_xmit_free(tx_chn, desc_tx); + + dev_kfree_skb_any(skb); +} + +static irqreturn_t prueth_tx_ts_irq(int irq, void *dev_id) +{ + struct prueth_emac *emac = dev_id; + + /* currently only TX timestamp is being returned */ + tx_ts_work(emac); + + return IRQ_HANDLED; +} + +/* get one packet from requested flow_id + * + * Returns skb pointer if packet found else NULL + * Caller must free the returned skb. + */ +static struct sk_buff *prueth_process_rx_mgm(struct prueth_emac *emac, + u32 flow_id) +{ + struct prueth_rx_chn *rx_chn = &emac->rx_mgm_chn; + struct net_device *ndev = emac->ndev; + struct cppi5_host_desc_t *desc_rx; + dma_addr_t desc_dma, buf_dma; + u32 buf_dma_len, pkt_len; + int ret; + void **swdata; + struct sk_buff *skb, *new_skb; + + ret = k3_udma_glue_pop_rx_chn(rx_chn->rx_chn, flow_id, &desc_dma); + if (ret) { + if (ret != -ENODATA) + netdev_err(ndev, "rx mgm pop: failed: %d\n", ret); + return NULL; + } + + if (cppi5_desc_is_tdcm(desc_dma)) /* Teardown */ + return NULL; + + desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma); + + /* Fix FW bug about incorrect PSDATA size */ + if (cppi5_hdesc_get_psdata_size(desc_rx) != PRUETH_NAV_PS_DATA_SIZE) { + cppi5_hdesc_update_psdata_size(desc_rx, + PRUETH_NAV_PS_DATA_SIZE); + } + + swdata = cppi5_hdesc_get_swdata(desc_rx); + skb = *swdata; + cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + pkt_len = cppi5_hdesc_get_pktlen(desc_rx); + + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + + new_skb = netdev_alloc_skb_ip_align(ndev, PRUETH_MAX_PKT_SIZE); + /* if allocation fails we drop the packet but push the + * descriptor back to the ring with old skb to prevent a stall + */ + if (!new_skb) { + netdev_err(ndev, + "skb alloc failed, dropped mgm pkt from flow %d\n", + flow_id); + new_skb = skb; + skb = NULL; /* return NULL */ + } else { + /* return the filled skb */ + skb_put(skb, pkt_len); + } + + /* queue another DMA */ + ret = prueth_dma_rx_push(emac, new_skb, &emac->rx_mgm_chn); + if (WARN_ON(ret < 0)) + dev_kfree_skb_any(new_skb); + + return skb; +} + +static void prueth_tx_ts_sr1(struct prueth_emac *emac, + struct emac_tx_ts_response_sr1 *tsr) +{ + u64 ns; + struct skb_shared_hwtstamps ssh; + struct sk_buff *skb; + + ns = (u64)tsr->hi_ts << 32 | tsr->lo_ts; + + if (!test_bit(__STATE_TX_TS_IN_PROGRESS, &emac->state)) { + netdev_err(emac->ndev, "unexpected TS response\n"); + return; + } + + skb = emac->tx_ts_skb; + if (tsr->cookie != emac->tx_ts_cookie) { + netdev_err(emac->ndev, "TX TS cookie mismatch 0x%x:0x%x\n", + tsr->cookie, emac->tx_ts_cookie); + goto error; + } + + emac->tx_ts_cookie++; + memset(&ssh, 0, sizeof(ssh)); + ssh.hwtstamp = ns_to_ktime(ns); + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); + + skb_tstamp_tx(skb, &ssh); + dev_consume_skb_any(skb); + + return; + +error: + dev_kfree_skb_any(skb); + emac->tx_ts_skb = NULL; + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); +} + +static irqreturn_t prueth_rx_mgm_ts_thread_sr1(int irq, void *dev_id) +{ + struct prueth_emac *emac = dev_id; + struct sk_buff *skb; + + skb = prueth_process_rx_mgm(emac, PRUETH_RX_MGM_FLOW_TIMESTAMP); + if (!skb) + return IRQ_NONE; + + prueth_tx_ts_sr1(emac, (void *)skb->data); + dev_kfree_skb_any(skb); + + return IRQ_HANDLED; +} + +static irqreturn_t prueth_rx_mgm_rsp_thread(int irq, void *dev_id) +{ + struct prueth_emac *emac = dev_id; + struct sk_buff *skb; + u32 rsp; + + skb = prueth_process_rx_mgm(emac, PRUETH_RX_MGM_FLOW_RESPONSE); + if (!skb) + return IRQ_NONE; + + /* Process command response */ + rsp = le32_to_cpu(*(u32 *)skb->data); + if ((rsp & 0xffff0000) == ICSSG_SHUTDOWN_CMD) { + netdev_dbg(emac->ndev, + "f/w Shutdown cmd resp %x\n", rsp); + complete(&emac->cmd_complete); + } else if ((rsp & 0xffff0000) == + ICSSG_PSTATE_SPEED_DUPLEX_CMD) { + netdev_dbg(emac->ndev, + "f/w Speed/Duplex cmd rsp %x\n", + rsp); + complete(&emac->cmd_complete); + } + + dev_kfree_skb_any(skb); + + return IRQ_HANDLED; +} + +static irqreturn_t prueth_rx_irq(int irq, void *dev_id) +{ + struct prueth_emac *emac = dev_id; + + disable_irq_nosync(irq); + napi_schedule(&emac->napi_rx); + + return IRQ_HANDLED; +} + +struct icssg_firmwares { + char *pru; + char *rtu; + char *txpru; +}; + +static struct icssg_firmwares icssg_switch_firmwares[] = { + { + .pru = "ti-pruss/am65x-sr2-pru0-prusw-fw.elf", + .rtu = "ti-pruss/am65x-sr2-rtu0-prusw-fw.elf", + .txpru = "ti-pruss/am65x-sr2-txpru0-prusw-fw.elf", + }, + { + .pru = "ti-pruss/am65x-sr2-pru1-prusw-fw.elf", + .rtu = "ti-pruss/am65x-sr2-rtu1-prusw-fw.elf", + .txpru = "ti-pruss/am65x-sr2-txpru1-prusw-fw.elf", + } +}; + +static struct icssg_firmwares icssg_emac_firmwares[] = { + { + .pru = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + .rtu = "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + .txpru = "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + }, + { + .pru = "ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + .rtu = "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + .txpru = "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf", + } +}; + +static struct icssg_firmwares icssg_emac_firmwares_sr1[] = { + { + .pru = "ti-pruss/am65x-pru0-prueth-fw.elf", + .rtu = "ti-pruss/am65x-rtu0-prueth-fw.elf", + }, + { + .pru = "ti-pruss/am65x-pru1-prueth-fw.elf", + .rtu = "ti-pruss/am65x-rtu1-prueth-fw.elf", + } +}; + +static int prueth_emac_start(struct prueth *prueth, struct prueth_emac *emac) +{ + struct icssg_firmwares *firmwares; + struct device *dev = prueth->dev; + int slice, ret; + + if (prueth->is_switch_mode) + firmwares = icssg_switch_firmwares; + else if (prueth->is_sr1) + firmwares = icssg_emac_firmwares_sr1; + else + firmwares = icssg_emac_firmwares; + + slice = prueth_emac_slice(emac); + if (slice < 0) { + netdev_err(emac->ndev, "invalid port\n"); + return -EINVAL; + } + + /* Set Load time configuration */ + if (emac->is_sr1) { + icssg_config_sr1(prueth, emac, slice); + } else { + ret = icssg_config_sr2(prueth, emac, slice); + if (ret) + return ret; + } + + ret = rproc_set_firmware(prueth->pru[slice], firmwares[slice].pru); + ret = rproc_boot(prueth->pru[slice]); + if (ret) { + dev_err(dev, "failed to boot PRU%d: %d\n", slice, ret); + return -EINVAL; + } + + ret = rproc_set_firmware(prueth->rtu[slice], firmwares[slice].rtu); + ret = rproc_boot(prueth->rtu[slice]); + if (ret) { + dev_err(dev, "failed to boot RTU%d: %d\n", slice, ret); + goto halt_pru; + } + + if (emac->is_sr1) + goto done; + + ret = rproc_set_firmware(prueth->txpru[slice], firmwares[slice].txpru); + ret = rproc_boot(prueth->txpru[slice]); + if (ret) { + dev_err(dev, "failed to boot TX_PRU%d: %d\n", slice, ret); + goto halt_rtu; + } + +done: + emac->fw_running = 1; + return 0; + +halt_rtu: + rproc_shutdown(prueth->rtu[slice]); + +halt_pru: + rproc_shutdown(prueth->pru[slice]); + + return ret; +} + +static void prueth_emac_stop(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + int slice; + + switch (emac->port_id) { + case PRUETH_PORT_MII0: + slice = ICSS_SLICE0; + break; + case PRUETH_PORT_MII1: + slice = ICSS_SLICE1; + break; + default: + netdev_err(emac->ndev, "invalid port\n"); + return; + } + + emac->fw_running = 0; + if (!emac->is_sr1) + rproc_shutdown(prueth->txpru[slice]); + rproc_shutdown(prueth->rtu[slice]); + rproc_shutdown(prueth->pru[slice]); +} + +/* called back by PHY layer if there is change in link state of hw port*/ +static void emac_adjust_link(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct phy_device *phydev = emac->phydev; + struct prueth *prueth = emac->prueth; + bool new_state = false; + unsigned long flags; + + if (phydev->link) { + /* check the mode of operation - full/half duplex */ + if (phydev->duplex != emac->duplex) { + new_state = true; + emac->duplex = phydev->duplex; + } + if (phydev->speed != emac->speed) { + new_state = true; + emac->speed = phydev->speed; + } + if (!emac->link) { + new_state = true; + emac->link = 1; + } + } else if (emac->link) { + new_state = true; + emac->link = 0; + /* defaults for no link */ + + /* f/w should support 100 & 1000 */ + emac->speed = SPEED_1000; + + /* half duplex may not be supported by f/w */ + emac->duplex = DUPLEX_FULL; + } + + if (new_state) { + phy_print_status(phydev); + + /* update RGMII and MII configuration based on PHY negotiated + * values + */ + if (emac->link) { + if (emac->duplex == DUPLEX_HALF) + icssg_config_half_duplex(emac); + /* Set the RGMII cfg for gig en and full duplex */ + icssg_update_rgmii_cfg(prueth->miig_rt, emac); + + /* update the Tx IPG based on 100M/1G speed */ + spin_lock_irqsave(&emac->lock, flags); + icssg_config_ipg(emac); + spin_unlock_irqrestore(&emac->lock, flags); + icssg_config_set_speed(emac); + if (!emac->is_sr1) + emac_set_port_state(emac, ICSSG_EMAC_PORT_FORWARD); + + } else { + if (!emac->is_sr1) + emac_set_port_state(emac, ICSSG_EMAC_PORT_DISABLE); + } + + /* send command to firmware to change speed and duplex + * setting when link is up. + */ + if (emac->link) { + emac_change_port_speed_duplex(emac); + icssg_qos_link_up(ndev); + } else { + icssg_qos_link_down(ndev); + } + } + + if (emac->link) { + /* link ON */ + netif_carrier_on(ndev); + /* reactivate the transmit queue */ + netif_tx_wake_all_queues(ndev); + } else { + /* link OFF */ + netif_carrier_off(ndev); + netif_tx_stop_all_queues(ndev); + } +} + +static int emac_napi_rx_poll(struct napi_struct *napi_rx, int budget) +{ + struct prueth_emac *emac = prueth_napi_to_emac(napi_rx); + int num_rx = 0; + int flow = emac->is_sr1 ? + PRUETH_MAX_RX_FLOWS_SR1 : PRUETH_MAX_RX_FLOWS_SR2; + int rx_flow = emac->is_sr1 ? + PRUETH_RX_FLOW_DATA_SR1 : PRUETH_RX_FLOW_DATA_SR2; + int cur_budget; + int ret; + + while (flow--) { + cur_budget = budget - num_rx; + + while (cur_budget--) { + ret = emac_rx_packet(emac, flow); + if (ret) + break; + num_rx++; + } + + if (num_rx >= budget) + break; + } + + if (num_rx < budget) { + napi_complete(napi_rx); + enable_irq(emac->rx_chns.irq[rx_flow]); + } + + return num_rx; +} + +static int prueth_prepare_rx_chan(struct prueth_emac *emac, + struct prueth_rx_chn *chn, + int buf_size) +{ + struct sk_buff *skb; + int i, ret; + + for (i = 0; i < chn->descs_num; i++) { + skb = __netdev_alloc_skb_ip_align(NULL, buf_size, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + ret = prueth_dma_rx_push(emac, skb, chn); + if (ret < 0) { + netdev_err(emac->ndev, + "cannot submit skb for rx chan %s ret %d\n", + chn->name, ret); + kfree_skb(skb); + return ret; + } + } + + return 0; +} + +static void prueth_reset_tx_chan(struct prueth_emac *emac, int ch_num, + bool free_skb) +{ + int i; + + for (i = 0; i < ch_num; i++) { + if (free_skb) + k3_udma_glue_reset_tx_chn(emac->tx_chns[i].tx_chn, + &emac->tx_chns[i], + prueth_tx_cleanup); + k3_udma_glue_disable_tx_chn(emac->tx_chns[i].tx_chn); + } +} + +static void prueth_reset_rx_chan(struct prueth_rx_chn *chn, + int num_flows, bool disable) +{ + int i; + + for (i = 0; i < num_flows; i++) + k3_udma_glue_reset_rx_chn(chn->rx_chn, i, chn, + prueth_rx_cleanup, !!i); + if (disable) + k3_udma_glue_disable_rx_chn(chn->rx_chn); +} + +u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts) +{ + u32 hi_rollover_count, hi_rollover_count_r; + struct prueth_emac *emac = clockops_data; + struct prueth *prueth = emac->prueth; + void __iomem *fw_hi_r_count_addr; + void __iomem *fw_count_hi_addr; + u32 iepcount_hi, iepcount_hi_r; + unsigned long flags; + u32 iepcount_lo; + u64 ts = 0; + + fw_count_hi_addr = prueth->shram.va + TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET; + fw_hi_r_count_addr = prueth->shram.va + TIMESYNC_FW_WC_HI_ROLLOVER_COUNT_OFFSET; + + local_irq_save(flags); + do { + iepcount_hi = icss_iep_get_count_hi(emac->iep); + iepcount_hi += readl(fw_count_hi_addr); + hi_rollover_count = readl(fw_hi_r_count_addr); + ptp_read_system_prets(sts); + iepcount_lo = icss_iep_get_count_low(emac->iep); + ptp_read_system_postts(sts); + + iepcount_hi_r = icss_iep_get_count_hi(emac->iep); + iepcount_hi_r += readl(fw_count_hi_addr); + hi_rollover_count_r = readl(fw_hi_r_count_addr); + } while ((iepcount_hi_r != iepcount_hi) || + (hi_rollover_count != hi_rollover_count_r)); + local_irq_restore(flags); + + ts = ((u64)hi_rollover_count) << 23 | iepcount_hi; + ts = ts * (u64)IEP_DEFAULT_CYCLE_TIME_NS + iepcount_lo; + + return ts; +} + +static void prueth_iep_settime(void *clockops_data, u64 ns) +{ + struct icssg_setclock_desc sc_desc, *sc_descp; + struct prueth_emac *emac = clockops_data; + u64 cyclecount; + u32 cycletime; + int timeout; + + if (!emac->fw_running) + return; + + sc_descp = emac->prueth->shram.va + TIMESYNC_FW_WC_SETCLOCK_DESC_OFFSET; + + cycletime = IEP_DEFAULT_CYCLE_TIME_NS; + cyclecount = ns / cycletime; + + memset(&sc_desc, 0, sizeof(sc_desc)); + sc_desc.margin = cycletime - 1000; + sc_desc.cyclecounter0_set = cyclecount & GENMASK(31, 0); + sc_desc.cyclecounter1_set = (cyclecount & GENMASK(63, 32)) >> 32; + sc_desc.iepcount_set = ns % cycletime; + sc_desc.CMP0_current = cycletime - 4; //Count from 0 to (cycle time)-4 + + memcpy_toio(sc_descp, &sc_desc, sizeof(sc_desc)); + + writeb(1, &sc_descp->request); + + timeout = 5; /* fw should take 2-3 ms */ + while (timeout--) { + if (readb(&sc_descp->acknowledgment)) + return; + + usleep_range(500, 1000); + } + + dev_err(emac->prueth->dev, "settime timeout\n"); +} + +static int prueth_perout_enable(void *clockops_data, + struct ptp_perout_request *req, int on, + u64 *cmp) +{ + struct prueth_emac *emac = clockops_data; + u32 reduction_factor = 0, offset = 0; + struct timespec64 ts; + u64 ns_period; + + if (!on) + return 0; + + /* Any firmware specific stuff for PPS/PEROUT handling */ + ts.tv_sec = req->period.sec; + ts.tv_nsec = req->period.nsec; + ns_period = timespec64_to_ns(&ts); + + /* f/w doesn't support period less than cycle time */ + if (ns_period < IEP_DEFAULT_CYCLE_TIME_NS) + return -ENXIO; + + reduction_factor = ns_period / IEP_DEFAULT_CYCLE_TIME_NS; + offset = ns_period % IEP_DEFAULT_CYCLE_TIME_NS; + + /* f/w requires at least 1uS within a cycle so CMP + * can trigger after SYNC is enabled + */ + if (offset < 5 * NSEC_PER_USEC) + offset = 5 * NSEC_PER_USEC; + + /* if offset is close to cycle time then we will miss + * the CMP event for last tick when IEP rolls over. + * In normal mode, IEP tick is 4ns. + * In slow compensation it could be 0ns or 8ns at + * every slow compensation cycle. + */ + if (offset > IEP_DEFAULT_CYCLE_TIME_NS - 8) + offset = IEP_DEFAULT_CYCLE_TIME_NS - 8; + + /* we're in shadow mode so need to set upper 32-bits */ + *cmp = (u64)offset << 32; + + writel(reduction_factor, emac->prueth->shram.va + + TIMESYNC_FW_WC_SYNCOUT_REDUCTION_FACTOR_OFFSET); + + /* HACK: till f/w supports START_TIME cyclcount we set it to 0 */ + writel(0, emac->prueth->shram.va + + TIMESYNC_FW_WC_SYNCOUT_START_TIME_CYCLECOUNT_OFFSET); + + return 0; +} + +const struct icss_iep_clockops prueth_iep_clockops = { + .settime = prueth_iep_settime, + .gettime = prueth_iep_gettime, + /* FIXME: add adjtime to use relative mode */ + .perout_enable = prueth_perout_enable, +}; + +static int emac_phy_connect(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + + /* connect PHY */ + emac->phydev = of_phy_connect(emac->ndev, emac->phy_node, + &emac_adjust_link, 0, emac->phy_if); + if (!emac->phydev) { + dev_err(prueth->dev, "couldn't connect to phy %s\n", + emac->phy_node->full_name); + return -ENODEV; + } + + /* remove unsupported modes */ + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); + + if (emac->phy_if == PHY_INTERFACE_MODE_MII) + phy_set_max_speed(emac->phydev, SPEED_100); + + return 0; +} + +/** + * emac_ndo_open - EMAC device open + * @ndev: network adapter device + * + * Called when system wants to start the interface. + * + * Returns 0 for a successful open, or appropriate error code + */ +static int emac_ndo_open(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int ret, i, num_data_chn = emac->tx_ch_num; + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + struct device *dev = prueth->dev; + int max_rx_flows; + int rx_flow; + + /* clear SMEM and MSMC settings for all slices */ + if (!prueth->emacs_initialized) { + memset_io(prueth->msmcram.va, 0, prueth->msmcram.size); + memset_io(prueth->shram.va, 0, ICSSG_CONFIG_OFFSET_SLICE1 * PRUETH_NUM_MACS); + } + + if (emac->is_sr1) { + /* For SR1, high priority channel is used exclusively for + * management messages. Do reduce number of data channels. + */ + num_data_chn--; + } + + /* set h/w MAC as user might have re-configured */ + ether_addr_copy(emac->mac_addr, ndev->dev_addr); + + icssg_class_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr); + if (!emac->is_sr1) + icssg_ft1_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr); + + icssg_class_default(prueth->miig_rt, slice, 0, emac->is_sr1); + + netif_carrier_off(ndev); + + /* Notify the stack of the actual queue counts. */ + ret = netif_set_real_num_tx_queues(ndev, num_data_chn); + if (ret) { + dev_err(dev, "cannot set real number of tx queues\n"); + return ret; + } + + init_completion(&emac->cmd_complete); + ret = prueth_init_tx_chns(emac); + if (ret) { + dev_err(dev, "failed to init tx channel: %d\n", ret); + return ret; + } + + max_rx_flows = emac->is_sr1 ? + PRUETH_MAX_RX_FLOWS_SR1 : PRUETH_MAX_RX_FLOWS_SR2; + ret = prueth_init_rx_chns(emac, &emac->rx_chns, "rx", + max_rx_flows, PRUETH_MAX_RX_DESC); + if (ret) { + dev_err(dev, "failed to init rx channel: %d\n", ret); + goto cleanup_tx; + } + + if (emac->is_sr1) { + ret = prueth_init_rx_chns(emac, &emac->rx_mgm_chn, "rxmgm", + PRUETH_MAX_RX_MGM_FLOWS, + PRUETH_MAX_RX_MGM_DESC); + if (ret) { + dev_err(dev, "failed to init rx mgmt channel: %d\n", + ret); + goto cleanup_rx; + } + } + + ret = prueth_ndev_add_tx_napi(emac); + if (ret) + goto cleanup_rx_mgm; + + /* we use only the highest priority flow for now i.e. @irq[3] */ + rx_flow = emac->is_sr1 ? + PRUETH_RX_FLOW_DATA_SR1 : PRUETH_RX_FLOW_DATA_SR2; + ret = request_irq(emac->rx_chns.irq[rx_flow], prueth_rx_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), emac); + if (ret) { + dev_err(dev, "unable to request RX IRQ\n"); + goto cleanup_napi; + } + + if (!emac->is_sr1) + goto skip_mgm_irq; + + ret = request_threaded_irq(emac->rx_mgm_chn.irq[PRUETH_RX_MGM_FLOW_RESPONSE], + NULL, prueth_rx_mgm_rsp_thread, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + dev_name(dev), emac); + if (ret) { + dev_err(dev, "unable to request RX Management RSP IRQ\n"); + goto free_rx_irq; + } + + ret = request_threaded_irq(emac->rx_mgm_chn.irq[PRUETH_RX_MGM_FLOW_TIMESTAMP], + NULL, prueth_rx_mgm_ts_thread_sr1, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + dev_name(dev), emac); + if (ret) { + dev_err(dev, "unable to request RX Management TS IRQ\n"); + goto free_rx_mgm_rsp_irq; + } + +skip_mgm_irq: + /* reset and start PRU firmware */ + ret = prueth_emac_start(prueth, emac); + if (ret) + goto free_rx_mgmt_ts_irq; + + if (!emac->is_sr1 && !prueth->emacs_initialized) { + ret = icss_iep_init(emac->iep, &prueth_iep_clockops, + emac, IEP_DEFAULT_CYCLE_TIME_NS); + } + + if (!emac->is_sr1) { + ret = request_threaded_irq(emac->tx_ts_irq, NULL, prueth_tx_ts_irq, + IRQF_ONESHOT, dev_name(dev), emac); + if (ret) + goto stop; + } + + /* Prepare RX */ + ret = prueth_prepare_rx_chan(emac, &emac->rx_chns, PRUETH_MAX_PKT_SIZE); + if (ret) + goto free_rx_ts_irq; + + if (emac->is_sr1) { + ret = prueth_prepare_rx_chan(emac, &emac->rx_mgm_chn, 64); + if (ret) + goto reset_rx_chn; + + ret = k3_udma_glue_enable_rx_chn(emac->rx_mgm_chn.rx_chn); + if (ret) + goto reset_rx_chn; + } + + ret = k3_udma_glue_enable_rx_chn(emac->rx_chns.rx_chn); + if (ret) + goto reset_rx_mgm_chn; + + for (i = 0; i < emac->tx_ch_num; i++) { + ret = k3_udma_glue_enable_tx_chn(emac->tx_chns[i].tx_chn); + if (ret) + goto reset_tx_chan; + } + + /* Enable NAPI in Tx and Rx direction */ + for (i = 0; i < emac->tx_ch_num; i++) + napi_enable(&emac->tx_chns[i].napi_tx); + napi_enable(&emac->napi_rx); + + icssg_qos_init(ndev); + + emac_phy_connect(emac); + /* Get attached phy details */ + phy_attached_info(emac->phydev); + + /* start PHY */ + phy_start(emac->phydev); + + prueth->emacs_initialized++; + + if (netif_msg_drv(emac)) + dev_notice(&ndev->dev, "started\n"); + + if (prueth->is_switch_mode) { + icssg_fdb_add_del(emac, eth_stp_addr, prueth->default_vlan, + ICSSG_FDB_ENTRY_P0_MEMBERSHIP | + ICSSG_FDB_ENTRY_P1_MEMBERSHIP | + ICSSG_FDB_ENTRY_P2_MEMBERSHIP | + ICSSG_FDB_ENTRY_BLOCK, + true); + icssg_vtbl_modify(emac, emac->port_vlan, BIT(emac->port_id), + BIT(emac->port_id), true); + icssg_set_pvid(emac->prueth, emac->port_vlan, emac->port_id); + emac_set_port_state(emac, ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE); + } + return 0; + +reset_tx_chan: + /* Since interface is not yet up, there is wouldn't be + * any SKB for completion. So set false to free_skb + */ + prueth_reset_tx_chan(emac, i, false); +reset_rx_mgm_chn: + if (emac->is_sr1) + prueth_reset_rx_chan(&emac->rx_mgm_chn, + PRUETH_MAX_RX_MGM_FLOWS, true); +reset_rx_chn: + prueth_reset_rx_chan(&emac->rx_chns, max_rx_flows, false); +free_rx_ts_irq: + if (!emac->is_sr1) + free_irq(emac->tx_ts_irq, emac); +stop: + prueth_emac_stop(emac); +free_rx_mgmt_ts_irq: + if (emac->is_sr1) + free_irq(emac->rx_mgm_chn.irq[PRUETH_RX_MGM_FLOW_TIMESTAMP], + emac); +free_rx_mgm_rsp_irq: + if (emac->is_sr1) + free_irq(emac->rx_mgm_chn.irq[PRUETH_RX_MGM_FLOW_RESPONSE], + emac); +free_rx_irq: + free_irq(emac->rx_chns.irq[rx_flow], emac); +cleanup_napi: + prueth_ndev_del_tx_napi(emac, emac->tx_ch_num); +cleanup_rx_mgm: + if (emac->is_sr1) + prueth_cleanup_rx_chns(emac, &emac->rx_mgm_chn, + PRUETH_MAX_RX_MGM_FLOWS); +cleanup_rx: + prueth_cleanup_rx_chns(emac, &emac->rx_chns, max_rx_flows); +cleanup_tx: + prueth_cleanup_tx_chns(emac); + + return ret; +} + +/** + * emac_ndo_stop - EMAC device stop + * @ndev: network adapter device + * + * Called when system wants to stop or down the interface. + */ +static int emac_ndo_stop(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int ret, i; + int max_rx_flows; + int rx_flow = emac->is_sr1 ? + PRUETH_RX_FLOW_DATA_SR1 : PRUETH_RX_FLOW_DATA_SR2; + + /* inform the upper layers. */ + netif_tx_stop_all_queues(ndev); + + /* block packets from wire */ + phy_stop(emac->phydev); + phy_disconnect(emac->phydev); + emac->phydev = NULL; + + icssg_class_disable(prueth->miig_rt, prueth_emac_slice(emac)); + + /* send shutdown command */ + emac_shutdown(ndev); + + atomic_set(&emac->tdown_cnt, emac->tx_ch_num); + /* ensure new tdown_cnt value is visible */ + smp_mb__after_atomic(); + /* tear down and disable UDMA channels */ + reinit_completion(&emac->tdown_complete); + for (i = 0; i < emac->tx_ch_num; i++) + k3_udma_glue_tdown_tx_chn(emac->tx_chns[i].tx_chn, false); + + ret = wait_for_completion_timeout(&emac->tdown_complete, + msecs_to_jiffies(1000)); + if (!ret) + netdev_err(ndev, "tx teardown timeout\n"); + + prueth_reset_tx_chan(emac, emac->tx_ch_num, true); + for (i = 0; i < emac->tx_ch_num; i++) + napi_disable(&emac->tx_chns[i].napi_tx); + + max_rx_flows = emac->is_sr1 ? + PRUETH_MAX_RX_FLOWS_SR1 : PRUETH_MAX_RX_FLOWS_SR2; + k3_udma_glue_tdown_rx_chn(emac->rx_chns.rx_chn, true); + + prueth_reset_rx_chan(&emac->rx_chns, max_rx_flows, true); + if (emac->is_sr1) { + /* Teardown RX MGM channel */ + k3_udma_glue_tdown_rx_chn(emac->rx_mgm_chn.rx_chn, true); + prueth_reset_rx_chan(&emac->rx_mgm_chn, + PRUETH_MAX_RX_MGM_FLOWS, true); + } + + napi_disable(&emac->napi_rx); + + if (!emac->is_sr1 && prueth->emacs_initialized == 1) + icss_iep_exit(emac->iep); + + cancel_work_sync(&emac->rx_mode_work); + /* stop PRUs */ + prueth_emac_stop(emac); + + if (!emac->is_sr1) + free_irq(emac->tx_ts_irq, emac); + + if (emac->is_sr1) { + free_irq(emac->rx_mgm_chn.irq[PRUETH_RX_MGM_FLOW_TIMESTAMP], + emac); + free_irq(emac->rx_mgm_chn.irq[PRUETH_RX_MGM_FLOW_RESPONSE], + emac); + } + free_irq(emac->rx_chns.irq[rx_flow], emac); + prueth_ndev_del_tx_napi(emac, emac->tx_ch_num); + prueth_cleanup_tx_chns(emac); + + if (emac->is_sr1) + prueth_cleanup_rx_chns(emac, &emac->rx_mgm_chn, + PRUETH_MAX_RX_MGM_FLOWS); + prueth_cleanup_rx_chns(emac, &emac->rx_chns, max_rx_flows); + prueth_cleanup_tx_chns(emac); + + prueth->emacs_initialized--; + + if (netif_msg_drv(emac)) + dev_notice(&ndev->dev, "stopped\n"); + + return 0; +} + +static void emac_ndo_tx_timeout(struct net_device *ndev, unsigned int txqueue) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (netif_msg_tx_err(emac)) + netdev_err(ndev, "xmit timeout"); + + ndev->stats.tx_errors++; + + /* TODO: can we recover or need to reboot firmware? */ +} + +static void emac_ndo_set_rx_mode_sr1(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int slice = prueth_emac_slice(emac); + bool promisc = ndev->flags & IFF_PROMISC; + bool allmulti = ndev->flags & IFF_ALLMULTI; + + if (promisc) { + icssg_class_promiscuous_sr1(prueth->miig_rt, slice); + return; + } + + if (allmulti) { + icssg_class_default(prueth->miig_rt, slice, 1, emac->is_sr1); + return; + } + + icssg_class_default(prueth->miig_rt, slice, 0, emac->is_sr1); + if (!netdev_mc_empty(ndev)) { + /* program multicast address list into Classifier */ + icssg_class_add_mcast_sr1(prueth->miig_rt, slice, ndev); + return; + } +} + +static void emac_ndo_set_rx_mode_work(struct work_struct *work) +{ + struct prueth_emac *emac = container_of(work, struct prueth_emac, rx_mode_work); + struct net_device *ndev = emac->ndev; + bool promisc, allmulti; + + if (!(ndev->flags & IFF_UP)) + return; + + promisc = ndev->flags & IFF_PROMISC; + allmulti = ndev->flags & IFF_ALLMULTI; + emac_set_port_state(emac, ICSSG_EMAC_PORT_UC_FLOODING_DISABLE); + emac_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_DISABLE); + + if (promisc) { + emac_set_port_state(emac, ICSSG_EMAC_PORT_UC_FLOODING_ENABLE); + emac_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_ENABLE); + return; + } + + if (allmulti) { + emac_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_ENABLE); + return; + } + + if (!netdev_mc_empty(ndev)) { + /* TODO: Add FDB entries for multicast. till then enable allmulti */ + emac_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_ENABLE); + return; + } +} + +/** + * emac_ndo_set_rx_mode - EMAC set receive mode function + * @ndev: The EMAC network adapter + * + * Called when system wants to set the receive mode of the device. + * + */ +static void emac_ndo_set_rx_mode(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + if (prueth->is_sr1) { + emac_ndo_set_rx_mode_sr1(ndev); + return; + } + + queue_work(emac->cmd_wq, &emac->rx_mode_work); +} + +static int emac_set_ts_config(struct net_device *ndev, struct ifreq *ifr) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct hwtstamp_config config; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + emac->tx_ts_enabled = 0; + break; + case HWTSTAMP_TX_ON: + emac->tx_ts_enabled = 1; + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + emac->rx_ts_enabled = 0; + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + emac->rx_ts_enabled = 1; + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static int emac_get_ts_config(struct net_device *ndev, struct ifreq *ifr) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct hwtstamp_config config; + + config.flags = 0; + config.tx_type = emac->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + config.rx_filter = emac->rx_ts_enabled ? HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE; + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static int emac_ndo_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + switch (cmd) { + case SIOCGHWTSTAMP: + return emac_get_ts_config(ndev, ifr); + case SIOCSHWTSTAMP: + return emac_set_ts_config(ndev, ifr); + default: + break; + } + + if (!emac->phydev) + return -EOPNOTSUPP; + + return phy_mii_ioctl(emac->phydev, ifr, cmd); +} + +static struct devlink_port *emac_ndo_get_devlink_port(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + return &emac->devlink_port; +} + +static const struct net_device_ops emac_netdev_ops = { + .ndo_open = emac_ndo_open, + .ndo_stop = emac_ndo_stop, + .ndo_start_xmit = emac_ndo_start_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = emac_ndo_tx_timeout, + .ndo_set_rx_mode = emac_ndo_set_rx_mode, + .ndo_do_ioctl = emac_ndo_ioctl, + .ndo_get_devlink_port = emac_ndo_get_devlink_port, + .ndo_setup_tc = icssg_qos_ndo_setup_tc, +}; + +/* get emac_port corresponding to eth_node name */ +static int prueth_node_port(struct device_node *eth_node) +{ + if (!strcmp(eth_node->name, "ethernet-mii0")) + return PRUETH_PORT_MII0; + else if (!strcmp(eth_node->name, "ethernet-mii1")) + return PRUETH_PORT_MII1; + else + return -EINVAL; +} + +/* get MAC instance corresponding to eth_node name */ +static int prueth_node_mac(struct device_node *eth_node) +{ + if (!strcmp(eth_node->name, "ethernet-mii0")) + return PRUETH_MAC0; + else if (!strcmp(eth_node->name, "ethernet-mii1")) + return PRUETH_MAC1; + else + return -EINVAL; +} + +static int prueth_config_rgmiidelay(struct prueth *prueth, + struct device_node *eth_np, + phy_interface_t phy_if) +{ + struct device *dev = prueth->dev; + struct regmap *ctrl_mmr; + u32 rgmii_tx_id = 0; + u32 icssgctrl_reg; + + if (!phy_interface_mode_is_rgmii(phy_if)) + return 0; + + ctrl_mmr = syscon_regmap_lookup_by_phandle(eth_np, "syscon-rgmii-delay"); + if (IS_ERR(ctrl_mmr)) { + dev_err(dev, "couldn't get syscon-rgmii-delay\n"); + return -ENODEV; + } + + if (of_property_read_u32_index(eth_np, "syscon-rgmii-delay", 1, + &icssgctrl_reg)) { + dev_err(dev, "couldn't get rgmii-delay reg. offset\n"); + return -ENODEV; + } + + if (phy_if == PHY_INTERFACE_MODE_RGMII_ID || + phy_if == PHY_INTERFACE_MODE_RGMII_TXID) + rgmii_tx_id |= ICSSG_CTRL_RGMII_ID_MODE; + + regmap_update_bits(ctrl_mmr, icssgctrl_reg, ICSSG_CTRL_RGMII_ID_MODE, rgmii_tx_id); + + return 0; +} + +extern const struct ethtool_ops icssg_ethtool_ops; + +static int prueth_netdev_init(struct prueth *prueth, + struct device_node *eth_node) +{ + int ret, num_tx_chn = PRUETH_MAX_TX_QUEUES; + struct prueth_emac *emac; + struct net_device *ndev; + enum prueth_port port; + const char *irq_name; + enum prueth_mac mac; + const u8 *mac_addr; + + port = prueth_node_port(eth_node); + if (port < 0) + return -EINVAL; + + mac = prueth_node_mac(eth_node); + if (mac < 0) + return -EINVAL; + + /* Use 1 channel for management messages on SR1 */ + if (prueth->is_sr1) + num_tx_chn--; + + ndev = alloc_etherdev_mq(sizeof(*emac), num_tx_chn); + if (!ndev) + return -ENOMEM; + + emac = netdev_priv(ndev); + prueth->emac[mac] = emac; + emac->prueth = prueth; + emac->ndev = ndev; + emac->port_id = port; + emac->cmd_wq = create_singlethread_workqueue("icssg_cmd_wq"); + if (!emac->cmd_wq) { + ret = -ENOMEM; + goto free_ndev; + } + INIT_WORK(&emac->rx_mode_work, emac_ndo_set_rx_mode_work); + + ret = pruss_request_mem_region(prueth->pruss, + port == PRUETH_PORT_MII0 ? + PRUSS_MEM_DRAM0 : PRUSS_MEM_DRAM1, + &emac->dram); + if (ret) { + dev_err(prueth->dev, "unable to get DRAM: %d\n", ret); + return -ENOMEM; + } + + emac->is_sr1 = prueth->is_sr1; + emac->tx_ch_num = 1; + if (emac->is_sr1) { + /* use a dedicated high priority channel for management + * messages which is +1 of highest priority data channel. + */ + emac->tx_ch_num++; + goto skip_irq; + } + + irq_name = "tx_ts0"; + if (emac->port_id == PRUETH_PORT_MII1) + irq_name = "tx_ts1"; + emac->tx_ts_irq = platform_get_irq_byname_optional(prueth->pdev, irq_name); + if (emac->tx_ts_irq < 0) { + ret = dev_err_probe(prueth->dev, emac->tx_ts_irq, "could not get tx_ts_irq\n"); + goto free; + } + +skip_irq: + SET_NETDEV_DEV(ndev, prueth->dev); + emac->msg_enable = netif_msg_init(debug_level, PRUETH_EMAC_DEBUG); + spin_lock_init(&emac->lock); + mutex_init(&emac->cmd_lock); + + emac->phy_node = of_parse_phandle(eth_node, "phy-handle", 0); + if (!emac->phy_node && !of_phy_is_fixed_link(eth_node)) { + dev_err(prueth->dev, "couldn't find phy-handle\n"); + ret = -ENODEV; + goto free; + } else if (of_phy_is_fixed_link(eth_node)) { + ret = of_phy_register_fixed_link(eth_node); + if (ret) { + ret = dev_err_probe(prueth->dev, ret, + "failed to register fixed-link phy\n"); + goto free; + } + + emac->phy_node = eth_node; + } + + ret = of_get_phy_mode(eth_node, &emac->phy_if); + if (ret) { + dev_err(prueth->dev, "could not get phy-mode property\n"); + goto free; + } + + if (emac->phy_if != PHY_INTERFACE_MODE_MII && + !phy_interface_mode_is_rgmii(emac->phy_if)) { + dev_err(prueth->dev, "PHY mode unsupported %s\n", phy_modes(emac->phy_if)); + goto free; + } + + ret = prueth_config_rgmiidelay(prueth, eth_node, emac->phy_if); + if (ret) + goto free; + + /* get mac address from DT and set private and netdev addr */ + mac_addr = of_get_mac_address(eth_node); + if (!IS_ERR(mac_addr)) + ether_addr_copy(ndev->dev_addr, mac_addr); + if (!is_valid_ether_addr(ndev->dev_addr)) { + eth_hw_addr_random(ndev); + dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n", + port, ndev->dev_addr); + } + ether_addr_copy(emac->mac_addr, ndev->dev_addr); + + ndev->netdev_ops = &emac_netdev_ops; + ndev->ethtool_ops = &icssg_ethtool_ops; + ndev->hw_features = NETIF_F_SG; + ndev->features = ndev->hw_features; + + netif_napi_add(ndev, &emac->napi_rx, + emac_napi_rx_poll, NAPI_POLL_WEIGHT); + + return 0; + +free: + pruss_release_mem_region(prueth->pruss, &emac->dram); + destroy_workqueue(emac->cmd_wq); +free_ndev: + free_netdev(ndev); + prueth->emac[mac] = NULL; + + return ret; +} + +static void prueth_netdev_exit(struct prueth *prueth, + struct device_node *eth_node) +{ + struct prueth_emac *emac; + enum prueth_mac mac; + + mac = prueth_node_mac(eth_node); + if (mac < 0) + return; + + emac = prueth->emac[mac]; + if (!emac) + return; + + if (of_phy_is_fixed_link(emac->phy_node)) + of_phy_deregister_fixed_link(emac->phy_node); + + netif_napi_del(&emac->napi_rx); + + pruss_release_mem_region(prueth->pruss, &emac->dram); + destroy_workqueue(emac->cmd_wq); + free_netdev(emac->ndev); + prueth->emac[mac] = NULL; +} + +static int prueth_get_cores(struct prueth *prueth, int slice) +{ + enum pruss_pru_id pruss_id; + struct device *dev = prueth->dev; + struct device_node *np = dev->of_node; + int pru, rtu, txpru = -1, ret; + + switch (slice) { + case ICSS_SLICE0: + pru = 0; + rtu = 1; + if (!prueth->is_sr1) + txpru = 2; + break; + case ICSS_SLICE1: + if (prueth->is_sr1) { + pru = 2; + rtu = 3; + } else { + pru = 3; + rtu = 4; + txpru = 5; + } + break; + default: + return -EINVAL; + } + + prueth->pru[slice] = pru_rproc_get(np, pru, &pruss_id); + if (IS_ERR(prueth->pru[slice])) { + ret = PTR_ERR(prueth->pru[slice]); + prueth->pru[slice] = NULL; + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get PRU%d: %d\n", slice, ret); + return ret; + } + prueth->pru_id[slice] = pruss_id; + + prueth->rtu[slice] = pru_rproc_get(np, rtu, NULL); + if (IS_ERR(prueth->rtu[slice])) { + ret = PTR_ERR(prueth->rtu[slice]); + prueth->rtu[slice] = NULL; + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get RTU%d: %d\n", slice, ret); + return ret; + } + + if (prueth->is_sr1) + return 0; + + prueth->txpru[slice] = pru_rproc_get(np, txpru, NULL); + if (IS_ERR(prueth->txpru[slice])) { + ret = PTR_ERR(prueth->txpru[slice]); + prueth->txpru[slice] = NULL; + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get TX_PRU%d: %d\n", + slice, ret); + return ret; + } + + return 0; +} + +static void prueth_put_cores(struct prueth *prueth, int slice) +{ + if (prueth->txpru[slice]) + pru_rproc_put(prueth->txpru[slice]); + + if (prueth->rtu[slice]) + pru_rproc_put(prueth->rtu[slice]); + + if (prueth->pru[slice]) + pru_rproc_put(prueth->pru[slice]); +} + +static void prueth_offload_fwd_mark_update(struct prueth *prueth) +{ + int set_val = 0; + int i; + + if (prueth->br_members == (PRUETH_PORT_MII0 | PRUETH_PORT_MII1)) + set_val = 1; + + dev_dbg(prueth->dev, "set offload_fwd_mark %d\n", set_val); + + for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) { + struct prueth_emac *emac = prueth->emac[i]; + + if (!emac || !emac->ndev) + continue; + + emac->offload_fwd_mark = set_val; + } +} + +bool prueth_dev_check(const struct net_device *ndev) +{ + if (ndev->netdev_ops == &emac_netdev_ops && netif_running(ndev)) { + struct prueth_emac *emac = netdev_priv(ndev); + + return emac->prueth->is_switch_mode; + } + + return false; +} + +static int prueth_netdevice_port_link(struct net_device *ndev, struct net_device *br_ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + if (!prueth->is_switch_mode) + return NOTIFY_DONE; + + if (!prueth->br_members) { + prueth->hw_bridge_dev = br_ndev; + } else { + /* This is adding the port to a second bridge, this is + * unsupported + */ + if (prueth->hw_bridge_dev != br_ndev) + return -EOPNOTSUPP; + } + + prueth->br_members |= BIT(emac->port_id); + + prueth_offload_fwd_mark_update(prueth); + + return NOTIFY_DONE; +} + +static void prueth_netdevice_port_unlink(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + prueth->br_members &= ~BIT(emac->port_id); + + prueth_offload_fwd_mark_update(prueth); + + if (!prueth->br_members) + prueth->hw_bridge_dev = NULL; +} + +/* netdev notifier */ +static int prueth_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + int ret = NOTIFY_DONE; + + if (ndev->netdev_ops != &emac_netdev_ops) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_CHANGEUPPER: + info = ptr; + + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) + ret = prueth_netdevice_port_link(ndev, info->upper_dev); + else + prueth_netdevice_port_unlink(ndev); + } + break; + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(ret); +} + +static int prueth_register_notifiers(struct prueth *prueth) +{ + int ret = 0; + + prueth->prueth_netdevice_nb.notifier_call = &prueth_netdevice_event; + ret = register_netdevice_notifier(&prueth->prueth_netdevice_nb); + if (ret) { + dev_err(prueth->dev, "can't register netdevice notifier\n"); + return ret; + } + + ret = prueth_switchdev_register_notifiers(prueth); + if (ret) + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); + + return ret; +} + +static void prueth_unregister_notifiers(struct prueth *prueth) +{ + prueth_switchdev_unregister_notifiers(prueth); + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); +} + +static const struct devlink_ops prueth_devlink_ops = {}; + +static int prueth_dl_switch_mode_get(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct prueth_devlink *dl_priv = devlink_priv(dl); + struct prueth *prueth = dl_priv->prueth; + + dev_dbg(prueth->dev, "%s id:%u\n", __func__, id); + + if (id != PRUETH_DL_PARAM_SWITCH_MODE) + return -EOPNOTSUPP; + + ctx->val.vbool = prueth->is_switch_mode; + + return 0; +} + +static int prueth_dl_switch_mode_set(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct prueth_devlink *dl_priv = devlink_priv(dl); + struct prueth *prueth = dl_priv->prueth; + bool switch_en = ctx->val.vbool; + int i; + + dev_dbg(prueth->dev, "%s id:%u\n", __func__, id); + + if (id != PRUETH_DL_PARAM_SWITCH_MODE) + return -EOPNOTSUPP; + + if (switch_en == prueth->is_switch_mode) + return 0; + + if (!switch_en && prueth->br_members) { + dev_err(prueth->dev, "Remove ports from bridge before disabling switch mode\n"); + return -EINVAL; + } + + rtnl_lock(); + + prueth->default_vlan = 1; + prueth->is_switch_mode = switch_en; + + for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) { + struct net_device *sl_ndev = prueth->emac[i]->ndev; + + if (!sl_ndev || !netif_running(sl_ndev)) + continue; + + dev_err(prueth->dev, "Cannot switch modes when i/f are up\n"); + goto exit; + } + + for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) { + struct net_device *sl_ndev = prueth->emac[i]->ndev; + struct prueth_emac *emac; + + if (!sl_ndev) + continue; + + emac = netdev_priv(sl_ndev); + if (switch_en) + emac->port_vlan = prueth->default_vlan; + else + emac->port_vlan = 0; + } + + dev_info(prueth->dev, "Enabling %s mode\n", + switch_en ? "switch" : "Dual EMAC"); + +exit: + rtnl_unlock(); + + return 0; +} + +static const struct devlink_param prueth_devlink_params[] = { + DEVLINK_PARAM_DRIVER(PRUETH_DL_PARAM_SWITCH_MODE, "switch_mode", + DEVLINK_PARAM_TYPE_BOOL, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + prueth_dl_switch_mode_get, + prueth_dl_switch_mode_set, NULL), +}; + +static void prueth_unregister_devlink_ports(struct prueth *prueth) +{ + struct devlink_port *dl_port; + struct prueth_emac *emac; + int i; + + for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) { + emac = prueth->emac[i]; + dl_port = &emac->devlink_port; + + if (dl_port->registered) + devlink_port_unregister(dl_port); + } +} + +static int prueth_register_devlink(struct prueth *prueth) +{ + struct devlink_port_attrs attrs = {}; + struct prueth_devlink *dl_priv; + struct device *dev = prueth->dev; + struct devlink_port *dl_port; + struct prueth_emac *emac; + int ret = 0; + int i; + + prueth->devlink = + devlink_alloc(&prueth_devlink_ops, sizeof(*dl_priv)); + if (!prueth->devlink) + return -ENOMEM; + + dl_priv = devlink_priv(prueth->devlink); + dl_priv->prueth = prueth; + + ret = devlink_register(prueth->devlink, dev); + if (ret) { + dev_err(dev, "devlink reg fail ret:%d\n", ret); + goto dl_free; + } + + /* Provide devlink hook to switch mode when multiple external ports + * are present NUSS switchdev driver is enabled. + */ + if (prueth->is_switchmode_supported) { + ret = devlink_params_register(prueth->devlink, + prueth_devlink_params, + ARRAY_SIZE(prueth_devlink_params)); + if (ret) { + dev_err(dev, "devlink params reg fail ret:%d\n", ret); + goto dl_unreg; + } + devlink_params_publish(prueth->devlink); + } + + for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) { + emac = prueth->emac[i]; + if (!emac) + continue; + + dl_port = &emac->devlink_port; + + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + attrs.phys.port_number = emac->port_id; + attrs.switch_id.id_len = sizeof(resource_size_t); + memcpy(attrs.switch_id.id, prueth->switch_id, attrs.switch_id.id_len); + devlink_port_attrs_set(dl_port, &attrs); + + ret = devlink_port_register(prueth->devlink, dl_port, emac->port_id); + if (ret) { + dev_err(dev, "devlink_port reg fail for port %d, ret:%d\n", + emac->port_id, ret); + goto dl_port_unreg; + } + devlink_port_type_eth_set(dl_port, emac->ndev); + } + + return ret; + +dl_port_unreg: + prueth_unregister_devlink_ports(prueth); +dl_unreg: + devlink_unregister(prueth->devlink); +dl_free: + devlink_free(prueth->devlink); + + return ret; +} + +static void prueth_unregister_devlink(struct prueth *prueth) +{ + if (prueth->is_switchmode_supported) { + devlink_params_unpublish(prueth->devlink); + devlink_params_unregister(prueth->devlink, prueth_devlink_params, + ARRAY_SIZE(prueth_devlink_params)); + } + + prueth_unregister_devlink_ports(prueth); + devlink_unregister(prueth->devlink); + devlink_free(prueth->devlink); +} + +static const struct of_device_id prueth_dt_match[]; + +static int prueth_probe(struct platform_device *pdev) +{ + struct prueth *prueth; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *eth0_node, *eth1_node; + const struct of_device_id *match; + struct pruss *pruss; + int i, ret; + u32 msmc_ram_size; + struct genpool_data_align gp_data = { + .align = SZ_64K, + }; + + match = of_match_device(prueth_dt_match, dev); + if (!match) + return -ENODEV; + + prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL); + if (!prueth) + return -ENOMEM; + + dev_set_drvdata(dev, prueth); + prueth->pdev = pdev; + prueth->pdata = *(const struct prueth_pdata *)match->data; + + if (of_device_is_compatible(np, "ti,am654-icssg-prueth-sr1")) + prueth->is_sr1 = true; + + prueth->dev = dev; + eth0_node = of_get_child_by_name(np, "ethernet-mii0"); + if (!of_device_is_available(eth0_node)) { + of_node_put(eth0_node); + eth0_node = NULL; + } + + eth1_node = of_get_child_by_name(np, "ethernet-mii1"); + if (!of_device_is_available(eth1_node)) { + of_node_put(eth1_node); + eth1_node = NULL; + } + + /* At least one node must be present and available else we fail */ + if (!eth0_node && !eth1_node) { + dev_err(dev, "neither ethernet-mii0 nor ethernet-mii1 node available\n"); + return -ENODEV; + } + + prueth->eth_node[PRUETH_MAC0] = eth0_node; + prueth->eth_node[PRUETH_MAC1] = eth1_node; + + prueth->miig_rt = syscon_regmap_lookup_by_phandle(np, "mii-g-rt"); + if (IS_ERR(prueth->miig_rt)) { + dev_err(dev, "couldn't get mii-g-rt syscon regmap\n"); + return -ENODEV; + } + + prueth->mii_rt = syscon_regmap_lookup_by_phandle(np, "mii-rt"); + if (IS_ERR(prueth->mii_rt)) { + dev_err(dev, "couldn't get mii-rt syscon regmap\n"); + return -ENODEV; + } + + if (eth0_node) { + ret = prueth_get_cores(prueth, ICSS_SLICE0); + if (ret) + goto put_cores; + } + + if (eth1_node) { + ret = prueth_get_cores(prueth, ICSS_SLICE1); + if (ret) + goto put_cores; + } + + pruss = pruss_get(eth0_node ? + prueth->pru[ICSS_SLICE0] : prueth->pru[ICSS_SLICE1]); + if (IS_ERR(pruss)) { + ret = PTR_ERR(pruss); + dev_err(dev, "unable to get pruss handle\n"); + goto put_cores; + } + + prueth->pruss = pruss; + + ret = pruss_request_mem_region(pruss, PRUSS_MEM_SHRD_RAM2, + &prueth->shram); + if (ret) { + dev_err(dev, "unable to get PRUSS SHRD RAM2: %d\n", ret); + goto put_mem; + } + + prueth->sram_pool = of_gen_pool_get(np, "sram", 0); + if (!prueth->sram_pool) { + dev_err(dev, "unable to get SRAM pool\n"); + ret = -ENODEV; + + goto put_mem; + } + + msmc_ram_size = prueth->is_sr1 ? MSMC_RAM_SIZE_SR1 : MSMC_RAM_SIZE_SR2; + prueth->is_switchmode_supported = prueth->pdata.switch_mode; + if (prueth->is_switchmode_supported) + msmc_ram_size = MSMC_RAM_SIZE_SR2_SWITCH_MODE; + + if (prueth->is_sr1) { + prueth->msmcram.va = + (void __iomem *)gen_pool_alloc(prueth->sram_pool, + msmc_ram_size); + } else { + /* TEMP: FW bug needs buffer base to be 64KB aligned */ + prueth->msmcram.va = + (void __iomem *)gen_pool_alloc_algo(prueth->sram_pool, + msmc_ram_size, + gen_pool_first_fit_align, + &gp_data); + } + + if (!prueth->msmcram.va) { + ret = -ENOMEM; + dev_err(dev, "unable to allocate MSMC resource\n"); + goto put_mem; + } + prueth->msmcram.pa = gen_pool_virt_to_phys(prueth->sram_pool, + (unsigned long)prueth->msmcram.va); + prueth->msmcram.size = msmc_ram_size; + memset(prueth->msmcram.va, 0, msmc_ram_size); + dev_dbg(dev, "sram: pa %llx va %p size %zx\n", prueth->msmcram.pa, + prueth->msmcram.va, prueth->msmcram.size); + + prueth->iep0 = icss_iep_get_idx(np, 0); + if (IS_ERR(prueth->iep0)) { + ret = dev_err_probe(dev, PTR_ERR(prueth->iep0), "iep0 get failed\n"); + prueth->iep0 = NULL; + goto free_pool; + } + + prueth->iep1 = icss_iep_get_idx(np, 1); + if (IS_ERR(prueth->iep1)) { + ret = dev_err_probe(dev, PTR_ERR(prueth->iep1), "iep1 get failed\n"); + icss_iep_put(prueth->iep1); + prueth->iep0 = NULL; + prueth->iep1 = NULL; + goto free_pool; + } + + if (prueth->is_sr1) { + ret = icss_iep_init(prueth->iep0, NULL, NULL, 0); + if (ret) { + dev_err(dev, "failed to init iep0\n"); + goto free_iep; + } + + ret = icss_iep_init(prueth->iep1, NULL, NULL, 0); + if (ret) { + dev_err(dev, "failed to init iep1\n"); + icss_iep_exit(prueth->iep1); + goto free_iep; + } + } else if (prueth->pdata.quirk_10m_link_issue) { + /* Enable IEP1 for FW in 64bit mode as W/A for 10M FD link detect issue under TX + * traffic. + */ + icss_iep_init_fw(prueth->iep1); + } + + /* setup netdev interfaces */ + if (eth0_node) { + ret = prueth_netdev_init(prueth, eth0_node); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_err(dev, "netdev init %s failed: %d\n", + eth0_node->name, ret); + } + goto exit_iep; + } + prueth->emac[PRUETH_MAC0]->iep = prueth->iep0; + } + + if (eth1_node) { + ret = prueth_netdev_init(prueth, eth1_node); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_err(dev, "netdev init %s failed: %d\n", + eth1_node->name, ret); + } + goto netdev_exit; + } + + if (prueth->is_sr1) + prueth->emac[PRUETH_MAC1]->iep = prueth->iep1; + else + prueth->emac[PRUETH_MAC1]->iep = prueth->iep0; + } + + /* register the network devices */ + if (eth0_node) { + ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); + if (ret) { + dev_err(dev, "can't register netdev for port MII0"); + goto netdev_exit; + } + + prueth->registered_netdevs[PRUETH_MAC0] = prueth->emac[PRUETH_MAC0]->ndev; + } + + if (eth1_node) { + ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); + if (ret) { + dev_err(dev, "can't register netdev for port MII1"); + goto netdev_unregister; + } + + prueth->registered_netdevs[PRUETH_MAC1] = prueth->emac[PRUETH_MAC1]->ndev; + } + + ret = prueth_register_devlink(prueth); + if (ret) + goto netdev_unregister; + + if (prueth->is_switchmode_supported) { + ret = prueth_register_notifiers(prueth); + if (ret) + goto netdev_unregister; + + sprintf(prueth->switch_id, "%s", dev_name(dev)); + } + + dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", + (!eth0_node || !eth1_node) ? "single" : "dual"); + + if (eth1_node) + of_node_put(eth1_node); + if (eth0_node) + of_node_put(eth0_node); + + return 0; + +netdev_unregister: + for (i = 0; i < PRUETH_NUM_MACS; i++) { + if (!prueth->registered_netdevs[i]) + continue; + unregister_netdev(prueth->registered_netdevs[i]); + } + +netdev_exit: + for (i = 0; i < PRUETH_NUM_MACS; i++) { + struct device_node *eth_node; + + eth_node = prueth->eth_node[i]; + if (!eth_node) + continue; + + prueth_netdev_exit(prueth, eth_node); + } +exit_iep: + if (prueth->is_sr1) { + icss_iep_exit(prueth->iep1); + icss_iep_exit(prueth->iep0); + } else if (prueth->pdata.quirk_10m_link_issue) { + icss_iep_exit_fw(prueth->iep1); + } + +free_iep: + icss_iep_put(prueth->iep1); + icss_iep_put(prueth->iep0); + +free_pool: + gen_pool_free(prueth->sram_pool, + (unsigned long)prueth->msmcram.va, msmc_ram_size); + +put_mem: + pruss_release_mem_region(prueth->pruss, &prueth->shram); + pruss_put(prueth->pruss); + +put_cores: + if (eth1_node) { + prueth_put_cores(prueth, ICSS_SLICE1); + of_node_put(eth1_node); + } + + if (eth0_node) { + prueth_put_cores(prueth, ICSS_SLICE0); + of_node_put(eth0_node); + } + + return ret; +} + +static int prueth_remove(struct platform_device *pdev) +{ + struct device_node *eth_node; + struct prueth *prueth = platform_get_drvdata(pdev); + int i; + + prueth_unregister_notifiers(prueth); + prueth_unregister_devlink(prueth); + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + if (!prueth->registered_netdevs[i]) + continue; + unregister_netdev(prueth->registered_netdevs[i]); + } + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + eth_node = prueth->eth_node[i]; + if (!eth_node) + continue; + + prueth_netdev_exit(prueth, eth_node); + } + + if (prueth->is_sr1) { + icss_iep_exit(prueth->iep1); + icss_iep_exit(prueth->iep0); + } else if (prueth->pdata.quirk_10m_link_issue) { + icss_iep_exit_fw(prueth->iep1); + } + + icss_iep_put(prueth->iep1); + icss_iep_put(prueth->iep0); + + gen_pool_free(prueth->sram_pool, + (unsigned long)prueth->msmcram.va, + prueth->is_sr1 ? MSMC_RAM_SIZE_SR1 : MSMC_RAM_SIZE_SR2); + + pruss_release_mem_region(prueth->pruss, &prueth->shram); + + pruss_put(prueth->pruss); + + if (prueth->eth_node[PRUETH_MAC1]) + prueth_put_cores(prueth, ICSS_SLICE1); + + if (prueth->eth_node[PRUETH_MAC0]) + prueth_put_cores(prueth, ICSS_SLICE0); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int prueth_suspend(struct device *dev) +{ + struct prueth *prueth = dev_get_drvdata(dev); + struct net_device *ndev; + int i, ret; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + ndev = prueth->registered_netdevs[i]; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + netif_device_detach(ndev); + ret = emac_ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return ret; + } + } + } + + return 0; +} + +static int prueth_resume(struct device *dev) +{ + struct prueth *prueth = dev_get_drvdata(dev); + struct net_device *ndev; + int i, ret; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + ndev = prueth->registered_netdevs[i]; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + ret = emac_ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return ret; + } + netif_device_attach(ndev); + } + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops prueth_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(prueth_suspend, prueth_resume) +}; + +static const struct prueth_pdata am654_icssg_pdata_sr1 = { + .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE, +}; + +static const struct prueth_pdata am654_icssg_pdata = { + .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE, + .quirk_10m_link_issue = 1, + .switch_mode = 1, +}; + +static const struct prueth_pdata am64x_icssg_pdata = { + .fdqring_mode = K3_RINGACC_RING_MODE_RING, +}; + +static const struct of_device_id prueth_dt_match[] = { + { .compatible = "ti,am654-icssg-prueth-sr1", .data = &am654_icssg_pdata_sr1 }, + { .compatible = "ti,am654-icssg-prueth", .data = &am654_icssg_pdata }, + { .compatible = "ti,am642-icssg-prueth", .data = &am64x_icssg_pdata }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, prueth_dt_match); + +static struct platform_driver prueth_driver = { + .probe = prueth_probe, + .remove = prueth_remove, + .driver = { + .name = "icssg-prueth", + .of_match_table = prueth_dt_match, + .pm = &prueth_dev_pm_ops, + }, +}; +module_platform_driver(prueth_driver); + +MODULE_AUTHOR("Roger Quadros "); +MODULE_DESCRIPTION("PRUSS ICSSG Ethernet Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_prueth.h b/drivers/net/ethernet/ti/icssg_prueth.h --- a/drivers/net/ethernet/ti/icssg_prueth.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_prueth.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,357 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSG Ethernet driver + * + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#ifndef __NET_TI_ICSSG_PRUETH_H +#define __NET_TI_ICSSG_PRUETH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "icssg_config.h" +#include "icss_iep.h" +#include "icssg_switch_map.h" +#include "icssg_qos.h" + +#define ICSS_SLICE0 0 +#define ICSS_SLICE1 1 + +#define ICSS_FW_PRU 0 +#define ICSS_FW_RTU 1 + +#define ICSSG_MAX_RFLOWS 8 /* per slice */ + +/* Firmware status codes */ +#define ICSS_HS_FW_READY 0x55555555 +#define ICSS_HS_FW_DEAD 0xDEAD0000 /* lower 16 bits contain error code */ + +/* Firmware command codes */ +#define ICSS_HS_CMD_BUSY 0x40000000 +#define ICSS_HS_CMD_DONE 0x80000000 +#define ICSS_HS_CMD_CANCEL 0x10000000 + +/* Firmware commands */ +#define ICSS_CMD_SPAD 0x20 +#define ICSS_CMD_RXTX 0x10 +#define ICSS_CMD_ADD_FDB 0x1 +#define ICSS_CMD_DEL_FDB 0x2 +#define ICSS_CMD_SET_RUN 0x4 +#define ICSS_CMD_GET_FDB_SLOT 0x5 +#define ICSS_CMD_ENABLE_VLAN 0x5 +#define ICSS_CMD_DISABLE_VLAN 0x6 +#define ICSS_CMD_ADD_FILTER 0x7 +#define ICSS_CMD_ADD_MAC 0x8 + +/* Firmware flags */ +#define ICSS_SET_RUN_FLAG_VLAN_ENABLE BIT(0) /* switch only */ +#define ICSS_SET_RUN_FLAG_FLOOD_UNICAST BIT(1) /* switch only */ +#define ICSS_SET_RUN_FLAG_PROMISC BIT(2) /* MAC only */ +#define ICSS_SET_RUN_FLAG_MULTICAST_PROMISC BIT(3) /* MAC only */ + +/* In switch mode there are 3 real ports i.e. 3 mac addrs. + * however Linux sees only the host side port. The other 2 ports + * are the switch ports. + * In emac mode there are 2 real ports i.e. 2 mac addrs. + * Linux sees both the ports. + */ +enum prueth_port { + PRUETH_PORT_HOST = 0, /* host side port */ + PRUETH_PORT_MII0, /* physical port RG/SG MII 0 */ + PRUETH_PORT_MII1, /* physical port RG/SG MII 1 */ +}; + +enum prueth_mac { + PRUETH_MAC0 = 0, + PRUETH_MAC1, + PRUETH_NUM_MACS, +}; + +struct prueth_tx_chn { + struct device *dma_dev; + struct napi_struct napi_tx; + struct k3_cppi_desc_pool *desc_pool; + struct k3_udma_glue_tx_channel *tx_chn; + struct prueth_emac *emac; + u32 id; + u32 descs_num; + unsigned int irq; + char name[32]; +}; + +struct prueth_rx_chn { + struct device *dev; + struct device *dma_dev; + struct k3_cppi_desc_pool *desc_pool; + struct k3_udma_glue_rx_channel *rx_chn; + u32 descs_num; + unsigned int irq[ICSSG_MAX_RFLOWS]; /* separate irq per flow */ + char name[32]; +}; + +enum prueth_state_flags { + __STATE_TX_TS_IN_PROGRESS, +}; + +enum prueth_devlink_param_id { + PRUETH_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, + PRUETH_DL_PARAM_SWITCH_MODE, +}; + +struct prueth_devlink { + struct prueth *prueth; +}; + +/* There are 4 Tx DMA channels, but the highest priority is CH3 (thread 3) + * and lower three are lower priority channels or threads. + */ +#define PRUETH_MAX_TX_QUEUES 4 + +/* data for each emac port */ +struct prueth_emac { + bool is_sr1; + bool fw_running; + struct prueth *prueth; + struct net_device *ndev; + u8 mac_addr[6]; + struct napi_struct napi_rx; + u32 msg_enable; + + int link; + int speed; + int duplex; + + const char *phy_id; + struct device_node *phy_node; + phy_interface_t phy_if; + struct phy_device *phydev; + enum prueth_port port_id; + struct icss_iep *iep; + unsigned int rx_ts_enabled : 1; + unsigned int tx_ts_enabled : 1; + unsigned int half_duplex : 1; + + /* DMA related */ + struct prueth_tx_chn tx_chns[PRUETH_MAX_TX_QUEUES]; + struct completion tdown_complete; + atomic_t tdown_cnt; + struct prueth_rx_chn rx_chns; + int rx_flow_id_base; + int tx_ch_num; + + /* SR1.0 Management channel */ + struct prueth_rx_chn rx_mgm_chn; + int rx_mgm_flow_id_base; + + spinlock_t lock; /* serialize access */ + + /* TX HW Timestamping */ + u32 tx_ts_cookie; + struct sk_buff *tx_ts_skb; + unsigned long state; + int tx_ts_irq; + + u8 cmd_seq; + /* shutdown related */ + u32 cmd_data[4]; + struct completion cmd_complete; + /* Mutex to serialize access to firmware command interface */ + struct mutex cmd_lock; + struct work_struct rx_mode_work; + struct workqueue_struct *cmd_wq; + + struct pruss_mem_region dram; + + bool offload_fwd_mark; + struct devlink_port devlink_port; + int port_vlan; + + struct prueth_qos qos; + struct work_struct ts_work; +}; + +/** + * struct prueth - PRUeth platform data + * @fdqring_mode: Free desc queue mode + * @quirk_10m_link_issue: 10M link detect errata + * @switch_mode: switch firmware support + */ +struct prueth_pdata { + enum k3_ring_mode fdqring_mode; + + u32 quirk_10m_link_issue:1; + u32 switch_mode:1; +}; + +/** + * struct prueth - PRUeth structure + * @is_sr1: device is pg1.0 (pg1.0 will be deprecated upstream) + * @dev: device + * @pruss: pruss handle + * @pru: rproc instances of PRUs + * @rtu: rproc instances of RTUs + * @rtu: rproc instances of TX_PRUs + * @shram: PRUSS shared RAM region + * @sram_pool: MSMC RAM pool for buffers + * @msmcram: MSMC RAM region + * @eth_node: DT node for the port + * @emac: private EMAC data structure + * @registered_netdevs: list of registered netdevs + * @fw_data: firmware names to be used with PRU remoteprocs + * @config: firmware load time configuration per slice + * @miig_rt: regmap to mii_g_rt block + * @pa_stats: regmap to pa_stats block + * @pru_id: ID for each of the PRUs + * @pdev: pointer to ICSSG platform device + * @iep0: pointer to IEP0 device + * @iep1: pointer to IEP1 device + * @pdata: pointer to platform data for ICSSG driver + * @vlan_tbl: VLAN-FID table pointer + * @icssg_hwcmdseq: seq counter or HWQ messages + * @emacs_initialized: num of EMACs/ext ports that are up/running + * @hw_bridge_dev: pointer to HW bridge net device + * @br_members: bitmask of bridge member ports + * @prueth_netdevice_nb: netdevice notifier block + * @prueth_switchdevice_nb: switchdev notifier block + * @prueth_switchdev_bl_nb: switchdev blocking notifier block + * @is_switch_mode: flag to indicate if device is in Switch mode + * @is_switchmode_supported: indicates platform support for switch mode + * @switch_id: ID for mapping switch ports to bridge + * @default_vlan: Default VLAN for host + * @devlink: pointer to devlink + */ +struct prueth { + bool is_sr1; + struct device *dev; + struct pruss *pruss; + struct rproc *pru[PRUSS_NUM_PRUS]; + struct rproc *rtu[PRUSS_NUM_PRUS]; + struct rproc *txpru[PRUSS_NUM_PRUS]; + struct pruss_mem_region shram; + struct gen_pool *sram_pool; + struct pruss_mem_region msmcram; + + struct device_node *eth_node[PRUETH_NUM_MACS]; + struct prueth_emac *emac[PRUETH_NUM_MACS]; + struct net_device *registered_netdevs[PRUETH_NUM_MACS]; + const struct prueth_private_data *fw_data; + struct icssg_config_sr1 config[PRUSS_NUM_PRUS]; + struct regmap *miig_rt; + struct regmap *mii_rt; + struct regmap *pa_stats; + + enum pruss_pru_id pru_id[PRUSS_NUM_PRUS]; + struct platform_device *pdev; + struct icss_iep *iep0; + struct icss_iep *iep1; + struct prueth_pdata pdata; + struct prueth_vlan_tbl *vlan_tbl; + u8 icssg_hwcmdseq; + + int emacs_initialized; + + struct net_device *hw_bridge_dev; + u8 br_members; + struct notifier_block prueth_netdevice_nb; + struct notifier_block prueth_switchdev_nb; + struct notifier_block prueth_switchdev_bl_nb; + bool is_switch_mode; + bool is_switchmode_supported; + unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; + int default_vlan; + struct devlink *devlink; +}; + +struct emac_tx_ts_response_sr1 { + u32 lo_ts; + u32 hi_ts; + u32 reserved; + u32 cookie; +}; + +struct emac_tx_ts_response { + u32 reserved[2]; + u32 cookie; + u32 lo_ts; + u32 hi_ts; +}; + +/* Classifier helpers */ +void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac); +void icssg_class_set_host_mac_addr(struct regmap *miig_rt, u8 *mac); +void icssg_class_disable(struct regmap *miig_rt, int slice); +void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti, + bool is_sr1); +void icssg_class_promiscuous_sr1(struct regmap *miig_rt, int slice); +void icssg_class_add_mcast_sr1(struct regmap *miig_rt, int slice, + struct net_device *ndev); +void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr); + +/* Buffer queue helpers */ +int icssg_queue_pop(struct prueth *prueth, u8 queue); +void icssg_queue_push(struct prueth *prueth, int queue, u16 addr); +u32 icssg_queue_level(struct prueth *prueth, int queue); + +/* get PRUSS SLICE number from prueth_emac */ +static inline int prueth_emac_slice(struct prueth_emac *emac) +{ + switch (emac->port_id) { + case PRUETH_PORT_MII0: + return ICSS_SLICE0; + case PRUETH_PORT_MII1: + return ICSS_SLICE1; + default: + return -EINVAL; + } +} + +/* config helpers */ +void icssg_config_ipg(struct prueth_emac *emac); +void icssg_config_sr1(struct prueth *prueth, struct prueth_emac *emac, + int slice); +int icssg_config_sr2(struct prueth *prueth, struct prueth_emac *emac, + int slice); +int emac_set_port_state(struct prueth_emac *emac, + enum icssg_port_state_cmd state); +void icssg_config_set_speed(struct prueth_emac *emac); +void icssg_config_half_duplex(struct prueth_emac *emac); + +int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd, + struct mgmt_cmd_rsp *rsp); +int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr, + u8 vid, u8 fid_c2, bool add); +int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr, + u8 vid); +void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask, + u8 untag_mask, bool add); +u16 icssg_get_pvid(struct prueth_emac *emac); +void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port); +#define prueth_napi_to_tx_chn(pnapi) \ + container_of(pnapi, struct prueth_tx_chn, napi_tx) + +u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts); +#endif /* __NET_TI_ICSSG_PRUETH_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_qos.c b/drivers/net/ethernet/ti/icssg_qos.c --- a/drivers/net/ethernet/ti/icssg_qos.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_qos.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG PRUETH QoS submodule + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include +#include "icssg_prueth.h" +#include "icssg_switch_map.h" + +/* in msec */ +#define ICSSG_IET_FPE_VERIFY_TIMEOUT_MS 1000 + +static void icssg_qos_tas_init(struct net_device *ndev); +static void icssg_prueth_iet_fpe_disable(struct prueth_qos_iet *iet); +static int icssg_prueth_iet_fpe_enable(struct prueth_emac *emac); +static void icssg_prueth_iet_fpe_disable(struct prueth_qos_iet *iet); +static void icssg_qos_enable_ietfpe(struct work_struct *work); + +void icssg_qos_init(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth_qos_iet *iet = &emac->qos.iet; + + icssg_qos_tas_init(ndev); + + if (!iet->fpe_configured) + return; + + /* Init work queue for IET MAC verify process */ + iet->emac = emac; + INIT_WORK(&iet->fpe_config_task, icssg_qos_enable_ietfpe); + init_completion(&iet->fpe_config_compl); + + /* As worker may be sleeping, check this flag to abort + * as soon as it comes of out of sleep and cancel the + * fpe config task. + */ + atomic_set(&iet->cancel_fpe_config, 0); +} + +static void tas_update_fw_list_pointers(struct prueth_emac *emac) +{ + struct tas_config *tas = &emac->qos.tas.config; + + if ((readb(tas->active_list)) == TAS_LIST0) { + tas->firmware_active_list = emac->dram.va + TAS_GATE_MASK_LIST0; + tas->firmware_shadow_list = emac->dram.va + TAS_GATE_MASK_LIST1; + } else { + tas->firmware_active_list = emac->dram.va + TAS_GATE_MASK_LIST1; + tas->firmware_shadow_list = emac->dram.va + TAS_GATE_MASK_LIST0; + } +} + +void icssg_qos_link_up(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth_qos_iet *iet = &emac->qos.iet; + + if (!iet->fpe_configured) + return; + + icssg_prueth_iet_fpe_enable(emac); +} + +void icssg_qos_link_down(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth_qos_iet *iet = &emac->qos.iet; + + if (iet->fpe_configured) + icssg_prueth_iet_fpe_disable(iet); +} +static void tas_update_maxsdu_table(struct prueth_emac *emac) +{ + struct tas_config *tas = &emac->qos.tas.config; + u16 *max_sdu_tbl_ptr; + u8 gate_idx; + + /* update the maxsdu table */ + max_sdu_tbl_ptr = emac->dram.va + TAS_QUEUE_MAX_SDU_LIST; + + for (gate_idx = 0; gate_idx < TAS_MAX_NUM_QUEUES; gate_idx++) + writew(tas->max_sdu_table.max_sdu[gate_idx], &max_sdu_tbl_ptr[gate_idx]); +} + +static void tas_reset(struct prueth_emac *emac) +{ + struct tas_config *tas = &emac->qos.tas.config; + int i; + + for (i = 0; i < TAS_MAX_NUM_QUEUES; i++) + tas->max_sdu_table.max_sdu[i] = 2048; + + tas_update_maxsdu_table(emac); + + writeb(TAS_LIST0, tas->active_list); + + memset_io(tas->firmware_active_list, 0, sizeof(*tas->firmware_active_list)); + memset_io(tas->firmware_shadow_list, 0, sizeof(*tas->firmware_shadow_list)); +} + +static int tas_set_state(struct prueth_emac *emac, enum tas_state state) +{ + struct tas_config *tas = &emac->qos.tas.config; + int ret; + + if (tas->state == state) + return 0; + + switch (state) { + case TAS_STATE_RESET: + tas_reset(emac); + ret = emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_RESET); + tas->state = TAS_STATE_RESET; + break; + case TAS_STATE_ENABLE: + ret = emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_ENABLE); + tas->state = TAS_STATE_ENABLE; + break; + case TAS_STATE_DISABLE: + ret = emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_DISABLE); + tas->state = TAS_STATE_DISABLE; + break; + default: + netdev_err(emac->ndev, "%s: unsupported state\n", __func__); + ret = -EINVAL; + break; + } + + if (ret) + netdev_err(emac->ndev, "TAS set state failed %d\n", ret); + return ret; +} + +static int tas_set_trigger_list_change(struct prueth_emac *emac) +{ + struct tc_taprio_qopt_offload *admin_list = emac->qos.tas.taprio_admin; + struct tas_config *tas = &emac->qos.tas.config; + struct ptp_system_timestamp sts; + u32 change_cycle_count; + u32 cycle_time; + u64 base_time; + u64 cur_time; + + cycle_time = admin_list->cycle_time - 4; /* -4ns to compensate for IEP wraparound time */ + base_time = admin_list->base_time; + cur_time = prueth_iep_gettime(emac, &sts); + + if (base_time > cur_time) + change_cycle_count = DIV_ROUND_UP_ULL(base_time - cur_time, cycle_time); + else + change_cycle_count = 1; + + writel(cycle_time, emac->dram.va + TAS_ADMIN_CYCLE_TIME); + writel(change_cycle_count, emac->dram.va + TAS_CONFIG_CHANGE_CYCLE_COUNT); + writeb(admin_list->num_entries, emac->dram.va + TAS_ADMIN_LIST_LENGTH); + + /* config_change cleared by f/w to ack reception of new shadow list */ + writeb(1, &tas->config_list->config_change); + /* config_pending cleared by f/w when new shadow list is copied to active list */ + writeb(1, &tas->config_list->config_pending); + + return emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_TRIGGER); +} + +static int tas_update_oper_list(struct prueth_emac *emac) +{ + struct tas_config *tas = &emac->qos.tas.config; + struct tc_taprio_qopt_offload *admin_list = emac->qos.tas.taprio_admin; + int ret; + u8 win_idx, gate_idx, val; + u32 tas_acc_gate_close_time = 0; + + tas_update_fw_list_pointers(emac); + + for (win_idx = 0; win_idx < admin_list->num_entries; win_idx++) { + tas->firmware_shadow_list->gate_mask_list[win_idx] = admin_list->entries[win_idx].gate_mask; + tas_acc_gate_close_time += admin_list->entries[win_idx].interval; + + /* extend last entry till end of cycle time */ + if (win_idx == admin_list->num_entries - 1) + tas->firmware_shadow_list->window_end_time_list[win_idx] = admin_list->cycle_time; + else + tas->firmware_shadow_list->window_end_time_list[win_idx] = tas_acc_gate_close_time; + } + + /* clear remaining entries */ + for (win_idx = admin_list->num_entries; win_idx < TAS_MAX_CMD_LISTS; win_idx++) { + tas->firmware_shadow_list->gate_mask_list[win_idx] = 0; + tas->firmware_shadow_list->window_end_time_list[win_idx] = 0; + } + + /* update the Array of gate close time for each queue in each window */ + for (win_idx = 0 ; win_idx < admin_list->num_entries; win_idx++) { + /* On Linux, only PRUETH_MAX_TX_QUEUES are supported per port */ + for (gate_idx = 0; gate_idx < PRUETH_MAX_TX_QUEUES; gate_idx++) { + u32 gate_close_time = 0; + + if (tas->firmware_shadow_list->gate_mask_list[win_idx] & BIT(gate_idx)) + gate_close_time = tas->firmware_shadow_list->window_end_time_list[win_idx]; + + tas->firmware_shadow_list->gate_close_time_list[win_idx][gate_idx] = gate_close_time; + } + } + + /* tell f/w to swap active & shadow list */ + ret = tas_set_trigger_list_change(emac); + if (ret) { + netdev_err(emac->ndev, "failed to swap f/w config list: %d\n", ret); + return ret; + } + + /* Wait for completion */ + ret = readb_poll_timeout(&tas->config_list->config_change, val, !val, + USEC_PER_MSEC, 10 * USEC_PER_MSEC); + if (ret) { + netdev_err(emac->ndev, "TAS list change completion time out\n"); + return ret; + } + + tas_update_fw_list_pointers(emac); + + return 0; +} + +static int emac_set_taprio(struct prueth_emac *emac) +{ + int ret; + struct tc_taprio_qopt_offload *taprio = emac->qos.tas.taprio_admin; + + if (!taprio->enable) + return tas_set_state(emac, TAS_STATE_DISABLE); + + ret = tas_update_oper_list(emac); + if (ret) + return ret; + + return tas_set_state(emac, TAS_STATE_ENABLE); +} + +static void emac_cp_taprio(struct tc_taprio_qopt_offload *from, + struct tc_taprio_qopt_offload *to) +{ + int i; + + *to = *from; + for (i = 0; i < from->num_entries; i++) + to->entries[i] = from->entries[i]; +} + +static int emac_setup_taprio(struct net_device *ndev, struct tc_taprio_qopt_offload *taprio) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct tc_taprio_qopt_offload *est_new; + int ret, win_idx; + + if (!netif_running(ndev)) { + netdev_err(ndev, "interface is down, link speed unknown\n"); + return -ENETDOWN; + } + + if (taprio->cycle_time_extension) { + netdev_err(ndev, "Failed to set cycle time extension"); + return -EOPNOTSUPP; + } + + if (taprio->num_entries == 0 || + taprio->num_entries > TAS_MAX_CMD_LISTS) { + netdev_err(ndev, "unsupported num_entries %ld in taprio config\n", + taprio->num_entries); + return -EINVAL; + } + + /* If any time_interval is 0 in between the list, then exit */ + for (win_idx = 0; win_idx < taprio->num_entries; win_idx++) { + if (taprio->entries[win_idx].interval == 0) { + netdev_err(ndev, "0 interval in taprio config not supported\n"); + return -EINVAL; + } + } + + if (emac->qos.tas.taprio_admin) + devm_kfree(&ndev->dev, emac->qos.tas.taprio_admin); + + est_new = devm_kzalloc(&ndev->dev, + struct_size(est_new, entries, taprio->num_entries), + GFP_KERNEL); + emac_cp_taprio(taprio, est_new); + emac->qos.tas.taprio_admin = est_new; + ret = emac_set_taprio(emac); + if (ret) + devm_kfree(&ndev->dev, est_new); + + return ret; +} + +int icssg_qos_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type, + void *type_data) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (emac->prueth->is_sr1) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_QDISC_TAPRIO: + return emac_setup_taprio(ndev, type_data); + default: + return -EOPNOTSUPP; + } +} + +static void icssg_qos_tas_init(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct tas_config *tas = &emac->qos.tas.config; + bool need_setup = false; + + if (emac->prueth->is_sr1) + return; + + if (tas->state == TAS_STATE_ENABLE) + need_setup = true; + + tas->config_list = emac->dram.va + TAS_CONFIG_CHANGE_TIME; + tas->active_list = emac->dram.va + TAS_ACTIVE_LIST_INDEX; + + tas_update_fw_list_pointers(emac); + + tas_set_state(emac, TAS_STATE_RESET); + + if (need_setup) + emac_set_taprio(emac); +} + +static int icssg_config_ietfpe(struct prueth_qos_iet *iet, bool enable) +{ + void *config = iet->emac->dram.va + ICSSG_CONFIG_OFFSET; + u8 val; + int ret, i; + + /* If FPE is to be enabled, first configure MAC Verify state + * machine in firmware as firmware kicks the Verify process + * as soon as ICSSG_EMAC_PORT_PREMPT_TX_ENABLE command is + * received. + */ + if (enable && iet->mac_verify_configured) { + writeb(1, config + PRE_EMPTION_ENABLE_VERIFY); + /* should be a multiple of 64. TODO to configure + * through ethtool. + */ + writew(64, config + PRE_EMPTION_ADD_FRAG_SIZE_LOCAL); + writel(ICSSG_IET_FPE_VERIFY_TIMEOUT_MS, config + PRE_EMPTION_VERIFY_TIME); + } + + /* Send command to enable FPE Tx side. Rx is always enabled */ + ret = emac_set_port_state(iet->emac, + enable ? ICSSG_EMAC_PORT_PREMPT_TX_ENABLE : + ICSSG_EMAC_PORT_PREMPT_TX_DISABLE); + if (ret) { + netdev_err(iet->emac->ndev, "TX pre-empt %s command failed\n", + enable ? "enable" : "disable"); + writeb(0, config + PRE_EMPTION_ENABLE_VERIFY); + return ret; + } + + /* Update FPE Tx enable bit. Assume firmware use this bit + * and enable PRE_EMPTION_ACTIVE_TX if everything looks + * good at firmware + */ + writeb(enable ? 1 : 0, config + PRE_EMPTION_ENABLE_TX); + + if (enable && iet->mac_verify_configured) { + ret = readb_poll_timeout(config + PRE_EMPTION_VERIFY_STATUS, val, + (val == ICSSG_IETFPE_STATE_SUCCEEDED), + USEC_PER_MSEC, 5 * USEC_PER_SEC); + if (ret == -ETIMEDOUT) { + netdev_err(iet->emac->ndev, + "timeout for MAC Verify: status %x\n", + val); + return ret; + } + } else { + /* Give f/w some time to update PRE_EMPTION_ACTIVE_TX state */ + usleep_range(100, 200); + } + + if (enable) { + val = readb(config + PRE_EMPTION_ACTIVE_TX); + if (val != 1) { + netdev_err(iet->emac->ndev, + "F/w fails to activate IET/FPE\n"); + writeb(0, config + PRE_EMPTION_ENABLE_TX); + return -ENODEV; + } + } else { + return ret; + } + + /* Configure highest queue as express. Set Bit 4 for FPE, + * Reset for express + */ + + /* first set all 8 queues as Pre-emptive */ + for (i = 0; i < PRUETH_MAX_TX_QUEUES * PRUETH_NUM_MACS; i++) + writeb(BIT(4), config + EXPRESS_PRE_EMPTIVE_Q_MAP + i); + + /* set highest priority channel queue as express */ + writeb(0, config + EXPRESS_PRE_EMPTIVE_Q_MAP + iet->emac->tx_ch_num - 1); + + /* set up queue mask for FPE. 1 means express */ + writeb(BIT(iet->emac->tx_ch_num - 1), config + EXPRESS_PRE_EMPTIVE_Q_MASK); + + iet->fpe_enabled = true; + + return ret; +} + +static void icssg_qos_enable_ietfpe(struct work_struct *work) +{ + struct prueth_qos_iet *iet = + container_of(work, struct prueth_qos_iet, fpe_config_task); + int ret; + + /* Set the required flag and send a command to ICSSG firmware to + * enable FPE and start MAC verify + */ + ret = icssg_config_ietfpe(iet, true); + + /* if verify configured, poll for the status and complete. + * Or just do completion + */ + if (!ret) + netdev_err(iet->emac->ndev, "IET FPE configured successfully\n"); + else + netdev_err(iet->emac->ndev, "IET FPE config error\n"); + complete(&iet->fpe_config_compl); +} + +static void icssg_prueth_iet_fpe_disable(struct prueth_qos_iet *iet) +{ + int ret; + + atomic_set(&iet->cancel_fpe_config, 1); + cancel_work_sync(&iet->fpe_config_task); + ret = icssg_config_ietfpe(iet, false); + if (!ret) + netdev_err(iet->emac->ndev, "IET FPE disabled successfully\n"); + else + netdev_err(iet->emac->ndev, "IET FPE disable failed\n"); +} + +static int icssg_prueth_iet_fpe_enable(struct prueth_emac *emac) +{ + struct prueth_qos_iet *iet = &emac->qos.iet; + int ret; + + /* Schedule MAC Verify and enable IET FPE if configured */ + atomic_set(&iet->cancel_fpe_config, 0); + reinit_completion(&iet->fpe_config_compl); + schedule_work(&iet->fpe_config_task); + /* By trial, found it takes about 1.5s. So + * wait for 10s + */ + ret = wait_for_completion_timeout(&iet->fpe_config_compl, + msecs_to_jiffies(10000)); + if (!ret) { + netdev_err(emac->ndev, + "IET verify completion timeout\n"); + /* cancel verify in progress */ + atomic_set(&iet->cancel_fpe_config, 1); + cancel_work_sync(&iet->fpe_config_task); + } + + return ret; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_qos.h b/drivers/net/ethernet/ti/icssg_qos.h --- a/drivers/net/ethernet/ti/icssg_qos.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_qos.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#ifndef __NET_TI_ICSSG_QOS_H +#define __NET_TI_ICSSG_QOS_H + +#include +#include +#include + +/** + * Maximum number of gate command entries in each list. + */ +#define TAS_MAX_CMD_LISTS (16) + +/** + * Maximum number of transmit queues supported by implementation + */ +#define TAS_MAX_NUM_QUEUES (8) + +/** + * Minimum cycle time supported by implementation (in ns) + */ +#define TAS_MIN_CYCLE_TIME (1000000) + +/** + * Minimum TAS window duration supported by implementation (in ns) + */ +#define TAS_MIN_WINDOW_DURATION (10000) + +/** + * List number 0 or 1. Also the value at memory location TAS_ACTIVE_LIST_INDEX + */ +enum tas_list_num { + TAS_LIST0 = 0, + TAS_LIST1 = 1 +}; + +/** + * state of TAS in f/w + */ +enum tas_state { + /* PRU's are idle */ + TAS_STATE_DISABLE = 0, + /* Enable TAS */ + TAS_STATE_ENABLE = 1, + /* Firmware will reset the state machine */ + TAS_STATE_RESET = 2, +}; + +/** + * Config state machine variables. See IEEE Std 802.1Q-2018 8.6.8.4 + */ +struct tas_config_list { + /* New list is copied at this time */ + u64 config_change_time; + /* config change error counter, incremented if + * admin->BaseTime < current time and TAS_enabled is true + */ + u32 config_change_error_counter; + /* True if list update is pending */ + u8 config_pending; + /* Set to true when application trigger updating of admin list + * to active list, cleared when configChangeTime is updated + */ + u8 config_change; +}; + +/** + * Max SDU table. See IEEE Std 802.1Q-2018 12.29.1.1 + */ +struct tas_max_sdu_table { + u16 max_sdu[TAS_MAX_NUM_QUEUES]; +}; + +/** + * TAS List Structure based on firmware memory map + */ +struct tas_firmware_list { + /* window gate mask list */ + u8 gate_mask_list[TAS_MAX_CMD_LISTS]; + /* window end time list */ + u32 window_end_time_list[TAS_MAX_CMD_LISTS]; + /* Array of gate close time for each queue in each window */ + u32 gate_close_time_list[TAS_MAX_CMD_LISTS][TAS_MAX_NUM_QUEUES]; +}; + +/** + * Main Time Aware Shaper Handle + */ +struct tas_config { + enum tas_state state; + struct tas_max_sdu_table max_sdu_table; + /* Config change variables */ + struct __iomem tas_config_list * config_list; + /* Whether list 1 or list 2 is the operating list */ + u8 __iomem *active_list; + /* active List pointer, used by firmware */ + struct __iomem tas_firmware_list * firmware_active_list; + /* shadow List pointer, used by driver */ + struct __iomem tas_firmware_list * firmware_shadow_list; +}; + +struct prueth_qos_tas { + struct tc_taprio_qopt_offload *taprio_admin; + struct tc_taprio_qopt_offload *taprio_oper; + struct tas_config config; +}; + +struct prueth_qos_iet { + struct work_struct fpe_config_task; + struct completion fpe_config_compl; + struct prueth_emac *emac; + atomic_t cancel_fpe_config; + /* Set through priv flags to enable IET frame preemption */ + bool fpe_configured; + /* Set if IET FPE is active */ + bool fpe_enabled; + /* Set through priv flags to enable IET MAC Verify state machine + * in firmware + */ + bool mac_verify_configured; +}; + +struct prueth_qos { + struct prueth_qos_iet iet; + struct prueth_qos_tas tas; +}; + +void icssg_qos_init(struct net_device *ndev); +int icssg_qos_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type, + void *type_data); +void icssg_qos_link_up(struct net_device *ndev); +void icssg_qos_link_down(struct net_device *ndev); +#endif /* __NET_TI_ICSSG_QOS_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_queues.c b/drivers/net/ethernet/ti/icssg_queues.c --- a/drivers/net/ethernet/ti/icssg_queues.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_queues.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ICSSG Buffer queue helpers + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include "icssg_prueth.h" + +#define ICSSG_QUEUES_MAX 64 +#define ICSSG_QUEUE_OFFSET 0xd00 +#define ICSSG_QUEUE_PEEK_OFFSET 0xe00 +#define ICSSG_QUEUE_CNT_OFFSET 0xe40 +#define ICSSG_QUEUE_RESET_OFFSET 0xf40 + +int icssg_queue_pop(struct prueth *prueth, u8 queue) +{ + u32 val, cnt; + + if (queue >= ICSSG_QUEUES_MAX) + return -EINVAL; + + regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4 * queue, &cnt); + if (!cnt) + return -EINVAL; + + regmap_read(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, &val); + + return val; +} + +void icssg_queue_push(struct prueth *prueth, int queue, u16 addr) +{ + if (queue >= ICSSG_QUEUES_MAX) + return; + + regmap_write(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, addr); +} + +u32 icssg_queue_level(struct prueth *prueth, int queue) +{ + u32 reg; + + if (queue >= ICSSG_QUEUES_MAX) + return 0; + + regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4 * queue, ®); + + return reg; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_switchdev.c b/drivers/net/ethernet/ti/icssg_switchdev.c --- a/drivers/net/ethernet/ti/icssg_switchdev.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_switchdev.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,494 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments K3 ICSSG Ethernet Switchdev Driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include +#include +#include + +#include "icssg_prueth.h" +#include "icssg_switchdev.h" +#include "icss_mii_rt.h" + +struct prueth_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct prueth_emac *emac; + unsigned long event; +}; + +static int prueth_switchdev_stp_state_set(struct prueth_emac *emac, + struct switchdev_trans *trans, + u8 state) +{ + enum icssg_port_state_cmd emac_state; + int ret = 0; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + switch (state) { + case BR_STATE_FORWARDING: + emac_state = ICSSG_EMAC_PORT_FORWARD; + break; + case BR_STATE_DISABLED: + emac_state = ICSSG_EMAC_PORT_DISABLE; + break; + case BR_STATE_LEARNING: + case BR_STATE_LISTENING: + case BR_STATE_BLOCKING: + emac_state = ICSSG_EMAC_PORT_BLOCK; + break; + default: + return -EOPNOTSUPP; + } + + emac_set_port_state(emac, emac_state); + netdev_dbg(emac->ndev, "STP state: %u\n", emac_state); + + return ret; +} + +static int prueth_switchdev_attr_br_flags_set(struct prueth_emac *emac, + struct switchdev_trans *trans, + struct net_device *orig_dev, + unsigned long brport_flags) +{ + enum icssg_port_state_cmd emac_state; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + if (brport_flags & BR_MCAST_FLOOD) + emac_state = ICSSG_EMAC_PORT_MC_FLOODING_ENABLE; + else + emac_state = ICSSG_EMAC_PORT_MC_FLOODING_DISABLE; + + netdev_dbg(emac->ndev, "BR_MCAST_FLOOD: %d port %u\n", + emac_state, emac->port_id); + + emac_set_port_state(emac, emac_state); + + return 0; +} + +static int prueth_switchdev_attr_br_flags_pre_set(struct net_device *netdev, + struct switchdev_trans *trans, + unsigned long flags) +{ + if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD)) + return -EINVAL; + + return 0; +} + +static int prueth_switchdev_attr_set(struct net_device *ndev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int ret; + + netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, emac->port_id); + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + ret = prueth_switchdev_attr_br_flags_pre_set(ndev, trans, + attr->u.brport_flags); + break; + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + ret = prueth_switchdev_stp_state_set(emac, trans, + attr->u.stp_state); + netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + ret = prueth_switchdev_attr_br_flags_set(emac, trans, attr->orig_dev, + attr->u.brport_flags); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void prueth_switchdev_fdb_offload_notify(struct net_device *ndev, + struct switchdev_notifier_fdb_info *rcv) +{ + struct switchdev_notifier_fdb_info info; + + memset(&info, 0, sizeof(info)); + info.addr = rcv->addr; + info.vid = rcv->vid; + info.offloaded = true; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, + ndev, &info.info, NULL); +} + +static void prueth_switchdev_event_work(struct work_struct *work) +{ + struct prueth_switchdev_event_work *switchdev_work = + container_of(work, struct prueth_switchdev_event_work, work); + struct prueth_emac *emac = switchdev_work->emac; + struct switchdev_notifier_fdb_info *fdb; + int port_id = emac->port_id; + int ret; + + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb = &switchdev_work->fdb_info; + + netdev_dbg(emac->ndev, "prueth_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, + fdb->offloaded, port_id); + + if (!fdb->added_by_user) + break; + if (memcmp(emac->mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0) + break; + + ret = icssg_fdb_add_del(emac, fdb->addr, fdb->vid, + BIT(port_id), true); + if (!ret) + prueth_switchdev_fdb_offload_notify(emac->ndev, fdb); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb = &switchdev_work->fdb_info; + + netdev_dbg(emac->ndev, "prueth_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, + fdb->offloaded, port_id); + + if (!fdb->added_by_user) + break; + if (memcmp(emac->mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0) + break; + icssg_fdb_add_del(emac, fdb->addr, fdb->vid, + BIT(port_id), false); + break; + default: + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(emac->ndev); +} + +static int prueth_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); + struct prueth_switchdev_event_work *switchdev_work; + struct prueth_emac *emac = netdev_priv(ndev); + struct switchdev_notifier_fdb_info *fdb_info = ptr; + int err; + + if (!prueth_dev_check(ndev)) + return NOTIFY_DONE; + + if (event == SWITCHDEV_PORT_ATTR_SET) { + err = switchdev_handle_port_attr_set(ndev, ptr, + prueth_dev_check, + prueth_switchdev_attr_set); + return notifier_from_errno(err); + } + + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, prueth_switchdev_event_work); + switchdev_work->emac = emac; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + if (!switchdev_work->fdb_info.addr) + goto err_addr_alloc; + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + dev_hold(ndev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(system_long_wq, &switchdev_work->work); + + return NOTIFY_DONE; + +err_addr_alloc: + kfree(switchdev_work); + return NOTIFY_BAD; +} + +static int prueth_switchdev_vlan_add(struct prueth_emac *emac, bool untag, bool pvid, + u8 vid, struct net_device *orig_dev) +{ + bool cpu_port = netif_is_bridge_master(orig_dev); + int untag_mask = 0; + int port_mask; + int ret = 0; + + if (cpu_port) + port_mask = BIT(PRUETH_PORT_HOST); + else + port_mask = BIT(emac->port_id); + + if (untag) + untag_mask = port_mask; + + icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true); + + netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X PVID %d\n", + vid, port_mask, untag_mask, pvid); + + if (!pvid) + return ret; + + icssg_set_pvid(emac->prueth, vid, emac->port_id); + + return ret; +} + +static int prueth_switchdev_vlan_del(struct prueth_emac *emac, u16 vid, + struct net_device *orig_dev) +{ + bool cpu_port = netif_is_bridge_master(orig_dev); + int port_mask; + int ret = 0; + + if (cpu_port) + port_mask = BIT(PRUETH_PORT_HOST); + else + port_mask = BIT(emac->port_id); + + icssg_vtbl_modify(emac, vid, port_mask, 0, false); + + if (cpu_port) + icssg_fdb_add_del(emac, emac->mac_addr, vid, + BIT(PRUETH_PORT_HOST), false); + + if (vid == icssg_get_pvid(emac)) + icssg_set_pvid(emac->prueth, 0, emac->port_id); + + netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X\n", + vid, port_mask); + + return ret; +} + +static int prueth_switchdev_vlans_add(struct prueth_emac *emac, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + struct net_device *orig_dev = vlan->obj.orig_dev; + bool cpu_port = netif_is_bridge_master(orig_dev); + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + + netdev_dbg(emac->ndev, "VID add vid:%u flags:%X\n", + vlan->vid_begin, vlan->flags); + + if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY)) + return 0; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + if (vlan->vid_begin > 0xff) + return 0; + + return prueth_switchdev_vlan_add(emac, untag, pvid, vlan->vid_begin, + orig_dev); +} + +static int prueth_switchdev_vlans_del(struct prueth_emac *emac, + const struct switchdev_obj_port_vlan *vlan) + +{ + if (vlan->vid_begin > 0xff) + return 0; + + return prueth_switchdev_vlan_del(emac, vlan->vid_begin, + vlan->obj.orig_dev); +} + +static int prueth_switchdev_mdb_add(struct prueth_emac *emac, + struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) + +{ + struct net_device *orig_dev = mdb->obj.orig_dev; + bool cpu_port = netif_is_bridge_master(orig_dev); + u8 port_mask, fid_c2; + int err; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + if (cpu_port) + port_mask = BIT(PRUETH_PORT_HOST); + else + port_mask = BIT(emac->port_id); + + fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid); + + err = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 | port_mask, true); + netdev_dbg(emac->ndev, "MDB add vid %u:%pM ports: %X\n", + mdb->vid, mdb->addr, port_mask); + + return err; +} + +static int prueth_switchdev_mdb_del(struct prueth_emac *emac, + struct switchdev_obj_port_mdb *mdb) + +{ + struct net_device *orig_dev = mdb->obj.orig_dev; + bool cpu_port = netif_is_bridge_master(orig_dev); + int del_mask, ret, fid_c2; + + if (cpu_port) + del_mask = BIT(PRUETH_PORT_HOST); + else + del_mask = BIT(emac->port_id); + + fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid); + + if (fid_c2 & ~del_mask) + ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 & ~del_mask, true); + else + ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, 0, false); + + netdev_dbg(emac->ndev, "MDB del vid %u:%pM ports: %X\n", + mdb->vid, mdb->addr, del_mask); + + return ret; +} + +static int prueth_switchdev_obj_add(struct net_device *ndev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans, + struct netlink_ext_ack *extack) +{ + struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + struct prueth_emac *emac = netdev_priv(ndev); + int err = 0; + + netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, emac->port_id); + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = prueth_switchdev_vlans_add(emac, vlan, trans); + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + case SWITCHDEV_OBJ_ID_HOST_MDB: + err = prueth_switchdev_mdb_add(emac, mdb, trans); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int prueth_switchdev_obj_del(struct net_device *ndev, + const struct switchdev_obj *obj) +{ + struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + struct prueth_emac *emac = netdev_priv(ndev); + int err = 0; + + netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, emac->port_id); + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = prueth_switchdev_vlans_del(emac, vlan); + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + case SWITCHDEV_OBJ_ID_HOST_MDB: + err = prueth_switchdev_mdb_del(emac, mdb); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int prueth_switchdev_blocking_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + int err; + + switch (event) { + case SWITCHDEV_PORT_OBJ_ADD: + err = switchdev_handle_port_obj_add(dev, ptr, + prueth_dev_check, + prueth_switchdev_obj_add); + return notifier_from_errno(err); + case SWITCHDEV_PORT_OBJ_DEL: + err = switchdev_handle_port_obj_del(dev, ptr, + prueth_dev_check, + prueth_switchdev_obj_del); + return notifier_from_errno(err); + case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(dev, ptr, + prueth_dev_check, + prueth_switchdev_attr_set); + return notifier_from_errno(err); + default: + break; + } + + return NOTIFY_DONE; +} + +int prueth_switchdev_register_notifiers(struct prueth *prueth) +{ + int ret = 0; + + prueth->prueth_switchdev_nb.notifier_call = &prueth_switchdev_event; + ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb); + if (ret) { + dev_err(prueth->dev, "register switchdev notifier fail ret:%d\n", + ret); + return ret; + } + + prueth->prueth_switchdev_bl_nb.notifier_call = &prueth_switchdev_blocking_event; + ret = register_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb); + if (ret) { + dev_err(prueth->dev, "register switchdev blocking notifier ret:%d\n", + ret); + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); + } + + return ret; +} + +void prueth_switchdev_unregister_notifiers(struct prueth *prueth) +{ + unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb); + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_switchdev.h b/drivers/net/ethernet/ti/icssg_switchdev.h --- a/drivers/net/ethernet/ti/icssg_switchdev.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_switchdev.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + */ +#ifndef __NET_TI_ICSSG_SWITCHDEV_H +#define __NET_TI_ICSSG_SWITCHDEV_H + +#include "icssg_prueth.h" + +int prueth_switchdev_register_notifiers(struct prueth *prueth); +void prueth_switchdev_unregister_notifiers(struct prueth *prueth); +bool prueth_dev_check(const struct net_device *ndev); + +#endif /* __NET_TI_ICSSG_SWITCHDEV_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icssg_switch_map.h b/drivers/net/ethernet/ti/icssg_switch_map.h --- a/drivers/net/ethernet/ti/icssg_switch_map.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icssg_switch_map.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSG Ethernet driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * + * + */ + +#ifndef __NET_TI_ICSSG_SWITCH_MAP_H +#define __NET_TI_ICSSG_SWITCH_MAP_H + +/************************* Ethernet Switch Constants *********************/ + +/* if bucket size is changed in firmware then this too should be changed */ +/* because it directly impacts FDB ageing calculation */ +#define NUMBER_OF_FDB_BUCKET_ENTRIES (4) +#define SIZE_OF_FDB (2048) /* This is fixed in ICSSG */ + +/* Memory Usage of : SHARED_MEMORY + * + */ + +#define FW_LINK_SPEED_1G (0x00) +#define FW_LINK_SPEED_100M (0x01) +#define FW_LINK_SPEED_10M (0x02) +#define FW_LINK_SPEED_HD (0x80) + +/*Time after which FDB entries are checked for aged out values. Value in nanoseconds*/ +#define FDB_AGEING_TIMEOUT_OFFSET 0x0014 +/*default VLAN tag for Host Port*/ +#define HOST_PORT_DF_VLAN_OFFSET 0x001C +/*Same as HOST_PORT_DF_VLAN_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET HOST_PORT_DF_VLAN_OFFSET +/*default VLAN tag for P1 Port*/ +#define P1_PORT_DF_VLAN_OFFSET 0x0020 +/*Same as P1_PORT_DF_VLAN_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET P1_PORT_DF_VLAN_OFFSET +/*default VLAN tag for P2 Port*/ +#define P2_PORT_DF_VLAN_OFFSET 0x0024 +/*Same as P2_PORT_DF_VLAN_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET P2_PORT_DF_VLAN_OFFSET +/*VLAN-FID Table offset. 4096 VIDs. 2B per VID = 8KB = 0x2000*/ +#define VLAN_STATIC_REG_TABLE_OFFSET 0x0100 +/*VLAN-FID Table offset for EMAC*/ +#define EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET VLAN_STATIC_REG_TABLE_OFFSET +/*packet descriptor Q reserved memory*/ +#define PORT_DESC0_HI 0x2104 +/*packet descriptor Q reserved memory*/ +#define PORT_DESC0_LO 0x2F6C +/*packet descriptor Q reserved memory*/ +#define PORT_DESC1_HI 0x3DD4 +/*packet descriptor Q reserved memory*/ +#define PORT_DESC1_LO 0x4C3C +/*packet descriptor Q reserved memory*/ +#define HOST_DESC0_HI 0x5AA4 +/*packet descriptor Q reserved memory*/ +#define HOST_DESC0_LO 0x5F0C +/*packet descriptor Q reserved memory*/ +#define HOST_DESC1_HI 0x6374 +/*packet descriptor Q reserved memory*/ +#define HOST_DESC1_LO 0x67DC +/*special packet descriptor Q reserved memory*/ +#define HOST_SPPD0 0x7AAC +/*special packet descriptor Q reserved memory*/ +#define HOST_SPPD1 0x7EAC +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_CYCLECOUNT_OFFSET 0x83EC +/*IEP count hi roll over count*/ +#define TIMESYNC_FW_WC_HI_ROLLOVER_COUNT_OFFSET 0x83F4 +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET 0x83F8 +/*Set clock descriptor*/ +#define TIMESYNC_FW_WC_SETCLOCK_DESC_OFFSET 0x83FC +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_SYNCOUT_REDUCTION_FACTOR_OFFSET 0x843C +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_SYNCOUT_REDUCTION_COUNT_OFFSET 0x8440 +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_SYNCOUT_START_TIME_CYCLECOUNT_OFFSET 0x8444 +/*Control variable to generate SYNC1*/ +#define TIMESYNC_FW_WC_ISOM_PIN_SIGNAL_EN_OFFSET 0x844C +/*SystemTime Sync0 periodicity*/ +#define TIMESYNC_FW_ST_SYNCOUT_PERIOD_OFFSET 0x8450 +/*pktTxDelay for P1 = link speed dependent p1 mac delay + p1 phy delay*/ +#define TIMESYNC_FW_WC_PKTTXDELAY_P1_OFFSET 0x8454 +/*pktTxDelay for P2 = link speed dependent p2 mac delay + p2 phy delay*/ +#define TIMESYNC_FW_WC_PKTTXDELAY_P2_OFFSET 0x8458 +/*Set clock operation done signal for next task*/ +#define TIMESYNC_FW_SIG_PNFW_OFFSET 0x845C +/*Set clock operation done signal for next task*/ +#define TIMESYNC_FW_SIG_TIMESYNCFW_OFFSET 0x8460 + +/* Memory Usage of : MSMC + * + */ + +/* Memory Usage of : DMEM0 + * + */ + +/*New list is copied at this time*/ +#define TAS_CONFIG_CHANGE_TIME 0x000C +/*config change error counter*/ +#define TAS_CONFIG_CHANGE_ERROR_COUNTER 0x0014 +/*TAS List update pending flag*/ +#define TAS_CONFIG_PENDING 0x0018 +/*TAS list update trigger flag*/ +#define TAS_CONFIG_CHANGE 0x0019 +/*List length for new TAS schedule*/ +#define TAS_ADMIN_LIST_LENGTH 0x001A +/*Currently active TAS list index*/ +#define TAS_ACTIVE_LIST_INDEX 0x001B +/*Cycle time for the new TAS schedule*/ +#define TAS_ADMIN_CYCLE_TIME 0x001C +/*Cycle counts remaining till the TAS list update*/ +#define TAS_CONFIG_CHANGE_CYCLE_COUNT 0x0020 +/*Base Flow ID for sending packets to Host for Slice0*/ +#define PSI_L_REGULAR_FLOW_ID_BASE_OFFSET 0x0024 +/*Same as PSI_L_REGULAR_FLOW_ID_BASE_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PSI_L_REGULAR_FLOW_ID_BASE_OFFSET PSI_L_REGULAR_FLOW_ID_BASE_OFFSET +/*Base Flow ID for sending mgmt and Tx TS to Host for Slice0*/ +#define PSI_L_MGMT_FLOW_ID_OFFSET 0x0026 +/*Same as PSI_L_MGMT_FLOW_ID_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PSI_L_MGMT_FLOW_ID_BASE_OFFSET PSI_L_MGMT_FLOW_ID_OFFSET +/*Queue number for Special packets written here*/ +#define SPL_PKT_DEFAULT_PRIORITY 0x0028 +/*Express Preemptible Queue Mask*/ +#define EXPRESS_PRE_EMPTIVE_Q_MASK 0x0029 +/*Port1/Port2 Default Queue number for untagged packets, only 1B is used*/ +#define QUEUE_NUM_UNTAGGED 0x002A +/*Stores the table used for priority regeneration. 1B per PCP/Queue*/ +#define PORT_Q_PRIORITY_REGEN_OFFSET 0x002C +/*For marking packet as priority/express (this feature is disabled) or cut-through/S&F.*/ +#define EXPRESS_PRE_EMPTIVE_Q_MAP 0x0034 +/*Stores the table used for priority mapping. 1B per PCP/Queue*/ +#define PORT_Q_PRIORITY_MAPPING_OFFSET 0x003C +/*Used to notify the FW of the current link speed*/ +#define PORT_LINK_SPEED_OFFSET 0x00A8 +/*TAS gate mask for windows list0*/ +#define TAS_GATE_MASK_LIST0 0x0100 +/*TAS gate mask for windows list1*/ +#define TAS_GATE_MASK_LIST1 0x0350 +/*Memory to Enable/Disable Preemption on TX side*/ +#define PRE_EMPTION_ENABLE_TX 0x05A0 +/*Active State of Preemption on TX side*/ +#define PRE_EMPTION_ACTIVE_TX 0x05A1 +/*Memory to Enable/Disable Verify State Machine Preemption*/ +#define PRE_EMPTION_ENABLE_VERIFY 0x05A2 +/*Verify Status of State Machine*/ +#define PRE_EMPTION_VERIFY_STATUS 0x05A3 +/*Non Final Fragment Size supported by Link Partner*/ +#define PRE_EMPTION_ADD_FRAG_SIZE_REMOTE 0x05A4 +/*Non Final Fragment Size supported by Firmware*/ +#define PRE_EMPTION_ADD_FRAG_SIZE_LOCAL 0x05A6 +/*Time in ms the State machine waits for respond packet*/ +#define PRE_EMPTION_VERIFY_TIME 0x05A8 +/*Memory used for R30 related management commands*/ +#define MGR_R30_CMD_OFFSET 0x05AC +/*HW Buffer Pool0 base address*/ +#define BUFFER_POOL_0_ADDR_OFFSET 0x05BC +/*16B for Host Egress MSMC Q (Pre-emptible) context*/ +#define HOST_RX_Q_PRE_CONTEXT_OFFSET 0x0684 +/*Buffer for 8 FDB entries to be added by 'Add Multiple FDB entries IOCTL*/ +#define FDB_CMD_BUFFER 0x0894 +/*TAS queue max sdu length list*/ +#define TAS_QUEUE_MAX_SDU_LIST 0x08FA +/*Used by FW to generate random number with the SEED value*/ +#define HD_RAND_SEED_OFFSET 0x0934 +/*16B for Host Egress MSMC Q (Express) context*/ +#define HOST_RX_Q_EXP_CONTEXT_OFFSET 0x0940 + +/* Memory Usage of : DMEM1 + * + */ + +/* Memory Usage of : PA_STAT + * + */ + +/*Start of 32 bits PA_STAT counters*/ +#define PA_STAT_32b_START_OFFSET 0x0080 + +#endif /* __NET_TI_ICSSG_SWITCH_MAP_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icss_iep.c b/drivers/net/ethernet/ti/icss_iep.c --- a/drivers/net/ethernet/ti/icss_iep.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icss_iep.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icss_iep.h" + +#define IEP_MAX_DEF_INC 0xf +#define IEP_MAX_COMPEN_INC 0xfff +#define IEP_MAX_COMPEN_COUNT 0xffffff + +#define IEP_GLOBAL_CFG_CNT_ENABLE BIT(0) +#define IEP_GLOBAL_CFG_DEFAULT_INC_MASK GENMASK(7, 4) +#define IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT 4 +#define IEP_GLOBAL_CFG_COMPEN_INC_MASK GENMASK(19, 8) +#define IEP_GLOBAL_CFG_COMPEN_INC_SHIFT 8 + +#define IEP_GLOBAL_STATUS_CNT_OVF BIT(0) + +#define CMP_INDEX(sync) ((sync) + 1) +#define IEP_CMP_CFG_SHADOW_EN BIT(17) +#define IEP_CMP_CFG_CMP0_RST_CNT_EN BIT(0) +#define IEP_CMP_CFG_CMP_EN(cmp) (GENMASK(16, 1) & (1 << ((cmp) + 1))) + +#define IEP_CMP_STATUS(cmp) (1 << (cmp)) + +#define IEP_SYNC_CTRL_SYNC_EN BIT(0) +#define IEP_SYNC_CTRL_SYNC_N_EN(n) (GENMASK(2, 1) & (BIT(1) << (n))) + +#define IEP_MIN_CMP 0 +#define IEP_MAX_CMP 15 + +#define ICSS_IEP_64BIT_COUNTER_SUPPORT BIT(0) +#define ICSS_IEP_SLOW_COMPEN_REG_SUPPORT BIT(1) +#define ICSS_IEP_SHADOW_MODE_SUPPORT BIT(2) + +#define LATCH_INDEX(ts_index) ((ts_index) + 6) +#define IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(n) BIT(LATCH_INDEX(n)) +#define IEP_CAP_CFG_CAPNF_1ST_EVENT_EN(n) BIT(LATCH_INDEX(n) + 1) +#define IEP_CAP_CFG_CAP_ASYNC_EN(n) BIT(LATCH_INDEX(n) + 10) + +enum { + ICSS_IEP_GLOBAL_CFG_REG, + ICSS_IEP_GLOBAL_STATUS_REG, + ICSS_IEP_COMPEN_REG, + ICSS_IEP_SLOW_COMPEN_REG, + ICSS_IEP_COUNT_REG0, + ICSS_IEP_COUNT_REG1, + ICSS_IEP_CAPTURE_CFG_REG, + ICSS_IEP_CAPTURE_STAT_REG, + + ICSS_IEP_CAP6_RISE_REG0, + ICSS_IEP_CAP6_RISE_REG1, + + ICSS_IEP_CAP7_RISE_REG0, + ICSS_IEP_CAP7_RISE_REG1, + + ICSS_IEP_CMP_CFG_REG, + ICSS_IEP_CMP_STAT_REG, + ICSS_IEP_CMP0_REG0, + ICSS_IEP_CMP0_REG1, + ICSS_IEP_CMP1_REG0, + ICSS_IEP_CMP1_REG1, + + ICSS_IEP_CMP8_REG0, + ICSS_IEP_CMP8_REG1, + ICSS_IEP_SYNC_CTRL_REG, + ICSS_IEP_SYNC0_STAT_REG, + ICSS_IEP_SYNC1_STAT_REG, + ICSS_IEP_SYNC_PWIDTH_REG, + ICSS_IEP_SYNC0_PERIOD_REG, + ICSS_IEP_SYNC1_DELAY_REG, + ICSS_IEP_SYNC_START_REG, + ICSS_IEP_MAX_REGS, +}; + +/** + * struct icss_iep_plat_data - Plat data to handle SoC variants + * @config: Regmap configuration data + * @reg_offs: register offsets to capture offset differences across SoCs + * @flags: Flags to represent IEP properties + */ +struct icss_iep_plat_data { + struct regmap_config *config; + u32 reg_offs[ICSS_IEP_MAX_REGS]; + u32 flags; +}; + +struct icss_iep { + struct device *dev; + void __iomem *base; + const struct icss_iep_plat_data *plat_data; + struct regmap *map; + struct device_node *client_np; + unsigned long refclk_freq; + int clk_tick_time; /* one refclk tick time in ns */ + struct ptp_clock_info ptp_info; + struct ptp_clock *ptp_clock; + struct mutex ptp_clk_mutex; /* PHC access serializer */ + spinlock_t irq_lock; /* CMP IRQ vs icss_iep_ptp_enable access */ + u32 def_inc; + s16 slow_cmp_inc; + u32 slow_cmp_count; + const struct icss_iep_clockops *ops; + void *clockops_data; + u32 cycle_time_ns; + u32 perout_enabled; + bool pps_enabled; + int cap_cmp_irq; + u64 period; + u32 latch_enable; + struct hrtimer sync_timer; +}; + +static u32 icss_iep_readl(struct icss_iep *iep, int reg) +{ + return readl(iep->base + iep->plat_data->reg_offs[reg]); +} + +static void icss_iep_writel(struct icss_iep *iep, int reg, u32 val) +{ + return writel(val, iep->base + iep->plat_data->reg_offs[reg]); +} + +/** + * icss_iep_get_count_hi() - Get the upper 32 bit IEP counter + * @iep: Pointer to structure representing IEP. + * + * Return: upper 32 bit IEP counter + */ +int icss_iep_get_count_hi(struct icss_iep *iep) +{ + u32 val = 0; + + if (iep && (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)) + val = icss_iep_readl(iep, ICSS_IEP_COUNT_REG1); + + return val; +} +EXPORT_SYMBOL_GPL(icss_iep_get_count_hi); + +/** + * icss_iep_get_count_low() - Get the lower 32 bit IEP counter + * @iep: Pointer to structure representing IEP. + * + * Return: lower 32 bit IEP counter + */ +int icss_iep_get_count_low(struct icss_iep *iep) +{ + u32 val = 0; + + if (iep) + val = icss_iep_readl(iep, ICSS_IEP_COUNT_REG0); + + return val; +} +EXPORT_SYMBOL_GPL(icss_iep_get_count_low); + +/** + * icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver + * @iep: Pointer to structure representing IEP. + * + * Return: PTP clock index, -1 if not registered + */ +int icss_iep_get_ptp_clock_idx(struct icss_iep *iep) +{ + if (!iep || !iep->ptp_clock) + return -1; + return ptp_clock_index(iep->ptp_clock); +} +EXPORT_SYMBOL_GPL(icss_iep_get_ptp_clock_idx); + +static void icss_iep_set_counter(struct icss_iep *iep, u64 ns) +{ + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + icss_iep_writel(iep, ICSS_IEP_COUNT_REG1, upper_32_bits(ns)); + icss_iep_writel(iep, ICSS_IEP_COUNT_REG0, lower_32_bits(ns)); +} + +static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns); + +static void icss_iep_settime(struct icss_iep *iep, u64 ns) +{ + unsigned long flags; + + if (iep->ops && iep->ops->settime) { + iep->ops->settime(iep->clockops_data, ns); + return; + } + + spin_lock_irqsave(&iep->irq_lock, flags); + if (iep->pps_enabled || iep->perout_enabled) + icss_iep_writel(iep, ICSS_IEP_SYNC_CTRL_REG, 0); + + icss_iep_set_counter(iep, ns); + + if (iep->pps_enabled || iep->perout_enabled) { + icss_iep_update_to_next_boundary(iep, ns); + icss_iep_writel(iep, ICSS_IEP_SYNC_CTRL_REG, + IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN); + } + spin_unlock_irqrestore(&iep->irq_lock, flags); +} + +static u64 icss_iep_gettime(struct icss_iep *iep, + struct ptp_system_timestamp *sts) +{ + u32 ts_hi = 0, ts_lo; + unsigned long flags; + + if (iep->ops && iep->ops->gettime) + return iep->ops->gettime(iep->clockops_data, sts); + + /* use local_irq_x() to make it work for both RT/non-RT */ + local_irq_save(flags); + + /* no need to play with hi-lo, hi is latched when lo is read */ + ptp_read_system_prets(sts); + ts_lo = icss_iep_readl(iep, ICSS_IEP_COUNT_REG0); + ptp_read_system_postts(sts); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + ts_hi = icss_iep_readl(iep, ICSS_IEP_COUNT_REG1); + + local_irq_restore(flags); + + return (u64)ts_lo | (u64)ts_hi << 32; +} + +static void icss_iep_enable(struct icss_iep *iep) +{ + regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, + IEP_GLOBAL_CFG_CNT_ENABLE, + IEP_GLOBAL_CFG_CNT_ENABLE); +} + +static void icss_iep_disable(struct icss_iep *iep) +{ + regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, + IEP_GLOBAL_CFG_CNT_ENABLE, + 0); +} + +static void icss_iep_enable_shadow_mode(struct icss_iep *iep) +{ + u32 cycle_time; + int cmp; + + /* FIXME: check why we need to decrement by def_inc */ + cycle_time = iep->cycle_time_ns - iep->def_inc; + + icss_iep_disable(iep); + + /* disable shadow mode */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_SHADOW_EN, 0); + + /* enable shadow mode */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_SHADOW_EN, IEP_CMP_CFG_SHADOW_EN); + + /* clear counters */ + icss_iep_set_counter(iep, 0); + + /* clear overflow status */ + regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_STATUS_REG, + IEP_GLOBAL_STATUS_CNT_OVF, + IEP_GLOBAL_STATUS_CNT_OVF); + + /* clear compare status */ + for (cmp = IEP_MIN_CMP; cmp < IEP_MAX_CMP; cmp++) { + regmap_update_bits(iep->map, ICSS_IEP_CMP_STAT_REG, + IEP_CMP_STATUS(cmp), IEP_CMP_STATUS(cmp)); + } + + /* enable reset counter on CMP0 event */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_CMP0_RST_CNT_EN, + IEP_CMP_CFG_CMP0_RST_CNT_EN); + /* enable compare */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_CMP_EN(0), + IEP_CMP_CFG_CMP_EN(0)); + + /* set CMP0 value to cycle time */ + regmap_write(iep->map, ICSS_IEP_CMP0_REG0, cycle_time); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + regmap_write(iep->map, ICSS_IEP_CMP0_REG1, cycle_time); + + icss_iep_set_counter(iep, 0); + icss_iep_enable(iep); +} + +static void icss_iep_set_default_inc(struct icss_iep *iep, u8 def_inc) +{ + regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, + IEP_GLOBAL_CFG_DEFAULT_INC_MASK, + def_inc << IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT); +} + +static void icss_iep_set_compensation_inc(struct icss_iep *iep, u16 compen_inc) +{ + struct device *dev = regmap_get_device(iep->map); + + if (compen_inc > IEP_MAX_COMPEN_INC) { + dev_err(dev, "%s: too high compensation inc %d\n", + __func__, compen_inc); + compen_inc = IEP_MAX_COMPEN_INC; + } + + regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, + IEP_GLOBAL_CFG_COMPEN_INC_MASK, + compen_inc << IEP_GLOBAL_CFG_COMPEN_INC_SHIFT); +} + +static void icss_iep_set_compensation_count(struct icss_iep *iep, + u32 compen_count) +{ + struct device *dev = regmap_get_device(iep->map); + + if (compen_count > IEP_MAX_COMPEN_COUNT) { + dev_err(dev, "%s: too high compensation count %d\n", + __func__, compen_count); + compen_count = IEP_MAX_COMPEN_COUNT; + } + + regmap_write(iep->map, ICSS_IEP_COMPEN_REG, compen_count); +} + +static void icss_iep_set_slow_compensation_count(struct icss_iep *iep, + u32 compen_count) +{ + regmap_write(iep->map, ICSS_IEP_SLOW_COMPEN_REG, compen_count); +} + +/* PTP PHC operations */ +static int icss_iep_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +{ + struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); + u32 cyc_count; + u16 cmp_inc; + + mutex_lock(&iep->ptp_clk_mutex); + + /* ppb is amount of frequency we want to adjust in 1GHz (billion) + * e.g. 100ppb means we need to speed up clock by 100Hz + * i.e. at end of 1 second (1 billion ns) clock time, we should be + * counting 100 more ns. + * We use IEP slow compensation to achieve continuous freq. adjustment. + * There are 2 parts. Cycle time and adjustment per cycle. + * Simplest case would be 1 sec Cycle time. Then adjustment + * pre cycle would be (def_inc + ppb) value. + * Cycle time will have to be chosen based on how worse the ppb is. + * e.g. smaller the ppb, cycle time has to be large. + * The minimum adjustment we can do is +-1ns per cycle so let's + * reduce the cycle time to get 1ns per cycle adjustment. + * 1ppb = 1sec cycle time & 1ns adjust + * 1000ppb = 1/1000 cycle time & 1ns adjust per cycle + */ + + if (iep->cycle_time_ns) + iep->slow_cmp_inc = iep->clk_tick_time; /* 4ns adj per cycle */ + else + iep->slow_cmp_inc = 1; /* 1ns adjust per cycle */ + + if (ppb < 0) { + iep->slow_cmp_inc = -iep->slow_cmp_inc; + ppb = -ppb; + } + + cyc_count = NSEC_PER_SEC; /* 1s cycle time @1GHz */ + cyc_count /= ppb; /* cycle time per ppb */ + + /* slow_cmp_count is decremented every clock cycle, e.g. @250MHz */ + if (!iep->cycle_time_ns) + cyc_count /= iep->clk_tick_time; + iep->slow_cmp_count = cyc_count; + + /* iep->clk_tick_time is def_inc */ + cmp_inc = iep->clk_tick_time + iep->slow_cmp_inc; + icss_iep_set_compensation_inc(iep, cmp_inc); + icss_iep_set_slow_compensation_count(iep, iep->slow_cmp_count); + + mutex_unlock(&iep->ptp_clk_mutex); + + return 0; +} + +static int icss_iep_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); + s64 ns; + + mutex_lock(&iep->ptp_clk_mutex); + if (iep->ops && iep->ops->adjtime) { + iep->ops->adjtime(iep->clockops_data, delta); + } else { + ns = icss_iep_gettime(iep, NULL); + ns += delta; + icss_iep_settime(iep, ns); + } + mutex_unlock(&iep->ptp_clk_mutex); + + return 0; +} + +static int icss_iep_ptp_gettimeex(struct ptp_clock_info *ptp, + struct timespec64 *ts, + struct ptp_system_timestamp *sts) +{ + struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); + u64 ns; + + mutex_lock(&iep->ptp_clk_mutex); + ns = icss_iep_gettime(iep, sts); + *ts = ns_to_timespec64(ns); + mutex_unlock(&iep->ptp_clk_mutex); + + return 0; +} + +static int icss_iep_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); + u64 ns; + + mutex_lock(&iep->ptp_clk_mutex); + ns = timespec64_to_ns(ts); + icss_iep_settime(iep, ns); + mutex_unlock(&iep->ptp_clk_mutex); + + return 0; +} + +static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns) +{ + u64 ns, p_ns; + u32 offset; + + ns = icss_iep_gettime(iep, NULL); + if (start_ns < ns) + start_ns = ns; + p_ns = iep->period; + /* Round up to next period boundary */ + start_ns += p_ns - 1; + offset = do_div(start_ns, p_ns); + start_ns = start_ns * p_ns; + /* If it is too close to update, shift to next boundary */ + if (p_ns - offset < 10) + start_ns += p_ns; + + regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(start_ns)); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(start_ns)); +} + +static int icss_iep_perout_enable_hw(struct icss_iep *iep, + struct ptp_perout_request *req, int on) +{ + int ret; + u64 cmp; + + if (iep->ops && iep->ops->perout_enable) { + ret = iep->ops->perout_enable(iep->clockops_data, req, on, &cmp); + if (ret) + return ret; + + if (on) { + /* Configure CMP */ + regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(cmp)); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(cmp)); + /* Configure SYNC */ + regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, 1000000); /* 1ms pulse width */ + regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0); + regmap_write(iep->map, ICSS_IEP_SYNC_START_REG, 0); + regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); /* one-shot mode */ + /* Enable CMP 1 */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1)); + } else { + /* Disable CMP 1 */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_CMP_EN(1), 0); + + /* clear regs */ + regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0); + } + } else { + if (on) { + u64 start_ns; + + iep->period = ((u64)req->period.sec * NSEC_PER_SEC) + + req->period.nsec; + start_ns = ((u64)req->period.sec * NSEC_PER_SEC) + + req->period.nsec; + icss_iep_update_to_next_boundary(iep, start_ns); + + /* Enable Sync in single shot mode */ + regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, + IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN); + /* Enable CMP 1 */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1)); + } else { + /* Disable CMP 1 */ + regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, + IEP_CMP_CFG_CMP_EN(1), 0); + + /* clear CMP regs */ + regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0); + + /* Disable sync */ + regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); + } + } + + return 0; +} + +static int icss_iep_perout_enable(struct icss_iep *iep, + struct ptp_perout_request *req, int on) +{ + unsigned long flags; + int ret = 0; + + mutex_lock(&iep->ptp_clk_mutex); + + if (iep->pps_enabled) { + ret = -EBUSY; + goto exit; + } + + if (iep->perout_enabled == !!on) + goto exit; + + spin_lock_irqsave(&iep->irq_lock, flags); + if (iep->cap_cmp_irq) + hrtimer_cancel(&iep->sync_timer); + ret = icss_iep_perout_enable_hw(iep, req, on); + if (!ret) + iep->perout_enabled = !!on; + spin_unlock_irqrestore(&iep->irq_lock, flags); + +exit: + mutex_unlock(&iep->ptp_clk_mutex); + + return ret; +} + +static irqreturn_t icss_iep_cap_cmp_handler(int irq, void *dev_id) +{ + struct icss_iep *iep = (struct icss_iep *)dev_id; + unsigned int val, index = 0, i, sts; + struct ptp_clock_event pevent; + irqreturn_t ret = IRQ_NONE; + unsigned long flags; + u64 ns, ns_next; + + spin_lock_irqsave(&iep->irq_lock, flags); + + val = icss_iep_readl(iep, ICSS_IEP_CMP_STAT_REG); + if (val & BIT(CMP_INDEX(index))) { + icss_iep_writel(iep, ICSS_IEP_CMP_STAT_REG, + BIT(CMP_INDEX(index))); + + if (!iep->pps_enabled && !iep->perout_enabled) + goto do_latch; + + ns = icss_iep_readl(iep, ICSS_IEP_CMP1_REG0); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) { + val = icss_iep_readl(iep, ICSS_IEP_CMP1_REG1); + ns |= (u64)val << 32; + } + /* set next event */ + ns_next = ns + iep->period; + icss_iep_writel(iep, ICSS_IEP_CMP1_REG0, + lower_32_bits(ns_next)); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) + icss_iep_writel(iep, ICSS_IEP_CMP1_REG1, + upper_32_bits(ns_next)); + + pevent.pps_times.ts_real = ns_to_timespec64(ns); + pevent.type = PTP_CLOCK_PPSUSR; + pevent.index = index; + ptp_clock_event(iep->ptp_clock, &pevent); + dev_dbg(iep->dev, "IEP:pps ts: %llu next:%llu:\n", ns, ns_next); + + hrtimer_start(&iep->sync_timer, ms_to_ktime(110), /* 100ms + buffer */ + HRTIMER_MODE_REL); + + ret = IRQ_HANDLED; + } + +do_latch: + sts = icss_iep_readl(iep, ICSS_IEP_CAPTURE_STAT_REG); + if (!sts) + goto cap_cmp_exit; + + for (i = 0; i < iep->ptp_info.n_ext_ts; i++) { + if (sts & IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(i * 2)) { + ns = icss_iep_readl(iep, + ICSS_IEP_CAP6_RISE_REG0 + (i * 2)); + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) { + val = icss_iep_readl(iep, + ICSS_IEP_CAP6_RISE_REG0 + (i * 2) + 1); + ns |= (u64)val << 32; + } + pevent.timestamp = ns; + pevent.type = PTP_CLOCK_EXTTS; + pevent.index = i; + ptp_clock_event(iep->ptp_clock, &pevent); + dev_dbg(iep->dev, "IEP:extts index=%d ts: %llu\n", i, ns); + ret = IRQ_HANDLED; + } + } + +cap_cmp_exit: + spin_unlock_irqrestore(&iep->irq_lock, flags); + return ret; +} + +static int icss_iep_pps_enable(struct icss_iep *iep, int on) +{ + int ret = 0; + struct timespec64 ts; + struct ptp_clock_request rq; + unsigned long flags; + u64 ns; + + mutex_lock(&iep->ptp_clk_mutex); + + if (iep->perout_enabled) { + ret = -EBUSY; + goto exit; + } + + if (iep->pps_enabled == !!on) + goto exit; + + spin_lock_irqsave(&iep->irq_lock, flags); + + rq.perout.index = 0; + if (on) { + ns = icss_iep_gettime(iep, NULL); + ts = ns_to_timespec64(ns); + rq.perout.period.sec = 1; + rq.perout.period.nsec = 0; + rq.perout.start.sec = ts.tv_sec + 2; + rq.perout.start.nsec = 0; + ret = icss_iep_perout_enable_hw(iep, &rq.perout, on); + } else { + if (iep->cap_cmp_irq) + hrtimer_cancel(&iep->sync_timer); + ret = icss_iep_perout_enable_hw(iep, &rq.perout, on); + } + + if (!ret) + iep->pps_enabled = !!on; + + spin_unlock_irqrestore(&iep->irq_lock, flags); + +exit: + mutex_unlock(&iep->ptp_clk_mutex); + + return ret; +} + +static int icss_iep_extts_enable(struct icss_iep *iep, u32 index, int on) +{ + u32 val, cap, ret = 0; + + mutex_lock(&iep->ptp_clk_mutex); + + if (iep->ops && iep->ops->extts_enable) { + ret = iep->ops->extts_enable(iep->clockops_data, index, on); + goto exit; + } + + if (!!(iep->latch_enable & BIT(index)) == !!on) + goto exit; + + regmap_read(iep->map, ICSS_IEP_CAPTURE_CFG_REG, &val); + cap = IEP_CAP_CFG_CAP_ASYNC_EN(index) | IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(index); + if (on) { + val |= cap; + iep->latch_enable |= BIT(index); + } else { + val &= ~cap; + iep->latch_enable &= ~BIT(index); + } + regmap_write(iep->map, ICSS_IEP_CAPTURE_CFG_REG, val); + +exit: + mutex_unlock(&iep->ptp_clk_mutex); + + return ret; +} + +static int icss_iep_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); + + switch (rq->type) { + case PTP_CLK_REQ_PEROUT: + return icss_iep_perout_enable(iep, &rq->perout, on); + case PTP_CLK_REQ_PPS: + return icss_iep_pps_enable(iep, on); + case PTP_CLK_REQ_EXTTS: + return icss_iep_extts_enable(iep, rq->extts.index, on); + default: + break; + } + + return -EOPNOTSUPP; +} + +static struct ptp_clock_info icss_iep_ptp_info = { + .owner = THIS_MODULE, + .name = "ICSS IEP timer", + .max_adj = 10000000, + .adjfreq = icss_iep_ptp_adjfreq, + .adjtime = icss_iep_ptp_adjtime, + .gettimex64 = icss_iep_ptp_gettimeex, + .settime64 = icss_iep_ptp_settime, + .enable = icss_iep_ptp_enable, +}; + +static enum hrtimer_restart icss_iep_sync0_work(struct hrtimer *timer) +{ + struct icss_iep *iep = container_of(timer, struct icss_iep, sync_timer); + + icss_iep_writel(iep, ICSS_IEP_SYNC_CTRL_REG, 0); + icss_iep_writel(iep, ICSS_IEP_SYNC_CTRL_REG, + IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN); + icss_iep_writel(iep, ICSS_IEP_SYNC0_STAT_REG, 1); + + return HRTIMER_NORESTART; +} + +struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx) +{ + struct platform_device *pdev; + struct device_node *iep_np; + struct icss_iep *iep; + + iep_np = of_parse_phandle(np, "iep", idx); + if (!iep_np || !of_device_is_available(iep_np)) + return ERR_PTR(-ENODEV); + + pdev = of_find_device_by_node(iep_np); + of_node_put(iep_np); + + if (!pdev) + /* probably IEP not yet probed */ + return ERR_PTR(-EPROBE_DEFER); + + iep = platform_get_drvdata(pdev); + if (!iep) + return ERR_PTR(-EPROBE_DEFER); + + device_lock(iep->dev); + if (iep->client_np) { + device_unlock(iep->dev); + dev_err(iep->dev, "IEP is already acquired by %s", + iep->client_np->name); + return ERR_PTR(-EBUSY); + } + iep->client_np = np; + device_unlock(iep->dev); + get_device(iep->dev); + + return iep; +} +EXPORT_SYMBOL_GPL(icss_iep_get_idx); + +struct icss_iep *icss_iep_get(struct device_node *np) +{ + return icss_iep_get_idx(np, 0); +} +EXPORT_SYMBOL_GPL(icss_iep_get); + +void icss_iep_put(struct icss_iep *iep) +{ + device_lock(iep->dev); + iep->client_np = NULL; + device_unlock(iep->dev); + put_device(iep->dev); + if (iep->cap_cmp_irq) + hrtimer_cancel(&iep->sync_timer); +} +EXPORT_SYMBOL_GPL(icss_iep_put); + +void icss_iep_init_fw(struct icss_iep *iep) +{ + /* start IEP for FW use in raw 64bit mode, no PTP support */ + iep->clk_tick_time = iep->def_inc; + iep->cycle_time_ns = 0; + iep->ops = NULL; + iep->clockops_data = NULL; + icss_iep_set_default_inc(iep, iep->def_inc); + icss_iep_set_compensation_inc(iep, iep->def_inc); + icss_iep_set_compensation_count(iep, 0); + regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */ + regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0); + if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT) + icss_iep_set_slow_compensation_count(iep, 0); + + icss_iep_enable(iep); + icss_iep_settime(iep, 0); +} +EXPORT_SYMBOL_GPL(icss_iep_init_fw); + +void icss_iep_exit_fw(struct icss_iep *iep) +{ + icss_iep_disable(iep); +} +EXPORT_SYMBOL_GPL(icss_iep_exit_fw); + +int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops, + void *clockops_data, u32 cycle_time_ns) +{ + int ret = 0; + + iep->cycle_time_ns = cycle_time_ns; + iep->clk_tick_time = iep->def_inc; + iep->ops = clkops; + iep->clockops_data = clockops_data; + icss_iep_set_default_inc(iep, iep->def_inc); + icss_iep_set_compensation_inc(iep, iep->def_inc); + icss_iep_set_compensation_count(iep, 0); + regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */ + regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0); + if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT) + icss_iep_set_slow_compensation_count(iep, 0); + + if (!(iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) || + !(iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT)) + goto skip_perout; + + if (iep->cap_cmp_irq || (iep->ops && iep->ops->perout_enable)) { + iep->ptp_info.n_per_out = 1; + iep->ptp_info.pps = 1; + } + + if (iep->cap_cmp_irq || (iep->ops && iep->ops->extts_enable)) + iep->ptp_info.n_ext_ts = 2; + +skip_perout: + if (cycle_time_ns) + icss_iep_enable_shadow_mode(iep); + else + icss_iep_enable(iep); + icss_iep_settime(iep, ktime_get_real_ns()); + + iep->ptp_clock = ptp_clock_register(&iep->ptp_info, iep->dev); + if (IS_ERR(iep->ptp_clock)) { + ret = PTR_ERR(iep->ptp_clock); + iep->ptp_clock = NULL; + dev_err(iep->dev, "Failed to register ptp clk %d\n", ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(icss_iep_init); + +int icss_iep_exit(struct icss_iep *iep) +{ + if (iep->ptp_clock) { + ptp_clock_unregister(iep->ptp_clock); + iep->ptp_clock = NULL; + } + icss_iep_disable(iep); + + return 0; +} +EXPORT_SYMBOL_GPL(icss_iep_exit); + +static const struct of_device_id icss_iep_of_match[]; + +static int icss_iep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct icss_iep *iep; + struct clk *iep_clk; + int ret; + + iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL); + if (!iep) + return -ENOMEM; + + iep->dev = dev; + iep->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(iep->base)) + return -ENODEV; + + iep->cap_cmp_irq = platform_get_irq_byname_optional(pdev, "iep_cap_cmp"); + if (iep->cap_cmp_irq < 0) { + if (iep->cap_cmp_irq == -EPROBE_DEFER) + return iep->cap_cmp_irq; + iep->cap_cmp_irq = 0; + } else { + ret = devm_request_irq(dev, iep->cap_cmp_irq, + icss_iep_cap_cmp_handler, IRQF_TRIGGER_HIGH, + "iep_cap_cmp", iep); + if (ret) { + dev_err(iep->dev, "Request irq failed for cap_cmp %d\n", ret); + return ret; + } + hrtimer_init(&iep->sync_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + iep->sync_timer.function = icss_iep_sync0_work; + } + + iep_clk = devm_clk_get(dev, NULL); + if (IS_ERR(iep_clk)) + return PTR_ERR(iep_clk); + + iep->refclk_freq = clk_get_rate(iep_clk); + + iep->def_inc = NSEC_PER_SEC / iep->refclk_freq; /* ns per clock tick */ + if (iep->def_inc > IEP_MAX_DEF_INC) { + dev_err(dev, "Failed to set def_inc %d. IEP_clock is too slow to be supported\n", + iep->def_inc); + return -EINVAL; + } + + iep->plat_data = of_device_get_match_data(dev); + if (!iep->plat_data) + return -EINVAL; + + iep->map = devm_regmap_init(dev, NULL, iep, iep->plat_data->config); + if (IS_ERR(iep->map)) { + dev_err(dev, "Failed to create regmap for IEP %ld\n", + PTR_ERR(iep->map)); + return PTR_ERR(iep->map); + } + + iep->ptp_info = icss_iep_ptp_info; + mutex_init(&iep->ptp_clk_mutex); + dev_set_drvdata(dev, iep); + icss_iep_disable(iep); + + return 0; +} + +static bool am654_icss_iep_valid_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ICSS_IEP_GLOBAL_CFG_REG ... ICSS_IEP_SYNC_START_REG: + return true; + default: + return false; + } + + return false; +} + +static int icss_iep_regmap_write(void *context, unsigned int reg, + unsigned int val) +{ + struct icss_iep *iep = context; + + writel(val, iep->base + iep->plat_data->reg_offs[reg]); + + return 0; +} + +static int icss_iep_regmap_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct icss_iep *iep = context; + + *val = readl(iep->base + iep->plat_data->reg_offs[reg]); + + return 0; +} + +static struct regmap_config am654_icss_iep_regmap_config = { + .name = "icss iep", + .reg_stride = 1, + .reg_write = icss_iep_regmap_write, + .reg_read = icss_iep_regmap_read, + .writeable_reg = am654_icss_iep_valid_reg, + .readable_reg = am654_icss_iep_valid_reg, +}; + +static const struct icss_iep_plat_data am654_icss_iep_plat_data = { + .flags = ICSS_IEP_64BIT_COUNTER_SUPPORT | + ICSS_IEP_SLOW_COMPEN_REG_SUPPORT | + ICSS_IEP_SHADOW_MODE_SUPPORT, + .reg_offs = { + [ICSS_IEP_GLOBAL_CFG_REG] = 0x00, + [ICSS_IEP_COMPEN_REG] = 0x08, + [ICSS_IEP_SLOW_COMPEN_REG] = 0x0C, + [ICSS_IEP_COUNT_REG0] = 0x10, + [ICSS_IEP_COUNT_REG1] = 0x14, + [ICSS_IEP_CAPTURE_CFG_REG] = 0x18, + [ICSS_IEP_CAPTURE_STAT_REG] = 0x1c, + + [ICSS_IEP_CAP6_RISE_REG0] = 0x50, + [ICSS_IEP_CAP6_RISE_REG1] = 0x54, + + [ICSS_IEP_CAP7_RISE_REG0] = 0x60, + [ICSS_IEP_CAP7_RISE_REG1] = 0x64, + + [ICSS_IEP_CMP_CFG_REG] = 0x70, + [ICSS_IEP_CMP_STAT_REG] = 0x74, + [ICSS_IEP_CMP0_REG0] = 0x78, + [ICSS_IEP_CMP0_REG1] = 0x7c, + [ICSS_IEP_CMP1_REG0] = 0x80, + [ICSS_IEP_CMP1_REG1] = 0x84, + + [ICSS_IEP_CMP8_REG0] = 0xc0, + [ICSS_IEP_CMP8_REG1] = 0xc4, + [ICSS_IEP_SYNC_CTRL_REG] = 0x180, + [ICSS_IEP_SYNC0_STAT_REG] = 0x188, + [ICSS_IEP_SYNC1_STAT_REG] = 0x18c, + [ICSS_IEP_SYNC_PWIDTH_REG] = 0x190, + [ICSS_IEP_SYNC0_PERIOD_REG] = 0x194, + [ICSS_IEP_SYNC1_DELAY_REG] = 0x198, + [ICSS_IEP_SYNC_START_REG] = 0x19c, + }, + .config = &am654_icss_iep_regmap_config, +}; + +static const struct icss_iep_plat_data am57xx_icss_iep_plat_data = { + .flags = ICSS_IEP_64BIT_COUNTER_SUPPORT | + ICSS_IEP_SLOW_COMPEN_REG_SUPPORT, + .reg_offs = { + [ICSS_IEP_GLOBAL_CFG_REG] = 0x00, + [ICSS_IEP_COMPEN_REG] = 0x08, + [ICSS_IEP_SLOW_COMPEN_REG] = 0x0C, + [ICSS_IEP_COUNT_REG0] = 0x10, + [ICSS_IEP_COUNT_REG1] = 0x14, + [ICSS_IEP_CAPTURE_CFG_REG] = 0x18, + [ICSS_IEP_CAPTURE_STAT_REG] = 0x1c, + + [ICSS_IEP_CAP6_RISE_REG0] = 0x50, + [ICSS_IEP_CAP6_RISE_REG1] = 0x54, + + [ICSS_IEP_CAP7_RISE_REG0] = 0x60, + [ICSS_IEP_CAP7_RISE_REG1] = 0x64, + + [ICSS_IEP_CMP_CFG_REG] = 0x70, + [ICSS_IEP_CMP_STAT_REG] = 0x74, + [ICSS_IEP_CMP0_REG0] = 0x78, + [ICSS_IEP_CMP0_REG1] = 0x7c, + [ICSS_IEP_CMP1_REG0] = 0x80, + [ICSS_IEP_CMP1_REG1] = 0x84, + + [ICSS_IEP_CMP8_REG0] = 0xc0, + [ICSS_IEP_CMP8_REG1] = 0xc4, + [ICSS_IEP_SYNC_CTRL_REG] = 0x180, + [ICSS_IEP_SYNC0_STAT_REG] = 0x188, + [ICSS_IEP_SYNC1_STAT_REG] = 0x18c, + [ICSS_IEP_SYNC_PWIDTH_REG] = 0x190, + [ICSS_IEP_SYNC0_PERIOD_REG] = 0x194, + [ICSS_IEP_SYNC1_DELAY_REG] = 0x198, + [ICSS_IEP_SYNC_START_REG] = 0x19c, + }, + .config = &am654_icss_iep_regmap_config, +}; + +static bool am335x_icss_iep_valid_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ICSS_IEP_GLOBAL_CFG_REG ... ICSS_IEP_CAPTURE_STAT_REG: + case ICSS_IEP_CAP6_RISE_REG0: + case ICSS_IEP_CMP_CFG_REG ... ICSS_IEP_CMP0_REG0: + case ICSS_IEP_CMP8_REG0 ... ICSS_IEP_SYNC_START_REG: + return true; + default: + return false; + } + + return false; +} + +static struct regmap_config am335x_icss_iep_regmap_config = { + .name = "icss iep", + .reg_stride = 1, + .reg_write = icss_iep_regmap_write, + .reg_read = icss_iep_regmap_read, + .writeable_reg = am335x_icss_iep_valid_reg, + .readable_reg = am335x_icss_iep_valid_reg, +}; + +static const struct icss_iep_plat_data am335x_icss_iep_plat_data = { + .flags = 0, + .reg_offs = { + [ICSS_IEP_GLOBAL_CFG_REG] = 0x00, + [ICSS_IEP_COMPEN_REG] = 0x08, + [ICSS_IEP_COUNT_REG0] = 0x0C, + [ICSS_IEP_CAPTURE_CFG_REG] = 0x10, + [ICSS_IEP_CAPTURE_STAT_REG] = 0x14, + + [ICSS_IEP_CAP6_RISE_REG0] = 0x30, + + [ICSS_IEP_CAP7_RISE_REG0] = 0x38, + + [ICSS_IEP_CMP_CFG_REG] = 0x40, + [ICSS_IEP_CMP_STAT_REG] = 0x44, + [ICSS_IEP_CMP0_REG0] = 0x48, + + [ICSS_IEP_CMP8_REG0] = 0x88, + [ICSS_IEP_SYNC_CTRL_REG] = 0x100, + [ICSS_IEP_SYNC0_STAT_REG] = 0x108, + [ICSS_IEP_SYNC1_STAT_REG] = 0x10C, + [ICSS_IEP_SYNC_PWIDTH_REG] = 0x110, + [ICSS_IEP_SYNC0_PERIOD_REG] = 0x114, + [ICSS_IEP_SYNC1_DELAY_REG] = 0x118, + [ICSS_IEP_SYNC_START_REG] = 0x11C, + }, + .config = &am335x_icss_iep_regmap_config, +}; + +static const struct of_device_id icss_iep_of_match[] = { + { + .compatible = "ti,am654-icss-iep", + .data = &am654_icss_iep_plat_data, + }, + { + .compatible = "ti,am5728-icss-iep", + .data = &am57xx_icss_iep_plat_data, + }, + { + .compatible = "ti,am3356-icss-iep", + .data = &am335x_icss_iep_plat_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, icss_iep_of_match); + +static struct platform_driver icss_iep_driver = { + .driver = { + .name = "icss-iep", + .of_match_table = of_match_ptr(icss_iep_of_match), + }, + .probe = icss_iep_probe, +}; +module_platform_driver(icss_iep_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI ICSS IEP driver"); +MODULE_AUTHOR("Roger Quadros "); diff -Naur --no-dereference a/drivers/net/ethernet/ti/icss_iep.h b/drivers/net/ethernet/ti/icss_iep.h --- a/drivers/net/ethernet/ti/icss_iep.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icss_iep.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * + */ + +#ifndef __NET_TI_ICSS_IEP_H +#define __NET_TI_ICSS_IEP_H + +#include +#include +#include + +struct icss_iep; + +/* Firmware specific clock operations */ +struct icss_iep_clockops { + void (*settime)(void *clockops_data, u64 ns); + void (*adjtime)(void *clockops_data, s64 delta); + u64 (*gettime)(void *clockops_data, struct ptp_system_timestamp *sts); + int (*perout_enable)(void *clockops_data, + struct ptp_perout_request *req, int on, + u64 *cmp); + int (*extts_enable)(void *clockops_data, u32 index, int on); +}; + +struct icss_iep *icss_iep_get(struct device_node *np); +struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx); +void icss_iep_put(struct icss_iep *iep); +int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops, + void *clockops_data, u32 cycle_time_ns); +int icss_iep_exit(struct icss_iep *iep); +int icss_iep_get_count_low(struct icss_iep *iep); +int icss_iep_get_count_hi(struct icss_iep *iep); +int icss_iep_get_ptp_clock_idx(struct icss_iep *iep); +void icss_iep_init_fw(struct icss_iep *iep); +void icss_iep_exit_fw(struct icss_iep *iep); + +#endif /* __NET_TI_ICSS_IEP_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icss_lre_firmware.h b/drivers/net/ethernet/ti/icss_lre_firmware.h --- a/drivers/net/ethernet/ti/icss_lre_firmware.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icss_lre_firmware.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017-2020 Texas Instruments Incorporated - http://www.ti.com + * + */ + +#ifndef __ICSS_LRE_FIRMWARE_H +#define __ICSS_LRE_FIRMWARE_H + +#define ICSS_LRE_TAG_RCT_SIZE 6 /* HSR tag or PRP RCT size */ + +#define ICSS_LRE_HSR_MODE 0x1E76 +#define ICSS_LRE_MODEH 0x01 + +/* PRU0 DMEM */ +#define ICSS_LRE_DBG_START 0x1E00 + +#define ICSS_LRE_DUPLICATE_HOST_TABLE 0x0200 + +/* PRU1 DMEM */ +#define ICSS_LRE_DUPLICATE_PORT_TABLE_PRU0 0x0200 +#define ICSS_LRE_DUPLICATE_PORT_TABLE_PRU1 0x0E00 + +/* Size and setup (N and M) of duplicate host table */ +#define ICSS_LRE_DUPLICATE_HOST_TABLE_SIZE 0x1C08 +/* Size and setup (N and M) of duplicate port table (HSR Only) */ +#define ICSS_LRE_DUPLICATE_PORT_TABLE_SIZE 0x1C1C +/* Time after which an entry is removed from the dup table (10ms resolution) */ +#define ICSS_LRE_DUPLI_FORGET_TIME 0x1C24 +/* Time interval to check the port duplicate table */ +#define ICSS_LRE_DUPLI_PORT_CHECK_RESO 0x1C2C +/* Time interval to check the host duplicate table */ +#define ICSS_LRE_DUPLI_HOST_CHECK_RESO 0x1C30 +/* NodeTable | Host | Port */ +#define ICSS_LRE_HOST_TIMER_CHECK_FLAGS 0x1C38 +/* Arbitration flag for the host duplicate t */ +#define ICSS_LRE_HOST_DUPLICATE_ARBITRATION 0x1C3C +/* Supervision address in LRE */ +#define ICSS_LRE_SUP_ADDR 0x1C4C +#define ICSS_LRE_SUP_ADDR_LOW 0x1C50 + +/* Time in TimeTicks (1/100s) */ +#define ICSS_LRE_DUPLICATE_FORGET_TIME_400_MS 40 +/* Time in TimeTicks (1/100s) */ +#define ICSS_LRE_NODE_FORGET_TIME_60000_MS 6000 +#define ICSS_LRE_MAX_FORGET_TIME 0xffdf + +#define ICSS_LRE_DUPLICATE_PORT_TABLE_DMEM_SIZE 0x0C00 +#define ICSS_LRE_DUPLICATE_HOST_TABLE_DMEM_SIZE 0x1800 +#define ICSS_LRE_STATS_DMEM_SIZE 0x0088 +#define ICSS_LRE_DEBUG_COUNTER_DMEM_SIZE 0x0050 + +#define ICSS_LRE_DUPLICATE_HOST_TABLE_SIZE_INIT 0x800004 /* N = 128, M = 4 */ +#define ICSS_LRE_DUPLICATE_PORT_TABLE_SIZE_INIT 0x400004 /* N = 64, M = 4 */ +#define ICSS_LRE_MASTER_SLAVE_BUSY_BITS_CLEAR 0x0 +#define ICSS_LRE_TABLE_CHECK_RESOLUTION_10_MS 0xA +#define ICSS_LRE_SUP_ADDRESS_INIT_OCTETS_HIGH 0x4E1501 /* 01-15-4E-00- */ +#define ICSS_LRE_SUP_ADDRESS_INIT_OCTETS_LOW 0x1 /* -01-00 */ + +/* SHARED RAM */ + +/* 8 bytes of VLAN PCP to RX QUEUE MAPPING */ +#define ICSS_LRE_QUEUE_2_PCP_MAP_OFFSET 0x120 +#define ICSS_LRE_START 0x140 + +/* Number of frames successfully sent over port A/B that are HSR/PRP tagged */ + +#define ICSS_LRE_CNT_TX_A (ICSS_LRE_START + 4) +#define ICSS_LRE_DUPLICATE_DISCARD (ICSS_LRE_START + 104) +#define ICSS_LRE_TRANSPARENT_RECEPTION (ICSS_LRE_START + 108) +#define ICSS_LRE_CNT_NODES (ICSS_LRE_START + 52) + +/* SRAM */ + +#define ICSS_LRE_IEC62439_CONST_DUPLICATE_ACCEPT 0x01 +#define ICSS_LRE_IEC62439_CONST_DUPLICATE_DISCARD 0x02 +#define ICSS_LRE_IEC62439_CONST_TRANSP_RECEPTION_REMOVE_RCT 0x01 +#define ICSS_LRE_IEC62439_CONST_TRANSP_RECEPTION_PASS_RCT 0x02 + +/* Enable/disable interrupts for high/low priority instead of per port. + * 0 = disabled (default) 1 = enabled + */ +#define ICSS_LRE_PRIORITY_INTRS_STATUS_OFFSET 0x1FAA +/* Enable/disable timestamping of packets. 0 = disabled (default) 1 = enabled */ +#define ICSS_LRE_TIMESTAMP_PKTS_STATUS_OFFSET 0x1FAB +#define ICSS_LRE_TIMESTAMP_ARRAY_OFFSET 0xC200 + +/* HOST_TIMER_CHECK_FLAGS bits */ +#define ICSS_LRE_HOST_TIMER_NODE_TABLE_CHECK_BIT BIT(0) +#define ICSS_LRE_HOST_TIMER_NODE_TABLE_CLEAR_BIT BIT(4) +#define ICSS_LRE_HOST_TIMER_HOST_TABLE_CHECK_BIT BIT(8) +#define ICSS_LRE_HOST_TIMER_P1_TABLE_CHECK_BIT BIT(16) +#define ICSS_LRE_HOST_TIMER_P2_TABLE_CHECK_BIT BIT(24) +#define ICSS_LRE_HOST_TIMER_PORT_TABLE_CHECK_BITS \ + (ICSS_LRE_HOST_TIMER_P1_TABLE_CHECK_BIT | \ + ICSS_LRE_HOST_TIMER_P2_TABLE_CHECK_BIT) + +#define ICSS_LRE_NODE_FREE 0x10 +/* PRU1 DMEM */ +#define ICSS_LRE_V2_1_HASH_MASK 0xFF +#define ICSS_LRE_V2_1_INDEX_ARRAY_NT 0x3000 +#define ICSS_LRE_V2_1_BIN_ARRAY \ + (ICSS_LRE_V2_1_INDEX_ARRAY_NT + \ + (ICSS_LRE_V2_1_INDEX_TBL_MAX_ENTRIES * 6)) +#define ICSS_LRE_V2_1_NODE_TABLE_NEW \ + (ICSS_LRE_V2_1_BIN_ARRAY + \ + (ICSS_LRE_V2_1_BIN_TBL_MAX_ENTRIES * 8)) +#define ICSS_LRE_V2_1_INDEX_ARRAY_LOC PRUETH_MEM_SHARED_RAM +#define ICSS_LRE_V2_1_BIN_ARRAY_LOC PRUETH_MEM_SHARED_RAM +#define ICSS_LRE_V2_1_NODE_TABLE_LOC PRUETH_MEM_SHARED_RAM +#define ICSS_LRE_V2_1_INDEX_TBL_MAX_ENTRIES 256 +#define ICSS_LRE_V2_1_BIN_TBL_MAX_ENTRIES 256 +#define ICSS_LRE_V2_1_NODE_TBL_MAX_ENTRIES 256 + +#define ICSS_LRE_NODE_FREE 0x10 +#define ICSS_LRE_NODE_TAKEN 0x01 +#define ICSS_LRE_NT_REM_NODE_TYPE_MASK 0x1F +#define ICSS_LRE_NT_REM_NODE_TYPE_SHIFT 0x00 + +#define ICSS_LRE_NT_REM_NODE_TYPE_SANA 0x01 +#define ICSS_LRE_NT_REM_NODE_TYPE_SANB 0x02 +#define ICSS_LRE_NT_REM_NODE_TYPE_SANAB 0x03 +#define ICSS_LRE_NT_REM_NODE_TYPE_DAN 0x04 +#define ICSS_LRE_NT_REM_NODE_TYPE_REDBOX 0x08 +#define ICSS_LRE_NT_REM_NODE_TYPE_VDAN 0x10 + +#define ICSS_LRE_NT_REM_NODE_HSR_BIT 0x20 /* if set node is HSR */ + +#define ICSS_LRE_NT_REM_NODE_DUP_MASK 0xC0 +#define ICSS_LRE_NT_REM_NODE_DUP_SHIFT 0x06 + +/* Node ent duplicate type: DupAccept */ +#define ICSS_LRE_NT_REM_NODE_DUP_ACCEPT 0x40 +/* Node ent duplicate type: DupDiscard */ +#define ICSS_LRE_NT_REM_NODE_DUP_DISCARD 0x80 + +#endif /* __ICSS_LRE_FIRMWARE_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icss_mii_rt.h b/drivers/net/ethernet/ti/icss_mii_rt.h --- a/drivers/net/ethernet/ti/icss_mii_rt.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icss_mii_rt.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* PRU-ICSS MII_RT register definitions + * + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#ifndef __NET_PRUSS_MII_RT_H__ +#define __NET_PRUSS_MII_RT_H__ + +#include +#include + +#include "icss_lre_firmware.h" + +/* PRUSS_MII_RT Registers */ +#define PRUSS_MII_RT_RXCFG0 0x0 +#define PRUSS_MII_RT_RXCFG1 0x4 +#define PRUSS_MII_RT_TXCFG0 0x10 +#define PRUSS_MII_RT_TXCFG1 0x14 +#define PRUSS_MII_RT_TX_CRC0 0x20 +#define PRUSS_MII_RT_TX_CRC1 0x24 +#define PRUSS_MII_RT_TX_IPG0 0x30 +#define PRUSS_MII_RT_TX_IPG1 0x34 +#define PRUSS_MII_RT_PRS0 0x38 +#define PRUSS_MII_RT_PRS1 0x3c +#define PRUSS_MII_RT_RX_FRMS0 0x40 +#define PRUSS_MII_RT_RX_FRMS1 0x44 +#define PRUSS_MII_RT_RX_PCNT0 0x48 +#define PRUSS_MII_RT_RX_PCNT1 0x4c +#define PRUSS_MII_RT_RX_ERR0 0x50 +#define PRUSS_MII_RT_RX_ERR1 0x54 + +/* PRUSS_MII_RT_RXCFG0/1 bits */ +#define PRUSS_MII_RT_RXCFG_RX_ENABLE BIT(0) +#define PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS BIT(1) +#define PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE BIT(2) +#define PRUSS_MII_RT_RXCFG_RX_MUX_SEL BIT(3) +#define PRUSS_MII_RT_RXCFG_RX_L2_EN BIT(4) +#define PRUSS_MII_RT_RXCFG_RX_BYTE_SWAP BIT(5) +#define PRUSS_MII_RT_RXCFG_RX_AUTO_FWD_PRE BIT(6) +#define PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS BIT(9) + +/* PRUSS_MII_RT_TXCFG0/1 bits */ +#define PRUSS_MII_RT_TXCFG_TX_ENABLE BIT(0) +#define PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE BIT(1) +#define PRUSS_MII_RT_TXCFG_TX_EN_MODE BIT(2) +#define PRUSS_MII_RT_TXCFG_TX_BYTE_SWAP BIT(3) +#define PRUSS_MII_RT_TXCFG_TX_MUX_SEL BIT(8) +#define PRUSS_MII_RT_TXCFG_PRE_TX_AUTO_SEQUENCE BIT(9) +#define PRUSS_MII_RT_TXCFG_PRE_TX_AUTO_ESC_ERR BIT(10) +#define PRUSS_MII_RT_TXCFG_TX_32_MODE_EN BIT(11) +#define PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN BIT(12) /* SR2.0 onwards */ + +#define PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT 16 +#define PRUSS_MII_RT_TXCFG_TX_START_DELAY_MASK GENMASK(25, 16) + +#define PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT 28 +#define PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK GENMASK(30, 28) + +/* PRUSS_MII_RT_TX_IPG0/1 bits */ +#define PRUSS_MII_RT_TX_IPG_IPG_SHIFT 0 +#define PRUSS_MII_RT_TX_IPG_IPG_MASK GENMASK(9, 0) + +/* PRUSS_MII_RT_PRS0/1 bits */ +#define PRUSS_MII_RT_PRS_COL BIT(0) +#define PRUSS_MII_RT_PRS_CRS BIT(1) + +/* PRUSS_MII_RT_RX_FRMS0/1 bits */ +#define PRUSS_MII_RT_RX_FRMS_MIN_FRM_SHIFT 0 +#define PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK GENMASK(15, 0) + +#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT 16 +#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK GENMASK(31, 16) + +/* Min/Max in MII_RT_RX_FRMS */ +/* For EMAC and Switch */ +#define PRUSS_MII_RT_RX_FRMS_MAX (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) +#define PRUSS_MII_RT_RX_FRMS_MIN_FRM (64) + +/* for HSR and PRP */ +#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_LRE (PRUSS_MII_RT_RX_FRMS_MAX + \ + ICSS_LRE_TAG_RCT_SIZE) +/* PRUSS_MII_RT_RX_PCNT0/1 bits */ +#define PRUSS_MII_RT_RX_PCNT_MIN_PCNT_SHIFT 0 +#define PRUSS_MII_RT_RX_PCNT_MIN_PCNT_MASK GENMASK(3, 0) + +#define PRUSS_MII_RT_RX_PCNT_MAX_PCNT_SHIFT 4 +#define PRUSS_MII_RT_RX_PCNT_MAX_PCNT_MASK GENMASK(7, 4) + +/* PRUSS_MII_RT_RX_ERR0/1 bits */ +#define PRUSS_MII_RT_RX_ERR_MIN_PCNT_ERR BIT(0) +#define PRUSS_MII_RT_RX_ERR_MAX_PCNT_ERR BIT(1) +#define PRUSS_MII_RT_RX_ERR_MIN_FRM_ERR BIT(2) +#define PRUSS_MII_RT_RX_ERR_MAX_FRM_ERR BIT(3) + +#define ICSSG_CFG_OFFSET 0 +#define RGMII_CFG_OFFSET 4 + +/* Constant to choose between MII0 and MII1 */ +#define ICSS_MII0 0 +#define ICSS_MII1 1 + +/* ICSSG_CFG Register bits */ +#define ICSSG_CFG_SGMII_MODE BIT(16) +#define ICSSG_CFG_TX_PRU_EN BIT(11) +#define ICSSG_CFG_RX_SFD_TX_SOF_EN BIT(10) +#define ICSSG_CFG_RTU_PRU_PSI_SHARE_EN BIT(9) +#define ICSSG_CFG_IEP1_TX_EN BIT(8) +#define ICSSG_CFG_MII1_MODE GENMASK(6, 5) +#define ICSSG_CFG_MII1_MODE_SHIFT 5 +#define ICSSG_CFG_MII0_MODE GENMASK(4, 3) +#define ICSSG_CFG_MII0_MODE_SHIFT 3 +#define ICSSG_CFG_RX_L2_G_EN BIT(2) +#define ICSSG_CFG_TX_L2_EN BIT(1) +#define ICSSG_CFG_TX_L1_EN BIT(0) + +enum mii_mode { + MII_MODE_MII = 0, + MII_MODE_RGMII, + MII_MODE_SGMII +}; + +/* RGMII CFG Register bits */ +#define RGMII_CFG_INBAND_EN_MII0 BIT(16) +#define RGMII_CFG_GIG_EN_MII0 BIT(17) +#define RGMII_CFG_INBAND_EN_MII1 BIT(20) +#define RGMII_CFG_GIG_EN_MII1 BIT(21) +#define RGMII_CFG_FULL_DUPLEX_MII0 BIT(18) +#define RGMII_CFG_FULL_DUPLEX_MII1 BIT(22) +#define RGMII_CFG_SPEED_MII0 GENMASK(2, 1) +#define RGMII_CFG_SPEED_MII1 GENMASK(6, 5) +#define RGMII_CFG_SPEED_MII0_SHIFT 1 +#define RGMII_CFG_SPEED_MII1_SHIFT 5 +#define RGMII_CFG_FULLDUPLEX_MII0 BIT(3) +#define RGMII_CFG_FULLDUPLEX_MII1 BIT(7) +#define RGMII_CFG_FULLDUPLEX_MII0_SHIFT 3 +#define RGMII_CFG_FULLDUPLEX_MII1_SHIFT 7 +#define RGMII_CFG_SPEED_10M 0 +#define RGMII_CFG_SPEED_100M 1 +#define RGMII_CFG_SPEED_1G 2 + +struct regmap; +struct prueth_emac; + +void icssg_mii_update_ipg(struct regmap *mii_rt, int mii, u32 ipg); +void icssg_update_rgmii_cfg(struct regmap *miig_rt, struct prueth_emac *emac); +u32 icssg_rgmii_cfg_get_bitfield(struct regmap *miig_rt, u32 mask, u32 shift); +u32 icssg_rgmii_get_speed(struct regmap *miig_rt, int mii); +u32 icssg_rgmii_get_fullduplex(struct regmap *miig_rt, int mii); +void icssg_miig_set_interface_mode(struct regmap *miig_rt, int mii, phy_interface_t phy_if); + +#endif /* __NET_PRUSS_MII_RT_H__ */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icss_switch.h b/drivers/net/ethernet/ti/icss_switch.h --- a/drivers/net/ethernet/ti/icss_switch.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icss_switch.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#ifndef __ICSS_SWITCH_H +#define __ICSS_SWITCH_H + +/* Basic Switch Parameters + * Used to auto compute offset addresses on L3 OCMC RAM. Do not modify these + * without changing firmware accordingly + */ +#define SWITCH_BUFFER_SIZE (64 * 1024) /* L3 buffer */ +#define ICSS_BLOCK_SIZE 32 /* data bytes per BD */ +#define BD_SIZE 4 /* byte buffer descriptor */ +#define NUM_QUEUES 4 /* Queues on Port 0/1/2 */ + +#define PORT_LINK_MASK 0x1 +#define PORT_IS_HD_MASK 0x2 + +/* Physical Port queue size (number of BDs). Same for both ports */ +#define QUEUE_1_SIZE 97 /* Network Management high */ +#define QUEUE_2_SIZE 97 /* Network Management low */ +#define QUEUE_3_SIZE 97 /* Protocol specific */ +#define QUEUE_4_SIZE 97 /* NRT (IP,ARP, ICMP) */ + +/* Host queue size (number of BDs). Each BD points to data buffer of 32 bytes. + * HOST PORT QUEUES can buffer up to 4 full sized frames per queue + */ +#define HOST_QUEUE_1_SIZE 194 /* Protocol and VLAN priority 7 & 6 */ +#define HOST_QUEUE_2_SIZE 194 /* Protocol mid */ +#define HOST_QUEUE_3_SIZE 194 /* Protocol low */ +#define HOST_QUEUE_4_SIZE 194 /* NRT (IP, ARP, ICMP) */ + +#define COL_QUEUE_SIZE 0 + +/* NRT Buffer descriptor definition + * Each buffer descriptor points to a max 32 byte block and has 32 bit in size + * to have atomic operation. + * PRU can address bytewise into memory. + * Definition of 32 bit descriptor is as follows + * + * Bits Name Meaning + * ============================================================================= + * 0..7 Index points to index in buffer queue, max 256 x 32 + * byte blocks can be addressed + * 6 LookupSuccess For switch, FDB lookup was successful (source + * MAC address found in FDB). + * For RED, NodeTable lookup was successful. + * 7 Flood Packet should be flooded (destination MAC + * address found in FDB). For switch only. + * 8..12 Block_length number of valid bytes in this specific block. + * Will be <=32 bytes on last block of packet + * 13 More "More" bit indicating that there are more blocks + * 14 Shadow indicates that "index" is pointing into shadow + * buffer + * 15 TimeStamp indicates that this packet has time stamp in + * separate buffer - only needed of PTCP runs on + * host + * 16..17 Port different meaning for ingress and egress, + * Ingress: Port = 0 indicates phy port 1 and + * Port = 1 indicates phy port 2. + * Egress: 0 sends on phy port 1 and 1 sends on + * phy port 2. Port = 2 goes over MAC table + * look-up + * 18..28 Length 11 bit of total packet length which is put into + * first BD only so that host access only one BD + * 29 VlanTag indicates that packet has Length/Type field of + * 0x08100 with VLAN tag in following byte + * 30 Broadcast indicates that packet goes out on both physical + * ports, there will be two bd but only one buffer + * 31 Error indicates there was an error in the packet + */ +#define PRUETH_BD_START_FLAG_MASK BIT(0) +#define PRUETH_BD_START_FLAG_SHIFT 0 + +#define PRUETH_BD_HSR_FRAME_MASK BIT(4) +#define PRUETH_BD_HSR_FRAME_SHIFT 4 + +#define PRUETH_BD_SUP_HSR_FRAME_MASK BIT(5) +#define PRUETH_BD_SUP_HSR_FRAME_SHIFT 5 + +#define PRUETH_BD_LOOKUP_SUCCESS_MASK BIT(6) +#define PRUETH_BD_LOOKUP_SUCCESS_SHIFT 6 + +#define PRUETH_BD_SW_FLOOD_MASK BIT(7) +#define PRUETH_BD_SW_FLOOD_SHIFT 7 + +#define PRUETH_BD_SHADOW_MASK BIT(14) +#define PRUETH_BD_SHADOW_SHIFT 14 + +#define PRUETH_BD_TIMESTAMP_MASK BIT(15) +#define PRUETH_BD_TIMESTAMP_SHIT 15 + +#define PRUETH_BD_PORT_MASK GENMASK(17, 16) +#define PRUETH_BD_PORT_SHIFT 16 + +#define PRUETH_BD_LENGTH_MASK GENMASK(28, 18) +#define PRUETH_BD_LENGTH_SHIFT 18 + +#define PRUETH_BD_BROADCAST_MASK BIT(30) +#define PRUETH_BD_BROADCAST_SHIFT 30 + +#define PRUETH_BD_ERROR_MASK BIT(31) +#define PRUETH_BD_ERROR_SHIFT 31 + +/* The following offsets indicate which sections of the memory are used + * for EMAC internal tasks + */ +#define DRAM_START_OFFSET 0x1e98 +#define SRAM_START_OFFSET 0x400 + +/* General Purpose Statistics + * These are present on both PRU0 and PRU1 DRAM + */ +/* base statistics offset */ +#define STATISTICS_OFFSET 0x1f00 +#define STAT_SIZE 0x98 + +/* The following offsets indicate which sections of the memory are used + * for switch internal tasks + */ +#define SWITCH_SPECIFIC_DRAM0_START_SIZE 0x100 +#define SWITCH_SPECIFIC_DRAM0_START_OFFSET 0x1F00 + +#define SWITCH_SPECIFIC_DRAM1_START_SIZE 0x300 +#define SWITCH_SPECIFIC_DRAM1_START_OFFSET 0x1D00 + +/* Offset for storing + * 1. Storm Prevention Params + * 2. PHY Speed Offset + * 3. Port Status Offset + * These are present on both PRU0 and PRU1 + */ +/* 4 bytes */ +#define STORM_PREVENTION_OFFSET_BC (STATISTICS_OFFSET + STAT_SIZE) +/* 4 bytes */ +#define PHY_SPEED_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 4) +/* 1 byte */ +#define PORT_STATUS_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 8) +/* 1 byte */ +#define COLLISION_COUNTER (STATISTICS_OFFSET + STAT_SIZE + 9) +/* 4 bytes */ +#define RX_PKT_SIZE_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 10) +/* 4 bytes */ +#define PORT_CONTROL_ADDR (STATISTICS_OFFSET + STAT_SIZE + 14) +/* 6 bytes */ +#define PORT_MAC_ADDR (STATISTICS_OFFSET + STAT_SIZE + 18) +/* 1 byte */ +#define RX_INT_STATUS_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 24) +/* 4 bytes */ +#define STORM_PREVENTION_OFFSET_MC (STATISTICS_OFFSET + STAT_SIZE + 25) +/* 4 bytes */ +#define STORM_PREVENTION_OFFSET_UC (STATISTICS_OFFSET + STAT_SIZE + 29) +/* 4 bytes ? */ +#define STP_INVALID_STATE_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 33) + +/* DRAM1 Offsets for Switch */ +/* 4 queue descriptors for port 0 (host receive) */ +#define P0_QUEUE_DESC_OFFSET 0x1E7C +#define P1_QUEUE_DESC_OFFSET 0x1E9C +#define P2_QUEUE_DESC_OFFSET 0x1EBC +/* collision descriptor of port 0 */ +#define P0_COL_QUEUE_DESC_OFFSET 0x1E64 +#define P1_COL_QUEUE_DESC_OFFSET 0x1E6C +#define P2_COL_QUEUE_DESC_OFFSET 0x1E74 +/* Collision Status Register + * P0: bit 0 is pending flag, bit 1..2 inidicates which queue, + * P1: bit 8 is pending flag, 9..10 is queue number + * P2: bit 16 is pending flag, 17..18 is queue number, remaining bits are 0. + */ +#define COLLISION_STATUS_ADDR 0x1E60 + +#define INTERFACE_MAC_ADDR 0x1E58 +#define P2_MAC_ADDR 0x1E50 +#define P1_MAC_ADDR 0x1E48 + +#define QUEUE_SIZE_ADDR 0x1E30 +#define QUEUE_OFFSET_ADDR 0x1E18 +#define QUEUE_DESCRIPTOR_OFFSET_ADDR 0x1E00 + +#define COL_RX_CONTEXT_P2_OFFSET_ADDR (COL_RX_CONTEXT_P1_OFFSET_ADDR + 12) +#define COL_RX_CONTEXT_P1_OFFSET_ADDR (COL_RX_CONTEXT_P0_OFFSET_ADDR + 12) +#define COL_RX_CONTEXT_P0_OFFSET_ADDR (P2_Q4_RX_CONTEXT_OFFSET + 8) + +/* Port 2 Rx Context */ +#define P2_Q4_RX_CONTEXT_OFFSET (P2_Q3_RX_CONTEXT_OFFSET + 8) +#define P2_Q3_RX_CONTEXT_OFFSET (P2_Q2_RX_CONTEXT_OFFSET + 8) +#define P2_Q2_RX_CONTEXT_OFFSET (P2_Q1_RX_CONTEXT_OFFSET + 8) +#define P2_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P2_Q1_OFFSET_ADDR +#define RX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_RX_CONTEXT_OFFSET + 8) + +/* Port 1 Rx Context */ +#define P1_Q4_RX_CONTEXT_OFFSET (P1_Q3_RX_CONTEXT_OFFSET + 8) +#define P1_Q3_RX_CONTEXT_OFFSET (P1_Q2_RX_CONTEXT_OFFSET + 8) +#define P1_Q2_RX_CONTEXT_OFFSET (P1_Q1_RX_CONTEXT_OFFSET + 8) +#define P1_Q1_RX_CONTEXT_OFFSET (RX_CONTEXT_P1_Q1_OFFSET_ADDR) +#define RX_CONTEXT_P1_Q1_OFFSET_ADDR (P0_Q4_RX_CONTEXT_OFFSET + 8) + +/* Host Port Rx Context */ +#define P0_Q4_RX_CONTEXT_OFFSET (P0_Q3_RX_CONTEXT_OFFSET + 8) +#define P0_Q3_RX_CONTEXT_OFFSET (P0_Q2_RX_CONTEXT_OFFSET + 8) +#define P0_Q2_RX_CONTEXT_OFFSET (P0_Q1_RX_CONTEXT_OFFSET + 8) +#define P0_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P0_Q1_OFFSET_ADDR +#define RX_CONTEXT_P0_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR + 8) + +/* Port 2 Tx Collision Context */ +#define COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR + 8) +/* Port 1 Tx Collision Context */ +#define COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR (P2_Q4_TX_CONTEXT_OFFSET + 8) + +/* Port 2 */ +#define P2_Q4_TX_CONTEXT_OFFSET (P2_Q3_TX_CONTEXT_OFFSET + 8) +#define P2_Q3_TX_CONTEXT_OFFSET (P2_Q2_TX_CONTEXT_OFFSET + 8) +#define P2_Q2_TX_CONTEXT_OFFSET (P2_Q1_TX_CONTEXT_OFFSET + 8) +#define P2_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P2_Q1_OFFSET_ADDR +#define TX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_TX_CONTEXT_OFFSET + 8) + +/* Port 1 */ +#define P1_Q4_TX_CONTEXT_OFFSET (P1_Q3_TX_CONTEXT_OFFSET + 8) +#define P1_Q3_TX_CONTEXT_OFFSET (P1_Q2_TX_CONTEXT_OFFSET + 8) +#define P1_Q2_TX_CONTEXT_OFFSET (P1_Q1_TX_CONTEXT_OFFSET + 8) +#define P1_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P1_Q1_OFFSET_ADDR +#define TX_CONTEXT_P1_Q1_OFFSET_ADDR SWITCH_SPECIFIC_DRAM1_START_OFFSET + +/* Shared RAM Offsets for Switch */ +/* NSP (Network Storm Prevention) timer re-uses NT timer */ +#define PRUETH_NSP_CREDIT_SHIFT 8 +#define PRUETH_NSP_ENABLE BIT(0) + +/* DRAM Offsets for EMAC + * Present on Both DRAM0 and DRAM1 + */ + +/* 4 queue descriptors for port tx = 32 bytes */ +#define TX_CONTEXT_Q1_OFFSET_ADDR (PORT_QUEUE_DESC_OFFSET + 32) +#define PORT_QUEUE_DESC_OFFSET (ICSS_EMAC_TTS_CYC_TX_SOF + 8) + +/* EMAC Time Triggered Send Offsets */ +#define ICSS_EMAC_TTS_CYC_TX_SOF (ICSS_EMAC_TTS_PREV_TX_SOF + 8) +#define ICSS_EMAC_TTS_PREV_TX_SOF (ICSS_EMAC_TTS_MISSED_CYCLE_CNT_OFFSET + 4) +#define ICSS_EMAC_TTS_MISSED_CYCLE_CNT_OFFSET (ICSS_EMAC_TTS_STATUS_OFFSET + 4) +#define ICSS_EMAC_TTS_STATUS_OFFSET (ICSS_EMAC_TTS_CFG_TIME_OFFSET + 4) +#define ICSS_EMAC_TTS_CFG_TIME_OFFSET (ICSS_EMAC_TTS_CYCLE_PERIOD_OFFSET + 4) +#define ICSS_EMAC_TTS_CYCLE_PERIOD_OFFSET (ICSS_EMAC_TTS_CYCLE_START_OFFSET + 8) +#define ICSS_EMAC_TTS_CYCLE_START_OFFSET ICSS_EMAC_TTS_BASE_OFFSET +#define ICSS_EMAC_TTS_BASE_OFFSET DRAM_START_OFFSET + +/* Shared RAM offsets for EMAC */ + +/* Queue Descriptors */ + +/* 4 queue descriptors for port 0 (host receive). 32 bytes */ +#define HOST_QUEUE_DESC_OFFSET (HOST_QUEUE_SIZE_ADDR + 16) + +/* table offset for queue size: + * 3 ports * 4 Queues * 1 byte offset = 12 bytes + */ +#define HOST_QUEUE_SIZE_ADDR (HOST_QUEUE_OFFSET_ADDR + 8) +/* table offset for queue: + * 4 Queues * 2 byte offset = 8 bytes + */ +#define HOST_QUEUE_OFFSET_ADDR (HOST_QUEUE_DESCRIPTOR_OFFSET_ADDR + 8) +/* table offset for Host queue descriptors: + * 1 ports * 4 Queues * 2 byte offset = 8 bytes + */ +#define HOST_QUEUE_DESCRIPTOR_OFFSET_ADDR (HOST_Q4_RX_CONTEXT_OFFSET + 8) + +/* Host Port Rx Context */ +#define HOST_Q4_RX_CONTEXT_OFFSET (HOST_Q3_RX_CONTEXT_OFFSET + 8) +#define HOST_Q3_RX_CONTEXT_OFFSET (HOST_Q2_RX_CONTEXT_OFFSET + 8) +#define HOST_Q2_RX_CONTEXT_OFFSET (HOST_Q1_RX_CONTEXT_OFFSET + 8) +#define HOST_Q1_RX_CONTEXT_OFFSET (EMAC_PROMISCUOUS_MODE_OFFSET + 4) + +/* Promiscuous mode control */ +#define EMAC_P1_PROMISCUOUS_BIT BIT(0) +#define EMAC_P2_PROMISCUOUS_BIT BIT(1) +#define EMAC_PROMISCUOUS_MODE_OFFSET (EMAC_RESERVED + 4) +#define EMAC_RESERVED EOF_48K_BUFFER_BD + +/* allow for max 48k buffer which spans the descriptors up to 0x1800 6kB */ +#define EOF_48K_BUFFER_BD (P0_BUFFER_DESC_OFFSET + HOST_BD_SIZE + PORT_BD_SIZE) + +#define HOST_BD_SIZE ((HOST_QUEUE_1_SIZE + HOST_QUEUE_2_SIZE + HOST_QUEUE_3_SIZE + HOST_QUEUE_4_SIZE) * BD_SIZE) +#define PORT_BD_SIZE ((QUEUE_1_SIZE + QUEUE_2_SIZE + QUEUE_3_SIZE + QUEUE_4_SIZE) * 2 * BD_SIZE) + +#define END_OF_BD_POOL (P2_Q4_BD_OFFSET + QUEUE_4_SIZE * BD_SIZE) +#define P2_Q4_BD_OFFSET (P2_Q3_BD_OFFSET + QUEUE_3_SIZE * BD_SIZE) +#define P2_Q3_BD_OFFSET (P2_Q2_BD_OFFSET + QUEUE_2_SIZE * BD_SIZE) +#define P2_Q2_BD_OFFSET (P2_Q1_BD_OFFSET + QUEUE_1_SIZE * BD_SIZE) +#define P2_Q1_BD_OFFSET (P1_Q4_BD_OFFSET + QUEUE_4_SIZE * BD_SIZE) +#define P1_Q4_BD_OFFSET (P1_Q3_BD_OFFSET + QUEUE_3_SIZE * BD_SIZE) +#define P1_Q3_BD_OFFSET (P1_Q2_BD_OFFSET + QUEUE_2_SIZE * BD_SIZE) +#define P1_Q2_BD_OFFSET (P1_Q1_BD_OFFSET + QUEUE_1_SIZE * BD_SIZE) +#define P1_Q1_BD_OFFSET (P0_Q4_BD_OFFSET + HOST_QUEUE_4_SIZE * BD_SIZE) +#define P0_Q4_BD_OFFSET (P0_Q3_BD_OFFSET + HOST_QUEUE_3_SIZE * BD_SIZE) +#define P0_Q3_BD_OFFSET (P0_Q2_BD_OFFSET + HOST_QUEUE_2_SIZE * BD_SIZE) +#define P0_Q2_BD_OFFSET (P0_Q1_BD_OFFSET + HOST_QUEUE_1_SIZE * BD_SIZE) +#define P0_Q1_BD_OFFSET P0_BUFFER_DESC_OFFSET +#define P0_BUFFER_DESC_OFFSET SRAM_START_OFFSET + +/* Memory Usage of L3 OCMC RAM */ + +/* L3 64KB Memory - mainly buffer Pool */ +#define END_OF_BUFFER_POOL (P2_Q4_BUFFER_OFFSET + QUEUE_4_SIZE * ICSS_BLOCK_SIZE) +#define P2_Q4_BUFFER_OFFSET (P2_Q3_BUFFER_OFFSET + QUEUE_3_SIZE * ICSS_BLOCK_SIZE) +#define P2_Q3_BUFFER_OFFSET (P2_Q2_BUFFER_OFFSET + QUEUE_2_SIZE * ICSS_BLOCK_SIZE) +#define P2_Q2_BUFFER_OFFSET (P2_Q1_BUFFER_OFFSET + QUEUE_1_SIZE * ICSS_BLOCK_SIZE) +#define P2_Q1_BUFFER_OFFSET (P1_Q4_BUFFER_OFFSET + QUEUE_4_SIZE * ICSS_BLOCK_SIZE) +#define P1_Q4_BUFFER_OFFSET (P1_Q3_BUFFER_OFFSET + QUEUE_3_SIZE * ICSS_BLOCK_SIZE) +#define P1_Q3_BUFFER_OFFSET (P1_Q2_BUFFER_OFFSET + QUEUE_2_SIZE * ICSS_BLOCK_SIZE) +#define P1_Q2_BUFFER_OFFSET (P1_Q1_BUFFER_OFFSET + QUEUE_1_SIZE * ICSS_BLOCK_SIZE) +#define P1_Q1_BUFFER_OFFSET (P0_Q4_BUFFER_OFFSET + HOST_QUEUE_4_SIZE * ICSS_BLOCK_SIZE) +#define P0_Q4_BUFFER_OFFSET (P0_Q3_BUFFER_OFFSET + HOST_QUEUE_3_SIZE * ICSS_BLOCK_SIZE) +#define P0_Q3_BUFFER_OFFSET (P0_Q2_BUFFER_OFFSET + HOST_QUEUE_2_SIZE * ICSS_BLOCK_SIZE) +#define P0_Q2_BUFFER_OFFSET (P0_Q1_BUFFER_OFFSET + HOST_QUEUE_1_SIZE * ICSS_BLOCK_SIZE) +#define P0_COL_BUFFER_OFFSET 0xEE00 +#define P0_Q1_BUFFER_OFFSET 0x0000 + +/* The below bit will be set in BD for EMAC mode in the egress + * direction and reset for PRP mode + */ +#define PRUETH_TX_PRP_EMAC_MODE BIT(0) + +/* 1 byte | 0 : Interrupt Pacing disabled | 1 : Interrupt Pacing enabled */ +#define INTR_PAC_STATUS_OFFSET_PRU1 0x1FAE +/* 1 byte | 0 : Interrupt Pacing disabled | 1 : Interrupt Pacing enabled */ +#define INTR_PAC_STATUS_OFFSET_PRU0 0x1FAF + +#define V2_1_FDB_TBL_LOC PRUETH_MEM_SHARED_RAM +#define V2_1_FDB_TBL_OFFSET 0x2000 + +#define FDB_INDEX_TBL_MAX_ENTRIES 256 +#define FDB_MAC_TBL_MAX_ENTRIES 256 + +#endif /* __ICSS_SWITCH_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/icss_vlan_mcast_filter_mmap.h b/drivers/net/ethernet/ti/icss_vlan_mcast_filter_mmap.h --- a/drivers/net/ethernet/ti/icss_vlan_mcast_filter_mmap.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/icss_vlan_mcast_filter_mmap.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + * + * This file contains VLAN/Multicast filtering feature memory map + * + */ + +#ifndef ICSS_VLAN_MULTICAST_FILTER_MM_H +#define ICSS_VLAN_MULTICAST_FILTER_MM_H + +/* VLAN/Multicast filter defines & offsets, present on both PRU0 and PRU1 DRAM */ + +/* Feature enable/disable values for multicast filtering */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED 0x00 +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED 0x01 + +/* Feature enable/disable values for VLAN filtering */ +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_DISABLED 0x00 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLED 0x01 + +/* Add/remove multicast mac id for filtering bin */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED 0x01 +#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED 0x00 + +/* Default HASH value for the multicast filtering Mask */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_INIT_VAL 0xFF + +/* Size requirements for Multicast filtering feature */ +#define ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES 256 +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES 6 +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES 1 +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES 1 +#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES 4 + +/* Size requirements for VLAN filtering feature : 4096 bits = 512 bytes */ +#define ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES 512 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES 1 +#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_SIZE_BYTES 4 + +/* Mask override set status */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_SET 1 +/* Mask override not set status */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_NOT_SET 0 +/* 6 bytes HASH Mask for the MAC */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET 0xF4 +/* 0 -> multicast filtering disabled | 1 -> multicast filtering enabled */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET (ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES) +/* Status indicating if the HASH override is done or not: 0: no, 1: yes */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS (ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES) +/* Multicast drop statistics */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET (ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES) +/* Multicast table */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_TABLE (ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES) + +/* Multicast filter defines & offsets for LRE + */ +#define ICSS_LRE_FW_MULTICAST_TABLE_SEARCH_OP_CONTROL_BIT 0xE0 +/* one byte field : + * 0 -> multicast filtering disabled + * 1 -> multicast filtering enabled + */ +#define ICSS_LRE_FW_MULTICAST_FILTER_MASK 0xE4 +#define ICSS_LRE_FW_MULTICAST_FILTER_TABLE 0x100 + +/* VLAN table Offsets */ +#define ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET 0xEF +#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET (ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET + ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES) + +/* VLAN filter Control Bit maps */ +/* one bit field, bit 0: | 0 : VLAN filter disabled (default), 1: VLAN filter enabled */ +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLE_BIT 0 +/* one bit field, bit 1: | 0 : untagged host rcv allowed (default), 1: untagged host rcv not allowed */ +#define ICSS_EMAC_FW_VLAN_FILTER_UNTAG_HOST_RCV_ALLOW_CTRL_BIT 1 +/* one bit field, bit 1: | 0 : priotag host rcv allowed (default), 1: priotag host rcv not allowed */ +#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_HOST_RCV_ALLOW_CTRL_BIT 2 +/* one bit field, bit 1: | 0 : skip sv vlan flow :1 : take sv vlan flow (not applicable for dual emac */ +#define ICSS_EMAC_FW_VLAN_FILTER_SV_VLAN_FLOW_HOST_RCV_ALLOW_CTRL_BIT 3 + +/* VLAN IDs */ +#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_VID 0 +#define ICSS_EMAC_FW_VLAN_FILTER_VID_MIN 0x0000 +#define ICSS_EMAC_FW_VLAN_FILTER_VID_MAX 0x0FFF + +/* VLAN Filtering Commands */ +#define ICSS_EMAC_FW_VLAN_FILTER_ADD_VLAN_VID_CMD 0x00 +#define ICSS_EMAC_FW_VLAN_FILTER_REMOVE_VLAN_VID_CMD 0x01 + +/* Switch defines for VLAN/MC filtering */ +/* SRAM + * VLAN filter defines & offsets + */ +#define ICSS_LRE_FW_VLAN_FLTR_CTRL_BYTE 0x1FE +/* one bit field | 0 : VLAN filter disabled + * | 1 : VLAN filter enabled + */ +#define ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 + +#endif /* ICSS_MULTICAST_FILTER_MM_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/j721e-cpsw-virt-mac.c b/drivers/net/ethernet/ti/j721e-cpsw-virt-mac.c --- a/drivers/net/ethernet/ti/j721e-cpsw-virt-mac.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/j721e-cpsw-virt-mac.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1614 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments K3 J721 Virt Ethernet Switch MAC Driver + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "k3-cppi-desc-pool.h" + +#define VIRT_CPSW_DRV_VER "0.1" + +#define VIRT_CPSW_MAX_TX_QUEUES 1 +#define VIRT_CPSW_MAX_RX_QUEUES 1 +#define VIRT_CPSW_MAX_RX_FLOWS 1 + +#define VIRT_CPSW_MIN_PACKET_SIZE ETH_ZLEN +#define VIRT_CPSW_MAX_PACKET_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) + +/* Number of TX/RX descriptors */ +#define VIRT_CPSW_MAX_TX_DESC 500 +#define VIRT_CPSW_MAX_RX_DESC 500 + +#define VIRT_CPSW_NAV_PS_DATA_SIZE 16 +#define VIRT_CPSW_NAV_SW_DATA_SIZE 16 + +#define VIRT_CPSW_DRV_NAME "j721e-cpsw-virt-mac" + +struct virt_cpsw_tx_chn { + struct device *dev; + struct k3_cppi_desc_pool *desc_pool; + struct k3_udma_glue_tx_channel *tx_chn; + u32 descs_num; + unsigned int irq; + u32 id; +}; + +struct virt_cpsw_rx_chn { + struct device *dev; + struct k3_cppi_desc_pool *desc_pool; + struct k3_udma_glue_rx_channel *rx_chn; + u32 descs_num; + unsigned int irq; +}; + +struct virt_cpsw_port { + struct virt_cpsw_common *common; + struct net_device *ndev; + const char *name; + u8 local_mac_addr[ETH_ALEN]; +}; + +struct virt_cpsw_common { + struct device *dev; + struct virt_cpsw_port ports; + + struct virt_cpsw_tx_chn tx_chns; + struct napi_struct napi_tx; + struct hrtimer tx_hrtimer; + unsigned long tx_pace_timeout; + struct completion tdown_complete; + atomic_t tdown_cnt; + struct virt_cpsw_rx_chn rx_chns; + struct napi_struct napi_rx; + bool rx_irq_disabled; + struct hrtimer rx_hrtimer; + unsigned long rx_pace_timeout; + u32 mac_only_port; + + const char *rdev_name; + struct rpmsg_remotedev *rdev; + struct rpmsg_remotedev_eth_switch_ops *rdev_switch_ops; + u32 rdev_features; + u32 rdev_mtu; + u8 rdev_mac_addr[ETH_ALEN]; + u32 rdev_tx_psil_dst_id; + u32 tx_psil_id_base; + u32 rdev_rx_flow_id; + struct notifier_block virt_cpsw_inetaddr_nb; + struct work_struct rx_mode_work; + struct workqueue_struct *cmd_wq; + struct netdev_hw_addr_list mc_list; + unsigned int mac_only:1; + unsigned int mc_filter:1; +}; + +struct virt_cpsw_ndev_stats { + u64 tx_packets; + u64 tx_bytes; + u64 rx_packets; + u64 rx_bytes; + struct u64_stats_sync syncp; +}; + +struct virt_cpsw_ndev_priv { + struct virt_cpsw_ndev_stats __percpu *stats; + struct virt_cpsw_port *port; +}; + +#define virt_ndev_to_priv(ndev) \ + ((struct virt_cpsw_ndev_priv *)netdev_priv(ndev)) +#define virt_ndev_to_port(ndev) (virt_ndev_to_priv(ndev)->port) +#define virt_ndev_to_common(ndev) (virt_ndev_to_port(ndev)->common) + +static void virt_cpsw_nuss_ndo_host_tx_timeout(struct net_device *ndev, + unsigned int txqueue) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct virt_cpsw_tx_chn *tx_chn = &common->tx_chns; + struct netdev_queue *netif_txq; + unsigned long trans_start; + + /* process every txq*/ + netif_txq = netdev_get_tx_queue(ndev, txqueue); + trans_start = netif_txq->trans_start; + + netdev_err(ndev, "txq:%d DRV_XOFF:%d tmo:%u dql_avail:%d free_desc:%zu\n", + txqueue, + netif_tx_queue_stopped(netif_txq), + jiffies_to_msecs(jiffies - trans_start), + dql_avail(&netif_txq->dql), + k3_cppi_desc_pool_avail(tx_chn->desc_pool)); + + if (netif_tx_queue_stopped(netif_txq)) { + /* try recover if stopped by us */ + txq_trans_update(netif_txq); + netif_tx_wake_queue(netif_txq); + } +} + +static int virt_cpsw_nuss_rx_push(struct virt_cpsw_common *common, + struct sk_buff *skb) +{ + struct cppi5_host_desc_t *desc_rx; + struct virt_cpsw_rx_chn *rx_chn = &common->rx_chns; + struct device *dev = common->dev; + dma_addr_t desc_dma; + dma_addr_t buf_dma; + u32 pkt_len = skb_tailroom(skb); + void *swdata; + + desc_rx = k3_cppi_desc_pool_alloc(rx_chn->desc_pool); + if (!desc_rx) { + dev_err(dev, "Failed to allocate RXFDQ descriptor\n"); + return -ENOMEM; + } + desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx); + + buf_dma = dma_map_single(dev, skb->data, pkt_len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, buf_dma))) { + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + dev_err(dev, "Failed to map rx skb buffer\n"); + return -EINVAL; + } + + cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT, + VIRT_CPSW_NAV_PS_DATA_SIZE); + cppi5_hdesc_attach_buf(desc_rx, 0, 0, buf_dma, skb_tailroom(skb)); + swdata = cppi5_hdesc_get_swdata(desc_rx); + *((void **)swdata) = skb; + + return k3_udma_glue_push_rx_chn(rx_chn->rx_chn, 0, desc_rx, desc_dma); +} + +static int virt_cpsw_nuss_common_open(struct virt_cpsw_common *common, + netdev_features_t features) +{ + struct sk_buff *skb; + int i, ret; + + for (i = 0; i < common->rx_chns.descs_num; i++) { + skb = __netdev_alloc_skb_ip_align(NULL, + VIRT_CPSW_MAX_PACKET_SIZE, + GFP_KERNEL); + if (!skb) { + dev_err(common->dev, "cannot allocate skb\n"); + return -ENOMEM; + } + + ret = virt_cpsw_nuss_rx_push(common, skb); + if (ret < 0) { + dev_err(common->dev, + "cannot submit skb to channel rx, error %d\n", + ret); + kfree_skb(skb); + return ret; + } + kmemleak_not_leak(skb); + } + ret = k3_udma_glue_rx_flow_enable(common->rx_chns.rx_chn, 0); + if (ret) + return ret; + + ret = k3_udma_glue_enable_tx_chn(common->tx_chns.tx_chn); + if (ret) + return ret; + + napi_enable(&common->napi_tx); + napi_enable(&common->napi_rx); + if (common->rx_irq_disabled) { + common->rx_irq_disabled = false; + enable_irq(common->rx_chns.irq); + } + + return 0; +} + +static void virt_cpsw_nuss_tx_cleanup(void *data, dma_addr_t desc_dma); +static void virt_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma); + +static void virt_cpsw_nuss_common_stop(struct virt_cpsw_common *common) +{ + int i; + + /* shutdown tx channels */ + atomic_set(&common->tdown_cnt, VIRT_CPSW_MAX_TX_QUEUES); + /* ensure new tdown_cnt value is visible */ + smp_mb__after_atomic(); + reinit_completion(&common->tdown_complete); + + k3_udma_glue_tdown_tx_chn(common->tx_chns.tx_chn, false); + + i = wait_for_completion_timeout(&common->tdown_complete, + msecs_to_jiffies(1000)); + if (!i) + dev_err(common->dev, "tx teardown timeout\n"); + + k3_udma_glue_reset_tx_chn(common->tx_chns.tx_chn, + &common->tx_chns, + virt_cpsw_nuss_tx_cleanup); + k3_udma_glue_disable_tx_chn(common->tx_chns.tx_chn); + napi_disable(&common->napi_tx); + hrtimer_cancel(&common->tx_hrtimer); + + k3_udma_glue_rx_flow_disable(common->rx_chns.rx_chn, 0); + /* Need some delay to process RX ring before reset */ + msleep(100); + k3_udma_glue_reset_rx_chn(common->rx_chns.rx_chn, 0, + &common->rx_chns, + virt_cpsw_nuss_rx_cleanup, false); + napi_disable(&common->napi_rx); + hrtimer_cancel(&common->rx_hrtimer); + cancel_work_sync(&common->rx_mode_work); +} + +static int virt_cpsw_nuss_del_mc(struct net_device *ndev, const u8 *addr); + +static int virt_cpsw_nuss_ndo_stop(struct net_device *ndev) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + struct device *dev = common->dev; + int ret; + + rdev_ops = common->rdev_switch_ops; + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + + ret = rdev_ops->unregister_mac(common->rdev, ndev->dev_addr, + common->rdev_rx_flow_id); + if (ret) + dev_err(dev, "unregister_mac rpmsg - fail %d\n", ret); + + __dev_mc_unsync(ndev, virt_cpsw_nuss_del_mc); + __hw_addr_init(&common->mc_list); + virt_cpsw_nuss_common_stop(common); + + dev_info(common->dev, "virt_cpsw_nuss mac stopped\n"); + return 0; +} + +static int virt_cpsw_nuss_ndo_open(struct net_device *ndev) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + struct device *dev = common->dev; + int ret; + + rdev_ops = common->rdev_switch_ops; + netdev_tx_reset_queue(netdev_get_tx_queue(ndev, 0)); + + ret = virt_cpsw_nuss_common_open(common, ndev->features); + if (ret) + return ret; + + ret = rdev_ops->register_mac(common->rdev, + ndev->dev_addr, + common->rdev_rx_flow_id); + if (ret) { + dev_err(dev, "register_mac rpmsg - fail %d\n", ret); + virt_cpsw_nuss_common_stop(common); + return ret; + } + + netif_tx_wake_all_queues(ndev); + netif_carrier_on(ndev); + + dev_info(common->dev, "virt_cpsw_nuss mac started\n"); + return 0; +} + +static void virt_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma) +{ + struct virt_cpsw_rx_chn *rx_chn = data; + struct cppi5_host_desc_t *desc_rx; + struct sk_buff *skb; + dma_addr_t buf_dma; + u32 buf_dma_len; + void **swdata; + + desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_rx); + skb = *swdata; + cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + + dma_unmap_single(rx_chn->dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + + dev_kfree_skb_any(skb); +} + +/* RX psdata[2] word format - checksum information */ +#define AM65_CPSW_RX_PSD_CSUM_ADD GENMASK(15, 0) +#define AM65_CPSW_RX_PSD_CSUM_ERR BIT(16) +#define AM65_CPSW_RX_PSD_IS_FRAGMENT BIT(17) +#define AM65_CPSW_RX_PSD_IS_TCP BIT(18) +#define AM65_CPSW_RX_PSD_IPV6_VALID BIT(19) +#define AM65_CPSW_RX_PSD_IPV4_VALID BIT(20) + +static void virt_cpsw_nuss_rx_csum(struct sk_buff *skb, u32 csum_info) +{ + /* HW can verify IPv4/IPv6 TCP/UDP packets checksum + * csum information provides in psdata[2] word: + * AM65_CPSW_RX_PSD_CSUM_ERR bit - indicates csum error + * AM65_CPSW_RX_PSD_IPV6_VALID and AM65_CPSW_RX_PSD_IPV4_VALID + * bits - indicates IPv4/IPv6 packet + * AM65_CPSW_RX_PSD_IS_FRAGMENT bit - indicates fragmented packet + * AM65_CPSW_RX_PSD_CSUM_ADD has value 0xFFFF for non fragmented packets + * or csum value for fragmented packets if !AM65_CPSW_RX_PSD_CSUM_ERR + */ + skb_checksum_none_assert(skb); + + if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) + return; + + if ((csum_info & (AM65_CPSW_RX_PSD_IPV6_VALID | + AM65_CPSW_RX_PSD_IPV4_VALID)) && + !(csum_info & AM65_CPSW_RX_PSD_CSUM_ERR)) { + /* csum for fragmented packets is unsupported */ + if (!(csum_info & AM65_CPSW_RX_PSD_IS_FRAGMENT)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + } +} + +static int virt_cpsw_nuss_rx_packets(struct virt_cpsw_common *common, + u32 flow_idx) +{ + struct virt_cpsw_rx_chn *rx_chn = &common->rx_chns; + struct device *dev = common->dev; + struct virt_cpsw_ndev_priv *ndev_priv; + struct virt_cpsw_ndev_stats *stats; + struct net_device *ndev; + struct cppi5_host_desc_t *desc_rx; + struct sk_buff *skb, *new_skb; + dma_addr_t desc_dma, buf_dma; + u32 buf_dma_len, pkt_len, port_id = 0, csum_info; + int ret = 0; + void **swdata; + u32 *psdata; + + ret = k3_udma_glue_pop_rx_chn(rx_chn->rx_chn, flow_idx, &desc_dma); + if (ret) { + if (ret != -ENODATA) + dev_err(dev, "RX: pop chn fail %d\n", ret); + return ret; + } + + if (desc_dma & 0x1) { + dev_dbg(dev, "%s RX tdown flow: %u\n", __func__, flow_idx); + return 0; + } + + desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma); + dev_dbg(dev, "%s flow_idx: %u desc %pad\n", + __func__, flow_idx, &desc_dma); + + swdata = cppi5_hdesc_get_swdata(desc_rx); + skb = *swdata; + cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + pkt_len = cppi5_hdesc_get_pktlen(desc_rx); + cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); + /* read port for dbg */ + dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id); + ndev = common->ports.ndev; + skb->dev = ndev; + + psdata = cppi5_hdesc_get_psdata(desc_rx); + csum_info = psdata[2]; + dev_dbg(dev, "%s rx csum_info:%#x\n", __func__, csum_info); + + dma_unmap_single(dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + + k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); + + if (unlikely(!netif_running(skb->dev))) { + dev_kfree_skb_any(skb); + return -ENODEV; + } + + new_skb = netdev_alloc_skb_ip_align(ndev, VIRT_CPSW_MAX_PACKET_SIZE); + if (new_skb) { + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, ndev); + virt_cpsw_nuss_rx_csum(skb, csum_info); + napi_gro_receive(&common->napi_rx, skb); + + ndev_priv = netdev_priv(ndev); + stats = this_cpu_ptr(ndev_priv->stats); + + u64_stats_update_begin(&stats->syncp); + stats->rx_packets++; + stats->rx_bytes += pkt_len; + u64_stats_update_end(&stats->syncp); + kmemleak_not_leak(new_skb); + } else { + ndev->stats.rx_dropped++; + new_skb = skb; + } + + if (netif_dormant(ndev)) { + dev_kfree_skb_any(new_skb); + ndev->stats.rx_dropped++; + return -ENODEV; + } + + ret = virt_cpsw_nuss_rx_push(common, new_skb); + if (WARN_ON(ret < 0)) { + dev_kfree_skb_any(new_skb); + ndev->stats.rx_errors++; + ndev->stats.rx_dropped++; + } + + return ret; +} + +static enum hrtimer_restart virt_cpsw_nuss_rx_timer_callback(struct hrtimer *timer) +{ + struct virt_cpsw_common *common = + container_of(timer, struct virt_cpsw_common, rx_hrtimer); + + enable_irq(common->rx_chns.irq); + return HRTIMER_NORESTART; +} + +static int virt_cpsw_nuss_rx_poll(struct napi_struct *napi_rx, int budget) +{ + struct virt_cpsw_common *common = + container_of(napi_rx, struct virt_cpsw_common, napi_rx); + int num_rx = 0; + int cur_budget; + int ret; + + /* process every flow */ + cur_budget = budget; + + while (cur_budget--) { + ret = virt_cpsw_nuss_rx_packets(common, 0); + if (ret) + break; + num_rx++; + } + + dev_dbg(common->dev, "%s num_rx:%d %d\n", __func__, num_rx, budget); + + if (num_rx < budget && napi_complete_done(napi_rx, num_rx)) { + if (common->rx_irq_disabled) { + common->rx_irq_disabled = false; + if (unlikely(common->rx_pace_timeout)) { + hrtimer_start(&common->rx_hrtimer, + ns_to_ktime(common->rx_pace_timeout), + HRTIMER_MODE_REL_PINNED); + } else { + enable_irq(common->rx_chns.irq); + } + } + } + + return num_rx; +} + +static void virt_cpsw_nuss_xmit_free(struct virt_cpsw_tx_chn *tx_chn, + struct device *dev, + struct cppi5_host_desc_t *desc) +{ + struct cppi5_host_desc_t *first_desc, *next_desc; + dma_addr_t buf_dma, next_desc_dma; + u32 buf_dma_len; + + first_desc = desc; + next_desc = first_desc; + + cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len); + + dma_unmap_single(dev, buf_dma, buf_dma_len, + DMA_TO_DEVICE); + + next_desc_dma = cppi5_hdesc_get_next_hbdesc(first_desc); + while (next_desc_dma) { + next_desc = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, + next_desc_dma); + cppi5_hdesc_get_obuf(next_desc, &buf_dma, &buf_dma_len); + + dma_unmap_page(dev, buf_dma, buf_dma_len, + DMA_TO_DEVICE); + + next_desc_dma = cppi5_hdesc_get_next_hbdesc(next_desc); + + k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); + } + + k3_cppi_desc_pool_free(tx_chn->desc_pool, first_desc); +} + +static void virt_cpsw_nuss_tx_cleanup(void *data, dma_addr_t desc_dma) +{ + struct virt_cpsw_tx_chn *tx_chn = data; + struct cppi5_host_desc_t *desc_tx; + struct sk_buff *skb; + void **swdata; + + desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_tx); + skb = *(swdata); + virt_cpsw_nuss_xmit_free(tx_chn, tx_chn->dev, desc_tx); + + dev_kfree_skb_any(skb); +} + +static int virt_cpsw_nuss_tx_compl_packets(struct virt_cpsw_common *common, + int chn, unsigned int budget, bool *tdown) +{ + struct cppi5_host_desc_t *desc_tx; + struct device *dev = common->dev; + struct netdev_queue *netif_txq; + struct virt_cpsw_tx_chn *tx_chn; + struct net_device *ndev; + unsigned int total_bytes = 0; + struct sk_buff *skb; + dma_addr_t desc_dma; + int res, num_tx = 0; + void **swdata; + + tx_chn = &common->tx_chns; + + while (budget--) { + struct virt_cpsw_ndev_priv *ndev_priv; + struct virt_cpsw_ndev_stats *stats; + + res = k3_udma_glue_pop_tx_chn(tx_chn->tx_chn, &desc_dma); + if (res == -ENODATA) + break; + + if (desc_dma & 0x1) { + if (atomic_dec_and_test(&common->tdown_cnt)) + complete(&common->tdown_complete); + *tdown = true; + break; + } + + desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, + desc_dma); + swdata = cppi5_hdesc_get_swdata(desc_tx); + skb = *(swdata); + virt_cpsw_nuss_xmit_free(tx_chn, dev, desc_tx); + + ndev = skb->dev; + + ndev_priv = netdev_priv(ndev); + stats = this_cpu_ptr(ndev_priv->stats); + u64_stats_update_begin(&stats->syncp); + stats->tx_packets++; + stats->tx_bytes += skb->len; + u64_stats_update_end(&stats->syncp); + + total_bytes += skb->len; + napi_consume_skb(skb, budget); + num_tx++; + } + + if (!num_tx) + return 0; + + netif_txq = netdev_get_tx_queue(ndev, 0); + + netdev_tx_completed_queue(netif_txq, num_tx, total_bytes); + dev_dbg(dev, "compl 0 %d Bytes\n", total_bytes); + + if (netif_tx_queue_stopped(netif_txq)) { + /* Check whether the queue is stopped due to stalled tx dma, + * if the queue is stopped then wake the queue as + * we have free desc for tx + */ + __netif_tx_lock(netif_txq, smp_processor_id()); + if (netif_running(ndev) && + (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >= + MAX_SKB_FRAGS)) + netif_tx_wake_queue(netif_txq); + + __netif_tx_unlock(netif_txq); + } + dev_dbg(dev, "%s:%u pkt:%d\n", __func__, chn, num_tx); + + return num_tx; +} + +static enum hrtimer_restart virt_cpsw_nuss_tx_timer_callback(struct hrtimer *timer) +{ + struct virt_cpsw_common *common = + container_of(timer, struct virt_cpsw_common, tx_hrtimer); + + enable_irq(common->tx_chns.irq); + return HRTIMER_NORESTART; +} + +static int virt_cpsw_nuss_tx_poll(struct napi_struct *napi_tx, int budget) +{ + struct virt_cpsw_common *common = + container_of(napi_tx, struct virt_cpsw_common, napi_tx); + bool tdown = false; + int num_tx; + + /* process every unprocessed channel */ + num_tx = virt_cpsw_nuss_tx_compl_packets(common, 0, budget, &tdown); + + if (num_tx >= budget) + return budget; + + if (napi_complete_done(napi_tx, num_tx)) { + if (unlikely(common->tx_pace_timeout && !tdown)) { + hrtimer_start(&common->tx_hrtimer, + ns_to_ktime(common->tx_pace_timeout), + HRTIMER_MODE_REL_PINNED); + } else { + enable_irq(common->tx_chns.irq); + } + } + + return 0; +} + +static irqreturn_t virt_cpsw_nuss_rx_irq(int irq, void *dev_id) +{ + struct virt_cpsw_common *common = dev_id; + + common->rx_irq_disabled = true; + disable_irq_nosync(irq); + napi_schedule(&common->napi_rx); + + return IRQ_HANDLED; +} + +static irqreturn_t virt_cpsw_nuss_tx_irq(int irq, void *dev_id) +{ + struct virt_cpsw_common *common = dev_id; + + disable_irq_nosync(irq); + napi_schedule(&common->napi_tx); + + return IRQ_HANDLED; +} + +static netdev_tx_t virt_cpsw_nuss_ndo_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct device *dev = common->dev; + struct cppi5_host_desc_t *first_desc, *next_desc, *cur_desc; + struct virt_cpsw_tx_chn *tx_chn; + struct netdev_queue *netif_txq; + dma_addr_t desc_dma, buf_dma; + int ret, i; + u32 pkt_len; + void **swdata; + u32 *psdata; + + /* padding enabled in hw */ + pkt_len = skb_headlen(skb); + + tx_chn = &common->tx_chns; + netif_txq = netdev_get_tx_queue(ndev, 0); + + /* Map the linear buffer */ + buf_dma = dma_map_single(dev, skb->data, pkt_len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, buf_dma))) { + dev_err(dev, "Failed to map tx skb buffer\n"); + ndev->stats.tx_errors++; + goto drop_free_skb; + } + + first_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); + if (!first_desc) { + dev_dbg(dev, "Failed to allocate descriptor\n"); + dma_unmap_single(dev, buf_dma, pkt_len, DMA_TO_DEVICE); + goto busy_stop_q; + } + + cppi5_hdesc_init(first_desc, CPPI5_INFO0_HDESC_EPIB_PRESENT, + VIRT_CPSW_NAV_PS_DATA_SIZE); + cppi5_desc_set_pktids(&first_desc->hdr, 0, 0x3FFF); + cppi5_hdesc_set_pkttype(first_desc, 0x7); + /* target port has to be 0 */ + cppi5_desc_set_tags_ids(&first_desc->hdr, 0, common->mac_only_port); + + cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len); + swdata = cppi5_hdesc_get_swdata(first_desc); + *(swdata) = skb; + psdata = cppi5_hdesc_get_psdata(first_desc); + + /* HW csum offload if enabled */ + psdata[2] = 0; + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + unsigned int cs_start, cs_offset; + + cs_start = skb_transport_offset(skb); + cs_offset = cs_start + skb->csum_offset; + /* HW numerates bytes starting from 1 */ + psdata[2] = ((cs_offset + 1) << 24) | + ((cs_start + 1) << 16) | (skb->len - cs_start); + dev_dbg(dev, "%s tx psdata:%#x\n", __func__, psdata[2]); + } + + if (!skb_is_nonlinear(skb)) + goto done_tx; + + dev_dbg(dev, "fragmented SKB\n"); + + /* Handle the case where skb is fragmented in pages */ + cur_desc = first_desc; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + u32 frag_size = skb_frag_size(frag); + + next_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); + if (!next_desc) { + dev_err(dev, "Failed to allocate descriptor\n"); + goto busy_free_descs; + } + + buf_dma = skb_frag_dma_map(dev, frag, 0, frag_size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, buf_dma))) { + dev_err(dev, "Failed to map tx skb page\n"); + k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); + ndev->stats.tx_errors++; + goto drop_free_descs; + } + + cppi5_hdesc_reset_hbdesc(next_desc); + cppi5_hdesc_attach_buf(next_desc, + buf_dma, frag_size, buf_dma, frag_size); + + desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, + next_desc); + cppi5_hdesc_link_hbdesc(cur_desc, desc_dma); + + pkt_len += frag_size; + cur_desc = next_desc; + } + WARN_ON(pkt_len != skb->len); + +done_tx: + skb_tx_timestamp(skb); + + /* report bql before sending packet */ + dev_dbg(dev, "push 0 %d Bytes\n", pkt_len); + + netdev_tx_sent_queue(netif_txq, pkt_len); + + cppi5_hdesc_set_pktlen(first_desc, pkt_len); + desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, first_desc); + ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + if (ret) { + dev_err(dev, "can't push desc %d\n", ret); + /* inform bql */ + netdev_tx_completed_queue(netif_txq, 1, pkt_len); + ndev->stats.tx_errors++; + goto drop_free_descs; + } + + if (k3_cppi_desc_pool_avail(tx_chn->desc_pool) < MAX_SKB_FRAGS) { + netif_tx_stop_queue(netif_txq); + /* Barrier, so that stop_queue visible to other cpus */ + smp_mb__after_atomic(); + dev_dbg(dev, "netif_tx_stop_queue %d\n", 0); + + /* re-check for smp */ + if (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >= + MAX_SKB_FRAGS) { + netif_tx_wake_queue(netif_txq); + dev_dbg(dev, "netif_tx_wake_queue %d\n", 0); + } + } + + return NETDEV_TX_OK; + +drop_free_descs: + virt_cpsw_nuss_xmit_free(tx_chn, dev, first_desc); +drop_free_skb: + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + +busy_free_descs: + virt_cpsw_nuss_xmit_free(tx_chn, dev, first_desc); +busy_stop_q: + netif_tx_stop_queue(netif_txq); + return NETDEV_TX_BUSY; +} + +static void virt_cpsw_nuss_ndo_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct virt_cpsw_ndev_priv *ndev_priv = netdev_priv(dev); + unsigned int start; + int cpu; + + for_each_possible_cpu(cpu) { + struct virt_cpsw_ndev_stats *cpu_stats; + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + + cpu_stats = per_cpu_ptr(ndev_priv->stats, cpu); + do { + start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + rx_packets = cpu_stats->rx_packets; + rx_bytes = cpu_stats->rx_bytes; + tx_packets = cpu_stats->tx_packets; + tx_bytes = cpu_stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_packets += tx_packets; + stats->tx_bytes += tx_bytes; + } + + stats->rx_errors = dev->stats.rx_errors; + stats->rx_dropped = dev->stats.rx_dropped; + stats->tx_dropped = dev->stats.tx_dropped; +} + +static int virt_cpsw_nuss_add_mc(struct net_device *ndev, const u8 *addr) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + struct device *dev; + int ret; + + dev = common->dev; + rdev_ops = common->rdev_switch_ops; + + ret = rdev_ops->filter_add_mc(common->rdev, addr, 0, common->rdev_rx_flow_id); + if (ret) { + dev_err(dev, "filter_add_mc rpmsg - fail %d\n", ret); + return ret; + } + + return 0; +} + +static int virt_cpsw_nuss_del_mc(struct net_device *ndev, const u8 *addr) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + struct device *dev; + int ret; + + dev = common->dev; + rdev_ops = common->rdev_switch_ops; + + ret = rdev_ops->filter_del_mc(common->rdev, addr, 0, common->rdev_rx_flow_id); + if (ret) { + dev_err(dev, "filter_add_mc rpmsg - fail %d\n", ret); + return ret; + } + + return 0; +} + +static void virt_cpsw_nuss_ndo_set_rx_mode_work(struct work_struct *work) +{ + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + struct virt_cpsw_common *common; + struct net_device *ndev; + struct device *dev; + int ret; + + common = container_of(work, struct virt_cpsw_common, rx_mode_work); + dev = common->dev; + rdev_ops = common->rdev_switch_ops; + + if (common->mac_only) { + ret = rdev_ops->set_promisc_mode(common->rdev, + common->ports.ndev->flags & IFF_PROMISC); + if (ret) { + dev_err(dev, "set_promisc rpmsg - fail %d\n", ret); + return; + } + } else if (common->mc_filter) { + ndev = common->ports.ndev; + + /* make a mc list copy */ + netif_addr_lock_bh(ndev); + __hw_addr_sync(&common->mc_list, &ndev->mc, ndev->addr_len); + netif_addr_unlock_bh(ndev); + + __hw_addr_sync_dev(&common->mc_list, ndev, + virt_cpsw_nuss_add_mc, virt_cpsw_nuss_del_mc); + } +} + +static void virt_cpsw_nuss_ndo_set_rx_mode(struct net_device *ndev) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + + if (common->mac_only || common->mc_filter) + queue_work(common->cmd_wq, &common->rx_mode_work); +} + +static const struct net_device_ops virt_cpsw_nuss_netdev_ops = { + .ndo_open = virt_cpsw_nuss_ndo_open, + .ndo_stop = virt_cpsw_nuss_ndo_stop, + .ndo_start_xmit = virt_cpsw_nuss_ndo_xmit, + .ndo_get_stats64 = virt_cpsw_nuss_ndo_get_stats, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_tx_timeout = virt_cpsw_nuss_ndo_host_tx_timeout, + .ndo_set_rx_mode = virt_cpsw_nuss_ndo_set_rx_mode, +}; + +static void virt_cpsw_nuss_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + char fw_version[ETHTOOL_FWVERS_LEN]; + + rdev_ops = common->rdev_switch_ops; + + strlcpy(info->driver, dev_driver_string(common->dev), + sizeof(info->driver)); + strlcpy(info->version, VIRT_CPSW_DRV_VER, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(common->dev), sizeof(info->bus_info)); + + rdev_ops->get_fw_ver(common->rdev, fw_version, ETHTOOL_FWVERS_LEN); + strlcpy(info->fw_version, fw_version, ETHTOOL_FWVERS_LEN); +} + +static const char virt_cpsw_nuss_ethtool_priv_flags[][ETH_GSTRING_LEN] = { + "RPMSG Ping test", + "RPMSG Read reg", + "RPMSG Dump stat", +}; + +static int +virt_cpsw_nuss_get_sset_count(struct net_device __always_unused *ndev, int sset) +{ + switch (sset) { + case ETH_SS_TEST: + return ARRAY_SIZE(virt_cpsw_nuss_ethtool_priv_flags); + default: + return -EOPNOTSUPP; + } +} + +static void +virt_cpsw_nuss_get_strings(struct net_device __always_unused *ndev, + u32 stringset, u8 *data) +{ + switch (stringset) { + case ETH_SS_TEST: + memcpy(data, virt_cpsw_nuss_ethtool_priv_flags, + sizeof(virt_cpsw_nuss_ethtool_priv_flags)); + break; + } +} + +static void virt_cpsw_nuss_self_test(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + struct device *dev = common->dev; + static const char ping_data[] = "0123456789"; + u32 reg_val; + int ret; + + data[0] = 0; + ret = common->rdev_switch_ops->ping(common->rdev, + ping_data, strlen(ping_data)); + if (ret) { + dev_err(dev, "rpmsg ping fail %d\n", ret); + eth_test->flags |= ETH_TEST_FL_FAILED; + data[0] = 1; + } + + data[1] = 0; + ret = common->rdev_switch_ops->read_reg(common->rdev, + 0x0C000000, ®_val); + if (ret) { + dev_err(dev, "rpmsg read_reg fail %d\n", ret); + eth_test->flags |= ETH_TEST_FL_FAILED; + data[1] = 1; + } + dev_dbg(dev, "read_reg rpmsg cpsw_nuss_ver - 0x0C000000:%08X\n", + reg_val); + + ret = common->rdev_switch_ops->read_reg(common->rdev, + 0x0C020000, ®_val); + if (ret) { + dev_err(dev, "rpmsg read_reg fail %d\n", ret); + eth_test->flags |= ETH_TEST_FL_FAILED; + data[1] = 1; + } + dev_dbg(dev, "read_reg rpmsg cpsw_ver - 0x0C020000:%08X\n", + reg_val); + + ret = 0; + data[2] = 0; + if (common->rdev_features & RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS) + ret = common->rdev_switch_ops->dbg_dump_stats(common->rdev); + if (ret) { + dev_err(dev, "rpmsg dump_stats fail %d\n", ret); + eth_test->flags |= ETH_TEST_FL_FAILED; + data[2] = 1; + } +} + +static int virt_cpsw_nuss_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + + coal->rx_coalesce_usecs = common->rx_pace_timeout / 1000; + coal->tx_coalesce_usecs = common->tx_pace_timeout / 1000; + return 0; +} + +static int virt_cpsw_nuss_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + + if (coal->rx_coalesce_usecs && coal->rx_coalesce_usecs < 20) + coal->rx_coalesce_usecs = 20; + + if (coal->tx_coalesce_usecs && coal->tx_coalesce_usecs < 20) + coal->tx_coalesce_usecs = 20; + + common->tx_pace_timeout = coal->tx_coalesce_usecs * 1000; + common->rx_pace_timeout = coal->rx_coalesce_usecs * 1000; + + return 0; +} + +const struct ethtool_ops virt_cpsw_nuss_ethtool_ops = { + .get_drvinfo = virt_cpsw_nuss_get_drvinfo, + .get_sset_count = virt_cpsw_nuss_get_sset_count, + .get_strings = virt_cpsw_nuss_get_strings, + .self_test = virt_cpsw_nuss_self_test, + .get_link = ethtool_op_get_link, + .supported_coalesce_params = ETHTOOL_COALESCE_USECS, + .get_coalesce = virt_cpsw_nuss_get_coalesce, + .set_coalesce = virt_cpsw_nuss_set_coalesce, +}; + +static void virt_cpsw_nuss_free_tx_chns(void *data) +{ + struct virt_cpsw_common *common = data; + struct virt_cpsw_tx_chn *tx_chn = &common->tx_chns; + + if (!IS_ERR_OR_NULL(tx_chn->desc_pool)) + k3_cppi_desc_pool_destroy(tx_chn->desc_pool); + + if (!IS_ERR_OR_NULL(tx_chn->tx_chn)) + k3_udma_glue_release_tx_chn(tx_chn->tx_chn); + + memset(tx_chn, 0, sizeof(*tx_chn)); +} + +static int virt_cpsw_nuss_init_tx_chns(struct virt_cpsw_common *common) +{ + u32 max_desc_num = ALIGN(VIRT_CPSW_MAX_TX_DESC, MAX_SKB_FRAGS); + struct virt_cpsw_tx_chn *tx_chn = &common->tx_chns; + struct k3_udma_glue_tx_channel_cfg tx_cfg = { 0 }; + struct device *dev = common->dev; + struct k3_ring_cfg ring_cfg = { + .elm_size = K3_RINGACC_RING_ELSIZE_8, + .mode = K3_RINGACC_RING_MODE_RING, + .flags = 0 + }; + char tx_chn_name[IFNAMSIZ]; + u32 hdesc_size, tx_chn_num; + int ret = 0, ret1; + + /* convert to tx chn offset */ + tx_chn_num = common->rdev_tx_psil_dst_id - common->tx_psil_id_base; + snprintf(tx_chn_name, sizeof(tx_chn_name), "tx%d", tx_chn_num); + + init_completion(&common->tdown_complete); + + hdesc_size = cppi5_hdesc_calc_size(true, VIRT_CPSW_NAV_PS_DATA_SIZE, + VIRT_CPSW_NAV_SW_DATA_SIZE); + + tx_cfg.swdata_size = VIRT_CPSW_NAV_SW_DATA_SIZE; + tx_cfg.tx_cfg = ring_cfg; + tx_cfg.txcq_cfg = ring_cfg; + tx_cfg.tx_cfg.size = max_desc_num; + tx_cfg.txcq_cfg.size = max_desc_num; + + tx_chn->dev = dev; + tx_chn->id = 0; + tx_chn->descs_num = max_desc_num; + tx_chn->desc_pool = k3_cppi_desc_pool_create_name(dev, + tx_chn->descs_num, + hdesc_size, + tx_chn_name); + if (IS_ERR(tx_chn->desc_pool)) { + ret = PTR_ERR(tx_chn->desc_pool); + dev_err(dev, "Failed to create poll %d\n", ret); + goto err; + } + + tx_chn->tx_chn = k3_udma_glue_request_tx_chn(dev, tx_chn_name, &tx_cfg); + if (IS_ERR(tx_chn->tx_chn)) { + ret = PTR_ERR(tx_chn->tx_chn); + dev_err(dev, "Failed to request tx dma channel %d\n", ret); + goto err; + } + + tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); + if (tx_chn->irq <= 0) { + dev_err(dev, "Failed to get tx dma irq %d\n", tx_chn->irq); + ret = -ENXIO; + } + +err: + ret1 = devm_add_action(dev, virt_cpsw_nuss_free_tx_chns, common); + if (ret1) { + dev_err(dev, "failed to add free_tx_chns action %d", ret1); + return ret1; + } + + return ret; +} + +static void virt_cpsw_nuss_free_rx_chns(void *data) +{ + struct virt_cpsw_common *common = data; + struct virt_cpsw_rx_chn *rx_chn = &common->rx_chns; + + if (!IS_ERR_OR_NULL(rx_chn->desc_pool)) + k3_cppi_desc_pool_destroy(rx_chn->desc_pool); + + if (!IS_ERR_OR_NULL(rx_chn->rx_chn)) + k3_udma_glue_release_rx_chn(rx_chn->rx_chn); +} + +static int virt_cpsw_nuss_init_rx_chns(struct virt_cpsw_common *common) +{ + struct virt_cpsw_rx_chn *rx_chn = &common->rx_chns; + struct k3_udma_glue_rx_channel_cfg rx_cfg = {0}; + u32 max_desc_num = VIRT_CPSW_MAX_RX_DESC; + struct device *dev = common->dev; + u32 hdesc_size; + int ret = 0, ret1; + struct k3_ring_cfg rxring_cfg = { + .elm_size = K3_RINGACC_RING_ELSIZE_8, + .mode = K3_RINGACC_RING_MODE_MESSAGE, + .flags = 0, + }; + struct k3_ring_cfg fdqring_cfg = { + .elm_size = K3_RINGACC_RING_ELSIZE_8, + .mode = K3_RINGACC_RING_MODE_MESSAGE, + .flags = 0, + }; + struct k3_udma_glue_rx_flow_cfg rx_flow_cfg = { + .rx_cfg = rxring_cfg, + .rxfdq_cfg = fdqring_cfg, + .ring_rxq_id = K3_RINGACC_RING_ID_ANY, + .ring_rxfdq0_id = K3_RINGACC_RING_ID_ANY, + .src_tag_lo_sel = K3_UDMA_GLUE_SRC_TAG_LO_USE_REMOTE_SRC_TAG, + }; + + hdesc_size = cppi5_hdesc_calc_size(true, VIRT_CPSW_NAV_PS_DATA_SIZE, + VIRT_CPSW_NAV_SW_DATA_SIZE); + + rx_cfg.swdata_size = VIRT_CPSW_NAV_SW_DATA_SIZE; + rx_cfg.flow_id_num = VIRT_CPSW_MAX_RX_FLOWS; + rx_cfg.flow_id_base = common->rdev_rx_flow_id; + rx_cfg.remote = true; + + /* init all flows */ + rx_chn->dev = dev; + rx_chn->descs_num = max_desc_num; + rx_chn->desc_pool = k3_cppi_desc_pool_create_name(dev, + rx_chn->descs_num, + hdesc_size, "rx"); + if (IS_ERR(rx_chn->desc_pool)) { + ret = PTR_ERR(rx_chn->desc_pool); + dev_err(dev, "Failed to create rx poll %d\n", ret); + goto err; + } + + rx_chn->rx_chn = k3_udma_glue_request_rx_chn(dev, "rx", &rx_cfg); + if (IS_ERR(rx_chn->rx_chn)) { + ret = PTR_ERR(rx_chn->rx_chn); + dev_err(dev, "Failed to request rx dma channel %d\n", ret); + goto err; + } + + common->rdev_rx_flow_id = + k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn); + dev_dbg(dev, "used flow-id-base %u\n", common->rdev_rx_flow_id); + + rx_flow_cfg.rx_cfg.size = max_desc_num; + rx_flow_cfg.rxfdq_cfg.size = max_desc_num; + ret = k3_udma_glue_rx_flow_init(rx_chn->rx_chn, + 0, &rx_flow_cfg); + if (ret) { + dev_err(dev, "Failed to init rx flow%d %d\n", 0, ret); + goto err; + } + + rx_chn->irq = k3_udma_glue_rx_get_irq(rx_chn->rx_chn, 0); + if (rx_chn->irq <= 0) { + ret = -ENXIO; + dev_err(dev, "Failed to get rx dma irq %d\n", rx_chn->irq); + } + +err: + ret1 = devm_add_action(dev, virt_cpsw_nuss_free_rx_chns, common); + if (ret1) { + dev_err(dev, "failed to add free_rx_chns action %d", ret1); + return ret1; + } + + return ret; +} + +static int virt_cpsw_nuss_of(struct virt_cpsw_common *common) +{ + struct device *dev = common->dev; + struct device_node *port_np; + struct virt_cpsw_port *port; + const void *mac_addr; + int ret; + + ret = of_property_read_u32(dev->of_node, "ti,psil-base", + &common->tx_psil_id_base); + if (ret) { + dev_err(dev, "ti,psil-base read fail %d\n", ret); + return ret; + } + + port_np = of_get_child_by_name(dev->of_node, "virt_emac_port"); + if (!port_np) + return -ENOENT; + + port = &common->ports; + port->common = common; + port->name = of_get_property(port_np, "ti,label", NULL); + + mac_addr = of_get_mac_address(port_np); + if (!IS_ERR(mac_addr)) + ether_addr_copy(port->local_mac_addr, mac_addr); + + of_node_put(port_np); + return 0; +} + +static int virt_cpsw_nuss_rdev_init(struct virt_cpsw_common *common) +{ + struct rpmsg_rdev_eth_switch_attach_ext_info attach_info = { 0 }; + struct device *dev = common->dev; + int ret; + + ret = common->rdev_switch_ops->attach_ext(common->rdev, &attach_info); + if (ret) { + dev_err(dev, "rpmsg attach - fail %d\n", ret); + return ret; + } + dev_err(dev, "rpmsg attach_ext - rx_mtu:%d features:%08X tx_mtu[0]:%d flow_idx:%d tx_cpsw_psil_dst_id:%d mac_addr:%pM mac-only:%d\n", + attach_info.rx_mtu, attach_info.features, + attach_info.tx_mtu[0], + attach_info.flow_idx, + attach_info.tx_cpsw_psil_dst_id, + attach_info.mac_addr, + attach_info.mac_only_port); + common->rdev_features = attach_info.features; + common->rdev_mtu = VIRT_CPSW_MAX_PACKET_SIZE; + common->rdev_tx_psil_dst_id = attach_info.tx_cpsw_psil_dst_id & + (~0x8000); + common->rdev_rx_flow_id = attach_info.flow_idx; + ether_addr_copy(common->rdev_mac_addr, attach_info.mac_addr); + + if (common->rdev_features & RPMSG_KDRV_ETHSWITCH_FEATURE_MAC_ONLY) { + common->mac_only = true; + common->mac_only_port = attach_info.mac_only_port; + } + + if (common->rdev_features & RPMSG_KDRV_ETHSWITCH_FEATURE_MC_FILTER) + common->mc_filter = true; + + if (!common->mac_only && common->mac_only_port) + return -EINVAL; + + return 0; +} + +static int virt_cpsw_nuss_init_ndev(struct virt_cpsw_common *common) +{ + struct virt_cpsw_ndev_priv *ndev_priv; + struct device *dev = common->dev; + struct virt_cpsw_port *port; + int ret; + + port = &common->ports; + + /* alloc netdev */ + port->ndev = devm_alloc_etherdev_mqs(common->dev, + sizeof(struct virt_cpsw_ndev_priv), + 1, 1); + if (!port->ndev) { + dev_err(dev, "error allocating net_device\n"); + return -ENOMEM; + } + + ndev_priv = netdev_priv(port->ndev); + ndev_priv->port = port; + SET_NETDEV_DEV(port->ndev, dev); + + if (is_valid_ether_addr(port->local_mac_addr)) + ether_addr_copy(port->ndev->dev_addr, port->local_mac_addr); + else if (is_valid_ether_addr(common->rdev_mac_addr)) + ether_addr_copy(port->ndev->dev_addr, common->rdev_mac_addr); + + port->ndev->min_mtu = VIRT_CPSW_MIN_PACKET_SIZE; + port->ndev->max_mtu = VIRT_CPSW_MAX_PACKET_SIZE; + port->ndev->hw_features = NETIF_F_SG | + NETIF_F_RXCSUM; + port->ndev->features = port->ndev->hw_features; + port->ndev->vlan_features |= NETIF_F_SG; + port->ndev->netdev_ops = &virt_cpsw_nuss_netdev_ops; + port->ndev->ethtool_ops = &virt_cpsw_nuss_ethtool_ops; + + /* TX checksum offload if supported */ + if (common->rdev_features & RPMSG_KDRV_ETHSWITCH_FEATURE_TXCSUM) + port->ndev->features |= NETIF_F_HW_CSUM; + + ndev_priv->stats = netdev_alloc_pcpu_stats(struct virt_cpsw_ndev_stats); + if (!ndev_priv->stats) + return -ENOMEM; + + ret = devm_add_action_or_reset(dev, (void(*)(void *))free_percpu, + ndev_priv->stats); + if (ret) { + dev_err(dev, "failed to add percpu stat free action %d", ret); + return ret; + } + + netif_tx_napi_add(port->ndev, &common->napi_tx, + virt_cpsw_nuss_tx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(port->ndev, &common->napi_rx, + virt_cpsw_nuss_rx_poll, NAPI_POLL_WEIGHT); + + hrtimer_init(&common->tx_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + common->tx_hrtimer.function = &virt_cpsw_nuss_tx_timer_callback; + hrtimer_init(&common->rx_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + common->rx_hrtimer.function = &virt_cpsw_nuss_rx_timer_callback; + + ret = register_netdev(port->ndev); + if (ret) + dev_err(dev, "error registering slave net device %d\n", ret); + + /* can't auto unregister ndev using devm_add_action() due to broken + * devres release sequence in DD core + */ + + return ret; +} + +static void virt_cpsw_nuss_cleanup_ndev(struct virt_cpsw_common *common) +{ + if (common->ports.ndev) + unregister_netdev(common->ports.ndev); +} + +static bool virt_cpsw_dev_check(const struct net_device *ndev) +{ + struct virt_cpsw_common *common = virt_ndev_to_common(ndev); + + return ndev->netdev_ops == &virt_cpsw_nuss_netdev_ops && !common->mac_only; +} + +static int virt_cpsw_inetaddr_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + struct rpmsg_remotedev_eth_switch_ops *rdev_ops; + struct net_device *ndev = ifa->ifa_dev->dev; + struct virt_cpsw_common *common; + int ret = 0; + + if (!virt_cpsw_dev_check(ndev)) + goto out; + + common = virt_ndev_to_common(ndev); + rdev_ops = common->rdev_switch_ops; + switch (event) { + case NETDEV_UP: + ret = rdev_ops->register_ipv4(common->rdev, + ndev->dev_addr, + ifa->ifa_address); + if (ret) + dev_err(common->dev, "register_ipv4 rpmsg - fail %d\n", + ret); + dev_dbg(common->dev, "NETDEV_UP %pI4 %s\n", + &ifa->ifa_address, ifa->ifa_label); + break; + + case NETDEV_DOWN: + ret = rdev_ops->unregister_ipv4(common->rdev, + ifa->ifa_address); + if (ret) + dev_err(common->dev, "unregister_ipv4 rpmsg - fail %d\n", + ret); + dev_dbg(common->dev, "NETDEV_DOWN %pI4\n", &ifa->ifa_address); + break; + } + +out: + return notifier_from_errno(ret); +} + +static const struct of_device_id virt_cpsw_virt_of_mtable[] = { + { .compatible = "ti,j721e-cpsw-virt-mac", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, virt_cpsw_virt_of_mtable); + +static int virt_cpsw_nuss_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct virt_cpsw_common *common; + int ret; + + common = devm_kzalloc(dev, sizeof(struct virt_cpsw_common), GFP_KERNEL); + if (!common) + return -ENOMEM; + common->dev = dev; + + ret = of_property_read_string(dev->of_node, "ti,remote-name", + &common->rdev_name); + if (ret < 0) { + dev_info(dev, "remote-name is not set %d\n", ret); + return ret; + } + + common->rdev = rpmsg_remotedev_get_named_device(common->rdev_name); + if (!common->rdev) + return -EPROBE_DEFER; + if (IS_ERR(common->rdev)) { + ret = PTR_ERR(common->rdev); + return ret; + } + common->rdev_switch_ops = common->rdev->device.eth_switch.ops; + ret = devm_add_action_or_reset(dev, + (void(*)(void *))rpmsg_remotedev_put_device, + common->rdev); + if (ret) { + dev_err(dev, "add remotedev put device action fail:%d", ret); + return ret; + } + + ret = virt_cpsw_nuss_of(common); + if (ret) + return ret; + + ret = virt_cpsw_nuss_rdev_init(common); + if (ret) + return ret; + /* init tx channels */ + ret = virt_cpsw_nuss_init_tx_chns(common); + if (ret) + return ret; + ret = virt_cpsw_nuss_init_rx_chns(common); + if (ret) + return ret; + + if (common->tx_chns.irq == 0 || common->rx_chns.irq == 0) + return -ENXIO; + + dev_set_drvdata(dev, common); + __hw_addr_init(&common->mc_list); + INIT_WORK(&common->rx_mode_work, virt_cpsw_nuss_ndo_set_rx_mode_work); + common->cmd_wq = create_singlethread_workqueue("virt_cpsw"); + if (!common->cmd_wq) { + dev_err(dev, "failure requesting wq\n"); + return -ENOMEM; + } + + ret = virt_cpsw_nuss_init_ndev(common); + if (ret) + return ret; + + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (ret) { + dev_err(dev, "error setting dma mask: %d\n", ret); + goto unreg_ndev; + } + + ret = devm_request_irq(dev, common->tx_chns.irq, + virt_cpsw_nuss_tx_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), common); + if (ret) { + dev_err(dev, "failure requesting tx irq %u, %d\n", + common->tx_chns.irq, ret); + goto unreg_ndev; + } + + ret = devm_request_irq(dev, common->rx_chns.irq, + virt_cpsw_nuss_rx_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), common); + if (ret) { + dev_err(dev, "failure requesting rx irq %u, %d\n", + common->rx_chns.irq, ret); + goto unreg_ndev; + } + + if (!common->mac_only) { + common->virt_cpsw_inetaddr_nb.notifier_call = &virt_cpsw_inetaddr_event; + register_inetaddr_notifier(&common->virt_cpsw_inetaddr_nb); + } + + dev_info(common->dev, "virt_cpsw_nuss mac loaded\n"); + dev_info(dev, "rdev_features:%08X rdev_mtu:%d flow_id:%d tx_psil_dst_id:%04X mac_only:%d\n", + common->rdev_features, + common->rdev_mtu, + common->rdev_rx_flow_id, + common->rdev_tx_psil_dst_id, + common->mac_only_port); + dev_info(dev, "local_mac_addr:%pM rdev_mac_addr:%pM\n", + common->ports.local_mac_addr, + common->rdev_mac_addr); + + return 0; + +unreg_ndev: + virt_cpsw_nuss_cleanup_ndev(common); + return ret; +} + +static int virt_cpsw_nuss_remove(struct platform_device *pdev) +{ + struct virt_cpsw_common *common = platform_get_drvdata(pdev); + struct device *dev = common->dev; + int ret; + + if (!common->mac_only) + unregister_inetaddr_notifier(&common->virt_cpsw_inetaddr_nb); + + /* must unregister ndevs here because DD release_driver routine calls + * dma_deconfigure(dev) before devres_release_all(dev) + */ + virt_cpsw_nuss_cleanup_ndev(common); + if (common->mac_only) + destroy_workqueue(common->cmd_wq); + + ret = common->rdev_switch_ops->detach(common->rdev); + if (ret) + dev_err(dev, "rpmsg detach - fail %d\n", ret); + + return 0; +} + +static struct platform_driver virt_cpsw_nuss_driver = { + .driver = { + .name = VIRT_CPSW_DRV_NAME, + .of_match_table = virt_cpsw_virt_of_mtable, + }, + .probe = virt_cpsw_nuss_probe, + .remove = virt_cpsw_nuss_remove, +}; + +module_platform_driver(virt_cpsw_nuss_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Grygorii Strashko "); +MODULE_DESCRIPTION("TI J721E VIRT CPSW Ethernet mac driver"); diff -Naur --no-dereference a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig --- a/drivers/net/ethernet/ti/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -92,6 +92,7 @@ config TI_K3_AM65_CPSW_NUSS tristate "TI K3 AM654x/J721E CPSW Ethernet driver" depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER + select NET_DEVLINK select TI_DAVINCI_MDIO imply PHY_TI_GMII_SEL depends on TI_K3_AM65_CPTS || !TI_K3_AM65_CPTS @@ -105,6 +106,15 @@ To compile this driver as a module, choose M here: the module will be called ti-am65-cpsw-nuss. +config TI_K3_AM65_CPSW_SWITCHDEV + bool "TI K3 AM654x/J721E CPSW Switch mode support" + depends on TI_K3_AM65_CPSW_NUSS + depends on NET_SWITCHDEV + help + This enables switchdev support for TI K3 CPSWxG Ethernet + Switch. Enable this driver to support hardware switch support for AM65 + CPSW NUSS driver. + config TI_K3_AM65_CPTS tristate "TI K3 AM65x CPTS" depends on ARCH_K3 && OF @@ -171,4 +181,45 @@ help TI AR7 CPMAC Ethernet support +config TI_RDEV_ETH_SWITCH_VIRT_EMAC + tristate "TI Virtual Eth MAC driver" + depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER + help + Support for 1 Port Virtual Eth MAC driver over remotedev + R5F Eth Switch FW RPMSG protocol. + This is available starting with the J721E platform. + +config TI_PRUETH + tristate "TI PRU Ethernet EMAC driver" + depends on PRU_REMOTEPROC + depends on NET_SWITCHDEV + select TI_ICSS_IEP + imply PTP_1588_CLOCK + help + Some TI SoCs has Programmable Realtime Units (PRUs) cores which can + support Single or Dual Ethernet ports with help of firmware code running + on PRU cores. This driver supports remoteproc based communication to + PRU firmware to expose ethernet interface to Linux. + +config TI_ICSS_IEP + tristate "TI PRU ICSS IEP driver" + depends on TI_PRUSS + default TI_PRUSS + help + This enables support for the PRU-ICSS Industrial Ethernet Peripheral + within a PRU-ICSS subsystem present on various TI SoCs. + +config TI_ICSSG_PRUETH + tristate "TI Gigabit PRU Ethernet driver" + select TI_DAVINCI_MDIO + select NET_PTP_CLASSIFY + select TI_ICSS_IEP + imply PTP_1588_CLOCK + depends on PRU_REMOTEPROC + depends on NET_SWITCHDEV + depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER + help + Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem + This subsystem is available starting with the AM65 platform. + endif # NET_VENDOR_TI diff -Naur --no-dereference a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile --- a/drivers/net/ethernet/ti/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ethernet/ti/Makefile 2022-01-06 12:45:53.822318139 -0500 @@ -25,5 +25,17 @@ keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o cpsw_ale.o obj-$(CONFIG_TI_K3_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o -ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o cpsw_ale.o k3-cppi-desc-pool.o am65-cpsw-qos.o +ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o cpsw_ale.o k3-cppi-desc-pool.o am65-cpsw-qos.o am65-debugfs.o +ti-am65-cpsw-nuss-$(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV) += am65-cpsw-switchdev.o obj-$(CONFIG_TI_K3_AM65_CPTS) += am65-cpts.o + + +obj-$(CONFIG_TI_RDEV_ETH_SWITCH_VIRT_EMAC) += ti-j721e-cpsw-virt-mac.o +ti-j721e-cpsw-virt-mac-y := j721e-cpsw-virt-mac.o k3-cppi-desc-pool.o + +obj-$(CONFIG_TI_PRUETH) += prueth.o +prueth-y := prueth_core.o prueth_qos.o prueth_switch.o prueth_lre.o +obj-$(CONFIG_TI_ICSS_IEP) += icss_iep.o + +obj-$(CONFIG_TI_ICSSG_PRUETH) += icssg-prueth.o +icssg-prueth-y := icssg_prueth.o icssg_classifier.o icssg_ethtool.o icssg_queues.o icssg_config.o k3-cppi-desc-pool.o icssg_mii_cfg.o icssg_switchdev.o icssg_qos.o diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_core.c b/drivers/net/ethernet/ti/prueth_core.c --- a/drivers/net/ethernet/ti/prueth_core.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_core.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,3522 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* PRU ICSS Ethernet Driver + * + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + * Roger Quadros + * Andrew F. Davis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prueth.h" +#include "icss_mii_rt.h" +#include "icss_vlan_mcast_filter_mmap.h" +#include "prueth_lre.h" +#include "prueth_switch.h" +#include "icss_iep.h" + +#define PRUETH_MODULE_VERSION "0.2" +#define PRUETH_MODULE_DESCRIPTION "PRUSS Ethernet driver" + +#define OCMC_RAM_SIZE (SZ_64K - SZ_8K) +#define PRUETH_ETH_TYPE_OFFSET 12 +#define PRUETH_ETH_TYPE_UPPER_SHIFT 8 + +/* TX Minimum Inter packet gap */ +#define TX_MIN_IPG 0xb8 + +#define TX_START_DELAY 0x40 +#define TX_CLK_DELAY_100M 0x6 +#define TX_CLK_DELAY_10M 0 + +/* PRUSS_IEP_GLOBAL_CFG register definitions */ +#define PRUSS_IEP_GLOBAL_CFG 0 + +#define PRUSS_IEP_GLOBAL_CFG_CNT_ENABLE BIT(0) + +/* Netif debug messages possible */ +#define PRUETH_EMAC_DEBUG (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | \ + NETIF_MSG_TX_QUEUED | \ + NETIF_MSG_INTR | \ + NETIF_MSG_TX_DONE | \ + NETIF_MSG_RX_STATUS | \ + NETIF_MSG_PKTDATA | \ + NETIF_MSG_HW | \ + NETIF_MSG_WOL) + +static int debug_level = -1; +module_param(debug_level, int, 0444); +MODULE_PARM_DESC(debug_level, "PRUETH debug level (NETIF_MSG bits)"); + +/* ensure that order of PRUSS mem regions is same as enum prueth_mem */ +static enum pruss_mem pruss_mem_ids[] = { PRUSS_MEM_DRAM0, PRUSS_MEM_DRAM1, + PRUSS_MEM_SHRD_RAM2 }; + +static struct prueth_fw_offsets fw_offsets_v2_1 = { + .hash_mask = ICSS_LRE_V2_1_HASH_MASK, + .index_array_offset = ICSS_LRE_V2_1_INDEX_ARRAY_NT, + .bin_array_offset = ICSS_LRE_V2_1_BIN_ARRAY, + .nt_array_offset = ICSS_LRE_V2_1_NODE_TABLE_NEW, + .index_array_loc = ICSS_LRE_V2_1_INDEX_ARRAY_LOC, + .bin_array_loc = ICSS_LRE_V2_1_BIN_ARRAY_LOC, + .nt_array_loc = ICSS_LRE_V2_1_NODE_TABLE_LOC, + .index_array_max_entries = ICSS_LRE_V2_1_INDEX_TBL_MAX_ENTRIES, + .bin_array_max_entries = ICSS_LRE_V2_1_BIN_TBL_MAX_ENTRIES, + .nt_array_max_entries = ICSS_LRE_V2_1_NODE_TBL_MAX_ENTRIES, + .iep_wrap = 0xffffffff, +}; + +static void prueth_set_fw_offsets(struct prueth *prueth) +{ + /* Set VLAN/Multicast filter control and table offsets */ + if (PRUETH_IS_EMAC(prueth) || PRUETH_IS_SWITCH(prueth)) { + prueth->fw_offsets->vlan_ctrl_byte = + ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET; + prueth->fw_offsets->vlan_filter_tbl = + ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR; + + prueth->fw_offsets->mc_ctrl_byte = + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET; + prueth->fw_offsets->mc_filter_mask = + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET; + prueth->fw_offsets->mc_filter_tbl = + ICSS_EMAC_FW_MULTICAST_FILTER_TABLE; + + } else { + prueth->fw_offsets->vlan_ctrl_byte = + ICSS_LRE_FW_VLAN_FLTR_CTRL_BYTE; + prueth->fw_offsets->vlan_filter_tbl = + ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR; + + prueth->fw_offsets->mc_ctrl_byte = + ICSS_LRE_FW_MULTICAST_TABLE_SEARCH_OP_CONTROL_BIT; + prueth->fw_offsets->mc_filter_mask = + ICSS_LRE_FW_MULTICAST_FILTER_MASK; + prueth->fw_offsets->mc_filter_tbl = + ICSS_LRE_FW_MULTICAST_FILTER_TABLE; + + } +} + +static inline u32 prueth_read_reg(struct prueth *prueth, + enum prueth_mem region, + unsigned int reg) +{ + return readl_relaxed(prueth->mem[region].va + reg); +} + +static inline void prueth_write_reg(struct prueth *prueth, + enum prueth_mem region, + unsigned int reg, u32 val) +{ + writel_relaxed(val, prueth->mem[region].va + reg); +} + +static inline void prueth_ptp_ts_enable(struct prueth_emac *emac) +{ + void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + u8 val = 0; + + if (emac->ptp_tx_enable) { + /* Disable fw background task */ + val &= ~TIMESYNC_CTRL_BG_ENABLE; + /* Enable forced 2-step */ + val |= TIMESYNC_CTRL_FORCED_2STEP; + } + + writeb(val, sram + TIMESYNC_CTRL_VAR_OFFSET); + /* disable PTP forwarding for switch */ + if (PRUETH_IS_SWITCH(emac->prueth)) + writeb(1, sram + DISABLE_PTP_FRAME_FORWARDING_CTRL_OFFSET); +} + +static inline void prueth_ptp_tx_ts_enable(struct prueth_emac *emac, bool enable) +{ + emac->ptp_tx_enable = enable; + prueth_ptp_ts_enable(emac); +} + +static inline bool prueth_ptp_tx_ts_is_enabled(struct prueth_emac *emac) +{ + return !!emac->ptp_tx_enable; +} + +static inline void prueth_ptp_rx_ts_enable(struct prueth_emac *emac, bool enable) +{ + emac->ptp_rx_enable = enable; + prueth_ptp_ts_enable(emac); +} + +static inline bool prueth_ptp_rx_ts_is_enabled(struct prueth_emac *emac) +{ + return !!emac->ptp_rx_enable; +} + +static inline +void prueth_set_reg(struct prueth *prueth, enum prueth_mem region, + unsigned int reg, u32 mask, u32 set) +{ + u32 val; + + val = prueth_read_reg(prueth, region, reg); + val &= ~mask; + val |= (set & mask); + prueth_write_reg(prueth, region, reg, val); +} + +static const struct prueth_queue_info queue_infos[][NUM_QUEUES] = { + [PRUETH_PORT_QUEUE_HOST] = { + [PRUETH_QUEUE1] = { + P0_Q1_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET, + P0_Q1_BD_OFFSET, + P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P0_Q2_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 8, + P0_Q2_BD_OFFSET, + P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P0_Q3_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 16, + P0_Q3_BD_OFFSET, + P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P0_Q4_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 24, + P0_Q4_BD_OFFSET, + P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII0] = { + [PRUETH_QUEUE1] = { + P1_Q1_BUFFER_OFFSET, + P1_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q1_BD_OFFSET, + P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P1_Q2_BUFFER_OFFSET, + P1_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q2_BD_OFFSET, + P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P1_Q3_BUFFER_OFFSET, + P1_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q3_BD_OFFSET, + P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P1_Q4_BUFFER_OFFSET, + P1_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q4_BD_OFFSET, + P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII1] = { + [PRUETH_QUEUE1] = { + P2_Q1_BUFFER_OFFSET, + P2_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q1_BD_OFFSET, + P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P2_Q2_BUFFER_OFFSET, + P2_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q2_BD_OFFSET, + P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P2_Q3_BUFFER_OFFSET, + P2_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q3_BD_OFFSET, + P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P2_Q4_BUFFER_OFFSET, + P2_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q4_BD_OFFSET, + P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, +}; + +const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = { + [PRUETH_PORT_QUEUE_HOST] = { + { .rd_ptr = P0_Q1_BD_OFFSET, .wr_ptr = P0_Q1_BD_OFFSET, }, + { .rd_ptr = P0_Q2_BD_OFFSET, .wr_ptr = P0_Q2_BD_OFFSET, }, + { .rd_ptr = P0_Q3_BD_OFFSET, .wr_ptr = P0_Q3_BD_OFFSET, }, + { .rd_ptr = P0_Q4_BD_OFFSET, .wr_ptr = P0_Q4_BD_OFFSET, }, + }, + [PRUETH_PORT_QUEUE_MII0] = { + { .rd_ptr = P1_Q1_BD_OFFSET, .wr_ptr = P1_Q1_BD_OFFSET, }, + { .rd_ptr = P1_Q2_BD_OFFSET, .wr_ptr = P1_Q2_BD_OFFSET, }, + { .rd_ptr = P1_Q3_BD_OFFSET, .wr_ptr = P1_Q3_BD_OFFSET, }, + { .rd_ptr = P1_Q4_BD_OFFSET, .wr_ptr = P1_Q4_BD_OFFSET, }, + }, + [PRUETH_PORT_QUEUE_MII1] = { + { .rd_ptr = P2_Q1_BD_OFFSET, .wr_ptr = P2_Q1_BD_OFFSET, }, + { .rd_ptr = P2_Q2_BD_OFFSET, .wr_ptr = P2_Q2_BD_OFFSET, }, + { .rd_ptr = P2_Q3_BD_OFFSET, .wr_ptr = P2_Q3_BD_OFFSET, }, + { .rd_ptr = P2_Q4_BD_OFFSET, .wr_ptr = P2_Q4_BD_OFFSET, }, + } +}; + +static void prueth_hostconfig(struct prueth *prueth) +{ + void __iomem *sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + void __iomem *sram; + + /* queue size lookup table */ + sram = sram_base + HOST_QUEUE_SIZE_ADDR; + writew(HOST_QUEUE_1_SIZE, sram); + writew(HOST_QUEUE_2_SIZE, sram + 2); + writew(HOST_QUEUE_3_SIZE, sram + 4); + writew(HOST_QUEUE_4_SIZE, sram + 6); + + /* queue information table */ + sram = sram_base + HOST_Q1_RX_CONTEXT_OFFSET; + memcpy_toio(sram, queue_infos[PRUETH_PORT_QUEUE_HOST], + sizeof(queue_infos[PRUETH_PORT_QUEUE_HOST])); + + /* buffer offset table */ + sram = sram_base + HOST_QUEUE_OFFSET_ADDR; + writew(P0_Q1_BUFFER_OFFSET, sram); + writew(P0_Q2_BUFFER_OFFSET, sram + 2); + writew(P0_Q3_BUFFER_OFFSET, sram + 4); + writew(P0_Q4_BUFFER_OFFSET, sram + 6); + + /* buffer descriptor offset table*/ + sram = sram_base + HOST_QUEUE_DESCRIPTOR_OFFSET_ADDR; + writew(P0_Q1_BD_OFFSET, sram); + writew(P0_Q2_BD_OFFSET, sram + 2); + writew(P0_Q3_BD_OFFSET, sram + 4); + writew(P0_Q4_BD_OFFSET, sram + 6); + + /* queue table */ + sram = sram_base + HOST_QUEUE_DESC_OFFSET; + memcpy_toio(sram, queue_descs[PRUETH_PORT_QUEUE_HOST], + sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); +} + +#define prueth_mii_set(dir, port, mask, set) \ + regmap_update_bits(prueth->mii_rt, PRUSS_MII_RT_##dir##CFG##port, \ + PRUSS_MII_RT_##dir##CFG_##dir##_##mask, set) + +static void prueth_mii_init(struct prueth *prueth) +{ + /* Configuration of Port 0 Rx */ + prueth_mii_set(RX, 0, ENABLE, PRUSS_MII_RT_RXCFG_RX_ENABLE); + prueth_mii_set(RX, 0, DATA_RDY_MODE_DIS, + PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS); + prueth_mii_set(RX, 0, MUX_SEL, 0x0); + prueth_mii_set(RX, 0, L2_EN, PRUSS_MII_RT_RXCFG_RX_L2_EN); + prueth_mii_set(RX, 0, CUT_PREAMBLE, PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE); + prueth_mii_set(RX, 0, L2_EOF_SCLR_DIS, + PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS); + + /* Configuration of Port 0 Tx */ + prueth_mii_set(TX, 0, ENABLE, PRUSS_MII_RT_TXCFG_TX_ENABLE); + prueth_mii_set(TX, 0, AUTO_PREAMBLE, + PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE); + prueth_mii_set(TX, 0, 32_MODE_EN, PRUSS_MII_RT_TXCFG_TX_32_MODE_EN); + if (!PRUETH_IS_EMAC(prueth)) + prueth_mii_set(TX, 0, MUX_SEL, PRUSS_MII_RT_TXCFG_TX_MUX_SEL); + else + prueth_mii_set(TX, 0, MUX_SEL, 0x0); + prueth_mii_set(TX, 0, START_DELAY_MASK, + TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT); + prueth_mii_set(TX, 0, CLK_DELAY_MASK, + TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); + + /* Configuration of Port 1 Rx */ + prueth_mii_set(RX, 1, ENABLE, PRUSS_MII_RT_RXCFG_RX_ENABLE); + prueth_mii_set(RX, 1, + DATA_RDY_MODE_DIS, PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS); + prueth_mii_set(RX, 1, MUX_SEL, PRUSS_MII_RT_RXCFG_RX_MUX_SEL); + prueth_mii_set(RX, 1, L2_EN, PRUSS_MII_RT_RXCFG_RX_L2_EN); + prueth_mii_set(RX, 1, CUT_PREAMBLE, PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE); + prueth_mii_set(RX, 1, L2_EOF_SCLR_DIS, + PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS); + + /* Configuration of Port 1 Tx */ + prueth_mii_set(TX, 1, ENABLE, PRUSS_MII_RT_TXCFG_TX_ENABLE); + prueth_mii_set(TX, 1, AUTO_PREAMBLE, + PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE); + prueth_mii_set(TX, 1, 32_MODE_EN, PRUSS_MII_RT_TXCFG_TX_32_MODE_EN); + if (!PRUETH_IS_EMAC(prueth)) + prueth_mii_set(TX, 1, MUX_SEL, 0x0); + else + prueth_mii_set(TX, 1, MUX_SEL, PRUSS_MII_RT_TXCFG_TX_MUX_SEL); + prueth_mii_set(TX, 1, START_DELAY_MASK, + TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT); + prueth_mii_set(TX, 1, CLK_DELAY_MASK, + TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); + + /* Min frame length should be set to 64 to allow receive of standard + * Ethernet frames such as PTP, LLDP that will not have the tag/rct. + * Actual size written to register is size - 1 per TRM. This also + * includes CRC/FCS. + */ + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_RX_FRMS0, + PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK, + (PRUSS_MII_RT_RX_FRMS_MIN_FRM - 1) << + PRUSS_MII_RT_RX_FRMS_MIN_FRM_SHIFT); + + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_RX_FRMS1, + PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK, + (PRUSS_MII_RT_RX_FRMS_MIN_FRM - 1) << + PRUSS_MII_RT_RX_FRMS_MIN_FRM_SHIFT); + + /* For EMAC, set Max frame size to 1522 i.e size with VLAN and for + * HSR/PRP set it to 1528 i.e size with tag or rct. Actual size + * written to register is size - 1 as per TRM. Since driver + * support run time change of protocol, driver must overwrite + * the values based on Ethernet type. + */ + if (PRUETH_IS_LRE(prueth)) { + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_RX_FRMS0, + PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK, + (PRUSS_MII_RT_RX_FRMS_MAX_FRM_LRE - 1) << + PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT); + + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_RX_FRMS1, + PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK, + (PRUSS_MII_RT_RX_FRMS_MAX_FRM_LRE - 1) << + PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT); + } else { + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_RX_FRMS0, + PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK, + (PRUSS_MII_RT_RX_FRMS_MAX - 1) << + PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT); + + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_RX_FRMS1, + PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK, + (PRUSS_MII_RT_RX_FRMS_MAX - 1) << + PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT); + } +} + +static void prueth_clearmem(struct prueth *prueth, enum prueth_mem region) +{ + memset_io(prueth->mem[region].va, 0, prueth->mem[region].size); +} + +static void prueth_hostinit(struct prueth *prueth) +{ + /* Clear shared RAM */ + prueth_clearmem(prueth, PRUETH_MEM_SHARED_RAM); + + /* Clear OCMC RAM */ + prueth_clearmem(prueth, PRUETH_MEM_OCMC); + + /* Clear data RAMs */ + if (prueth->eth_node[PRUETH_MAC0]) + prueth_clearmem(prueth, PRUETH_MEM_DRAM0); + if (prueth->eth_node[PRUETH_MAC1]) + prueth_clearmem(prueth, PRUETH_MEM_DRAM1); + + /* Initialize host queues in shared RAM */ + if (!PRUETH_IS_EMAC(prueth)) + prueth_sw_hostconfig(prueth); + else + prueth_hostconfig(prueth); + + /* Configure MII_RT */ + prueth_mii_init(prueth); +} + +/* This function initialize the driver in EMAC or HSR or PRP mode + * based on eth_type + */ +static void prueth_init_ethernet_mode(struct prueth *prueth) +{ + prueth_set_fw_offsets(prueth); + prueth_hostinit(prueth); + if (PRUETH_IS_LRE(prueth)) + prueth_lre_config(prueth); +} + +static void prueth_port_enable(struct prueth_emac *emac, bool enable) +{ + void __iomem *port_ctrl, *vlan_ctrl; + struct prueth *prueth = emac->prueth; + u32 vlan_ctrl_offset = prueth->fw_offsets->vlan_ctrl_byte; + void __iomem *ram = prueth->mem[emac->dram].va; + + port_ctrl = ram + PORT_CONTROL_ADDR; + writeb(!!enable, port_ctrl); + + /* HSR/PRP firmware use a different memory and offset + * for VLAN filter control + */ + if (PRUETH_IS_LRE(prueth)) + ram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + vlan_ctrl = ram + vlan_ctrl_offset; + writeb(!!enable, vlan_ctrl); +} + +static int prueth_emac_config(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + + /* PRU needs local shared RAM address for C28 */ + u32 sharedramaddr = ICSS_LOCAL_SHARED_RAM; + + /* PRU needs real global OCMC address for C30*/ + u32 ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + void __iomem *dram_base; + void __iomem *mac_addr; + void __iomem *dram; + + /* Clear data RAM */ + prueth_clearmem(prueth, emac->dram); + + dram_base = prueth->mem[emac->dram].va; + + /* setup mac address */ + mac_addr = dram_base + PORT_MAC_ADDR; + memcpy_toio(mac_addr, emac->mac_addr, 6); + + /* queue information table */ + dram = dram_base + TX_CONTEXT_Q1_OFFSET_ADDR; + memcpy_toio(dram, queue_infos[emac->port_id], + sizeof(queue_infos[emac->port_id])); + + /* queue table */ + dram = dram_base + PORT_QUEUE_DESC_OFFSET; + memcpy_toio(dram, queue_descs[emac->port_id], + sizeof(queue_descs[emac->port_id])); + + emac->rx_queue_descs = sram + HOST_QUEUE_DESC_OFFSET; + emac->tx_queue_descs = dram; + + /* Set in constant table C28 of PRU0 to ICSS Shared memory */ + pru_rproc_set_ctable(emac->pru, PRU_C28, sharedramaddr); + + /* Set in constant table C30 of PRU0 to OCMC memory */ + pru_rproc_set_ctable(emac->pru, PRU_C30, ocmcaddr); + + return 0; +} + +/* update phy/port status information for firmware */ +static void emac_update_phystatus(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + enum prueth_mem region; + u32 phy_speed, port_status = 0; + u8 delay; + + region = emac->dram; + phy_speed = emac->speed; + prueth_write_reg(prueth, region, PHY_SPEED_OFFSET, phy_speed); + + if (phy_speed == SPEED_10) + delay = TX_CLK_DELAY_10M; + else + delay = TX_CLK_DELAY_100M; + + if (emac->port_id) { + prueth_mii_set(TX, 1, CLK_DELAY_MASK, + delay << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); + } else { + prueth_mii_set(TX, 0, CLK_DELAY_MASK, + delay << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); + } + + if (emac->duplex == DUPLEX_HALF) + port_status |= PORT_IS_HD_MASK; + if (emac->link) + port_status |= PORT_LINK_MASK; + writeb(port_status, prueth->mem[region].va + PORT_STATUS_OFFSET); +} + +/* called back by PHY layer if there is change in link state of hw port*/ +static void emac_adjust_link(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct phy_device *phydev = emac->phydev; + unsigned long flags; + bool new_state = false; + + spin_lock_irqsave(&emac->lock, flags); + + if (phydev->link) { + /* check the mode of operation - full/half duplex */ + if (phydev->duplex != emac->duplex) { + new_state = true; + emac->duplex = phydev->duplex; + } + if (phydev->speed != emac->speed) { + new_state = true; + emac->speed = phydev->speed; + } + if (!emac->link) { + new_state = true; + emac->link = 1; + } + } else if (emac->link) { + new_state = true; + emac->link = 0; + /* defaults for no link */ + + /* f/w only support 10 or 100 */ + emac->speed = SPEED_100; + + /* half duplex may not be supported by f/w */ + emac->duplex = DUPLEX_FULL; + } + + emac_update_phystatus(emac); + + if (new_state) + phy_print_status(phydev); + + if (emac->link) { + /* link ON */ + netif_carrier_on(ndev); + + /* reactivate the transmit queue if it is stopped */ + if (netif_running(ndev) && netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } else { + /* link OFF */ + netif_carrier_off(ndev); + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + } + + spin_unlock_irqrestore(&emac->lock, flags); +} + +/** + * emac_tx_hardirq - EMAC Tx interrupt handler + * @irq: interrupt number + * @dev_id: pointer to net_device + * + * This is called whenever a packet has finished being transmitted, this clears + * up hardware buffer space, our only task is to re-enable the transmit queue + * if it was previously disabled due to hardware queue being full + * + * Returns interrupt handled condition + */ +static irqreturn_t emac_tx_hardirq(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + + if (unlikely(netif_queue_stopped(ndev))) + netif_wake_queue(ndev); + + return IRQ_HANDLED; +} + +static u8 prueth_ptp_ts_event_type(struct sk_buff *skb, u8 *ptp_msgtype) +{ + unsigned int ptp_class = ptp_classify_raw(skb); + struct ptp_header *hdr; + u8 msgtype, event_type; + + if (ptp_class == PTP_CLASS_NONE) + return PRUETH_PTP_TS_EVENTS; + + hdr = ptp_parse_header(skb, ptp_class); + if (!hdr) + return PRUETH_PTP_TS_EVENTS; + + msgtype = ptp_get_msgtype(hdr, ptp_class); + /* Treat E2E Delay Req/Resp messages sane as P2P peer delay req/resp + * in driver here since firmware stores timestamps in the same memory + * location for either (since they cannot operate simultaneously + * anyway) + */ + switch (msgtype) { + case PTP_MSGTYPE_SYNC: + event_type = PRUETH_PTP_SYNC; + break; + case PTP_MSGTYPE_DELAY_REQ: + case PTP_MSGTYPE_PDELAY_REQ: + event_type = PRUETH_PTP_DLY_REQ; + break; + /* TODO: Check why PTP_MSGTYPE_DELAY_RESP needs timestamp + * and need for it. + */ + case 0x9: + case PTP_MSGTYPE_PDELAY_RESP: + event_type = PRUETH_PTP_DLY_RESP; + break; + default: + event_type = PRUETH_PTP_TS_EVENTS; + } + + if (ptp_msgtype) + *ptp_msgtype = msgtype; + + return event_type; +} + +static void prueth_ptp_tx_ts_reset(struct prueth_emac *emac, u8 event) +{ + void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + u32 ts_notify_offs, ts_offs; + + ts_offs = prueth_tx_ts_offs_get(emac->port_id - 1, event); + ts_notify_offs = prueth_tx_ts_notify_offs_get(emac->port_id - 1, event); + + writeb(0, sram + ts_notify_offs); + memset_io(sram + ts_offs, 0, sizeof(u64)); +} + +static int prueth_ptp_tx_ts_enqueue(struct prueth_emac *emac, struct sk_buff *skb) +{ + struct skb_redundant_info *sred = skb_redinfo(skb); + u8 event, changed = 0; + unsigned long flags; + + if (skb_vlan_tagged(skb)) { + __skb_pull(skb, VLAN_HLEN); + changed += VLAN_HLEN; + } + + if (sred && sred->ethertype == ETH_P_HSR) { + __skb_pull(skb, ICSS_LRE_TAG_RCT_SIZE); + changed += ICSS_LRE_TAG_RCT_SIZE; + } + + skb_reset_mac_header(skb); + event = prueth_ptp_ts_event_type(skb, NULL); + __skb_push(skb, changed); + if (event == PRUETH_PTP_TS_EVENTS) { + netdev_err(emac->ndev, "invalid PTP event\n"); + return -EINVAL; + } + + spin_lock_irqsave(&emac->ptp_skb_lock, flags); + if (emac->ptp_skb[event]) { + dev_consume_skb_any(emac->ptp_skb[event]); + prueth_ptp_tx_ts_reset(emac, event); + netdev_warn(emac->ndev, "Dropped event waiting for tx ts.\n"); + } + + skb_get(skb); + emac->ptp_skb[event] = skb; + spin_unlock_irqrestore(&emac->ptp_skb_lock, flags); + + return 0; +} + +irqreturn_t prueth_ptp_tx_irq_handle(int irq, void *dev) +{ + struct net_device *ndev = (struct net_device *)dev; + struct prueth_emac *emac = netdev_priv(ndev); + + if (unlikely(netif_queue_stopped(ndev))) + netif_wake_queue(ndev); + + if (prueth_ptp_tx_ts_is_enabled(emac)) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static u64 prueth_ptp_ts_get(struct prueth_emac *emac, u32 ts_offs) +{ + void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + u64 cycles; + + memcpy_fromio(&cycles, sram + ts_offs, sizeof(cycles)); + memset_io(sram + ts_offs, 0, sizeof(cycles)); + + return cycles; +} + +static void prueth_ptp_tx_ts_get(struct prueth_emac *emac, u8 event) +{ + struct skb_shared_hwtstamps *red_ssh; + struct skb_shared_hwtstamps ssh; + struct sk_buff *skb; + unsigned long flags; + bool ct_ts = false; + u64 ns; + + /* get the msg from list */ + spin_lock_irqsave(&emac->ptp_skb_lock, flags); + skb = emac->ptp_skb[event]; + emac->ptp_skb[event] = NULL; + if (!skb) { + /* In case of HSR, tx timestamp may be generated by + * cut-through packets such as SYNC, pass this ts in skb redinfo. + */ + skb = emac->ptp_ct_skb[event]; + emac->ptp_ct_skb[event] = NULL; + ct_ts = true; + } + spin_unlock_irqrestore(&emac->ptp_skb_lock, flags); + if (!skb) { + /* TS for cut throguh packet might have already be read by emac_rx_packet() + * So ignore this interrupt for HSR. + */ + if (!PRUETH_IS_HSR(emac->prueth)) + netdev_err(emac->ndev, "no tx msg %u found waiting for ts\n", event); + return; + } + + /* get timestamp */ + ns = prueth_ptp_ts_get(emac, + prueth_tx_ts_offs_get(emac->port_id - 1, event)); + if (ct_ts) { + /* Save the cut-through tx ts in skb redinfo. */ + red_ssh = skb_redinfo_hwtstamps(skb); + memset(red_ssh, 0, sizeof(*red_ssh)); + red_ssh->hwtstamp = ns_to_ktime(ns); + skb->protocol = eth_type_trans(skb, emac->ndev); + local_bh_disable(); + netif_receive_skb(skb); + local_bh_enable(); + } else { + memset(&ssh, 0, sizeof(ssh)); + ssh.hwtstamp = ns_to_ktime(ns); + skb_tstamp_tx(skb, &ssh); + dev_consume_skb_any(skb); + } +} + +irqreturn_t prueth_ptp_tx_irq_work(int irq, void *dev) +{ + struct prueth_emac *emac = netdev_priv(dev); + u32 ts_notify_offs, ts_notify_mask, i; + void __iomem *sram; + + /* get and reset the ts notifications */ + sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + for (i = 0; i < PRUETH_PTP_TS_EVENTS; i++) { + ts_notify_offs = prueth_tx_ts_notify_offs_get(emac->port_id - 1, + i); + memcpy_fromio(&ts_notify_mask, sram + ts_notify_offs, + PRUETH_PTP_TS_NOTIFY_SIZE); + memset_io(sram + ts_notify_offs, 0, PRUETH_PTP_TS_NOTIFY_SIZE); + + if (ts_notify_mask & PRUETH_PTP_TS_NOTIFY_MASK) + prueth_ptp_tx_ts_get(emac, i); + } + + return IRQ_HANDLED; +} + +/** + * prueth_tx_enqueue - queue a packet to firmware for transmission + * + * @emac: EMAC data structure + * @skb: packet data buffer + * @queue_id: priority queue id + */ +static int prueth_tx_enqueue(struct prueth_emac *emac, struct sk_buff *skb, + enum prueth_queue_id queue_id) +{ + struct net_device *ndev = emac->ndev; + struct prueth *prueth = emac->prueth; + int pktlen; + struct prueth_queue_desc __iomem *queue_desc; + const struct prueth_queue_info *txqueue; + u16 bd_rd_ptr, bd_wr_ptr, update_wr_ptr; + int write_block, read_block, free_blocks, update_block, pkt_block_size; + unsigned int buffer_desc_count; + bool buffer_wrapped = false; + void *src_addr; + void *dst_addr; + + /* OCMC RAM is not cached and write order is not important */ + void *ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + void __iomem *dram; + u32 wr_buf_desc; + int txport = emac->tx_port_queue; /* which port to tx: MII0 or MII1 */ + + if (!PRUETH_IS_EMAC(prueth)) + dram = prueth->mem[PRUETH_MEM_DRAM1].va; + else + dram = emac->prueth->mem[emac->dram].va; + + if (eth_skb_pad(skb)) { + if (netif_msg_tx_err(emac) && net_ratelimit()) + netdev_err(ndev, "packet pad failed"); + return -ENOMEM; + } + src_addr = skb->data; + pktlen = skb->len; + + /* Get the tx queue */ + queue_desc = emac->tx_queue_descs + queue_id; + if (!PRUETH_IS_EMAC(prueth)) + txqueue = &sw_queue_infos[txport][queue_id]; + else + txqueue = &queue_infos[txport][queue_id]; + buffer_desc_count = txqueue->buffer_desc_end - + txqueue->buffer_desc_offset; + buffer_desc_count /= BD_SIZE; + buffer_desc_count++; + + bd_rd_ptr = readw(&queue_desc->rd_ptr); + bd_wr_ptr = readw(&queue_desc->wr_ptr); + + /* the PRU firmware deals mostly in pointers already + * offset into ram, we would like to deal in indexes + * within the queue we are working with for code + * simplicity, calculate this here + */ + write_block = (bd_wr_ptr - txqueue->buffer_desc_offset) / BD_SIZE; + read_block = (bd_rd_ptr - txqueue->buffer_desc_offset) / BD_SIZE; + if (write_block > read_block) { + free_blocks = buffer_desc_count - write_block; + free_blocks += read_block; + } else if (write_block < read_block) { + free_blocks = read_block - write_block; + } else { /* they are all free */ + free_blocks = buffer_desc_count; + } + pkt_block_size = DIV_ROUND_UP(pktlen, ICSS_BLOCK_SIZE); + if (pkt_block_size > free_blocks) /* out of queue space */ + return -ENOBUFS; + + /* calculate end BD address post write */ + update_block = write_block + pkt_block_size; + + /* Check for wrap around */ + if (update_block >= buffer_desc_count) { + update_block %= buffer_desc_count; + buffer_wrapped = true; + } + + dst_addr = ocmc_ram + txqueue->buffer_offset + + (write_block * ICSS_BLOCK_SIZE); + + /* Copy the data from socket buffer(DRAM) to PRU buffers(OCMC) */ + if (buffer_wrapped) { /* wrapped around buffer */ + int bytes = (buffer_desc_count - write_block) * ICSS_BLOCK_SIZE; + int remaining; + + /* bytes is integral multiple of ICSS_BLOCK_SIZE but + * entire packet may have fit within the last BD + * if pkt_info.length is not integral multiple of + * ICSS_BLOCK_SIZE + */ + if (pktlen < bytes) + bytes = pktlen; + + /* copy non-wrapped part */ + memcpy(dst_addr, src_addr, bytes); + + /* copy wrapped part */ + src_addr += bytes; + remaining = pktlen - bytes; + dst_addr = ocmc_ram + txqueue->buffer_offset; + memcpy(dst_addr, src_addr, remaining); + } else { + memcpy(dst_addr, src_addr, pktlen); + } + + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + prueth_ptp_tx_ts_is_enabled(emac)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + prueth_ptp_tx_ts_enqueue(emac, skb); + } + + /* update first buffer descriptor */ + wr_buf_desc = (pktlen << PRUETH_BD_LENGTH_SHIFT) & PRUETH_BD_LENGTH_MASK; + if (PRUETH_IS_HSR(prueth)) + wr_buf_desc |= BIT(PRUETH_BD_HSR_FRAME_SHIFT); + + if (!PRUETH_IS_EMAC(prueth)) + writel(wr_buf_desc, sram + bd_wr_ptr); + else + writel(wr_buf_desc, dram + bd_wr_ptr); + + /* update the write pointer in this queue descriptor, the firmware + * polls for this change so this will signal the start of transmission + */ + update_wr_ptr = txqueue->buffer_desc_offset + (update_block * BD_SIZE); + writew(update_wr_ptr, &queue_desc->wr_ptr); + + return 0; +} + +void parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, + struct prueth_packet_info *pkt_info) +{ + /* For HSR, start_offset indicates Tag is not present and actual + * data starts at an offset of 6 bytes from start of the buffer. + * For example, for Supervisor frame start_offset is set, but for + * data frame it is reset. For PRP, start_offset indicate if RCT + * is present in the data or not. i.e in this case, depending upon + * LRE_TRANSPARENT_RECEPTION state RCT is to be stripped or not + * before passing data to upper layer. Software adjust the skb->len + * accordingly. TODO Support for LRE_TRANSPARENT_RECEPTION set to + * passRCT is TBD. + */ + if (PRUETH_IS_LRE(prueth)) + pkt_info->start_offset = !!(buffer_descriptor & + PRUETH_BD_START_FLAG_MASK); + else + pkt_info->start_offset = false; + + pkt_info->shadow = !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK); + pkt_info->port = (buffer_descriptor & PRUETH_BD_PORT_MASK) >> + PRUETH_BD_PORT_SHIFT; + pkt_info->length = (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >> + PRUETH_BD_LENGTH_SHIFT; + pkt_info->broadcast = !!(buffer_descriptor & PRUETH_BD_BROADCAST_MASK); + pkt_info->error = !!(buffer_descriptor & PRUETH_BD_ERROR_MASK); + if (PRUETH_IS_LRE(prueth)) + pkt_info->sv_frame = !!(buffer_descriptor & + PRUETH_BD_SUP_HSR_FRAME_MASK); + else + pkt_info->sv_frame = false; + pkt_info->lookup_success = !!(buffer_descriptor & + PRUETH_BD_LOOKUP_SUCCESS_MASK); + pkt_info->flood = !!(buffer_descriptor & PRUETH_BD_SW_FLOOD_MASK); + pkt_info->timestamp = !!(buffer_descriptor & PRUETH_BD_TIMESTAMP_MASK); +} + +static int prueth_hsr_ptp_ct_tx_ts_enqueue(struct prueth_emac *emac, struct sk_buff *skb, u16 type) +{ + struct prueth_emac *other_emac = emac->prueth->emac[other_port_id(emac->port_id) - 1]; + struct skb_shared_hwtstamps *red_ssh; + unsigned long flags; + u8 ptp_type, event; + int changed = 0; + u64 ns; + + if (type == ETH_P_8021Q) { + __skb_pull(skb, VLAN_HLEN); + changed += VLAN_HLEN; + } + + __skb_pull(skb, ICSS_LRE_TAG_RCT_SIZE); + changed += ICSS_LRE_TAG_RCT_SIZE; + + skb_reset_mac_header(skb); + event = prueth_ptp_ts_event_type(skb, &ptp_type); + + __skb_push(skb, changed); + + /* Store skbs for only cut through packets */ + if (event == PRUETH_PTP_TS_EVENTS || + (ptp_type != PTP_MSGTYPE_SYNC && + ptp_type != PTP_MSGTYPE_DELAY_REQ)) + return 0; + + /* cut through packet might have already be forwarded before the rx packet has reached + * the host. In this case tx irq handler ignores the interrupt as there is no skb stored. + * So check if ts is already available before storing the skb. + */ + ns = prueth_ptp_ts_get(other_emac, prueth_tx_ts_offs_get(other_emac->port_id - 1, event)); + if (ns || !other_emac->link) { + struct skb_shared_hwtstamps *ssh1; + + ssh1 = skb_hwtstamps(skb); + /* Save the cut-through tx ts in skb redinfo. */ + red_ssh = skb_redinfo_hwtstamps(skb); + memset(red_ssh, 0, sizeof(*red_ssh)); + red_ssh->hwtstamp = ns_to_ktime(ns); + return 0; + } + + /* Store the skb so that tx irq handler will populate the ts */ + spin_lock_irqsave(&other_emac->ptp_skb_lock, flags); + if (other_emac->ptp_ct_skb[event]) { + netdev_warn(other_emac->ndev, "Dropped cut through event waiting for tx ts.\n"); + dev_consume_skb_any(other_emac->ptp_ct_skb[event]); + prueth_ptp_tx_ts_reset(other_emac, event); + } + + other_emac->ptp_ct_skb[event] = skb; + spin_unlock_irqrestore(&other_emac->ptp_skb_lock, flags); + + return -EAGAIN; +} + +/* get packet from queue + * negative for error + */ +int emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, + struct prueth_packet_info *pkt_info, + const struct prueth_queue_info *rxqueue) +{ + struct net_device *ndev = emac->ndev; + struct prueth *prueth = emac->prueth; + int read_block, update_block, pkt_block_size; + bool buffer_wrapped = false, prp_rct = false; + unsigned int buffer_desc_count; + struct sk_buff *skb; + void *src_addr; + void *dst_addr; + u64 ts; + + void *nt_dst_addr; + u8 macid[6]; + /* OCMC RAM is not cached and read order is not important */ + void *ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; + struct skb_shared_hwtstamps *ssh; + unsigned int actual_pkt_len; + u16 start_offset = 0, type; + u8 offset = 0, *ptr; + int ret; + + if (PRUETH_IS_HSR(prueth)) + start_offset = (pkt_info->start_offset ? + ICSS_LRE_TAG_RCT_SIZE : 0); + else if (PRUETH_IS_PRP(prueth) && pkt_info->start_offset) + prp_rct = true; + + /* the PRU firmware deals mostly in pointers already + * offset into ram, we would like to deal in indexes + * within the queue we are working with for code + * simplicity, calculate this here + */ + buffer_desc_count = rxqueue->buffer_desc_end - + rxqueue->buffer_desc_offset; + buffer_desc_count /= BD_SIZE; + buffer_desc_count++; + read_block = (*bd_rd_ptr - rxqueue->buffer_desc_offset) / BD_SIZE; + pkt_block_size = DIV_ROUND_UP(pkt_info->length, ICSS_BLOCK_SIZE); + if (pkt_info->timestamp) + pkt_block_size++; + + /* calculate end BD address post read */ + update_block = read_block + pkt_block_size; + + /* Check for wrap around */ + if (update_block >= buffer_desc_count) { + update_block %= buffer_desc_count; + if (update_block) + buffer_wrapped = true; + } + + /* calculate new pointer in ram */ + *bd_rd_ptr = rxqueue->buffer_desc_offset + (update_block * BD_SIZE); + + /* Pkt len w/ HSR tag removed, If applicable */ + actual_pkt_len = pkt_info->length - start_offset; + + /* Allocate a socket buffer for this packet */ + skb = netdev_alloc_skb_ip_align(ndev, actual_pkt_len); + if (!skb) { + if (netif_msg_rx_err(emac) && net_ratelimit()) + netdev_err(ndev, "failed rx buffer alloc\n"); + return -ENOMEM; + } + dst_addr = skb->data; + nt_dst_addr = dst_addr; + + /* Get the start address of the first buffer from + * the read buffer description + */ + if (pkt_info->shadow) { + src_addr = ocmc_ram + P0_COL_BUFFER_OFFSET; + } else { + src_addr = ocmc_ram + rxqueue->buffer_offset + + (read_block * ICSS_BLOCK_SIZE); + } + src_addr += start_offset; + + /* Copy the data from PRU buffers(OCMC) to socket buffer(DRAM) */ + if (buffer_wrapped) { /* wrapped around buffer */ + int bytes = (buffer_desc_count - read_block) * ICSS_BLOCK_SIZE; + int remaining; + + /* bytes is integral multiple of ICSS_BLOCK_SIZE but + * entire packet may have fit within the last BD + * if pkt_info.length is not integral multiple of + * ICSS_BLOCK_SIZE + */ + if (pkt_info->length < bytes) + bytes = pkt_info->length; + + /* If applicable, account for the HSR tag removed */ + bytes -= start_offset; + + /* copy non-wrapped part */ + memcpy(dst_addr, src_addr, bytes); + + /* copy wrapped part */ + dst_addr += bytes; + remaining = actual_pkt_len - bytes; + if (pkt_info->shadow) + src_addr += bytes; + else + src_addr = ocmc_ram + rxqueue->buffer_offset; + memcpy(dst_addr, src_addr, remaining); + src_addr += remaining; + } else { + memcpy(dst_addr, src_addr, actual_pkt_len); + src_addr += actual_pkt_len; + } + + if (pkt_info->timestamp) { + src_addr = (void *)roundup((uintptr_t)src_addr, ICSS_BLOCK_SIZE); + dst_addr = &ts; + memcpy(dst_addr, src_addr, sizeof(ts)); + } + + if (PRUETH_IS_SWITCH(emac->prueth)) { + skb->offload_fwd_mark = emac->offload_fwd_mark; + if (!pkt_info->lookup_success) + prueth_sw_learn_fdb(emac, skb->data + ETH_ALEN); + } + + /* Check if VLAN tag is present since SV payload location will change + * based on that + */ + if (PRUETH_IS_LRE(prueth)) { + ptr = nt_dst_addr + PRUETH_ETH_TYPE_OFFSET; + type = (*ptr++) << PRUETH_ETH_TYPE_UPPER_SHIFT; + type |= *ptr++; + if (type == ETH_P_8021Q) + offset = 4; + } + + /* TODO. The check for FW_REV_V1_0 is a workaround since + * lookup of MAC address in Node table by this version of firmware + * is not reliable. Once this issue is fixed in firmware, this driver + * check has to be removed. + */ + if (PRUETH_IS_LRE(prueth) && !pkt_info->lookup_success) { + if (PRUETH_IS_PRP(prueth)) { + memcpy(macid, + ((pkt_info->sv_frame) ? + nt_dst_addr + LRE_SV_FRAME_OFFSET + offset : + nt_dst_addr + ICSS_LRE_TAG_RCT_SIZE), + ICSS_LRE_TAG_RCT_SIZE); + + prueth_lre_nt_insert(prueth, macid, emac->port_id, + pkt_info->sv_frame, + LRE_PROTO_PRP); + + } else if (pkt_info->sv_frame) { + memcpy(macid, + nt_dst_addr + LRE_SV_FRAME_OFFSET + offset, + ICSS_LRE_TAG_RCT_SIZE); + prueth_lre_nt_insert(prueth, macid, emac->port_id, + pkt_info->sv_frame, + LRE_PROTO_HSR); + } + } + + /* For PRP, firmware always send us RCT. So skip Tag if + * prp_tr_mode is IEC62439_3_TR_REMOVE_RCT + */ + if (prp_rct && prueth->prp_tr_mode == IEC62439_3_TR_REMOVE_RCT) + actual_pkt_len -= ICSS_LRE_TAG_RCT_SIZE; + + if (!pkt_info->sv_frame) { + skb_put(skb, actual_pkt_len); + + if (prueth_ptp_rx_ts_is_enabled(emac) && pkt_info->timestamp) { + ssh = skb_hwtstamps(skb); + memset(ssh, 0, sizeof(*ssh)); + ssh->hwtstamp = ns_to_ktime(ts); + if (PRUETH_IS_HSR(prueth)) { + ret = prueth_hsr_ptp_ct_tx_ts_enqueue(emac, skb, type); + if (ret == -EAGAIN) + goto out; + } + } + + /* send packet up the stack */ + skb->protocol = eth_type_trans(skb, ndev); + local_bh_disable(); + netif_receive_skb(skb); + local_bh_enable(); + } else { + dev_kfree_skb_any(skb); + } +out: + + /* update stats */ + ndev->stats.rx_bytes += actual_pkt_len; + ndev->stats.rx_packets++; + + return 0; +} + +/** + * emac_rx_thread - EMAC Rx interrupt thread handler + * @irq: interrupt number + * @dev_id: pointer to net_device + * + * EMAC Rx Interrupt thread handler - function to process the rx frames in a + * irq thread function. There is only limited buffer at the ingress to + * queue the frames. As the frames are to be emptied as quickly as + * possible to avoid overflow, irq thread is necessary. Current implementation + * based on NAPI poll results in packet loss due to overflow at + * the ingress queues. Industrial use case requires loss free packet + * processing. Tests shows that with threaded irq based processing, + * no overflow happens when receiving at ~92Mbps for MTU sized frames and thus + * meet the requirement for industrial use case. + * + * Returns interrupt handled condition + */ +static irqreturn_t emac_rx_thread(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int start_queue, end_queue; + struct prueth_queue_desc __iomem *queue_desc; + const struct prueth_queue_info *rxqueue; + u8 overflow_cnt; + u16 bd_rd_ptr, bd_wr_ptr, update_rd_ptr; + u32 rd_buf_desc; + void __iomem *shared_ram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + struct prueth_packet_info pkt_info; + struct net_device_stats *ndevstats = &emac->ndev->stats; + int i, ret, used = 0; + struct prueth_emac *other_emac; + + other_emac = prueth->emac[other_port_id(emac->port_id) - 1]; + + if (PRUETH_IS_SWITCH(prueth)) { + start_queue = PRUETH_QUEUE1; + end_queue = PRUETH_QUEUE4; + } else { + start_queue = emac->rx_queue_start; + end_queue = emac->rx_queue_end; + } + +retry: + /* search host queues for packets */ + for (i = start_queue; i <= end_queue; i++) { + queue_desc = emac->rx_queue_descs + i; + if (PRUETH_IS_SWITCH(emac->prueth)) + rxqueue = &sw_queue_infos[PRUETH_PORT_HOST][i]; + else + rxqueue = &queue_infos[PRUETH_PORT_HOST][i]; + + overflow_cnt = readb(&queue_desc->overflow_cnt); + if (overflow_cnt > 0) { + emac->ndev->stats.rx_over_errors += overflow_cnt; + + /* reset to zero */ + writeb(0, &queue_desc->overflow_cnt); + } + + bd_rd_ptr = readw(&queue_desc->rd_ptr); + bd_wr_ptr = readw(&queue_desc->wr_ptr); + + /* while packets are available in this queue */ + while (bd_rd_ptr != bd_wr_ptr) { + /* get packet info from the read buffer descriptor */ + rd_buf_desc = readl(shared_ram + bd_rd_ptr); + parse_packet_info(prueth, rd_buf_desc, &pkt_info); + + if (pkt_info.length <= 0) { + /* a packet length of zero will cause us to + * never move the read pointer ahead, locking + * the driver, so we manually have to move it + * to the write pointer, discarding all + * remaining packets in this queue. This should + * never happen. + */ + update_rd_ptr = bd_wr_ptr; + ndevstats->rx_length_errors++; + } else if (pkt_info.length > EMAC_MAX_PKTLEN) { + /* if the packet is too large we skip it but we + * still need to move the read pointer ahead + * and assume something is wrong with the read + * pointer as the firmware should be filtering + * these packets + */ + update_rd_ptr = bd_wr_ptr; + ndevstats->rx_length_errors++; + } else { + update_rd_ptr = bd_rd_ptr; + if (PRUETH_IS_SWITCH(emac->prueth)) { + if (pkt_info.port == + other_emac->port_id) { + emac = other_emac; + } + } + + ret = emac_rx_packet(emac, &update_rd_ptr, + &pkt_info, rxqueue); + if (ret) + return IRQ_HANDLED; + used++; + } + + /* after reading the buffer descriptor we clear it + * to prevent improperly moved read pointer errors + * from simply looking like old packets. + */ + writel(0, shared_ram + bd_rd_ptr); + + /* update read pointer in queue descriptor */ + writew(update_rd_ptr, &queue_desc->rd_ptr); + bd_rd_ptr = update_rd_ptr; + } + } + + if (used) { + used = 0; + goto retry; + } + + return IRQ_HANDLED; +} + +/* get statistics maintained by the PRU firmware into @pstats */ +static void emac_get_stats(struct prueth_emac *emac, + struct port_statistics *pstats) +{ + void __iomem *dram; + + dram = emac->prueth->mem[emac->dram].va; + memcpy_fromio(pstats, dram + STATISTICS_OFFSET, STAT_SIZE); + + pstats->vlan_dropped = + readl(dram + ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET); + pstats->multicast_dropped = + readl(dram + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET); +} + +/* set PRU firmware statistics */ +static void emac_set_stats(struct prueth_emac *emac, + struct port_statistics *pstats) +{ + void __iomem *dram; + + dram = emac->prueth->mem[emac->dram].va; + memcpy_toio(dram + STATISTICS_OFFSET, pstats, STAT_SIZE); + + writel(pstats->vlan_dropped, dram + + ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET); + writel(pstats->multicast_dropped, dram + + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET); +} + +static int emac_set_boot_pru(struct prueth_emac *emac, struct net_device *ndev) +{ + const struct prueth_firmware *pru_firmwares; + struct prueth *prueth = emac->prueth; + const char *fw_name; + int ret = 0; + + pru_firmwares = &prueth->fw_data->fw_pru[emac->port_id - 1]; + fw_name = pru_firmwares->fw_name[prueth->eth_type]; + if (!fw_name) { + netdev_err(ndev, "eth_type %d not supported\n", + prueth->eth_type); + return -ENODEV; + } + + ret = rproc_set_firmware(emac->pru, fw_name); + if (ret) { + netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n", + fw_name, ret); + return ret; + } + + ret = rproc_boot(emac->pru); + if (ret) { + netdev_err(ndev, "failed to boot PRU0: %d\n", ret); + return ret; + } + + return ret; +} + +static int emac_request_irqs(struct prueth_emac *emac) +{ + struct net_device *ndev = emac->ndev; + int ret = 0; + + ret = request_threaded_irq(emac->rx_irq, NULL, emac_rx_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + ndev->name, ndev); + if (ret) { + netdev_err(ndev, "unable to request RX IRQ\n"); + return ret; + } + + if (PRUETH_IS_EMAC(emac->prueth) && emac->tx_irq > 0) { + ret = request_irq(emac->tx_irq, emac_tx_hardirq, + IRQF_TRIGGER_HIGH, ndev->name, ndev); + if (ret) { + netdev_err(ndev, "unable to request TX IRQ\n"); + free_irq(emac->rx_irq, ndev); + return ret; + } + } + + if (emac->emac_ptp_tx_irq) { + ret = request_threaded_irq(emac->emac_ptp_tx_irq, + prueth_ptp_tx_irq_handle, + prueth_ptp_tx_irq_work, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + ndev->name, ndev); + if (ret) { + netdev_err(ndev, "unable to request PTP TX IRQ\n"); + free_irq(emac->rx_irq, ndev); + free_irq(emac->tx_irq, ndev); + } + } + + return ret; +} + +static int emac_sanitize_feature_flags(struct prueth_emac *emac) +{ + if ((PRUETH_IS_HSR(emac->prueth) || PRUETH_IS_PRP(emac->prueth)) && + !(emac->ndev->features & NETIF_F_HW_HSR_TAG_RM)) { + netdev_err(emac->ndev, "Error: Turn ON HSR offload\n"); + return -EINVAL; + } + + if ((PRUETH_IS_EMAC(emac->prueth) || PRUETH_IS_SWITCH(emac->prueth)) && + (emac->ndev->features & NETIF_F_HW_HSR_TAG_RM)) { + netdev_err(emac->ndev, "Error: Turn OFF HSR offload\n"); + return -EINVAL; + } + + return 0; +} + +/* Function to free memory related to sw/lre */ +static void prueth_free_memory(struct prueth *prueth) +{ + if (PRUETH_IS_SWITCH(prueth)) + prueth_sw_free_fdb_table(prueth); + if (PRUETH_IS_LRE(prueth)) + prueth_lre_free_memory(prueth); +} + +static void icss_ptp_dram_init(struct prueth_emac *emac) +{ + void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + u64 temp64; + + writew(0, sram + MII_RX_CORRECTION_OFFSET); + writew(0, sram + MII_TX_CORRECTION_OFFSET); + + /* Initialize RCF to 1 (Linux N/A) */ + writel(1 * 1024, sram + TIMESYNC_TC_RCF_OFFSET); + + /* This flag will be set and cleared by firmware */ + /* Write Sync0 period for sync signal generation in PTP + * memory in shared RAM + */ + writel(200000000 / 50, sram + TIMESYNC_SYNC0_WIDTH_OFFSET); + + /* Write CMP1 period for sync signal generation in PTP + * memory in shared RAM + */ + temp64 = 1000000; + memcpy_toio(sram + TIMESYNC_CMP1_CMP_OFFSET, &temp64, sizeof(temp64)); + + /* Write Sync0 period for sync signal generation in PTP + * memory in shared RAM + */ + writel(1000000, sram + TIMESYNC_CMP1_PERIOD_OFFSET); + + /* Configures domainNumber list. Firmware supports 2 domains */ + writeb(0, sram + TIMESYNC_DOMAIN_NUMBER_LIST); + writeb(0, sram + TIMESYNC_DOMAIN_NUMBER_LIST + 1); + + /* Configure 1-step/2-step */ + writeb(1, sram + DISABLE_SWITCH_SYNC_RELAY_OFFSET); + + /* Configures the setting to Link local frame without HSR tag */ + writeb(0, sram + LINK_LOCAL_FRAME_HAS_HSR_TAG); + + /* Enable E2E/UDP PTP message timestamping */ + writeb(1, sram + PTP_IPV4_UDP_E2E_ENABLE); +} + +/** + * emac_ndo_open - EMAC device open + * @ndev: network adapter device + * + * Called when system wants to start the interface. + * + * Returns 0 for a successful open, or appropriate error code + */ +static int emac_ndo_open(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int ret; + + /* set h/w MAC as user might have re-configured */ + ether_addr_copy(emac->mac_addr, ndev->dev_addr); + + netif_carrier_off(ndev); + + if (!prueth->emac_configured) + prueth_init_ethernet_mode(prueth); + + ret = emac_sanitize_feature_flags(emac); + if (ret) + return ret; + + /* reset and start PRU firmware */ + if (!PRUETH_IS_EMAC(prueth)) { + ret = prueth_sw_emac_config(emac); + if (ret) + return ret; + + if (PRUETH_IS_SWITCH(prueth)) { + ret = prueth_sw_init_fdb_table(prueth); + } else { + /* HSR/PRP */ + prueth_lre_config_check_flags(prueth); + ret = prueth_lre_init_node_table(prueth); + } + } else { + prueth_emac_config(emac); + } + + if (ret) + return ret; + + /* restore stats */ + emac_set_stats(emac, &emac->stats); + if (PRUETH_IS_LRE(prueth)) + prueth_lre_set_stats(prueth, prueth->lre_stats); + + if (!prueth->emac_configured) { + icss_ptp_dram_init(emac); + ret = icss_iep_init(prueth->iep, NULL, NULL, 0); + if (ret) { + netdev_err(ndev, "Failed to initialize iep: %d\n", ret); + goto free_mem; + } + } + + if (!PRUETH_IS_EMAC(prueth)) { + ret = prueth_sw_boot_prus(prueth, ndev); + if (ret) + goto iep_exit; + } else { + /* boot the PRU */ + ret = emac_set_boot_pru(emac, ndev); + if (ret) { + netdev_err(ndev, "failed to boot PRU: %d\n", ret); + goto iep_exit; + } + } + + if (PRUETH_IS_EMAC(prueth) || PRUETH_IS_SWITCH(prueth)) + ret = emac_request_irqs(emac); + else + ret = prueth_lre_request_irqs(emac); + if (ret) + goto rproc_shutdown; + + /* start PHY */ + phy_start(emac->phydev); + + /* enable the port and vlan */ + prueth_port_enable(emac, true); + + prueth->emac_configured |= BIT(emac->port_id); + if (PRUETH_IS_SWITCH(prueth)) + prueth_sw_port_set_stp_state(prueth, emac->port_id, + BR_STATE_LEARNING); + if (netif_msg_drv(emac)) + dev_notice(&ndev->dev, "started\n"); + + return 0; + +rproc_shutdown: + if (!PRUETH_IS_EMAC(prueth)) + prueth_sw_shutdown_prus(emac, ndev); + else + rproc_shutdown(emac->pru); +iep_exit: + if (!prueth->emac_configured) + icss_iep_exit(prueth->iep); +free_mem: + if (PRUETH_IS_SWITCH(prueth)) + prueth_sw_free_fdb_table(prueth); + + prueth_free_memory(emac->prueth); + return ret; +} + +/** + * emac_ndo_stop - EMAC device stop + * @ndev: network adapter device + * + * Called when system wants to stop or down the interface. + */ +static int emac_ndo_stop(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int i; + + prueth->emac_configured &= ~BIT(emac->port_id); + + /* disable the mac port */ + prueth_port_enable(emac, false); + + /* stop PHY */ + phy_stop(emac->phydev); + + /* inform the upper layers. */ + netif_stop_queue(ndev); + + netif_carrier_off(ndev); + + /* stop the PRU */ + if (!PRUETH_IS_EMAC(prueth)) + prueth_sw_shutdown_prus(emac, ndev); + else + rproc_shutdown(emac->pru); + + /* save and lre stats */ + emac_get_stats(emac, &emac->stats); + if (PRUETH_IS_LRE(prueth) && !prueth->emac_configured) + prueth_lre_get_stats(prueth, prueth->lre_stats); + + /* free table memory of the switch */ + if (PRUETH_IS_SWITCH(emac->prueth)) + prueth_sw_free_fdb_table(prueth); + + /* Cleanup ptp related stuff for all protocols */ + prueth_ptp_tx_ts_enable(emac, 0); + prueth_ptp_rx_ts_enable(emac, 0); + for (i = 0; i < PRUETH_PTP_TS_EVENTS; i++) { + if (emac->ptp_skb[i]) { + prueth_ptp_tx_ts_reset(emac, i); + dev_consume_skb_any(emac->ptp_skb[i]); + emac->ptp_skb[i] = NULL; + } + if (emac->ptp_ct_skb[i]) { + prueth_ptp_tx_ts_reset(emac, i); + dev_consume_skb_any(emac->ptp_ct_skb[i]); + emac->ptp_ct_skb[i] = NULL; + } + } + + /* free rx and tx interrupts */ + if (PRUETH_IS_EMAC(emac->prueth) && emac->tx_irq > 0) + free_irq(emac->tx_irq, ndev); + /* For EMAC and Switch, interrupt is per port. + * So free interrupts same way + */ + if (PRUETH_IS_EMAC(emac->prueth) || PRUETH_IS_SWITCH(prueth)) { + free_irq(emac->rx_irq, ndev); + if (emac->emac_ptp_tx_irq) + free_irq(emac->emac_ptp_tx_irq, ndev); + + } else { + /* Free interrupts on last port */ + prueth_lre_free_irqs(emac); + } + + /* free memory related to sw/lre */ + prueth_free_memory(emac->prueth); + + if (!prueth->emac_configured) + icss_iep_exit(prueth->iep); + + if (netif_msg_drv(emac)) + dev_notice(&ndev->dev, "stopped\n"); + + return 0; +} + +static void prueth_change_to_switch_mode(struct prueth *prueth) +{ + bool portstatus[PRUETH_NUM_MACS]; + struct prueth_emac *emac; + struct net_device *ndev; + int i, ret; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + emac = prueth->emac[i]; + ndev = emac->ndev; + + portstatus[i] = netif_running(ndev); + if (!portstatus[i]) + continue; + + ret = ndev->netdev_ops->ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return; + } + } + + prueth->eth_type = PRUSS_ETHTYPE_SWITCH; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + emac = prueth->emac[i]; + ndev = emac->ndev; + + if (!portstatus[i]) + continue; + + ret = ndev->netdev_ops->ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return; + } + } + + dev_info(prueth->dev, "TI PRU ethernet now in Switch mode\n"); +} + +static void prueth_change_to_emac_mode(struct prueth *prueth) +{ + struct prueth_emac *emac; + struct net_device *ndev; + bool portstatus[PRUETH_NUM_MACS]; + int i, ret; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + emac = prueth->emac[i]; + ndev = emac->ndev; + + portstatus[i] = netif_running(ndev); + if (!portstatus[i]) + continue; + + ret = ndev->netdev_ops->ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return; + } + } + + prueth->eth_type = PRUSS_ETHTYPE_EMAC; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + emac = prueth->emac[i]; + ndev = emac->ndev; + + if (!portstatus[i]) + continue; + + ret = ndev->netdev_ops->ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return; + } + } + + dev_info(prueth->dev, "TI PRU ethernet now in Dual EMAC mode\n"); +} + +/* VLAN-tag PCP to priority queue map for EMAC used by driver. Should be + * in sync with fw_pcp_default_priority_queue_map[] + * Index is PCP val. + * low - pcp 0..1 maps to Q4 + * 2..3 maps to Q3 + * 4..5 maps to Q2 + * high - pcp 6..7 maps to Q1. + * + * VLAN-tag PCP to priority queue map for Switch/HSR/PRP used by driver + * Index is PCP val / 2. + * low - pcp 0..3 maps to Q4 for Host + * high - pcp 4..7 maps to Q3 for Host + * low - pcp 0..3 maps to Q2 for PRU-x where x = 1 for PRUETH_PORT_MII0 + * 0 for PRUETH_PORT_MII1 + * high - pcp 4..7 maps to Q1 for PRU-x + */ +static const unsigned short emac_pcp_tx_priority_queue_map[] = { + PRUETH_QUEUE4, PRUETH_QUEUE4, + PRUETH_QUEUE3, PRUETH_QUEUE3, + PRUETH_QUEUE2, PRUETH_QUEUE2, + PRUETH_QUEUE1, PRUETH_QUEUE1, +}; + +static u16 prueth_get_tx_queue_id(struct prueth *prueth, struct sk_buff *skb) +{ + u16 vlan_tci, pcp; + int err; + + err = vlan_get_tag(skb, &vlan_tci); + if (likely(err)) + pcp = 0; + else + pcp = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + /* For HSR/PRP, we use only QUEUE4 and QUEUE3 at the egress. QUEUE2 and + * QUEUE1 are used for port to port traffic. Current version of SWITCH + * firmware uses 4 egress queues. + */ + if (PRUETH_IS_LRE(prueth)) + pcp >>= 1; + + return emac_pcp_tx_priority_queue_map[pcp]; +} + +/** + * emac_ndo_start_xmit - EMAC Transmit function + * @skb: SKB pointer + * @ndev: EMAC network adapter + * + * Called by the system to transmit a packet - we queue the packet in + * EMAC hardware transmit queue + * + * Returns success(NETDEV_TX_OK) or error code (typically out of desc's) + */ +static int emac_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int ret = 0; + u16 qid; + + if (unlikely(!emac->link)) { + if (netif_msg_tx_err(emac) && net_ratelimit()) + netdev_err(ndev, "No link to transmit"); + goto fail_tx; + } + + qid = prueth_get_tx_queue_id(emac->prueth, skb); + ret = prueth_tx_enqueue(emac, skb, qid); + if (ret) { + if (ret != -ENOBUFS && netif_msg_tx_err(emac) && + net_ratelimit()) + netdev_err(ndev, "packet queue failed: %d\n", ret); + goto fail_tx; + } + + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; + +fail_tx: + if (ret == -ENOBUFS) { + /* no free TX queue */ + if (emac->tx_irq > 0) + netif_stop_queue(ndev); + ret = NETDEV_TX_BUSY; + } else { + /* error */ + ndev->stats.tx_dropped++; + ret = NET_XMIT_DROP; + } + + return ret; +} + +/** + * emac_ndo_tx_timeout - EMAC Transmit timeout function + * @ndev: The EMAC network adapter + * @txqueue: TX queue being used + * + * Called when system detects that a skb timeout period has expired + * potentially due to a fault in the adapter in not being able to send + * it out on the wire. + */ +static void emac_ndo_tx_timeout(struct net_device *ndev, unsigned int txqueue) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (netif_msg_tx_err(emac)) + netdev_err(ndev, "xmit timeout"); + + ndev->stats.tx_errors++; + + /* TODO: can we recover or need to reboot firmware? */ + + netif_wake_queue(ndev); +} + +/** + * emac_ndo_getstats - EMAC get statistics function + * @ndev: The EMAC network adapter + * + * Called when system wants to get statistics from the device. + * + * We return the statistics in net_device_stats structure pulled from emac + */ +static struct net_device_stats *emac_ndo_get_stats(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct port_statistics pstats; + struct net_device_stats *stats = &ndev->stats; + + emac_get_stats(emac, &pstats); + stats->collisions = pstats.late_coll + pstats.single_coll + + pstats.multi_coll + pstats.excess_coll; + stats->multicast = pstats.rx_mcast; + + return stats; +} + +/* enable/disable MC filter */ +static void emac_mc_filter_ctrl(struct prueth_emac *emac, bool enable) +{ + struct prueth *prueth = emac->prueth; + void __iomem *ram = prueth->mem[emac->dram].va; + u32 mc_ctrl_byte = prueth->fw_offsets->mc_ctrl_byte; + void __iomem *mc_filter_ctrl; + u32 reg; + + if (PRUETH_IS_LRE(prueth)) + ram = prueth->mem[PRUETH_MEM_DRAM1].va; + + mc_filter_ctrl = ram + mc_ctrl_byte; + + if (enable) + reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED; + else + reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED; + + writeb(reg, mc_filter_ctrl); +} + +/* reset MC filter bins */ +static void emac_mc_filter_reset(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + void __iomem *ram = prueth->mem[emac->dram].va; + u32 mc_filter_tbl_base = prueth->fw_offsets->mc_filter_tbl; + void __iomem *mc_filter_tbl; + + if (PRUETH_IS_LRE(prueth)) + ram = prueth->mem[PRUETH_MEM_DRAM1].va; + + mc_filter_tbl = ram + mc_filter_tbl_base; + memset_io(mc_filter_tbl, 0, ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES); +} + +/* set MC filter hashmask */ +static void emac_mc_filter_hashmask(struct prueth_emac *emac, + u8 mask[ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES]) +{ + struct prueth *prueth = emac->prueth; + void __iomem *ram = prueth->mem[emac->dram].va; + u32 mc_filter_mask_base = prueth->fw_offsets->mc_filter_mask; + void __iomem *mc_filter_mask; + + if (PRUETH_IS_LRE(prueth)) + ram = prueth->mem[PRUETH_MEM_DRAM1].va; + + mc_filter_mask = ram + mc_filter_mask_base; + memcpy_toio(mc_filter_mask, mask, + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES); +} + +static void emac_mc_filter_bin_update(struct prueth_emac *emac, u8 hash, u8 val) +{ + struct prueth *prueth = emac->prueth; + u32 mc_filter_tbl_base = prueth->fw_offsets->mc_filter_tbl; + void __iomem *mc_filter_tbl; + void __iomem *ram = prueth->mem[emac->dram].va; + + if (PRUETH_IS_LRE(prueth)) + ram = prueth->mem[PRUETH_MEM_DRAM1].va; + + mc_filter_tbl = ram + mc_filter_tbl_base; + writeb(val, mc_filter_tbl + hash); +} + +void emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) +{ + emac_mc_filter_bin_update(emac, hash, ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED); +} + +void emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash) +{ + emac_mc_filter_bin_update(emac, hash, ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED); +} + +u8 emac_get_mc_hash(u8 *mac, u8 *mask) +{ + int j; + u8 hash; + + for (j = 0, hash = 0; j < ETH_ALEN; j++) + hash ^= (mac[j] & mask[j]); + + return hash; +} + +/** + * emac_ndo_set_rx_mode - EMAC set receive mode function + * @ndev: The EMAC network adapter + * + * Called when system wants to set the receive mode of the device. + * + */ +static void emac_ndo_set_rx_mode(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + u32 reg = readl(sram + EMAC_PROMISCUOUS_MODE_OFFSET); + bool promisc = ndev->flags & IFF_PROMISC; + struct netdev_hw_addr *ha; + unsigned long flags; + u32 mask; + u8 hash; + + if (PRUETH_IS_SWITCH(prueth)) { + netdev_dbg(ndev, + "%s: promisc/mc filtering not supported for switch\n", + __func__); + return; + } + + if (promisc && PRUETH_IS_LRE(prueth)) { + netdev_dbg(ndev, + "%s: promisc mode not supported for LRE\n", + __func__); + return; + } + + /* for LRE, it is a shared table. So lock the access */ + spin_lock_irqsave(&emac->addr_lock, flags); + + /* Disable and reset multicast filter, allows allmulti */ + emac_mc_filter_ctrl(emac, false); + emac_mc_filter_reset(emac); + emac_mc_filter_hashmask(emac, emac->mc_filter_mask); + + if (PRUETH_IS_EMAC(prueth)) { + switch (emac->port_id) { + case PRUETH_PORT_MII0: + mask = EMAC_P1_PROMISCUOUS_BIT; + break; + case PRUETH_PORT_MII1: + mask = EMAC_P2_PROMISCUOUS_BIT; + break; + default: + netdev_err(ndev, "%s: invalid port\n", __func__); + goto unlock; + } + + if (promisc) { + /* Enable promiscuous mode */ + reg |= mask; + } else { + /* Disable promiscuous mode */ + reg &= ~mask; + } + + writel(reg, sram + EMAC_PROMISCUOUS_MODE_OFFSET); + + if (promisc) + goto unlock; + } + + if (ndev->flags & IFF_ALLMULTI && !PRUETH_IS_SWITCH(prueth)) + goto unlock; + + emac_mc_filter_ctrl(emac, true); /* all multicast blocked */ + + if (netdev_mc_empty(ndev)) + goto unlock; + + netdev_for_each_mc_addr(ha, ndev) { + hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); + emac_mc_filter_bin_allow(emac, hash); + } + + /* Add bridge device's MC addresses as well */ + if (prueth->hw_bridge_dev) { + netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { + hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); + emac_mc_filter_bin_allow(emac, hash); + } + } + +unlock: + spin_unlock_irqrestore(&emac->addr_lock, flags); +} + +static int emac_hwtstamp_config_set(struct net_device *ndev, struct ifreq *ifr) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct hwtstamp_config cfg; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + /* reserved for future extensions */ + if (cfg.flags) + return -EINVAL; + + if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON) + return -ERANGE; + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + prueth_ptp_rx_ts_enable(emac, 0); + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + prueth_ptp_rx_ts_enable(emac, 1); + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + default: + return -ERANGE; + } + + prueth_ptp_tx_ts_enable(emac, cfg.tx_type == HWTSTAMP_TX_ON); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static int emac_hwtstamp_config_get(struct net_device *ndev, struct ifreq *ifr) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct hwtstamp_config cfg; + + cfg.flags = 0; + cfg.tx_type = prueth_ptp_tx_ts_is_enabled(emac) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + cfg.rx_filter = prueth_ptp_rx_ts_is_enabled(emac) ? + HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE; + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static int emac_ndo_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + switch (cmd) { + case SIOCSHWTSTAMP: + return emac_hwtstamp_config_set(ndev, ifr); + case SIOCGHWTSTAMP: + return emac_hwtstamp_config_get(ndev, ifr); + } + + return phy_mii_ioctl(emac->phydev, ifr, cmd); +} + +int emac_add_del_vid(struct prueth_emac *emac, + bool add, __be16 proto, u16 vid) +{ + struct prueth *prueth = emac->prueth; + u32 vlan_filter_tbl = prueth->fw_offsets->vlan_filter_tbl; + void __iomem *ram = prueth->mem[emac->dram].va; + unsigned long flags; + u8 bit_index, val; + u16 byte_index; + + if (proto != htons(ETH_P_8021Q)) + return -EINVAL; + + if (vid >= ICSS_EMAC_FW_VLAN_FILTER_VID_MAX) + return -EINVAL; + + if (PRUETH_IS_LRE(prueth)) + ram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + /* By default, VLAN ID 0 (priority tagged packets) is routed to + * host, so nothing to be done if vid = 0 + */ + if (!vid) + return 0; + + /* for LRE, it is a shared table. So lock the access */ + spin_lock_irqsave(&emac->addr_lock, flags); + + /* VLAN filter table is 512 bytes (4096 bit) bitmap. + * Each bit controls enabling or disabling corresponding + * VID. Therefore byte index that controls a given VID is + * can calculated as vid / 8 and the bit within that byte + * that controls VID is given by vid % 8. Allow untagged + * frames to host by default. + */ + byte_index = vid / BITS_PER_BYTE; + bit_index = vid % BITS_PER_BYTE; + val = readb(ram + vlan_filter_tbl + byte_index); + if (add) + val |= BIT(bit_index); + else + val &= ~BIT(bit_index); + writeb(val, ram + vlan_filter_tbl + byte_index); + + spin_unlock_irqrestore(&emac->addr_lock, flags); + + netdev_dbg(emac->ndev, "%s VID bit at index %d and bit %d\n", + add ? "Setting" : "Clearing", byte_index, bit_index); + + return 0; +} + +static int emac_ndo_vlan_rx_add_vid(struct net_device *dev, + __be16 proto, u16 vid) +{ + struct prueth_emac *emac = netdev_priv(dev); + + return emac_add_del_vid(emac, true, proto, vid); +} + +static int emac_ndo_vlan_rx_kill_vid(struct net_device *dev, + __be16 proto, u16 vid) +{ + struct prueth_emac *emac = netdev_priv(dev); + + return emac_add_del_vid(emac, false, proto, vid); +} + +static int emac_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) +{ + struct prueth_emac *emac = netdev_priv(dev); + struct prueth *prueth = emac->prueth; + + ppid->id_len = sizeof(prueth->base_mac); + memcpy(&ppid->id, &prueth->base_mac, ppid->id_len); + + return 0; +} + +static int emac_ndo_get_phys_port_name(struct net_device *ndev, char *name, + size_t len) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int err; + + err = snprintf(name, len, "p%d", emac->port_id); + + if (err >= len) + return -EINVAL; + + return 0; +} + +/** + * emac_ndo_set_features - function to set feature flag + * @ndev: The network adapter device + * + * Called when ethtool -K option is invoked by user + * + * Change the eth_type in the prueth structure based on hsr or prp + * offload options from user through ethtool -K command. If the device + * is running or if the other paired device is running, then don't accept. + * Otherwise, set the ethernet type and offload feature flag + * + * Returns success if eth_type and feature flags are updated or error + * otherwise. + */ +static int emac_ndo_set_features(struct net_device *ndev, + netdev_features_t features) +{ + struct prueth_emac *emac = netdev_priv(ndev), *other_emac; + struct prueth *prueth = emac->prueth; + enum prueth_port other_port; + netdev_features_t wanted = features & NETIF_F_HW_HSR_TAG_RM; + netdev_features_t have = ndev->features & NETIF_F_HW_HSR_TAG_RM; + bool change_request = ((wanted ^ have) != 0); + int ret = -EBUSY; + + if (!prueth->support_lre) + return 0; + + if (PRUETH_IS_SWITCH(prueth)) { + /* Don't allow switching to HSR/PRP ethtype from Switch. + * User needs to first remove eth ports from a bridge which + * will automatically put the ethtype back to EMAC. So + * disallow this. + */ + netdev_err(ndev, + "Switch to HSR/PRP/EMAC not allowed\n"); + return -EINVAL; + } + + if (netif_running(ndev) && change_request) { + netdev_err(ndev, + "Can't change feature when device runs\n"); + return ret; + } + + other_port = other_port_id(emac->port_id); + /* MAC instance index starts from 0. So index by port_id - 1 */ + other_emac = prueth->emac[other_port - 1]; + if (other_emac && netif_running(other_emac->ndev) && change_request) { + netdev_err(ndev, + "Can't change feature when other device runs\n"); + return ret; + } + + if (features & NETIF_F_HW_HSR_TAG_RM) { + ndev->features |= NETIF_F_HW_HSR_TAG_RM; + } else if (features & NETIF_F_HW_HSR_FWD) { + ndev->features |= NETIF_F_HW_HSR_FWD; + } else { + prueth->eth_type = PRUSS_ETHTYPE_EMAC; + ndev->features &= ~(NETIF_F_HW_HSR_TAG_RM | NETIF_F_HW_HSR_FWD); + } + + return 0; +} + +static const struct net_device_ops emac_netdev_ops = { + .ndo_open = emac_ndo_open, + .ndo_stop = emac_ndo_stop, + .ndo_start_xmit = emac_ndo_start_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = emac_ndo_tx_timeout, + .ndo_get_stats = emac_ndo_get_stats, + .ndo_set_rx_mode = emac_ndo_set_rx_mode, + .ndo_do_ioctl = emac_ndo_ioctl, + .ndo_set_features = emac_ndo_set_features, + .ndo_vlan_rx_add_vid = emac_ndo_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = emac_ndo_vlan_rx_kill_vid, + .ndo_setup_tc = emac_ndo_setup_tc, + .ndo_get_port_parent_id = emac_get_port_parent_id, + .ndo_get_phys_port_name = emac_ndo_get_phys_port_name, +}; + +/** + * emac_get_drvinfo - Get EMAC driver information + * @ndev: The network adapter + * @info: ethtool info structure containing name and version + * + * Returns EMAC driver information (name and version) + */ +static void emac_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, PRUETH_MODULE_DESCRIPTION, sizeof(info->driver)); + strlcpy(info->version, PRUETH_MODULE_VERSION, sizeof(info->version)); +} + +/** + * emac_get_link_ksettings - Get EMAC settings + * @ndev: The network adapter + * @ecmd: ethtool command + * + * Executes ethool get command + */ +static int emac_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *ecmd) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev) + return -EOPNOTSUPP; + + phy_ethtool_ksettings_get(emac->phydev, ecmd); + return 0; +} + +/** + * emac_set_link_ksettings - Set EMAC settings + * @ndev: The EMAC network adapter + * @ecmd: ethtool command + * + * Executes ethool set command + */ +static int emac_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *ecmd) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if (!emac->phydev) + return -EOPNOTSUPP; + + return phy_ethtool_ksettings_set(emac->phydev, ecmd); +} + +#define PRUETH_STAT_OFFSET(m) offsetof(struct port_statistics, m) + +static const struct { + char string[ETH_GSTRING_LEN]; + u32 offset; +} prueth_ethtool_stats[] = { + {"txBcast", PRUETH_STAT_OFFSET(tx_bcast)}, + {"txMcast", PRUETH_STAT_OFFSET(tx_mcast)}, + {"txUcast", PRUETH_STAT_OFFSET(tx_ucast)}, + {"txOctets", PRUETH_STAT_OFFSET(tx_octets)}, + {"rxBcast", PRUETH_STAT_OFFSET(rx_bcast)}, + {"rxMcast", PRUETH_STAT_OFFSET(rx_mcast)}, + {"rxUcast", PRUETH_STAT_OFFSET(rx_ucast)}, + {"rxOctets", PRUETH_STAT_OFFSET(rx_octets)}, + + {"tx64byte", PRUETH_STAT_OFFSET(tx64byte)}, + {"tx65_127byte", PRUETH_STAT_OFFSET(tx65_127byte)}, + {"tx128_255byte", PRUETH_STAT_OFFSET(tx128_255byte)}, + {"tx256_511byte", PRUETH_STAT_OFFSET(tx256_511byte)}, + {"tx512_1023byte", PRUETH_STAT_OFFSET(tx512_1023byte)}, + {"tx1024byte", PRUETH_STAT_OFFSET(tx1024byte)}, + {"rx64byte", PRUETH_STAT_OFFSET(rx64byte)}, + {"rx65_127byte", PRUETH_STAT_OFFSET(rx65_127byte)}, + {"rx128_255byte", PRUETH_STAT_OFFSET(rx128_255byte)}, + {"rx256_511byte", PRUETH_STAT_OFFSET(rx256_511byte)}, + {"rx512_1023byte", PRUETH_STAT_OFFSET(rx512_1023byte)}, + {"rx1024byte", PRUETH_STAT_OFFSET(rx1024byte)}, + + {"lateColl", PRUETH_STAT_OFFSET(late_coll)}, + {"singleColl", PRUETH_STAT_OFFSET(single_coll)}, + {"multiColl", PRUETH_STAT_OFFSET(multi_coll)}, + {"excessColl", PRUETH_STAT_OFFSET(excess_coll)}, + + {"rxMisAlignmentFrames", PRUETH_STAT_OFFSET(rx_misalignment_frames)}, + {"stormPrevCounterBC", PRUETH_STAT_OFFSET(stormprev_counter_bc)}, + {"stormPrevCounterMC", PRUETH_STAT_OFFSET(stormprev_counter_mc)}, + {"stormPrevCounterUC", PRUETH_STAT_OFFSET(stormprev_counter_uc)}, + {"macRxError", PRUETH_STAT_OFFSET(mac_rxerror)}, + {"SFDError", PRUETH_STAT_OFFSET(sfd_error)}, + {"defTx", PRUETH_STAT_OFFSET(def_tx)}, + {"macTxError", PRUETH_STAT_OFFSET(mac_txerror)}, + {"rxOverSizedFrames", PRUETH_STAT_OFFSET(rx_oversized_frames)}, + {"rxUnderSizedFrames", PRUETH_STAT_OFFSET(rx_undersized_frames)}, + {"rxCRCFrames", PRUETH_STAT_OFFSET(rx_crc_frames)}, + {"droppedPackets", PRUETH_STAT_OFFSET(dropped_packets)}, + + {"txHWQOverFlow", PRUETH_STAT_OFFSET(tx_hwq_overflow)}, + {"txHWQUnderFlow", PRUETH_STAT_OFFSET(tx_hwq_underflow)}, + {"vlanDropped", PRUETH_STAT_OFFSET(vlan_dropped)}, + {"multicastDropped", PRUETH_STAT_OFFSET(multicast_dropped)}, +}; + +static int emac_get_sset_count(struct net_device *ndev, int stringset) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int a_size; + + switch (stringset) { + case ETH_SS_STATS: + a_size = ARRAY_SIZE(prueth_ethtool_stats); + a_size += prueth_lre_get_sset_count(emac->prueth); + + return a_size; + default: + return -EOPNOTSUPP; + } +} + +static void emac_get_strings(struct net_device *ndev, u32 stringset, u8 *data) +{ + struct prueth_emac *emac = netdev_priv(ndev); + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(prueth_ethtool_stats); i++) { + memcpy(p, prueth_ethtool_stats[i].string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + prueth_lre_get_strings(emac->prueth, p); + break; + default: + break; + } +} + +static void emac_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, u64 *data) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct port_statistics pstats; + u32 val; + int i; + void *ptr; + + emac_get_stats(emac, &pstats); + + for (i = 0; i < ARRAY_SIZE(prueth_ethtool_stats); i++) { + ptr = &pstats; + ptr += prueth_ethtool_stats[i].offset; + val = *(u32 *)ptr; + data[i] = val; + } + prueth_lre_update_stats(emac->prueth, &data[i]); +} + +static int emac_get_regs_len(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + /* VLAN Table at the end of the memory map, after MultiCast + * filter region. So VLAN table base + + * size will give the entire size of reg dump in case of + * Dual-EMAC firmware. + */ + if (PRUETH_IS_EMAC(prueth) || PRUETH_IS_SWITCH(prueth)) { + return ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR + + ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES; + } + + /* MultiCast table and VLAN filter table are in different + * memories in case of HSR/PRP firmware. Therefore add the sizes + * of individual region. + */ + if (PRUETH_IS_LRE(prueth)) { + return ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR + + ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES + + ICSS_LRE_FW_MULTICAST_FILTER_TABLE + + ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES; + } + + return 0; +} + +static void emac_get_regs(struct net_device *ndev, struct ethtool_regs *regs, + void *p) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + void __iomem *ram; + u8 *reg = p; + + regs->version = PRUETH_REG_DUMP_GET_VER(prueth); + + /* Dump firmware's VLAN and MC tables */ + if (PRUETH_IS_EMAC(prueth) || PRUETH_IS_SWITCH(prueth)) { + ram = prueth->mem[emac->dram].va; + memcpy_fromio(reg, ram, emac_get_regs_len(ndev)); + return; + } + + if (PRUETH_IS_LRE(prueth)) { + size_t len = ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR + + ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES; + + ram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + memcpy_fromio(reg, ram, len); + + reg += len; + + ram = prueth->mem[PRUETH_MEM_DRAM1].va; + len = ICSS_LRE_FW_MULTICAST_FILTER_TABLE + + ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES; + memcpy_fromio(reg, ram, len); + } +} + +static int emac_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + if ((PRUETH_IS_EMAC(emac->prueth) && !emac->emac_ptp_tx_irq) || + (PRUETH_IS_LRE(emac->prueth) && !emac->hsr_ptp_tx_irq)) + return ethtool_op_get_ts_info(ndev, info); + + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->phc_index = icss_iep_get_ptp_clock_idx(emac->prueth->iep); + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); + + return 0; +} + +/* Ethtool support for EMAC adapter */ +static const struct ethtool_ops emac_ethtool_ops = { + .get_drvinfo = emac_get_drvinfo, + .get_link_ksettings = emac_get_link_ksettings, + .set_link_ksettings = emac_set_link_ksettings, + .get_link = ethtool_op_get_link, + .get_ts_info = emac_get_ts_info, + .get_sset_count = emac_get_sset_count, + .get_strings = emac_get_strings, + .get_ethtool_stats = emac_get_ethtool_stats, + .get_regs = emac_get_regs, + .get_regs_len = emac_get_regs_len, +}; + +/* get emac_port corresponding to eth_node name */ +static int prueth_node_port(struct device_node *eth_node) +{ + if (!strcmp(eth_node->name, "ethernet-mii0")) + return PRUETH_PORT_MII0; + else if (!strcmp(eth_node->name, "ethernet-mii1")) + return PRUETH_PORT_MII1; + else + return -EINVAL; +} + +/* get MAC instance corresponding to eth_node name */ +static int prueth_node_mac(struct device_node *eth_node) +{ + if (!strcmp(eth_node->name, "ethernet-mii0")) + return PRUETH_MAC0; + else if (!strcmp(eth_node->name, "ethernet-mii1")) + return PRUETH_MAC1; + else + return -EINVAL; +} + +static int prueth_netdev_init(struct prueth *prueth, + struct device_node *eth_node) +{ + enum prueth_port port; + enum prueth_mac mac; + struct net_device *ndev; + struct prueth_emac *emac; + const u8 *mac_addr; + int ret; + + port = prueth_node_port(eth_node); + if (port < 0) + return -EINVAL; + + mac = prueth_node_mac(eth_node); + if (mac < 0) + return -EINVAL; + + ndev = devm_alloc_etherdev(prueth->dev, sizeof(*emac)); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, prueth->dev); + emac = netdev_priv(ndev); + prueth->emac[mac] = emac; + emac->prueth = prueth; + emac->ndev = ndev; + emac->port_id = port; + memset(&emac->mc_filter_mask[0], 0xff, ETH_ALEN); /* default mask */ + + /* by default eth_type is EMAC */ + switch (port) { + case PRUETH_PORT_MII0: + emac->tx_port_queue = PRUETH_PORT_QUEUE_MII0; + + /* packets from MII0 are on queues 1 through 2 */ + emac->rx_queue_start = PRUETH_QUEUE1; + emac->rx_queue_end = PRUETH_QUEUE2; + + emac->dram = PRUETH_MEM_DRAM0; + emac->pru = prueth->pru0; + break; + case PRUETH_PORT_MII1: + emac->tx_port_queue = PRUETH_PORT_QUEUE_MII1; + + /* packets from MII1 are on queues 3 through 4 */ + emac->rx_queue_start = PRUETH_QUEUE3; + emac->rx_queue_end = PRUETH_QUEUE4; + + emac->dram = PRUETH_MEM_DRAM1; + emac->pru = prueth->pru1; + break; + default: + return -EINVAL; + } + + emac->rx_irq = of_irq_get_byname(eth_node, "rx"); + if (emac->rx_irq < 0) { + ret = emac->rx_irq; + if (ret != -EPROBE_DEFER) + dev_err(prueth->dev, "could not get rx irq\n"); + goto free; + } + emac->tx_irq = of_irq_get_byname(eth_node, "tx"); + if (emac->tx_irq < 0) { + if (emac->tx_irq != -EPROBE_DEFER) + dev_dbg(prueth->dev, "tx irq not configured\n"); + } + + emac->emac_ptp_tx_irq = of_irq_get_byname(eth_node, "emac_ptp_tx"); + if (emac->emac_ptp_tx_irq < 0) { + emac->emac_ptp_tx_irq = 0; + dev_err(prueth->dev, "could not get ptp tx irq. Skipping PTP support\n"); + } + + emac->hsr_ptp_tx_irq = of_irq_get_byname(eth_node, "hsr_ptp_tx"); + if (emac->hsr_ptp_tx_irq < 0) { + emac->hsr_ptp_tx_irq = 0; + dev_err(prueth->dev, "could not get hsr ptp tx irq. Skipping PTP support\n"); + } + + emac->msg_enable = netif_msg_init(debug_level, PRUETH_EMAC_DEBUG); + spin_lock_init(&emac->lock); + spin_lock_init(&emac->ptp_skb_lock); + spin_lock_init(&emac->addr_lock); + + /* get mac address from DT and set private and netdev addr */ + mac_addr = of_get_mac_address(eth_node); + if (!IS_ERR(mac_addr)) + ether_addr_copy(ndev->dev_addr, mac_addr); + if (!is_valid_ether_addr(ndev->dev_addr)) { + eth_hw_addr_random(ndev); + dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n", + port, ndev->dev_addr); + } + ether_addr_copy(emac->mac_addr, ndev->dev_addr); + + emac->phy_node = of_parse_phandle(eth_node, "phy-handle", 0); + if (!emac->phy_node) { + dev_err(prueth->dev, "couldn't find phy-handle\n"); + ret = -ENODEV; + goto free; + } + + ret = of_get_phy_mode(eth_node, &emac->phy_if); + if (ret) { + dev_err(prueth->dev, "could not get phy-mode property err %d\n", ret); + goto free; + } + + /* connect PHY */ + emac->phydev = of_phy_connect(ndev, emac->phy_node, + &emac_adjust_link, 0, emac->phy_if); + if (!emac->phydev) { + dev_dbg(prueth->dev, "couldn't connect to phy %s\n", + emac->phy_node->full_name); + ret = -EPROBE_DEFER; + goto free; + } + + /* remove unsupported modes */ + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT); + + if (of_property_read_bool(eth_node, "ti,no-half-duplex")) { + phy_remove_link_mode(emac->phydev, + ETHTOOL_LINK_MODE_100baseT_Half_BIT); + } + + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); + + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC; + + if (prueth->support_lre) + ndev->hw_features |= (NETIF_F_HW_HSR_FWD | NETIF_F_HW_HSR_TAG_RM); + + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + + ndev->netdev_ops = &emac_netdev_ops; + ndev->ethtool_ops = &emac_ethtool_ops; + if (prueth->support_lre) + ndev->lredev_ops = &prueth_lredev_ops; + + /* for HSR/PRP */ + if (prueth->support_lre && emac->port_id == PRUETH_PORT_MII0) { + prueth->hp->ndev = ndev; + prueth->hp->priority = 0; + prueth->lp->ndev = ndev; + prueth->lp->priority = 1; + } + + return 0; + +free: + prueth->emac[mac] = NULL; + + return ret; +} + +static void prueth_netdev_exit(struct prueth *prueth, + struct device_node *eth_node) +{ + struct prueth_emac *emac; + enum prueth_mac mac; + + mac = prueth_node_mac(eth_node); + if (mac < 0) + return; + + emac = prueth->emac[mac]; + if (!emac) + return; + + phy_disconnect(emac->phydev); + + prueth->emac[mac] = NULL; +} + +bool prueth_sw_port_dev_check(const struct net_device *ndev) +{ + if (ndev->netdev_ops != &emac_netdev_ops) + return false; + + if (ndev->features & NETIF_F_HW_HSR_TAG_RM) + return true; + + return false; +} + +static void prueth_port_offload_fwd_mark_update(struct prueth *prueth) +{ + int set_val = 0; + int i; + u8 all_slaves = BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1); + + if (prueth->br_members == all_slaves) + set_val = 1; + + dev_dbg(prueth->dev, "set offload_fwd_mark %d, mbrs=0x%x\n", + set_val, prueth->br_members); + + for (i = 0; i < PRUETH_NUM_MACS; i++) + prueth->emac[i]->offload_fwd_mark = set_val; + + /* Bridge is created, load switch firmware, if not already in + * that mode + */ + if (set_val && !PRUETH_IS_SWITCH(prueth)) + prueth_change_to_switch_mode(prueth); + + /* Bridge is deleted, switch to Dual EMAC mode */ + if (!prueth->br_members && !PRUETH_IS_EMAC(prueth)) + prueth_change_to_emac_mode(prueth); +} + +static int prueth_ndev_port_link(struct net_device *ndev, + struct net_device *br_ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + dev_dbg(prueth->dev, "%s: br_mbrs=0x%x %s\n", + __func__, prueth->br_members, ndev->name); + + if (!prueth->br_members) { + prueth->hw_bridge_dev = br_ndev; + } else { + /* This is adding the port to a second bridge, this is + * unsupported + */ + if (prueth->hw_bridge_dev != br_ndev) + return -EOPNOTSUPP; + } + + prueth->br_members |= BIT(emac->port_id); + + prueth_port_offload_fwd_mark_update(prueth); + + return NOTIFY_DONE; +} + +static void prueth_ndev_port_unlink(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + dev_dbg(prueth->dev, "emac_sw_ndev_port_unlink\n"); + + prueth->br_members &= ~BIT(emac->port_id); + + prueth_port_offload_fwd_mark_update(prueth); + + if (!prueth->br_members) + prueth->hw_bridge_dev = NULL; +} + +static int prueth_ndev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int ret = NOTIFY_DONE; + enum hsr_version ver; + + if (!prueth_sw_port_dev_check(ndev)) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_CHANGEUPPER: + info = ptr; + + if (ndev->features & NETIF_F_HW_HSR_TAG_RM) { + if (is_hsr_master(info->upper_dev)) { + hsr_get_version(info->upper_dev, &ver); + if (ver == HSR_V1) + prueth->eth_type = PRUSS_ETHTYPE_HSR; + else if (ver == PRP_V1) + prueth->eth_type = PRUSS_ETHTYPE_PRP; + } + } + + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) + ret = prueth_ndev_port_link(ndev, + info->upper_dev); + else + prueth_ndev_port_unlink(ndev); + } + break; + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(ret); +} + +static int prueth_register_notifiers(struct prueth *prueth) +{ + struct notifier_block *nb; + int ret; + + nb = &prueth->prueth_ndev_nb; + nb->notifier_call = prueth_ndev_event; + ret = register_netdevice_notifier(nb); + if (ret) { + dev_err(prueth->dev, + "register netdevice notifier failed ret: %d\n", ret); + return ret; + } + + ret = prueth_sw_register_notifiers(prueth); + if (ret) { + unregister_netdevice_notifier(nb); + return ret; + } + + return 0; +} +static const struct of_device_id prueth_dt_match[]; + +static int prueth_probe(struct platform_device *pdev) +{ + struct prueth *prueth; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *eth0_node, *eth1_node; + const struct of_device_id *match; + enum pruss_pru_id pruss_id0, pruss_id1; + bool has_lre = false; + struct pruss *pruss; + int i, ret; + + if (!np) + return -ENODEV; /* we don't support non DT */ + + match = of_match_device(prueth_dt_match, dev); + if (!match) + return -ENODEV; + + prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL); + if (!prueth) + return -ENOMEM; + + platform_set_drvdata(pdev, prueth); + + prueth->dev = dev; + prueth->fw_data = match->data; + prueth->prueth_np = np; + prueth->fw_offsets = &fw_offsets_v2_1; + + eth0_node = of_get_child_by_name(np, "ethernet-mii0"); + if (!of_device_is_available(eth0_node)) { + of_node_put(eth0_node); + eth0_node = NULL; + } + + eth1_node = of_get_child_by_name(np, "ethernet-mii1"); + if (!of_device_is_available(eth1_node)) { + of_node_put(eth1_node); + eth1_node = NULL; + } + + /* At least one node must be present and available else we fail */ + if (!eth0_node && !eth1_node) { + dev_err(dev, "neither ethernet-mii0 nor ethernet-mii1 node available\n"); + ret = -ENODEV; + goto put_node; + } + + prueth->eth_node[PRUETH_MAC0] = eth0_node; + prueth->eth_node[PRUETH_MAC1] = eth1_node; + + prueth->mii_rt = syscon_regmap_lookup_by_phandle(np, "mii-rt"); + if (IS_ERR(prueth->mii_rt)) { + dev_err(dev, "couldn't get mii-rt syscon regmap\n"); + return -ENODEV; + } + + if (eth0_node) { + prueth->pru0 = pru_rproc_get(np, 0, &pruss_id0); + if (IS_ERR(prueth->pru0)) { + ret = PTR_ERR(prueth->pru0); + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get PRU0: %d\n", ret); + goto put_node; + } + } + + if (eth1_node) { + prueth->pru1 = pru_rproc_get(np, 1, &pruss_id1); + if (IS_ERR(prueth->pru1)) { + ret = PTR_ERR(prueth->pru1); + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get PRU1: %d\n", ret); + goto put_pru0; + } + } + + pruss = pruss_get(prueth->pru0 ? prueth->pru0 : prueth->pru1); + if (IS_ERR(pruss)) { + ret = PTR_ERR(pruss); + dev_err(dev, "unable to get pruss handle\n"); + goto put_pru1; + } + prueth->pruss = pruss; + + ret = pruss_cfg_ocp_master_ports(prueth->pruss, 1); + if (ret) { + dev_err(dev, "couldn't enabled ocp master port: %d\n", ret); + goto put_pruss; + } + + /* Configure PRUSS */ + if (eth0_node) + pruss_cfg_gpimode(pruss, pruss_id0, PRUSS_GPI_MODE_MII); + if (eth1_node) + pruss_cfg_gpimode(pruss, pruss_id1, PRUSS_GPI_MODE_MII); + pruss_cfg_miirt_enable(pruss, true); + pruss_cfg_xfr_enable(pruss, true); + + /* Get PRUSS mem resources */ + /* OCMC is system resource which we get separately */ + for (i = 0; i < ARRAY_SIZE(pruss_mem_ids); i++) { + /* skip appropriate DRAM if not required */ + if (!eth0_node && i == PRUETH_MEM_DRAM0) + continue; + + if (!eth1_node && i == PRUETH_MEM_DRAM1) + continue; + + ret = pruss_request_mem_region(pruss, pruss_mem_ids[i], + &prueth->mem[i]); + if (ret) { + dev_err(dev, "unable to get PRUSS resource %d: %d\n", + i, ret); + goto put_mem; + } + } + + prueth->sram_pool = of_gen_pool_get(np, "sram", 0); + if (!prueth->sram_pool) { + dev_err(dev, "unable to get SRAM pool\n"); + ret = -ENODEV; + + goto put_mem; + } + prueth->mem[PRUETH_MEM_OCMC].va = + (void __iomem *)gen_pool_alloc(prueth->sram_pool, + OCMC_RAM_SIZE); + if (!prueth->mem[PRUETH_MEM_OCMC].va) { + dev_err(dev, "unable to allocate OCMC resource\n"); + ret = -ENOMEM; + goto put_mem; + } + prueth->mem[PRUETH_MEM_OCMC].pa = + gen_pool_virt_to_phys(prueth->sram_pool, + (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va); + prueth->mem[PRUETH_MEM_OCMC].size = OCMC_RAM_SIZE; + dev_dbg(dev, "ocmc: pa %pa va %p size %#zx\n", + &prueth->mem[PRUETH_MEM_OCMC].pa, + prueth->mem[PRUETH_MEM_OCMC].va, + prueth->mem[PRUETH_MEM_OCMC].size); + + if (IS_ENABLED(CONFIG_HSR) && prueth->fw_data->support_lre) + has_lre = true; + + /* if lre is supported, then both eth nodes to be present in + * DT node. If not, reset the support flag + */ + if (has_lre && (!eth0_node || !eth1_node)) + has_lre = false; + + if (has_lre) { + /* need to configure interrupts per queue common for + * both ports + */ + prueth->hp = devm_kzalloc(dev, + sizeof(struct prueth_ndev_priority), + GFP_KERNEL); + if (!prueth->hp) { + ret = -ENOMEM; + goto free_pool; + } + prueth->lp = devm_kzalloc(dev, + sizeof(struct prueth_ndev_priority), + GFP_KERNEL); + if (!prueth->hp) { + ret = -ENOMEM; + goto free_pool; + } + + prueth->lre_stats = devm_kzalloc(dev, + sizeof(*prueth->lre_stats), + GFP_KERNEL); + if (!prueth->lre_stats) { + ret = -ENOMEM; + goto free_pool; + } + + prueth->rx_lpq_irq = of_irq_get_byname(np, "rx_lre_lp"); + prueth->rx_hpq_irq = of_irq_get_byname(np, "rx_lre_hp"); + if (prueth->rx_lpq_irq < 0 || prueth->rx_hpq_irq < 0) + has_lre = false; + } + prueth->support_lre = has_lre; + + /* setup netdev interfaces */ + if (eth0_node) { + ret = prueth_netdev_init(prueth, eth0_node); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_err(dev, "netdev init %s failed: %d\n", + eth0_node->name, ret); + } + goto free_pool; + } + } + + if (eth1_node) { + ret = prueth_netdev_init(prueth, eth1_node); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_err(dev, "netdev init %s failed: %d\n", + eth1_node->name, ret); + } + goto netdev_exit; + } + } + + prueth->iep = icss_iep_get(np); + if (IS_ERR(prueth->iep)) { + ret = PTR_ERR(prueth->iep); + dev_err(dev, "unable to get IEP\n"); + goto netdev_exit; + } + + prueth_set_fw_offsets(prueth); + prueth_hostinit(prueth); + + /* register the network devices */ + if (eth0_node) { + ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); + if (ret) { + dev_err(dev, "can't register netdev for port MII0"); + goto iep_put; + } + + prueth->registered_netdevs[PRUETH_MAC0] = prueth->emac[PRUETH_MAC0]->ndev; + } + + if (eth1_node) { + ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); + if (ret) { + dev_err(dev, "can't register netdev for port MII1"); + goto netdev_unregister; + } + + prueth->registered_netdevs[PRUETH_MAC1] = prueth->emac[PRUETH_MAC1]->ndev; + } + + ret = prueth_register_notifiers(prueth); + if (ret) { + dev_err(dev, "can't register switchdev notifiers"); + goto netdev_unregister; + } + + eth_random_addr(prueth->base_mac); + + dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", + (!eth0_node || !eth1_node) ? "single" : "dual"); + + return 0; + +netdev_unregister: + for (i = 0; i < PRUETH_NUM_MACS; i++) { + if (!prueth->registered_netdevs[i]) + continue; + unregister_netdev(prueth->registered_netdevs[i]); + } + +iep_put: + icss_iep_put(prueth->iep); +netdev_exit: + for (i = 0; i < PRUETH_NUM_MACS; i++) { + struct device_node *eth_node; + + eth_node = prueth->eth_node[i]; + if (!eth_node) + continue; + + prueth_netdev_exit(prueth, eth_node); + } + +free_pool: + gen_pool_free(prueth->sram_pool, + (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va, OCMC_RAM_SIZE); + +put_mem: + pruss_cfg_ocp_master_ports(prueth->pruss, 0); + for (i = PRUETH_MEM_DRAM0; i < PRUETH_MEM_OCMC; i++) { + if (prueth->mem[i].va) + pruss_release_mem_region(pruss, &prueth->mem[i]); + } + +put_pruss: + pruss_put(prueth->pruss); + +put_pru1: + if (eth1_node) + pru_rproc_put(prueth->pru1); +put_pru0: + if (eth0_node) + pru_rproc_put(prueth->pru0); + +put_node: + of_node_put(eth1_node); + of_node_put(eth0_node); + + return ret; +} + +static int prueth_remove(struct platform_device *pdev) +{ + struct device_node *eth_node; + struct prueth *prueth = platform_get_drvdata(pdev); + int i; + + unregister_netdevice_notifier(&prueth->prueth_ndev_nb); + prueth_sw_unregister_notifiers(prueth); + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + if (!prueth->registered_netdevs[i]) + continue; + unregister_netdev(prueth->registered_netdevs[i]); + } + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + eth_node = prueth->eth_node[i]; + if (!eth_node) + continue; + + prueth_netdev_exit(prueth, eth_node); + of_node_put(eth_node); + } + + gen_pool_free(prueth->sram_pool, + (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va, + OCMC_RAM_SIZE); + + for (i = PRUETH_MEM_DRAM0; i < PRUETH_MEM_OCMC; i++) { + if (prueth->mem[i].va) + pruss_release_mem_region(prueth->pruss, &prueth->mem[i]); + } + + icss_iep_put(prueth->iep); + + pruss_put(prueth->pruss); + + if (prueth->eth_node[PRUETH_MAC0]) + pru_rproc_put(prueth->pru1); + if (prueth->eth_node[PRUETH_MAC1]) + pru_rproc_put(prueth->pru0); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int prueth_suspend(struct device *dev) +{ + struct prueth *prueth = dev_get_drvdata(dev); + struct net_device *ndev; + int i, ret; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + ndev = prueth->registered_netdevs[i]; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + netif_device_detach(ndev); + ret = emac_ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return ret; + } + } + } + + pruss_cfg_ocp_master_ports(prueth->pruss, 0); + + return 0; +} + +static int prueth_resume(struct device *dev) +{ + struct prueth *prueth = dev_get_drvdata(dev); + struct net_device *ndev; + int i, ret; + + pruss_cfg_ocp_master_ports(prueth->pruss, 1); + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + ndev = prueth->registered_netdevs[i]; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + ret = emac_ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return ret; + } + netif_device_attach(ndev); + } + } + + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops prueth_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(prueth_suspend, prueth_resume) +}; + +/* AM33xx SoC-specific firmware data */ +static struct prueth_private_data am335x_prueth_pdata = { + .fw_pru[PRUSS_PRU0] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am335x-pru0-prueth-fw.elf", + }, + .fw_pru[PRUSS_PRU1] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am335x-pru1-prueth-fw.elf", + }, + .support_lre = false, +}; + +/* AM437x SoC-specific firmware data */ +static struct prueth_private_data am437x_prueth_pdata = { + .fw_pru[PRUSS_PRU0] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am437x-pru0-prueth-fw.elf", + }, + .fw_pru[PRUSS_PRU1] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am437x-pru1-prueth-fw.elf", + }, + .support_lre = false, +}; + +/* AM57xx SoC-specific firmware data */ +static struct prueth_private_data am57xx_prueth_pdata = { + .fw_pru[PRUSS_PRU0] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am57xx-pru0-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_HSR] = + "ti-pruss/am57xx-pru0-pruhsr-fw.elf", + .fw_name[PRUSS_ETHTYPE_PRP] = + "ti-pruss/am57xx-pru0-pruprp-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] = + "ti-pruss/am57xx-pru0-prusw-fw.elf", + }, + .fw_pru[PRUSS_PRU1] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am57xx-pru1-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_HSR] = + "ti-pruss/am57xx-pru1-pruhsr-fw.elf", + .fw_name[PRUSS_ETHTYPE_PRP] = + "ti-pruss/am57xx-pru1-pruprp-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] = + "ti-pruss/am57xx-pru1-prusw-fw.elf", + }, + .support_lre = true, + .support_switch = true, +}; + +/* 66AK2G SoC-specific firmware data */ +static struct prueth_private_data k2g_prueth_pdata = { + .fw_pru[PRUSS_PRU0] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/k2g-pru0-prueth-fw.elf", + }, + .fw_pru[PRUSS_PRU1] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/k2g-pru1-prueth-fw.elf", + }, + .support_lre = false, +}; + +static const struct of_device_id prueth_dt_match[] = { + { .compatible = "ti,am57-prueth", .data = &am57xx_prueth_pdata, }, + { .compatible = "ti,am4376-prueth", .data = &am437x_prueth_pdata, }, + { .compatible = "ti,am3359-prueth", .data = &am335x_prueth_pdata, }, + { .compatible = "ti,k2g-prueth", .data = &k2g_prueth_pdata, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, prueth_dt_match); + +static struct platform_driver prueth_driver = { + .probe = prueth_probe, + .remove = prueth_remove, + .driver = { + .name = "prueth", + .of_match_table = prueth_dt_match, + .pm = &prueth_dev_pm_ops, + }, +}; +module_platform_driver(prueth_driver); + +MODULE_AUTHOR("Roger Quadros "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("PRU Ethernet Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_fdb_tbl.h b/drivers/net/ethernet/ti/prueth_fdb_tbl.h --- a/drivers/net/ethernet/ti/prueth_fdb_tbl.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_fdb_tbl.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021 Texas Instruments Incorporated - https://www.ti.com */ +#ifndef __NET_TI_PRUSS_FDB_TBL_H +#define __NET_TI_PRUSS_FDB_TBL_H + +#include +#include +#include "prueth.h" + +#define ETHER_ADDR_LEN 6 + +/* 4 bytes */ +struct fdb_index_tbl_entry_t { + u16 bucket_idx; /* Bucket Table index of first Bucket + * with this MAC address + */ + u16 bucket_entries; /* Number of entries in this bucket */ +} __packed; + +/* 4 * 256 = 1024 = 0x200 bytes */ +struct fdb_index_array_t { + struct fdb_index_tbl_entry_t index_tbl_entry[FDB_INDEX_TBL_MAX_ENTRIES]; +} __packed; + +/* 10 bytes */ +struct fdb_mac_tbl_entry_t { + u8 mac[ETHER_ADDR_LEN]; + u16 age; + u8 port; /* 0 based: 0=port1, 1=port2 */ + u8 is_static:1; + u8 active:1; +} __packed; + +/* 10 * 256 = 2560 = 0xa00 bytes */ +struct fdb_mac_tbl_array_t { + struct fdb_mac_tbl_entry_t mac_tbl_entry[FDB_MAC_TBL_MAX_ENTRIES]; +} __packed; + +/* 1 byte */ +struct fdb_stp_config { + u8 state; /* per-port STP state (defined in FW header) */ +} __packed; + +/* 1 byte */ +struct fdb_flood_config { + u8 host_flood_enable:1; + u8 port1_flood_enable:1; + u8 port2_flood_enable:1; +} __packed; + +/* 2 byte */ +struct fdb_arbitration { + u8 host_lock; + u8 pru_locks; +} __packed; + +struct fdb_tbl { + struct fdb_index_array_t *index_a; /* fdb index table */ + struct fdb_mac_tbl_array_t *mac_tbl_a; /* fdb mac table */ + struct fdb_stp_config *port1_stp_cfg; /* port 1 strp config */ + struct fdb_stp_config *port2_stp_cfg; /* port 2 strp config */ + struct fdb_flood_config *flood_enable_flags; /* per-port flood enable */ + struct fdb_arbitration *locks; /* fdb locking mechanism */ + u16 total_entries; /* total num entries in hash table */ +}; + +#endif diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth.h b/drivers/net/ethernet/ti/prueth.h --- a/drivers/net/ethernet/ti/prueth.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,491 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* PRU ICSS Ethernet driver + * + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#ifndef __NET_TI_PRUETH_H +#define __NET_TI_PRUETH_H + +#include +#include +#include +#include +#include + +#include "icss_switch.h" +#include "prueth_ptp.h" + +#define PRUETH_NUMQUEUES 5 + +/* PRUSS local memory map */ +#define ICSS_LOCAL_SHARED_RAM 0x00010000 + +#define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */ +#define EMAC_MAX_PKTLEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) + +#define PRUETH_NSP_TIMER_MS (100) /* Refresh NSP counters every 100ms */ +/* default timer for NSP and HSR/PRP */ +#define PRUETH_TIMER_MS (10) + +#define PRUETH_REG_DUMP_VER 1 + +/* Encoding: 32-16: Reserved, 16-8: Reg dump version, 8-0: Ethertype */ +#define PRUETH_REG_DUMP_GET_VER(x) ((PRUETH_REG_DUMP_VER << 8) | ((x)->eth_type)) + +/* PRU Ethernet Type - Ethernet functionality (protocol + * implemented) provided by the PRU firmware being loaded. + */ +enum pruss_ethtype { + PRUSS_ETHTYPE_EMAC = 0, + PRUSS_ETHTYPE_HSR, + PRUSS_ETHTYPE_PRP, + PRUSS_ETHTYPE_SWITCH, + PRUSS_ETHTYPE_MAX, +}; + +#define PRUETH_IS_EMAC(p) ((p)->eth_type == PRUSS_ETHTYPE_EMAC) +#define PRUETH_IS_SWITCH(p) ((p)->eth_type == PRUSS_ETHTYPE_SWITCH) +#define PRUETH_IS_HSR(p) ((p)->eth_type == PRUSS_ETHTYPE_HSR) +#define PRUETH_IS_PRP(p) ((p)->eth_type == PRUSS_ETHTYPE_PRP) +#define PRUETH_IS_LRE(p) (PRUETH_IS_HSR(p) || PRUETH_IS_PRP(p)) + +/** + * struct prueth_queue_desc - Queue descriptor + * @rd_ptr: Read pointer, points to a buffer descriptor in Shared PRU RAM. + * @wr_ptr: Write pointer, points to a buffer descriptor in Shared PRU RAM. + * @busy_s: Slave queue busy flag, set by slave(us) to request access from + * master(PRU). + * @status: Bit field status register, Bits: + * 0: Master queue busy flag. + * 1: Packet has been placed in collision queue. + * 2: Packet has been discarded due to overflow. + * @max_fill_level: Maximum queue usage seen. + * @overflow_cnt: Count of queue overflows. + * + * Each port has up to 4 queues with variable length. The queue is processed + * as ring buffer with read and write pointers. Both pointers are address + * pointers and increment by 4 for each buffer descriptor position. Queue has + * a length defined in constants and a status. + */ +struct prueth_queue_desc { + u16 rd_ptr; + u16 wr_ptr; + u8 busy_s; + u8 status; + u8 max_fill_level; + u8 overflow_cnt; +} __packed; + +/** + * struct prueth_queue - Information about a queue in memory + * @buffer_offset: buffer offset in OCMC RAM + * @queue_desc_offset: queue descriptor offset in Shared RAM + * @buffer_desc_offset: buffer descriptors offset in Shared RAM + * @buffer_desc_end: end address of buffer descriptors in Shared RAM + */ +struct prueth_queue_info { + u16 buffer_offset; + u16 queue_desc_offset; + u16 buffer_desc_offset; + u16 buffer_desc_end; +} __packed; + +/** + * struct prueth_packet_info - Info about a packet in buffer + * @start_offset: start offset of the frame in the buffer for HSR/PRP + * @shadow: this packet is stored in the collision queue + * @port: port packet is on + * @length: length of packet + * @broadcast: this packet is a broadcast packet + * @error: this packet has an error + * @sv_frame: indicate if the frame is a SV frame for HSR/PRP + * @lookup_success: src mac found in FDB + * @flood: packet is to be flooded + * @timstamp: Specifies if timestamp is appended to the packet + */ +struct prueth_packet_info { + bool start_offset; + bool shadow; + unsigned int port; + unsigned int length; + bool broadcast; + bool error; + bool sv_frame; + bool lookup_success; + bool flood; + bool timestamp; +}; + +/** + * struct port_statistics - Statistics structure for capturing statistics + * on PRUs + * @tx_bcast: Number of broadcast packets sent + * @tx_mcast:Number of multicast packets sent + * @tx_ucast:Number of unicast packets sent + * + * @tx_octets:Number of undersized frames rcvd + * + * @rx_bcast:Number of broadcast packets rcvd + * @rx_mcast:Number of multicast packets rcvd + * @rx_ucast:Number of unicast packets rcvd + * + * @rx_octets:Number of Rx packets + * + * @tx64byte:Number of 64 byte packets sent + * @tx65_127byte:Number of 65-127 byte packets sent + * @tx128_255byte:Number of 128-255 byte packets sent + * @tx256_511byte:Number of 256-511 byte packets sent + * @tx512_1023byte:Number of 512-1023 byte packets sent + * @tx1024byte:Number of 1024 and larger size packets sent + * + * @rx64byte:Number of 64 byte packets rcvd + * @rx65_127byte:Number of 65-127 byte packets rcvd + * @rx128_255byte:Number of 128-255 byte packets rcvd + * @rx256_511byte:Number of 256-511 byte packets rcvd + * @rx512_1023byte:Number of 512-1023 byte packets rcvd + * @rx1024byte:Number of 1024 and larger size packets rcvd + * + * @late_coll:Number of late collisions(Half Duplex) + * @single_coll:Number of single collisions (Half Duplex) + * @multi_coll:Number of multiple collisions (Half Duplex) + * @excess_coll:Number of excess collisions(Half Duplex) + * + * @rx_misalignment_frames:Number of non multiple of 8 byte frames rcvd + * @stormprev_counter:Number of packets dropped because of Storm Prevention + * @mac_rxerror:Number of MAC receive errors + * @sfd_error:Number of invalid SFD + * @def_tx:Number of transmissions deferred + * @mac_txerror:Number of MAC transmit errors + * @rx_oversized_frames:Number of oversized frames rcvd + * @rx_undersized_frames:Number of undersized frames rcvd + * @rx_crc_frames:Number of CRC error frames rcvd + * @dropped_packets:Number of packets dropped due to link down on opposite port + * + * @tx_hwq_overflow:Hardware Tx Queue (on PRU) over flow count + * @tx_hwq_underflow:Hardware Tx Queue (on PRU) under flow count + * + * @u32 cs_error: Number of carrier sense errors + * @sqe_test_error: Number of MAC receive errors + * + * Above fields are aligned so that it's consistent + * with the memory layout in PRU DRAM, this is to facilitate easy + * memcpy. Don't change the order of the fields. + * + * @vlan_dropped: Number of VLAN tagged packets dropped + * @multicast_dropped: Number of multicast packets dropped + */ +struct port_statistics { + u32 tx_bcast; + u32 tx_mcast; + u32 tx_ucast; + + u32 tx_octets; + + u32 rx_bcast; + u32 rx_mcast; + u32 rx_ucast; + + u32 rx_octets; + + u32 tx64byte; + u32 tx65_127byte; + u32 tx128_255byte; + u32 tx256_511byte; + u32 tx512_1023byte; + u32 tx1024byte; + + u32 rx64byte; + u32 rx65_127byte; + u32 rx128_255byte; + u32 rx256_511byte; + u32 rx512_1023byte; + u32 rx1024byte; + + u32 late_coll; + u32 single_coll; + u32 multi_coll; + u32 excess_coll; + + u32 rx_misalignment_frames; + u32 stormprev_counter_bc; + u32 stormprev_counter_mc; + u32 stormprev_counter_uc; + u32 mac_rxerror; + u32 sfd_error; + u32 def_tx; + u32 mac_txerror; + u32 rx_oversized_frames; + u32 rx_undersized_frames; + u32 rx_crc_frames; + u32 dropped_packets; + + u32 tx_hwq_overflow; + u32 tx_hwq_underflow; + + u32 cs_error; + u32 sqe_test_error; + + u32 vlan_dropped; + u32 multicast_dropped; +} __packed; + +/* In switch mode there are 3 real ports i.e. 3 mac addrs. + * however Linux sees only the host side port. The other 2 ports + * are the switch ports. + * In emac mode there are 2 real ports i.e. 2 mac addrs. + * Linux sees both the ports. + */ +enum prueth_port { + PRUETH_PORT_HOST = 0, /* host side port */ + PRUETH_PORT_MII0, /* physical port MII 0 */ + PRUETH_PORT_MII1, /* physical port MII 1 */ +}; + +enum prueth_mac { + PRUETH_MAC0 = 0, + PRUETH_MAC1, + PRUETH_NUM_MACS, +}; + +/* In both switch & emac modes there are 3 port queues + * EMAC mode: + * RX packets for both MII0 & MII1 ports come on + * QUEUE_HOST. + * TX packets for MII0 go on QUEUE_MII0, TX packets + * for MII1 go on QUEUE_MII1. + * Switch mode: + * Host port RX packets come on QUEUE_HOST + * TX packets might have to go on MII0 or MII1 or both. + * MII0 TX queue is QUEUE_MII0 and MII1 TX queue is + * QUEUE_MII1. + */ +enum prueth_port_queue_id { + PRUETH_PORT_QUEUE_HOST = 0, + PRUETH_PORT_QUEUE_MII0, + PRUETH_PORT_QUEUE_MII1, + PRUETH_PORT_QUEUE_MAX, +}; + +/* Each port queue has 4 queues and 1 collision queue */ +enum prueth_queue_id { + PRUETH_QUEUE1 = 0, + PRUETH_QUEUE2, + PRUETH_QUEUE3, + PRUETH_QUEUE4, + PRUETH_COLQUEUE, /* collision queue */ +}; + +/* PRUeth memory range identifiers */ +enum prueth_mem { + PRUETH_MEM_DRAM0 = 0, + PRUETH_MEM_DRAM1, + PRUETH_MEM_SHARED_RAM, + PRUETH_MEM_OCMC, + PRUETH_MEM_MAX, +}; + +/* Firmware offsets/size information */ +struct prueth_fw_offsets { + u32 index_array_offset; + u32 bin_array_offset; + u32 nt_array_offset; + u32 index_array_loc; + u32 bin_array_loc; + u32 nt_array_loc; + u32 index_array_max_entries; + u32 bin_array_max_entries; + u32 nt_array_max_entries; + u32 vlan_ctrl_byte; + u32 vlan_filter_tbl; + u32 mc_ctrl_byte; + u32 mc_filter_mask; + u32 mc_filter_tbl; + /* IEP wrap is used in the rx packet ordering logic and + * is different for ICSSM v1.0 vs 2.1 + */ + u32 iep_wrap; + u16 hash_mask; +}; + +/** + * @fw_name: firmware names of firmware to run on PRU + */ +struct prueth_firmware { + const char *fw_name[PRUSS_ETHTYPE_MAX]; +}; + +/** + * struct prueth_private_data - PRU Ethernet private data + * @fw_names: firmware names to be used for PRUSS ethernet usecases + */ +struct prueth_private_data { + const struct prueth_firmware fw_pru[PRUSS_NUM_PRUS]; + bool support_lre; + bool support_switch; +}; + +struct nsp_counter { + unsigned long cookie; + u16 credit; +}; + +/* data for each emac port */ +struct prueth_emac { + struct prueth *prueth; + struct net_device *ndev; + u8 mac_addr[6]; + u32 msg_enable; + + int link; + int speed; + int duplex; + + const char *phy_id; + struct device_node *phy_node; + phy_interface_t phy_if; + struct phy_device *phydev; + struct rproc *pru; + + enum prueth_port port_id; + enum prueth_port_queue_id tx_port_queue; + + enum prueth_queue_id rx_queue_start; + enum prueth_queue_id rx_queue_end; + + enum prueth_mem dram; + + int rx_irq; + int tx_irq; + + struct prueth_queue_desc __iomem *rx_queue_descs; + struct prueth_queue_desc __iomem *tx_queue_descs; + + struct port_statistics stats; /* stats holder when i/f is down */ + unsigned char mc_filter_mask[ETH_ALEN]; /* for multicast filtering */ + + spinlock_t lock; /* serialize access */ + spinlock_t addr_lock; /* serialize access to VLAN/MC filter table */ + + struct nsp_counter nsp_bc; + struct nsp_counter nsp_mc; + struct nsp_counter nsp_uc; + bool nsp_enabled; + + int offload_fwd_mark; + + struct sk_buff *ptp_skb[PRUETH_PTP_TS_EVENTS]; + struct sk_buff *ptp_ct_skb[PRUETH_PTP_TS_EVENTS]; + spinlock_t ptp_skb_lock; /* serialize access */ + int emac_ptp_tx_irq; + int hsr_ptp_tx_irq; + bool ptp_tx_enable; + bool ptp_rx_enable; +}; + +struct prueth_ndev_priority { + struct net_device *ndev; + int priority; +}; + +/** + * struct prueth - PRUeth structure + * @dev: device + * @pruss: pruss handle + * @pru0: rproc instance to PRU0 + * @pru1: rproc instance to PRU1 + * @mem: PRUSS memory resources we need to access + * @sram_pool: OCMC ram pool for buffers + * @mii_rt: regmap to mii_rt block + * @iep: Pointer to ICSS IEP data + * + * @eth_node: node for each emac node + * @emac: emac data for three ports, one host and two physical + * @registered_netdevs: net device for each registered emac + * + * @hw_bridge_dev: pointer to hw_bridge device + * @fdb_tbl: pointer to FDB table struct + * + * @prueth_ndev_nb: netdev notifier block + * @prueth_sw_switchdev_notifier: non blocking switchdev notifier block + * @prueth_sw_switchdev_bl_notifier: blocking switchdev notifier block + * + * @emac_configured: bit mask to configured ports + * @br_members: bit mask indicating ports that are part of the bridge + * @eth_type: flag indicate firmware mode (Dual emac vs Switch etc) + * @base_mac: random mac used as physical ID for each port of a switch + */ +struct prueth { + struct device *dev; + struct pruss *pruss; + struct rproc *pru0, *pru1; + struct pruss_mem_region mem[PRUETH_MEM_MAX]; + struct gen_pool *sram_pool; + struct regmap *mii_rt; + struct icss_iep *iep; + struct hrtimer tbl_check_timer; + const struct prueth_private_data *fw_data; + struct prueth_fw_offsets *fw_offsets; + + /* HSR-PRP */ + bool support_lre; + struct prueth_ndev_priority *hp, *lp; + int rx_lpq_irq; + int rx_hpq_irq; + unsigned int hsr_mode; + unsigned int tbl_check_period; + unsigned int node_table_clear; + unsigned int node_table_clear_last_cmd; + unsigned int tbl_check_mask; + enum iec62439_3_tr_modes prp_tr_mode; + struct node_tbl *nt; + struct nt_queue_t *mac_queue; + struct kthread_worker *nt_kworker; + struct kthread_work nt_work; + u32 rem_cnt; + /* lock between kthread worker and rx packet processing code */ + spinlock_t nt_lock; + struct lre_statistics *lre_stats; + + struct device_node *eth_node[PRUETH_NUM_MACS]; + struct prueth_emac *emac[PRUETH_NUM_MACS]; + struct net_device *registered_netdevs[PRUETH_NUM_MACS]; + struct device_node *prueth_np; + + struct net_device *hw_bridge_dev; + struct fdb_tbl *fdb_tbl; + + struct notifier_block prueth_ndev_nb; + struct notifier_block prueth_sw_switchdev_notifier; + struct notifier_block prueth_sw_switchdev_bl_notifier; + + unsigned int eth_type; + /* mutex to enter critical region in ndo_open() and + * ndo_kill() as common resources for switch based firmware is + * to be initialized for the first port in ndo_open() and + * cleaned up on last port in ndo_stop(). + */ + u8 emac_configured; + u8 br_members; + u8 base_mac[ETH_ALEN]; +}; + +int emac_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data); +void parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, + struct prueth_packet_info *pkt_info); +int emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, + struct prueth_packet_info *pkt_info, + const struct prueth_queue_info *rxqueue); +int emac_add_del_vid(struct prueth_emac *emac, + bool add, __be16 proto, u16 vid); +irqreturn_t prueth_ptp_tx_irq_handle(int irq, void *dev); +irqreturn_t prueth_ptp_tx_irq_work(int irq, void *dev); + +extern const struct prueth_queue_desc queue_descs[][NUM_QUEUES]; + +void emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash); +void emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash); +u8 emac_get_mc_hash(u8 *mac, u8 *mask); +#endif /* __NET_TI_PRUETH_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_lre.c b/drivers/net/ethernet/ti/prueth_lre.c --- a/drivers/net/ethernet/ti/prueth_lre.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_lre.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments PRUETH hsr/prp Link Redunancy Entity (LRE) Driver. + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + */ + +#include +#include +#include +#include + +#include "icss_lre_firmware.h" +#include "prueth.h" +#include "prueth_lre.h" +#include "prueth_switch.h" + +void prueth_lre_config_check_flags(struct prueth *prueth) +{ + void __iomem *dram1 = prueth->mem[PRUETH_MEM_DRAM1].va; + + /* HSR/PRP: initialize check table when first port is up */ + if (prueth->emac_configured) + return; + + prueth->tbl_check_mask = (ICSS_LRE_HOST_TIMER_NODE_TABLE_CHECK_BIT | + ICSS_LRE_HOST_TIMER_HOST_TABLE_CHECK_BIT); + if (PRUETH_IS_HSR(prueth)) + prueth->tbl_check_mask |= + ICSS_LRE_HOST_TIMER_PORT_TABLE_CHECK_BITS; + writel(prueth->tbl_check_mask, dram1 + ICSS_LRE_HOST_TIMER_CHECK_FLAGS); +} + +/* A group of PCPs are mapped to a Queue. This is the size of firmware + * array in shared memory + */ +#define PCP_GROUP_TO_QUEUE_MAP_SIZE 4 + +/* PRU firmware default PCP to priority Queue map for ingress & egress + * + * At ingress to Host + * ================== + * byte 0 => PRU 1, PCP 0-3 => Q3 + * byte 1 => PRU 1, PCP 4-7 => Q2 + * byte 2 => PRU 0, PCP 0-3 => Q1 + * byte 3 => PRU 0, PCP 4-7 => Q0 + * + * At egress to wire/network on PRU-0 and PRU-1 + * ============================================ + * byte 0 => Host, PCP 0-3 => Q3 + * byte 1 => Host, PCP 4-7 => Q2 + * + * PRU-0 + * ----- + * byte 2 => PRU-1, PCP 0-3 => Q1 + * byte 3 => PRU-1, PCP 4-7 => Q0 + * + * PRU-1 + * ----- + * byte 2 => PRU-0, PCP 0-3 => Q1 + * byte 3 => PRU-0, PCP 4-7 => Q0 + * + * queue names below are named 1 based. i.e PRUETH_QUEUE1 is Q0, + * PRUETH_QUEUE2 is Q1 and so forth. Firmware convention is that + * a lower queue number has higher priority than a higher queue + * number. + */ +static u8 fw_pcp_default_priority_queue_map[PCP_GROUP_TO_QUEUE_MAP_SIZE] = { + /* port 2 or PRU 1 */ + PRUETH_QUEUE4, PRUETH_QUEUE3, + /* port 1 or PRU 0 */ + PRUETH_QUEUE2, PRUETH_QUEUE1, +}; + +static void prueth_lre_pcp_queue_map_config(struct prueth *prueth) +{ + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + memcpy_toio(sram + ICSS_LRE_QUEUE_2_PCP_MAP_OFFSET, + &fw_pcp_default_priority_queue_map[0], + PCP_GROUP_TO_QUEUE_MAP_SIZE); +} + +static void prueth_lre_host_table_init(struct prueth *prueth) +{ + void __iomem *dram0 = prueth->mem[PRUETH_MEM_DRAM0].va; + void __iomem *dram1 = prueth->mem[PRUETH_MEM_DRAM1].va; + + memset_io(dram0 + ICSS_LRE_DUPLICATE_HOST_TABLE, 0, + ICSS_LRE_DUPLICATE_HOST_TABLE_DMEM_SIZE); + + writel(ICSS_LRE_DUPLICATE_HOST_TABLE_SIZE_INIT, + dram1 + ICSS_LRE_DUPLICATE_HOST_TABLE_SIZE); + + writel(ICSS_LRE_TABLE_CHECK_RESOLUTION_10_MS, + dram1 + ICSS_LRE_DUPLI_HOST_CHECK_RESO); + + writel(ICSS_LRE_MASTER_SLAVE_BUSY_BITS_CLEAR, + dram1 + ICSS_LRE_HOST_DUPLICATE_ARBITRATION); +} + +static void pru2host_mac(u8 *mac) +{ + swap(mac[0], mac[3]); + swap(mac[1], mac[2]); + swap(mac[4], mac[5]); +} + +static u16 get_hash(u8 *mac, u16 hash_mask) +{ + int j; + u16 hash; + + for (j = 0, hash = 0; j < ETH_ALEN; j++) + hash ^= mac[j]; + hash = hash & hash_mask; + + return hash; +} + +static void pru_spin_lock(struct node_tbl *nt) +{ + while (1) { + nt->nt_info->arm_lock = 1; + if (!nt->nt_info->fw_lock) + break; + nt->nt_info->arm_lock = 0; + } +} + +static inline void pru_spin_unlock(struct node_tbl *nt) +{ + nt->nt_info->arm_lock = 0; +} + +int prueth_lre_nt_insert(struct prueth *prueth, + u8 *mac, int port, int sv_frame, int proto) +{ + struct nt_queue_t *q = prueth->mac_queue; + unsigned long flags; + int ret = LRE_OK; + + /* Will encounter a null mac_queue if we are in the middle of + * ndo_close. So check and return. Otherwise a kernel crash is + * seen when doing ifdown continuously. + */ + if (!q) + return ret; + + spin_lock_irqsave(&prueth->nt_lock, flags); + if (q->full) { + ret = LRE_ERR; + } else { + memcpy(q->nt_queue[q->wr_ind].mac, mac, ETH_ALEN); + q->nt_queue[q->wr_ind].sv_frame = sv_frame; + q->nt_queue[q->wr_ind].port_id = port; + q->nt_queue[q->wr_ind].proto = proto; + + q->wr_ind++; + q->wr_ind &= (PRUETH_MAC_QUEUE_MAX - 1); + if (q->wr_ind == q->rd_ind) + q->full = true; + } + spin_unlock_irqrestore(&prueth->nt_lock, flags); + + return ret; +} + +static inline bool node_expired(struct node_tbl *nt, u16 node, u16 forget_time) +{ + struct node_tbl_t nt_node = nt->nt_array->node_tbl[node]; + + return ((nt_node.time_last_seen_s > forget_time || + nt_node.status & ICSS_LRE_NT_REM_NODE_TYPE_SANAB) && + nt_node.time_last_seen_a > forget_time && + nt_node.time_last_seen_b > forget_time); +} + +#define IND_BIN_NO(x) nt->index_array->index_tbl[x].bin_no_entries +#define IND_BINOFS(x) nt->index_array->index_tbl[x].bin_offset +#define BIN_NODEOFS(x) nt->bin_array->bin_tbl[x].node_tbl_offset + +static void _prueth_lre_init_node_table(struct prueth *prueth) +{ + struct nt_queue_t *q = prueth->mac_queue; + struct node_tbl *nt = prueth->nt; + int j; + + const struct prueth_fw_offsets *fw_offsets = prueth->fw_offsets; + + nt->nt_array = prueth->mem[fw_offsets->nt_array_loc].va + + fw_offsets->nt_array_offset; + memset_io(nt->nt_array, 0, sizeof(struct node_tbl_t) * + fw_offsets->nt_array_max_entries); + + nt->bin_array = prueth->mem[fw_offsets->bin_array_loc].va + + fw_offsets->bin_array_offset; + memset_io(nt->bin_array, 0, sizeof(struct bin_tbl_t) * + fw_offsets->bin_array_max_entries); + + nt->index_array = prueth->mem[fw_offsets->index_array_loc].va + + fw_offsets->index_array_offset; + memset_io(nt->index_array, 0, sizeof(struct node_index_tbl_t) * + fw_offsets->index_array_max_entries); + + nt->nt_info = prueth->mem[fw_offsets->nt_array_loc].va + + fw_offsets->nt_array_offset + + (sizeof(struct node_tbl_t) * + fw_offsets->nt_array_max_entries); + memset_io(nt->nt_info, 0, sizeof(struct node_tbl_info_t)); + + nt->nt_lre_cnt = + prueth->mem[PRUETH_MEM_SHARED_RAM].va + ICSS_LRE_CNT_NODES; + memset_io(nt->nt_lre_cnt, 0, sizeof(struct node_tbl_lre_cnt_t)); + + nt->nt_array_max_entries = fw_offsets->nt_array_max_entries; + nt->bin_array_max_entries = fw_offsets->bin_array_max_entries; + nt->index_array_max_entries = fw_offsets->index_array_max_entries; + nt->hash_mask = fw_offsets->hash_mask; + + for (j = 0; j < fw_offsets->index_array_max_entries; j++) + IND_BINOFS(j) = fw_offsets->bin_array_max_entries; + for (j = 0; j < fw_offsets->bin_array_max_entries; j++) + BIN_NODEOFS(j) = fw_offsets->nt_array_max_entries; + for (j = 0; j < fw_offsets->nt_array_max_entries; j++) + nt->nt_array->node_tbl[j].entry_state = ICSS_LRE_NODE_FREE; + + q->rd_ind = 0; + q->wr_ind = 0; + q->full = false; +} + +static u16 find_free_bin(struct node_tbl *nt) +{ + u16 j; + + for (j = 0; j < nt->bin_array_max_entries; j++) + if (BIN_NODEOFS(j) == nt->nt_array_max_entries) + break; + + return j; +} + +/* find first free node table slot and write it to the next_free_slot */ +static u16 next_free_slot_update(struct node_tbl *nt) +{ + int j; + + nt->nt_info->next_free_slot = nt->nt_array_max_entries; + for (j = 0; j < nt->nt_array_max_entries; j++) { + if (nt->nt_array->node_tbl[j].entry_state == + ICSS_LRE_NODE_FREE) { + nt->nt_info->next_free_slot = j; + break; + } + } + + return nt->nt_info->next_free_slot; +} + +static void inc_time(u16 *t) +{ + *t += 1; + if (*t > ICSS_LRE_MAX_FORGET_TIME) + *t = ICSS_LRE_MAX_FORGET_TIME; +} + +static void node_table_update_time(struct node_tbl *nt) +{ + int j; + u16 ofs; + struct nt_array_t *nt_arr = nt->nt_array; + struct node_tbl_t *node; + + for (j = 0; j < nt->bin_array_max_entries; j++) { + ofs = nt->bin_array->bin_tbl[j].node_tbl_offset; + if (ofs < nt->nt_array_max_entries) { + node = &nt_arr->node_tbl[ofs]; + inc_time(&node->time_last_seen_a); + inc_time(&node->time_last_seen_b); + /* increment time_last_seen_s if nod is not SAN */ + if ((node->status & + ICSS_LRE_NT_REM_NODE_TYPE_SANAB) == 0) + inc_time(&node->time_last_seen_s); + } + } +} + +static void write2node_slot(struct node_tbl *nt, u16 node, int port, + int sv_frame, int proto) +{ + memset(&nt->nt_array->node_tbl[node], 0, sizeof(struct node_tbl_t)); + nt->nt_array->node_tbl[node].entry_state = ICSS_LRE_NODE_TAKEN; + + if (port == 0x01) { + nt->nt_array->node_tbl[node].status = + ICSS_LRE_NT_REM_NODE_TYPE_SANA; + nt->nt_array->node_tbl[node].cnt_ra = 1; + if (sv_frame) + nt->nt_array->node_tbl[node].cnt_rx_sup_a = 1; + } else { + nt->nt_array->node_tbl[node].status = + ICSS_LRE_NT_REM_NODE_TYPE_SANB; + nt->nt_array->node_tbl[node].cnt_rb = 1; + if (sv_frame) + nt->nt_array->node_tbl[node].cnt_rx_sup_b = 1; + } + + if (sv_frame) { + nt->nt_array->node_tbl[node].status = (proto == LRE_PROTO_PRP) ? + ICSS_LRE_NT_REM_NODE_TYPE_DAN : + ICSS_LRE_NT_REM_NODE_TYPE_DAN | + ICSS_LRE_NT_REM_NODE_HSR_BIT; + } +} + +/* We assume that the _start_ cannot point to middle of a bin */ +static void update_indexes(u16 start, u16 end, struct node_tbl *nt) +{ + u16 hash, hash_prev; + + hash_prev = 0xffff; /* invalid hash */ + for (; start <= end; start++) { + hash = get_hash(nt->bin_array->bin_tbl[start].src_mac_id, + nt->hash_mask); + if (hash != hash_prev) + IND_BINOFS(hash) = start; + hash_prev = hash; + } +} + +/* start > end */ +static void move_up(u16 start, u16 end, struct node_tbl *nt, + bool update) +{ + u16 j = end; + + pru_spin_lock(nt); + + for (; j < start; j++) + memcpy(&nt->bin_array->bin_tbl[j], + &nt->bin_array->bin_tbl[j + 1], + sizeof(struct bin_tbl_t)); + + BIN_NODEOFS(start) = nt->nt_array_max_entries; + + if (update) + update_indexes(end, start + 1, nt); + + pru_spin_unlock(nt); +} + +/* start < end */ +static void move_down(u16 start, u16 end, struct node_tbl *nt, + bool update) +{ + u16 j = end; + + pru_spin_lock(nt); + + for (; j > start; j--) + memcpy(&nt->bin_array->bin_tbl[j], + &nt->bin_array->bin_tbl[j - 1], + sizeof(struct bin_tbl_t)); + + nt->bin_array->bin_tbl[start].node_tbl_offset = + nt->nt_array_max_entries; + + if (update) + update_indexes(start + 1, end, nt); + + pru_spin_unlock(nt); +} + +static int node_table_insert_from_queue(struct node_tbl *nt, + struct nt_queue_entry *entry) +{ + u8 macid[ETH_ALEN]; + u16 hash; + u16 index; + u16 free_node; + bool not_found; + u16 empty_slot; + + if (!nt) + return LRE_ERR; + + memcpy(macid, entry->mac, ETH_ALEN); + pru2host_mac(macid); + + hash = get_hash(macid, nt->hash_mask); + + not_found = 1; + if (IND_BIN_NO(hash) == 0) { + /* there is no bin for this hash, create one */ + index = find_free_bin(nt); + if (index == nt->bin_array_max_entries) + return LRE_ERR; + + IND_BINOFS(hash) = index; + } else { + for (index = IND_BINOFS(hash); + index < IND_BINOFS(hash) + IND_BIN_NO(hash); index++) { + if ((memcmp(nt->bin_array->bin_tbl[index].src_mac_id, + macid, ETH_ALEN) == 0)) { + not_found = 0; + break; + } + } + } + + if (not_found) { + free_node = next_free_slot_update(nt); + + /* at this point we might create a new bin and set + * bin_offset at the index table. It was only possible + * if we found a free slot in the bin table. + * So, it also must be a free slot in the node table + * and we will not exit here in this case. + * So, be don't have to take care about fixing IND_BINOFS() + * on return LRE_ERR + */ + if (free_node >= nt->nt_array_max_entries) + return LRE_ERR; + + /* if we are here, we have at least one empty slot in the bin + * table and one slot at the node table + */ + + IND_BIN_NO(hash)++; + + /* look for an empty slot downwards */ + for (empty_slot = index; + (BIN_NODEOFS(empty_slot) != nt->nt_array_max_entries) && + (empty_slot < nt->nt_array_max_entries); + empty_slot++) + ; + + /* if emptySlot != maxNodes => empty slot is found, + * else no space available downwards, look upwards + */ + if (empty_slot != nt->nt_array_max_entries) { + move_down(index, empty_slot, nt, true); + } else { + for (empty_slot = index - 1; + (BIN_NODEOFS(empty_slot) != + nt->nt_array_max_entries) && + (empty_slot > 0); + empty_slot--) + ; + /* we're sure to get a space here as nodetable + * has a empty slot, so no need to check for + * value of emptySlot + */ + move_up(index, empty_slot, nt, true); + } + + /* space created, now populate the values*/ + BIN_NODEOFS(index) = free_node; + memcpy(nt->bin_array->bin_tbl[index].src_mac_id, macid, + ETH_ALEN); + write2node_slot(nt, free_node, entry->port_id, entry->sv_frame, + entry->proto); + + nt->nt_lre_cnt->lre_cnt++; + } + + return LRE_OK; +} + +static void node_table_check_and_remove(struct node_tbl *nt, u16 forget_time) +{ + int j, end_bin; + u16 node; + u16 hash; + + /*loop to remove a node reaching NODE_FORGET_TIME*/ + for (j = 0; j < nt->bin_array_max_entries; j++) { + node = BIN_NODEOFS(j); + if (node >= nt->nt_array_max_entries) + continue; + + if (node_expired(nt, node, forget_time)) { + hash = get_hash(nt->bin_array->bin_tbl[j].src_mac_id, + nt->hash_mask); + + /* remove entry from bin array */ + end_bin = IND_BINOFS(hash) + IND_BIN_NO(hash) - 1; + + move_up(end_bin, j, nt, false); + (IND_BIN_NO(hash))--; + + if (!IND_BIN_NO(hash)) + IND_BINOFS(hash) = nt->bin_array_max_entries; + + nt->nt_array->node_tbl[node].entry_state = + ICSS_LRE_NODE_FREE; + BIN_NODEOFS(end_bin) = nt->nt_array_max_entries; + + nt->nt_lre_cnt->lre_cnt--; + } + } +} + +static int pop_queue(struct prueth *prueth, spinlock_t *lock) +{ + unsigned long flags; + struct node_tbl *nt = prueth->nt; + struct nt_queue_t *q = prueth->mac_queue; + struct nt_queue_entry one_mac; + int ret = 0; + + spin_lock_irqsave(lock, flags); + if (!q->full && q->wr_ind == q->rd_ind) { /* queue empty */ + ret = 1; + } else { + memcpy(&one_mac, &q->nt_queue[q->rd_ind], + sizeof(struct nt_queue_entry)); + spin_unlock_irqrestore(lock, flags); + node_table_insert_from_queue(nt, &one_mac); + spin_lock_irqsave(lock, flags); + q->rd_ind++; + q->rd_ind &= (PRUETH_MAC_QUEUE_MAX - 1); + q->full = false; + } + spin_unlock_irqrestore(lock, flags); + + return ret; +} + +static void pop_queue_process(struct prueth *prueth, spinlock_t *lock) +{ + while (pop_queue(prueth, lock) == 0) + ; +} + +static void prueth_lre_port_table_init(struct prueth *prueth) +{ + void __iomem *dram1 = prueth->mem[PRUETH_MEM_DRAM1].va; + + if (PRUETH_IS_HSR(prueth)) { + memset_io(dram1 + ICSS_LRE_DUPLICATE_PORT_TABLE_PRU0, 0, + ICSS_LRE_DUPLICATE_PORT_TABLE_DMEM_SIZE); + memset_io(dram1 + ICSS_LRE_DUPLICATE_PORT_TABLE_PRU1, 0, + ICSS_LRE_DUPLICATE_PORT_TABLE_DMEM_SIZE); + + writel(ICSS_LRE_DUPLICATE_PORT_TABLE_SIZE_INIT, + dram1 + ICSS_LRE_DUPLICATE_PORT_TABLE_SIZE); + } else { + writel(0, dram1 + ICSS_LRE_DUPLICATE_PORT_TABLE_SIZE); + } + + writel(ICSS_LRE_TABLE_CHECK_RESOLUTION_10_MS, + dram1 + ICSS_LRE_DUPLI_PORT_CHECK_RESO); +} + +static void prueth_lre_init(struct prueth *prueth) +{ + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + memset_io(sram + ICSS_LRE_START, 0, ICSS_LRE_STATS_DMEM_SIZE); + + writel(ICSS_LRE_IEC62439_CONST_DUPLICATE_DISCARD, + sram + ICSS_LRE_DUPLICATE_DISCARD); + writel(ICSS_LRE_IEC62439_CONST_TRANSP_RECEPTION_REMOVE_RCT, + sram + ICSS_LRE_TRANSPARENT_RECEPTION); + prueth->prp_tr_mode = IEC62439_3_TR_REMOVE_RCT; +} + +static void prueth_lre_dbg_init(struct prueth *prueth) +{ + void __iomem *dram0 = prueth->mem[PRUETH_MEM_DRAM0].va; + + memset_io(dram0 + ICSS_LRE_DBG_START, 0, + ICSS_LRE_DEBUG_COUNTER_DMEM_SIZE); +} + +static void prueth_lre_protocol_init(struct prueth *prueth) +{ + void __iomem *dram0 = prueth->mem[PRUETH_MEM_DRAM0].va; + void __iomem *dram1 = prueth->mem[PRUETH_MEM_DRAM1].va; + + if (PRUETH_IS_HSR(prueth)) + writew(prueth->hsr_mode, dram0 + ICSS_LRE_HSR_MODE); + + writel(ICSS_LRE_DUPLICATE_FORGET_TIME_400_MS, + dram1 + ICSS_LRE_DUPLI_FORGET_TIME); + writel(ICSS_LRE_SUP_ADDRESS_INIT_OCTETS_HIGH, + dram1 + ICSS_LRE_SUP_ADDR); + writel(ICSS_LRE_SUP_ADDRESS_INIT_OCTETS_LOW, + dram1 + ICSS_LRE_SUP_ADDR_LOW); +} + +static void prueth_lre_config_packet_timestamping(struct prueth *prueth) +{ + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + writeb(1, sram + ICSS_LRE_PRIORITY_INTRS_STATUS_OFFSET); + writeb(1, sram + ICSS_LRE_TIMESTAMP_PKTS_STATUS_OFFSET); +} + +static void prueth_lre_process_check_flags_event(struct prueth *prueth) +{ + void __iomem *dram = prueth->mem[PRUETH_MEM_DRAM1].va; + unsigned long flags; + + if (prueth->node_table_clear) { + pru_spin_lock(prueth->nt); + spin_lock_irqsave(&prueth->nt_lock, flags); + _prueth_lre_init_node_table(prueth); + spin_unlock_irqrestore(&prueth->nt_lock, flags); + /* we don't have to release the prueth lock + * the note_table_init() cleares it anyway + */ + prueth->node_table_clear = 0; + } else { + prueth->tbl_check_mask &= + ~ICSS_LRE_HOST_TIMER_NODE_TABLE_CLEAR_BIT; + } + + /* schedule work here */ + kthread_queue_work(prueth->nt_kworker, &prueth->nt_work); + + writel(prueth->tbl_check_mask, dram + ICSS_LRE_HOST_TIMER_CHECK_FLAGS); +} + +static enum hrtimer_restart prueth_lre_timer(struct hrtimer *timer) +{ + struct prueth *prueth = container_of(timer, struct prueth, + tbl_check_timer); + unsigned int timeout = PRUETH_TIMER_MS; + + hrtimer_forward_now(timer, ms_to_ktime(timeout)); + if (prueth->emac_configured != + (BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1))) + return HRTIMER_RESTART; + + prueth_lre_process_check_flags_event(prueth); + + return HRTIMER_RESTART; +} + +static void prueth_lre_init_timer(struct prueth *prueth) +{ + hrtimer_init(&prueth->tbl_check_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + prueth->tbl_check_timer.function = prueth_lre_timer; +} + +static void prueth_lre_start_timer(struct prueth *prueth) +{ + unsigned int timeout = PRUETH_TIMER_MS; + + if (hrtimer_active(&prueth->tbl_check_timer)) + return; + + hrtimer_start(&prueth->tbl_check_timer, ms_to_ktime(timeout), + HRTIMER_MODE_REL); +} + +void prueth_lre_config(struct prueth *prueth) +{ + if (PRUETH_IS_HSR(prueth)) + prueth->hsr_mode = ICSS_LRE_MODEH; + + prueth_lre_init_timer(prueth); + prueth_lre_start_timer(prueth); + prueth_lre_pcp_queue_map_config(prueth); + prueth_lre_host_table_init(prueth); + prueth_lre_port_table_init(prueth); + prueth_lre_init(prueth); + prueth_lre_dbg_init(prueth); + prueth_lre_protocol_init(prueth); + /* for HSR/PRP LRE driver order the frames based on + * packet timestamp. + */ + prueth_lre_config_packet_timestamping(prueth); +} + +static void nt_updater(struct kthread_work *work) +{ + struct prueth *prueth = container_of(work, struct prueth, nt_work); + + pop_queue_process(prueth, &prueth->nt_lock); + + node_table_update_time(prueth->nt); + if (++prueth->rem_cnt >= 100) { + node_table_check_and_remove(prueth->nt, + ICSS_LRE_NODE_FORGET_TIME_60000_MS); + prueth->rem_cnt = 0; + } +} + +static int prueth_lre_emac_rx_packets(struct prueth_emac *emac, + u8 qid1, u8 qid2) +{ + struct prueth *prueth = emac->prueth; + void *ocmc_ram = (__force void *)prueth->mem[PRUETH_MEM_OCMC].va; + u16 bd_rd_ptr, bd_wr_ptr, update_rd_ptr, bd_rd_ptr_o, bd_wr_ptr_o; + void __iomem *shared_ram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + struct prueth_queue_desc __iomem *queue_desc, *queue_desc_o; + struct net_device_stats *ndevstats = &emac->ndev->stats; + int ret, used = 0, port, port0_q_empty, port1_q_empty; + unsigned int emac_max_pktlen = PRUETH_MAX_PKTLEN_LRE; + const struct prueth_queue_info *rxqueue, *rxqueue_o; + struct prueth_packet_info pkt_info, pkt_info_o; + const struct prueth_queue_info *rxqueue_p; + struct prueth_packet_info *pkt_info_p; + struct net_device_stats *ndevstats_o; + struct net_device_stats *ndevstats_p; + u8 overflow_cnt, overflow_cnt_o; + u32 rd_buf_desc, rd_buf_desc_o; + struct prueth_emac *other_emac; + u16 *bd_rd_ptr_p, *bd_wr_ptr_p; + struct prueth_emac *emac_p; + u32 pkt_ts, pkt_ts_o; + u32 iep_wrap; + + other_emac = prueth->emac[(emac->port_id ^ 0x3) - 1]; + ndevstats_o = &other_emac->ndev->stats; + + /* use the correct wrap value based on ICSSM version */ + iep_wrap = prueth->fw_offsets->iep_wrap; + /* search host queues for packets */ + queue_desc = emac->rx_queue_descs + qid1; + queue_desc_o = other_emac->rx_queue_descs + qid2; + + rxqueue = &sw_queue_infos[PRUETH_PORT_HOST][qid1]; + rxqueue_o = &sw_queue_infos[PRUETH_PORT_HOST][qid2]; + +retry: + overflow_cnt = readb(&queue_desc->overflow_cnt); + overflow_cnt_o = readb(&queue_desc_o->overflow_cnt); + + if (overflow_cnt > 0) { + emac->ndev->stats.rx_over_errors += overflow_cnt; + /* reset to zero */ + writeb(0, &queue_desc->overflow_cnt); + } + if (overflow_cnt_o > 0) { + other_emac->ndev->stats.rx_over_errors += overflow_cnt_o; + + /* reset to zero */ + writeb(0, &queue_desc_o->overflow_cnt); + } + + bd_rd_ptr = readw(&queue_desc->rd_ptr); + bd_wr_ptr = readw(&queue_desc->wr_ptr); + + bd_rd_ptr_o = readw(&queue_desc_o->rd_ptr); + bd_wr_ptr_o = readw(&queue_desc_o->wr_ptr); + + port0_q_empty = (bd_rd_ptr == bd_wr_ptr) ? 1 : 0; + port1_q_empty = (bd_rd_ptr_o == bd_wr_ptr_o) ? 1 : 0; + + /* while packets are available in this queue */ + while (!port0_q_empty || !port1_q_empty) { + /* get packet info from the read buffer descriptor */ + rd_buf_desc = readl(shared_ram + bd_rd_ptr); + rd_buf_desc_o = readl(shared_ram + bd_rd_ptr_o); + + parse_packet_info(prueth, rd_buf_desc, &pkt_info); + parse_packet_info(prueth, rd_buf_desc_o, &pkt_info_o); + + pkt_ts = readl(ocmc_ram + ICSS_LRE_TIMESTAMP_ARRAY_OFFSET + + bd_rd_ptr - SRAM_START_OFFSET); + pkt_ts_o = readl(ocmc_ram + ICSS_LRE_TIMESTAMP_ARRAY_OFFSET + + bd_rd_ptr_o - SRAM_START_OFFSET); + + if (!port0_q_empty && !port1_q_empty) { + /* Packets in both port queues */ + /* Calculate diff b/n timestamps and account for + * wraparound + */ + if (pkt_ts > pkt_ts_o) + port = (pkt_ts - pkt_ts_o) > (iep_wrap / 2) ? + 0 : 1; + else + port = (pkt_ts_o - pkt_ts) > (iep_wrap / 2) ? + 1 : 0; + + } else if (!port0_q_empty) { + /* Packet(s) in port0 queue only */ + port = 0; + } else { + /* Packet(s) in port1 queue only */ + port = 1; + } + + /* Select correct data structures for queue/packet selected */ + if (port == 0) { + pkt_info_p = &pkt_info; + bd_wr_ptr_p = &bd_wr_ptr; + bd_rd_ptr_p = &bd_rd_ptr; + emac_p = emac; + ndevstats_p = ndevstats; + rxqueue_p = rxqueue; + } else { + pkt_info_p = &pkt_info_o; + bd_wr_ptr_p = &bd_wr_ptr_o; + bd_rd_ptr_p = &bd_rd_ptr_o; + emac_p = other_emac; + ndevstats_p = ndevstats_o; + rxqueue_p = rxqueue_o; + } + + if ((*pkt_info_p).length <= 0) { + /* a packet length of zero will cause us to + * never move the read pointer ahead, locking + * the driver, so we manually have to move it + * to the write pointer, discarding all + * remaining packets in this queue. This should + * never happen. + */ + update_rd_ptr = *bd_wr_ptr_p; + ndevstats_p->rx_length_errors++; + } else if ((*pkt_info_p).length > emac_max_pktlen) { + /* if the packet is too large we skip it but we + * still need to move the read pointer ahead + * and assume something is wrong with the read + * pointer as the firmware should be filtering + * these packets + */ + update_rd_ptr = *bd_wr_ptr_p; + ndevstats_p->rx_length_errors++; + } else { + update_rd_ptr = *bd_rd_ptr_p; + ret = emac_rx_packet(emac_p, &update_rd_ptr, + pkt_info_p, rxqueue_p); + if (ret) + return IRQ_HANDLED; + + used++; + } + + /* after reading the buffer descriptor we clear it + * to prevent improperly moved read pointer errors + * from simply looking like old packets. + */ + + /* update read pointer in queue descriptor */ + if (port == 0) { + writel(0, shared_ram + bd_rd_ptr); + writew(update_rd_ptr, &queue_desc->rd_ptr); + bd_rd_ptr = update_rd_ptr; + } else { + writel(0, shared_ram + bd_rd_ptr_o); + writew(update_rd_ptr, &queue_desc_o->rd_ptr); + bd_rd_ptr_o = update_rd_ptr; + } + + port0_q_empty = (bd_rd_ptr == bd_wr_ptr) ? 1 : 0; + port1_q_empty = (bd_rd_ptr_o == bd_wr_ptr_o) ? 1 : 0; + } + + if (used) { + used = 0; + goto retry; + } + + return IRQ_HANDLED; +} + +static irqreturn_t prueth_lre_emac_rx_hardirq_lp(int irq, void *dev_id) +{ + struct prueth_ndev_priority *ndev_prio = + (struct prueth_ndev_priority *)dev_id; + struct net_device *ndev = ndev_prio->ndev; + struct prueth_emac *emac = netdev_priv(ndev); + + return prueth_lre_emac_rx_packets(emac, PRUETH_QUEUE2, PRUETH_QUEUE4); +} + +static irqreturn_t prueth_lre_emac_rx_hardirq_hp(int irq, void *dev_id) +{ + struct prueth_ndev_priority *ndev_prio = + (struct prueth_ndev_priority *)dev_id; + struct net_device *ndev = ndev_prio->ndev; + struct prueth_emac *emac = netdev_priv(ndev); + + return prueth_lre_emac_rx_packets(emac, PRUETH_QUEUE1, PRUETH_QUEUE3); +} + +int prueth_lre_request_irqs(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + int ret; + + if (emac->hsr_ptp_tx_irq) { + ret = request_threaded_irq(emac->hsr_ptp_tx_irq, + prueth_ptp_tx_irq_handle, + prueth_ptp_tx_irq_work, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + emac->ndev->name, emac->ndev); + if (ret) { + netdev_err(emac->ndev, "unable to request PTP TX IRQ\n"); + return ret; + } + } + + /* HSR/PRP. Request irq when first port is initialized */ + if (prueth->emac_configured) + return 0; + + ret = request_threaded_irq(prueth->rx_hpq_irq, NULL, prueth_lre_emac_rx_hardirq_hp, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "eth_hp_int", prueth->hp); + if (ret) { + netdev_err(emac->ndev, "unable to request RX HPQ IRQ\n"); + goto free_ptp_irq; + } + + ret = request_threaded_irq(prueth->rx_lpq_irq, NULL, prueth_lre_emac_rx_hardirq_lp, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "eth_lp_int", prueth->lp); + if (ret) { + netdev_err(emac->ndev, "unable to request RX LPQ IRQ\n"); + goto free_rx_hpq_irq; + } + + return 0; + +free_rx_hpq_irq: + free_irq(prueth->rx_hpq_irq, prueth->hp); +free_ptp_irq: + if (emac->hsr_ptp_tx_irq) + free_irq(emac->hsr_ptp_tx_irq, emac->ndev); + + return ret; +} + +void prueth_lre_free_irqs(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + + if (emac->hsr_ptp_tx_irq) + free_irq(emac->hsr_ptp_tx_irq, emac->ndev); + + /* HSR/PRP: free irqs when last port is down */ + if (prueth->emac_configured) + return; + + free_irq(prueth->rx_lpq_irq, prueth->lp); + free_irq(prueth->rx_hpq_irq, prueth->hp); +} + +void prueth_lre_free_memory(struct prueth *prueth) +{ + /* HSR/PRP: initialize node table when first port is up */ + if (prueth->emac_configured) + return; + + kfree(prueth->nt); + kfree(prueth->mac_queue); + prueth->mac_queue = NULL; + prueth->nt = NULL; +} + +#define PRUETH_LRE_STAT_OFS(m) offsetof(struct lre_statistics, m) +static const struct { + char string[ETH_GSTRING_LEN]; + u32 offset; +} prueth_ethtool_lre_stats[] = { + {"lreTxA", PRUETH_LRE_STAT_OFS(cnt_tx_a)}, + {"lreTxB", PRUETH_LRE_STAT_OFS(cnt_tx_b)}, + {"lreTxC", PRUETH_LRE_STAT_OFS(cnt_tx_c)}, + + {"lreErrWrongLanA", PRUETH_LRE_STAT_OFS(cnt_errwronglan_a)}, + {"lreErrWrongLanB", PRUETH_LRE_STAT_OFS(cnt_errwronglan_b)}, + {"lreErrWrongLanC", PRUETH_LRE_STAT_OFS(cnt_errwronglan_c)}, + + {"lreRxA", PRUETH_LRE_STAT_OFS(cnt_rx_a)}, + {"lreRxB", PRUETH_LRE_STAT_OFS(cnt_rx_b)}, + {"lreRxC", PRUETH_LRE_STAT_OFS(cnt_rx_c)}, + + {"lreErrorsA", PRUETH_LRE_STAT_OFS(cnt_errors_a)}, + {"lreErrorsB", PRUETH_LRE_STAT_OFS(cnt_errors_b)}, + {"lreErrorsC", PRUETH_LRE_STAT_OFS(cnt_errors_c)}, + + {"lreNodes", PRUETH_LRE_STAT_OFS(cnt_nodes)}, + {"lreProxyNodes", PRUETH_LRE_STAT_OFS(cnt_proxy_nodes)}, + + {"lreUniqueRxA", PRUETH_LRE_STAT_OFS(cnt_unique_rx_a)}, + {"lreUniqueRxB", PRUETH_LRE_STAT_OFS(cnt_unique_rx_b)}, + {"lreUniqueRxC", PRUETH_LRE_STAT_OFS(cnt_unique_rx_c)}, + + {"lreDuplicateRxA", PRUETH_LRE_STAT_OFS(cnt_duplicate_rx_a)}, + {"lreDuplicateRxB", PRUETH_LRE_STAT_OFS(cnt_duplicate_rx_b)}, + {"lreDuplicateRxC", PRUETH_LRE_STAT_OFS(cnt_duplicate_rx_c)}, + + {"lreMultiRxA", PRUETH_LRE_STAT_OFS(cnt_multiple_rx_a)}, + {"lreMultiRxB", PRUETH_LRE_STAT_OFS(cnt_multiple_rx_b)}, + {"lreMultiRxC", PRUETH_LRE_STAT_OFS(cnt_multiple_rx_c)}, + + {"lreOwnRxA", PRUETH_LRE_STAT_OFS(cnt_own_rx_a)}, + {"lreOwnRxB", PRUETH_LRE_STAT_OFS(cnt_own_rx_b)}, + + {"lreDuplicateDiscard", PRUETH_LRE_STAT_OFS(duplicate_discard)}, + {"lreTransRecept", PRUETH_LRE_STAT_OFS(transparent_reception)}, + + {"lreNtLookupErrA", PRUETH_LRE_STAT_OFS(node_table_lookup_error_a)}, + {"lreNtLookupErrB", PRUETH_LRE_STAT_OFS(node_table_lookup_error_b)}, + {"lreNodeTableFull", PRUETH_LRE_STAT_OFS(node_table_full)}, + {"lreMulticastDropped", PRUETH_LRE_STAT_OFS(lre_multicast_dropped)}, + {"lreVlanDropped", PRUETH_LRE_STAT_OFS(lre_vlan_dropped)}, + {"lrePaceTimerExpired", PRUETH_LRE_STAT_OFS(lre_intr_tmr_exp)}, + {"lreTotalRxA", PRUETH_LRE_STAT_OFS(lre_total_rx_a)}, + {"lreTotalRxB", PRUETH_LRE_STAT_OFS(lre_total_rx_b)}, + {"lreOverflowPru0", PRUETH_LRE_STAT_OFS(lre_overflow_pru0)}, + {"lreOverflowPru1", PRUETH_LRE_STAT_OFS(lre_overflow_pru1)}, + {"lreDDCountPru0", PRUETH_LRE_STAT_OFS(lre_cnt_dd_pru0)}, + {"lreDDCountPru1", PRUETH_LRE_STAT_OFS(lre_cnt_dd_pru1)}, + {"lreCntSupPru0", PRUETH_LRE_STAT_OFS(lre_cnt_sup_pru0)}, + {"lreCntSupPru1", PRUETH_LRE_STAT_OFS(lre_cnt_sup_pru1)}, +}; + +void prueth_lre_set_stats(struct prueth *prueth, + struct lre_statistics *pstats) +{ + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + if (prueth->emac_configured) + return; + + /* These two are actually not statistics, so keep original */ + pstats->duplicate_discard = readl(sram + ICSS_LRE_DUPLICATE_DISCARD); + pstats->transparent_reception = + readl(sram + ICSS_LRE_TRANSPARENT_RECEPTION); + memcpy_fromio(sram + ICSS_LRE_START + 4, pstats, sizeof(*pstats)); +} + +void prueth_lre_get_stats(struct prueth *prueth, + struct lre_statistics *pstats) +{ + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + memcpy_fromio(pstats, sram + ICSS_LRE_CNT_TX_A, sizeof(*pstats)); +} + +int prueth_lre_get_sset_count(struct prueth *prueth) +{ + if (!PRUETH_IS_LRE(prueth)) + return 0; + + return ARRAY_SIZE(prueth_ethtool_lre_stats); +} + +void prueth_lre_get_strings(struct prueth *prueth, u8 *data) +{ + int i; + + if (!PRUETH_IS_LRE(prueth)) + return; + + for (i = 0; i < ARRAY_SIZE(prueth_ethtool_lre_stats); i++) { + memcpy(data, prueth_ethtool_lre_stats[i].string, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } +} + +void prueth_lre_update_stats(struct prueth *prueth, u64 *data) +{ + struct lre_statistics lre_stats; + void *ptr; + u32 val; + int i; + + if (!PRUETH_IS_LRE(prueth)) + return; + + prueth_lre_get_stats(prueth, &lre_stats); + for (i = 0; i < ARRAY_SIZE(prueth_ethtool_lre_stats); i++) { + ptr = &lre_stats; + ptr += prueth_ethtool_lre_stats[i].offset; + val = *(u32 *)ptr; + data[i] = val; + } +} + +static int prueth_lre_attr_get(struct net_device *ndev, + struct lredev_attr *attr) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + void __iomem *dram0 = prueth->mem[PRUETH_MEM_DRAM0].va; + void __iomem *dram1 = prueth->mem[PRUETH_MEM_DRAM1].va; + int ret = 0; + + netdev_dbg(ndev, "%d:%s, id %d\n", __LINE__, __func__, attr->id); + + switch (attr->id) { + case LREDEV_ATTR_ID_HSR_MODE: + if (!PRUETH_IS_HSR(prueth)) + return -EPERM; + attr->mode = readl(dram0 + ICSS_LRE_HSR_MODE); + break; + case LREDEV_ATTR_ID_DD_MODE: + attr->dd_mode = readl(sram + ICSS_LRE_DUPLICATE_DISCARD); + break; + case LREDEV_ATTR_ID_PRP_TR: + if (!PRUETH_IS_PRP(prueth)) + return -EINVAL; + attr->tr_mode = prueth->prp_tr_mode; + break; + case LREDEV_ATTR_ID_DLRMT: + attr->dl_reside_max_time = + readl(dram1 + ICSS_LRE_DUPLI_FORGET_TIME) * 10; + break; + case LREDEV_ATTR_ID_CLEAR_NT: + attr->clear_nt_cmd = prueth->node_table_clear_last_cmd; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int prueth_lre_attr_set(struct net_device *ndev, + struct lredev_attr *attr) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + void __iomem *dram0 = prueth->mem[PRUETH_MEM_DRAM0].va; + void __iomem *dram1 = prueth->mem[PRUETH_MEM_DRAM1].va; + int ret = 0; + + netdev_dbg(ndev, "%d:%s, id = %d\n", __LINE__, __func__, attr->id); + + switch (attr->id) { + case LREDEV_ATTR_ID_HSR_MODE: + if (!PRUETH_IS_HSR(prueth)) + return -EPERM; + prueth->hsr_mode = attr->mode; + writel(prueth->hsr_mode, dram0 + ICSS_LRE_HSR_MODE); + break; + case LREDEV_ATTR_ID_DD_MODE: + writel(attr->dd_mode, sram + ICSS_LRE_DUPLICATE_DISCARD); + break; + case LREDEV_ATTR_ID_PRP_TR: + if (!PRUETH_IS_PRP(prueth)) + return -EINVAL; + prueth->prp_tr_mode = attr->tr_mode; + break; + case LREDEV_ATTR_ID_DLRMT: + /* input is in milli seconds. Firmware expects in unit + * of 10 msec + */ + writel((attr->dl_reside_max_time / 10), + dram1 + ICSS_LRE_DUPLI_FORGET_TIME); + break; + case LREDEV_ATTR_ID_CLEAR_NT: + /* need to return last cmd received for corresponding + * get command. So save it + */ + prueth->node_table_clear_last_cmd = attr->clear_nt_cmd; + if (attr->clear_nt_cmd == IEC62439_3_CLEAR_NT) + prueth->node_table_clear = 1; + else + prueth->node_table_clear = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int emac_lredev_update_node_entry(struct node_tbl_t *node, + struct lre_node_table_entry table[], + int j) +{ + u8 val, is_hsr, updated = 1; + + table[j].time_last_seen_a = node->time_last_seen_a; + table[j].time_last_seen_b = node->time_last_seen_b; + + is_hsr = node->status & ICSS_LRE_NT_REM_NODE_HSR_BIT; + val = (node->status & ICSS_LRE_NT_REM_NODE_TYPE_MASK) >> + ICSS_LRE_NT_REM_NODE_TYPE_SHIFT; + switch (val) { + case ICSS_LRE_NT_REM_NODE_TYPE_DAN: + if (is_hsr) + table[j].node_type = IEC62439_3_DANH; + else + table[j].node_type = IEC62439_3_DANP; + break; + + case ICSS_LRE_NT_REM_NODE_TYPE_REDBOX: + if (is_hsr) + table[j].node_type = IEC62439_3_REDBOXH; + else + table[j].node_type = IEC62439_3_REDBOXP; + break; + + case ICSS_LRE_NT_REM_NODE_TYPE_VDAN: + if (is_hsr) + table[j].node_type = IEC62439_3_VDANH; + else + table[j].node_type = IEC62439_3_VDANP; + break; + default: + updated = 0; + break; + } + + return updated; +} + +static int prueth_lre_get_node_table(struct net_device *ndev, + struct lre_node_table_entry table[], + int size) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + struct node_tbl *nt = prueth->nt; + struct bin_tbl_t *bin; + struct node_tbl_t *node; + int i, j = 0, updated; + unsigned long flags; + + netdev_dbg(ndev, "%d:%s\n", __LINE__, __func__); + + if (size < nt->nt_lre_cnt->lre_cnt) + netdev_warn(ndev, + "actual table size %d is < required size %d\n", + size, nt->nt_lre_cnt->lre_cnt); + + spin_lock_irqsave(&prueth->nt_lock, flags); + for (i = 0; i < nt->bin_array_max_entries; i++) { + if (nt->bin_array->bin_tbl[i].node_tbl_offset < + nt->nt_array_max_entries) { + bin = &nt->bin_array->bin_tbl[i]; + if (WARN_ON(bin->node_tbl_offset >= + nt->nt_array_max_entries)) + continue; + node = &nt->nt_array->node_tbl[bin->node_tbl_offset]; + + if (!(node->entry_state & 0x1)) + continue; + + updated = emac_lredev_update_node_entry(node, table, j); + if (updated) { + table[j].mac_address[0] = bin->src_mac_id[3]; + table[j].mac_address[1] = bin->src_mac_id[2]; + table[j].mac_address[2] = bin->src_mac_id[1]; + table[j].mac_address[3] = bin->src_mac_id[0]; + table[j].mac_address[4] = bin->src_mac_id[5]; + table[j].mac_address[5] = bin->src_mac_id[4]; + j++; + } + } + } + spin_unlock_irqrestore(&prueth->nt_lock, flags); + + return j; +} + +static int prueth_lre_get_lre_stats(struct net_device *ndev, + struct lre_stats *stats) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + void __iomem *sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + memcpy_fromio(stats, sram + ICSS_LRE_CNT_TX_A, sizeof(*stats)); + + return 0; +} + +static int prueth_lre_set_sv_vlan_id(struct net_device *ndev, u16 vid) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + if (!PRUETH_IS_LRE(prueth)) + return 0; + + return emac_add_del_vid(emac, true, htons(ETH_P_8021Q), vid); +} + +const struct lredev_ops prueth_lredev_ops = { + .lredev_attr_get = prueth_lre_attr_get, + .lredev_attr_set = prueth_lre_attr_set, + .lredev_get_node_table = prueth_lre_get_node_table, + .lredev_get_stats = prueth_lre_get_lre_stats, + .lredev_set_sv_vlan_id = prueth_lre_set_sv_vlan_id, +}; + +int prueth_lre_init_node_table(struct prueth *prueth) +{ + /* HSR/PRP: initialize node table when first port is up */ + if (prueth->emac_configured) + return 0; + + /* initialize for node table handling in driver for HSR/PRP */ + prueth->mac_queue = kmalloc(sizeof(*prueth->mac_queue), GFP_KERNEL); + prueth->nt = kmalloc(sizeof(*prueth->nt), GFP_KERNEL); + if (!prueth->mac_queue || !prueth->nt) { + kfree(prueth->mac_queue); + kfree(prueth->nt); + prueth->mac_queue = NULL; + prueth->nt = NULL; + return -ENOMEM; + } + + _prueth_lre_init_node_table(prueth); + spin_lock_init(&prueth->nt_lock); + kthread_init_work(&prueth->nt_work, nt_updater); + prueth->nt_kworker = kthread_create_worker(0, "prueth_nt"); + + return 0; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_lre.h b/drivers/net/ethernet/ti/prueth_lre.h --- a/drivers/net/ethernet/ti/prueth_lre.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_lre.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + */ + +#ifndef __NET_TI_PRUETH_LRE_H +#define __NET_TI_PRUETH_LRE_H + +#include +#include +#include + +#include "prueth.h" +#include "icss_lre_firmware.h" + +#define PRUETH_MAX_PKTLEN_LRE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN + \ + ICSS_LRE_TAG_RCT_SIZE) +#define PRUETH_MAC_QUEUE_MAX_SHIFT 6 +#define PRUETH_MAC_QUEUE_MAX BIT(PRUETH_MAC_QUEUE_MAX_SHIFT) +#define PRUETH_LRE_INDEX_TBL_MAX_ENTRIES 256 +#define PRUETH_LRE_BIN_TBL_MAX_ENTRIES 256 +#define PRUETH_LRE_NODE_TBL_MAX_ENTRIES 256 +#define LRE_PROTO_HSR 0 +#define LRE_PROTO_PRP 1 +#define LRE_OK 0 +#define LRE_ERR -1 +#define LRE_SV_FRAME_OFFSET 20 + +/* Link Redundancy Entity stats counters */ +struct lre_statistics { + u32 cnt_tx_a; + u32 cnt_tx_b; + u32 cnt_tx_c; + + u32 cnt_errwronglan_a; + u32 cnt_errwronglan_b; + u32 cnt_errwronglan_c; + + u32 cnt_rx_a; + u32 cnt_rx_b; + u32 cnt_rx_c; + + u32 cnt_errors_a; + u32 cnt_errors_b; + u32 cnt_errors_c; + + u32 cnt_nodes; + u32 cnt_proxy_nodes; + + u32 cnt_unique_rx_a; + u32 cnt_unique_rx_b; + u32 cnt_unique_rx_c; + + u32 cnt_duplicate_rx_a; + u32 cnt_duplicate_rx_b; + u32 cnt_duplicate_rx_c; + + u32 cnt_multiple_rx_a; + u32 cnt_multiple_rx_b; + u32 cnt_multiple_rx_c; + + u32 cnt_own_rx_a; + u32 cnt_own_rx_b; + + u32 duplicate_discard; + u32 transparent_reception; + + u32 node_table_lookup_error_a; + u32 node_table_lookup_error_b; + u32 node_table_full; + u32 lre_multicast_dropped; + u32 lre_vlan_dropped; + u32 lre_intr_tmr_exp; + + /* additional debug counters */ + u32 lre_total_rx_a; /* count of all frames received at port-A */ + u32 lre_total_rx_b; /* count of all frames received at port-B */ + u32 lre_overflow_pru0; /* count of overflow frames to host on PRU 0 */ + u32 lre_overflow_pru1; /* count of overflow frames to host on PRU 1 */ + u32 lre_cnt_dd_pru0; /* count of DD frames to host on PRU 0 */ + u32 lre_cnt_dd_pru1; /* count of DD frames to host on PRU 1 */ + u32 lre_cnt_sup_pru0; /* count of supervisor frames to host on PRU 0 */ + u32 lre_cnt_sup_pru1; /* count of supervisor frames to host on PRU 1 */ +} __packed; + +/* node table info */ +struct prueth_lre_node { + u8 mac[6]; + u8 state; + u8 status; + + u32 cnt_rx_a; + u32 cnt_rx_b; + + u32 prp_lid_err_a; + u32 prp_lid_err_b; + + u8 cnt_rx_sup_a; + u8 cnt_rx_sup_b; + u16 time_last_seen_sup; + + u16 time_last_seen_a; + u16 time_last_seen_b; +} __packed; + +/* NT queue definitions */ +struct nt_queue_entry { + u8 mac[ETH_ALEN]; + unsigned int sv_frame:1; + unsigned int proto:1; + int port_id:6; +}; + +struct nt_queue_t { + struct nt_queue_entry nt_queue[PRUETH_MAC_QUEUE_MAX]; + int rd_ind; + int wr_ind; + bool full; +}; + +struct node_index_tbl_t { + u16 bin_offset; + u16 bin_no_entries; + u8 lin_bin; /* 0 - linear; 1 - binary; */ + u8 res1; +} __packed; + +struct bin_tbl_t { + u8 src_mac_id[ETH_ALEN]; + u16 node_tbl_offset; +} __packed; + +struct node_tbl_t { + u8 mac[ETH_ALEN]; + u8 entry_state; + u8 status; + u32 cnt_ra; + u32 cnt_rb; + u32 err_wla; + u32 err_wlb; + u8 cnt_rx_sup_a; + u8 cnt_rx_sup_b; + u16 time_last_seen_s; + u16 time_last_seen_a; + u16 time_last_seen_b; +} __packed; + +struct node_tbl_lre_cnt_t { + u16 lre_cnt; +} __packed; + +struct node_tbl_info_t { + u32 next_free_slot; + u8 arm_lock; + u8 res; + u16 fw_lock; /* firmware use this field as 2 independent bytes + * first byte for PRU0, second for PRU1 + */ +} __packed; + +struct nt_array_t { + struct node_tbl_t node_tbl[PRUETH_LRE_NODE_TBL_MAX_ENTRIES]; +} __packed; +struct index_array_t { + struct node_index_tbl_t index_tbl[PRUETH_LRE_INDEX_TBL_MAX_ENTRIES]; +} __packed; +struct bin_array_t { + struct bin_tbl_t bin_tbl[PRUETH_LRE_BIN_TBL_MAX_ENTRIES]; +} __packed; + +struct node_tbl { + struct bin_array_t *bin_array; + struct index_array_t *index_array; + struct nt_array_t *nt_array; + struct node_tbl_info_t *nt_info; + struct node_tbl_lre_cnt_t *nt_lre_cnt; + u32 index_array_max_entries; + u32 bin_array_max_entries; + u32 nt_array_max_entries; + u16 hash_mask; +}; + +void prueth_lre_config(struct prueth *prueth); +int prueth_lre_init_node_table(struct prueth *prueth); +int prueth_lre_request_irqs(struct prueth_emac *emac); +void prueth_lre_free_irqs(struct prueth_emac *emac); +int prueth_lre_get_sset_count(struct prueth *prueth); +void prueth_lre_get_strings(struct prueth *prueth, u8 *data); +void prueth_lre_update_stats(struct prueth *prueth, u64 *data); +void prueth_lre_set_stats(struct prueth *prueth, + struct lre_statistics *pstats); +void prueth_lre_get_stats(struct prueth *prueth, + struct lre_statistics *pstats); +void prueth_lre_config_check_flags(struct prueth *prueth); +void prueth_lre_free_memory(struct prueth *prueth); +int prueth_lre_nt_insert(struct prueth *prueth, + u8 *mac, int port, int sv_frame, int proto); + +extern const struct lredev_ops prueth_lredev_ops; + +#endif /* __NET_TI_PRUETH_LRE_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_ptp.h b/drivers/net/ethernet/ti/prueth_ptp.h --- a/drivers/net/ethernet/ti/prueth_ptp.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_ptp.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com + */ +#ifndef PRUETH_PTP_H +#define PRUETH_PTP_H + +#define RX_SYNC_TIMESTAMP_OFFSET_P1 0x8 /* 8 bytes */ +#define RX_PDELAY_REQ_TIMESTAMP_OFFSET_P1 0x14 /* 12 bytes */ + +#define DISABLE_PTP_FRAME_FORWARDING_CTRL_OFFSET 0x14 /* 1 byte */ + +#define RX_PDELAY_RESP_TIMESTAMP_OFFSET_P1 0x20 /* 12 bytes */ +#define RX_SYNC_TIMESTAMP_OFFSET_P2 0x2c /* 12 bytes */ +#define RX_PDELAY_REQ_TIMESTAMP_OFFSET_P2 0x38 /* 12 bytes */ +#define RX_PDELAY_RESP_TIMESTAMP_OFFSET_P2 0x44 /* 12 bytes */ +#define TIMESYNC_DOMAIN_NUMBER_LIST 0x50 /* 2 bytes */ +#define P1_SMA_LINE_DELAY_OFFSET 0x52 /* 4 bytes */ +#define P2_SMA_LINE_DELAY_OFFSET 0x56 /* 4 bytes */ +#define TIMESYNC_SECONDS_COUNT_OFFSET 0x5a /* 6 bytes */ +#define TIMESYNC_TC_RCF_OFFSET 0x60 /* 4 bytes */ +#define DUT_IS_MASTER_OFFSET 0x64 /* 1 byte */ +#define MASTER_PORT_NUM_OFFSET 0x65 /* 1 byte */ +#define SYNC_MASTER_MAC_OFFSET 0x66 /* 6 bytes */ +#define TX_TS_NOTIFICATION_OFFSET_SYNC_P1 0x6c /* 1 byte */ +#define TX_TS_NOTIFICATION_OFFSET_PDEL_REQ_P1 0x6d /* 1 byte */ +#define TX_TS_NOTIFICATION_OFFSET_PDEL_RES_P1 0x6e /* 1 byte */ +#define TX_TS_NOTIFICATION_OFFSET_SYNC_P2 0x6f /* 1 byte */ +#define TX_TS_NOTIFICATION_OFFSET_PDEL_REQ_P2 0x70 /* 1 byte */ +#define TX_TS_NOTIFICATION_OFFSET_PDEL_RES_P2 0x71 /* 1 byte */ +#define TX_SYNC_TIMESTAMP_OFFSET_P1 0x72 /* 12 bytes */ +#define TX_PDELAY_REQ_TIMESTAMP_OFFSET_P1 0x7e /* 12 bytes */ +#define TX_PDELAY_RESP_TIMESTAMP_OFFSET_P1 0x8a /* 12 bytes */ +#define TX_SYNC_TIMESTAMP_OFFSET_P2 0x96 /* 12 bytes */ +#define TX_PDELAY_REQ_TIMESTAMP_OFFSET_P2 0xa2 /* 12 bytes */ +#define TX_PDELAY_RESP_TIMESTAMP_OFFSET_P2 0xae /* 12 bytes */ +#define TIMESYNC_CTRL_VAR_OFFSET 0xba /* 1 byte */ +#define DISABLE_SWITCH_SYNC_RELAY_OFFSET 0xbb /* 1 byte */ +#define MII_RX_CORRECTION_OFFSET 0xbc /* 2 bytes */ +#define MII_TX_CORRECTION_OFFSET 0xbe /* 2 bytes */ +#define TIMESYNC_CMP1_CMP_OFFSET 0xc0 /* 8 bytes */ +#define TIMESYNC_SYNC0_CMP_OFFSET 0xc8 /* 8 bytes */ +#define TIMESYNC_CMP1_PERIOD_OFFSET 0xd0 /* 4 bytes */ +#define TIMESYNC_SYNC0_WIDTH_OFFSET 0xd4 /* 4 bytes */ +#define SINGLE_STEP_IEP_OFFSET_P1 0xd8 /* 8 bytes */ +#define SINGLE_STEP_SECONDS_OFFSET_P1 0xe0 /* 8 bytes */ +#define SINGLE_STEP_IEP_OFFSET_P2 0xe8 /* 8 bytes */ +#define SINGLE_STEP_SECONDS_OFFSET_P2 0xf0 /* 8 bytes */ +#define LINK_LOCAL_FRAME_HAS_HSR_TAG 0xf8 /* 1 bytes */ +#define PTP_PREV_TX_TIMESTAMP_P1 0xf9 /* 8 bytes */ +#define PTP_PREV_TX_TIMESTAMP_P2 0x101 /* 8 bytes */ +#define PTP_CLK_IDENTITY_OFFSET 0x109 /* 8 bytes */ +#define PTP_SCRATCH_MEM 0x111 /* 16 byte */ +#define PTP_IPV4_UDP_E2E_ENABLE 0x121 /* 1 byte */ + +enum { + PRUETH_PTP_SYNC, + PRUETH_PTP_DLY_REQ, + PRUETH_PTP_DLY_RESP, + PRUETH_PTP_TS_EVENTS, +}; + +#define PRUETH_PTP_TS_SIZE 12 +#define PRUETH_PTP_TS_NOTIFY_SIZE 1 +#define PRUETH_PTP_TS_NOTIFY_MASK 0xff + +/* Bit definitions for TIMESYNC_CTRL */ +#define TIMESYNC_CTRL_BG_ENABLE BIT(0) +#define TIMESYNC_CTRL_FORCED_2STEP BIT(1) + +static inline u32 prueth_tx_ts_offs_get(u8 port, u8 event) +{ + return TX_SYNC_TIMESTAMP_OFFSET_P1 + port * + PRUETH_PTP_TS_EVENTS * PRUETH_PTP_TS_SIZE + + event * PRUETH_PTP_TS_SIZE; +} + +static inline u32 prueth_tx_ts_notify_offs_get(u8 port, u8 event) +{ + return TX_TS_NOTIFICATION_OFFSET_SYNC_P1 + + PRUETH_PTP_TS_EVENTS * PRUETH_PTP_TS_NOTIFY_SIZE * port + + event * PRUETH_PTP_TS_NOTIFY_SIZE; +} + +#endif /* PRUETH_PTP_H */ diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_qos.c b/drivers/net/ethernet/ti/prueth_qos.c --- a/drivers/net/ethernet/ti/prueth_qos.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_qos.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include +#include +#include +#include + +#include "icss_mii_rt.h" +#include "icss_vlan_mcast_filter_mmap.h" +#include "prueth.h" + +static void emac_nsp_enable(void __iomem *counter, u16 credit) +{ + writel((credit << PRUETH_NSP_CREDIT_SHIFT) | PRUETH_NSP_ENABLE, + counter); +} + +static void prueth_enable_nsp(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + void __iomem *dram = prueth->mem[emac->dram].va; + + if (emac->nsp_bc.cookie) + emac_nsp_enable(dram + STORM_PREVENTION_OFFSET_BC, + emac->nsp_bc.credit); + if (emac->nsp_mc.cookie) + emac_nsp_enable(dram + STORM_PREVENTION_OFFSET_MC, + emac->nsp_mc.credit); + if (emac->nsp_uc.cookie) + emac_nsp_enable(dram + STORM_PREVENTION_OFFSET_UC, + emac->nsp_uc.credit); +} + +static int emac_flower_parse_policer(struct prueth_emac *emac, + struct netlink_ext_ack *extack, + struct flow_cls_offload *cls, + u64 rate_bytes_per_sec) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct flow_dissector *dissector = rule->match.dissector; + u8 null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + u8 mc_mac[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + struct flow_match_eth_addrs match; + struct nsp_counter *nsp = NULL; + char *str; + u32 pps; + + if (dissector->used_keys & + ~(BIT(FLOW_DISSECTOR_KEY_BASIC) | + BIT(FLOW_DISSECTOR_KEY_CONTROL) | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { + NL_SET_ERR_MSG_MOD(extack, + "Unsupported keys used"); + return -EOPNOTSUPP; + } + + if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + NL_SET_ERR_MSG_MOD(extack, "Not matching on eth address"); + return -EOPNOTSUPP; + } + + flow_rule_match_eth_addrs(rule, &match); + + if (!ether_addr_equal_masked(match.key->src, null_mac, + match.mask->src)) { + NL_SET_ERR_MSG_MOD(extack, + "Matching on source MAC not supported"); + return -EOPNOTSUPP; + } + + if (ether_addr_equal(match.key->dst, bc_mac)) { + if (!emac->nsp_bc.cookie || + emac->nsp_bc.cookie == cls->cookie) + nsp = &emac->nsp_bc; + else + NL_SET_ERR_MSG_MOD(extack, "BC Filter already set"); + str = "Broad"; + } else if (ether_addr_equal_masked(match.key->dst, mc_mac, mc_mac)) { + if (!emac->nsp_mc.cookie || + emac->nsp_mc.cookie == cls->cookie) + nsp = &emac->nsp_mc; + else + NL_SET_ERR_MSG_MOD(extack, "MC Filter already set"); + str = "Multi"; + } else { + if (!emac->nsp_uc.cookie || + emac->nsp_uc.cookie == cls->cookie) + nsp = &emac->nsp_uc; + else + NL_SET_ERR_MSG_MOD(extack, "UC Filter already set"); + str = "Uni"; + } + + if (!nsp) + return -EOPNOTSUPP; + + /* Calculate number of packets per second for given bps + * assuming min ethernet packet size + */ + pps = div_u64(rate_bytes_per_sec, ETH_ZLEN); + /* Convert that to packets per 100ms */ + pps /= MSEC_PER_SEC / PRUETH_NSP_TIMER_MS; + + nsp->cookie = cls->cookie; + nsp->credit = pps; + emac->nsp_enabled = emac->nsp_bc.cookie | emac->nsp_mc.cookie | + emac->nsp_uc.cookie; + + prueth_enable_nsp(emac); + + netdev_dbg(emac->ndev, + "%scast filter set to %d packets per %dms\n", str, + nsp->credit, PRUETH_NSP_TIMER_MS); + + return 0; +} + +static int emac_configure_clsflower(struct prueth_emac *emac, + struct flow_cls_offload *cls) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct netlink_ext_ack *extack = cls->common.extack; + const struct flow_action_entry *act; + int i; + + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_POLICE: + return emac_flower_parse_policer(emac, extack, cls, + act->police.rate_bytes_ps); + default: + NL_SET_ERR_MSG_MOD(extack, + "Action not supported"); + return -EOPNOTSUPP; + } + } + return -EOPNOTSUPP; +} + +static int emac_delete_clsflower(struct prueth_emac *emac, + struct flow_cls_offload *cls) +{ + struct prueth *prueth = emac->prueth; + void __iomem *dram = prueth->mem[emac->dram].va; + + if (cls->cookie == emac->nsp_bc.cookie) { + emac->nsp_bc.cookie = 0; + emac->nsp_bc.credit = 0; + writel(0, dram + STORM_PREVENTION_OFFSET_BC); + } else if (cls->cookie == emac->nsp_mc.cookie) { + emac->nsp_mc.cookie = 0; + emac->nsp_mc.credit = 0; + writel(0, dram + STORM_PREVENTION_OFFSET_MC); + } else if (cls->cookie == emac->nsp_uc.cookie) { + emac->nsp_uc.cookie = 0; + emac->nsp_uc.credit = 0; + writel(0, dram + STORM_PREVENTION_OFFSET_UC); + } + + emac->nsp_enabled = emac->nsp_bc.cookie | emac->nsp_mc.cookie | + emac->nsp_uc.cookie; + + return 0; +} + +static int emac_setup_tc_cls_flower(struct prueth_emac *emac, + struct flow_cls_offload *cls_flower) +{ + switch (cls_flower->command) { + case FLOW_CLS_REPLACE: + return emac_configure_clsflower(emac, cls_flower); + case FLOW_CLS_DESTROY: + return emac_delete_clsflower(emac, cls_flower); + default: + return -EOPNOTSUPP; + } +} + +static int emac_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct prueth_emac *emac = cb_priv; + + if (!tc_cls_can_offload_and_chain0(emac->ndev, type_data)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return emac_setup_tc_cls_flower(emac, type_data); + default: + return -EOPNOTSUPP; + } +} + +static LIST_HEAD(emac_block_cb_list); + +int emac_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct prueth_emac *emac = netdev_priv(dev); + + if (type == TC_SETUP_BLOCK) { + return flow_block_cb_setup_simple(type_data, + &emac_block_cb_list, + emac_setup_tc_block_cb, + emac, emac, true); + } + + return -EOPNOTSUPP; +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_switch.c b/drivers/net/ethernet/ti/prueth_switch.c --- a/drivers/net/ethernet/ti/prueth_switch.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_switch.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,1341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments PRUETH Switch Driver + * + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include +#include +#include "prueth.h" +#include "prueth_switch.h" +#include "prueth_fdb_tbl.h" + +#define FDB_IDX_TBL() \ + (&prueth->fdb_tbl->index_a->index_tbl_entry[0]) + +#define FDB_IDX_TBL_ENTRY(n) \ + (&prueth->fdb_tbl->index_a->index_tbl_entry[n]) + +#define FDB_MAC_TBL() \ + (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[0]) + +#define FDB_MAC_TBL_ENTRY(n) \ + (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n]) + +#define FDB_LEARN 1 +#define FDB_DELETE 2 +#define FDB_PURGE 3 + +struct prueth_sw_fdb_work { + struct work_struct work; + struct prueth_emac *emac; + u8 addr[ETH_ALEN]; + int event; +}; + +static inline +u8 prueth_sw_port_get_stp_state(struct prueth *prueth, enum prueth_port port) +{ + struct fdb_tbl *t = prueth->fdb_tbl; + u8 state; + + state = readb(port - 1 ? + &t->port2_stp_cfg->state : &t->port1_stp_cfg->state); + return state; +} + +const struct prueth_queue_info sw_queue_infos[][NUM_QUEUES] = { + [PRUETH_PORT_QUEUE_HOST] = { + [PRUETH_QUEUE1] = { + P0_Q1_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET, + P0_Q1_BD_OFFSET, + P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P0_Q2_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET + 8, + P0_Q2_BD_OFFSET, + P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P0_Q3_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET + 16, + P0_Q3_BD_OFFSET, + P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P0_Q4_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET + 24, + P0_Q4_BD_OFFSET, + P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII0] = { + [PRUETH_QUEUE1] = { + P1_Q1_BUFFER_OFFSET, + P1_Q1_BUFFER_OFFSET + + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q1_BD_OFFSET, + P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P1_Q2_BUFFER_OFFSET, + P1_Q2_BUFFER_OFFSET + + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q2_BD_OFFSET, + P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P1_Q3_BUFFER_OFFSET, + P1_Q3_BUFFER_OFFSET + + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q3_BD_OFFSET, + P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P1_Q4_BUFFER_OFFSET, + P1_Q4_BUFFER_OFFSET + + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q4_BD_OFFSET, + P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII1] = { + [PRUETH_QUEUE1] = { + P2_Q1_BUFFER_OFFSET, + P2_Q1_BUFFER_OFFSET + + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q1_BD_OFFSET, + P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P2_Q2_BUFFER_OFFSET, + P2_Q2_BUFFER_OFFSET + + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q2_BD_OFFSET, + P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P2_Q3_BUFFER_OFFSET, + P2_Q3_BUFFER_OFFSET + + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q3_BD_OFFSET, + P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P2_Q4_BUFFER_OFFSET, + P2_Q4_BUFFER_OFFSET + + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q4_BD_OFFSET, + P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, +}; + +static const struct prueth_queue_info rx_queue_infos[][NUM_QUEUES] = { + [PRUETH_PORT_QUEUE_HOST] = { + [PRUETH_QUEUE1] = { + P0_Q1_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET, + P0_Q1_BD_OFFSET, + P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P0_Q2_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 8, + P0_Q2_BD_OFFSET, + P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P0_Q3_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 16, + P0_Q3_BD_OFFSET, + P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P0_Q4_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 24, + P0_Q4_BD_OFFSET, + P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII0] = { + [PRUETH_QUEUE1] = { + P1_Q1_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET, + P1_Q1_BD_OFFSET, + P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P1_Q2_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET + 8, + P1_Q2_BD_OFFSET, + P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P1_Q3_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET + 16, + P1_Q3_BD_OFFSET, + P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P1_Q4_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET + 24, + P1_Q4_BD_OFFSET, + P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII1] = { + [PRUETH_QUEUE1] = { + P2_Q1_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET, + P2_Q1_BD_OFFSET, + P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] = { + P2_Q2_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET + 8, + P2_Q2_BD_OFFSET, + P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] = { + P2_Q3_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET + 16, + P2_Q3_BD_OFFSET, + P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] = { + P2_Q4_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET + 24, + P2_Q4_BD_OFFSET, + P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, +}; + +static const struct prueth_col_rx_context_info col_rx_context_infos[] = { + [PRUETH_PORT_QUEUE_HOST] = { + P0_COL_BUFFER_OFFSET, + P0_COL_BUFFER_OFFSET, + P0_COL_QUEUE_DESC_OFFSET, + END_OF_BD_POOL, + END_OF_BD_POOL + ((COL_QUEUE_SIZE - 1) * BD_SIZE) + }, + [PRUETH_PORT_QUEUE_MII0] = { + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_QUEUE_DESC_OFFSET + 8, + END_OF_BD_POOL, + END_OF_BD_POOL + ((COL_QUEUE_SIZE - 1) * BD_SIZE) + }, + + [PRUETH_PORT_QUEUE_MII1] = { + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_QUEUE_DESC_OFFSET + 16, + END_OF_BD_POOL, + END_OF_BD_POOL + ((COL_QUEUE_SIZE - 1) * BD_SIZE) + }, +}; + +static const struct prueth_col_tx_context_info col_tx_context_infos[] = { + [PRUETH_PORT_QUEUE_HOST] = { + P0_COL_BUFFER_OFFSET, + P0_COL_BUFFER_OFFSET, + P0_COL_BUFFER_OFFSET + ((COL_QUEUE_SIZE - 1) * ICSS_BLOCK_SIZE), + }, + [PRUETH_PORT_QUEUE_MII0] = { + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_BUFFER_OFFSET + ((COL_QUEUE_SIZE - 1) * ICSS_BLOCK_SIZE), + }, + [PRUETH_PORT_QUEUE_MII1] = { + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_BUFFER_OFFSET + (COL_QUEUE_SIZE * ICSS_BLOCK_SIZE), + P0_COL_BUFFER_OFFSET + ((COL_QUEUE_SIZE - 1) * ICSS_BLOCK_SIZE), + } +}; + +static const struct prueth_queue_desc col_queue_descs[3] = { + [PRUETH_PORT_QUEUE_MII0] = { + .rd_ptr = END_OF_BD_POOL, .wr_ptr = END_OF_BD_POOL, }, + [PRUETH_PORT_QUEUE_MII1] = { + .rd_ptr = END_OF_BD_POOL, .wr_ptr = END_OF_BD_POOL, } +}; + +void prueth_sw_free_fdb_table(struct prueth *prueth) +{ + if (prueth->emac_configured) + return; + + kfree(prueth->fdb_tbl); + prueth->fdb_tbl = NULL; +} + +void prueth_sw_hostconfig(struct prueth *prueth) +{ + void __iomem *dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va; + void __iomem *dram; + + /* queue information table */ + dram = dram1_base + P0_Q1_RX_CONTEXT_OFFSET; + memcpy_toio(dram, sw_queue_infos[PRUETH_PORT_QUEUE_HOST], + sizeof(sw_queue_infos[PRUETH_PORT_QUEUE_HOST])); + + dram = dram1_base + COL_RX_CONTEXT_P0_OFFSET_ADDR; + memcpy_toio(dram, &col_rx_context_infos[PRUETH_PORT_QUEUE_HOST], + sizeof(col_rx_context_infos[PRUETH_PORT_QUEUE_HOST])); + + /* buffer descriptor offset table*/ + dram = dram1_base + QUEUE_DESCRIPTOR_OFFSET_ADDR; + writew(P0_Q1_BD_OFFSET, dram); + writew(P0_Q2_BD_OFFSET, dram + 2); + writew(P0_Q3_BD_OFFSET, dram + 4); + writew(P0_Q4_BD_OFFSET, dram + 6); + + /* buffer offset table */ + dram = dram1_base + QUEUE_OFFSET_ADDR; + writew(P0_Q1_BUFFER_OFFSET, dram); + writew(P0_Q2_BUFFER_OFFSET, dram + 2); + writew(P0_Q3_BUFFER_OFFSET, dram + 4); + writew(P0_Q4_BUFFER_OFFSET, dram + 6); + + /* queue size lookup table */ + dram = dram1_base + QUEUE_SIZE_ADDR; + writew(HOST_QUEUE_1_SIZE, dram); + writew(HOST_QUEUE_1_SIZE, dram + 2); + writew(HOST_QUEUE_1_SIZE, dram + 4); + writew(HOST_QUEUE_1_SIZE, dram + 6); + + /* queue table */ + dram = dram1_base + P0_QUEUE_DESC_OFFSET; + memcpy_toio(dram, queue_descs[PRUETH_PORT_QUEUE_HOST], + sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); +} + +static int prueth_sw_port_config(struct prueth *prueth, + enum prueth_port port_id) +{ + unsigned int tx_context_ofs_addr, col_tx_context_ofs_addr, + rx_context_ofs, col_rx_context_ofs_addr, + queue_desc_ofs, col_queue_desc_ofs; + void __iomem *dram, *dram_base, *dram_mac; + struct prueth_emac *emac; + void __iomem *dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va; + + emac = prueth->emac[port_id - 1]; + switch (port_id) { + case PRUETH_PORT_MII0: + tx_context_ofs_addr = TX_CONTEXT_P1_Q1_OFFSET_ADDR; + col_tx_context_ofs_addr = COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR; + rx_context_ofs = P1_Q1_RX_CONTEXT_OFFSET; + col_rx_context_ofs_addr = COL_RX_CONTEXT_P1_OFFSET_ADDR; + queue_desc_ofs = P1_QUEUE_DESC_OFFSET; + col_queue_desc_ofs = P1_COL_QUEUE_DESC_OFFSET; + + /* for switch PORT MII0 mac addr is in DRAM0. */ + dram_mac = prueth->mem[PRUETH_MEM_DRAM0].va; + break; + case PRUETH_PORT_MII1: + tx_context_ofs_addr = TX_CONTEXT_P2_Q1_OFFSET_ADDR; + col_tx_context_ofs_addr = COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR; + rx_context_ofs = P2_Q1_RX_CONTEXT_OFFSET; + col_rx_context_ofs_addr = COL_RX_CONTEXT_P2_OFFSET_ADDR; + queue_desc_ofs = P2_QUEUE_DESC_OFFSET; + col_queue_desc_ofs = P2_COL_QUEUE_DESC_OFFSET; + + /* for switch PORT MII1 mac addr is in DRAM1. */ + dram_mac = prueth->mem[PRUETH_MEM_DRAM1].va; + break; + default: + netdev_err(emac->ndev, "invalid port\n"); + return -EINVAL; + } + + /* setup mac address */ + memcpy_toio(dram_mac + PORT_MAC_ADDR, emac->mac_addr, 6); + + /* Remaining switch port configs are in DRAM1 */ + dram_base = prueth->mem[PRUETH_MEM_DRAM1].va; + + /* queue information table */ + memcpy_toio(dram_base + tx_context_ofs_addr, + sw_queue_infos[port_id], + sizeof(sw_queue_infos[port_id])); + + memcpy_toio(dram_base + col_tx_context_ofs_addr, + &col_tx_context_infos[port_id], + sizeof(col_tx_context_infos[port_id])); + + memcpy_toio(dram_base + rx_context_ofs, + rx_queue_infos[port_id], + sizeof(rx_queue_infos[port_id])); + + memcpy_toio(dram_base + col_rx_context_ofs_addr, + &col_rx_context_infos[port_id], + sizeof(col_rx_context_infos[port_id])); + + /* buffer descriptor offset table*/ + dram = dram_base + QUEUE_DESCRIPTOR_OFFSET_ADDR + + (port_id * NUM_QUEUES * sizeof(u16)); + writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_desc_offset, dram); + writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_desc_offset, + dram + 2); + writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_desc_offset, + dram + 4); + writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_desc_offset, + dram + 6); + + /* buffer offset table */ + dram = dram_base + QUEUE_OFFSET_ADDR + + port_id * NUM_QUEUES * sizeof(u16); + writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_offset, dram); + writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_offset, + dram + 2); + writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_offset, + dram + 4); + writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_offset, + dram + 6); + + /* queue size lookup table */ + dram = dram_base + QUEUE_SIZE_ADDR + + port_id * NUM_QUEUES * sizeof(u16); + writew(QUEUE_1_SIZE, dram); + writew(QUEUE_2_SIZE, dram + 2); + writew(QUEUE_3_SIZE, dram + 4); + writew(QUEUE_4_SIZE, dram + 6); + + /* collision queue table */ + memcpy_toio(dram_base + col_queue_desc_ofs, + &col_queue_descs[port_id], + sizeof(col_queue_descs[port_id])); + + /* queue table */ + memcpy_toio(dram_base + queue_desc_ofs, + &queue_descs[port_id][0], + 4 * sizeof(queue_descs[port_id][0])); + + emac->rx_queue_descs = dram1_base + P0_QUEUE_DESC_OFFSET; + emac->tx_queue_descs = dram1_base + + rx_queue_infos[port_id][PRUETH_QUEUE1].queue_desc_offset; + + return 0; +} + +int prueth_sw_emac_config(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + + /* PRU needs local shared RAM address for C28 */ + u32 sharedramaddr = ICSS_LOCAL_SHARED_RAM; + /* PRU needs real global OCMC address for C30*/ + u32 ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; + int ret; + + if (prueth->emac_configured & BIT(emac->port_id)) + return 0; + + ret = prueth_sw_port_config(prueth, emac->port_id); + if (ret) + return ret; + + if (!prueth->emac_configured) { + /* Set in constant table C28 of PRUn to ICSS Shared memory */ + pru_rproc_set_ctable(prueth->pru0, PRU_C28, sharedramaddr); + pru_rproc_set_ctable(prueth->pru1, PRU_C28, sharedramaddr); + + /* Set in constant table C30 of PRUn to OCMC memory */ + pru_rproc_set_ctable(prueth->pru0, PRU_C30, ocmcaddr); + pru_rproc_set_ctable(prueth->pru1, PRU_C30, ocmcaddr); + } + return 0; +} + +void prueth_sw_fdb_tbl_init(struct prueth *prueth) +{ + struct fdb_tbl *t = prueth->fdb_tbl; + + t->index_a = prueth->mem[V2_1_FDB_TBL_LOC].va + + V2_1_FDB_TBL_OFFSET; + + t->mac_tbl_a = (void __iomem *)t->index_a + + FDB_INDEX_TBL_MAX_ENTRIES * + sizeof(struct fdb_index_tbl_entry_t); + + t->port1_stp_cfg = (void __iomem *)t->mac_tbl_a + + FDB_MAC_TBL_MAX_ENTRIES * + sizeof(struct fdb_mac_tbl_entry_t); + + t->port2_stp_cfg = (void __iomem *)t->port1_stp_cfg + + sizeof(struct fdb_stp_config); + + t->flood_enable_flags = (void __iomem *)t->port2_stp_cfg + + sizeof(struct fdb_stp_config); + + t->locks = (void __iomem *)t->flood_enable_flags + + sizeof(struct fdb_flood_config); + + t->flood_enable_flags->host_flood_enable = 1; + t->flood_enable_flags->port1_flood_enable = 1; + t->flood_enable_flags->port2_flood_enable = 1; + t->locks->host_lock = 0; + t->total_entries = 0; +} + +static void prueth_sw_fdb_spin_lock(struct fdb_tbl *fdb_tbl) +{ + /* Take the host lock */ + writeb(1, &fdb_tbl->locks->host_lock); + + /* Wait for the PRUs to release their locks */ + while (readb(&fdb_tbl->locks->pru_locks)) + ; +} + +static inline void prueth_sw_fdb_spin_unlock(struct fdb_tbl *fdb_tbl) +{ + writeb(0, &fdb_tbl->locks->host_lock); +} + +static void mac_copy(u8 *dst, const u8 *src) +{ + u8 i; + + for (i = 0; i < 6; i++) { + *(dst) = *(src); + dst++; + src++; + } +} + +/* -1 mac_a < mac_b + * 0 mac_a == mac_b + * 1 mac_a > mac_b + */ +static s8 mac_cmp(const u8 *mac_a, const u8 *mac_b) +{ + s8 ret = 0, i; + + for (i = 0; i < 6; i++) { + if (mac_a[i] == mac_b[i]) + continue; + + ret = mac_a[i] < mac_b[i] ? -1 : 1; + break; + } + + return ret; +} + +static inline u8 prueth_sw_fdb_hash(const u8 *mac) +{ + return mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]; +} + +static s16 +prueth_sw_fdb_search(struct fdb_mac_tbl_array_t *mac_tbl, + struct fdb_index_tbl_entry_t *bucket_info, + const u8 *mac) +{ + int i; + u8 mac_tbl_idx = bucket_info->bucket_idx; + + for (i = 0; i < bucket_info->bucket_entries; i++, mac_tbl_idx++) { + if (!mac_cmp(mac, mac_tbl->mac_tbl_entry[mac_tbl_idx].mac)) + return mac_tbl_idx; + } + + return -ENODATA; +} + +static u16 prueth_sw_fdb_find_open_slot(struct fdb_tbl *fdb_tbl) +{ + u16 i; + + for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { + if (!fdb_tbl->mac_tbl_a->mac_tbl_entry[i].active) + break; + } + + return i; +} + +/* port: 0 based: 0=port1, 1=port2 */ +static s16 +prueth_sw_fdb_find_bucket_insert_point(struct fdb_tbl *fdb, + struct fdb_index_tbl_entry_t *bkt_info, + const u8 *mac, const u8 port) +{ + struct fdb_mac_tbl_array_t *mac_tbl = fdb->mac_tbl_a; + struct fdb_mac_tbl_entry_t *e; + int i; + u8 mac_tbl_idx; + s8 cmp; + + mac_tbl_idx = bkt_info->bucket_idx; + + for (i = 0; i < bkt_info->bucket_entries; i++, mac_tbl_idx++) { + e = &mac_tbl->mac_tbl_entry[mac_tbl_idx]; + cmp = mac_cmp(mac, e->mac); + if (cmp < 0) { + return mac_tbl_idx; + } else if (cmp == 0) { + if (e->port != port) { + /* mac is already in FDB, only port is + * different. So just update the port. + * Note: total_entries and bucket_entries + * remain the same. + */ + prueth_sw_fdb_spin_lock(fdb); + e->port = port; + prueth_sw_fdb_spin_unlock(fdb); + } + + /* mac and port are the same, touch the fdb */ + e->age = 0; + return -1; + } + } + + return mac_tbl_idx; +} + +static s16 +prueth_sw_fdb_check_empty_slot_left(struct fdb_mac_tbl_array_t *mac_tbl, + u8 mac_tbl_idx) +{ + s16 i; + + for (i = mac_tbl_idx - 1; i > -1; i--) { + if (!mac_tbl->mac_tbl_entry[i].active) + break; + } + + return i; +} + +static s16 +prueth_sw_fdb_check_empty_slot_right(struct fdb_mac_tbl_array_t *mac_tbl, + u8 mac_tbl_idx) +{ + s16 i; + + for (i = mac_tbl_idx; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { + if (!mac_tbl->mac_tbl_entry[i].active) + return i; + } + + return -1; +} + +static void prueth_sw_fdb_move_range_left(struct prueth *prueth, + u16 left, u16 right) +{ + u16 i; + u8 *src, *dst; + u32 sz = 0; + + for (i = left; i < right; i++) { + dst = (u8 *)FDB_MAC_TBL_ENTRY(i); + src = (u8 *)FDB_MAC_TBL_ENTRY(i + 1); + sz = sizeof(struct fdb_mac_tbl_entry_t); + memcpy_toio(dst, src, sz); + } +} + +static void prueth_sw_fdb_move_range_right(struct prueth *prueth, + u16 left, u16 right) +{ + u16 i; + u8 *src, *dst; + u32 sz = 0; + + for (i = right; i > left; i--) { + dst = (u8 *)FDB_MAC_TBL_ENTRY(i); + src = (u8 *)FDB_MAC_TBL_ENTRY(i - 1); + sz = sizeof(struct fdb_mac_tbl_entry_t); + memcpy_toio(dst, src, sz); + } +} + +static void prueth_sw_fdb_update_index_tbl(struct prueth *prueth, + u16 left, u16 right) +{ + u16 i; + u8 hash, hash_prev; + + /* To ensure we don't improperly update the + * bucket index, initialize with an invalid + * hash in case we are in leftmost slot + */ + hash_prev = 0xff; + + if (left > 0) { + hash_prev = + prueth_sw_fdb_hash(FDB_MAC_TBL_ENTRY(left - 1)->mac); + } + + /* For each moved element, update the bucket index */ + for (i = left; i <= right; i++) { + hash = prueth_sw_fdb_hash(FDB_MAC_TBL_ENTRY(i)->mac); + + /* Only need to update buckets once */ + if (hash != hash_prev) + FDB_IDX_TBL_ENTRY(hash)->bucket_idx = i; + + hash_prev = hash; + } +} + +static struct fdb_mac_tbl_entry_t * +prueth_sw_get_empty_mac_tbl_entry(struct prueth *prueth, + struct fdb_index_tbl_entry_t *bucket_info, + u8 suggested_mac_tbl_idx, + bool *update_indexes) +{ + struct fdb_tbl *fdb = prueth->fdb_tbl; + struct fdb_mac_tbl_array_t *mt = fdb->mac_tbl_a; + s16 empty_slot_idx = 0, left = 0, right = 0; + u8 mti = suggested_mac_tbl_idx; + + if (!FDB_MAC_TBL_ENTRY(mti)->active) { + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(mti)->active = 1; + + return FDB_MAC_TBL_ENTRY(mti); + } + + if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) + return NULL; + + empty_slot_idx = prueth_sw_fdb_check_empty_slot_left(mt, mti); + if (empty_slot_idx == -1) { + /* Nothing available on the left. But table isn't full + * so there must be space to the right, + */ + empty_slot_idx = prueth_sw_fdb_check_empty_slot_right(mt, mti); + + /* Shift right */ + left = mti; + right = empty_slot_idx; + prueth_sw_fdb_move_range_right(prueth, left, right); + + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(mti)->active = 1; + + /* There is a chance we moved something in a + * different bucket, update index table + */ + prueth_sw_fdb_update_index_tbl(prueth, left, right); + + return FDB_MAC_TBL_ENTRY(mti); + } + + if (empty_slot_idx == mti - 1) { + /* There is space immediately left of the open slot, + * which means the inserted MAC address. + * Must be the lowest-valued MAC address in bucket. + * Update bucket pointer accordingly. + */ + bucket_info->bucket_idx = empty_slot_idx; + + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(empty_slot_idx)->active = 1; + + return FDB_MAC_TBL_ENTRY(empty_slot_idx); + } + + /* There is empty space to the left, shift MAC table entries left */ + left = empty_slot_idx; + right = mti - 1; + prueth_sw_fdb_move_range_left(prueth, left, right); + + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(mti - 1)->active = 1; + + /* There is a chance we moved something in a + * different bucket, update index table + */ + prueth_sw_fdb_update_index_tbl(prueth, left, right); + + return FDB_MAC_TBL_ENTRY(mti - 1); +} + +static int prueth_sw_insert_fdb_entry(struct prueth_emac *emac, + const u8 *mac, u8 is_static) +{ + struct prueth *prueth = emac->prueth; + struct prueth_emac *other_emac; + struct fdb_tbl *fdb = prueth->fdb_tbl; + struct fdb_index_tbl_entry_t *bucket_info; + struct fdb_mac_tbl_entry_t *mac_info; + u8 hash_val, mac_tbl_idx; + s16 ret; + + other_emac = prueth->emac[other_port_id(emac->port_id) - 1]; + + if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) + return -ENOMEM; + + if (mac_cmp(mac, emac->mac_addr) == 0 || + mac_cmp(mac, other_emac->mac_addr) == 0) { + /* Don't insert fdb of own mac addr */ + return -EINVAL; + } + + /* Empty mac table entries are available */ + + /* Get the bucket that the mac belongs to */ + hash_val = prueth_sw_fdb_hash(mac); + bucket_info = FDB_IDX_TBL_ENTRY(hash_val); + + if (!bucket_info->bucket_entries) { + mac_tbl_idx = prueth_sw_fdb_find_open_slot(fdb); + bucket_info->bucket_idx = mac_tbl_idx; + } + + ret = prueth_sw_fdb_find_bucket_insert_point(fdb, bucket_info, mac, + emac->port_id - 1); + + if (ret < 0) + /* mac is already in fdb table */ + return 0; + + mac_tbl_idx = ret; + + prueth_sw_fdb_spin_lock(fdb); + + mac_info = prueth_sw_get_empty_mac_tbl_entry(prueth, bucket_info, + mac_tbl_idx, NULL); + if (!mac_info) { + /* Should not happen */ + dev_warn(prueth->dev, "OUT of MEM\n"); + return -ENOMEM; + } + + mac_copy(mac_info->mac, mac); + mac_info->active = 1; + mac_info->age = 0; + mac_info->port = emac->port_id - 1; + mac_info->is_static = is_static; + + bucket_info->bucket_entries++; + fdb->total_entries++; + + prueth_sw_fdb_spin_unlock(fdb); + + dev_dbg(prueth->dev, "added fdb: %pM port=%d total_entries=%u\n", + mac, emac->port_id, fdb->total_entries); + + return 0; +} + +static int prueth_sw_delete_fdb_entry(struct prueth_emac *emac, + const u8 *mac, u8 is_static) +{ + struct prueth *prueth = emac->prueth; + struct fdb_tbl *fdb = prueth->fdb_tbl; + struct fdb_mac_tbl_array_t *mt = fdb->mac_tbl_a; + struct fdb_index_tbl_entry_t *bucket_info; + struct fdb_mac_tbl_entry_t *mac_info; + u8 hash_val, mac_tbl_idx; + s16 ret, left, right; + + if (fdb->total_entries == 0) + return 0; + + /* Get the bucket that the mac belongs to */ + hash_val = prueth_sw_fdb_hash(mac); + bucket_info = FDB_IDX_TBL_ENTRY(hash_val); + + ret = prueth_sw_fdb_search(mt, bucket_info, mac); + if (ret < 0) + return ret; + + mac_tbl_idx = ret; + mac_info = FDB_MAC_TBL_ENTRY(mac_tbl_idx); + + prueth_sw_fdb_spin_lock(fdb); + + /* Shift all elements in bucket to the left. No need to + * update index table since only shifting within bucket. + */ + left = mac_tbl_idx; + right = bucket_info->bucket_idx + bucket_info->bucket_entries - 1; + prueth_sw_fdb_move_range_left(prueth, left, right); + + /* Remove end of bucket from table */ + mac_info = FDB_MAC_TBL_ENTRY(right); + mac_info->active = 0; + bucket_info->bucket_entries--; + fdb->total_entries--; + + prueth_sw_fdb_spin_unlock(fdb); + + dev_dbg(prueth->dev, "del fdb: %pM total_entries=%u\n", + mac, fdb->total_entries); + + return 0; +} + +static int prueth_sw_do_purge_fdb(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + struct fdb_tbl *fdb = prueth->fdb_tbl; + s16 i; + + if (fdb->total_entries == 0) + return 0; + + prueth_sw_fdb_spin_lock(fdb); + + for (i = 0; i < FDB_INDEX_TBL_MAX_ENTRIES; i++) + fdb->index_a->index_tbl_entry[i].bucket_entries = 0; + + for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) + fdb->mac_tbl_a->mac_tbl_entry[i].active = 0; + + fdb->total_entries = 0; + + prueth_sw_fdb_spin_unlock(fdb); + return 0; +} + +static void prueth_sw_fdb_work(struct work_struct *work) +{ + struct prueth_sw_fdb_work *fdb_work = + container_of(work, struct prueth_sw_fdb_work, work); + struct prueth_emac *emac = fdb_work->emac; + + rtnl_lock(); + + /* Interface is not up */ + if (!emac->prueth->fdb_tbl) { + rtnl_unlock(); + return; + } + + switch (fdb_work->event) { + case FDB_LEARN: + prueth_sw_insert_fdb_entry(emac, fdb_work->addr, 0); + break; + case FDB_PURGE: + prueth_sw_do_purge_fdb(emac); + break; + default: + break; + } + rtnl_unlock(); + + kfree(fdb_work); + dev_put(emac->ndev); +} + +int prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac) +{ + struct prueth_sw_fdb_work *fdb_work; + + fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC); + if (WARN_ON(!fdb_work)) + return -ENOMEM; + + INIT_WORK(&fdb_work->work, prueth_sw_fdb_work); + + fdb_work->event = FDB_LEARN; + fdb_work->emac = emac; + ether_addr_copy(fdb_work->addr, src_mac); + + dev_hold(emac->ndev); + queue_work(system_long_wq, &fdb_work->work); + return 0; +} + +static int prueth_sw_purge_fdb(struct prueth_emac *emac) +{ + struct prueth_sw_fdb_work *fdb_work; + + fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC); + if (WARN_ON(!fdb_work)) + return -ENOMEM; + + INIT_WORK(&fdb_work->work, prueth_sw_fdb_work); + + fdb_work->event = FDB_PURGE; + fdb_work->emac = emac; + + dev_hold(emac->ndev); + queue_work(system_long_wq, &fdb_work->work); + return 0; +} + +int prueth_sw_init_fdb_table(struct prueth *prueth) +{ + if (prueth->emac_configured) + return 0; + + prueth->fdb_tbl = kmalloc(sizeof(*prueth->fdb_tbl), GFP_KERNEL); + if (!prueth->fdb_tbl) + return -ENOMEM; + + prueth_sw_fdb_tbl_init(prueth); + + return 0; +} + +int prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev) +{ + const struct prueth_firmware *pru_firmwares; + const char *fw_name, *fw_name1; + int ret; + + if (prueth->emac_configured) + return 0; + + pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU0]; + fw_name = pru_firmwares->fw_name[prueth->eth_type]; + pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU1]; + fw_name1 = pru_firmwares->fw_name[prueth->eth_type]; + + ret = rproc_set_firmware(prueth->pru0, fw_name); + if (ret) { + netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n", + fw_name, ret); + return ret; + } + ret = rproc_boot(prueth->pru0); + if (ret) { + netdev_err(ndev, "failed to boot PRU: %d\n", ret); + return ret; + } + + ret = rproc_set_firmware(prueth->pru1, fw_name1); + if (ret) { + netdev_err(ndev, "failed to set PRU1 firmware %s: %d\n", + fw_name, ret); + goto rproc0_shutdown; + } + ret = rproc_boot(prueth->pru1); + if (ret) { + netdev_err(ndev, "failed to boot PRU: %d\n", ret); + goto rproc0_shutdown; + } + + return 0; + +rproc0_shutdown: + rproc_shutdown(prueth->pru0); + return ret; +} + +int prueth_sw_shutdown_prus(struct prueth_emac *emac, struct net_device *ndev) +{ + struct prueth *prueth = emac->prueth; + + if (prueth->emac_configured) + return 0; + + rproc_shutdown(prueth->pru0); + rproc_shutdown(prueth->pru1); + + return 0; +} + +static int prueth_switchdev_attr_set(struct net_device *ndev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int err = 0; + u8 o_state; + + /* Interface is not up */ + if (!prueth->fdb_tbl) + return 0; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + o_state = prueth_sw_port_get_stp_state(prueth, emac->port_id); + prueth_sw_port_set_stp_state(prueth, emac->port_id, + attr->u.stp_state); + + if (o_state != attr->u.stp_state) + prueth_sw_purge_fdb(emac); + + dev_dbg(prueth->dev, "attr set: stp state:%u port:%u\n", + attr->u.stp_state, emac->port_id); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int prueth_switchdev_obj_add(struct net_device *ndev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans, + struct netlink_ext_ack *extack) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + struct switchdev_obj_port_mdb *mdb; + int ret = 0; + u8 hash; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_HOST_MDB: + mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n", + ndev->name, mdb->vid, mdb->addr, emac->port_id); + hash = emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); + emac_mc_filter_bin_allow(emac, hash); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static int prueth_switchdev_obj_del(struct net_device *ndev, + const struct switchdev_obj *obj) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + struct switchdev_obj_port_mdb *mdb; + struct netdev_hw_addr *ha; + u8 hash, tmp_hash; + int ret = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_HOST_MDB: + mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n", + ndev->name, mdb->vid, mdb->addr, emac->port_id); + hash = emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); + netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { + tmp_hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); + /* Another MC address is in the bin. Don't disable. */ + if (tmp_hash == hash) + return 0; + } + emac_mc_filter_bin_disallow(emac, hash); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +/* switchdev notifiers */ +static int prueth_sw_switchdev_blocking_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + int err; + + if (!PRUETH_IS_SWITCH(prueth)) + return NOTIFY_DONE; + + switch (event) { + case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(ndev, ptr, + prueth_sw_port_dev_check, + prueth_switchdev_attr_set); + return notifier_from_errno(err); + + case SWITCHDEV_PORT_OBJ_ADD: + err = switchdev_handle_port_obj_add(ndev, ptr, + prueth_sw_port_dev_check, + prueth_switchdev_obj_add); + return notifier_from_errno(err); + + case SWITCHDEV_PORT_OBJ_DEL: + err = switchdev_handle_port_obj_del(ndev, ptr, + prueth_sw_port_dev_check, + prueth_switchdev_obj_del); + return notifier_from_errno(err); + default: + break; + } + + return NOTIFY_DONE; +} + +/* switchev event work */ +struct prueth_sw_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct prueth_emac *emac; + unsigned long event; +}; + +static void +prueth_sw_fdb_offload_notify(struct net_device *ndev, + struct switchdev_notifier_fdb_info *rcv) +{ + struct switchdev_notifier_fdb_info info; + + info.addr = rcv->addr; + info.vid = rcv->vid; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ndev, &info.info, + NULL); +} + +static void prueth_sw_fdb_add(struct prueth_emac *emac, + struct switchdev_notifier_fdb_info *fdb) +{ + prueth_sw_insert_fdb_entry(emac, fdb->addr, 1); +} + +static void prueth_sw_fdb_del(struct prueth_emac *emac, + struct switchdev_notifier_fdb_info *fdb) +{ + prueth_sw_delete_fdb_entry(emac, fdb->addr, 1); +} + +static void prueth_sw_switchdev_event_work(struct work_struct *work) +{ + struct prueth_sw_switchdev_event_work *switchdev_work = + container_of(work, struct prueth_sw_switchdev_event_work, work); + struct prueth_emac *emac = switchdev_work->emac; + struct switchdev_notifier_fdb_info *fdb; + struct prueth *prueth = emac->prueth; + int port = emac->port_id; + + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb = &switchdev_work->fdb_info; + dev_dbg(prueth->dev, + "prueth fdb add: MACID = %pM vid = %u flags = %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, port); + + if (!fdb->added_by_user) + break; + + prueth_sw_fdb_add(emac, fdb); + prueth_sw_fdb_offload_notify(emac->ndev, fdb); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb = &switchdev_work->fdb_info; + dev_dbg(prueth->dev, + "prueth fdb del: MACID = %pM vid = %u flags = %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, port); + + if (!fdb->added_by_user) + break; + + prueth_sw_fdb_del(emac, fdb); + break; + default: + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(emac->ndev); +} + +/* called under rcu_read_lock() */ +static int prueth_sw_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); + struct switchdev_notifier_fdb_info *fdb_info = ptr; + struct prueth_sw_switchdev_event_work *switchdev_work; + struct prueth_emac *emac = netdev_priv(ndev); + int err; + + netdev_dbg(ndev, "switchdev_event: event=%lu", event); + + if (event == SWITCHDEV_PORT_ATTR_SET) { + err = switchdev_handle_port_attr_set(ndev, ptr, + prueth_sw_port_dev_check, + prueth_switchdev_attr_set); + return notifier_from_errno(err); + } + + if (!prueth_sw_port_dev_check(ndev)) + return NOTIFY_DONE; + + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, prueth_sw_switchdev_event_work); + switchdev_work->emac = emac; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + if (!switchdev_work->fdb_info.addr) + goto err_addr_alloc; + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + dev_hold(ndev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(system_long_wq, &switchdev_work->work); + + return NOTIFY_DONE; + +err_addr_alloc: + kfree(switchdev_work); + return NOTIFY_BAD; +} + +int prueth_sw_register_notifiers(struct prueth *prueth) +{ + struct notifier_block *nb; + int ret; + + nb = &prueth->prueth_sw_switchdev_notifier; + nb->notifier_call = prueth_sw_switchdev_event; + ret = register_switchdev_notifier(nb); + if (ret) { + dev_err(prueth->dev, + "register switchdev notifier failed ret:%d\n", ret); + return ret; + } + + nb = &prueth->prueth_sw_switchdev_bl_notifier; + nb->notifier_call = prueth_sw_switchdev_blocking_event; + ret = register_switchdev_blocking_notifier(nb); + if (ret) { + dev_err(prueth->dev, "register switchdev blocking notifier failed ret:%d\n", + ret); + nb = &prueth->prueth_sw_switchdev_notifier; + unregister_switchdev_notifier(nb); + return ret; + } + + return 0; +} + +void prueth_sw_unregister_notifiers(struct prueth *prueth) +{ + unregister_switchdev_blocking_notifier(&prueth->prueth_sw_switchdev_bl_notifier); + unregister_switchdev_notifier(&prueth->prueth_sw_switchdev_notifier); +} diff -Naur --no-dereference a/drivers/net/ethernet/ti/prueth_switch.h b/drivers/net/ethernet/ti/prueth_switch.h --- a/drivers/net/ethernet/ti/prueth_switch.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ethernet/ti/prueth_switch.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#ifndef __NET_TI_PRUETH_SWITCH_H +#define __NET_TI_PRUETH_SWITCH_H + +#include "prueth.h" +#include "prueth_fdb_tbl.h" + +struct prueth_col_rx_context_info { + u16 buffer_offset; + u16 buffer_offset2; + u16 queue_desc_offset; + u16 buffer_desc_offset; + u16 buffer_desc_end; +} __packed; + +struct prueth_col_tx_context_info { + u16 buffer_offset; + u16 buffer_offset2; + u16 buffer_offset_end; +} __packed; + +static inline enum prueth_port other_port_id(enum prueth_port port_id) +{ + enum prueth_port other_port_id = + (port_id == PRUETH_PORT_MII0) ? PRUETH_PORT_MII1 : + PRUETH_PORT_MII0; + return other_port_id; +} + +static inline +void prueth_sw_port_set_stp_state(struct prueth *prueth, + enum prueth_port port, u8 state) +{ + struct fdb_tbl *t = prueth->fdb_tbl; + + writeb(state, port - 1 ? + &t->port2_stp_cfg->state : &t->port1_stp_cfg->state); +} + +void prueth_sw_hostconfig(struct prueth *prueth); +int prueth_sw_emac_config(struct prueth_emac *emac); +void prueth_sw_fdb_tbl_init(struct prueth *prueth); +int prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac); +int prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev); +int prueth_sw_shutdown_prus(struct prueth_emac *emac, struct net_device *ndev); +int prueth_sw_register_notifiers(struct prueth *prueth); +void prueth_sw_unregister_notifiers(struct prueth *prueth); +bool prueth_sw_port_dev_check(const struct net_device *ndev); +int prueth_sw_init_fdb_table(struct prueth *prueth); +void prueth_sw_free_fdb_table(struct prueth *prueth); + + +extern const struct prueth_queue_info sw_queue_infos[][4]; + +#endif /* __NET_TI_PRUETH_SWITCH_H */ diff -Naur --no-dereference a/drivers/net/ieee802154/bcfserial.c b/drivers/net/ieee802154/bcfserial.c --- a/drivers/net/ieee802154/bcfserial.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ieee802154/bcfserial.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,680 @@ + +/* + * bcfserial.c - Serial interface driver for BeagleConnect Freedom. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEBUG + +#define BCFSERIAL_DRV_VERSION "0.1.0" +#define BCFSERIAL_DRV_NAME "bcfserial" + +#define HDLC_FRAME 0x7E +#define HDLC_ESC 0x7D +#define HDLC_XOR 0x20 + +#define ADDRESS_CTRL 0x01 +#define ADDRESS_WPAN 0x03 +#define ADDRESS_CDC 0x05 +#define ADDRESS_HW 0x41 + +#define MAX_PSDU 127 +#define MAX_RX_XFER (1 + MAX_PSDU + 2 + 1) /* PHR+PSDU+CRC+LQI */ +#define HDLC_HEADER_LEN 2 +#define PACKET_HEADER_LEN 8 +#define CRC_LEN 2 +#define RX_HDLC_PAYLOAD 140 +#define MAX_TX_HDLC (1 + HDLC_HEADER_LEN + PACKET_HEADER_LEN + MAX_RX_XFER + CRC_LEN + 1) +#define MAX_RX_HDLC (1 + RX_HDLC_PAYLOAD + CRC_LEN) +#define TX_CIRC_BUF_SIZE 1024 + +enum bcfserial_requests { + RESET, + TX, + XMIT_ASYNC, + ED, + SET_CHANNEL, + START, + STOP, + SET_SHORT_ADDR, + SET_PAN_ID, + SET_IEEE_ADDR, + SET_TXPOWER, + SET_CCA_MODE, + SET_CCA_ED_LEVEL, + SET_CSMA_PARAMS, + SET_LBT, + SET_FRAME_RETRIES, + SET_PROMISCUOUS_MODE, + GET_EXTENDED_ADDR, + GET_SUPPORTED_CHANNELS, +}; + +struct bcfserial { + struct serdev_device *serdev; + struct ieee802154_hw *hw; + + struct work_struct tx_work; + spinlock_t tx_producer_lock; + spinlock_t tx_consumer_lock; + struct circ_buf tx_circ_buf; + struct sk_buff *tx_skb; + u16 tx_crc; + u8 tx_ack_seq; /* current TX ACK sequence number */ + + size_t response_size; + u8 *response_buffer; + + u8 rx_in_esc; + u8 rx_address; + u16 rx_offset; + u8 *rx_buffer; +}; + +// RX Packet Format: +// - WPAN RX PACKET: [len] payload [lqi] +// - WPAN TX ACK: [seq] +// - WPAN CAPABILITIES: supported_channels_mask(4) +// - CDC: printable_chars + +static void bcfserial_serdev_write_locked(struct bcfserial *bcfserial) +{ + //must be locked already + int head = smp_load_acquire(&bcfserial->tx_circ_buf.head); + int tail = bcfserial->tx_circ_buf.tail; + int count = CIRC_CNT_TO_END(head, tail, TX_CIRC_BUF_SIZE); + int written; + + if (count >= 1) { + written = serdev_device_write_buf(bcfserial->serdev, &bcfserial->tx_circ_buf.buf[tail], count); + + smp_store_release(&(bcfserial->tx_circ_buf.tail), (tail + written) & (TX_CIRC_BUF_SIZE - 1)); + } +} + +static void bcfserial_append(struct bcfserial *bcfserial, u8 value) +{ + //must be locked already + int head = bcfserial->tx_circ_buf.head; + + while(true) + { + int tail = READ_ONCE(bcfserial->tx_circ_buf.tail); + + if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= 1) { + + bcfserial->tx_circ_buf.buf[head] = value; + + smp_store_release(&(bcfserial->tx_circ_buf.head), + (head + 1) & (TX_CIRC_BUF_SIZE - 1)); + return; + } else { + dev_dbg(&bcfserial->serdev->dev, "Tx circ buf full\n"); + usleep_range(3000,5000); + } + } +} + +static void bcfserial_append_tx_frame(struct bcfserial *bcfserial) +{ + bcfserial->tx_crc = 0xFFFF; + bcfserial_append(bcfserial, HDLC_FRAME); +} + +static void bcfserial_append_escaped(struct bcfserial *bcfserial, u8 value) +{ + if (value == HDLC_FRAME || value == HDLC_ESC) { + bcfserial_append(bcfserial, HDLC_ESC); + value ^= HDLC_XOR; + } + bcfserial_append(bcfserial, value); +} + +static void bcfserial_append_tx_u8(struct bcfserial *bcfserial, u8 value) +{ + bcfserial->tx_crc = crc_ccitt(bcfserial->tx_crc, &value, 1); + bcfserial_append_escaped(bcfserial, value); +} + +static void bcfserial_append_tx_buffer(struct bcfserial *bcfserial, const void *buffer, size_t len) +{ + size_t i; + for (i=0; itx_crc ^= 0xffff; + bcfserial_append_escaped(bcfserial, bcfserial->tx_crc & 0xff); + bcfserial_append_escaped(bcfserial, (bcfserial->tx_crc >> 8) & 0xff); +} + +static void bcfserial_hdlc_send(struct bcfserial *bcfserial, u8 cmd, u16 value, u16 index, u16 length, const void* buffer) +{ + // HDLC_FRAME + // 0 address : 0x01 + // 1 control : 0x03 + // 2 [bmRequestType] : 0x00 + // 3 cmd (TX, START, STOP, etc) + // 4/5 value + // 6/7 index + // 8/9 length + // contents + // x/y crc + // HDLC_FRAME + + spin_lock(&bcfserial->tx_producer_lock); + + bcfserial_append_tx_frame(bcfserial); + bcfserial_append_tx_u8(bcfserial, 0x01); //address + bcfserial_append_tx_u8(bcfserial, 0x03); //control + bcfserial_append_tx_u8(bcfserial, 0x00); //ignored + bcfserial_append_tx_u8(bcfserial, cmd); + bcfserial_append_tx_le16(bcfserial, value); + bcfserial_append_tx_le16(bcfserial, index); + bcfserial_append_tx_le16(bcfserial, length); + bcfserial_append_tx_buffer(bcfserial, buffer, length); + bcfserial_append_tx_crc(bcfserial); + bcfserial_append_tx_frame(bcfserial); + + spin_unlock(&bcfserial->tx_producer_lock); + + spin_lock(&bcfserial->tx_consumer_lock); + bcfserial_serdev_write_locked(bcfserial); + spin_unlock(&bcfserial->tx_consumer_lock); +} + +static void bcfserial_hdlc_send_cmd(struct bcfserial *bcfserial, u8 cmd) +{ + bcfserial_hdlc_send(bcfserial, cmd, 0, 0, 0, NULL); +} + +static void bcfserial_hdlc_send_ack(struct bcfserial *bcfserial, u8 address, u8 seq) +{ + // To make this a valid S-frame: + // u8 ctrl = (((seq + 1) & 0x07) << 5) | 0x01; + // TODO Fix control frame type bug here and in wpanusb_bc + + spin_lock(&bcfserial->tx_producer_lock); + + bcfserial_append_tx_frame(bcfserial); + bcfserial_append_tx_u8(bcfserial, address); //address + bcfserial_append_tx_u8(bcfserial, 0x00); //control + bcfserial_append_tx_crc(bcfserial); + bcfserial_append_tx_frame(bcfserial); + + spin_unlock(&bcfserial->tx_producer_lock); + + spin_lock(&bcfserial->tx_consumer_lock); + bcfserial_serdev_write_locked(bcfserial); + spin_unlock(&bcfserial->tx_consumer_lock); +} + +static int bcfserial_hdlc_receive(struct bcfserial *bcfserial, u8 cmd, void *buffer, size_t count) +{ + int retries = 5; + bcfserial->response_size = count; + bcfserial->response_buffer = (u8*)buffer; + bcfserial_hdlc_send_cmd(bcfserial, cmd); + // TODO semaphore? give/take + do { + usleep_range(10000,10001); + } while (bcfserial->response_size && retries--); + bcfserial->response_buffer = NULL; + if (bcfserial->response_size) { + bcfserial->response_size = 0; + return -EAGAIN; + } + return 0; +} + +static int bcfserial_start(struct ieee802154_hw *hw) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "START\n"); + bcfserial_hdlc_send_cmd(bcfserial, START); + return 0; +} + +static void bcfserial_stop(struct ieee802154_hw *hw) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "STOP\n"); + bcfserial_hdlc_send_cmd(bcfserial, STOP); +} + +static int bcfserial_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct bcfserial *bcfserial = hw->priv; + + if (bcfserial->tx_skb) + { + dev_err(&bcfserial->serdev->dev, "SKB not freed! %d\n", bcfserial->tx_ack_seq); + } + + bcfserial->tx_skb = skb; + bcfserial->tx_ack_seq++; + if (!bcfserial->tx_ack_seq) { + bcfserial->tx_ack_seq++; + } + + dev_dbg(&bcfserial->serdev->dev, "XMIT %02x %d\n", bcfserial->tx_ack_seq, skb->len); + + bcfserial_hdlc_send(bcfserial, TX, 0, bcfserial->tx_ack_seq, skb->len, skb->data); + + return 0; +} + +static int bcfserial_ed(struct ieee802154_hw *hw, u8 *level) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "ED\n"); + WARN_ON(!level); + *level = 0xbe; + return 0; +} + +static int bcfserial_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct bcfserial *bcfserial = hw->priv; + u8 buffer[2] = {page, channel}; + dev_dbg(&bcfserial->serdev->dev, "SET CHANNEL %u %u\n", page, channel); + bcfserial_hdlc_send(bcfserial, SET_CHANNEL, 0, 0, 2, &buffer); + return 0; +} + +static int bcfserial_set_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct bcfserial *bcfserial = hw->priv; + + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + u16 addr = le16_to_cpu(filt->short_addr); + dev_dbg(&bcfserial->serdev->dev, "Short Address changed %x\n", addr); + bcfserial_hdlc_send(bcfserial, SET_SHORT_ADDR, 0, 0, sizeof(addr), &addr); + } + + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + u16 pan = le16_to_cpu(filt->pan_id); + dev_dbg(&bcfserial->serdev->dev, "PAN ID changed %x\n", pan); + bcfserial_hdlc_send(bcfserial, SET_PAN_ID, 0, 0, sizeof(pan), &pan); + } + + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + u64 ieee_addr = le64_to_cpu(filt->ieee_addr); + dev_dbg(&bcfserial->serdev->dev, "IEEE Addr changed %llx\n", ieee_addr); + bcfserial_hdlc_send(bcfserial, SET_IEEE_ADDR, 0, 0, sizeof(ieee_addr), &ieee_addr); + } + return 0; +} + +static int bcfserial_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET TXPOWER\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_lbt(struct ieee802154_hw *hw, bool on) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET LBT\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET CCA MODE\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET CCA ED LEVEL\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, + u8 retries) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET CSMA PARAMS\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET FRAME RETRIES\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET PROMISCUOUS\n"); + return -ENOTSUPP; +} + +static const struct ieee802154_ops bcfserial_ops = { + .owner = THIS_MODULE, + .start = bcfserial_start, + .stop = bcfserial_stop, + .xmit_async = bcfserial_xmit, + .ed = bcfserial_ed, + .set_channel = bcfserial_set_channel, + .set_hw_addr_filt = bcfserial_set_hw_addr_filt, + .set_txpower = bcfserial_set_txpower, + .set_lbt = bcfserial_set_lbt, + .set_cca_mode = bcfserial_set_cca_mode, + .set_cca_ed_level = bcfserial_set_cca_ed_level, + .set_csma_params = bcfserial_set_csma_params, + .set_frame_retries = bcfserial_set_frame_retries, + .set_promiscuous_mode = bcfserial_set_promiscuous_mode, +}; + +static void bcfserial_wpan_rx(struct bcfserial *bcfserial, const u8 *buffer, size_t count) +{ + struct sk_buff *skb; + u8 len, lqi; + + if (count == 1) { + // TX ACK + dev_dbg(&bcfserial->serdev->dev, "TX ACK: 0x%02x:0x%02x\n", buffer[0], bcfserial->tx_ack_seq); + + if (buffer[0] == bcfserial->tx_ack_seq && bcfserial->tx_skb) { + skb = bcfserial->tx_skb; + bcfserial->tx_skb = NULL; + ieee802154_xmit_complete(bcfserial->hw, skb, false); + } else { + dev_err(&bcfserial->serdev->dev, "unknown ack %u\n", bcfserial->tx_ack_seq); + } + } else if (bcfserial->response_size == count && bcfserial->response_buffer) { + //TODO replace with semaphore + dev_dbg(&bcfserial->serdev->dev, "Response size %u found\n", count); + memcpy(bcfserial->response_buffer, buffer, count); + bcfserial->response_size = 0; + } else { + // RX Packet + dev_dbg(&bcfserial->serdev->dev, "RX Packet Len:%u LQI:%u\n", buffer[0], buffer[count-1]); + len = buffer[0]; + lqi = buffer[count-1]; + + if (len+2 != count) { + dev_err(&bcfserial->serdev->dev, "RX Packet invalid length\n"); + return; + } + + if (!ieee802154_is_valid_psdu_len(len)) { + dev_err(&bcfserial->serdev->dev, "frame corrupted\n"); + return; + } + + skb = dev_alloc_skb(IEEE802154_MTU); + if (!skb) { + dev_err(&bcfserial->serdev->dev, "failed to allocate sk_buff\n"); + return; + } + + skb_put_data(skb, buffer+1, len); + ieee802154_rx_irqsafe(bcfserial->hw, skb, lqi); + } +} + +static int bcfserial_tty_receive(struct serdev_device *serdev, + const unsigned char *data, size_t count) +{ + struct bcfserial *bcfserial = serdev_device_get_drvdata(serdev); + u16 crc_check = 0; + size_t i; + u8 c; + + + for (i = 0; i < count; i++) { + c = data[i]; + + if (c == HDLC_FRAME) { + if (bcfserial->rx_address != 0xFF) { + crc_check = crc_ccitt(0xffff, &bcfserial->rx_address, 1); + crc_check = crc_ccitt(crc_check, bcfserial->rx_buffer, bcfserial->rx_offset); + + if (crc_check == 0xf0b8) { + if ((bcfserial->rx_buffer[0] & 1) == 0) { + //I-Frame, send S-Frame ACK + bcfserial_hdlc_send_ack(bcfserial, bcfserial->rx_address, (bcfserial->rx_buffer[0] >> 1) & 0x7); + } + + if (bcfserial->rx_address == ADDRESS_WPAN) { + bcfserial_wpan_rx(bcfserial, bcfserial->rx_buffer + 1, bcfserial->rx_offset - 3); + } + else if (bcfserial->rx_address == ADDRESS_CDC) { + bcfserial->rx_buffer[bcfserial->rx_offset-2] = 0; + printk("> %s", bcfserial->rx_buffer+1); + } + } + else { + dev_err(&bcfserial->serdev->dev, "CRC Failed from %02x: 0x%04x\n", bcfserial->rx_address, crc_check); + } + } + bcfserial->rx_offset = 0; + bcfserial->rx_address = 0xFF; + } else if (c == HDLC_ESC) { + bcfserial->rx_in_esc = 1; + } else { + if (bcfserial->rx_in_esc) { + c ^= 0x20; + bcfserial->rx_in_esc = 0; + } + + if (bcfserial->rx_address == 0xFF) { + bcfserial->rx_address = c; + if (bcfserial->rx_address == ADDRESS_WPAN || + bcfserial->rx_address == ADDRESS_CDC || + bcfserial->rx_address == ADDRESS_HW) { + } else { + bcfserial->rx_address = 0xFF; + } + bcfserial->rx_offset = 0; + } else { + if (bcfserial->rx_offset < MAX_RX_HDLC) { + bcfserial->rx_buffer[bcfserial->rx_offset] = c; + bcfserial->rx_offset++; + } else { + //buffer overflow + dev_err(&bcfserial->serdev->dev, "RX Buffer Overflow\n"); + bcfserial->rx_address = 0xFF; + bcfserial->rx_offset = 0; + } + } + } + } + + return count; +} + +static void bcfserial_uart_transmit(struct work_struct *work) +{ + struct bcfserial *bcfserial = container_of(work, struct bcfserial, tx_work); + + spin_lock_bh(&bcfserial->tx_consumer_lock); + bcfserial_serdev_write_locked(bcfserial); + spin_unlock_bh(&bcfserial->tx_consumer_lock); +} + +static void bcfserial_tty_wakeup(struct serdev_device *serdev) +{ + struct bcfserial *bcfserial = serdev_device_get_drvdata(serdev); + + schedule_work(&bcfserial->tx_work); +} + +static struct serdev_device_ops bcfserial_serdev_ops = { + .receive_buf = bcfserial_tty_receive, + .write_wakeup = bcfserial_tty_wakeup, +}; + +static const struct of_device_id bcfserial_of_match[] = { + { + .compatible = "beagle,bcfserial", + }, + {} +}; +MODULE_DEVICE_TABLE(of, bcfserial_of_match); + +static const s32 channel_powers[] = { + 300, 280, 230, 180, 130, 70, 0, -100, -200, -300, -400, -500, -700, + -900, -1200, -1700, +}; + +static int bcfserial_get_device_capabilities(struct bcfserial *bcfserial) +{ + u32 valid_channels = 0; + int ret = 0; + struct ieee802154_hw *hw = bcfserial->hw; + + bcfserial_hdlc_send_cmd(bcfserial, RESET); + + ret = bcfserial_hdlc_receive(bcfserial, GET_SUPPORTED_CHANNELS, &valid_channels, sizeof(valid_channels)); + if (ret < 0) { + return ret; + } + dev_dbg(&bcfserial->serdev->dev, "Supported Channels %x\n", valid_channels); + + /* FIXME: these need to come from device capabilities */ + hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; + + /* FIXME: these need to come from device capabilities */ + hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; + + /* Set default and supported channels */ + hw->phy->current_page = 0; + hw->phy->current_channel = ffs(valid_channels) - 1; //set to lowest valid channel + hw->phy->supported.channels[0] = valid_channels; + + /* FIXME: these need to come from device capabilities */ + hw->phy->supported.tx_powers = channel_powers; + hw->phy->supported.tx_powers_size = ARRAY_SIZE(channel_powers); + hw->phy->transmit_power = hw->phy->supported.tx_powers[0]; + + return ret; +} + +static int bcfserial_probe(struct serdev_device *serdev) +{ + struct ieee802154_hw *hw; + struct bcfserial *bcfserial = NULL; + u32 speed = 115200; + int ret; + + hw = ieee802154_alloc_hw(sizeof(struct bcfserial), &bcfserial_ops); + if (!hw) + return -ENOMEM; + + bcfserial = hw->priv; + bcfserial->hw = hw; + hw->parent = &serdev->dev; + bcfserial->serdev = serdev; + + INIT_WORK(&bcfserial->tx_work, bcfserial_uart_transmit); + + spin_lock_init(&bcfserial->tx_producer_lock); + spin_lock_init(&bcfserial->tx_consumer_lock); + bcfserial->tx_circ_buf.head = 0; + bcfserial->tx_circ_buf.tail = 0; + bcfserial->tx_circ_buf.buf = devm_kmalloc(&serdev->dev, TX_CIRC_BUF_SIZE, GFP_KERNEL); + + bcfserial->rx_buffer = devm_kmalloc(&serdev->dev, MAX_RX_HDLC, GFP_KERNEL); + bcfserial->rx_offset = 0; + bcfserial->rx_address = 0xff; + bcfserial->rx_in_esc = 0; + + serdev_device_set_drvdata(serdev, bcfserial); + serdev_device_set_client_ops(serdev, &bcfserial_serdev_ops); + + ret = serdev_device_open(serdev); + if (ret) { + dev_err(&bcfserial->serdev->dev, "Unable to open device\n"); + goto fail_hw; + } + + speed = serdev_device_set_baudrate(serdev, speed); + dev_dbg(&bcfserial->serdev->dev, "Using baudrate %u\n", speed); + + serdev_device_set_flow_control(serdev, false); + + bcfserial_hdlc_send_ack(bcfserial, 0x41, 0x00); + + ret = bcfserial_get_device_capabilities(bcfserial); + + if (ret < 0) { + // dev_err(&udev->dev, "Failed to get device capabilities"); + dev_err(&bcfserial->serdev->dev, "Failed to get device capabilities\n"); + goto fail; + } + + ret = ieee802154_register_hw(hw); + + dev_info(&bcfserial->serdev->dev, "bcfserial started"); + if (ret) + goto fail; + + return 0; + +fail: + dev_err(&bcfserial->serdev->dev, "Closing serial device on failure\n"); + serdev_device_close(serdev); +fail_hw: + printk(KERN_ERR "Failed to open bcfserial\n"); + ieee802154_free_hw(hw); + return ret; +} + +static void bcfserial_remove(struct serdev_device *serdev) +{ + struct bcfserial *bcfserial = serdev_device_get_drvdata(serdev); + dev_info(&bcfserial->serdev->dev, "Closing serial device\n"); + ieee802154_unregister_hw(bcfserial->hw); + flush_work(&bcfserial->tx_work); + ieee802154_free_hw(bcfserial->hw); + serdev_device_close(serdev); +} + +static struct serdev_device_driver bcfserial_driver = { + .probe = bcfserial_probe, + .remove = bcfserial_remove, + .driver = { + .name = BCFSERIAL_DRV_NAME, + .of_match_table = of_match_ptr(bcfserial_of_match), + }, +}; + +module_serdev_device_driver(bcfserial_driver); + +MODULE_DESCRIPTION("WPAN serial driver for BeagleConnect Freedom"); +MODULE_AUTHOR("Erik Larson "); +MODULE_VERSION("0.1.0"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig --- a/drivers/net/ieee802154/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ieee802154/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -73,6 +73,15 @@ This driver can also be built as a module. To do so say M here. The module will be called 'atusb'. +config IEEE802154_WPANUSB + tristate "WPANUSB driver" + depends on IEEE802154_DRIVERS && MAC802154 && USB + help + Adds support for WPANUSB 802.15.4 adapters. + + This driver should work with at least the following devices: + * BeagleBoard.org BeagleConnect Freedom + config IEEE802154_ADF7242 tristate "ADF7242 transceiver driver" depends on IEEE802154_DRIVERS && MAC802154 @@ -127,3 +136,12 @@ This driver can also be built as a module. To do so say M here. The module will be called 'mac802154_hwsim'. + +config IEEE802154_BCFSERIAL + tristate "BCFSERIAL driver" + depends on IEEE802154_DRIVERS && MAC802154 + help + Adds support for BCFSERIAL 802.15.4 adapters. + + This driver should work with at least the following devices: + * BeagleBoard.org BeagleConnect Freedom diff -Naur --no-dereference a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile --- a/drivers/net/ieee802154/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/ieee802154/Makefile 2022-01-06 12:45:53.822318139 -0500 @@ -4,7 +4,9 @@ obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o +obj-$(CONFIG_IEEE802154_WPANUSB) += wpanusb.o obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o obj-$(CONFIG_IEEE802154_MCR20A) += mcr20a.o obj-$(CONFIG_IEEE802154_HWSIM) += mac802154_hwsim.o +obj-$(CONFIG_IEEE802154_BCFSERIAL) += bcfserial.o diff -Naur --no-dereference a/drivers/net/ieee802154/wpanusb.c b/drivers/net/ieee802154/wpanusb.c --- a/drivers/net/ieee802154/wpanusb.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ieee802154/wpanusb.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the WPANUSB IEEE 802.15.4 dongle + * + * Copyright (C) 2018 Intel Corp. + * + * The driver implements SoftMAC 802.15.4 protocol based on atusb + * driver for ATUSB IEEE 802.15.4 dongle. + * + * Written by Andrei Emeltchenko + */ + +#include +#include +#include +#include + +#include +#include + +#define DEBUG +#include "wpanusb.h" + +#define WPANUSB_NUM_RX_URBS 4 /* allow for a bit of local latency */ +#define WPANUSB_ALLOC_DELAY_MS 100 /* delay after failed allocation */ + +#define VENDOR_OUT (USB_TYPE_VENDOR | USB_DIR_OUT) +#define VENDOR_IN (USB_TYPE_VENDOR | USB_DIR_IN) + +#define WPANUSB_VALID_CHANNELS (0x07FFFFFF) + +struct wpanusb { + struct ieee802154_hw *hw; + struct usb_device *udev; + int shutdown; /* non-zero if shutting down */ + + /* RX variables */ + struct delayed_work work; /* memory allocations */ + struct usb_anchor idle_urbs; /* URBs waiting to be submitted */ + struct usb_anchor rx_urbs; /* URBs waiting for reception */ + + /* TX variables */ + struct usb_ctrlrequest tx_dr; + struct urb *tx_urb; + struct sk_buff *tx_skb; + u8 tx_ack_seq; /* current TX ACK sequence number */ +}; + +/* ----- USB commands without data ----------------------------------------- */ + +static int wpanusb_control_send(struct wpanusb *wpanusb, unsigned int pipe, + u8 request, void *data, u16 size) +{ + struct usb_device *udev = wpanusb->udev; + + return usb_control_msg(udev, pipe, request, VENDOR_OUT, + 0, 0, data, size, 1000); +} + +static int wpanusb_control_recv(struct wpanusb *wpanusb, u8 request, void *data, u16 size) +{ + struct usb_device *udev = wpanusb->udev; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, VENDOR_OUT, + 0, 0, NULL, 0, 1000); + + return usb_control_msg(udev, usb_rcvbulkpipe(udev, 1), request, VENDOR_IN, + 0, 0, data, size, 1000); +} + +/* ----- skb allocation ---------------------------------------------------- */ + +#define MAX_PSDU 127 +#define MAX_RX_XFER (1 + MAX_PSDU + 2 + 1) /* PHR+PSDU+CRC+LQI */ + +#define SKB_WPANUSB(skb) (*(struct wpanusb **)(skb)->cb) + +static void wpanusb_bulk_complete(struct urb *urb); + +static int wpanusb_submit_rx_urb(struct wpanusb *wpanusb, struct urb *urb) +{ + struct usb_device *udev = wpanusb->udev; + struct sk_buff *skb = urb->context; + int ret; + + if (!skb) { + skb = alloc_skb(MAX_RX_XFER, GFP_KERNEL); + if (!skb) { + dev_warn_ratelimited(&udev->dev, + "can't allocate skb\n"); + return -ENOMEM; + } + skb_put(skb, MAX_RX_XFER); + SKB_WPANUSB(skb) = wpanusb; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, 1), + skb->data, MAX_RX_XFER, wpanusb_bulk_complete, skb); + usb_anchor_urb(urb, &wpanusb->rx_urbs); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + usb_unanchor_urb(urb); + kfree_skb(skb); + urb->context = NULL; + } + + return ret; +} + +static void wpanusb_work_urbs(struct work_struct *work) +{ + struct wpanusb *wpanusb = + container_of(to_delayed_work(work), struct wpanusb, work); + struct usb_device *udev = wpanusb->udev; + struct urb *urb; + int ret; + + if (wpanusb->shutdown) + return; + + do { + urb = usb_get_from_anchor(&wpanusb->idle_urbs); + if (!urb) + return; + + ret = wpanusb_submit_rx_urb(wpanusb, urb); + } while (!ret); + + usb_anchor_urb(urb, &wpanusb->idle_urbs); + dev_warn_ratelimited(&udev->dev, "can't allocate/submit URB (%d)\n", + ret); + schedule_delayed_work(&wpanusb->work, + msecs_to_jiffies(WPANUSB_ALLOC_DELAY_MS) + 1); +} + +/* ----- Asynchronous USB -------------------------------------------------- */ + +static void wpanusb_tx_done(struct wpanusb *wpanusb, uint8_t seq) +{ + struct usb_device *udev = wpanusb->udev; + u8 expect = wpanusb->tx_ack_seq; + + dev_dbg(&udev->dev, "seq 0x%02x expect 0x%02x\n", seq, expect); + + if (seq == expect) { + ieee802154_xmit_complete(wpanusb->hw, wpanusb->tx_skb, false); + } else { + dev_dbg(&udev->dev, "unknown ack %u\n", seq); + + ieee802154_wake_queue(wpanusb->hw); + if (wpanusb->tx_skb) + dev_kfree_skb_irq(wpanusb->tx_skb); + } +} + +static void wpanusb_process_urb(struct urb *urb) +{ + struct usb_device *udev = urb->dev; + struct sk_buff *skb = urb->context; + struct wpanusb *wpanusb = SKB_WPANUSB(skb); + u8 len, lqi; + + if (!urb->actual_length) { + dev_dbg(&udev->dev, "zero-sized URB ?\n"); + return; + } + + len = *skb->data; + + dev_dbg(&udev->dev, "urb %p urb len %u pkt len %u", urb, + urb->actual_length, len); + + /* Handle ACK */ + if (urb->actual_length == 1) { + wpanusb_tx_done(wpanusb, len); + return; + } + + if (len + 1 > urb->actual_length - 1) { + dev_dbg(&udev->dev, "frame len %d+1 > URB %u-1\n", + len, urb->actual_length); + return; + } + + if (!ieee802154_is_valid_psdu_len(len)) { + dev_dbg(&udev->dev, "frame corrupted\n"); + return; + } + + print_hex_dump_bytes("> ", DUMP_PREFIX_OFFSET, skb->data, + urb->actual_length); + + /* Get LQI at the end of the packet */ + lqi = skb->data[len + 1]; + dev_dbg(&udev->dev, "rx len %d lqi 0x%02x\n", len, lqi); + skb_pull(skb, 1); /* remove length */ + skb_trim(skb, len); /* remove LQI */ + ieee802154_rx_irqsafe(wpanusb->hw, skb, lqi); + urb->context = NULL; /* skb is gone */ +} + +static void wpanusb_bulk_complete(struct urb *urb) +{ + struct usb_device *udev = urb->dev; + struct sk_buff *skb = urb->context; + struct wpanusb *wpanusb = SKB_WPANUSB(skb); + + dev_dbg(&udev->dev, "status %d len %d\n", + urb->status, urb->actual_length); + + if (urb->status) { + if (urb->status == -ENOENT) { /* being killed */ + kfree_skb(skb); + urb->context = NULL; + return; + } + + dev_dbg(&udev->dev, "URB error %d\n", urb->status); + } else { + wpanusb_process_urb(urb); + } + + usb_anchor_urb(urb, &wpanusb->idle_urbs); + if (!wpanusb->shutdown) + schedule_delayed_work(&wpanusb->work, 0); +} + +/* ----- URB allocation/deallocation --------------------------------------- */ + +static void wpanusb_free_urbs(struct wpanusb *wpanusb) +{ + struct urb *urb; + + do { + urb = usb_get_from_anchor(&wpanusb->idle_urbs); + if (!urb) + break; + kfree_skb(urb->context); + usb_free_urb(urb); + } while (true); +} + +static int wpanusb_alloc_urbs(struct wpanusb *wpanusb, unsigned int n) +{ + struct urb *urb; + + while (n--) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + wpanusb_free_urbs(wpanusb); + return -ENOMEM; + } + usb_anchor_urb(urb, &wpanusb->idle_urbs); + } + + return 0; +} + +/* ----- IEEE 802.15.4 interface operations -------------------------------- */ + +static void wpanusb_xmit_complete(struct urb *urb) +{ + dev_dbg(&urb->dev->dev, "urb transmit completed"); +} + +static int wpanusb_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret = 0; + + dev_dbg(&udev->dev, "len %u", skb->len); + + /* ack_seq range is 0x01 - 0xff */ + wpanusb->tx_ack_seq++; + if (!wpanusb->tx_ack_seq) + wpanusb->tx_ack_seq++; + + wpanusb->tx_skb = skb; + wpanusb->tx_dr.wIndex = cpu_to_le16(wpanusb->tx_ack_seq); + wpanusb->tx_dr.wLength = cpu_to_le16(skb->len); + + usb_fill_control_urb(wpanusb->tx_urb, udev, + usb_sndctrlpipe(udev, 0), + (unsigned char *)&wpanusb->tx_dr, skb->data, + skb->len, wpanusb_xmit_complete, NULL); + ret = usb_submit_urb(wpanusb->tx_urb, GFP_ATOMIC); + + dev_dbg(&udev->dev, "%s: ret %d len %u seq %u\n", __func__, ret, + skb->len, wpanusb->tx_ack_seq); + + return ret; +} + +static int wpanusb_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + struct set_channel *req; + int ret; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->page = page; + req->channel = channel; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_CHANNEL, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed set channel, ret %d", ret); + return ret; + } + + dev_dbg(&udev->dev, "set page %u channel %u", page, channel); + + return 0; +} + +static int wpanusb_ed(struct ieee802154_hw *hw, u8 *level) +{ + WARN_ON(!level); + + *level = 0xbe; + + return 0; +} + +static int wpanusb_set_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret = 0; + + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + struct set_short_addr *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->short_addr = filt->short_addr; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_SHORT_ADDR, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed to set short_addr, ret %d", + ret); + return ret; + } + + dev_dbg(&udev->dev, "short addr changed to 0x%04x", + le16_to_cpu(filt->short_addr)); + } + + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + struct set_pan_id *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->pan_id = filt->pan_id; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_PAN_ID, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed to set pan_id, ret %d", + ret); + return ret; + } + + dev_dbg(&udev->dev, "pan id changed to 0x%04x", + le16_to_cpu(filt->pan_id)); + } + + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + struct set_ieee_addr *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memcpy(&req->ieee_addr, &filt->ieee_addr, + sizeof(req->ieee_addr)); + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_IEEE_ADDR, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed to set ieee_addr, ret %d", + ret); + return ret; + } + + dev_dbg(&udev->dev, "IEEE addr changed"); + } + + if (changed & IEEE802154_AFILT_PANC_CHANGED) { + dev_dbg(&udev->dev, "panc changed"); + + dev_err(&udev->dev, "Not handled AFILT_PANC_CHANGED"); + } + + return ret; +} + +static int wpanusb_set_extended_addr(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + unsigned char *buffer; + __le64 extended_addr; + int ret = 0; + u64 addr; + + buffer = kmalloc(IEEE802154_EXTENDED_ADDR_LEN, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), GET_EXTENDED_ADDR, buffer, + IEEE802154_EXTENDED_ADDR_LEN); + if (ret < 0) { + dev_err(&udev->dev, "failed to fetch extended address, random address set\n"); + ieee802154_random_extended_addr(&wpanusb->hw->phy->perm_extended_addr); + kfree(buffer); + return ret; + } + + memcpy(&extended_addr, buffer, IEEE802154_EXTENDED_ADDR_LEN); + /* Check if read address is not empty and the unicast bit is set correctly */ + if (!ieee802154_is_valid_extended_unicast_addr(extended_addr)) { + dev_info(&udev->dev, "no permanent extended address found, random address set\n"); + ieee802154_random_extended_addr(&wpanusb->hw->phy->perm_extended_addr); + } else { + wpanusb->hw->phy->perm_extended_addr = extended_addr; + addr = swab64((__force u64)wpanusb->hw->phy->perm_extended_addr); + dev_info(&udev->dev, "Read permanent extended address %8phC from device\n", &addr); + } + + kfree(buffer); + return ret; +} + +/* FIXME: these need to come as capabilities from the device */ +static const s32 wpanusb_powers[] = { + 300, 280, 230, 180, 130, 70, 0, -100, -200, -300, -400, -500, -700, + -900, -1200, -1700, +}; + +static int wpanusb_get_device_capabilities(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + unsigned char *buffer; + uint32_t valid_channels; + int ret = 0; + + buffer = kmalloc(IEEE802154_EXTENDED_ADDR_LEN, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), GET_EXTENDED_ADDR, buffer, + IEEE802154_EXTENDED_ADDR_LEN); + if (ret < 0) { + dev_err(&udev->dev, "failed to fetch extended address, random address set\n"); + ieee802154_random_extended_addr(&wpanusb->hw->phy->perm_extended_addr); + kfree(buffer); + return ret; + } + + buffer = kmalloc(sizeof(valid_channels), GFP_NOIO); + if (!buffer) + return -ENOMEM; + ret = wpanusb_control_recv(wpanusb, GET_SUPPORTED_CHANNELS, buffer, sizeof(valid_channels)); + if (ret != sizeof(uint32_t)) { + dev_err(&udev->dev, "failed to fetch supported channels\n"); + kfree(buffer); + return ret; + } + valid_channels = *(uint32_t *)buffer; + if (!valid_channels) { + dev_err(&udev->dev, "failed to fetch valid channels, setting default valid channels\n"); + valid_channels = WPANUSB_VALID_CHANNELS; + } + + /* FIXME: these need to come from device capabilities */ + hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; + + /* FIXME: these need to come from device capabilities */ + hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; + + /* Set default and supported channels */ + hw->phy->current_page = 0; + hw->phy->current_channel = ffs(valid_channels) - 1; //set to lowest valid channel + hw->phy->supported.channels[0] = valid_channels; + + /* FIXME: these need to come from device capabilities */ + hw->phy->supported.tx_powers = wpanusb_powers; + hw->phy->supported.tx_powers_size = ARRAY_SIZE(wpanusb_powers); + hw->phy->transmit_power = hw->phy->supported.tx_powers[0]; + + kfree(buffer); + return ret; +} + +static int wpanusb_start(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret; + + schedule_delayed_work(&wpanusb->work, 0); + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + START, NULL, 0); + if (ret < 0) { + dev_err(&udev->dev, "Failed to start ieee802154"); + usb_kill_anchored_urbs(&wpanusb->idle_urbs); + } + + return ret; +} + +static void wpanusb_stop(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret; + + dev_dbg(&udev->dev, "stop"); + + usb_kill_anchored_urbs(&wpanusb->idle_urbs); + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + STOP, NULL, 0); + if (ret < 0) + dev_err(&udev->dev, "Failed to stop ieee802154"); +} + +static int wpanusb_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, mbm %d", __func__, mbm); + + return -ENOTSUPP; +} + +static int wpanusb_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, mode %u opt %u", + __func__, cca->mode, cca->opt); + + switch (cca->mode) { + case NL802154_CCA_ENERGY: + break; + case NL802154_CCA_CARRIER: + break; + case NL802154_CCA_ENERGY_CARRIER: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wpanusb_set_lbt(struct ieee802154_hw *hw, bool on) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret = 0; + + if (on) + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_LBT, NULL, 0); + + return ret; +} + +static int wpanusb_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret; + + /* FIXME pass retries onwards to device */ + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_FRAME_RETRIES, NULL, 0); + + return ret; +} + +static int wpanusb_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, mbm %d", __func__, mbm); + + return 0; +} + +static int wpanusb_set_csma_params(struct ieee802154_hw *hw, u8 min_be, + u8 max_be, u8 retries) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, min_be %u max_be %u retr %u", + __func__, min_be, max_be, retries); + + return 0; +} + +static int wpanusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, on %d", __func__, on); + + return 0; +} + +static const struct ieee802154_ops wpanusb_ops = { + .owner = THIS_MODULE, + .xmit_async = wpanusb_xmit, + .ed = wpanusb_ed, + .set_channel = wpanusb_channel, + .start = wpanusb_start, + .stop = wpanusb_stop, + .set_hw_addr_filt = wpanusb_set_hw_addr_filt, + .set_txpower = wpanusb_set_txpower, + .set_lbt = wpanusb_set_lbt, + .set_cca_mode = wpanusb_set_cca_mode, + .set_cca_ed_level = wpanusb_set_cca_ed_level, + .set_csma_params = wpanusb_set_csma_params, + .set_frame_retries = wpanusb_set_frame_retries, + .set_promiscuous_mode = wpanusb_set_promiscuous_mode, +}; + +/* ----- Setup ------------------------------------------------------------- */ + +static int wpanusb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct ieee802154_hw *hw; + struct wpanusb *wpanusb; + int ret; + + hw = ieee802154_alloc_hw(sizeof(struct wpanusb), &wpanusb_ops); + if (!hw) + return -ENOMEM; + + wpanusb = hw->priv; + wpanusb->hw = hw; + wpanusb->udev = usb_get_dev(udev); + usb_set_intfdata(interface, wpanusb); + + wpanusb->shutdown = 0; + INIT_DELAYED_WORK(&wpanusb->work, wpanusb_work_urbs); + init_usb_anchor(&wpanusb->idle_urbs); + init_usb_anchor(&wpanusb->rx_urbs); + + ret = wpanusb_alloc_urbs(wpanusb, WPANUSB_NUM_RX_URBS); + if (ret) + goto fail; + + wpanusb->tx_dr.bRequestType = VENDOR_OUT; + wpanusb->tx_dr.bRequest = TX; + wpanusb->tx_dr.wValue = cpu_to_le16(0); + + wpanusb->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!wpanusb->tx_urb) + goto fail; + + hw->parent = &udev->dev; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), RESET, + NULL, 0); + if (ret < 0) { + dev_err(&udev->dev, "Failed to RESET ieee802154"); + goto fail; + } + + ret = wpanusb_get_device_capabilities(hw); + + if (ret < 0) { + dev_err(&udev->dev, "Failed to get device capabilities"); + goto fail; + } + + ret = wpanusb_set_extended_addr(hw); + + if (ret < 0) { + dev_err(&udev->dev, "Failed to set permanent address"); + goto fail; + } + + ret = ieee802154_register_hw(hw); + if (ret) { + dev_err(&udev->dev, "Failed to register ieee802154"); + goto fail; + } + + dev_dbg(&udev->dev, "ieee802154 ready to go"); + + return 0; + +fail: + dev_err(&udev->dev, "Failed ieee802154 probe"); + wpanusb_free_urbs(wpanusb); + usb_kill_urb(wpanusb->tx_urb); + usb_free_urb(wpanusb->tx_urb); + usb_put_dev(udev); + ieee802154_free_hw(hw); + + return ret; +} + +static void wpanusb_disconnect(struct usb_interface *interface) +{ + struct wpanusb *wpanusb = usb_get_intfdata(interface); + + wpanusb->shutdown = 1; + cancel_delayed_work_sync(&wpanusb->work); + + usb_kill_anchored_urbs(&wpanusb->rx_urbs); + wpanusb_free_urbs(wpanusb); + usb_kill_urb(wpanusb->tx_urb); + usb_free_urb(wpanusb->tx_urb); + + ieee802154_unregister_hw(wpanusb->hw); + + ieee802154_free_hw(wpanusb->hw); + + usb_set_intfdata(interface, NULL); + usb_put_dev(wpanusb->udev); +} + +/* The devices we work with */ +static const struct usb_device_id wpanusb_device_table[] = { + { + USB_DEVICE_AND_INTERFACE_INFO(WPANUSB_VENDOR_ID, + WPANUSB_PRODUCT_ID, + USB_CLASS_VENDOR_SPEC, + 0, 0), + USB_DEVICE_AND_INTERFACE_INFO(BEAGLECONNECT_VENDOR_ID, + BEAGLECONNECT_PRODUCT_ID, + USB_CLASS_VENDOR_SPEC, + 0, 0) + }, + /* end with null element */ + {} +}; +MODULE_DEVICE_TABLE(usb, wpanusb_device_table); + +static struct usb_driver wpanusb_driver = { + .name = "wpanusb", + .probe = wpanusb_probe, + .disconnect = wpanusb_disconnect, + .id_table = wpanusb_device_table, +}; +module_usb_driver(wpanusb_driver); + +MODULE_AUTHOR("Andrei Emeltchenko "); +MODULE_DESCRIPTION("WPANUSB IEEE 802.15.4 over USB Driver"); +MODULE_LICENSE("GPL"); diff -Naur --no-dereference a/drivers/net/ieee802154/wpanusb.h b/drivers/net/ieee802154/wpanusb.h --- a/drivers/net/ieee802154/wpanusb.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/ieee802154/wpanusb.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Definitions shared between kernel and WPANUSB firmware + * + * Copyright (C) 2018 Intel Corp. + * + * Written by Andrei Emeltchenko + */ + +#define WPANUSB_VENDOR_ID 0x2fe3 +#define WPANUSB_PRODUCT_ID 0x0101 + +#define BEAGLECONNECT_VENDOR_ID 0x2047 +#define BEAGLECONNECT_PRODUCT_ID 0x0aa5 + +enum wpanusb_requests { + RESET, + TX, + XMIT_ASYNC, + ED, + SET_CHANNEL, + START, + STOP, + SET_SHORT_ADDR, + SET_PAN_ID, + SET_IEEE_ADDR, + SET_TXPOWER, + SET_CCA_MODE, + SET_CCA_ED_LEVEL, + SET_CSMA_PARAMS, + SET_LBT, + SET_FRAME_RETRIES, + SET_PROMISCUOUS_MODE, + GET_EXTENDED_ADDR, + GET_SUPPORTED_CHANNELS, +}; + +struct set_channel { + __u8 page; + __u8 channel; +} __packed; + +struct set_short_addr { + __le16 short_addr; +} __packed; + +struct set_pan_id { + __le16 pan_id; +} __packed; + +struct set_ieee_addr { + __le64 ieee_addr; +} __packed; diff -Naur --no-dereference a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c --- a/drivers/net/phy/dp83867.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/phy/dp83867.c 2022-01-06 12:45:53.822318139 -0500 @@ -41,6 +41,7 @@ #define DP83867_STRAP_STS1 0x006E #define DP83867_STRAP_STS2 0x006f #define DP83867_RGMIIDCTL 0x0086 +#define DP83867_DSP_FFE_CFG 0x012C #define DP83867_RXFCFG 0x0134 #define DP83867_RXFPMD1 0x0136 #define DP83867_RXFPMD2 0x0137 @@ -798,8 +799,20 @@ usleep_range(10, 20); - return phy_modify(phydev, MII_DP83867_PHYCTRL, + err = phy_modify(phydev, MII_DP83867_PHYCTRL, DP83867_PHYCR_FORCE_LINK_GOOD, 0); + if (err < 0) + return err; + + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_DSP_FFE_CFG, 0x0E81); + + err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESTART); + if (err < 0) + return err; + + usleep_range(10, 20); + + return 0; } static struct phy_driver dp83867_driver[] = { diff -Naur --no-dereference a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c --- a/drivers/net/phy/dp83869.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/phy/dp83869.c 2022-01-06 12:45:53.822318139 -0500 @@ -659,8 +659,11 @@ /* Below init sequence for each operational mode is defined in * section 9.4.8 of the datasheet. */ + phy_ctrl_val = dp83869->mode; + if (phydev->interface == PHY_INTERFACE_MODE_MII) + phy_ctrl_val |= DP83869_OP_MODE_MII; ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, - dp83869->mode); + phy_ctrl_val); if (ret) return ret; diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c 2022-01-06 12:45:53.822318139 -0500 @@ -87,6 +87,8 @@ * plus any space that might be needed * for bus alignment padding. */ +#define ROUND_UP_MARGIN 2048 + struct brcmf_bcdc { u16 reqid; u8 bus_header[BUS_HEADER_LEN]; @@ -368,8 +370,7 @@ /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(bcdc->fws)) { - if (!success) - brcmf_fws_bustxfail(bcdc->fws, txp); + brcmf_fws_bustxcomplete(bcdc->fws, txp, success); } else { if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); @@ -471,7 +472,7 @@ drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + - sizeof(struct brcmf_proto_bcdc_dcmd); + sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN; return 0; fail: diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c 2022-01-06 12:45:53.822318139 -0500 @@ -36,6 +36,7 @@ #include "sdio.h" #include "core.h" #include "common.h" +#include "cfg80211.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -43,9 +44,10 @@ #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 -#define SDIO_4373_FUNC2_BLOCKSIZE 256 +#define SDIO_4373_FUNC2_BLOCKSIZE 128 #define SDIO_435X_FUNC2_BLOCKSIZE 256 #define SDIO_4329_FUNC2_BLOCKSIZE 128 +#define SDIO_89459_FUNC2_BLOCKSIZE 256 /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 @@ -669,7 +671,7 @@ uint dsize; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); - pkt = dev_alloc_skb(dsize); + pkt = __dev_alloc_skb(dsize, GFP_KERNEL); if (!pkt) { brcmf_err("dev_alloc_skb failed: len %d\n", dsize); return -EIO; @@ -924,6 +926,12 @@ case SDIO_DEVICE_ID_BROADCOM_4329: f2_blksz = SDIO_4329_FUNC2_BLOCKSIZE; break; + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459: + case SDIO_DEVICE_ID_CYPRESS_54590: + case SDIO_DEVICE_ID_CYPRESS_54591: + case SDIO_DEVICE_ID_CYPRESS_54594: + f2_blksz = SDIO_89459_FUNC2_BLOCKSIZE; + break; default: break; } @@ -969,6 +977,9 @@ #define BRCMF_SDIO_DEVICE(dev_id) \ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} +#define CYF_SDIO_DEVICE(dev_id) \ + {SDIO_DEVICE(SDIO_VENDOR_ID_CYPRESS, dev_id)} + /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), @@ -988,9 +999,14 @@ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43439), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54590), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54591), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54594), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -1059,6 +1075,7 @@ dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func1->dev, bus_if); sdiodev->dev = &sdiodev->func1->dev; + dev_set_drvdata(&sdiodev->func2->dev, bus_if); brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); @@ -1075,6 +1092,7 @@ fail: dev_set_drvdata(&func->dev, NULL); dev_set_drvdata(&sdiodev->func1->dev, NULL); + dev_set_drvdata(&sdiodev->func2->dev, NULL); kfree(sdiodev); kfree(bus_if); return err; @@ -1129,15 +1147,27 @@ struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t pm_caps, sdio_flags; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; int ret = 0; func = container_of(dev, struct sdio_func, dev); + bus_if = dev_get_drvdata(dev); + config = bus_if->drvr->config; + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err("timed out wait for cfg80211 suspended\n"); + if (func->num != 1) return 0; - - bus_if = dev_get_drvdata(dev); sdiodev = bus_if->bus_priv.sdio; pm_caps = sdio_get_host_pm_caps(func); @@ -1217,9 +1247,13 @@ }, }; -int brcmf_sdio_register(void) +void brcmf_sdio_register(void) { - return sdio_register_driver(&brcmf_sdmmc_driver); + int ret; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); } void brcmf_sdio_exit(void) diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: ISC + +/* Copyright 2019, Cypress Semiconductor Corporation or a subsidiary of + * Cypress Semiconductor Corporation. All rights reserved. + * This software, including source code, documentation and related + * materials ("Software"), is owned by Cypress Semiconductor + * Corporation or one of its subsidiaries ("Cypress") and is protected by + * and subject to worldwide patent protection (United States and foreign), + * United States copyright laws and international treaty provisions. + * Therefore, you may use this Software only as provided in the license + * agreement accompanying the software package from which you + * obtained this Software ("EULA"). If no EULA applies, Cypress hereby grants + * you a personal, nonexclusive, non-transferable license to copy, modify, + * and compile the Software source code solely for use in connection with + * Cypress's integrated circuit products. Any reproduction, modification, + * translation, compilation, or representation of this Software except as + * specified above is prohibited without the express written permission of + * Cypress. + * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress + * reserves the right to make changes to the Software without notice. Cypress + * does not assume any liability arising out of the application or use of the + * Software or any product or circuit described in the Software. Cypress does + * not authorize its products for use in any products where a malfunction or + * failure of the Cypress product may reasonably be expected to result in + * significant property damage, injury or death ("High Risk Product"). By + * including Cypress's product in a High Risk Product, the manufacturer + * of such system or application assumes all risk of such use and in doing + * so agrees to indemnify Cypress against all liability. + */ + +#include +#include +#include +#include +#include +#include "bus.h" +#include "chipcommon.h" +#include "core.h" +#include "sdio.h" +#include "soc.h" +#include "fwil.h" + +#define SDIOD_ADDR_BOUND 0x1000 +#define SDIOD_ADDR_BOUND_MASK 0xfff + +struct brcmf_bus *g_bus_if; + +enum bus_owner { + WLAN_MODULE = 0, + BT_MODULE +}; + +struct btsdio_info { + u32 bt_buf_reg_addr; + u32 host_ctrl_reg_addr; + u32 bt_ctrl_reg_addr; + u32 bt_buf_addr; + u32 wlan_buf_addr; +}; + +void brcmf_btsdio_int_handler(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (btdev && btdev->bt_sdio_int_cb) + btdev->bt_sdio_int_cb(btdev->bt_data); +} + +int brcmf_btsdio_init(struct brcmf_bus *bus_if) +{ + if (!bus_if) + return -EINVAL; + + g_bus_if = bus_if; + return 0; +} + +int brcmf_btsdio_attach(struct brcmf_bus *bus_if, void *btdata, + void (*bt_int_fun)(void *data)) +{ + struct brcmf_bt_dev *btdev; + + /* Allocate bt dev */ + btdev = kzalloc(sizeof(*btdev), GFP_ATOMIC); + if (!btdev) + return -ENOMEM; + + btdev->bt_data = btdata; + btdev->bt_sdio_int_cb = bt_int_fun; + bus_if->bt_dev = btdev; + + return 0; +} + +void brcmf_btsdio_detach(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (!btdev) + return; + + if (btdev->bt_data) + btdev->bt_data = NULL; + if (btdev->bt_sdio_int_cb) + btdev->bt_sdio_int_cb = NULL; + if (bus_if->bt_dev) { + bus_if->bt_dev = NULL; + kfree(btdev); + } +} + +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (!btdev) + return 0; + + return btdev->use_count; +} + +void *brcmf_bt_sdio_attach(void *btdata, void (*bt_int_fun)(void *data)) +{ + int err; + + if (!g_bus_if) { + brcmf_err("BTSDIO is not initialized\n"); + return NULL; + } + + err = brcmf_btsdio_attach(g_bus_if, btdata, bt_int_fun); + if (err) { + brcmf_err("BTSDIO attach failed, err=%d\n", err); + return NULL; + } + + return (void *)g_bus_if; +} +EXPORT_SYMBOL(brcmf_bt_sdio_attach); + +int brcmf_get_wlan_info(struct brcmf_bus *bus_if, struct btsdio_info *bs_info) +{ + struct brcmf_if *ifp; + + if (!bus_if || !bs_info) + return -EINVAL; + + ifp = bus_if->drvr->iflist[0]; + + bs_info->bt_buf_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_input[6]); + bs_info->host_ctrl_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_output[3]); + bs_info->bt_ctrl_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_input[7]); + brcmf_dbg(INFO, "BT buf reg addr: 0x%x\n", + bs_info->bt_buf_reg_addr); + brcmf_dbg(INFO, "HOST ctrl reg addr: 0x%x\n", + bs_info->host_ctrl_reg_addr); + brcmf_dbg(INFO, "BT ctrl reg addr: 0x%x\n", + bs_info->bt_ctrl_reg_addr); + return 0; +} +EXPORT_SYMBOL(brcmf_get_wlan_info); + +u32 brcmf_bus_reg_read(struct brcmf_bus *bus_if, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + u32 val; + + if (!bus_if) + return -EINVAL; + + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + val = brcmf_sdiod_readl(sdiodev, addr, &err); + if (err) { + brcmf_err("sdio reg read failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + return err; + } + sdio_release_host(sdiodev->func1); + + return val; +} +EXPORT_SYMBOL(brcmf_bus_reg_read); + +void brcmf_bus_reg_write(struct brcmf_bus *bus_if, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + + if (!bus_if) + return; + + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + brcmf_sdiod_writel(sdiodev, addr, val, &err); + if (err) + brcmf_err("sdio reg write failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); +} +EXPORT_SYMBOL(brcmf_bus_reg_write); + +int brcmf_membytes(struct brcmf_bus *bus_if, bool set, u32 address, u8 *data, + unsigned int size) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + u32 block1_offset; + u32 block2_addr; + u16 block1_size; + u16 block2_size; + u8 *block2_data; + + if (!bus_if || !data) + return -EINVAL; + + sdiodev = bus_if->bus_priv.sdio; + /* To avoid SDIO access crosses AXI 4k address boundaries crossing */ + if (((address & SDIOD_ADDR_BOUND_MASK) + size) > SDIOD_ADDR_BOUND) { + brcmf_dbg(SDIO, "data cross 4K boundary\n"); + /* The 1st 4k packet */ + block1_offset = address & SDIOD_ADDR_BOUND_MASK; + block1_size = (SDIOD_ADDR_BOUND - block1_offset); + sdio_claim_host(sdiodev->func1); + err = brcmf_sdiod_ramrw(sdiodev, set, address, + data, block1_size); + if (err) { + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + return err; + } + /* The 2nd 4k packet */ + block2_addr = address + block1_size; + block2_size = size - block1_size; + block2_data = data + block1_size; + err = brcmf_sdiod_ramrw(sdiodev, set, block2_addr, + block2_data, block2_size); + if (err) + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + } else { + sdio_claim_host(sdiodev->func1); + err = brcmf_sdiod_ramrw(sdiodev, set, address, data, size); + if (err) + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + } + return err; +} +EXPORT_SYMBOL(brcmf_membytes); + +/* Function to enable the Bus Clock + * This function is not callable from non-sleepable context + */ +int brcmf_bus_clk_enable(struct brcmf_bus *bus_if, enum bus_owner owner) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + int err = 0; + + if (!bus_if) + return -EINVAL; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + btdev->use_count++; + sdio_release_host(sdiodev->func1); + err = brcmf_sdio_sleep(sdiodev->bus, false); + + return err; +} +EXPORT_SYMBOL(brcmf_bus_clk_enable); + +/* Function to disable the Bus Clock + * This function is not callable from non-sleepable context + */ +int brcmf_bus_clk_disable(struct brcmf_bus *bus_if, enum bus_owner owner) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + int err = 0; + + if (!bus_if) + return -EINVAL; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + if (btdev->use_count != 0) + btdev->use_count--; + sdio_release_host(sdiodev->func1); + err = brcmf_sdio_sleep(sdiodev->bus, true); + + return err; +} +EXPORT_SYMBOL(brcmf_bus_clk_disable); + +/* Function to reset bt_use_count counter to zero. + * This function is not callable from non-sleepable context + */ +void brcmf_bus_reset_bt_use_count(struct brcmf_bus *bus_if) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + + if (!bus_if) + return; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + btdev->use_count = 0; + sdio_release_host(sdiodev->func1); +} +EXPORT_SYMBOL(brcmf_bus_reset_bt_use_count); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h 2022-01-06 12:45:53.822318139 -0500 @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright 2019, Cypress Semiconductor Corporation or a subsidiary of + * Cypress Semiconductor Corporation. All rights reserved. + * This software, including source code, documentation and related + * materials ("Software"), is owned by Cypress Semiconductor + * Corporation or one of its subsidiaries ("Cypress") and is protected by + * and subject to worldwide patent protection (United States and foreign), + * United States copyright laws and international treaty provisions. + * Therefore, you may use this Software only as provided in the license + * agreement accompanying the software package from which you + * obtained this Software ("EULA"). If no EULA applies, Cypress hereby grants + * you a personal, nonexclusive, non-transferable license to copy, modify, + * and compile the Software source code solely for use in connection with + * Cypress's integrated circuit products. Any reproduction, modification, + * translation, compilation, or representation of this Software except as + * specified above is prohibited without the express written permission of + * Cypress. + * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress + * reserves the right to make changes to the Software without notice. Cypress + * does not assume any liability arising out of the application or use of the + * Software or any product or circuit described in the Software. Cypress does + * not authorize its products for use in any products where a malfunction or + * failure of the Cypress product may reasonably be expected to result in + * significant property damage, injury or death ("High Risk Product"). By + * including Cypress's product in a High Risk Product, the manufacturer + * of such system or application assumes all risk of such use and in doing + * so agrees to indemnify Cypress against all liability. + */ + +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO +int brcmf_btsdio_init(struct brcmf_bus *bus_if); +void brcmf_btsdio_detach(struct brcmf_bus *bus_if); +void brcmf_btsdio_int_handler(struct brcmf_bus *bus_if); +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if); +#else +static inline +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if) +{ + return 0; +} +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h 2022-01-06 12:45:53.822318139 -0500 @@ -7,6 +7,7 @@ #define BRCMFMAC_BUS_H #include "debug.h" +#include /* IDs of the 6 default common rings of msgbuf protocol */ #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 @@ -22,6 +23,12 @@ #define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ BRCMF_NROF_D2H_COMMON_MSGRINGS) +/* The interval to poll console */ +#define BRCMF_CONSOLE 10 + +/* The maximum console interval value (5 mins) */ +#define MAX_CONSOLE_INTERVAL (5 * 60) + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_DOWN, /* Not ready for frame transfers */ @@ -118,6 +125,19 @@ }; /** + * struct brcmf_bt_dev - bt shared SDIO device. + * + * @ bt_data: bt internal structure data + * @ bt_sdio_int_cb: bt registered interrupt callback function + * @ bt_use_count: Counter that tracks whether BT is using the bus + */ +struct brcmf_bt_dev { + void *bt_data; + void (*bt_sdio_int_cb)(void *data); + u32 use_count; /* Counter for tracking if BT is using the bus */ +}; + +/** * struct brcmf_bus - interface structure between common and bus layer * * @bus_priv: pointer to private bus device. @@ -132,6 +152,7 @@ * @wowl_supported: is wowl supported by bus driver. * @chiprev: revision of the dongle chip. * @msgbuf: msgbuf protocol parameters provided by bus layer. + * @bt_dev: bt shared SDIO device */ struct brcmf_bus { union { @@ -152,6 +173,12 @@ const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + bool allow_skborphan; +#endif +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + struct brcmf_bt_dev *bt_dev; +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ }; /* @@ -256,7 +283,7 @@ int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings); /* Indication from bus module regarding presence/insertion of dongle. */ -int brcmf_attach(struct device *dev); +int brcmf_attach(struct device *dev, bool start_bus); /* Indication from bus module regarding removal/absence of dongle */ void brcmf_detach(struct device *dev); void brcmf_free(struct device *dev); @@ -272,29 +299,15 @@ s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); void brcmf_bus_add_txhdrlen(struct device *dev, uint len); +int brcmf_fwlog_attach(struct device *dev); #ifdef CONFIG_BRCMFMAC_SDIO void brcmf_sdio_exit(void); -int brcmf_sdio_register(void); -#else -static inline void brcmf_sdio_exit(void) { } -static inline int brcmf_sdio_register(void) { return 0; } +void brcmf_sdio_register(void); #endif - #ifdef CONFIG_BRCMFMAC_USB void brcmf_usb_exit(void); -int brcmf_usb_register(void); -#else -static inline void brcmf_usb_exit(void) { } -static inline int brcmf_usb_register(void) { return 0; } -#endif - -#ifdef CONFIG_BRCMFMAC_PCIE -void brcmf_pcie_exit(void); -int brcmf_pcie_register(void); -#else -static inline void brcmf_pcie_exit(void) { } -static inline int brcmf_pcie_register(void) { return 0; } +void brcmf_usb_register(void); #endif #endif /* BRCMFMAC_BUS_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c 2022-01-06 12:45:53.822318139 -0500 @@ -63,6 +63,9 @@ #define RSN_CAP_MFPC_MASK BIT(7) #define RSN_PMKID_COUNT_LEN 2 +#define DPP_AKM_SUITE_TYPE 2 +#define WLAN_AKM_SUITE_DPP SUITE(WLAN_OUI_WFA, DPP_AKM_SUITE_TYPE) + #define VNDR_IE_CMD_LEN 4 /* length of the set command * string :"add", "del" (+ NUL) */ @@ -87,9 +90,42 @@ #define BRCMF_PS_MAX_TIMEOUT_MS 2000 +#define MGMT_AUTH_FRAME_DWELL_TIME 4000 +#define MGMT_AUTH_FRAME_WAIT_TIME (MGMT_AUTH_FRAME_DWELL_TIME + 100) + +/* Dump obss definitions */ +#define ACS_MSRMNT_DELAY 80 +#define CHAN_NOISE_DUMMY (-80) +#define OBSS_TOKEN_IDX 15 +#define IBSS_TOKEN_IDX 15 +#define TX_TOKEN_IDX 14 +#define CTG_TOKEN_IDX 13 +#define PKT_TOKEN_IDX 15 +#define IDLE_TOKEN_IDX 12 + #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) +struct brcmf_dump_survey { + u32 obss; + u32 ibss; + u32 no_ctg; + u32 no_pckt; + u32 tx; + u32 idle; +}; + +struct cca_stats_n_flags { + u32 msrmnt_time; /* Time for Measurement (msec) */ + u32 msrmnt_done; /* flag set when measurement complete */ + char buf[1]; +}; + +struct cca_msrmnt_query { + u32 msrmnt_query; + u32 time_req; +}; + static bool check_vif_up(struct brcmf_cfg80211_vif *vif) { if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { @@ -233,6 +269,51 @@ struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +#define WLC_E_IF_ROLE_STA 0 /* Infra STA */ +#define WLC_E_IF_ROLE_AP 1 /* Access Point */ + +#define WL_INTERFACE_CREATE_VER_1 1 +#define WL_INTERFACE_CREATE_VER_2 2 +#define WL_INTERFACE_CREATE_VER_3 3 +#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3 + +#define WL_INTERFACE_MAC_DONT_USE 0x0 +#define WL_INTERFACE_MAC_USE 0x2 + +#define WL_INTERFACE_CREATE_STA 0x0 +#define WL_INTERFACE_CREATE_AP 0x1 + +struct wl_interface_create_v1 { + u16 ver; /* structure version */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v2 { + u16 ver; /* structure version */ + u8 pad1[2]; + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 iftype; /* type of interface created */ + u8 pad2; + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v3 { + u16 ver; /* structure version */ + u16 len; /* length of structure + data */ + u16 fixed_len; /* length of structure */ + u8 iftype; /* type of interface created */ + u8 wlc_index; /* optional for wlc index */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 bssid[ETH_ALEN]; /* optional for BSSID */ + u8 if_index; /* interface index request */ + u8 pad[3]; + u8 data[]; /* Optional for specific data */ +}; + static u8 nl80211_band_to_fwil(enum nl80211_band band) { switch (band) { @@ -486,7 +567,7 @@ return err; } -static void +void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) { struct brcmf_cfg80211_vif *vif; @@ -520,40 +601,227 @@ return -ENOMEM; } +static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr) +{ + u8 mac_idx = ifp->drvr->sta_mac_idx; + + /* set difference MAC address with locally administered bit */ + memcpy(mac_addr, ifp->mac_addr, ETH_ALEN); + mac_addr[0] |= 0x02; + mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; + mac_idx++; + mac_idx = mac_idx % 2; + ifp->drvr->sta_mac_idx = mac_idx; +} + +static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) +{ + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; + int err; + + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_STA | + WL_INTERFACE_MAC_USE; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_info("failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_info("failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("station interface creation failed (%d)\n", + err); + return -EIO; + } + + return 0; +} + static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) { + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; struct brcmf_pub *drvr = ifp->drvr; struct brcmf_mbss_ssid_le mbss_ssid_le; int bsscfgidx; int err; - memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); - bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); - if (bsscfgidx < 0) - return bsscfgidx; - - mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); - mbss_ssid_le.SSID_len = cpu_to_le32(5); - sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_AP | + WL_INTERFACE_MAC_USE; + + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_info("failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } - err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, - sizeof(mbss_ssid_le)); - if (err < 0) - bphy_err(drvr, "setting ssid failed %d\n", err); + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_AP; + + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_info("failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_AP; + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("Does not support interface_create (%d)\n", + err); + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx); + + err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + + if (err < 0) + bphy_err(drvr, "setting ssid failed %d\n", err); + } return err; } /** - * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * brcmf_ap_add_vif() - create a new AP or STA virtual interface * * @wiphy: wiphy device of new interface. * @name: name of the new interface. - * @params: contains mac address for AP device. + * @params: contains mac address for AP or STA device. */ static -struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - struct vif_params *params) +struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name, + struct vif_params *params, + enum nl80211_iftype type) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -561,18 +829,24 @@ struct brcmf_cfg80211_vif *vif; int err; + if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP) + return ERR_PTR(-EINVAL); + if (brcmf_cfg80211_vif_event_armed(cfg)) return ERR_PTR(-EBUSY); brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP); + vif = brcmf_alloc_vif(cfg, type); if (IS_ERR(vif)) return (struct wireless_dev *)vif; brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_cfg80211_request_ap_if(ifp); + if (type == NL80211_IFTYPE_STATION) + err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr); + else + err = brcmf_cfg80211_request_ap_if(ifp); if (err) { brcmf_cfg80211_arm_vif_event(cfg, NULL); goto fail; @@ -719,15 +993,15 @@ } switch (type) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_MONITOR: return brcmf_mon_add_vif(wiphy, name); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, params); + wdev = brcmf_apsta_add_vif(wiphy, name, params, type); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: @@ -758,9 +1032,16 @@ { struct brcmf_pub *drvr = ifp->drvr; s32 err = 0; + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + ifp->drvr->req_mpc = mpc; if (check_vif_up(ifp->vif)) { - err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); + if (cfg->pwr_save) + err = brcmf_fil_iovar_int_set(ifp, "mpc", + ifp->drvr->req_mpc); + else + err = brcmf_fil_iovar_int_set(ifp, "mpc", 0); + if (err) { bphy_err(drvr, "fail to set mpc\n"); return; @@ -769,6 +1050,21 @@ } } +bool brcmf_is_apmode_operating(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + bool ret = false; + + list_for_each_entry(vif, &cfg->vif_list, list) { + if (brcmf_is_apmode(vif) && + test_bit(BRCMF_VIF_STATUS_AP_CREATED, &vif->sme_state)) + ret = true; + } + + return ret; +} + s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, bool aborted, bool fw_abort) @@ -847,8 +1143,8 @@ return err; } -static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy, - struct wireless_dev *wdev) +static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy, + struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = wdev->netdev; @@ -905,15 +1201,15 @@ switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return -EOPNOTSUPP; case NL80211_IFTYPE_MONITOR: return brcmf_mon_del_vif(wiphy, wdev); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - return brcmf_cfg80211_del_ap_iface(wiphy, wdev); + return brcmf_cfg80211_del_apsta_iface(wiphy, wdev); case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: @@ -1211,11 +1507,6 @@ if (err) goto scan_out; - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, - request->ie, request->ie_len); - if (err) - goto scan_out; - err = brcmf_do_escan(vif->ifp, request); if (err) goto scan_out; @@ -1416,6 +1707,8 @@ locally_generated, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { @@ -1610,14 +1903,18 @@ s32 val = 0; s32 err = 0; - if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) { val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) - val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) { + if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE) + val = WPA3_AUTH_SAE_PSK; + else + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) { val = WPA3_AUTH_SAE_PSK; - else + } else { val = WPA_AUTH_DISABLED; + } brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val); if (err) { @@ -1753,6 +2050,7 @@ struct brcmf_pub *drvr = ifp->drvr; s32 val; s32 err; + s32 okc_enable; const struct brcmf_tlv *rsn_ie; const u8 *ie; u32 ie_len; @@ -1763,6 +2061,7 @@ profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; profile->is_ft = false; + profile->is_okc = false; if (!sme->crypto.n_akm_suites) return 0; @@ -1815,6 +2114,10 @@ val = WPA2_AUTH_PSK | WPA2_AUTH_FT; profile->is_ft = true; break; + case WLAN_AKM_SUITE_DPP: + val = WFA_AUTH_DPP; + profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; + break; default: bphy_err(drvr, "invalid cipher group (%d)\n", sme->crypto.cipher_group); @@ -1836,8 +2139,17 @@ } } - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) { brcmf_dbg(INFO, "using 1X offload\n"); + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "get okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; @@ -1874,7 +2186,7 @@ brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp); skip_mfp_config: - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); if (err) { bphy_err(drvr, "could not set wpa_auth (%d)\n", err); @@ -2038,6 +2350,14 @@ return -EOPNOTSUPP; } + if (sme->channel_hint) { + chan = sme->channel_hint; + } + + if (sme->bssid_hint) { + sme->bssid = sme->bssid_hint; + } + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { /* A normal (non P2P) connection request setup. */ ie = NULL; @@ -2113,44 +2433,48 @@ goto done; } - if (sme->crypto.psk && - profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { - if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) { - err = -EINVAL; - goto done; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) { + if (sme->crypto.psk) { + if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { + if (WARN_ON(profile->use_fwsup != + BRCMF_PROFILE_FWSUP_NONE)) { + err = -EINVAL; + goto done; + } + brcmf_dbg(INFO, "using PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } } - brcmf_dbg(INFO, "using PSK offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; - } - if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { - /* enable firmware supplicant for this interface */ - err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); - if (err < 0) { - bphy_err(drvr, "failed to enable fw supplicant\n"); - goto done; + if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { + /* enable firmware supplicant for this interface */ + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); + if (err < 0) { + bphy_err(drvr, "failed to enable fw supplicant\n"); + goto done; + } + } else { + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 0); } - } - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) - err = brcmf_set_pmk(ifp, sme->crypto.psk, - BRCMF_WSEC_MAX_PSK_LEN); - else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { - /* clean up user-space RSNE */ - err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0); - if (err) { - bphy_err(drvr, "failed to clean up user-space RSNE\n"); - goto done; - } - err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, - sme->crypto.sae_pwd_len); - if (!err && sme->crypto.psk) + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) err = brcmf_set_pmk(ifp, sme->crypto.psk, BRCMF_WSEC_MAX_PSK_LEN); + else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { + /* clean up user-space RSNE */ + if (brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0)) { + bphy_err(drvr, "failed to clean up user-space RSNE\n"); + goto done; + } + err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, + sme->crypto.sae_pwd_len); + if (!err && sme->crypto.psk) + err = brcmf_set_pmk(ifp, sme->crypto.psk, + BRCMF_WSEC_MAX_PSK_LEN); + } + if (err) + goto done; } - if (err) - goto done; - /* Join with specific BSSID and cached SSID * If SSID is zero join based on BSSID only */ @@ -2260,6 +2584,8 @@ clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state); cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); @@ -2767,9 +3093,8 @@ struct brcmf_sta_info_le sta_info_le; u32 sta_flags; u32 is_tdls_peer; - s32 total_rssi_avg = 0; - s32 total_rssi = 0; - s32 count_rssi = 0; + s32 total_rssi; + s32 count_rssi; int rssi; u32 i; @@ -2835,27 +3160,25 @@ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes); } + total_rssi = 0; + count_rssi = 0; for (i = 0; i < BRCMF_ANT_MAX; i++) { - if (sta_info_le.rssi[i] == 0 || - sta_info_le.rx_lastpkt_rssi[i] == 0) - continue; - sinfo->chains |= BIT(count_rssi); - sinfo->chain_signal[count_rssi] = - sta_info_le.rx_lastpkt_rssi[i]; - sinfo->chain_signal_avg[count_rssi] = - sta_info_le.rssi[i]; - total_rssi += sta_info_le.rx_lastpkt_rssi[i]; - total_rssi_avg += sta_info_le.rssi[i]; - count_rssi++; + if (sta_info_le.rssi[i]) { + sinfo->chain_signal_avg[count_rssi] = + sta_info_le.rssi[i]; + sinfo->chain_signal[count_rssi] = + sta_info_le.rssi[i]; + total_rssi += sta_info_le.rssi[i]; + count_rssi++; + } } if (count_rssi) { - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); - sinfo->filled |= - BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); - sinfo->signal = total_rssi / count_rssi; - sinfo->signal_avg = total_rssi_avg / count_rssi; + sinfo->chains = count_rssi; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + total_rssi /= count_rssi; + sinfo->signal = total_rssi; } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) { memset(&scb_val, 0, sizeof(scb_val)); @@ -2934,12 +3257,15 @@ goto done; } - pm = enabled ? PM_FAST : PM_OFF; + pm = enabled ? ifp->drvr->settings->default_pm : PM_OFF; /* Do not enable the power save after assoc if it is a p2p interface */ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); pm = PM_OFF; } + + brcmf_set_mpc(ifp, ifp->drvr->req_mpc); + brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); @@ -2975,6 +3301,7 @@ u8 *notify_ie; size_t notify_ielen; struct cfg80211_inform_bss bss_data = {}; + const struct brcmf_tlv *ssid = NULL; if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { bphy_err(drvr, "Bss info is larger than buffer. Discarding\n"); @@ -3004,6 +3331,12 @@ notify_ielen = le32_to_cpu(bi->ie_length); bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100; + ssid = brcmf_parse_tlvs(notify_ie, notify_ielen, WLAN_EID_SSID); + if (ssid && ssid->data[0] == '\0' && ssid->len == bi->SSID_len) { + /* Update SSID for hidden AP */ + memcpy((u8 *)ssid->data, bi->SSID, bi->SSID_len); + } + brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); brcmf_dbg(CONN, "Capability: %X\n", notify_capability); @@ -3146,10 +3479,7 @@ struct brcmf_if *ifp) { struct brcmf_pub *drvr = cfg->pub; - struct brcmf_bss_info_le *bi; - const struct brcmf_tlv *tim; - size_t ie_len; - u8 *ie; + struct brcmf_bss_info_le *bi = NULL; s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -3163,29 +3493,8 @@ bphy_err(drvr, "Could not get bss info %d\n", err); goto update_bss_info_out; } - bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); err = brcmf_inform_single_bss(cfg, bi); - if (err) - goto update_bss_info_out; - - ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); - ie_len = le32_to_cpu(bi->ie_length); - - tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); - if (!tim) { - /* - * active scan was done so we could not get dtim - * information out of probe response. - * so we speficially query dtim information to dongle. - */ - u32 var; - err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var); - if (err) { - bphy_err(drvr, "wl dtim_assoc failed (%d)\n", err); - goto update_bss_info_out; - } - } update_bss_info_out: brcmf_dbg(TRACE, "Exit"); @@ -3809,17 +4118,34 @@ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus_if = drvr->bus_if; + struct brcmf_cfg80211_info *config = drvr->config; + int retry = BRCMF_PM_WAIT_MAXRETRY; + s32 power_mode; + + power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF; brcmf_dbg(TRACE, "Enter\n"); + config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMING; + if (cfg->wowl.active) { + /* wait for bus resumed */ + while (retry && bus_if->state != BRCMF_BUS_UP) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && bus_if->state != BRCMF_BUS_UP) + brcmf_err("timed out wait for bus resume\n"); + brcmf_report_wowl_wakeind(wiphy, ifp); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) brcmf_configure_arp_nd_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->wowl.pre_pmmode); + power_mode); cfg->wowl.active = false; if (cfg->wowl.nd_enabled) { brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0); @@ -3828,7 +4154,12 @@ brcmf_notify_sched_scan_results); cfg->wowl.nd_enabled = false; } + + /* disable packet filters */ + brcmf_pktfilter_enable(ifp->ndev, false); + } + config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED; return 0; } @@ -3844,7 +4175,6 @@ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) brcmf_configure_arp_nd_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; @@ -3886,6 +4216,9 @@ brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); cfg->wowl.active = true; + + /* enable packet filters */ + brcmf_pktfilter_enable(ifp->ndev, true); } static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, @@ -3895,9 +4228,12 @@ struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_vif *vif; + struct brcmf_cfg80211_info *config = ifp->drvr->config; brcmf_dbg(TRACE, "Enter\n"); + config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDING; + /* if the primary net_device is not READY there is nothing * we can do but pray resume goes smoothly. */ @@ -3912,7 +4248,8 @@ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); - if (wowl == NULL) { + if (!wowl || !test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state)) { brcmf_bus_wowl_config(cfg->pub->bus_if, false); list_for_each_entry(vif, &cfg->vif_list, list) { if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) @@ -3932,14 +4269,19 @@ brcmf_set_mpc(ifp, 1); } else { - /* Configure WOWL paramaters */ - brcmf_configure_wowl(cfg, ifp, wowl); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + /* Configure WOWL parameters */ + brcmf_configure_wowl(cfg, ifp, wowl); } exit: - brcmf_dbg(TRACE, "Exit\n"); + /* set cfg80211 pm state to cfg80211 suspended state */ + config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDED; + /* clear any scanning activity */ cfg->scan_status = 0; + + brcmf_dbg(TRACE, "Exit\n"); return 0; } @@ -4105,6 +4447,12 @@ return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); } +static bool brcmf_valid_dpp_suite(u8 *oui) +{ + return (memcmp(oui, WFA_OUI, TLV_OUI_LEN) == 0 && + *(oui + TLV_OUI_LEN) == DPP_AKM_SUITE_TYPE); +} + static s32 brcmf_configure_wpaie(struct brcmf_if *ifp, const struct brcmf_vs_tlv *wpa_ie, @@ -4218,42 +4566,47 @@ goto exit; } for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + if (brcmf_valid_dpp_suite(&data[offset])) { + wpa_auth |= WFA_AUTH_DPP; + offset += TLV_OUI_LEN; + } else if (brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + offset += TLV_OUI_LEN; + switch (data[offset]) { + case RSN_AKM_NONE: + brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); + wpa_auth |= WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); + is_rsn_ie ? + (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : + (wpa_auth |= WPA_AUTH_UNSPECIFIED); + break; + case RSN_AKM_PSK: + brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); + is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : + (wpa_auth |= WPA_AUTH_PSK); + break; + case RSN_AKM_SHA256_PSK: + brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); + wpa_auth |= WPA2_AUTH_PSK_SHA256; + break; + case RSN_AKM_SHA256_1X: + brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); + wpa_auth |= WPA2_AUTH_1X_SHA256; + break; + case RSN_AKM_SAE: + brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); + wpa_auth |= WPA3_AUTH_SAE_PSK; + break; + default: + bphy_err(drvr, "Invalid key mgmt info\n"); + } + } else { err = -EINVAL; bphy_err(drvr, "ivalid OUI\n"); goto exit; } - offset += TLV_OUI_LEN; - switch (data[offset]) { - case RSN_AKM_NONE: - brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); - wpa_auth |= WPA_AUTH_NONE; - break; - case RSN_AKM_UNSPECIFIED: - brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : - (wpa_auth |= WPA_AUTH_UNSPECIFIED); - break; - case RSN_AKM_PSK: - brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : - (wpa_auth |= WPA_AUTH_PSK); - break; - case RSN_AKM_SHA256_PSK: - brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); - wpa_auth |= WPA2_AUTH_PSK_SHA256; - break; - case RSN_AKM_SHA256_1X: - brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); - wpa_auth |= WPA2_AUTH_1X_SHA256; - break; - case RSN_AKM_SAE: - brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); - wpa_auth |= WPA3_AUTH_SAE_PSK; - break; - default: - bphy_err(drvr, "Invalid key mgmt info\n"); - } offset++; } @@ -4273,10 +4626,12 @@ */ if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 | WPA2_AUTH_1X_SHA256 | + WFA_AUTH_DPP | WPA3_AUTH_SAE_PSK))) { err = -EINVAL; goto exit; } + /* Firmware has requirement that WPA2_AUTH_PSK/ * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI * is to be included in the rsn ie. @@ -4712,6 +5067,7 @@ settings->inactivity_timeout); dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; + brcmf_dbg(TRACE, "mbss %s\n", mbss ? "enabled" : "disabled"); /* store current 11d setting */ if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, @@ -4801,7 +5157,7 @@ err = -EINVAL; goto exit; } - + ifp->isap = false; /* Interface specific setup */ if (dev_role == NL80211_IFTYPE_AP) { if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) @@ -4813,17 +5169,17 @@ err); goto exit; } - if (!mbss) { - /* Firmware 10.x requires setting channel after enabling - * AP and before bringing interface up. - */ - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", - chanspec, err); - goto exit; - } + + /* Firmware 10.x requires setting channel after enabling + * AP and before bringing interface up. + */ + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); + if (err < 0) { + bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", + chanspec, err); + goto exit; } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) { bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err); @@ -4881,7 +5237,7 @@ err); goto exit; } - + ifp->isap = true; brcmf_dbg(TRACE, "AP mode configuration complete\n"); } else if (dev_role == NL80211_IFTYPE_P2P_GO) { err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); @@ -4913,6 +5269,7 @@ goto exit; } + ifp->isap = true; brcmf_dbg(TRACE, "GO mode configuration complete\n"); } else { WARN_ON(1); @@ -4926,6 +5283,9 @@ if ((err) && (!mbss)) { brcmf_set_mpc(ifp, 1); brcmf_configure_arp_nd_offload(ifp, true); + } else { + cfg->num_softap++; + brcmf_dbg(TRACE, "Num of SoftAP %u\n", cfg->num_softap); } return err; } @@ -4939,6 +5299,7 @@ s32 err; struct brcmf_fil_bss_enable_le bss_enable; struct brcmf_join_params join_params; + s32 apsta = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -4955,6 +5316,27 @@ profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE); } + cfg->num_softap--; + + /* Clear bss configuration and SSID */ + bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); + bss_enable.enable = cpu_to_le32(0); + err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, + sizeof(bss_enable)); + if (err < 0) + brcmf_err("bss_enable config failed %d\n", err); + + memset(&join_params, 0, sizeof(join_params)); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, sizeof(join_params)); + if (err < 0) + bphy_err(drvr, "SET SSID error (%d)\n", err); + + if (cfg->num_softap) { + brcmf_dbg(TRACE, "Num of SoftAP %u\n", cfg->num_softap); + return 0; + } + if (ifp->vif->mbss) { err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); return err; @@ -4964,17 +5346,18 @@ if (ifp->bsscfgidx == 0) brcmf_fil_iovar_int_set(ifp, "closednet", 0); - memset(&join_params, 0, sizeof(join_params)); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, sizeof(join_params)); - if (err < 0) - bphy_err(drvr, "SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + err = brcmf_fil_iovar_int_get(ifp, "apsta", &apsta); if (err < 0) - bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); - if (err < 0) - bphy_err(drvr, "setting AP mode failed %d\n", err); + brcmf_err("wl apsta failed (%d)\n", err); + + if (!apsta) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) + bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + if (err < 0) + bphy_err(drvr, "Set AP mode error %d\n", err); + } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, @@ -4994,8 +5377,8 @@ bphy_err(drvr, "bss_enable config failed %d\n", err); } brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_nd_offload(ifp, true); clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_configure_arp_nd_offload(ifp, true); brcmf_net_setcarrier(ifp, false); return err; @@ -5107,9 +5490,13 @@ s32 ie_len; struct brcmf_fil_action_frame_le *action_frame; struct brcmf_fil_af_params_le *af_params; - bool ack; + bool ack = false; s32 chan_nr; u32 freq; + struct brcmf_mf_params_le *mf_params; + u32 mf_params_len; + s32 timeout; + u32 hw_channel; brcmf_dbg(TRACE, "Enter\n"); @@ -5170,13 +5557,16 @@ /* Add the channel. Use the one specified as parameter if any or * the current one (got from the firmware) otherwise */ - if (chan) + if (chan) { freq = chan->center_freq; - else + chan_nr = ieee80211_frequency_to_channel(freq); + af_params->channel = cpu_to_le32(chan_nr); + } else { brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, - &freq); - chan_nr = ieee80211_frequency_to_channel(freq); - af_params->channel = cpu_to_le32(chan_nr); + &hw_channel); + af_params->channel = hw_channel; + } + af_params->dwell_time = cpu_to_le32(params->wait); memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], le16_to_cpu(action_frame->len)); @@ -5185,11 +5575,79 @@ *cookie, le16_to_cpu(action_frame->len), freq); ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), - af_params); + af_params, vif); cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, GFP_KERNEL); kfree(af_params); + } else if (ieee80211_is_auth(mgmt->frame_control)) { + reinit_completion(&vif->mgmt_tx); + clear_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + &vif->mgmt_tx_status); + + mf_params_len = offsetof(struct brcmf_mf_params_le, data) + + (len - DOT11_MGMT_HDR_LEN); + mf_params = kzalloc(mf_params_len, GFP_KERNEL); + if (!mf_params) { + err = -ENOMEM; + goto exit; + } + + mf_params->dwell_time = cpu_to_le32(MGMT_AUTH_FRAME_DWELL_TIME); + mf_params->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); + mf_params->frame_control = mgmt->frame_control; + + if (chan) { + freq = chan->center_freq; + chan_nr = ieee80211_frequency_to_channel(freq); + mf_params->channel = cpu_to_le32(chan_nr); + } else { + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &hw_channel); + mf_params->channel = hw_channel; + } + + memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN); + memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); + mf_params->packet_id = cpu_to_le32(*cookie); + memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN], + le16_to_cpu(mf_params->len)); + + brcmf_dbg(TRACE, "Auth frame, cookie=%d, fc=%04x, len=%d, channel=%d\n", + le32_to_cpu(mf_params->packet_id), + le16_to_cpu(mf_params->frame_control), + le16_to_cpu(mf_params->len), + le32_to_cpu(mf_params->channel)); + + vif->mgmt_tx_id = le32_to_cpu(mf_params->packet_id); + set_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status); + + err = brcmf_fil_bsscfg_data_set(vif->ifp, "mgmt_frame", + mf_params, mf_params_len); + if (err) { + bphy_err(drvr, "Failed to send Auth frame: err=%d\n", + err); + goto tx_status; + } + + timeout = + wait_for_completion_timeout(&vif->mgmt_tx, + MGMT_AUTH_FRAME_WAIT_TIME); + if (test_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status)) { + brcmf_dbg(TRACE, "TX Auth frame operation is success\n"); + ack = true; + } else { + bphy_err(drvr, "TX Auth frame operation is failed: status=%ld)\n", + vif->mgmt_tx_status); + } + +tx_status: + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + GFP_KERNEL); + kfree(mf_params); + } else { brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len); @@ -5443,17 +5901,27 @@ const struct cfg80211_pmk_conf *conf) { struct brcmf_if *ifp; + struct brcmf_pub *drvr; + int ret; brcmf_dbg(TRACE, "enter\n"); /* expect using firmware supplicant for 1X */ ifp = netdev_priv(dev); + drvr = ifp->drvr; if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) return -EINVAL; if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN) return -ERANGE; + if (ifp->vif->profile.is_okc) { + ret = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", conf->pmk, + conf->pmk_len); + if (ret < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", ret); + } + return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); } @@ -5470,6 +5938,74 @@ return brcmf_set_pmk(ifp, NULL, 0); } +static int +brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params) +{ + struct brcmf_if *ifp; + int ret = 0; + u32 ap_isolate, val; + + brcmf_dbg(TRACE, "Enter\n"); + ifp = netdev_priv(dev); + if (params->ap_isolate >= 0) { + ap_isolate = (u32)params->ap_isolate; + ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", ap_isolate); + if (ret < 0) + brcmf_err("ap_isolate iovar failed: ret=%d\n", ret); + } + + /* Get ap_isolate value from firmware to detemine whether fmac */ + /* driver supports packet forwarding. */ + if (brcmf_fil_iovar_int_get(ifp, "ap_isolate", &val) == 0) { + ifp->fmac_pkt_fwd_en = + ((params->ap_isolate == 0) && (val == 1)) ? + true : false; + } else { + brcmf_err("get ap_isolate iovar failed: ret=%d\n", ret); + ifp->fmac_pkt_fwd_en = false; + } + + return ret; +} + +static int +brcmf_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + struct brcmf_if *ifp; + struct brcmf_pub *drvr; + struct brcmf_auth_req_status_le auth_status; + int ret = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + ifp = netdev_priv(dev); + drvr = ifp->drvr; + if (params->status == WLAN_STATUS_SUCCESS) { + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS); + } else { + bphy_err(drvr, "External authentication failed: status=%d\n", + params->status); + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_FAIL); + } + + memcpy(auth_status.peer_mac, params->bssid, ETH_ALEN); + auth_status.ssid_len = cpu_to_le32(min_t(u8, params->ssid.ssid_len, + IEEE80211_MAX_SSID_LEN)); + memcpy(auth_status.ssid, params->ssid.ssid, auth_status.ssid_len); + memset(auth_status.pmkid, 0, WLAN_PMKID_LEN); + if (params->pmkid) + memcpy(auth_status.pmkid, params->pmkid, WLAN_PMKID_LEN); + + ret = brcmf_fil_iovar_data_set(ifp, "auth_status", &auth_status, + sizeof(auth_status)); + if (ret < 0) + bphy_err(drvr, "auth_status iovar failed: ret=%d\n", ret); + + return ret; +} + static struct cfg80211_ops brcmf_cfg80211_ops = { .add_virtual_intf = brcmf_cfg80211_add_iface, .del_virtual_intf = brcmf_cfg80211_del_iface, @@ -5516,6 +6052,8 @@ .update_connect_params = brcmf_cfg80211_update_conn_params, .set_pmk = brcmf_cfg80211_set_pmk, .del_pmk = brcmf_cfg80211_del_pmk, + .change_bss = brcmf_cfg80211_change_bss, + .external_auth = brcmf_cfg80211_external_auth, }; struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings) @@ -5562,6 +6100,7 @@ vif->mbss = mbss; } + init_completion(&vif->mgmt_tx); list_add_tail(&vif->list, &cfg->vif_list); return vif; } @@ -5580,8 +6119,10 @@ ifp = netdev_priv(ndev); vif = ifp->vif; - if (vif) + if (vif) { brcmf_free_vif(vif); + ifp->vif = NULL; + } } static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, @@ -5614,8 +6155,7 @@ return false; } -static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif, - const struct brcmf_event_msg *e) +static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) { u32 event = e->event_code; u16 flags = e->flags; @@ -5624,8 +6164,6 @@ (event == BRCMF_E_DISASSOC_IND) || ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { brcmf_dbg(CONN, "Processing link down\n"); - clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); - clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); return true; } return false; @@ -5884,6 +6422,47 @@ return err; } +static bool +brcmf_has_pmkid(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *rsn_ie; + const u8 *ie; + u32 ie_len; + u32 offset; + u16 count; + + rsn_ie = brcmf_parse_tlvs(parse, len, WLAN_EID_RSN); + if (!rsn_ie) + goto done; + ie = (const u8 *)rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + /* Skip group data cipher suite */ + offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto done; + /* Skip pairwise cipher suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto done; + /* Skip auth key management suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + RSN_CAP_LEN >= ie_len) + goto done; + /* Skip rsn capabilities */ + offset += RSN_CAP_LEN; + if (offset + RSN_PMKID_COUNT_LEN > ie_len) + goto done; + /* Extract PMKID count */ + count = ie[offset] + (ie[offset + 1] << 8); + if (count) + return true; + +done: + return false; +} + static s32 brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, @@ -5944,14 +6523,14 @@ roam_info.resp_ie = conn_info->resp_ie; roam_info.resp_ie_len = conn_info->resp_ie_len; + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && + (brcmf_has_pmkid(roam_info.req_ie, roam_info.req_ie_len) || + profile->is_ft || profile->is_okc)) + roam_info.authorized = true; + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); brcmf_dbg(CONN, "Report roaming result\n"); - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { - cfg80211_port_authorized(ndev, profile->bssid, GFP_KERNEL); - brcmf_dbg(CONN, "Report port authorized\n"); - } - set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -5979,6 +6558,10 @@ &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_SUCCESS; } else { + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, + &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, + &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; } conn_params.bssid = profile->bssid; @@ -5986,6 +6569,11 @@ conn_params.req_ie_len = conn_info->req_ie_len; conn_params.resp_ie = conn_info->resp_ie; conn_params.resp_ie_len = conn_info->resp_ie_len; + + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && + brcmf_has_pmkid(conn_params.req_ie, conn_params.req_ie_len)) + conn_params.authorized = true; + cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); brcmf_dbg(CONN, "Report connect result - connection %s\n", completed ? "succeeded" : "failed"); @@ -6058,6 +6646,14 @@ } if (brcmf_is_apmode(ifp->vif)) { + if (e->event_code == BRCMF_E_ASSOC_IND || + e->event_code == BRCMF_E_REASSOC_IND) { + brcmf_findadd_sta(ifp, e->addr); + } else if ((e->event_code == BRCMF_E_DISASSOC_IND) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DEAUTH)) { + brcmf_del_sta(ifp, e->addr); + } err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(ifp->vif, e)) { brcmf_dbg(CONN, "Linkup\n"); @@ -6073,12 +6669,16 @@ } else brcmf_bss_connect_done(cfg, ndev, e, true); brcmf_net_setcarrier(ifp, true); - } else if (brcmf_is_linkdown(ifp->vif, e)) { + } else if (brcmf_is_linkdown(e)) { brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif) && - test_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) { - if (memcmp(profile->bssid, e->addr, ETH_ALEN)) + (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) || + test_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state))) { + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) && + memcmp(profile->bssid, e->addr, ETH_ALEN)) return err; brcmf_bss_connect_done(cfg, ndev, e, false); @@ -6150,6 +6750,9 @@ struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; struct brcmf_cfg80211_vif *vif; + enum nl80211_iftype iftype = NL80211_IFTYPE_UNSPECIFIED; + bool vif_pend = false; + int err; brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n", ifevent->action, ifevent->flags, ifevent->ifidx, @@ -6162,9 +6765,28 @@ switch (ifevent->action) { case BRCMF_E_IF_ADD: /* waiting process may have timed out */ - if (!cfg->vif_event.vif) { + if (!vif) { + /* handle IF_ADD event from firmware */ spin_unlock(&event->vif_event_lock); - return -EBADF; + vif_pend = true; + if (ifevent->role == WLC_E_IF_ROLE_STA) + iftype = NL80211_IFTYPE_STATION; + else if (ifevent->role == WLC_E_IF_ROLE_AP) + iftype = NL80211_IFTYPE_AP; + else + vif_pend = false; + + if (vif_pend) { + vif = brcmf_alloc_vif(cfg, iftype); + if (IS_ERR(vif)) { + brcmf_err("Role:%d failed to alloc vif\n", + ifevent->role); + return PTR_ERR(vif); + } + } else { + brcmf_err("Invalid Role:%d\n", ifevent->role); + return -EBADF; + } } ifp->vif = vif; @@ -6174,6 +6796,18 @@ ifp->ndev->ieee80211_ptr = &vif->wdev; SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); } + + if (vif_pend) { + err = brcmf_net_attach(ifp, false); + if (err) { + brcmf_err("netdevice register failed with err:%d\n", + err); + brcmf_free_vif(vif); + free_netdev(ifp->ndev); + } + return err; + } + spin_unlock(&event->vif_event_lock); wake_up(&event->vif_wq); return 0; @@ -6197,6 +6831,122 @@ return -EINVAL; } +static s32 +brcmf_notify_ext_auth_request(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct cfg80211_external_auth_params params; + struct brcmf_auth_req_status_le *auth_req = + (struct brcmf_auth_req_status_le *)data; + s32 err = 0; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*auth_req)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + params.action = NL80211_EXTERNAL_AUTH_START; + params.key_mgmt_suite = ntohl(WLAN_AKM_SUITE_SAE); + params.status = WLAN_STATUS_SUCCESS; + params.ssid.ssid_len = min_t(u32, 32, le32_to_cpu(auth_req->ssid_len)); + memcpy(params.ssid.ssid, auth_req->ssid, params.ssid.ssid_len); + memcpy(params.bssid, auth_req->peer_mac, ETH_ALEN); + + err = cfg80211_external_auth_request(ifp->ndev, ¶ms, GFP_ATOMIC); + if (err) + bphy_err(drvr, "Ext Auth request to supplicant failed (%d)\n", + err); + + return err; +} + +static s32 +brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = drvr->config; + struct wireless_dev *wdev; + u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); + struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; + u8 *frame = (u8 *)(rxframe + 1); + struct brcmu_chan ch; + struct ieee80211_mgmt *mgmt_frame; + s32 freq; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*rxframe)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + wdev = &ifp->vif->wdev; + WARN_ON(!wdev); + + ch.chspec = be16_to_cpu(rxframe->chanspec); + cfg->d11inf.decchspec(&ch); + + mgmt_frame = kzalloc(mgmt_frame_len, GFP_KERNEL); + if (!mgmt_frame) + return -ENOMEM; + + mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_AUTH); + memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN); + memcpy(mgmt_frame->sa, e->addr, ETH_ALEN); + brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid, + ETH_ALEN); + frame += offsetof(struct ieee80211_mgmt, u); + memcpy(&mgmt_frame->u, frame, + mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); + + freq = ieee80211_channel_to_frequency(ch.control_ch_num, + ch.band == BRCMU_CHAN_BAND_2G ? + NL80211_BAND_2GHZ : + NL80211_BAND_5GHZ); + + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH); + kfree(mgmt_frame); + return 0; +} + +static s32 +brcmf_notify_mgmt_tx_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_vif *vif = ifp->vif; + u32 *packet_id = (u32 *)data; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!test_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status) || + (*packet_id != vif->mgmt_tx_id)) + return 0; + + if (e->event_code == BRCMF_E_MGMT_FRAME_TXSTATUS) { + if (e->status == BRCMF_E_STATUS_SUCCESS) + set_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + else + set_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + } else { + set_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, &vif->mgmt_tx_status); + } + + complete(&vif->mgmt_tx); + return 0; +} + static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) { conf->frag_threshold = (u32)-1; @@ -6241,6 +6991,14 @@ brcmf_p2p_notify_action_tx_complete); brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP, brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_EXT_AUTH_REQ, + brcmf_notify_ext_auth_request); + brcmf_fweh_register(cfg->pub, BRCMF_E_EXT_AUTH_FRAME_RX, + brcmf_notify_auth_frame_rx); + brcmf_fweh_register(cfg->pub, BRCMF_E_MGMT_FRAME_TXSTATUS, + brcmf_notify_mgmt_tx_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_MGMT_FRAME_OFF_CHAN_COMPLETE, + brcmf_notify_mgmt_tx_status); } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -6309,6 +7067,7 @@ cfg->dongle_up = false; /* dongle down */ brcmf_abort_scanning(cfg); brcmf_deinit_priv_mem(cfg); + brcmf_clear_assoc_ies(cfg); } static void init_vif_event(struct brcmf_cfg80211_vif_event *event) @@ -6808,6 +7567,7 @@ [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { @@ -6871,7 +7631,7 @@ * * p2p, mchan, and mbss: * - * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total + * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total * #AP <= 4, matching BI, channels = 1, 4 total * @@ -6917,7 +7677,7 @@ goto err; combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan)); - c0_limits[i].max = 1; + c0_limits[i].max = 1 + (p2p && mchan); c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); if (mon_flag) { c0_limits[i].max = 1; @@ -7017,6 +7777,7 @@ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_pub *drvr = cfg->pub; struct wiphy_wowlan_support *wowl; + struct cfg80211_wowlan *brcmf_wowlan_config = NULL; wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support), GFP_KERNEL); @@ -7039,12 +7800,34 @@ } wiphy->wowlan = wowl; + + /* wowlan_config structure report for kernels */ + brcmf_wowlan_config = kzalloc(sizeof(*brcmf_wowlan_config), + GFP_KERNEL); + if (brcmf_wowlan_config) { + brcmf_wowlan_config->any = false; + brcmf_wowlan_config->disconnect = true; + brcmf_wowlan_config->eap_identity_req = true; + brcmf_wowlan_config->four_way_handshake = true; + brcmf_wowlan_config->rfkill_release = false; + brcmf_wowlan_config->patterns = NULL; + brcmf_wowlan_config->n_patterns = 0; + brcmf_wowlan_config->tcp = NULL; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) + brcmf_wowlan_config->gtk_rekey_failure = true; + else + brcmf_wowlan_config->gtk_rekey_failure = false; + } else { + brcmf_err("Can not allocate memory for brcm_wowlan_config\n"); + } + wiphy->wowlan_config = brcmf_wowlan_config; #endif } static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); const struct ieee80211_iface_combination *combo; struct ieee80211_supported_band *band; u16 max_interfaces = 0; @@ -7113,6 +7896,12 @@ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SAE_OFFLOAD_AP); } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) { + wiphy->features |= NL80211_FEATURE_SAE; + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING); + } + wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { @@ -7122,9 +7911,12 @@ /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + wiphy->vendor_events = brcmf_vendor_events; + wiphy->n_vendor_events = BRCMF_VNDR_EVTS_LAST; + brcmf_fweh_register(cfg->pub, BRCMF_E_PHY_TEMP, + brcmf_wiphy_phy_temp_evt_handler); - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy, ifp); + brcmf_wiphy_wowl_params(wiphy, ifp); err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, sizeof(bandlist)); if (err) { @@ -7187,6 +7979,7 @@ struct wireless_dev *wdev; struct brcmf_if *ifp; s32 power_mode; + s32 eap_restrict; s32 err = 0; if (cfg->dongle_up) @@ -7201,7 +7994,7 @@ brcmf_dongle_scantime(ifp); - power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; + power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); if (err) goto default_conf_out; @@ -7211,6 +8004,14 @@ err = brcmf_dongle_roam(ifp); if (err) goto default_conf_out; + + eap_restrict = ifp->drvr->settings->eap_restrict; + if (eap_restrict) { + err = brcmf_fil_iovar_int_set(ifp, "eap_restrict", + eap_restrict); + if (err) + brcmf_info("eap_restrict error (%d)\n", err); + } err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, NULL); if (err) @@ -7396,6 +8197,226 @@ return 0; } +static int +brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey) +{ + int i; + char *token; + char delim[] = "\n "; + unsigned long val; + int err = 0; + + token = strsep(&buf, delim); + while (token) { + if (!strcmp(token, "OBSS")) { + for (i = 0; i < OBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->obss = val; + } + + if (!strcmp(token, "IBSS")) { + for (i = 0; i < IBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->ibss = val; + } + + if (!strcmp(token, "TXDur")) { + for (i = 0; i < TX_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->tx = val; + } + + if (!strcmp(token, "Category")) { + for (i = 0; i < CTG_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->no_ctg = val; + } + + if (!strcmp(token, "Packet")) { + for (i = 0; i < PKT_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->no_pckt = val; + } + + if (!strcmp(token, "Opp(time):")) { + for (i = 0; i < IDLE_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->idle = val; + } + + token = strsep(&buf, delim); + + if (err) + return err; + } + + return 0; +} + +static int +brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, + struct brcmf_dump_survey *survey) +{ + struct cca_stats_n_flags *results; + char *buf; + int err; + + buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); + err = brcmf_fil_iovar_data_get(ifp, "dump_obss", + buf, BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("dump_obss error (%d)\n", err); + err = -EINVAL; + goto exit; + } + results = (struct cca_stats_n_flags *)(buf); + + if (req.msrmnt_query) + brcmf_parse_dump_obss(results->buf, survey); + +exit: + kfree(buf); + return err; +} + +static s32 +cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u16 chspec = 0; + int err = 0; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + + /* set_channel */ + chspec = channel_to_chanspec(&cfg->d11inf, chan); + if (chspec != INVCHANSPEC) { + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec); + if (err) + err = -EINVAL; + } else { + brcmf_err("failed to convert host chanspec to fw chanspec\n"); + err = -EINVAL; + } + + return err; +} + +static int +brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, + int idx, struct survey_info *info) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_dump_survey *survey; + struct ieee80211_supported_band *band; + struct ieee80211_channel *chan; + struct cca_msrmnt_query req; + u32 noise; + int err; + + brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); + + /* Do not run survey when VIF in CONNECTING / CONNECTED states */ + if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) || + (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) { + return -EBUSY; + } + + band = wiphy->bands[NL80211_BAND_2GHZ]; + if (band && idx >= band->n_channels) { + idx -= band->n_channels; + band = NULL; + } + + if (!band || idx >= band->n_channels) { + band = wiphy->bands[NL80211_BAND_5GHZ]; + if (idx >= band->n_channels) + return -ENOENT; + } + + /* Setting current channel to the requested channel */ + chan = &band->channels[idx]; + err = cfg80211_set_channel(wiphy, ndev, chan, NL80211_CHAN_HT20); + if (err) { + info->channel = chan; + info->filled = 0; + return 0; + } + + survey = kzalloc(sizeof(*survey), GFP_KERNEL); + if (!survey) + return -ENOMEM; + + /* Disable mpc */ + brcmf_set_mpc(ifp, 0); + + /* Set interface up, explicitly. */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err) { + brcmf_err("set interface up failed, err = %d\n", err); + goto exit; + } + + /* Get noise value */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); + if (err) { + brcmf_err("Get Phy Noise failed, use dummy value\n"); + noise = CHAN_NOISE_DUMMY; + } + + + /* Start Measurement for obss stats on current channel */ + req.msrmnt_query = 0; + req.time_req = ACS_MSRMNT_DELAY; + err = brcmf_dump_obss(ifp, req, survey); + if (err) + goto exit; + + /* Add 10 ms for IOVAR completion */ + msleep(ACS_MSRMNT_DELAY + 10); + + /* Issue IOVAR to collect measurement results */ + req.msrmnt_query = 1; + err = brcmf_dump_obss(ifp, req, survey); + if (err) + goto exit; + + info->channel = chan; + info->noise = noise; + info->time = ACS_MSRMNT_DELAY; + info->time_busy = ACS_MSRMNT_DELAY - survey->idle; + info->time_rx = survey->obss + survey->ibss + survey->no_ctg + + survey->no_pckt; + info->time_tx = survey->tx; + info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; + + brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n", + ieee80211_frequency_to_channel(chan->center_freq), + ACS_MSRMNT_DELAY); + brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n", + info->noise, info->time_busy, info->time_rx, info->time_tx); + +exit: + if (!brcmf_is_apmode(ifp->vif)) + brcmf_set_mpc(ifp, 1); + kfree(survey); + return err; +} + static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *req) { @@ -7491,6 +8512,8 @@ cfg->wiphy = wiphy; cfg->pub = drvr; + cfg->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED; + cfg->num_softap = 0; init_vif_event(&cfg->vif_event); INIT_LIST_HEAD(&cfg->vif_list); @@ -7547,6 +8570,11 @@ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; #endif + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS)) + ops->dump_survey = brcmf_cfg80211_dump_survey; + else + ops->dump_survey = NULL; + err = wiphy_register(wiphy); if (err < 0) { bphy_err(drvr, "Could not register wiphy device (%d)\n", err); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h 2022-01-06 12:45:53.822318139 -0500 @@ -92,6 +92,15 @@ #define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500) +#define BRCMF_PM_WAIT_MAXRETRY 100 + +/* cfg80211 wowlan definitions */ +#define WL_WOWLAN_MAX_PATTERNS 8 +#define WL_WOWLAN_MIN_PATTERN_LEN 1 +#define WL_WOWLAN_MAX_PATTERN_LEN 255 +#define WL_WOWLAN_PKT_FILTER_ID_FIRST 201 +#define WL_WOWLAN_PKT_FILTER_ID_LAST (WL_WOWLAN_PKT_FILTER_ID_FIRST + \ + WL_WOWLAN_MAX_PATTERNS - 1) /** * enum brcmf_scan_status - scan engine status * @@ -155,6 +164,7 @@ enum brcmf_profile_fwsup use_fwsup; u16 use_fwauth; bool is_ft; + bool is_okc; }; /** @@ -178,6 +188,28 @@ BRCMF_VIF_STATUS_ASSOC_SUCCESS, }; +enum brcmf_cfg80211_pm_state { + BRCMF_CFG80211_PM_STATE_RESUMED, + BRCMF_CFG80211_PM_STATE_RESUMING, + BRCMF_CFG80211_PM_STATE_SUSPENDED, + BRCMF_CFG80211_PM_STATE_SUSPENDING, +}; + +/** + * enum brcmf_mgmt_tx_status - mgmt frame tx status + * + * @BRCMF_MGMT_TX_ACK: mgmt frame acked + * @BRCMF_MGMT_TX_NOACK: mgmt frame not acked + * @BRCMF_MGMT_TX_OFF_CHAN_COMPLETED: off-channel complete + * @BRCMF_MGMT_TX_SEND_FRAME: mgmt frame tx is in progres + */ +enum brcmf_mgmt_tx_status { + BRCMF_MGMT_TX_ACK, + BRCMF_MGMT_TX_NOACK, + BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + BRCMF_MGMT_TX_SEND_FRAME +}; + /** * struct vif_saved_ie - holds saved IEs for a virtual interface. * @@ -221,6 +253,9 @@ unsigned long sme_state; struct vif_saved_ie saved_ie; struct list_head list; + struct completion mgmt_tx; + unsigned long mgmt_tx_status; + u32 mgmt_tx_id; u16 mgmt_rx_reg; bool mbss; int is_11d; @@ -365,6 +400,8 @@ struct brcmf_cfg80211_wowl wowl; struct brcmf_pno_info *pno; u8 ac_priority[MAX_8021D_PRIO]; + u8 pm_state; + u8 num_softap; }; /** @@ -461,5 +498,6 @@ void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); void brcmf_cfg80211_free_netdev(struct net_device *ndev); - +bool brcmf_is_apmode_operating(struct wiphy *wiphy); +void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev); #endif /* BRCMFMAC_CFG80211_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c 2022-01-06 12:45:53.822318139 -0500 @@ -213,6 +213,24 @@ #define ARMCR4_BSZ_MASK 0x3f #define ARMCR4_BSZ_MULT 8192 +/* Minimum PMU resource mask for 43012C0 */ +#define CY_43012_PMU_MIN_RES_MASK 0xF8BFE77 + +/* PMU STATUS mask for 43012C0 */ +#define CY_43012_PMU_STATUS_MASK 0x1AC + +/* PMU CONTROL EXT mask for 43012C0 */ +#define CY_43012_PMU_CONTROL_EXT_MASK 0x11 + +/* PMU Watchdog Counter Tick value for 43012C0 */ +#define CY_43012_PMU_WATCHDOG_TICK_VAL 0x04 + +/* PMU Watchdog Counter Tick value for 4373 */ +#define CY_4373_PMU_WATCHDOG_TICK_VAL 0x04 + +/* Minimum PMU resource mask for 4373 */ +#define CY_4373_PMU_MIN_RES_MASK 0xFCAFF7F + struct brcmf_core_priv { struct brcmf_core pub; u32 wrapbase; @@ -639,6 +657,7 @@ *srsize = (32 * 1024); break; case BRCM_CC_43430_CHIP_ID: + case CY_CC_43439_CHIP_ID: /* assume sr for now as we can not check * firmware sr capability at this point. */ @@ -726,6 +745,8 @@ case BRCM_CC_4364_CHIP_ID: case CY_CC_4373_CHIP_ID: return 0x160000; + case CY_CC_89459_CHIP_ID: + return ((ci->pub.chiprev < 9) ? 0x180000 : 0x160000); default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; @@ -1203,6 +1224,14 @@ return cc; } +struct brcmf_core *brcmf_chip_get_gci(struct brcmf_chip *pub) +{ + struct brcmf_core *gci; + + gci = brcmf_chip_get_core(pub, BCMA_CORE_GCI); + return gci; +} + bool brcmf_chip_iscoreup(struct brcmf_core *pub) { struct brcmf_core_priv *core; @@ -1244,7 +1273,8 @@ brcmf_chip_resetcore(core, 0, 0, 0); /* disable bank #3 remap for this device */ - if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID || + chip->pub.chip == CY_CC_43439_CHIP_ID) { sr = container_of(core, struct brcmf_core_priv, pub); brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); @@ -1401,10 +1431,12 @@ reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; case BRCM_CC_43430_CHIP_ID: + case CY_CC_43439_CHIP_ID: addr = CORE_CC_REG(base, sr_control1); reg = chip->ops->read32(chip->ctx, addr); return reg != 0; case CY_CC_4373_CHIP_ID: + case CY_CC_89459_CHIP_ID: /* explicitly check SR engine enable bit */ addr = CORE_CC_REG(base, sr_control0); reg = chip->ops->read32(chip->ctx, addr); @@ -1427,3 +1459,151 @@ PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; } } + +void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 base; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + /* SW scratch */ + addr = CORE_CC_REG(base, swscratch); + chip->ops->write32(chip->ctx, addr, 0); + + /* PMU status */ + addr = CORE_CC_REG(base, pmustatus); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_STATUS_MASK); + + /* PMU control ext */ + addr = CORE_CC_REG(base, pmucontrol_ext); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_CONTROL_EXT_MASK); + break; + + default: + brcmf_err("Unsupported chip id\n"); + break; + } +} + +void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 base; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + break; + + default: + brcmf_err("Unsupported chip id\n"); + break; + } +} + +void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 base; + u32 addr; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_gci(pub)->base; + + /* LHL Top Level Power Sequence Control */ + addr = CORE_GCI_REG(base, lhl_top_pwrseq_ctl_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* GPIO Interrupt Enable0 */ + addr = CORE_GCI_REG(base, gpio_int_en_port_adr[0]); + chip->ops->write32(chip->ctx, addr, 0); + + /* GPIO Interrupt Status0 */ + addr = CORE_GCI_REG(base, gpio_int_st_port_adr[0]); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL ARM Timer0 Interrupt Mask */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_intrp_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL ARM Timer0 Interrupt Status */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_st_adr); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL ARM Timer */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL MAC Timer0 Interrupt Mask */ + addr = CORE_GCI_REG(base, lhl_wl_mactim0_intrp_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL MAC Timer0 Interrupt Status */ + addr = CORE_GCI_REG(base, lhl_wl_mactim0_st_adr); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL MAC TimerInt0 */ + addr = CORE_GCI_REG(base, lhl_wl_mactim_int0_adr); + chip->ops->write32(chip->ctx, addr, 0x0); +} + +void brcmf_chip_reset_watchdog(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 base; + u32 addr; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + /* Watchdog res mask */ + addr = CORE_CC_REG(base, watchdog_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + /* PMU watchdog */ + addr = CORE_CC_REG(base, pmuwatchdog); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_WATCHDOG_TICK_VAL); + break; + case CY_CC_4373_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_MIN_RES_MASK); + addr = CORE_CC_REG(base, watchdog_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_MIN_RES_MASK); + addr = CORE_CC_REG(base, pmuwatchdog); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_WATCHDOG_TICK_VAL); + mdelay(100); + break; + default: + break; + } +} + diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h 2022-01-06 12:45:53.822318139 -0500 @@ -8,7 +8,10 @@ #include #define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) + ((base) + offsetof(struct chipcregs, field)) + +#define CORE_GCI_REG(base, field) \ + ((base) + offsetof(struct chipgciregs, field)) /** * struct brcmf_chip - chip level information. @@ -85,5 +88,9 @@ bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); bool brcmf_chip_sr_capable(struct brcmf_chip *pub); char *brcmf_chip_name(u32 chipid, u32 chiprev, char *buf, uint len); +void brcmf_chip_reset_watchdog(struct brcmf_chip *pub); +void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub); +void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub); +void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub); #endif /* BRCMF_AXIDMP_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c 2022-01-06 12:45:53.822318139 -0500 @@ -20,6 +20,9 @@ #include "of.h" #include "firmware.h" #include "chip.h" +#include "defs.h" +#include "fweh.h" +#include MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -67,6 +70,14 @@ module_param_named(iapp, brcmf_iapp_enable, int, 0); MODULE_PARM_DESC(iapp, "Enable partial support for the obsoleted Inter-Access Point Protocol"); +static int brcmf_eap_restrict; +module_param_named(eap_restrict, brcmf_eap_restrict, int, 0400); +MODULE_PARM_DESC(eap_restrict, "Block non-802.1X frames until auth finished"); + +static int brcmf_max_pm; +module_param_named(max_pm, brcmf_max_pm, int, 0); +MODULE_PARM_DESC(max_pm, "Use max power management mode by default"); + #ifdef DEBUG /* always succeed brcmf_bus_started() */ static int brcmf_ignore_probe_fail; @@ -201,8 +212,10 @@ char *clmver; char *ptr; s32 err; + struct eventmsgs_ext *eventmask_msg = NULL; + u8 msglen; - /* retreive mac address */ + /* retrieve mac addresses */ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, sizeof(ifp->mac_addr)); if (err < 0) { @@ -292,6 +305,11 @@ brcmf_dbg(INFO, "CLM version = %s\n", clmver); } + /* set apsta */ + err = brcmf_fil_iovar_int_set(ifp, "apsta", 1); + if (err) + brcmf_info("failed setting apsta, %d\n", err); + /* set mpc */ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); if (err) { @@ -316,6 +334,43 @@ goto done; } + /* Enable event_msg_ext specific to 43012 chip */ + if (bus->chip == CY_CC_43012_CHIP_ID) { + /* Program event_msg_ext to support event larger than 128 */ + msglen = (roundup(BRCMF_E_LAST, NBBY) / NBBY) + + EVENTMSGS_EXT_STRUCT_SIZE; + /* Allocate buffer for eventmask_msg */ + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) { + err = -ENOMEM; + goto done; + } + + /* Read the current programmed event_msgs_ext */ + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->len = roundup(BRCMF_E_LAST, NBBY) / NBBY; + err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext", + eventmask_msg, + msglen); + + /* Enable ULP event */ + brcmf_dbg(EVENT, "enable event ULP\n"); + setbit(eventmask_msg->mask, BRCMF_E_ULP); + + /* Write updated Event mask */ + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = EVENTMSGS_SET_MASK; + eventmask_msg->len = (roundup(BRCMF_E_LAST, NBBY) / NBBY); + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", + eventmask_msg, msglen); + if (err) { + brcmf_err("Set event_msgs_ext error (%d)\n", err); + kfree(eventmask_msg); + goto done; + } + kfree(eventmask_msg); + } /* Setup default scan channel time */ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, BRCMF_DEFAULT_SCAN_CHANNEL_TIME); @@ -336,6 +391,18 @@ /* Enable tx beamforming, errors can be ignored (not supported) */ (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1); + + /* add unicast packet filter */ + err = brcmf_pktfilter_add_remove(ifp->ndev, + BRCMF_UNICAST_FILTER_NUM, true); + if (err == -BRCMF_FW_UNSUPPORTED) { + /* FW not support can be ignored */ + err = 0; + goto done; + } else if (err) { + bphy_err(drvr, "Add unicast filter error (%d)\n", err); + } + done: return err; } @@ -407,12 +474,14 @@ if (!settings) return NULL; - /* start by using the module paramaters */ + /* start by using the module parameters */ settings->p2p_enable = !!brcmf_p2p_enable; settings->feature_disable = brcmf_feature_disable; settings->fcmode = brcmf_fcmode; settings->roamoff = !!brcmf_roamoff; settings->iapp = !!brcmf_iapp_enable; + settings->eap_restrict = !!brcmf_eap_restrict; + settings->default_pm = !!brcmf_max_pm ? PM_MAX : PM_FAST; #ifdef DEBUG settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; #endif @@ -460,7 +529,7 @@ brcmfmac_pdata = dev_get_platdata(&pdev->dev); - if (brcmfmac_pdata->power_on) + if (brcmfmac_pdata && brcmfmac_pdata->power_on) brcmfmac_pdata->power_on(); return 0; @@ -492,7 +561,7 @@ if (err == -ENODEV) brcmf_dbg(INFO, "No platform data available.\n"); - /* Initialize global module paramaters */ + /* Initialize global module parameters */ brcmf_mp_attach(); /* Continue the initialization by registering the different busses */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h 2022-01-06 12:45:53.822318139 -0500 @@ -37,6 +37,8 @@ * @feature_disable: Feature_disable bitmask. * @fcmode: FWS flow control. * @roamoff: Firmware roaming off? + * @eap_restrict: Not allow data tx/rx until 802.1X auth succeeds + * @default_pm: default power management (PM) mode. * @ignore_probe_fail: Ignore probe failure. * @country_codes: If available, pointer to struct for translating country codes * @bus: Bus specific platform data. Only SDIO at the mmoment. @@ -47,6 +49,8 @@ int fcmode; bool roamoff; bool iapp; + bool eap_restrict; + int default_pm; bool ignore_probe_fail; struct brcmfmac_pd_cc *country_codes; const char *board_type; diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c 2022-01-06 12:45:53.822318139 -0500 @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,14 @@ s8 rxpwr[4]; } __packed; +#define BRCMF_IF_STA_LIST_LOCK_INIT(ifp) spin_lock_init(&(ifp)->sta_list_lock) +#define BRCMF_IF_STA_LIST_LOCK(ifp, flags) \ + spin_lock_irqsave(&(ifp)->sta_list_lock, (flags)) +#define BRCMF_IF_STA_LIST_UNLOCK(ifp, flags) \ + spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags)) + +#define BRCMF_STA_NULL ((struct brcmf_sta *)NULL) + char *brcmf_ifname(struct brcmf_if *ifp) { if (!ifp) @@ -96,6 +105,11 @@ s32 err; u32 mode; + if (enable && brcmf_is_apmode_operating(ifp->drvr->wiphy)) { + brcmf_dbg(TRACE, "Skip ARP/ND offload enable when soft AP is running\n"); + return; + } + if (enable) mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; else @@ -576,6 +590,7 @@ static const struct ethtool_ops brcmf_ethtool_ops = { .get_drvinfo = brcmf_ethtool_get_drvinfo, + .get_ts_info = ethtool_op_get_ts_info, }; static int brcmf_netdev_stop(struct net_device *ndev) @@ -898,7 +913,9 @@ init_waitqueue_head(&ifp->pend_8021x_wait); spin_lock_init(&ifp->netif_stop_lock); - + BRCMF_IF_STA_LIST_LOCK_INIT(ifp); + /* Initialize STA info list */ + INIT_LIST_HEAD(&ifp->sta_list); if (mac_addr != NULL) memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); @@ -1130,6 +1147,15 @@ } #endif + +int brcmf_fwlog_attach(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + return brcmf_debug_fwlog_init(drvr); +} + static int brcmf_revinfo_read(struct seq_file *s, void *data) { struct brcmf_bus *bus_if = dev_get_drvdata(s->private); @@ -1319,7 +1345,7 @@ return 0; } -int brcmf_attach(struct device *dev) +int brcmf_attach(struct device *dev, bool start_bus) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; @@ -1336,6 +1362,7 @@ /* Link to bus module */ drvr->hdrlen = 0; + drvr->req_mpc = 1; /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); if (ret != 0) { @@ -1350,10 +1377,13 @@ /* attach firmware event handler */ brcmf_fweh_attach(drvr); - ret = brcmf_bus_started(drvr, drvr->ops); - if (ret != 0) { - bphy_err(drvr, "dongle is not responding: err=%d\n", ret); - goto fail; + if (start_bus) { + ret = brcmf_bus_started(drvr, drvr->ops); + if (ret != 0) { + bphy_err(drvr, "dongle is not responding: err=%d\n", + ret); + goto fail; + } } return 0; @@ -1403,7 +1433,8 @@ brcmf_dev_coredump(dev); - schedule_work(&drvr->bus_reset); + if (drvr->bus_reset.func) + schedule_work(&drvr->bus_reset); } void brcmf_detach(struct device *dev) @@ -1485,8 +1516,10 @@ !brcmf_get_pend_8021x_cnt(ifp), MAX_WAIT_FOR_8021X_TX); - if (!err) + if (!err) { bphy_err(drvr, "Timed out waiting for no pending 802.1x packets\n"); + atomic_set(&ifp->pend_8021x_cnt, 0); + } return !err; } @@ -1518,34 +1551,307 @@ } } -int __init brcmf_core_init(void) +static void brcmf_driver_register(struct work_struct *work) { - int err; - - err = brcmf_sdio_register(); - if (err) - return err; +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_register(); +#endif +#ifdef CONFIG_BRCMFMAC_USB + brcmf_usb_register(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_register(); +#endif +} +static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); - err = brcmf_usb_register(); - if (err) - goto error_usb_register; +int __init brcmf_core_init(void) +{ + if (!schedule_work(&brcmf_driver_work)) + return -EBUSY; - err = brcmf_pcie_register(); - if (err) - goto error_pcie_register; return 0; - -error_pcie_register: - brcmf_usb_exit(); -error_usb_register: - brcmf_sdio_exit(); - return err; } void __exit brcmf_core_exit(void) { + cancel_work_sync(&brcmf_driver_work); + +#ifdef CONFIG_BRCMFMAC_SDIO brcmf_sdio_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_USB brcmf_usb_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE brcmf_pcie_exit(); +#endif } +int +brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_pkt_filter_le *pkt_filter; + int filter_fixed_len = offsetof(struct brcmf_pkt_filter_le, u); + int pattern_fixed_len = offsetof(struct brcmf_pkt_filter_pattern_le, + mask_and_pattern); + u16 mask_and_pattern[MAX_PKTFILTER_PATTERN_SIZE]; + int buflen = 0; + int ret = 0; + + brcmf_dbg(INFO, "%s packet filter number %d\n", + (add ? "add" : "remove"), filter_num); + + pkt_filter = kzalloc(sizeof(*pkt_filter) + + (MAX_PKTFILTER_PATTERN_SIZE * 2), GFP_ATOMIC); + if (!pkt_filter) + return -ENOMEM; + + switch (filter_num) { + case BRCMF_UNICAST_FILTER_NUM: + pkt_filter->id = 100; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 1; + mask_and_pattern[0] = 0x0001; + break; + case BRCMF_BROADCAST_FILTER_NUM: + //filter_pattern = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + pkt_filter->id = 101; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 6; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0xFFFF; + mask_and_pattern[4] = 0xFFFF; + mask_and_pattern[5] = 0xFFFF; + break; + case BRCMF_MULTICAST4_FILTER_NUM: + //filter_pattern = "102 0 0 0 0xFFFFFF 0x01005E"; + pkt_filter->id = 102; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 3; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x01FF; + mask_and_pattern[2] = 0x5E00; + break; + case BRCMF_MULTICAST6_FILTER_NUM: + //filter_pattern = "103 0 0 0 0xFFFF 0x3333"; + pkt_filter->id = 103; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 2; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x3333; + break; + case BRCMF_MDNS_FILTER_NUM: + //filter_pattern = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; + pkt_filter->id = 104; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 6; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0x0001; + mask_and_pattern[4] = 0x005E; + mask_and_pattern[5] = 0xFB00; + break; + case BRCMF_ARP_FILTER_NUM: + //filter_pattern = "105 0 0 12 0xFFFF 0x0806"; + pkt_filter->id = 105; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 12; + pkt_filter->u.pattern.size_bytes = 2; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x0608; + break; + case BRCMF_BROADCAST_ARP_FILTER_NUM: + //filter_pattern = "106 0 0 0 + //0xFFFFFFFFFFFF0000000000000806 + //0xFFFFFFFFFFFF0000000000000806"; + pkt_filter->id = 106; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 14; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0x0000; + mask_and_pattern[4] = 0x0000; + mask_and_pattern[5] = 0x0000; + mask_and_pattern[6] = 0x0608; + mask_and_pattern[7] = 0xFFFF; + mask_and_pattern[8] = 0xFFFF; + mask_and_pattern[9] = 0xFFFF; + mask_and_pattern[10] = 0x0000; + mask_and_pattern[11] = 0x0000; + mask_and_pattern[12] = 0x0000; + mask_and_pattern[13] = 0x0608; + break; + default: + ret = -EINVAL; + goto failed; + } + memcpy(pkt_filter->u.pattern.mask_and_pattern, mask_and_pattern, + pkt_filter->u.pattern.size_bytes * 2); + buflen = filter_fixed_len + pattern_fixed_len + + pkt_filter->u.pattern.size_bytes * 2; + + if (add) { + /* Add filter */ + ifp->fwil_fwerr = true; + ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", + pkt_filter, buflen); + ifp->fwil_fwerr = false; + if (ret) + goto failed; + drvr->pkt_filter[filter_num].id = pkt_filter->id; + drvr->pkt_filter[filter_num].enable = 0; + + } else { + /* Delete filter */ + ifp->fwil_fwerr = true; + ret = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete", + pkt_filter->id); + ifp->fwil_fwerr = false; + if (ret == -BRCMF_FW_BADARG) + ret = 0; + if (ret) + goto failed; + + drvr->pkt_filter[filter_num].id = 0; + drvr->pkt_filter[filter_num].enable = 0; + } +failed: + if (ret) + brcmf_err("%s packet filter failed, ret=%d\n", + (add ? "add" : "remove"), ret); + + kfree(pkt_filter); + return ret; +} + +int brcmf_pktfilter_enable(struct net_device *ndev, bool enable) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + int ret = 0; + int idx = 0; + + for (idx = 0; idx < MAX_PKT_FILTER_COUNT; ++idx) { + if (drvr->pkt_filter[idx].id != 0) { + drvr->pkt_filter[idx].enable = enable; + ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", + &drvr->pkt_filter[idx], + sizeof(struct brcmf_pkt_filter_enable_le)); + if (ret) { + brcmf_err("%s packet filter id(%d) failed, ret=%d\n", + (enable ? "enable" : "disable"), + drvr->pkt_filter[idx].id, ret); + } + } + } + return ret; +} + +/** Find STA with MAC address ea in an interface's STA list. */ +struct brcmf_sta * +brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta; + unsigned long flags; + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + list_for_each_entry(sta, &ifp->sta_list, list) { + if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) { + brcmf_dbg(INFO, "Found STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n", + sta->ea.octet[0], sta->ea.octet[1], + sta->ea.octet[2], sta->ea.octet[3], + sta->ea.octet[4], sta->ea.octet[5]); + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + return sta; + } + } + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + + return BRCMF_STA_NULL; +} + +/** Add STA into the interface's STA list. */ +struct brcmf_sta * +brcmf_add_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta; + unsigned long flags; + + sta = kzalloc(sizeof(*sta), GFP_KERNEL); + if (sta == BRCMF_STA_NULL) { + brcmf_err("Alloc failed\n"); + return BRCMF_STA_NULL; + } + memcpy(sta->ea.octet, ea, ETH_ALEN); + brcmf_dbg(INFO, "Add STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n", + sta->ea.octet[0], sta->ea.octet[1], + sta->ea.octet[2], sta->ea.octet[3], + sta->ea.octet[4], sta->ea.octet[5]); + + /* link the sta and the dhd interface */ + sta->ifp = ifp; + INIT_LIST_HEAD(&sta->list); + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + + list_add_tail(&sta->list, &ifp->sta_list); + + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + return sta; +} + +/** Delete STA from the interface's STA list. */ +void +brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta, *next; + unsigned long flags; + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { + if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) { + brcmf_dbg(INFO, "del STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x from sta list\n", + ea[0], ea[1], ea[2], ea[3], + ea[4], ea[5]); + list_del(&sta->list); + kfree(sta); + } + } + + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); +} + +/** Add STA if it doesn't exist. Not reentrant. */ +struct brcmf_sta* +brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta = NULL; + + sta = brcmf_find_sta(ifp, ea); + + if (!sta) { + /* Add entry */ + sta = brcmf_add_sta(ifp, ea); + } + return sta; +} diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h 2022-01-06 12:45:53.822318139 -0500 @@ -12,6 +12,7 @@ #include #include "fweh.h" +#include "fwil_types.h" #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 @@ -123,6 +124,7 @@ u32 feat_flags; u32 chip_quirks; + int req_mpc; struct brcmf_rev_info revinfo; #ifdef DEBUG @@ -136,6 +138,9 @@ struct work_struct bus_reset; u8 clmver[BRCMF_DCMD_SMLEN]; + struct brcmf_pkt_filter_enable_le pkt_filter[MAX_PKT_FILTER_COUNT]; + u8 sta_mac_idx; + }; /* forward declarations */ @@ -185,6 +190,7 @@ struct brcmf_fws_mac_descriptor *fws_desc; int ifidx; s32 bsscfgidx; + bool isap; u8 mac_addr[ETH_ALEN]; u8 netif_stop; spinlock_t netif_stop_lock; @@ -193,6 +199,20 @@ struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES]; u8 ipv6addr_idx; bool fwil_fwerr; + struct list_head sta_list; /* sll of associated stations */ + spinlock_t sta_list_lock; + bool fmac_pkt_fwd_en; +}; + +struct ether_addr { + u8 octet[ETH_ALEN]; +}; + +/** Per STA params. A list of dhd_sta objects are managed in dhd_if */ +struct brcmf_sta { + void *ifp; /* associated brcm_if */ + struct ether_addr ea; /* stations ethernet mac address */ + struct list_head list; /* link into brcmf_if::sta_list */ }; int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); @@ -215,5 +235,10 @@ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); int __init brcmf_core_init(void); void __exit brcmf_core_exit(void); - +int brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, + bool add); +int brcmf_pktfilter_enable(struct net_device *ndev, bool enable); +void brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea); +struct brcmf_sta *brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea); +struct brcmf_sta *brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea); #endif /* BRCMFMAC_CORE_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c 2022-01-06 12:45:53.822318139 -0500 @@ -14,6 +14,82 @@ #include "fweh.h" #include "debug.h" +static int +brcmf_debug_msgtrace_seqchk(u32 *prev, u32 cur) +{ + if ((cur == 0 && *prev == 0xFFFFFFFF) || ((cur - *prev) == 1)) { + goto done; + } else if (cur == *prev) { + brcmf_dbg(FWCON, "duplicate trace\n"); + return -1; + } else if (cur > *prev) { + brcmf_dbg(FWCON, "lost %d packets\n", cur - *prev); + } else { + brcmf_dbg(FWCON, "seq out of order, host %d, dongle %d\n", + *prev, cur); + } +done: + *prev = cur; + return 0; +} + +static int +brcmf_debug_msg_parser(void *event_data) +{ + int err = 0; + struct msgtrace_hdr *hdr; + char *data, *s; + static u32 seqnum_prev; + + hdr = (struct msgtrace_hdr *)event_data; + data = (char *)event_data + MSGTRACE_HDRLEN; + + /* There are 2 bytes available at the end of data */ + data[ntohs(hdr->len)] = '\0'; + + if (ntohl(hdr->discarded_bytes) || ntohl(hdr->discarded_printf)) { + brcmf_dbg(FWCON, "Discarded_bytes %d discarded_printf %d\n", + ntohl(hdr->discarded_bytes), + ntohl(hdr->discarded_printf)); + } + + err = brcmf_debug_msgtrace_seqchk(&seqnum_prev, ntohl(hdr->seqnum)); + if (err) + return err; + + while (*data != '\0' && (s = strstr(data, "\n")) != NULL) { + *s = '\0'; + brcmf_dbg(FWCON, "CONSOLE: %s\n", data); + data = s + 1; + } + if (*data) + brcmf_dbg(FWCON, "CONSOLE: %s", data); + + return err; +} + +static int +brcmf_debug_trace_parser(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *event_data) +{ + int err = 0; + struct msgtrace_hdr *hdr; + + hdr = (struct msgtrace_hdr *)event_data; + if (hdr->version != MSGTRACE_VERSION) { + brcmf_dbg(FWCON, "trace version mismatch host %d dngl %d\n", + MSGTRACE_VERSION, hdr->version); + err = -EPROTO; + return err; + } + + if (hdr->trace_type == MSGTRACE_HDR_TYPE_MSG) + err = brcmf_debug_msg_parser(event_data); + + return err; +} + int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, size_t len) { @@ -42,6 +118,13 @@ return 0; } + +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr) +{ + return brcmf_fweh_register(drvr, BRCMF_E_TRACE, + brcmf_debug_trace_parser); +} + struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { return drvr->wiphy->debugfsdir; diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h 2022-01-06 12:45:53.822318139 -0500 @@ -29,6 +29,7 @@ #define BRCMF_MSGBUF_VAL 0x00040000 #define BRCMF_PCIE_VAL 0x00080000 #define BRCMF_FWCON_VAL 0x00100000 +#define BRCMF_ULP_VAL 0x00200000 /* set default print format */ #undef pr_fmt @@ -103,6 +104,10 @@ #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ +#define MSGTRACE_VERSION 1 +#define MSGTRACE_HDR_TYPE_MSG 0 +#define MSGTRACE_HDR_TYPE_LOG 1 + #define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ do { \ trace_brcmf_hexdump((void *)data, len); \ @@ -120,6 +125,7 @@ int (*read_fn)(struct seq_file *seq, void *data)); int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, size_t len); +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr); #else static inline struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { @@ -135,6 +141,25 @@ { return 0; } + +static inline +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr) +{ + return 0; +} #endif +/* Message trace header */ +struct msgtrace_hdr { + u8 version; + u8 trace_type; + u16 len; /* Len of the trace */ + u32 seqnum; /* Sequence number of message */ + /* Number of discarded bytes because of trace overflow */ + u32 discarded_bytes; + /* Number of discarded printf because of trace overflow */ + u32 discarded_printf; +}; + +#define MSGTRACE_HDRLEN sizeof(struct msgtrace_hdr) #endif /* BRCMFMAC_DEBUG_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c 2022-01-06 12:45:53.822318139 -0500 @@ -40,18 +40,6 @@ BRCM_CC_43340_CHIP_ID, 2, "pov-tab-p1006w-data" }; -static const struct brcmf_dmi_data predia_basic_data = { - BRCM_CC_43341_CHIP_ID, 2, "predia-basic" -}; - -/* Note the Voyo winpad A15 tablet uses the same Ampak AP6330 module, with the - * exact same nvram file as the Prowise-PT301 tablet. Since the nvram for the - * Prowise-PT301 is already in linux-firmware we just point to that here. - */ -static const struct brcmf_dmi_data voyo_winpad_a15_data = { - BRCM_CC_4330_CHIP_ID, 4, "Prowise-PT301" -}; - static const struct dmi_system_id dmi_platform_data[] = { { /* ACEPC T8 Cherry Trail Z8350 mini PC */ @@ -76,16 +64,6 @@ .driver_data = (void *)&acepc_t8_data, }, { - /* Cyberbook T116 rugged tablet */ - .matches = { - DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), - DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"), - }, - /* The factory image nvram file is identical to the ACEPC T8 one */ - .driver_data = (void *)&acepc_t8_data, - }, - { /* Match for the GPDwin which unfortunately uses somewhat * generic dmi strings, which is why we test for 4 strings. * Comparing against 23 other byt/cht boards, board_vendor @@ -133,26 +111,6 @@ }, .driver_data = (void *)&pov_tab_p1006w_data, }, - { - /* Predia Basic tablet (+ with keyboard dock) */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), - /* Mx.WT107.KUBNGEA02 with the version-nr dropped */ - DMI_MATCH(DMI_BIOS_VERSION, "Mx.WT107.KUBNGEA"), - }, - .driver_data = (void *)&predia_basic_data, - }, - { - /* Voyo winpad A15 tablet */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are too generic, also match on BIOS date */ - DMI_MATCH(DMI_BIOS_DATE, "11/20/2014"), - }, - .driver_data = (void *)&voyo_winpad_a15_data, - }, {} }; diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c 2022-01-06 12:45:53.822318139 -0500 @@ -16,7 +16,6 @@ #include "feature.h" #include "common.h" -#define BRCMF_FW_UNSUPPORTED 23 /* * expand feature list to array of feature strings. @@ -41,8 +40,9 @@ { BRCMF_FEAT_MONITOR_FLAG, "rtap" }, { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" }, { BRCMF_FEAT_DOT11H, "802.11h" }, - { BRCMF_FEAT_SAE, "sae" }, + { BRCMF_FEAT_SAE, "sae " }, { BRCMF_FEAT_FWAUTH, "idauth" }, + { BRCMF_FEAT_SAE_EXT, "sae_ext " }, }; #ifdef DEBUG @@ -143,7 +143,7 @@ ifp->fwil_fwerr = true; err = brcmf_fil_iovar_int_get(ifp, name, &data); - if (err == 0) { + if (err != -BRCMF_FW_UNSUPPORTED) { brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); ifp->drvr->feat_flags |= BIT(id); } else { @@ -248,7 +248,8 @@ brcmf_feat_firmware_capabilities(ifp); memset(&gscan_cfg, 0, sizeof(gscan_cfg)); if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID && - drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID) + drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID && + drvr->bus_if->chip != CY_CC_43439_CHIP_ID) brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg", &gscan_cfg, sizeof(gscan_cfg)); @@ -279,6 +280,7 @@ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss"); pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h 2022-01-06 12:45:53.822318139 -0500 @@ -29,6 +29,8 @@ * DOT11H: firmware supports 802.11h * SAE: simultaneous authentication of equals * FWAUTH: Firmware authenticator + * DUMP_OBSS: Firmware has capable to dump obss info to support ACS + * SAE_EXT: SAE be handled by userspace supplicant */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -51,7 +53,9 @@ BRCMF_FEAT_DEF(MONITOR_FMT_HW_RX_HDR) \ BRCMF_FEAT_DEF(DOT11H) \ BRCMF_FEAT_DEF(SAE) \ - BRCMF_FEAT_DEF(FWAUTH) + BRCMF_FEAT_DEF(FWAUTH) \ + BRCMF_FEAT_DEF(DUMP_OBSS) \ + BRCMF_FEAT_DEF(SAE_EXT) /* * Quirks: diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c 2022-01-06 12:45:53.822318139 -0500 @@ -613,8 +613,11 @@ strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN); strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN); - ret = request_firmware(fw, alt_path, fwctx->dev); - if (ret == 0) + ret = request_firmware_direct(fw, alt_path, fwctx->dev); + if (ret) + brcmf_info("no board-specific nvram available (ret=%d), device will use %s\n", + ret, cur->path); + else return ret; } diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h 2022-01-06 12:45:53.822318139 -0500 @@ -11,6 +11,8 @@ #define BRCMF_FW_DEFAULT_PATH "brcm/" +#define CY_FW_DEFAULT_PATH "cypress/" + /** * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware * filename and nvram filename. Each bus type implementation should create @@ -32,6 +34,11 @@ BRCMF_FW_DEFAULT_PATH fw_base; \ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw_base ".bin") +#define CY_FW_DEF(fw_name, fw_base) \ +static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ + CY_FW_DEFAULT_PATH fw_base; \ +MODULE_FIRMWARE(CY_FW_DEFAULT_PATH fw_base ".bin") + #define BRCMF_FW_ENTRY(chipid, mask, name) \ { chipid, mask, BRCM_ ## name ## _FIRMWARE_BASENAME } diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c 2022-01-06 12:45:53.822318139 -0500 @@ -419,7 +419,6 @@ flowid = flow->hash[i].flowid; if (flow->rings[flowid]->status != RING_OPEN) continue; - flow->rings[flowid]->status = RING_CLOSING; brcmf_msgbuf_delete_flowring(drvr, flowid); } } @@ -458,10 +457,8 @@ if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && (hash[i].ifidx == ifidx)) { flowid = flow->hash[i].flowid; - if (flow->rings[flowid]->status == RING_OPEN) { - flow->rings[flowid]->status = RING_CLOSING; + if (flow->rings[flowid]->status == RING_OPEN) brcmf_msgbuf_delete_flowring(drvr, flowid); - } } } diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c 2022-01-06 12:45:53.822318139 -0500 @@ -355,26 +355,42 @@ { struct brcmf_pub *drvr = ifp->drvr; int i, err; - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + struct eventmsgs_ext *eventmask_msg; + u32 msglen; + + msglen = EVENTMSGS_EXT_STRUCT_SIZE + BRCMF_EVENTING_MASK_LEN; + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) + return -ENOMEM; - memset(eventmask, 0, sizeof(eventmask)); for (i = 0; i < BRCMF_E_LAST; i++) { if (ifp->drvr->fweh.evt_handler[i]) { brcmf_dbg(EVENT, "enable event %s\n", brcmf_fweh_event_name(i)); - setbit(eventmask, i); + setbit(eventmask_msg->mask, i); } } /* want to handle IF event as well */ brcmf_dbg(EVENT, "enable event IF\n"); - setbit(eventmask, BRCMF_E_IF); + setbit(eventmask_msg->mask, BRCMF_E_IF); + + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = EVENTMSGS_SET_MASK; + eventmask_msg->len = BRCMF_EVENTING_MASK_LEN; + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmask_msg, + msglen); + if (!err) + goto end; - err = brcmf_fil_iovar_data_set(ifp, "event_msgs", - eventmask, BRCMF_EVENTING_MASK_LEN); + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask_msg->mask, + BRCMF_EVENTING_MASK_LEN); if (err) bphy_err(drvr, "Set event_msgs error (%d)\n", err); +end: + kfree(eventmask_msg); return err; } diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h 2022-01-06 12:45:53.822318139 -0500 @@ -90,7 +90,13 @@ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + BRCMF_ENUM_DEF(PHY_TEMP, 111) \ + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ + BRCMF_ENUM_DEF(ULP, 146) \ + BRCMF_ENUM_DEF(EXT_AUTH_REQ, 187) \ + BRCMF_ENUM_DEF(EXT_AUTH_FRAME_RX, 188) \ + BRCMF_ENUM_DEF(MGMT_FRAME_TXSTATUS, 189) \ + BRCMF_ENUM_DEF(MGMT_FRAME_OFF_CHAN_COMPLETE, 190) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -102,7 +108,7 @@ * minimum length check in device firmware so it is * hard-coded here. */ - BRCMF_E_LAST = 139 + BRCMF_E_LAST = 191 }; #undef BRCMF_ENUM_DEF @@ -283,6 +289,28 @@ u8 role; }; +enum event_msgs_ext_command { + EVENTMSGS_NONE = 0, + EVENTMSGS_SET_BIT = 1, + EVENTMSGS_RESET_BIT = 2, + EVENTMSGS_SET_MASK = 3 +}; + +#define EVENTMSGS_VER 1 +#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(struct eventmsgs_ext, mask[0]) + +/* len- for SET it would be mask size from the application to the firmware */ +/* for GET it would be actual firmware mask size */ +/* maxgetsize - is only used for GET. indicate max mask size that the */ +/* application can read from the firmware */ +struct eventmsgs_ext { + u8 ver; + u8 command; + u8 len; + u8 maxgetsize; + u8 mask[1]; +}; + typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, const struct brcmf_event_msg *evtmsg, void *data); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h 2022-01-06 12:45:53.822318139 -0500 @@ -71,6 +71,7 @@ #define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 #define BRCMF_C_SET_ASSOC_PREFER 205 #define BRCMF_C_GET_VALID_CHANNELS 217 +#define BRCMF_C_GET_FAKEFRAG 218 #define BRCMF_C_SET_FAKEFRAG 219 #define BRCMF_C_GET_KEY_PRIMARY 235 #define BRCMF_C_SET_KEY_PRIMARY 236 @@ -79,6 +80,9 @@ #define BRCMF_C_SET_VAR 263 #define BRCMF_C_SET_WSEC_PMK 268 +#define BRCMF_FW_BADARG 2 +#define BRCMF_FW_UNSUPPORTED 23 + s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h 2022-01-06 12:45:53.822318139 -0500 @@ -135,9 +135,22 @@ /* Link Down indication in WoWL mode: */ #define BRCMF_WOWL_LINKDOWN (1 << 31) -#define BRCMF_WOWL_MAXPATTERNS 8 +#define BRCMF_WOWL_MAXPATTERNS 16 #define BRCMF_WOWL_MAXPATTERNSIZE 128 +enum { + BRCMF_UNICAST_FILTER_NUM = 0, + BRCMF_BROADCAST_FILTER_NUM, + BRCMF_MULTICAST4_FILTER_NUM, + BRCMF_MULTICAST6_FILTER_NUM, + BRCMF_MDNS_FILTER_NUM, + BRCMF_ARP_FILTER_NUM, + BRCMF_BROADCAST_ARP_FILTER_NUM, + MAX_PKT_FILTER_COUNT +}; + +#define MAX_PKTFILTER_PATTERN_SIZE 16 + #define BRCMF_COUNTRY_BUF_SZ 4 #define BRCMF_ANT_MAX 4 @@ -169,6 +182,11 @@ #define BRCMF_HE_CAP_MCS_MAP_NSS_MAX 8 +#define BRCMF_EXTAUTH_START 1 +#define BRCMF_EXTAUTH_ABORT 2 +#define BRCMF_EXTAUTH_FAIL 3 +#define BRCMF_EXTAUTH_SUCCESS 4 + /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each * ioctl. It is relatively small because firmware has small maximum size input * playload restriction for ioctls. @@ -531,6 +549,47 @@ u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN]; }; +/** + * struct brcmf_auth_req_status_le - external auth request and status update + * + * @flags: flags for external auth status + * @peer_mac: peer MAC address + * @ssid_len: length of ssid + * @ssid: ssid characters + */ +struct brcmf_auth_req_status_le { + __le16 flags; + u8 peer_mac[ETH_ALEN]; + __le32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 pmkid[WLAN_PMKID_LEN]; +}; + +/** + * struct brcmf_mf_params_le - management frame parameters for mgmt_frame iovar + * + * @version: version of the iovar + * @dwell_time: dwell duration in ms + * @len: length of frame data + * @frame_control: frame control + * @channel: channel + * @da: peer MAC address + * @bssid: BSS network identifier + * @packet_id: packet identifier + * @data: frame data + */ +struct brcmf_mf_params_le { + __le32 version; + __le32 dwell_time; + __le16 len; + __le16 frame_control; + __le16 channel; + u8 da[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le32 packet_id; + u8 data[1]; +}; + /* Used to get specific STA parameters */ struct brcmf_scb_val_le { __le32 val; diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c 2022-01-06 12:45:53.822318139 -0500 @@ -502,6 +502,9 @@ bool creditmap_received; u8 mode; bool avoid_queueing; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + int fifo_init_credit[BRCMF_FWS_FIFO_COUNT]; +#endif }; #define BRCMF_FWS_TLV_DEF(name, id, len) \ @@ -621,7 +624,6 @@ static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, int ifidx) { - struct brcmf_fws_hanger_item *hi; bool (*matchfn)(struct sk_buff *, void *) = NULL; struct sk_buff *skb; int prec; @@ -633,9 +635,6 @@ skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); while (skb) { hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - hi = &fws->hanger.items[hslot]; - WARN_ON(skb != hi->pkt); - hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); brcmu_pkt_buf_free_skb(skb); @@ -1617,9 +1616,13 @@ fws->fifo_credit_map |= 1 << i; else fws->fifo_credit_map &= ~(1 << i); + WARN_ONCE(fws->fifo_credit[i] < 0, "fifo_credit[%d] is negative(%d)\n", i, fws->fifo_credit[i]); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + fws->fifo_init_credit[i] = fws->fifo_credit[i]; +#endif } brcmf_fws_schedule_deq(fws); brcmf_fws_unlock(fws); @@ -2197,6 +2200,38 @@ brcmf_fws_unlock(fws); } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) +static bool brcmf_fws_ismultistream(struct brcmf_fws_info *fws) +{ + bool ret = false; + u8 credit_usage = 0; + + /* Check only for BE, VI and VO traffic */ + u32 delay_map = fws->fifo_delay_map & + ((1 << BRCMF_FWS_FIFO_AC_BE) | + (1 << BRCMF_FWS_FIFO_AC_VI) | + (1 << BRCMF_FWS_FIFO_AC_VO)); + + if (hweight_long(delay_map) > 1) { + ret = true; + } else { + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_BE] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_BE]) + credit_usage++; + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VI] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VI]) + credit_usage++; + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VO] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VO]) + credit_usage++; + + if (credit_usage > 1) + ret = true; + } + return ret; +} +#endif + static void brcmf_fws_dequeue_worker(struct work_struct *worker) { struct brcmf_fws_info *fws; @@ -2210,6 +2245,13 @@ fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); drvr = fws->drvr; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + if (brcmf_fws_ismultistream(fws)) + drvr->bus_if->allow_skborphan = false; + else + drvr->bus_if->allow_skborphan = true; +#endif + brcmf_fws_lock(fws); for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; fifo--) { @@ -2475,7 +2517,8 @@ return fws->fcmode != BRCMF_FWS_FCMODE_NONE; } -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) +void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, + bool success) { u32 hslot; @@ -2483,11 +2526,13 @@ brcmu_pkt_buf_free_skb(skb); return; } - brcmf_fws_lock(fws); - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0, - 1); - brcmf_fws_unlock(fws); + if (!success) { + brcmf_fws_lock(fws); + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, + hslot, 0, 0, 1); + brcmf_fws_unlock(fws); + } } void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h 2022-01-06 12:45:53.826318157 -0500 @@ -40,7 +40,8 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp); void brcmf_fws_add_interface(struct brcmf_if *ifp); void brcmf_fws_del_interface(struct brcmf_if *ifp); -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); +void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, + bool success); void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -48,3 +48,10 @@ IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to use the driver for an PCIE wireless card. +config BRCMFMAC_BT_SHARED_SDIO + bool "FMAC shares SDIO bus to Bluetooth" + depends on BRCMFMAC_SDIO + default n + help + This option enables the feautre of sharing the SDIO bus interface + between Cypress BT and WiFi host drivers. diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile 2022-01-06 12:45:53.822318139 -0500 @@ -46,3 +46,5 @@ of.o brcmfmac-$(CONFIG_DMI) += \ dmi.o +brcmfmac-${CONFIG_BRCMFMAC_BT_SHARED_SDIO} += \ + bt_shared_sdio.o diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c 2022-01-06 12:45:53.826318157 -0500 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ #include "flowring.h" #include "bus.h" #include "tracepoint.h" +#include "pcie.h" #define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -47,6 +49,8 @@ #define MSGBUF_TYPE_RX_CMPLT 0x12 #define MSGBUF_TYPE_LPBK_DMAXFER 0x13 #define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 +#define MSGBUF_TYPE_H2D_MAILBOX_DATA 0x23 +#define MSGBUF_TYPE_D2H_MAILBOX_DATA 0x24 #define NR_TX_PKTIDS 2048 #define NR_RX_PKTIDS 1024 @@ -71,6 +75,7 @@ #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 #define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48 +#define BRCMF_MAX_TXSTATUS_WAIT_RETRIES 10 struct msgbuf_common_hdr { u8 msgtype; @@ -103,6 +108,12 @@ __le32 rsvd0; }; +struct msgbuf_h2d_mbdata { + struct msgbuf_common_hdr msg; + __le32 mbdata; + __le16 rsvd0[7]; +}; + struct msgbuf_rx_bufpost { struct msgbuf_common_hdr msg; __le16 metadata_buf_len; @@ -217,6 +228,13 @@ __le32 rsvd0[3]; }; +struct msgbuf_d2h_mailbox_data { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 mbdata; + __le32 rsvd0[2]; +} d2h_mailbox_data_t; + struct brcmf_msgbuf_work_item { struct list_head queue; u32 flowid; @@ -289,6 +307,8 @@ }; static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); +static void brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf, + void *buf); static struct brcmf_msgbuf_pktids * @@ -423,6 +443,34 @@ msgbuf->tx_pktids); } +int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + struct msgbuf_h2d_mbdata *h2d_mbdata; + void *ret_ptr; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return -ENOMEM; + } + h2d_mbdata = (struct msgbuf_h2d_mbdata *)ret_ptr; + memset(h2d_mbdata, 0, sizeof(*h2d_mbdata)); + + h2d_mbdata->msg.msgtype = MSGBUF_TYPE_H2D_MAILBOX_DATA; + h2d_mbdata->mbdata = cpu_to_le32(mbdata); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + + return err; +} + static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) @@ -719,6 +767,7 @@ brcmf_flowring_qlen(flow, flowid)); break; } + skb_tx_timestamp(skb); skb_orphan(skb); if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, skb, ETH_HLEN, @@ -807,8 +856,12 @@ flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); if (flowid == BRCMF_FLOWRING_INVALID_ID) { flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); - if (flowid == BRCMF_FLOWRING_INVALID_ID) + if (flowid == BRCMF_FLOWRING_INVALID_ID) { return -ENOMEM; + } else { + brcmf_flowring_enqueue(flow, flowid, skb); + return 0; + } } queue_count = brcmf_flowring_enqueue(flow, flowid, skb); force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); @@ -1141,7 +1194,8 @@ { struct brcmf_pub *drvr = msgbuf->drvr; struct msgbuf_rx_complete *rx_complete; - struct sk_buff *skb; + struct sk_buff *skb, *cpskb = NULL; + struct ethhdr *eh; u16 data_offset; u16 buflen; u16 flags; @@ -1190,6 +1244,34 @@ return; } + if (ifp->isap && ifp->fmac_pkt_fwd_en) { + eh = (struct ethhdr *)(skb->data); + skb_set_network_header(skb, sizeof(struct ethhdr)); + skb->protocol = eh->h_proto; + skb->priority = cfg80211_classify8021d(skb, NULL); + if (is_unicast_ether_addr(eh->h_dest)) { + if (brcmf_find_sta(ifp, eh->h_dest)) { + /* determine the priority */ + if (skb->priority == 0 || skb->priority > 7) { + skb->priority = + cfg80211_classify8021d(skb, + NULL); + } + brcmf_proto_tx_queue_data(ifp->drvr, + ifp->ifidx, skb); + return; + } + } else { + cpskb = pskb_copy(skb, GFP_ATOMIC); + if (cpskb) { + brcmf_proto_tx_queue_data(ifp->drvr, + ifp->ifidx, + cpskb); + } else { + brcmf_err("Unable to do skb copy\n"); + } + } + } skb->protocol = eth_type_trans(skb, ifp->ndev); brcmf_netif_rx(ifp, skb, false); } @@ -1277,6 +1359,21 @@ brcmf_msgbuf_remove_flowring(msgbuf, flowid); } +static void +brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_d2h_mailbox_data *d2h_mbdata; + + d2h_mbdata = (struct msgbuf_d2h_mailbox_data *)buf; + + if (!d2h_mbdata) { + brcmf_err("d2h_mbdata is null\n"); + return; + } + + brcmf_pcie_handle_mb_data(msgbuf->drvr->bus_if, d2h_mbdata->mbdata); +} static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) { @@ -1320,6 +1417,11 @@ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); brcmf_msgbuf_process_rx_complete(msgbuf, buf); break; + case MSGBUF_TYPE_D2H_MAILBOX_DATA: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_D2H_MAILBOX_DATA\n"); + brcmf_msgbuf_process_d2h_mbdata(msgbuf, buf); + break; + default: bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype); break; @@ -1396,9 +1498,27 @@ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; struct msgbuf_tx_flowring_delete_req *delete; struct brcmf_commonring *commonring; + struct brcmf_commonring *commonring_del = msgbuf->flowrings[flowid]; + struct brcmf_flowring *flow = msgbuf->flow; void *ret_ptr; u8 ifidx; int err; + int retry = BRCMF_MAX_TXSTATUS_WAIT_RETRIES; + + /* make sure it is not in txflow */ + brcmf_commonring_lock(commonring_del); + flow->rings[flowid]->status = RING_CLOSING; + brcmf_commonring_unlock(commonring_del); + + /* wait for commonring txflow finished */ + while (retry && atomic_read(&commonring_del->outstanding_tx)) { + usleep_range(5000, 10000); + retry--; + } + if (!retry) { + brcmf_err("timed out waiting for txstatus\n"); + atomic_set(&commonring_del->outstanding_tx, 0); + } /* no need to submit if firmware can not be reached */ if (drvr->bus_if->state != BRCMF_BUS_UP) { diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h 2022-01-06 12:45:53.826318157 -0500 @@ -39,5 +39,6 @@ } static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} #endif +int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata); #endif /* BRCMFMAC_MSGBUF_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c 2022-01-06 12:45:53.826318157 -0500 @@ -19,7 +19,8 @@ struct device_node *root, *np = dev->of_node; int irq; u32 irqf; - u32 val; + u32 val32; + u16 val16; /* Set board-type to the first string of the machine compatible prop */ root = of_find_node_by_path("/"); @@ -47,8 +48,15 @@ !of_device_is_compatible(np, "brcm,bcm4329-fmac")) return; - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdio->drive_strength = val; + if (of_property_read_u32(np, "brcm,drive-strength", &val32) == 0) + sdio->drive_strength = val32; + + sdio->broken_sg_support = of_property_read_bool(np, + "brcm,broken_sg_support"); + if (of_property_read_u16(np, "brcm,sd_head_align", &val16) == 0) + sdio->sd_head_align = val16; + if (of_property_read_u16(np, "brcm,sd_sgentry_align", &val16) == 0) + sdio->sd_sgentry_align = val16; /* make sure there are interrupts defined in the node */ if (!of_find_property(np, "interrupts", NULL)) diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c 2022-01-06 12:45:53.826318157 -0500 @@ -231,7 +231,35 @@ if (pact_frm->category == P2P_PUB_AF_CATEGORY && pact_frm->action == P2P_PUB_AF_ACTION && pact_frm->oui_type == P2P_VER && - memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) + memcmp(pact_frm->oui, WFA_OUI, P2P_OUI_LEN) == 0) + return true; + + return false; +} + +/** + * brcmf_p2p_is_dpp_pub_action() - true if dpp public type frame. + * + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Determine if action frame is dpp public action type + */ +static bool brcmf_p2p_is_dpp_pub_action(void *frame, u32 frame_len) +{ + struct brcmf_p2p_pub_act_frame *pact_frm; + + if (!frame) + return false; + + pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; + if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1) + return false; + + if (pact_frm->category == WLAN_CATEGORY_PUBLIC && + pact_frm->action == WLAN_PUB_ACTION_VENDOR_SPECIFIC && + pact_frm->oui_type == DPP_VER && + memcmp(pact_frm->oui, WFA_OUI, TLV_OUI_LEN) == 0) return true; return false; @@ -894,7 +922,7 @@ { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; - int err; + int err = 0; if (brcmf_p2p_scan_is_p2p_request(request)) { /* find my listen channel */ @@ -912,10 +940,14 @@ if (err) return err; + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + /* override .run_escan() callback. */ cfg->escan_info.run = brcmf_p2p_run_escan; } - return 0; + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, + request->ie, request->ie_len); + return err; } @@ -991,6 +1023,8 @@ if (err) goto exit; + p2p->remin_on_channel_wdev = wdev; + memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); *cookie = p2p->remain_on_channel_cookie; cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); @@ -1014,6 +1048,7 @@ { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct brcmf_p2p_info *p2p = &cfg->p2p; + struct wireless_dev *wdev = p2p->remin_on_channel_wdev; brcmf_dbg(TRACE, "Enter\n"); if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, @@ -1026,10 +1061,16 @@ complete(&p2p->wait_next_af); } - cfg80211_remain_on_channel_expired(&ifp->vif->wdev, + wdev = p2p->remin_on_channel_wdev ? + p2p->remin_on_channel_wdev : + &ifp->vif->wdev; + + cfg80211_remain_on_channel_expired(wdev, p2p->remain_on_channel_cookie, &p2p->remain_on_channel, GFP_KERNEL); + p2p->remin_on_channel_wdev = NULL; + } return 0; } @@ -1281,6 +1322,10 @@ brcmf_dbg(TRACE, "Enter\n"); vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + + if (!vif) + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe_abort", &int_val, sizeof(s32)); if (err) @@ -1531,6 +1576,7 @@ * * @p2p: p2p info struct for vif. * @af_params: action frame data/info. + * @vif: vif to send * * Send an action frame immediately without doing channel synchronization. * @@ -1539,12 +1585,17 @@ * frame is transmitted. */ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, - struct brcmf_fil_af_params_le *af_params) + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif + ) { struct brcmf_pub *drvr = p2p->cfg->pub; - struct brcmf_cfg80211_vif *vif; - struct brcmf_p2p_action_frame *p2p_af; s32 err = 0; + struct brcmf_fil_action_frame_le *action_frame; + u16 action_frame_len; + + action_frame = &af_params->action_frame; + action_frame_len = le16_to_cpu(action_frame->len); brcmf_dbg(TRACE, "Enter\n"); @@ -1552,13 +1603,6 @@ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - /* check if it is a p2p_presence response */ - p2p_af = (struct brcmf_p2p_action_frame *)af_params->action_frame.data; - if (p2p_af->subtype == P2P_AF_PRESENCE_RSP) - vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; - else - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params, sizeof(*af_params)); if (err) { @@ -1714,10 +1758,13 @@ * @cfg: driver private data for cfg80211 interface. * @ndev: net device to transmit on. * @af_params: configuration data for action frame. + * @vif: virtual interface to send */ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params) + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif + ) { struct brcmf_p2p_info *p2p = &cfg->p2p; struct brcmf_if *ifp = netdev_priv(ndev); @@ -1789,7 +1836,9 @@ goto exit; } } else if (brcmf_p2p_is_p2p_action(action_frame->data, - action_frame_len)) { + action_frame_len) || + brcmf_p2p_is_dpp_pub_action(action_frame->data, + action_frame_len)) { /* do not configure anything. it will be */ /* sent with a default configuration */ } else { @@ -1826,6 +1875,7 @@ /* validate channel and p2p ies */ if (config_af_params.search_channel && IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) && + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif && p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) { afx_hdl = &p2p->afx_hdl; afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel); @@ -1857,7 +1907,7 @@ if (af_params->channel) msleep(P2P_AF_RETRY_DELAY_TIME); - ack = !brcmf_p2p_tx_action_frame(p2p, af_params); + ack = !brcmf_p2p_tx_action_frame(p2p, af_params, vif); tx_retry++; dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell, dwell_jiffies); @@ -2424,8 +2474,12 @@ brcmf_remove_interface(vif->ifp, true); brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (iftype != NL80211_IFTYPE_P2P_DEVICE) - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (iftype != NL80211_IFTYPE_P2P_DEVICE) { + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif = NULL; + } return err; } @@ -2504,6 +2558,7 @@ pri_ifp = brcmf_get_ifp(cfg->pub, 0); p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + init_completion(&p2p->send_af_done); if (p2pdev_forced) { err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h 2022-01-06 12:45:53.826318157 -0500 @@ -138,6 +138,7 @@ bool block_gon_req_tx; bool p2pdev_dynamically; bool wait_for_offchan_complete; + struct wireless_dev *remin_on_channel_wdev; }; s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); @@ -170,7 +171,8 @@ void *data); bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params); + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif); bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi); s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c 2022-01-06 12:45:53.826318157 -0500 @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -38,6 +40,7 @@ #include "chip.h" #include "core.h" #include "common.h" +#include "cfg80211.h" enum brcmf_pcie_state { @@ -48,16 +51,17 @@ BRCMF_FW_DEF(43602, "brcmfmac43602-pcie"); BRCMF_FW_DEF(4350, "brcmfmac4350-pcie"); BRCMF_FW_DEF(4350C, "brcmfmac4350c2-pcie"); -BRCMF_FW_DEF(4356, "brcmfmac4356-pcie"); -BRCMF_FW_DEF(43570, "brcmfmac43570-pcie"); +CY_FW_DEF(4356, "cyfmac4356-pcie"); +CY_FW_DEF(43570, "cyfmac43570-pcie"); BRCMF_FW_DEF(4358, "brcmfmac4358-pcie"); -BRCMF_FW_DEF(4359, "brcmfmac4359-pcie"); +CY_FW_DEF(4359, "cyfmac4359-pcie"); BRCMF_FW_DEF(4364, "brcmfmac4364-pcie"); BRCMF_FW_DEF(4365B, "brcmfmac4365b-pcie"); BRCMF_FW_DEF(4365C, "brcmfmac4365c-pcie"); BRCMF_FW_DEF(4366B, "brcmfmac4366b-pcie"); BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie"); BRCMF_FW_DEF(4371, "brcmfmac4371-pcie"); +CY_FW_DEF(4355, "cyfmac54591-pcie"); static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), @@ -78,6 +82,7 @@ BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_43664_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), + BRCMF_FW_ENTRY(CY_CC_89459_CHIP_ID, 0xFFFFFFFF, 4355), }; #define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */ @@ -137,12 +142,14 @@ BRCMF_PCIE_MB_INT_D2H3_DB0 | \ BRCMF_PCIE_MB_INT_D2H3_DB1) +#define BRCMF_PCIE_SHARED_VERSION_6 6 #define BRCMF_PCIE_SHARED_VERSION_7 7 #define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION BRCMF_PCIE_SHARED_VERSION_7 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF #define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 #define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 +#define BRCMF_PCIE_SHARED_USE_MAILBOX 0x2000000 #define BRCMF_PCIE_SHARED_HOSTRDY_DB1 0x10000000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 @@ -159,6 +166,7 @@ #define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 #define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 #define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 +#define BRCMF_SHARED_HOST_CAP_OFFSET 84 #define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 #define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 @@ -173,6 +181,8 @@ #define BRCMF_DEF_MAX_RXBUFPOST 255 +#define BRCMF_H2D_ENABLE_HOSTRDY 0x400 + #define BRCMF_CONSOLE_BUFADDR_OFFSET 8 #define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 #define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 @@ -259,6 +269,9 @@ struct brcmf_chip *ci; u32 coreid; struct brcmf_pcie_shared_info shared; + u8 hostready; + bool use_mailbox; + bool use_d0_inform; wait_queue_head_t mbdata_resp_wait; bool mbdata_completed; bool irq_allocated; @@ -271,6 +284,14 @@ void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u16 value); struct brcmf_mp_device *settings; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + ulong bar1_size; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ +#ifdef DEBUG + u32 console_interval; + bool console_active; + struct timer_list timer; +#endif }; struct brcmf_pcie_ringbuf { @@ -342,6 +363,14 @@ static struct brcmf_fw_request * brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo); +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active); +static void brcmf_pcie_debugfs_create(struct device *dev); + +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ +DEFINE_RAW_SPINLOCK(pcie_lock); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) { @@ -365,8 +394,24 @@ brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + u8 value; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread8(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread8(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -374,8 +419,24 @@ brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u16 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread16(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread16(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -384,8 +445,22 @@ u16 value) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } iowrite16(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite16(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -412,8 +487,24 @@ brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u32 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread32(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread32(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -422,17 +513,47 @@ u32 value) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + iowrite32(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else iowrite32(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } static u32 brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + void __iomem *address = devinfo->tcm + devinfo->ci->rambase + + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u32 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread32(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); - return (ioread32(addr)); + return value; +#else + return (ioread32(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -440,9 +561,23 @@ brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u32 value) { - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; - - iowrite32(value, addr); + void __iomem *address = devinfo->tcm + devinfo->ci->rambase + + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + iowrite32(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite32(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -454,12 +589,30 @@ __le32 *src32; __le16 *src16; u8 *src8; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { src8 = (u8 *)srcaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ iowrite8(*src8, address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address++; src8++; len--; @@ -468,7 +621,22 @@ len = len / 2; src16 = (__le16 *)srcaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ iowrite16(le16_to_cpu(*src16), address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 2; src16++; len--; @@ -478,12 +646,29 @@ len = len / 4; src32 = (__le32 *)srcaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ iowrite32(le32_to_cpu(*src32), address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 4; src32++; len--; } } +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -495,12 +680,30 @@ __le32 *dst32; __le16 *dst16; u8 *dst8; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) { if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) { dst8 = (u8 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst8 = ioread8(address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address++; dst8++; len--; @@ -509,7 +712,22 @@ len = len / 2; dst16 = (__le16 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst16 = cpu_to_le16(ioread16(address)); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 2; dst16++; len--; @@ -519,12 +737,29 @@ len = len / 4; dst32 = (__le32 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst32 = cpu_to_le32(ioread32(address)); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 4; dst32++; len--; } } +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -568,6 +803,9 @@ BRCMF_PCIE_CFGREG_MSI_ADDR_L, BRCMF_PCIE_CFGREG_MSI_ADDR_H, BRCMF_PCIE_CFGREG_MSI_DATA, +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + BCMA_PCI_BAR1_WIN, +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, BRCMF_PCIE_CFGREG_RBAR_CTRL, BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, @@ -667,41 +905,53 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) { struct brcmf_pcie_shared_info *shared; + struct brcmf_bus *bus; + int err; struct brcmf_core *core; u32 addr; u32 cur_htod_mb_data; u32 i; shared = &devinfo->shared; - addr = shared->htod_mb_data_addr; - cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - - if (cur_htod_mb_data != 0) - brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", - cur_htod_mb_data); - - i = 0; - while (cur_htod_mb_data != 0) { - msleep(10); - i++; - if (i > 100) - return -EIO; + bus = dev_get_drvdata(&devinfo->pdev->dev); + if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6 && + !devinfo->use_mailbox) { + err = brcmf_msgbuf_tx_mbdata(bus->drvr, htod_mb_data); + if (err) { + brcmf_err(bus, "sendimg mbdata failed err=%d\n", err); + return err; + } + } else { + addr = shared->htod_mb_data_addr; cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - } - brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + if (cur_htod_mb_data != 0) + brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", + cur_htod_mb_data); + + i = 0; + while (cur_htod_mb_data != 0) { + msleep(10); + i++; + if (i > 100) + return -EIO; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + } - /* Send mailbox interrupt twice as a hardware workaround */ - core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); - if (core->rev <= 13) + brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + /* Send mailbox interrupt twice as a hardware workaround */ + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) + pci_write_config_dword(devinfo->pdev, + BRCMF_PCIE_REG_SBMBX, 1); + } return 0; } -static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) +static u32 brcmf_pcie_read_mb_data(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_shared_info *shared; u32 addr; @@ -710,32 +960,37 @@ shared = &devinfo->shared; addr = shared->dtoh_mb_data_addr; dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + brcmf_pcie_write_tcm32(devinfo, addr, 0); + return dtoh_mb_data; +} - if (!dtoh_mb_data) - return; +void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data) +{ + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; - brcmf_pcie_write_tcm32(devinfo, addr, 0); + brcmf_dbg(INFO, "D2H_MB_DATA: 0x%04x\n", d2h_mb_data); - brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); + if (d2h_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { + brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP REQ\n"); brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); - brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); + brcmf_dbg(INFO, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); } - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); - if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { - brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); + + if (d2h_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) + brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); + if (d2h_mb_data & BRCMF_D2H_DEV_D3_ACK) { + brcmf_dbg(INFO, "D2H_MB_DATA: D3 ACK\n"); devinfo->mbdata_completed = true; wake_up(&devinfo->mbdata_resp_wait); } - if (dtoh_mb_data & BRCMF_D2H_DEV_FWHALT) { - brcmf_dbg(PCIE, "D2H_MB_DATA: FW HALT\n"); + + if (d2h_mb_data & BRCMF_D2H_DEV_FWHALT) { + brcmf_dbg(INFO, "D2H_MB_DATA: FW HALT\n"); brcmf_fw_crashed(&devinfo->pdev->dev); } } - static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_shared_info *shared; @@ -844,6 +1099,9 @@ { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; u32 status; + u32 d2h_mbdata; + struct pci_dev *pdev = devinfo->pdev; + struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev); devinfo->in_irq = true; status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); @@ -852,8 +1110,11 @@ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status); if (status & (BRCMF_PCIE_MB_INT_FN0_0 | - BRCMF_PCIE_MB_INT_FN0_1)) - brcmf_pcie_handle_mb_data(devinfo); + BRCMF_PCIE_MB_INT_FN0_1)) { + d2h_mbdata = brcmf_pcie_read_mb_data(devinfo); + brcmf_pcie_handle_mb_data(bus, d2h_mbdata); + } + if (status & BRCMF_PCIE_MB_INT_D2H_DB) { if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) brcmf_proto_msgbuf_rx_trigger( @@ -1136,9 +1397,14 @@ u16 max_flowrings; u16 max_submissionrings; u16 max_completionrings; - +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + brcmf_pcie_copy_dev_tomem(devinfo, devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); +#else memcpy_fromio(&ringinfo, devinfo->tcm + devinfo->shared.ring_info_addr, sizeof(ringinfo)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + if (devinfo->shared.version >= 6) { max_submissionrings = le16_to_cpu(ringinfo.max_submissionrings); max_flowrings = le16_to_cpu(ringinfo.max_flowrings); @@ -1149,6 +1415,10 @@ BRCMF_NROF_H2D_COMMON_MSGRINGS; max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS; } + if (max_flowrings > 256) { + brcmf_err(bus, "invalid max_flowrings(%d)\n", max_flowrings); + return -EIO; + } if (devinfo->dma_idx_sz != 0) { bufsz = (max_submissionrings + max_completionrings) * @@ -1207,8 +1477,14 @@ ringinfo.d2h_r_idx_hostaddr.high_addr = cpu_to_le32(address >> 32); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + brcmf_pcie_copy_mem_todev(devinfo, + devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); +#else memcpy_toio(devinfo->tcm + devinfo->shared.ring_info_addr, &ringinfo, sizeof(ringinfo)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ brcmf_dbg(PCIE, "Using host memory indices\n"); } @@ -1344,6 +1620,11 @@ static void brcmf_pcie_down(struct device *dev) { + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + + brcmf_pcie_fwcon_timer(devinfo, false); } @@ -1407,8 +1688,10 @@ struct brcmf_fw_name fwnames[] = { { ext, fw_name }, }; + u32 chip; - fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev, + chip = bus_if->chip; + fwreq = brcmf_fw_alloc_request(chip, bus_if->chiprev, brcmf_pcie_fwnames, ARRAY_SIZE(brcmf_pcie_fwnames), fwnames, ARRAY_SIZE(fwnames)); @@ -1463,6 +1746,7 @@ .get_memdump = brcmf_pcie_get_memdump, .get_fwname = brcmf_pcie_get_fwname, .reset = brcmf_pcie_reset, + .debugfs_create = brcmf_pcie_debugfs_create, }; @@ -1495,6 +1779,7 @@ struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); struct brcmf_pcie_shared_info *shared; u32 addr; + u32 host_cap; shared = &devinfo->shared; shared->tcm_base_address = sharedram_addr; @@ -1534,6 +1819,26 @@ addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); + if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6) { + host_cap = shared->version; + + devinfo->hostready = + ((shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1) + == BRCMF_PCIE_SHARED_HOSTRDY_DB1); + if (devinfo->hostready) { + brcmf_dbg(PCIE, "HostReady supported by dongle.\n"); + host_cap = host_cap | BRCMF_H2D_ENABLE_HOSTRDY; + } + devinfo->use_mailbox = + ((shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX) + == BRCMF_PCIE_SHARED_USE_MAILBOX); + devinfo->use_d0_inform = false; + addr = sharedram_addr + BRCMF_SHARED_HOST_CAP_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, host_cap); + } else { + devinfo->use_d0_inform = true; + } + brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", shared->max_rxbufpost, shared->rx_dataoffset); @@ -1647,6 +1952,9 @@ devinfo->regs = ioremap(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); devinfo->tcm = ioremap(bar1_addr, bar1_size); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + devinfo->bar1_size = bar1_size; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (!devinfo->regs || !devinfo->tcm) { brcmf_err(bus, "ioremap() failed (%p,%p)\n", devinfo->regs, @@ -1758,13 +2066,14 @@ struct brcmf_commonring **flowrings; u32 i, nvram_len; + bus = dev_get_drvdata(dev); + pcie_bus_dev = bus->bus_priv.pcie; + devinfo = pcie_bus_dev->devinfo; + /* check firmware loading result */ if (ret) goto fail; - bus = dev_get_drvdata(dev); - pcie_bus_dev = bus->bus_priv.pcie; - devinfo = pcie_bus_dev->devinfo; brcmf_pcie_attach(devinfo); fw = fwreq->items[BRCMF_PCIE_FW_CODE].binary; @@ -1827,15 +2136,20 @@ brcmf_pcie_intr_enable(devinfo); brcmf_pcie_hostready(devinfo); - ret = brcmf_attach(&devinfo->pdev->dev); + ret = brcmf_attach(&devinfo->pdev->dev, true); if (ret) goto fail; brcmf_pcie_bus_console_read(devinfo, false); + brcmf_pcie_fwcon_timer(devinfo, true); + return; fail: + brcmf_err(bus, "Dongle setup failed\n"); + brcmf_pcie_bus_console_read(devinfo, true); + brcmf_fw_crashed(dev); device_release_driver(dev); } @@ -1847,8 +2161,10 @@ { ".bin", devinfo->fw_name }, { ".txt", devinfo->nvram_name }, }; + u32 chip; - fwreq = brcmf_fw_alloc_request(devinfo->ci->chip, devinfo->ci->chiprev, + chip = devinfo->ci->chip; + fwreq = brcmf_fw_alloc_request(chip, devinfo->ci->chiprev, brcmf_pcie_fwnames, ARRAY_SIZE(brcmf_pcie_fwnames), fwnames, ARRAY_SIZE(fwnames)); @@ -1866,6 +2182,105 @@ return fwreq; } +#ifdef DEBUG +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ + if (!active) { + if (devinfo->console_active) { + del_timer_sync(&devinfo->timer); + devinfo->console_active = false; + } + return; + } + + /* don't start the timer */ + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP || + !devinfo->console_interval || !BRCMF_FWCON_ON()) + return; + + if (!devinfo->console_active) { + devinfo->timer.expires = jiffies + devinfo->console_interval; + add_timer(&devinfo->timer); + devinfo->console_active = true; + } else { + /* Reschedule the timer */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); + } +} + +static void +brcmf_pcie_fwcon(struct timer_list *t) +{ + struct brcmf_pciedev_info *devinfo = from_timer(devinfo, t, timer); + + if (!devinfo->console_active) + return; + + brcmf_pcie_bus_console_read(devinfo, false); + + /* Reschedule the timer if console interval is not zero */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); +} + +static int brcmf_pcie_console_interval_get(void *data, u64 *val) +{ + struct brcmf_pciedev_info *devinfo = data; + + *val = devinfo->console_interval; + + return 0; +} + +static int brcmf_pcie_console_interval_set(void *data, u64 val) +{ + struct brcmf_pciedev_info *devinfo = data; + + if (val > MAX_CONSOLE_INTERVAL) + return -EINVAL; + + devinfo->console_interval = val; + + if (!val && devinfo->console_active) + brcmf_pcie_fwcon_timer(devinfo, false); + else if (val) + brcmf_pcie_fwcon_timer(devinfo, true); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(brcmf_pcie_console_interval_fops, + brcmf_pcie_console_interval_get, + brcmf_pcie_console_interval_set, + "%llu\n"); + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + + if (IS_ERR_OR_NULL(dentry)) + return; + + devinfo->console_interval = BRCMF_CONSOLE; + + debugfs_create_file("console_interval", 0644, dentry, devinfo, + &brcmf_pcie_console_interval_fops); +} + +#else +void brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ +} + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ +} +#endif + static int brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1933,6 +2348,10 @@ if (ret) goto fail_bus; +#ifdef DEBUG + /* Set up the fwcon timer */ + timer_setup(&devinfo->timer, brcmf_pcie_fwcon, 0); +#endif fwreq = brcmf_pcie_prepare_fw_request(devinfo); if (!fwreq) { ret = -ENOMEM; @@ -1978,6 +2397,8 @@ devinfo = bus->bus_priv.pcie->devinfo; + brcmf_pcie_fwcon_timer(devinfo, false); + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; if (devinfo->ci) brcmf_pcie_intr_disable(devinfo); @@ -2013,11 +2434,24 @@ { struct brcmf_pciedev_info *devinfo; struct brcmf_bus *bus; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; brcmf_dbg(PCIE, "Enter\n"); bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; + config = bus->drvr->config; + + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err(bus, "timed out wait for cfg80211 suspended\n"); + + brcmf_pcie_fwcon_timer(devinfo, false); brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); @@ -2054,14 +2488,26 @@ /* Check if device is still up and running, if so we are ready */ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { brcmf_dbg(PCIE, "Try to wakeup device....\n"); - if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM)) - goto cleanup; + if (devinfo->use_d0_inform) { + if (brcmf_pcie_send_mb_data(devinfo, + BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + } else { + brcmf_pcie_hostready(devinfo); + } + brcmf_dbg(PCIE, "Hot resume, continue....\n"); devinfo->state = BRCMFMAC_PCIE_STATE_UP; brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_bus_change_state(bus, BRCMF_BUS_UP); brcmf_pcie_intr_enable(devinfo); - brcmf_pcie_hostready(devinfo); + if (devinfo->use_d0_inform) { + brcmf_dbg(TRACE, "sending brcmf_pcie_hostready since use_d0_inform=%d\n", + devinfo->use_d0_inform); + brcmf_pcie_hostready(devinfo); + } + + brcmf_pcie_fwcon_timer(devinfo, true); return 0; } @@ -2073,7 +2519,7 @@ err = brcmf_pcie_probe(pdev, NULL); if (err) - __brcmf_err(NULL, __func__, "probe after resume failed, err=%d\n", err); + brcmf_err(bus, "probe after resume failed, err=%d\n", err); return err; } @@ -2103,6 +2549,7 @@ BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_RAW_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), @@ -2118,13 +2565,16 @@ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54591_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54590_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54594_DEVICE_ID), { /* end: all zeroes */ } }; - MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); - static struct pci_driver brcmf_pciedrvr = { .node = {}, .name = KBUILD_MODNAME, @@ -2137,14 +2587,17 @@ .driver.coredump = brcmf_dev_coredump, }; - -int brcmf_pcie_register(void) +void brcmf_pcie_register(void) { + int err; + brcmf_dbg(PCIE, "Enter\n"); - return pci_register_driver(&brcmf_pciedrvr); + err = pci_register_driver(&brcmf_pciedrvr); + if (err) + brcmf_err(NULL, "PCIE driver registration failed, err=%d\n", + err); } - void brcmf_pcie_exit(void) { brcmf_dbg(PCIE, "Enter\n"); diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h 2022-01-06 12:45:53.826318157 -0500 @@ -11,4 +11,9 @@ struct brcmf_pciedev_info *devinfo; }; + +void brcmf_pcie_exit(void); +void brcmf_pcie_register(void); +void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data); + #endif /* BRCMFMAC_PCIE_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c 2022-01-06 12:45:53.826318157 -0500 @@ -158,12 +158,12 @@ struct brcmf_pno_macaddr_le pfn_mac; u8 *mac_addr = NULL; u8 *mac_mask = NULL; - int err, i; + int err, i, ri; - for (i = 0; i < pi->n_reqs; i++) - if (pi->reqs[i]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - mac_addr = pi->reqs[i]->mac_addr; - mac_mask = pi->reqs[i]->mac_addr_mask; + for (ri = 0; ri < pi->n_reqs; ri++) + if (pi->reqs[ri]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + mac_addr = pi->reqs[ri]->mac_addr; + mac_mask = pi->reqs[ri]->mac_addr_mask; break; } @@ -185,7 +185,7 @@ pfn_mac.mac[0] |= 0x02; brcmf_dbg(SCAN, "enabling random mac: reqid=%llu mac=%pM\n", - pi->reqs[i]->reqid, pfn_mac.mac); + pi->reqs[ri]->reqid, pfn_mac.mac); err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, sizeof(pfn_mac)); if (err) diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c 2022-01-06 12:45:53.826318157 -0500 @@ -3,6 +3,7 @@ * Copyright (c) 2010 Broadcom Corporation */ +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -35,14 +37,19 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "fwil.h" +#include "bt_shared_sdio.h" #define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) #define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) +#define ULP_HUDI_PROC_DONE_TIME msecs_to_jiffies(2500) /* watermark expressed in number of words */ #define DEFAULT_F2_WATERMARK 0x8 -#define CY_4373_F2_WATERMARK 0x40 -#define CY_4373_F1_MESBUSYCTRL (CY_4373_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB) +#define CY_4373_F2_WATERMARK 0x4C +#define CY_4373_MES_WATERMARK 0x44 +#define CY_4373_MESBUSYCTRL (CY_4373_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) #define CY_43012_F2_WATERMARK 0x60 #define CY_43012_MES_WATERMARK 0x50 #define CY_43012_MESBUSYCTRL (CY_43012_MES_WATERMARK | \ @@ -58,6 +65,10 @@ #define CY_435X_F2_WATERMARK 0x40 #define CY_435X_F1_MESBUSYCTRL (CY_435X_F2_WATERMARK | \ SBSDIO_MESBUSYCTRL_ENAB) +#define CY_89459_F2_WATERMARK 0x40 +#define CY_89459_MES_WATERMARK 0x40 +#define CY_89459_MESBUSYCTRL (CY_89459_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) #ifdef DEBUG @@ -135,8 +146,6 @@ #define BRCMF_FIRSTREAD (1 << 6) -#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ - /* SBSDIO_DEVICE_CTL */ /* 1: device will assert busy signal when receiving CMD53 */ @@ -327,7 +336,16 @@ #define KSO_WAIT_US 50 #define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) -#define BRCMF_SDIO_MAX_ACCESS_ERRORS 5 +#define BRCMF_SDIO_MAX_ACCESS_ERRORS 20 + +static void brcmf_sdio_firmware_callback(struct device *dev, int err, + struct brcmf_fw_request *fwreq); +static struct brcmf_fw_request * + brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus); +static int brcmf_sdio_f2_ready(struct brcmf_sdio *bus); +static int brcmf_ulp_event_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); #ifdef DEBUG /* Device console log buffer state */ @@ -610,20 +628,22 @@ BRCMF_FW_DEF(4329, "brcmfmac4329-sdio"); BRCMF_FW_DEF(4330, "brcmfmac4330-sdio"); BRCMF_FW_DEF(4334, "brcmfmac4334-sdio"); -BRCMF_FW_DEF(43340, "brcmfmac43340-sdio"); +CY_FW_DEF(43340, "cyfmac43340-sdio"); BRCMF_FW_DEF(4335, "brcmfmac4335-sdio"); -BRCMF_FW_DEF(43362, "brcmfmac43362-sdio"); -BRCMF_FW_DEF(4339, "brcmfmac4339-sdio"); +CY_FW_DEF(43362, "cyfmac43362-sdio"); +CY_FW_DEF(4339, "cyfmac4339-sdio"); BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio"); /* Note the names are not postfixed with a1 for backward compatibility */ -BRCMF_FW_DEF(43430A1, "brcmfmac43430-sdio"); -BRCMF_FW_DEF(43455, "brcmfmac43455-sdio"); +CY_FW_DEF(43430A1, "cyfmac43430-sdio"); +CY_FW_DEF(43439, "cyfmac43439-sdio"); +CY_FW_DEF(43455, "cyfmac43455-sdio"); BRCMF_FW_DEF(43456, "brcmfmac43456-sdio"); -BRCMF_FW_DEF(4354, "brcmfmac4354-sdio"); -BRCMF_FW_DEF(4356, "brcmfmac4356-sdio"); -BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); -BRCMF_FW_DEF(4373, "brcmfmac4373-sdio"); -BRCMF_FW_DEF(43012, "brcmfmac43012-sdio"); +CY_FW_DEF(4354, "cyfmac4354-sdio"); +CY_FW_DEF(4356, "cyfmac4356-sdio"); +CY_FW_DEF(4359, "cyfmac4359-sdio"); +CY_FW_DEF(4373, "cyfmac4373-sdio"); +CY_FW_DEF(43012, "cyfmac43012-sdio"); +CY_FW_DEF(89459, "cyfmac54591-sdio"); static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -639,14 +659,17 @@ BRCMF_FW_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362), BRCMF_FW_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339), BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0), - BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x0000001E, 43430A1), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFE0, 43439), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0x00000200, 43456), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455), BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), + BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439), BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), - BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012) + BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012), + BRCMF_FW_ENTRY(CY_CC_89459_CHIP_ID, 0xFFFFFFFF, 89459) }; #define TXCTL_CREDITS 2 @@ -924,6 +947,20 @@ break; case CLK_SDONLY: +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* If the request is to switch off backplane clock, + * confirm that BT is inactive before doing so. + * If this call had come from Non Watchdog context any way + * the Watchdog would switch off the clock again when + * nothing is to be done & BT has finished using the bus. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "BT is active, not switching off\n"); + brcmf_sdio_wd_timer(bus, true); + break; + } + +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) brcmf_sdio_sdclk(bus, true); @@ -935,6 +972,19 @@ break; case CLK_NONE: +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* If the request is to switch off backplane clock, + * confirm that BT is inactive before doing so. + * If this call had come from non-watchdog context any way + * the watchdog would switch off the clock again when + * nothing is to be done & BT has finished using the bus. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "BT is active, not switching off\n"); + break; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) brcmf_sdio_htclk(bus, false, false); @@ -959,6 +1009,29 @@ (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* The following is the assumption based on which the hook is placed. + * From WLAN driver, either from the active contexts OR from the + * watchdog contexts, we will be attempting to go to sleep. At that + * moment if we see that BT is still actively using the bus, we will + * return -EBUSY from here, and the bus sleep state would not have + * changed, so the caller can then schedule the watchdog again + * which will come and attempt to sleep at a later point. + * + * In case if BT is the only one and is the last user, we don't switch + * off the clock immediately, we allow the WLAN to decide when to sleep + * i.e from the watchdog. + * Now if the watchdog becomes active and attempts to switch off the + * clock and if another WLAN context is active they are any way + * serialized with sdlock. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "Cannot sleep when BT is active\n"); + err = -EBUSY; + goto done; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ @@ -1093,7 +1166,7 @@ } #endif /* DEBUG */ -static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus, u32 *hmbd) { struct brcmf_sdio_dev *sdiod = bus->sdiodev; struct brcmf_core *core = bus->sdio_core; @@ -1182,6 +1255,9 @@ HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) brcmf_err("Unknown mailbox data content: 0x%02x\n", hmb_data); + /* Populate hmb_data if argument is passed for DS1 check later */ + if (hmbd) + *hmbd = hmb_data; return intstatus; } @@ -2345,6 +2421,9 @@ &prec_out); if (pkt == NULL) break; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + skb_orphan(pkt); +#endif __skb_queue_tail(&pktq, pkt); } spin_unlock_bh(&bus->txq_lock); @@ -2451,6 +2530,14 @@ return false; } +static bool brcmf_sdio_use_ht_avail(struct brcmf_chip *ci) +{ + if (ci->chip == CY_CC_4373_CHIP_ID) + return true; + else + return false; +} + static void brcmf_sdio_bus_stop(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); @@ -2487,7 +2574,8 @@ &err); if (!err) { bpreq = saveclk; - bpreq |= brcmf_chip_is_ulp(bus->ci) ? + bpreq |= (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) ? SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT; brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, @@ -2568,6 +2656,182 @@ return ret; } +/* This Function is used to retrieve important + * details from dongle related to ULP mode Mostly + * values/SHM details that will be vary depending + * on the firmware branches + */ +static void +brcmf_sdio_ulp_preinit(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_if *ifp = bus_if->drvr->iflist[0]; + + brcmf_dbg(ULP, "Enter\n"); + + /* Query ulp_sdioctrl iovar to get the ULP related SHM offsets */ + brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl", + &sdiodev->fmac_ulp.ulp_shm_offset, + sizeof(sdiodev->fmac_ulp.ulp_shm_offset)); + + sdiodev->ulp = false; + + brcmf_dbg(ULP, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n", + M_DS1_CTRL_SDIO(sdiodev->fmac_ulp), + M_WAKEEVENT_IND(sdiodev->fmac_ulp)); + brcmf_dbg(ULP, "m_ulp_wakeind [%x]\n", + M_ULP_WAKE_IND(sdiodev->fmac_ulp)); +} + +/* Reinitialize ARM because In DS1 mode ARM got off */ +static int +brcmf_sdio_ulp_reinit_fw(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + struct brcmf_fw_request *fwreq; + int err = 0; + + /* After firmware redownload tx/rx seq are reset accordingly + * these values are reset on FMAC side tx_max is initially set to 4, + * which later is updated by FW. + */ + bus->tx_seq = 0; + bus->rx_seq = 0; + bus->tx_max = 4; + + fwreq = brcmf_sdio_prepare_fw_request(bus); + if (!fwreq) + return -ENOMEM; + + err = brcmf_fw_get_firmwares(sdiodev->dev, fwreq, + brcmf_sdio_firmware_callback); + if (err != 0) { + brcmf_err("async firmware request failed: %d\n", err); + kfree(fwreq); + } + + return err; +} + +/* Check if device is in DS1 mode and handshake with ULP UCODE */ +static bool +brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus, u32 hmb_data) +{ + struct brcmf_sdio_dev *sdiod = bus->sdiodev; + int err = 0; + u32 value = 0; + u32 val32, ulp_wake_ind, wowl_wake_ind; + int reg_addr; + unsigned long timeout; + struct brcmf_ulp *fmac_ulp = &bus->sdiodev->fmac_ulp; + int i = 0; + + /* If any host mail box data is present, ignore DS1 exit sequence */ + if (hmb_data) + return false; + /* Skip if DS1 Exit is already in progress + * This can happen if firmware download is taking more time + */ + if (fmac_ulp->ulp_state == FMAC_ULP_TRIGGERED) + return false; + + value = brcmf_sdiod_func0_rb(sdiod, SDIO_CCCR_IOEx, &err); + + if (value == SDIO_FUNC_ENABLE_1) { + brcmf_dbg(ULP, "GOT THE INTERRUPT FROM UCODE\n"); + sdiod->ulp = true; + fmac_ulp->ulp_state = FMAC_ULP_TRIGGERED; + ulp_wake_ind = D11SHM_RDW(sdiod, + M_ULP_WAKE_IND(sdiod->fmac_ulp), + &err); + wowl_wake_ind = D11SHM_RDW(sdiod, + M_WAKEEVENT_IND(sdiod->fmac_ulp), + &err); + + brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x state %s\n", + wowl_wake_ind, ulp_wake_ind, (fmac_ulp->ulp_state) ? + "DS1 Exit Triggered" : "IDLE State"); + + if (wowl_wake_ind || ulp_wake_ind) { + /* RX wake Don't do anything. + * Just bail out and re-download firmware. + */ + /* Print out PHY TX error block when bit 9 set */ + if ((ulp_wake_ind & C_DS1_PHY_TXERR) && + M_DS1_PHYTX_ERR_BLK(sdiod->fmac_ulp)) { + brcmf_err("Dump PHY TX Error SHM Locations\n"); + for (i = 0; i < PHYTX_ERR_BLK_SIZE; i++) { + pr_err("0x%x", + D11SHM_RDW(sdiod, + (M_DS1_PHYTX_ERR_BLK(sdiod->fmac_ulp) + + (i * 2)), &err)); + } + brcmf_err("\n"); + } + } else { + /* TX wake negotiate with MAC */ + brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n", + (u32)D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err)); + val32 = D11SHM_RD(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + D11SHM_WR(sdiod, M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + val32, (C_DS1_CTRL_SDIO_DS1_EXIT | + C_DS1_CTRL_REQ_VALID), &err); + val32 = D11REG_RD(sdiod, D11_MACCONTROL_REG, &err); + val32 = val32 | D11_MACCONTROL_REG_WAKE; + D11REG_WR(sdiod, D11_MACCONTROL_REG, val32, &err); + + /* Poll for PROC_DONE to be set by ucode */ + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + /* Wait here (polling) for C_DS1_CTRL_PROC_DONE */ + timeout = jiffies + ULP_HUDI_PROC_DONE_TIME; + while (!(value & C_DS1_CTRL_PROC_DONE)) { + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + if (time_after(jiffies, timeout)) + break; + usleep_range(1000, 2000); + } + brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n", + (u32)D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), &err)); + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + if (!(value & C_DS1_CTRL_PROC_DONE)) { + brcmf_err("Timeout Failed to enter DS1 Exit state!\n"); + return false; + } + } + + ulp_wake_ind = D11SHM_RDW(sdiod, + M_ULP_WAKE_IND(sdiod->fmac_ulp), + &err); + wowl_wake_ind = D11SHM_RDW(sdiod, + M_WAKEEVENT_IND(sdiod->fmac_ulp), + &err); + brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n", + wowl_wake_ind, ulp_wake_ind); + reg_addr = CORE_CC_REG( + brcmf_chip_get_pmu(bus->ci)->base, min_res_mask); + brcmf_sdiod_writel(sdiod, reg_addr, + DEFAULT_43012_MIN_RES_MASK, &err); + if (err) + brcmf_err("min_res_mask failed\n"); + + return true; + } + + return false; +} + static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { struct brcmf_sdio_dev *sdiod = bus->sdiodev; @@ -2635,12 +2899,18 @@ atomic_set(&bus->fcstate, !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); intstatus |= (newstatus & bus->hostintmask); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + brcmf_btsdio_int_handler(bus->sdiodev->bus_if); +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ } /* Handle host mailbox indication */ if (intstatus & I_HMB_HOST_INT) { + u32 hmb_data = 0; intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdio_hostmail(bus); + intstatus |= brcmf_sdio_hostmail(bus, &hmb_data); + if (brcmf_sdio_ulp_pre_redownload_check(bus, hmb_data)) + brcmf_sdio_ulp_reinit_fw(bus); } sdio_release_host(bus->sdiodev->func1); @@ -2685,7 +2955,7 @@ brcmf_sdio_clrintr(bus); if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - txctl_ok(bus)) { + txctl_ok(bus) && brcmf_sdio_f2_ready(bus)) { sdio_claim_host(bus->sdiodev->func1); if (bus->ctrl_frame_stat) { err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, @@ -2811,6 +3081,8 @@ brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); bus->sdcnt.fcqueued++; + skb_tx_timestamp(pkt); + /* Priority based enq */ spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ @@ -3412,7 +3684,12 @@ static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus) { - if (bus->ci->chip == CY_CC_43012_CHIP_ID) + if (bus->ci->chip == CY_CC_43012_CHIP_ID || + bus->ci->chip == CY_CC_4373_CHIP_ID || + bus->ci->chip == BRCM_CC_4339_CHIP_ID || + bus->ci->chip == BRCM_CC_4345_CHIP_ID || + bus->ci->chip == BRCM_CC_4354_CHIP_ID || + bus->ci->chip == BRCM_CC_4356_CHIP_ID) return true; else return false; @@ -3428,7 +3705,8 @@ brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_chip_is_ulp(bus->ci)) { + if (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) { wakeupctrl = SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT; chipclkcsr = SBSDIO_HT_AVAIL_REQ; } else { @@ -3552,6 +3830,10 @@ if (err < 0) goto done; + /* initialize SHM address from firmware for DS1 */ + if (!bus->sdiodev->ulp) + brcmf_sdio_ulp_preinit(dev); + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; if (sdiodev->sg_support) { bus->txglom = false; @@ -3711,7 +3993,8 @@ #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ - if (!bus->dpc_triggered) { + if (!bus->dpc_triggered && + brcmf_btsdio_bus_count(bus->sdiodev->bus_if) == 0) { rmb(); if ((!bus->dpc_running) && (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { @@ -4157,6 +4440,7 @@ if (ret) { brcmf_err("Failed to probe after sdio device reset: ret %d\n", ret); + brcmf_sdiod_remove(sdiodev); } return ret; @@ -4193,7 +4477,7 @@ u8 saveclk, bpreq; u8 devctl; - brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err); + brcmf_dbg(ULP, "Enter: dev=%s, err=%d\n", dev_name(dev), err); if (err) goto fail; @@ -4210,10 +4494,6 @@ goto fail; bus->alp_only = false; - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, true); - sdio_claim_host(sdiod->func1); /* Make sure backplane clock is on, needed to generate F2 interrupt */ @@ -4225,7 +4505,8 @@ saveclk = brcmf_sdiod_readb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { bpreq = saveclk; - bpreq |= brcmf_chip_is_ulp(bus->ci) ? + bpreq |= (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) ? SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT; brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, bpreq, &err); @@ -4262,7 +4543,7 @@ brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, &err); brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, - CY_4373_F1_MESBUSYCTRL, &err); + CY_4373_MESBUSYCTRL, &err); break; case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012: brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", @@ -4319,6 +4600,22 @@ brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, CY_435X_F1_MESBUSYCTRL, &err); break; + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459: + case SDIO_DEVICE_ID_CYPRESS_54590: + case SDIO_DEVICE_ID_CYPRESS_54591: + case SDIO_DEVICE_ID_CYPRESS_54594: + brcmf_dbg(INFO, "set F2/MES watermark to 0x%x*4 / 0x%x bytes for 89459\n", + CY_89459_F2_WATERMARK, CY_89459_MESBUSYCTRL); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + CY_89459_F2_WATERMARK, &err); + devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, + &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, + CY_89459_MESBUSYCTRL, &err); + break; default: brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err); @@ -4361,19 +4658,44 @@ sdio_release_host(sdiod->func1); + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, true); + err = brcmf_alloc(sdiod->dev, sdiod->settings); if (err) { brcmf_err("brcmf_alloc failed\n"); goto claim; } +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + err = brcmf_btsdio_init(bus_if); + if (err) { + brcmf_err("brcmf_btsdio_init failed\n"); + goto free; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* Attach to the common layer, reserve hdr space */ - err = brcmf_attach(sdiod->dev); + err = brcmf_attach(sdiod->dev, !bus->sdiodev->ulp); if (err != 0) { brcmf_err("brcmf_attach failed\n"); goto free; } + /* Register for ULP events */ + if (sdiod->func1->device == SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012) + brcmf_fweh_register(bus_if->drvr, BRCMF_E_ULP, + brcmf_ulp_event_notify); + + if (bus->sdiodev->ulp) { + /* For ULP, after firmware redownload complete + * set ULP state to IDLE + */ + if (bus->sdiodev->fmac_ulp.ulp_state == FMAC_ULP_TRIGGERED) + bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_IDLE; + } + /* ready */ return; @@ -4437,7 +4759,7 @@ bus->tx_seq = SDPCM_SEQ_WRAP - 1; /* single-threaded workqueue */ - wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, + wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM | WQ_HIGHPRI, dev_name(&sdiodev->func1->dev)); if (!wq) { brcmf_err("insufficient memory to create txworkqueue\n"); @@ -4556,7 +4878,17 @@ * necessary cores. */ msleep(20); - brcmf_chip_set_passive(bus->ci); + if (bus->sdiodev->fmac_ulp.ulp_state == + FMAC_ULP_ENTRY_RECV) { + brcmf_chip_ulp_reset_lhl_regs(bus->ci); + brcmf_chip_reset_pmu_regs(bus->ci); + } else { + brcmf_chip_set_passive(bus->ci); + } + /* Reset the PMU, backplane and all the + * cores by using the PMUWatchdogCounter. + */ + brcmf_chip_reset_watchdog(bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func1); } @@ -4564,6 +4896,9 @@ } if (bus->sdiodev->settings) brcmf_release_module_param(bus->sdiodev->settings); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + brcmf_btsdio_detach(bus->sdiodev->bus_if); +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ kfree(bus->rxbuf); kfree(bus->hdrbuf); @@ -4612,3 +4947,39 @@ return ret; } +/* Check F2 Ready bit before sending data to Firmware */ +static int +brcmf_sdio_f2_ready(struct brcmf_sdio *bus) +{ + int ret = -1; + int iordy_status = 0; + + sdio_claim_host(bus->sdiodev->func1); + /* Read the status of IOR2 */ + iordy_status = brcmf_sdiod_func0_rb(bus->sdiodev, SDIO_CCCR_IORx, NULL); + + sdio_release_host(bus->sdiodev->func1); + ret = iordy_status & SDIO_FUNC_ENABLE_2; + return ret; +} + +static int brcmf_ulp_event_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data) +{ + int err = 0; + struct brcmf_bus *bus_if = ifp->drvr->bus_if; + struct brcmf_sdio_dev *sdiodev; + struct brcmf_sdio *bus; + struct brcmf_ulp_event *ulp_event = (struct brcmf_ulp_event *)data; + + sdiodev = bus_if->bus_priv.sdio; + bus = sdiodev->bus; + + brcmf_dbg(ULP, "Chip went to DS1 state : action %d\n", + ulp_event->ulp_dongle_action); + if (ulp_event->ulp_dongle_action == FMAC_ULP_ENTRY) + bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_ENTRY_RECV; + + return err; +} diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h 2022-01-06 12:45:53.826318157 -0500 @@ -165,6 +165,35 @@ struct brcmf_sdio; struct brcmf_sdiod_freezer; +/* ULP SHM Offsets info */ +struct ulp_shm_info { + u32 m_ulp_ctrl_sdio; + u32 m_ulp_wakeevt_ind; + u32 m_ulp_wakeind; + u32 m_ulp_phytxblk; +}; + +/* FMAC ULP state machine */ +#define FMAC_ULP_IDLE (0) +#define FMAC_ULP_ENTRY_RECV (1) +#define FMAC_ULP_TRIGGERED (2) + +/* BRCMF_E_ULP event data */ +#define FMAC_ULP_EVENT_VERSION 1 +#define FMAC_ULP_DISABLE_CONSOLE 1 /* Disable console */ +#define FMAC_ULP_UCODE_DOWNLOAD 2 /* Download ULP ucode file */ +#define FMAC_ULP_ENTRY 3 /* Inform ulp entry to Host */ + +struct brcmf_ulp { + uint ulp_state; + struct ulp_shm_info ulp_shm_offset; +}; + +struct brcmf_ulp_event { + u16 version; + u16 ulp_dongle_action; +}; + struct brcmf_sdio_dev { struct sdio_func *func1; struct sdio_func *func2; @@ -189,6 +218,8 @@ bool wowl_enabled; enum brcmf_sdiod_state state; struct brcmf_sdiod_freezer *freezer; + struct brcmf_ulp fmac_ulp; + bool ulp; }; /* sdio core registers */ @@ -379,4 +410,83 @@ int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); +/* SHM offsets */ +#define M_DS1_CTRL_SDIO(ptr) ((ptr).ulp_shm_offset.m_ulp_ctrl_sdio) +#define M_WAKEEVENT_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeevt_ind) +#define M_ULP_WAKE_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeind) +#define M_DS1_PHYTX_ERR_BLK(ptr) ((ptr).ulp_shm_offset.m_ulp_phytxblk) + +#define D11_BASE_ADDR 0x18001000 +#define D11_AXI_BASE_ADDR 0xE8000000 +#define D11_SHM_BASE_ADDR (D11_AXI_BASE_ADDR + 0x4000) + +#define D11REG_ADDR(offset) (D11_BASE_ADDR + (offset)) +#define D11IHR_ADDR(offset) (D11_AXI_BASE_ADDR + 0x400 + (2 * (offset))) +#define D11SHM_ADDR(offset) (D11_SHM_BASE_ADDR + (offset)) + +/* MacControl register */ +#define D11_MACCONTROL_REG D11REG_ADDR(0x120) +#define D11_MACCONTROL_REG_WAKE 0x4000000 + +/* HUDI Sequence SHM bits */ +#define C_DS1_CTRL_SDIO_DS1_SLEEP 0x1 +#define C_DS1_CTRL_SDIO_MAC_ON 0x2 +#define C_DS1_CTRL_SDIO_RADIO_PHY_ON 0x4 +#define C_DS1_CTRL_SDIO_DS1_EXIT 0x8 +#define C_DS1_CTRL_PROC_DONE 0x100 +#define C_DS1_CTRL_REQ_VALID 0x200 + +/* M_ULP_WAKEIND bits */ +#define C_WATCHDOG_EXPIRY BIT(0) +#define C_FCBS_ERROR BIT(1) +#define C_RETX_FAILURE BIT(2) +#define C_HOST_WAKEUP BIT(3) +#define C_INVALID_FCBS_BLOCK BIT(4) +#define C_HUDI_DS1_EXIT BIT(5) +#define C_LOB_SLEEP BIT(6) +#define C_DS1_PHY_TXERR BIT(9) +#define C_DS1_WAKE_TIMER BIT(10) + +#define PHYTX_ERR_BLK_SIZE 18 +#define D11SHM_FIRST2BYTE_MASK 0xFFFF0000 +#define D11SHM_SECOND2BYTE_MASK 0x0000FFFF +#define D11SHM_2BYTE_SHIFT 16 + +#define D11SHM_RD(sdh, offset, ret) \ + brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) + +/* SHM Read is motified based on SHM 4 byte alignment as SHM size is 2 bytes and + * 2 byte is currently not working on FMAC + * If SHM address is not 4 byte aligned, then right shift by 16 + * otherwise, mask the first two MSB bytes + * Suppose data in address 7260 is 0x440002 and it is 4 byte aligned + * Correct SHM value is 0x2 for this SHM offset and next SHM value is 0x44 + */ +#define D11SHM_RDW(sdh, offset, ret) \ + ((offset % 4) ? \ + (brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) \ + >> D11SHM_2BYTE_SHIFT) : \ + (brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) \ + & D11SHM_SECOND2BYTE_MASK)) + +/* SHM is of size 2 bytes, 4 bytes write will overwrite other SHM's + * First read 4 bytes and then clear the required two bytes based on + * 4 byte alignment, then update the required value and write the + * 4 byte value now + */ +#define D11SHM_WR(sdh, offset, val, mask, ret) \ + do { \ + if ((offset) % 4) \ + val = (val & D11SHM_SECOND2BYTE_MASK) | \ + ((mask) << D11SHM_2BYTE_SHIFT); \ + else \ + val = (mask) | (val & D11SHM_FIRST2BYTE_MASK); \ + brcmf_sdiod_writel(sdh, D11SHM_ADDR(offset), val, ret); \ + } while (0) +#define D11REG_WR(sdh, addr, val, ret) \ + brcmf_sdiod_writel(sdh, addr, val, ret) + +#define D11REG_RD(sdh, addr, ret) \ + brcmf_sdiod_readl(sdh, addr, ret) + #endif /* BRCMFMAC_SDIO_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c 2022-01-06 12:45:53.826318157 -0500 @@ -19,6 +19,7 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "cfg80211.h" #define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -39,7 +40,7 @@ BRCMF_FW_DEF(43236B, "brcmfmac43236b"); BRCMF_FW_DEF(43242A, "brcmfmac43242a"); BRCMF_FW_DEF(43569, "brcmfmac43569"); -BRCMF_FW_DEF(4373, "brcmfmac4373"); +CY_FW_DEF(4373, "cyfmac4373"); static const struct brcmf_firmware_mapping brcmf_usb_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -638,6 +639,10 @@ goto fail; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + if (devinfo->bus_pub.bus->allow_skborphan) + skb_orphan(skb); +#endif req->skb = skb; req->devinfo = devinfo; usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe, @@ -1219,8 +1224,14 @@ if (ret) goto error; + if (BRCMF_FWCON_ON()) { + ret = brcmf_fwlog_attach(devinfo->dev); + if (ret) + goto error; + } + /* Attach to the common driver interface */ - ret = brcmf_attach(devinfo->dev); + ret = brcmf_attach(devinfo->dev, true); if (ret) goto error; @@ -1279,6 +1290,9 @@ bus->ops = &brcmf_usb_bus_ops; bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + bus->allow_skborphan = true; +#endif #ifdef CONFIG_PM bus->wowl_supported = true; #endif @@ -1295,9 +1309,17 @@ ret = brcmf_alloc(devinfo->dev, devinfo->settings); if (ret) goto fail; - ret = brcmf_attach(devinfo->dev); + + if (BRCMF_FWCON_ON()) { + ret = brcmf_fwlog_attach(devinfo->dev); + if (ret) + goto fail; + } + + ret = brcmf_attach(devinfo->dev, true); if (ret) goto fail; + /* we are done */ complete(&devinfo->dev_init_done); return 0; @@ -1480,8 +1502,22 @@ { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + struct brcmf_bus *bus; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; brcmf_dbg(USB, "Enter\n"); + + bus = devinfo->bus_pub.bus; + config = bus->drvr->config; + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err("timed out wait for cfg80211 suspended\n"); + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; brcmf_cancel_all_urbs(devinfo); device_set_wakeup_enable(devinfo->dev, true); @@ -1584,8 +1620,12 @@ usb_deregister(&brcmf_usbdrvr); } -int brcmf_usb_register(void) +void brcmf_usb_register(void) { + int ret; + brcmf_dbg(USB, "Enter\n"); - return usb_register(&brcmf_usbdrvr); + ret = usb_register(&brcmf_usbdrvr); + if (ret) + brcmf_err("usb_register failed %d\n", ret); } diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c 2022-01-06 12:45:53.826318157 -0500 @@ -64,6 +64,16 @@ *(char *)(dcmd_buf + len) = '\0'; } + if (cmdhdr->cmd == BRCMF_C_SET_AP) { + if (*(int *)(dcmd_buf) == 1) { + ifp->vif->wdev.iftype = NL80211_IFTYPE_AP; + brcmf_net_setcarrier(ifp, true); + } else { + ifp->vif->wdev.iftype = NL80211_IFTYPE_STATION; + } + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + } + if (cmdhdr->set) ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, ret_len); @@ -104,6 +114,110 @@ return ret; } +static int brcmf_cfg80211_vndr_cmds_int_get(struct brcmf_if *ifp, + u32 cmd, struct wiphy *wiphy) +{ + struct sk_buff *reply; + int get_value = 0; + int ret; + + ret = brcmf_fil_cmd_int_get(ifp, cmd, &get_value); + if (ret) + brcmf_err("Command %u get failure. Error : %d\n", cmd, ret); + + reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(int)); + nla_put_nohdr(reply, sizeof(int), &get_value); + ret = cfg80211_vendor_cmd_reply(reply); + if (ret) + brcmf_err("Command %u failure. Error : %d\n", cmd, ret); + return ret; +} + +static int brcmf_cfg80211_vndr_cmds_int_set(struct brcmf_if *ifp, int val, u32 cmd) +{ + int ret; + + ret = brcmf_fil_cmd_int_set(ifp, cmd, val); + if (ret < 0) + brcmf_err("Command %u set failure. Error : %d\n", cmd, ret); + return ret; +} + +static int brcmf_cfg80211_vndr_cmds_frameburst(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret; + int val = *(int *)data; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + + if (val == 0x0 || val == 0x1) + ret = brcmf_cfg80211_vndr_cmds_int_set(ifp, val, + BRCMF_C_SET_FAKEFRAG); + else if (val == 0xff) + ret = brcmf_cfg80211_vndr_cmds_int_get(ifp, + BRCMF_C_GET_FAKEFRAG, + wiphy); + else + brcmf_err("Invalid Input\n"); + + return ret; +} + +s32 +brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) + +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct sk_buff *skb; + struct nlattr *phy_temp_data; + u32 version, temp, tempdelta; + struct brcmf_phy_temp_evt *phy_temp_evt; + + phy_temp_evt = (struct brcmf_phy_temp_evt *)data; + + version = le32_to_cpu(phy_temp_evt->version); + temp = le32_to_cpu(phy_temp_evt->temp); + tempdelta = le32_to_cpu(phy_temp_evt->tempdelta); + + skb = cfg80211_vendor_event_alloc(wiphy, NULL, + sizeof(*phy_temp_evt), + BRCMF_VNDR_EVTS_PHY_TEMP, + GFP_KERNEL); + + if (!skb) { + brcmf_dbg(EVENT, "NO MEM: can't allocate skb for vendor PHY_TEMP_EVENT\n"); + return -ENOMEM; + } + + phy_temp_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_EVENTS); + if (!phy_temp_data) { + nla_nest_cancel(skb, phy_temp_data); + kfree_skb(skb); + brcmf_dbg(EVENT, "skb could not nest vendor attributes\n"); + return -EMSGSIZE; + } + + if (nla_put_u32(skb, BRCMF_NLATTR_VERS, version) || + nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMP, temp) || + nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMPDELTA, tempdelta)) { + kfree_skb(skb); + brcmf_dbg(EVENT, "NO ROOM in skb for vendor PHY_TEMP_EVENT\n"); + return -EMSGSIZE; + } + + nla_nest_end(skb, phy_temp_data); + + cfg80211_vendor_event(skb, GFP_KERNEL); + return 0; +} + const struct wiphy_vendor_command brcmf_vendor_cmds[] = { { { @@ -115,4 +229,21 @@ .policy = VENDOR_CMD_RAW_DATA, .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler }, + { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_CMDS_FRAMEBURST + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .policy = VENDOR_CMD_RAW_DATA, + .doit = brcmf_cfg80211_vndr_cmds_frameburst + }, +}; + +const struct nl80211_vendor_cmd_info brcmf_vendor_events[] = { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_EVTS_PHY_TEMP, + }, }; diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h 2022-01-06 12:45:53.826318157 -0500 @@ -11,9 +11,15 @@ enum brcmf_vndr_cmds { BRCMF_VNDR_CMDS_UNSPEC, BRCMF_VNDR_CMDS_DCMD, + BRCMF_VNDR_CMDS_FRAMEBURST, BRCMF_VNDR_CMDS_LAST }; +enum brcmf_vndr_evts { + BRCMF_VNDR_EVTS_PHY_TEMP, + BRCMF_VNDR_EVTS_LAST +}; + /** * enum brcmf_nlattrs - nl80211 message attributes * @@ -25,11 +31,21 @@ BRCMF_NLATTR_LEN, BRCMF_NLATTR_DATA, + BRCMF_NLATTR_VERS, + BRCMF_NLATTR_PHY_TEMP, + BRCMF_NLATTR_PHY_TEMPDELTA, __BRCMF_NLATTR_AFTER_LAST, BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 }; +/* structure of event sent up by firmware: is this the right place for it? */ +struct brcmf_phy_temp_evt { + __le32 version; + __le32 temp; + __le32 tempdelta; +} __packed; + /** * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd * support @@ -49,5 +65,9 @@ }; extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; +extern const struct nl80211_vendor_cmd_info brcmf_vendor_events[]; +s32 brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); #endif /* _vendor_h_ */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c 2022-01-06 12:45:53.826318157 -0500 @@ -1221,7 +1221,6 @@ { struct brcms_info *wl; struct ieee80211_hw *hw; - int ret; dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n", pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class, @@ -1246,16 +1245,11 @@ wl = brcms_attach(pdev); if (!wl) { pr_err("%s: brcms_attach failed!\n", __func__); - ret = -ENODEV; - goto err_free_ieee80211; + return -ENODEV; } brcms_led_register(wl); return 0; - -err_free_ieee80211: - ieee80211_free_hw(hw); - return ret; } static int brcms_suspend(struct bcma_device *pdev) diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h 2022-01-06 12:45:53.826318157 -0500 @@ -49,8 +49,11 @@ #define BRCM_CC_4366_CHIP_ID 0x4366 #define BRCM_CC_43664_CHIP_ID 43664 #define BRCM_CC_4371_CHIP_ID 0x4371 +#define CY_CC_43430_CHIP_ID 43430 +#define CY_CC_43439_CHIP_ID 43439 #define CY_CC_4373_CHIP_ID 0x4373 #define CY_CC_43012_CHIP_ID 43012 +#define CY_CC_89459_CHIP_ID 0x4355 /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e @@ -69,6 +72,7 @@ #define BRCM_PCIE_4356_DEVICE_ID 0x43ec #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_43570_RAW_DEVICE_ID 0xaa31 #define BRCM_PCIE_4358_DEVICE_ID 0x43e9 #define BRCM_PCIE_4359_DEVICE_ID 0x43ef #define BRCM_PCIE_43602_DEVICE_ID 0x43ba @@ -83,7 +87,11 @@ #define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 #define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 #define BRCM_PCIE_4371_DEVICE_ID 0x440d - +#define CY_PCIE_89459_DEVICE_ID 0x4415 +#define CY_PCIE_89459_RAW_DEVICE_ID 0x4355 +#define CY_PCIE_54591_DEVICE_ID 0x4417 +#define CY_PCIE_54590_DEVICE_ID 0x4416 +#define CY_PCIE_54594_DEVICE_ID 0x441a /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h 2022-01-06 12:45:53.826318157 -0500 @@ -233,6 +233,11 @@ #define WPA3_AUTH_SAE_PSK 0x40000 /* SAE with 4-way handshake */ +#define WFA_AUTH_DPP 0x200000 /* WFA DPP AUTH */ + +#define WFA_OUI "\x50\x6F\x9A" /* WFA OUI */ +#define DPP_VER 0x1A /* WFA DPP v1.0 */ + #define DOT11_DEFAULT_RTS_LEN 2347 #define DOT11_DEFAULT_FRAG_LEN 2346 diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h --- a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h 2022-01-06 12:45:53.826318157 -0500 @@ -214,8 +214,197 @@ u32 PAD[3]; u32 retention_grpidx; /* 0x680 */ u32 retention_grpctl; /* 0x684 */ - u32 PAD[94]; - u16 sromotp[768]; + u32 mac_res_req_timer; /* 0x688 */ + u32 mac_res_req_mask; /* 0x68c */ + u32 PAD[18]; + u32 pmucontrol_ext; /* 0x6d8 */ + u32 slowclkperiod; /* 0x6dc */ + u32 PAD[8]; + u32 pmuintmask0; /* 0x700 */ + u32 pmuintmask1; /* 0x704 */ + u32 PAD[14]; + u32 pmuintstatus; /* 0x740 */ + u32 extwakeupstatus; /* 0x744 */ + u32 watchdog_res_mask; /* 0x748 */ + u32 swscratch; /* 0x750 */ + u32 PAD[3]; + u32 extwakemask[2]; /* 0x760-0x764 */ + u32 PAD[2]; + u32 extwakereqmask[2]; /* 0x770-0x774 */ + u32 PAD[2]; + u32 pmuintctrl0; /* 0x780 */ + u32 pmuintctrl1; /* 0x784 */ + u32 PAD[2]; + u32 extwakectrl[2]; /* 0x790 */ +}; + +#define CHIPGCIREGOFFS(field) offsetof(struct chipgciregs, field) + +struct chipgciregs { + u32 gci_corecaps0; /* 0x000 */ + u32 gci_corecaps1; /* 0x004 */ + u32 gci_corecaps2; /* 0x008 */ + u32 gci_corectrl; /* 0x00c */ + u32 gci_corestat; /* 0x010 */ + u32 gci_intstat; /* 0x014 */ + u32 gci_intmask; /* 0x018 */ + u32 gci_wakemask; /* 0x01c */ + u32 gci_levelintstat; /* 0x020 */ + u32 gci_eventintstat; /* 0x024 */ + u32 gci_wakelevelintstat; /* 0x028 */ + u32 gci_wakeeventintstat; /* 0x02c */ + u32 semaphoreintstatus; /* 0x030 */ + u32 semaphoreintmask; /* 0x034 */ + u32 semaphorerequest; /* 0x038 */ + u32 semaphorereserve; /* 0x03c */ + u32 gci_indirect_addr; /* 0x040 */ + u32 gci_gpioctl; /* 0x044 */ + u32 gci_gpiostatus; /* 0x048 */ + u32 gci_gpiomask; /* 0x04c */ + u32 eventsummary; /* 0x050 */ + u32 gci_miscctl; /* 0x054 */ + u32 gci_gpiointmask; /* 0x058 */ + u32 gci_gpiowakemask; /* 0x05c */ + u32 gci_input[32]; /* 0x060 */ + u32 gci_event[32]; /* 0x0e0 */ + u32 gci_output[4]; /* 0x160 */ + u32 gci_control_0; /* 0x170 */ + u32 gci_control_1; /* 0x174 */ + u32 gci_intpolreg; /* 0x178 */ + u32 gci_levelintmask; /* 0x17c */ + u32 gci_eventintmask; /* 0x180 */ + u32 wakelevelintmask; /* 0x184 */ + u32 wakeeventintmask; /* 0x188 */ + u32 hwmask; /* 0x18c */ + u32 PAD; + u32 gci_inbandeventintmask; /* 0x194 */ + u32 PAD; + u32 gci_inbandeventstatus; /* 0x19c */ + u32 gci_seciauxtx; /* 0x1a0 */ + u32 gci_seciauxrx; /* 0x1a4 */ + u32 gci_secitx_datatag; /* 0x1a8 */ + u32 gci_secirx_datatag; /* 0x1ac */ + u32 gci_secitx_datamask; /* 0x1b0 */ + u32 gci_seciusef0tx_reg; /* 0x1b4 */ + u32 gci_secif0tx_offset; /* 0x1b8 */ + u32 gci_secif0rx_offset; /* 0x1bc */ + u32 gci_secif1tx_offset; /* 0x1c0 */ + u32 gci_rxfifo_common_ctrl; /* 0x1c4 */ + u32 gci_rxfifoctrl; /* 0x1c8 */ + u32 gci_hw_sema_status; /* 0x1cc */ + u32 gci_seciuartescval; /* 0x1d0 */ + u32 gic_seciuartautobaudctr; /* 0x1d4 */ + u32 gci_secififolevel; /* 0x1d8 */ + u32 gci_seciuartdata; /* 0x1dc */ + u32 gci_secibauddiv; /* 0x1e0 */ + u32 gci_secifcr; /* 0x1e4 */ + u32 gci_secilcr; /* 0x1e8 */ + u32 gci_secimcr; /* 0x1ec */ + u32 gci_secilsr; /* 0x1f0 */ + u32 gci_secimsr; /* 0x1f4 */ + u32 gci_baudadj; /* 0x1f8 */ + u32 gci_inbandintmask; /* 0x1fc */ + u32 gci_chipctrl; /* 0x200 */ + u32 gci_chipsts; /* 0x204 */ + u32 gci_gpioout; /* 0x208 */ + u32 gci_gpioout_read; /* 0x20C */ + u32 gci_mpwaketx; /* 0x210 */ + u32 gci_mpwakedetect; /* 0x214 */ + u32 gci_seciin_ctrl; /* 0x218 */ + u32 gci_seciout_ctrl; /* 0x21C */ + u32 gci_seciin_auxfifo_en; /* 0x220 */ + u32 gci_seciout_txen_txbr; /* 0x224 */ + u32 gci_seciin_rxbrstatus; /* 0x228 */ + u32 gci_seciin_rxerrstatus; /* 0x22C */ + u32 gci_seciin_fcstatus; /* 0x230 */ + u32 gci_seciout_txstatus; /* 0x234 */ + u32 gci_seciout_txbrstatus; /* 0x238 */ + u32 wlan_mem_info; /* 0x23C */ + u32 wlan_bankxinfo; /* 0x240 */ + u32 bt_smem_select; /* 0x244 */ + u32 bt_smem_stby; /* 0x248 */ + u32 bt_smem_status; /* 0x24C */ + u32 wlan_bankxactivepda; /* 0x250 */ + u32 wlan_bankxsleeppda; /* 0x254 */ + u32 wlan_bankxkill; /* 0x258 */ + u32 PAD[41]; + u32 gci_chipid; /* 0x300 */ + u32 PAD[3]; + u32 otpstatus; /* 0x310 */ + u32 otpcontrol; /* 0x314 */ + u32 otpprog; /* 0x318 */ + u32 otplayout; /* 0x31c */ + u32 otplayoutextension; /* 0x320 */ + u32 otpcontrol1; /* 0x324 */ + u32 otpprogdata; /* 0x328 */ + u32 PAD[52]; + u32 otpECCstatus; /* 0x3FC */ + u32 PAD[512]; + u32 lhl_core_capab_adr; /* 0xC00 */ + u32 lhl_main_ctl_adr; /* 0xC04 */ + u32 lhl_pmu_ctl_adr; /* 0xC08 */ + u32 lhl_extlpo_ctl_adr; /* 0xC0C */ + u32 lpo_ctl_adr; /* 0xC10 */ + u32 lhl_lpo2_ctl_adr; /* 0xC14 */ + u32 lhl_osc32k_ctl_adr; /* 0xC18 */ + u32 lhl_clk_status_adr; /* 0xC1C */ + u32 lhl_clk_det_ctl_adr; /* 0xC20 */ + u32 lhl_clk_sel_adr; /* 0xC24 */ + u32 hidoff_cnt_adr[2]; /* 0xC28-0xC2C */ + u32 lhl_autoclk_ctl_adr; /* 0xC30 */ + u32 PAD; + u32 lhl_hibtim_adr; /* 0xC38 */ + u32 lhl_wl_ilp_val_adr; /* 0xC3C */ + u32 lhl_wl_armtim0_intrp_adr; /* 0xC40 */ + u32 lhl_wl_armtim0_st_adr; /* 0xC44 */ + u32 lhl_wl_armtim0_adr; /* 0xC48 */ + u32 PAD[9]; + u32 lhl_wl_mactim0_intrp_adr; /* 0xC70 */ + u32 lhl_wl_mactim0_st_adr; /* 0xC74 */ + u32 lhl_wl_mactim_int0_adr; /* 0xC78 */ + u32 lhl_wl_mactim_frac0_adr; /* 0xC7C */ + u32 lhl_wl_mactim1_intrp_adr; /* 0xC80 */ + u32 lhl_wl_mactim1_st_adr; /* 0xC84 */ + u32 lhl_wl_mactim_int1_adr; /* 0xC88 */ + u32 lhl_wl_mactim_frac1_adr; /* 0xC8C */ + u32 PAD[8]; + u32 gpio_int_en_port_adr[4]; /* 0xCB0-0xCBC */ + u32 gpio_int_st_port_adr[4]; /* 0xCC0-0xCCC */ + u32 gpio_ctrl_iocfg_p_adr[64]; /* 0xCD0-0xDCC */ + u32 gpio_gctrl_iocfg_p0_p39_adr; /* 0xDD0 */ + u32 gpio_gdsctrl_iocfg_p0_p25_p30_p39_adr; /* 0xDD4 */ + u32 gpio_gdsctrl_iocfg_p26_p29_adr; /* 0xDD8 */ + u32 PAD[8]; + u32 lhl_gpio_din0_adr; /* 0xDFC */ + u32 lhl_gpio_din1_adr; /* 0xE00 */ + u32 lhl_wkup_status_adr; /* 0xE04 */ + u32 lhl_ctl_adr; /* 0xE08 */ + u32 lhl_adc_ctl_adr; /* 0xE0C */ + u32 lhl_qdxyz_in_dly_adr; /* 0xE10 */ + u32 lhl_optctl_adr; /* 0xE14 */ + u32 lhl_optct2_adr; /* 0xE18 */ + u32 lhl_scanp_cntr_init_val_adr; /* 0xE1C */ + u32 lhl_opt_togg_val_adr[6]; /* 0xE20-0xE34 */ + u32 lhl_optx_smp_val_adr; /* 0xE38 */ + u32 lhl_opty_smp_val_adr; /* 0xE3C */ + u32 lhl_optz_smp_val_adr; /* 0xE40 */ + u32 lhl_hidoff_keepstate_adr[3]; /* 0xE44-0xE4C */ + u32 lhl_bt_slmboot_ctl0_adr[4]; /* 0xE50-0xE5C */ + u32 lhl_wl_fw_ctl; /* 0xE60 */ + u32 lhl_wl_hw_ctl_adr[2]; /* 0xE64-0xE68 */ + u32 lhl_bt_hw_ctl_adr; /* 0xE6C */ + u32 lhl_top_pwrseq_en_adr; /* 0xE70 */ + u32 lhl_top_pwrdn_ctl_adr; /* 0xE74 */ + u32 lhl_top_pwrup_ctl_adr; /* 0xE78 */ + u32 lhl_top_pwrseq_ctl_adr; /* 0xE7C */ + u32 lhl_top_pwrdn2_ctl_adr; /* 0xE80 */ + u32 lhl_top_pwrup2_ctl_adr; /* 0xE84 */ + u32 wpt_regon_intrp_cfg_adr; /* 0xE88 */ + u32 bt_regon_intrp_cfg_adr; /* 0xE8C */ + u32 wl_regon_intrp_cfg_adr; /* 0xE90 */ + u32 regon_intrp_st_adr; /* 0xE94 */ + u32 regon_intrp_en_adr; /* 0xE98 */ + }; /* chipid */ @@ -308,4 +497,6 @@ */ #define PMU_MAX_TRANSITION_DLY 15000 +#define DEFAULT_43012_MIN_RES_MASK 0x0f8bfe77 + #endif /* _SBCHIPC_H */ diff -Naur --no-dereference a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig 2022-01-06 12:45:53.822318139 -0500 @@ -34,6 +34,26 @@ config BRCMDBG bool "Broadcom driver debug functions" depends on BRCMSMAC || BRCMFMAC - select WANT_DEV_COREDUMP if BRCMFMAC + select WANT_DEV_COREDUMP help Selecting this enables additional code for debug purposes. + +config BRCMFMAC_PCIE_BARWIN_SZ + bool "Custom PCIE BAR window size support for FullMAC driver" + depends on BRCMFMAC + depends on PCI + default n + help + If you say Y here, the FMAC driver will use custom PCIE BAR + window size. Say Y to allow developers to use custom PCIE + BAR window size when HOST PCIE IP can support less then 4MB + BAR window. + +config BRCMFMAC_BT_SHARED_SDIO + bool "FMAC shares SDIO bus to Bluetooth" + depends on BRCMFMAC + depends on BRCMFMAC_SDIO + default n + help + Selecting this to enables sharing the SDIO bus interface between + Cypress BT and WiFi host drivers. diff -Naur --no-dereference a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c --- a/drivers/net/wireless/ti/wl1251/cmd.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/ti/wl1251/cmd.c 2022-01-06 12:45:53.826318157 -0500 @@ -63,7 +63,7 @@ * * @wl: wl struct * @buf: buffer containing the command, with all headers, must work with dma - * @len: length of the buffer + * @buf_len: length of the buffer * @answer: is answer needed */ int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) @@ -175,10 +175,8 @@ wl1251_debug(DEBUG_CMD, "cmd vbm"); vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); - if (!vbm) { - ret = -ENOMEM; - goto out; - } + if (!vbm) + return -ENOMEM; /* Count and period will be filled by the target */ vbm->tim.bitmap_ctrl = bitmap_control; @@ -213,10 +211,8 @@ wl1251_debug(DEBUG_CMD, "cmd data path"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } + if (!cmd) + return -ENOMEM; cmd->channel = channel; @@ -279,10 +275,8 @@ u8 *bssid; join = kzalloc(sizeof(*join), GFP_KERNEL); - if (!join) { - ret = -ENOMEM; - goto out; - } + if (!join) + return -ENOMEM; wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", bss_type == BSS_TYPE_IBSS ? " ibss" : "", @@ -324,10 +318,8 @@ wl1251_debug(DEBUG_CMD, "cmd set ps mode"); ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); - if (!ps_params) { - ret = -ENOMEM; - goto out; - } + if (!ps_params) + return -ENOMEM; ps_params->ps_mode = ps_mode; ps_params->send_null_data = 1; @@ -356,10 +348,8 @@ wl1251_debug(DEBUG_CMD, "cmd read memory"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } + if (!cmd) + return -ENOMEM; WARN_ON(len > MAX_READ_SIZE); len = min_t(size_t, len, MAX_READ_SIZE); @@ -401,10 +391,8 @@ cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); cmd = kzalloc(cmd_len, GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } + if (!cmd) + return -ENOMEM; cmd->size = cpu_to_le16(buf_len); diff -Naur --no-dereference a/drivers/net/wireless/ti/wl1251/debugfs.c b/drivers/net/wireless/ti/wl1251/debugfs.c --- a/drivers/net/wireless/ti/wl1251/debugfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/ti/wl1251/debugfs.c 2022-01-06 12:45:53.826318157 -0500 @@ -39,7 +39,7 @@ #define DEBUGFS_ADD(name, parent) \ wl->debugfs.name = debugfs_create_file(#name, 0400, parent, \ - wl, &name## _ops); \ + wl, &name## _ops) \ #define DEBUGFS_DEL(name) \ do { \ diff -Naur --no-dereference a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c --- a/drivers/net/wireless/ti/wlcore/main.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/ti/wlcore/main.c 2022-01-06 12:45:53.826318157 -0500 @@ -2227,7 +2227,7 @@ switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_P2P_CLIENT: wlvif->p2p = 1; - /* fall-through */ + fallthrough; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_DEVICE: wlvif->bss_type = BSS_TYPE_STA_BSS; @@ -2237,7 +2237,7 @@ break; case NL80211_IFTYPE_P2P_GO: wlvif->p2p = 1; - /* fall-through */ + fallthrough; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: wlvif->bss_type = BSS_TYPE_AP_BSS; @@ -5381,7 +5381,7 @@ if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) { ret = -EBUSY; - wl1271_error("exceeded max RX BA sessions"); + wl1271_debug(DEBUG_RX, "exceeded max RX BA sessions"); break; } diff -Naur --no-dereference a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c --- a/drivers/net/wireless/ti/wlcore/spi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/ti/wlcore/spi.c 2022-01-06 12:45:53.826318157 -0500 @@ -391,7 +391,7 @@ return ret; } -/** +/* * wl12xx_spi_set_block_size * * This function is not needed for spi mode, but need to be present. @@ -431,7 +431,6 @@ /** * wlcore_probe_of - DT node parsing. * @spi: SPI slave device parameters. - * @res: resource parameters. * @glue: wl12xx SPI bus to slave device glue parameters. * @pdev_data: wlcore device parameters */ diff -Naur --no-dereference a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c --- a/drivers/net/wireless/ti/wlcore/sysfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/net/wireless/ti/wlcore/sysfs.c 2022-01-06 12:45:53.826318157 -0500 @@ -100,7 +100,7 @@ struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; int ret; diff -Naur --no-dereference a/drivers/ntb/hw/epf/Kconfig b/drivers/ntb/hw/epf/Kconfig --- a/drivers/ntb/hw/epf/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/ntb/hw/epf/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,6 @@ +config NTB_EPF + tristate "Generic EPF Non-Transparent Bridge support" + depends on m + help + This driver supports EPF NTB on configurable endpoint. + If unsure, say N. diff -Naur --no-dereference a/drivers/ntb/hw/epf/Makefile b/drivers/ntb/hw/epf/Makefile --- a/drivers/ntb/hw/epf/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/ntb/hw/epf/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1 @@ +obj-$(CONFIG_NTB_EPF) += ntb_hw_epf.o diff -Naur --no-dereference a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c --- a/drivers/ntb/hw/epf/ntb_hw_epf.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Host side endpoint driver to implement Non-Transparent Bridge functionality + * + * Copyright (C) 2020 Texas Instruments + * Author: Kishon Vijay Abraham I + */ + +#include +#include +#include +#include +#include + +#define NTB_EPF_COMMAND 0x0 +#define CMD_CONFIGURE_DOORBELL 1 +#define CMD_TEARDOWN_DOORBELL 2 +#define CMD_CONFIGURE_MW 3 +#define CMD_TEARDOWN_MW 4 +#define CMD_LINK_UP 5 +#define CMD_LINK_DOWN 6 + +#define NTB_EPF_ARGUMENT 0x4 +#define MSIX_ENABLE BIT(16) + +#define NTB_EPF_CMD_STATUS 0x8 +#define COMMAND_STATUS_OK 1 +#define COMMAND_STATUS_ERROR 2 + +#define NTB_EPF_LINK_STATUS 0x0A +#define LINK_STATUS_UP BIT(0) + +#define NTB_EPF_TOPOLOGY 0x0C +#define NTB_EPF_LOWER_ADDR 0x10 +#define NTB_EPF_UPPER_ADDR 0x14 +#define NTB_EPF_LOWER_SIZE 0x18 +#define NTB_EPF_UPPER_SIZE 0x1C +#define NTB_EPF_MW_COUNT 0x20 +#define NTB_EPF_MW1_OFFSET 0x24 +#define NTB_EPF_SPAD_OFFSET 0x28 +#define NTB_EPF_SPAD_COUNT 0x2C +#define NTB_EPF_DB_ENTRY_SIZE 0x30 +#define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4) +#define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4) + +#define NTB_EPF_MIN_DB_COUNT 3 +#define NTB_EPF_MAX_DB_COUNT 31 +#define NTB_EPF_MW_OFFSET 2 + +#define NTB_EPF_COMMAND_TIMEOUT 1000 /* 1 Sec */ + +enum pci_barno { + BAR_0, + BAR_1, + BAR_2, + BAR_3, + BAR_4, + BAR_5, +}; + +struct ntb_epf_dev { + struct ntb_dev ntb; + struct device *dev; + /* Mutex to protect providing commands to NTB EPF */ + struct mutex cmd_lock; + + enum pci_barno ctrl_reg_bar; + enum pci_barno peer_spad_reg_bar; + enum pci_barno db_reg_bar; + + unsigned int mw_count; + unsigned int spad_count; + unsigned int db_count; + + void __iomem *ctrl_reg; + void __iomem *db_reg; + void __iomem *peer_spad_reg; + + unsigned int self_spad; + unsigned int peer_spad; + + int db_val; + u64 db_valid_mask; +}; + +#define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) + +struct ntb_epf_data { + /* BAR that contains both control region and self spad region */ + enum pci_barno ctrl_reg_bar; + /* BAR that contains peer spad region */ + enum pci_barno peer_spad_reg_bar; + /* BAR that contains Doorbell region and Memory window '1' */ + enum pci_barno db_reg_bar; +}; + +static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command, + u32 argument) +{ + ktime_t timeout; + bool timedout; + int ret = 0; + u32 status; + + mutex_lock(&ndev->cmd_lock); + writel(argument, ndev->ctrl_reg + NTB_EPF_ARGUMENT); + writel(command, ndev->ctrl_reg + NTB_EPF_COMMAND); + + timeout = ktime_add_ms(ktime_get(), NTB_EPF_COMMAND_TIMEOUT); + while (1) { + timedout = ktime_after(ktime_get(), timeout); + status = readw(ndev->ctrl_reg + NTB_EPF_CMD_STATUS); + + if (status == COMMAND_STATUS_ERROR) { + ret = -EINVAL; + break; + } + + if (status == COMMAND_STATUS_OK) + break; + + if (WARN_ON(timedout)) { + ret = -ETIMEDOUT; + break; + } + + usleep_range(5, 10); + } + + writew(0, ndev->ctrl_reg + NTB_EPF_CMD_STATUS); + mutex_unlock(&ndev->cmd_lock); + + return ret; +} + +static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx) +{ + struct device *dev = ndev->dev; + + if (idx < 0 || idx > ndev->mw_count) { + dev_err(dev, "Unsupported Memory Window index %d\n", idx); + return -EINVAL; + } + + return idx + 2; +} + +static int ntb_epf_mw_count(struct ntb_dev *ntb, int pidx) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + + if (pidx != NTB_DEF_PEER_IDX) { + dev_err(dev, "Unsupported Peer ID %d\n", pidx); + return -EINVAL; + } + + return ndev->mw_count; +} + +static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx, + resource_size_t *addr_align, + resource_size_t *size_align, + resource_size_t *size_max) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + int bar; + + if (pidx != NTB_DEF_PEER_IDX) { + dev_err(dev, "Unsupported Peer ID %d\n", pidx); + return -EINVAL; + } + + bar = ntb_epf_mw_to_bar(ndev, idx); + if (bar < 0) + return bar; + + if (addr_align) + *addr_align = SZ_4K; + + if (size_align) + *size_align = 1; + + if (size_max) + *size_max = pci_resource_len(ndev->ntb.pdev, bar); + + return 0; +} + +static u64 ntb_epf_link_is_up(struct ntb_dev *ntb, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + u32 status; + + status = readw(ndev->ctrl_reg + NTB_EPF_LINK_STATUS); + + return status & LINK_STATUS_UP; +} + +static u32 ntb_epf_spad_read(struct ntb_dev *ntb, int idx) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + u32 offset; + + if (idx < 0 || idx >= ndev->spad_count) { + dev_err(dev, "READ: Invalid ScratchPad Index %d\n", idx); + return 0; + } + + offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); + offset += (idx << 2); + + return readl(ndev->ctrl_reg + offset); +} + +static int ntb_epf_spad_write(struct ntb_dev *ntb, + int idx, u32 val) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + u32 offset; + + if (idx < 0 || idx >= ndev->spad_count) { + dev_err(dev, "WRITE: Invalid ScratchPad Index %d\n", idx); + return -EINVAL; + } + + offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); + offset += (idx << 2); + writel(val, ndev->ctrl_reg + offset); + + return 0; +} + +static u32 ntb_epf_peer_spad_read(struct ntb_dev *ntb, int pidx, int idx) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + u32 offset; + + if (pidx != NTB_DEF_PEER_IDX) { + dev_err(dev, "Unsupported Peer ID %d\n", pidx); + return -EINVAL; + } + + if (idx < 0 || idx >= ndev->spad_count) { + dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx); + return -EINVAL; + } + + offset = (idx << 2); + return readl(ndev->peer_spad_reg + offset); +} + +static int ntb_epf_peer_spad_write(struct ntb_dev *ntb, int pidx, + int idx, u32 val) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + u32 offset; + + if (pidx != NTB_DEF_PEER_IDX) { + dev_err(dev, "Unsupported Peer ID %d\n", pidx); + return -EINVAL; + } + + if (idx < 0 || idx >= ndev->spad_count) { + dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx); + return -EINVAL; + } + + offset = (idx << 2); + writel(val, ndev->peer_spad_reg + offset); + + return 0; +} + +static int ntb_epf_link_enable(struct ntb_dev *ntb, + enum ntb_speed max_speed, + enum ntb_width max_width) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + int ret; + + ret = ntb_epf_send_command(ndev, CMD_LINK_UP, 0); + if (ret) { + dev_err(dev, "Fail to enable link\n"); + return ret; + } + + return 0; +} + +static int ntb_epf_link_disable(struct ntb_dev *ntb) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + int ret; + + ret = ntb_epf_send_command(ndev, CMD_LINK_DOWN, 0); + if (ret) { + dev_err(dev, "Fail to disable link\n"); + return ret; + } + + return 0; +} + +static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) +{ + struct ntb_epf_dev *ndev = dev; + int irq_no; + + irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0); + ndev->db_val = irq_no + 1; + + if (irq_no == 0) + ntb_link_event(&ndev->ntb); + else + ntb_db_event(&ndev->ntb, irq_no); + + return IRQ_HANDLED; +} + +static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) +{ + struct pci_dev *pdev = ndev->ntb.pdev; + struct device *dev = ndev->dev; + u32 argument = MSIX_ENABLE; + int irq; + int ret; + int i; + + irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, PCI_IRQ_MSIX); + if (irq < 0) { + dev_dbg(dev, "Failed to get MSIX interrupts\n"); + irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, + PCI_IRQ_MSI); + if (irq < 0) { + dev_err(dev, "Failed to get MSI interrupts\n"); + return irq; + } + argument &= ~MSIX_ENABLE; + } + + for (i = 0; i < irq; i++) { + ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, + 0, "ntb_epf", ndev); + if (ret) { + dev_err(dev, "Failed to request irq\n"); + goto err_request_irq; + } + } + + ndev->db_count = irq - 1; + + ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL, + argument | irq); + if (ret) { + dev_err(dev, "Failed to configure doorbell\n"); + goto err_configure_db; + } + + return 0; + +err_configure_db: + for (i = 0; i < ndev->db_count + 1; i++) + free_irq(pci_irq_vector(pdev, i), ndev); + +err_request_irq: + pci_free_irq_vectors(pdev); + + return ret; +} + +static int ntb_epf_peer_mw_count(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->mw_count; +} + +static int ntb_epf_spad_count(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->spad_count; +} + +static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->db_valid_mask; +} + +static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) +{ + return 0; +} + +static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, + dma_addr_t addr, resource_size_t size) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + resource_size_t mw_size; + int bar; + + if (pidx != NTB_DEF_PEER_IDX) { + dev_err(dev, "Unsupported Peer ID %d\n", pidx); + return -EINVAL; + } + + bar = idx + NTB_EPF_MW_OFFSET; + + mw_size = pci_resource_len(ntb->pdev, bar); + + if (size > mw_size) { + dev_err(dev, "Size:%pa is greater than the MW size %pa\n", + &size, &mw_size); + return -EINVAL; + } + + writel(lower_32_bits(addr), ndev->ctrl_reg + NTB_EPF_LOWER_ADDR); + writel(upper_32_bits(addr), ndev->ctrl_reg + NTB_EPF_UPPER_ADDR); + writel(lower_32_bits(size), ndev->ctrl_reg + NTB_EPF_LOWER_SIZE); + writel(upper_32_bits(size), ndev->ctrl_reg + NTB_EPF_UPPER_SIZE); + ntb_epf_send_command(ndev, CMD_CONFIGURE_MW, idx); + + return 0; +} + +static int ntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + struct device *dev = ndev->dev; + int ret = 0; + + ntb_epf_send_command(ndev, CMD_TEARDOWN_MW, idx); + if (ret) + dev_err(dev, "Failed to teardown memory window\n"); + + return ret; +} + +static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx, + phys_addr_t *base, resource_size_t *size) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + u32 offset = 0; + int bar; + + if (idx == 0) + offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET); + + bar = idx + NTB_EPF_MW_OFFSET; + + if (base) + *base = pci_resource_start(ndev->ntb.pdev, bar) + offset; + + if (size) + *size = pci_resource_len(ndev->ntb.pdev, bar) - offset; + + return 0; +} + +static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + u32 interrupt_num = ffs(db_bits) + 1; + struct device *dev = ndev->dev; + u32 db_entry_size; + u32 db_offset; + u32 db_data; + + if (interrupt_num > ndev->db_count) { + dev_err(dev, "DB interrupt %d greater than Max Supported %d\n", + interrupt_num, ndev->db_count); + return -EINVAL; + } + + db_entry_size = readl(ndev->ctrl_reg + NTB_EPF_DB_ENTRY_SIZE); + + db_data = readl(ndev->ctrl_reg + NTB_EPF_DB_DATA(interrupt_num)); + db_offset = readl(ndev->ctrl_reg + NTB_EPF_DB_OFFSET(interrupt_num)); + writel(db_data, ndev->db_reg + (db_entry_size * interrupt_num) + + db_offset); + + return 0; +} + +static u64 ntb_epf_db_read(struct ntb_dev *ntb) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + + return ndev->db_val; +} + +static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) +{ + return 0; +} + +static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + + ndev->db_val = 0; + + return 0; +} + +static const struct ntb_dev_ops ntb_epf_ops = { + .mw_count = ntb_epf_mw_count, + .spad_count = ntb_epf_spad_count, + .peer_mw_count = ntb_epf_peer_mw_count, + .db_valid_mask = ntb_epf_db_valid_mask, + .db_set_mask = ntb_epf_db_set_mask, + .mw_set_trans = ntb_epf_mw_set_trans, + .mw_clear_trans = ntb_epf_mw_clear_trans, + .peer_mw_get_addr = ntb_epf_peer_mw_get_addr, + .link_enable = ntb_epf_link_enable, + .spad_read = ntb_epf_spad_read, + .spad_write = ntb_epf_spad_write, + .peer_spad_read = ntb_epf_peer_spad_read, + .peer_spad_write = ntb_epf_peer_spad_write, + .peer_db_set = ntb_epf_peer_db_set, + .db_read = ntb_epf_db_read, + .mw_get_align = ntb_epf_mw_get_align, + .link_is_up = ntb_epf_link_is_up, + .db_clear_mask = ntb_epf_db_clear_mask, + .db_clear = ntb_epf_db_clear, + .link_disable = ntb_epf_link_disable, +}; + +static inline void ntb_epf_init_struct(struct ntb_epf_dev *ndev, + struct pci_dev *pdev) +{ + ndev->ntb.pdev = pdev; + ndev->ntb.topo = NTB_TOPO_NONE; + ndev->ntb.ops = &ntb_epf_ops; +} + +static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) +{ + struct device *dev = ndev->dev; + int ret; + + /* One Link interrupt and rest doorbell interrupt */ + ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1, + NTB_EPF_MAX_DB_COUNT + 1); + if (ret) { + dev_err(dev, "Failed to init ISR\n"); + return ret; + } + + ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; + ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); + ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); + + return 0; +} + +static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, + struct pci_dev *pdev) +{ + struct device *dev = ndev->dev; + int ret; + + pci_set_drvdata(pdev, ndev); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "Cannot enable PCI device\n"); + goto err_pci_enable; + } + + ret = pci_request_regions(pdev, "ntb"); + if (ret) { + dev_err(dev, "Cannot obtain PCI resources\n"); + goto err_pci_regions; + } + + pci_set_master(pdev); + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "Cannot set DMA mask\n"); + goto err_dma_mask; + } + dev_warn(&pdev->dev, "Cannot DMA highmem\n"); + } + + ndev->ctrl_reg = pci_iomap(pdev, ndev->ctrl_reg_bar, 0); + if (!ndev->ctrl_reg) { + ret = -EIO; + goto err_dma_mask; + } + + ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); + if (!ndev->peer_spad_reg) { + ret = -EIO; + goto err_dma_mask; + } + + ndev->db_reg = pci_iomap(pdev, ndev->db_reg_bar, 0); + if (!ndev->db_reg) { + ret = -EIO; + goto err_dma_mask; + } + + return 0; + +err_dma_mask: + pci_clear_master(pdev); + +err_pci_regions: + pci_disable_device(pdev); + +err_pci_enable: + pci_set_drvdata(pdev, NULL); + + return ret; +} + +static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev) +{ + struct pci_dev *pdev = ndev->ntb.pdev; + + pci_iounmap(pdev, ndev->ctrl_reg); + pci_iounmap(pdev, ndev->peer_spad_reg); + pci_iounmap(pdev, ndev->db_reg); + + pci_clear_master(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev) +{ + struct pci_dev *pdev = ndev->ntb.pdev; + int i; + + ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1); + + for (i = 0; i < ndev->db_count + 1; i++) + free_irq(pci_irq_vector(pdev, i), ndev); + pci_free_irq_vectors(pdev); +} + +static int ntb_epf_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + enum pci_barno peer_spad_reg_bar = BAR_1; + enum pci_barno ctrl_reg_bar = BAR_0; + enum pci_barno db_reg_bar = BAR_2; + struct device *dev = &pdev->dev; + struct ntb_epf_data *data; + struct ntb_epf_dev *ndev; + int ret; + + if (pci_is_bridge(pdev)) + return -ENODEV; + + ndev = devm_kzalloc(dev, sizeof(*ndev), GFP_KERNEL); + if (!ndev) + return -ENOMEM; + + data = (struct ntb_epf_data *)id->driver_data; + if (data) { + if (data->peer_spad_reg_bar) + peer_spad_reg_bar = data->peer_spad_reg_bar; + if (data->ctrl_reg_bar) + ctrl_reg_bar = data->ctrl_reg_bar; + if (data->db_reg_bar) + db_reg_bar = data->db_reg_bar; + } + + ndev->peer_spad_reg_bar = peer_spad_reg_bar; + ndev->ctrl_reg_bar = ctrl_reg_bar; + ndev->db_reg_bar = db_reg_bar; + ndev->dev = dev; + + ntb_epf_init_struct(ndev, pdev); + mutex_init(&ndev->cmd_lock); + + ret = ntb_epf_init_pci(ndev, pdev); + if (ret) { + dev_err(dev, "Failed to init PCI\n"); + return ret; + } + + ret = ntb_epf_init_dev(ndev); + if (ret) { + dev_err(dev, "Failed to init device\n"); + goto err_init_dev; + } + + ret = ntb_register_device(&ndev->ntb); + if (ret) { + dev_err(dev, "Failed to register NTB device\n"); + goto err_register_dev; + } + + return 0; + +err_register_dev: + ntb_epf_cleanup_isr(ndev); + +err_init_dev: + ntb_epf_deinit_pci(ndev); + + return ret; +} + +static void ntb_epf_pci_remove(struct pci_dev *pdev) +{ + struct ntb_epf_dev *ndev = pci_get_drvdata(pdev); + + ntb_unregister_device(&ndev->ntb); + ntb_epf_cleanup_isr(ndev); + ntb_epf_deinit_pci(ndev); +} + +static const struct ntb_epf_data j721e_data = { + .ctrl_reg_bar = BAR_0, + .peer_spad_reg_bar = BAR_1, + .db_reg_bar = BAR_2, +}; + +static const struct pci_device_id ntb_epf_pci_tbl[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E), + .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, + .driver_data = (kernel_ulong_t)&j721e_data, + }, + { }, +}; + +static struct pci_driver ntb_epf_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = ntb_epf_pci_tbl, + .probe = ntb_epf_pci_probe, + .remove = ntb_epf_pci_remove, +}; +module_pci_driver(ntb_epf_pci_driver); + +MODULE_DESCRIPTION("PCI ENDPOINT NTB HOST DRIVER"); +MODULE_AUTHOR("Kishon Vijay Abraham I "); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/ntb/hw/Kconfig b/drivers/ntb/hw/Kconfig --- a/drivers/ntb/hw/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/ntb/hw/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -2,4 +2,5 @@ source "drivers/ntb/hw/amd/Kconfig" source "drivers/ntb/hw/idt/Kconfig" source "drivers/ntb/hw/intel/Kconfig" +source "drivers/ntb/hw/epf/Kconfig" source "drivers/ntb/hw/mscc/Kconfig" diff -Naur --no-dereference a/drivers/ntb/hw/Makefile b/drivers/ntb/hw/Makefile --- a/drivers/ntb/hw/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/ntb/hw/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -2,4 +2,5 @@ obj-$(CONFIG_NTB_AMD) += amd/ obj-$(CONFIG_NTB_IDT) += idt/ obj-$(CONFIG_NTB_INTEL) += intel/ +obj-$(CONFIG_NTB_EPF) += epf/ obj-$(CONFIG_NTB_SWITCHTEC) += mscc/ diff -Naur --no-dereference a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c 2022-01-06 12:45:53.826318157 -0500 @@ -16,11 +16,22 @@ #define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1 #define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3 -static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, +static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_header *hdr) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie *pcie = &ep->pcie; + u32 reg; + + if (vfn > 1) { + dev_dbg(&epc->dev, "Only Virtual Function #1 has deviceID\n"); + return 0; + } else if (vfn == 1) { + reg = cap + PCI_SRIOV_VF_DID; + cdns_pcie_ep_fn_writew(pcie, fn, reg, hdr->deviceid); + return 0; + } cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid); cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid); @@ -47,16 +58,18 @@ return 0; } -static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, +static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie_epf *epf = &ep->epf[fn]; struct cdns_pcie *pcie = &ep->pcie; dma_addr_t bar_phys = epf_bar->phys_addr; enum pci_barno bar = epf_bar->barno; int flags = epf_bar->flags; u32 addr0, addr1, reg, cfg, b, aperture, ctrl; + u32 first_vf_offset, stride; u64 sz; /* BAR size is 2^(aperture + 7) */ @@ -92,19 +105,39 @@ addr0 = lower_32_bits(bar_phys); addr1 = upper_32_bits(bar_phys); + + if (vfn == 1) { + if (bar < BAR_4) { + reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + } else { + if (bar < BAR_4) { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + } + + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + epf = &epf->epf[vfn - 1]; + } + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), addr0); cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), addr1); - if (bar < BAR_4) { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); - b = bar; - } else { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); - b = bar - BAR_4; - } - cfg = cdns_pcie_readl(pcie, reg); cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); @@ -117,21 +150,42 @@ return 0; } -static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, +static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie_epf *epf = &ep->epf[fn]; struct cdns_pcie *pcie = &ep->pcie; enum pci_barno bar = epf_bar->barno; + u32 first_vf_offset, stride; u32 reg, cfg, b, ctrl; - if (bar < BAR_4) { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); - b = bar; + if (vfn == 1) { + if (bar < BAR_4) { + reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } } else { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); - b = bar - BAR_4; + if (bar < BAR_4) { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + } + + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + epf = &epf->epf[vfn - 1]; } ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; @@ -147,13 +201,23 @@ epf->epf_bar[bar] = NULL; } -static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, - u64 pci_addr, size_t size) +static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, + phys_addr_t addr, u64 pci_addr, size_t size) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; + u32 first_vf_offset, stride; u32 r; + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + } + r = find_first_zero_bit(&ep->ob_region_map, sizeof(ep->ob_region_map) * BITS_PER_LONG); if (r >= ep->max_regions - 1) { @@ -169,7 +233,7 @@ return 0; } -static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, +static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); @@ -189,13 +253,23 @@ clear_bit(r, &ep->ob_region_map); } -static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 mmc) +static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 mmc) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; + u32 first_vf_offset, stride; u16 flags; + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + } + /* * Set the Multiple Message Capable bitfield into the Message Control * register. @@ -209,13 +283,23 @@ return 0; } -static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) +static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; + u32 first_vf_offset, stride; u16 flags, mme; + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + } + /* Validate that the MSI feature is actually enabled. */ flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); if (!(flags & PCI_MSI_FLAGS_ENABLE)) @@ -230,13 +314,23 @@ return mme; } -static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no) +static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; + u32 first_vf_offset, stride; u32 val, reg; + if (vfunc_no > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, func_no, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, func_no, sriov_cap + + PCI_SRIOV_VF_STRIDE); + func_no = func_no + first_vf_offset + ((vfunc_no - 1) * stride); + } + reg = cap + PCI_MSIX_FLAGS; val = cdns_pcie_ep_fn_readw(pcie, func_no, reg); if (!(val & PCI_MSIX_FLAGS_ENABLE)) @@ -247,14 +341,25 @@ return val; } -static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u16 interrupts, - enum pci_barno bir, u32 offset) +static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u8 vfn, + u16 interrupts, enum pci_barno bir, + u32 offset) { + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; + u32 first_vf_offset, stride; u32 val, reg; + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + } + reg = cap + PCI_MSIX_FLAGS; val = cdns_pcie_ep_fn_readw(pcie, fn, reg); val &= ~PCI_MSIX_FLAGS_QSIZE; @@ -274,8 +379,8 @@ return 0; } -static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, - u8 intx, bool is_asserted) +static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx, + bool is_asserted) { struct cdns_pcie *pcie = &ep->pcie; unsigned long flags; @@ -317,7 +422,8 @@ writel(0, ep->irq_cpu_addr + offset); } -static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 intx) +static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, + u8 intx) { u16 cmd; @@ -334,14 +440,24 @@ return 0; } -static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, +static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, u8 interrupt_num) { + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie *pcie = &ep->pcie; u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; u16 flags, mme, data, data_mask; u8 msi_count; u64 pci_addr, pci_addr_mask = 0xff; + u32 first_vf_offset, stride; + + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + } /* Check whether the MSI feature has been enabled by the PCI host. */ flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); @@ -382,19 +498,93 @@ return 0; } -static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, +static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn, u8 vfn, + phys_addr_t addr, u8 interrupt_num, + u32 entry_size, u32 *msi_data, + u32 *msi_addr_offset) +{ + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; + struct cdns_pcie *pcie = &ep->pcie; + u64 pci_addr, pci_addr_mask = 0xff; + u16 flags, mme, data, data_mask; + u32 first_vf_offset, stride; + u8 msi_count; + int ret; + int i; + + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + } + + /* Check whether the MSI feature has been enabled by the PCI host. */ + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); + if (!(flags & PCI_MSI_FLAGS_ENABLE)) + return -EINVAL; + + /* Get the number of enabled MSIs */ + mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; + msi_count = 1 << mme; + if (!interrupt_num || interrupt_num > msi_count) + return -EINVAL; + + /* Compute the data value to be written. */ + data_mask = msi_count - 1; + data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64); + data = data & ~data_mask; + + /* Get the PCI address where to write the data into. */ + pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI); + pci_addr <<= 32; + pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO); + pci_addr &= GENMASK_ULL(63, 2); + + for (i = 0; i < interrupt_num; i++) { + ret = cdns_pcie_ep_map_addr(epc, fn, vfn, addr, + pci_addr & ~pci_addr_mask, + entry_size); + if (ret) + return ret; + addr = addr + entry_size; + } + + *msi_data = data; + *msi_addr_offset = pci_addr & pci_addr_mask; + + return 0; +} + +static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, u16 interrupt_num) { + u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; u32 tbl_offset, msg_data, reg; struct cdns_pcie *pcie = &ep->pcie; struct pci_epf_msix_tbl *msix_tbl; + u32 first_vf_offset, stride; struct cdns_pcie_epf *epf; u64 pci_addr_mask = 0xff; u64 msg_addr; u16 flags; u8 bir; + epf = &ep->epf[fn]; + + if (vfn > 0) { + first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_OFFSET); + stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap + + PCI_SRIOV_VF_STRIDE); + fn = fn + first_vf_offset + ((vfn - 1) * stride); + epf = &epf->epf[vfn - 1]; + } + /* Check whether the MSI-X feature has been enabled by the PCI host. */ flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS); if (!(flags & PCI_MSIX_FLAGS_ENABLE)) @@ -405,7 +595,6 @@ bir = tbl_offset & PCI_MSIX_TABLE_BIR; tbl_offset &= PCI_MSIX_TABLE_OFFSET; - epf = &ep->epf[fn]; msix_tbl = epf->epf_bar[bir]->addr + tbl_offset; msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr; msg_data = msix_tbl[(interrupt_num - 1)].msg_data; @@ -427,21 +616,27 @@ return 0; } -static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, +static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, enum pci_epc_irq_type type, u16 interrupt_num) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + struct device *dev = pcie->dev; switch (type) { case PCI_EPC_IRQ_LEGACY: - return cdns_pcie_ep_send_legacy_irq(ep, fn, 0); + if (vfn > 0) { + dev_err(dev, "Cannot raise legacy interrupts for VF\n"); + return -EINVAL; + } + return cdns_pcie_ep_send_legacy_irq(ep, fn, vfn, 0); case PCI_EPC_IRQ_MSI: - return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num); + return cdns_pcie_ep_send_msi_irq(ep, fn, vfn, interrupt_num); case PCI_EPC_IRQ_MSIX: - return cdns_pcie_ep_send_msix_irq(ep, fn, interrupt_num); + return cdns_pcie_ep_send_msix_irq(ep, fn, vfn, interrupt_num); default: break; @@ -455,18 +650,13 @@ struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; struct device *dev = pcie->dev; - struct pci_epf *epf; - u32 cfg; int ret; /* * BIT(0) is hardwired to 1, hence function 0 is always enabled * and can't be disabled anyway. */ - cfg = BIT(0); - list_for_each_entry(epf, &epc->pci_epf, list) - cfg |= BIT(epf->func_no); - cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); + cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, epc->function_num_map); ret = cdns_pcie_start_link(pcie); if (ret) { @@ -477,16 +667,27 @@ return 0; } +static const struct pci_epc_features cdns_pcie_epc_vf_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = true, + .align = 65536, +}; + static const struct pci_epc_features cdns_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = true, + .align = 256, }; static const struct pci_epc_features* -cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { - return &cdns_pcie_epc_features; + if (!vfunc_no) + return &cdns_pcie_epc_features; + + return &cdns_pcie_epc_vf_features; } static const struct pci_epc_ops cdns_pcie_epc_ops = { @@ -500,6 +701,7 @@ .set_msix = cdns_pcie_ep_set_msix, .get_msix = cdns_pcie_ep_get_msix, .raise_irq = cdns_pcie_ep_raise_irq, + .map_msi_irq = cdns_pcie_ep_map_msi_irq, .start = cdns_pcie_ep_start, .get_features = cdns_pcie_ep_get_features, }; @@ -511,9 +713,11 @@ struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; struct cdns_pcie *pcie = &ep->pcie; + struct cdns_pcie_epf *epf; struct resource *res; struct pci_epc *epc; int ret; + int i; pcie->is_rc = false; @@ -530,12 +734,9 @@ } pcie->mem_res = res; - ret = of_property_read_u32(np, "cdns,max-outbound-regions", - &ep->max_regions); - if (ret < 0) { - dev_err(dev, "missing \"cdns,max-outbound-regions\"\n"); - return ret; - } + ep->max_regions = CDNS_PCIE_MAX_OB; + of_property_read_u32(np, "cdns,max-outbound-regions", &ep->max_regions); + ep->ob_addr = devm_kcalloc(dev, ep->max_regions, sizeof(*ep->ob_addr), GFP_KERNEL); @@ -561,6 +762,25 @@ if (!ep->epf) return -ENOMEM; + epc->max_vfs = devm_kcalloc(dev, epc->max_functions, + sizeof(*epc->max_vfs), GFP_KERNEL); + if (!epc->max_vfs) + return -ENOMEM; + + ret = of_property_read_u8_array(np, "max-virtual-functions", + epc->max_vfs, epc->max_functions); + if (ret == 0) { + for (i = 0; i < epc->max_functions; i++) { + epf = &ep->epf[i]; + if (epc->max_vfs[i] == 0) + continue; + epf->epf = devm_kcalloc(dev, epc->max_vfs[i], + sizeof(*ep->epf), GFP_KERNEL); + if (!epf->epf) + return -ENOMEM; + } + } + ret = pci_epc_mem_init(epc, pcie->mem_res->start, resource_size(pcie->mem_res), PAGE_SIZE); if (ret < 0) { diff -Naur --no-dereference a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h --- a/drivers/pci/controller/cadence/pcie-cadence.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/cadence/pcie-cadence.h 2022-01-06 12:45:53.826318157 -0500 @@ -50,6 +50,10 @@ (CDNS_PCIE_LM_BASE + 0x0240 + (fn) * 0x0008) #define CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn) \ (CDNS_PCIE_LM_BASE + 0x0244 + (fn) * 0x0008) +#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn) \ + (CDNS_PCIE_LM_BASE + 0x0280 + (fn) * 0x0008) +#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn) \ + (CDNS_PCIE_LM_BASE + 0x0284 + (fn) * 0x0008) #define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \ (GENMASK(4, 0) << ((b) * 8)) #define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \ @@ -114,6 +118,7 @@ #define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90 #define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xb0 +#define CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET 0x200 /* * Root Port Registers (PCI configuration space for the root port function) @@ -205,6 +210,7 @@ }; #define CDNS_PCIE_RP_MAX_IB 0x3 +#define CDNS_PCIE_MAX_OB 32 struct cdns_pcie_rp_ib_bar { u64 size; @@ -314,9 +320,11 @@ /** * struct cdns_pcie_epf - Structure to hold info about endpoint function + * @epf: Info about virtual functions attached to the physical function * @epf_bar: reference to the pci_epf_bar for the six Base Address Registers */ struct cdns_pcie_epf { + struct cdns_pcie_epf *epf; struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; }; diff -Naur --no-dereference a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c --- a/drivers/pci/controller/cadence/pci-j721e.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/cadence/pci-j721e.c 2022-01-06 12:45:53.826318157 -0500 @@ -6,12 +6,14 @@ * Author: Kishon Vijay Abraham I */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -27,6 +29,17 @@ #define LINK_DOWN BIT(1) #define J7200_LINK_DOWN BIT(10) +#define EOI_REG 0x10 + +#define ENABLE_REG_SYS_0 0x100 +#define STATUS_REG_SYS_0 0x500 +#define STATUS_CLR_REG_SYS_0 0x700 +#define INTx_EN(num) (1 << (num)) + +#define ENABLE_REG_SYS_1 0x104 +#define STATUS_REG_SYS_1 0x504 +#define SYS1_INTx_EN(num) (1 << (22 + (num))) + #define J721E_PCIE_USER_CMD_STATUS 0x4 #define LINK_TRAINING_ENABLE BIT(0) @@ -40,6 +53,14 @@ LINK_UP_DL_COMPLETED, }; +#define USER_EOI_REG 0xC8 +enum eoi_reg { + EOI_DOWNSTREAM_INTERRUPT, + EOI_FLR_INTERRUPT, + EOI_LEGACY_INTERRUPT, + EOI_POWER_STATE_INTERRUPT, +}; + #define J721E_MODE_RC BIT(7) #define LANE_COUNT_MASK BIT(8) #define LANE_COUNT(n) ((n) << 8) @@ -50,11 +71,14 @@ struct j721e_pcie { struct device *dev; + struct clk *refclk; u32 mode; u32 num_lanes; struct cdns_pcie *cdns_pcie; void __iomem *user_cfg_base; void __iomem *intd_cfg_base; + struct irq_domain *legacy_irq_domain; + bool is_intc_v1; u32 linkdown_irq_regfield; }; @@ -65,6 +89,7 @@ struct j721e_pcie_data { enum j721e_pcie_mode mode; + bool is_intc_v1; unsigned int quirk_retrain_flag:1; unsigned int quirk_detect_quiet_flag:1; u32 linkdown_irq_regfield; @@ -118,6 +143,117 @@ j721e_pcie_intd_writel(pcie, ENABLE_REG_SYS_2, reg); } +static void j721e_pcie_legacy_irq_handler(struct irq_desc *desc) +{ + struct j721e_pcie *pcie = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int virq; + u32 reg; + int i; + + chained_irq_enter(chip, desc); + + for (i = 0; i < PCI_NUM_INTX; i++) { + reg = j721e_pcie_intd_readl(pcie, STATUS_REG_SYS_1); + if (!(reg & SYS1_INTx_EN(i))) + continue; + + virq = irq_find_mapping(pcie->legacy_irq_domain, i); + generic_handle_irq(virq); + j721e_pcie_user_writel(pcie, USER_EOI_REG, + EOI_LEGACY_INTERRUPT); + } + + chained_irq_exit(chip, desc); +} + +static void j721e_pcie_v1_legacy_irq_handler(struct irq_desc *desc) +{ + struct j721e_pcie *pcie = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int virq, i; + u32 reg; + + chained_irq_enter(chip, desc); + + for (i = 0; i < PCI_NUM_INTX; i++) { + reg = j721e_pcie_intd_readl(pcie, STATUS_REG_SYS_0); + if (!(reg & INTx_EN(i))) + continue; + + virq = irq_find_mapping(pcie->legacy_irq_domain, 3 - i); + generic_handle_irq(virq); + j721e_pcie_intd_writel(pcie, STATUS_CLR_REG_SYS_0, INTx_EN(i)); + j721e_pcie_intd_writel(pcie, EOI_REG, 3 - i); + } + + chained_irq_exit(chip, desc); +} + +static int j721e_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops j721e_pcie_intx_domain_ops = { + .map = j721e_pcie_intx_map, +}; + +static int j721e_pcie_config_legacy_irq(struct j721e_pcie *pcie) +{ + struct irq_domain *legacy_irq_domain; + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; + struct device_node *intc_node; + int irq, i; + u32 reg; + + intc_node = of_get_child_by_name(node, "interrupt-controller"); + if (!intc_node) { + dev_WARN(dev, "legacy-interrupt-controller node is absent\n"); + return -EINVAL; + } + + irq = irq_of_parse_and_map(intc_node, 0); + if (!irq) { + dev_err(dev, "Failed to parse and map legacy irq\n"); + return -EINVAL; + } + + if (pcie->is_intc_v1) + irq_set_chained_handler_and_data(irq, j721e_pcie_v1_legacy_irq_handler, pcie); + else + irq_set_chained_handler_and_data(irq, j721e_pcie_legacy_irq_handler, pcie); + + legacy_irq_domain = irq_domain_add_linear(intc_node, PCI_NUM_INTX, + &j721e_pcie_intx_domain_ops, NULL); + if (!legacy_irq_domain) { + dev_err(dev, "Failed to add irq domain for legacy irqs\n"); + return -EINVAL; + } + pcie->legacy_irq_domain = legacy_irq_domain; + + if (pcie->is_intc_v1) { + for (i = 0; i < PCI_NUM_INTX; i++) { + reg = j721e_pcie_intd_readl(pcie, ENABLE_REG_SYS_0); + reg |= INTx_EN(i); + j721e_pcie_intd_writel(pcie, ENABLE_REG_SYS_0, reg); + } + } else { + for (i = 0; i < PCI_NUM_INTX; i++) { + reg = j721e_pcie_intd_readl(pcie, ENABLE_REG_SYS_1); + reg |= SYS1_INTx_EN(i); + j721e_pcie_intd_writel(pcie, ENABLE_REG_SYS_1, reg); + } + } + + return 0; +} + static int j721e_pcie_start_link(struct cdns_pcie *cdns_pcie) { struct j721e_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); @@ -159,7 +295,14 @@ .link_up = j721e_pcie_link_up, }; -static int j721e_pcie_set_mode(struct j721e_pcie *pcie, struct regmap *syscon) +static const struct cdns_pcie_ops j7200_pcie_ops = { + .start_link = j721e_pcie_start_link, + .stop_link = j721e_pcie_stop_link, + .link_up = j721e_pcie_link_up, +}; + +static int j721e_pcie_set_mode(struct j721e_pcie *pcie, struct regmap *syscon, + unsigned int offset) { struct device *dev = pcie->dev; u32 mask = J721E_MODE_RC; @@ -170,7 +313,7 @@ if (mode == PCI_MODE_RC) val = J721E_MODE_RC; - ret = regmap_update_bits(syscon, 0, mask, val); + ret = regmap_update_bits(syscon, offset, mask, val); if (ret) dev_err(dev, "failed to set pcie mode\n"); @@ -178,7 +321,7 @@ } static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie, - struct regmap *syscon) + struct regmap *syscon, unsigned int offset) { struct device *dev = pcie->dev; struct device_node *np = dev->of_node; @@ -191,7 +334,7 @@ link_speed = 2; val = link_speed - 1; - ret = regmap_update_bits(syscon, 0, GENERATION_SEL_MASK, val); + ret = regmap_update_bits(syscon, offset, GENERATION_SEL_MASK, val); if (ret) dev_err(dev, "failed to set link speed\n"); @@ -199,7 +342,7 @@ } static int j721e_pcie_set_lane_count(struct j721e_pcie *pcie, - struct regmap *syscon) + struct regmap *syscon, unsigned int offset) { struct device *dev = pcie->dev; u32 lanes = pcie->num_lanes; @@ -207,7 +350,7 @@ int ret; val = LANE_COUNT(lanes - 1); - ret = regmap_update_bits(syscon, 0, LANE_COUNT_MASK, val); + ret = regmap_update_bits(syscon, offset, LANE_COUNT_MASK, val); if (ret) dev_err(dev, "failed to set link count\n"); @@ -218,6 +361,8 @@ { struct device *dev = pcie->dev; struct device_node *node = dev->of_node; + struct of_phandle_args args; + unsigned int offset = 0; struct regmap *syscon; int ret; @@ -227,19 +372,25 @@ return PTR_ERR(syscon); } - ret = j721e_pcie_set_mode(pcie, syscon); + /* Do not error out to maintain old DT compatibility */ + ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-pcie-ctrl", 1, + 0, &args); + if (!ret) + offset = args.args[0]; + + ret = j721e_pcie_set_mode(pcie, syscon, offset); if (ret < 0) { dev_err(dev, "Failed to set pci mode\n"); return ret; } - ret = j721e_pcie_set_link_speed(pcie, syscon); + ret = j721e_pcie_set_link_speed(pcie, syscon, offset); if (ret < 0) { dev_err(dev, "Failed to set link speed\n"); return ret; } - ret = j721e_pcie_set_lane_count(pcie, syscon); + ret = j721e_pcie_set_lane_count(pcie, syscon, offset); if (ret < 0) { dev_err(dev, "Failed to set num-lanes\n"); return ret; @@ -277,6 +428,7 @@ static const struct j721e_pcie_data j721e_pcie_rc_data = { .mode = PCI_MODE_RC, .quirk_retrain_flag = true, + .is_intc_v1 = true, .byte_access_allowed = false, .linkdown_irq_regfield = LINK_DOWN, }; @@ -289,8 +441,9 @@ static const struct j721e_pcie_data j7200_pcie_rc_data = { .mode = PCI_MODE_RC, .quirk_detect_quiet_flag = true, - .linkdown_irq_regfield = J7200_LINK_DOWN, + .is_intc_v1 = false, .byte_access_allowed = true, + .linkdown_irq_regfield = J7200_LINK_DOWN, }; static const struct j721e_pcie_data j7200_pcie_ep_data = { @@ -349,6 +502,7 @@ struct cdns_pcie_ep *ep; struct gpio_desc *gpiod; void __iomem *base; + struct clk *clk; u32 num_lanes; u32 mode; int ret; @@ -366,6 +520,7 @@ pcie->dev = dev; pcie->mode = mode; + pcie->is_intc_v1 = data->is_intc_v1; pcie->linkdown_irq_regfield = data->linkdown_irq_regfield; base = devm_platform_ioremap_resource_byname(pdev, "intd_cfg"); @@ -420,6 +575,10 @@ goto err_get_sync; } + ret = j721e_pcie_config_legacy_irq(pcie); + if (ret < 0) + goto err_get_sync; + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); if (!bridge) { ret = -ENOMEM; @@ -451,6 +610,20 @@ goto err_get_sync; } + clk = devm_clk_get_optional(dev, "pcie_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get pcie_refclk\n"); + ret = PTR_ERR(clk); + goto err_pcie_setup; + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "failed to enable pcie_refclk\n"); + goto err_get_sync; + } + pcie->refclk = clk; + /* * "Power Sequencing and Reset Signal Timings" table in * PCI EXPRESS CARD ELECTROMECHANICAL SPECIFICATION, REV. 3.0 @@ -465,8 +638,10 @@ } ret = cdns_pcie_host_setup(rc); - if (ret < 0) + if (ret < 0) { + clk_disable_unprepare(pcie->refclk); goto err_pcie_setup; + } break; case PCI_MODE_EP: @@ -520,6 +695,7 @@ struct cdns_pcie *cdns_pcie = pcie->cdns_pcie; struct device *dev = &pdev->dev; + clk_disable_unprepare(pcie->refclk); cdns_pcie_disable_phy(cdns_pcie); pm_runtime_put(dev); pm_runtime_disable(dev); diff -Naur --no-dereference a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c --- a/drivers/pci/controller/dwc/pcie-designware-ep.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c 2022-01-06 12:45:53.826318157 -0500 @@ -124,7 +124,7 @@ return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); } -static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, +static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -201,7 +201,7 @@ return 0; } -static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, +static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -216,7 +216,7 @@ ep->epf_bar[bar] = NULL; } -static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, +static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { int ret; @@ -274,7 +274,7 @@ return -EINVAL; } -static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, +static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t addr) { int ret; @@ -290,9 +290,8 @@ clear_bit(atu_index, ep->ob_window_map); } -static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, - phys_addr_t addr, - u64 pci_addr, size_t size) +static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + phys_addr_t addr, u64 pci_addr, size_t size) { int ret; struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -307,7 +306,7 @@ return 0; } -static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) +static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -331,7 +330,8 @@ return val; } -static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u8 interrupts) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -356,7 +356,7 @@ return 0; } -static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no) +static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -380,8 +380,8 @@ return val; } -static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts, - enum pci_barno bir, u32 offset) +static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u16 interrupts, enum pci_barno bir, u32 offset) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -416,7 +416,7 @@ return 0; } -static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, +static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, enum pci_epc_irq_type type, u16 interrupt_num) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -450,7 +450,7 @@ } static const struct pci_epc_features* -dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -525,14 +525,14 @@ aligned_offset = msg_addr_lower & (epc->mem->window.page_size - 1); msg_addr = ((u64)msg_addr_upper) << 32 | (msg_addr_lower & ~aligned_offset); - ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, + ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, epc->mem->window.page_size); if (ret) return ret; writel(msg_data | (interrupt_num - 1), ep->msi_mem + aligned_offset); - dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); + dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); return 0; } @@ -593,14 +593,14 @@ } aligned_offset = msg_addr & (epc->mem->window.page_size - 1); - ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, + ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, epc->mem->window.page_size); if (ret) return ret; writel(msg_data, ep->msi_mem + aligned_offset); - dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); + dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); return 0; } diff -Naur --no-dereference a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c --- a/drivers/pci/controller/dwc/pci-keystone.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/dwc/pci-keystone.c 2022-01-06 12:45:53.826318157 -0500 @@ -35,6 +35,11 @@ #define PCIE_DEVICEID_SHIFT 16 /* Application registers */ +#define PID 0x000 +#define RTL GENMASK(15, 11) +#define RTL_SHIFT 11 +#define AM6_PCI_PG1_RTL_VER 0x15 + #define CMD_STATUS 0x004 #define LTSSM_EN_VAL BIT(0) #define OB_XLAT_EN_VAL BIT(1) @@ -69,6 +74,7 @@ #define IRQ_STATUS(n) (0x184 + ((n) << 4)) #define IRQ_ENABLE_SET(n) (0x188 + ((n) << 4)) +#define IRQ_ENABLE_CLR(n) (0x18c + ((n) << 4)) #define INTx_EN BIT(0) #define ERR_IRQ_STATUS 0x1c4 @@ -105,6 +111,8 @@ #define to_keystone_pcie(x) dev_get_drvdata((x)->dev) +#define PCI_DEVICE_ID_TI_AM654X 0xb00c + struct ks_pcie_of_data { enum dw_pcie_device_mode mode; const struct dw_pcie_host_ops *host_ops; @@ -116,15 +124,14 @@ struct dw_pcie *pci; /* PCI Device ID */ u32 device_id; - int legacy_host_irqs[PCI_NUM_INTX]; struct device_node *legacy_intc_np; + struct irq_domain *legacy_irq_domain; int msi_host_irq; int num_lanes; struct phy **phy; struct device_link **link; struct device_node *msi_intc_np; - struct irq_domain *legacy_irq_domain; struct device_node *np; /* Application register space */ @@ -252,26 +259,6 @@ return dw_pcie_allocate_domains(pp); } -static void ks_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, - int offset) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct device *dev = pci->dev; - u32 pending; - int virq; - - pending = ks_pcie_app_readl(ks_pcie, IRQ_STATUS(offset)); - - if (BIT(0) & pending) { - virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset); - dev_dbg(dev, ": irq: irq_offset %d, virq %d\n", offset, virq); - generic_handle_irq(virq); - } - - /* EOI the INTx interrupt */ - ks_pcie_app_writel(ks_pcie, IRQ_EOI, offset); -} - /* * Dummy function so that DW core doesn't configure MSI */ @@ -317,39 +304,143 @@ return IRQ_HANDLED; } -static void ks_pcie_ack_legacy_irq(struct irq_data *d) +static void ks_pcie_am654_legacy_irq_handler(struct irq_desc *desc) +{ + struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int virq, i; + u32 reg; + + chained_irq_enter(chip, desc); + + for (i = 0; i < PCI_NUM_INTX; i++) { + reg = ks_pcie_app_readl(ks_pcie, IRQ_STATUS(i)); + if (!(reg & INTx_EN)) + continue; + + virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, i); + generic_handle_irq(virq); + ks_pcie_app_writel(ks_pcie, IRQ_STATUS(i), INTx_EN); + ks_pcie_app_writel(ks_pcie, IRQ_EOI, i); + } + + chained_irq_exit(chip, desc); +} + +void ks_pcie_irq_eoi(struct irq_data *data) { + struct keystone_pcie *ks_pcie = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = data->hwirq; + + ks_pcie_app_writel(ks_pcie, IRQ_EOI, hwirq); + irq_chip_eoi_parent(data); } -static void ks_pcie_mask_legacy_irq(struct irq_data *d) +void ks_pcie_irq_enable(struct irq_data *data) { + struct keystone_pcie *ks_pcie = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = data->hwirq; + + ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_SET(hwirq), INTx_EN); + irq_chip_enable_parent(data); } -static void ks_pcie_unmask_legacy_irq(struct irq_data *d) +void ks_pcie_irq_disable(struct irq_data *data) { + struct keystone_pcie *ks_pcie = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = data->hwirq; + + ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_CLR(hwirq), INTx_EN); + irq_chip_disable_parent(data); } static struct irq_chip ks_pcie_legacy_irq_chip = { - .name = "Keystone-PCI-Legacy-IRQ", - .irq_ack = ks_pcie_ack_legacy_irq, - .irq_mask = ks_pcie_mask_legacy_irq, - .irq_unmask = ks_pcie_unmask_legacy_irq, + .name = "Keystone-PCI-Legacy-IRQ", + .irq_enable = ks_pcie_irq_enable, + .irq_disable = ks_pcie_irq_disable, + .irq_eoi = ks_pcie_irq_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, }; -static int ks_pcie_init_legacy_irq_map(struct irq_domain *d, - unsigned int irq, - irq_hw_number_t hw_irq) -{ - irq_set_chip_and_handler(irq, &ks_pcie_legacy_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, d->host_data); +static int ks_pcie_legacy_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct keystone_pcie *ks_pcie = domain->host_data; + struct device_node *np = ks_pcie->legacy_intc_np; + struct irq_fwspec parent_fwspec, *fwspec = data; + struct of_phandle_args out_irq; + int ret; + + if (nr_irqs != 1) + return -EINVAL; + + /* + * Get the correct interrupt from legacy-interrupt-controller node + * corresponding to INTA/INTB/INTC/INTD (passed in fwspec->param[0]) + * after performing mapping specified in "interrupt-map". + * interrupt-map = <0 0 0 1 &pcie_intc0 0>, INTA (4th cell in + * interrupt-map) corresponds to 1st entry in "interrupts" (6th cell + * in interrupt-map) + */ + ret = of_irq_parse_one(np, fwspec->param[0], &out_irq); + if (ret < 0) { + pr_err("Failed to parse interrupt node\n"); + return ret; + } + + of_phandle_args_to_fwspec(np, out_irq.args, out_irq.args_count, &parent_fwspec); + + ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); + if (ret < 0) { + pr_err("Failed to allocate parent IRQ %u: %d\n", + parent_fwspec.param[0], ret); + return ret; + } + + ret = irq_domain_set_hwirq_and_chip(domain, virq, fwspec->param[0], + &ks_pcie_legacy_irq_chip, ks_pcie); + if (ret < 0) { + pr_err("Failed to set hwirq and chip\n"); + goto err_set_hwirq_and_chip; + } return 0; + +err_set_hwirq_and_chip: + irq_domain_free_irqs_parent(domain, virq, 1); + + return ret; +} + +static int ks_pcie_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 2) + return -EINVAL; + + if (fwspec->param[0] >= PCI_NUM_INTX) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + + return 0; + } + + return -EINVAL; } static const struct irq_domain_ops ks_pcie_legacy_irq_domain_ops = { - .map = ks_pcie_init_legacy_irq_map, - .xlate = irq_domain_xlate_onetwocell, + .alloc = ks_pcie_legacy_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = ks_pcie_irq_domain_translate, }; /** @@ -439,6 +530,17 @@ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); u32 reg; + /* + * Checking whether the link is up here is a last line of defense + * against platforms that forward errors on the system bus as + * SError upon PCI configuration transactions issued when the link + * is down. This check is racy by definition and does not stop + * the system from triggering an SError if the link goes down + * after this check is performed. + */ + if (!dw_pcie_link_up(pci)) + return NULL; + reg = CFG_BUS(bus->number) | CFG_DEVICE(PCI_SLOT(devfn)) | CFG_FUNC(PCI_FUNC(devfn)); if (!pci_is_root_bus(bus->parent)) @@ -537,7 +639,11 @@ static void ks_pcie_quirk(struct pci_dev *dev) { struct pci_bus *bus = dev->bus; + struct keystone_pcie *ks_pcie; + struct device *bridge_dev; struct pci_dev *bridge; + u32 val; + static const struct pci_device_id rc_pci_devids[] = { { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2HK), .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, @@ -549,9 +655,20 @@ .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, { 0, }, }; + static const struct pci_device_id am6_pci_devids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654X), + .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, + { 0, }, + }; - if (pci_is_root_bus(bus)) + if (pci_is_root_bus(bus)) { bridge = dev; + if (pci_match_id(am6_pci_devids, bridge)) { + struct resource *r = &dev->resource[0]; + + r->flags |= IORESOURCE_UNSET; + } + } /* look for the host bridge */ while (!pci_is_root_bus(bus)) { @@ -574,6 +691,32 @@ pcie_set_readrq(dev, 256); } } + + /* + * Memory transactions fail with PCI controller in AM654 PG1.0 + * when MRRS is set to more than 128 bytes. Force the MRRS to + * 128 Bytes in all downstream devices. + */ + if (pci_match_id(am6_pci_devids, bridge)) { + bridge_dev = pci_get_host_bridge_device(dev); + if (!bridge_dev && !bridge_dev->parent) + return; + + ks_pcie = dev_get_drvdata(bridge_dev->parent); + if (!ks_pcie) + return; + + val = ks_pcie_app_readl(ks_pcie, PID); + val &= RTL; + val >>= RTL_SHIFT; + if (val != AM6_PCI_PG1_RTL_VER) + return; + + if (pcie_get_readrq(dev) > 128) { + dev_info(&dev->dev, "limiting MRRS to 128 bytes\n"); + pcie_set_readrq(dev, 128); + } + } } DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, ks_pcie_quirk); @@ -616,35 +759,6 @@ chained_irq_exit(chip, desc); } -/** - * ks_pcie_legacy_irq_handler() - Handle legacy interrupt - * @irq: IRQ line for legacy interrupts - * @desc: Pointer to irq descriptor - * - * Traverse through pending legacy interrupts and invoke handler for each. Also - * takes care of interrupt controller level mask/ack operation. - */ -static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) -{ - unsigned int irq = irq_desc_get_irq(desc); - struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); - struct dw_pcie *pci = ks_pcie->pci; - struct device *dev = pci->dev; - u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; - struct irq_chip *chip = irq_desc_get_chip(desc); - - dev_dbg(dev, ": Handling legacy irq %d\n", irq); - - /* - * The chained irq handler installation would have replaced normal - * interrupt driver handler so we need to take care of mask/unmask and - * ack operation. - */ - chained_irq_enter(chip, desc); - ks_pcie_handle_legacy_irq(ks_pcie, irq_offset); - chained_irq_exit(chip, desc); -} - static int ks_pcie_config_msi_irq(struct keystone_pcie *ks_pcie) { struct device *dev = ks_pcie->pci->dev; @@ -699,25 +813,86 @@ return ret; } +static int ks_pcie_am654_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops ks_pcie_am654_irq_domain_ops = { + .map = ks_pcie_am654_intx_map, +}; + +static int ks_pcie_am654_config_legacy_irq(struct keystone_pcie *ks_pcie) +{ + struct device *dev = ks_pcie->pci->dev; + struct irq_domain *legacy_irq_domain; + struct device_node *np = ks_pcie->np; + struct device_node *intc_np; + int ret = 0; + int irq; + int i; + + intc_np = of_get_child_by_name(np, "interrupt-controller"); + if (!intc_np) { + dev_warn(dev, "legacy interrupt-controller node is absent\n"); + return -EINVAL; + } + + irq = irq_of_parse_and_map(intc_np, 0); + if (!irq) + return -EINVAL; + + irq_set_chained_handler_and_data(irq, ks_pcie_am654_legacy_irq_handler, ks_pcie); + legacy_irq_domain = irq_domain_add_linear(intc_np, PCI_NUM_INTX, + &ks_pcie_am654_irq_domain_ops, ks_pcie); + if (!legacy_irq_domain) { + dev_err(dev, "Failed to add IRQ domain for legacy IRQS\n"); + return -EINVAL; + } + ks_pcie->legacy_irq_domain = legacy_irq_domain; + + for (i = 0; i < PCI_NUM_INTX; i++) + ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_SET(i), INTx_EN); + + return ret; +} + static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) { struct device *dev = ks_pcie->pci->dev; struct irq_domain *legacy_irq_domain; struct device_node *np = ks_pcie->np; + struct irq_domain *parent_domain; + struct device_node *parent_node; struct device_node *intc_np; - int irq_count, irq, ret = 0, i; + int irq_count, ret = 0; - intc_np = of_get_child_by_name(np, "legacy-interrupt-controller"); + intc_np = of_get_child_by_name(np, "interrupt-controller"); if (!intc_np) { - /* - * Since legacy interrupts are modeled as edge-interrupts in - * AM6, keep it disabled for now. - */ - if (ks_pcie->is_am6) - return 0; dev_warn(dev, "legacy-interrupt-controller node is absent\n"); return -EINVAL; } + ks_pcie->legacy_intc_np = intc_np; + + parent_node = of_irq_find_parent(intc_np); + if (!parent_node) { + dev_err(dev, "Unable to obtain parent node\n"); + ret = -ENXIO; + goto err; + } + + parent_domain = irq_find_host(parent_node); + if (!parent_domain) { + dev_err(dev, "Unable to obtain parent domain\n"); + ret = -ENXIO; + goto err; + } + + of_node_put(parent_node); irq_count = of_irq_count(intc_np); if (!irq_count) { @@ -726,31 +901,13 @@ goto err; } - for (i = 0; i < irq_count; i++) { - irq = irq_of_parse_and_map(intc_np, i); - if (!irq) { - ret = -EINVAL; - goto err; - } - ks_pcie->legacy_host_irqs[i] = irq; - - irq_set_chained_handler_and_data(irq, - ks_pcie_legacy_irq_handler, - ks_pcie); - } - - legacy_irq_domain = - irq_domain_add_linear(intc_np, PCI_NUM_INTX, - &ks_pcie_legacy_irq_domain_ops, NULL); + legacy_irq_domain = irq_domain_add_hierarchy(parent_domain, 0, PCI_NUM_INTX, intc_np, + &ks_pcie_legacy_irq_domain_ops, ks_pcie); if (!legacy_irq_domain) { dev_err(dev, "Failed to add irq domain for legacy irqs\n"); ret = -EINVAL; goto err; } - ks_pcie->legacy_irq_domain = legacy_irq_domain; - - for (i = 0; i < PCI_NUM_INTX; i++) - ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_SET(i), INTx_EN); err: of_node_put(intc_np); @@ -811,10 +968,14 @@ int ret; pp->bridge->ops = &ks_pcie_ops; - if (!ks_pcie->is_am6) + + if (!ks_pcie->is_am6) { pp->bridge->child_ops = &ks_child_pcie_ops; + ret = ks_pcie_config_legacy_irq(ks_pcie); + } else { + ret = ks_pcie_am654_config_legacy_irq(ks_pcie); + } - ret = ks_pcie_config_legacy_irq(ks_pcie); if (ret) return ret; @@ -1110,6 +1271,7 @@ static const struct ks_pcie_of_data ks_pcie_rc_of_data = { .host_ops = &ks_pcie_host_ops, + .mode = DW_PCIE_RC_TYPE, .version = 0x365A, }; diff -Naur --no-dereference a/drivers/pci/controller/pcie-rcar-ep.c b/drivers/pci/controller/pcie-rcar-ep.c --- a/drivers/pci/controller/pcie-rcar-ep.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/pcie-rcar-ep.c 2022-01-06 12:45:53.826318157 -0500 @@ -159,7 +159,7 @@ return 0; } -static int rcar_pcie_ep_write_header(struct pci_epc *epc, u8 fn, +static int rcar_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_header *hdr) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); @@ -195,7 +195,7 @@ return 0; } -static int rcar_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, +static int rcar_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { int flags = epf_bar->flags | LAR_ENABLE | LAM_64BIT; @@ -246,7 +246,7 @@ return 0; } -static void rcar_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, +static void rcar_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_bar *epf_bar) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); @@ -259,7 +259,8 @@ clear_bit(atu_index + 1, ep->ib_window_map); } -static int rcar_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 interrupts) +static int rcar_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, + u8 interrupts) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct rcar_pcie *pcie = &ep->pcie; @@ -272,7 +273,7 @@ return 0; } -static int rcar_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) +static int rcar_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct rcar_pcie *pcie = &ep->pcie; @@ -285,7 +286,7 @@ return ((flags & MSICAP0_MMESE_MASK) >> MSICAP0_MMESE_OFFSET); } -static int rcar_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, +static int rcar_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr, u64 pci_addr, size_t size) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); @@ -322,7 +323,7 @@ return 0; } -static void rcar_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, +static void rcar_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); @@ -403,7 +404,7 @@ return 0; } -static int rcar_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, +static int rcar_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, enum pci_epc_irq_type type, u16 interrupt_num) { @@ -451,7 +452,7 @@ }; static const struct pci_epc_features* -rcar_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +rcar_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { return &rcar_pcie_epc_features; } diff -Naur --no-dereference a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c --- a/drivers/pci/controller/pcie-rockchip-ep.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/controller/pcie-rockchip-ep.c 2022-01-06 12:45:53.826318157 -0500 @@ -122,7 +122,7 @@ ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r)); } -static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, +static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_header *hdr) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); @@ -159,7 +159,7 @@ return 0; } -static int rockchip_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, +static int rockchip_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_bar *epf_bar) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); @@ -227,7 +227,7 @@ return 0; } -static void rockchip_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, +static void rockchip_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_bar *epf_bar) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); @@ -256,7 +256,7 @@ ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar)); } -static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, +static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr, u64 pci_addr, size_t size) { @@ -284,7 +284,7 @@ return 0; } -static void rockchip_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, +static void rockchip_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); @@ -308,7 +308,7 @@ clear_bit(r, &ep->ob_region_map); } -static int rockchip_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, +static int rockchip_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 multi_msg_cap) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); @@ -329,7 +329,7 @@ return 0; } -static int rockchip_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) +static int rockchip_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie *rockchip = &ep->rockchip; @@ -471,7 +471,7 @@ return 0; } -static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, +static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, enum pci_epc_irq_type type, u16 interrupt_num) { @@ -510,7 +510,7 @@ }; static const struct pci_epc_features* -rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { return &rockchip_pcie_epc_features; } diff -Naur --no-dereference a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig --- a/drivers/pci/endpoint/functions/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/endpoint/functions/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -12,3 +12,16 @@ for PCI Endpoint. If in doubt, say "N" to disable Endpoint test driver. + +config PCI_EPF_NTB + tristate "PCI Endpoint NTB driver" + depends on PCI_ENDPOINT + select CONFIGFS_FS + help + Select this configuration option to enable the Non-Transparent + Bridge (NTB) driver for PCI Endpoint. NTB driver implements NTB + controller functionality using multiple PCIe endpoint instances. + It can support NTB endpoint function devices created using + device tree. + + If in doubt, say "N" to disable Endpoint NTB driver. diff -Naur --no-dereference a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile --- a/drivers/pci/endpoint/functions/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/endpoint/functions/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -4,3 +4,4 @@ # obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o +obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o diff -Naur --no-dereference a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,2145 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Endpoint Function Driver to implement Non-Transparent Bridge functionality + * + * Copyright (C) 2020 Texas Instruments + * Author: Kishon Vijay Abraham I + */ + +/* + * The PCI NTB function driver configures the SoC with multiple PCIe Endpoint + * (EP) controller instances (see diagram below) in such a way that + * transactions from one EP controller are routed to the other EP controller. + * Once PCI NTB function driver configures the SoC with multiple EP instances, + * HOST1 and HOST2 can communicate with each other using SoC as a bridge. + * + * +-------------+ +-------------+ + * | | | | + * | HOST1 | | HOST2 | + * | | | | + * +------^------+ +------^------+ + * | | + * | | + * +---------|-------------------------------------------------|---------+ + * | +------v------+ +------v------+ | + * | | | | | | + * | | EP | | EP | | + * | | CONTROLLER1 | | CONTROLLER2 | | + * | | <-----------------------------------> | | + * | | | | | | + * | | | | | | + * | | | SoC With Multiple EP Instances | | | + * | | | (Configured using NTB Function) | | | + * | +-------------+ +-------------+ | + * +---------------------------------------------------------------------+ + */ + +#include +#include +#include +#include + +#include +#include + +static struct workqueue_struct *kpcintb_workqueue; + +#define COMMAND_CONFIGURE_DOORBELL 1 +#define COMMAND_TEARDOWN_DOORBELL 2 +#define COMMAND_CONFIGURE_MW 3 +#define COMMAND_TEARDOWN_MW 4 +#define COMMAND_LINK_UP 5 +#define COMMAND_LINK_DOWN 6 + +#define COMMAND_STATUS_OK 1 +#define COMMAND_STATUS_ERROR 2 + +#define LINK_STATUS_UP BIT(0) + +#define SPAD_COUNT 64 +#define DB_COUNT 4 +#define NTB_MW_OFFSET 2 +#define DB_COUNT_MASK GENMASK(15, 0) +#define MSIX_ENABLE BIT(16) +#define MAX_DB_COUNT 32 +#define MAX_MW 4 + +enum epf_ntb_bar { + BAR_CONFIG, + BAR_PEER_SPAD, + BAR_DB_MW1, + BAR_MW2, + BAR_MW3, + BAR_MW4, +}; + +struct epf_ntb { + u32 num_mws; + u32 db_count; + u32 spad_count; + struct pci_epf *epf; + u64 mws_size[MAX_MW]; + struct config_group group; + struct epf_ntb_epc *epc[2]; +}; + +#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group) + +struct epf_ntb_epc { + u8 func_no; + u8 vfunc_no; + bool linkup; + bool is_msix; + int msix_bar; + u32 spad_size; + struct pci_epc *epc; + struct epf_ntb *epf_ntb; + void __iomem *mw_addr[6]; + size_t msix_table_offset; + struct epf_ntb_ctrl *reg; + struct pci_epf_bar *epf_bar; + enum pci_barno epf_ntb_bar[6]; + struct delayed_work cmd_handler; + enum pci_epc_interface_type type; + const struct pci_epc_features *epc_features; +}; + +struct epf_ntb_ctrl { + u32 command; + u32 argument; + u16 command_status; + u16 link_status; + u32 topology; + u64 addr; + u64 size; + u32 num_mws; + u32 mw1_offset; + u32 spad_offset; + u32 spad_count; + u32 db_entry_size; + u32 db_data[MAX_DB_COUNT]; + u32 db_offset[MAX_DB_COUNT]; +} __packed; + +static struct pci_epf_header epf_ntb_header = { + .vendorid = PCI_ANY_ID, + .deviceid = PCI_ANY_ID, + .baseclass_code = PCI_BASE_CLASS_MEMORY, + .interrupt_pin = PCI_INTERRUPT_INTA, +}; + +/** + * epf_ntb_link_up() - Raise link_up interrupt to both the hosts + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @link_up: true or false indicating Link is UP or Down + * + * Once NTB function in HOST1 and the NTB function in HOST2 invoke + * ntb_link_enable(), this NTB function driver will trigger a link event to + * the NTB client in both the hosts. + */ +static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) +{ + enum pci_epc_interface_type type; + enum pci_epc_irq_type irq_type; + struct epf_ntb_epc *ntb_epc; + struct epf_ntb_ctrl *ctrl; + struct pci_epc *epc; + u8 func_no, vfunc_no; + bool is_msix; + int ret; + + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) { + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + is_msix = ntb_epc->is_msix; + ctrl = ntb_epc->reg; + if (link_up) + ctrl->link_status |= LINK_STATUS_UP; + else + ctrl->link_status &= ~LINK_STATUS_UP; + irq_type = is_msix ? PCI_EPC_IRQ_MSIX : PCI_EPC_IRQ_MSI; + ret = pci_epc_raise_irq(epc, func_no, vfunc_no, irq_type, 1); + if (ret) { + dev_err(&epc->dev, + "%s intf: Failed to raise Link Up IRQ\n", + pci_epc_interface_string(type)); + return ret; + } + } + + return 0; +} + +/** + * epf_ntb_configure_mw() - Configure the Outbound Address Space for one host + * to access the memory window of other host + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * @mw: Index of the memory window (either 0, 1, 2 or 3) + * + * +-----------------+ +---->+----------------+-----------+-----------------+ + * | BAR0 | | | Doorbell 1 +-----------> MSI|X ADDRESS 1 | + * +-----------------+ | +----------------+ +-----------------+ + * | BAR1 | | | Doorbell 2 +---------+ | | + * +-----------------+----+ +----------------+ | | | + * | BAR2 | | Doorbell 3 +-------+ | +-----------------+ + * +-----------------+----+ +----------------+ | +-> MSI|X ADDRESS 2 | + * | BAR3 | | | Doorbell 4 +-----+ | +-----------------+ + * +-----------------+ | |----------------+ | | | | + * | BAR4 | | | | | | +-----------------+ + * +-----------------+ | | MW1 +---+ | +-->+ MSI|X ADDRESS 3|| + * | BAR5 | | | | | | +-----------------+ + * +-----------------+ +---->-----------------+ | | | | + * EP CONTROLLER 1 | | | | +-----------------+ + * | | | +---->+ MSI|X ADDRESS 4 | + * +----------------+ | +-----------------+ + * (A) EP CONTROLLER 2 | | | + * (OB SPACE) | | | + * +-------> MW1 | + * | | + * | | + * (B) +-----------------+ + * | | + * | | + * | | + * | | + * | | + * +-----------------+ + * PCI Address Space + * (Managed by HOST2) + * + * This function performs stage (B) in the above diagram (see MW1) i.e., map OB + * address space of memory window to PCI address space. + * + * This operation requires 3 parameters + * 1) Address in the outbound address space + * 2) Address in the PCI Address space + * 3) Size of the address region to be mapped + * + * The address in the outbound address space (for MW1, MW2, MW3 and MW4) is + * stored in epf_bar corresponding to BAR_DB_MW1 for MW1 and BAR_MW2, BAR_MW3 + * BAR_MW4 for rest of the BARs of epf_ntb_epc that is connected to HOST1. This + * is populated in epf_ntb_alloc_peer_mem() in this driver. + * + * The address and size of the PCI address region that has to be mapped would + * be provided by HOST2 in ctrl->addr and ctrl->size of epf_ntb_epc that is + * connected to HOST2. + * + * Please note Memory window1 (MW1) and Doorbell registers together will be + * mapped to a single BAR (BAR2) above for 32-bit BARs. The exact BAR that's + * used for Memory window (MW) can be obtained from epf_ntb_bar[BAR_DB_MW1], + * epf_ntb_bar[BAR_MW2], epf_ntb_bar[BAR_MW2], epf_ntb_bar[BAR_MW2]. + */ +static int epf_ntb_configure_mw(struct epf_ntb *ntb, + enum pci_epc_interface_type type, u32 mw) +{ + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + struct pci_epf_bar *peer_epf_bar; + enum pci_barno peer_barno; + struct epf_ntb_ctrl *ctrl; + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + struct pci_epc *epc; + u64 addr, size; + int ret = 0; + + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + + peer_ntb_epc = ntb->epc[!type]; + peer_barno = peer_ntb_epc->epf_ntb_bar[mw + NTB_MW_OFFSET]; + peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno]; + + phys_addr = peer_epf_bar->phys_addr; + ctrl = ntb_epc->reg; + addr = ctrl->addr; + size = ctrl->size; + if (mw + NTB_MW_OFFSET == BAR_DB_MW1) + phys_addr += ctrl->mw1_offset; + + if (size > ntb->mws_size[mw]) { + dev_err(&epc->dev, + "%s intf: MW: %d Req Sz:%llxx > Supported Sz:%llx\n", + pci_epc_interface_string(type), mw, size, + ntb->mws_size[mw]); + ret = -EINVAL; + goto err_invalid_size; + } + + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + ret = pci_epc_map_addr(epc, func_no, vfunc_no, phys_addr, addr, size); + if (ret) + dev_err(&epc->dev, + "%s intf: Failed to map memory window %d address\n", + pci_epc_interface_string(type), mw); + +err_invalid_size: + + return ret; +} + +/** + * epf_ntb_teardown_mw() - Teardown the configured OB ATU + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * @mw: Index of the memory window (either 0, 1, 2 or 3) + * + * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using + * pci_epc_unmap_addr() + */ +static void epf_ntb_teardown_mw(struct epf_ntb *ntb, + enum pci_epc_interface_type type, u32 mw) +{ + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + struct pci_epf_bar *peer_epf_bar; + enum pci_barno peer_barno; + struct epf_ntb_ctrl *ctrl; + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + struct pci_epc *epc; + + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + + peer_ntb_epc = ntb->epc[!type]; + peer_barno = peer_ntb_epc->epf_ntb_bar[mw + NTB_MW_OFFSET]; + peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno]; + + phys_addr = peer_epf_bar->phys_addr; + ctrl = ntb_epc->reg; + if (mw + NTB_MW_OFFSET == BAR_DB_MW1) + phys_addr += ctrl->mw1_offset; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + pci_epc_unmap_addr(epc, func_no, vfunc_no, phys_addr); +} + +/** + * epf_ntb_configure_msi() - Map OB address space to MSI address + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * @db_count: Number of doorbell interrupts to map + * + *+-----------------+ +----->+----------------+-----------+-----------------+ + *| BAR0 | | | Doorbell 1 +---+-------> MSI ADDRESS | + *+-----------------+ | +----------------+ | +-----------------+ + *| BAR1 | | | Doorbell 2 +---+ | | + *+-----------------+----+ +----------------+ | | | + *| BAR2 | | Doorbell 3 +---+ | | + *+-----------------+----+ +----------------+ | | | + *| BAR3 | | | Doorbell 4 +---+ | | + *+-----------------+ | |----------------+ | | + *| BAR4 | | | | | | + *+-----------------+ | | MW1 | | | + *| BAR5 | | | | | | + *+-----------------+ +----->-----------------+ | | + * EP CONTROLLER 1 | | | | + * | | | | + * +----------------+ +-----------------+ + * (A) EP CONTROLLER 2 | | + * (OB SPACE) | | + * | MW1 | + * | | + * | | + * (B) +-----------------+ + * | | + * | | + * | | + * | | + * | | + * +-----------------+ + * PCI Address Space + * (Managed by HOST2) + * + * + * This function performs stage (B) in the above diagram (see Doorbell 1, + * Doorbell 2, Doorbell 3, Doorbell 4) i.e map OB address space corresponding to + * doorbell to MSI address in PCI address space. + * + * This operation requires 3 parameters + * 1) Address reserved for doorbell in the outbound address space + * 2) MSI-X address in the PCIe Address space + * 3) Number of MSI-X interrupts that has to be configured + * + * The address in the outbound address space (for the Doorbell) is stored in + * epf_bar corresponding to BAR_DB_MW1 of epf_ntb_epc that is connected to + * HOST1. This is populated in epf_ntb_alloc_peer_mem() in this driver along + * with address for MW1. + * + * pci_epc_map_msi_irq() takes the MSI address from MSI capability register + * and maps the OB address (obtained in epf_ntb_alloc_peer_mem()) to the MSI + * address. + * + * epf_ntb_configure_msi() also stores the MSI data to raise each interrupt + * in db_data of the peer's control region. This helps the peer to raise + * doorbell of the other host by writing db_data to the BAR corresponding to + * BAR_DB_MW1. + */ +static int epf_ntb_configure_msi(struct epf_ntb *ntb, + enum pci_epc_interface_type type, u16 db_count) +{ + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + u32 db_entry_size, db_data, db_offset; + struct pci_epf_bar *peer_epf_bar; + struct epf_ntb_ctrl *peer_ctrl; + enum pci_barno peer_barno; + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + struct pci_epc *epc; + int ret, i; + + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + + peer_ntb_epc = ntb->epc[!type]; + peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1]; + peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno]; + peer_ctrl = peer_ntb_epc->reg; + db_entry_size = peer_ctrl->db_entry_size; + + phys_addr = peer_epf_bar->phys_addr; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + ret = pci_epc_map_msi_irq(epc, func_no, vfunc_no, phys_addr, db_count, + db_entry_size, &db_data, &db_offset); + if (ret) { + dev_err(&epc->dev, "%s intf: Failed to map MSI IRQ\n", + pci_epc_interface_string(type)); + return ret; + } + + for (i = 0; i < db_count; i++) { + peer_ctrl->db_data[i] = db_data | i; + peer_ctrl->db_offset[i] = db_offset; + } + + return 0; +} + +/** + * epf_ntb_configure_msix() - Map OB address space to MSI-X address + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * @db_count: Number of doorbell interrupts to map + * + *+-----------------+ +----->+----------------+-----------+-----------------+ + *| BAR0 | | | Doorbell 1 +-----------> MSI-X ADDRESS 1 | + *+-----------------+ | +----------------+ +-----------------+ + *| BAR1 | | | Doorbell 2 +---------+ | | + *+-----------------+----+ +----------------+ | | | + *| BAR2 | | Doorbell 3 +-------+ | +-----------------+ + *+-----------------+----+ +----------------+ | +-> MSI-X ADDRESS 2 | + *| BAR3 | | | Doorbell 4 +-----+ | +-----------------+ + *+-----------------+ | |----------------+ | | | | + *| BAR4 | | | | | | +-----------------+ + *+-----------------+ | | MW1 + | +-->+ MSI-X ADDRESS 3|| + *| BAR5 | | | | | +-----------------+ + *+-----------------+ +----->-----------------+ | | | + * EP CONTROLLER 1 | | | +-----------------+ + * | | +---->+ MSI-X ADDRESS 4 | + * +----------------+ +-----------------+ + * (A) EP CONTROLLER 2 | | + * (OB SPACE) | | + * | MW1 | + * | | + * | | + * (B) +-----------------+ + * | | + * | | + * | | + * | | + * | | + * +-----------------+ + * PCI Address Space + * (Managed by HOST2) + * + * This function performs stage (B) in the above diagram (see Doorbell 1, + * Doorbell 2, Doorbell 3, Doorbell 4) i.e map OB address space corresponding to + * doorbell to MSI-X address in PCI address space. + * + * This operation requires 3 parameters + * 1) Address reserved for doorbell in the outbound address space + * 2) MSI-X address in the PCIe Address space + * 3) Number of MSI-X interrupts that has to be configured + * + * The address in the outbound address space (for the Doorbell) is stored in + * epf_bar corresponding to BAR_DB_MW1 of epf_ntb_epc that is connected to + * HOST1. This is populated in epf_ntb_alloc_peer_mem() in this driver along + * with address for MW1. + * + * The MSI-X address is in the MSI-X table of EP CONTROLLER 2 and + * the count of doorbell is in ctrl->argument of epf_ntb_epc that is connected + * to HOST2. MSI-X table is stored memory mapped to ntb_epc->msix_bar and the + * offset is in ntb_epc->msix_table_offset. From this epf_ntb_configure_msix() + * gets the MSI-X address and data. + * + * epf_ntb_configure_msix() also stores the MSI-X data to raise each interrupt + * in db_data of the peer's control region. This helps the peer to raise + * doorbell of the other host by writing db_data to the BAR corresponding to + * BAR_DB_MW1. + */ +static int epf_ntb_configure_msix(struct epf_ntb *ntb, + enum pci_epc_interface_type type, + u16 db_count) +{ + const struct pci_epc_features *epc_features; + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + struct pci_epf_bar *peer_epf_bar, *epf_bar; + struct pci_epf_msix_tbl *msix_tbl; + struct epf_ntb_ctrl *peer_ctrl; + u32 db_entry_size, msg_data; + enum pci_barno peer_barno; + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + struct pci_epc *epc; + size_t align; + u64 msg_addr; + int ret, i; + + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + + epf_bar = &ntb_epc->epf_bar[ntb_epc->msix_bar]; + msix_tbl = epf_bar->addr + ntb_epc->msix_table_offset; + + peer_ntb_epc = ntb->epc[!type]; + peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1]; + peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno]; + phys_addr = peer_epf_bar->phys_addr; + peer_ctrl = peer_ntb_epc->reg; + epc_features = ntb_epc->epc_features; + align = epc_features->align; + + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + db_entry_size = peer_ctrl->db_entry_size; + + for (i = 0; i < db_count; i++) { + msg_addr = ALIGN_DOWN(msix_tbl[i].msg_addr, align); + msg_data = msix_tbl[i].msg_data; + ret = pci_epc_map_addr(epc, func_no, vfunc_no, phys_addr, msg_addr, + db_entry_size); + if (ret) { + dev_err(&epc->dev, + "%s intf: Failed to configure MSI-X IRQ\n", + pci_epc_interface_string(type)); + return ret; + } + phys_addr = phys_addr + db_entry_size; + peer_ctrl->db_data[i] = msg_data; + peer_ctrl->db_offset[i] = msix_tbl[i].msg_addr & (align - 1); + } + ntb_epc->is_msix = true; + + return 0; +} + +/** + * epf_ntb_configure_db() - Configure the Outbound Address Space for one host + * to ring the doorbell of other host + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * @db_count: Count of the number of doorbells that has to be configured + * @msix: Indicates whether MSI-X or MSI should be used + * + * Invokes epf_ntb_configure_msix() or epf_ntb_configure_msi() required for + * one HOST to ring the doorbell of other HOST. + */ +static int epf_ntb_configure_db(struct epf_ntb *ntb, + enum pci_epc_interface_type type, + u16 db_count, bool msix) +{ + struct epf_ntb_epc *ntb_epc; + struct pci_epc *epc; + int ret; + + if (db_count > MAX_DB_COUNT) + return -EINVAL; + + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + + if (msix) + ret = epf_ntb_configure_msix(ntb, type, db_count); + else + ret = epf_ntb_configure_msi(ntb, type, db_count); + + if (ret) + dev_err(&epc->dev, "%s intf: Failed to configure DB\n", + pci_epc_interface_string(type)); + + return ret; +} + +/** + * epf_ntb_teardown_db() - Unmap address in OB address space to MSI/MSI-X + * address + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Invoke pci_epc_unmap_addr() to unmap OB address to MSI/MSI-X address. + */ +static void +epf_ntb_teardown_db(struct epf_ntb *ntb, enum pci_epc_interface_type type) +{ + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + struct pci_epf_bar *peer_epf_bar; + enum pci_barno peer_barno; + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + struct pci_epc *epc; + + ntb_epc = ntb->epc[type]; + epc = ntb_epc->epc; + + peer_ntb_epc = ntb->epc[!type]; + peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1]; + peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno]; + phys_addr = peer_epf_bar->phys_addr; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + pci_epc_unmap_addr(epc, func_no, vfunc_no, phys_addr); +} + +/** + * epf_ntb_cmd_handler() - Handle commands provided by the NTB Host + * @work: work_struct for the two epf_ntb_epc (PRIMARY and SECONDARY) + * + * Workqueue function that gets invoked for the two epf_ntb_epc + * periodically (once every 5ms) to see if it has received any commands + * from NTB host. The host can send commands to configure doorbell or + * configure memory window or to update link status. + */ +static void epf_ntb_cmd_handler(struct work_struct *work) +{ + enum pci_epc_interface_type type; + struct epf_ntb_epc *ntb_epc; + struct epf_ntb_ctrl *ctrl; + u32 command, argument; + struct epf_ntb *ntb; + struct device *dev; + u16 db_count; + bool is_msix; + int ret; + + ntb_epc = container_of(work, struct epf_ntb_epc, cmd_handler.work); + ctrl = ntb_epc->reg; + command = ctrl->command; + if (!command) + goto reset_handler; + argument = ctrl->argument; + + ctrl->command = 0; + ctrl->argument = 0; + + ctrl = ntb_epc->reg; + type = ntb_epc->type; + ntb = ntb_epc->epf_ntb; + dev = &ntb->epf->dev; + + switch (command) { + case COMMAND_CONFIGURE_DOORBELL: + db_count = argument & DB_COUNT_MASK; + is_msix = argument & MSIX_ENABLE; + ret = epf_ntb_configure_db(ntb, type, db_count, is_msix); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_TEARDOWN_DOORBELL: + epf_ntb_teardown_db(ntb, type); + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_CONFIGURE_MW: + ret = epf_ntb_configure_mw(ntb, type, argument); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_TEARDOWN_MW: + epf_ntb_teardown_mw(ntb, type, argument); + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_LINK_UP: + ntb_epc->linkup = true; + if (ntb->epc[PRIMARY_INTERFACE]->linkup && + ntb->epc[SECONDARY_INTERFACE]->linkup) { + ret = epf_ntb_link_up(ntb, true); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + goto reset_handler; + } + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_LINK_DOWN: + ntb_epc->linkup = false; + ret = epf_ntb_link_up(ntb, false); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + default: + dev_err(dev, "%s intf UNKNOWN command: %d\n", + pci_epc_interface_string(type), command); + break; + } + +reset_handler: + queue_delayed_work(kpcintb_workqueue, &ntb_epc->cmd_handler, + msecs_to_jiffies(5)); +} + +/** + * epf_ntb_peer_spad_bar_clear() - Clear Peer Scratchpad BAR + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + *+-----------------+------->+------------------+ +-----------------+ + *| BAR0 | | CONFIG REGION | | BAR0 | + *+-----------------+----+ +------------------+<-------+-----------------+ + *| BAR1 | | |SCRATCHPAD REGION | | BAR1 | + *+-----------------+ +-->+------------------+<-------+-----------------+ + *| BAR2 | Local Memory | BAR2 | + *+-----------------+ +-----------------+ + *| BAR3 | | BAR3 | + *+-----------------+ +-----------------+ + *| BAR4 | | BAR4 | + *+-----------------+ +-----------------+ + *| BAR5 | | BAR5 | + *+-----------------+ +-----------------+ + * EP CONTROLLER 1 EP CONTROLLER 2 + * + * Clear BAR1 of EP CONTROLLER 2 which contains the HOST2's peer scratchpad + * region. While BAR1 is the default peer scratchpad BAR, an NTB could have + * other BARs for peer scratchpad (because of 64-bit BARs or reserved BARs). + * This function can get the exact BAR used for peer scratchpad from + * epf_ntb_bar[BAR_PEER_SPAD]. + * + * Since HOST2's peer scratchpad is also HOST1's self scratchpad, this function + * gets the address of peer scratchpad from + * peer_ntb_epc->epf_ntb_bar[BAR_CONFIG]. + */ +static void epf_ntb_peer_spad_bar_clear(struct epf_ntb_epc *ntb_epc) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct pci_epc *epc; + + epc = ntb_epc->epc; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + barno = ntb_epc->epf_ntb_bar[BAR_PEER_SPAD]; + epf_bar = &ntb_epc->epf_bar[barno]; + pci_epc_clear_bar(epc, func_no, vfunc_no, epf_bar); +} + +/** + * epf_ntb_peer_spad_bar_set() - Set peer scratchpad BAR + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + *+-----------------+------->+------------------+ +-----------------+ + *| BAR0 | | CONFIG REGION | | BAR0 | + *+-----------------+----+ +------------------+<-------+-----------------+ + *| BAR1 | | |SCRATCHPAD REGION | | BAR1 | + *+-----------------+ +-->+------------------+<-------+-----------------+ + *| BAR2 | Local Memory | BAR2 | + *+-----------------+ +-----------------+ + *| BAR3 | | BAR3 | + *+-----------------+ +-----------------+ + *| BAR4 | | BAR4 | + *+-----------------+ +-----------------+ + *| BAR5 | | BAR5 | + *+-----------------+ +-----------------+ + * EP CONTROLLER 1 EP CONTROLLER 2 + * + * Set BAR1 of EP CONTROLLER 2 which contains the HOST2's peer scratchpad + * region. While BAR1 is the default peer scratchpad BAR, an NTB could have + * other BARs for peer scratchpad (because of 64-bit BARs or reserved BARs). + * This function can get the exact BAR used for peer scratchpad from + * epf_ntb_bar[BAR_PEER_SPAD]. + * + * Since HOST2's peer scratchpad is also HOST1's self scratchpad, this function + * gets the address of peer scratchpad from + * peer_ntb_epc->epf_ntb_bar[BAR_CONFIG]. + */ +static int epf_ntb_peer_spad_bar_set(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + struct pci_epf_bar *peer_epf_bar, *epf_bar; + enum pci_barno peer_barno, barno; + u32 peer_spad_offset; + u8 func_no, vfunc_no; + struct pci_epc *epc; + struct device *dev; + int ret; + + dev = &ntb->epf->dev; + + peer_ntb_epc = ntb->epc[!type]; + peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_CONFIG]; + peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno]; + + ntb_epc = ntb->epc[type]; + barno = ntb_epc->epf_ntb_bar[BAR_PEER_SPAD]; + epf_bar = &ntb_epc->epf_bar[barno]; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + epc = ntb_epc->epc; + + peer_spad_offset = peer_ntb_epc->reg->spad_offset; + epf_bar->phys_addr = peer_epf_bar->phys_addr + peer_spad_offset; + epf_bar->size = peer_ntb_epc->spad_size; + epf_bar->barno = barno; + epf_bar->flags = PCI_BASE_ADDRESS_MEM_TYPE_32; + + ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "%s intf: peer SPAD BAR set failed\n", + pci_epc_interface_string(type)); + return ret; + } + + return 0; +} + +/** + * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * +-----------------+------->+------------------+ +-----------------+ + * | BAR0 | | CONFIG REGION | | BAR0 | + * +-----------------+----+ +------------------+<-------+-----------------+ + * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | + * +-----------------+ +-->+------------------+<-------+-----------------+ + * | BAR2 | Local Memory | BAR2 | + * +-----------------+ +-----------------+ + * | BAR3 | | BAR3 | + * +-----------------+ +-----------------+ + * | BAR4 | | BAR4 | + * +-----------------+ +-----------------+ + * | BAR5 | | BAR5 | + * +-----------------+ +-----------------+ + * EP CONTROLLER 1 EP CONTROLLER 2 + * + * Clear BAR0 of EP CONTROLLER 1 which contains the HOST1's config and + * self scratchpad region (removes inbound ATU configuration). While BAR0 is + * the default self scratchpad BAR, an NTB could have other BARs for self + * scratchpad (because of reserved BARs). This function can get the exact BAR + * used for self scratchpad from epf_ntb_bar[BAR_CONFIG]. + * + * Please note the self scratchpad region and config region is combined to + * a single region and mapped using the same BAR. Also note HOST2's peer + * scratchpad is HOST1's self scratchpad. + */ +static void epf_ntb_config_sspad_bar_clear(struct epf_ntb_epc *ntb_epc) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct pci_epc *epc; + + epc = ntb_epc->epc; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + barno = ntb_epc->epf_ntb_bar[BAR_CONFIG]; + epf_bar = &ntb_epc->epf_bar[barno]; + pci_epc_clear_bar(epc, func_no, vfunc_no, epf_bar); +} + +/** + * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * +-----------------+------->+------------------+ +-----------------+ + * | BAR0 | | CONFIG REGION | | BAR0 | + * +-----------------+----+ +------------------+<-------+-----------------+ + * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | + * +-----------------+ +-->+------------------+<-------+-----------------+ + * | BAR2 | Local Memory | BAR2 | + * +-----------------+ +-----------------+ + * | BAR3 | | BAR3 | + * +-----------------+ +-----------------+ + * | BAR4 | | BAR4 | + * +-----------------+ +-----------------+ + * | BAR5 | | BAR5 | + * +-----------------+ +-----------------+ + * EP CONTROLLER 1 EP CONTROLLER 2 + * + * Map BAR0 of EP CONTROLLER 1 which contains the HOST1's config and + * self scratchpad region. While BAR0 is the default self scratchpad BAR, an + * NTB could have other BARs for self scratchpad (because of reserved BARs). + * This function can get the exact BAR used for self scratchpad from + * epf_ntb_bar[BAR_CONFIG]. + * + * Please note the self scratchpad region and config region is combined to + * a single region and mapped using the same BAR. Also note HOST2's peer + * scratchpad is HOST1's self scratchpad. + */ +static int epf_ntb_config_sspad_bar_set(struct epf_ntb_epc *ntb_epc) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct epf_ntb *ntb; + struct pci_epc *epc; + struct device *dev; + int ret; + + ntb = ntb_epc->epf_ntb; + dev = &ntb->epf->dev; + + epc = ntb_epc->epc; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + barno = ntb_epc->epf_ntb_bar[BAR_CONFIG]; + epf_bar = &ntb_epc->epf_bar[barno]; + + ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "%s inft: Config/Status/SPAD BAR set failed\n", + pci_epc_interface_string(ntb_epc->type)); + return ret; + } + + return 0; +} + +/** + * epf_ntb_config_spad_bar_free() - Free the physical memory associated with + * config + scratchpad region + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * +-----------------+------->+------------------+ +-----------------+ + * | BAR0 | | CONFIG REGION | | BAR0 | + * +-----------------+----+ +------------------+<-------+-----------------+ + * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | + * +-----------------+ +-->+------------------+<-------+-----------------+ + * | BAR2 | Local Memory | BAR2 | + * +-----------------+ +-----------------+ + * | BAR3 | | BAR3 | + * +-----------------+ +-----------------+ + * | BAR4 | | BAR4 | + * +-----------------+ +-----------------+ + * | BAR5 | | BAR5 | + * +-----------------+ +-----------------+ + * EP CONTROLLER 1 EP CONTROLLER 2 + * + * Free the Local Memory mentioned in the above diagram. After invoking this + * function, any of config + self scratchpad region of HOST1 or peer scratchpad + * region of HOST2 should not be accessed. + */ +static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb) +{ + enum pci_epc_interface_type type; + struct epf_ntb_epc *ntb_epc; + enum pci_barno barno; + struct pci_epf *epf; + + epf = ntb->epf; + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) { + ntb_epc = ntb->epc[type]; + barno = ntb_epc->epf_ntb_bar[BAR_CONFIG]; + if (ntb_epc->reg) + pci_epf_free_space(epf, ntb_epc->reg, barno, type); + } +} + +/** + * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad + * region + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * +-----------------+------->+------------------+ +-----------------+ + * | BAR0 | | CONFIG REGION | | BAR0 | + * +-----------------+----+ +------------------+<-------+-----------------+ + * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | + * +-----------------+ +-->+------------------+<-------+-----------------+ + * | BAR2 | Local Memory | BAR2 | + * +-----------------+ +-----------------+ + * | BAR3 | | BAR3 | + * +-----------------+ +-----------------+ + * | BAR4 | | BAR4 | + * +-----------------+ +-----------------+ + * | BAR5 | | BAR5 | + * +-----------------+ +-----------------+ + * EP CONTROLLER 1 EP CONTROLLER 2 + * + * Allocate the Local Memory mentioned in the above diagram. The size of + * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION + * is obtained from "spad-count" configfs entry. + * + * The size of both config region and scratchpad region has to be aligned, + * since the scratchpad region will also be mapped as PEER SCRATCHPAD of + * other host using a separate BAR. + */ +static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + const struct pci_epc_features *peer_epc_features, *epc_features; + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + size_t msix_table_size, pba_size, align; + enum pci_barno peer_barno, barno; + struct epf_ntb_ctrl *ctrl; + u32 spad_size, ctrl_size; + u64 size, peer_size; + struct pci_epf *epf; + struct device *dev; + bool msix_capable; + u32 spad_count; + void *base; + + epf = ntb->epf; + dev = &epf->dev; + ntb_epc = ntb->epc[type]; + + epc_features = ntb_epc->epc_features; + barno = ntb_epc->epf_ntb_bar[BAR_CONFIG]; + size = epc_features->bar_fixed_size[barno]; + align = epc_features->align; + + peer_ntb_epc = ntb->epc[!type]; + peer_epc_features = peer_ntb_epc->epc_features; + peer_barno = ntb_epc->epf_ntb_bar[BAR_PEER_SPAD]; + peer_size = peer_epc_features->bar_fixed_size[peer_barno]; + + /* Check if epc_features is populated incorrectly */ + if ((!IS_ALIGNED(size, align))) + return -EINVAL; + + spad_count = ntb->spad_count; + + ctrl_size = sizeof(struct epf_ntb_ctrl); + spad_size = spad_count * 4; + + msix_capable = epc_features->msix_capable; + if (msix_capable) { + msix_table_size = PCI_MSIX_ENTRY_SIZE * ntb->db_count; + ctrl_size = ALIGN(ctrl_size, 8); + ntb_epc->msix_table_offset = ctrl_size; + ntb_epc->msix_bar = barno; + /* Align to QWORD or 8 Bytes */ + pba_size = ALIGN(DIV_ROUND_UP(ntb->db_count, 8), 8); + ctrl_size = ctrl_size + msix_table_size + pba_size; + } + + if (!align) { + ctrl_size = roundup_pow_of_two(ctrl_size); + spad_size = roundup_pow_of_two(spad_size); + } else { + ctrl_size = ALIGN(ctrl_size, align); + spad_size = ALIGN(spad_size, align); + } + + if (peer_size) { + if (peer_size < spad_size) + spad_count = peer_size / 4; + spad_size = peer_size; + } + + /* + * In order to make sure SPAD offset is aligned to its size, + * expand control region size to the size of SPAD if SPAD size + * is greater than control region size. + */ + if (spad_size > ctrl_size) + ctrl_size = spad_size; + + if (!size) + size = ctrl_size + spad_size; + else if (size < ctrl_size + spad_size) + return -EINVAL; + + base = pci_epf_alloc_space(epf, size, barno, align, type); + if (!base) { + dev_err(dev, "%s intf: Config/Status/SPAD alloc region fail\n", + pci_epc_interface_string(type)); + return -ENOMEM; + } + + ntb_epc->reg = base; + + ctrl = ntb_epc->reg; + ctrl->spad_offset = ctrl_size; + ctrl->spad_count = spad_count; + ctrl->num_mws = ntb->num_mws; + ctrl->db_entry_size = align ? align : 4; + ntb_epc->spad_size = spad_size; + + return 0; +} + +/** + * epf_ntb_config_spad_bar_alloc_interface() - Allocate memory for config + + * scratchpad region for each of PRIMARY and SECONDARY interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Wrapper for epf_ntb_config_spad_bar_alloc() which allocates memory for + * config + scratchpad region for a specific interface + */ +static int epf_ntb_config_spad_bar_alloc_interface(struct epf_ntb *ntb) +{ + enum pci_epc_interface_type type; + struct device *dev; + int ret; + + dev = &ntb->epf->dev; + + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) { + ret = epf_ntb_config_spad_bar_alloc(ntb, type); + if (ret) { + dev_err(dev, "%s intf: Config/SPAD BAR alloc failed\n", + pci_epc_interface_string(type)); + return ret; + } + } + + return 0; +} + +/** + * epf_ntb_free_peer_mem() - Free memory allocated in peers outbound address + * space + * @ntb_epc: EPC associated with one of the HOST which holds peers outbound + * address regions + * + * +-----------------+ +---->+----------------+-----------+-----------------+ + * | BAR0 | | | Doorbell 1 +-----------> MSI|X ADDRESS 1 | + * +-----------------+ | +----------------+ +-----------------+ + * | BAR1 | | | Doorbell 2 +---------+ | | + * +-----------------+----+ +----------------+ | | | + * | BAR2 | | Doorbell 3 +-------+ | +-----------------+ + * +-----------------+----+ +----------------+ | +-> MSI|X ADDRESS 2 | + * | BAR3 | | | Doorbell 4 +-----+ | +-----------------+ + * +-----------------+ | |----------------+ | | | | + * | BAR4 | | | | | | +-----------------+ + * +-----------------+ | | MW1 +---+ | +-->+ MSI|X ADDRESS 3|| + * | BAR5 | | | | | | +-----------------+ + * +-----------------+ +---->-----------------+ | | | | + * EP CONTROLLER 1 | | | | +-----------------+ + * | | | +---->+ MSI|X ADDRESS 4 | + * +----------------+ | +-----------------+ + * (A) EP CONTROLLER 2 | | | + * (OB SPACE) | | | + * +-------> MW1 | + * | | + * | | + * (B) +-----------------+ + * | | + * | | + * | | + * | | + * | | + * +-----------------+ + * PCI Address Space + * (Managed by HOST2) + * + * Free memory allocated in EP CONTROLLER 2 (OB SPACE) in the above diagram. + * It'll free Doorbell 1, Doorbell 2, Doorbell 3, Doorbell 4, MW1 (and MW2, MW3, + * MW4). + */ +static void epf_ntb_free_peer_mem(struct epf_ntb_epc *ntb_epc) +{ + struct pci_epf_bar *epf_bar; + void __iomem *mw_addr; + phys_addr_t phys_addr; + enum epf_ntb_bar bar; + enum pci_barno barno; + struct pci_epc *epc; + size_t size; + + epc = ntb_epc->epc; + + for (bar = BAR_DB_MW1; bar < BAR_MW4; bar++) { + barno = ntb_epc->epf_ntb_bar[bar]; + mw_addr = ntb_epc->mw_addr[barno]; + epf_bar = &ntb_epc->epf_bar[barno]; + phys_addr = epf_bar->phys_addr; + size = epf_bar->size; + if (mw_addr) { + pci_epc_mem_free_addr(epc, phys_addr, mw_addr, size); + ntb_epc->mw_addr[barno] = NULL; + } + } +} + +/** + * epf_ntb_db_mw_bar_clear() - Clear doorbell and memory BAR + * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound + * address + * + * +-----------------+ +---->+----------------+-----------+-----------------+ + * | BAR0 | | | Doorbell 1 +-----------> MSI|X ADDRESS 1 | + * +-----------------+ | +----------------+ +-----------------+ + * | BAR1 | | | Doorbell 2 +---------+ | | + * +-----------------+----+ +----------------+ | | | + * | BAR2 | | Doorbell 3 +-------+ | +-----------------+ + * +-----------------+----+ +----------------+ | +-> MSI|X ADDRESS 2 | + * | BAR3 | | | Doorbell 4 +-----+ | +-----------------+ + * +-----------------+ | |----------------+ | | | | + * | BAR4 | | | | | | +-----------------+ + * +-----------------+ | | MW1 +---+ | +-->+ MSI|X ADDRESS 3|| + * | BAR5 | | | | | | +-----------------+ + * +-----------------+ +---->-----------------+ | | | | + * EP CONTROLLER 1 | | | | +-----------------+ + * | | | +---->+ MSI|X ADDRESS 4 | + * +----------------+ | +-----------------+ + * (A) EP CONTROLLER 2 | | | + * (OB SPACE) | | | + * +-------> MW1 | + * | | + * | | + * (B) +-----------------+ + * | | + * | | + * | | + * | | + * | | + * +-----------------+ + * PCI Address Space + * (Managed by HOST2) + * + * Clear doorbell and memory BARs (remove inbound ATU configuration). In the above + * diagram it clears BAR2 TO BAR5 of EP CONTROLLER 1 (Doorbell BAR, MW1 BAR, MW2 + * BAR, MW3 BAR and MW4 BAR). + */ +static void epf_ntb_db_mw_bar_clear(struct epf_ntb_epc *ntb_epc) +{ + struct pci_epf_bar *epf_bar; + enum epf_ntb_bar bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct pci_epc *epc; + + epc = ntb_epc->epc; + + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + for (bar = BAR_DB_MW1; bar < BAR_MW4; bar++) { + barno = ntb_epc->epf_ntb_bar[bar]; + epf_bar = &ntb_epc->epf_bar[barno]; + pci_epc_clear_bar(epc, func_no, vfunc_no, epf_bar); + } +} + +/** + * epf_ntb_db_mw_bar_cleanup() - Clear doorbell/memory BAR and free memory + * allocated in peers outbound address space + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Wrapper for epf_ntb_db_mw_bar_clear() to clear HOST1's BAR and + * epf_ntb_free_peer_mem() which frees up HOST2 outbound memory. + */ +static void epf_ntb_db_mw_bar_cleanup(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + + ntb_epc = ntb->epc[type]; + peer_ntb_epc = ntb->epc[!type]; + + epf_ntb_db_mw_bar_clear(ntb_epc); + epf_ntb_free_peer_mem(peer_ntb_epc); +} + +/** + * epf_ntb_configure_interrupt() - Configure MSI/MSI-X capaiblity + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Configure MSI/MSI-X capability for each interface with number of + * interrupts equal to "db_count" configfs entry. + */ +static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + const struct pci_epc_features *epc_features; + bool msix_capable, msi_capable; + struct epf_ntb_epc *ntb_epc; + u8 func_no, vfunc_no; + struct pci_epc *epc; + struct device *dev; + u32 db_count; + int ret; + + ntb_epc = ntb->epc[type]; + dev = &ntb->epf->dev; + + epc_features = ntb_epc->epc_features; + msix_capable = epc_features->msix_capable; + msi_capable = epc_features->msi_capable; + + if (!(msix_capable || msi_capable)) { + dev_err(dev, "MSI or MSI-X is required for doorbell\n"); + return -EINVAL; + } + + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + db_count = ntb->db_count; + if (db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + return -EINVAL; + } + + ntb->db_count = db_count; + epc = ntb_epc->epc; + + if (msi_capable) { + ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count); + if (ret) { + dev_err(dev, "%s intf: MSI configuration failed\n", + pci_epc_interface_string(type)); + return ret; + } + } + + if (msix_capable) { + ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count, + ntb_epc->msix_bar, + ntb_epc->msix_table_offset); + if (ret) { + dev_err(dev, "MSI configuration failed\n"); + return ret; + } + } + + return 0; +} + +/** + * epf_ntb_alloc_peer_mem() - Allocate memory in peer's outbound address space + * @ntb_epc: EPC associated with one of the HOST whose BAR holds peer's outbound + * address + * @bar: BAR of @ntb_epc in for which memory has to be allocated (could be + * BAR_DB_MW1, BAR_MW2, BAR_MW3, BAR_MW4) + * @peer_ntb_epc: EPC associated with HOST whose outbound address space is + * used by @ntb_epc + * @size: Size of the address region that has to be allocated in peers OB SPACE + * + * + * +-----------------+ +---->+----------------+-----------+-----------------+ + * | BAR0 | | | Doorbell 1 +-----------> MSI|X ADDRESS 1 | + * +-----------------+ | +----------------+ +-----------------+ + * | BAR1 | | | Doorbell 2 +---------+ | | + * +-----------------+----+ +----------------+ | | | + * | BAR2 | | Doorbell 3 +-------+ | +-----------------+ + * +-----------------+----+ +----------------+ | +-> MSI|X ADDRESS 2 | + * | BAR3 | | | Doorbell 4 +-----+ | +-----------------+ + * +-----------------+ | |----------------+ | | | | + * | BAR4 | | | | | | +-----------------+ + * +-----------------+ | | MW1 +---+ | +-->+ MSI|X ADDRESS 3|| + * | BAR5 | | | | | | +-----------------+ + * +-----------------+ +---->-----------------+ | | | | + * EP CONTROLLER 1 | | | | +-----------------+ + * | | | +---->+ MSI|X ADDRESS 4 | + * +----------------+ | +-----------------+ + * (A) EP CONTROLLER 2 | | | + * (OB SPACE) | | | + * +-------> MW1 | + * | | + * | | + * (B) +-----------------+ + * | | + * | | + * | | + * | | + * | | + * +-----------------+ + * PCI Address Space + * (Managed by HOST2) + * + * Allocate memory in OB space of EP CONTROLLER 2 in the above diagram. Allocate + * for Doorbell 1, Doorbell 2, Doorbell 3, Doorbell 4, MW1 (and MW2, MW3, MW4). + */ +static int epf_ntb_alloc_peer_mem(struct device *dev, + struct epf_ntb_epc *ntb_epc, + enum epf_ntb_bar bar, + struct epf_ntb_epc *peer_ntb_epc, + size_t size) +{ + const struct pci_epc_features *epc_features; + struct pci_epf_bar *epf_bar; + struct pci_epc *peer_epc; + phys_addr_t phys_addr; + void __iomem *mw_addr; + enum pci_barno barno; + size_t align; + + epc_features = ntb_epc->epc_features; + align = epc_features->align; + + if (size < 128) + size = 128; + + if (align) + size = ALIGN(size, align); + else + size = roundup_pow_of_two(size); + + peer_epc = peer_ntb_epc->epc; + mw_addr = pci_epc_mem_alloc_addr(peer_epc, &phys_addr, size); + if (!mw_addr) { + dev_err(dev, "%s intf: Failed to allocate OB address\n", + pci_epc_interface_string(peer_ntb_epc->type)); + return -ENOMEM; + } + + barno = ntb_epc->epf_ntb_bar[bar]; + epf_bar = &ntb_epc->epf_bar[barno]; + ntb_epc->mw_addr[barno] = mw_addr; + + epf_bar->phys_addr = phys_addr; + epf_bar->size = size; + epf_bar->barno = barno; + epf_bar->flags = PCI_BASE_ADDRESS_MEM_TYPE_32; + + return 0; +} + +/** + * epf_ntb_db_mw_bar_init() - Configure Doorbell and Memory window BARs + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Wrapper for epf_ntb_alloc_peer_mem() and pci_epc_set_bar() that allocates + * memory in OB address space of HOST2 and configures BAR of HOST1 + */ +static int epf_ntb_db_mw_bar_init(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + const struct pci_epc_features *epc_features; + struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; + struct pci_epf_bar *epf_bar; + struct epf_ntb_ctrl *ctrl; + u32 num_mws, db_count; + enum epf_ntb_bar bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct pci_epc *epc; + struct device *dev; + size_t align; + int ret, i; + u64 size; + + ntb_epc = ntb->epc[type]; + peer_ntb_epc = ntb->epc[!type]; + + dev = &ntb->epf->dev; + epc_features = ntb_epc->epc_features; + align = epc_features->align; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + epc = ntb_epc->epc; + num_mws = ntb->num_mws; + db_count = ntb->db_count; + + for (bar = BAR_DB_MW1, i = 0; i < num_mws; bar++, i++) { + if (bar == BAR_DB_MW1) { + align = align ? align : 4; + size = db_count * align; + size = ALIGN(size, ntb->mws_size[i]); + ctrl = ntb_epc->reg; + ctrl->mw1_offset = size; + size += ntb->mws_size[i]; + } else { + size = ntb->mws_size[i]; + } + + ret = epf_ntb_alloc_peer_mem(dev, ntb_epc, bar, + peer_ntb_epc, size); + if (ret) { + dev_err(dev, "%s intf: DoorBell mem alloc failed\n", + pci_epc_interface_string(type)); + goto err_alloc_peer_mem; + } + + barno = ntb_epc->epf_ntb_bar[bar]; + epf_bar = &ntb_epc->epf_bar[barno]; + + ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "%s intf: DoorBell BAR set failed\n", + pci_epc_interface_string(type)); + goto err_alloc_peer_mem; + } + } + + return 0; + +err_alloc_peer_mem: + epf_ntb_db_mw_bar_cleanup(ntb, type); + + return ret; +} + +/** + * epf_ntb_epc_destroy_interface() - Cleanup NTB EPC interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Unbind NTB function device from EPC and relinquish reference to pci_epc + * for each of the interface. + */ +static void epf_ntb_epc_destroy_interface(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + struct epf_ntb_epc *ntb_epc; + struct pci_epc *epc; + struct pci_epf *epf; + + if (type < 0) + return; + + epf = ntb->epf; + ntb_epc = ntb->epc[type]; + if (!ntb_epc) + return; + epc = ntb_epc->epc; + pci_epc_remove_epf(epc, epf, type); + pci_epc_put(epc); +} + +/** + * epf_ntb_epc_destroy() - Cleanup NTB EPC interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces + */ +static void epf_ntb_epc_destroy(struct epf_ntb *ntb) +{ + enum pci_epc_interface_type type; + + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) + epf_ntb_epc_destroy_interface(ntb, type); +} + +/** + * epf_ntb_epc_create_interface() - Create and initialize NTB EPC interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @epc: struct pci_epc to which a particular NTB interface should be associated + * @type: PRIMARY interface or SECONDARY interface + * + * Allocate memory for NTB EPC interface and initialize it. + */ +static int epf_ntb_epc_create_interface(struct epf_ntb *ntb, + struct pci_epc *epc, + enum pci_epc_interface_type type) +{ + const struct pci_epc_features *epc_features; + struct pci_epf_bar *epf_bar; + struct epf_ntb_epc *ntb_epc; + u8 func_no, vfunc_no; + struct pci_epf *epf; + struct device *dev; + + dev = &ntb->epf->dev; + + ntb_epc = devm_kzalloc(dev, sizeof(*ntb_epc), GFP_KERNEL); + if (!ntb_epc) + return -ENOMEM; + + epf = ntb->epf; + vfunc_no = epf->vfunc_no; + if (type == PRIMARY_INTERFACE) { + func_no = epf->func_no; + epf_bar = epf->bar; + } else { + func_no = epf->sec_epc_func_no; + epf_bar = epf->sec_epc_bar; + } + + ntb_epc->linkup = false; + ntb_epc->epc = epc; + ntb_epc->func_no = func_no; + ntb_epc->vfunc_no = vfunc_no; + ntb_epc->type = type; + ntb_epc->epf_bar = epf_bar; + ntb_epc->epf_ntb = ntb; + + epc_features = pci_epc_get_features(epc, func_no, vfunc_no); + if (!epc_features) + return -EINVAL; + ntb_epc->epc_features = epc_features; + + ntb->epc[type] = ntb_epc; + + return 0; +} + +/** + * epf_ntb_epc_create() - Create and initialize NTB EPC interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Get a reference to EPC device and bind NTB function device to that EPC + * for each of the interface. It is also a wrapper to + * epf_ntb_epc_create_interface() to allocate memory for NTB EPC interface + * and initialize it + */ +static int epf_ntb_epc_create(struct epf_ntb *ntb) +{ + struct pci_epf *epf; + struct device *dev; + int ret; + + epf = ntb->epf; + dev = &epf->dev; + + ret = epf_ntb_epc_create_interface(ntb, epf->epc, PRIMARY_INTERFACE); + if (ret) { + dev_err(dev, "PRIMARY intf: Fail to create NTB EPC\n"); + return ret; + } + + ret = epf_ntb_epc_create_interface(ntb, epf->sec_epc, + SECONDARY_INTERFACE); + if (ret) { + dev_err(dev, "SECONDARY intf: Fail to create NTB EPC\n"); + goto err_epc_create; + } + + return 0; + +err_epc_create: + epf_ntb_epc_destroy_interface(ntb, PRIMARY_INTERFACE); + + return ret; +} + +/** + * epf_ntb_init_epc_bar_interface() - Identify BARs to be used for each of + * the NTB constructs (scratchpad region, doorbell, memorywindow) + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Identify the free BARs to be used for each of BAR_CONFIG, BAR_PEER_SPAD, + * BAR_DB_MW1, BAR_MW2, BAR_MW3 and BAR_MW4. + */ +static int epf_ntb_init_epc_bar_interface(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + const struct pci_epc_features *epc_features; + struct epf_ntb_epc *ntb_epc; + enum pci_barno barno; + enum epf_ntb_bar bar; + struct device *dev; + u32 num_mws; + int i; + + barno = BAR_0; + ntb_epc = ntb->epc[type]; + num_mws = ntb->num_mws; + dev = &ntb->epf->dev; + epc_features = ntb_epc->epc_features; + + /* These are required BARs which are mandatory for NTB functionality */ + for (bar = BAR_CONFIG; bar <= BAR_DB_MW1; bar++, barno++) { + barno = pci_epc_get_next_free_bar(epc_features, barno); + if (barno < 0) { + dev_err(dev, "%s intf: Fail to get NTB function BAR\n", + pci_epc_interface_string(type)); + return barno; + } + ntb_epc->epf_ntb_bar[bar] = barno; + } + + /* These are optional BARs which don't impact NTB functionality */ + for (bar = BAR_MW2, i = 1; i < num_mws; bar++, barno++, i++) { + barno = pci_epc_get_next_free_bar(epc_features, barno); + if (barno < 0) { + ntb->num_mws = i; + dev_dbg(dev, "BAR not available for > MW%d\n", i + 1); + } + ntb_epc->epf_ntb_bar[bar] = barno; + } + + return 0; +} + +/** + * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB + * constructs (scratchpad region, doorbell, memorywindow) + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Wrapper to epf_ntb_init_epc_bar_interface() to identify the free BARs + * to be used for each of BAR_CONFIG, BAR_PEER_SPAD, BAR_DB_MW1, BAR_MW2, + * BAR_MW3 and BAR_MW4 for all the interfaces. + */ +static int epf_ntb_init_epc_bar(struct epf_ntb *ntb) +{ + enum pci_epc_interface_type type; + struct device *dev; + int ret; + + dev = &ntb->epf->dev; + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) { + ret = epf_ntb_init_epc_bar_interface(ntb, type); + if (ret) { + dev_err(dev, "Fail to init EPC bar for %s interface\n", + pci_epc_interface_string(type)); + return ret; + } + } + + return 0; +} + +/** + * epf_ntb_epc_init_interface() - Initialize NTB interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Wrapper to initialize a particular EPC interface and start the workqueue + * to check for commands from host. This function will write to the + * EP controller HW for configuring it. + */ +static int epf_ntb_epc_init_interface(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + struct epf_ntb_epc *ntb_epc; + u8 func_no, vfunc_no; + struct pci_epc *epc; + struct pci_epf *epf; + struct device *dev; + int ret; + + ntb_epc = ntb->epc[type]; + epf = ntb->epf; + dev = &epf->dev; + epc = ntb_epc->epc; + func_no = ntb_epc->func_no; + vfunc_no = ntb_epc->vfunc_no; + + ret = epf_ntb_config_sspad_bar_set(ntb->epc[type]); + if (ret) { + dev_err(dev, "%s intf: Config/self SPAD BAR init failed\n", + pci_epc_interface_string(type)); + return ret; + } + + ret = epf_ntb_peer_spad_bar_set(ntb, type); + if (ret) { + dev_err(dev, "%s intf: Peer SPAD BAR init failed\n", + pci_epc_interface_string(type)); + goto err_peer_spad_bar_init; + } + + ret = epf_ntb_configure_interrupt(ntb, type); + if (ret) { + dev_err(dev, "%s intf: Interrupt configuration failed\n", + pci_epc_interface_string(type)); + goto err_peer_spad_bar_init; + } + + ret = epf_ntb_db_mw_bar_init(ntb, type); + if (ret) { + dev_err(dev, "%s intf: DB/MW BAR init failed\n", + pci_epc_interface_string(type)); + goto err_db_mw_bar_init; + } + + ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header); + if (ret) { + dev_err(dev, "%s intf: Configuration header write failed\n", + pci_epc_interface_string(type)); + goto err_write_header; + } + + INIT_DELAYED_WORK(&ntb->epc[type]->cmd_handler, epf_ntb_cmd_handler); + queue_work(kpcintb_workqueue, &ntb->epc[type]->cmd_handler.work); + + return 0; + +err_write_header: + epf_ntb_db_mw_bar_cleanup(ntb, type); + +err_db_mw_bar_init: + epf_ntb_peer_spad_bar_clear(ntb->epc[type]); + +err_peer_spad_bar_init: + epf_ntb_config_sspad_bar_clear(ntb->epc[type]); + + return ret; +} + +/** + * epf_ntb_epc_cleanup_interface() - Cleanup NTB interface + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface + * + * Wrapper to cleanup a particular NTB interface. + */ +static void epf_ntb_epc_cleanup_interface(struct epf_ntb *ntb, + enum pci_epc_interface_type type) +{ + struct epf_ntb_epc *ntb_epc; + + if (type < 0) + return; + + ntb_epc = ntb->epc[type]; + cancel_delayed_work(&ntb_epc->cmd_handler); + epf_ntb_db_mw_bar_cleanup(ntb, type); + epf_ntb_peer_spad_bar_clear(ntb_epc); + epf_ntb_config_sspad_bar_clear(ntb_epc); +} + +/** + * epf_ntb_epc_cleanup() - Cleanup all NTB interfaces + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Wrapper to cleanup all NTB interfaces. + */ +static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) +{ + enum pci_epc_interface_type type; + + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) + epf_ntb_epc_cleanup_interface(ntb, type); +} + +/** + * epf_ntb_epc_init() - Initialize all NTB interfaces + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Wrapper to initialize all NTB interface and start the workqueue + * to check for commands from host. + */ +static int epf_ntb_epc_init(struct epf_ntb *ntb) +{ + enum pci_epc_interface_type type; + struct device *dev; + int ret; + + dev = &ntb->epf->dev; + + for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) { + ret = epf_ntb_epc_init_interface(ntb, type); + if (ret) { + dev_err(dev, "%s intf: Failed to initialize\n", + pci_epc_interface_string(type)); + goto err_init_type; + } + } + + return 0; + +err_init_type: + epf_ntb_epc_cleanup_interface(ntb, type - 1); + + return ret; +} + +/** + * epf_ntb_bind() - Initialize endpoint controller to provide NTB functionality + * @epf: NTB endpoint function device + * + * Initialize both the endpoint controllers associated with NTB function device. + * Invoked when a primary interface or secondary interface is bound to EPC + * device. This function will succeed only when EPC is bound to both the + * interfaces. + */ +static int epf_ntb_bind(struct pci_epf *epf) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + struct device *dev = &epf->dev; + int ret; + + if (!epf->epc) { + dev_dbg(dev, "PRIMARY EPC interface not yet bound\n"); + return 0; + } + + if (!epf->sec_epc) { + dev_dbg(dev, "SECONDARY EPC interface not yet bound\n"); + return 0; + } + + ret = epf_ntb_epc_create(ntb); + if (ret) { + dev_err(dev, "Failed to create NTB EPC\n"); + return ret; + } + + ret = epf_ntb_init_epc_bar(ntb); + if (ret) { + dev_err(dev, "Failed to create NTB EPC\n"); + goto err_bar_init; + } + + ret = epf_ntb_config_spad_bar_alloc_interface(ntb); + if (ret) { + dev_err(dev, "Failed to allocate BAR memory\n"); + goto err_bar_alloc; + } + + ret = epf_ntb_epc_init(ntb); + if (ret) { + dev_err(dev, "Failed to initialize EPC\n"); + goto err_bar_alloc; + } + + epf_set_drvdata(epf, ntb); + + return 0; + +err_bar_alloc: + epf_ntb_config_spad_bar_free(ntb); + +err_bar_init: + epf_ntb_epc_destroy(ntb); + + return ret; +} + +/** + * epf_ntb_unbind() - Cleanup the initialization from epf_ntb_bind() + * @epf: NTB endpoint function device + * + * Cleanup the initialization from epf_ntb_bind() + */ +static void epf_ntb_unbind(struct pci_epf *epf) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + + epf_ntb_epc_cleanup(ntb); + epf_ntb_config_spad_bar_free(ntb); + epf_ntb_epc_destroy(ntb); +} + +#define EPF_NTB_R(_name) \ +static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + \ + return sprintf(page, "%d\n", ntb->_name); \ +} + +#define EPF_NTB_W(_name) \ +static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + u32 val; \ + int ret; \ + \ + ret = kstrtou32(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + ntb->_name = val; \ + \ + return len; \ +} + +#define EPF_NTB_MW_R(_name) \ +static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + int win_no; \ + \ + sscanf(#_name, "mw%d", &win_no); \ + \ + return sprintf(page, "%lld\n", ntb->mws_size[win_no - 1]); \ +} + +#define EPF_NTB_MW_W(_name) \ +static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + struct device *dev = &ntb->epf->dev; \ + int win_no; \ + u64 val; \ + int ret; \ + \ + ret = kstrtou64(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + if (sscanf(#_name, "mw%d", &win_no) != 1) \ + return -EINVAL; \ + \ + if (ntb->num_mws < win_no) { \ + dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + return -EINVAL; \ + } \ + \ + ntb->mws_size[win_no - 1] = val; \ + \ + return len; \ +} + +static ssize_t epf_ntb_num_mws_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item); + struct epf_ntb *ntb = to_epf_ntb(group); + u32 val; + int ret; + + ret = kstrtou32(page, 0, &val); + if (ret) + return ret; + + if (val > MAX_MW) + return -EINVAL; + + ntb->num_mws = val; + + return len; +} + +EPF_NTB_R(spad_count) +EPF_NTB_W(spad_count) +EPF_NTB_R(db_count) +EPF_NTB_W(db_count) +EPF_NTB_R(num_mws) +EPF_NTB_MW_R(mw1) +EPF_NTB_MW_W(mw1) +EPF_NTB_MW_R(mw2) +EPF_NTB_MW_W(mw2) +EPF_NTB_MW_R(mw3) +EPF_NTB_MW_W(mw3) +EPF_NTB_MW_R(mw4) +EPF_NTB_MW_W(mw4) + +CONFIGFS_ATTR(epf_ntb_, spad_count); +CONFIGFS_ATTR(epf_ntb_, db_count); +CONFIGFS_ATTR(epf_ntb_, num_mws); +CONFIGFS_ATTR(epf_ntb_, mw1); +CONFIGFS_ATTR(epf_ntb_, mw2); +CONFIGFS_ATTR(epf_ntb_, mw3); +CONFIGFS_ATTR(epf_ntb_, mw4); + +static struct configfs_attribute *epf_ntb_attrs[] = { + &epf_ntb_attr_spad_count, + &epf_ntb_attr_db_count, + &epf_ntb_attr_num_mws, + &epf_ntb_attr_mw1, + &epf_ntb_attr_mw2, + &epf_ntb_attr_mw3, + &epf_ntb_attr_mw4, + NULL, +}; + +static const struct config_item_type ntb_group_type = { + .ct_attrs = epf_ntb_attrs, + .ct_owner = THIS_MODULE, +}; + +/** + * epf_ntb_add_cfs() - Add configfs directory specific to NTB + * @epf: NTB endpoint function device + * + * Add configfs directory specific to NTB. This directory will hold + * NTB specific properties like db_count, spad_count, num_mws etc., + */ +static struct config_group *epf_ntb_add_cfs(struct pci_epf *epf, + struct config_group *group) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + struct config_group *ntb_group = &ntb->group; + struct device *dev = &epf->dev; + + config_group_init_type_name(ntb_group, dev_name(dev), &ntb_group_type); + + return ntb_group; +} + +/** + * epf_ntb_probe() - Probe NTB function driver + * @epf: NTB endpoint function device + * + * Probe NTB function driver when endpoint function bus detects a NTB + * endpoint function. + */ +static int epf_ntb_probe(struct pci_epf *epf) +{ + struct epf_ntb *ntb; + struct device *dev; + + dev = &epf->dev; + + ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + + epf->header = &epf_ntb_header; + ntb->epf = epf; + epf_set_drvdata(epf, ntb); + + return 0; +} + +static struct pci_epf_ops epf_ntb_ops = { + .bind = epf_ntb_bind, + .unbind = epf_ntb_unbind, + .add_cfs = epf_ntb_add_cfs, +}; + +static const struct pci_epf_device_id epf_ntb_ids[] = { + { + .name = "pci_epf_ntb", + }, + {}, +}; + +static struct pci_epf_driver epf_ntb_driver = { + .driver.name = "pci_epf_ntb", + .probe = epf_ntb_probe, + .id_table = epf_ntb_ids, + .ops = &epf_ntb_ops, + .owner = THIS_MODULE, +}; + +static int __init epf_ntb_init(void) +{ + int ret; + + kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM | + WQ_HIGHPRI, 0); + ret = pci_epf_register_driver(&epf_ntb_driver); + if (ret) { + destroy_workqueue(kpcintb_workqueue); + pr_err("Failed to register pci epf ntb driver --> %d\n", ret); + return ret; + } + + return 0; +} +module_init(epf_ntb_init); + +static void __exit epf_ntb_exit(void) +{ + pci_epf_unregister_driver(&epf_ntb_driver); + destroy_workqueue(kpcintb_workqueue); +} +module_exit(epf_ntb_exit); + +MODULE_DESCRIPTION("PCI EPF NTB DRIVER"); +MODULE_AUTHOR("Kishon Vijay Abraham I "); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c --- a/drivers/pci/endpoint/functions/pci-epf-test.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/endpoint/functions/pci-epf-test.c 2022-01-06 12:45:53.826318157 -0500 @@ -247,8 +247,8 @@ goto err; } - ret = pci_epc_map_addr(epc, epf->func_no, src_phys_addr, reg->src_addr, - reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr, + reg->src_addr, reg->size); if (ret) { dev_err(dev, "Failed to map source address\n"); reg->status = STATUS_SRC_ADDR_INVALID; @@ -263,8 +263,8 @@ goto err_src_map_addr; } - ret = pci_epc_map_addr(epc, epf->func_no, dst_phys_addr, reg->dst_addr, - reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr, + reg->dst_addr, reg->size); if (ret) { dev_err(dev, "Failed to map destination address\n"); reg->status = STATUS_DST_ADDR_INVALID; @@ -291,13 +291,13 @@ pci_epf_test_print_rate("COPY", reg->size, &start, &end, use_dma); err_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, dst_phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr); err_dst_addr: pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size); err_src_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, src_phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr); err_src_addr: pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size); @@ -314,12 +314,12 @@ u32 crc32; bool use_dma; phys_addr_t phys_addr; + struct device *dma_dev; phys_addr_t dst_phys_addr; struct timespec64 start, end; struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; - struct device *dma_dev = epf->epc->dev.parent; enum pci_barno test_reg_bar = epf_test->test_reg_bar; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; @@ -331,8 +331,8 @@ goto err; } - ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->src_addr, - reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr, + reg->src_addr, reg->size); if (ret) { dev_err(dev, "Failed to map address\n"); reg->status = STATUS_SRC_ADDR_INVALID; @@ -353,6 +353,7 @@ goto err_dma_map; } + dma_dev = dmaengine_get_dma_device(epf_test->dma_chan); dst_phys_addr = dma_map_single(dma_dev, buf, reg->size, DMA_FROM_DEVICE); if (dma_mapping_error(dma_dev, dst_phys_addr)) { @@ -386,7 +387,7 @@ kfree(buf); err_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr); err_addr: pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size); @@ -402,12 +403,12 @@ void *buf; bool use_dma; phys_addr_t phys_addr; + struct device *dma_dev; phys_addr_t src_phys_addr; struct timespec64 start, end; struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; - struct device *dma_dev = epf->epc->dev.parent; enum pci_barno test_reg_bar = epf_test->test_reg_bar; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; @@ -419,8 +420,8 @@ goto err; } - ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->dst_addr, - reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr, + reg->dst_addr, reg->size); if (ret) { dev_err(dev, "Failed to map address\n"); reg->status = STATUS_DST_ADDR_INVALID; @@ -444,6 +445,7 @@ goto err_map_addr; } + dma_dev = dmaengine_get_dma_device(epf_test->dma_chan); src_phys_addr = dma_map_single(dma_dev, buf, reg->size, DMA_TO_DEVICE); if (dma_mapping_error(dma_dev, src_phys_addr)) { @@ -479,7 +481,7 @@ kfree(buf); err_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr); err_addr: pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size); @@ -501,13 +503,16 @@ switch (irq_type) { case IRQ_TYPE_LEGACY: - pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0); + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, + PCI_EPC_IRQ_LEGACY, 0); break; case IRQ_TYPE_MSI: - pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, + PCI_EPC_IRQ_MSI, irq); break; case IRQ_TYPE_MSIX: - pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, irq); + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, + PCI_EPC_IRQ_MSIX, irq); break; default: dev_err(dev, "Failed to raise IRQ, unknown type\n"); @@ -542,7 +547,8 @@ if (command & COMMAND_RAISE_LEGACY_IRQ) { reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0); + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, + PCI_EPC_IRQ_LEGACY, 0); goto reset_handler; } @@ -580,22 +586,22 @@ } if (command & COMMAND_RAISE_MSI_IRQ) { - count = pci_epc_get_msi(epc, epf->func_no); + count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no); if (reg->irq_number > count || count <= 0) goto reset_handler; reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, - reg->irq_number); + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, + PCI_EPC_IRQ_MSI, reg->irq_number); goto reset_handler; } if (command & COMMAND_RAISE_MSIX_IRQ) { - count = pci_epc_get_msix(epc, epf->func_no); + count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no); if (reg->irq_number > count || count <= 0) goto reset_handler; reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, - reg->irq_number); + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, + PCI_EPC_IRQ_MSIX, reg->irq_number); goto reset_handler; } @@ -618,8 +624,10 @@ epf_bar = &epf->bar[bar]; if (epf_test->reg[bar]) { - pci_epc_clear_bar(epc, epf->func_no, epf_bar); - pci_epf_free_space(epf, epf_test->reg[bar], bar); + pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, + epf_bar); + pci_epf_free_space(epf, epf_test->reg[bar], bar, + PRIMARY_INTERFACE); } } } @@ -649,9 +657,11 @@ if (!!(epc_features->reserved_bar & (1 << bar))) continue; - ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, + epf_bar); if (ret) { - pci_epf_free_space(epf, epf_test->reg[bar], bar); + pci_epf_free_space(epf, epf_test->reg[bar], bar, + PRIMARY_INTERFACE); dev_err(dev, "Failed to set BAR%d\n", bar); if (bar == test_reg_bar) return ret; @@ -672,13 +682,13 @@ bool msi_capable = true; int ret; - epc_features = pci_epc_get_features(epc, epf->func_no); + epc_features = pci_epc_get_features(epc, epf->func_no, epf->vfunc_no); if (epc_features) { msix_capable = epc_features->msix_capable; msi_capable = epc_features->msi_capable; } - ret = pci_epc_write_header(epc, epf->func_no, header); + ret = pci_epc_write_header(epc, epf->func_no, epf->vfunc_no, header); if (ret) { dev_err(dev, "Configuration header write failed\n"); return ret; @@ -689,7 +699,8 @@ return ret; if (msi_capable) { - ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); + ret = pci_epc_set_msi(epc, epf->func_no, epf->vfunc_no, + epf->msi_interrupts); if (ret) { dev_err(dev, "MSI configuration failed\n"); return ret; @@ -697,7 +708,8 @@ } if (msix_capable) { - ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts, + ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no, + epf->msix_interrupts, epf_test->test_reg_bar, epf_test->msix_table_offset); if (ret) { @@ -771,7 +783,7 @@ } base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar, - epc_features->align); + epc_features->align, PRIMARY_INTERFACE); if (!base) { dev_err(dev, "Failed to allocated register space\n"); return -ENOMEM; @@ -789,7 +801,8 @@ continue; base = pci_epf_alloc_space(epf, bar_size[bar], bar, - epc_features->align); + epc_features->align, + PRIMARY_INTERFACE); if (!base) dev_err(dev, "Failed to allocate space for BAR%d\n", bar); @@ -829,7 +842,7 @@ if (WARN_ON_ONCE(!epc)) return -EINVAL; - epc_features = pci_epc_get_features(epc, epf->func_no); + epc_features = pci_epc_get_features(epc, epf->func_no, epf->vfunc_no); if (!epc_features) { dev_err(&epf->dev, "epc_features not implemented\n"); return -EOPNOTSUPP; diff -Naur --no-dereference a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c --- a/drivers/pci/endpoint/pci-epc-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/endpoint/pci-epc-core.c 2022-01-06 12:45:53.826318157 -0500 @@ -137,24 +137,29 @@ * @epc: the features supported by *this* EPC device will be returned * @func_no: the features supported by the EPC device specific to the * endpoint function with func_no will be returned + * @vfunc_no: the features supported by the EPC device specific to the + * virtual endpoint function with vfunc_no will be returned * * Invoke to get the features provided by the EPC which may be * specific to an endpoint function. Returns pci_epc_features on success * and NULL for any failures. */ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, - u8 func_no) + u8 func_no, u8 vfunc_no) { const struct pci_epc_features *epc_features; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return NULL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return NULL; + if (!epc->ops->get_features) return NULL; mutex_lock(&epc->lock); - epc_features = epc->ops->get_features(epc, func_no); + epc_features = epc->ops->get_features(epc, func_no, vfunc_no); mutex_unlock(&epc->lock); return epc_features; @@ -205,13 +210,14 @@ /** * pci_epc_raise_irq() - interrupt the host system * @epc: the EPC device which has to interrupt the host - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @type: specify the type of interrupt; legacy, MSI or MSI-X * @interrupt_num: the MSI or MSI-X interrupt number * * Invoke to raise an legacy, MSI or MSI-X interrupt */ -int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, +int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, enum pci_epc_irq_type type, u16 interrupt_num) { int ret; @@ -219,11 +225,14 @@ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + if (!epc->ops->raise_irq) return 0; mutex_lock(&epc->lock); - ret = epc->ops->raise_irq(epc, func_no, type, interrupt_num); + ret = epc->ops->raise_irq(epc, func_no, vfunc_no, type, interrupt_num); mutex_unlock(&epc->lock); return ret; @@ -231,24 +240,74 @@ EXPORT_SYMBOL_GPL(pci_epc_raise_irq); /** + * pci_epc_map_msi_irq() - Map physical address to MSI address and return + * MSI data + * @epc: the EPC device which has the MSI capability + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function + * @phys_addr: the physical address of the outbound region + * @interrupt_num: the MSI interrupt number + * @entry_size: Size of Outbound address region for each interrupt + * @msi_data: the data that should be written in order to raise MSI interrupt + * with interrupt number as 'interrupt num' + * @msi_addr_offset: Offset of MSI address from the aligned outbound address + * to which the MSI address is mapped + * + * Invoke to map physical address to MSI address and return MSI data. The + * physical address should be an address in the outbound region. This is + * required to implement doorbell functionality of NTB wherein EPC on either + * side of the interface (primary and secondary) can directly write to the + * physical address (in outbound region) of the other interface to ring + * doorbell. + */ +int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + phys_addr_t phys_addr, u8 interrupt_num, u32 entry_size, + u32 *msi_data, u32 *msi_addr_offset) +{ + int ret; + + if (IS_ERR_OR_NULL(epc)) + return -EINVAL; + + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + + if (!epc->ops->map_msi_irq) + return -EINVAL; + + mutex_lock(&epc->lock); + ret = epc->ops->map_msi_irq(epc, func_no, vfunc_no, phys_addr, + interrupt_num, entry_size, msi_data, + msi_addr_offset); + mutex_unlock(&epc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_map_msi_irq); + +/** * pci_epc_get_msi() - get the number of MSI interrupt numbers allocated * @epc: the EPC device to which MSI interrupts was requested - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * * Invoke to get the number of MSI interrupts allocated by the RC */ -int pci_epc_get_msi(struct pci_epc *epc, u8 func_no) +int pci_epc_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { int interrupt; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return 0; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return 0; + if (!epc->ops->get_msi) return 0; mutex_lock(&epc->lock); - interrupt = epc->ops->get_msi(epc, func_no); + interrupt = epc->ops->get_msi(epc, func_no, vfunc_no); mutex_unlock(&epc->lock); if (interrupt < 0) @@ -263,12 +322,13 @@ /** * pci_epc_set_msi() - set the number of MSI interrupt numbers required * @epc: the EPC device on which MSI has to be configured - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @interrupts: number of MSI interrupts required by the EPF * * Invoke to set the required number of MSI interrupts. */ -int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) +int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u8 interrupts) { int ret; u8 encode_int; @@ -277,13 +337,16 @@ interrupts > 32) return -EINVAL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + if (!epc->ops->set_msi) return 0; encode_int = order_base_2(interrupts); mutex_lock(&epc->lock); - ret = epc->ops->set_msi(epc, func_no, encode_int); + ret = epc->ops->set_msi(epc, func_no, vfunc_no, encode_int); mutex_unlock(&epc->lock); return ret; @@ -293,22 +356,26 @@ /** * pci_epc_get_msix() - get the number of MSI-X interrupt numbers allocated * @epc: the EPC device to which MSI-X interrupts was requested - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * * Invoke to get the number of MSI-X interrupts allocated by the RC */ -int pci_epc_get_msix(struct pci_epc *epc, u8 func_no) +int pci_epc_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { int interrupt; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return 0; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return 0; + if (!epc->ops->get_msix) return 0; mutex_lock(&epc->lock); - interrupt = epc->ops->get_msix(epc, func_no); + interrupt = epc->ops->get_msix(epc, func_no, vfunc_no); mutex_unlock(&epc->lock); if (interrupt < 0) @@ -321,15 +388,16 @@ /** * pci_epc_set_msix() - set the number of MSI-X interrupt numbers required * @epc: the EPC device on which MSI-X has to be configured - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @interrupts: number of MSI-X interrupts required by the EPF * @bir: BAR where the MSI-X table resides * @offset: Offset pointing to the start of MSI-X table * * Invoke to set the required number of MSI-X interrupts. */ -int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts, - enum pci_barno bir, u32 offset) +int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u16 interrupts, enum pci_barno bir, u32 offset) { int ret; @@ -337,11 +405,15 @@ interrupts < 1 || interrupts > 2048) return -EINVAL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + if (!epc->ops->set_msix) return 0; mutex_lock(&epc->lock); - ret = epc->ops->set_msix(epc, func_no, interrupts - 1, bir, offset); + ret = epc->ops->set_msix(epc, func_no, vfunc_no, interrupts - 1, bir, + offset); mutex_unlock(&epc->lock); return ret; @@ -351,22 +423,26 @@ /** * pci_epc_unmap_addr() - unmap CPU address from PCI address * @epc: the EPC device on which address is allocated - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @phys_addr: physical address of the local system * * Invoke to unmap the CPU address from PCI address. */ -void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, +void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr) { if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return; + if (!epc->ops->unmap_addr) return; mutex_lock(&epc->lock); - epc->ops->unmap_addr(epc, func_no, phys_addr); + epc->ops->unmap_addr(epc, func_no, vfunc_no, phys_addr); mutex_unlock(&epc->lock); } EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); @@ -374,14 +450,15 @@ /** * pci_epc_map_addr() - map CPU address to PCI address * @epc: the EPC device on which address is allocated - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @phys_addr: physical address of the local system * @pci_addr: PCI address to which the physical address should be mapped * @size: the size of the allocation * * Invoke to map CPU address with PCI address. */ -int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, +int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr, u64 pci_addr, size_t size) { int ret; @@ -389,11 +466,15 @@ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + if (!epc->ops->map_addr) return 0; mutex_lock(&epc->lock); - ret = epc->ops->map_addr(epc, func_no, phys_addr, pci_addr, size); + ret = epc->ops->map_addr(epc, func_no, vfunc_no, phys_addr, pci_addr, + size); mutex_unlock(&epc->lock); return ret; @@ -403,12 +484,13 @@ /** * pci_epc_clear_bar() - reset the BAR * @epc: the EPC device for which the BAR has to be cleared - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to reset the BAR of the endpoint device. */ -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || @@ -416,11 +498,14 @@ epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) return; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return; + if (!epc->ops->clear_bar) return; mutex_lock(&epc->lock); - epc->ops->clear_bar(epc, func_no, epf_bar); + epc->ops->clear_bar(epc, func_no, vfunc_no, epf_bar); mutex_unlock(&epc->lock); } EXPORT_SYMBOL_GPL(pci_epc_clear_bar); @@ -428,12 +513,13 @@ /** * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space * @epc: the EPC device on which BAR has to be configured - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to configure the BAR of the endpoint device. */ -int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { int ret; @@ -448,11 +534,14 @@ !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))) return -EINVAL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + if (!epc->ops->set_bar) return 0; mutex_lock(&epc->lock); - ret = epc->ops->set_bar(epc, func_no, epf_bar); + ret = epc->ops->set_bar(epc, func_no, vfunc_no, epf_bar); mutex_unlock(&epc->lock); return ret; @@ -462,7 +551,8 @@ /** * pci_epc_write_header() - write standard configuration header * @epc: the EPC device to which the configuration header should be written - * @func_no: the endpoint function number in the EPC device + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function * @header: standard configuration header fields * * Invoke to write the configuration header to the endpoint controller. Every @@ -470,7 +560,7 @@ * configuration header would be written. The callback function should write * the header fields to this dedicated location. */ -int pci_epc_write_header(struct pci_epc *epc, u8 func_no, +int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *header) { int ret; @@ -478,11 +568,14 @@ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return -EINVAL; + if (!epc->ops->write_header) return 0; mutex_lock(&epc->lock); - ret = epc->ops->write_header(epc, func_no, header); + ret = epc->ops->write_header(epc, func_no, vfunc_no, header); mutex_unlock(&epc->lock); return ret; @@ -493,21 +586,28 @@ * pci_epc_add_epf() - bind PCI endpoint function to an endpoint controller * @epc: the EPC device to which the endpoint function should be added * @epf: the endpoint function to be added + * @type: Identifies if the EPC is connected to the primary or secondary + * interface of EPF * * A PCI endpoint device can have one or more functions. In the case of PCIe, * the specification allows up to 8 PCIe endpoint functions. Invoke * pci_epc_add_epf() to add a PCI endpoint function to an endpoint controller. */ -int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf) +int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, + enum pci_epc_interface_type type) { + struct list_head *list; u32 func_no; int ret = 0; - if (epf->epc) + if (IS_ERR_OR_NULL(epc) || epf->is_vf) + return -EINVAL; + + if (type == PRIMARY_INTERFACE && epf->epc) return -EBUSY; - if (IS_ERR(epc)) - return -EINVAL; + if (type == SECONDARY_INTERFACE && epf->sec_epc) + return -EBUSY; mutex_lock(&epc->lock); func_no = find_first_zero_bit(&epc->function_num_map, @@ -524,11 +624,17 @@ } set_bit(func_no, &epc->function_num_map); - epf->func_no = func_no; - epf->epc = epc; - - list_add_tail(&epf->list, &epc->pci_epf); + if (type == PRIMARY_INTERFACE) { + epf->func_no = func_no; + epf->epc = epc; + list = &epf->list; + } else { + epf->sec_epc_func_no = func_no; + epf->sec_epc = epc; + list = &epf->sec_epc_list; + } + list_add_tail(list, &epc->pci_epf); ret: mutex_unlock(&epc->lock); @@ -543,14 +649,26 @@ * * Invoke to remove PCI endpoint function from the endpoint controller. */ -void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf) +void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, + enum pci_epc_interface_type type) { + struct list_head *list; + u32 func_no = 0; + if (!epc || IS_ERR(epc) || !epf) return; + if (type == PRIMARY_INTERFACE) { + func_no = epf->func_no; + list = &epf->list; + } else { + func_no = epf->sec_epc_func_no; + list = &epf->sec_epc_list; + } + mutex_lock(&epc->lock); - clear_bit(epf->func_no, &epc->function_num_map); - list_del(&epf->list); + clear_bit(func_no, &epc->function_num_map); + list_del(list); epf->epc = NULL; mutex_unlock(&epc->lock); } diff -Naur --no-dereference a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c --- a/drivers/pci/endpoint/pci-ep-cfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/endpoint/pci-ep-cfs.c 2022-01-06 12:45:53.826318157 -0500 @@ -21,6 +21,9 @@ struct pci_epf_group { struct config_group group; + struct config_group primary_epc_group; + struct config_group secondary_epc_group; + struct delayed_work cfs_work; struct pci_epf *epf; int index; }; @@ -41,6 +44,127 @@ return container_of(to_config_group(item), struct pci_epc_group, group); } +static int pci_secondary_epc_epf_link(struct config_item *epf_item, + struct config_item *epc_item) +{ + int ret; + struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent); + struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); + struct pci_epc *epc = epc_group->epc; + struct pci_epf *epf = epf_group->epf; + + ret = pci_epc_add_epf(epc, epf, SECONDARY_INTERFACE); + if (ret) + return ret; + + ret = pci_epf_bind(epf); + if (ret) { + pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE); + return ret; + } + + return 0; +} + +static void pci_secondary_epc_epf_unlink(struct config_item *epc_item, + struct config_item *epf_item) +{ + struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent); + struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); + struct pci_epc *epc; + struct pci_epf *epf; + + WARN_ON_ONCE(epc_group->start); + + epc = epc_group->epc; + epf = epf_group->epf; + pci_epf_unbind(epf); + pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE); +} + +static struct configfs_item_operations pci_secondary_epc_item_ops = { + .allow_link = pci_secondary_epc_epf_link, + .drop_link = pci_secondary_epc_epf_unlink, +}; + +static const struct config_item_type pci_secondary_epc_type = { + .ct_item_ops = &pci_secondary_epc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group +*pci_ep_cfs_add_secondary_group(struct pci_epf_group *epf_group) +{ + struct config_group *secondary_epc_group; + + secondary_epc_group = &epf_group->secondary_epc_group; + config_group_init_type_name(secondary_epc_group, "secondary", + &pci_secondary_epc_type); + configfs_register_group(&epf_group->group, secondary_epc_group); + + return secondary_epc_group; +} + +static int pci_primary_epc_epf_link(struct config_item *epf_item, + struct config_item *epc_item) +{ + int ret; + struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent); + struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); + struct pci_epc *epc = epc_group->epc; + struct pci_epf *epf = epf_group->epf; + + ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE); + if (ret) + return ret; + + ret = pci_epf_bind(epf); + if (ret) { + pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE); + return ret; + } + + return 0; +} + +static void pci_primary_epc_epf_unlink(struct config_item *epc_item, + struct config_item *epf_item) +{ + struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent); + struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); + struct pci_epc *epc; + struct pci_epf *epf; + + WARN_ON_ONCE(epc_group->start); + + epc = epc_group->epc; + epf = epf_group->epf; + pci_epf_unbind(epf); + pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE); +} + +static struct configfs_item_operations pci_primary_epc_item_ops = { + .allow_link = pci_primary_epc_epf_link, + .drop_link = pci_primary_epc_epf_unlink, +}; + +static const struct config_item_type pci_primary_epc_type = { + .ct_item_ops = &pci_primary_epc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group +*pci_ep_cfs_add_primary_group(struct pci_epf_group *epf_group) +{ + struct config_group *primary_epc_group = &epf_group->primary_epc_group; + + config_group_init_type_name(primary_epc_group, "primary", + &pci_primary_epc_type); + configfs_register_group(&epf_group->group, primary_epc_group); + + return primary_epc_group; +} + static ssize_t pci_epc_start_store(struct config_item *item, const char *page, size_t len) { @@ -94,13 +218,13 @@ struct pci_epc *epc = epc_group->epc; struct pci_epf *epf = epf_group->epf; - ret = pci_epc_add_epf(epc, epf); + ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE); if (ret) return ret; ret = pci_epf_bind(epf); if (ret) { - pci_epc_remove_epf(epc, epf); + pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE); return ret; } @@ -120,7 +244,7 @@ epc = epc_group->epc; epf = epf_group->epf; pci_epf_unbind(epf); - pci_epc_remove_epf(epc, epf); + pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE); } static struct configfs_item_operations pci_epc_item_ops = { @@ -351,6 +475,28 @@ NULL, }; +static int pci_epf_vepf_link(struct config_item *epf_pf_item, + struct config_item *epf_vf_item) +{ + struct pci_epf_group *epf_vf_group = to_pci_epf_group(epf_vf_item); + struct pci_epf_group *epf_pf_group = to_pci_epf_group(epf_pf_item); + struct pci_epf *epf_pf = epf_pf_group->epf; + struct pci_epf *epf_vf = epf_vf_group->epf; + + return pci_epf_add_vepf(epf_pf, epf_vf); +} + +static void pci_epf_vepf_unlink(struct config_item *epf_pf_item, + struct config_item *epf_vf_item) +{ + struct pci_epf_group *epf_vf_group = to_pci_epf_group(epf_vf_item); + struct pci_epf_group *epf_pf_group = to_pci_epf_group(epf_pf_item); + struct pci_epf *epf_pf = epf_pf_group->epf; + struct pci_epf *epf_vf = epf_vf_group->epf; + + pci_epf_remove_vepf(epf_pf, epf_vf); +} + static void pci_epf_release(struct config_item *item) { struct pci_epf_group *epf_group = to_pci_epf_group(item); @@ -363,15 +509,58 @@ } static struct configfs_item_operations pci_epf_ops = { + .allow_link = pci_epf_vepf_link, + .drop_link = pci_epf_vepf_unlink, .release = pci_epf_release, }; +static struct config_group *pci_epf_type_make(struct config_group *group, + const char *name) +{ + struct pci_epf_group *epf_group = to_pci_epf_group(&group->cg_item); + struct config_group *epf_type_group; + + epf_type_group = pci_epf_type_add_cfs(epf_group->epf, group); + return epf_type_group; +} + +static void pci_epf_type_drop(struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations pci_epf_type_group_ops = { + .make_group = &pci_epf_type_make, + .drop_item = &pci_epf_type_drop, +}; + static const struct config_item_type pci_epf_type = { + .ct_group_ops = &pci_epf_type_group_ops, .ct_item_ops = &pci_epf_ops, .ct_attrs = pci_epf_attrs, .ct_owner = THIS_MODULE, }; +static void pci_epf_cfs_work(struct work_struct *work) +{ + struct pci_epf_group *epf_group; + struct config_group *group; + + epf_group = container_of(work, struct pci_epf_group, cfs_work.work); + group = pci_ep_cfs_add_primary_group(epf_group); + if (IS_ERR(group)) { + pr_err("failed to create 'primary' EPC interface\n"); + return; + } + + group = pci_ep_cfs_add_secondary_group(epf_group); + if (IS_ERR(group)) { + pr_err("failed to create 'secondary' EPC interface\n"); + return; + } +} + static struct config_group *pci_epf_make(struct config_group *group, const char *name) { @@ -410,10 +599,15 @@ goto free_name; } + epf->group = &epf_group->group; epf_group->epf = epf; kfree(epf_name); + INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work); + queue_delayed_work(system_wq, &epf_group->cfs_work, + msecs_to_jiffies(1)); + return &epf_group->group; free_name: diff -Naur --no-dereference a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c --- a/drivers/pci/endpoint/pci-epf-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pci/endpoint/pci-epf-core.c 2022-01-06 12:45:53.826318157 -0500 @@ -21,6 +21,38 @@ static const struct device_type pci_epf_type; /** + * pci_epf_type_add_cfs() - Help function drivers to expose function specific + * attributes in configfs + * @epf: the EPF device that has to be configured using configfs + * @group: the parent configfs group (corresponding to entries in + * pci_epf_device_id) + * + * Invoke to expose function specific attributes in configfs. If the function + * driver does not have anything to expose (attributes configured by user), + * return NULL. + */ +struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, + struct config_group *group) +{ + struct config_group *epf_type_group; + + if (!epf->driver) { + dev_err(&epf->dev, "epf device not bound to driver\n"); + return NULL; + } + + if (!epf->driver->ops->add_cfs) + return NULL; + + mutex_lock(&epf->lock); + epf_type_group = epf->driver->ops->add_cfs(epf, group); + mutex_unlock(&epf->lock); + + return epf_type_group; +} +EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs); + +/** * pci_epf_unbind() - Notify the function driver that the binding between the * EPF device and EPC device has been lost * @epf: the EPF device which has lost the binding with the EPC device @@ -30,13 +62,20 @@ */ void pci_epf_unbind(struct pci_epf *epf) { + struct pci_epf *epf_vf; + if (!epf->driver) { dev_WARN(&epf->dev, "epf device not bound to driver\n"); return; } mutex_lock(&epf->lock); - epf->driver->ops->unbind(epf); + list_for_each_entry(epf_vf, &epf->pci_vepf, list) { + if (epf_vf->is_bound) + epf_vf->driver->ops->unbind(epf_vf); + } + if (epf->is_bound) + epf->driver->ops->unbind(epf); mutex_unlock(&epf->lock); module_put(epf->driver->owner); } @@ -51,10 +90,14 @@ */ int pci_epf_bind(struct pci_epf *epf) { + struct device *dev = &epf->dev; + struct pci_epf *epf_vf; + u8 func_no, vfunc_no; + struct pci_epc *epc; int ret; if (!epf->driver) { - dev_WARN(&epf->dev, "epf device not bound to driver\n"); + dev_WARN(dev, "epf device not bound to driver\n"); return -EINVAL; } @@ -62,36 +105,174 @@ return -EAGAIN; mutex_lock(&epf->lock); + list_for_each_entry(epf_vf, &epf->pci_vepf, list) { + vfunc_no = epf_vf->vfunc_no; + + if (vfunc_no < 1) { + dev_err(dev, "Invalid virtual function number\n"); + ret = -EINVAL; + goto ret; + } + + epc = epf->epc; + func_no = epf->func_no; + if (!IS_ERR_OR_NULL(epc)) { + if (!epc->max_vfs) { + dev_err(dev, "No support for virt function\n"); + ret = -EINVAL; + goto ret; + } + + if (vfunc_no > epc->max_vfs[func_no]) { + dev_err(dev, "PF%d: Exceeds max vfunc number\n", + func_no); + ret = -EINVAL; + goto ret; + } + } + + epc = epf->sec_epc; + func_no = epf->sec_epc_func_no; + if (!IS_ERR_OR_NULL(epc)) { + if (!epc->max_vfs) { + dev_err(dev, "No support for virt function\n"); + ret = -EINVAL; + goto ret; + } + + if (vfunc_no > epc->max_vfs[func_no]) { + dev_err(dev, "PF%d: Exceeds max vfunc number\n", + func_no); + ret = -EINVAL; + goto ret; + } + } + + epf_vf->func_no = epf->func_no; + epf_vf->sec_epc_func_no = epf->sec_epc_func_no; + epf_vf->epc = epf->epc; + epf_vf->sec_epc = epf->sec_epc; + ret = epf_vf->driver->ops->bind(epf_vf); + if (ret) + goto ret; + epf_vf->is_bound = true; + } + ret = epf->driver->ops->bind(epf); + if (ret) + goto ret; + epf->is_bound = true; + + mutex_unlock(&epf->lock); + return 0; + +ret: mutex_unlock(&epf->lock); + pci_epf_unbind(epf); return ret; } EXPORT_SYMBOL_GPL(pci_epf_bind); /** + * pci_epf_add_vepf() - associate virtual EP function to physical EP function + * @epf_pf: the physical EP function to which the virtual EP function should be + * associated + * @epf_vf: the virtual EP function to be added + * + * A physical endpoint function can be associated with multiple virtual + * endpoint functions. Invoke pci_epf_add_epf() to add a virtual PCI endpoint + * function to a physical PCI endpoint function. + */ +int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf) +{ + u32 vfunc_no; + + if (IS_ERR_OR_NULL(epf_pf) || IS_ERR_OR_NULL(epf_vf)) + return -EINVAL; + + if (epf_pf->epc || epf_vf->epc || epf_vf->epf_pf) + return -EBUSY; + + if (epf_pf->sec_epc || epf_vf->sec_epc) + return -EBUSY; + + mutex_lock(&epf_pf->lock); + vfunc_no = find_first_zero_bit(&epf_pf->vfunction_num_map, + BITS_PER_LONG); + if (vfunc_no >= BITS_PER_LONG) + return -EINVAL; + + set_bit(vfunc_no, &epf_pf->vfunction_num_map); + epf_vf->vfunc_no = vfunc_no; + + epf_vf->epf_pf = epf_pf; + epf_vf->is_vf = true; + + list_add_tail(&epf_vf->list, &epf_pf->pci_vepf); + mutex_unlock(&epf_pf->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_epf_add_vepf); + +/** + * pci_epf_remove_vepf() - remove virtual EP function from physical EP function + * @epf_pf: the physical EP function from which the virtual EP function should + * be removed + * @epf_vf: the virtual EP function to be removed + * + * Invoke to remove a virtual endpoint function from the physcial endpoint + * function. + */ +void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf) +{ + if (IS_ERR_OR_NULL(epf_pf) || IS_ERR_OR_NULL(epf_vf)) + return; + + mutex_lock(&epf_pf->lock); + clear_bit(epf_vf->vfunc_no, &epf_pf->vfunction_num_map); + list_del(&epf_vf->list); + mutex_unlock(&epf_pf->lock); +} +EXPORT_SYMBOL_GPL(pci_epf_remove_vepf); + +/** * pci_epf_free_space() - free the allocated PCI EPF register space * @epf: the EPF device from whom to free the memory * @addr: the virtual address of the PCI EPF register space * @bar: the BAR number corresponding to the register space + * @type: Identifies if the allocated space is for primary EPC or secondary EPC * * Invoke to free the allocated PCI EPF register space. */ -void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar) +void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar, + enum pci_epc_interface_type type) { struct device *dev = epf->epc->dev.parent; + struct pci_epf_bar *epf_bar; + struct pci_epc *epc; if (!addr) return; - dma_free_coherent(dev, epf->bar[bar].size, addr, - epf->bar[bar].phys_addr); + if (type == PRIMARY_INTERFACE) { + epc = epf->epc; + epf_bar = epf->bar; + } else { + epc = epf->sec_epc; + epf_bar = epf->sec_epc_bar; + } - epf->bar[bar].phys_addr = 0; - epf->bar[bar].addr = NULL; - epf->bar[bar].size = 0; - epf->bar[bar].barno = 0; - epf->bar[bar].flags = 0; + dev = epc->dev.parent; + dma_free_coherent(dev, epf_bar[bar].size, addr, + epf_bar[bar].phys_addr); + + epf_bar[bar].phys_addr = 0; + epf_bar[bar].addr = NULL; + epf_bar[bar].size = 0; + epf_bar[bar].barno = 0; + epf_bar[bar].flags = 0; } EXPORT_SYMBOL_GPL(pci_epf_free_space); @@ -101,15 +282,18 @@ * @size: the size of the memory that has to be allocated * @bar: the BAR number corresponding to the allocated register space * @align: alignment size for the allocation region + * @type: Identifies if the allocation is for primary EPC or secondary EPC * * Invoke to allocate memory for the PCI EPF register space. */ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, - size_t align) + size_t align, enum pci_epc_interface_type type) { - void *space; - struct device *dev = epf->epc->dev.parent; + struct pci_epf_bar *epf_bar; dma_addr_t phys_addr; + struct pci_epc *epc; + struct device *dev; + void *space; if (size < 128) size = 128; @@ -119,17 +303,26 @@ else size = roundup_pow_of_two(size); + if (type == PRIMARY_INTERFACE) { + epc = epf->epc; + epf_bar = epf->bar; + } else { + epc = epf->sec_epc; + epf_bar = epf->sec_epc_bar; + } + + dev = epc->dev.parent; space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); if (!space) { dev_err(dev, "failed to allocate mem space\n"); return NULL; } - epf->bar[bar].phys_addr = phys_addr; - epf->bar[bar].addr = space; - epf->bar[bar].size = size; - epf->bar[bar].barno = bar; - epf->bar[bar].flags |= upper_32_bits(size) ? + epf_bar[bar].phys_addr = phys_addr; + epf_bar[bar].addr = space; + epf_bar[bar].size = size; + epf_bar[bar].barno = bar; + epf_bar[bar].flags |= upper_32_bits(size) ? PCI_BASE_ADDRESS_MEM_TYPE_64 : PCI_BASE_ADDRESS_MEM_TYPE_32; @@ -260,6 +453,10 @@ return ERR_PTR(-ENOMEM); } + /* VFs are numbered starting with 1. So set BIT(0) by default */ + epf->vfunction_num_map = 1; + INIT_LIST_HEAD(&epf->pci_vepf); + dev = &epf->dev; device_initialize(dev); dev->bus = &pci_epf_bus_type; @@ -282,22 +479,6 @@ } EXPORT_SYMBOL_GPL(pci_epf_create); -const struct pci_epf_device_id * -pci_epf_match_device(const struct pci_epf_device_id *id, struct pci_epf *epf) -{ - if (!id || !epf) - return NULL; - - while (*id->name) { - if (strcmp(epf->name, id->name) == 0) - return id; - id++; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(pci_epf_match_device); - static void pci_epf_dev_release(struct device *dev) { struct pci_epf *epf = to_pci_epf(dev); diff -Naur --no-dereference a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c --- a/drivers/phy/cadence/cdns-dphy.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/cadence/cdns-dphy.c 2022-01-06 12:45:53.826318157 -0500 @@ -1,14 +1,18 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright: 2017-2018 Cadence Design Systems, Inc. + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include +#include #include #include +#include #include #include #include +#include #include #include @@ -25,10 +29,15 @@ #define DPHY_PMA_RCLK(reg) (0x600 + (reg)) #define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg)) #define DPHY_PCS(reg) (0xb00 + (reg)) +#define DPHY_ISO(reg) (0xc00 + (reg)) +#define DPHY_WRAP(reg) (0x1000 + (reg)) #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) #define DPHY_CMN_SSM_EN BIT(0) +#define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1) #define DPHY_CMN_TX_MODE_EN BIT(9) +#define DPHY_CMN_RX_MODE_EN BIT(10) +#define DPHY_CMN_RX_BANDGAP_TIMER 0x14 #define DPHY_CMN_PWM DPHY_PMA_CMN(0x40) #define DPHY_CMN_PWM_DIV(x) ((x) << 20) @@ -45,10 +54,30 @@ #define DPHY_CMN_OPDIV_FROM_REG BIT(6) #define DPHY_CMN_OPDIV(x) ((x) << 7) +#define DPHY_BAND_CFG DPHY_PCS(0x0) +#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) +#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) + #define DPHY_PSM_CFG DPHY_PCS(0x4) #define DPHY_PSM_CFG_FROM_REG BIT(0) #define DPHY_PSM_CLK_DIV(x) ((x) << 1) +#define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8) +#define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa +#define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc) +#define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa + +#define DPHY_LANE DPHY_WRAP(0x0) +#define DPHY_LANE_RESET_CMN_EN BIT(23) + +#define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10) +#define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14) +#define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20) +#define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30) +#define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c) +#define DPHY_ISO_LANE_READY_BIT 0 +#define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL + #define DSI_HBP_FRAME_OVERHEAD 12 #define DSI_HSA_FRAME_OVERHEAD 14 #define DSI_HFP_FRAME_OVERHEAD 6 @@ -57,6 +86,9 @@ #define DSI_NULL_FRAME_OVERHEAD 6 #define DSI_EOT_PKT_SIZE 4 +#define DPHY_LANES_MIN 1 +#define DPHY_LANES_MAX 4 + struct cdns_dphy_cfg { u8 pll_ipdiv; u8 pll_opdiv; @@ -75,6 +107,11 @@ struct cdns_dphy_ops { int (*probe)(struct cdns_dphy *dphy); void (*remove)(struct cdns_dphy *dphy); + int (*power_on)(struct cdns_dphy *dphy); + int (*power_off)(struct cdns_dphy *dphy); + int (*validate)(struct cdns_dphy *dphy, enum phy_mode mode, int submode, + union phy_configure_opts *opts); + int (*configure)(struct cdns_dphy *dphy, union phy_configure_opts *opts); void (*set_psm_div)(struct cdns_dphy *dphy, u8 div); void (*set_clk_lane_cfg)(struct cdns_dphy *dphy, enum cdns_dphy_clk_lane_cfg cfg); @@ -83,15 +120,41 @@ unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy); }; +struct cdns_dphy_soc_data { + bool has_cmn_reset; +}; + struct cdns_dphy { struct cdns_dphy_cfg cfg; void __iomem *regs; + struct device *dev; struct clk *psm_clk; struct clk *pll_ref_clk; const struct cdns_dphy_ops *ops; struct phy *phy; }; +struct cdns_dphy_driver_data { + const struct cdns_dphy_ops *tx; + const struct cdns_dphy_ops *rx; +}; + +struct cdns_dphy_rx_band { + unsigned int min_rate; + unsigned int max_rate; +}; + +/* Order of bands is important since the index is the band number. */ +struct cdns_dphy_rx_band bands[] = { + {80, 100}, {100, 120}, {120, 160}, {160, 200}, {200, 240}, + {240, 280}, {280, 320}, {320, 360}, {360, 400}, {400, 480}, + {480, 560}, {560, 640}, {640, 720}, {720, 800}, {800, 880}, + {880, 1040}, {1040, 1200}, {1200, 1350}, {1350, 1500}, {1500, 1750}, + {1750, 2000}, {2000, 2250}, {2250, 2500} +}; + +int num_bands = ARRAY_SIZE(bands); + static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, struct cdns_dphy_cfg *cfg, struct phy_configure_opts_mipi_dphy *opts, @@ -199,20 +262,9 @@ dphy->regs + DPHY_PSM_CFG); } -/* - * This is the reference implementation of DPHY hooks. Specific integration of - * this IP may have to re-implement some of them depending on how they decided - * to wire things in the SoC. - */ -static const struct cdns_dphy_ops ref_dphy_ops = { - .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns, - .set_pll_cfg = cdns_dphy_ref_set_pll_cfg, - .set_psm_div = cdns_dphy_ref_set_psm_div, -}; - -static int cdns_dphy_config_from_opts(struct phy *phy, - struct phy_configure_opts_mipi_dphy *opts, - struct cdns_dphy_cfg *cfg) +static int cdns_dphy_tx_config_from_opts(struct phy *phy, + struct phy_configure_opts_mipi_dphy *opts, + struct cdns_dphy_cfg *cfg) { struct cdns_dphy *dphy = phy_get_drvdata(phy); unsigned int dsi_hfp_ext = 0; @@ -232,24 +284,13 @@ return 0; } -static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, - union phy_configure_opts *opts) -{ - struct cdns_dphy_cfg cfg = { 0 }; - - if (mode != PHY_MODE_MIPI_DPHY) - return -EINVAL; - - return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); -} - -static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +static int cdns_dphy_tx_configure(struct cdns_dphy *dphy, + union phy_configure_opts *opts) { - struct cdns_dphy *dphy = phy_get_drvdata(phy); struct cdns_dphy_cfg cfg = { 0 }; int ret; - ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); + ret = cdns_dphy_tx_config_from_opts(dphy->phy, &opts->mipi_dphy, &cfg); if (ret) return ret; @@ -279,9 +320,21 @@ return 0; } -static int cdns_dphy_power_on(struct phy *phy) +static int cdns_dphy_tx_validate(struct cdns_dphy *dphy, enum phy_mode mode, + int submode, union phy_configure_opts *opts) { - struct cdns_dphy *dphy = phy_get_drvdata(phy); + struct cdns_dphy_cfg cfg = { 0 }; + + if (submode != PHY_MIPI_DPHY_SUBMODE_TX) + return -EINVAL; + + return cdns_dphy_tx_config_from_opts(dphy->phy, &opts->mipi_dphy, &cfg); +} + +static int cdns_dphy_tx_power_on(struct cdns_dphy *dphy) +{ + if (!dphy->psm_clk || !dphy->pll_ref_clk) + return -EINVAL; clk_prepare_enable(dphy->psm_clk); clk_prepare_enable(dphy->pll_ref_clk); @@ -293,12 +346,256 @@ return 0; } +static int cdns_dphy_tx_power_off(struct cdns_dphy *dphy) +{ + clk_disable_unprepare(dphy->pll_ref_clk); + clk_disable_unprepare(dphy->psm_clk); + + return 0; +} + +static const struct cdns_dphy_ops tx_ref_dphy_ops = { + .power_on = cdns_dphy_tx_power_on, + .power_off = cdns_dphy_tx_power_off, + .validate = cdns_dphy_tx_validate, + .configure = cdns_dphy_tx_configure, + .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns, + .set_pll_cfg = cdns_dphy_ref_set_pll_cfg, + .set_psm_div = cdns_dphy_ref_set_psm_div, +}; + +static int cdns_dphy_rx_power_on(struct cdns_dphy *dphy) +{ + /* Start RX state machine. */ + writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN | + FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK, + DPHY_CMN_RX_BANDGAP_TIMER), + dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_rx_power_off(struct cdns_dphy *dphy) +{ + writel(0, dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate) +{ + unsigned int rate; + int i; + + rate = hs_clk_rate / 1000000UL; + /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */ + rate *= 2; + + if (rate < bands[0].min_rate || rate >= bands[num_bands - 1].max_rate) + return -EOPNOTSUPP; + + for (i = 0; i < num_bands; i++) { + if (rate >= bands[i].min_rate && rate < bands[i].max_rate) + return i; + } + + /* Unreachable. */ + WARN(1, "Reached unreachable code."); + return -EINVAL; +} + +static int cdns_dphy_rx_wait_for_bit(void __iomem *addr, unsigned int bit) +{ + u32 val; + + return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10, + DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000); +} + +static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy *dphy, int lanes) +{ + void __iomem *reg = dphy->regs; + u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0, DPHY_ISO_DL_CTRL_L1, + DPHY_ISO_DL_CTRL_L2, DPHY_ISO_DL_CTRL_L3}; + int ret, i; + + /* Data lanes. Minimum one lane is mandatory. */ + if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX) + return -EINVAL; + + /* Clock lane */ + ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L, + DPHY_ISO_LANE_READY_BIT); + if (ret) + return ret; + + for (i = 0; i < lanes; i++) { + ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i], + DPHY_ISO_LANE_READY_BIT); + if (ret) + return ret; + } + + return 0; +} + +static struct cdns_dphy_soc_data j721e_soc_data = { + .has_cmn_reset = true, +}; + +static const struct soc_device_attribute cdns_dphy_socinfo[] = { + { + .family = "J721E", + .revision = "SR2.0", + .data = &j721e_soc_data, + }, + {/* sentinel */} +}; + +static int cdns_dphy_rx_configure(struct cdns_dphy *dphy, + union phy_configure_opts *opts) +{ + const struct soc_device_attribute *soc; + const struct cdns_dphy_soc_data *soc_data; + unsigned int reg; + int band_ctrl, ret; + + soc = soc_device_match(cdns_dphy_socinfo); + if (soc && soc->data) { + soc_data = soc->data; + if (soc_data->has_cmn_reset) { + reg = DPHY_LANE_RESET_CMN_EN; + writel(reg, dphy->regs + DPHY_LANE); + } + } + + band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (band_ctrl < 0) + return band_ctrl; + + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); + writel(reg, dphy->regs + DPHY_BAND_CFG); + + /* + * Set the required power island phase 2 time. This is mandated by DPHY + * specs. + */ + reg = DPHY_POWER_ISLAND_EN_DATA_VAL; + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA); + reg = DPHY_POWER_ISLAND_EN_CLK_VAL; + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK); + + ret = cdns_dphy_rx_wait_lane_ready(dphy, opts->mipi_dphy.lanes); + if (ret) { + dev_err(dphy->dev, "DPHY wait for lane ready timeout\n"); + return ret; + } + + return 0; +} + +static int cdns_dphy_rx_validate(struct cdns_dphy *dphy, enum phy_mode mode, + int submode, union phy_configure_opts *opts) +{ + int ret; + + if (submode != PHY_MIPI_DPHY_SUBMODE_RX) + return -EINVAL; + + ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (ret < 0) + return ret; + + return phy_mipi_dphy_config_validate(&opts->mipi_dphy); +} + +static const struct cdns_dphy_ops rx_ref_dphy_ops = { + .power_on = cdns_dphy_rx_power_on, + .power_off = cdns_dphy_rx_power_off, + .configure = cdns_dphy_rx_configure, + .validate = cdns_dphy_rx_validate, +}; + +/* + * This is the reference implementation of DPHY hooks. Specific integration of + * this IP may have to re-implement some of them depending on how they decided + * to wire things in the SoC. + */ +static const struct cdns_dphy_driver_data ref_dphy_ops = { + .tx = &tx_ref_dphy_ops, + .rx = &rx_ref_dphy_ops, +}; + +static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + + if (mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; + + if (dphy->ops->validate) + return dphy->ops->validate(dphy, mode, submode, opts); + + return 0; +} + +static int cdns_dphy_power_on(struct phy *phy) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + + if (dphy->ops->power_on) + return dphy->ops->power_on(dphy); + + return 0; +} + static int cdns_dphy_power_off(struct phy *phy) { struct cdns_dphy *dphy = phy_get_drvdata(phy); - clk_disable_unprepare(dphy->pll_ref_clk); - clk_disable_unprepare(dphy->psm_clk); + if (dphy->ops->power_off) + return dphy->ops->power_off(dphy); + + return 0; +} + +static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + + if (dphy->ops->configure) + return dphy->ops->configure(dphy, opts); + + return 0; +} + +static int cdns_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + const struct cdns_dphy_driver_data *ddata; + + ddata = of_device_get_match_data(dphy->dev); + if (!ddata) + return -EINVAL; + + if (mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; + + if (submode == PHY_MIPI_DPHY_SUBMODE_TX) { + if (!ddata->tx) + return -EOPNOTSUPP; + + dphy->ops = ddata->tx; + } else if (submode == PHY_MIPI_DPHY_SUBMODE_RX) { + if (!ddata->rx) + return -EOPNOTSUPP; + + dphy->ops = ddata->rx; + } else { + return -EOPNOTSUPP; + } return 0; } @@ -308,6 +605,7 @@ .validate = cdns_dphy_validate, .power_on = cdns_dphy_power_on, .power_off = cdns_dphy_power_off, + .set_mode = cdns_dphy_set_mode, }; static int cdns_dphy_probe(struct platform_device *pdev) @@ -315,14 +613,20 @@ struct phy_provider *phy_provider; struct cdns_dphy *dphy; struct resource *res; + const struct cdns_dphy_driver_data *ddata; int ret; dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); if (!dphy) return -ENOMEM; dev_set_drvdata(&pdev->dev, dphy); + dphy->dev = &pdev->dev; + + ddata = of_device_get_match_data(&pdev->dev); + if (!ddata) + return -EINVAL; - dphy->ops = of_device_get_match_data(&pdev->dev); + dphy->ops = ddata->tx; if (!dphy->ops) return -EINVAL; @@ -331,11 +635,11 @@ if (IS_ERR(dphy->regs)) return PTR_ERR(dphy->regs); - dphy->psm_clk = devm_clk_get(&pdev->dev, "psm"); + dphy->psm_clk = devm_clk_get_optional(dphy->dev, "psm"); if (IS_ERR(dphy->psm_clk)) return PTR_ERR(dphy->psm_clk); - dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref"); + dphy->pll_ref_clk = devm_clk_get_optional(dphy->dev, "pll_ref"); if (IS_ERR(dphy->pll_ref_clk)) return PTR_ERR(dphy->pll_ref_clk); @@ -372,6 +676,7 @@ static const struct of_device_id cdns_dphy_of_match[] = { { .compatible = "cdns,dphy", .data = &ref_dphy_ops }, + { .compatible = "ti,j721e-dphy", .data = &ref_dphy_ops }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, cdns_dphy_of_match); @@ -387,5 +692,6 @@ module_platform_driver(cdns_dphy_platform_driver); MODULE_AUTHOR("Maxime Ripard "); +MODULE_AUTHOR("Pratyush Yadav "); MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver"); MODULE_LICENSE("GPL"); diff -Naur --no-dereference a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig --- a/drivers/phy/cadence/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/cadence/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -7,6 +7,7 @@ tristate "Cadence Torrent PHY driver" depends on OF depends on HAS_IOMEM + depends on COMMON_CLK select GENERIC_PHY help Support for Cadence Torrent PHY. @@ -24,6 +25,7 @@ config PHY_CADENCE_SIERRA tristate "Cadence Sierra PHY Driver" depends on OF && HAS_IOMEM && RESET_CONTROLLER + depends on COMMON_CLK select GENERIC_PHY help Enable this to support the Cadence Sierra PHY driver diff -Naur --no-dereference a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c --- a/drivers/phy/cadence/phy-cadence-sierra.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/cadence/phy-cadence-sierra.c 2022-01-06 12:45:53.826318157 -0500 @@ -7,6 +7,7 @@ * */ #include +#include #include #include #include @@ -20,10 +21,12 @@ #include #include #include +#include /* PHY register offsets */ #define SIERRA_COMMON_CDB_OFFSET 0x0 #define SIERRA_MACRO_ID_REG 0x0 +#define SIERRA_CMN_PLLLC_GEN_PREG 0x42 #define SIERRA_CMN_PLLLC_MODE_PREG 0x48 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A @@ -31,6 +34,9 @@ #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50 #define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62 +#define SIERRA_CMN_REFRCV_PREG 0x98 +#define SIERRA_CMN_REFRCV1_PREG 0xB8 +#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2 #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ ((0x4000 << (block_offset)) + \ @@ -144,6 +150,19 @@ #define SIERRA_MAX_LANES 16 #define PLL_LOCK_TIME 100000 +#define CDNS_SIERRA_OUTPUT_CLOCKS 2 +#define CDNS_SIERRA_INPUT_CLOCKS 5 +enum cdns_sierra_clock_input { + PHY_CLK, + CMN_REFCLK_DIG_DIV, + CMN_REFCLK1_DIG_DIV, + PLL0_REFCLK, + PLL1_REFCLK, +}; + +#define SIERRA_NUM_CMN_PLLC 2 +#define SIERRA_NUM_CMN_PLLC_PARENTS 2 + static const struct reg_field macro_id_type = REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); static const struct reg_field phy_pll_cfg_1 = @@ -151,6 +170,53 @@ static const struct reg_field pllctrl_lock = REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0); +static const char * const clk_names[] = { + [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc", + [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1", +}; + +enum cdns_sierra_cmn_plllc { + CMN_PLLLC, + CMN_PLLLC1, +}; + +struct cdns_sierra_pll_mux_reg_fields { + struct reg_field pfdclk_sel_preg; + struct reg_field plllc1en_field; + struct reg_field termen_field; +}; + +static const struct cdns_sierra_pll_mux_reg_fields cmn_plllc_pfdclk1_sel_preg[] = { + [CMN_PLLLC] = { + .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1), + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8, 8), + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0, 0), + }, + [CMN_PLLLC1] = { + .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1), + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8, 8), + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0), + }, +}; + +struct cdns_sierra_pll_mux { + struct clk_hw hw; + struct regmap_field *pfdclk_sel_preg; + struct regmap_field *plllc1en_field; + struct regmap_field *termen_field; + struct clk_init_data clk_data; +}; + +#define to_cdns_sierra_pll_mux(_hw) \ + container_of(_hw, struct cdns_sierra_pll_mux, hw) + +static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = { + [CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK }, + [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK }, +}; + +static u32 cdns_sierra_pll_mux_table[] = { 0, 1 }; + struct cdns_sierra_inst { struct phy *phy; u32 phy_type; @@ -197,12 +263,15 @@ struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; - struct clk *clk; - struct clk *cmn_refclk_dig_div; - struct clk *cmn_refclk1_dig_div; + struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC]; + struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC]; + struct regmap_field *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC]; + struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS]; int nsubnodes; u32 num_lanes; bool autoconf; + struct clk_onecell_data clk_data; + struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS]; }; static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val) @@ -281,8 +350,8 @@ if (phy->autoconf) return 0; - clk_set_rate(phy->cmn_refclk_dig_div, 25000000); - clk_set_rate(phy->cmn_refclk1_dig_div, 25000000); + clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000); + clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000); if (ins->phy_type == PHY_TYPE_PCIE) { num_cmn_regs = phy->init_data->pcie_cmn_regs; num_ln_regs = phy->init_data->pcie_ln_regs; @@ -364,6 +433,153 @@ .owner = THIS_MODULE, }; +static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw) +{ + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw); + struct regmap_field *field = mux->pfdclk_sel_preg; + unsigned int val; + + regmap_field_read(field, &val); + return clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table, 0, val); +} + +static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw); + struct regmap_field *plllc1en_field = mux->plllc1en_field; + struct regmap_field *termen_field = mux->termen_field; + struct regmap_field *field = mux->pfdclk_sel_preg; + int val, ret; + + ret = regmap_field_write(plllc1en_field, 0); + ret |= regmap_field_write(termen_field, 0); + if (index == 1) { + ret |= regmap_field_write(plllc1en_field, 1); + ret |= regmap_field_write(termen_field, 1); + } + + val = cdns_sierra_pll_mux_table[index]; + ret |= regmap_field_write(field, val); + + return ret; +} + +static const struct clk_ops cdns_sierra_pll_mux_ops = { + .set_parent = cdns_sierra_pll_mux_set_parent, + .get_parent = cdns_sierra_pll_mux_get_parent, +}; + +static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp, + struct regmap_field *pfdclk1_sel_field, + struct regmap_field *plllc1en_field, + struct regmap_field *termen_field, + int clk_index) +{ + struct cdns_sierra_pll_mux *mux; + struct device *dev = sp->dev; + struct clk_init_data *init; + const char **parent_names; + unsigned int num_parents; + char clk_name[100]; + struct clk *clk; + int i; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + num_parents = SIERRA_NUM_CMN_PLLC_PARENTS; + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + clk = sp->input_clks[pll_mux_parent_index[clk_index][i]]; + if (IS_ERR_OR_NULL(clk)) { + dev_err(dev, "No parent clock for derived_refclk\n"); + return PTR_ERR(clk); + } + parent_names[i] = __clk_get_name(clk); + } + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[clk_index]); + + init = &mux->clk_data; + + init->ops = &cdns_sierra_pll_mux_ops; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + init->name = clk_name; + + mux->pfdclk_sel_preg = pfdclk1_sel_field; + mux->plllc1en_field = plllc1en_field; + mux->termen_field = termen_field; + mux->hw.init = init; + + clk = devm_clk_register(dev, &mux->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + sp->output_clks[clk_index] = clk; + + return 0; +} + +static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp) +{ + struct regmap_field *pfdclk1_sel_field; + struct regmap_field *plllc1en_field; + struct regmap_field *termen_field; + struct device *dev = sp->dev; + int ret = 0, i, clk_index; + + clk_index = CDNS_SIERRA_PLL_CMNLC; + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) { + pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i]; + plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i]; + termen_field = sp->cmn_refrcv_refclk_termen_preg[i]; + + ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field, plllc1en_field, + termen_field, clk_index); + if (ret) { + dev_err(dev, "Fail to register cmn plllc mux\n"); + return ret; + } + } + + return 0; +} + +static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp) +{ + struct device *dev = sp->dev; + struct device_node *node = dev->of_node; + + of_clk_del_provider(node); +} + +static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp) +{ + struct device *dev = sp->dev; + struct device_node *node = dev->of_node; + int ret; + + ret = cdns_sierra_phy_register_pll_mux(sp); + if (ret) { + dev_err(dev, "Failed to pll mux clocks\n"); + return ret; + } + + sp->clk_data.clks = sp->output_clks; + sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS; + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp->clk_data); + if (ret) + dev_err(dev, "Failed to add clock provider: %s\n", node->name); + + return ret; +} + static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, struct device_node *child) { @@ -402,6 +618,7 @@ { struct device *dev = sp->dev; struct regmap_field *field; + struct reg_field reg_field; struct regmap *regmap; int i; @@ -413,6 +630,32 @@ } sp->macro_id_type = field; + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) { + reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg; + field = devm_regmap_field_alloc(dev, regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i); + return PTR_ERR(field); + } + sp->cmn_plllc_pfdclk1_sel_preg[i] = field; + + reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field; + field = devm_regmap_field_alloc(dev, regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n", i); + return PTR_ERR(field); + } + sp->cmn_refrcv_refclk_plllc1en_preg[i] = field; + + reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field; + field = devm_regmap_field_alloc(dev, regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n", i); + return PTR_ERR(field); + } + sp->cmn_refrcv_refclk_termen_preg[i] = field; + } + regmap = sp->regmap_phy_config_ctrl; field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1); if (IS_ERR(field)) { @@ -477,6 +720,110 @@ return 0; } +static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp, + struct device *dev) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get_optional(dev, "phy_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get clock phy_clk\n"); + return PTR_ERR(clk); + } + sp->input_clks[PHY_CLK] = clk; + + clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[CMN_REFCLK_DIG_DIV] = clk; + + clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk; + + clk = devm_clk_get_optional(dev, "pll0_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "pll0_refclk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[PLL0_REFCLK] = clk; + + clk = devm_clk_get_optional(dev, "pll1_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "pll1_refclk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[PLL1_REFCLK] = clk; + + return 0; +} + +static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp) +{ + int ret; + + ret = clk_prepare_enable(sp->input_clks[PHY_CLK]); + if (ret) + return ret; + + ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); + if (ret) + goto err_pll_cmnlc; + + ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]); + if (ret) + goto err_pll_cmnlc1; + + return 0; + +err_pll_cmnlc1: + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); + +err_pll_cmnlc: + clk_disable_unprepare(sp->input_clks[PHY_CLK]); + + return ret; +} + +static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp) +{ + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]); + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); + clk_disable_unprepare(sp->input_clks[PHY_CLK]); +} + +static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp, + struct device *dev) +{ + struct reset_control *rst; + + rst = devm_reset_control_get_exclusive(dev, "sierra_reset"); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(rst); + } + sp->phy_rst = rst; + + rst = devm_reset_control_get_optional_exclusive(dev, "sierra_apb"); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get apb reset\n"); + return PTR_ERR(rst); + } + sp->apb_rst = rst; + + return 0; +} + static int cdns_sierra_phy_probe(struct platform_device *pdev) { struct cdns_sierra_phy *sp; @@ -488,7 +835,6 @@ struct resource *res; int i, ret, node = 0; void __iomem *base; - struct clk *clk; struct device_node *dn = dev->of_node, *child; if (of_get_child_count(dn) == 0) @@ -526,43 +872,21 @@ platform_set_drvdata(pdev, sp); - sp->clk = devm_clk_get_optional(dev, "phy_clk"); - if (IS_ERR(sp->clk)) { - dev_err(dev, "failed to get clock phy_clk\n"); - return PTR_ERR(sp->clk); - } - - sp->phy_rst = devm_reset_control_get(dev, "sierra_reset"); - if (IS_ERR(sp->phy_rst)) { - dev_err(dev, "failed to get reset\n"); - return PTR_ERR(sp->phy_rst); - } - - sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb"); - if (IS_ERR(sp->apb_rst)) { - dev_err(dev, "failed to get apb reset\n"); - return PTR_ERR(sp->apb_rst); - } - - clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); - if (IS_ERR(clk)) { - dev_err(dev, "cmn_refclk_dig_div clock not found\n"); - ret = PTR_ERR(clk); + ret = cdns_sierra_phy_get_clocks(sp, dev); + if (ret) return ret; - } - sp->cmn_refclk_dig_div = clk; - clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); - if (IS_ERR(clk)) { - dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); - ret = PTR_ERR(clk); + ret = cdns_sierra_clk_register(sp); + if (ret) return ret; - } - sp->cmn_refclk1_dig_div = clk; - ret = clk_prepare_enable(sp->clk); + ret = cdns_sierra_phy_get_resets(sp, dev); if (ret) - return ret; + goto unregister_clk; + + ret = cdns_sierra_phy_enable_clocks(sp); + if (ret) + goto unregister_clk; /* Enable APB */ reset_control_deassert(sp->apb_rst); @@ -579,6 +903,10 @@ for_each_available_child_of_node(dn, child) { struct phy *gphy; + if (!(of_node_name_eq(child, "phy") || + of_node_name_eq(child, "link"))) + continue; + sp->phys[node].lnk_rst = of_reset_control_array_get_exclusive(child); @@ -634,8 +962,10 @@ reset_control_put(sp->phys[i].lnk_rst); of_node_put(child); clk_disable: - clk_disable_unprepare(sp->clk); + cdns_sierra_phy_disable_clocks(sp); reset_control_assert(sp->apb_rst); +unregister_clk: + cdns_sierra_clk_unregister(sp); return ret; } @@ -648,6 +978,7 @@ reset_control_assert(phy->apb_rst); pm_runtime_disable(&pdev->dev); + cdns_sierra_phy_disable_clocks(phy); /* * The device level resets will be put automatically. * Need to put the subnode resets here though. @@ -656,6 +987,9 @@ reset_control_assert(phy->phys[i].lnk_rst); reset_control_put(phy->phys[i].lnk_rst); } + + cdns_sierra_clk_unregister(phy); + return 0; } diff -Naur --no-dereference a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c --- a/drivers/phy/cadence/phy-cadence-torrent.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/cadence/phy-cadence-torrent.c 2022-01-06 12:45:53.826318157 -0500 @@ -7,7 +7,9 @@ */ #include +#include #include +#include #include #include #include @@ -84,6 +86,8 @@ #define CMN_PLLSM1_PLLLOCK_TMR 0x0034U #define CMN_CDIAG_CDB_PWRI_OVRD 0x0041U #define CMN_CDIAG_XCVRC_PWRI_OVRD 0x0047U +#define CMN_CDIAG_REFCLK_OVRD 0x004CU +#define CMN_CDIAG_REFCLK_DRV0_CTRL 0x0050U #define CMN_BGCAL_INIT_TMR 0x0064U #define CMN_BGCAL_ITER_TMR 0x0065U #define CMN_IBCAL_INIT_TMR 0x0074U @@ -122,6 +126,8 @@ #define CMN_PLL1_FRACDIVH_M0 0x00D2U #define CMN_PLL1_HIGH_THR_M0 0x00D3U #define CMN_PLL1_DSM_DIAG_M0 0x00D4U +#define CMN_PLL1_DSM_FBH_OVRD_M0 0x00D5U +#define CMN_PLL1_DSM_FBL_OVRD_M0 0x00D6U #define CMN_PLL1_SS_CTRL1_M0 0x00D8U #define CMN_PLL1_SS_CTRL2_M0 0x00D9U #define CMN_PLL1_SS_CTRL3_M0 0x00DAU @@ -163,10 +169,12 @@ #define TX_TXCC_CPOST_MULT_00 0x004CU #define TX_TXCC_CPOST_MULT_01 0x004DU #define TX_TXCC_MGNFS_MULT_000 0x0050U +#define TX_TXCC_MGNFS_MULT_100 0x0054U #define DRV_DIAG_TX_DRV 0x00C6U #define XCVR_DIAG_PLLDRC_CTRL 0x00E5U #define XCVR_DIAG_HSCLK_SEL 0x00E6U #define XCVR_DIAG_HSCLK_DIV 0x00E7U +#define XCVR_DIAG_RXCLK_CTRL 0x00E9U #define XCVR_DIAG_BIDI_CTRL 0x00EAU #define XCVR_DIAG_PSC_OVRD 0x00EBU #define TX_PSC_A0 0x0100U @@ -206,6 +214,7 @@ #define RX_DIAG_ACYA 0x01FFU /* PHY PCS common registers */ +#define PHY_PIPE_CMN_CTRL1 0x0000U #define PHY_PLL_CFG 0x000EU #define PHY_PIPE_USB3_GEN2_PRE_CFG0 0x0020U #define PHY_PIPE_USB3_GEN2_POST_CFG0 0x0022U @@ -216,6 +225,10 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U +static const char * const clk_names[] = { + [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", +}; + static const struct reg_field phy_pll_cfg = REG_FIELD(PHY_PLL_CFG, 0, 1); @@ -231,6 +244,26 @@ static const struct reg_field phy_reset_ctrl = REG_FIELD(PHY_RESET, 8, 8); +static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL1, 0, 0); + +#define REFCLK_OUT_NUM_CMN_CONFIG 5 + +enum cdns_torrent_refclk_out_cmn { + CMN_CDIAG_REFCLK_OVRD_4, + CMN_CDIAG_REFCLK_DRV0_CTRL_1, + CMN_CDIAG_REFCLK_DRV0_CTRL_4, + CMN_CDIAG_REFCLK_DRV0_CTRL_5, + CMN_CDIAG_REFCLK_DRV0_CTRL_6, +}; + +static const struct reg_field refclk_out_cmn_cfg[] = { + [CMN_CDIAG_REFCLK_OVRD_4] = REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4), + [CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1), + [CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4), + [CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5), + [CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6), +}; + enum cdns_torrent_phy_type { TYPE_NONE, TYPE_DP, @@ -279,6 +312,8 @@ struct regmap_field *phy_pma_cmn_ctrl_2; struct regmap_field *phy_pma_pll_raw_ctrl; struct regmap_field *phy_reset_ctrl; + struct clk *clks[CDNS_TORRENT_REFCLK_DRIVER + 1]; + struct clk_onecell_data clk_data; }; enum phy_powerstate { @@ -288,6 +323,16 @@ POWERSTATE_A3 = 3, }; +struct cdns_torrent_derived_refclk { + struct clk_hw hw; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG]; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_derived_refclk(_hw) \ + container_of(_hw, struct cdns_torrent_derived_refclk, hw) + static int cdns_torrent_phy_init(struct phy *phy); static int cdns_torrent_dp_init(struct phy *phy); static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, @@ -326,6 +371,19 @@ .owner = THIS_MODULE, }; +static int cdns_torrent_noop_phy_on(struct phy *phy) +{ + /* Give 5ms to 10ms delay for the PIPE clock to be stable */ + usleep_range(5000, 10000); + + return 0; +} + +static const struct phy_ops noop_ops = { + .power_on = cdns_torrent_noop_phy_on, + .owner = THIS_MODULE, +}; + struct cdns_reg_pairs { u32 val; u32 off; @@ -1604,6 +1662,108 @@ return ret; } +static int cdns_torrent_derived_refclk_enable(struct clk_hw *hw) +{ + struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); + + regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); + regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); + regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); + regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); + regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_OVRD_4], 1); + regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 1); + + return 0; +} + +static void cdns_torrent_derived_refclk_disable(struct clk_hw *hw) +{ + struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); + + regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 0); +} + +static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); + int val; + + regmap_field_read(derived_refclk->phy_pipe_cmn_ctrl1_0, &val); + + return !!val; +} + +static const struct clk_ops cdns_torrent_derived_refclk_ops = { + .enable = cdns_torrent_derived_refclk_enable, + .disable = cdns_torrent_derived_refclk_disable, + .is_enabled = cdns_torrent_derived_refclk_is_enabled, +}; + +static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_derived_refclk *derived_refclk; + struct device *dev = cdns_phy->dev; + struct regmap_field *field; + struct clk_init_data *init; + const char *parent_name; + struct regmap *regmap; + char clk_name[100]; + struct clk *clk; + int i; + + derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); + if (!derived_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_REFCLK_DRIVER]); + + clk = devm_clk_get_optional(dev, "phy_en_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "No parent clock for derived_refclk\n"); + return PTR_ERR(clk); + } + + init = &derived_refclk->clk_data; + + if (clk) { + parent_name = __clk_get_name(clk); + init->parent_names = &parent_name; + init->num_parents = 1; + } + init->ops = &cdns_torrent_derived_refclk_ops; + init->flags = 0; + init->name = clk_name; + + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0); + if (IS_ERR(field)) { + dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n"); + return PTR_ERR(field); + } + derived_refclk->phy_pipe_cmn_ctrl1_0 = field; + + regmap = cdns_phy->regmap_common_cdb; + for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) { + field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]); + if (IS_ERR(field)) { + dev_err(dev, "CMN reg field init failed\n"); + return PTR_ERR(field); + } + derived_refclk->cmn_fields[i] = field; + } + + derived_refclk->hw.init = init; + + clk = devm_clk_register(dev, &derived_refclk->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + cdns_phy->clks[CDNS_TORRENT_REFCLK_DRIVER] = clk; + + return 0; +} + static int cdns_torrent_phy_on(struct phy *phy) { struct cdns_torrent_inst *inst = phy_get_drvdata(phy); @@ -2071,6 +2231,85 @@ return 0; } +static void cdns_torrent_clk_cleanup(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + + of_clk_del_provider(dev->of_node); +} + +static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + struct device_node *node = dev->of_node; + int ret; + + ret = cdns_torrent_derived_refclk_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register derived refclk\n"); + return ret; + } + + cdns_phy->clk_data.clks = cdns_phy->clks; + cdns_phy->clk_data.clk_num = CDNS_TORRENT_REFCLK_DRIVER + 1; + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &cdns_phy->clk_data); + if (ret) { + dev_err(dev, "Failed to add clock provider: %s\n", node->name); + return ret; + } + + return 0; +} + +static int cdns_torrent_reset(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + + cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0); + if (IS_ERR(cdns_phy->phy_rst)) { + dev_err(dev, "%s: failed to get reset\n", + dev->of_node->full_name); + return PTR_ERR(cdns_phy->phy_rst); + } + + cdns_phy->apb_rst = devm_reset_control_get_optional_exclusive(dev, "torrent_apb"); + if (IS_ERR(cdns_phy->apb_rst)) { + dev_err(dev, "%s: failed to get apb reset\n", + dev->of_node->full_name); + return PTR_ERR(cdns_phy->apb_rst); + } + + return 0; +} + +static int cdns_torrent_clk(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + int ret; + + cdns_phy->clk = devm_clk_get(dev, "refclk"); + if (IS_ERR(cdns_phy->clk)) { + dev_err(dev, "phy ref clock not found\n"); + return PTR_ERR(cdns_phy->clk); + } + + ret = clk_prepare_enable(cdns_phy->clk); + if (ret) { + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); + return ret; + } + + cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk); + if (!(cdns_phy->ref_clk_rate)) { + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); + clk_disable_unprepare(cdns_phy->clk); + return -EINVAL; + } + + return 0; +} + static int cdns_torrent_phy_probe(struct platform_device *pdev) { struct cdns_torrent_phy *cdns_phy; @@ -2080,6 +2319,7 @@ struct device_node *child; int ret, subnodes, node = 0, i; u32 total_num_lanes = 0; + int already_configured; u8 init_dp_regmap = 0; u32 phy_type; @@ -2096,26 +2336,6 @@ cdns_phy->dev = dev; cdns_phy->init_data = data; - cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0); - if (IS_ERR(cdns_phy->phy_rst)) { - dev_err(dev, "%s: failed to get reset\n", - dev->of_node->full_name); - return PTR_ERR(cdns_phy->phy_rst); - } - - cdns_phy->apb_rst = devm_reset_control_get_optional(dev, "torrent_apb"); - if (IS_ERR(cdns_phy->apb_rst)) { - dev_err(dev, "%s: failed to get apb reset\n", - dev->of_node->full_name); - return PTR_ERR(cdns_phy->apb_rst); - } - - cdns_phy->clk = devm_clk_get(dev, "refclk"); - if (IS_ERR(cdns_phy->clk)) { - dev_err(dev, "phy ref clock not found\n"); - return PTR_ERR(cdns_phy->clk); - } - cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cdns_phy->sd_base)) return PTR_ERR(cdns_phy->sd_base); @@ -2134,21 +2354,24 @@ if (ret) return ret; - ret = clk_prepare_enable(cdns_phy->clk); - if (ret) { - dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); + ret = cdns_torrent_clk_register(cdns_phy); + if (ret) return ret; - } - cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk); - if (!(cdns_phy->ref_clk_rate)) { - dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); - clk_disable_unprepare(cdns_phy->clk); - return -EINVAL; - } + regmap_field_read(cdns_phy->phy_pma_cmn_ctrl_1, &already_configured); - /* Enable APB */ - reset_control_deassert(cdns_phy->apb_rst); + if (!already_configured) { + ret = cdns_torrent_reset(cdns_phy); + if (ret) + goto clk_cleanup; + + ret = cdns_torrent_clk(cdns_phy); + if (ret) + goto clk_cleanup; + + /* Enable APB */ + reset_control_deassert(cdns_phy->apb_rst); + } for_each_available_child_of_node(dev->of_node, child) { struct phy *gphy; @@ -2218,7 +2441,10 @@ of_property_read_u32(child, "cdns,ssc-mode", &cdns_phy->phys[node].ssc_mode); - gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops); + if (!already_configured) + gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops); + else + gphy = devm_phy_create(dev, child, &noop_ops); if (IS_ERR(gphy)) { ret = PTR_ERR(gphy); goto put_child; @@ -2302,7 +2528,7 @@ goto put_lnk_rst; } - if (cdns_phy->nsubnodes > 1) { + if (cdns_phy->nsubnodes > 1 && !already_configured) { ret = cdns_torrent_phy_configure_multilink(cdns_phy); if (ret) goto put_lnk_rst; @@ -2324,6 +2550,8 @@ of_node_put(child); reset_control_assert(cdns_phy->apb_rst); clk_disable_unprepare(cdns_phy->clk); +clk_cleanup: + cdns_torrent_clk_cleanup(cdns_phy); return ret; } @@ -2340,6 +2568,7 @@ } clk_disable_unprepare(cdns_phy->clk); + cdns_torrent_clk_cleanup(cdns_phy); return 0; } @@ -2455,8 +2684,6 @@ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, - {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL}, {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, @@ -2464,7 +2691,9 @@ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, - {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD} + {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} }; static struct cdns_torrent_vals usb_100_int_ssc_cmn_vals = { @@ -2507,13 +2736,28 @@ }; /* USB 100 MHz Ref clk, no SSC */ -static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = { +static struct cdns_reg_pairs sl_usb_100_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}, {0x0003, CMN_PLL0_VCOCAL_TCTRL}, {0x0003, CMN_PLL1_VCOCAL_TCTRL}, {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD} }; +static struct cdns_torrent_vals sl_usb_100_no_ssc_cmn_vals = { + .reg_pairs = sl_usb_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_usb_100_no_ssc_cmn_regs), +}; + +static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = { + {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, + {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + static struct cdns_reg_pairs usb_100_no_ssc_tx_ln_regs[] = { {0x02FF, TX_PSC_A0}, {0x06AF, TX_PSC_A1}, @@ -2645,12 +2889,22 @@ }; /* SGMII 100 MHz Ref clk, no SSC */ -static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = { +static struct cdns_reg_pairs sl_sgmii_100_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}, {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL}, - {0x3700, CMN_DIAG_BIAS_OVRD1}, - {0x0008, CMN_TXPUCAL_TUNE}, - {0x0008, CMN_TXPDCAL_TUNE} + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +static struct cdns_torrent_vals sl_sgmii_100_no_ssc_cmn_vals = { + .reg_pairs = sl_sgmii_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_sgmii_100_no_ssc_cmn_regs), +}; + +static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = { + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} }; static struct cdns_reg_pairs sgmii_100_no_ssc_tx_ln_regs[] = { @@ -2661,6 +2915,15 @@ {0x00B3, DRV_DIAG_TX_DRV} }; +static struct cdns_reg_pairs ti_sgmii_100_no_ssc_tx_ln_regs[] = { + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x00B3, DRV_DIAG_TX_DRV}, + {0x4000, XCVR_DIAG_RXCLK_CTRL}, +}; + static struct cdns_reg_pairs sgmii_100_no_ssc_rx_ln_regs[] = { {0x091D, RX_PSC_A0}, {0x0900, RX_PSC_A2}, @@ -2689,6 +2952,11 @@ .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_tx_ln_regs), }; +static struct cdns_torrent_vals ti_sgmii_100_no_ssc_tx_ln_vals = { + .reg_pairs = ti_sgmii_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(ti_sgmii_100_no_ssc_tx_ln_regs), +}; + static struct cdns_torrent_vals sgmii_100_no_ssc_rx_ln_vals = { .reg_pairs = sgmii_100_no_ssc_rx_ln_regs, .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_rx_ln_regs), @@ -2736,17 +3004,14 @@ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, - {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL}, {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, - {0x3700, CMN_DIAG_BIAS_OVRD1}, - {0x0008, CMN_TXPUCAL_TUNE}, - {0x0008, CMN_TXPDCAL_TUNE} + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} }; static struct cdns_torrent_vals sgmii_100_int_ssc_cmn_vals = { @@ -2755,19 +3020,43 @@ }; /* QSGMII 100 MHz Ref clk, no SSC */ -static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = { +static struct cdns_reg_pairs sl_qsgmii_100_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}, {0x0003, CMN_PLL0_VCOCAL_TCTRL}, {0x0003, CMN_PLL1_VCOCAL_TCTRL} }; +static struct cdns_torrent_vals sl_qsgmii_100_no_ssc_cmn_vals = { + .reg_pairs = sl_qsgmii_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_qsgmii_100_no_ssc_cmn_regs), +}; + +static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = { + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + static struct cdns_reg_pairs qsgmii_100_no_ssc_tx_ln_regs[] = { {0x00F3, TX_PSC_A0}, {0x04A2, TX_PSC_A2}, {0x04A2, TX_PSC_A3}, {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x0011, TX_TXCC_MGNFS_MULT_100}, {0x0003, DRV_DIAG_TX_DRV} }; +static struct cdns_reg_pairs ti_qsgmii_100_no_ssc_tx_ln_regs[] = { + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x0011, TX_TXCC_MGNFS_MULT_100}, + {0x0003, DRV_DIAG_TX_DRV}, + {0x4000, XCVR_DIAG_RXCLK_CTRL}, +}; + static struct cdns_reg_pairs qsgmii_100_no_ssc_rx_ln_regs[] = { {0x091D, RX_PSC_A0}, {0x0900, RX_PSC_A2}, @@ -2796,6 +3085,11 @@ .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_tx_ln_regs), }; +static struct cdns_torrent_vals ti_qsgmii_100_no_ssc_tx_ln_vals = { + .reg_pairs = ti_qsgmii_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(ti_qsgmii_100_no_ssc_tx_ln_regs), +}; + static struct cdns_torrent_vals qsgmii_100_no_ssc_rx_ln_vals = { .reg_pairs = qsgmii_100_no_ssc_rx_ln_regs, .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_rx_ln_regs), @@ -2843,14 +3137,14 @@ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, - {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL}, {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, - {0x0005, CMN_PLL1_LOCK_PLLCNT_THR} + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} }; static struct cdns_torrent_vals qsgmii_100_int_ssc_cmn_vals = { @@ -2922,8 +3216,6 @@ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, - {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL}, {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, @@ -2979,8 +3271,6 @@ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, - {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL}, {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, @@ -2996,8 +3286,9 @@ /* PCIe, 100 MHz Ref clk, no SSC & external SSC */ static struct cdns_reg_pairs pcie_100_ext_no_ssc_cmn_regs[] = { - {0x0003, CMN_PLL0_VCOCAL_TCTRL}, - {0x0003, CMN_PLL1_VCOCAL_TCTRL} + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0} }; static struct cdns_reg_pairs pcie_100_ext_no_ssc_rx_ln_regs[] = { @@ -3198,8 +3489,8 @@ .cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals, }, [TYPE_SGMII] = { @@ -3220,7 +3511,7 @@ }, [TYPE_SGMII] = { [TYPE_NONE] = { - [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals, }, [TYPE_PCIE] = { [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, @@ -3235,7 +3526,7 @@ }, [TYPE_QSGMII] = { [TYPE_NONE] = { - [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals, }, [TYPE_PCIE] = { [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, @@ -3250,8 +3541,8 @@ }, [TYPE_USB] = { [TYPE_NONE] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, [TYPE_PCIE] = { @@ -3260,13 +3551,13 @@ [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals, }, [TYPE_SGMII] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, [TYPE_QSGMII] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, }, @@ -3607,8 +3898,8 @@ .cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals, }, [TYPE_SGMII] = { @@ -3629,7 +3920,7 @@ }, [TYPE_SGMII] = { [TYPE_NONE] = { - [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals, }, [TYPE_PCIE] = { [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, @@ -3644,7 +3935,7 @@ }, [TYPE_QSGMII] = { [TYPE_NONE] = { - [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals, }, [TYPE_PCIE] = { [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, @@ -3659,8 +3950,8 @@ }, [TYPE_USB] = { [TYPE_NONE] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, [TYPE_PCIE] = { @@ -3669,13 +3960,13 @@ [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals, }, [TYPE_SGMII] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, [TYPE_QSGMII] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, }, @@ -3705,32 +3996,32 @@ }, [TYPE_SGMII] = { [TYPE_NONE] = { - [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, }, [TYPE_PCIE] = { - [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, }, [TYPE_USB] = { - [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, }, }, [TYPE_QSGMII] = { [TYPE_NONE] = { - [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, }, [TYPE_PCIE] = { - [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, }, [TYPE_USB] = { - [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, }, }, [TYPE_USB] = { diff -Naur --no-dereference a/drivers/phy/Kconfig b/drivers/phy/Kconfig --- a/drivers/phy/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -61,6 +61,15 @@ interface to interact with USB GEN-II and USB 3.x PHY that is part of the Intel network SOC. +config PHY_CAN_TRANSCEIVER + tristate "CAN transceiver PHY" + select GENERIC_PHY + help + This option enables support for CAN transceivers as a PHY. This + driver provides function for putting the transceivers in various + functional modes using gpios and sets the attribute max link + rate, for CAN drivers. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff -Naur --no-dereference a/drivers/phy/Makefile b/drivers/phy/Makefile --- a/drivers/phy/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -5,6 +5,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o +obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o diff -Naur --no-dereference a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c --- a/drivers/phy/phy-can-transceiver.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/phy/phy-can-transceiver.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-can-transceiver.c - phy driver for CAN transceivers + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com + * + */ +#include +#include +#include +#include +#include + +struct can_transceiver_data { + u32 flags; +#define CAN_TRANSCEIVER_STB_PRESENT BIT(0) +#define CAN_TRANSCEIVER_EN_PRESENT BIT(1) +}; + +struct can_transceiver_phy { + struct phy *generic_phy; + struct gpio_desc *standby_gpio; + struct gpio_desc *enable_gpio; +}; + +/* Power on function */ +static int can_transceiver_phy_power_on(struct phy *phy) +{ + struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + + if (can_transceiver_phy->standby_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0); + if (can_transceiver_phy->enable_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1); + + return 0; +} + +/* Power off function */ +static int can_transceiver_phy_power_off(struct phy *phy) +{ + struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + + if (can_transceiver_phy->standby_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); + if (can_transceiver_phy->enable_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); + + return 0; +} + +static const struct phy_ops can_transceiver_phy_ops = { + .power_on = can_transceiver_phy_power_on, + .power_off = can_transceiver_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct can_transceiver_data tcan1042_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT, +}; + +static const struct can_transceiver_data tcan1043_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT, +}; + +static const struct of_device_id can_transceiver_phy_ids[] = { + { + .compatible = "ti,tcan1042", + .data = &tcan1042_drvdata + }, + { + .compatible = "ti,tcan1043", + .data = &tcan1043_drvdata + }, + { } +}; +MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); + +static int can_transceiver_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct can_transceiver_phy *can_transceiver_phy; + const struct can_transceiver_data *drvdata; + const struct of_device_id *match; + struct phy *phy; + struct gpio_desc *standby_gpio; + struct gpio_desc *enable_gpio; + u32 max_bitrate = 0; + + can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL); + if (!can_transceiver_phy) + return -ENOMEM; + + match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); + drvdata = match->data; + + phy = devm_phy_create(dev, dev->of_node, + &can_transceiver_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create can transceiver phy\n"); + return PTR_ERR(phy); + } + + device_property_read_u32(dev, "max-bitrate", &max_bitrate); + if (!max_bitrate) + dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n"); + phy->attrs.max_link_rate = max_bitrate; + + can_transceiver_phy->generic_phy = phy; + + if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) { + standby_gpio = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH); + if (IS_ERR(standby_gpio)) + return PTR_ERR(standby_gpio); + can_transceiver_phy->standby_gpio = standby_gpio; + } + + if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) { + enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(enable_gpio)) + return PTR_ERR(enable_gpio); + can_transceiver_phy->enable_gpio = enable_gpio; + } + + phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver can_transceiver_phy_driver = { + .probe = can_transceiver_phy_probe, + .driver = { + .name = "can-transceiver-phy", + .of_match_table = can_transceiver_phy_ids, + }, +}; + +module_platform_driver(can_transceiver_phy_driver); + +MODULE_AUTHOR("Faiz Abbas "); +MODULE_AUTHOR("Aswath Govindraju "); +MODULE_DESCRIPTION("CAN TRANSCEIVER PHY driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c --- a/drivers/phy/phy-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/phy-core.c 2022-01-06 12:45:53.826318157 -0500 @@ -667,16 +667,18 @@ struct phy *phy; struct device_link *link; - if (string == NULL) { - dev_WARN(dev, "missing string\n"); - return ERR_PTR(-EINVAL); - } - if (dev->of_node) { - index = of_property_match_string(dev->of_node, "phy-names", - string); + if (string) + index = of_property_match_string(dev->of_node, "phy-names", + string); + else + index = 0; phy = _of_phy_get(dev->of_node, index); } else { + if (!string) { + dev_WARN(dev, "missing string\n"); + return ERR_PTR(-EINVAL); + } phy = phy_find(dev, string); } if (IS_ERR(phy)) diff -Naur --no-dereference a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c --- a/drivers/phy/ti/phy-j721e-wiz.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/phy/ti/phy-j721e-wiz.c 2022-01-06 12:45:53.826318157 -0500 @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -26,6 +27,11 @@ #define WIZ_SERDES_RST 0x40c #define WIZ_SERDES_TYPEC 0x410 #define WIZ_LANECTL(n) (0x480 + (0x40 * (n))) +#define WIZ_LANEDIV(n) (0x484 + (0x40 * (n))) + +#define WIZ_MAX_INPUT_CLOCKS 4 +/* To include mux clocks, divider clocks and gate clocks */ +#define WIZ_MAX_OUTPUT_CLOCKS 32 #define WIZ_MAX_LANES 4 #define WIZ_MUX_NUM_CLOCKS 3 @@ -52,8 +58,16 @@ CMN_REFCLK1_DIG_DIV, }; +enum wiz_clock_input { + WIZ_CORE_REFCLK, + WIZ_EXT_REFCLK, + WIZ_CORE_REFCLK1, + WIZ_EXT_REFCLK1, +}; + static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31); static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31); +static const struct reg_field phy_en_refclk = REG_FIELD(WIZ_SERDES_RST, 30, 30); static const struct reg_field pll1_refclk_mux_sel = REG_FIELD(WIZ_SERDES_RST, 29, 29); static const struct reg_field pll0_refclk_mux_sel = @@ -70,6 +84,12 @@ REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27); static const struct reg_field pma_cmn_refclk1_dig_div = REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25); +static const char * const output_clk_names[] = { + [TI_WIZ_PLL0_REFCLK] = "pll0-refclk", + [TI_WIZ_PLL1_REFCLK] = "pll1-refclk", + [TI_WIZ_REFCLK_DIG] = "refclk-dig", + [TI_WIZ_PHY_EN_REFCLK] = "phy-en-refclk", +}; static const struct reg_field p_enable[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(0), 30, 31), @@ -101,13 +121,34 @@ REG_FIELD(WIZ_LANECTL(3), 24, 25), }; +static const struct reg_field p0_fullrt_div[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 22, 23), + REG_FIELD(WIZ_LANECTL(1), 22, 23), + REG_FIELD(WIZ_LANECTL(2), 22, 23), + REG_FIELD(WIZ_LANECTL(3), 22, 23), +}; + +static const struct reg_field p_mac_div_sel0[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANEDIV(0), 16, 22), + REG_FIELD(WIZ_LANEDIV(1), 16, 22), + REG_FIELD(WIZ_LANEDIV(2), 16, 22), + REG_FIELD(WIZ_LANEDIV(3), 16, 22), +}; + +static const struct reg_field p_mac_div_sel1[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANEDIV(0), 0, 8), + REG_FIELD(WIZ_LANEDIV(1), 0, 8), + REG_FIELD(WIZ_LANEDIV(2), 0, 8), + REG_FIELD(WIZ_LANEDIV(3), 0, 8), +}; + static const struct reg_field typec_ln10_swap = REG_FIELD(WIZ_SERDES_TYPEC, 30, 30); struct wiz_clk_mux { struct clk_hw hw; struct regmap_field *field; - u32 *table; + const u32 *table; struct clk_init_data clk_data; }; @@ -123,18 +164,26 @@ #define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw) struct wiz_clk_mux_sel { - struct regmap_field *field; - u32 table[4]; + u32 table[WIZ_MAX_INPUT_CLOCKS]; const char *node_name; + u32 num_parents; + u32 parents[WIZ_MAX_INPUT_CLOCKS]; }; struct wiz_clk_div_sel { - struct regmap_field *field; - const struct clk_div_table *table; + const struct clk_div_table *table; const char *node_name; }; -static struct wiz_clk_mux_sel clk_mux_sel_16g[] = { +struct wiz_phy_en_refclk { + struct clk_hw hw; + struct regmap_field *phy_en_refclk; + struct clk_init_data clk_data; +}; + +#define to_wiz_phy_en_refclk(_hw) container_of(_hw, struct wiz_phy_en_refclk, hw) + +static const struct wiz_clk_mux_sel clk_mux_sel_16g[] = { { /* * Mux value to be configured for each of the input clocks @@ -153,20 +202,26 @@ }, }; -static struct wiz_clk_mux_sel clk_mux_sel_10g[] = { +static const struct wiz_clk_mux_sel clk_mux_sel_10g[] = { { /* * Mux value to be configured for each of the input clocks * in the order populated in device tree */ + .num_parents = 2, + .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK }, .table = { 1, 0 }, .node_name = "pll0-refclk", }, { + .num_parents = 2, + .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK }, .table = { 1, 0 }, .node_name = "pll1-refclk", }, { + .num_parents = 2, + .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK }, .table = { 1, 0 }, .node_name = "refclk-dig", }, @@ -179,7 +234,7 @@ { .val = 3, .div = 8, }, }; -static struct wiz_clk_div_sel clk_div_sel[] = { +static const struct wiz_clk_div_sel clk_div_sel[] = { { .table = clk_div_table, .node_name = "cmn-refclk-dig-div", @@ -193,6 +248,7 @@ enum wiz_type { J721E_WIZ_16G, J721E_WIZ_10G, + AM64_WIZ_10G, }; #define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */ @@ -201,19 +257,25 @@ struct wiz { struct regmap *regmap; enum wiz_type type; - struct wiz_clk_mux_sel *clk_mux_sel; - struct wiz_clk_div_sel *clk_div_sel; + const struct wiz_clk_mux_sel *clk_mux_sel; + const struct wiz_clk_div_sel *clk_div_sel; unsigned int clk_div_sel_num; struct regmap_field *por_en; struct regmap_field *phy_reset_n; + struct regmap_field *phy_en_refclk; struct regmap_field *p_enable[WIZ_MAX_LANES]; struct regmap_field *p_align[WIZ_MAX_LANES]; struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES]; struct regmap_field *p_standard_mode[WIZ_MAX_LANES]; + struct regmap_field *p_mac_div_sel0[WIZ_MAX_LANES]; + struct regmap_field *p_mac_div_sel1[WIZ_MAX_LANES]; + struct regmap_field *p0_fullrt_div[WIZ_MAX_LANES]; struct regmap_field *pma_cmn_refclk_int_mode; struct regmap_field *pma_cmn_refclk_mode; struct regmap_field *pma_cmn_refclk_dig_div; struct regmap_field *pma_cmn_refclk1_dig_div; + struct regmap_field *mux_sel_field[WIZ_MUX_NUM_CLOCKS]; + struct regmap_field *div_sel_field[WIZ_DIV_NUM_CLOCKS_16G]; struct regmap_field *typec_ln10_swap; struct device *dev; @@ -223,6 +285,9 @@ struct gpio_desc *gpio_typec_dir; int typec_dir_delay; u32 lane_phy_type[WIZ_MAX_LANES]; + struct clk *input_clks[WIZ_MAX_INPUT_CLOCKS]; + struct clk *output_clks[WIZ_MAX_OUTPUT_CLOCKS]; + struct clk_onecell_data clk_data; }; static int wiz_reset(struct wiz *wiz) @@ -242,6 +307,27 @@ return 0; } +static int wiz_p_mac_div_sel(struct wiz *wiz) +{ + u32 num_lanes = wiz->num_lanes; + int ret; + int i; + + for (i = 0; i < num_lanes; i++) { + if (wiz->lane_phy_type[i] == PHY_TYPE_QSGMII) { + ret = regmap_field_write(wiz->p_mac_div_sel0[i], 1); + if (ret) + return ret; + + ret = regmap_field_write(wiz->p_mac_div_sel1[i], 2); + if (ret) + return ret; + } + } + + return 0; +} + static int wiz_mode_select(struct wiz *wiz) { u32 num_lanes = wiz->num_lanes; @@ -252,8 +338,8 @@ for (i = 0; i < num_lanes; i++) { if (wiz->lane_phy_type[i] == PHY_TYPE_DP) mode = LANE_MODE_GEN1; - else - mode = LANE_MODE_GEN4; + else if (wiz->lane_phy_type[i] == PHY_TYPE_QSGMII) + mode = LANE_MODE_GEN2; ret = regmap_field_write(wiz->p_standard_mode[i], mode); if (ret) @@ -299,6 +385,12 @@ return ret; } + ret = wiz_p_mac_div_sel(wiz); + if (ret) { + dev_err(dev, "Configuring P0 MAC DIV SEL failed\n"); + return ret; + } + ret = wiz_init_raw_interface(wiz, true); if (ret) { dev_err(dev, "WIZ interface initialization failed\n"); @@ -310,8 +402,6 @@ static int wiz_regfield_init(struct wiz *wiz) { - struct wiz_clk_mux_sel *clk_mux_sel; - struct wiz_clk_div_sel *clk_div_sel; struct regmap *regmap = wiz->regmap; int num_lanes = wiz->num_lanes; struct device *dev = wiz->dev; @@ -344,54 +434,49 @@ return PTR_ERR(wiz->pma_cmn_refclk_mode); } - clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV]; - clk_div_sel->field = devm_regmap_field_alloc(dev, regmap, - pma_cmn_refclk_dig_div); - if (IS_ERR(clk_div_sel->field)) { + wiz->div_sel_field[CMN_REFCLK_DIG_DIV] = + devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_dig_div); + if (IS_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV])) { dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n"); - return PTR_ERR(clk_div_sel->field); + return PTR_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV]); } if (wiz->type == J721E_WIZ_16G) { - clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV]; - clk_div_sel->field = + wiz->div_sel_field[CMN_REFCLK1_DIG_DIV] = devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk1_dig_div); - if (IS_ERR(clk_div_sel->field)) { + if (IS_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV])) { dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n"); - return PTR_ERR(clk_div_sel->field); + return PTR_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV]); } } - clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK]; - clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap, - pll0_refclk_mux_sel); - if (IS_ERR(clk_mux_sel->field)) { + wiz->mux_sel_field[PLL0_REFCLK] = + devm_regmap_field_alloc(dev, regmap, pll0_refclk_mux_sel); + if (IS_ERR(wiz->mux_sel_field[PLL0_REFCLK])) { dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n"); - return PTR_ERR(clk_mux_sel->field); + return PTR_ERR(wiz->mux_sel_field[PLL0_REFCLK]); } - clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK]; - clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap, - pll1_refclk_mux_sel); - if (IS_ERR(clk_mux_sel->field)) { + wiz->mux_sel_field[PLL1_REFCLK] = + devm_regmap_field_alloc(dev, regmap, pll1_refclk_mux_sel); + if (IS_ERR(wiz->mux_sel_field[PLL1_REFCLK])) { dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n"); - return PTR_ERR(clk_mux_sel->field); + return PTR_ERR(wiz->mux_sel_field[PLL1_REFCLK]); } - clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG]; - if (wiz->type == J721E_WIZ_10G) - clk_mux_sel->field = + if (wiz->type == J721E_WIZ_10G || wiz->type == AM64_WIZ_10G) + wiz->mux_sel_field[REFCLK_DIG] = devm_regmap_field_alloc(dev, regmap, refclk_dig_sel_10g); else - clk_mux_sel->field = + wiz->mux_sel_field[REFCLK_DIG] = devm_regmap_field_alloc(dev, regmap, refclk_dig_sel_16g); - if (IS_ERR(clk_mux_sel->field)) { + if (IS_ERR(wiz->mux_sel_field[REFCLK_DIG])) { dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n"); - return PTR_ERR(clk_mux_sel->field); + return PTR_ERR(wiz->mux_sel_field[REFCLK_DIG]); } for (i = 0; i < num_lanes; i++) { @@ -424,6 +509,28 @@ i); return PTR_ERR(wiz->p_standard_mode[i]); } + + wiz->p0_fullrt_div[i] = devm_regmap_field_alloc(dev, regmap, p0_fullrt_div[i]); + if (IS_ERR(wiz->p0_fullrt_div[i])) { + dev_err(dev, "P%d_FULLRT_DIV reg field init failed\n", i); + return PTR_ERR(wiz->p0_fullrt_div[i]); + } + + wiz->p_mac_div_sel0[i] = + devm_regmap_field_alloc(dev, regmap, p_mac_div_sel0[i]); + if (IS_ERR(wiz->p_mac_div_sel0[i])) { + dev_err(dev, "P%d_MAC_DIV_SEL0 reg field init fail\n", + i); + return PTR_ERR(wiz->p_mac_div_sel0[i]); + } + + wiz->p_mac_div_sel1[i] = + devm_regmap_field_alloc(dev, regmap, p_mac_div_sel1[i]); + if (IS_ERR(wiz->p_mac_div_sel1[i])) { + dev_err(dev, "P%d_MAC_DIV_SEL1 reg field init fail\n", + i); + return PTR_ERR(wiz->p_mac_div_sel1[i]); + } } wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap, @@ -433,6 +540,76 @@ return PTR_ERR(wiz->typec_ln10_swap); } + wiz->phy_en_refclk = devm_regmap_field_alloc(dev, regmap, phy_en_refclk); + if (IS_ERR(wiz->phy_en_refclk)) { + dev_err(dev, "PHY_EN_REFCLK reg field init failed\n"); + return PTR_ERR(wiz->phy_en_refclk); + } + + return 0; +} + +static int wiz_phy_en_refclk_enable(struct clk_hw *hw) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw); + struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk; + + regmap_field_write(phy_en_refclk, 1); + + return 0; +} + +static void wiz_phy_en_refclk_disable(struct clk_hw *hw) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw); + struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk; + + regmap_field_write(phy_en_refclk, 0); +} + +static int wiz_phy_en_refclk_is_enabled(struct clk_hw *hw) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw); + struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk; + int val; + + regmap_field_read(phy_en_refclk, &val); + + return !!val; +} + +static const struct clk_ops wiz_phy_en_refclk_ops = { + .enable = wiz_phy_en_refclk_enable, + .disable = wiz_phy_en_refclk_disable, + .is_enabled = wiz_phy_en_refclk_is_enabled, +}; + +static int wiz_phy_en_refclk_register(struct wiz *wiz) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk; + struct device *dev = wiz->dev; + struct clk_init_data *init; + struct clk *clk; + + wiz_phy_en_refclk = devm_kzalloc(dev, sizeof(*wiz_phy_en_refclk), GFP_KERNEL); + if (!wiz_phy_en_refclk) + return -ENOMEM; + + init = &wiz_phy_en_refclk->clk_data; + + init->ops = &wiz_phy_en_refclk_ops; + init->flags = 0; + init->name = output_clk_names[TI_WIZ_PHY_EN_REFCLK]; + + wiz_phy_en_refclk->phy_en_refclk = wiz->phy_en_refclk; + wiz_phy_en_refclk->hw.init = init; + + clk = devm_clk_register(dev, &wiz_phy_en_refclk->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + wiz->output_clks[TI_WIZ_PHY_EN_REFCLK] = clk; + return 0; } @@ -443,7 +620,7 @@ unsigned int val; regmap_field_read(field, &val); - return clk_mux_val_to_index(hw, mux->table, 0, val); + return clk_mux_val_to_index(hw, (u32 *)mux->table, 0, val); } static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index) @@ -461,8 +638,69 @@ .get_parent = wiz_clk_mux_get_parent, }; -static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node, - struct regmap_field *field, u32 *table) +static int wiz_mux_clk_register(struct wiz *wiz, struct regmap_field *field, + const struct wiz_clk_mux_sel *mux_sel, int clk_index) +{ + struct device *dev = wiz->dev; + struct clk_init_data *init; + const char **parent_names; + unsigned int num_parents; + struct wiz_clk_mux *mux; + char clk_name[100]; + struct clk *clk; + int ret = 0, i; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + num_parents = mux_sel->num_parents; + + parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + clk = wiz->input_clks[mux_sel->parents[i]]; + if (IS_ERR_OR_NULL(clk)) { + dev_err(dev, "Failed to get parent clk for %s\n", + output_clk_names[clk_index]); + ret = -EINVAL; + goto err; + } + parent_names[i] = __clk_get_name(clk); + } + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), output_clk_names[clk_index]); + + init = &mux->clk_data; + + init->ops = &wiz_clk_mux_ops; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + init->name = clk_name; + + mux->field = field; + mux->table = mux_sel->table; + mux->hw.init = init; + + clk = devm_clk_register(dev, &mux->hw); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err; + } + + wiz->output_clks[clk_index] = clk; + +err: + kfree(parent_names); + + return ret; +} + +static int wiz_mux_of_clk_register(struct wiz *wiz, struct device_node *node, + struct regmap_field *field, const u32 *table) { struct device *dev = wiz->dev; struct clk_init_data *init; @@ -606,10 +844,16 @@ static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node) { - struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + struct device *dev = wiz->dev; struct device_node *clk_node; int i; + if (wiz->type == AM64_WIZ_10G) { + of_clk_del_provider(dev->of_node); + return; + } + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) { clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name); of_clk_del_provider(clk_node); @@ -621,11 +865,49 @@ of_clk_del_provider(clk_node); of_node_put(clk_node); } + + of_clk_del_provider(wiz->dev->of_node); +} + +static int wiz_clock_register(struct wiz *wiz) +{ + const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + struct device *dev = wiz->dev; + struct device_node *node = dev->of_node; + int clk_index; + int ret; + int i; + + if (wiz->type != AM64_WIZ_10G) + return 0; + + clk_index = TI_WIZ_PLL0_REFCLK; + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++, clk_index++) { + ret = wiz_mux_clk_register(wiz, wiz->mux_sel_field[i], &clk_mux_sel[i], clk_index); + if (ret) { + dev_err(dev, "Failed to register clk: %s\n", output_clk_names[clk_index]); + return ret; + } + } + + ret = wiz_phy_en_refclk_register(wiz); + if (ret) { + dev_err(dev, "Failed to add phy-en-refclk\n"); + return ret; + } + + wiz->clk_data.clks = wiz->output_clks; + wiz->clk_data.clk_num = WIZ_MAX_OUTPUT_CLOCKS; + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &wiz->clk_data); + if (ret) + dev_err(dev, "Failed to add clock provider: %s\n", node->name); + + return ret; } static int wiz_clock_init(struct wiz *wiz, struct device_node *node) { - struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; struct device *dev = wiz->dev; struct device_node *clk_node; const char *node_name; @@ -640,6 +922,7 @@ ret = PTR_ERR(clk); return ret; } + wiz->input_clks[WIZ_CORE_REFCLK] = clk; rate = clk_get_rate(clk); if (rate >= 100000000) @@ -653,6 +936,7 @@ ret = PTR_ERR(clk); return ret; } + wiz->input_clks[WIZ_EXT_REFCLK] = clk; rate = clk_get_rate(clk); if (rate >= 100000000) @@ -660,6 +944,13 @@ else regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2); + if (wiz->type == AM64_WIZ_10G) { + ret = wiz_clock_register(wiz); + if (ret) + dev_err(dev, "Failed to register wiz clocks\n"); + return ret; + } + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) { node_name = clk_mux_sel[i].node_name; clk_node = of_get_child_by_name(node, node_name); @@ -669,8 +960,8 @@ goto err; } - ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field, - clk_mux_sel[i].table); + ret = wiz_mux_of_clk_register(wiz, clk_node, wiz->mux_sel_field[i], + clk_mux_sel[i].table); if (ret) { dev_err(dev, "Failed to register %s clock\n", node_name); @@ -690,7 +981,7 @@ goto err; } - ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field, + ret = wiz_div_clk_register(wiz, clk_node, wiz->div_sel_field[i], clk_div_sel[i].table); if (ret) { dev_err(dev, "Failed to register %s clock\n", @@ -725,6 +1016,17 @@ return ret; } +static int wiz_phy_fullrt_div(struct wiz *wiz, int lane) +{ + if (wiz->type != AM64_WIZ_10G) + return 0; + + if (wiz->lane_phy_type[lane] == PHY_TYPE_PCIE) + return regmap_field_write(wiz->p0_fullrt_div[lane], 0x1); + + return 0; +} + static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) { @@ -748,6 +1050,10 @@ return ret; } + ret = wiz_phy_fullrt_div(wiz, id - 1); + if (ret) + return ret; + if (wiz->lane_phy_type[id - 1] == PHY_TYPE_DP) ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE); else @@ -775,6 +1081,9 @@ { .compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G }, + { + .compatible = "ti,am64-wiz-10g", .data = (void *)AM64_WIZ_10G + }, {} }; MODULE_DEVICE_TABLE(of, wiz_id_table); @@ -793,6 +1102,10 @@ u32 reg, num_lanes = 1, phy_type = PHY_NONE; int ret, i; + if (!(of_node_name_eq(subnode, "phy") || + of_node_name_eq(subnode, "link"))) + continue; + ret = of_property_read_u32(subnode, "reg", ®); if (ret) { dev_err(dev, @@ -819,13 +1132,14 @@ struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct platform_device *serdes_pdev; + bool already_configured = false; struct device_node *child_node; struct regmap *regmap; struct resource res; void __iomem *base; struct wiz *wiz; + int ret, val, i; u32 num_lanes; - int ret; wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL); if (!wiz) @@ -907,14 +1221,14 @@ wiz->dev = dev; wiz->regmap = regmap; wiz->num_lanes = num_lanes; - if (wiz->type == J721E_WIZ_10G) + if (wiz->type == J721E_WIZ_10G || wiz->type == AM64_WIZ_10G) wiz->clk_mux_sel = clk_mux_sel_10g; else wiz->clk_mux_sel = clk_mux_sel_16g; wiz->clk_div_sel = clk_div_sel; - if (wiz->type == J721E_WIZ_10G) + if (wiz->type == J721E_WIZ_10G || wiz->type == AM64_WIZ_10G) wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G; else wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G; @@ -954,10 +1268,20 @@ goto err_get_sync; } - ret = wiz_init(wiz); - if (ret) { - dev_err(dev, "WIZ initialization failed\n"); - goto err_wiz_init; + for (i = 0; i < wiz->num_lanes; i++) { + regmap_field_read(wiz->p_enable[i], &val); + if (val & (P_ENABLE | P_ENABLE_FORCE)) { + already_configured = true; + break; + } + } + + if (!already_configured) { + ret = wiz_init(wiz); + if (ret) { + dev_err(dev, "WIZ initialization failed\n"); + goto err_wiz_init; + } } serdes_pdev = of_platform_device_create(child_node, NULL, dev); diff -Naur --no-dereference a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c --- a/drivers/pinctrl/pinmux.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/pinctrl/pinmux.c 2022-01-06 12:45:53.826318157 -0500 @@ -12,6 +12,7 @@ */ #define pr_fmt(fmt) "pinmux core: " fmt +#include #include #include #include @@ -564,7 +565,7 @@ continue; } - seq_printf(s, "function: %s, groups = [ ", func); + seq_printf(s, "function %d: %s, groups = [ ", func_selector, func); for (i = 0; i < num_groups; i++) seq_printf(s, "%s ", groups[i]); seq_puts(s, "]\n"); @@ -673,13 +674,114 @@ DEFINE_SHOW_ATTRIBUTE(pinmux_functions); DEFINE_SHOW_ATTRIBUTE(pinmux_pins); +#define PINMUX_SELECT_MAX 128 +static ssize_t pinmux_select(struct file *file, const char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct seq_file *sfile = file->private_data; + struct pinctrl_dev *pctldev = sfile->private; + const struct pinmux_ops *pmxops = pctldev->desc->pmxops; + const char *const *groups; + char *buf, *gname, *fname; + unsigned int num_groups; + int fsel, gsel, ret; + + if (len > PINMUX_SELECT_MAX) + return -ENOMEM; + + buf = kzalloc(PINMUX_SELECT_MAX, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = strncpy_from_user(buf, user_buf, PINMUX_SELECT_MAX); + if (ret < 0) + goto exit_free_buf; + buf[len-1] = '\0'; + + /* remove leading and trailing spaces of input buffer */ + gname = strstrip(buf); + if (*gname == '\0') { + ret = -EINVAL; + goto exit_free_buf; + } + + /* find a separator which is a spacelike character */ + for (fname = gname; !isspace(*fname); fname++) { + if (*fname == '\0') { + ret = -EINVAL; + goto exit_free_buf; + } + } + *fname = '\0'; + + /* drop extra spaces between function and group names */ + fname = skip_spaces(fname + 1); + if (*fname == '\0') { + ret = -EINVAL; + goto exit_free_buf; + } + + ret = pinmux_func_name_to_selector(pctldev, fname); + if (ret < 0) { + dev_err(pctldev->dev, "invalid function %s in map table\n", fname); + goto exit_free_buf; + } + fsel = ret; + + ret = pmxops->get_function_groups(pctldev, fsel, &groups, &num_groups); + if (ret) { + dev_err(pctldev->dev, "no groups for function %d (%s)", fsel, fname); + goto exit_free_buf; + } + + ret = match_string(groups, num_groups, gname); + if (ret < 0) { + dev_err(pctldev->dev, "invalid group %s", gname); + goto exit_free_buf; + } + + ret = pinctrl_get_group_selector(pctldev, gname); + if (ret < 0) { + dev_err(pctldev->dev, "failed to get group selector for %s", gname); + goto exit_free_buf; + } + gsel = ret; + + ret = pmxops->set_mux(pctldev, fsel, gsel); + if (ret) { + dev_err(pctldev->dev, "set_mux() failed: %d", ret); + goto exit_free_buf; + } + ret = len; + +exit_free_buf: + kfree(buf); + + return ret; +} + +static int pinmux_select_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + +static const struct file_operations pinmux_select_ops = { + .owner = THIS_MODULE, + .open = pinmux_select_open, + .write = pinmux_select, + .llseek = no_llseek, + .release = single_release, +}; + void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { - debugfs_create_file("pinmux-functions", S_IFREG | S_IRUGO, + debugfs_create_file("pinmux-functions", 0444, devroot, pctldev, &pinmux_functions_fops); - debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO, + debugfs_create_file("pinmux-pins", 0444, devroot, pctldev, &pinmux_pins_fops); + debugfs_create_file("pinmux-select", 0200, + devroot, pctldev, &pinmux_select_ops); } #endif /* CONFIG_DEBUG_FS */ diff -Naur --no-dereference a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c --- a/drivers/regulator/palmas-regulator.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/regulator/palmas-regulator.c 2022-01-06 12:45:53.826318157 -0500 @@ -1016,6 +1016,7 @@ struct palmas_reg_init *reg_init; struct palmas_regs_info *rinfo; struct regulator_desc *desc; + unsigned int reg; for (id = ddata->ldo_begin; id < ddata->max_reg; id++) { if (pdata && pdata->reg_init[id]) @@ -1064,6 +1065,31 @@ TPS65917_LDO1_CTRL_BYPASS_EN; desc->bypass_mask = TPS65917_LDO1_CTRL_BYPASS_EN; + + /* + * OTP Values are set to bypass enable. + * Switch to disable so that use count + * does not go negative while directly + * disabling bypass. + */ + ret = palmas_ldo_read(pmic->palmas, + rinfo->ctrl_addr, ®); + if (ret) { + dev_err(pmic->dev, + "Error reading %s ctrl_addr reg, ret = %d\n", + rinfo->name, ret); + return ret; + } + reg &= ~TPS65917_LDO1_CTRL_BYPASS_EN; + ret = palmas_ldo_write(pmic->palmas, + rinfo->ctrl_addr, reg); + if (ret) { + dev_err(pmic->dev, + "Error disabling bypass mode for %s, ret = %d\n", + rinfo->name, ret); + return ret; + } + } } else { desc->n_voltages = 1; diff -Naur --no-dereference a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c --- a/drivers/remoteproc/da8xx_remoteproc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/da8xx_remoteproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -223,7 +223,7 @@ res->start & DA8XX_RPROC_LOCAL_ADDRESS_MASK; drproc->mem[i].size = resource_size(res); - dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da 0x%x\n", + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %pK da 0x%x\n", mem_names[i], &drproc->mem[i].bus_addr, drproc->mem[i].size, drproc->mem[i].cpu_addr, drproc->mem[i].dev_addr); @@ -347,6 +347,9 @@ goto free_rproc; } + if (rproc_get_id(rproc) < 0) + dev_warn(dev, "device does not have an alias id or platform device id\n"); + return 0; free_rproc: diff -Naur --no-dereference a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig --- a/drivers/remoteproc/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -118,6 +118,7 @@ config KEYSTONE_REMOTEPROC tristate "Keystone Remoteproc support" depends on ARCH_KEYSTONE + depends on UIO help Say Y here here to support Keystone remote processors (DSP) via the remote processor framework. @@ -125,6 +126,18 @@ It's safe to say N here if you're not interested in the Keystone DSPs or just want to use a bare minimum kernel. +config PRU_REMOTEPROC + tristate "TI PRU remoteproc support" + depends on TI_PRUSS + default TI_PRUSS + help + Support for TI PRU remote processors present within a PRU-ICSS + subsystem via the remote processor framework. + + Say Y or M here to support the Programmable Realtime Unit (PRU) + processors on various TI SoCs. It's safe to say N here if you're + not interested in the PRU or if you are unsure. + config QCOM_PIL_INFO tristate @@ -285,6 +298,19 @@ on various TI K3 family of SoCs through the remote processor framework. + It's safe to say N here if you're not interested in utilizing + a slave processor. + +config TI_K3_M4_REMOTEPROC + tristate "TI K3 M4 remoteproc support" + depends on ARCH_K3 + select MAILBOX + select OMAP2PLUS_MBOX + help + Say m here to support TI's M4 remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + It's safe to say N here if you're not interested in utilizing a slave processor. diff -Naur --no-dereference a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c --- a/drivers/remoteproc/keystone_remoteproc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/keystone_remoteproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -2,13 +2,15 @@ /* * TI Keystone DSP remoteproc driver * - * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include +#include #include #include #include +#include #include #include #include @@ -18,24 +20,40 @@ #include #include #include +#include +#include #include +#include + #include "remoteproc_internal.h" +#define DRIVER_UIO_VERSION "0.1" + +#define KEYSTONE_RPROC_MAX_RSC_TABLE SZ_1K #define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1) +/* + * XXX: evaluate if this param needs to be enhanced so that the switch between + * userspace and remoteproc core loaders can be controlled per device. + */ +static bool use_rproc_core_loader; +module_param(use_rproc_core_loader, bool, 0444); + /** * struct keystone_rproc_mem - internal memory structure * @cpu_addr: MPU virtual address of the memory region * @bus_addr: Bus address used to access the memory region * @dev_addr: Device address of the memory region from DSP view * @size: Size of the memory region + * @kobj: kobject for the sysfs directory file */ struct keystone_rproc_mem { void __iomem *cpu_addr; phys_addr_t bus_addr; u32 dev_addr; size_t size; + struct kobject kobj; }; /** @@ -51,6 +69,18 @@ * @irq_fault: irq entry for exception * @kick_gpio: gpio used for virtio kicks * @workqueue: workqueue for processing virtio interrupts + * @misc: misc device structure used to expose fops to user-space + * @uio: uio device information + * @mlock: lock to protect resources in fops + * @lock: lock to protect shared resources within UIO interrupt handlers + * @flags: flags to keep track of UIO interrupt occurrence + * @rsc_table: resource table pointer copied from userspace + * @rsc_table_size: size of resource table + * @loaded_rsc_table: kernel pointer of loaded resource table + * @boot_addr: remote processor boot address used with userspace loader + * @open_count: fops open reference counter + * @use_userspace_loader: flag to denote if driver is configured for userspace + * loader */ struct keystone_rproc { struct device *dev; @@ -64,8 +94,281 @@ int irq_fault; int kick_gpio; struct work_struct workqueue; + struct miscdevice misc; + struct uio_info uio; + struct mutex mlock; /* fops lock */ + spinlock_t lock; /* uio handler lock */ + unsigned long flags; + struct resource_table *rsc_table; + int rsc_table_size; + void *loaded_rsc_table; + u32 boot_addr; + int open_count; + unsigned int use_userspace_loader : 1; +}; + +struct mem_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct keystone_rproc_mem *mem, char *buf); + ssize_t (*store)(struct keystone_rproc_mem *mem, const char *buf, + size_t len); +}; + +static ssize_t mem_addr_show(struct keystone_rproc_mem *mem, char *buf) +{ + return sprintf(buf, "%pa\n", &mem->bus_addr); +} + +static ssize_t mem_size_show(struct keystone_rproc_mem *mem, char *buf) +{ + return sprintf(buf, "0x%016zx\n", mem->size); +} + +static struct mem_sysfs_entry addr_attribute = + __ATTR(addr, 0444, mem_addr_show, NULL); +static struct mem_sysfs_entry size_attribute = + __ATTR(size, 0444, mem_size_show, NULL); + +static struct attribute *attrs[] = { + &addr_attribute.attr, + &size_attribute.attr, + NULL, /* sentinel */ }; +#define to_dsp_mem(m) container_of(m, struct keystone_rproc_mem, kobj) + +static ssize_t mem_type_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct keystone_rproc_mem *mem = to_dsp_mem(kobj); + struct mem_sysfs_entry *entry; + + entry = container_of(attr, struct mem_sysfs_entry, attr); + if (!entry->show) + return -EIO; + + return entry->show(mem, buf); +} + +static const struct sysfs_ops mem_sysfs_ops = { + .show = mem_type_show, +}; + +static struct kobj_type mem_attr_type = { + .sysfs_ops = &mem_sysfs_ops, + .default_attrs = attrs, +}; + +static int keystone_rproc_mem_add_attrs(struct keystone_rproc *ksproc) +{ + int i, ret; + struct keystone_rproc_mem *mem; + struct kobject *kobj_parent = &ksproc->misc.this_device->kobj; + + for (i = 0; i < ksproc->num_mems; i++) { + mem = &ksproc->mem[i]; + kobject_init(&mem->kobj, &mem_attr_type); + ret = kobject_add(&mem->kobj, kobj_parent, "memory%d", i); + if (ret) + goto err_kobj; + ret = kobject_uevent(&mem->kobj, KOBJ_ADD); + if (ret) + goto err_kobj; + } + + return 0; + +err_kobj: + for (; i >= 0; i--) { + mem = &ksproc->mem[i]; + kobject_put(&mem->kobj); + } + return ret; +} + +static void keystone_rproc_mem_del_attrs(struct keystone_rproc *ksproc) +{ + int i; + struct keystone_rproc_mem *mem; + + for (i = 0; i < ksproc->num_mems; i++) { + mem = &ksproc->mem[i]; + kobject_put(&mem->kobj); + } +} + +static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len); + +/* uio handler dealing with userspace controlled exception interrupt */ +static irqreturn_t keystone_rproc_uio_handler(int irq, struct uio_info *uio) +{ + struct keystone_rproc *ksproc = uio->priv; + + spin_lock(&ksproc->lock); + if (!__test_and_set_bit(0, &ksproc->flags)) + disable_irq_nosync(irq); + spin_unlock(&ksproc->lock); + + return IRQ_HANDLED; +} + +/* uio driver interrupt control dealing with exception interrupt */ +static int keystone_rproc_uio_irqcontrol(struct uio_info *uio, s32 irq_on) +{ + struct keystone_rproc *ksproc = uio->priv; + unsigned long flags; + + spin_lock_irqsave(&ksproc->lock, flags); + if (irq_on) { + if (__test_and_clear_bit(0, &ksproc->flags)) + enable_irq(uio->irq); + } else { + if (!__test_and_set_bit(0, &ksproc->flags)) + disable_irq(uio->irq); + } + spin_unlock_irqrestore(&ksproc->lock, flags); + + return 0; +} + +/* Reset previously set rsc table variables */ +static void keystone_rproc_reset_rsc_table(struct keystone_rproc *ksproc) +{ + kfree(ksproc->rsc_table); + ksproc->rsc_table = NULL; + ksproc->loaded_rsc_table = NULL; + ksproc->rsc_table_size = 0; +} + +/* + * Create/delete the virtio devices in kernel once the user-space loading is + * complete, configure the remoteproc states appropriately, and boot or reset + * the remote processor. The resource table should have been published through + * KEYSTONE_RPROC_IOC_SET_RSC_TABLE & KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE + * ioctls before invoking this. The boot address is passed through the + * KEYSTONE_RPROC_IOC_SET_STATE ioctl when setting the KEYSTONE_RPROC_RUNNING + * state. + * + * NOTE: + * The ioctls KEYSTONE_RPROC_IOC_DSP_RESET and KEYSTONE_RPROC_IOC_DSP_BOOT + * are restricted to support the booting or resetting the DSP devices only + * for firmware images without any resource table. + */ +static int keystone_rproc_set_state(struct keystone_rproc *ksproc, + void __user *argp) +{ + struct rproc *rproc = ksproc->rproc; + struct keystone_rproc_set_state_params set_state_params; + int ret = 0; + + if (copy_from_user(&set_state_params, argp, sizeof(set_state_params))) + return -EFAULT; + + switch (set_state_params.state) { + case KEYSTONE_RPROC_RUNNING: + if (!ksproc->rsc_table || !ksproc->loaded_rsc_table) + return -EINVAL; + + /* + * store boot address for .get_boot_addr() rproc fw ops + * XXX: validate the boot address so it is not set to a + * random address + */ + ksproc->boot_addr = set_state_params.boot_addr; + + /* + * invoke rproc_boot to trigger the boot, the resource table + * is parsed during the process and is agnostic of the presence + * or absence of virtio devices + */ + ret = rproc_boot(rproc); + break; + + case KEYSTONE_RPROC_OFFLINE: + if (rproc->state != RPROC_RUNNING) + return -EINVAL; + + /* invoke rproc_shutdown to match rproc_boot */ + rproc_shutdown(rproc); + + mutex_lock(&ksproc->mlock); + keystone_rproc_reset_rsc_table(ksproc); + mutex_unlock(&ksproc->mlock); + + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* Copy the resource table from userspace into kernel */ +static int keystone_rproc_set_rsc_table(struct keystone_rproc *ksproc, + void __user *data) +{ + unsigned long len = 0; + void *rsc_table = NULL; + + if (!data) + return -EFAULT; + + if (copy_from_user(&len, data, sizeof(len))) + return -EFAULT; + + if (len >= KEYSTONE_RPROC_MAX_RSC_TABLE) + return -EOVERFLOW; + + data += sizeof(len); + + rsc_table = kzalloc(len, GFP_KERNEL); + if (!rsc_table) + return -ENOMEM; + + if (copy_from_user(rsc_table, data, len)) + goto error_return; + + mutex_lock(&ksproc->mlock); + + kfree(ksproc->rsc_table); + + ksproc->rsc_table = rsc_table; + ksproc->rsc_table_size = len; + ksproc->loaded_rsc_table = NULL; + + mutex_unlock(&ksproc->mlock); + + return 0; + +error_return: + kfree(rsc_table); + return -EFAULT; +} + +/* + * Store the equivalent kernel virtual address of the loaded resource table in + * device memory. Userspace published the device address of the loaded resource + * table. + */ +static int keystone_rproc_set_loaded_rsc_table(struct keystone_rproc *ksproc, + unsigned int dma_addr) +{ + struct rproc *rproc = ksproc->rproc; + void *ptr; + + if (!ksproc->rsc_table_size || !ksproc->rsc_table) + return -EINVAL; + + ptr = keystone_rproc_da_to_va(rproc, dma_addr, ksproc->rsc_table_size); + if (!ptr) + return -EINVAL; + + ksproc->loaded_rsc_table = ptr; + + return 0; +} + /* Put the DSP processor into reset */ static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc) { @@ -73,7 +376,8 @@ } /* Configure the boot address and boot the DSP processor */ -static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr) +static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, + uint32_t boot_addr) { int ret; @@ -95,6 +399,235 @@ return 0; } +static long +keystone_rproc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct miscdevice *misc = filp->private_data; + struct keystone_rproc *ksproc = + container_of(misc, struct keystone_rproc, misc); + void __user *argp = (void __user *)arg; + int ret = 0; + + dev_dbg(ksproc->dev, "%s: cmd 0x%.8x (%d), arg 0x%lx\n", + __func__, cmd, _IOC_NR(cmd), arg); + + if (_IOC_TYPE(cmd) != KEYSTONE_RPROC_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_NR(cmd) >= KEYSTONE_RPROC_IOC_MAXNR) + return -ENOTTY; + + switch (cmd) { + case KEYSTONE_RPROC_IOC_SET_STATE: + ret = keystone_rproc_set_state(ksproc, argp); + break; + + case KEYSTONE_RPROC_IOC_SET_RSC_TABLE: + ret = keystone_rproc_set_rsc_table(ksproc, argp); + break; + + case KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE: + ret = keystone_rproc_set_loaded_rsc_table(ksproc, arg); + break; + + case KEYSTONE_RPROC_IOC_DSP_RESET: + if (ksproc->rsc_table) { + ret = -EINVAL; + break; + } + + keystone_rproc_dsp_reset(ksproc); + break; + + case KEYSTONE_RPROC_IOC_DSP_BOOT: + if (ksproc->rsc_table) { + ret = -EINVAL; + break; + } + + ret = keystone_rproc_dsp_boot(ksproc, arg); + break; + + default: + ret = -ENOTTY; + break; + } + + if (ret) { + dev_err(ksproc->dev, "error in ioctl call: cmd 0x%.8x (%d), ret %d\n", + cmd, _IOC_NR(cmd), ret); + } + + return ret; +} + +/* + * Map DSP memories into userspace for supporting Userspace loading. + * + * This is a custom mmap function following semantics based on the UIO + * mmap implementation. The vm_pgoff passed in the vma structure is a + * combination of the memory region index and the actual page offset in + * that region. This checks if user request is in valid range before + * providing mmap access. + * + * XXX: Evaluate this approach, as the internal memories can be mapped in + * whole into userspace as they are not super-large, or switch to using + * direct addresses to look more like a traditional implementation. + */ +static int keystone_rproc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct miscdevice *misc = file->private_data; + struct keystone_rproc *ksproc = + container_of(misc, struct keystone_rproc, misc); + size_t size = vma->vm_end - vma->vm_start; + size_t req_offset; + u32 idx; + + idx = vma->vm_pgoff & KEYSTONE_RPROC_UIO_MAP_INDEX_MASK; + + if (idx >= ksproc->num_mems) { + dev_err(ksproc->dev, "invalid mmap region index %d\n", idx); + return -EINVAL; + } + + req_offset = (vma->vm_pgoff - idx) << PAGE_SHIFT; + if (req_offset + size < req_offset) { + dev_err(ksproc->dev, "invalid request - overflow, mmap offset = 0x%zx size 0x%zx region %d\n", + req_offset, size, idx); + return -EINVAL; + } + + if ((req_offset + size) > ksproc->mem[idx].size) { + dev_err(ksproc->dev, "invalid request - out of range, mmap offset 0x%zx size 0x%zx region %d\n", + req_offset, size, idx); + return -EINVAL; + } + + vma->vm_page_prot = + phys_mem_access_prot(file, + (ksproc->mem[idx].bus_addr >> PAGE_SHIFT) + + (vma->vm_pgoff - idx), size, + vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + (ksproc->mem[idx].bus_addr >> PAGE_SHIFT) + + (vma->vm_pgoff - idx), size, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static int keystone_rproc_open(struct inode *inode, struct file *file) +{ + struct miscdevice *misc = file->private_data; + struct keystone_rproc *ksproc = + container_of(misc, struct keystone_rproc, misc); + + mutex_lock(&ksproc->mlock); + ksproc->open_count++; + mutex_unlock(&ksproc->mlock); + + return 0; +} + +static int keystone_rproc_release(struct inode *inode, struct file *filp) +{ + struct miscdevice *misc = filp->private_data; + struct keystone_rproc *ksproc = + container_of(misc, struct keystone_rproc, misc); + struct rproc *rproc = ksproc->rproc; + + mutex_lock(&ksproc->mlock); + + if ((WARN_ON(ksproc->open_count == 0))) + goto end; + + if (--ksproc->open_count > 0) + goto end; + + if (rproc->state != RPROC_OFFLINE) { + rproc_shutdown(rproc); + WARN_ON(rproc->state != RPROC_OFFLINE); + } + + keystone_rproc_reset_rsc_table(ksproc); + +end: + mutex_unlock(&ksproc->mlock); + return 0; +} + +/* + * File operations exposed through a miscdevice for supporting + * the userspace loader/boot mechanism. + */ +static const struct file_operations keystone_rproc_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = keystone_rproc_ioctl, + .mmap = keystone_rproc_mmap, + .open = keystone_rproc_open, + .release = keystone_rproc_release, +}; + +/* + * Used only with userspace loader/boot mechanism, the parsing of the firmware + * is done in userspace, and a copy of the resource table is added for the + * kernel-level access through an ioctl. Create the remoteproc cached table + * using this resource table and configure the table pointer and table size + * accordingly to allow the remoteproc core to process the resource table for + * creating the vrings and traces. + */ +static int keystone_rproc_load_rsc_table(struct rproc *rproc, + const struct firmware *fw) +{ + struct keystone_rproc *ksproc = rproc->priv; + + rproc->cached_table = kmemdup(ksproc->rsc_table, ksproc->rsc_table_size, + GFP_KERNEL); + if (!rproc->cached_table) + return -ENOMEM; + + rproc->table_ptr = rproc->cached_table; + rproc->table_sz = ksproc->rsc_table_size; + + return 0; +} + +/* + * Used only with userspace loader/boot mechanism, the device address of the + * loaded resource table is published to the kernel-level through an ioctl + * at which point the equivalent kernel virtual pointer is stored in a local + * variable in the keystone_rproc device structure. Return this kernel pointer + * to the remoteproc core for runtime publishing/modification of the resource + * table entries. + * + * NOTE: Only loaded resource tables in the DSP internal memories is supported + * at present. + */ +static struct resource_table * +keystone_rproc_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw) +{ + struct keystone_rproc *ksproc = rproc->priv; + + return ksproc->loaded_rsc_table; +} + +/* + * Used only with userspace loader/boot mechanism, the boot address + * is published to the kernel-level through an ioctl call and is + * stored in a local variable in the keystone_rproc device structure. + * Return this address to the remoteproc core through the .get_boot_addr() + * remoteproc firmware ops + */ +static u64 keystone_rproc_get_boot_addr(struct rproc *rproc, + const struct firmware *fw) +{ + struct keystone_rproc *ksproc = rproc->priv; + + return ksproc->boot_addr; +} + /* * Process the remoteproc exceptions * @@ -104,7 +637,9 @@ * generation register. * * This function just invokes the rproc_report_crash to report the exception - * to the remoteproc driver core, to trigger a recovery. + * to the remoteproc driver core, to trigger a recovery. This is the case + * only when using in-kernel remoteproc core loader/boot mechanism, and is + * handled through an UIO interrupt otherwise. */ static irqreturn_t keystone_rproc_exception_interrupt(int irq, void *dev_id) { @@ -164,7 +699,11 @@ * * This function will be invoked only after the firmware for this rproc * was loaded, parsed successfully, and all of its resource requirements - * were met. + * were met. The function skips releasing the processor from reset and + * registering for the exception interrupt if using the userspace controlled + * load/boot mechanism. The processor will be started through an ioctl when + * controlled from userspace, but the virtio interrupt still is handled at + * the kernel layer. */ static int keystone_rproc_start(struct rproc *rproc) { @@ -181,12 +720,15 @@ goto out; } - ret = request_irq(ksproc->irq_fault, keystone_rproc_exception_interrupt, - 0, dev_name(ksproc->dev), ksproc); - if (ret) { - dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n", - ret); - goto free_vring_irq; + if (!ksproc->use_userspace_loader) { + ret = request_irq(ksproc->irq_fault, + keystone_rproc_exception_interrupt, 0, + dev_name(ksproc->dev), ksproc); + if (ret) { + dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n", + ret); + goto free_vring_irq; + } } ret = keystone_rproc_dsp_boot(ksproc, rproc->bootaddr); @@ -208,24 +750,29 @@ * Stop the DSP remote processor. * * This function puts the DSP processor into reset, and finishes processing - * of any pending messages. + * of any pending messages. The reset procedure is completed only if using + * kernel-mode remoteproc loading/booting mechanism, it is handled outside + * if using userspace load/boot mechanism either through an ioctl, or when + * the handle to the device is closed without triggering a reset. */ static int keystone_rproc_stop(struct rproc *rproc) { struct keystone_rproc *ksproc = rproc->priv; - keystone_rproc_dsp_reset(ksproc); - free_irq(ksproc->irq_fault, ksproc); + if (!ksproc->use_userspace_loader) { + keystone_rproc_dsp_reset(ksproc); + free_irq(ksproc->irq_fault, ksproc); + } + free_irq(ksproc->irq_ring, ksproc); flush_work(&ksproc->workqueue); - return 0; } /* * Kick the remote processor to notify about pending unprocessed messages. * The vqid usage is not used and is inconsequential, as the kick is performed - * through a simulated GPIO (a bit in an IPC interrupt-triggering register), + * through a simulated GPIO (an bit in an IPC interrupt-triggering register), * the remote processor is expected to process both its Tx and Rx virtqueues. */ static void keystone_rproc_kick(struct rproc *rproc, int vqid) @@ -363,8 +910,11 @@ struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct keystone_rproc *ksproc; + struct miscdevice *misc; + struct uio_info *uio; struct rproc *rproc; int dsp_id; + char *uio_name = NULL; char *fw_name = NULL; char *template = "keystone-dsp%d-fw"; int name_len = 0; @@ -381,6 +931,13 @@ return dsp_id; } + /* construct a name for uio devices - assuming a single digit alias */ + name_len = strlen("dsp%d"); + uio_name = devm_kzalloc(dev, name_len, GFP_KERNEL); + if (!uio_name) + return -ENOMEM; + snprintf(uio_name, name_len, "dsp%d", dsp_id); + /* construct a custom default fw name - subject to change in future */ name_len = strlen(template); /* assuming a single digit alias */ fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL); @@ -394,9 +951,32 @@ return -ENOMEM; rproc->has_iommu = false; + ksproc = rproc->priv; ksproc->rproc = rproc; ksproc->dev = dev; + ksproc->use_userspace_loader = !use_rproc_core_loader; + + /* + * customize the remoteproc core config flags and ELF fw ops for + * userspace loader/boot mechanism + */ + if (ksproc->use_userspace_loader) { + rproc->recovery_disabled = true; + rproc->auto_boot = false; + rproc->skip_firmware_load = true; + rproc->deny_sysfs_ops = true; + + rproc->ops->parse_fw = keystone_rproc_load_rsc_table; + rproc->ops->find_loaded_rsc_table = + keystone_rproc_find_loaded_rsc_table; + rproc->ops->get_boot_addr = keystone_rproc_get_boot_addr; + rproc->ops->sanity_check = NULL; + rproc->ops->load = NULL; + } + + mutex_init(&ksproc->mlock); + spin_lock_init(&ksproc->lock); ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc); if (ret) @@ -463,8 +1043,52 @@ platform_set_drvdata(pdev, ksproc); + if (ksproc->use_userspace_loader) { + uio = &ksproc->uio; + uio->name = uio_name; + uio->version = DRIVER_UIO_VERSION; + uio->irq = ksproc->irq_fault; + uio->priv = ksproc; + uio->handler = keystone_rproc_uio_handler; + uio->irqcontrol = keystone_rproc_uio_irqcontrol; + ret = uio_register_device(dev, uio); + if (ret) { + dev_err(dev, "failed to register uio device, status = %d\n", + ret); + goto del_rproc; + } + dev_dbg(dev, "registered uio device %s\n", uio->name); + + misc = &ksproc->misc; + misc->minor = MISC_DYNAMIC_MINOR; + misc->name = uio->name; + misc->fops = &keystone_rproc_fops; + misc->parent = dev; + ret = misc_register(misc); + if (ret) { + dev_err(dev, "failed to register misc device, status = %d\n", + ret); + goto unregister_uio; + } + + ret = keystone_rproc_mem_add_attrs(ksproc); + if (ret) { + dev_err(ksproc->dev, "error creating sysfs files (%d)\n", + ret); + goto unregister_misc; + } + + dev_dbg(dev, "registered misc device %s\n", misc->name); + } + return 0; +unregister_misc: + misc_deregister(misc); +unregister_uio: + uio_unregister_device(uio); +del_rproc: + rproc_del(rproc); release_mem: of_reserved_mem_device_release(dev); disable_clk: @@ -480,6 +1104,11 @@ { struct keystone_rproc *ksproc = platform_get_drvdata(pdev); + if (ksproc->use_userspace_loader) { + keystone_rproc_mem_del_attrs(ksproc); + misc_deregister(&ksproc->misc); + uio_unregister_device(&ksproc->uio); + } rproc_del(ksproc->rproc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -507,8 +1136,22 @@ }, }; -module_platform_driver(keystone_rproc_driver); +static int __init keystone_rproc_init(void) +{ + keystone_rproc_driver.driver.suppress_bind_attrs = + !use_rproc_core_loader; + + return platform_driver_register(&keystone_rproc_driver); +} +module_init(keystone_rproc_init); + +static void __exit keystone_rproc_exit(void) +{ + platform_driver_unregister(&keystone_rproc_driver); +} +module_exit(keystone_rproc_exit); MODULE_AUTHOR("Suman Anna "); +MODULE_AUTHOR("Sam Nelson "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TI Keystone DSP Remoteproc driver"); diff -Naur --no-dereference a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile --- a/drivers/remoteproc/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -18,6 +18,7 @@ obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o +obj-$(CONFIG_PRU_REMOTEPROC) += pru_rproc.o obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil_info.o obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o @@ -34,3 +35,4 @@ obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o +obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o diff -Naur --no-dereference a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c --- a/drivers/remoteproc/pru_rproc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/remoteproc/pru_rproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PRU-ICSS remoteproc driver for various TI SoCs + * + * Copyright (C) 2014-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author(s): + * Suman Anna + * Andrew F. Davis + * Grzegorz Jaszczyk for Texas Instruments + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" +#include "remoteproc_elf_helpers.h" +#include "pru_rproc.h" + +/* PRU_ICSS_PRU_CTRL registers */ +#define PRU_CTRL_CTRL 0x0000 +#define PRU_CTRL_STS 0x0004 +#define PRU_CTRL_WAKEUP_EN 0x0008 +#define PRU_CTRL_CYCLE 0x000C +#define PRU_CTRL_STALL 0x0010 +#define PRU_CTRL_CTBIR0 0x0020 +#define PRU_CTRL_CTBIR1 0x0024 +#define PRU_CTRL_CTPPR0 0x0028 +#define PRU_CTRL_CTPPR1 0x002C + +/* CTRL register bit-fields */ +#define CTRL_CTRL_SOFT_RST_N BIT(0) +#define CTRL_CTRL_EN BIT(1) +#define CTRL_CTRL_SLEEPING BIT(2) +#define CTRL_CTRL_CTR_EN BIT(3) +#define CTRL_CTRL_SINGLE_STEP BIT(8) +#define CTRL_CTRL_RUNSTATE BIT(15) + +/* PRU_ICSS_PRU_DEBUG registers */ +#define PRU_DEBUG_GPREG(x) (0x0000 + (x) * 4) +#define PRU_DEBUG_CT_REG(x) (0x0080 + (x) * 4) + +/* PRU/RTU/Tx_PRU Core IRAM address masks */ +#define PRU_IRAM_ADDR_MASK 0x3ffff +#define PRU0_IRAM_ADDR_MASK 0x34000 +#define PRU1_IRAM_ADDR_MASK 0x38000 +#define RTU0_IRAM_ADDR_MASK 0x4000 +#define RTU1_IRAM_ADDR_MASK 0x6000 +#define TX_PRU0_IRAM_ADDR_MASK 0xa000 +#define TX_PRU1_IRAM_ADDR_MASK 0xc000 + +/* PRU device addresses for various type of PRU RAMs */ +#define PRU_IRAM_DA 0 /* Instruction RAM */ +#define PRU_PDRAM_DA 0 /* Primary Data RAM */ +#define PRU_SDRAM_DA 0x2000 /* Secondary Data RAM */ +#define PRU_SHRDRAM_DA 0x10000 /* Shared Data RAM */ + +#define MAX_PRU_SYS_EVENTS 160 + +/** + * enum pru_iomem - PRU core memory/register range identifiers + * + * @PRU_IOMEM_IRAM: PRU Instruction RAM range + * @PRU_IOMEM_CTRL: PRU Control register range + * @PRU_IOMEM_DEBUG: PRU Debug register range + * @PRU_IOMEM_MAX: just keep this one at the end + */ +enum pru_iomem { + PRU_IOMEM_IRAM = 0, + PRU_IOMEM_CTRL, + PRU_IOMEM_DEBUG, + PRU_IOMEM_MAX, +}; + +/** + * enum pru_type - PRU core type identifier + * + * @PRU_TYPE_PRU: Programmable Real-time Unit + * @PRU_TYPE_RTU: Auxiliary Programmable Real-Time Unit + * @PRU_TYPE_TX_PRU: Transmit Programmable Real-Time Unit + * @PRU_TYPE_MAX: just keep this one at the end + */ +enum pru_type { + PRU_TYPE_PRU = 0, + PRU_TYPE_RTU, + PRU_TYPE_TX_PRU, + PRU_TYPE_MAX, +}; + +/** + * struct pru_private_data - device data for a PRU core + * @type: type of the PRU core (PRU, RTU, Tx_PRU) + * @is_k3: flag used to identify the need for special load handling + */ +struct pru_private_data { + enum pru_type type; + unsigned int is_k3 : 1; +}; + +/** + * struct pru_rproc - PRU remoteproc structure + * @id: id of the PRU core within the PRUSS + * @dev: PRU core device pointer + * @pruss: back-reference to parent PRUSS structure + * @rproc: remoteproc pointer for this PRU core + * @data: PRU core specific data + * @mem_regions: data for each of the PRU memory regions + * @client_np: client device node + * @lock: mutex to protect client usage + * @fw_name: name of firmware image used during loading + * @mapped_irq: virtual interrupt numbers of created fw specific mapping + * @pru_interrupt_map: pointer to interrupt mapping description (firmware) + * @pru_interrupt_map_sz: pru_interrupt_map size + * @rmw_lock: lock for read, modify, write operations on registers + * @irq_vring: IRQ number to use for processing vring buffers + * @dbg_single_step: debug state variable to set PRU into single step mode + * @dbg_continuous: debug state variable to restore PRU execution mode + * @evt_count: number of mapped events + * @gpmux_save: saved value for gpmux config + */ +struct pru_rproc { + int id; + struct device *dev; + struct pruss *pruss; + struct rproc *rproc; + const struct pru_private_data *data; + struct pruss_mem_region mem_regions[PRU_IOMEM_MAX]; + struct device_node *client_np; + struct mutex lock; /* client access lock */ + const char *fw_name; + unsigned int *mapped_irq; + struct pru_irq_rsc *pru_interrupt_map; + size_t pru_interrupt_map_sz; + spinlock_t rmw_lock; /* register access lock */ + int irq_vring; + u32 dbg_single_step; + u32 dbg_continuous; + u8 evt_count; + u8 gpmux_save; +}; + +static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg) +{ + return readl_relaxed(pru->mem_regions[PRU_IOMEM_CTRL].va + reg); +} + +static inline +void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val) +{ + writel_relaxed(val, pru->mem_regions[PRU_IOMEM_CTRL].va + reg); +} + +static inline +void pru_control_set_reg(struct pru_rproc *pru, unsigned int reg, + u32 mask, u32 set) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&pru->rmw_lock, flags); + + val = pru_control_read_reg(pru, reg); + val &= ~mask; + val |= (set & mask); + pru_control_write_reg(pru, reg, val); + + spin_unlock_irqrestore(&pru->rmw_lock, flags); +} + +/** + * pru_rproc_set_firmware() - set firmware for a pru core + * @rproc: the rproc instance of the PRU + * @fw_name: the new firmware name, or NULL if default is desired + * + * Return: 0 on success, or errno in error case. + */ +static int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name) +{ + struct pru_rproc *pru = rproc->priv; + + if (!fw_name) + fw_name = pru->fw_name; + + return rproc_set_firmware(rproc, fw_name); +} + +static struct rproc *__pru_rproc_get(struct device_node *np, int index) +{ + struct device_node *rproc_np = NULL; + struct platform_device *pdev; + struct rproc *rproc; + + rproc_np = of_parse_phandle(np, "ti,prus", index); + if (!rproc_np || !of_device_is_available(rproc_np)) + return ERR_PTR(-ENODEV); + + pdev = of_find_device_by_node(rproc_np); + of_node_put(rproc_np); + + if (!pdev) + /* probably PRU not yet probed */ + return ERR_PTR(-EPROBE_DEFER); + + /* make sure it is PRU rproc */ + if (!is_pru_rproc(&pdev->dev)) { + put_device(&pdev->dev); + return ERR_PTR(-ENODEV); + } + + rproc = platform_get_drvdata(pdev); + put_device(&pdev->dev); + if (!rproc) + return ERR_PTR(-EPROBE_DEFER); + + get_device(&rproc->dev); + + return rproc; +} + +/** + * pru_rproc_get() - get the PRU rproc instance from a device node + * @np: the user/client device node + * @index: index to use for the ti,prus property + * @pru_id: optional pointer to return the PRU remoteproc processor id + * + * This function looks through a client device node's "ti,prus" property at + * index @index and returns the rproc handle for a valid PRU remote processor if + * found. The function allows only one user to own the PRU rproc resource at a + * time. Caller must call pru_rproc_put() when done with using the rproc, not + * required if the function returns a failure. + * + * When optional @pru_id pointer is passed the PRU remoteproc processor id is + * returned. + * + * Return: rproc handle on success, and an ERR_PTR on failure using one + * of the following error values + * -ENODEV if device is not found + * -EBUSY if PRU is already acquired by anyone + * -EPROBE_DEFER is PRU device is not probed yet + */ +struct rproc *pru_rproc_get(struct device_node *np, int index, + enum pruss_pru_id *pru_id) +{ + struct rproc *rproc; + struct pru_rproc *pru; + const char *fw_name; + struct device *dev; + int ret; + u32 mux; + + rproc = __pru_rproc_get(np, index); + if (IS_ERR(rproc)) + return rproc; + + pru = rproc->priv; + dev = &rproc->dev; + + mutex_lock(&pru->lock); + + if (pru->client_np) { + mutex_unlock(&pru->lock); + put_device(dev); + return ERR_PTR(-EBUSY); + } + + pru->client_np = np; + rproc->deny_sysfs_ops = true; + + mutex_unlock(&pru->lock); + + ret = pruss_cfg_get_gpmux(pru->pruss, pru->id, &pru->gpmux_save); + if (ret) { + dev_err(dev, "failed to get cfg gpmux: %d\n", ret); + goto err; + } + + ret = of_property_read_u32_index(np, "ti,pruss-gp-mux-sel", index, + &mux); + if (!ret) { + ret = pruss_cfg_set_gpmux(pru->pruss, pru->id, mux); + if (ret) { + dev_err(dev, "failed to set cfg gpmux: %d\n", ret); + goto err; + } + } + + ret = of_property_read_string_index(np, "firmware-name", index, + &fw_name); + if (!ret) { + ret = pru_rproc_set_firmware(rproc, fw_name); + if (ret) { + dev_err(dev, "failed to set firmware: %d\n", ret); + goto err; + } + } + + if (pru_id) + *pru_id = pru->id; + + return rproc; + +err: + pru_rproc_put(rproc); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(pru_rproc_get); + +/** + * pru_rproc_put() - release the PRU rproc resource + * @rproc: the rproc resource to release + * + * Releases the PRU rproc resource and makes it available to other + * users. + */ +void pru_rproc_put(struct rproc *rproc) +{ + struct pru_rproc *pru; + + if (IS_ERR_OR_NULL(rproc) || !is_pru_rproc(rproc->dev.parent)) + return; + + pru = rproc->priv; + if (!pru->client_np) + return; + + pruss_cfg_set_gpmux(pru->pruss, pru->id, pru->gpmux_save); + + pru_rproc_set_firmware(rproc, NULL); + + mutex_lock(&pru->lock); + pru->client_np = NULL; + rproc->deny_sysfs_ops = false; + mutex_unlock(&pru->lock); + + put_device(&rproc->dev); +} +EXPORT_SYMBOL_GPL(pru_rproc_put); + +/** + * pru_rproc_set_ctable() - set the constant table index for the PRU + * @rproc: the rproc instance of the PRU + * @c: constant table index to set + * @addr: physical address to set it to + * + * Return: 0 on success, or errno in error case. + */ +int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr) +{ + struct pru_rproc *pru = rproc->priv; + unsigned int reg; + u32 mask, set; + u16 idx; + u16 idx_mask; + + if (IS_ERR_OR_NULL(rproc)) + return -EINVAL; + + if (!rproc->dev.parent || !is_pru_rproc(rproc->dev.parent)) + return -ENODEV; + + /* pointer is 16 bit and index is 8-bit so mask out the rest */ + idx_mask = (c >= PRU_C28) ? 0xFFFF : 0xFF; + + /* ctable uses bit 8 and upwards only */ + idx = (addr >> 8) & idx_mask; + + /* configurable ctable (i.e. C24) starts at PRU_CTRL_CTBIR0 */ + reg = PRU_CTRL_CTBIR0 + 4 * (c >> 1); + mask = idx_mask << (16 * (c & 1)); + set = idx << (16 * (c & 1)); + + pru_control_set_reg(pru, reg, mask, set); + + return 0; +} +EXPORT_SYMBOL_GPL(pru_rproc_set_ctable); + +static inline u32 pru_debug_read_reg(struct pru_rproc *pru, unsigned int reg) +{ + return readl_relaxed(pru->mem_regions[PRU_IOMEM_DEBUG].va + reg); +} + +static int regs_show(struct seq_file *s, void *data) +{ + struct rproc *rproc = s->private; + struct pru_rproc *pru = rproc->priv; + int i, nregs = 32; + u32 pru_sts; + int pru_is_running; + + seq_puts(s, "============== Control Registers ==============\n"); + seq_printf(s, "CTRL := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_CTRL)); + pru_sts = pru_control_read_reg(pru, PRU_CTRL_STS); + seq_printf(s, "STS (PC) := 0x%08x (0x%08x)\n", pru_sts, pru_sts << 2); + seq_printf(s, "WAKEUP_EN := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_WAKEUP_EN)); + seq_printf(s, "CYCLE := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_CYCLE)); + seq_printf(s, "STALL := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_STALL)); + seq_printf(s, "CTBIR0 := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_CTBIR0)); + seq_printf(s, "CTBIR1 := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_CTBIR1)); + seq_printf(s, "CTPPR0 := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_CTPPR0)); + seq_printf(s, "CTPPR1 := 0x%08x\n", + pru_control_read_reg(pru, PRU_CTRL_CTPPR1)); + + seq_puts(s, "=============== Debug Registers ===============\n"); + pru_is_running = pru_control_read_reg(pru, PRU_CTRL_CTRL) & + CTRL_CTRL_RUNSTATE; + if (pru_is_running) { + seq_puts(s, "PRU is executing, cannot print/access debug registers.\n"); + return 0; + } + + for (i = 0; i < nregs; i++) { + seq_printf(s, "GPREG%-2d := 0x%08x\tCT_REG%-2d := 0x%08x\n", + i, pru_debug_read_reg(pru, PRU_DEBUG_GPREG(i)), + i, pru_debug_read_reg(pru, PRU_DEBUG_CT_REG(i))); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(regs); + +/* + * Control PRU single-step mode + * + * This is a debug helper function used for controlling the single-step + * mode of the PRU. The PRU Debug registers are not accessible when the + * PRU is in RUNNING state. + * + * Writing a non-zero value sets the PRU into single-step mode irrespective + * of its previous state. The PRU mode is saved only on the first set into + * a single-step mode. Writing a zero value will restore the PRU into its + * original mode. + */ +static int pru_rproc_debug_ss_set(void *data, u64 val) +{ + struct rproc *rproc = data; + struct pru_rproc *pru = rproc->priv; + u32 reg_val; + + val = val ? 1 : 0; + if (!val && !pru->dbg_single_step) + return 0; + + reg_val = pru_control_read_reg(pru, PRU_CTRL_CTRL); + + if (val && !pru->dbg_single_step) + pru->dbg_continuous = reg_val; + + if (val) + reg_val |= CTRL_CTRL_SINGLE_STEP | CTRL_CTRL_EN; + else + reg_val = pru->dbg_continuous; + + pru->dbg_single_step = val; + pru_control_write_reg(pru, PRU_CTRL_CTRL, reg_val); + + return 0; +} + +static int pru_rproc_debug_ss_get(void *data, u64 *val) +{ + struct rproc *rproc = data; + struct pru_rproc *pru = rproc->priv; + + *val = pru->dbg_single_step; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get, + pru_rproc_debug_ss_set, "%llu\n"); + +/* + * Create PRU-specific debugfs entries + * + * The entries are created only if the parent remoteproc debugfs directory + * exists, and will be cleaned up by the remoteproc core. + */ +static void pru_rproc_create_debug_entries(struct rproc *rproc) +{ + if (!rproc->dbg_dir) + return; + + debugfs_create_file("regs", 0400, rproc->dbg_dir, + rproc, ®s_fops); + debugfs_create_file("single_step", 0600, rproc->dbg_dir, + rproc, &pru_rproc_debug_ss_fops); +} + +static void pru_dispose_irq_mapping(struct pru_rproc *pru) +{ + if (!pru->mapped_irq) + return; + + while (pru->evt_count) { + pru->evt_count--; + if (pru->mapped_irq[pru->evt_count] > 0) + irq_dispose_mapping(pru->mapped_irq[pru->evt_count]); + } + + kfree(pru->mapped_irq); + pru->mapped_irq = NULL; +} + +/* + * pru_rproc_vring_interrupt() - interrupt handler for processing vrings + * @irq: irq number associated with the PRU event MPU is listening on + * @data: interrupt handler data, will be a PRU rproc structure + * + * This handler is used by the PRU remoteproc driver when using PRU system + * events for processing the virtqueues. Unlike the mailbox IP, there is + * no payload associated with an interrupt, so either a unique event is + * used for each virtqueue kick, or both virtqueues are processed on a + * single event. The latter is chosen to conserve the usable PRU system + * events. + */ +static irqreturn_t pru_rproc_vring_interrupt(int irq, void *data) +{ + struct pru_rproc *pru = data; + + dev_dbg(&pru->rproc->dev, "got vring irq\n"); + + /* process incoming buffers on both the Rx and Tx vrings */ + rproc_vq_interrupt(pru->rproc, 0); + rproc_vq_interrupt(pru->rproc, 1); + + return IRQ_HANDLED; +} + +/* Kick a virtqueue. */ +static void pru_rproc_kick(struct rproc *rproc, int vq_id) +{ + struct device *dev = &rproc->dev; + struct pru_rproc *pru = rproc->priv; + int ret; + const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; + + if (list_empty(&pru->rproc->rvdevs)) + return; + + dev_dbg(dev, "kicking vqid %d on %s%d\n", vq_id, + names[pru->data->type], pru->id); + + ret = irq_set_irqchip_state(pru->mapped_irq[0], IRQCHIP_STATE_PENDING, true); + if (ret < 0) + dev_err(dev, "pruss_intc_trigger failed: %d\n", ret); +} + +/* Register vring irq handler if needed. */ +static int pru_vring_interrupt_setup(struct rproc *rproc) +{ + struct device *dev = &rproc->dev; + struct pru_rproc *pru = rproc->priv; + struct platform_device *pdev = to_platform_device(pru->dev); + int ret; + + if (list_empty(&pru->rproc->rvdevs)) + return 0; + + /* get vring interrupts for supporting virtio rpmsg */ + pru->irq_vring = platform_get_irq_byname(pdev, "vring"); + if (pru->irq_vring <= 0) { + ret = pru->irq_vring; + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get vring interrupt, status = %d\n", + ret); + + return ret; + } + + ret = request_threaded_irq(pru->irq_vring, NULL, + pru_rproc_vring_interrupt, IRQF_ONESHOT, + dev_name(dev), pru); + if (ret) { + dev_err(dev, "failed to register vring irq handler: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Parse the custom PRU interrupt map resource and configure the INTC + * appropriately. + */ +static int pru_handle_intrmap(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct pru_rproc *pru = rproc->priv; + struct pru_irq_rsc *rsc = pru->pru_interrupt_map; + struct irq_fwspec fwspec; + struct device_node *parent, *irq_parent; + int i, ret = 0; + + /* not having pru_interrupt_map is not an error */ + if (!rsc) + return 0; + + /* currently supporting only type 0 */ + if (rsc->type != 0) { + dev_err(dev, "unsupported rsc type: %d\n", rsc->type); + return -EINVAL; + } + + if (rsc->num_evts > MAX_PRU_SYS_EVENTS) + return -EINVAL; + + if (sizeof(*rsc) + rsc->num_evts * sizeof(struct pruss_int_map) != + pru->pru_interrupt_map_sz) + return -EINVAL; + + pru->evt_count = rsc->num_evts; + pru->mapped_irq = kcalloc(pru->evt_count, sizeof(unsigned int), + GFP_KERNEL); + if (!pru->mapped_irq) { + pru->evt_count = 0; + return -ENOMEM; + } + + /* + * parse and fill in system event to interrupt channel and + * channel-to-host mapping. The interrupt controller to be used + * for these mappings for a given PRU remoteproc is always its + * corresponding sibling PRUSS INTC node. + */ + parent = of_get_parent(dev_of_node(pru->dev)); + if (!parent) { + kfree(pru->mapped_irq); + pru->mapped_irq = NULL; + pru->evt_count = 0; + return -ENODEV; + } + + irq_parent = of_get_child_by_name(parent, "interrupt-controller"); + of_node_put(parent); + if (!irq_parent) { + kfree(pru->mapped_irq); + pru->mapped_irq = NULL; + pru->evt_count = 0; + return -ENODEV; + } + + fwspec.fwnode = of_node_to_fwnode(irq_parent); + fwspec.param_count = 3; + for (i = 0; i < pru->evt_count; i++) { + fwspec.param[0] = rsc->pru_intc_map[i].event; + fwspec.param[1] = rsc->pru_intc_map[i].chnl; + fwspec.param[2] = rsc->pru_intc_map[i].host; + + dev_dbg(dev, "mapping%d: event %d, chnl %d, host %d\n", + i, fwspec.param[0], fwspec.param[1], fwspec.param[2]); + + pru->mapped_irq[i] = irq_create_fwspec_mapping(&fwspec); + if (!pru->mapped_irq[i]) { + dev_err(dev, "failed to get virq for fw mapping %d: event %d chnl %d host %d\n", + i, fwspec.param[0], fwspec.param[1], + fwspec.param[2]); + ret = -EINVAL; + goto map_fail; + } + } + of_node_put(irq_parent); + + return ret; + +map_fail: + pru_dispose_irq_mapping(pru); + of_node_put(irq_parent); + + return ret; +} + +static int pru_rproc_start(struct rproc *rproc) +{ + struct device *dev = &rproc->dev; + struct pru_rproc *pru = rproc->priv; + const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; + u32 val; + int ret; + + dev_dbg(dev, "starting %s%d: entry-point = 0x%llx\n", + names[pru->data->type], pru->id, (rproc->bootaddr >> 2)); + + ret = pru_handle_intrmap(rproc); + /* + * reset references to pru interrupt map - they will stop being valid + * after rproc_start returns + */ + pru->pru_interrupt_map = NULL; + pru->pru_interrupt_map_sz = 0; + if (ret) + return ret; + + ret = pru_vring_interrupt_setup(rproc); + if (ret) + goto fail; + + val = CTRL_CTRL_EN | ((rproc->bootaddr >> 2) << 16); + pru_control_write_reg(pru, PRU_CTRL_CTRL, val); + + return 0; + +fail: + /* dispose irq mapping - new firmware can provide new mapping */ + if (pru->mapped_irq) + pru_dispose_irq_mapping(pru); + + return ret; +} + +static int pru_rproc_stop(struct rproc *rproc) +{ + struct device *dev = &rproc->dev; + struct pru_rproc *pru = rproc->priv; + const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; + u32 val; + + dev_dbg(dev, "stopping %s%d\n", names[pru->data->type], pru->id); + + val = pru_control_read_reg(pru, PRU_CTRL_CTRL); + val &= ~CTRL_CTRL_EN; + pru_control_write_reg(pru, PRU_CTRL_CTRL, val); + + if (!list_empty(&pru->rproc->rvdevs) && pru->irq_vring > 0) + free_irq(pru->irq_vring, pru); + + /* dispose irq mapping - new firmware can provide new mapping */ + pru_dispose_irq_mapping(pru); + + /* dispose vring mapping as well */ + if (pru->irq_vring > 0) + irq_dispose_mapping(pru->irq_vring); + + return 0; +} + +/* + * Convert PRU device address (data spaces only) to kernel virtual address. + * + * Each PRU has access to all data memories within the PRUSS, accessible at + * different ranges. So, look through both its primary and secondary Data + * RAMs as well as any shared Data RAM to convert a PRU device address to + * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data + * RAM1 is primary Data RAM for PRU1. + */ +static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, size_t len) +{ + struct pruss_mem_region dram0, dram1, shrd_ram; + struct pruss *pruss = pru->pruss; + u32 offset; + void *va = NULL; + + if (len == 0) + return NULL; + + dram0 = pruss->mem_regions[PRUSS_MEM_DRAM0]; + dram1 = pruss->mem_regions[PRUSS_MEM_DRAM1]; + /* PRU1 has its local RAM addresses reversed */ + if (pru->id == 1) + swap(dram0, dram1); + shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2]; + + if (da >= PRU_PDRAM_DA && da + len <= PRU_PDRAM_DA + dram0.size) { + offset = da - PRU_PDRAM_DA; + va = (__force void *)(dram0.va + offset); + } else if (da >= PRU_SDRAM_DA && + da + len <= PRU_SDRAM_DA + dram1.size) { + offset = da - PRU_SDRAM_DA; + va = (__force void *)(dram1.va + offset); + } else if (da >= PRU_SHRDRAM_DA && + da + len <= PRU_SHRDRAM_DA + shrd_ram.size) { + offset = da - PRU_SHRDRAM_DA; + va = (__force void *)(shrd_ram.va + offset); + } + + return va; +} + +/* + * Convert PRU device address (instruction space) to kernel virtual address. + * + * A PRU does not have an unified address space. Each PRU has its very own + * private Instruction RAM, and its device address is identical to that of + * its primary Data RAM device address. + */ +static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len) +{ + u32 offset; + void *va = NULL; + + if (len == 0) + return NULL; + + /* + * GNU binutils do not support multiple address spaces. The GNU + * linker's default linker script places IRAM at an arbitrary high + * offset, in order to differentiate it from DRAM. Hence we need to + * strip the artificial offset in the IRAM addresses coming from the + * ELF file. + * + * The TI proprietary linker would never set those higher IRAM address + * bits anyway. PRU architecture limits the program counter to 16-bit + * word-address range. This in turn corresponds to 18-bit IRAM + * byte-address range for ELF. + * + * Two more bits are added just in case to make the final 20-bit mask. + * Idea is to have a safeguard in case TI decides to add banking + * in future SoCs. + */ + da &= 0xfffff; + + if (da >= PRU_IRAM_DA && + da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { + offset = da - PRU_IRAM_DA; + va = (__force void *)(pru->mem_regions[PRU_IOMEM_IRAM].va + + offset); + } + + return va; +} + +/* + * Provide address translations for only PRU Data RAMs through the remoteproc + * core for any PRU client drivers. The PRU Instruction RAM access is restricted + * only to the PRU loader code. + */ +static void *pru_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) +{ + struct pru_rproc *pru = rproc->priv; + + return pru_d_da_to_va(pru, da, len); +} + +/* PRU-specific address translator used by PRU loader. */ +static void *pru_da_to_va(struct rproc *rproc, u64 da, size_t len, bool is_iram) +{ + struct pru_rproc *pru = rproc->priv; + void *va; + + if (is_iram) + va = pru_i_da_to_va(pru, da, len); + else + va = pru_d_da_to_va(pru, da, len); + + return va; +} + +static struct rproc_ops pru_rproc_ops = { + .start = pru_rproc_start, + .stop = pru_rproc_stop, + .kick = pru_rproc_kick, + .da_to_va = pru_rproc_da_to_va, +}; + +/* + * Custom memory copy implementation for ICSSG PRU/RTU/Tx_PRU Cores + * + * The ICSSG PRU/RTU/Tx_PRU cores have a memory copying issue with IRAM + * memories, that is not seen on previous generation SoCs. The data is reflected + * properly in the IRAM memories only for integer (4-byte) copies. Any unaligned + * copies result in all the other pre-existing bytes zeroed out within that + * 4-byte boundary, thereby resulting in wrong text/code in the IRAMs. Also, the + * IRAM memory port interface does not allow any 8-byte copies (as commonly used + * by ARM64 memcpy implementation) and throws an exception. The DRAM memory + * ports do not show this behavior. + */ +static int pru_rproc_memcpy(void *dest, const void *src, size_t count) +{ + const u32 *s = src; + u32 *d = dest; + size_t size = count / 4; + u32 *tmp_src = NULL; + + /* + * TODO: relax limitation of 4-byte aligned dest addresses and copy + * sizes + */ + if ((long)dest % 4 || count % 4) + return -EINVAL; + + /* src offsets in ELF firmware image can be non-aligned */ + if ((long)src % 4) { + tmp_src = kmemdup(src, count, GFP_KERNEL); + if (!tmp_src) + return -ENOMEM; + s = tmp_src; + } + + while (size--) + *d++ = *s++; + + kfree(tmp_src); + + return 0; +} + +static int +pru_rproc_load_elf_segments(struct rproc *rproc, const struct firmware *fw) +{ + struct pru_rproc *pru = rproc->priv; + struct device *dev = &rproc->dev; + struct elf32_hdr *ehdr; + struct elf32_phdr *phdr; + int i, ret = 0; + const u8 *elf_data = fw->data; + + ehdr = (struct elf32_hdr *)elf_data; + phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + u32 da = phdr->p_paddr; + u32 memsz = phdr->p_memsz; + u32 filesz = phdr->p_filesz; + u32 offset = phdr->p_offset; + bool is_iram; + void *ptr; + + if (phdr->p_type != PT_LOAD || !filesz) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", + phdr->p_type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > fw->size) { + dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n", + offset + filesz, fw->size); + ret = -EINVAL; + break; + } + + /* grab the kernel address for this device address */ + is_iram = phdr->p_flags & PF_X; + ptr = pru_da_to_va(rproc, da, memsz, is_iram); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); + ret = -EINVAL; + break; + } + + if (pru->data->is_k3) { + ret = pru_rproc_memcpy(ptr, elf_data + phdr->p_offset, + filesz); + if (ret) { + dev_err(dev, "PRU memory copy failed for da 0x%x memsz 0x%x\n", + da, memsz); + break; + } + } else { + memcpy(ptr, elf_data + phdr->p_offset, filesz); + } + + /* skip the memzero logic performed by remoteproc ELF loader */ + } + + return ret; +} + +static const void * +pru_rproc_find_interrupt_map(struct device *dev, const struct firmware *fw) +{ + struct elf32_shdr *shdr, *name_table_shdr; + const char *name_table; + const u8 *elf_data = fw->data; + struct elf32_hdr *ehdr = (struct elf32_hdr *)elf_data; + u16 shnum = ehdr->e_shnum; + u16 shstrndx = ehdr->e_shstrndx; + int i; + + /* first, get the section header */ + shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); + /* compute name table section header entry in shdr array */ + name_table_shdr = shdr + shstrndx; + /* finally, compute the name table section address in elf */ + name_table = elf_data + name_table_shdr->sh_offset; + + for (i = 0; i < shnum; i++, shdr++) { + u32 size = shdr->sh_size; + u32 offset = shdr->sh_offset; + u32 name = shdr->sh_name; + + if (strcmp(name_table + name, ".pru_irq_map")) + continue; + + /* make sure we have the entire irq map */ + if (offset + size > fw->size || offset + size < size) { + dev_err(dev, ".pru_irq_map section truncated\n"); + return ERR_PTR(-EINVAL); + } + + /* make sure irq map has at least the header */ + if (sizeof(struct pru_irq_rsc) > size) { + dev_err(dev, "header-less .pru_irq_map section\n"); + return ERR_PTR(-EINVAL); + } + + return shdr; + } + + dev_dbg(dev, "no .pru_irq_map section found for this fw\n"); + + return NULL; +} + +/* + * Use a custom parse_fw callback function for dealing with PRU firmware + * specific sections. + * + * The firmware blob can contain optional ELF sections: .resource_table section + * and .pru_irq_map one. The second one contains the PRUSS interrupt mapping + * description, which needs to be setup before powering on the PRU core. To + * avoid RAM wastage this ELF section is not mapped to any ELF segment (by the + * firmware linker) and therefore is not loaded to PRU memory. + */ +static int pru_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = &rproc->dev; + struct pru_rproc *pru = rproc->priv; + const u8 *elf_data = fw->data; + const void *shdr; + u8 class = fw_elf_get_class(fw); + u64 sh_offset; + int ret; + + /* load optional rsc table */ + ret = rproc_elf_load_rsc_table(rproc, fw); + if (ret == -EINVAL) + dev_dbg(&rproc->dev, "no resource table found for this fw\n"); + else if (ret) + return ret; + + /* find .pru_interrupt_map section, not having it is not an error */ + shdr = pru_rproc_find_interrupt_map(dev, fw); + if (IS_ERR(shdr)) + return PTR_ERR(shdr); + + if (!shdr) + return 0; + + /* preserve pointer to PRU interrupt map together with it size */ + sh_offset = elf_shdr_get_sh_offset(class, shdr); + pru->pru_interrupt_map = (struct pru_irq_rsc *)(elf_data + sh_offset); + pru->pru_interrupt_map_sz = elf_shdr_get_sh_size(class, shdr); + + return 0; +} + +/* + * Compute PRU id based on the IRAM addresses. The PRU IRAMs are + * always at a particular offset within the PRUSS address space. + */ +static int pru_rproc_set_id(struct pru_rproc *pru) +{ + int ret = 0; + + switch (pru->mem_regions[PRU_IOMEM_IRAM].pa & PRU_IRAM_ADDR_MASK) { + case TX_PRU0_IRAM_ADDR_MASK: + fallthrough; + case RTU0_IRAM_ADDR_MASK: + fallthrough; + case PRU0_IRAM_ADDR_MASK: + pru->id = PRUSS_PRU0; + break; + case TX_PRU1_IRAM_ADDR_MASK: + fallthrough; + case RTU1_IRAM_ADDR_MASK: + fallthrough; + case PRU1_IRAM_ADDR_MASK: + pru->id = PRUSS_PRU1; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int pru_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct platform_device *ppdev = to_platform_device(dev->parent); + struct pru_rproc *pru; + const char *fw_name; + struct rproc *rproc = NULL; + struct resource *res; + int i, ret; + const struct pru_private_data *data; + const char *mem_names[PRU_IOMEM_MAX] = { "iram", "control", "debug" }; + + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -ENODEV; + + ret = of_property_read_string(np, "firmware-name", &fw_name); + if (ret) { + dev_err(dev, "unable to retrieve firmware-name %d\n", ret); + return ret; + } + + rproc = devm_rproc_alloc(dev, pdev->name, &pru_rproc_ops, fw_name, + sizeof(*pru)); + if (!rproc) { + dev_err(dev, "rproc_alloc failed\n"); + return -ENOMEM; + } + /* use a custom load function to deal with PRU-specific quirks */ + rproc->ops->load = pru_rproc_load_elf_segments; + + /* use a custom parse function to deal with PRU-specific resources */ + rproc->ops->parse_fw = pru_rproc_parse_fw; + + /* error recovery is not supported for PRUs */ + rproc->recovery_disabled = true; + + /* + * rproc_add will auto-boot the processor normally, but this is not + * desired with PRU client driven boot-flow methodology. A PRU + * application/client driver will boot the corresponding PRU + * remote-processor as part of its state machine either through the + * remoteproc sysfs interface or through the equivalent kernel API. + */ + rproc->auto_boot = false; + + pru = rproc->priv; + pru->dev = dev; + pru->data = data; + pru->pruss = platform_get_drvdata(ppdev); + pru->rproc = rproc; + pru->fw_name = fw_name; + spin_lock_init(&pru->rmw_lock); + mutex_init(&pru->lock); + + for (i = 0; i < ARRAY_SIZE(mem_names); i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + mem_names[i]); + pru->mem_regions[i].va = devm_ioremap_resource(dev, res); + if (IS_ERR(pru->mem_regions[i].va)) { + dev_err(dev, "failed to parse and map memory resource %d %s\n", + i, mem_names[i]); + ret = PTR_ERR(pru->mem_regions[i].va); + return ret; + } + pru->mem_regions[i].pa = res->start; + pru->mem_regions[i].size = resource_size(res); + + dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n", + mem_names[i], &pru->mem_regions[i].pa, + pru->mem_regions[i].size, pru->mem_regions[i].va); + } + + ret = pru_rproc_set_id(pru); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, rproc); + + ret = devm_rproc_add(dev, pru->rproc); + if (ret) { + dev_err(dev, "rproc_add failed: %d\n", ret); + return ret; + } + + pru_rproc_create_debug_entries(rproc); + + dev_dbg(dev, "PRU rproc node %pOF probed successfully\n", np); + + return 0; +} + +static int pru_rproc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rproc *rproc = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: removing rproc %s\n", __func__, rproc->name); + + return 0; +} + +static const struct pru_private_data pru_data = { + .type = PRU_TYPE_PRU, +}; + +static const struct pru_private_data k3_pru_data = { + .type = PRU_TYPE_PRU, + .is_k3 = 1, +}; + +static const struct pru_private_data k3_rtu_data = { + .type = PRU_TYPE_RTU, + .is_k3 = 1, +}; + +static const struct pru_private_data k3_tx_pru_data = { + .type = PRU_TYPE_TX_PRU, + .is_k3 = 1, +}; + +static const struct of_device_id pru_rproc_match[] = { + { .compatible = "ti,am3356-pru", .data = &pru_data }, + { .compatible = "ti,am4376-pru", .data = &pru_data }, + { .compatible = "ti,am5728-pru", .data = &pru_data }, + { .compatible = "ti,k2g-pru", .data = &pru_data }, + { .compatible = "ti,am654-pru", .data = &k3_pru_data }, + { .compatible = "ti,am654-rtu", .data = &k3_rtu_data }, + { .compatible = "ti,am654-tx-pru", .data = &k3_tx_pru_data }, + { .compatible = "ti,j721e-pru", .data = &k3_pru_data }, + { .compatible = "ti,j721e-rtu", .data = &k3_rtu_data }, + { .compatible = "ti,j721e-tx-pru", .data = &k3_tx_pru_data }, + { .compatible = "ti,am642-pru", .data = &k3_pru_data }, + { .compatible = "ti,am642-rtu", .data = &k3_rtu_data }, + { .compatible = "ti,am642-tx-pru", .data = &k3_tx_pru_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, pru_rproc_match); + +static struct platform_driver pru_rproc_driver = { + .driver = { + .name = PRU_RPROC_DRVNAME, + .of_match_table = pru_rproc_match, + .suppress_bind_attrs = true, + }, + .probe = pru_rproc_probe, + .remove = pru_rproc_remove, +}; +module_platform_driver(pru_rproc_driver); + +MODULE_AUTHOR("Suman Anna "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_AUTHOR("Grzegorz Jaszczyk "); +MODULE_DESCRIPTION("PRU-ICSS Remote Processor Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/remoteproc/pru_rproc.h b/drivers/remoteproc/pru_rproc.h --- a/drivers/remoteproc/pru_rproc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/remoteproc/pru_rproc.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * PRUSS Remote Processor specific types + * + * Copyright (C) 2014-2020 Texas Instruments Incorporated - https://www.ti.com/ + * Suman Anna + */ + +#ifndef _PRU_RPROC_H_ +#define _PRU_RPROC_H_ + +/** + * struct pruss_int_map - PRU system events _to_ channel and host mapping + * @event: number of the system event + * @chnl: channel number assigned to a given @event + * @host: host number assigned to a given @chnl + * + * PRU system events are mapped to channels, and these channels are mapped + * to host interrupts. Events can be mapped to channels in a one-to-one or + * many-to-one ratio (multiple events per channel), and channels can be + * mapped to host interrupts in a one-to-one or many-to-one ratio (multiple + * channels per interrupt). + */ +struct pruss_int_map { + u8 event; + u8 chnl; + u8 host; +}; + +/** + * struct pru_irq_rsc - PRU firmware section header for IRQ data + * @type: resource type + * @num_evts: number of described events + * @pru_intc_map: PRU interrupt routing description + * + * The PRU firmware blob can contain optional .pru_irq_map ELF section, which + * provides the PRUSS interrupt mapping description. The pru_irq_rsc struct + * describes resource entry format. + */ +struct pru_irq_rsc { + u8 type; + u8 num_evts; + struct pruss_int_map pru_intc_map[]; +} __packed; + +#endif /* _PRU_RPROC_H_ */ diff -Naur --no-dereference a/drivers/remoteproc/remoteproc_cdev.c b/drivers/remoteproc/remoteproc_cdev.c --- a/drivers/remoteproc/remoteproc_cdev.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/remoteproc_cdev.c 2022-01-06 12:45:53.826318157 -0500 @@ -32,15 +32,29 @@ return -EFAULT; if (!strncmp(cmd, "start", len)) { - if (rproc->state == RPROC_RUNNING) + if (rproc->state == RPROC_RUNNING || + rproc->state == RPROC_ATTACHED) return -EBUSY; ret = rproc_boot(rproc); } else if (!strncmp(cmd, "stop", len)) { - if (rproc->state != RPROC_RUNNING) + if (rproc->state != RPROC_RUNNING && + rproc->state != RPROC_ATTACHED) return -EINVAL; + if (rproc->state == RPROC_ATTACHED && + rproc->detach_on_shutdown) { + dev_err(&rproc->dev, + "stop not supported for this rproc, use detach\n"); + return -EINVAL; + } + rproc_shutdown(rproc); + } else if (!strncmp(cmd, "detach", len)) { + if (rproc->state != RPROC_ATTACHED) + return -EINVAL; + + ret = rproc_detach(rproc); } else { dev_err(&rproc->dev, "Unrecognized option\n"); ret = -EINVAL; @@ -79,11 +93,17 @@ static int rproc_cdev_release(struct inode *inode, struct file *filp) { struct rproc *rproc = container_of(inode->i_cdev, struct rproc, cdev); + int ret = 0; + + if (!rproc->cdev_put_on_release) + return 0; - if (rproc->cdev_put_on_release && rproc->state == RPROC_RUNNING) + if (rproc->state == RPROC_RUNNING) rproc_shutdown(rproc); + else if (rproc->state == RPROC_ATTACHED) + ret = rproc_detach(rproc); - return 0; + return ret; } static const struct file_operations rproc_fops = { diff -Naur --no-dereference a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c --- a/drivers/remoteproc/remoteproc_core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/remoteproc_core.c 2022-01-06 12:45:53.826318157 -0500 @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -226,6 +228,61 @@ EXPORT_SYMBOL(rproc_da_to_va); /** + * rproc_pa_to_da() - lookup the rproc device address for a physical address + * @rproc: handle of a remote processor + * @pa: physical address of the buffer to translate + * @da: device address to return + * + * Communication clients of remote processors usually would need a means to + * convert a host buffer pointer to an equivalent device virtual address pointer + * that the code running on the remote processor can operate on. These buffer + * pointers can either be from the physically contiguous memory regions (or + * "carveouts") or can be some memory-mapped Device IO memory. This function + * provides a means to translate a given physical address to its associated + * device address. + * + * The function looks through both the carveouts and the device memory mappings + * since both of them are stored in separate lists. + * + * Return: 0 on success, or an appropriate error code otherwise. The translated + * device address is returned through the appropriate function argument. + */ +int rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da) +{ + int ret = -EINVAL; + struct rproc_mem_entry *maps = NULL; + + if (!rproc || !da) + return -EINVAL; + + if (mutex_lock_interruptible(&rproc->lock)) + return -EINTR; + + if (rproc->state == RPROC_RUNNING || rproc->state == RPROC_SUSPENDED) { + /* Look in the mappings first */ + list_for_each_entry(maps, &rproc->mappings, node) { + if (pa >= maps->dma && pa < (maps->dma + maps->len)) { + *da = maps->da + (pa - maps->dma); + ret = 0; + goto exit; + } + } + /* If not, check in the carveouts */ + list_for_each_entry(maps, &rproc->carveouts, node) { + if (pa >= maps->dma && pa < (maps->dma + maps->len)) { + *da = maps->da + (pa - maps->dma); + ret = 0; + break; + } + } + } +exit: + mutex_unlock(&rproc->lock); + return ret; +} +EXPORT_SYMBOL(rproc_pa_to_da); + +/** * rproc_find_carveout_by_name() - lookup the carveout region by a name * @rproc: handle of a remote processor * @name: carveout name to find (format string) @@ -482,7 +539,7 @@ /** * rproc_handle_vdev() - handle a vdev fw resource * @rproc: the remote processor - * @rsc: the vring resource descriptor + * @ptr: the vring resource descriptor * @offset: offset of the resource entry * @avail: size of available data (for sanity checking the image) * @@ -507,9 +564,10 @@ * * Returns 0 on success, or an appropriate error code otherwise */ -static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, +static int rproc_handle_vdev(struct rproc *rproc, void *ptr, int offset, int avail) { + struct fw_rsc_vdev *rsc = ptr; struct device *dev = &rproc->dev; struct rproc_vdev *rvdev; int i, ret; @@ -629,7 +687,7 @@ /** * rproc_handle_trace() - handle a shared trace buffer resource * @rproc: the remote processor - * @rsc: the trace resource descriptor + * @ptr: the trace resource descriptor * @offset: offset of the resource entry * @avail: size of available data (for sanity checking the image) * @@ -643,9 +701,10 @@ * * Returns 0 on success, or an appropriate error code otherwise */ -static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, +static int rproc_handle_trace(struct rproc *rproc, void *ptr, int offset, int avail) { + struct fw_rsc_trace *rsc = ptr; struct rproc_debug_trace *trace; struct device *dev = &rproc->dev; char name[15]; @@ -695,7 +754,7 @@ /** * rproc_handle_devmem() - handle devmem resource entry * @rproc: remote processor handle - * @rsc: the devmem resource entry + * @ptr: the devmem resource entry * @offset: offset of the resource entry * @avail: size of available data (for sanity checking the image) * @@ -718,9 +777,10 @@ * and not allow firmwares to request access to physical addresses that * are outside those ranges. */ -static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, +static int rproc_handle_devmem(struct rproc *rproc, void *ptr, int offset, int avail) { + struct fw_rsc_devmem *rsc = ptr; struct rproc_mem_entry *mapping; struct device *dev = &rproc->dev; int ret; @@ -757,6 +817,7 @@ * We can't trust the remote processor not to change the resource * table, so we must maintain this info independently. */ + mapping->dma = rsc->pa; mapping->da = rsc->da; mapping->len = rsc->len; list_add_tail(&mapping->node, &rproc->mappings); @@ -898,7 +959,7 @@ /** * rproc_handle_carveout() - handle phys contig memory allocation requests * @rproc: rproc handle - * @rsc: the resource entry + * @ptr: the resource entry * @offset: offset of the resource entry * @avail: size of available data (for image validation) * @@ -915,9 +976,9 @@ * pressure is important; it may have a substantial impact on performance. */ static int rproc_handle_carveout(struct rproc *rproc, - struct fw_rsc_carveout *rsc, - int offset, int avail) + void *ptr, int offset, int avail) { + struct fw_rsc_carveout *rsc = ptr; struct rproc_mem_entry *carveout; struct device *dev = &rproc->dev; @@ -1099,10 +1160,10 @@ * enum fw_resource_type. */ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = { - [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, - [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, - [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, - [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, + [RSC_CARVEOUT] = rproc_handle_carveout, + [RSC_DEVMEM] = rproc_handle_devmem, + [RSC_TRACE] = rproc_handle_trace, + [RSC_VDEV] = rproc_handle_vdev, }; /* handle firmware resource entries before booting the remote processor */ @@ -1359,11 +1420,14 @@ struct device *dev = &rproc->dev; int ret; - /* load the ELF segments to memory */ - ret = rproc_load_segments(rproc, fw); - if (ret) { - dev_err(dev, "Failed to load program segments: %d\n", ret); - return ret; + if (!rproc->skip_firmware_load) { + /* load the ELF segments to memory */ + ret = rproc_load_segments(rproc, fw); + if (ret) { + dev_err(dev, "Failed to load program segments: %d\n", + ret); + return ret; + } } /* @@ -1418,7 +1482,7 @@ return ret; } -static int rproc_attach(struct rproc *rproc) +static int __rproc_attach(struct rproc *rproc) { struct device *dev = &rproc->dev; int ret; @@ -1446,7 +1510,7 @@ goto stop_rproc; } - rproc->state = RPROC_RUNNING; + rproc->state = RPROC_ATTACHED; dev_info(dev, "remote processor %s is now attached\n", rproc->name); @@ -1473,7 +1537,11 @@ if (ret) return ret; - dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size); + if (!rproc->skip_firmware_load) + dev_info(dev, "Booting fw image %s, size %zd\n", + name, fw->size); + else + dev_info(dev, "Booting unspecified pre-loaded fw image\n"); /* * if enabling an IOMMU isn't relevant for this rproc, this is @@ -1539,11 +1607,149 @@ return ret; } +static int rproc_set_rsc_table(struct rproc *rproc) +{ + struct resource_table *table_ptr; + struct device *dev = &rproc->dev; + size_t table_sz; + int ret; + + table_ptr = rproc_get_loaded_rsc_table(rproc, &table_sz); + if (!table_ptr) { + /* Not having a resource table is acceptable */ + return 0; + } + + if (IS_ERR(table_ptr)) { + ret = PTR_ERR(table_ptr); + dev_err(dev, "can't load resource table: %d\n", ret); + return ret; + } + + /* + * If it is possible to detach the remote processor, keep an untouched + * copy of the resource table. That way we can start fresh again when + * the remote processor is re-attached, that is: + * + * DETACHED -> ATTACHED -> DETACHED -> ATTACHED + * + * Free'd in rproc_reset_rsc_table_on_detach() and + * rproc_reset_rsc_table_on_stop(). + */ + if (rproc->ops->detach) { + rproc->clean_table = kmemdup(table_ptr, table_sz, GFP_KERNEL); + if (!rproc->clean_table) + return -ENOMEM; + } else { + rproc->clean_table = NULL; + } + + rproc->cached_table = NULL; + rproc->table_ptr = table_ptr; + rproc->table_sz = table_sz; + + return 0; +} + +static int rproc_reset_rsc_table_on_detach(struct rproc *rproc) +{ + struct resource_table *table_ptr; + + /* A resource table was never retrieved, nothing to do here */ + if (!rproc->table_ptr) + return 0; + + /* + * If we made it to this point a clean_table _must_ have been + * allocated in rproc_set_rsc_table(). If one isn't present + * something went really wrong and we must complain. + */ + if (WARN_ON(!rproc->clean_table)) + return -EINVAL; + + /* Remember where the external entity installed the resource table */ + table_ptr = rproc->table_ptr; + + /* + * If we made it here the remote processor was started by another + * entity and a cache table doesn't exist. As such make a copy of + * the resource table currently used by the remote processor and + * use that for the rest of the shutdown process. The memory + * allocated here is free'd in rproc_detach(). + */ + rproc->cached_table = kmemdup(rproc->table_ptr, + rproc->table_sz, GFP_KERNEL); + if (!rproc->cached_table) + return -ENOMEM; + + /* + * Use a copy of the resource table for the remainder of the + * shutdown process. + */ + rproc->table_ptr = rproc->cached_table; + + /* + * Reset the memory area where the firmware loaded the resource table + * to its original value. That way when we re-attach the remote + * processor the resource table is clean and ready to be used again. + */ + memcpy(table_ptr, rproc->clean_table, rproc->table_sz); + + /* + * The clean resource table is no longer needed. Allocated in + * rproc_set_rsc_table(). + */ + kfree(rproc->clean_table); + + return 0; +} + +static int rproc_reset_rsc_table_on_stop(struct rproc *rproc) +{ + /* A resource table was never retrieved, nothing to do here */ + if (!rproc->table_ptr) + return 0; + + /* + * If a cache table exists the remote processor was started by + * the remoteproc core. That cache table should be used for + * the rest of the shutdown process. + */ + if (rproc->cached_table) + goto out; + + /* + * If we made it here the remote processor was started by another + * entity and a cache table doesn't exist. As such make a copy of + * the resource table currently used by the remote processor and + * use that for the rest of the shutdown process. The memory + * allocated here is free'd in rproc_shutdown(). + */ + rproc->cached_table = kmemdup(rproc->table_ptr, + rproc->table_sz, GFP_KERNEL); + if (!rproc->cached_table) + return -ENOMEM; + + /* + * Since the remote processor is being switched off the clean table + * won't be needed. Allocated in rproc_set_rsc_table(). + */ + kfree(rproc->clean_table); + +out: + /* + * Use a copy of the resource table for the remainder of the + * shutdown process. + */ + rproc->table_ptr = rproc->cached_table; + return 0; +} + /* * Attach to remote processor - similar to rproc_fw_boot() but without * the steps that deal with the firmware image. */ -static int rproc_actuate(struct rproc *rproc) +static int rproc_attach(struct rproc *rproc) { struct device *dev = &rproc->dev; int ret; @@ -1558,6 +1764,19 @@ return ret; } + /* Do anything that is needed to boot the remote processor */ + ret = rproc_prepare_device(rproc); + if (ret) { + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret); + goto disable_iommu; + } + + ret = rproc_set_rsc_table(rproc); + if (ret) { + dev_err(dev, "can't load resource table: %d\n", ret); + goto unprepare_device; + } + /* reset max_notifyid */ rproc->max_notifyid = -1; @@ -1572,7 +1791,7 @@ ret = rproc_handle_resources(rproc, rproc_loading_handlers); if (ret) { dev_err(dev, "Failed to process resources: %d\n", ret); - goto disable_iommu; + goto unprepare_device; } /* Allocate carveout resources associated to rproc */ @@ -1583,7 +1802,7 @@ goto clean_up_resources; } - ret = rproc_attach(rproc); + ret = __rproc_attach(rproc); if (ret) goto clean_up_resources; @@ -1591,6 +1810,9 @@ clean_up_resources: rproc_resource_cleanup(rproc); +unprepare_device: + /* release HW resources if needed */ + rproc_unprepare_device(rproc); disable_iommu: rproc_disable_iommu(rproc); return ret; @@ -1644,11 +1866,20 @@ struct device *dev = &rproc->dev; int ret; + /* No need to continue if a stop() operation has not been provided */ + if (!rproc->ops->stop) + return -EINVAL; + /* Stop any subdevices for the remote processor */ rproc_stop_subdevices(rproc, crashed); /* the installed resource table is no longer accessible */ - rproc->table_ptr = rproc->cached_table; + ret = rproc_reset_rsc_table_on_stop(rproc); + if (ret) { + dev_err(dev, "can't reset resource table: %d\n", ret); + return ret; + } + /* power off the remote processor */ ret = rproc->ops->stop(rproc); @@ -1661,19 +1892,48 @@ rproc->state = RPROC_OFFLINE; - /* - * The remote processor has been stopped and is now offline, which means - * that the next time it is brought back online the remoteproc core will - * be responsible to load its firmware. As such it is no longer - * autonomous. - */ - rproc->autonomous = false; - dev_info(dev, "stopped remote processor %s\n", rproc->name); return 0; } +/* + * __rproc_detach(): Does the opposite of __rproc_attach() + */ +static int __rproc_detach(struct rproc *rproc) +{ + struct device *dev = &rproc->dev; + int ret; + + /* No need to continue if a detach() operation has not been provided */ + if (!rproc->ops->detach) + return -EINVAL; + + /* Stop any subdevices for the remote processor */ + rproc_stop_subdevices(rproc, false); + + /* the installed resource table is no longer accessible */ + ret = rproc_reset_rsc_table_on_detach(rproc); + if (ret) { + dev_err(dev, "can't reset resource table: %d\n", ret); + return ret; + } + + /* Tell the remote processor the core isn't available anymore */ + ret = rproc_detach_device(rproc); + if (ret) { + dev_err(dev, "can't detach from rproc: %d\n", ret); + return ret; + } + + rproc_unprepare_subdevices(rproc); + + rproc->state = RPROC_DETACHED; + + dev_info(dev, "detached remote processor %s\n", rproc->name); + + return 0; +} /** * rproc_trigger_recovery() - recover a remoteproc @@ -1760,6 +2020,36 @@ } /** + * rproc_get_id() - return the id for the rproc device + * @rproc: handle of a remote processor + * + * Each rproc device is associated with a platform device, which is created + * either from device tree (majority newer platforms) or using legacy style + * platform device creation (fewer legacy platforms). This function retrieves + * an unique id for each remote processor and is useful for clients needing + * to distinguish each of the remoteprocs. This unique id is derived using + * the platform device id for non-DT devices, or an alternate alias id for + * DT devices (since they do not have a valid platform device id). It is + * assumed that the platform devices were created with known ids or were + * given proper alias ids using the stem "rproc". + * + * Return: alias id for DT devices or platform device id for non-DT devices + * associated with the rproc + */ +int rproc_get_id(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + + if (np) + return of_alias_get_id(np, "rproc"); + else + return pdev->id; +} +EXPORT_SYMBOL(rproc_get_id); + +/** * rproc_boot() - boot a remote processor * @rproc: handle of a remote processor * @@ -1804,20 +2094,23 @@ if (rproc->state == RPROC_DETACHED) { dev_info(dev, "attaching to %s\n", rproc->name); - ret = rproc_actuate(rproc); + ret = rproc_attach(rproc); } else { dev_info(dev, "powering up %s\n", rproc->name); - /* load firmware */ - ret = request_firmware(&firmware_p, rproc->firmware, dev); - if (ret < 0) { - dev_err(dev, "request_firmware failed: %d\n", ret); + if (!rproc->skip_firmware_load) { + /* load firmware */ + ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (ret < 0) { + dev_err(dev, "request_firmware failed: %d\n", ret); goto downref_rproc; + } } ret = rproc_fw_boot(rproc, firmware_p); - release_firmware(firmware_p); + if (!rproc->skip_firmware_load) + release_firmware(firmware_p); } downref_rproc: @@ -1863,7 +2156,10 @@ if (!atomic_dec_and_test(&rproc->power)) goto out; - ret = rproc_stop(rproc, false); + if (rproc->detach_on_shutdown && rproc->state == RPROC_ATTACHED) + ret = __rproc_detach(rproc); + else + ret = rproc_stop(rproc, false); if (ret) { atomic_inc(&rproc->power); goto out; @@ -1887,6 +2183,65 @@ EXPORT_SYMBOL(rproc_shutdown); /** + * rproc_detach() - Detach the remote processor from the + * remoteproc core + * + * @rproc: the remote processor + * + * Detach a remote processor (previously attached to with rproc_attach()). + * + * In case @rproc is still being used by an additional user(s), then + * this function will just decrement the power refcount and exit, + * without disconnecting the device. + * + * Function rproc_detach() calls __rproc_detach() in order to let a remote + * processor know that services provided by the application processor are + * no longer available. From there it should be possible to remove the + * platform driver and even power cycle the application processor (if the HW + * supports it) without needing to switch off the remote processor. + */ +int rproc_detach(struct rproc *rproc) +{ + struct device *dev = &rproc->dev; + int ret; + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return ret; + } + + /* if the remote proc is still needed, bail out */ + if (!atomic_dec_and_test(&rproc->power)) { + ret = 0; + goto out; + } + + ret = __rproc_detach(rproc); + if (ret) { + atomic_inc(&rproc->power); + goto out; + } + + /* clean up all acquired resources */ + rproc_resource_cleanup(rproc); + + /* release HW resources if needed */ + rproc_unprepare_device(rproc); + + rproc_disable_iommu(rproc); + + /* Free the copy of the resource table */ + kfree(rproc->cached_table); + rproc->cached_table = NULL; + rproc->table_ptr = NULL; +out: + mutex_unlock(&rproc->lock); + return ret; +} +EXPORT_SYMBOL(rproc_detach); + +/** * rproc_get_by_phandle() - find a remote processor by phandle * @phandle: phandle to the rproc * @@ -1936,6 +2291,69 @@ #endif EXPORT_SYMBOL(rproc_get_by_phandle); +/** + * rproc_set_firmware() - assign a new firmware + * @rproc: rproc handle to which the new firmware is being assigned + * @fw_name: new firmware name to be assigned + * + * This function allows remoteproc drivers or clients to configure a custom + * firmware name that is different from the default name used during remoteproc + * registration. The function does not trigger a remote processor boot, + * only sets the firmware name used for a subsequent boot. This function + * should also be called only when the remote processor is offline. + * + * This allows either the userspace to configure a different name through + * sysfs or a kernel-level remoteproc or a remoteproc client driver to set + * a specific firmware when it is controlling the boot and shutdown of the + * remote processor. + * + * Return: 0 on success or a negative value upon failure + */ +int rproc_set_firmware(struct rproc *rproc, const char *fw_name) +{ + struct device *dev; + int ret, len; + char *p; + + if (!rproc || !fw_name) + return -EINVAL; + + dev = rproc->dev.parent; + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return -EINVAL; + } + + if (rproc->state != RPROC_OFFLINE) { + dev_err(dev, "can't change firmware while running\n"); + ret = -EBUSY; + goto out; + } + + len = strcspn(fw_name, "\n"); + if (!len) { + dev_err(dev, "can't provide empty string for firmware name\n"); + ret = -EINVAL; + goto out; + } + + p = kstrndup(fw_name, len, GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto out; + } + + kfree_const(rproc->firmware); + rproc->firmware = p; + +out: + mutex_unlock(&rproc->lock); + return ret; +} +EXPORT_SYMBOL(rproc_set_firmware); + static int rproc_validate(struct rproc *rproc) { switch (rproc->state) { @@ -1946,6 +2364,13 @@ */ if (!rproc->ops->start) return -EINVAL; + + /* + * Userspace driven loading cannot expect to have + * auto_boot set. + */ + if (rproc->auto_boot && rproc->skip_firmware_load) + return -EINVAL; break; case RPROC_DETACHED: /* @@ -2016,16 +2441,6 @@ if (ret < 0) return ret; - /* - * Remind ourselves the remote processor has been attached to rather - * than booted by the remoteproc core. This is important because the - * RPROC_DETACHED state will be lost as soon as the remote processor - * has been attached to. Used in firmware_show() and reset in - * rproc_stop(). - */ - if (rproc->state == RPROC_DETACHED) - rproc->autonomous = true; - /* if rproc is marked always-on, request it to boot */ if (rproc->auto_boot) { ret = rproc_trigger_auto_boot(rproc); @@ -2427,7 +2842,11 @@ rcu_read_lock(); list_for_each_entry_rcu(rproc, &rproc_list, node) { - if (!rproc->ops->panic || rproc->state != RPROC_RUNNING) + if (!rproc->ops->panic) + continue; + + if (rproc->state != RPROC_RUNNING && + rproc->state != RPROC_ATTACHED) continue; d = rproc->ops->panic(rproc); diff -Naur --no-dereference a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h --- a/drivers/remoteproc/remoteproc_internal.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/remoteproc_internal.h 2022-01-06 12:45:53.826318157 -0500 @@ -84,7 +84,6 @@ void rproc_free_vring(struct rproc_vring *rvring); int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); -void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len); phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); @@ -121,6 +120,14 @@ return 0; } +static inline int rproc_detach_device(struct rproc *rproc) +{ + if (rproc->ops->detach) + return rproc->ops->detach(rproc); + + return 0; +} + static inline int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) { @@ -176,6 +183,16 @@ return NULL; } + +static inline +struct resource_table *rproc_get_loaded_rsc_table(struct rproc *rproc, + size_t *size) +{ + if (rproc->ops->get_loaded_rsc_table) + return rproc->ops->get_loaded_rsc_table(rproc, size); + + return NULL; +} static inline bool rproc_u64_fit_in_size_t(u64 val) diff -Naur --no-dereference a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c --- a/drivers/remoteproc/remoteproc_sysfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/remoteproc_sysfs.c 2022-01-06 12:45:53.826318157 -0500 @@ -3,6 +3,7 @@ * Remote Processor Framework */ +#include #include #include @@ -15,7 +16,7 @@ { struct rproc *rproc = to_rproc(dev); - return sprintf(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n"); + return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n"); } /* @@ -48,6 +49,10 @@ { struct rproc *rproc = to_rproc(dev); + /* restrict sysfs operations if not allowed by remoteproc drivers */ + if (rproc->deny_sysfs_ops) + return -EPERM; + if (sysfs_streq(buf, "enabled")) { /* change the flag and begin the recovery process if needed */ rproc->recovery_disabled = false; @@ -82,7 +87,7 @@ { struct rproc *rproc = to_rproc(dev); - return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); + return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); } /* @@ -107,6 +112,10 @@ { struct rproc *rproc = to_rproc(dev); + /* restrict sysfs operations if not allowed by remoteproc drivers */ + if (rproc->deny_sysfs_ops) + return -EPERM; + if (rproc->state == RPROC_CRASHED) { dev_err(&rproc->dev, "can't change coredump configuration\n"); return -EBUSY; @@ -138,11 +147,8 @@ * If the remote processor has been started by an external * entity we have no idea of what image it is running. As such * simply display a generic string rather then rproc->firmware. - * - * Here we rely on the autonomous flag because a remote processor - * may have been attached to and currently in a running state. */ - if (rproc->autonomous) + if (rproc->state == RPROC_ATTACHED || rproc->skip_firmware_load) firmware = "unknown"; return sprintf(buf, "%s\n", firmware); @@ -154,38 +160,13 @@ const char *buf, size_t count) { struct rproc *rproc = to_rproc(dev); - char *p; - int err, len = count; + int err; - err = mutex_lock_interruptible(&rproc->lock); - if (err) { - dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err); - return -EINVAL; - } + /* restrict sysfs operations if not allowed by remoteproc drivers */ + if (rproc->deny_sysfs_ops) + return -EPERM; - if (rproc->state != RPROC_OFFLINE) { - dev_err(dev, "can't change firmware while running\n"); - err = -EBUSY; - goto out; - } - - len = strcspn(buf, "\n"); - if (!len) { - dev_err(dev, "can't provide a NULL firmware\n"); - err = -EINVAL; - goto out; - } - - p = kstrndup(buf, len, GFP_KERNEL); - if (!p) { - err = -ENOMEM; - goto out; - } - - kfree(rproc->firmware); - rproc->firmware = p; -out: - mutex_unlock(&rproc->lock); + err = rproc_set_firmware(rproc, buf); return err ? err : count; } @@ -201,6 +182,7 @@ [RPROC_RUNNING] = "running", [RPROC_CRASHED] = "crashed", [RPROC_DELETED] = "deleted", + [RPROC_ATTACHED] = "attached", [RPROC_DETACHED] = "detached", [RPROC_LAST] = "invalid", }; @@ -224,18 +206,48 @@ struct rproc *rproc = to_rproc(dev); int ret = 0; + /* restrict sysfs operations if not allowed by remoteproc drivers */ + if (rproc->deny_sysfs_ops) + return -EPERM; + if (sysfs_streq(buf, "start")) { - if (rproc->state == RPROC_RUNNING) + if (rproc->state == RPROC_RUNNING || + rproc->state == RPROC_ATTACHED) return -EBUSY; + /* + * prevent underlying implementation from being removed + * when remoteproc does not support auto-boot + */ + if (!rproc->auto_boot && + !try_module_get(dev->parent->driver->owner)) + return -EINVAL; + ret = rproc_boot(rproc); - if (ret) + if (ret) { dev_err(&rproc->dev, "Boot failed: %d\n", ret); + if (!rproc->auto_boot) + module_put(dev->parent->driver->owner); + } } else if (sysfs_streq(buf, "stop")) { - if (rproc->state != RPROC_RUNNING) + if (rproc->state != RPROC_RUNNING && + rproc->state != RPROC_ATTACHED) return -EINVAL; + if (rproc->state == RPROC_ATTACHED && + rproc->detach_on_shutdown) { + dev_err(&rproc->dev, "stop not supported for this rproc, use detach\n"); + return -EINVAL; + } + rproc_shutdown(rproc); + if (!rproc->auto_boot) + module_put(dev->parent->driver->owner); + } else if (sysfs_streq(buf, "detach")) { + if (rproc->state != RPROC_ATTACHED) + return -EINVAL; + + ret = rproc_detach(rproc); } else { dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); ret = -EINVAL; diff -Naur --no-dereference a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -76,6 +76,7 @@ * @ti_sci_id: TI-SCI device identifier * @mbox: mailbox channel handle * @client: mailbox client to request the mailbox channel + * @ipc_only: flag to indicate IPC-only mode */ struct k3_dsp_rproc { struct device *dev; @@ -91,6 +92,7 @@ u32 ti_sci_id; struct mbox_chan *mbox; struct mbox_client client; + bool ipc_only; }; /** @@ -216,6 +218,43 @@ return ret; } +static int k3_dsp_rproc_request_mbox(struct rproc *rproc) +{ + struct k3_dsp_rproc *kproc = rproc->priv; + struct mbox_client *client = &kproc->client; + struct device *dev = kproc->dev; + int ret; + + client->dev = dev; + client->tx_done = NULL; + client->rx_callback = k3_dsp_rproc_mbox_callback; + client->tx_block = false; + client->knows_txdone = false; + + kproc->mbox = mbox_request_channel(client, 0); + if (IS_ERR(kproc->mbox)) { + ret = -EBUSY; + dev_err(dev, "mbox_request_channel failed: %ld\n", + PTR_ERR(kproc->mbox)); + return ret; + } + + /* + * Ping the remote processor, this is only for sanity-sake for now; + * there is no functional effect whatsoever. + * + * Note that the reply will _not_ arrive immediately: this message + * will wait in the mailbox fifo until the remote processor is booted. + */ + ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); + if (ret < 0) { + dev_err(dev, "mbox_send_message failed: %d\n", ret); + mbox_free_channel(kproc->mbox); + return ret; + } + + return 0; +} /* * The C66x DSP cores have a local reset that affects only the CPU, and a * generic module reset that powers on the device and allows the DSP internal @@ -231,6 +270,10 @@ struct device *dev = kproc->dev; int ret; + /* IPC-only mode does not require the core to be released from reset */ + if (kproc->ipc_only) + return 0; + ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci, kproc->ti_sci_id); if (ret) @@ -255,6 +298,10 @@ struct device *dev = kproc->dev; int ret; + /* do not put back the cores into reset in IPC-only mode */ + if (kproc->ipc_only) + return 0; + ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, kproc->ti_sci_id); if (ret) @@ -273,37 +320,19 @@ static int k3_dsp_rproc_start(struct rproc *rproc) { struct k3_dsp_rproc *kproc = rproc->priv; - struct mbox_client *client = &kproc->client; struct device *dev = kproc->dev; u32 boot_addr; int ret; - client->dev = dev; - client->tx_done = NULL; - client->rx_callback = k3_dsp_rproc_mbox_callback; - client->tx_block = false; - client->knows_txdone = false; - - kproc->mbox = mbox_request_channel(client, 0); - if (IS_ERR(kproc->mbox)) { - ret = -EBUSY; - dev_err(dev, "mbox_request_channel failed: %ld\n", - PTR_ERR(kproc->mbox)); - return ret; + if (kproc->ipc_only) { + dev_err(dev, "%s cannot be invoked in IPC-only mode\n", + __func__); + return -EINVAL; } - /* - * Ping the remote processor, this is only for sanity-sake for now; - * there is no functional effect whatsoever. - * - * Note that the reply will _not_ arrive immediately: this message - * will wait in the mailbox fifo until the remote processor is booted. - */ - ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); - if (ret < 0) { - dev_err(dev, "mbox_send_message failed: %d\n", ret); - goto put_mbox; - } + ret = k3_dsp_rproc_request_mbox(rproc); + if (ret) + return ret; boot_addr = rproc->bootaddr; if (boot_addr & (kproc->data->boot_align_addr - 1)) { @@ -338,6 +367,13 @@ static int k3_dsp_rproc_stop(struct rproc *rproc) { struct k3_dsp_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (kproc->ipc_only) { + dev_err(dev, "%s cannot be invoked in IPC-only mode\n", + __func__); + return -EINVAL; + } mbox_free_channel(kproc->mbox); @@ -347,6 +383,85 @@ } /* + * Attach to a running DSP remote processor (IPC-only mode) + * + * This rproc attach callback only needs to request the mailbox, the remote + * processor is already booted, so there is no need to issue any TI-SCI + * commands to boot the DSP core. + */ +static int k3_dsp_rproc_attach(struct rproc *rproc) +{ + struct k3_dsp_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + if (!kproc->ipc_only || rproc->state != RPROC_DETACHED) { + dev_err(dev, "DSP is expected to be in IPC-only mode and RPROC_DETACHED state\n"); + return -EINVAL; + } + + ret = k3_dsp_rproc_request_mbox(rproc); + if (ret) + return ret; + + dev_err(dev, "DSP initialized in IPC-only mode\n"); + return 0; +} + +/* + * Detach from a running DSP remote processor (IPC-only mode) + * + * This rproc detach callback performs the opposite operation to attach callback + * and only needs to release the mailbox, the DSP core is not stopped and will + * be left to continue to run its booted firmware. + */ +static int k3_dsp_rproc_detach(struct rproc *rproc) +{ + struct k3_dsp_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->ipc_only || rproc->state != RPROC_ATTACHED) { + dev_err(dev, "DSP is expected to be in IPC-only mode and RPROC_ATTACHED state\n"); + return -EINVAL; + } + + mbox_free_channel(kproc->mbox); + dev_err(dev, "DSP deinitialized in IPC-only mode\n"); + return 0; +} + +/* + * This function implements the .get_loaded_rsc_table() callback and is used + * to provide the resource table for a booted DSP in IPC-only mode. The K3 DSP + * firmwares follow a design-by-contract approach and are expected to have the + * resource table at the base of the DDR region reserved for firmware usage. + * This provides flexibility for the remote processor to be booted by different + * bootloaders that may or may not have the ability to publish the resource table + * address and size through a DT property. + */ +static struct resource_table *k3_dsp_get_loaded_rsc_table(struct rproc *rproc, + size_t *rsc_table_sz) +{ + struct k3_dsp_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->rmem[0].cpu_addr) { + dev_err(dev, "memory-region #1 does not exist, loaded rsc table can't be found"); + return ERR_PTR(-ENOMEM); + } + + /* + * NOTE: The resource table size is currently hard-coded to a maximum + * of 256 bytes. The most common resource table usage for K3 firmwares + * is to only have the vdev resource entry and an optional trace entry. + * The exact size could be computed based on resource table address, but + * the hard-coded value suffices to support the IPC-only mode. + */ + *rsc_table_sz = 256; + return (struct resource_table *)kproc->rmem[0].cpu_addr; +} + +/* * Custom function to translate a DSP device address (internal RAMs only) to a * kernel virtual address. The DSPs can access their RAMs at either an internal * address visible only from a DSP, or at the SoC-level bus address. Both these @@ -408,8 +523,11 @@ static const struct rproc_ops k3_dsp_rproc_ops = { .start = k3_dsp_rproc_start, .stop = k3_dsp_rproc_stop, + .attach = k3_dsp_rproc_attach, + .detach = k3_dsp_rproc_detach, .kick = k3_dsp_rproc_kick, .da_to_va = k3_dsp_rproc_da_to_va, + .get_loaded_rsc_table = k3_dsp_get_loaded_rsc_table, }; static int k3_dsp_rproc_of_get_memories(struct platform_device *pdev, @@ -592,6 +710,8 @@ struct k3_dsp_rproc *kproc; struct rproc *rproc; const char *fw_name; + bool r_state = false; + bool p_state = false; int ret = 0; int ret1; @@ -670,19 +790,37 @@ goto release_tsp; } - /* - * ensure the DSP local reset is asserted to ensure the DSP doesn't - * execute bogus code in .prepare() when the module reset is released. - */ - if (data->uses_lreset) { - ret = reset_control_status(kproc->reset); - if (ret < 0) { - dev_err(dev, "failed to get reset status, status = %d\n", - ret); - goto release_mem; - } else if (ret == 0) { - dev_warn(dev, "local reset is deasserted for device\n"); - k3_dsp_rproc_reset(kproc); + ret = kproc->ti_sci->ops.dev_ops.is_on(kproc->ti_sci, kproc->ti_sci_id, + &r_state, &p_state); + if (ret) { + dev_err(dev, "failed to get initial state, mode cannot be determined, ret = %d\n", + ret); + goto release_mem; + } + + /* configure J721E devices for either remoteproc or IPC-only mode */ + if (p_state) { + dev_err(dev, "configured DSP for IPC-only mode\n"); + rproc->state = RPROC_DETACHED; + rproc->detach_on_shutdown = true; + kproc->ipc_only = true; + } else { + dev_err(dev, "configured DSP for remoteproc mode\n"); + /* + * ensure the DSP local reset is asserted to ensure the DSP + * doesn't execute bogus code in .prepare() when the module + * reset is released. + */ + if (data->uses_lreset) { + ret = reset_control_status(kproc->reset); + if (ret < 0) { + dev_err(dev, "failed to get reset status, status = %d\n", + ret); + goto release_mem; + } else if (ret == 0) { + dev_warn(dev, "local reset is deasserted for device\n"); + k3_dsp_rproc_reset(kproc); + } } } diff -Naur --no-dereference a/drivers/remoteproc/ti_k3_m4_remoteproc.c b/drivers/remoteproc/ti_k3_m4_remoteproc.c --- a/drivers/remoteproc/ti_k3_m4_remoteproc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/remoteproc/ti_k3_m4_remoteproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI K3 Cortex-M4 Remote Processor(s) driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ + * Hari Nagalla + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "omap_remoteproc.h" +#include "remoteproc_internal.h" +#include "ti_sci_proc.h" + +#define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1) + +/** + * struct k3_m4_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address of the memory region from DSP view + * @size: Size of the memory region + */ +struct k3_m4_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; +}; + +/** + * struct k3_m4_mem_data - memory definitions for a DSP + * @name: name for this memory entry + * @dev_addr: device address for the memory entry + */ +struct k3_m4_mem_data { + const char *name; + const u32 dev_addr; +}; + +/** + * struct k3_m4_dev_data - device data structure for a DSP + * @mems: pointer to memory definitions for a DSP + * @num_mems: number of memory regions in @mems + * @boot_align_addr: boot vector address alignment granularity + * @uses_lreset: flag to denote the need for local reset management + */ +struct k3_m4_dev_data { + const struct k3_m4_mem_data *mems; + u32 num_mems; + u32 boot_align_addr; + bool uses_lreset; +}; + +/** + * struct k3_m4_rproc - k3 M4 remote processor driver structure + * @dev: cached device pointer + * @rproc: remoteproc device handle + * @mem: internal memory regions data + * @num_mems: number of internal memory regions + * @rmem: reserved memory regions data + * @num_rmems: number of reserved memory regions + * @reset: reset control handle + * @data: pointer to M4-specific device data + * @tsp: TI-SCI processor control handle + * @ti_sci: TI-SCI handle + * @ti_sci_id: TI-SCI device identifier + * @mbox: mailbox channel handle + * @client: mailbox client to request the mailbox channel + * @ipc_only: flag to indicate IPC-only mode + */ +struct k3_m4_rproc { + struct device *dev; + struct rproc *rproc; + struct k3_m4_mem *mem; + int num_mems; + struct k3_m4_mem *rmem; + int num_rmems; + struct reset_control *reset; + const struct k3_m4_dev_data *data; + struct ti_sci_proc *tsp; + const struct ti_sci_handle *ti_sci; + u32 ti_sci_id; + struct mbox_chan *mbox; + struct mbox_client client; + bool ipc_only; +}; + +/** + * k3_m4_rproc_mbox_callback() - inbound mailbox message handler + * @client: mailbox client pointer used for requesting the mailbox channel + * @data: mailbox payload + * + * This handler is invoked by the OMAP mailbox driver whenever a mailbox + * message is received. Usually, the mailbox payload simply contains + * the index of the virtqueue that is kicked by the remote processor, + * and we let remoteproc core handle it. + * + * In addition to virtqueue indices, we also have some out-of-band values + * that indicate different events. Those values are deliberately very + * large so they don't coincide with virtqueue indices. + */ +static void k3_m4_rproc_mbox_callback(struct mbox_client *client, void *data) +{ + struct k3_m4_rproc *kproc = container_of(client, struct k3_m4_rproc, + client); + struct device *dev = kproc->rproc->dev.parent; + const char *name = kproc->rproc->name; + u32 msg = omap_mbox_message(data); + + dev_dbg(dev, "mbox msg: 0x%x\n", msg); + + switch (msg) { + case RP_MBOX_CRASH: + /* + * remoteproc detected an exception, but error recovery is not + * supported. So, just log this for now + */ + dev_err(dev, "K3 M4 rproc %s crashed\n", name); + break; + case RP_MBOX_ECHO_REPLY: + dev_info(dev, "received echo reply from %s\n", name); + break; + default: + /* silently handle all other valid messages */ + if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) + return; + if (msg > kproc->rproc->max_notifyid) { + dev_dbg(dev, "dropping unknown message 0x%x", msg); + return; + } + /* msg contains the index of the triggered vring */ + if (rproc_vq_interrupt(kproc->rproc, msg) == IRQ_NONE) + dev_dbg(dev, "no message was found in vqid %d\n", msg); + } +} + +/* + * Kick the remote processor to notify about pending unprocessed messages. + * The vqid usage is not used and is inconsequential, as the kick is performed + * through a simulated GPIO (a bit in an IPC interrupt-triggering register), + * the remote processor is expected to process both its Tx and Rx virtqueues. + */ +static void k3_m4_rproc_kick(struct rproc *rproc, int vqid) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = rproc->dev.parent; + mbox_msg_t msg = (mbox_msg_t)vqid; + int ret; + + /* send the index of the triggered virtqueue in the mailbox payload */ + ret = mbox_send_message(kproc->mbox, (void *)msg); + if (ret < 0) + dev_err(dev, "failed to send mailbox message, status = %d\n", + ret); +} + +/* Put the M4 processor into reset */ +static int k3_m4_rproc_reset(struct k3_m4_rproc *kproc) +{ + struct device *dev = kproc->dev; + int ret; + + ret = reset_control_assert(kproc->reset); + if (ret) { + dev_err(dev, "local-reset assert failed, ret = %d\n", ret); + return ret; + } + + if (kproc->data->uses_lreset) + return ret; + + ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, + kproc->ti_sci_id); + if (ret) { + dev_err(dev, "module-reset assert failed, ret = %d\n", ret); + if (reset_control_deassert(kproc->reset)) + dev_warn(dev, "local-reset deassert back failed\n"); + } + + return ret; +} + +/* Release the M4 processor from reset */ +static int k3_m4_rproc_release(struct k3_m4_rproc *kproc) +{ + struct device *dev = kproc->dev; + int ret; + + if (kproc->data->uses_lreset) + goto lreset; + + ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci, + kproc->ti_sci_id); + if (ret) { + dev_err(dev, "module-reset deassert failed, ret = %d\n", ret); + return ret; + } + + dev_info(dev, "released m4 reset\n"); + +lreset: + ret = reset_control_deassert(kproc->reset); + if (ret) { + dev_err(dev, "local-reset deassert failed, ret = %d\n", ret); + if (kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, + kproc->ti_sci_id)) + dev_warn(dev, "module-reset assert back failed\n"); + } + + return ret; +} + +static int k3_m4_rproc_request_mbox(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct mbox_client *client = &kproc->client; + struct device *dev = kproc->dev; + int ret; + + client->dev = dev; + client->tx_done = NULL; + client->rx_callback = k3_m4_rproc_mbox_callback; + client->tx_block = false; + client->knows_txdone = false; + + kproc->mbox = mbox_request_channel(client, 0); + if (IS_ERR(kproc->mbox)) { + ret = -EBUSY; + dev_err(dev, "mbox_request_channel failed: %ld\n", + PTR_ERR(kproc->mbox)); + return ret; + } + + /* + * Ping the remote processor, this is only for sanity-sake for now; + * there is no functional effect whatsoever. + * + * Note that the reply will _not_ arrive immediately: this message + * will wait in the mailbox fifo until the remote processor is booted. + */ + ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); + if (ret < 0) { + dev_err(dev, "mbox_send_message failed: %d\n", ret); + mbox_free_channel(kproc->mbox); + return ret; + } + + return 0; +} + +/* + * The M4F cores have a local reset that affects only the CPU, and a + * generic module reset that powers on the device and allows the M4 internal + * memories to be accessed while the local reset is asserted. This function is + * used to release the global reset on M4F to allow loading into the M4F + * internal RAMs. The .prepare() ops is invoked by remoteproc core before any + * firmware loading, and is followed by the .start() ops after loading to + * actually let the M4F core run. + */ +static int k3_m4_rproc_prepare(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + /* IPC-only mode does not require the core to be released from reset */ + if (kproc->ipc_only) + return 0; + + ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci, + kproc->ti_sci_id); + if (ret) + dev_err(dev, "module-reset deassert failed, cannot enable internal RAM loading, ret = %d\n", + ret); + + return ret; +} + +/* + * This function implements the .unprepare() ops and performs the complimentary + * operations to that of the .prepare() ops. The function is used to assert the + * global reset on applicable M4F cores. This completes the second portion of + * powering down the M4F cores. The cores themselves are only halted in the + * .stop() callback through the local reset, and the .unprepare() ops is invoked + * by the remoteproc core after the remoteproc is stopped to balance the global + * reset. + */ +static int k3_m4_rproc_unprepare(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + /* do not put back the cores into reset in IPC-only mode */ + if (kproc->ipc_only) + return 0; + + ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, + kproc->ti_sci_id); + if (ret) + dev_err(dev, "module-reset assert failed, ret = %d\n", ret); + + return ret; +} + +/* + * Power up the M4F remote processor. + * + * This function will be invoked only after the firmware for this rproc + * was loaded, parsed successfully, and all of its resource requirements + * were met. + */ +static int k3_m4_rproc_start(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + u32 boot_addr; + int ret; + + if (kproc->ipc_only) { + dev_err(dev, "%s cannot be invoked in IPC-only mode\n", + __func__); + return -EINVAL; + } + + ret = k3_m4_rproc_request_mbox(rproc); + if (ret) + return ret; + + boot_addr = rproc->bootaddr; + ret = k3_m4_rproc_release(kproc); + if (ret) + goto put_mbox; + + return 0; + +put_mbox: + mbox_free_channel(kproc->mbox); + return ret; +} + +/* + * Stop the M4 remote processor. + * + * This function puts the M4 processor into reset, and finishes processing + * of any pending messages. + */ +static int k3_m4_rproc_stop(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (kproc->ipc_only) { + dev_err(dev, "%s cannot be invoked in IPC-only mode\n", + __func__); + return -EINVAL; + } + + mbox_free_channel(kproc->mbox); + + k3_m4_rproc_reset(kproc); + + return 0; +} + +/* + * Attach to a running M4 remote processor (IPC-only mode) + * + * This rproc attach callback only needs to request the mailbox, the remote + * processor is already booted, so there is no need to issue any TI-SCI + * commands to boot the M4 core. + */ +static int k3_m4_rproc_attach(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + if (!kproc->ipc_only || rproc->state != RPROC_DETACHED) { + dev_err(dev, "M4 is expected to be in IPC-only mode and RPROC_DETACHED state\n"); + return -EINVAL; + } + + ret = k3_m4_rproc_request_mbox(rproc); + if (ret) + return ret; + + dev_err(dev, "M4 initialized in IPC-only mode\n"); + return 0; +} + +/* + * Detach from a running M4 remote processor (IPC-only mode) + * + * This rproc detach callback performs the opposite operation to attach callback + * and only needs to release the mailbox, the M4 core is not stopped and will + * be left to continue to run its booted firmware. + */ +static int k3_m4_rproc_detach(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->ipc_only || rproc->state != RPROC_ATTACHED) { + dev_err(dev, "M4 is expected to be in IPC-only mode and RPROC_ATTACHED state\n"); + return -EINVAL; + } + + mbox_free_channel(kproc->mbox); + dev_err(dev, "M4 deinitialized in IPC-only mode\n"); + return 0; +} + +/* + * This function implements the .get_loaded_rsc_table() callback and is used + * to provide the resource table for a booted M4 in IPC-only mode. The K3 M4 + * firmwares follow a design-by-contract approach and are expected to have the + * resource table at the base of the DDR region reserved for firmware usage. + * This provides flexibility for the remote processor to be booted by different + * bootloaders that may or may not have the ability to publish the resource table + * address and size through a DT property. + */ +static struct resource_table *k3_m4_get_loaded_rsc_table(struct rproc *rproc, + size_t *rsc_table_sz) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->rmem[0].cpu_addr) { + dev_err(dev, "memory-region #1 does not exist, loaded rsc table can't be found"); + return ERR_PTR(-ENOMEM); + } + + /* + * NOTE: The resource table size is currently hard-coded to a maximum + * of 256 bytes. The most common resource table usage for K3 firmwares + * is to only have the vdev resource entry and an optional trace entry. + * The exact size could be computed based on resource table address, but + * the hard-coded value suffices to support the IPC-only mode. + */ + *rsc_table_sz = 256; + return (struct resource_table *)kproc->rmem[0].cpu_addr; +} + +/* + * Custom function to translate a M4 device address (internal RAMs only) to a + * kernel virtual address. The M4s can access their RAMs at either an internal + * address visible only from a M4, or at the SoC-level bus address. Both these + * addresses need to be looked through for translation. The translated addresses + * can be used either by the remoteproc core for loading (when using kernel + * remoteproc loader), or by any rpmsg bus drivers. + */ +static void *k3_m4_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) +{ + struct k3_m4_rproc *kproc = rproc->priv; + void __iomem *va = NULL; + phys_addr_t bus_addr; + u32 dev_addr, offset; + size_t size; + int i; + + if (len == 0) + return NULL; + + for (i = 0; i < kproc->num_mems; i++) { + bus_addr = kproc->mem[i].bus_addr; + dev_addr = kproc->mem[i].dev_addr; + size = kproc->mem[i].size; + + if (da < KEYSTONE_RPROC_LOCAL_ADDRESS_MASK) { + /* handle M4-view addresses */ + if (da >= dev_addr && + ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = kproc->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } else { + /* handle SoC-view addresses */ + if (da >= bus_addr && + (da + len) <= (bus_addr + size)) { + offset = da - bus_addr; + va = kproc->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + } + + /* handle static DDR reserved memory regions */ + for (i = 0; i < kproc->num_rmems; i++) { + dev_addr = kproc->rmem[i].dev_addr; + size = kproc->rmem[i].size; + + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = kproc->rmem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + return NULL; +} + +static const struct rproc_ops k3_m4_rproc_ops = { + .start = k3_m4_rproc_start, + .stop = k3_m4_rproc_stop, + .attach = k3_m4_rproc_attach, + .detach = k3_m4_rproc_detach, + .kick = k3_m4_rproc_kick, + .da_to_va = k3_m4_rproc_da_to_va, + .get_loaded_rsc_table = k3_m4_get_loaded_rsc_table, +}; + +static int k3_m4_rproc_of_get_memories(struct platform_device *pdev, + struct k3_m4_rproc *kproc) +{ + const struct k3_m4_dev_data *data = kproc->data; + struct device *dev = &pdev->dev; + struct resource *res; + int num_mems = 0; + int i; + + num_mems = kproc->data->num_mems; + kproc->mem = devm_kcalloc(kproc->dev, num_mems, + sizeof(*kproc->mem), GFP_KERNEL); + if (!kproc->mem) + return -ENOMEM; + + for (i = 0; i < num_mems; i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + data->mems[i].name); + if (!res) { + dev_err(dev, "found no memory resource for %s\n", + data->mems[i].name); + return -EINVAL; + } + if (!devm_request_mem_region(dev, res->start, + resource_size(res), + dev_name(dev))) { + dev_err(dev, "could not request %s region for resource\n", + data->mems[i].name); + return -EBUSY; + } + + kproc->mem[i].cpu_addr = devm_ioremap_wc(dev, res->start, + resource_size(res)); + if (!kproc->mem[i].cpu_addr) { + dev_err(dev, "failed to map %s memory\n", + data->mems[i].name); + return -ENOMEM; + } + kproc->mem[i].bus_addr = res->start; + kproc->mem[i].dev_addr = data->mems[i].dev_addr; + kproc->mem[i].size = resource_size(res); + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %pK da 0x%x\n", + data->mems[i].name, &kproc->mem[i].bus_addr, + kproc->mem[i].size, kproc->mem[i].cpu_addr, + kproc->mem[i].dev_addr); + } + kproc->num_mems = num_mems; + + return 0; +} + +static int k3_m4_reserved_mem_init(struct k3_m4_rproc *kproc) +{ + struct device *dev = kproc->dev; + struct device_node *np = dev->of_node; + struct device_node *rmem_np; + struct reserved_mem *rmem; + int num_rmems; + int ret, i; + + num_rmems = of_property_count_elems_of_size(np, "memory-region", + sizeof(phandle)); + if (num_rmems <= 0) { + dev_err(dev, "device does not reserved memory regions, ret = %d\n", + num_rmems); + return -EINVAL; + } + if (num_rmems < 2) { + dev_err(dev, "device needs atleast two memory regions to be defined, num = %d\n", + num_rmems); + return -EINVAL; + } + + /* use reserved memory region 0 for vring DMA allocations */ + ret = of_reserved_mem_device_init_by_idx(dev, np, 0); + if (ret) { + dev_err(dev, "device cannot initialize DMA pool, ret = %d\n", + ret); + return ret; + } + + num_rmems--; + kproc->rmem = kcalloc(num_rmems, sizeof(*kproc->rmem), GFP_KERNEL); + if (!kproc->rmem) { + ret = -ENOMEM; + goto release_rmem; + } + + /* use remaining reserved memory regions for static carveouts */ + for (i = 0; i < num_rmems; i++) { + rmem_np = of_parse_phandle(np, "memory-region", i + 1); + if (!rmem_np) { + ret = -EINVAL; + goto unmap_rmem; + } + + rmem = of_reserved_mem_lookup(rmem_np); + if (!rmem) { + of_node_put(rmem_np); + ret = -EINVAL; + goto unmap_rmem; + } + of_node_put(rmem_np); + + kproc->rmem[i].bus_addr = rmem->base; + /* 64-bit address regions currently not supported */ + kproc->rmem[i].dev_addr = (u32)rmem->base; + kproc->rmem[i].size = rmem->size; + kproc->rmem[i].cpu_addr = ioremap_wc(rmem->base, rmem->size); + if (!kproc->rmem[i].cpu_addr) { + dev_err(dev, "failed to map reserved memory#%d at %pa of size %pa\n", + i + 1, &rmem->base, &rmem->size); + ret = -ENOMEM; + goto unmap_rmem; + } + + dev_dbg(dev, "reserved memory%d: bus addr %pa size 0x%zx va %pK da 0x%x\n", + i + 1, &kproc->rmem[i].bus_addr, + kproc->rmem[i].size, kproc->rmem[i].cpu_addr, + kproc->rmem[i].dev_addr); + } + kproc->num_rmems = num_rmems; + + return 0; + +unmap_rmem: + for (i--; i >= 0; i--) + iounmap(kproc->rmem[i].cpu_addr); + kfree(kproc->rmem); +release_rmem: + of_reserved_mem_device_release(kproc->dev); + return ret; +} + +static void k3_m4_reserved_mem_exit(struct k3_m4_rproc *kproc) +{ + int i; + + for (i = 0; i < kproc->num_rmems; i++) + iounmap(kproc->rmem[i].cpu_addr); + kfree(kproc->rmem); + + of_reserved_mem_device_release(kproc->dev); +} + +static struct ti_sci_proc *k3_m4_rproc_of_get_tsp(struct device *dev, + const struct ti_sci_handle *sci) +{ + struct ti_sci_proc *tsp; + u32 temp[2]; + int ret; + + ret = of_property_read_u32_array(dev->of_node, "ti,sci-proc-ids", + temp, 2); + if (ret < 0) + return ERR_PTR(ret); + + tsp = kzalloc(sizeof(*tsp), GFP_KERNEL); + if (!tsp) + return ERR_PTR(-ENOMEM); + + tsp->dev = dev; + tsp->sci = sci; + tsp->ops = &sci->ops.proc_ops; + tsp->proc_id = temp[0]; + tsp->host_id = temp[1]; + + return tsp; +} + +static int k3_m4_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct k3_m4_dev_data *data; + struct k3_m4_rproc *kproc; + struct rproc *rproc; + const char *fw_name; + bool r_state = false; + bool p_state = false; + int ret = 0; + int ret1; + + data = of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + ret = rproc_of_parse_firmware(dev, 0, &fw_name); + if (ret) { + dev_err(dev, "failed to parse firmware-name property, ret = %d\n", + ret); + return ret; + } + + rproc = rproc_alloc(dev, dev_name(dev), &k3_m4_rproc_ops, fw_name, + sizeof(*kproc)); + if (!rproc) + return -ENOMEM; + + rproc->has_iommu = false; + rproc->recovery_disabled = true; + if (data->uses_lreset) { + rproc->ops->prepare = k3_m4_rproc_prepare; + rproc->ops->unprepare = k3_m4_rproc_unprepare; + } + kproc = rproc->priv; + kproc->rproc = rproc; + kproc->dev = dev; + kproc->data = data; + + kproc->ti_sci = ti_sci_get_by_phandle(np, "ti,sci"); + if (IS_ERR(kproc->ti_sci)) { + ret = PTR_ERR(kproc->ti_sci); + if (ret != -EPROBE_DEFER) { + dev_err(dev, "failed to get ti-sci handle, ret = %d\n", + ret); + } + kproc->ti_sci = NULL; + goto free_rproc; + } + + ret = of_property_read_u32(np, "ti,sci-dev-id", &kproc->ti_sci_id); + if (ret) { + dev_err(dev, "missing 'ti,sci-dev-id' property\n"); + goto put_sci; + } + + kproc->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(kproc->reset)) { + ret = PTR_ERR(kproc->reset); + dev_err(dev, "failed to get reset, status = %d\n", ret); + goto put_sci; + } + + kproc->tsp = k3_m4_rproc_of_get_tsp(dev, kproc->ti_sci); + if (IS_ERR(kproc->tsp)) { + dev_err(dev, "failed to construct ti-sci proc control, ret = %d\n", + ret); + ret = PTR_ERR(kproc->tsp); + goto put_sci; + } + + ret = ti_sci_proc_request(kproc->tsp); + if (ret < 0) { + dev_err(dev, "ti_sci_proc_request failed, ret = %d\n", ret); + goto free_tsp; + } + + ret = k3_m4_rproc_of_get_memories(pdev, kproc); + if (ret) + goto release_tsp; + + ret = k3_m4_reserved_mem_init(kproc); + if (ret) { + dev_err(dev, "reserved memory init failed, ret = %d\n", ret); + goto release_tsp; + } + + ret = kproc->ti_sci->ops.dev_ops.is_on(kproc->ti_sci, kproc->ti_sci_id, + &r_state, &p_state); + if (ret) { + dev_err(dev, "failed to get initial state, mode cannot be determined, ret = %d\n", + ret); + goto release_mem; + } + + /* configure devices for either remoteproc or IPC-only mode */ + if (p_state) { + dev_err(dev, "configured M4 for IPC-only mode\n"); + rproc->state = RPROC_DETACHED; + rproc->detach_on_shutdown = true; + kproc->ipc_only = true; + } else { + dev_err(dev, "configured M4 for remoteproc mode\n"); + /* + * ensure the M4 local reset is asserted to ensure the core + * doesn't execute bogus code in .prepare() when the module + * reset is released. + */ + if (data->uses_lreset) { + ret = reset_control_status(kproc->reset); + if (ret < 0) { + dev_err(dev, "failed to get reset status, status = %d\n", + ret); + goto release_mem; + } else if (ret == 0) { + dev_warn(dev, "local reset is deasserted for device\n"); + k3_m4_rproc_reset(kproc); + } + } + } + + ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "failed to add register device with remoteproc core, status = %d\n", + ret); + goto release_mem; + } + + platform_set_drvdata(pdev, kproc); + + return 0; + +release_mem: + k3_m4_reserved_mem_exit(kproc); +release_tsp: + ret1 = ti_sci_proc_release(kproc->tsp); + if (ret1) + dev_err(dev, "failed to release proc, ret = %d\n", ret1); +free_tsp: + kfree(kproc->tsp); +put_sci: + ret1 = ti_sci_put_handle(kproc->ti_sci); + if (ret1) + dev_err(dev, "failed to put ti_sci handle, ret = %d\n", ret1); +free_rproc: + rproc_free(rproc); + return ret; +} + +static int k3_m4_rproc_remove(struct platform_device *pdev) +{ + struct k3_m4_rproc *kproc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + rproc_del(kproc->rproc); + + ret = ti_sci_proc_release(kproc->tsp); + if (ret) + dev_err(dev, "failed to release proc, ret = %d\n", ret); + + kfree(kproc->tsp); + + ret = ti_sci_put_handle(kproc->ti_sci); + if (ret) + dev_err(dev, "failed to put ti_sci handle, ret = %d\n", ret); + + k3_m4_reserved_mem_exit(kproc); + rproc_free(kproc->rproc); + + return 0; +} + +static const struct k3_m4_mem_data am64_m4_mems[] = { + { .name = "iram", .dev_addr = 0x0 }, + { .name = "dram", .dev_addr = 0x30000 }, +}; + +static const struct k3_m4_dev_data am64_m4_data = { + .mems = am64_m4_mems, + .num_mems = ARRAY_SIZE(am64_m4_mems), + .boot_align_addr = SZ_1K, + .uses_lreset = true, +}; + +static const struct of_device_id k3_m4_of_match[] = { + { .compatible = "ti,am64-m4fss", .data = &am64_m4_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, k3_m4_of_match); + +static struct platform_driver k3_m4_rproc_driver = { + .probe = k3_m4_rproc_probe, + .remove = k3_m4_rproc_remove, + .driver = { + .name = "k3-m4-rproc", + .of_match_table = k3_m4_of_match, + }, +}; + +module_platform_driver(k3_m4_rproc_driver); + +MODULE_AUTHOR("Hari Nagalla "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI K3 M4 Remoteproc driver"); diff -Naur --no-dereference a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -38,6 +38,10 @@ #define PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE 0x00000800 #define PROC_BOOT_CFG_FLAG_R5_BTCM_EN 0x00001000 #define PROC_BOOT_CFG_FLAG_R5_ATCM_EN 0x00002000 +/* Available from J7200 SoCs onwards */ +#define PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS 0x00004000 +/* Applicable to only AM64x SoCs */ +#define PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE 0x00008000 /* R5 TI-SCI Processor Control Flags */ #define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT 0x00000001 @@ -47,6 +51,8 @@ #define PROC_BOOT_STATUS_FLAG_R5_WFI 0x00000002 #define PROC_BOOT_STATUS_FLAG_R5_CLK_GATED 0x00000004 #define PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED 0x00000100 +/* Applicable to only AM64x SoCs */ +#define PROC_BOOT_STATUS_FLAG_R5_SINGLECORE_ONLY 0x00000200 /** * struct k3_r5_mem - internal memory structure @@ -62,9 +68,29 @@ size_t size; }; +/* + * All cluster mode values are not applicable on all SoCs. The following + * are the modes supported on various SoCs: + * Split mode : AM65x, J721E, J7200 and AM64x SoCs + * LockStep mode : AM65x, J721E and J7200 SoCs + * Single-CPU mode : AM64x SoCs only + */ enum cluster_mode { CLUSTER_MODE_SPLIT = 0, CLUSTER_MODE_LOCKSTEP, + CLUSTER_MODE_SINGLECPU, +}; + +/** + * struct k3_r5_soc_data - match data to handle SoC variations + * @tcm_is_double: flag to denote the larger unified TCMs in certain modes + * @tcm_ecc_autoinit: flag to denote the auto-initialization of TCMs for ECC + * @single_cpu_mode: flag to denote if SoC/IP supports Single-CPU mode + */ +struct k3_r5_soc_data { + bool tcm_is_double; + bool tcm_ecc_autoinit; + bool single_cpu_mode; }; /** @@ -72,11 +98,13 @@ * @dev: cached device pointer * @mode: Mode to configure the Cluster - Split or LockStep * @cores: list of R5 cores within the cluster + * @soc_data: SoC-specific feature data for a R5FSS */ struct k3_r5_cluster { struct device *dev; enum cluster_mode mode; struct list_head cores; + const struct k3_r5_soc_data *soc_data; }; /** @@ -123,6 +151,7 @@ * @core: cached pointer to r5 core structure being used * @rmem: reserved memory regions data * @num_rmems: number of reserved memory regions + * @ipc_only: flag to indicate IPC-only mode */ struct k3_r5_rproc { struct device *dev; @@ -133,6 +162,7 @@ struct k3_r5_core *core; struct k3_r5_mem *rmem; int num_rmems; + bool ipc_only; }; /** @@ -348,6 +378,44 @@ 0, PROC_BOOT_CTRL_FLAG_R5_CORE_HALT); } +static int k3_r5_rproc_request_mbox(struct rproc *rproc) +{ + struct k3_r5_rproc *kproc = rproc->priv; + struct mbox_client *client = &kproc->client; + struct device *dev = kproc->dev; + int ret; + + client->dev = dev; + client->tx_done = NULL; + client->rx_callback = k3_r5_rproc_mbox_callback; + client->tx_block = false; + client->knows_txdone = false; + + kproc->mbox = mbox_request_channel(client, 0); + if (IS_ERR(kproc->mbox)) { + ret = -EBUSY; + dev_err(dev, "mbox_request_channel failed: %ld\n", + PTR_ERR(kproc->mbox)); + return ret; + } + + /* + * Ping the remote processor, this is only for sanity-sake for now; + * there is no functional effect whatsoever. + * + * Note that the reply will _not_ arrive immediately: this message + * will wait in the mailbox fifo until the remote processor is booted. + */ + ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); + if (ret < 0) { + dev_err(dev, "mbox_send_message failed: %d\n", ret); + mbox_free_channel(kproc->mbox); + return ret; + } + + return 0; +} + /* * The R5F cores have controls for both a reset and a halt/run. The code * execution from DDR requires the initial boot-strapping code to be run @@ -355,6 +423,13 @@ * applicable cores to allow loading into the TCMs. The .prepare() ops is * invoked by remoteproc core before any firmware loading, and is followed * by the .start() ops after loading to actually let the R5 cores run. + * + * The Single-CPU mode on applicable SoCs (eg: AM64x) only uses Core0 to + * execute code, but combines the TCMs from both cores. The resets for both + * cores need to be released to make this possible, as the TCMs are in general + * private to each core. Only Core0 needs to be unhalted for running the + * cluster in this mode. The function uses the same reset logic as LockStep + * mode for this (though the behavior is agnostic of the reset release order). */ static int k3_r5_rproc_prepare(struct rproc *rproc) { @@ -362,9 +437,23 @@ struct k3_r5_cluster *cluster = kproc->cluster; struct k3_r5_core *core = kproc->core; struct device *dev = kproc->dev; + u32 ctrl = 0, cfg = 0, stat = 0; + u64 boot_vec = 0; + bool mem_init_dis; int ret; - ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ? + /* IPC-only mode does not require the cores to be released from reset */ + if (kproc->ipc_only) + return 0; + + ret = ti_sci_proc_get_status(core->tsp, &boot_vec, &cfg, &ctrl, &stat); + if (ret < 0) + return ret; + mem_init_dis = !!(cfg & PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS); + + /* Re-use LockStep-mode reset logic for Single-CPU mode */ + ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP || + cluster->mode == CLUSTER_MODE_SINGLECPU) ? k3_r5_lockstep_release(cluster) : k3_r5_split_release(core); if (ret) { dev_err(dev, "unable to enable cores for TCM loading, ret = %d\n", @@ -373,6 +462,17 @@ } /* + * Newer IP revisions like on J7200 SoCs support h/w auto-initialization + * of TCMs, so there is no need to perform the s/w memzero. This bit is + * configurable through System Firmware, the default value does perform + * auto-init, but account for it in case it is disabled + */ + if (cluster->soc_data->tcm_ecc_autoinit && !mem_init_dis) { + dev_dbg(dev, "leveraging h/w init for TCM memories\n"); + return 0; + } + + /* * Zero out both TCMs unconditionally (access from v8 Arm core is not * affected by ATCM & BTCM enable configuration values) so that ECC * can be effective on all TCM addresses. @@ -394,6 +494,12 @@ * cores. The cores themselves are only halted in the .stop() ops, and the * .unprepare() ops is invoked by the remoteproc core after the remoteproc is * stopped. + * + * The Single-CPU mode on applicable SoCs (eg: AM64x) combines the TCMs from + * both cores. The access is made possible only with releasing the resets for + * both cores, but with only Core0 unhalted. This function re-uses the same + * reset assert logic as LockStep mode for this mode (though the behavior is + * agnostic of the reset assert order). */ static int k3_r5_rproc_unprepare(struct rproc *rproc) { @@ -403,7 +509,13 @@ struct device *dev = kproc->dev; int ret; - ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ? + /* do not put back the cores into reset in IPC-only mode */ + if (kproc->ipc_only) + return 0; + + /* Re-use LockStep-mode reset logic for Single-CPU mode */ + ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP || + cluster->mode == CLUSTER_MODE_SINGLECPU) ? k3_r5_lockstep_reset(cluster) : k3_r5_split_reset(core); if (ret) dev_err(dev, "unable to disable cores, ret = %d\n", ret); @@ -422,43 +534,29 @@ * first followed by Core0. The Split-mode requires that Core0 to be maintained * always in a higher power state that Core1 (implying Core1 needs to be started * always only after Core0 is started). + * + * The Single-CPU mode on applicable SoCs (eg: AM64x) only uses Core0 to execute + * code, so only Core0 needs to be unhalted. The function uses the same logic + * flow as Split-mode for this. */ static int k3_r5_rproc_start(struct rproc *rproc) { struct k3_r5_rproc *kproc = rproc->priv; struct k3_r5_cluster *cluster = kproc->cluster; - struct mbox_client *client = &kproc->client; struct device *dev = kproc->dev; struct k3_r5_core *core; u32 boot_addr; int ret; - client->dev = dev; - client->tx_done = NULL; - client->rx_callback = k3_r5_rproc_mbox_callback; - client->tx_block = false; - client->knows_txdone = false; - - kproc->mbox = mbox_request_channel(client, 0); - if (IS_ERR(kproc->mbox)) { - ret = -EBUSY; - dev_err(dev, "mbox_request_channel failed: %ld\n", - PTR_ERR(kproc->mbox)); - return ret; + if (kproc->ipc_only) { + dev_err(dev, "%s cannot be invoked in IPC-only mode\n", + __func__); + return -EINVAL; } - /* - * Ping the remote processor, this is only for sanity-sake for now; - * there is no functional effect whatsoever. - * - * Note that the reply will _not_ arrive immediately: this message - * will wait in the mailbox fifo until the remote processor is booted. - */ - ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); - if (ret < 0) { - dev_err(dev, "mbox_send_message failed: %d\n", ret); - goto put_mbox; - } + ret = k3_r5_rproc_request_mbox(rproc); + if (ret) + return ret; boot_addr = rproc->bootaddr; /* TODO: add boot_addr sanity checking */ @@ -506,6 +604,10 @@ * Core0 to be maintained always in a higher power state that Core1 (implying * Core1 needs to be stopped first before Core0). * + * The Single-CPU mode on applicable SoCs (eg: AM64x) only uses Core0 to execute + * code, so only Core0 needs to be halted. The function uses the same logic + * flow as Split-mode for this. + * * Note that the R5F halt operation in general is not effective when the R5F * core is running, but is needed to make sure the core won't run after * deasserting the reset the subsequent time. The asserting of reset can @@ -518,9 +620,16 @@ { struct k3_r5_rproc *kproc = rproc->priv; struct k3_r5_cluster *cluster = kproc->cluster; + struct device *dev = kproc->dev; struct k3_r5_core *core = kproc->core; int ret; + if (kproc->ipc_only) { + dev_err(dev, "%s cannot be invoked in IPC-only mode\n", + __func__); + return -EINVAL; + } + /* halt all applicable cores */ if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { list_for_each_entry(core, &cluster->cores, elem) { @@ -550,6 +659,85 @@ } /* + * Attach to a running R5F remote processor (IPC-only mode) + * + * The R5F attach callback only needs to request the mailbox, the remote + * processor is already booted, so there is no need to issue any TI-SCI + * commands to boot the R5F cores in IPC-only mode. + */ +static int k3_r5_rproc_attach(struct rproc *rproc) +{ + struct k3_r5_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + if (!kproc->ipc_only || rproc->state != RPROC_DETACHED) { + dev_err(dev, "R5F is expected to be in IPC-only mode and RPROC_DETACHED state\n"); + return -EINVAL; + } + + ret = k3_r5_rproc_request_mbox(rproc); + if (ret) + return ret; + + dev_err(dev, "R5F core initialized in IPC-only mode\n"); + return 0; +} + +/* + * Detach from a running R5F remote processor (IPC-only mode) + * + * The R5F detach callback performs the opposite operation to attach callback + * and only needs to release the mailbox, the R5F cores are not stopped and + * will be left in booted state in IPC-only mode. + */ +static int k3_r5_rproc_detach(struct rproc *rproc) +{ + struct k3_r5_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->ipc_only || rproc->state != RPROC_ATTACHED) { + dev_err(dev, "R5F is expected to be in IPC-only mode and RPROC_ATTACHED state\n"); + return -EINVAL; + } + + mbox_free_channel(kproc->mbox); + dev_err(dev, "R5F core deinitialized in IPC-only mode\n"); + return 0; +} + +/* + * This function implements the .get_loaded_rsc_table() callback and is used + * to provide the resource table for the booted R5F in IPC-only mode. The K3 R5F + * firmwares follow a design-by-contract approach and are expected to have the + * resource table at the base of the DDR region reserved for firmware usage. + * This provides flexibility for the remote processor to be booted by different + * bootloaders that may or may not have the ability to publish the resource table + * address and size through a DT property. + */ +static struct resource_table *k3_r5_get_loaded_rsc_table(struct rproc *rproc, + size_t *rsc_table_sz) +{ + struct k3_r5_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->rmem[0].cpu_addr) { + dev_err(dev, "memory-region #1 does not exist, loaded rsc table can't be found"); + return ERR_PTR(-ENOMEM); + } + + /* + * NOTE: The resource table size is currently hard-coded to a maximum + * of 256 bytes. The most common resource table usage for K3 firmwares + * is to only have the vdev resource entry and an optional trace entry. + * The exact size could be computed based on resource table address, but + * the hard-coded value suffices to support the IPC-only mode. + */ + *rsc_table_sz = 256; + return (struct resource_table *)kproc->rmem[0].cpu_addr; +} + +/* * Internal Memory translation helper * * Custom function implementing the rproc .da_to_va ops to provide address @@ -623,8 +811,11 @@ .unprepare = k3_r5_rproc_unprepare, .start = k3_r5_rproc_start, .stop = k3_r5_rproc_stop, + .attach = k3_r5_rproc_attach, + .detach = k3_r5_rproc_detach, .kick = k3_r5_rproc_kick, .da_to_va = k3_r5_rproc_da_to_va, + .get_loaded_rsc_table = k3_r5_get_loaded_rsc_table, }; /* @@ -632,7 +823,9 @@ * * Each R5FSS has a cluster-level setting for configuring the processor * subsystem either in a safety/fault-tolerant LockStep mode or a performance - * oriented Split mode. Each R5F core has a number of settings to either + * oriented Split mode on most SoCs. A fewer SoCs support a non-safety mode + * as an alternate for LockStep mode that exercises only a single R5F core + * called Single-CPU mode. Each R5F core has a number of settings to either * enable/disable each of the TCMs, control which TCM appears at the R5F core's * address 0x0. These settings need to be configured before the resets for the * corresponding core are released. These settings are all protected and managed @@ -644,11 +837,13 @@ * the cores are halted before the .prepare() step. * * The function is called from k3_r5_cluster_rproc_init() and is invoked either - * once (in LockStep mode) or twice (in Split mode). Support for LockStep-mode - * is dictated by an eFUSE register bit, and the config settings retrieved from - * DT are adjusted accordingly as per the permitted cluster mode. All cluster - * level settings like Cluster mode and TEINIT (exception handling state - * dictating ARM or Thumb mode) can only be set and retrieved using Core0. + * once (in LockStep mode or Single-CPU modes) or twice (in Split mode). Support + * for LockStep-mode is dictated by an eFUSE register bit, and the config + * settings retrieved from DT are adjusted accordingly as per the permitted + * cluster mode. Another eFUSE register bit dictates if the R5F cluster only + * supports a Single-CPU mode. All cluster level settings like Cluster mode and + * TEINIT (exception handling state dictating ARM or Thumb mode) can only be set + * and retrieved using Core0. * * The function behavior is different based on the cluster mode. The R5F cores * are configured independently as per their individual settings in Split mode. @@ -667,10 +862,16 @@ u32 set_cfg = 0, clr_cfg = 0; u64 boot_vec = 0; bool lockstep_en; + bool single_cpu; int ret; core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); - core = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ? core0 : kproc->core; + if (cluster->mode == CLUSTER_MODE_LOCKSTEP || + cluster->mode == CLUSTER_MODE_SINGLECPU) { + core = core0; + } else { + core = kproc->core; + } ret = ti_sci_proc_get_status(core->tsp, &boot_vec, &cfg, &ctrl, &stat); @@ -680,23 +881,48 @@ dev_dbg(dev, "boot_vector = 0x%llx, cfg = 0x%x ctrl = 0x%x stat = 0x%x\n", boot_vec, cfg, ctrl, stat); + /* check if only Single-CPU mode is supported on applicable SoCs */ + if (cluster->soc_data->single_cpu_mode) { + single_cpu = + !!(stat & PROC_BOOT_STATUS_FLAG_R5_SINGLECORE_ONLY); + if (single_cpu && cluster->mode == CLUSTER_MODE_SPLIT) { + dev_err(cluster->dev, "split-mode not permitted, force configuring for single-cpu mode\n"); + cluster->mode = CLUSTER_MODE_SINGLECPU; + } + goto config; + } + + /* check conventional LockStep vs Split mode configuration */ lockstep_en = !!(stat & PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED); if (!lockstep_en && cluster->mode == CLUSTER_MODE_LOCKSTEP) { dev_err(cluster->dev, "lockstep mode not permitted, force configuring for split-mode\n"); cluster->mode = CLUSTER_MODE_SPLIT; } +config: /* always enable ARM mode and set boot vector to 0 */ boot_vec = 0x0; if (core == core0) { clr_cfg = PROC_BOOT_CFG_FLAG_R5_TEINIT; - /* - * LockStep configuration bit is Read-only on Split-mode _only_ - * devices and system firmware will NACK any requests with the - * bit configured, so program it only on permitted devices - */ - if (lockstep_en) - clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; + if (cluster->soc_data->single_cpu_mode) { + /* + * Single-CPU configuration bit can only be configured + * on Core0 and system firmware will NACK any requests + * with the bit configured, so program it only on + * permitted cores + */ + if (cluster->mode == CLUSTER_MODE_SINGLECPU) + set_cfg = PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE; + } else { + /* + * LockStep configuration bit is Read-only on Split-mode + * _only_ devices and system firmware will NACK any + * requests with the bit configured, so program it only + * on permitted devices + */ + if (lockstep_en) + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; + } } if (core->atcm_enable) @@ -855,6 +1081,147 @@ of_reserved_mem_device_release(kproc->dev); } +/* + * Each R5F core within a typical R5FSS instance has a total of 64 KB of TCMs, + * split equally into two 32 KB banks between ATCM and BTCM. The TCMs from both + * cores are usable in Split-mode, but only the Core0 TCMs can be used in + * LockStep-mode. The newer revisions of the R5FSS IP maximizes these TCMs by + * leveraging the Core1 TCMs as well in certain modes where they would have + * otherwise been unusable (Eg: LockStep-mode on J7200 SoCs, Single-CPU mode on + * AM64x SoCs). This is done by making a Core1 TCM visible immediately after the + * corresponding Core0 TCM. The SoC memory map uses the larger 64 KB sizes for + * the Core0 TCMs, and the dts representation reflects this increased size on + * supported SoCs. The Core0 TCM sizes therefore have to be adjusted to only + * half the original size in Split mode. + */ +static void k3_r5_adjust_tcm_sizes(struct k3_r5_rproc *kproc) +{ + struct k3_r5_cluster *cluster = kproc->cluster; + struct k3_r5_core *core = kproc->core; + struct device *cdev = core->dev; + struct k3_r5_core *core0; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP || + cluster->mode == CLUSTER_MODE_SINGLECPU || + !cluster->soc_data->tcm_is_double) + return; + + core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); + if (core == core0) { + WARN_ON(core->mem[0].size != SZ_64K); + WARN_ON(core->mem[1].size != SZ_64K); + + core->mem[0].size /= 2; + core->mem[1].size /= 2; + + dev_dbg(cdev, "adjusted TCM sizes, ATCM = 0x%zx BTCM = 0x%zx\n", + core->mem[0].size, core->mem[1].size); + } +} + +/* + * This function checks and configures a R5F core for IPC-only or remoteproc + * mode. The driver is configured to be in IPC-only mode for a R5F core when + * the core has been loaded and started by a bootloader. The IPC-only mode is + * detected by querying the System Firmware for reset, power on and halt status + * and ensuring that the core is running. Any incomplete steps at bootloader + * are validated and errored out. + * + * In IPC-only mode, the driver state flags for ATCM, BTCM and LOCZRAMA settings + * and cluster mode parsed originally from kernel DT are updated to reflect the + * actual values configured by bootloader. The driver internal device memory + * addresses for TCMs are also updated. + */ +static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) +{ + struct k3_r5_cluster *cluster = kproc->cluster; + struct k3_r5_core *core = kproc->core; + struct device *cdev = core->dev; + bool r_state = false, c_state = false; + u32 ctrl = 0, cfg = 0, stat = 0, halted = 0; + u64 boot_vec = 0; + u32 atcm_enable, btcm_enable, loczrama; + struct k3_r5_core *core0; + enum cluster_mode mode; + int ret; + + core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); + + ret = core->ti_sci->ops.dev_ops.is_on(core->ti_sci, core->ti_sci_id, + &r_state, &c_state); + if (ret) { + dev_err(cdev, "failed to get initial state, mode cannot be determined, ret = %d\n", + ret); + return ret; + } + if (r_state != c_state) { + dev_warn(cdev, "R5F core may have been powered on by a different host, programmed state (%d) != actual state (%d)\n", + r_state, c_state); + } + + ret = reset_control_status(core->reset); + if (ret < 0) { + dev_err(cdev, "failed to get initial local reset status, ret = %d\n", + ret); + return ret; + } + + ret = ti_sci_proc_get_status(core->tsp, &boot_vec, &cfg, &ctrl, + &stat); + if (ret < 0) { + dev_err(cdev, "failed to get initial processor status, ret = %d\n", + ret); + return ret; + } + atcm_enable = cfg & PROC_BOOT_CFG_FLAG_R5_ATCM_EN ? 1 : 0; + btcm_enable = cfg & PROC_BOOT_CFG_FLAG_R5_BTCM_EN ? 1 : 0; + loczrama = cfg & PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE ? 1 : 0; + if (cluster->soc_data->single_cpu_mode) { + mode = cfg & PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE ? + CLUSTER_MODE_SINGLECPU : CLUSTER_MODE_SPLIT; + } else { + mode = cfg & PROC_BOOT_CFG_FLAG_R5_LOCKSTEP ? + CLUSTER_MODE_LOCKSTEP : CLUSTER_MODE_SPLIT; + } + halted = ctrl & PROC_BOOT_CTRL_FLAG_R5_CORE_HALT; + + /* + * IPC-only mode detection requires both local and module resets to + * be deasserted and R5F core to be unhalted. Local reset status is + * irrelevant if module reset is asserted (POR value has local reset + * deasserted), and is deemed as remoteproc mode + */ + if (c_state && !ret && !halted) { + dev_err(cdev, "configured R5F for IPC-only mode\n"); + kproc->rproc->state = RPROC_DETACHED; + kproc->rproc->detach_on_shutdown = true; + kproc->ipc_only = true; + ret = 1; + } else if (!c_state) { + dev_err(cdev, "configured R5F for remoteproc mode\n"); + ret = 0; + } else { + dev_err(cdev, "mismatched mode: local_reset = %s, module_reset = %s, core_state = %s\n", + !ret ? "deasserted" : "asserted", + c_state ? "deasserted" : "asserted", + halted ? "halted" : "unhalted"); + ret = -EINVAL; + } + + /* fixup TCMs, cluster & core flags to actual values in IPC-only mode */ + if (ret > 0) { + if (core == core0) + cluster->mode = mode; + core->atcm_enable = atcm_enable; + core->btcm_enable = btcm_enable; + core->loczrama = loczrama; + core->mem[0].dev_addr = loczrama ? 0 : K3_R5_TCM_DEV_ADDR; + core->mem[1].dev_addr = loczrama ? K3_R5_TCM_DEV_ADDR : 0; + } + + return ret; +} + static int k3_r5_cluster_rproc_init(struct platform_device *pdev) { struct k3_r5_cluster *cluster = platform_get_drvdata(pdev); @@ -895,6 +1262,12 @@ kproc->rproc = rproc; core->rproc = rproc; + ret = k3_r5_rproc_configure_mode(kproc); + if (ret < 0) + goto err_config; + if (ret) + goto init_rmem; + ret = k3_r5_rproc_configure(kproc); if (ret) { dev_err(dev, "initial configure failed, ret = %d\n", @@ -902,6 +1275,9 @@ goto err_config; } +init_rmem: + k3_r5_adjust_tcm_sizes(kproc); + ret = k3_r5_reserved_mem_init(kproc); if (ret) { dev_err(dev, "reserved memory init failed, ret = %d\n", @@ -915,8 +1291,9 @@ goto err_add; } - /* create only one rproc in lockstep mode */ - if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + /* create only one rproc in lockstep mode or single-cpu mode */ + if (cluster->mode == CLUSTER_MODE_LOCKSTEP || + cluster->mode == CLUSTER_MODE_SINGLECPU) break; } @@ -940,19 +1317,20 @@ return ret; } -static int k3_r5_cluster_rproc_exit(struct platform_device *pdev) +static void k3_r5_cluster_rproc_exit(void *data) { - struct k3_r5_cluster *cluster = platform_get_drvdata(pdev); + struct k3_r5_cluster *cluster = platform_get_drvdata(data); struct k3_r5_rproc *kproc; struct k3_r5_core *core; struct rproc *rproc; /* - * lockstep mode has only one rproc associated with first core, whereas - * split-mode has two rprocs associated with each core, and requires - * that core1 be powered down first + * lockstep mode and single-cpu modes have only one rproc associated + * with first core, whereas split-mode has two rprocs associated with + * each core, and requires that core1 be powered down first */ - core = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ? + core = (cluster->mode == CLUSTER_MODE_LOCKSTEP || + cluster->mode == CLUSTER_MODE_SINGLECPU) ? list_first_entry(&cluster->cores, struct k3_r5_core, elem) : list_last_entry(&cluster->cores, struct k3_r5_core, elem); @@ -967,8 +1345,6 @@ rproc_free(rproc); core->rproc = NULL; } - - return 0; } static int k3_r5_core_of_get_internal_memories(struct platform_device *pdev, @@ -1255,9 +1631,9 @@ devres_release_group(dev, k3_r5_core_of_init); } -static void k3_r5_cluster_of_exit(struct platform_device *pdev) +static void k3_r5_cluster_of_exit(void *data) { - struct k3_r5_cluster *cluster = platform_get_drvdata(pdev); + struct k3_r5_cluster *cluster = platform_get_drvdata(data); struct platform_device *cpdev; struct k3_r5_core *core, *temp; @@ -1311,15 +1687,28 @@ struct device *dev = &pdev->dev; struct device_node *np = dev_of_node(dev); struct k3_r5_cluster *cluster; + const struct k3_r5_soc_data *data; int ret; int num_cores; + data = of_device_get_match_data(&pdev->dev); + if (!data) { + dev_err(dev, "SoC-specific data is not defined\n"); + return -ENODEV; + } + cluster = devm_kzalloc(dev, sizeof(*cluster), GFP_KERNEL); if (!cluster) return -ENOMEM; cluster->dev = dev; - cluster->mode = CLUSTER_MODE_LOCKSTEP; + /* + * default to most common efuse configurations - Split-mode on AM64x + * and LockStep-mode on all others + */ + cluster->mode = data->single_cpu_mode ? + CLUSTER_MODE_SPLIT : CLUSTER_MODE_LOCKSTEP; + cluster->soc_data = data; INIT_LIST_HEAD(&cluster->cores); ret = of_property_read_u32(np, "ti,cluster-mode", &cluster->mode); @@ -1328,6 +1717,12 @@ ret); return ret; } + /* + * Translate SoC-specific dts value of 1 or 2 into appropriate + * driver-specific mode. Valid values are dictated by YAML binding + */ + if (cluster->mode && data->single_cpu_mode) + cluster->mode = CLUSTER_MODE_SINGLECPU; num_cores = of_get_available_child_count(np); if (num_cores != 2) { @@ -1351,9 +1746,7 @@ return ret; } - ret = devm_add_action_or_reset(dev, - (void(*)(void *))k3_r5_cluster_of_exit, - pdev); + ret = devm_add_action_or_reset(dev, k3_r5_cluster_of_exit, pdev); if (ret) return ret; @@ -1364,18 +1757,36 @@ return ret; } - ret = devm_add_action_or_reset(dev, - (void(*)(void *))k3_r5_cluster_rproc_exit, - pdev); + ret = devm_add_action_or_reset(dev, k3_r5_cluster_rproc_exit, pdev); if (ret) return ret; return 0; } +static const struct k3_r5_soc_data am65_j721e_soc_data = { + .tcm_is_double = false, + .tcm_ecc_autoinit = false, + .single_cpu_mode = false, +}; + +static const struct k3_r5_soc_data j7200_soc_data = { + .tcm_is_double = true, + .tcm_ecc_autoinit = true, + .single_cpu_mode = false, +}; + +static const struct k3_r5_soc_data am64_soc_data = { + .tcm_is_double = true, + .tcm_ecc_autoinit = true, + .single_cpu_mode = true, +}; + static const struct of_device_id k3_r5_of_match[] = { - { .compatible = "ti,am654-r5fss", }, - { .compatible = "ti,j721e-r5fss", }, + { .compatible = "ti,am654-r5fss", .data = &am65_j721e_soc_data, }, + { .compatible = "ti,j721e-r5fss", .data = &am65_j721e_soc_data, }, + { .compatible = "ti,j7200-r5fss", .data = &j7200_soc_data, }, + { .compatible = "ti,am64-r5fss", .data = &am64_soc_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, k3_r5_of_match); diff -Naur --no-dereference a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c --- a/drivers/remoteproc/wkup_m3_rproc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/remoteproc/wkup_m3_rproc.c 2022-01-06 12:45:53.826318157 -0500 @@ -160,6 +160,7 @@ } rproc->auto_boot = false; + rproc->deny_sysfs_ops = true; wkupm3 = rproc->priv; wkupm3->rproc = rproc; diff -Naur --no-dereference a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig --- a/drivers/rpmsg/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -64,4 +64,17 @@ select RPMSG select VIRTIO +config RPMSG_PRU + tristate "PRU RPMsg Communication driver" + depends on RPMSG_VIRTIO + depends on REMOTEPROC + depends on PRU_REMOTEPROC + help + An rpmsg driver that exposes interfaces to user space, to allow + applications to communicate with the PRU processors on available + TI SoCs. This is restricted to SoCs that have the PRUSS remoteproc + support. + + If unsure, say N. + endmenu diff -Naur --no-dereference a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile --- a/drivers/rpmsg/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -8,3 +8,4 @@ obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o +obj-$(CONFIG_RPMSG_PRU) += rpmsg_pru.o diff -Naur --no-dereference a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c --- a/drivers/rpmsg/qcom_glink_native.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/qcom_glink_native.c 2022-01-06 12:45:53.826318157 -0500 @@ -1475,6 +1475,7 @@ strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; + chinfo.desc[0] = '\0'; rpmsg_unregister_device(glink->dev, &chinfo); } diff -Naur --no-dereference a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c --- a/drivers/rpmsg/qcom_smd.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/qcom_smd.c 2022-01-06 12:45:53.826318157 -0500 @@ -1307,6 +1307,7 @@ strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; + chinfo.desc[0] = '\0'; rpmsg_unregister_device(&edge->dev, &chinfo); channel->registered = false; spin_lock_irqsave(&edge->channels_lock, flags); diff -Naur --no-dereference a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c --- a/drivers/rpmsg/rpmsg_char.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/rpmsg_char.c 2022-01-06 12:45:53.826318157 -0500 @@ -137,6 +137,8 @@ } eptdev->ept = ept; + if (eptdev->chinfo.src == RPMSG_ADDR_ANY) + eptdev->chinfo.src = ept->addr; filp->private_data = eptdev; return 0; @@ -440,6 +442,7 @@ chinfo.name[RPMSG_NAME_SIZE-1] = '\0'; chinfo.src = eptinfo.src; chinfo.dst = eptinfo.dst; + chinfo.desc[0] = '\0'; return rpmsg_eptdev_create(ctrldev, chinfo); }; @@ -535,12 +538,19 @@ put_device(&ctrldev->dev); } +static const struct rpmsg_device_id rpmsg_char_id_table[] = { + { .name = "rpmsg_chrdev" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_char_id_table); + static struct rpmsg_driver rpmsg_chrdev_driver = { .probe = rpmsg_chrdev_probe, .remove = rpmsg_chrdev_remove, .drv = { .name = "rpmsg_chrdev", }, + .id_table = rpmsg_char_id_table, }; static int rpmsg_char_init(void) diff -Naur --no-dereference a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c --- a/drivers/rpmsg/rpmsg_core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/rpmsg_core.c 2022-01-06 12:45:53.826318157 -0500 @@ -283,6 +283,27 @@ } EXPORT_SYMBOL(rpmsg_trysend_offchannel); +/** + * rpmsg_get_mtu() - get maximum transmission buffer size for sending message. + * @ept: the rpmsg endpoint + * + * This function returns maximum buffer size available for a single message. + * + * Return: the maximum transmission size on success and an appropriate error + * value on failure. + */ + +ssize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->get_mtu) + return -EOPNOTSUPP; + + return ept->ops->get_mtu(ept); +} +EXPORT_SYMBOL(rpmsg_get_mtu); + /* * match a rpmsg channel with a channel info struct. * this is used to make sure we're not creating rpmsg devices for channels @@ -365,6 +386,7 @@ /* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */ rpmsg_show_attr(name, id.name, "%s\n"); +rpmsg_show_attr(desc, desc, "%s\n"); rpmsg_show_attr(src, src, "0x%x\n"); rpmsg_show_attr(dst, dst, "0x%x\n"); rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); @@ -386,6 +408,7 @@ static struct attribute *rpmsg_dev_attrs[] = { &dev_attr_name.attr, + &dev_attr_desc.attr, &dev_attr_modalias.attr, &dev_attr_dst.attr, &dev_attr_src.attr, diff -Naur --no-dereference a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h --- a/drivers/rpmsg/rpmsg_internal.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/rpmsg_internal.h 2022-01-06 12:45:53.826318157 -0500 @@ -47,6 +47,7 @@ * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel: see @rpmsg_trysend_offchannel(), optional * @poll: see @rpmsg_poll(), optional + * @get_mtu: see @rpmsg_get_mtu(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and @@ -66,6 +67,7 @@ void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); + ssize_t (*get_mtu)(struct rpmsg_endpoint *ept); }; int rpmsg_register_device(struct rpmsg_device *rpdev); diff -Naur --no-dereference a/drivers/rpmsg/rpmsg_pru.c b/drivers/rpmsg/rpmsg_pru.c --- a/drivers/rpmsg/rpmsg_pru.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg/rpmsg_pru.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PRU Remote Processor Messaging Driver + * + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com/ + * Jason Reeder + * Suman Anna + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRU_MAX_DEVICES (16) +/* Matches the RPMSG_BUF_SIZE definition in virtio_rpmsg_bus.c */ +#define FIFO_MSG_SIZE (512) +#define MAX_FIFO_MSG (32) + +/** + * struct rpmsg_pru_dev - Structure that contains the per-device data + * @rpdev: rpmsg channel device that is associated with this rpmsg_pru device + * @dev: device + * @cdev: character device + * @locked: boolean used to determine whether or not the device file is in use + * @devt: dev_t structure for the rpmsg_pru device + * @msg_fifo: kernel fifo used to buffer the messages between userspace and PRU + * @msg_len: array storing the lengths of each message in the kernel fifo + * @msg_idx_rd: kernel fifo read index + * @msg_idx_wr: kernel fifo write index + * @wait_list: wait queue used to implement the poll operation of the character + * device + * + * Each rpmsg_pru device provides an interface, using an rpmsg channel (rpdev), + * between a user space character device (cdev) and a PRU core. A kernel fifo + * (msg_fifo) is used to buffer the messages in the kernel that are + * being passed between the character device and the PRU. + */ +struct rpmsg_pru_dev { + struct rpmsg_device *rpdev; + struct device *dev; + struct cdev cdev; + bool locked; + dev_t devt; + struct kfifo msg_fifo; + u32 msg_len[MAX_FIFO_MSG]; + int msg_idx_rd; + int msg_idx_wr; + wait_queue_head_t wait_list; +}; + +static struct class *rpmsg_pru_class; +static dev_t rpmsg_pru_devt; +static DEFINE_MUTEX(rpmsg_pru_lock); +static DEFINE_IDR(rpmsg_pru_minors); + +static int rpmsg_pru_open(struct inode *inode, struct file *filp) +{ + struct rpmsg_pru_dev *prudev; + int ret = -EACCES; + + prudev = container_of(inode->i_cdev, struct rpmsg_pru_dev, cdev); + + mutex_lock(&rpmsg_pru_lock); + if (!prudev->locked) { + prudev->locked = true; + filp->private_data = prudev; + ret = 0; + } + mutex_unlock(&rpmsg_pru_lock); + + if (ret) + dev_err(prudev->dev, "Device already open\n"); + + return ret; +} + +static int rpmsg_pru_release(struct inode *inode, struct file *filp) +{ + struct rpmsg_pru_dev *prudev; + + prudev = container_of(inode->i_cdev, struct rpmsg_pru_dev, cdev); + mutex_lock(&rpmsg_pru_lock); + prudev->locked = false; + mutex_unlock(&rpmsg_pru_lock); + return 0; +} + +static ssize_t rpmsg_pru_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int ret; + u32 length; + struct rpmsg_pru_dev *prudev; + + prudev = filp->private_data; + + if (kfifo_is_empty(&prudev->msg_fifo) && + (filp->f_flags & O_NONBLOCK)) + return -EAGAIN; + + ret = wait_event_interruptible(prudev->wait_list, + !kfifo_is_empty(&prudev->msg_fifo)); + if (ret) + return -EINTR; + + ret = kfifo_to_user(&prudev->msg_fifo, buf, + prudev->msg_len[prudev->msg_idx_rd], &length); + prudev->msg_idx_rd = (prudev->msg_idx_rd + 1) % MAX_FIFO_MSG; + + return ret ? ret : length; +} + +static ssize_t rpmsg_pru_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int ret; + struct rpmsg_pru_dev *prudev; + static char rpmsg_pru_buf[FIFO_MSG_SIZE]; + ssize_t max_payload; + + prudev = filp->private_data; + max_payload = rpmsg_get_mtu(prudev->rpdev->ept); + + if (count > max_payload) { + dev_err(prudev->dev, "Data too large for RPMsg Buffer\n"); + return -EINVAL; + } + + if (copy_from_user(rpmsg_pru_buf, buf, count)) { + dev_err(prudev->dev, "Error copying buffer from user space"); + return -EFAULT; + } + + ret = rpmsg_send(prudev->rpdev->ept, (void *)rpmsg_pru_buf, count); + if (ret) + dev_err(prudev->dev, "rpmsg_send failed: %d\n", ret); + + return ret ? ret : count; +} + +static unsigned int rpmsg_pru_poll(struct file *filp, + struct poll_table_struct *wait) +{ + int mask; + struct rpmsg_pru_dev *prudev; + + prudev = filp->private_data; + + poll_wait(filp, &prudev->wait_list, wait); + + mask = POLLOUT | POLLWRNORM; + + if (!kfifo_is_empty(&prudev->msg_fifo)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static const struct file_operations rpmsg_pru_fops = { + .owner = THIS_MODULE, + .open = rpmsg_pru_open, + .release = rpmsg_pru_release, + .read = rpmsg_pru_read, + .write = rpmsg_pru_write, + .poll = rpmsg_pru_poll, + .llseek = noop_llseek, +}; + +static int rpmsg_pru_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + u32 length; + struct rpmsg_pru_dev *prudev; + + prudev = dev_get_drvdata(&rpdev->dev); + + if (kfifo_avail(&prudev->msg_fifo) < len) { + dev_err(&rpdev->dev, "Not enough space on the FIFO\n"); + return -ENOSPC; + } + + if ((prudev->msg_idx_wr + 1) % MAX_FIFO_MSG == + prudev->msg_idx_rd) { + dev_err(&rpdev->dev, "Message length table is full\n"); + return -ENOSPC; + } + + length = kfifo_in(&prudev->msg_fifo, data, len); + prudev->msg_len[prudev->msg_idx_wr] = length; + prudev->msg_idx_wr = (prudev->msg_idx_wr + 1) % MAX_FIFO_MSG; + + wake_up_interruptible(&prudev->wait_list); + + return 0; +} + +static int rpmsg_pru_probe(struct rpmsg_device *rpdev) +{ + int ret; + struct rpmsg_pru_dev *prudev; + int minor_got; + + prudev = devm_kzalloc(&rpdev->dev, sizeof(*prudev), GFP_KERNEL); + if (!prudev) + return -ENOMEM; + + mutex_lock(&rpmsg_pru_lock); + minor_got = idr_alloc(&rpmsg_pru_minors, prudev, 0, PRU_MAX_DEVICES, + GFP_KERNEL); + mutex_unlock(&rpmsg_pru_lock); + if (minor_got < 0) { + ret = minor_got; + dev_err(&rpdev->dev, "Failed to get a minor number for the rpmsg_pru device: %d\n", + ret); + goto fail_alloc_minor; + } + + prudev->devt = MKDEV(MAJOR(rpmsg_pru_devt), minor_got); + + cdev_init(&prudev->cdev, &rpmsg_pru_fops); + prudev->cdev.owner = THIS_MODULE; + ret = cdev_add(&prudev->cdev, prudev->devt, 1); + if (ret) { + dev_err(&rpdev->dev, "Unable to add cdev for the rpmsg_pru device\n"); + goto fail_add_cdev; + } + + prudev->dev = device_create(rpmsg_pru_class, &rpdev->dev, prudev->devt, + NULL, "rpmsg_pru%d", rpdev->dst); + if (IS_ERR(prudev->dev)) { + dev_err(&rpdev->dev, "Unable to create the rpmsg_pru device\n"); + ret = PTR_ERR(prudev->dev); + goto fail_create_device; + } + + prudev->rpdev = rpdev; + + ret = kfifo_alloc(&prudev->msg_fifo, MAX_FIFO_MSG * FIFO_MSG_SIZE, + GFP_KERNEL); + if (ret) { + dev_err(&rpdev->dev, "Unable to allocate fifo for the rpmsg_pru device\n"); + goto fail_alloc_fifo; + } + + init_waitqueue_head(&prudev->wait_list); + + dev_set_drvdata(&rpdev->dev, prudev); + + dev_info(&rpdev->dev, "new rpmsg_pru device: /dev/rpmsg_pru%d", + rpdev->dst); + + return 0; + +fail_alloc_fifo: + device_destroy(rpmsg_pru_class, prudev->devt); +fail_create_device: + cdev_del(&prudev->cdev); +fail_add_cdev: + mutex_lock(&rpmsg_pru_lock); + idr_remove(&rpmsg_pru_minors, minor_got); + mutex_unlock(&rpmsg_pru_lock); +fail_alloc_minor: + return ret; +} + +static void rpmsg_pru_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_pru_dev *prudev; + + prudev = dev_get_drvdata(&rpdev->dev); + + kfifo_free(&prudev->msg_fifo); + device_destroy(rpmsg_pru_class, prudev->devt); + cdev_del(&prudev->cdev); + mutex_lock(&rpmsg_pru_lock); + idr_remove(&rpmsg_pru_minors, MINOR(prudev->devt)); + mutex_unlock(&rpmsg_pru_lock); +} + +/* .name matches on RPMsg Channels and causes a probe */ +static const struct rpmsg_device_id rpmsg_driver_pru_id_table[] = { + { .name = "rpmsg-pru" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_pru_id_table); + +static struct rpmsg_driver rpmsg_pru_driver = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_pru_id_table, + .probe = rpmsg_pru_probe, + .callback = rpmsg_pru_cb, + .remove = rpmsg_pru_remove, +}; + +static int __init rpmsg_pru_init(void) +{ + int ret; + + rpmsg_pru_class = class_create(THIS_MODULE, "rpmsg_pru"); + if (IS_ERR(rpmsg_pru_class)) { + pr_err("Unable to create class\n"); + ret = PTR_ERR(rpmsg_pru_class); + goto fail_create_class; + } + + ret = alloc_chrdev_region(&rpmsg_pru_devt, 0, PRU_MAX_DEVICES, + "rpmsg_pru"); + if (ret) { + pr_err("Unable to allocate chrdev region\n"); + goto fail_alloc_region; + } + + ret = register_rpmsg_driver(&rpmsg_pru_driver); + if (ret) { + pr_err("Unable to register rpmsg driver"); + goto fail_register_rpmsg_driver; + } + + return 0; + +fail_register_rpmsg_driver: + unregister_chrdev_region(rpmsg_pru_devt, PRU_MAX_DEVICES); +fail_alloc_region: + class_destroy(rpmsg_pru_class); +fail_create_class: + return ret; +} + +static void __exit rpmsg_pru_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_pru_driver); + idr_destroy(&rpmsg_pru_minors); + mutex_destroy(&rpmsg_pru_lock); + class_destroy(rpmsg_pru_class); + unregister_chrdev_region(rpmsg_pru_devt, PRU_MAX_DEVICES); +} + +module_init(rpmsg_pru_init); +module_exit(rpmsg_pru_exit); + +MODULE_AUTHOR("Jason Reeder "); +MODULE_DESCRIPTION("PRU Remote Processor Messaging Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c --- a/drivers/rpmsg/virtio_rpmsg_bus.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/rpmsg/virtio_rpmsg_bus.c 2022-01-06 12:45:53.826318157 -0500 @@ -112,6 +112,23 @@ } __packed; /** + * struct rpmsg_ns_msg_ext - dynamic name service announcement message v2 + * @name: name of remote service that is published + * @desc: description of remote service + * @addr: address of remote service that is published + * @flags: indicates whether service is created or destroyed + * + * Interchangeable nameservice message with rpmsg_ns_msg. This one has + * the addition of the desc field for extra flexibility. + */ +struct rpmsg_ns_msg_ext { + char name[RPMSG_NAME_SIZE]; + char desc[RPMSG_NAME_SIZE]; + u32 addr; + u32 flags; +} __packed; + +/** * enum rpmsg_ns_flags - dynamic name service announcement flags * * @RPMSG_NS_CREATE: a new remote service was just created @@ -181,6 +198,7 @@ int len, u32 dst); static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); +static ssize_t virtio_rpmsg_get_mtu(struct rpmsg_endpoint *ept); static const struct rpmsg_endpoint_ops virtio_endpoint_ops = { .destroy_ept = virtio_rpmsg_destroy_ept, @@ -190,6 +208,7 @@ .trysend = virtio_rpmsg_trysend, .trysendto = virtio_rpmsg_trysendto, .trysend_offchannel = virtio_rpmsg_trysend_offchannel, + .get_mtu = virtio_rpmsg_get_mtu, }; /** @@ -390,6 +409,24 @@ kfree(vch); } +static int virtio_rpmsg_desc_match(struct device *dev, void *data) +{ + struct rpmsg_channel_info *chinfo = data; + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + + if (!*chinfo->desc) + return 0; + + if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) + return 0; + + if (strncmp(chinfo->desc, rpdev->desc, RPMSG_NAME_SIZE)) + return 0; + + /* found a match ! */ + return 1; +} + /* * create an rpmsg channel using its name and address info. * this function will be used to create both static and dynamic @@ -413,6 +450,15 @@ return NULL; } + tmp = device_find_child(dev, chinfo, virtio_rpmsg_desc_match); + if (tmp) { + /* decrement the matched device's refcount back */ + put_device(tmp); + dev_err(dev, "channel %s:%x:%x failed, desc '%s' already exists\n", + chinfo->name, chinfo->src, chinfo->dst, chinfo->desc); + return NULL; + } + vch = kzalloc(sizeof(*vch), GFP_KERNEL); if (!vch) return NULL; @@ -425,6 +471,7 @@ rpdev->src = chinfo->src; rpdev->dst = chinfo->dst; rpdev->ops = &virtio_rpmsg_ops; + strncpy(rpdev->desc, chinfo->desc, RPMSG_NAME_SIZE); /* * rpmsg server channels has predefined local address (for now), @@ -705,6 +752,14 @@ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); } +static ssize_t virtio_rpmsg_get_mtu(struct rpmsg_endpoint *ept) +{ + struct rpmsg_device *rpdev = ept->rpdev; + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + + return vch->vrp->buf_size - sizeof(struct rpmsg_hdr); +} + static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, struct rpmsg_hdr *msg, unsigned int len) { @@ -826,18 +881,30 @@ void *priv, u32 src) { struct rpmsg_ns_msg *msg = data; + struct rpmsg_ns_msg_ext *msg_ext = data; struct rpmsg_device *newch; struct rpmsg_channel_info chinfo; struct virtproc_info *vrp = priv; struct device *dev = &vrp->vdev->dev; int ret; + u32 addr; + u32 flags; #if defined(CONFIG_DYNAMIC_DEBUG) dynamic_hex_dump("NS announcement: ", DUMP_PREFIX_NONE, 16, 1, data, len, true); #endif - if (len != sizeof(*msg)) { + if (len == sizeof(*msg)) { + addr = virtio32_to_cpu(vrp->vdev, msg->addr); + flags = virtio32_to_cpu(vrp->vdev, msg->flags); + chinfo.desc[0] = '\0'; + } else if (len == sizeof(*msg_ext)) { + addr = virtio32_to_cpu(vrp->vdev, msg_ext->addr); + flags = virtio32_to_cpu(vrp->vdev, msg_ext->flags); + msg_ext->desc[RPMSG_NAME_SIZE - 1] = '\0'; + strncpy(chinfo.desc, msg_ext->desc, sizeof(chinfo.desc)); + } else { dev_err(dev, "malformed ns msg (%d)\n", len); return -EINVAL; } @@ -858,13 +925,13 @@ strncpy(chinfo.name, msg->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; - chinfo.dst = virtio32_to_cpu(vrp->vdev, msg->addr); + chinfo.dst = addr; dev_info(dev, "%sing channel %s addr 0x%x\n", - virtio32_to_cpu(vrp->vdev, msg->flags) & RPMSG_NS_DESTROY ? + flags & RPMSG_NS_DESTROY ? "destroy" : "creat", msg->name, chinfo.dst); - if (virtio32_to_cpu(vrp->vdev, msg->flags) & RPMSG_NS_DESTROY) { + if (flags & RPMSG_NS_DESTROY) { ret = rpmsg_unregister_device(&vrp->vdev->dev, &chinfo); if (ret) dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret); diff -Naur --no-dereference a/drivers/rpmsg-kdrv/Kconfig b/drivers/rpmsg-kdrv/Kconfig --- a/drivers/rpmsg-kdrv/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0 + +menu "Rpmsg virtual device drivers" + +# RPMSG always gets selected by whoever wants it +config RPMSG_KDRV + tristate "RPMSG virtual device interface" + select RPMSG + help + Say Y here enable support for RPMSG based remote devices, usually + exported by a firmware running rpmsg stack and remote_device stack. + This feature enables the framework for para-virtualizing entire H/W + or specific resources of a hardware + + +config RPMSG_KDRV_DEMO + tristate "RPMSG virtual demo device support" + select RPMSG_KDRV + help + Say Y here to enable support for remote device based demo device. + This setup expects that the demo device server will be running on a + remoteproc and a client (sample) driver will be able to call demo device + APIs using remote_device framework. + + The demo device is no real device. It serves the purpose of providing + a sample driver to be used as a reference for developing more + remote_device drivers (like display or ethernet) + +config RPMSG_KDRV_DISPLAY + tristate "RPMSG virtual display device support" + select RPMSG_KDRV + help + Say Y here to enable support for remote device based display + virtualization. This setup expects that the display will be driven + by a remoteproc and DRM driver will be able to use display features + using remote_device framework + +config RPMSG_KDRV_ETH_SWITCH + tristate "RPMSG virtual eth switch device support" + select RPMSG_KDRV + default m + help + Say Y here to enable support for remote device based Eth switch + virtualization. This setup expects that the Eth switch will be driven + by a remoteproc and virtual Network device will be able to use + Eth switch features using remote_device framework + +endmenu diff -Naur --no-dereference a/drivers/rpmsg-kdrv/Makefile b/drivers/rpmsg-kdrv/Makefile --- a/drivers/rpmsg-kdrv/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RPMSG_KDRV) += rpmsg_kdrv.o +obj-$(CONFIG_RPMSG_KDRV_DEMO) += rpmsg_kdrv_demo.o +obj-$(CONFIG_RPMSG_KDRV_DISPLAY) += rpmsg_kdrv_display.o +obj-$(CONFIG_RPMSG_KDRV_ETH_SWITCH) += rpmsg_kdrv_switch.o diff -Naur --no-dereference a/drivers/rpmsg-kdrv/rpmsg_kdrv.c b/drivers/rpmsg-kdrv/rpmsg_kdrv.c --- a/drivers/rpmsg-kdrv/rpmsg_kdrv.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/rpmsg_kdrv.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#include +#include +#include +#include + +#include +#include +#include "shared/rpmsg-kdrv-transport.h" +#include "rpmsg_kdrv_internal.h" + +struct rpmsg_kdrv_priv { + struct rpmsg_device *rpdev; + + struct idr message_idr; + struct mutex message_lock; + + int num_raw_devices; + struct rpmsg_kdrv_init_device_info raw_devices[RPMSG_KDRV_TP_MAX_DEVICES]; + void *raw_device_data[RPMSG_KDRV_TP_MAX_DEVICES]; + int raw_device_data_size[RPMSG_KDRV_TP_MAX_DEVICES]; +}; + +struct rpmsg_kdrv_ctx { + struct rpmsg_device *rpdev; + bool wait_for_response; + request_cb_t callback; + void *cb_data; + bool response_recv; + struct wait_queue_head response_wq; + + struct rpmsg_kdrv_device_header *dev_hdr; + void *req; + void *resp; + int req_size; + int resp_size; +}; + +static struct bus_type rpmsg_kdrv_bus; + +#define to_rpmsg_kdrv_device(d) container_of(d, struct rpmsg_kdrv_device, dev) +#define to_rpmsg_kdrv_driver(d) container_of(d, struct rpmsg_kdrv_driver, drv) + +static int rpmsg_kdrv_match_id(struct device *dev, const void *data) +{ + const uint32_t *idptr = data; + struct rpmsg_kdrv_device *kddev = container_of(dev, struct rpmsg_kdrv_device, dev); + + if (kddev->device_id == *idptr) + return 1; + return 0; +} + +static int rpmsg_kdrv_match_remotedev(struct device *dev, const void *data) +{ + const struct rpmsg_remotedev *rdev = data; + struct rpmsg_kdrv_device *kddev = container_of(dev, struct rpmsg_kdrv_device, dev); + + if (kddev->remotedev == rdev) + return 1; + return 0; +} + +static int rpmsg_kdrv_match_name(struct device *dev, const void *data) +{ + const char *name = data; + struct rpmsg_kdrv_device *kddev = container_of(dev, struct rpmsg_kdrv_device, dev); + + if (strcmp(kddev->device_name, name) == 0) + return 1; + return 0; +} + +int rpmsg_kdrv_register_driver(struct rpmsg_kdrv_driver *drv) +{ + int ret; + + drv->drv.bus = &rpmsg_kdrv_bus; + drv->drv.owner = THIS_MODULE; + + ret = driver_register(&drv->drv); + if (ret) + pr_err("%s: driver_register failed\n", __func__); + + return ret; +} +EXPORT_SYMBOL(rpmsg_kdrv_register_driver); + +static void rpmsg_kdrv_driver_handle_data(struct rpmsg_device *rpdev, void *data, int len, void *private, u32 src) +{ + struct device *dev; + struct rpmsg_kdrv_device_header *hdr = data; + struct rpmsg_kdrv_device *kddev = NULL; + struct rpmsg_kdrv_driver *kddrv = NULL; + void *message; + int message_size; + uint32_t msg_device_id; + int ret; + + msg_device_id = hdr->device_id; + dev = bus_find_device(&rpmsg_kdrv_bus, NULL, &(msg_device_id), rpmsg_kdrv_match_id); + if (!dev) { + dev_err(&rpdev->dev, "%s: message received for unknown device\n", __func__); + return; + } + kddev = container_of(dev, struct rpmsg_kdrv_device, dev); + kddrv = to_rpmsg_kdrv_driver(kddev->dev.driver); + if (!kddrv) { + dev_err(&rpdev->dev, "%s: message received for device with no driver\n", __func__); + return; + } + + message = (void *)(&hdr[1]); + message_size = len - sizeof(*hdr); + ret = kddrv->callback(kddev, message, message_size); + if (ret) + dev_err(&rpdev->dev, "%s: message callback returns %d\n", __func__, ret); + +} + +static int rpmsg_kdrv_connect(struct rpmsg_device *rpdev, struct rpmsg_kdrv_device *kddev) +{ + int ret; + struct rpmsg_kdrv_init_connect_message *connect_req; + + connect_req = devm_kzalloc(&rpdev->dev, sizeof(*connect_req), GFP_KERNEL); + if (!connect_req) + return -ENOMEM; + + connect_req->header.message_type = RPMSG_KDRV_TP_INIT_CONNECT_MESSAGE; + connect_req->device_id = kddev->device_id; + + ret = rpmsg_kdrv_send_message(rpdev, RPMSG_KDRV_TP_DEVICE_ID_INIT, + connect_req, sizeof(*connect_req)); + + devm_kfree(&rpdev->dev, connect_req); + return ret; +} + +static int rpmsg_kdrv_disconnect(struct rpmsg_device *rpdev, struct rpmsg_kdrv_device *kddev) +{ + int ret; + struct rpmsg_kdrv_init_disconnect_message *disconnect_req; + + disconnect_req = devm_kzalloc(&rpdev->dev, sizeof(*disconnect_req), GFP_KERNEL); + if (!disconnect_req) + return -ENOMEM; + + disconnect_req->header.message_type = RPMSG_KDRV_TP_INIT_DISCONNECT_MESSAGE; + disconnect_req->device_id = kddev->device_id; + + ret = rpmsg_kdrv_send_message(rpdev, RPMSG_KDRV_TP_DEVICE_ID_INIT, + disconnect_req, sizeof(*disconnect_req)); + + devm_kfree(&rpdev->dev, disconnect_req); + return ret; +} + +struct rpmsg_remotedev *rpmsg_remotedev_get_named_device(const char *device_name) +{ + struct device *dev; + struct rpmsg_kdrv_device *kddev = NULL; + + dev = bus_find_device(&rpmsg_kdrv_bus, NULL, (void *)device_name, rpmsg_kdrv_match_name); + if (!dev) + return ERR_PTR(-EPROBE_DEFER); + + kddev = container_of(dev, struct rpmsg_kdrv_device, dev); + if (!kddev->remotedev) + return ERR_PTR(-EPROBE_DEFER); + + rpmsg_kdrv_connect(kddev->rpdev, kddev); + + return kddev->remotedev; +} +EXPORT_SYMBOL(rpmsg_remotedev_get_named_device); + +void rpmsg_remotedev_put_device(struct rpmsg_remotedev *rdev) +{ + struct device *dev; + struct rpmsg_kdrv_device *kddev = NULL; + + dev = bus_find_device(&rpmsg_kdrv_bus, NULL, (void *)rdev, rpmsg_kdrv_match_remotedev); + if (!dev) { + pr_err("%s: could not find device for remotedev\n", __func__); + return; + } + + kddev = container_of(dev, struct rpmsg_kdrv_device, dev); + + rpmsg_kdrv_disconnect(kddev->rpdev, kddev); +} +EXPORT_SYMBOL(rpmsg_remotedev_put_device); + +static void rpmsg_kdrv_release_device(struct device *dev) +{ + struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev); + + dev_dbg(dev, "%s\n", __func__); + + devm_kfree(&kddev->rpdev->dev, kddev); +} + +static struct rpmsg_kdrv_device *rpmsg_kdrv_device_create(struct rpmsg_device *rpdev, int index) +{ + struct rpmsg_kdrv_device *kddev = devm_kzalloc(&rpdev->dev, sizeof(*kddev), GFP_KERNEL); + struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev); + struct rpmsg_kdrv_init_device_info *dev = &priv->raw_devices[index]; + int ret; + + if (!kddev) { + dev_err(&rpdev->dev, "%s: could not allocate kddev\n", __func__); + return NULL; + } + + kddev->rpdev = rpdev; + kddev->device_id = dev->device_id; + kddev->device_type = dev->device_type; + kddev->device_data_len = priv->raw_device_data_size[index]; + kddev->device_data = priv->raw_device_data[index]; + kddev->device_name = devm_kstrdup(&rpdev->dev, dev->device_name, GFP_KERNEL); + if (!kddev->device_name) { + dev_err(&rpdev->dev, "%s: could not allocate device name\n", __func__); + devm_kfree(&rpdev->dev, kddev); + return NULL; + } + + kddev->dev.parent = &rpdev->dev; + kddev->dev.release = rpmsg_kdrv_release_device; + kddev->dev.bus = &rpmsg_kdrv_bus; + + dev_set_name(&kddev->dev, "rpmsg-kdrv-%u-%s", dev->device_id, dev->device_name); + + ret = device_register(&kddev->dev); + if (ret) { + dev_err(&rpdev->dev, "%s: device_register failed: %d\n", __func__, ret); + put_device(&kddev->dev); + return NULL; + } + dev_dbg(&rpdev->dev, "%s: registered new device : %s\n", __func__, dev_name(&kddev->dev)); + + return kddev; +} + +static int rpmsg_kdrv_get_devices_cb(void *cb_data, void *req, int req_sz, void *resp, int resp_sz) +{ + int i, cnt; + struct rpmsg_device *rpdev = cb_data; + struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev); + struct rpmsg_kdrv_init_dev_info_response *info_resp = resp; + struct rpmsg_kdrv_init_device_info *dev; + int ret = 0; + + if (info_resp->header.message_type != RPMSG_KDRV_TP_INIT_DEV_INFO_RESPONSE) { + dev_err(&rpdev->dev, "%s: wrong response type\n", __func__); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < info_resp->num_devices; i++) { + dev = &info_resp->devices[i]; + cnt = priv->num_raw_devices; + + priv->raw_device_data_size[cnt] = dev->device_data_len; + priv->raw_device_data[cnt] = devm_kzalloc(&rpdev->dev, dev->device_data_len, GFP_KERNEL); + if (!priv->raw_device_data[cnt]) { + ret = -ENOMEM; + goto out; + } + memcpy(priv->raw_device_data[cnt], + &info_resp->device_data[dev->device_data_offset], + dev->device_data_len); + memcpy(&priv->raw_devices[cnt], dev, sizeof(*dev)); + priv->num_raw_devices++; + + dev_dbg(&rpdev->dev, "new device: %s\n", dev->device_name); + } + + for (i = 0; i < priv->num_raw_devices; i++) + rpmsg_kdrv_device_create(rpdev, i); + +out: + devm_kfree(&rpdev->dev, req); + return ret; +} + +static int rpmsg_kdrv_get_devices(struct rpmsg_device *rpdev) +{ + int ret; + struct rpmsg_kdrv_init_dev_info_request *info_req; + + info_req = devm_kzalloc(&rpdev->dev, sizeof(*info_req), GFP_KERNEL); + if (!info_req) + return -ENOMEM; + + info_req->header.message_type = RPMSG_KDRV_TP_INIT_DEV_INFO_REQUEST; + + ret = rpmsg_kdrv_send_request_with_callback(rpdev, RPMSG_KDRV_TP_DEVICE_ID_INIT, + info_req, sizeof(*info_req), rpdev, rpmsg_kdrv_get_devices_cb); + if (ret) + goto nosend; + + return 0; + +nosend: + devm_kfree(&rpdev->dev, info_req); + return ret; +} + +static void rpmsg_kdrv_del_packet_id(struct rpmsg_device *rpdev, int id) +{ + struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev); + + mutex_lock(&priv->message_lock); + idr_remove(&priv->message_idr, id); + mutex_unlock(&priv->message_lock); +} + +static uint32_t rpmsg_kdrv_new_packet_id(struct rpmsg_device *rpdev, void *data) +{ + struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev); + int id; + + mutex_lock(&priv->message_lock); + id = idr_alloc(&priv->message_idr, data, RPMSG_KDRV_TP_PACKET_ID_FIRST, 0, GFP_KERNEL); + mutex_unlock(&priv->message_lock); + + if (id < 0) + return 0; + + return id; +} + +static void rpmsg_kdrv_dev_hdr_delete(struct rpmsg_device *rpdev, struct rpmsg_kdrv_device_header *hdr) +{ + rpmsg_kdrv_del_packet_id(rpdev, hdr->packet_id); + devm_kfree(&rpdev->dev, hdr); +} + +static struct rpmsg_kdrv_device_header *rpmsg_kdrv_dev_hdr_alloc(struct rpmsg_device *rpdev, + int device_id, int size, int pkt_type, int pkt_src, void *msg, int len, struct rpmsg_kdrv_ctx *ctx) +{ + struct rpmsg_kdrv_device_header *dev_hdr; + void *dst; + + dev_hdr = devm_kzalloc(&rpdev->dev, size, GFP_KERNEL); + if (!dev_hdr) + return NULL; + + dev_hdr->device_id = device_id; + dev_hdr->packet_type = pkt_type; + dev_hdr->packet_source = pkt_src; + dev_hdr->packet_size = size; + dev_hdr->packet_id = RPMSG_KDRV_TP_PACKET_ID_NONE; + + + dst = (void *)(&dev_hdr[1]); + memcpy(dst, msg, len); + + if (pkt_type == RPMSG_KDRV_TP_PACKET_TYPE_MESSAGE) + return dev_hdr; + + dev_hdr->packet_id = rpmsg_kdrv_new_packet_id(rpdev, ctx); + if (!dev_hdr->packet_id) { + devm_kfree(&rpdev->dev, dev_hdr); + return NULL; + } + + ctx->dev_hdr = dev_hdr; + + return dev_hdr; +} + +static struct rpmsg_kdrv_ctx *rpmsg_kdrv_ctx_alloc(struct rpmsg_device *rpdev, bool blocking, + request_cb_t callback, void *cb_data, void *req, int req_size, void *resp, int resp_size) +{ + struct rpmsg_kdrv_ctx *ctx; + + ctx = devm_kzalloc(&rpdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + ctx->rpdev = rpdev; + if (blocking) { + ctx->wait_for_response = true; + ctx->response_recv = false; + init_waitqueue_head(&ctx->response_wq); + } else { + ctx->wait_for_response = false; + ctx->callback = callback; + } + + ctx->cb_data = cb_data; + ctx->req = req; + ctx->req_size = req_size; + ctx->resp = resp; + ctx->resp_size = resp_size; + + return ctx; +} + +static int rpmsg_kdrv_send_packet(struct rpmsg_device *rpdev, void *data, int len) +{ + return rpmsg_send(rpdev->ept, data, len); +} + +/* + * rpmsg_kdrv_send_request_with_callback + * + * Send a message where + * a) the caller does not block + * b) the caller expects multile responses + * + * The callback function must return + * a) RRMSG_KDRV_CALLBACK_DONE when no more responses are expected + * b) RPMSG_KDRV_CALLBACK_MORE when more responses are awaited + * + * The caller is expected to destroy message when it does not + * expect any more responses + */ +int rpmsg_kdrv_send_request_with_callback(struct rpmsg_device *rpdev, uint32_t device_id, + void *message, uint32_t message_size, + void *cb_data, request_cb_t callback) +{ + struct rpmsg_kdrv_device_header *dev_hdr; + int total_size = message_size + sizeof(*dev_hdr); + struct rpmsg_kdrv_ctx *ctx = NULL; + int ret; + + ctx = rpmsg_kdrv_ctx_alloc(rpdev, false, callback, cb_data, message, message_size, NULL, 0); + if (!ctx) { + dev_err(&rpdev->dev, "%s: ctx allocation failed\n", __func__); + return -ENOMEM; + } + + dev_hdr = rpmsg_kdrv_dev_hdr_alloc(rpdev, device_id, total_size, + RPMSG_KDRV_TP_PACKET_TYPE_REQUEST, + RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT, + message, message_size, + ctx); + if (!dev_hdr) { + dev_err(&rpdev->dev, "%s: device header allocation failed\n", __func__); + ret = -ENOMEM; + goto dev_hdr_fail; + } + + ret = rpmsg_kdrv_send_packet(rpdev, dev_hdr, total_size); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + goto nosend; + } + + return 0; + +nosend: + rpmsg_kdrv_dev_hdr_delete(rpdev, dev_hdr); +dev_hdr_fail: + devm_kfree(&rpdev->dev, ctx); + return ret; +} +EXPORT_SYMBOL(rpmsg_kdrv_send_request_with_callback); + +/* + * rpmsg_kdrv_send_request_with_response + * + * Send a message where the caller will block for a response + * + * The caller is expected to destroy message and response + * when this function returns + */ +int rpmsg_kdrv_send_request_with_response(struct rpmsg_device *rpdev, uint32_t device_id, + void *message, uint32_t message_size, + void *response, uint32_t response_size) +{ + struct rpmsg_kdrv_device_header *dev_hdr; + int total_size = message_size + sizeof(*dev_hdr); + struct rpmsg_kdrv_ctx *ctx = NULL; + int ret; + + ctx = rpmsg_kdrv_ctx_alloc(rpdev, true, NULL, NULL, message, message_size, response, response_size); + if (!ctx) { + dev_err(&rpdev->dev, "%s: ctx allocation failed\n", __func__); + return -ENOMEM; + } + + dev_hdr = rpmsg_kdrv_dev_hdr_alloc(rpdev, device_id, total_size, + RPMSG_KDRV_TP_PACKET_TYPE_REQUEST, + RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT, + message, message_size, + ctx); + if (!dev_hdr) { + dev_err(&rpdev->dev, "%s: device header allocation failed\n", __func__); + ret = -ENOMEM; + goto dev_hdr_fail; + } + + ret = rpmsg_kdrv_send_packet(rpdev, dev_hdr, total_size); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + goto nosend; + } + + wait_event(ctx->response_wq, ctx->response_recv == true); + +nosend: + rpmsg_kdrv_dev_hdr_delete(rpdev, dev_hdr); +dev_hdr_fail: + devm_kfree(&rpdev->dev, ctx); + return ret; +} +EXPORT_SYMBOL(rpmsg_kdrv_send_request_with_response); + +/* + * rpmsg_kdrv_send_message + * + * Send a message and dont expect a response + * + * The caller is expected to destroy message when + * this function returns + */ +int rpmsg_kdrv_send_message(struct rpmsg_device *rpdev, uint32_t device_id, + void *message, uint32_t message_size) +{ + struct rpmsg_kdrv_device_header *dev_hdr; + int total_size = message_size + sizeof(*dev_hdr); + int ret; + + /* We dont need a ctx for direct messages */ + + dev_hdr = rpmsg_kdrv_dev_hdr_alloc(rpdev, device_id, total_size, + RPMSG_KDRV_TP_PACKET_TYPE_MESSAGE, + RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT, + message, message_size, + NULL); + if (!dev_hdr) { + dev_err(&rpdev->dev, "%s: device header allocation failed\n", __func__); + return -ENOMEM; + } + + ret = rpmsg_kdrv_send_packet(rpdev, dev_hdr, total_size); + if (ret) { + dev_err(&rpdev->dev, "%s: rpmsg_send failed: %d\n", __func__, ret); + goto out; + } + +out: + rpmsg_kdrv_dev_hdr_delete(rpdev, dev_hdr); + return ret; +} +EXPORT_SYMBOL(rpmsg_kdrv_send_message); + +static int rpmsg_kdrv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *private, u32 src) +{ + struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev); + struct rpmsg_kdrv_device_header *hdr = data; + struct rpmsg_kdrv_message_header *msg; + int msg_len; + struct rpmsg_kdrv_ctx *ctx; + int ret; + + if (hdr->packet_type != RPMSG_KDRV_TP_PACKET_TYPE_RESPONSE) { + rpmsg_kdrv_driver_handle_data(rpdev, data, len, private, src); + return 0; + } + + mutex_lock(&priv->message_lock); + ctx = idr_find(&priv->message_idr, hdr->packet_id); + mutex_unlock(&priv->message_lock); + + if (!ctx) { + dev_err(&rpdev->dev, "%s: response received with no pending request\n", __func__); + return 0; + } + + msg = (struct rpmsg_kdrv_message_header *)((void *)(&hdr[1])); + msg_len = len - sizeof(*hdr); + + /* process callback if expected */ + if (ctx->callback) { + ret = ctx->callback(ctx->cb_data, ctx->req, ctx->req_size, msg, msg_len); + if (ret == RRMSG_KDRV_CALLBACK_DONE) { + /* No need to keep the ctx alive */ + rpmsg_kdrv_dev_hdr_delete(rpdev, ctx->dev_hdr); + devm_kfree(&rpdev->dev, ctx); + } + return 0; + } + + /* copy the response and wake up caller, caller will destroy ctx & dev_hdr */ + memcpy(ctx->resp, msg, min(msg_len, ctx->resp_size)); + + ctx->response_recv = true; + wake_up(&ctx->response_wq); + + return 0; +} + +static int rpmsg_kdrv_dev_match(struct device *dev, struct device_driver *drv) +{ + struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev); + struct rpmsg_kdrv_driver *kddrv = to_rpmsg_kdrv_driver(drv); + + if (kddrv->device_type == kddev->device_type) { + dev_dbg(dev, "%s: matching with driver %s\n", __func__, drv->name); + return 1; + } + + dev_dbg(dev, "%s: does not match driver %s\n", __func__, drv->name); + return 0; +} + +static int rpmsg_kdrv_dev_probe(struct device *dev) +{ + struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev); + struct rpmsg_kdrv_driver *kddrv = to_rpmsg_kdrv_driver(kddev->dev.driver); + int ret; + + dev_dbg(dev, "%s: probe\n", __func__); + + ret = kddrv->probe(kddev); + if (ret) { + dev_err(dev, "%s: child probe failed\n", __func__); + return ret; + } + + return 0; +} + +static int rpmsg_kdrv_dev_remove(struct device *dev) +{ + struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev); + struct rpmsg_kdrv_driver *kddrv = to_rpmsg_kdrv_driver(kddev->dev.driver); + + dev_dbg(dev, "%s: remove\n", __func__); + + kddrv->remove(kddev); + return 0; +} + +static int rpmsg_kdrv_probe(struct rpmsg_device *rpdev) +{ + int ret; + struct rpmsg_kdrv_priv *priv; + + dev_dbg(&rpdev->dev, "%s: probing rpmsg kdrv driver\n", __func__); + + priv = devm_kzalloc(&rpdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&rpdev->dev, priv); + priv->rpdev = rpdev; + + idr_init(&priv->message_idr); + mutex_init(&priv->message_lock); + + dev_dbg(&rpdev->dev, "%s: sending device info request\n", __func__); + ret = rpmsg_kdrv_get_devices(rpdev); + if (ret) { + dev_err(&rpdev->dev, "%s: error collecting device info\n", __func__); + goto out; + } + + return 0; + +out: + dev_set_drvdata(&rpdev->dev, NULL); + devm_kfree(&rpdev->dev, priv); + return ret; +} + +static void rpmsg_kdrv_remove(struct rpmsg_device *rpdev) +{ + dev_dbg(&rpdev->dev, "removing rpmsg kdrv driver\n"); + + /* TODO check for pending responses for any of the child devices */ + /* TODO disconnect them all */ +} + +static struct bus_type rpmsg_kdrv_bus = { + .name = "rpmsg_kdrv", + .match = rpmsg_kdrv_dev_match, + .probe = rpmsg_kdrv_dev_probe, + .remove = rpmsg_kdrv_dev_remove, +}; + +static struct rpmsg_device_id rpmsg_kdrv_id_table[] = { + { .name = "rpmsg-kdrv" }, + { }, +}; + +static struct rpmsg_driver rpmsg_kdrv = { + .drv.name = "rpmsg-kdrv", + .id_table = rpmsg_kdrv_id_table, + .probe = rpmsg_kdrv_probe, + .callback = rpmsg_kdrv_cb, + .remove = rpmsg_kdrv_remove, +}; + +static int __init rpmsg_kdrv_init(void) +{ + int ret; + + ret = bus_register(&rpmsg_kdrv_bus); + if (ret) { + pr_err("failed to register rpmsg kdrv bus: %d\n", ret); + goto out; + } + + ret = register_rpmsg_driver(&rpmsg_kdrv); + if (ret) { + pr_err("failed to register rpmsg kdrv driver: %d\n", ret); + goto rpdrv_fail; + } + + pr_debug("registered rpmsg kdrv driver\n"); + + return 0; + +rpdrv_fail: + bus_unregister(&rpmsg_kdrv_bus); +out: + return ret; +} +module_init(rpmsg_kdrv_init); + +static void __exit rpmsg_kdrv_fini(void) +{ + pr_debug("unregistering rpmsg kdrv driver\n"); + + unregister_rpmsg_driver(&rpmsg_kdrv); + bus_unregister(&rpmsg_kdrv_bus); +} +module_exit(rpmsg_kdrv_fini); + +MODULE_AUTHOR("Subhajit Paul "); +MODULE_DESCRIPTION("TI Remote-device framework Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/rpmsg-kdrv/rpmsg_kdrv_demo.c b/drivers/rpmsg-kdrv/rpmsg_kdrv_demo.c --- a/drivers/rpmsg-kdrv/rpmsg_kdrv_demo.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/rpmsg_kdrv_demo.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#include +#include +#include + +#include +#include + +#include "shared/rpmsg-kdrv-transport-demo.h" +#include "rpmsg_kdrv_internal.h" + +struct rpmsg_kdrv_demo_private { + struct rpmsg_kdrv_device *kddev; + struct rpmsg_remotedev rdev; + + void *data; + ssize_t data_len; +}; + +static int rpmsg_kdrv_demo_get_data(struct rpmsg_remotedev *rdev, void *data, ssize_t len) +{ + struct rpmsg_kdrv_demo_private *priv = container_of(rdev, struct rpmsg_kdrv_demo_private, rdev); + + if (!data) + if (!len) + return priv->data_len; + else + return -EINVAL; + else if (len < priv->data_len) + return -EINVAL; + + memcpy(data, priv->data, priv->data_len); + return priv->data_len; +} + +static int rpmsg_kdrv_demo_ping(struct rpmsg_remotedev *rdev, void *ping_data, ssize_t ping_len) +{ + struct rpmsg_kdrv_demo_private *priv = container_of(rdev, struct rpmsg_kdrv_demo_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_demodev_ping_request *req; + struct rpmsg_kdrv_demodev_ping_response *resp; + int ret; + + if (!ping_len) + return 0; + + if (ping_len > RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_DEMODEV_PING_REQUEST; + memcpy(req->data, ping_data, ping_len); + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__); + goto out; + } + + + if (resp->header.message_type != RPMSG_KDRV_TP_DEMODEV_PING_RESPONSE) { + dev_err(&kddev->dev, "%s: wrong response type\n", __func__); + ret = -EIO; + goto out; + } + + + memcpy(ping_data, resp->data, RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN); + +out: + devm_kfree(&kddev->dev, req); + devm_kfree(&kddev->dev, resp); + return ret; +} + +static int rpmsg_kdrv_demo_c2s_message(struct rpmsg_remotedev *rdev, void *c2s_msg_data, ssize_t len) +{ + struct rpmsg_kdrv_demo_private *priv = container_of(rdev, struct rpmsg_kdrv_demo_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_demodev_c2s_message *msg; + int ret; + + if (!len) + return 0; + + if (len > RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN) + return -EINVAL; + + msg = devm_kzalloc(&kddev->dev, sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + + msg->header.message_type = RPMSG_KDRV_TP_DEMODEV_C2S_MESSAGE; + memcpy(msg->data, c2s_msg_data, len); + + ret = rpmsg_kdrv_send_message(rpdev, kddev->device_id, msg, sizeof(*msg)); + if (ret) + dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_message\n", __func__); + + devm_kfree(&kddev->dev, msg); + return ret; +} + + +static struct rpmsg_remotedev_demo_ops demo_ops = { + .get_data = rpmsg_kdrv_demo_get_data, + .ping = rpmsg_kdrv_demo_ping, + .c2s_message = rpmsg_kdrv_demo_c2s_message, +}; + +static void rpmsg_kdrv_demo_device_init(struct rpmsg_kdrv_device *dev, void *data, int len) +{ + struct rpmsg_kdrv_demo_private *priv = dev->driver_private; + + priv->data = devm_kzalloc(&dev->dev, len, GFP_KERNEL); + if (!priv->data) + return; + + memcpy(priv->data, data, len); + priv->data_len = len; +} + +static int rpmsg_kdrv_demo_probe(struct rpmsg_kdrv_device *dev) +{ + struct rpmsg_kdrv_demo_private *priv; + + dev_dbg(&dev->dev, "%s\n", __func__); + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rdev.type = RPMSG_REMOTEDEV_DEMO_DEVICE; + priv->rdev.device.demo.ops = &demo_ops; + + priv->kddev = dev; + dev->driver_private = priv; + dev->remotedev = &priv->rdev; + + rpmsg_kdrv_demo_device_init(dev, dev->device_data, dev->device_data_len); + + return 0; +} + +static void rpmsg_kdrv_demo_remove(struct rpmsg_kdrv_device *dev) +{ + dev_dbg(&dev->dev, "%s\n", __func__); +} + +static void rpmsg_kdrv_demo_handle_s2c_message(struct rpmsg_kdrv_device *dev, void *msg, ssize_t len) +{ + struct rpmsg_kdrv_demo_private *priv = dev->driver_private; + struct rpmsg_remotedev *rdev = &priv->rdev; + + if (rdev->device.demo.cb_ops && rdev->device.demo.cb_ops->s2c_message) + rdev->device.demo.cb_ops->s2c_message(msg, len, rdev->cb_data); +} + +static int rpmsg_kdrv_demo_callback(struct rpmsg_kdrv_device *dev, void *msg, int len) +{ + struct rpmsg_kdrv_demodev_message_header *hdr = msg; + + if (hdr->message_type == RPMSG_KDRV_TP_DEMODEV_S2C_MESSAGE) { + struct rpmsg_kdrv_demodev_s2c_message *s2c = msg; + + rpmsg_kdrv_demo_handle_s2c_message(dev, s2c->data, + RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN); + } else + dev_err(&dev->dev, "%s: unknown message type (%d) for demo device\n", __func__, hdr->message_type); + + return 0; +} + + +static struct rpmsg_kdrv_driver rpmsg_kdrv_demo = { + .drv.name = "rpmsg-kdrv-demo", + .device_type = RPMSG_KDRV_TP_DEVICE_TYPE_DEMO, + .probe = rpmsg_kdrv_demo_probe, + .remove = rpmsg_kdrv_demo_remove, + .callback = rpmsg_kdrv_demo_callback, +}; + +static int __init rpmsg_kdrv_demo_driver_init(void) +{ + return rpmsg_kdrv_register_driver(&rpmsg_kdrv_demo); +} +module_init(rpmsg_kdrv_demo_driver_init); + +static void rpmsg_kdrv_demo_driver_fini(void) +{ +} +module_exit(rpmsg_kdrv_demo_driver_fini); + +MODULE_AUTHOR("Subhajit Paul "); +MODULE_DESCRIPTION("TI Remote-device Demo Device Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/rpmsg-kdrv/rpmsg_kdrv_display.c b/drivers/rpmsg-kdrv/rpmsg_kdrv_display.c --- a/drivers/rpmsg-kdrv/rpmsg_kdrv_display.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/rpmsg_kdrv_display.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#include +#include +#include + +#include +#include +#include + +#include "shared/rpmsg-kdrv-transport-display.h" +#include "rpmsg_kdrv_internal.h" + +#define RPMSG_KDRV_DISPLAY_RES_ID_FIRST (0x10) + +struct rpmsg_kdrv_display_private { + struct rpmsg_kdrv_device *kddev; + + struct rpmsg_remotedev rdev; + + struct idr res_idr; + struct mutex res_lock; + +}; + +static uint32_t check_min(uint32_t a, uint32_t b, int line) +{ + uint32_t res = min(a, b); + + if (res != b) { + pr_err("Copy mismatch at Line %d\n", line); + WARN_ON(1); + } + + return res; +} + +static inline enum rpmsg_kdrv_display_format rpmsg_kdrv_display_fmt_to_rpmsg_fmt(uint32_t in_fmt) +{ + switch (in_fmt) { + case DRM_FORMAT_ARGB8888: + return RPMSG_KDRV_TP_DISPLAY_FORMAT_ARGB8888; + case DRM_FORMAT_XRGB8888: + return RPMSG_KDRV_TP_DISPLAY_FORMAT_XRGB8888; + default: + return RPMSG_KDRV_TP_DISPLAY_FORMAT_MAX; + } +} + +static inline uint32_t rpmsg_kdrv_display_fmt_to_drm_fmt(uint32_t in_fmt) +{ + switch (in_fmt) { + case RPMSG_KDRV_TP_DISPLAY_FORMAT_ARGB8888: + return DRM_FORMAT_ARGB8888; + case RPMSG_KDRV_TP_DISPLAY_FORMAT_XRGB8888: + return DRM_FORMAT_XRGB8888; + default: + return 0; + } +} + +static bool rpmsg_kdrv_display_ready(struct rpmsg_remotedev *rdev) +{ + struct rpmsg_kdrv_display_private *priv = container_of(rdev, struct rpmsg_kdrv_display_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_display_ready_query_request *req; + struct rpmsg_kdrv_display_ready_query_response *resp; + int ret; + bool retval; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return false; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return false; + } + + req->header.message_type = RPMSG_KDRV_TP_DISPLAY_READY_QUERY_REQUEST; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req), resp, sizeof(*resp)); + if (ret) { + dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__); + retval = false; + goto out; + } + + if (resp->header.message_type != RPMSG_KDRV_TP_DISPLAY_READY_QUERY_RESPONSE) { + dev_err(&kddev->dev, "%s: wrong response type\n", __func__); + retval = false; + goto out; + } + + retval = resp->ready ? true : false; + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return retval; + +} + +static void rpmsg_kdrv_display_copy_vid_info(struct rpmsg_remotedev_display_pipe *dst, struct rpmsg_kdrv_display_vid_info *src) +{ + int cnt; + uint32_t out_fmt; + + dst->pipe_id = src->id; + dst->can_scale = src->can_scale ? true : false; + dst->can_mod_win = src->mutable_window ? true : false; + if (dst->can_mod_win) + dst->fixed_win_x = dst->fixed_win_y = dst->fixed_win_w = dst->fixed_win_h = 0; + else { + dst->fixed_win_x = src->fixed_window_x; + dst->fixed_win_y = src->fixed_window_y; + dst->fixed_win_w = src->fixed_window_w; + dst->fixed_win_h = src->fixed_window_h; + } + dst->initial_zorder = src->init_zorder; + dst->num_formats = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_FORMATS, src->num_formats, __LINE__); + + dst->num_allowed_zorders = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_ZORDERS, src->num_zorders, __LINE__); + + for (cnt = 0; cnt < dst->num_formats; cnt++) { + out_fmt = rpmsg_kdrv_display_fmt_to_drm_fmt(src->format[cnt]); + WARN_ON(out_fmt == 0); + dst->formats[cnt] = out_fmt; + } + + for (cnt = 0; cnt < dst->num_allowed_zorders; cnt++) + dst->allowed_zorders[cnt] = src->zorder[cnt]; +} + +static void rpmsg_kdrv_display_copy_vp_info(struct rpmsg_remotedev_display_disp *dst, struct rpmsg_kdrv_display_vp_info *src) +{ + int vidcnt; + + dst->disp_id = src->id; + dst->width = src->width; + dst->height = src->height; + dst->refresh = src->refresh; + dst->num_pipes = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES, src->num_vids, __LINE__); + + for (vidcnt = 0; vidcnt < dst->num_pipes; vidcnt++) + rpmsg_kdrv_display_copy_vid_info(&dst->pipes[vidcnt], &src->vid[vidcnt]); +} + +static int rpmsg_kdrv_display_get_res(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_resinfo *res) +{ + struct rpmsg_kdrv_display_private *priv = container_of(rdev, struct rpmsg_kdrv_display_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_display_res_info_request *req; + struct rpmsg_kdrv_display_res_info_response *resp; + int ret, vpcnt; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_DISPLAY_RES_INFO_REQUEST; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req), resp, sizeof(*resp)); + if (ret) { + dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__); + goto out; + } + + if (resp->header.message_type != RPMSG_KDRV_TP_DISPLAY_RES_INFO_RESPONSE) { + dev_err(&kddev->dev, "%s: wrong response type\n", __func__); + ret = -EINVAL; + goto out; + } + + res->num_disps = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS, resp->num_vps, __LINE__); + + for (vpcnt = 0; vpcnt < res->num_disps; vpcnt++) + rpmsg_kdrv_display_copy_vp_info(&res->disps[vpcnt], &resp->vp[vpcnt]); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static uint32_t rpmsg_kdrv_display_res_id_new(struct rpmsg_kdrv_device *kddev, void *data) +{ + struct rpmsg_kdrv_display_private *priv = kddev->driver_private; + int id; + + mutex_lock(&priv->res_lock); + id = idr_alloc(&priv->res_idr, data, RPMSG_KDRV_DISPLAY_RES_ID_FIRST, 0, GFP_KERNEL); + mutex_unlock(&priv->res_lock); + + if (id < 0) + return 0; + + return id; +} + +static void rpmsg_kdrv_display_free_res_id(struct rpmsg_kdrv_device *kddev, uint32_t id) +{ + struct rpmsg_kdrv_display_private *priv = kddev->driver_private; + + mutex_lock(&priv->res_lock); + idr_remove(&priv->res_idr, id); + mutex_unlock(&priv->res_lock); +} + +static void rpmsg_kdrv_free_request_res(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_commit_request *req) +{ + int i; + + rpmsg_kdrv_display_free_res_id(kddev, req->commit_id); + + for (i = 0; i < req->num_vid_updates; i++) + if (req->vid[i].enabled) + rpmsg_kdrv_display_free_res_id(kddev, req->vid[i].buffer.buffer_id); + +} + +static bool rpmsg_kdrv_display_copy_buffer(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_buffer_info *dst, + struct rpmsg_remotedev_display_buffer *src) +{ + int i; + + dst->width = src->width; + dst->height = src->height; + + dst->format = rpmsg_kdrv_display_fmt_to_rpmsg_fmt(src->format); + if (WARN_ON(dst->format == RPMSG_KDRV_TP_DISPLAY_FORMAT_MAX)) + return false; + + dst->num_planes = check_min(RPMSG_KDRV_TP_DISPLAY_MAX_PLANES, src->num_planes, __LINE__); + if (dst->num_planes != src->num_planes) + return false; + + for (i = 0; i < dst->num_planes; i++) { + dst->plane[i] = (uint64_t)src->planes[i]; + dst->pitch[i] = src->pitches[i]; + } + + dst->buffer_id = rpmsg_kdrv_display_res_id_new(kddev, src); + if (!dst->buffer_id) + return false; + + return true; +} + +static bool rpmsg_kdrv_display_copy_vid_commit(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_vid_update_info *dst, + struct rpmsg_remotedev_display_pipe_update *src) +{ + dst->id = src->pipe_id; + dst->enabled = src->enabled ? 1 : 0; + if (dst->enabled) { + dst->dst_w = src->dst_w; + dst->dst_h = src->dst_h; + dst->dst_x = src->dst_x; + dst->dst_y = src->dst_y; + + if (!rpmsg_kdrv_display_copy_buffer(kddev, &dst->buffer, src->buffer)) + return false; + } + + return true; +} + +static bool rpmsg_kdrv_display_copy_commit(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_commit_request *dst, + struct rpmsg_remotedev_display_commit *src) +{ + int i, copied_vids; + + dst->id = src->disp_id; + dst->num_vid_updates = check_min(RPMSG_KDRV_TP_DISPLAY_MAX_VIDS, src->num_pipe_updates, __LINE__); + + for (i = 0, copied_vids = 0; i < dst->num_vid_updates; i++, copied_vids++) + if (!rpmsg_kdrv_display_copy_vid_commit(kddev, &dst->vid[i], &src->pipes[i])) + goto free_vid_res; + + dst->commit_id = rpmsg_kdrv_display_res_id_new(kddev, src); + if (!dst->commit_id) + goto free_vid_res; + + return true; + +free_vid_res: + for (i = 0; i < copied_vids; i++) + if (dst->vid[i].enabled) + rpmsg_kdrv_display_free_res_id(kddev, dst->vid[i].buffer.buffer_id); + return false; + +} + +static int rpmsg_kdrv_display_commit(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_commit *commit) +{ + struct rpmsg_kdrv_display_private *priv = container_of(rdev, struct rpmsg_kdrv_display_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_display_commit_request *req; + struct rpmsg_kdrv_display_commit_response *resp; + int ret; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_DISPLAY_COMMIT_REQUEST; + + if (!rpmsg_kdrv_display_copy_commit(kddev, req, commit)) { + dev_err(&kddev->dev, "%s: failed to copy commit request\n", __func__); + ret = -ENOMEM; + goto out; + } + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__); + goto nosend; + } + + + if (resp->header.message_type != RPMSG_KDRV_TP_DISPLAY_COMMIT_RESPONSE) { + dev_err(&kddev->dev, "%s: wrong response type\n", __func__); + goto out; + } + + ret = ((resp->status == 0) ? 0 : -EINVAL); + goto out; + +nosend: + rpmsg_kdrv_free_request_res(kddev, req); +out: + devm_kfree(&kddev->dev, req); + devm_kfree(&kddev->dev, resp); + return ret; +} + + +static struct rpmsg_remotedev_display_ops disp_ops = { + .ready = rpmsg_kdrv_display_ready, + .get_res_info = rpmsg_kdrv_display_get_res, + .commit = rpmsg_kdrv_display_commit, +}; + +static void rpmsg_kdrv_display_device_init(struct rpmsg_kdrv_device *kddev, void *data, int len) +{ +} + +static int rpmsg_kdrv_display_probe(struct rpmsg_kdrv_device *dev) +{ + struct rpmsg_kdrv_display_private *priv; + + dev_dbg(&dev->dev, "%s\n", __func__); + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rdev.type = RPMSG_REMOTEDEV_DISPLAY_DEVICE; + priv->rdev.device.display.ops = &disp_ops; + + mutex_init(&priv->res_lock); + idr_init(&priv->res_idr); + + priv->kddev = dev; + dev->driver_private = priv; + dev->remotedev = &priv->rdev; + + rpmsg_kdrv_display_device_init(dev, dev->device_data, dev->device_data_len); + + return 0; +} + +static void rpmsg_kdrv_display_remove(struct rpmsg_kdrv_device *dev) +{ + dev_dbg(&dev->dev, "%s\n", __func__); +} + +static void rpmsg_kdrv_display_handle_commit(struct rpmsg_kdrv_device *dev, struct rpmsg_kdrv_display_commit_done_message *msg) +{ + struct rpmsg_kdrv_display_private *priv = dev->driver_private; + struct rpmsg_remotedev *rdev = &priv->rdev; + struct rpmsg_remotedev_display_commit *commit; + + mutex_lock(&priv->res_lock); + commit = idr_find(&priv->res_idr, msg->commit_id); + idr_remove(&priv->res_idr, msg->commit_id); + mutex_unlock(&priv->res_lock); + + if (!commit) { + dev_err(&dev->dev, "%s: no pending commit found\n", __func__); + return; + } + + if (rdev->device.display.cb_ops && rdev->device.display.cb_ops->commit_done) + rdev->device.display.cb_ops->commit_done(commit, rdev->cb_data); +} + +static void rpmsg_kdrv_display_handle_buffer(struct rpmsg_kdrv_device *dev, struct rpmsg_kdrv_display_buffer_done_message *msg) +{ + struct rpmsg_kdrv_display_private *priv = dev->driver_private; + struct rpmsg_remotedev *rdev = &priv->rdev; + struct rpmsg_remotedev_display_buffer *buffer; + + mutex_lock(&priv->res_lock); + buffer = idr_find(&priv->res_idr, msg->buffer_id); + idr_remove(&priv->res_idr, msg->buffer_id); + mutex_unlock(&priv->res_lock); + + if (!buffer) { + dev_err(&dev->dev, "%s: no pending buffer found\n", __func__); + return; + } + + if (rdev->device.display.cb_ops && rdev->device.display.cb_ops->buffer_done) + rdev->device.display.cb_ops->buffer_done(buffer, rdev->cb_data); +} + +static int rpmsg_kdrv_display_callback(struct rpmsg_kdrv_device *dev, void *msg, int len) +{ + struct rpmsg_kdrv_display_message_header *hdr = msg; + + if (hdr->message_type == RPMSG_KDRV_TP_DISPLAY_COMMIT_DONE_MESSAGE) + rpmsg_kdrv_display_handle_commit(dev, msg); + else if (hdr->message_type == RPMSG_KDRV_TP_DISPLAY_BUFFER_DONE_MESSAGE) + rpmsg_kdrv_display_handle_buffer(dev, msg); + + return 0; +} + + +static struct rpmsg_kdrv_driver rpmsg_kdrv_display = { + .drv.name = "rpmsg-kdrv-display", + .device_type = RPMSG_KDRV_TP_DEVICE_TYPE_DISPLAY, + .probe = rpmsg_kdrv_display_probe, + .remove = rpmsg_kdrv_display_remove, + .callback = rpmsg_kdrv_display_callback, +}; + +static int __init rpmsg_kdrv_display_driver_init(void) +{ + return rpmsg_kdrv_register_driver(&rpmsg_kdrv_display); +} +module_init(rpmsg_kdrv_display_driver_init); + +static void rpmsg_kdrv_display_driver_fini(void) +{ +} +module_exit(rpmsg_kdrv_display_driver_fini); + +MODULE_AUTHOR("Subhajit Paul "); +MODULE_DESCRIPTION("TI Remote-device Virtual Display Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/rpmsg-kdrv/rpmsg_kdrv_internal.h b/drivers/rpmsg-kdrv/rpmsg_kdrv_internal.h --- a/drivers/rpmsg-kdrv/rpmsg_kdrv_internal.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/rpmsg_kdrv_internal.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#ifndef __RPMSG_KDRV_INTERNAL_H__ +#define __RPMSG_KDRV_INTERNAL_H__ + +#define RRMSG_KDRV_CALLBACK_DONE (0) +#define RRMSG_KDRV_CALLBACK_MORE (1) + +struct rpmsg_kdrv_device { + struct device dev; + struct rpmsg_device *rpdev; + int device_type; + int device_id; + void *device_data; + int device_data_len; + char *device_name; + void *device_private; + void *driver_private; + struct rpmsg_remotedev *remotedev; +}; + +struct rpmsg_kdrv_driver { + struct device_driver drv; + int device_type; + int (*probe)(struct rpmsg_kdrv_device *dev); + void (*remove)(struct rpmsg_kdrv_device *dev); + int (*callback)(struct rpmsg_kdrv_device *dev, void *msg, int len); +}; + +typedef int (*request_cb_t)(void *data, void *req, int req_sz, void *resp, int resp_sz); + +extern int rpmsg_kdrv_register_driver(struct rpmsg_kdrv_driver *drv); + +extern int rpmsg_kdrv_send_request_with_callback(struct rpmsg_device *rpdev, + uint32_t device_id, void *message, uint32_t message_size, void *cb_data, + request_cb_t callback); +extern int rpmsg_kdrv_send_request_with_response(struct rpmsg_device *rpdev, + uint32_t device_id, void *message, uint32_t message_size, + void *response, uint32_t response_size); +extern int rpmsg_kdrv_send_message(struct rpmsg_device *rpdev, + uint32_t device_id, void *message, uint32_t message_size); + + +#endif diff -Naur --no-dereference a/drivers/rpmsg-kdrv/rpmsg_kdrv_switch.c b/drivers/rpmsg-kdrv/rpmsg_kdrv_switch.c --- a/drivers/rpmsg-kdrv/rpmsg_kdrv_switch.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/rpmsg_kdrv_switch.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,977 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Grygorii Strashko + */ + +#include +#include +#include +#include + +#include "rpmsg_kdrv_internal.h" +#include "shared/rpmsg-kdrv-transport-switch.h" + +struct rpmsg_kdrv_switch_private { + struct rpmsg_kdrv_device *kddev; + struct rpmsg_remotedev rdev; + u64 session_id; + u32 core_key; + u32 permissions; + u32 uart_id; + u32 attached:1; + u32 uart_connected:1; +}; + +static bool +rpmsg_kdrv_switch_check_perm(struct rpmsg_kdrv_switch_private *priv, + enum rpmsg_kdrv_ethswitch_message_type msg_type) +{ + struct rpmsg_kdrv_device *kddev = priv->kddev; + + if (priv->permissions & BIT(msg_type)) + return true; + + dev_err_ratelimited(&kddev->dev, "permission denied msg: 0x%02X\n", + msg_type); + return false; +} + +static int +rpmsg_kdrv_switch_check_resp_status(struct rpmsg_kdrv_ethswitch_common_resp_info *info) +{ + if (info->status == RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EAGAIN) + return -EAGAIN; + if (info->status == RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EFAIL) + return -EIO; + return 0; +} + +static int rpmsg_kdrv_switch_ping(struct rpmsg_remotedev *rdev, + const u8 *data, int size) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_ping_req *req; + struct rpmsg_kdrv_ethswitch_ping_resp *resp; + int ret; + + if (!rpmsg_kdrv_switch_check_perm(priv, + RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST)) + return -EPERM; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + if (size > RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN) + size = RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN; + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST; + memcpy(req->data, data, size); + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + if (memcmp(req->data, resp->data, size)) { + dev_dbg(&kddev->dev, "%s: ping fail - data\n", __func__); + ret = -EINVAL; + } + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int +rpmsg_kdrv_switch_attach(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_attach_info *attach_info) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_attach_req *req; + struct rpmsg_kdrv_ethswitch_attach_resp *resp; + int ret; + + if (priv->attached) + return -EBUSY; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ATTACH; + req->cpsw_type = RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_MAIN_CPSW; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + priv->session_id = resp->id; + priv->core_key = resp->core_key; + priv->attached = true; + + dev_dbg(&kddev->dev, "%s: done id:%llX core_key:%08X\n", + __func__, priv->session_id, priv->core_key); + + attach_info->rx_mtu = resp->rx_mtu; + memcpy(attach_info->tx_mtu, resp->tx_mtu, sizeof(attach_info->tx_mtu)); + attach_info->features = resp->features; + if (priv->uart_connected) + attach_info->features |= + RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS; + attach_info->mac_only_port = resp->mac_only_port; + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int +rpmsg_kdrv_switch_attach_ext(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_attach_ext_info *attach_ext_info) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_attach_extended_req *req; + struct rpmsg_kdrv_ethswitch_attach_extended_resp *resp; + int ret; + + if (priv->attached) + return -EBUSY; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT; + req->cpsw_type = RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_MAIN_CPSW; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + priv->session_id = resp->id; + priv->core_key = resp->core_key; + priv->attached = true; + + dev_dbg(&kddev->dev, "%s: done id:%llX core_key:%08X\n", + __func__, priv->session_id, priv->core_key); + + attach_ext_info->rx_mtu = resp->rx_mtu; + memcpy(attach_ext_info->tx_mtu, resp->tx_mtu, + sizeof(attach_ext_info->tx_mtu)); + attach_ext_info->features = resp->features; + if (priv->uart_connected) + attach_ext_info->features |= + RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS; + attach_ext_info->flow_idx = resp->alloc_flow_idx; + attach_ext_info->tx_cpsw_psil_dst_id = resp->tx_cpsw_psil_dst_id; + ether_addr_copy(attach_ext_info->mac_addr, resp->mac_address); + if (attach_ext_info->features | RPMSG_KDRV_ETHSWITCH_FEATURE_MAC_ONLY) + attach_ext_info->mac_only_port = resp->mac_only_port; + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_detach(struct rpmsg_remotedev *rdev) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_detach_req *req; + struct rpmsg_kdrv_ethswitch_detach_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_DETACH; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + priv->attached = false; + + dev_dbg(&kddev->dev, "%s: done ret:%d\n", __func__, ret); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int +rpmsg_kdrv_switch_get_tx_info(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_tx_info *info) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_alloc_req *req; + struct rpmsg_kdrv_ethswitch_alloc_tx_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + info->tx_cpsw_psil_dst_id = resp->tx_cpsw_psil_dst_id; + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int +rpmsg_kdrv_switch_get_rx_info(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_rx_info *info) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_alloc_req *req; + struct rpmsg_kdrv_ethswitch_alloc_rx_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + info->flow_idx = resp->alloc_flow_idx; + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_get_mac(struct rpmsg_remotedev *rdev, + void *mac_addr) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_alloc_req *req; + struct rpmsg_kdrv_ethswitch_alloc_mac_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + ether_addr_copy(mac_addr, resp->mac_address); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_register_mac(struct rpmsg_remotedev *rdev, + void *mac_addr, u32 flow_idx_offset) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_register_mac_req *req; + struct rpmsg_kdrv_ethswitch_register_mac_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + ether_addr_copy(req->mac_address, mac_addr); + req->flow_idx = flow_idx_offset; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int +rpmsg_kdrv_switch_unregister_mac(struct rpmsg_remotedev *rdev, + void *mac_addr, u32 flow_idx_offset) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_unregister_mac_req *req; + struct rpmsg_kdrv_ethswitch_unregister_mac_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + ether_addr_copy(req->mac_address, mac_addr); + req->flow_idx = flow_idx_offset; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_reg_ipv4(struct rpmsg_remotedev *rdev, + void *mac_addr, __be32 ipv4) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_ipv4_register_mac_req *req; + struct rpmsg_kdrv_ethswitch_ipv4_register_mac_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + ether_addr_copy(req->mac_address, mac_addr); + memcpy(req->ipv4_addr, &ipv4, RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN); + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_unreg_ipv4(struct rpmsg_remotedev *rdev, + __be32 ipv4) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_req *req; + struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + memcpy(req->ipv4_addr, &ipv4, RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN); + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_reg_read(struct rpmsg_remotedev *rdev, + u32 reg_addr, u32 *val) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_regrd_req *req; + struct rpmsg_kdrv_ethswitch_regrd_resp *resp; + int ret; + + if (!rpmsg_kdrv_switch_check_perm(priv, RPMSG_KDRV_TP_ETHSWITCH_REGRD)) + return -EPERM; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_REGRD; + req->regaddr = reg_addr; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + *val = resp->regval; + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_c2s_dbg_dump_stats(struct rpmsg_remotedev *rdev) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_c2s_notify *req; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + req->notifyid = RPMSG_KDRV_TP_ETHSWITCH_CLIENTNOTIFY_DUMPSTATS; + + ret = rpmsg_kdrv_send_message(rpdev, kddev->device_id, + req, sizeof(*req)); + + dev_dbg(&kddev->dev, "%s: done ret:%d\n", __func__, ret); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_set_promisc(struct rpmsg_remotedev *rdev, u32 enable) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_set_promisc_mode_req *req; + struct rpmsg_kdrv_ethswitch_set_promisc_mode_resp *resp; + int ret; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_SET_PROMISC_MODE; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + req->enable = enable ? 1 : 0; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + if (ret) + goto out; + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_filter_add_mc(struct rpmsg_remotedev *rdev, + const void *mac_addr, u16 vlan_id, u32 flow_idx) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_filter_add_mc_req *req; + struct rpmsg_kdrv_ethswitch_filter_add_mc_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_FILTER_ADD_MAC; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + ether_addr_copy(req->mac_address, mac_addr); + req->flow_idx = flow_idx; + req->vlan_id = vlan_id; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static int rpmsg_kdrv_switch_filter_del_mc(struct rpmsg_remotedev *rdev, + const void *mac_addr, u16 vlan_id, u32 flow_idx) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_device *kddev = priv->kddev; + struct rpmsg_device *rpdev = kddev->rpdev; + struct rpmsg_kdrv_ethswitch_filter_del_mc_req *req; + struct rpmsg_kdrv_ethswitch_filter_del_mc_resp *resp; + int ret; + + if (!priv->attached) + return -EINVAL; + + req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL); + if (!resp) { + devm_kfree(&kddev->dev, req); + return -ENOMEM; + } + + req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_FILTER_DEL_MAC; + req->info.id = priv->session_id; + req->info.core_key = priv->core_key; + ether_addr_copy(req->mac_address, mac_addr); + req->flow_idx = flow_idx; + req->vlan_id = vlan_id; + + ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, + req, sizeof(*req), + resp, sizeof(*resp)); + if (ret) { + dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret); + goto out; + } + + ret = rpmsg_kdrv_switch_check_resp_status(&resp->info); + + dev_dbg(&kddev->dev, "%s: done\n", __func__); + +out: + devm_kfree(&kddev->dev, resp); + devm_kfree(&kddev->dev, req); + return ret; +} + +static void rpmsg_kdrv_switch_get_fw_ver(struct rpmsg_remotedev *rdev, + char *buf, size_t size) +{ + struct rpmsg_kdrv_switch_private *priv = + container_of(rdev, struct rpmsg_kdrv_switch_private, rdev); + struct rpmsg_kdrv_ethswitch_fw_version_info *fw_info; + struct rpmsg_kdrv_ethswitch_device_data *kddev_data; + + kddev_data = priv->kddev->device_data; + fw_info = &kddev_data->fw_ver; + + snprintf(buf, size, "%u.%u.%u %.*s/%.*s/%.*s SHA:%.*s", + fw_info->major, fw_info->minor, fw_info->rev, + RPMSG_KDRV_TP_ETHSWITCH_DATELEN, fw_info->date, + RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN, fw_info->month, + RPMSG_KDRV_TP_ETHSWITCH_YEARLEN, fw_info->year, + RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN, fw_info->commit_hash); +} + +static struct rpmsg_remotedev_eth_switch_ops switch_ops = { + .get_fw_ver = rpmsg_kdrv_switch_get_fw_ver, + .attach = rpmsg_kdrv_switch_attach, + .attach_ext = rpmsg_kdrv_switch_attach_ext, + .detach = rpmsg_kdrv_switch_detach, + .get_tx_info = rpmsg_kdrv_switch_get_tx_info, + .get_rx_info = rpmsg_kdrv_switch_get_rx_info, + .get_mac = rpmsg_kdrv_switch_get_mac, + .register_mac = rpmsg_kdrv_switch_register_mac, + .unregister_mac = rpmsg_kdrv_switch_unregister_mac, + .register_ipv4 = rpmsg_kdrv_switch_reg_ipv4, + .unregister_ipv4 = rpmsg_kdrv_switch_unreg_ipv4, + .ping = rpmsg_kdrv_switch_ping, + .read_reg = rpmsg_kdrv_switch_reg_read, + .dbg_dump_stats = rpmsg_kdrv_switch_c2s_dbg_dump_stats, + .set_promisc_mode = rpmsg_kdrv_switch_set_promisc, + .filter_add_mc = rpmsg_kdrv_switch_filter_add_mc, + .filter_del_mc = rpmsg_kdrv_switch_filter_del_mc, +}; + +static int rpmsg_kdrv_switch_callback(struct rpmsg_kdrv_device *dev, + void *msg, int len) +{ + return 0; +} + +static int +rpmsg_kdrv_switch_dev_data_parse(struct rpmsg_kdrv_device *kddev, + void *data, int len, + struct rpmsg_kdrv_switch_private *priv) +{ + struct rpmsg_kdrv_ethswitch_device_data *kddev_data = data; + struct rpmsg_kdrv_ethswitch_fw_version_info *fw_info; + + if (sizeof(*kddev_data) != len) + return -EINVAL; + + dev_info(&kddev->dev, "Device info: permissions: %08X uart_id: %d\n", + kddev_data->permission_flags, + kddev_data->uart_connected ? kddev_data->uart_id : -1); + + fw_info = &kddev_data->fw_ver; + + dev_info(&kddev->dev, "FW ver %u.%u (rev %u) %.*s/%.*s/%.*s SHA:%.*s\n", + fw_info->major, fw_info->minor, fw_info->rev, + RPMSG_KDRV_TP_ETHSWITCH_DATELEN, fw_info->date, + RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN, fw_info->month, + RPMSG_KDRV_TP_ETHSWITCH_YEARLEN, fw_info->year, + RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN, fw_info->commit_hash); + + if (fw_info->major != RPMSG_KDRV_TP_ETHSWITCH_VERSION_MAJOR && + fw_info->minor != RPMSG_KDRV_TP_ETHSWITCH_VERSION_MINOR) { + dev_err(&kddev->dev, "Unsupported EthSwitch FW version\n"); + return -EOPNOTSUPP; + } + + priv->uart_connected = kddev_data->uart_connected; + priv->uart_id = kddev_data->uart_connected ? kddev_data->uart_id : -1; + priv->permissions = kddev_data->permission_flags; + + return 0; +} + +static int rpmsg_kdrv_switch_probe(struct rpmsg_kdrv_device *dev) +{ + struct rpmsg_kdrv_switch_private *priv; + int ret; + + dev_dbg(&dev->dev, "%s\n", __func__); + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rdev.type = RPMSG_REMOTEDEV_ETH_SWITCH_DEVICE; + priv->rdev.device.eth_switch.ops = &switch_ops; + + priv->kddev = dev; + + ret = rpmsg_kdrv_switch_dev_data_parse(dev, dev->device_data, + dev->device_data_len, priv); + if (ret) + return ret; + + dev->driver_private = priv; + dev->remotedev = &priv->rdev; + + return 0; +} + +static void rpmsg_kdrv_switch_remove(struct rpmsg_kdrv_device *dev) +{ + dev->driver_private = NULL; + dev->remotedev = NULL; + + dev_dbg(&dev->dev, "%s\n", __func__); +} + +static struct rpmsg_kdrv_driver rpmsg_kdrv_switch = { + .drv = { + .name = "rpmsg-kdrv-eth-switch", + }, + .device_type = RPMSG_KDRV_TP_DEVICE_TYPE_ETHSWITCH, + .probe = rpmsg_kdrv_switch_probe, + .remove = rpmsg_kdrv_switch_remove, + .callback = rpmsg_kdrv_switch_callback, +}; + +static int __init rpmsg_kdrv_display_driver_init(void) +{ + return rpmsg_kdrv_register_driver(&rpmsg_kdrv_switch); +} +module_init(rpmsg_kdrv_display_driver_init); + +static void rpmsg_kdrv_display_driver_fini(void) +{ +} +module_exit(rpmsg_kdrv_display_driver_fini); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Grygorii Strashko "); +MODULE_DESCRIPTION("TI J721E RPMSG KDRV Ethernet switch driver"); diff -Naur --no-dereference a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-common.h b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-common.h --- a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-common.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-common.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#ifndef __RPMSG_KDRV_TRANSPORT_COMMON_H__ +#define __RPMSG_KDRV_TRANSPORT_COMMON_H__ + +/* + * Device types supported by RPMSG-KDRV framework + * Currently supported device types: display + * Planned future support for capture and i2c devices + */ +#define RPMSG_KDRV_TP_DEVICE_TYPE_INIT (0x0) +#define RPMSG_KDRV_TP_DEVICE_TYPE_DISPLAY (0x1) +#define RPMSG_KDRV_TP_DEVICE_TYPE_DEMO (0x2) +#define RPMSG_KDRV_TP_DEVICE_TYPE_ETHSWITCH (0x3) + +/* More device types here*/ +#define RPMSG_KDRV_TP_DEVICE_TYPE_MAX (0x4) + +/* + * Maximum number of proxy devices per remotecore + */ +#define RPMSG_KDRV_TP_MAX_DEVICES (4) + +/* + * Maximum length of proxy device name + */ +#define RPMSG_KDRV_TP_DEVICE_NAME_LEN (32) + +/* + * Statically assigned device ID for init device + * Remote device framework dynamically assigns device + * IDs for other devices. All dynamically assigned IDs + * are greater than RPMSG_KDRV_TP_DEVICE_ID_INIT + */ +#define RPMSG_KDRV_TP_DEVICE_ID_INIT (0) + +/* + * Packet IDs are assigned dynamically (for REQUEST packets) + * starting from RPMSG_KDRV_TP_PACKET_ID_FIRST + * For MESSAGE packets, framework can use RPMSG_KDRV_TP_PACKET_ID_NONE + */ +#define RPMSG_KDRV_TP_PACKET_ID_NONE (0x10) +#define RPMSG_KDRV_TP_PACKET_ID_FIRST (RPMSG_KDRV_TP_PACKET_ID_NONE + 1) + +enum rpmsg_kdrv_packet_source { + RPMSG_KDRV_TP_PACKET_SOURCE_SERVER, + RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT, + RPMSG_KDRV_TP_PACKET_SOURCE_MAX, +}; + +enum rpmsg_kdrv_packet_type { + RPMSG_KDRV_TP_PACKET_TYPE_REQUEST, + RPMSG_KDRV_TP_PACKET_TYPE_RESPONSE, + RPMSG_KDRV_TP_PACKET_TYPE_MESSAGE, + RPMSG_KDRV_TP_PACKET_TYPE_MAX, +}; + +/*RPMSG_KDRV message : + * => device_header + * => message_header : defined by each device type + * => request / response / message payload + */ +struct rpmsg_kdrv_device_header { + /* ID of device sending the packet */ + u8 device_id; + /* enum: rpmsg_kdrv_packet_type */ + u8 packet_type; + /* enum: rpmsg_kdrv_packet_source */ + u8 packet_source; + /* dynamically assigned packet ID for response matching */ + u32 packet_id; + /* size of packet */ + u32 packet_size; +} __packed; + +#endif diff -Naur --no-dereference a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-demo.h b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-demo.h --- a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-demo.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-demo.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#ifndef __RPMSG_KDRV_TRANSPORT_DEMODEV_H__ +#define __RPMSG_KDRV_TRANSPORT_DEMODEV_H__ + +#include "rpmsg-kdrv-transport-common.h" + +enum rpmsg_kdrv_display_message_type { + RPMSG_KDRV_TP_DEMODEV_PING_REQUEST, + RPMSG_KDRV_TP_DEMODEV_PING_RESPONSE, + RPMSG_KDRV_TP_DEMODEV_S2C_MESSAGE, + RPMSG_KDRV_TP_DEMODEV_C2S_MESSAGE, + RPMSG_KDRV_TP_DEMODEV_MAX, +}; + +/* + * Maximum length of demo device data + */ +#define RPMSG_KDRV_TP_DEMODEV_DEVICE_DATA_LEN (32) + +/* + * Maximum length of demo device message data + */ +#define RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN (128) + +/* + * per-device data for demo device + */ +struct rpmsg_kdrv_demodev_device_data { + /* Does the device send all vsyncs? */ + u8 charString[RPMSG_KDRV_TP_DEMODEV_DEVICE_DATA_LEN]; +} __packed; + +/* + * message header for demo device + */ +struct rpmsg_kdrv_demodev_message_header { + /* enum: rpmsg_kdrv_demodev_message_type */ + u8 message_type; +} __packed; + +/* demo device ping request - always client to server */ +struct rpmsg_kdrv_demodev_ping_request { + /* message header */ + struct rpmsg_kdrv_demodev_message_header header; + /* ping data */ + u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN]; +} __packed; + +/* demo device ping response - always server to client */ +struct rpmsg_kdrv_demodev_ping_response { + /* message header */ + struct rpmsg_kdrv_demodev_message_header header; + /* ping data */ + u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN]; +} __packed; + +/* demo device server to client one-way message */ +struct rpmsg_kdrv_demodev_s2c_message { + /* message header */ + struct rpmsg_kdrv_demodev_message_header header; + /* message data */ + u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN]; +} __packed; + +/* demo device client to server one-way message */ +struct rpmsg_kdrv_demodev_c2s_message { + /* message header */ + struct rpmsg_kdrv_demodev_message_header header; + /* message data */ + u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN]; +} __packed; + +#endif diff -Naur --no-dereference a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-display.h b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-display.h --- a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-display.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-display.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#ifndef __RPMSG_KDRV_TRANSPORT_DISPLAY_H__ +#define __RPMSG_KDRV_TRANSPORT_DISPLAY_H__ + +#include "rpmsg-kdrv-transport-common.h" + +/* + * Maximum number of planes per buffer + */ +#define RPMSG_KDRV_TP_DISPLAY_MAX_PLANES (2) + +/* + * Maximum number of shared displays + */ +#define RPMSG_KDRV_TP_DISPLAY_MAX_VPS (2) + +/* + * Maximum number of pipes per shared display + */ +#define RPMSG_KDRV_TP_DISPLAY_MAX_VIDS (4) + +/* + * Maximum number of formats supported per pipe + */ +#define RPMSG_KDRV_TP_DISPLAY_MAX_FORMATS (2) + +/* + * Maximum number of zorders supported per pipe + */ +#define RPMSG_KDRV_TP_DISPLAY_MAX_ZORDERS (4) + +enum rpmsg_kdrv_display_format { + RPMSG_KDRV_TP_DISPLAY_FORMAT_ARGB8888, + RPMSG_KDRV_TP_DISPLAY_FORMAT_XRGB8888, + RPMSG_KDRV_TP_DISPLAY_FORMAT_MAX, +}; + +enum rpmsg_kdrv_display_message_type { + RPMSG_KDRV_TP_DISPLAY_READY_QUERY_REQUEST, + RPMSG_KDRV_TP_DISPLAY_READY_QUERY_RESPONSE, + RPMSG_KDRV_TP_DISPLAY_RES_INFO_REQUEST, + RPMSG_KDRV_TP_DISPLAY_RES_INFO_RESPONSE, + RPMSG_KDRV_TP_DISPLAY_COMMIT_REQUEST, + RPMSG_KDRV_TP_DISPLAY_COMMIT_RESPONSE, + RPMSG_KDRV_TP_DISPLAY_COMMIT_DONE_MESSAGE, + RPMSG_KDRV_TP_DISPLAY_BUFFER_DONE_MESSAGE, + RPMSG_KDRV_TP_DISPLAY_MAX, +}; + +/* + * per-device data for display device + */ +struct rpmsg_kdrv_display_device_data { + /* Does the device send all vsyncs? */ + u8 periodic_vsync; + /*Does the device defer the use of buffers? */ + u8 deferred_buffer_usage; +} __packed; + +/* + * message header for display device + */ +struct rpmsg_kdrv_display_message_header { + /* enum: rpmsg_kdrv_display_message_type */ + u8 message_type; +} __packed; + +/* display device request to provide ready / not-ready info */ +struct rpmsg_kdrv_display_ready_query_request { + /* message header */ + struct rpmsg_kdrv_display_message_header header; +} __packed; + +/* display device response indicating ready / not-ready status */ +struct rpmsg_kdrv_display_ready_query_response { + /* message header */ + struct rpmsg_kdrv_display_message_header header; + /* can be 0 : if not ready 1: if ready */ + u8 ready; +} __packed; + +/* display device buffer update info */ +struct rpmsg_kdrv_display_buffer_info { + /* buffer width */ + u16 width; + /* buffer height */ + u16 height; + /* enum: rpmsg_kdrv_display_format */ + u8 format; + /* number of planes */ + u8 num_planes; + /* per plane start addresses */ + u64 plane[RPMSG_KDRV_TP_DISPLAY_MAX_PLANES]; + /* per plane pitch */ + u16 pitch[RPMSG_KDRV_TP_DISPLAY_MAX_PLANES]; + /* buffer id : to be used in buffer-done message */ + u32 buffer_id; +} __packed; + +/* display device pipe update info */ +struct rpmsg_kdrv_display_vid_update_info { + /* pipe ID */ + u8 id; + /*enable / disable request */ + u8 enabled; + /* window width */ + u16 dst_w; + /* window height */ + u16 dst_h; + /* window position X */ + u16 dst_x; + /* window position Y */ + u16 dst_y; + /* buffer */ + struct rpmsg_kdrv_display_buffer_info buffer; +} __packed; + +/* display device commit request */ +struct rpmsg_kdrv_display_commit_request { + /* message header */ + struct rpmsg_kdrv_display_message_header header; + /*ID of shared display */ + u8 id; + /* number of pipe updates in the commit */ + u8 num_vid_updates; + /* list of pipe updates */ + struct rpmsg_kdrv_display_vid_update_info vid[RPMSG_KDRV_TP_DISPLAY_MAX_VIDS]; + /*commit id : to be used in commit-done message */ + u32 commit_id; +} __packed; + +/* display device commit response */ +struct rpmsg_kdrv_display_commit_response { + /* message header */ + struct rpmsg_kdrv_display_message_header header; + /*commit id : from commit request */ + u32 commit_id; + /*status : 0 = accepted, 1 = rejected */ + u8 status; +} __packed; + +/* display device commit done message */ +struct rpmsg_kdrv_display_commit_done_message { + /* message header */ + struct rpmsg_kdrv_display_message_header header; + /* commit id : from commit request */ + u32 commit_id; +} __packed; + +/*display device buffer deferred release message */ +struct rpmsg_kdrv_display_buffer_done_message { + /* message header */ + struct rpmsg_kdrv_display_message_header header; + /* buffer id: from bufer_info */ + u32 buffer_id; +} __packed; + +/* display device request to provide list of shared resources */ +struct rpmsg_kdrv_display_res_info_request { + /* message header */ + struct rpmsg_kdrv_display_message_header header; +} __packed; + +/* display device shared pipe */ +struct rpmsg_kdrv_display_vid_info { + /* pipe ID */ + u8 id; + /* is pipe window fixed on display? */ + u8 mutable_window; + /* fixed window position X, if applicable */ + u16 fixed_window_x; + /* fixed window position Y, if applicable */ + u16 fixed_window_y; + /* fixed window width, if applicable */ + u16 fixed_window_w; + /* fixed window height, if applicable */ + u16 fixed_window_h; + /* can pipe scale buffers? */ + u8 can_scale; + /* number of formats supported */ + u8 num_formats; + /*enum: rpmsg_kdrv_display_format */ + u8 format[RPMSG_KDRV_TP_DISPLAY_MAX_FORMATS]; + /* initial zorder of pipe */ + u8 init_zorder; + /* number of allowed zorders */ + u8 num_zorders; + /* list of allowed zorders */ + u8 zorder[RPMSG_KDRV_TP_DISPLAY_MAX_ZORDERS]; +} __packed; + +/* display device shared display */ +struct rpmsg_kdrv_display_vp_info { + /* ID of shared display */ + u8 id; + /* raster width */ + u16 width; + /* raster height */ + u16 height; + /* refresh rate */ + u8 refresh; + /* number of pipes for this display */ + u8 num_vids; + /* list of pipes */ + struct rpmsg_kdrv_display_vid_info vid[RPMSG_KDRV_TP_DISPLAY_MAX_VIDS]; +} __packed; + +/* display device response providing list of shared resources */ +struct rpmsg_kdrv_display_res_info_response { + /* message header */ + struct rpmsg_kdrv_display_message_header header; + /* number of shared displays */ + u8 num_vps; + /* list of shared displays */ + struct rpmsg_kdrv_display_vp_info vp[RPMSG_KDRV_TP_DISPLAY_MAX_VPS]; +} __packed; + +#endif diff -Naur --no-dereference a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport.h b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport.h --- a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#ifndef __RPMSG_KDRV_TRANSPORT_H__ +#define __RPMSG_KDRV_TRANSPORT_H__ + +#include "rpmsg-kdrv-transport-common.h" + +enum rpmsg_kdrv_init_message_type { + RPMSG_KDRV_TP_INIT_DEV_INFO_REQUEST, + RPMSG_KDRV_TP_INIT_DEV_INFO_RESPONSE, + RPMSG_KDRV_TP_INIT_CONNECT_MESSAGE, + RPMSG_KDRV_TP_INIT_DISCONNECT_MESSAGE, + RPMSG_KDRV_TP_INIT_MAX, +}; + +/* + * message header for init device + */ +struct rpmsg_kdrv_init_message_header { + /* enum: rpmsg_kdrv_init_message_type */ + u8 message_type; +} __packed; + +/* + * init device request to provide list of devices + */ +struct rpmsg_kdrv_init_dev_info_request { + /* message header */ + struct rpmsg_kdrv_init_message_header header; +} __packed; + +struct rpmsg_kdrv_init_device_info { + /* device id */ + u8 device_id; + /* device type (display, capture etc) */ + u8 device_type; + /* name of device */ + u8 device_name[RPMSG_KDRV_TP_DEVICE_NAME_LEN]; + /* device specific info length */ + u16 device_data_len; + /* per device-type info offset */ + u16 device_data_offset; +} __packed; + +/* + * init device response with list of devices + */ +struct rpmsg_kdrv_init_dev_info_response { + /* message header */ + struct rpmsg_kdrv_init_message_header header; + /*number of exported devices */ + u8 num_devices; + /* list of exported devices */ + struct rpmsg_kdrv_init_device_info devices[RPMSG_KDRV_TP_MAX_DEVICES]; + /* device specific data */ + u8 device_data[0]; +} __packed; + +/* + * init device per-device connect message + */ +struct rpmsg_kdrv_init_connect_message { + /* message header */ + struct rpmsg_kdrv_init_message_header header; + /* device ID to connect */ + u8 device_id; +} __packed; + +/* + * init device per-device disconnect message + */ +struct rpmsg_kdrv_init_disconnect_message { + /* message header */ + struct rpmsg_kdrv_init_message_header header; + /* device ID to disconnect */ + u8 device_id; +} __packed; + +#endif diff -Naur --no-dereference a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-switch.h b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-switch.h --- a/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-switch.h 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-switch.h 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,664 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Grygorii Strashko + */ + +#ifndef DRIVERS_RPMSG_KDRV_SHARED_RPMSG_KDRV_TRANSPORT_SWITCH_H_ +#define DRIVERS_RPMSG_KDRV_SHARED_RPMSG_KDRV_TRANSPORT_SWITCH_H_ + +#include +#include "rpmsg-kdrv-transport-common.h" + +#define RPMSG_KDRV_TP_ETHSWITCH_VERSION_MAJOR (0) +#define RPMSG_KDRV_TP_ETHSWITCH_VERSION_MINOR (1) +#define RPMSG_KDRV_TP_ETHSWITCH_VERSION_REVISION (1) + +/** + * enum rpmsg_kdrv_ethswitch_message_type - Eth switch rpmsg protocol messages + */ +enum rpmsg_kdrv_ethswitch_message_type { + RPMSG_KDRV_TP_ETHSWITCH_ATTACH = 0x00, + RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT = 0x01, + RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX = 0x02, + RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX = 0x03, + RPMSG_KDRV_TP_ETHSWITCH_REGISTER_DEFAULTFLOW = 0x04, + RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC = 0x05, + RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC = 0x06, + RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC = 0x07, + RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_DEFAULTFLOW = 0x08, + RPMSG_KDRV_TP_ETHSWITCH_FREE_MAC = 0x09, + RPMSG_KDRV_TP_ETHSWITCH_FREE_TX = 0x0A, + RPMSG_KDRV_TP_ETHSWITCH_FREE_RX = 0x0B, + RPMSG_KDRV_TP_ETHSWITCH_DETACH = 0x0C, + RPMSG_KDRV_TP_ETHSWITCH_IOCTL = 0x0D, + RPMSG_KDRV_TP_ETHSWITCH_REGWR = 0x0E, + RPMSG_KDRV_TP_ETHSWITCH_REGRD = 0x0F, + RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER = 0x10, + RPMSG_KDRV_TP_ETHSWITCH_IPV6_MAC_REGISTER = 0x11, + RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER = 0x12, + RPMSG_KDRV_TP_ETHSWITCH_IPV6_MAC_UNREGISTER = 0x13, + RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST = 0x14, + RPMSG_KDRV_TP_ETHSWITCH_S2C_NOTIFY = 0x15, + RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY = 0x16, + RPMSG_KDRV_TP_ETHSWITCH_REGISTER_ETHTYPE = 0x17, + RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_ETHTYPE = 0x18, + RPMSG_KDRV_TP_ETHSWITCH_REGISTER_REMOTETIMER = 0x19, + RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_REMOTETIMER = 0x1A, + RPMSG_KDRV_TP_ETHSWITCH_SET_PROMISC_MODE = 0x1B, + RPMSG_KDRV_TP_ETHSWITCH_FILTER_ADD_MAC = 0x1C, + RPMSG_KDRV_TP_ETHSWITCH_FILTER_DEL_MAC = 0x1D, + RPMSG_KDRV_TP_ETHSWITCH_MAX = 0x1E, +}; + +/** + * Client to Eth switch notification events @RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY + */ +enum rpmsg_kdrv_ethswitch_c2s_notify_type { + RPMSG_KDRV_TP_ETHSWITCH_CLIENTNOTIFY_DUMPSTATS = 0x00, + RPMSG_KDRV_TP_ETHSWITCH_CLIENTNOTIFY_MAX, +}; + +/** + * Eth switch HW ID + */ +enum rpmsg_kdrv_ethswitch_cpsw_type { + RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_MCU_CPSW, + RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_MAIN_CPSW, + RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_MAX, +}; + +/** + * Response status codes returned by Eth switch FW in + * struct @rpmsg_kdrv_ethswitch_common_resp_info + */ +#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK (0) +#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EAGAIN (-1) +#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EFAIL (-2) +#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EACCESS (-3) + +/* Maximum length of message data */ +#define RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN (128) + +/* Number of priorities supported by CPSW */ +#define RPMSG_KDRV_TP_ETHSWITCH_PRIORITY_NUM (8) + +/* IPv4 Address length in octets */ +#define RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN (4) + +/** + * struct rpmsg_kdrv_ethswitch_msg_header - Message Header for outgoing messages + * + * @message_type: Type of messages: One of + * enum @rpmsg_kdrv_ethswitch_message_type values + */ +struct rpmsg_kdrv_ethswitch_msg_header { + u8 message_type; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_common_req_info - common request msgs data + * + * @id: unique handle + * @core_key: core specific key to indicate attached core + * + * Common structure used for all Eth switch FW request msgs except + * @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. It has to be filled with values returned + * by @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. + */ +struct rpmsg_kdrv_ethswitch_common_req_info { + u64 id; + u32 core_key; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_common_resp_info - common response data + * + * @status: status of request + * + * Common data returned by Eth switch FW in all response messages to identify + * status of request message processing. + */ +struct rpmsg_kdrv_ethswitch_common_resp_info { + s32 status; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_attach_req - attach cmd client request msg + * + * @header: msg header + * @cpsw_type: CPSW HW type enum @rpmsg_kdrv_ethswitch_cpsw_type + * + * Client attach message @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. it should be always + * sent first before other requests to Eth switch FW. + */ +struct rpmsg_kdrv_ethswitch_attach_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + u8 cpsw_type; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_attach_resp - attach client response msg + * + * @info: common response data. Status of the request processing + * @id: unique handle used by all further CMDs + * @core_key: core specific key to indicate attached core + * @rx_mtu: MTU of rx packets + * @tx_mtu: MTU of tx packet per priority + * @features: supported features mask + * @mac_only_port: 1-relative MAC port number for ports in MAC-only mode, 0 + * for switch ports. + * + * Attach client response msg received as response to client attach request + * @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. The @id and @core_key should be used to + * fill struct @rpmsg_kdrv_ethswitch_common_req_info in all further request + * messages. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_attach_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; + u64 id; + u32 core_key; + u32 rx_mtu; + u32 tx_mtu[RPMSG_KDRV_TP_ETHSWITCH_PRIORITY_NUM]; + u32 features; +#define RPMSG_KDRV_TP_ETHSWITCH_FEATURE_TXCSUM BIT(0) +#define RPMSG_KDRV_ETHSWITCH_FEATURE_MAC_ONLY BIT(2) +#define RPMSG_KDRV_ETHSWITCH_FEATURE_MC_FILTER BIT(3) + u32 mac_only_port; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_attach_extended_req - extended attach request msg + * + * @header: msg header + * @cpsw_type: CPSW HW type enum @rpmsg_kdrv_ethswitch_cpsw_type + * + * Client extended attach request @RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT. It can + * be used instead of @RPMSG_KDRV_TP_ETHSWITCH_ATTACH and has to sent first + * before other requests to Eth switch FW. + */ +struct rpmsg_kdrv_ethswitch_attach_extended_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + u8 cpsw_type; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_common_response_info - extended attach resp msg + * + * @info: common response data. Status of the request processing + * @id: unique handle used by all further CMDs + * @core_key: core specific key to indicate attached core + * @rx_mtu: MTU of rx packets + * @tx_mtu: MTU of tx packet per priority + * @features: supported features mask + * @alloc_flow_idx: RX UDMA flow ID + * @tx_cpsw_psil_dst_id: PSI-L dest thread id + * @mac_address: default eth MAC address assigned to this client + * @mac_only_port: 1-relative MAC port number for ports in MAC-only mode, 0 + * for switch ports. + * + * Extended attach response msg received as response to client extended attach + * request @RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT. The @id and @core_key should be + * used to fill struct @rpmsg_kdrv_ethswitch_common_req_info in all further + * request messages. In addition, it provides allocated DMA resources and + * MAC address. + * + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_attach_extended_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; + u64 id; + u32 core_key; + u32 rx_mtu; + u32 tx_mtu[RPMSG_KDRV_TP_ETHSWITCH_PRIORITY_NUM]; + u32 features; + u32 alloc_flow_idx; + u32 tx_cpsw_psil_dst_id; + u8 mac_address[ETH_ALEN]; + u32 mac_only_port; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_detach_req - detach client request msg + * + * @header: msg header + * @info: common request msgs data + * + * Client detach request message @RPMSG_KDRV_TP_ETHSWITCH_DETACH. + * it should be always sent as the last message to Eth switch FW. + */ +struct rpmsg_kdrv_ethswitch_detach_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_detach_resp - detach client response msg + * + * @info: common response data. Status of the request processing + * + * Client detach response msg received as response to client detach request + * @RPMSG_KDRV_TP_ETHSWITCH_DETACH. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_detach_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_alloc_req - alloc resources request msg + * + * @header: msg header + * @info: common request msgs data + * + * Client resources allocation request messages + * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX: get RX DMA resources + * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX: get TX DMA resources + * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC: get MAC address + */ +struct rpmsg_kdrv_ethswitch_alloc_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_alloc_rx_resp - alloc rx resources response msg + * + * @info: common response data. Status of the request processing + * @alloc_flow_idx: RX UDMA flow ID + * + * Client alloc rx resources response msg received as response to request + * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX. The @alloc_flow_idx is RX UDMA flow ID + * to be used for ingress packets reception. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_alloc_rx_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; + u32 alloc_flow_idx; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_alloc_tx_resp - alloc tx resources response msg + * + * @info: common response data. Status of the request processing + * @tx_cpsw_psil_dst_id: PSI-L dest thread id + * + * Client alloc tx resources response msg received as response to request + * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX. The @tx_cpsw_psil_dst_id is TX PSI-L dest + * thread ID to be used for TX UDMA channel setup. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_alloc_tx_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; + u32 tx_cpsw_psil_dst_id; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_alloc_mac_resp - alloc MAC resources response msg + * + * @info: common response data. Status of the request processing + * @mac_address: default eth MAC address assigned to this client + * + * Client alloc MAC resources response msg received as response to request + * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_alloc_mac_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; + u8 mac_address[ETH_ALEN]; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_register_mac_req - register MAC addr + * + * @header: msg header + * @info: common request msgs data + * @mac_address: eth MAC address used by client + * @flow_idx: RX UDMA flow ID + * + * Client register MAC addr message @RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC. + * it should be sent to Eth switch FW to configure HW network traffic + * classifiers so all network traffic directed to @mac_address will be + * redirected to this client and can be received through allocated RX UDMA flow + * @flow_idx. + * + * This message has to be sent by client when it's ready to receive network + * traffic. + */ +struct rpmsg_kdrv_ethswitch_register_mac_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 mac_address[ETH_ALEN]; + u32 flow_idx; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_register_mac_resp - register MAC addr response + * + * @info: common response data. Status of the request processing + * + * Client register MAC addr response msg received as response to + * request @RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_register_mac_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_unregister_mac_req - unregister MAC addr + * + * @header: msg header + * @info: common request msgs data + * @mac_address: eth MAC address used by client + * @flow_idx: RX UDMA flow ID + * + * Client unregister MAC addr message @RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC. + * it should be sent to Eth switch FW to disable HW network traffic + * classifiers so all network traffic directed to @mac_address will be dropped. + * + * This message has to be sent by client when it does not want to receive any + * more network traffic. + */ +struct rpmsg_kdrv_ethswitch_unregister_mac_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 mac_address[ETH_ALEN]; + u32 flow_idx; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_unregister_mac_resp - unregister MAC addr resp + * + * @info: common response data. Status of the request processing + * + * Client unregister MAC addr response msg received as response to + * request @RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_unregister_mac_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_ipv4_register_mac_req - register IPv4:MAC pair + * + * @header: msg header + * @info: common request msgs data + * @mac_address: eth MAC address used by client + * @ipv4_addr: IPv4 addr + * + * Client register IPv4:MAC addr pair message + * @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER registers pair of IPv4 @ipv4_addr + * and Eth MAC @mac_address addresses in Eth switch FW ARP database. + * + * This message has to be sent by client when there is new IPv4 addr assigned. + */ +struct rpmsg_kdrv_ethswitch_ipv4_register_mac_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 mac_address[ETH_ALEN]; + u8 ipv4_addr[RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN]; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_ipv4_register_mac_resp - register IPv4:MAC pair + * response + * + * @info: common response data. Status of the request processing + * + * Client register IPv4:MAC addr pair response msg received as response to + * request @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_ipv4_register_mac_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_req - unregister IPv4 addr + * + * @header: msg header + * @info: common request msgs data + * @ipv4_addr: IPv4 addr + * + * Client unregister IPv4 addr message + * @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER. It removes IPv4 @ipv4_addr + * address from Eth switch FW ARP database. + * + * This message has to be sent by client when there is IPv4 addr unassigned. + */ +struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 ipv4_addr[RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN]; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_resp - unregister IPv4 addr + * + * @info: common response data. Status of the request processing + * + * Client unregister IPv4 addr response msg received as response to + * request @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_ping_req - ping request + * + * @header: msg header + * @data: custom data + * + * Client ping request @RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST. The Eth switch FW + * should return the same @data in struct @rpmsg_kdrv_ethswitch_ping_resp. + * Can be used any time - no attach required. + */ +struct rpmsg_kdrv_ethswitch_ping_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + u8 data[RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN]; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_ping_resp - ping response + * + * @data: custom data + * + * The ping response msg received as response to request + * @RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST. The Eth switch FW should return + * the same @data as was provided in struct @rpmsg_kdrv_ethswitch_ping_req. + */ +struct rpmsg_kdrv_ethswitch_ping_resp { + u8 data[RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN]; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_regrd_req - read hw register request + * + * @header: msg header + * @regaddr: phys register address + * + * The read hw register request @RPMSG_KDRV_TP_ETHSWITCH_REGRD. + * The Eth switch FW should return the @regaddr register value. + * Can be used any time - no attach required. + */ +struct rpmsg_kdrv_ethswitch_regrd_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + u32 regaddr; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_regrd_resp - read hw register response + * + * @info: common response data. Status of the request processing + * @regval: register value + * + * The read hw register response received as response to request + * @RPMSG_KDRV_TP_ETHSWITCH_REGRD. The @regval is hw register value from + * @regaddr phys register address provided in + * struct @rpmsg_kdrv_ethswitch_regrd_req + * + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_regrd_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; + u32 regval; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_c2s_notify - notification request + * + * @header: msg header + * @info: common request msg data + * @notifyid: enum @rpmsg_kdrv_ethswitch_c2s_notify_type + * @notify_info_len: length of @notify_info + * @notify_info: notification message data + * + * The notification request message @RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY is one + * way message to Eth switch FW without response. + */ +struct rpmsg_kdrv_ethswitch_c2s_notify { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 notifyid; + u32 notify_info_len; + u8 notify_info[RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN]; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_set_promisc_mode_req - set promiscuous mode + * + * @header: msg header + * @info: common request msg data + * @enable: promiscuous mode (enable or disable) + * + * Client message @RPMSG_KDRV_TP_ETHSWITCH_SET_PROMISC_MODE is sent to change + * the promiscuous mode. + */ +struct rpmsg_kdrv_ethswitch_set_promisc_mode_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u32 enable; +} __packed; + +/** + * Set promiscuous mode response msg received as response to client's mode change + * request @RPMSG_KDRV_TP_ETHSWITCH_SET_PROMISC_MODE. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_set_promisc_mode_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_filter_add_mc_req - add multicast MAC address to filter + * + * @header: msg header + * @info: common request msg data + * @mac_address: Multicast address to be added + * @vlan_id: VLAN id + * @flow_idx: RX UDMA flow ID (used for multicast addresses marked as 'exclusive' in + * switch firmware) + * + * Client message @RPMSG_KDRV_TP_ETHSWITCH_FILTER_ADD_MAC is sent to add a multicast + * address to the receive filter. + */ +struct rpmsg_kdrv_ethswitch_filter_add_mc_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 mac_address[ETH_ALEN]; + u16 vlan_id; + u32 flow_idx; +} __packed; + +/** + * Response msg received as response to client's request to add a multicas address to + * receive filter via @RPMSG_KDRV_TP_ETHSWITCH_FILTER_ADD_MAC. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_filter_add_mc_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_filter_del_mac_req - delete multicast MAC address + * from filter + * + * @header: msg header + * @info: common request msg data + * @mac_address: Multicast address to be removed + * @vlan_id: VLAN id + * @flow_idx: RX UDMA flow ID (used for multicast addresses marked as 'exclusive' in + * switch firmware) + * + * Client message @RPMSG_KDRV_TP_ETHSWITCH_FILTER_DEL_MAC is sent to delete a multicast + * address from the receive filter. + */ +struct rpmsg_kdrv_ethswitch_filter_del_mc_req { + struct rpmsg_kdrv_ethswitch_msg_header header; + struct rpmsg_kdrv_ethswitch_common_req_info info; + u8 mac_address[ETH_ALEN]; + u16 vlan_id; + u32 flow_idx; +} __packed; + +/** + * Response msg received as response to client's request to delete a multicas address to + * receive filter via @RPMSG_KDRV_TP_ETHSWITCH_FILTER_DEL_MAC. + * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success. + */ +struct rpmsg_kdrv_ethswitch_filter_del_mc_resp { + struct rpmsg_kdrv_ethswitch_common_resp_info info; +} __packed; + +/** + * struct rpmsg_kdrv_ethswitch_fw_version_info - fw version info + * + * @major: major + * @minor: minor + * @rev: revision + * @year: + * @month: + * @date: build date + * @commit_hash: commit hash + */ +struct rpmsg_kdrv_ethswitch_fw_version_info { +#define RPMSG_KDRV_TP_ETHSWITCH_YEARLEN (4) +#define RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN (3) +#define RPMSG_KDRV_TP_ETHSWITCH_DATELEN (2) +#define RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN (8) + u32 major; + u32 minor; + u32 rev; + char year[RPMSG_KDRV_TP_ETHSWITCH_YEARLEN]; + char month[RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN]; + char date[RPMSG_KDRV_TP_ETHSWITCH_DATELEN]; + char commit_hash[RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN]; +} __packed; + +/* + * per-device data for ethswitch device + */ +/** + * struct rpmsg_kdrv_ethswitch_device_data - rpmsg_kdrv_device data + * + * @fw_ver: fw version info + * @permission_flags: permission enabled for each + * enum @rpmsg_kdrv_ethswitch_message_type command + * @uart_connected: flag indicating if UART is connected + * @uart_id: UART ID used by firmware for log prints + * + * Provided as part of RPMSG KDRV device discovery protocol + */ +struct rpmsg_kdrv_ethswitch_device_data { + struct rpmsg_kdrv_ethswitch_fw_version_info fw_ver; + u32 permission_flags; + u32 uart_connected; + u32 uart_id; +} __packed; + +#endif /* DRIVERS_RPMSG_KDRV_SHARED_RPMSG_KDRV_TRANSPORT_SWITCH_H_ */ diff -Naur --no-dereference a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c --- a/drivers/soc/qcom/wcnss_ctrl.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/qcom/wcnss_ctrl.c 2022-01-06 12:45:53.826318157 -0500 @@ -276,6 +276,7 @@ strscpy(chinfo.name, name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; + chinfo.desc[0] = '\0'; return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo); } diff -Naur --no-dereference a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c --- a/drivers/soc/ti/k3-ringacc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/k3-ringacc.c 2022-01-06 12:45:53.826318157 -0500 @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -21,6 +23,7 @@ static DEFINE_MUTEX(k3_ringacc_list_lock); #define K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK GENMASK(19, 0) +#define K3_DMARING_CFG_RING_SIZE_ELCNT_MASK GENMASK(15, 0) /** * struct k3_ring_rt_regs - The RA realtime Control/Status Registers region @@ -43,7 +46,13 @@ u32 hwindx; }; -#define K3_RINGACC_RT_REGS_STEP 0x1000 +#define K3_RINGACC_RT_REGS_STEP 0x1000 +#define K3_DMARING_RT_REGS_STEP 0x2000 +#define K3_DMARING_RT_REGS_REVERSE_OFS 0x1000 +#define K3_RINGACC_RT_OCC_MASK GENMASK(20, 0) +#define K3_DMARING_RT_OCC_TDOWN_COMPLETE BIT(31) +#define K3_DMARING_RT_DB_ENTRY_MASK GENMASK(7, 0) +#define K3_DMARING_RT_DB_TDOWN_ACK BIT(31) /** * struct k3_ring_fifo_regs - The Ring Accelerator Queues Registers region @@ -122,6 +131,7 @@ u32 occ; u32 windex; u32 rindex; + u32 tdown_complete:1; }; /** @@ -137,10 +147,13 @@ * @elm_size: Size of the ring element * @mode: Ring mode * @flags: flags + * @state: Ring state * @ring_id: Ring Id * @parent: Pointer on struct @k3_ringacc * @use_count: Use count for shared rings * @proxy_id: RA Ring Proxy Id (only if @K3_RINGACC_RING_USE_PROXY) + * @dma_dev: device to be used for DMA API (allocation, mapping) + * @asel: Address Space Select value for physical addresses */ struct k3_ring { struct k3_ring_rt_regs __iomem *rt; @@ -155,11 +168,15 @@ u32 flags; #define K3_RING_FLAG_BUSY BIT(1) #define K3_RING_FLAG_SHARED BIT(2) +#define K3_RING_FLAG_REVERSE BIT(3) struct k3_ring_state state; u32 ring_id; struct k3_ringacc *parent; u32 use_count; int proxy_id; + struct device *dma_dev; + u32 asel; +#define K3_ADDRESS_ASEL_SHIFT 48 }; struct k3_ringacc_ops { @@ -185,6 +202,7 @@ * @tisci_ring_ops: ti-sci rings ops * @tisci_dev_id: ti-sci device id * @ops: SoC specific ringacc operation + * @dma_rings: indicate DMA ring (dual ring within BCDMA/PKTDMA) */ struct k3_ringacc { struct device *dev; @@ -207,6 +225,7 @@ u32 tisci_dev_id; const struct k3_ringacc_ops *ops; + bool dma_rings; }; /** @@ -218,6 +237,21 @@ unsigned dma_ring_reset_quirk:1; }; +static int k3_ringacc_ring_read_occ(struct k3_ring *ring) +{ + return readl(&ring->rt->occ) & K3_RINGACC_RT_OCC_MASK; +} + +static void k3_ringacc_ring_update_occ(struct k3_ring *ring) +{ + u32 val; + + val = readl(&ring->rt->occ); + + ring->state.occ = val & K3_RINGACC_RT_OCC_MASK; + ring->state.tdown_complete = !!(val & K3_DMARING_RT_OCC_TDOWN_COMPLETE); +} + static long k3_ringacc_ring_get_fifo_pos(struct k3_ring *ring) { return K3_RINGACC_FIFO_WINDOW_SIZE_BYTES - @@ -231,12 +265,24 @@ static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem); static int k3_ringacc_ring_pop_mem(struct k3_ring *ring, void *elem); +static int k3_dmaring_fwd_pop(struct k3_ring *ring, void *elem); +static int k3_dmaring_reverse_pop(struct k3_ring *ring, void *elem); static struct k3_ring_ops k3_ring_mode_ring_ops = { .push_tail = k3_ringacc_ring_push_mem, .pop_head = k3_ringacc_ring_pop_mem, }; +static struct k3_ring_ops k3_dmaring_fwd_ops = { + .push_tail = k3_ringacc_ring_push_mem, + .pop_head = k3_dmaring_fwd_pop, +}; + +static struct k3_ring_ops k3_dmaring_reverse_ops = { + /* Reverse side of the DMA ring can only be popped by SW */ + .pop_head = k3_dmaring_reverse_pop, +}; + static int k3_ringacc_ring_push_io(struct k3_ring *ring, void *elem); static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem); static int k3_ringacc_ring_push_head_io(struct k3_ring *ring, void *elem); @@ -339,6 +385,40 @@ } EXPORT_SYMBOL_GPL(k3_ringacc_request_ring); +static int k3_dmaring_request_dual_ring(struct k3_ringacc *ringacc, int fwd_id, + struct k3_ring **fwd_ring, + struct k3_ring **compl_ring) +{ + int ret = 0; + + /* + * DMA rings must be requested by ID, completion ring is the reverse + * side of the forward ring + */ + if (fwd_id < 0) + return -EINVAL; + + mutex_lock(&ringacc->req_lock); + + if (test_bit(fwd_id, ringacc->rings_inuse)) { + ret = -EBUSY; + goto error; + } + + *fwd_ring = &ringacc->rings[fwd_id]; + *compl_ring = &ringacc->rings[fwd_id + ringacc->num_rings]; + set_bit(fwd_id, ringacc->rings_inuse); + ringacc->rings[fwd_id].use_count++; + dev_dbg(ringacc->dev, "Giving ring#%d\n", fwd_id); + + mutex_unlock(&ringacc->req_lock); + return 0; + +error: + mutex_unlock(&ringacc->req_lock); + return ret; +} + int k3_ringacc_request_rings_pair(struct k3_ringacc *ringacc, int fwd_id, int compl_id, struct k3_ring **fwd_ring, @@ -349,6 +429,10 @@ if (!fwd_ring || !compl_ring) return -EINVAL; + if (ringacc->dma_rings) + return k3_dmaring_request_dual_ring(ringacc, fwd_id, + fwd_ring, compl_ring); + *fwd_ring = k3_ringacc_request_ring(ringacc, fwd_id, 0); if (!(*fwd_ring)) return -ENODEV; @@ -365,20 +449,16 @@ static void k3_ringacc_ring_reset_sci(struct k3_ring *ring) { + struct ti_sci_msg_rm_ring_cfg ring_cfg = { 0 }; struct k3_ringacc *ringacc = ring->parent; int ret; - ret = ringacc->tisci_ring_ops->config( - ringacc->tisci, - TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID, - ringacc->tisci_dev_id, - ring->ring_id, - 0, - 0, - ring->size, - 0, - 0, - 0); + ring_cfg.nav_id = ringacc->tisci_dev_id; + ring_cfg.index = ring->ring_id; + ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID; + ring_cfg.count = ring->size; + + ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg); if (ret) dev_err(ringacc->dev, "TISCI reset ring fail (%d) ring_idx %d\n", ret, ring->ring_id); @@ -398,20 +478,16 @@ static void k3_ringacc_ring_reconfig_qmode_sci(struct k3_ring *ring, enum k3_ring_mode mode) { + struct ti_sci_msg_rm_ring_cfg ring_cfg = { 0 }; struct k3_ringacc *ringacc = ring->parent; int ret; - ret = ringacc->tisci_ring_ops->config( - ringacc->tisci, - TI_SCI_MSG_VALUE_RM_RING_MODE_VALID, - ringacc->tisci_dev_id, - ring->ring_id, - 0, - 0, - 0, - mode, - 0, - 0); + ring_cfg.nav_id = ringacc->tisci_dev_id; + ring_cfg.index = ring->ring_id; + ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_RING_MODE_VALID; + ring_cfg.mode = mode; + + ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg); if (ret) dev_err(ringacc->dev, "TISCI reconf qmode fail (%d) ring_idx %d\n", ret, ring->ring_id); @@ -426,7 +502,7 @@ goto reset; if (!occ) - occ = readl(&ring->rt->occ); + occ = k3_ringacc_ring_read_occ(ring); if (occ) { u32 db_ring_cnt, db_ring_cnt_cur; @@ -478,20 +554,15 @@ static void k3_ringacc_ring_free_sci(struct k3_ring *ring) { + struct ti_sci_msg_rm_ring_cfg ring_cfg = { 0 }; struct k3_ringacc *ringacc = ring->parent; int ret; - ret = ringacc->tisci_ring_ops->config( - ringacc->tisci, - TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER, - ringacc->tisci_dev_id, - ring->ring_id, - 0, - 0, - 0, - 0, - 0, - 0); + ring_cfg.nav_id = ringacc->tisci_dev_id; + ring_cfg.index = ring->ring_id; + ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER; + + ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg); if (ret) dev_err(ringacc->dev, "TISCI ring free fail (%d) ring_idx %d\n", ret, ring->ring_id); @@ -506,6 +577,13 @@ ringacc = ring->parent; + /* + * DMA rings: rings shared memory and configuration, only forward ring + * is configured and reverse ring considered as slave. + */ + if (ringacc->dma_rings && (ring->flags & K3_RING_FLAG_REVERSE)) + return 0; + dev_dbg(ring->parent->dev, "flags: 0x%08x\n", ring->flags); if (!test_bit(ring->ring_id, ringacc->rings_inuse)) @@ -521,11 +599,14 @@ k3_ringacc_ring_free_sci(ring); - dma_free_coherent(ringacc->dev, + dma_free_coherent(ring->dma_dev, ring->size * (4 << ring->elm_size), ring->ring_mem_virt, ring->ring_mem_dma); ring->flags = 0; ring->ops = NULL; + ring->dma_dev = NULL; + ring->asel = 0; + if (ring->proxy_id != K3_RINGACC_PROXY_NOT_USED) { clear_bit(ring->proxy_id, ringacc->proxy_inuse); ring->proxy = NULL; @@ -575,29 +656,112 @@ static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring) { + struct ti_sci_msg_rm_ring_cfg ring_cfg = { 0 }; struct k3_ringacc *ringacc = ring->parent; - u32 ring_idx; int ret; if (!ringacc->tisci) return -EINVAL; - ring_idx = ring->ring_id; - ret = ringacc->tisci_ring_ops->config( - ringacc->tisci, - TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER, - ringacc->tisci_dev_id, - ring_idx, - lower_32_bits(ring->ring_mem_dma), - upper_32_bits(ring->ring_mem_dma), - ring->size, - ring->mode, - ring->elm_size, - 0); + ring_cfg.nav_id = ringacc->tisci_dev_id; + ring_cfg.index = ring->ring_id; + ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER; + ring_cfg.addr_lo = lower_32_bits(ring->ring_mem_dma); + ring_cfg.addr_hi = upper_32_bits(ring->ring_mem_dma); + ring_cfg.count = ring->size; + ring_cfg.mode = ring->mode; + ring_cfg.size = ring->elm_size; + ring_cfg.asel = ring->asel; + + ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg); if (ret) dev_err(ringacc->dev, "TISCI config ring fail (%d) ring_idx %d\n", - ret, ring_idx); + ret, ring->ring_id); + + return ret; +} + +static int k3_dmaring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg) +{ + struct k3_ringacc *ringacc; + struct k3_ring *reverse_ring; + int ret = 0; + + if (cfg->elm_size != K3_RINGACC_RING_ELSIZE_8 || + cfg->mode != K3_RINGACC_RING_MODE_RING || + cfg->size & ~K3_DMARING_CFG_RING_SIZE_ELCNT_MASK) + return -EINVAL; + + ringacc = ring->parent; + + /* + * DMA rings: rings shared memory and configuration, only forward ring + * is configured and reverse ring considered as slave. + */ + if (ringacc->dma_rings && (ring->flags & K3_RING_FLAG_REVERSE)) + return 0; + + if (!test_bit(ring->ring_id, ringacc->rings_inuse)) + return -EINVAL; + + ring->size = cfg->size; + ring->elm_size = cfg->elm_size; + ring->mode = cfg->mode; + ring->asel = cfg->asel; + ring->dma_dev = cfg->dma_dev; + if (!ring->dma_dev) { + dev_warn(ringacc->dev, "dma_dev is not provided for ring%d\n", + ring->ring_id); + ring->dma_dev = ringacc->dev; + } + + memset(&ring->state, 0, sizeof(ring->state)); + + ring->ops = &k3_dmaring_fwd_ops; + + ring->ring_mem_virt = dma_alloc_coherent(ring->dma_dev, + ring->size * (4 << ring->elm_size), + &ring->ring_mem_dma, GFP_KERNEL); + if (!ring->ring_mem_virt) { + dev_err(ringacc->dev, "Failed to alloc ring mem\n"); + ret = -ENOMEM; + goto err_free_ops; + } + + ret = k3_ringacc_ring_cfg_sci(ring); + if (ret) + goto err_free_mem; + + ring->flags |= K3_RING_FLAG_BUSY; + + k3_ringacc_ring_dump(ring); + + /* DMA rings: configure reverse ring */ + reverse_ring = &ringacc->rings[ring->ring_id + ringacc->num_rings]; + reverse_ring->size = cfg->size; + reverse_ring->elm_size = cfg->elm_size; + reverse_ring->mode = cfg->mode; + reverse_ring->asel = cfg->asel; + memset(&reverse_ring->state, 0, sizeof(reverse_ring->state)); + reverse_ring->ops = &k3_dmaring_reverse_ops; + + reverse_ring->ring_mem_virt = ring->ring_mem_virt; + reverse_ring->ring_mem_dma = ring->ring_mem_dma; + reverse_ring->flags |= K3_RING_FLAG_BUSY; + k3_ringacc_ring_dump(reverse_ring); + + return 0; +err_free_mem: + dma_free_coherent(ring->dma_dev, + ring->size * (4 << ring->elm_size), + ring->ring_mem_virt, + ring->ring_mem_dma); +err_free_ops: + ring->ops = NULL; + ring->proxy = NULL; + ring->dma_dev = NULL; + ring->asel = 0; return ret; } @@ -608,8 +772,12 @@ if (!ring || !cfg) return -EINVAL; + ringacc = ring->parent; + if (ringacc->dma_rings) + return k3_dmaring_cfg(ring, cfg); + if (cfg->elm_size > K3_RINGACC_RING_ELSIZE_256 || cfg->mode >= K3_RINGACC_RING_MODE_INVALID || cfg->size & ~K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK || @@ -648,8 +816,12 @@ switch (ring->mode) { case K3_RINGACC_RING_MODE_RING: ring->ops = &k3_ring_mode_ring_ops; + ring->dma_dev = cfg->dma_dev; + if (!ring->dma_dev) + ring->dma_dev = ringacc->dev; break; case K3_RINGACC_RING_MODE_MESSAGE: + ring->dma_dev = ringacc->dev; if (ring->proxy) ring->ops = &k3_ring_mode_proxy_ops; else @@ -661,9 +833,9 @@ goto err_free_proxy; } - ring->ring_mem_virt = dma_alloc_coherent(ringacc->dev, - ring->size * (4 << ring->elm_size), - &ring->ring_mem_dma, GFP_KERNEL); + ring->ring_mem_virt = dma_alloc_coherent(ring->dma_dev, + ring->size * (4 << ring->elm_size), + &ring->ring_mem_dma, GFP_KERNEL); if (!ring->ring_mem_virt) { dev_err(ringacc->dev, "Failed to alloc ring mem\n"); ret = -ENOMEM; @@ -684,12 +856,13 @@ return 0; err_free_mem: - dma_free_coherent(ringacc->dev, + dma_free_coherent(ring->dma_dev, ring->size * (4 << ring->elm_size), ring->ring_mem_virt, ring->ring_mem_dma); err_free_ops: ring->ops = NULL; + ring->dma_dev = NULL; err_free_proxy: ring->proxy = NULL; return ret; @@ -711,7 +884,7 @@ return -EINVAL; if (!ring->state.free) - ring->state.free = ring->size - readl(&ring->rt->occ); + ring->state.free = ring->size - k3_ringacc_ring_read_occ(ring); return ring->state.free; } @@ -722,7 +895,7 @@ if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) return -EINVAL; - return readl(&ring->rt->occ); + return k3_ringacc_ring_read_occ(ring); } EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_occ); @@ -898,6 +1071,72 @@ K3_RINGACC_ACCESS_MODE_POP_HEAD); } +/* + * The element is 48 bits of address + ASEL bits in the ring. + * ASEL is used by the DMAs and should be removed for the kernel as it is not + * part of the physical memory address. + */ +static void k3_dmaring_remove_asel_from_elem(u64 *elem) +{ + *elem &= GENMASK_ULL(K3_ADDRESS_ASEL_SHIFT - 1, 0); +} + +static int k3_dmaring_fwd_pop(struct k3_ring *ring, void *elem) +{ + void *elem_ptr; + u32 elem_idx; + + /* + * DMA rings: forward ring is always tied DMA channel and HW does not + * maintain any state data required for POP operation and its unknown + * how much elements were consumed by HW. So, to actually + * do POP, the read pointer has to be recalculated every time. + */ + ring->state.occ = k3_ringacc_ring_read_occ(ring); + if (ring->state.windex >= ring->state.occ) + elem_idx = ring->state.windex - ring->state.occ; + else + elem_idx = ring->size - (ring->state.occ - ring->state.windex); + + elem_ptr = k3_ringacc_get_elm_addr(ring, elem_idx); + memcpy(elem, elem_ptr, (4 << ring->elm_size)); + k3_dmaring_remove_asel_from_elem(elem); + + ring->state.occ--; + writel(-1, &ring->rt->db); + + dev_dbg(ring->parent->dev, "%s: occ%d Windex%d Rindex%d pos_ptr%px\n", + __func__, ring->state.occ, ring->state.windex, elem_idx, + elem_ptr); + return 0; +} + +static int k3_dmaring_reverse_pop(struct k3_ring *ring, void *elem) +{ + void *elem_ptr; + + elem_ptr = k3_ringacc_get_elm_addr(ring, ring->state.rindex); + + if (ring->state.occ) { + memcpy(elem, elem_ptr, (4 << ring->elm_size)); + k3_dmaring_remove_asel_from_elem(elem); + + ring->state.rindex = (ring->state.rindex + 1) % ring->size; + ring->state.occ--; + writel(-1 & K3_DMARING_RT_DB_ENTRY_MASK, &ring->rt->db); + } else if (ring->state.tdown_complete) { + dma_addr_t *value = elem; + + *value = CPPI5_TDCM_MARKER; + writel(K3_DMARING_RT_DB_TDOWN_ACK, &ring->rt->db); + ring->state.tdown_complete = false; + } + + dev_dbg(ring->parent->dev, "%s: occ%d index%d pos_ptr%px\n", + __func__, ring->state.occ, ring->state.rindex, elem_ptr); + return 0; +} + static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem) { void *elem_ptr; @@ -905,6 +1144,11 @@ elem_ptr = k3_ringacc_get_elm_addr(ring, ring->state.windex); memcpy(elem_ptr, elem, (4 << ring->elm_size)); + if (ring->parent->dma_rings) { + u64 *addr = elem_ptr; + + *addr |= ((u64)ring->asel << K3_ADDRESS_ASEL_SHIFT); + } ring->state.windex = (ring->state.windex + 1) % ring->size; ring->state.free--; @@ -981,12 +1225,12 @@ return -EINVAL; if (!ring->state.occ) - ring->state.occ = k3_ringacc_ring_get_occ(ring); + k3_ringacc_ring_update_occ(ring); dev_dbg(ring->parent->dev, "ring_pop: occ%d index%d\n", ring->state.occ, ring->state.rindex); - if (!ring->state.occ) + if (!ring->state.occ && !ring->state.tdown_complete) return -ENODATA; if (ring->ops && ring->ops->pop_head) @@ -1004,7 +1248,7 @@ return -EINVAL; if (!ring->state.occ) - ring->state.occ = k3_ringacc_ring_get_occ(ring); + k3_ringacc_ring_update_occ(ring); dev_dbg(ring->parent->dev, "ring_pop_tail: occ%d index%d\n", ring->state.occ, ring->state.rindex); @@ -1209,18 +1453,78 @@ {}, }; +struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev, + struct k3_ringacc_init_data *data) +{ + struct device *dev = &pdev->dev; + struct k3_ringacc *ringacc; + void __iomem *base_rt; + struct resource *res; + int i; + + ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL); + if (!ringacc) + return ERR_PTR(-ENOMEM); + + ringacc->dev = dev; + ringacc->dma_rings = true; + ringacc->num_rings = data->num_rings; + ringacc->tisci = data->tisci; + ringacc->tisci_dev_id = data->tisci_dev_id; + + mutex_init(&ringacc->req_lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ringrt"); + base_rt = devm_ioremap_resource(dev, res); + if (IS_ERR(base_rt)) + return ERR_CAST(base_rt); + + ringacc->rings = devm_kzalloc(dev, + sizeof(*ringacc->rings) * + ringacc->num_rings * 2, + GFP_KERNEL); + ringacc->rings_inuse = devm_kcalloc(dev, + BITS_TO_LONGS(ringacc->num_rings), + sizeof(unsigned long), GFP_KERNEL); + + if (!ringacc->rings || !ringacc->rings_inuse) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < ringacc->num_rings; i++) { + struct k3_ring *ring = &ringacc->rings[i]; + + ring->rt = base_rt + K3_DMARING_RT_REGS_STEP * i; + ring->parent = ringacc; + ring->ring_id = i; + ring->proxy_id = K3_RINGACC_PROXY_NOT_USED; + + ring = &ringacc->rings[ringacc->num_rings + i]; + ring->rt = base_rt + K3_DMARING_RT_REGS_STEP * i + + K3_DMARING_RT_REGS_REVERSE_OFS; + ring->parent = ringacc; + ring->ring_id = i; + ring->proxy_id = K3_RINGACC_PROXY_NOT_USED; + ring->flags = K3_RING_FLAG_REVERSE; + } + + ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops; + + dev_info(dev, "Number of rings: %u\n", ringacc->num_rings); + + return ringacc; +} +EXPORT_SYMBOL_GPL(k3_ringacc_dmarings_init); + static int k3_ringacc_probe(struct platform_device *pdev) { const struct ringacc_match_data *match_data; - const struct of_device_id *match; struct device *dev = &pdev->dev; struct k3_ringacc *ringacc; int ret; - match = of_match_node(k3_ringacc_of_match, dev->of_node); - if (!match) + match_data = of_device_get_match_data(&pdev->dev); + if (!match_data) return -ENODEV; - match_data = match->data; ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL); if (!ringacc) diff -Naur --no-dereference a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c --- a/drivers/soc/ti/k3-socinfo.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/k3-socinfo.c 2022-01-06 12:45:53.826318157 -0500 @@ -40,6 +40,8 @@ { 0xBB5A, "AM65X" }, { 0xBB64, "J721E" }, { 0xBB6D, "J7200" }, + { 0xBB38, "AM64X" }, + { 0xBB75, "J721S2"}, }; static int diff -Naur --no-dereference a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig --- a/drivers/soc/ti/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/Kconfig 2022-01-06 12:45:53.826318157 -0500 @@ -1,22 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -# 64-bit ARM SoCs from TI -if ARM64 - -if ARCH_K3 - -config ARCH_K3_AM6_SOC - bool "K3 AM6 SoC" - help - Enable support for TI's AM6 SoC Family support - -config ARCH_K3_J721E_SOC - bool "K3 J721E SoC" - help - Enable support for TI's J721E SoC Family support - -endif - -endif # # TI SOC drivers @@ -26,6 +8,17 @@ if SOC_TI +config KEYSTONE_DSP_MEM + tristate "TI Keystone DSP Memory Mapping Driver" + depends on ARCH_KEYSTONE + help + Userspace memory mapping interface driver for TI Keystone SoCs. + Provides access to MSM SRAM memory regions and dedicated DDR + carveout memory regions to user space to aid userspace loading + of the DSPs within the SoC. + + If unsure, say N. + config KEYSTONE_NAVIGATOR_QMSS tristate "Keystone Queue Manager Sub System" depends on ARCH_KEYSTONE diff -Naur --no-dereference a/drivers/soc/ti/keystone_dsp_mem.c b/drivers/soc/ti/keystone_dsp_mem.c --- a/drivers/soc/ti/keystone_dsp_mem.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/soc/ti/keystone_dsp_mem.c 2022-01-06 12:45:53.826318157 -0500 @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Keystone DSP Memory Mapping Driver + * + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEYSTONE_ALIAS_PHYS_START 0x80000000ULL +#define KEYSTONE_ALIAS_PHYS_SIZE 0x80000000ULL /* 2G */ + +#define KEYSTONE_HIGH_PHYS_START 0x800000000ULL +#define KEYSTONE_HIGH_PHYS_LIMIT (KEYSTONE_HIGH_PHYS_START + \ + KEYSTONE_ALIAS_PHYS_SIZE) + +#define to_alias_addr(addr) (((addr) - KEYSTONE_HIGH_PHYS_START) + \ + KEYSTONE_ALIAS_PHYS_START) + +/** + * struct keystone_dsp_mem - internal memory structure + * @addr: physical address on the bus to access the memory region + * @size: size of the memory region + * @kobj: kobject for the sysfs directory file + */ +struct keystone_dsp_mem { + phys_addr_t addr; + resource_size_t size; + struct kobject kobj; +}; + +#define to_dsp_mem(obj) container_of(obj, struct keystone_dsp_mem, kobj) + +/** + * struct keystone_dsp_mem_info - Keystone DSP Memory device structure + * @misc: child miscdevice structure + * @mem: memory region array pointer + * @num_maps: number of memory regions + */ +struct keystone_dsp_mem_info { + struct miscdevice misc; + struct keystone_dsp_mem *mem; + int num_maps; +}; + +static struct keystone_dsp_mem_info *dsp_mem; + +#define to_dsp_mem_info(m) container_of(m, struct keystone_dsp_mem_info, misc) + +static ssize_t mem_addr_show(struct keystone_dsp_mem *mem, char *buf) +{ + return sprintf(buf, "%pa\n", &mem->addr); +} + +static ssize_t mem_size_show(struct keystone_dsp_mem *mem, char *buf) +{ + return sprintf(buf, "%pa\n", &mem->size); +} + +struct mem_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct keystone_dsp_mem *mem, char *buf); + ssize_t (*store)(struct keystone_dsp_mem *mem, const char *buf, + size_t len); +}; + +static struct mem_sysfs_entry addr_attribute = + __ATTR(addr, 0444, mem_addr_show, NULL); +static struct mem_sysfs_entry size_attribute = + __ATTR(size, 0444, mem_size_show, NULL); + +static struct attribute *attrs[] = { + &addr_attribute.attr, + &size_attribute.attr, + NULL, /* sentinel */ +}; + +static ssize_t mem_type_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct keystone_dsp_mem *mem = to_dsp_mem(kobj); + struct mem_sysfs_entry *entry; + + entry = container_of(attr, struct mem_sysfs_entry, attr); + if (!entry->show) + return -EIO; + + return entry->show(mem, buf); +} + +static const struct sysfs_ops mem_sysfs_ops = { + .show = mem_type_show, +}; + +static struct kobj_type mem_attr_type = { + .sysfs_ops = &mem_sysfs_ops, + .default_attrs = attrs, +}; + +static int keystone_dsp_mem_add_attrs(struct keystone_dsp_mem_info *dsp_mem) +{ + int i, ret; + struct keystone_dsp_mem *mem; + struct kobject *kobj_parent = &dsp_mem->misc.this_device->kobj; + + for (i = 0; i < dsp_mem->num_maps; i++) { + mem = &dsp_mem->mem[i]; + kobject_init(&mem->kobj, &mem_attr_type); + ret = kobject_add(&mem->kobj, kobj_parent, "memory%d", i); + if (ret) + goto err_kobj; + ret = kobject_uevent(&mem->kobj, KOBJ_ADD); + if (ret) + goto err_kobj; + } + + return 0; + +err_kobj: + for (; i >= 0; i--) { + mem = &dsp_mem->mem[i]; + kobject_put(&mem->kobj); + } + return ret; +} + +static void keystone_dsp_mem_del_attrs(struct keystone_dsp_mem_info *dsp_mem) +{ + int i; + struct keystone_dsp_mem *mem; + + for (i = 0; i < dsp_mem->num_maps; i++) { + mem = &dsp_mem->mem[i]; + kobject_put(&mem->kobj); + } +} + +static int keystone_dsp_mem_check_addr(struct keystone_dsp_mem_info *dsp_mem, + int mask, size_t size) +{ + size_t req_offset; + u32 index; + + index = mask & KEYSTONE_DSP_MEM_MAP_INDEX_MASK; + if (index >= dsp_mem->num_maps) { + pr_err("%s: invalid mmap region index %d\n", __func__, index); + return -EINVAL; + } + + req_offset = (mask - index) << PAGE_SHIFT; + if (req_offset + size < req_offset) { + pr_err("%s: invalid request - overflow, mmap offset = 0x%zx size 0x%zx region %d\n", + __func__, req_offset, size, index); + return -EINVAL; + } + + if ((req_offset + size) > dsp_mem->mem[index].size) { + pr_err("%s: invalid request - out of range, mmap offset 0x%zx size 0x%zx region %d\n", + __func__, req_offset, size, index); + return -EINVAL; + } + + return index; +} + +/* + * This is a custom mmap function following semantics based on the UIO + * mmap implementation. The vm_pgoff passed in the vma structure is a + * combination of the memory region index and the actual page offset in + * that region. This checks if user request is in valid range before + * providing mmap access. + * + * XXX: Evaluate this approach, as the MSMC memory can be mapped in whole + * into userspace as it is not super-large, and the allowable kernel + * unmapped DDR memory can be mmaped using traditional mmap semantics. + */ +static int keystone_dsp_mem_mmap(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + struct miscdevice *misc = file->private_data; + struct keystone_dsp_mem_info *dsp_mem = to_dsp_mem_info(misc); + int index; + + index = keystone_dsp_mem_check_addr(dsp_mem, vma->vm_pgoff, size); + if (index < 0) + return index; + + vma->vm_page_prot = + phys_mem_access_prot(file, + (dsp_mem->mem[index].addr >> PAGE_SHIFT) + + (vma->vm_pgoff - index), size, + vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + (dsp_mem->mem[index].addr >> PAGE_SHIFT) + + (vma->vm_pgoff - index), size, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static const struct file_operations keystone_dsp_mem_fops = { + .owner = THIS_MODULE, + .mmap = keystone_dsp_mem_mmap, +}; + +static int keystone_dsp_mem_parse(struct device_node *np, int index) +{ + phys_addr_t start, end, addr, size; + struct resource res; + resource_size_t rsize; + int ret, j; + + if (!of_find_property(np, "no-map", NULL)) { + pr_err("dsp reserved memory regions without no-map are not supported\n"); + return -EINVAL; + } + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + + /* make sure only aliased addresses are covered */ + rsize = resource_size(&res); + start = res.start; + end = res.start + rsize; + if (start < KEYSTONE_HIGH_PHYS_START || + start >= KEYSTONE_HIGH_PHYS_LIMIT || + end > KEYSTONE_HIGH_PHYS_LIMIT) { + pr_err("invalid address/size for keystone dsp memory carveout: %pa of size %pa\n", + &start, &rsize); + return -EINVAL; + } + + /* check for overlaps */ + start = to_alias_addr(start); + end = to_alias_addr(end); + for (j = 0; j < index; j++) { + addr = dsp_mem->mem[j].addr; + size = dsp_mem->mem[j].size; + if ((end > addr && end <= addr + size) || + (start >= addr && start < addr + size) || + (start < addr && end > addr + size)) { + pr_err("dsp memory carveout (%pa of size %pa) overlaps with (%pa of size %pa)\n", + &start, &rsize, &addr, &size); + return -EINVAL; + } + } + + dsp_mem->mem[index].addr = to_alias_addr(res.start); + dsp_mem->mem[index].size = resource_size(&res); + + return 0; +} + +static int keystone_dsp_mem_init(void) +{ + struct miscdevice *misc; + struct resource res; + struct device_node *rmem_np, *sram_np, *np; + int ret, i = 0; + int num_maps = 0, num_sram = 0; + + if (!of_have_populated_dt()) + return -EOPNOTSUPP; + + /* module is supported only on TI Keystone SoCs */ + if (!of_machine_is_compatible("ti,keystone")) + return -EOPNOTSUPP; + + /* count the number of DDR regions */ + rmem_np = of_find_node_by_path("/reserved-memory"); + if (rmem_np) { + for_each_available_child_of_node(rmem_np, np) { + if (of_device_is_compatible(np, + "ti,keystone-dsp-mem-pool")) + num_maps++; + } + } + + for_each_compatible_node(sram_np, NULL, "ti,keystone-dsp-msm-ram") { + if (!of_device_is_available(sram_np)) + continue; + num_sram++; + } + + if ((!num_maps && !num_sram) || + (num_maps + num_sram > KEYSTONE_DSP_MEM_MAP_INDEX_MASK)) { + ret = -EINVAL; + goto put_rmem; + } + + dsp_mem = kzalloc(sizeof(*dsp_mem), GFP_KERNEL); + if (!dsp_mem) { + ret = -ENOMEM; + goto put_rmem; + } + + dsp_mem->mem = kcalloc(num_maps + num_sram, sizeof(*dsp_mem->mem), + GFP_KERNEL); + if (!dsp_mem->mem) { + ret = -ENOMEM; + goto free_dsp; + } + + /* handle reserved-memory carveouts */ + if (num_maps) { + for_each_available_child_of_node(rmem_np, np) { + if (!of_device_is_compatible(np, "ti,keystone-dsp-mem-pool")) + continue; + + ret = keystone_dsp_mem_parse(np, i); + if (ret) { + of_node_put(np); + goto free_mem; + } + i++; + dsp_mem->num_maps++; + } + } + + /* handle on-chip SRAM reserved regions */ + if (num_sram) { + for_each_compatible_node(sram_np, NULL, + "ti,keystone-dsp-msm-ram") { + if (!of_device_is_available(sram_np)) + continue; + + ret = of_address_to_resource(sram_np, 0, &res); + if (ret) { + ret = -EINVAL; + of_node_put(sram_np); + goto free_mem; + } + dsp_mem->mem[i].addr = res.start; + dsp_mem->mem[i].size = resource_size(&res); + i++; + dsp_mem->num_maps++; + } + } + + misc = &dsp_mem->misc; + misc->minor = MISC_DYNAMIC_MINOR; + misc->name = "dspmem"; + misc->fops = &keystone_dsp_mem_fops; + misc->parent = NULL; + ret = misc_register(misc); + if (ret) { + pr_err("%s: could not register dspmem misc device\n", __func__); + goto free_mem; + } + + ret = keystone_dsp_mem_add_attrs(dsp_mem); + if (ret) { + pr_err("%s: error creating sysfs files (%d)\n", __func__, ret); + goto unregister_misc; + } + of_node_put(rmem_np); + + pr_info("registered dspmem misc device\n"); + + return 0; + +unregister_misc: + misc_deregister(&dsp_mem->misc); +free_mem: + kfree(dsp_mem->mem); +free_dsp: + kfree(dsp_mem); + dsp_mem = NULL; +put_rmem: + of_node_put(rmem_np); + return ret; +} + +static void keystone_dsp_mem_exit(void) +{ + keystone_dsp_mem_del_attrs(dsp_mem); + + misc_deregister(&dsp_mem->misc); + + kfree(dsp_mem->mem); + kfree(dsp_mem); + dsp_mem = NULL; +} + +module_init(keystone_dsp_mem_init); +module_exit(keystone_dsp_mem_exit); + +MODULE_AUTHOR("Suman Anna "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI Keystone DSP Memory Mapping Driver"); diff -Naur --no-dereference a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c --- a/drivers/soc/ti/knav_dma.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/knav_dma.c 2022-01-06 12:45:53.826318157 -0500 @@ -500,7 +500,7 @@ /** * knav_dma_close_channel() - Destroy a dma channel * - * channel: dma channel handle + * @channel: dma channel handle * */ void knav_dma_close_channel(void *channel) @@ -758,6 +758,7 @@ for_each_child_of_node(node, child) { ret = dma_init(node, child); if (ret) { + of_node_put(child); dev_err(&pdev->dev, "init failed with %d\n", ret); break; } diff -Naur --no-dereference a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c --- a/drivers/soc/ti/knav_qmss_queue.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/knav_qmss_queue.c 2022-01-06 12:45:53.826318157 -0500 @@ -79,7 +79,7 @@ /** * knav_queue_notify: qmss queue notfier call * - * @inst: qmss queue instance like accumulator + * @inst: - qmss queue instance like accumulator */ void knav_queue_notify(struct knav_queue_inst *inst) { @@ -511,10 +511,10 @@ /** * knav_queue_open() - open a hardware queue - * @name - name to give the queue handle - * @id - desired queue number if any or specifes the type + * @name: - name to give the queue handle + * @id: - desired queue number if any or specifes the type * of queue - * @flags - the following flags are applicable to queues: + * @flags: - the following flags are applicable to queues: * KNAV_QUEUE_SHARED - allow the queue to be shared. Queues are * exclusive by default. * Subsequent attempts to open a shared queue should @@ -545,7 +545,7 @@ /** * knav_queue_close() - close a hardware queue handle - * @qh - handle to close + * @qhandle: - handle to close */ void knav_queue_close(void *qhandle) { @@ -572,9 +572,9 @@ /** * knav_queue_device_control() - Perform control operations on a queue - * @qh - queue handle - * @cmd - control commands - * @arg - command argument + * @qhandle: - queue handle + * @cmd: - control commands + * @arg: - command argument * * Returns 0 on success, errno otherwise. */ @@ -623,10 +623,10 @@ /** * knav_queue_push() - push data (or descriptor) to the tail of a queue - * @qh - hardware queue handle - * @data - data to push - * @size - size of data to push - * @flags - can be used to pass additional information + * @qhandle: - hardware queue handle + * @dma: - DMA data to push + * @size: - size of data to push + * @flags: - can be used to pass additional information * * Returns 0 on success, errno otherwise. */ @@ -646,8 +646,8 @@ /** * knav_queue_pop() - pop data (or descriptor) from the head of a queue - * @qh - hardware queue handle - * @size - (optional) size of the data pop'ed. + * @qhandle: - hardware queue handle + * @size: - (optional) size of the data pop'ed. * * Returns a DMA address on success, 0 on failure. */ @@ -746,9 +746,9 @@ /** * knav_pool_create() - Create a pool of descriptors - * @name - name to give the pool handle - * @num_desc - numbers of descriptors in the pool - * @region_id - QMSS region id from which the descriptors are to be + * @name: - name to give the pool handle + * @num_desc: - numbers of descriptors in the pool + * @region_id: - QMSS region id from which the descriptors are to be * allocated. * * Returns a pool handle on success. @@ -856,7 +856,7 @@ /** * knav_pool_destroy() - Free a pool of descriptors - * @pool - pool handle + * @ph: - pool handle */ void knav_pool_destroy(void *ph) { @@ -884,7 +884,7 @@ /** * knav_pool_desc_get() - Get a descriptor from the pool - * @pool - pool handle + * @ph: - pool handle * * Returns descriptor from the pool. */ @@ -905,7 +905,8 @@ /** * knav_pool_desc_put() - return a descriptor to the pool - * @pool - pool handle + * @ph: - pool handle + * @desc: - virtual address */ void knav_pool_desc_put(void *ph, void *desc) { @@ -918,11 +919,11 @@ /** * knav_pool_desc_map() - Map descriptor for DMA transfer - * @pool - pool handle - * @desc - address of descriptor to map - * @size - size of descriptor to map - * @dma - DMA address return pointer - * @dma_sz - adjusted return pointer + * @ph: - pool handle + * @desc: - address of descriptor to map + * @size: - size of descriptor to map + * @dma: - DMA address return pointer + * @dma_sz: - adjusted return pointer * * Returns 0 on success, errno otherwise. */ @@ -945,9 +946,9 @@ /** * knav_pool_desc_unmap() - Unmap descriptor after DMA transfer - * @pool - pool handle - * @dma - DMA address of descriptor to unmap - * @dma_sz - size of descriptor to unmap + * @ph: - pool handle + * @dma: - DMA address of descriptor to unmap + * @dma_sz: - size of descriptor to unmap * * Returns descriptor address on success, Use IS_ERR_OR_NULL() to identify * error values on return. @@ -968,7 +969,7 @@ /** * knav_pool_count() - Get the number of descriptors in pool. - * @pool - pool handle + * @ph: - pool handle * Returns number of elements in the pool. */ int knav_pool_count(void *ph) @@ -1086,6 +1087,7 @@ for_each_child_of_node(regions, child) { region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); if (!region) { + of_node_put(child); dev_err(dev, "out of memory allocating region\n"); return -ENOMEM; } @@ -1307,12 +1309,11 @@ struct device_node *queue_pools) { struct device_node *type, *range; - int ret; for_each_child_of_node(queue_pools, type) { for_each_child_of_node(type, range) { - ret = knav_setup_queue_range(kdev, range); /* return value ignored, we init the rest... */ + knav_setup_queue_range(kdev, range); } } @@ -1399,6 +1400,7 @@ for_each_child_of_node(qmgrs, child) { qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL); if (!qmgr) { + of_node_put(child); dev_err(dev, "out of memory allocating qmgr\n"); return -ENOMEM; } @@ -1498,6 +1500,7 @@ for_each_child_of_node(pdsps, child) { pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL); if (!pdsp) { + of_node_put(child); dev_err(dev, "out of memory allocating pdsp\n"); return -ENOMEM; } diff -Naur --no-dereference a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile --- a/drivers/soc/ti/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/Makefile 2022-01-06 12:45:53.826318157 -0500 @@ -2,6 +2,7 @@ # # TI Keystone SOC drivers # +obj-$(CONFIG_KEYSTONE_DSP_MEM) += keystone_dsp_mem.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o diff -Naur --no-dereference a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c --- a/drivers/soc/ti/pm33xx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/pm33xx.c 2022-01-06 12:45:53.826318157 -0500 @@ -135,13 +135,11 @@ static int am33xx_do_sram_idle(u32 wfi_flags) { - int ret = 0; - if (!m3_ipc || !pm_ops) return 0; if (wfi_flags & WFI_FLAG_WAKE_M3) - ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_IDLE); + m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_IDLE); return pm_ops->cpu_suspend(am33xx_do_wfi_sram, wfi_flags); } diff -Naur --no-dereference a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c --- a/drivers/soc/ti/pruss.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/pruss.c 2022-01-06 12:45:53.826318157 -0500 @@ -2,10 +2,11 @@ /* * PRU-ICSS platform driver for various TI SoCs * - * Copyright (C) 2014-2020 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2014-2021 Texas Instruments Incorporated - https://www.ti.com/ * Author(s): * Suman Anna * Andrew F. Davis + * Tero Kristo */ #include @@ -18,18 +19,264 @@ #include #include #include +#include #include +#define SYSCFG_STANDBY_INIT BIT(4) +#define SYSCFG_SUB_MWAIT_READY BIT(5) + /** * struct pruss_private_data - PRUSS driver private data * @has_no_sharedram: flag to indicate the absence of PRUSS Shared Data RAM * @has_core_mux_clock: flag to indicate the presence of PRUSS core clock + * @has_ocp_syscfg: flag to indicate if OCP SYSCFG is present */ struct pruss_private_data { bool has_no_sharedram; bool has_core_mux_clock; + bool has_ocp_syscfg; }; +/** + * pruss_get() - get the pruss for a given PRU remoteproc + * @rproc: remoteproc handle of a PRU instance + * + * Finds the parent pruss device for a PRU given the @rproc handle of the + * PRU remote processor. This function increments the pruss device's refcount, + * so always use pruss_put() to decrement it back once pruss isn't needed + * anymore. + * + * Return: pruss handle on success, and an ERR_PTR on failure using one + * of the following error values + * -EINVAL if invalid parameter + * -ENODEV if PRU device or PRUSS device is not found + */ +struct pruss *pruss_get(struct rproc *rproc) +{ + struct pruss *pruss; + struct device *dev; + struct platform_device *ppdev; + + if (IS_ERR_OR_NULL(rproc)) + return ERR_PTR(-EINVAL); + + dev = &rproc->dev; + + /* make sure it is PRU rproc */ + if (!dev->parent || !is_pru_rproc(dev->parent)) + return ERR_PTR(-ENODEV); + + ppdev = to_platform_device(dev->parent->parent); + pruss = platform_get_drvdata(ppdev); + if (!pruss) + return ERR_PTR(-ENODEV); + + get_device(pruss->dev); + + return pruss; +} +EXPORT_SYMBOL_GPL(pruss_get); + +/** + * pruss_put() - decrement pruss device's usecount + * @pruss: pruss handle + * + * Complimentary function for pruss_get(). Needs to be called + * after the PRUSS is used, and only if the pruss_get() succeeds. + */ +void pruss_put(struct pruss *pruss) +{ + if (IS_ERR_OR_NULL(pruss)) + return; + + put_device(pruss->dev); +} +EXPORT_SYMBOL_GPL(pruss_put); + +/** + * pruss_request_mem_region() - request a memory resource + * @pruss: the pruss instance + * @mem_id: the memory resource id + * @region: pointer to memory region structure to be filled in + * + * This function allows a client driver to request a memory resource, + * and if successful, will let the client driver own the particular + * memory region until released using the pruss_release_mem_region() + * API. + * + * Return: 0 if requested memory region is available with the memory region + * values returned in memory pointed by @region, an error otherwise + */ +int pruss_request_mem_region(struct pruss *pruss, enum pruss_mem mem_id, + struct pruss_mem_region *region) +{ + if (!pruss || !region || mem_id >= PRUSS_MEM_MAX) + return -EINVAL; + + mutex_lock(&pruss->lock); + + if (pruss->mem_in_use[mem_id]) { + mutex_unlock(&pruss->lock); + return -EBUSY; + } + + *region = pruss->mem_regions[mem_id]; + pruss->mem_in_use[mem_id] = region; + + mutex_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_request_mem_region); + +/** + * pruss_release_mem_region() - release a memory resource + * @pruss: the pruss instance + * @region: the memory region to release + * + * This function is the complimentary function to + * pruss_request_mem_region(), and allows the client drivers to + * release back a memory resource. + * + * Return: 0 on success, an error code otherwise + */ +int pruss_release_mem_region(struct pruss *pruss, + struct pruss_mem_region *region) +{ + int id; + + if (!pruss || !region) + return -EINVAL; + + mutex_lock(&pruss->lock); + + /* find out the memory region being released */ + for (id = 0; id < PRUSS_MEM_MAX; id++) { + if (pruss->mem_in_use[id] == region) + break; + } + + if (id == PRUSS_MEM_MAX) { + mutex_unlock(&pruss->lock); + return -EINVAL; + } + + pruss->mem_in_use[id] = NULL; + memset(region, 0, sizeof(*region)); + + mutex_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_release_mem_region); + +/** + * pruss_cfg_read() - read a PRUSS CFG sub-module register + * @pruss: the pruss instance handle + * @reg: register offset within the CFG sub-module + * @val: pointer to return the value in + * + * Reads a given register within the PRUSS CFG sub-module and + * returns it through the passed-in @val pointer + * + * Return: 0 on success, or an error code otherwise + */ +int pruss_cfg_read(struct pruss *pruss, unsigned int reg, unsigned int *val) +{ + if (IS_ERR_OR_NULL(pruss)) + return -EINVAL; + + return regmap_read(pruss->cfg_regmap, reg, val); +} +EXPORT_SYMBOL_GPL(pruss_cfg_read); + +/** + * pruss_cfg_update() - configure a PRUSS CFG sub-module register + * @pruss: the pruss instance handle + * @reg: register offset within the CFG sub-module + * @mask: bit mask to use for programming the @val + * @val: value to write + * + * Programs a given register within the PRUSS CFG sub-module + * + * Return: 0 on success, or an error code otherwise + */ +int pruss_cfg_update(struct pruss *pruss, unsigned int reg, + unsigned int mask, unsigned int val) +{ + if (IS_ERR_OR_NULL(pruss)) + return -EINVAL; + + return regmap_update_bits(pruss->cfg_regmap, reg, mask, val); +} +EXPORT_SYMBOL_GPL(pruss_cfg_update); + +/** + * pruss_cfg_ocp_master_ports() - configure PRUSS OCP master ports + * @pruss: the pruss instance handle + * @enable: set to true for enabling or false for disabling the OCP master ports + * + * This function programs the PRUSS_SYSCFG.STANDBY_INIT bit either to enable or + * disable the OCP master ports (applicable only on SoCs using OCP interconnect + * like the OMAP family). Clearing the bit achieves dual functionalities - one + * is to deassert the MStandby signal to the device PRCM, and the other is to + * enable OCP master ports to allow accesses outside of the PRU-ICSS. The + * function has to wait for the PRCM to acknowledge through the monitoring of + * the PRUSS_SYSCFG.SUB_MWAIT bit when enabling master ports. Setting the bit + * disables the master access, and also signals the PRCM that the PRUSS is ready + * for Standby. + * + * Return: 0 on success, or an error code otherwise. ETIMEDOUT is returned + * when the ready-state fails. + */ +int pruss_cfg_ocp_master_ports(struct pruss *pruss, bool enable) +{ + int ret; + u32 syscfg_val, i; + const struct pruss_private_data *data; + + if (IS_ERR_OR_NULL(pruss)) + return -EINVAL; + + data = of_device_get_match_data(pruss->dev); + + /* nothing to do on non OMAP-SoCs */ + if (!data || !data->has_ocp_syscfg) + return 0; + + /* assert the MStandby signal during disable path */ + if (!enable) + return pruss_cfg_update(pruss, PRUSS_CFG_SYSCFG, + SYSCFG_STANDBY_INIT, + SYSCFG_STANDBY_INIT); + + /* enable the OCP master ports and disable MStandby */ + ret = pruss_cfg_update(pruss, PRUSS_CFG_SYSCFG, SYSCFG_STANDBY_INIT, 0); + if (ret) + return ret; + + /* wait till we are ready for transactions - delay is arbitrary */ + for (i = 0; i < 10; i++) { + ret = pruss_cfg_read(pruss, PRUSS_CFG_SYSCFG, &syscfg_val); + if (ret) + goto disable; + + if (!(syscfg_val & SYSCFG_SUB_MWAIT_READY)) + return 0; + + udelay(5); + } + + dev_err(pruss->dev, "timeout waiting for SUB_MWAIT_READY\n"); + ret = -ETIMEDOUT; + +disable: + pruss_cfg_update(pruss, PRUSS_CFG_SYSCFG, SYSCFG_STANDBY_INIT, + SYSCFG_STANDBY_INIT); + return ret; +} +EXPORT_SYMBOL_GPL(pruss_cfg_ocp_master_ports); + static void pruss_of_free_clk_provider(void *data) { struct device_node *clk_mux_np = data; @@ -126,8 +373,6 @@ int ret = 0; data = of_device_get_match_data(dev); - if (IS_ERR(data)) - return -ENODEV; clks_np = of_get_child_by_name(cfg_node, "clocks"); if (!clks_np) { @@ -163,6 +408,53 @@ .reg_stride = 4, }; +static int pruss_cfg_of_init(struct device *dev, struct pruss *pruss) +{ + struct device_node *np = dev_of_node(dev); + struct device_node *child; + struct resource res; + int ret; + + child = of_get_child_by_name(np, "cfg"); + if (!child) { + dev_err(dev, "%pOF is missing its 'cfg' node\n", child); + return -ENODEV; + } + + if (of_address_to_resource(child, 0, &res)) { + ret = -ENOMEM; + goto node_put; + } + + pruss->cfg_base = devm_ioremap(dev, res.start, resource_size(&res)); + if (!pruss->cfg_base) { + ret = -ENOMEM; + goto node_put; + } + + regmap_conf.name = kasprintf(GFP_KERNEL, "%pOFn@%llx", child, + (u64)res.start); + regmap_conf.max_register = resource_size(&res) - 4; + + pruss->cfg_regmap = devm_regmap_init_mmio(dev, pruss->cfg_base, + ®map_conf); + kfree(regmap_conf.name); + if (IS_ERR(pruss->cfg_regmap)) { + dev_err(dev, "regmap_init_mmio failed for cfg, ret = %ld\n", + PTR_ERR(pruss->cfg_regmap)); + ret = PTR_ERR(pruss->cfg_regmap); + goto node_put; + } + + ret = pruss_clk_init(pruss, child); + if (ret) + dev_err(dev, "pruss_clk_init failed, ret = %d\n", ret); + +node_put: + of_node_put(child); + return ret; +} + static int pruss_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -175,10 +467,6 @@ const char *mem_names[PRUSS_MEM_MAX] = { "dram0", "dram1", "shrdram2" }; data = of_device_get_match_data(&pdev->dev); - if (IS_ERR(data)) { - dev_err(dev, "missing private data\n"); - return -ENODEV; - } ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); if (ret) { @@ -191,6 +479,7 @@ return -ENOMEM; pruss->dev = dev; + mutex_init(&pruss->lock); child = of_get_child_by_name(np, "memories"); if (!child) { @@ -245,56 +534,18 @@ goto rpm_disable; } - child = of_get_child_by_name(np, "cfg"); - if (!child) { - dev_err(dev, "%pOF is missing its 'cfg' node\n", child); - ret = -ENODEV; + ret = pruss_cfg_of_init(dev, pruss); + if (ret < 0) goto rpm_put; - } - - if (of_address_to_resource(child, 0, &res)) { - ret = -ENOMEM; - goto node_put; - } - - pruss->cfg_base = devm_ioremap(dev, res.start, resource_size(&res)); - if (!pruss->cfg_base) { - ret = -ENOMEM; - goto node_put; - } - - regmap_conf.name = kasprintf(GFP_KERNEL, "%pOFn@%llx", child, - (u64)res.start); - regmap_conf.max_register = resource_size(&res) - 4; - - pruss->cfg_regmap = devm_regmap_init_mmio(dev, pruss->cfg_base, - ®map_conf); - kfree(regmap_conf.name); - if (IS_ERR(pruss->cfg_regmap)) { - dev_err(dev, "regmap_init_mmio failed for cfg, ret = %ld\n", - PTR_ERR(pruss->cfg_regmap)); - ret = PTR_ERR(pruss->cfg_regmap); - goto node_put; - } - - ret = pruss_clk_init(pruss, child); - if (ret) { - dev_err(dev, "failed to setup coreclk-mux\n"); - goto node_put; - } ret = devm_of_platform_populate(dev); if (ret) { dev_err(dev, "failed to register child devices\n"); - goto node_put; + goto rpm_put; } - of_node_put(child); - return 0; -node_put: - of_node_put(child); rpm_put: pm_runtime_put_sync(dev); rpm_disable: @@ -317,10 +568,16 @@ /* instance-specific driver private data */ static const struct pruss_private_data am437x_pruss1_data = { .has_no_sharedram = false, + .has_ocp_syscfg = true, }; static const struct pruss_private_data am437x_pruss0_data = { .has_no_sharedram = true, + .has_ocp_syscfg = false, +}; + +static const struct pruss_private_data am33xx_am57xx_data = { + .has_ocp_syscfg = true, }; static const struct pruss_private_data am65x_j721e_pruss_data = { @@ -328,13 +585,14 @@ }; static const struct of_device_id pruss_of_match[] = { - { .compatible = "ti,am3356-pruss" }, + { .compatible = "ti,am3356-pruss", .data = &am33xx_am57xx_data }, { .compatible = "ti,am4376-pruss0", .data = &am437x_pruss0_data, }, { .compatible = "ti,am4376-pruss1", .data = &am437x_pruss1_data, }, - { .compatible = "ti,am5728-pruss" }, + { .compatible = "ti,am5728-pruss", .data = &am33xx_am57xx_data }, { .compatible = "ti,k2g-pruss" }, { .compatible = "ti,am654-icssg", .data = &am65x_j721e_pruss_data, }, { .compatible = "ti,j721e-icssg", .data = &am65x_j721e_pruss_data, }, + { .compatible = "ti,am642-icssg", .data = &am65x_j721e_pruss_data, }, {}, }; MODULE_DEVICE_TABLE(of, pruss_of_match); diff -Naur --no-dereference a/drivers/soc/ti/ti_sci_inta_msi.c b/drivers/soc/ti/ti_sci_inta_msi.c --- a/drivers/soc/ti/ti_sci_inta_msi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/ti_sci_inta_msi.c 2022-01-06 12:45:53.826318157 -0500 @@ -89,6 +89,18 @@ list_add_tail(&msi_desc->list, dev_to_msi_list(dev)); count++; } + for (i = 0; i < res->desc[set].num_sec; i++) { + msi_desc = alloc_msi_entry(dev, 1, NULL); + if (!msi_desc) { + ti_sci_inta_msi_free_descs(dev); + return -ENOMEM; + } + + msi_desc->inta.dev_index = res->desc[set].start_sec + i; + INIT_LIST_HEAD(&msi_desc->list); + list_add_tail(&msi_desc->list, dev_to_msi_list(dev)); + count++; + } } return count; diff -Naur --no-dereference a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c --- a/drivers/soc/ti/wkup_m3_ipc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/soc/ti/wkup_m3_ipc.c 2022-01-06 12:45:53.826318157 -0500 @@ -7,7 +7,9 @@ * Dave Gerlach */ +#include #include +#include #include #include #include @@ -40,12 +42,30 @@ #define M3_FW_VERSION_MASK 0xffff #define M3_WAKE_SRC_MASK 0xff +#define IPC_MEM_TYPE_SHIFT (0x0) +#define IPC_MEM_TYPE_MASK (0x7 << 0) +#define IPC_VTT_STAT_SHIFT (0x3) +#define IPC_VTT_STAT_MASK (0x1 << 3) +#define IPC_VTT_GPIO_PIN_SHIFT (0x4) +#define IPC_VTT_GPIO_PIN_MASK (0x3f << 4) +#define IPC_IO_ISOLATION_STAT_SHIFT (10) +#define IPC_IO_ISOLATION_STAT_MASK (0x1 << 10) + +#define IPC_DBG_HALT_SHIFT (11) +#define IPC_DBG_HALT_MASK (0x1 << 11) + #define M3_STATE_UNKNOWN 0 #define M3_STATE_RESET 1 #define M3_STATE_INITED 2 #define M3_STATE_MSG_FOR_LP 3 #define M3_STATE_MSG_FOR_RESET 4 +#define WKUP_M3_SD_FW_MAGIC 0x570C + +#define WKUP_M3_DMEM_START 0x80000 +#define WKUP_M3_AUXDATA_OFFSET 0x1000 +#define WKUP_M3_AUXDATA_SIZE 0xFF + static struct wkup_m3_ipc *m3_ipc_state; static const struct wkup_m3_wakeup_src wakeups[] = { @@ -66,6 +86,147 @@ {.irq_nr = 0, .src = "Unknown"}, }; +/** + * wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem + * @data - pointer to data + * @sz - size of data to copy (limit 256 bytes) + * + * Copies any additional blob of data to the wkup_m3 dmem to be used by the + * firmware + */ +static unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc, + const void *data, int sz) +{ + unsigned long aux_data_dev_addr; + void *aux_data_addr; + + aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET; + aux_data_addr = rproc_da_to_va(m3_ipc->rproc, + aux_data_dev_addr, + WKUP_M3_AUXDATA_SIZE); + memcpy(aux_data_addr, data, sz); + + return WKUP_M3_AUXDATA_OFFSET; +} + +static void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context) +{ + unsigned long val, aux_base; + struct wkup_m3_scale_data_header hdr; + struct wkup_m3_ipc *m3_ipc = context; + struct device *dev = m3_ipc->dev; + + if (!fw) { + dev_err(dev, "Voltage scale fw name given but file missing.\n"); + return; + } + + memcpy(&hdr, fw->data, sizeof(hdr)); + + if (hdr.magic != WKUP_M3_SD_FW_MAGIC) { + dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n"); + goto release_sd_fw; + } + + aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr), + fw->size - sizeof(hdr)); + + val = (aux_base + hdr.sleep_offset); + val |= ((aux_base + hdr.wake_offset) << 16); + + m3_ipc->volt_scale_offsets = val; + +release_sd_fw: + release_firmware(fw); +}; + +static int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc, + struct device *dev) +{ + int ret = 0; + + /* + * If no name is provided, user has already been warned, pm will + * still work so return 0 + */ + + if (!m3_ipc->sd_fw_name) + return ret; + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + m3_ipc->sd_fw_name, dev, GFP_ATOMIC, + m3_ipc, wkup_m3_scale_data_fw_cb); + + return ret; +} + +#ifdef CONFIG_DEBUG_FS +static void wkup_m3_set_halt_late(bool enabled) +{ + if (enabled) + m3_ipc_state->halt = (1 << IPC_DBG_HALT_SHIFT); + else + m3_ipc_state->halt = 0; +} + +static int option_get(void *data, u64 *val) +{ + u32 *option = data; + + *val = *option; + + return 0; +} + +static int option_set(void *data, u64 val) +{ + u32 *option = data; + + *option = val; + + if (option == &m3_ipc_state->halt) { + if (val) + wkup_m3_set_halt_late(true); + else + wkup_m3_set_halt_late(false); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(wkup_m3_ipc_option_fops, option_get, option_set, + "%llu\n"); + +static int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc) +{ + m3_ipc->dbg_path = debugfs_create_dir("wkup_m3_ipc", NULL); + + if (!m3_ipc->dbg_path) + return -EINVAL; + + (void)debugfs_create_file("enable_late_halt", 0644, + m3_ipc->dbg_path, + &m3_ipc->halt, + &wkup_m3_ipc_option_fops); + + return 0; +} + +static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc) +{ + debugfs_remove_recursive(m3_ipc->dbg_path); +} +#else +static inline int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc) +{ + return 0; +} + +static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc) +{ +} +#endif /* CONFIG_DEBUG_FS */ + static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc) { writel(AM33XX_M3_TXEV_ACK, @@ -130,6 +291,7 @@ } m3_ipc->state = M3_STATE_INITED; + wkup_m3_init_scale_data(m3_ipc, dev); complete(&m3_ipc->sync_complete); break; case M3_STATE_MSG_FOR_RESET: @@ -215,9 +377,21 @@ (m3_ipc->state != M3_STATE_UNKNOWN)); } +static void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio) +{ + m3_ipc->vtt_conf = (1 << IPC_VTT_STAT_SHIFT) | + (gpio << IPC_VTT_GPIO_PIN_SHIFT); +} + +static void wkup_m3_set_io_isolation(struct wkup_m3_ipc *m3_ipc) +{ + m3_ipc->isolation_conf = (1 << IPC_IO_ISOLATION_STAT_SHIFT); +} + /* Public functions */ /** * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use + * @m3_ipc: Pointer to wkup_m3_ipc context * @mem_type: memory type value read directly from emif * * wkup_m3 must know what memory type is in use to properly suspend @@ -230,6 +404,7 @@ /** * wkup_m3_set_resume_address - Pass wkup_m3 resume address + * @m3_ipc: Pointer to wkup_m3_ipc context * @addr: Physical address from which resume code should execute */ static void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr) @@ -239,6 +414,7 @@ /** * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend + * @m3_ipc: Pointer to wkup_m3_ipc context * * Returns code representing the status of a low power mode transition. * 0 - Successful transition @@ -260,6 +436,7 @@ /** * wkup_m3_prepare_low_power - Request preparation for transition to * low power state + * @m3_ipc: Pointer to wkup_m3_ipc context * @state: A kernel suspend state to enter, either MEM or STANDBY * * Returns 0 if preparation was successful, otherwise returns error code @@ -276,12 +453,15 @@ switch (state) { case WKUP_M3_DEEPSLEEP: m3_power_state = IPC_CMD_DS0; + wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5); break; case WKUP_M3_STANDBY: m3_power_state = IPC_CMD_STANDBY; + wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); break; case WKUP_M3_IDLE: m3_power_state = IPC_CMD_IDLE; + wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); break; default: return 1; @@ -290,11 +470,13 @@ /* Program each required IPC register then write defaults to others */ wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0); wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); - wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type, 4); + wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type | + m3_ipc->vtt_conf | + m3_ipc->isolation_conf | + m3_ipc->halt, 4); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); - wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7); @@ -315,6 +497,7 @@ /** * wkup_m3_finish_low_power - Return m3 to reset state + * @m3_ipc: Pointer to wkup_m3_ipc context * * Returns 0 if reset was successful, otherwise returns error code */ @@ -362,8 +545,7 @@ /** * wkup_m3_set_rtc_only - Set the rtc_only flag - * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the - * wakeup src value + * @m3_ipc: Pointer to wkup_m3_ipc context */ static void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc) { @@ -428,12 +610,13 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - int irq, ret; + int irq, ret, temp; phandle rproc_phandle; struct rproc *m3_rproc; struct resource *res; struct task_struct *task; struct wkup_m3_ipc *m3_ipc; + struct device_node *np = dev->of_node; m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL); if (!m3_ipc) @@ -493,6 +676,23 @@ m3_ipc->ops = &ipc_ops; + if (of_find_property(np, "ti,needs-vtt-toggle", NULL) && + !(of_property_read_u32(np, "ti,vtt-gpio-pin", &temp))) { + if (temp >= 0 && temp <= 31) + wkup_m3_set_vtt_gpio(m3_ipc, temp); + else + dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp); + } + + if (of_find_property(np, "ti,set-io-isolation", NULL)) + wkup_m3_set_io_isolation(m3_ipc); + + ret = of_property_read_string(np, "ti,scale-data-fw", + &m3_ipc->sd_fw_name); + if (ret) { + dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n"); + }; + /* * Wait for firmware loading completion in a thread so we * can boot the wkup_m3 as soon as it's ready without holding @@ -507,6 +707,8 @@ goto err_put_rproc; } + wkup_m3_ipc_dbg_init(m3_ipc); + return 0; err_put_rproc: @@ -518,6 +720,8 @@ static int wkup_m3_ipc_remove(struct platform_device *pdev) { + wkup_m3_ipc_dbg_destroy(m3_ipc_state); + mbox_free_channel(m3_ipc_state->mbox); rproc_shutdown(m3_ipc_state->rproc); diff -Naur --no-dereference a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c --- a/drivers/spi/spi-cadence-quadspi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/spi/spi-cadence-quadspi.c 2022-01-06 12:45:53.826318157 -0500 @@ -41,18 +41,30 @@ struct cqspi_st; +struct phy_setting { + u8 rx; + u8 tx; + u8 read_delay; +}; + struct cqspi_flash_pdata { - struct cqspi_st *cqspi; - u32 clk_rate; - u32 read_delay; - u32 tshsl_ns; - u32 tsd2d_ns; - u32 tchsh_ns; - u32 tslch_ns; - u8 inst_width; - u8 addr_width; - u8 data_width; - u8 cs; + struct cqspi_st *cqspi; + u32 clk_rate; + u32 read_delay; + u32 tshsl_ns; + u32 tsd2d_ns; + u32 tchsh_ns; + u32 tslch_ns; + u8 inst_width; + u8 addr_width; + u8 data_width; + bool dtr; + u8 cs; + bool use_phy; + struct phy_setting phy_setting; + struct spi_mem_op phy_read_op; + u32 phy_tx_start; + u32 phy_tx_end; }; struct cqspi_st { @@ -106,11 +118,15 @@ /* Register map */ #define CQSPI_REG_CONFIG 0x00 #define CQSPI_REG_CONFIG_ENABLE_MASK BIT(0) +#define CQSPI_REG_CONFIG_PHY_EN BIT(3) #define CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL BIT(7) #define CQSPI_REG_CONFIG_DECODE_MASK BIT(9) #define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10 #define CQSPI_REG_CONFIG_DMA_MASK BIT(15) #define CQSPI_REG_CONFIG_BAUD_LSB 19 +#define CQSPI_REG_CONFIG_DTR_PROTO BIT(24) +#define CQSPI_REG_CONFIG_PHY_PIPELINE BIT(25) +#define CQSPI_REG_CONFIG_DUAL_OPCODE BIT(30) #define CQSPI_REG_CONFIG_IDLE_LSB 31 #define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF #define CQSPI_REG_CONFIG_BAUD_MASK 0xF @@ -146,6 +162,7 @@ #define CQSPI_REG_READCAPTURE_BYPASS_LSB 0 #define CQSPI_REG_READCAPTURE_DELAY_LSB 1 #define CQSPI_REG_READCAPTURE_DELAY_MASK 0xF +#define CQSPI_REG_READCAPTURE_DQS_LSB 8 #define CQSPI_REG_SIZE 0x14 #define CQSPI_REG_SIZE_ADDRESS_LSB 0 @@ -173,6 +190,9 @@ #define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF #define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF +#define CQSPI_REG_WR_COMPLETION_CTRL 0x38 +#define CQSPI_REG_WR_DISABLE_AUTO_POLL BIT(14) + #define CQSPI_REG_IRQSTATUS 0x40 #define CQSPI_REG_IRQMASK 0x44 @@ -188,6 +208,7 @@ #define CQSPI_REG_CMDCTRL 0x90 #define CQSPI_REG_CMDCTRL_EXECUTE_MASK BIT(0) #define CQSPI_REG_CMDCTRL_INPROGRESS_MASK BIT(1) +#define CQSPI_REG_CMDCTRL_DUMMY_LSB 7 #define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12 #define CQSPI_REG_CMDCTRL_WR_EN_LSB 15 #define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16 @@ -198,6 +219,7 @@ #define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7 #define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3 #define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7 +#define CQSPI_REG_CMDCTRL_DUMMY_MASK 0x1F #define CQSPI_REG_INDIRECTWR 0x70 #define CQSPI_REG_INDIRECTWR_START_MASK BIT(0) @@ -214,6 +236,21 @@ #define CQSPI_REG_CMDWRITEDATALOWER 0xA8 #define CQSPI_REG_CMDWRITEDATAUPPER 0xAC +#define CQSPI_REG_POLLING_STATUS 0xB0 +#define CQSPI_REG_POLLING_STATUS_DUMMY_LSB 16 + +#define CQSPI_REG_PHY_CONFIG 0xB4 +#define CQSPI_REG_PHY_CONFIG_RX_DEL_LSB 0 +#define CQSPI_REG_PHY_CONFIG_RX_DEL_MASK 0x7F +#define CQSPI_REG_PHY_CONFIG_TX_DEL_LSB 16 +#define CQSPI_REG_PHY_CONFIG_TX_DEL_MASK 0x7F +#define CQSPI_REG_PHY_CONFIG_RESYNC BIT(31) + +#define CQSPI_REG_OP_EXT_LOWER 0xE0 +#define CQSPI_REG_OP_EXT_READ_LSB 24 +#define CQSPI_REG_OP_EXT_WRITE_LSB 16 +#define CQSPI_REG_OP_EXT_STIG_LSB 0 + /* Interrupt status bits */ #define CQSPI_REG_IRQ_MODE_ERR BIT(0) #define CQSPI_REG_IRQ_UNDERFLOW BIT(1) @@ -234,6 +271,576 @@ #define CQSPI_IRQ_STATUS_MASK 0x1FFFF +#define CQSPI_PHY_INIT_RD 1 +#define CQSPI_PHY_MAX_RD 4 +#define CQSPI_PHY_MAX_RX 63 +#define CQSPI_PHY_MAX_TX 63 +#define CQSPI_PHY_LOW_RX_BOUND 15 +#define CQSPI_PHY_HIGH_RX_BOUND 25 +#define CQSPI_PHY_LOW_TX_BOUND 32 +#define CQSPI_PHY_HIGH_TX_BOUND 48 +#define CQSPI_PHY_TX_LOOKUP_LOW_BOUND 24 +#define CQSPI_PHY_TX_LOOKUP_HIGH_BOUND 38 + +#define CQSPI_PHY_DEFAULT_TEMP 45 +#define CQSPI_PHY_MIN_TEMP -45 +#define CQSPI_PHY_MAX_TEMP 130 +#define CQSPI_PHY_MID_TEMP (CQSPI_PHY_MIN_TEMP + \ + ((CQSPI_PHY_MAX_TEMP - CQSPI_PHY_MIN_TEMP) / 2)) + +static const u8 phy_tuning_pattern[] = { +0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xFE, 0xFE, 0x01, 0x01, +0x01, 0x01, 0x00, 0x00, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, +0x00, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0xFF, 0x01, +0x01, 0x01, 0x01, 0x01, 0xFE, 0x00, 0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0xFE, +0x00, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFE, 0xFE, +0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFE, 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, +0x01, 0x00, 0xFE, 0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0x00, 0xFE, 0xFE, 0xFE, +0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, +0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFE, 0xFE, +0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0xFE, 0xFE, 0xFE, 0xFE, 0x01, +}; + +static void cqspi_set_tx_dll(void __iomem *reg_base, u8 dll) +{ + unsigned int reg; + + reg = readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg &= ~(CQSPI_REG_PHY_CONFIG_TX_DEL_MASK << + CQSPI_REG_PHY_CONFIG_TX_DEL_LSB); + reg |= (dll & CQSPI_REG_PHY_CONFIG_TX_DEL_MASK) << + CQSPI_REG_PHY_CONFIG_TX_DEL_LSB; + reg |= CQSPI_REG_PHY_CONFIG_RESYNC; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); +} + +static void cqspi_set_rx_dll(void __iomem *reg_base, u8 dll) +{ + unsigned int reg; + + reg = readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg &= ~(CQSPI_REG_PHY_CONFIG_RX_DEL_MASK << + CQSPI_REG_PHY_CONFIG_RX_DEL_LSB); + reg |= (dll & CQSPI_REG_PHY_CONFIG_RX_DEL_MASK) << + CQSPI_REG_PHY_CONFIG_RX_DEL_LSB; + reg |= CQSPI_REG_PHY_CONFIG_RESYNC; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); +} + +/* TODO: Figure out how to get the temperature here. */ +static int cqspi_get_temp(int *temp) +{ + return -EOPNOTSUPP; +} + +static void cqspi_phy_apply_setting(struct cqspi_flash_pdata *f_pdata, + struct phy_setting *phy) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + + cqspi_set_rx_dll(cqspi->iobase, phy->rx); + cqspi_set_tx_dll(cqspi->iobase, phy->tx); + f_pdata->phy_setting.read_delay = phy->read_delay; +} + +static int cqspi_phy_check_pattern(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem) +{ + struct spi_mem_op op = f_pdata->phy_read_op; + u8 *read_data; + unsigned int size = sizeof(phy_tuning_pattern); + int ret; + + read_data = kmalloc(size, GFP_KERNEL); + if (!read_data) + return -ENOMEM; + + op.data.buf.in = read_data; + op.data.nbytes = size; + + ret = spi_mem_exec_op(mem, &op); + if (ret) + goto out; + + if (memcmp(read_data, phy_tuning_pattern, + ARRAY_SIZE(phy_tuning_pattern))) { + ret = -EAGAIN; + goto out; + } + + ret = 0; + +out: + kfree(read_data); + return ret; +} + +static int cqspi_find_rx_low(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, struct phy_setting *phy) +{ + struct device *dev = &f_pdata->cqspi->pdev->dev; + int ret; + + do { + phy->rx = 0; + do { + cqspi_phy_apply_setting(f_pdata, phy); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (!ret) + return 0; + + phy->rx++; + } while (phy->rx <= CQSPI_PHY_LOW_RX_BOUND); + + phy->read_delay++; + } while (phy->read_delay <= CQSPI_PHY_MAX_RD); + + dev_dbg(dev, "Unable to find RX low\n"); + return -ENOENT; +} + +static int cqspi_find_rx_high(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, struct phy_setting *phy) +{ + struct device *dev = &f_pdata->cqspi->pdev->dev; + int ret; + + do { + phy->rx = CQSPI_PHY_MAX_RX; + do { + cqspi_phy_apply_setting(f_pdata, phy); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (!ret) + return 0; + + phy->rx--; + } while (phy->rx >= CQSPI_PHY_HIGH_RX_BOUND); + + phy->read_delay++; + } while (phy->read_delay <= CQSPI_PHY_MAX_RD); + + dev_dbg(dev, "Unable to find RX high\n"); + return -ENOENT; +} + +static int cqspi_find_tx_low(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, struct phy_setting *phy) +{ + struct device *dev = &f_pdata->cqspi->pdev->dev; + int ret; + + do { + phy->tx = 0; + do { + cqspi_phy_apply_setting(f_pdata, phy); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (!ret) + return 0; + + phy->tx++; + } while (phy->tx <= CQSPI_PHY_LOW_TX_BOUND); + + phy->read_delay++; + } while (phy->read_delay <= CQSPI_PHY_MAX_RD); + + dev_dbg(dev, "Unable to find TX low\n"); + return -ENOENT; +} + +static int cqspi_find_tx_high(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, struct phy_setting *phy) +{ + struct device *dev = &f_pdata->cqspi->pdev->dev; + int ret; + + do { + phy->tx = CQSPI_PHY_MAX_TX; + do { + cqspi_phy_apply_setting(f_pdata, phy); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (!ret) + return 0; + + phy->tx--; + } while (phy->tx >= CQSPI_PHY_HIGH_TX_BOUND); + + phy->read_delay++; + } while (phy->read_delay <= CQSPI_PHY_MAX_RD); + + dev_dbg(dev, "Unable to find TX high\n"); + return -ENOENT; +} + +static int cqspi_phy_find_gaplow(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, + struct phy_setting *bottomleft, + struct phy_setting *topright, + struct phy_setting *gaplow) +{ + struct phy_setting left, right, mid; + int ret; + + left = *bottomleft; + right = *topright; + + mid.tx = left.tx + ((right.tx - left.tx) / 2); + mid.rx = left.rx + ((right.rx - left.rx) / 2); + mid.read_delay = left.read_delay; + + do { + cqspi_phy_apply_setting(f_pdata, &mid); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + /* The pattern was not found. Go to the lower half. */ + right.tx = mid.tx; + right.rx = mid.rx; + + mid.tx = left.tx + ((mid.tx - left.tx) / 2); + mid.rx = left.rx + ((mid.rx - left.rx) / 2); + } else { + /* The pattern was found. Go to the upper half. */ + left.tx = mid.tx; + left.rx = mid.rx; + + mid.tx = mid.tx + ((right.tx - mid.tx) / 2); + mid.rx = mid.rx + ((right.rx - mid.rx) / 2); + } + + /* Break the loop if the window has closed. */ + } while ((right.tx - left.tx >= 2) && (right.rx - left.rx >= 2)); + + *gaplow = mid; + return 0; +} + +static int cqspi_phy_find_gaphigh(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, + struct phy_setting *bottomleft, + struct phy_setting *topright, + struct phy_setting *gaphigh) +{ + struct phy_setting left, right, mid; + int ret; + + left = *bottomleft; + right = *topright; + + mid.tx = left.tx + ((right.tx - left.tx) / 2); + mid.rx = left.rx + ((right.rx - left.rx) / 2); + mid.read_delay = right.read_delay; + + do { + cqspi_phy_apply_setting(f_pdata, &mid); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + /* The pattern was not found. Go to the upper half. */ + left.tx = mid.tx; + left.rx = mid.rx; + + mid.tx = mid.tx + ((right.tx - mid.tx) / 2); + mid.rx = mid.rx + ((right.rx - mid.rx) / 2); + } else { + /* The pattern was found. Go to the lower half. */ + right.tx = mid.tx; + right.rx = mid.rx; + + mid.tx = left.tx + ((mid.tx - left.tx) / 2); + mid.rx = left.rx + ((mid.rx - left.rx) / 2); + } + + /* Break the loop if the window has closed. */ + } while ((right.tx - left.tx >= 2) && (right.rx - left.rx >= 2)); + + *gaphigh = mid; + return 0; +} + +static int cqspi_phy_calibrate(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; + struct phy_setting rxlow, rxhigh, txlow, txhigh, temp; + struct phy_setting bottomleft, topright, searchpoint, gaplow, gaphigh; + int ret, tmp; + + f_pdata->use_phy = true; + + /* Look for RX boundaries at lower TX range. */ + rxlow.tx = f_pdata->phy_tx_start; + + do { + dev_dbg(dev, "Searching for rxlow on TX = %d\n", rxlow.tx); + rxlow.read_delay = CQSPI_PHY_INIT_RD; + ret = cqspi_find_rx_low(f_pdata, mem, &rxlow); + } while (ret && ++rxlow.tx <= CQSPI_PHY_TX_LOOKUP_LOW_BOUND); + + if (ret) + goto out; + dev_dbg(dev, "rxlow: RX: %d TX: %d RD: %d\n", rxlow.rx, rxlow.tx, + rxlow.read_delay); + + rxhigh.tx = rxlow.tx; + rxhigh.read_delay = rxlow.read_delay; + ret = cqspi_find_rx_high(f_pdata, mem, &rxhigh); + if (ret) + goto out; + dev_dbg(dev, "rxhigh: RX: %d TX: %d RD: %d\n", rxhigh.rx, rxhigh.tx, + rxhigh.read_delay); + + /* + * Check a different point if rxlow and rxhigh are on the same read + * delay. This avoids mistaking the failing region for an RX boundary. + */ + if (rxlow.read_delay == rxhigh.read_delay) { + dev_dbg(dev, + "rxlow and rxhigh at the same read delay.\n"); + + /* Look for RX boundaries at upper TX range. */ + temp.tx = f_pdata->phy_tx_end; + + do { + dev_dbg(dev, "Searching for rxlow on TX = %d\n", + temp.tx); + temp.read_delay = CQSPI_PHY_INIT_RD; + ret = cqspi_find_rx_low(f_pdata, mem, &temp); + } while (ret && --temp.tx >= CQSPI_PHY_TX_LOOKUP_HIGH_BOUND); + + if (ret) + goto out; + dev_dbg(dev, "rxlow: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx, + temp.read_delay); + + if (temp.rx < rxlow.rx) { + rxlow = temp; + dev_dbg(dev, "Updating rxlow to the one at TX = 48\n"); + } + + /* Find RX max. */ + ret = cqspi_find_rx_high(f_pdata, mem, &temp); + if (ret) + goto out; + dev_dbg(dev, "rxhigh: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx, + temp.read_delay); + + if (temp.rx < rxhigh.rx) { + rxhigh = temp; + dev_dbg(dev, "Updating rxhigh to the one at TX = 48\n"); + } + } + + /* Look for TX boundaries at 1/4 of RX window. */ + txlow.rx = rxlow.rx + ((rxhigh.rx - rxlow.rx) / 4); + txhigh.rx = txlow.rx; + + txlow.read_delay = CQSPI_PHY_INIT_RD; + ret = cqspi_find_tx_low(f_pdata, mem, &txlow); + if (ret) + goto out; + dev_dbg(dev, "txlow: RX: %d TX: %d RD: %d\n", txlow.rx, txlow.tx, + txlow.read_delay); + + txhigh.read_delay = txlow.read_delay; + ret = cqspi_find_tx_high(f_pdata, mem, &txhigh); + if (ret) + goto out; + dev_dbg(dev, "txhigh: RX: %d TX: %d RD: %d\n", txhigh.rx, txhigh.tx, + txhigh.read_delay); + + /* + * Check a different point if txlow and txhigh are on the same read + * delay. This avoids mistaking the failing region for an TX boundary. + */ + if (txlow.read_delay == txhigh.read_delay) { + /* Look for TX boundaries at 3/4 of RX window. */ + temp.rx = rxlow.rx + (3 * (rxhigh.rx - rxlow.rx) / 4); + temp.read_delay = CQSPI_PHY_INIT_RD; + dev_dbg(dev, + "txlow and txhigh at the same read delay. Searching at RX = %d\n", + temp.rx); + + ret = cqspi_find_tx_low(f_pdata, mem, &temp); + if (ret) + goto out; + dev_dbg(dev, "txlow: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx, + temp.read_delay); + + if (temp.tx < txlow.tx) { + txlow = temp; + dev_dbg(dev, "Updating txlow with the one at RX = %d\n", + txlow.rx); + } + + ret = cqspi_find_tx_high(f_pdata, mem, &temp); + if (ret) + goto out; + dev_dbg(dev, "txhigh: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx, + temp.read_delay); + + if (temp.tx < txhigh.tx) { + txhigh = temp; + dev_dbg(dev, "Updating txhigh with the one at RX = %d\n", + txhigh.rx); + } + } + + /* + * Set bottom left and top right corners. These are theoretical + * corners. They may not actually be "good" points. But the longest + * diagonal will be between these corners. + */ + bottomleft.tx = txlow.tx; + bottomleft.rx = rxlow.rx; + if (txlow.read_delay <= rxlow.read_delay) + bottomleft.read_delay = txlow.read_delay; + else + bottomleft.read_delay = rxlow.read_delay; + + temp = bottomleft; + temp.tx += 4; + temp.rx += 4; + cqspi_phy_apply_setting(f_pdata, &temp); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + temp.read_delay--; + cqspi_phy_apply_setting(f_pdata, &temp); + ret = cqspi_phy_check_pattern(f_pdata, mem); + } + + if (!ret) + bottomleft.read_delay = temp.read_delay; + + topright.tx = txhigh.tx; + topright.rx = rxhigh.rx; + if (txhigh.read_delay >= rxhigh.read_delay) + topright.read_delay = txhigh.read_delay; + else + topright.read_delay = rxhigh.read_delay; + + temp = topright; + temp.tx -= 4; + temp.rx -= 4; + cqspi_phy_apply_setting(f_pdata, &temp); + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + temp.read_delay++; + cqspi_phy_apply_setting(f_pdata, &temp); + ret = cqspi_phy_check_pattern(f_pdata, mem); + } + + if (!ret) + topright.read_delay = temp.read_delay; + + dev_dbg(dev, "topright: RX: %d TX: %d RD: %d\n", topright.rx, + topright.tx, topright.read_delay); + dev_dbg(dev, "bottomleft: RX: %d TX: %d RD: %d\n", bottomleft.rx, + bottomleft.tx, bottomleft.read_delay); + + ret = cqspi_phy_find_gaplow(f_pdata, mem, &bottomleft, &topright, + &gaplow); + if (ret) + goto out; + dev_dbg(dev, "gaplow: RX: %d TX: %d RD: %d\n", gaplow.rx, gaplow.tx, + gaplow.read_delay); + + if (bottomleft.read_delay == topright.read_delay) { + /* + * If there is only one passing region, it means that the "true" + * topright is too small to find, so the start of the failing + * region is a good approximation. Put the tuning point in the + * middle and adjust for temperature. + */ + topright = gaplow; + searchpoint.read_delay = bottomleft.read_delay; + searchpoint.tx = bottomleft.tx + + ((topright.tx - bottomleft.tx) / 2); + searchpoint.rx = bottomleft.rx + + ((topright.rx - bottomleft.rx) / 2); + + ret = cqspi_get_temp(&tmp); + if (ret) { + /* + * Assume room temperature if it couldn't be obtained + * from the thermal sensor. + * + * TODO: Change it to dev_warn once support for finding + * out the temperature is added. + */ + dev_dbg(dev, + "Unable to get temperature. Assuming room temperature\n"); + tmp = CQSPI_PHY_DEFAULT_TEMP; + } + + if (tmp < CQSPI_PHY_MIN_TEMP || tmp > CQSPI_PHY_MAX_TEMP) { + dev_err(dev, + "Temperature outside operating range: %dC\n", + tmp); + ret = -EINVAL; + goto out; + } + + /* Avoid a divide-by-zero. */ + if (tmp == CQSPI_PHY_MID_TEMP) + tmp++; + dev_dbg(dev, "Temperature: %dC\n", tmp); + + searchpoint.tx += (topright.tx - bottomleft.tx) / + (330 / (tmp - CQSPI_PHY_MID_TEMP)); + searchpoint.rx += (topright.rx - bottomleft.rx) / + (330 / (tmp - CQSPI_PHY_MID_TEMP)); + } else { + /* + * If there are two passing regions, find the start and end of + * the second one. + */ + ret = cqspi_phy_find_gaphigh(f_pdata, mem, &bottomleft, + &topright, &gaphigh); + if (ret) + goto out; + dev_dbg(dev, "gaphigh: RX: %d TX: %d RD: %d\n", gaphigh.rx, + gaphigh.tx, gaphigh.read_delay); + + /* + * Place the final tuning point in the corner furthest from the + * failing region but leave some margin for temperature changes. + */ + if ((abs(gaplow.tx - bottomleft.tx) + + abs(gaplow.rx - bottomleft.rx)) < + (abs(gaphigh.tx - topright.tx) + + abs(gaphigh.rx - topright.rx))) { + searchpoint = topright; + searchpoint.tx -= 16; + searchpoint.rx -= (16 * (topright.rx - bottomleft.rx)) / + (topright.tx - bottomleft.tx); + } else { + searchpoint = bottomleft; + searchpoint.tx += 16; + searchpoint.rx += (16 * (topright.rx - bottomleft.rx)) / + (topright.tx - bottomleft.tx); + } + } + + /* Set the final PHY settings and check if they are working. */ + cqspi_phy_apply_setting(f_pdata, &searchpoint); + dev_dbg(dev, "Final tuning point: RX: %d TX: %d RD: %d\n", + searchpoint.rx, searchpoint.tx, searchpoint.read_delay); + + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + dev_err(dev, + "Failed to find pattern at final calibration point\n"); + ret = -EINVAL; + goto out; + } + + ret = 0; + f_pdata->phy_setting.read_delay = searchpoint.read_delay; +out: + if (ret) + f_pdata->use_phy = false; + return ret; +} + static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr) { u32 val; @@ -288,6 +895,80 @@ return rdreg; } +static unsigned int cqspi_calc_dummy(const struct spi_mem_op *op, bool dtr) +{ + unsigned int dummy_clk; + + dummy_clk = op->dummy.nbytes * (8 / op->dummy.buswidth); + if (dtr) + dummy_clk /= 2; + + return dummy_clk; +} + +static int cqspi_set_protocol(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) +{ + f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE; + f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE; + f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; + f_pdata->dtr = op->data.dtr && op->cmd.dtr && op->addr.dtr; + + switch (op->data.buswidth) { + case 0: + break; + case 1: + f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; + break; + case 2: + f_pdata->data_width = CQSPI_INST_TYPE_DUAL; + break; + case 4: + f_pdata->data_width = CQSPI_INST_TYPE_QUAD; + break; + case 8: + f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + + /* Right now we only support 8-8-8 DTR mode. */ + if (f_pdata->dtr) { + switch (op->cmd.buswidth) { + case 0: + break; + case 8: + f_pdata->inst_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + + switch (op->addr.buswidth) { + case 0: + break; + case 8: + f_pdata->addr_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + + switch (op->data.buswidth) { + case 0: + break; + case 8: + f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + } + + return 0; +} + static int cqspi_wait_idle(struct cqspi_st *cqspi) { const unsigned int poll_idle_retry = 3; @@ -345,19 +1026,85 @@ return cqspi_wait_idle(cqspi); } +static int cqspi_setup_opcode_ext(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op, + unsigned int shift) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + void __iomem *reg_base = cqspi->iobase; + unsigned int reg; + u8 ext; + + if (op->cmd.nbytes != 2) + return -EINVAL; + + /* Opcode extension is the LSB. */ + ext = op->cmd.opcode & 0xff; + + reg = readl(reg_base + CQSPI_REG_OP_EXT_LOWER); + reg &= ~(0xff << shift); + reg |= ext << shift; + writel(reg, reg_base + CQSPI_REG_OP_EXT_LOWER); + + return 0; +} + +static int cqspi_enable_dtr(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op, unsigned int shift, + bool enable) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + void __iomem *reg_base = cqspi->iobase; + unsigned int reg; + int ret; + + reg = readl(reg_base + CQSPI_REG_CONFIG); + + /* + * We enable dual byte opcode here. The callers have to set up the + * extension opcode based on which type of operation it is. + */ + if (enable) { + reg |= CQSPI_REG_CONFIG_DTR_PROTO; + reg |= CQSPI_REG_CONFIG_DUAL_OPCODE; + + /* Set up command opcode extension. */ + ret = cqspi_setup_opcode_ext(f_pdata, op, shift); + if (ret) + return ret; + } else { + reg &= ~CQSPI_REG_CONFIG_DTR_PROTO; + reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE; + } + + writel(reg, reg_base + CQSPI_REG_CONFIG); + + return cqspi_wait_idle(cqspi); +} + static int cqspi_command_read(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; u8 *rxbuf = op->data.buf.in; - u8 opcode = op->cmd.opcode; + u8 opcode; size_t n_rx = op->data.nbytes; unsigned int rdreg; unsigned int reg; + unsigned int dummy_clk; size_t read_len; int status; + status = cqspi_set_protocol(f_pdata, op); + if (status) + return status; + + status = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_STIG_LSB, + f_pdata->dtr); + if (status) + return status; + if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) { dev_err(&cqspi->pdev->dev, "Invalid input argument, len %zu rxbuf 0x%p\n", @@ -365,11 +1112,24 @@ return -EINVAL; } + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; + reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; rdreg = cqspi_calc_rdreg(f_pdata); writel(rdreg, reg_base + CQSPI_REG_RD_INSTR); + dummy_clk = cqspi_calc_dummy(op, f_pdata->dtr); + if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) + return -EOPNOTSUPP; + + if (dummy_clk) + reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK) + << CQSPI_REG_CMDCTRL_DUMMY_LSB; + reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); /* 0 means 1 byte. */ @@ -401,12 +1161,22 @@ { struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; - const u8 opcode = op->cmd.opcode; + u8 opcode; const u8 *txbuf = op->data.buf.out; size_t n_tx = op->data.nbytes; unsigned int reg; unsigned int data; size_t write_len; + int ret; + + ret = cqspi_set_protocol(f_pdata, op); + if (ret) + return ret; + + ret = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_STIG_LSB, + f_pdata->dtr); + if (ret) + return ret; if (n_tx > CQSPI_STIG_DATA_LEN_MAX || (n_tx && !txbuf)) { dev_err(&cqspi->pdev->dev, @@ -415,6 +1185,14 @@ return -EINVAL; } + reg = cqspi_calc_rdreg(f_pdata); + writel(reg, reg_base + CQSPI_REG_RD_INSTR); + + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; + reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; if (op->addr.nbytes) { @@ -454,12 +1232,25 @@ void __iomem *reg_base = cqspi->iobase; unsigned int dummy_clk = 0; unsigned int reg; + int ret; + u8 opcode; - reg = op->cmd.opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; + ret = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_READ_LSB, + f_pdata->dtr); + if (ret) + return ret; + + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; + + reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; reg |= cqspi_calc_rdreg(f_pdata); /* Setup dummy clock cycles */ - dummy_clk = op->dummy.nbytes * 8; + dummy_clk = cqspi_calc_dummy(op, f_pdata->dtr); + if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) return -EOPNOTSUPP; @@ -474,6 +1265,7 @@ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (op->addr.nbytes - 1); writel(reg, reg_base + CQSPI_REG_SIZE); + readl(reg_base + CQSPI_REG_SIZE); /* Flush posted write. */ return 0; } @@ -573,19 +1365,48 @@ const struct spi_mem_op *op) { unsigned int reg; + int ret; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; + u8 opcode; + + ret = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_WRITE_LSB, + f_pdata->dtr); + if (ret) + return ret; + + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; /* Set opcode. */ - reg = op->cmd.opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; + reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; + reg |= f_pdata->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB; + reg |= f_pdata->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB; writel(reg, reg_base + CQSPI_REG_WR_INSTR); reg = cqspi_calc_rdreg(f_pdata); writel(reg, reg_base + CQSPI_REG_RD_INSTR); + if (f_pdata->dtr) { + /* + * Some flashes like the cypress Semper flash expect a 4-byte + * dummy address with the Read SR command in DTR mode, but this + * controller does not support sending address with the Read SR + * command. So, disable write completion polling on the + * controller's side. spi-nor will take care of polling the + * status register. + */ + reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL); + reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; + writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL); + } + reg = readl(reg_base + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (op->addr.nbytes - 1); writel(reg, reg_base + CQSPI_REG_SIZE); + readl(reg_base + CQSPI_REG_SIZE); /* Flush posted write. */ return 0; } @@ -770,6 +1591,7 @@ static void cqspi_readdata_capture(struct cqspi_st *cqspi, const bool bypass, + const bool dqs, const unsigned int delay) { void __iomem *reg_base = cqspi->iobase; @@ -788,6 +1610,11 @@ reg |= (delay & CQSPI_REG_READCAPTURE_DELAY_MASK) << CQSPI_REG_READCAPTURE_DELAY_LSB; + if (dqs) + reg |= (1 << CQSPI_REG_READCAPTURE_DQS_LSB); + else + reg &= ~(1 << CQSPI_REG_READCAPTURE_DQS_LSB); + writel(reg, reg_base + CQSPI_REG_READCAPTURE); } @@ -806,6 +1633,64 @@ writel(reg, reg_base + CQSPI_REG_CONFIG); } +static void cqspi_phy_enable(struct cqspi_flash_pdata *f_pdata, bool enable) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + void __iomem *reg_base = cqspi->iobase; + u32 reg; + u8 dummy; + + if (enable) { + cqspi_readdata_capture(cqspi, 1, true, + f_pdata->phy_setting.read_delay); + + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_PHY_EN | + CQSPI_REG_CONFIG_PHY_PIPELINE; + writel(reg, reg_base + CQSPI_REG_CONFIG); + + /* + * Reduce dummy cycle by 1. This is a requirement of PHY mode + * operation for correctly reading the data. + */ + reg = readl(reg_base + CQSPI_REG_RD_INSTR); + dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) & + CQSPI_REG_RD_INSTR_DUMMY_MASK; + dummy--; + reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK << + CQSPI_REG_RD_INSTR_DUMMY_LSB); + + reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK) + << CQSPI_REG_RD_INSTR_DUMMY_LSB; + writel(reg, reg_base + CQSPI_REG_RD_INSTR); + } else { + cqspi_readdata_capture(cqspi, !cqspi->rclk_en, false, + f_pdata->read_delay); + + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg &= ~(CQSPI_REG_CONFIG_PHY_EN | + CQSPI_REG_CONFIG_PHY_PIPELINE); + writel(reg, reg_base + CQSPI_REG_CONFIG); + + /* + * Dummy cycles were decremented when enabling PHY. Increment + * dummy cycle by 1 to restore the original value. + */ + reg = readl(reg_base + CQSPI_REG_RD_INSTR); + dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) & + CQSPI_REG_RD_INSTR_DUMMY_MASK; + dummy++; + reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK << + CQSPI_REG_RD_INSTR_DUMMY_LSB); + + reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK) + << CQSPI_REG_RD_INSTR_DUMMY_LSB; + writel(reg, reg_base + CQSPI_REG_RD_INSTR); + } + + cqspi_wait_idle(cqspi); +} + static void cqspi_configure(struct cqspi_flash_pdata *f_pdata, unsigned long sclk) { @@ -827,7 +1712,7 @@ cqspi->sclk = sclk; cqspi_config_baudrate_div(cqspi); cqspi_delay(f_pdata); - cqspi_readdata_capture(cqspi, !cqspi->rclk_en, + cqspi_readdata_capture(cqspi, !cqspi->rclk_en, false, f_pdata->read_delay); } @@ -835,35 +1720,6 @@ cqspi_controller_enable(cqspi, 1); } -static int cqspi_set_protocol(struct cqspi_flash_pdata *f_pdata, - const struct spi_mem_op *op) -{ - f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE; - f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE; - f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; - - if (op->data.dir == SPI_MEM_DATA_IN) { - switch (op->data.buswidth) { - case 1: - f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; - break; - case 2: - f_pdata->data_width = CQSPI_INST_TYPE_DUAL; - break; - case 4: - f_pdata->data_width = CQSPI_INST_TYPE_QUAD; - break; - case 8: - f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; - break; - default: - return -EINVAL; - } - } - - return 0; -} - static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { @@ -881,7 +1737,16 @@ if (ret) return ret; - if (cqspi->use_direct_mode && ((to + len) <= cqspi->ahb_size)) { + /* + * Some flashes like the Cypress Semper flash expect a dummy 4-byte + * address (all 0s) with the read status register command in DTR mode. + * But this controller does not support sending dummy address bytes to + * the flash when it is polling the write completion register in DTR + * mode. So, we can not use direct mode when in DTR mode for writing + * data. + */ + if (!f_pdata->dtr && cqspi->use_direct_mode && + ((to + len) <= cqspi->ahb_size)) { memcpy_toio(cqspi->ahb_base + to, buf, len); return cqspi_wait_idle(cqspi); } @@ -889,6 +1754,39 @@ return cqspi_indirect_write_execute(f_pdata, to, buf, len); } +/* + * Check if PHY mode can be used on the given op. This is assuming it will be a + * DAC mode read, since PHY won't work on any other type of operation anyway. + */ +static bool cqspi_phy_op_eligible(const struct spi_mem_op *op) +{ + /* PHY is only tuned for 8D-8D-8D. */ + if (!(op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr)) + return false; + if (op->cmd.buswidth != 8) + return false; + if (op->addr.nbytes && op->addr.buswidth != 8) + return false; + if (op->dummy.nbytes && op->dummy.buswidth != 8) + return false; + if (op->data.nbytes && op->data.buswidth != 8) + return false; + + return true; +} + +static bool cqspi_use_phy(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) +{ + if (!f_pdata->use_phy) + return false; + + if (op->data.nbytes < 16) + return false; + + return cqspi_phy_op_eligible(op); +} + static void cqspi_rx_dma_callback(void *param) { struct cqspi_st *cqspi = param; @@ -896,8 +1794,8 @@ complete(&cqspi->rx_dma_complete); } -static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, - u_char *buf, loff_t from, size_t len) +static int cqspi_direct_read_dma(struct cqspi_flash_pdata *f_pdata, + u_char *buf, loff_t from, size_t len) { struct cqspi_st *cqspi = f_pdata->cqspi; struct device *dev = &cqspi->pdev->dev; @@ -909,11 +1807,6 @@ dma_addr_t dma_dst; struct device *ddev; - if (!cqspi->rx_chan || !virt_addr_valid(buf)) { - memcpy_fromio(buf, cqspi->ahb_base + from, len); - return 0; - } - ddev = cqspi->rx_chan->device->dev; dma_dst = dma_map_single(ddev, buf, len, DMA_FROM_DEVICE); if (dma_mapping_error(ddev, dma_dst)) { @@ -942,7 +1835,7 @@ dma_async_issue_pending(cqspi->rx_chan); if (!wait_for_completion_timeout(&cqspi->rx_dma_complete, - msecs_to_jiffies(len))) { + msecs_to_jiffies(max_t(size_t, len, 500)))) { dmaengine_terminate_sync(cqspi->rx_chan); dev_err(dev, "DMA wait_for_completion_timeout\n"); ret = -ETIMEDOUT; @@ -955,6 +1848,104 @@ return ret; } +static void cqspi_memcpy_fromio(const struct spi_mem_op *op, void *to, + const void __iomem *from, size_t count) +{ + if (op->data.buswidth == 8 && op->data.dtr) { + /* + * 8D-8D-8D ops with odd length should be rejected by + * supports_op() so no need to worry about that. + */ + while (count && !IS_ALIGNED((unsigned long)from, 4)) { + *(u16 *)to = __raw_readw(from); + from += 2; + to += 2; + count -= 2; + } + + /* + * The controller can work with both 32-bit and 64-bit + * platforms. 32-bit platforms won't have a readq. So use a + * readl instead. + */ + while (count >= 4) { + *(u32 *)to = __raw_readl(from); + from += 4; + to += 4; + count -= 4; + } + + while (count) { + *(u16 *)to = __raw_readw(from); + from += 2; + to += 2; + count -= 2; + } + + return; + } + + memcpy_fromio(to, from, count); +} + +static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + loff_t from = op->addr.val; + loff_t from_aligned, to_aligned; + size_t len = op->data.nbytes; + size_t len_aligned; + u_char *buf = op->data.buf.in; + int ret; + + if (!cqspi->rx_chan || !virt_addr_valid(buf)) { + cqspi_memcpy_fromio(op, buf, cqspi->ahb_base + from, len); + return 0; + } + + if (!cqspi_use_phy(f_pdata, op)) + return cqspi_direct_read_dma(f_pdata, buf, from, len); + + /* + * PHY reads must be 16-byte aligned, and they must be a multiple of 16 + * bytes. + */ + from_aligned = (from + 0xF) & ~0xF; + to_aligned = (from + len) & ~0xF; + len_aligned = to_aligned - from_aligned; + + /* Read the unaligned part at the start. */ + if (from != from_aligned) { + ret = cqspi_direct_read_dma(f_pdata, buf, from, + from_aligned - from); + if (ret) + return ret; + buf += from_aligned - from; + } + + if (len_aligned) { + cqspi_phy_enable(f_pdata, true); + ret = cqspi_direct_read_dma(f_pdata, buf, from_aligned, + len_aligned); + cqspi_phy_enable(f_pdata, false); + if (ret) + return ret; + buf += len_aligned; + } + + /* Now read the remaining part, if any. */ + if (to_aligned != (from + len)) { + ret = cqspi_direct_read_dma(f_pdata, buf, to_aligned, + (from + len) - to_aligned); + if (ret) + return ret; + buf += (from + len) - to_aligned; + } + + return 0; +} + static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { @@ -973,7 +1964,7 @@ return ret; if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size)) - return cqspi_direct_read_execute(f_pdata, buf, from, len); + return cqspi_direct_read_execute(f_pdata, op); return cqspi_indirect_read_execute(f_pdata, buf, from, len); } @@ -1010,6 +2001,53 @@ return ret; } +static bool cqspi_supports_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + bool all_true, all_false; + + all_true = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && + op->data.dtr; + all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr && + !op->data.dtr; + + /* Mixed DTR modes not supported. */ + if (!(all_true || all_false)) + return false; + + if (all_true) + return spi_mem_dtr_supports_op(mem, op); + else + return spi_mem_default_supports_op(mem, op); +} + +static void cqspi_mem_do_calibration(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct cqspi_st *cqspi = spi_master_get_devdata(mem->spi->master); + struct cqspi_flash_pdata *f_pdata; + struct device *dev = &cqspi->pdev->dev; + int ret; + + f_pdata = &cqspi->f_pdata[mem->spi->chip_select]; + + /* Check if the op is eligible for PHY mode operation. */ + if (!cqspi_phy_op_eligible(op)) + return; + + f_pdata->phy_read_op = *op; + + ret = cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + dev_dbg(dev, "Pattern not found. Skipping calibration.\n"); + return; + } + + ret = cqspi_phy_calibrate(f_pdata, mem); + if (ret) + dev_info(&cqspi->pdev->dev, "PHY calibration failed: %d\n", ret); +} + static int cqspi_of_get_flash_pdata(struct platform_device *pdev, struct cqspi_flash_pdata *f_pdata, struct device_node *np) @@ -1044,6 +2082,12 @@ return -ENXIO; } + if (of_property_read_u32(np, "cdns,phy-tx-start", &f_pdata->phy_tx_start)) + f_pdata->phy_tx_start = 16; + + if (of_property_read_u32(np, "cdns,phy-tx-end", &f_pdata->phy_tx_end)) + f_pdata->phy_tx_end = 48; + return 0; } @@ -1138,6 +2182,8 @@ static const struct spi_controller_mem_ops cqspi_mem_ops = { .exec_op = cqspi_exec_mem_op, .get_name = cqspi_get_name, + .supports_op = cqspi_supports_mem_op, + .do_calibration = cqspi_mem_do_calibration, }; static int cqspi_setup_flash(struct cqspi_st *cqspi) @@ -1280,13 +2326,14 @@ reset_control_deassert(rstc_ocp); cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); + master->max_speed_hz = cqspi->master_ref_clk_hz; ddata = of_device_get_match_data(dev); if (ddata) { if (ddata->quirks & CQSPI_NEEDS_WR_DELAY) - cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC, + cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, cqspi->master_ref_clk_hz); if (ddata->hwcaps_mask & CQSPI_SUPPORTS_OCTAL) - master->mode_bits |= SPI_RX_OCTAL; + master->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL; if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) cqspi->use_direct_mode = true; } @@ -1428,3 +2475,4 @@ MODULE_AUTHOR("Graham Moore "); MODULE_AUTHOR("Vadivel Murugan R "); MODULE_AUTHOR("Vignesh Raghavendra "); +MODULE_AUTHOR("Pratyush Yadav "); diff -Naur --no-dereference a/drivers/spi/spidev.c b/drivers/spi/spidev.c --- a/drivers/spi/spidev.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/spi/spidev.c 2022-01-06 12:45:53.826318157 -0500 @@ -682,6 +682,8 @@ { .compatible = "lwn,bk4" }, { .compatible = "dh,dhcom-board" }, { .compatible = "menlo,m53cpld" }, + { .compatible = "cisco,spi-petra" }, + { .compatible = "micron,spi-authenta" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); @@ -737,9 +739,9 @@ * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ - WARN(spi->dev.of_node && - of_device_is_compatible(spi->dev.of_node, "spidev"), - "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node); +// WARN(spi->dev.of_node && +// of_device_is_compatible(spi->dev.of_node, "spidev"), +// "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node); spidev_probe_acpi(spi); diff -Naur --no-dereference a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c --- a/drivers/spi/spi-mem.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/spi/spi-mem.c 2022-01-06 12:45:53.826318157 -0500 @@ -137,8 +137,8 @@ return -ENOTSUPP; } -bool spi_mem_default_supports_op(struct spi_mem *mem, - const struct spi_mem_op *op) +static bool spi_mem_check_buswidth(struct spi_mem *mem, + const struct spi_mem_op *op) { if (spi_check_buswidth_req(mem, op->cmd.buswidth, true)) return false; @@ -156,13 +156,39 @@ op->data.dir == SPI_MEM_DATA_OUT)) return false; + return true; +} + +bool spi_mem_dtr_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (op->cmd.buswidth == 8 && op->cmd.nbytes % 2) + return false; + + if (op->addr.nbytes && op->addr.buswidth == 8 && op->addr.nbytes % 2) + return false; + + if (op->dummy.nbytes && op->dummy.buswidth == 8 && op->dummy.nbytes % 2) + return false; + + if (op->data.dir != SPI_MEM_NO_DATA && + op->dummy.buswidth == 8 && op->data.nbytes % 2) + return false; + + return spi_mem_check_buswidth(mem, op); +} +EXPORT_SYMBOL_GPL(spi_mem_dtr_supports_op); + +bool spi_mem_default_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) return false; if (op->cmd.nbytes != 1) return false; - return true; + return spi_mem_check_buswidth(mem, op); } EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); @@ -447,6 +473,18 @@ } EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); +int spi_mem_do_calibration(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct spi_controller *ctlr = mem->spi->controller; + + if (!ctlr->mem_ops || !ctlr->mem_ops->do_calibration) + return -EOPNOTSUPP; + + ctlr->mem_ops->do_calibration(mem, op); + return 0; +} +EXPORT_SYMBOL_GPL(spi_mem_do_calibration); + static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, void *buf) { diff -Naur --no-dereference a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c --- a/drivers/staging/fbtft/fb_ssd1306.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/fbtft/fb_ssd1306.c 2022-01-06 12:45:53.826318157 -0500 @@ -55,6 +55,9 @@ write_reg(par, 0x3F); else if (par->info->var.yres == 48) write_reg(par, 0x2F); + else if (par->info->var.yres == 39) + /* https://libstock.mikroe.com/projects/download/1111/2577/1411057038_oled_b_click___e_mikroc_arm.zip */ + write_reg(par, 0x27); else write_reg(par, 0x1F); @@ -76,19 +79,27 @@ write_reg(par, 0x01); /* Set Segment Re-map */ - /* column address 127 is mapped to SEG0 */ - write_reg(par, 0xA0 | 0x1); + if (par->info->var.yres == 39) + /* no segment re-map */ + write_reg(par, 0xA0 | 0x0); + else + /* column address 127 is mapped to SEG0 */ + write_reg(par, 0xA0 | 0x1); /* Set COM Output Scan Direction */ - /* remapped mode. Scan from COM[N-1] to COM0 */ - write_reg(par, 0xC8); + if (par->info->var.yres == 39) + /* no columnt re-map mode. Scan from COM0 to COM[N-1] */ + write_reg(par, 0xC0); + else + /* remapped mode. Scan from COM[N-1] to COM0 */ + write_reg(par, 0xC8); /* Set COM Pins Hardware Configuration */ write_reg(par, 0xDA); if (par->info->var.yres == 64) /* A[4]=1b, Alternative COM pin configuration */ write_reg(par, 0x12); - else if (par->info->var.yres == 48) + else if (par->info->var.yres == 48 || par->info->var.yres == 39) /* A[4]=1b, Alternative COM pin configuration */ write_reg(par, 0x12); else @@ -97,12 +108,18 @@ /* Set Pre-charge Period */ write_reg(par, 0xD9); - write_reg(par, 0xF1); + if (par->info->var.yres == 39) + write_reg(par, 0x25); + else + write_reg(par, 0xF1); /* Set VCOMH Deselect Level */ write_reg(par, 0xDB); - /* according to the datasheet, this value is out of bounds */ - write_reg(par, 0x40); + if (par->info->var.yres == 39) + write_reg(par, 0x20); + else + /* according to the datasheet, this value is out of bounds */ + write_reg(par, 0x40); /* Entire Display ON */ /* Resume to RAM content display. Output follows RAM content */ @@ -133,6 +150,20 @@ write_reg(par, 0x5); } +static void set_addr_win_96x39(struct fbtft_par *par) +{ + /* Set Page Address */ + write_reg(par, 0xB0); + /* Set Column Address */ + write_reg(par, 0x21); + write_reg(par, 0x00); + write_reg(par, 0x5F); + /* Set Page Address Range */ + write_reg(par, 0x22); + write_reg(par, 0x0); + write_reg(par, 0x4); +} + static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) { /* Set Lower Column Start Address for Page Addressing Mode */ @@ -144,6 +175,8 @@ if (par->info->var.xres == 64 && par->info->var.yres == 48) set_addr_win_64x48(par); + else if (par->info->var.xres == 96 && par->info->var.yres == 39) + set_addr_win_96x39(par); } static int blank(struct fbtft_par *par, bool on) @@ -188,11 +221,19 @@ *buf |= BIT(i); buf++; } + if (yres % 8) { + *buf = 0x00; + for (i = 0; i < (yres - (y * 8)); i++) + if (vmem16[(y * 8 + i) * xres + x]) + *buf |= BIT(i); + buf++; + y++; + } } /* Write data */ gpiod_set_value(par->gpio.dc, 1); - ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8); + ret = par->fbtftops.write(par, par->txbuf.buf, xres * (yres / 8 + (yres % 8 != 0))); if (ret < 0) dev_err(par->info->device, "write failed and returned: %d\n", ret); diff -Naur --no-dereference a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c --- a/drivers/staging/greybus/audio_codec.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/audio_codec.c 2022-01-06 12:45:53.826318157 -0500 @@ -71,15 +71,13 @@ i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_register_cport(data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); if (ret) { - dev_err_ratelimited(module->dev, - "reg_cport failed:%d\n", ret); + dev_err_ratelimited(module->dev, "reg_cport failed:%d\n", ret); return ret; } - data->state[SNDRV_PCM_STREAM_PLAYBACK] = - GBAUDIO_CODEC_STARTUP; + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_STARTUP; dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); } @@ -93,12 +91,10 @@ ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); if (ret) { - dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", - ret); + dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", ret); return ret; } - data->state[SNDRV_PCM_STREAM_PLAYBACK] = - GBAUDIO_CODEC_HWPARAMS; + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); } @@ -113,15 +109,13 @@ ret); return ret; } - ret = gb_audio_gb_activate_tx(module->mgmt_connection, - data_cport); + ret = gb_audio_gb_activate_tx(module->mgmt_connection, data_cport); if (ret) { dev_err_ratelimited(module->dev, "activate_tx failed:%d\n", ret); return ret; } - data->state[SNDRV_PCM_STREAM_PLAYBACK] = - GBAUDIO_CODEC_PREPARE; + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_PREPARE; dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); } @@ -153,25 +147,22 @@ return ret; } dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); - data->state[SNDRV_PCM_STREAM_PLAYBACK] = - GBAUDIO_CODEC_HWPARAMS; + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; } if (module_state > GBAUDIO_CODEC_SHUTDOWN) { i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_unregister_cport(data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); if (ret) { dev_err_ratelimited(module->dev, - "unregister_cport failed:%d\n", - ret); + "unregister_cport failed:%d\n", ret); return ret; } dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); - data->state[SNDRV_PCM_STREAM_PLAYBACK] = - GBAUDIO_CODEC_SHUTDOWN; + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_SHUTDOWN; } return 0; @@ -206,15 +197,13 @@ i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_register_cport(data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); if (ret) { - dev_err_ratelimited(module->dev, - "reg_cport failed:%d\n", ret); + dev_err_ratelimited(module->dev, "reg_cport failed:%d\n", ret); return ret; } - data->state[SNDRV_PCM_STREAM_CAPTURE] = - GBAUDIO_CODEC_STARTUP; + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_STARTUP; dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); } @@ -228,12 +217,10 @@ ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); if (ret) { - dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", - ret); + dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", ret); return ret; } - data->state[SNDRV_PCM_STREAM_CAPTURE] = - GBAUDIO_CODEC_HWPARAMS; + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); } @@ -255,8 +242,7 @@ "activate_rx failed:%d\n", ret); return ret; } - data->state[SNDRV_PCM_STREAM_CAPTURE] = - GBAUDIO_CODEC_PREPARE; + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_PREPARE; dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); } @@ -288,25 +274,22 @@ return ret; } dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); - data->state[SNDRV_PCM_STREAM_CAPTURE] = - GBAUDIO_CODEC_HWPARAMS; + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; } if (module_state > GBAUDIO_CODEC_SHUTDOWN) { i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_unregister_cport(data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); if (ret) { dev_err_ratelimited(module->dev, - "unregister_cport failed:%d\n", - ret); + "unregister_cport failed:%d\n", ret); return ret; } dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); - data->state[SNDRV_PCM_STREAM_CAPTURE] = - GBAUDIO_CODEC_SHUTDOWN; + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_SHUTDOWN; } return 0; @@ -330,8 +313,7 @@ /* parse dai_id from AIF widget's stream_name */ ret = sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir); if (ret < 3) { - dev_err(codec->dev, "Error while parsing dai_id for %s\n", - w->name); + dev_err(codec->dev, "Error while parsing dai_id for %s\n", w->name); return -EINVAL; } @@ -449,8 +431,7 @@ rate = GB_AUDIO_PCM_RATE_48000; if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) { - dev_err(dai->dev, "Invalid format:%d\n", - params_format(hwparams)); + dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); mutex_unlock(&codec->lock); return -EINVAL; } @@ -558,19 +539,16 @@ switch (substream->stream) { case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, - 192); + ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, 192); break; case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, - 192); + ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, 192); break; } if (ret) { gb_pm_runtime_put_noidle(bundle); mutex_unlock(&codec->lock); - dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n", - ret); + dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n", ret); return ret; } @@ -635,30 +613,24 @@ } if (!mute && !stream) {/* start playback */ - ret = gb_audio_apbridgea_prepare_tx(data->connection, - 0); + ret = gb_audio_apbridgea_prepare_tx(data->connection, 0); if (!ret) - ret = gb_audio_apbridgea_start_tx(data->connection, - 0, 0); + ret = gb_audio_apbridgea_start_tx(data->connection, 0, 0); params->state = GBAUDIO_CODEC_START; } else if (!mute && stream) {/* start capture */ - ret = gb_audio_apbridgea_prepare_rx(data->connection, - 0); + ret = gb_audio_apbridgea_prepare_rx(data->connection, 0); if (!ret) - ret = gb_audio_apbridgea_start_rx(data->connection, - 0); + ret = gb_audio_apbridgea_start_rx(data->connection, 0); params->state = GBAUDIO_CODEC_START; } else if (mute && !stream) {/* stop playback */ ret = gb_audio_apbridgea_stop_tx(data->connection, 0); if (!ret) - ret = gb_audio_apbridgea_shutdown_tx(data->connection, - 0); + ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); params->state = GBAUDIO_CODEC_STOP; } else if (mute && stream) {/* stop capture */ ret = gb_audio_apbridgea_stop_rx(data->connection, 0); if (!ret) - ret = gb_audio_apbridgea_shutdown_rx(data->connection, - 0); + ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); params->state = GBAUDIO_CODEC_STOP; } else { ret = -EINVAL; @@ -868,8 +840,7 @@ /* card already instantiated, create widgets here only */ if (comp->card->instantiated) { - gbaudio_dapm_link_component_dai_widgets(comp->card, - &comp->dapm); + gbaudio_dapm_link_component_dai_widgets(comp->card, &comp->dapm); #ifdef CONFIG_SND_JACK /* * register jack devices for this module @@ -904,8 +875,7 @@ ret = gb_audio_apbridgea_stop_tx(data->connection, 0); if (ret) return; - ret = gb_audio_apbridgea_shutdown_tx(data->connection, - 0); + ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); if (ret) return; } @@ -926,8 +896,7 @@ ret = gb_audio_apbridgea_stop_rx(data->connection, 0); if (ret) return; - ret = gb_audio_apbridgea_shutdown_rx(data->connection, - 0); + ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); if (ret) return; } diff -Naur --no-dereference a/drivers/staging/greybus/audio_helper.c b/drivers/staging/greybus/audio_helper.c --- a/drivers/staging/greybus/audio_helper.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/audio_helper.c 2022-01-06 12:45:53.826318157 -0500 @@ -166,7 +166,7 @@ snprintf(id.name, sizeof(id.name), "%s %s", prefix, control->name); else - strlcpy(id.name, control->name, sizeof(id.name)); + strscpy(id.name, control->name, sizeof(id.name)); id.numid = 0; id.iface = control->iface; id.device = control->device; @@ -192,11 +192,7 @@ unsigned int num_controls) { struct snd_card *card = component->card->snd_card; - int err; - down_write(&card->controls_rwsem); - err = gbaudio_remove_controls(card, component->dev, controls, - num_controls, component->name_prefix); - up_write(&card->controls_rwsem); - return err; + return gbaudio_remove_controls(card, component->dev, controls, + num_controls, component->name_prefix); } diff -Naur --no-dereference a/drivers/staging/greybus/audio_manager_module.c b/drivers/staging/greybus/audio_manager_module.c --- a/drivers/staging/greybus/audio_manager_module.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/audio_manager_module.c 2022-01-06 12:45:53.826318157 -0500 @@ -213,8 +213,7 @@ err = kobject_init_and_add(&m->kobj, &gb_audio_module_type, NULL, "%d", id); if (err) { - pr_err("failed initializing kobject for audio module #%d\n", - id); + pr_err("failed initializing kobject for audio module #%d\n", id); kobject_put(&m->kobj); return err; } diff -Naur --no-dereference a/drivers/staging/greybus/audio_manager_sysfs.c b/drivers/staging/greybus/audio_manager_sysfs.c --- a/drivers/staging/greybus/audio_manager_sysfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/audio_manager_sysfs.c 2022-01-06 12:45:53.826318157 -0500 @@ -18,8 +18,8 @@ struct gb_audio_manager_module_descriptor desc = { {0} }; int num = sscanf(buf, - "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s " - "vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X", + "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF + "s vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X", desc.name, &desc.vid, &desc.pid, &desc.intf_id, &desc.ip_devices, &desc.op_devices); diff -Naur --no-dereference a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c --- a/drivers/staging/greybus/audio_module.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/audio_module.c 2022-01-06 12:45:53.826318157 -0500 @@ -175,8 +175,8 @@ } static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule, - struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle) + struct greybus_descriptor_cport *cport_desc, + struct gb_bundle *bundle) { struct gb_connection *connection; @@ -199,8 +199,8 @@ } static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, - struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle) + struct greybus_descriptor_cport *cport_desc, + struct gb_bundle *bundle) { struct gb_connection *connection; struct gbaudio_data_connection *dai; @@ -342,7 +342,7 @@ /* inform above layer for uevent */ dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); /* prepare for the audio manager */ - strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN); + strscpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN); desc.vid = 2; /* todo */ desc.pid = 3; /* todo */ desc.intf_id = gbmodule->dev_id; diff -Naur --no-dereference a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c --- a/drivers/staging/greybus/audio_topology.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/audio_topology.c 2022-01-06 12:45:53.826318157 -0500 @@ -200,7 +200,7 @@ return -EINVAL; name = gbaudio_map_controlid(module, data->ctl_id, uinfo->value.enumerated.item); - strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); + strscpy(uinfo->value.enumerated.name, name, NAME_SIZE); break; default: dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n", @@ -1047,7 +1047,7 @@ } /* Prefix dev_id to widget control_name */ - strlcpy(temp_name, w->name, NAME_SIZE); + strscpy(temp_name, w->name, NAME_SIZE); snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); switch (w->type) { @@ -1169,7 +1169,7 @@ } control->id = curr->id; /* Prefix dev_id to widget_name */ - strlcpy(temp_name, curr->name, NAME_SIZE); + strscpy(temp_name, curr->name, NAME_SIZE); snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); control->name = curr->name; diff -Naur --no-dereference a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c --- a/drivers/staging/greybus/hid.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/hid.c 2022-01-06 12:45:53.826318157 -0500 @@ -221,8 +221,8 @@ } static int __gb_hid_get_raw_report(struct hid_device *hid, - unsigned char report_number, __u8 *buf, size_t count, - unsigned char report_type) + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) { struct gb_hid *ghid = hid->driver_data; int ret; @@ -254,7 +254,7 @@ ret = gb_hid_set_report(ghid, report_type, report_id, buf, len); if (report_id && ret >= 0) - ret++; /* add report_id to the number of transfered bytes */ + ret++; /* add report_id to the number of transferred bytes */ return 0; } diff -Naur --no-dereference a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c --- a/drivers/staging/greybus/light.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/light.c 2022-01-06 12:45:53.826318157 -0500 @@ -290,8 +290,7 @@ channel->attrs = kcalloc(size + 1, sizeof(*channel->attrs), GFP_KERNEL); if (!channel->attrs) return -ENOMEM; - channel->attr_group = kcalloc(1, sizeof(*channel->attr_group), - GFP_KERNEL); + channel->attr_group = kzalloc(sizeof(*channel->attr_group), GFP_KERNEL); if (!channel->attr_group) return -ENOMEM; channel->attr_groups = kcalloc(2, sizeof(*channel->attr_groups), diff -Naur --no-dereference a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c --- a/drivers/staging/greybus/power_supply.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/power_supply.c 2022-01-06 12:45:53.826318157 -0500 @@ -449,7 +449,7 @@ if (!strlen(init_name)) init_name = "gb_power_supply"; - strlcpy(name, init_name, len); + strscpy(name, init_name, len); while ((ret < len) && (psy = power_supply_get_by_name(name))) { power_supply_put(psy); diff -Naur --no-dereference a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c --- a/drivers/staging/greybus/spilib.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/spilib.c 2022-01-06 12:45:53.826318157 -0500 @@ -455,10 +455,10 @@ dev_type = response.device_type; if (dev_type == GB_SPI_SPI_DEV) - strlcpy(spi_board.modalias, "spidev", + strscpy(spi_board.modalias, "spidev", sizeof(spi_board.modalias)); else if (dev_type == GB_SPI_SPI_NOR) - strlcpy(spi_board.modalias, "spi-nor", + strscpy(spi_board.modalias, "spi-nor", sizeof(spi_board.modalias)); else if (dev_type == GB_SPI_SPI_MODALIAS) memcpy(spi_board.modalias, response.name, diff -Naur --no-dereference a/drivers/staging/greybus/TODO b/drivers/staging/greybus/TODO --- a/drivers/staging/greybus/TODO 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/TODO 2022-01-06 12:45:53.826318157 -0500 @@ -1,3 +1,5 @@ * Convert all uses of the old GPIO API from to the GPIO descriptor API in and look up GPIO lines from device tree or ACPI. +* Make pwm.c use the struct pwm_ops::apply instead of ::config, ::set_polarity, + ::enable and ::disable. diff -Naur --no-dereference a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c --- a/drivers/staging/greybus/uart.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/greybus/uart.c 2022-01-06 12:45:53.826318157 -0500 @@ -778,17 +778,6 @@ gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); } -static void gb_tty_port_destruct(struct tty_port *port) -{ - struct gb_tty *gb_tty = container_of(port, struct gb_tty, port); - - if (gb_tty->minor != GB_NUM_MINORS) - release_minor(gb_tty); - kfifo_free(&gb_tty->write_fifo); - kfree(gb_tty->buffer); - kfree(gb_tty); -} - static const struct tty_operations gb_ops = { .install = gb_tty_install, .open = gb_tty_open, @@ -814,7 +803,6 @@ .dtr_rts = gb_tty_dtr_rts, .activate = gb_tty_port_activate, .shutdown = gb_tty_port_shutdown, - .destruct = gb_tty_port_destruct, }; static int gb_uart_probe(struct gbphy_device *gbphy_dev, @@ -827,11 +815,17 @@ int retval; int minor; + gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); + if (!gb_tty) + return -ENOMEM; + connection = gb_connection_create(gbphy_dev->bundle, le16_to_cpu(gbphy_dev->cport_desc->id), gb_uart_request_handler); - if (IS_ERR(connection)) - return PTR_ERR(connection); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto exit_tty_free; + } max_payload = gb_operation_get_payload_size_max(connection); if (max_payload < sizeof(struct gb_uart_send_data_request)) { @@ -839,23 +833,13 @@ goto exit_connection_destroy; } - gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); - if (!gb_tty) { - retval = -ENOMEM; - goto exit_connection_destroy; - } - - tty_port_init(&gb_tty->port); - gb_tty->port.ops = &gb_port_ops; - gb_tty->minor = GB_NUM_MINORS; - gb_tty->buffer_payload_max = max_payload - sizeof(struct gb_uart_send_data_request); gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); if (!gb_tty->buffer) { retval = -ENOMEM; - goto exit_put_port; + goto exit_connection_destroy; } INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); @@ -863,7 +847,7 @@ retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, GFP_KERNEL); if (retval) - goto exit_put_port; + goto exit_buf_free; gb_tty->credits = GB_UART_FIRMWARE_CREDITS; init_completion(&gb_tty->credits_complete); @@ -877,7 +861,7 @@ } else { retval = minor; } - goto exit_put_port; + goto exit_kfifo_free; } gb_tty->minor = minor; @@ -886,6 +870,9 @@ init_waitqueue_head(&gb_tty->wioctl); mutex_init(&gb_tty->mutex); + tty_port_init(&gb_tty->port); + gb_tty->port.ops = &gb_port_ops; + gb_tty->connection = connection; gb_tty->gbphy_dev = gbphy_dev; gb_connection_set_data(connection, gb_tty); @@ -893,7 +880,7 @@ retval = gb_connection_enable_tx(connection); if (retval) - goto exit_put_port; + goto exit_release_minor; send_control(gb_tty, gb_tty->ctrlout); @@ -920,10 +907,16 @@ exit_connection_disable: gb_connection_disable(connection); -exit_put_port: - tty_port_put(&gb_tty->port); +exit_release_minor: + release_minor(gb_tty); +exit_kfifo_free: + kfifo_free(&gb_tty->write_fifo); +exit_buf_free: + kfree(gb_tty->buffer); exit_connection_destroy: gb_connection_destroy(connection); +exit_tty_free: + kfree(gb_tty); return retval; } @@ -954,10 +947,15 @@ gb_connection_disable_rx(connection); tty_unregister_device(gb_tty_driver, gb_tty->minor); + /* FIXME - free transmit / receive buffers */ + gb_connection_disable(connection); + tty_port_destroy(&gb_tty->port); gb_connection_destroy(connection); - - tty_port_put(&gb_tty->port); + release_minor(gb_tty); + kfifo_free(&gb_tty->write_fifo); + kfree(gb_tty->buffer); + kfree(gb_tty); } static int gb_tty_init(void) diff -Naur --no-dereference a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c 2022-01-06 12:45:53.826318157 -0500 @@ -972,7 +972,7 @@ } static int gc0310_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -1008,7 +1008,7 @@ fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; mutex_unlock(&dev->input_lock); return 0; } @@ -1042,7 +1042,7 @@ } static int gc0310_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -1221,7 +1221,7 @@ } static int gc0310_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= MAX_FMTS) @@ -1232,7 +1232,7 @@ } static int gc0310_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; diff -Naur --no-dereference a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c 2022-01-06 12:45:53.826318157 -0500 @@ -767,7 +767,7 @@ } static int gc2235_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -796,7 +796,7 @@ } fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; mutex_unlock(&dev->input_lock); return 0; } @@ -825,7 +825,7 @@ } static int gc2235_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -963,7 +963,7 @@ } static int gc2235_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= MAX_FMTS) @@ -974,7 +974,7 @@ } static int gc2235_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; diff -Naur --no-dereference a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c 2022-01-06 12:45:53.826318157 -0500 @@ -801,7 +801,7 @@ } static int mt9m114_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -822,7 +822,7 @@ } static int mt9m114_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -846,7 +846,7 @@ mt9m114_try_res(&width, &height); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; return 0; } res_index = mt9m114_to_res(width, height); @@ -1158,7 +1158,7 @@ * This function is for touch exposure feature. */ static int mt9m114_s_exposure_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1722,7 +1722,7 @@ } static int mt9m114_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index) @@ -1733,7 +1733,7 @@ } static int mt9m114_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { unsigned int index = fse->index; diff -Naur --no-dereference a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c 2022-01-06 12:45:53.826318157 -0500 @@ -911,7 +911,7 @@ } static int ov2680_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -948,7 +948,7 @@ } fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; mutex_unlock(&dev->input_lock); return 0; } @@ -997,7 +997,7 @@ } static int ov2680_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -1155,7 +1155,7 @@ } static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= MAX_FMTS) @@ -1166,7 +1166,7 @@ } static int ov2680_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; diff -Naur --no-dereference a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c 2022-01-06 12:45:53.826318157 -0500 @@ -876,7 +876,7 @@ } static int ov2722_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -906,7 +906,7 @@ } fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; mutex_unlock(&dev->input_lock); return 0; } @@ -961,7 +961,7 @@ } static int ov2722_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -1104,7 +1104,7 @@ } static int ov2722_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= MAX_FMTS) @@ -1115,7 +1115,7 @@ } static int ov2722_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; diff -Naur --no-dereference a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c --- a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c 2022-01-06 12:45:53.826318157 -0500 @@ -1577,7 +1577,7 @@ } static int ov5693_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -1608,7 +1608,7 @@ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; mutex_unlock(&dev->input_lock); return 0; } @@ -1676,7 +1676,7 @@ } static int ov5693_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -1825,7 +1825,7 @@ } static int ov5693_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= MAX_FMTS) @@ -1836,7 +1836,7 @@ } static int ov5693_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c 2022-01-06 12:45:53.826318157 -0500 @@ -4843,6 +4843,9 @@ struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -4878,7 +4881,7 @@ snr_mbus_fmt->width, snr_mbus_fmt->height); ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - pad, set_fmt, &pad_cfg, &format); + pad, set_fmt, &pad_state, &format); if (ret) return ret; @@ -5258,11 +5261,11 @@ atomisp_output_fmts[] in atomisp_v4l2.c */ vf_ffmt.code = V4L2_MBUS_FMT_CUSTOM_YUV420; - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SOURCE_VF, V4L2_SEL_TGT_COMPOSE, 0, &vf_size); - atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, + atomisp_subdev_set_ffmt(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SOURCE_VF, &vf_ffmt); asd->video_out_vf.sh_fmt = IA_CSS_FRAME_FORMAT_NV12; @@ -5514,6 +5517,9 @@ struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; const struct atomisp_format_bridge *format; struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; struct v4l2_subdev_format vformat = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -5552,7 +5558,7 @@ source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) { vformat.which = V4L2_SUBDEV_FORMAT_TRY; ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - pad, set_fmt, &pad_cfg, &vformat); + pad, set_fmt, &pad_state, &vformat); if (ret) return ret; if (ffmt->width < req_ffmt->width || @@ -5590,7 +5596,7 @@ asd->params.video_dis_en = false; } - atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, + atomisp_subdev_set_ffmt(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, ffmt); @@ -5669,7 +5675,7 @@ } atomisp_subdev_set_selection( - &asd->subdev, fh.pad, + &asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, source_pad, V4L2_SEL_TGT_COMPOSE, 0, &r); @@ -5800,7 +5806,7 @@ ATOMISP_SUBDEV_PAD_SINK); isp_source_fmt.code = format_bridge->mbus_code; - atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, + atomisp_subdev_set_ffmt(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, source_pad, &isp_source_fmt); @@ -5919,13 +5925,13 @@ isp_sink_crop.height = f->fmt.pix.height; } - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP, V4L2_SEL_FLAG_KEEP_CONFIG, &isp_sink_crop); - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, source_pad, V4L2_SEL_TGT_COMPOSE, 0, &isp_sink_crop); @@ -5944,7 +5950,7 @@ f->fmt.pix.height); } - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, source_pad, V4L2_SEL_TGT_COMPOSE, 0, @@ -5978,14 +5984,14 @@ f->fmt.pix.width, ATOM_ISP_STEP_HEIGHT); } - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP, V4L2_SEL_FLAG_KEEP_CONFIG, &sink_crop); } - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, source_pad, V4L2_SEL_TGT_COMPOSE, 0, @@ -6076,7 +6082,8 @@ ffmt.height = f->fmt.pix.height; ffmt.code = format_bridge->mbus_code; - atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, V4L2_SUBDEV_FORMAT_ACTIVE, + atomisp_subdev_set_ffmt(&asd->subdev, fh.state, + V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, &ffmt); return 0; diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c 2022-01-06 12:45:53.826318157 -0500 @@ -25,13 +25,13 @@ static struct v4l2_mbus_framefmt *__csi2_get_format(struct atomisp_mipi_csi2_device * csi2, - struct - v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which, unsigned int pad) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, + pad); else return &csi2->formats[pad]; } @@ -44,7 +44,7 @@ * return -EINVAL or zero on success */ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { const struct atomisp_in_fmt_conv *ic = atomisp_in_fmt_conv; @@ -70,13 +70,13 @@ * return -EINVAL or zero on success */ static int csi2_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, cfg, fmt->which, fmt->pad); + format = __csi2_get_format(csi2, sd_state, fmt->which, fmt->pad); fmt->format = *format; @@ -84,12 +84,14 @@ } int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int which, uint16_t pad, struct v4l2_mbus_framefmt *ffmt) { struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *actual_ffmt = __csi2_get_format(csi2, cfg, which, pad); + struct v4l2_mbus_framefmt *actual_ffmt = __csi2_get_format(csi2, + sd_state, + which, pad); if (pad == CSI2_PAD_SINK) { const struct atomisp_in_fmt_conv *ic; @@ -110,12 +112,14 @@ tmp_ffmt = *ffmt = *actual_ffmt; - return atomisp_csi2_set_ffmt(sd, cfg, which, CSI2_PAD_SOURCE, + return atomisp_csi2_set_ffmt(sd, sd_state, which, + CSI2_PAD_SOURCE, &tmp_ffmt); } /* FIXME: DPCM decompression */ - *actual_ffmt = *ffmt = *__csi2_get_format(csi2, cfg, which, CSI2_PAD_SINK); + *actual_ffmt = *ffmt = *__csi2_get_format(csi2, sd_state, which, + CSI2_PAD_SINK); return 0; } @@ -129,10 +133,10 @@ * return -EINVAL or zero on success */ static int csi2_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - return atomisp_csi2_set_ffmt(sd, cfg, fmt->which, fmt->pad, + return atomisp_csi2_set_ffmt(sd, sd_state, fmt->which, fmt->pad, &fmt->format); } diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h 2022-01-06 12:45:53.826318157 -0500 @@ -44,7 +44,7 @@ }; int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int which, uint16_t pad, struct v4l2_mbus_framefmt *ffmt); int atomisp_mipi_csi2_init(struct atomisp_device *isp); diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_file.c b/drivers/staging/media/atomisp/pci/atomisp_file.c --- a/drivers/staging/media/atomisp/pci/atomisp_file.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_file.c 2022-01-06 12:45:53.826318157 -0500 @@ -80,7 +80,7 @@ } static int file_input_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -104,16 +104,16 @@ } static int file_input_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; if (format->pad) return -EINVAL; - file_input_get_fmt(sd, cfg, format); + file_input_get_fmt(sd, sd_state, format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; return 0; } @@ -130,7 +130,7 @@ } static int file_input_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /*to fake*/ @@ -138,7 +138,7 @@ } static int file_input_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { /*to fake*/ @@ -146,7 +146,7 @@ } static int file_input_enum_frame_ival(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c --- a/drivers/staging/media/atomisp/pci/atomisp_fops.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c 2022-01-06 12:45:53.826318157 -0500 @@ -963,7 +963,7 @@ if (!isp->sw_contex.file_input && asd->fmt_auto->val) { struct v4l2_mbus_framefmt isp_sink_fmt = { 0 }; - atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, + atomisp_subdev_set_ffmt(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, &isp_sink_fmt); } @@ -975,7 +975,7 @@ if (isp->sw_contex.file_input && asd->fmt_auto->val) { struct v4l2_mbus_framefmt isp_sink_fmt = { 0 }; - atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, + atomisp_subdev_set_ffmt(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, &isp_sink_fmt); } @@ -1016,7 +1016,7 @@ done: if (!acc_node) { - atomisp_subdev_set_selection(&asd->subdev, fh.pad, + atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, atomisp_subdev_source_pad(vdev), V4L2_SEL_TGT_COMPOSE, 0, diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c 2022-01-06 12:45:53.826318157 -0500 @@ -213,7 +213,7 @@ * return -EINVAL or zero on success */ static int isp_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(atomisp_in_fmt_conv) - 1) @@ -246,7 +246,7 @@ } struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, u32 which, uint32_t pad, uint32_t target) { @@ -255,9 +255,9 @@ if (which == V4L2_SUBDEV_FORMAT_TRY) { switch (target) { case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(sd, cfg, pad); + return v4l2_subdev_get_try_crop(sd, sd_state, pad); case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(sd, cfg, pad); + return v4l2_subdev_get_try_compose(sd, sd_state, pad); } } @@ -273,19 +273,20 @@ struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, uint32_t which, + struct v4l2_subdev_state *sd_state, uint32_t which, uint32_t pad) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, cfg, pad); + return v4l2_subdev_get_try_format(sd, sd_state, pad); return &isp_sd->fmt[pad].fmt; } static void isp_get_fmt_rect(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, uint32_t which, + struct v4l2_subdev_state *sd_state, + uint32_t which, struct v4l2_mbus_framefmt **ffmt, struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM], struct v4l2_rect *comp[ATOMISP_SUBDEV_PADS_NUM]) @@ -293,16 +294,16 @@ unsigned int i; for (i = 0; i < ATOMISP_SUBDEV_PADS_NUM; i++) { - ffmt[i] = atomisp_subdev_get_ffmt(sd, cfg, which, i); - crop[i] = atomisp_subdev_get_rect(sd, cfg, which, i, + ffmt[i] = atomisp_subdev_get_ffmt(sd, sd_state, which, i); + crop[i] = atomisp_subdev_get_rect(sd, sd_state, which, i, V4L2_SEL_TGT_CROP); - comp[i] = atomisp_subdev_get_rect(sd, cfg, which, i, + comp[i] = atomisp_subdev_get_rect(sd, sd_state, which, i, V4L2_SEL_TGT_COMPOSE); } } static void isp_subdev_propagate(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, u32 which, uint32_t pad, uint32_t target, uint32_t flags) { @@ -313,7 +314,7 @@ if (flags & V4L2_SEL_FLAG_KEEP_CONFIG) return; - isp_get_fmt_rect(sd, cfg, which, ffmt, crop, comp); + isp_get_fmt_rect(sd, sd_state, which, ffmt, crop, comp); switch (pad) { case ATOMISP_SUBDEV_PAD_SINK: { @@ -323,7 +324,7 @@ r.width = ffmt[pad]->width; r.height = ffmt[pad]->height; - atomisp_subdev_set_selection(sd, cfg, which, pad, + atomisp_subdev_set_selection(sd, sd_state, which, pad, target, flags, &r); break; } @@ -331,7 +332,7 @@ } static int isp_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct v4l2_rect *rec; @@ -340,7 +341,7 @@ if (rval) return rval; - rec = atomisp_subdev_get_rect(sd, cfg, sel->which, sel->pad, + rec = atomisp_subdev_get_rect(sd, sd_state, sel->which, sel->pad, sel->target); if (!rec) return -EINVAL; @@ -365,7 +366,7 @@ } int atomisp_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, u32 which, uint32_t pad, uint32_t target, u32 flags, struct v4l2_rect *r) { @@ -382,7 +383,7 @@ stream_id = atomisp_source_pad_to_stream_id(isp_sd, vdev_pad); - isp_get_fmt_rect(sd, cfg, which, ffmt, crop, comp); + isp_get_fmt_rect(sd, sd_state, which, ffmt, crop, comp); dev_dbg(isp->dev, "sel: pad %s tgt %s l %d t %d w %d h %d which %s f 0x%8.8x\n", @@ -450,7 +451,8 @@ struct v4l2_rect tmp = *crop[pad]; atomisp_subdev_set_selection( - sd, cfg, which, i, V4L2_SEL_TGT_COMPOSE, + sd, sd_state, which, i, + V4L2_SEL_TGT_COMPOSE, flags, &tmp); } } @@ -551,9 +553,9 @@ ffmt[pad]->height = comp[pad]->height; } - if (!atomisp_subdev_get_rect(sd, cfg, which, pad, target)) + if (!atomisp_subdev_get_rect(sd, sd_state, which, pad, target)) return -EINVAL; - *r = *atomisp_subdev_get_rect(sd, cfg, which, pad, target); + *r = *atomisp_subdev_get_rect(sd, sd_state, which, pad, target); dev_dbg(isp->dev, "sel actual: l %d t %d w %d h %d\n", r->left, r->top, r->width, r->height); @@ -562,7 +564,7 @@ } static int isp_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { int rval = isp_subdev_validate_rect(sd, sel->pad, sel->target); @@ -570,7 +572,8 @@ if (rval) return rval; - return atomisp_subdev_set_selection(sd, cfg, sel->which, sel->pad, + return atomisp_subdev_set_selection(sd, sd_state, sel->which, + sel->pad, sel->target, sel->flags, &sel->r); } @@ -609,13 +612,14 @@ } void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, uint32_t which, + struct v4l2_subdev_state *sd_state, + uint32_t which, u32 pad, struct v4l2_mbus_framefmt *ffmt) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); struct atomisp_device *isp = isp_sd->isp; struct v4l2_mbus_framefmt *__ffmt = - atomisp_subdev_get_ffmt(sd, cfg, which, pad); + atomisp_subdev_get_ffmt(sd, sd_state, which, pad); u16 vdev_pad = atomisp_subdev_source_pad(sd->devnode); enum atomisp_input_stream_id stream_id; @@ -640,7 +644,7 @@ *__ffmt = *ffmt; - isp_subdev_propagate(sd, cfg, which, pad, + isp_subdev_propagate(sd, sd_state, which, pad, V4L2_SEL_TGT_CROP, 0); if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { @@ -679,10 +683,11 @@ * to the format type. */ static int isp_subdev_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - fmt->format = *atomisp_subdev_get_ffmt(sd, cfg, fmt->which, fmt->pad); + fmt->format = *atomisp_subdev_get_ffmt(sd, sd_state, fmt->which, + fmt->pad); return 0; } @@ -698,10 +703,11 @@ * to the format type. */ static int isp_subdev_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - atomisp_subdev_set_ffmt(sd, cfg, fmt->which, fmt->pad, &fmt->format); + atomisp_subdev_set_ffmt(sd, sd_state, fmt->which, fmt->pad, + &fmt->format); return 0; } diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h 2022-01-06 12:45:53.826318157 -0500 @@ -437,19 +437,20 @@ /* Get pointer to appropriate format */ struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, uint32_t which, + struct v4l2_subdev_state *sd_state, uint32_t which, uint32_t pad); struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, u32 which, uint32_t pad, uint32_t target); int atomisp_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, u32 which, uint32_t pad, uint32_t target, u32 flags, struct v4l2_rect *r); /* Actually set the format */ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, uint32_t which, + struct v4l2_subdev_state *sd_state, + uint32_t which, u32 pad, struct v4l2_mbus_framefmt *ffmt); int atomisp_update_run_mode(struct atomisp_sub_device *asd); diff -Naur --no-dereference a/drivers/staging/media/atomisp/pci/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp_tpg.c --- a/drivers/staging/media/atomisp/pci/atomisp_tpg.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/atomisp/pci/atomisp_tpg.c 2022-01-06 12:45:53.826318157 -0500 @@ -29,7 +29,7 @@ } static int tpg_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { /*to fake*/ @@ -37,7 +37,7 @@ } static int tpg_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; @@ -47,7 +47,7 @@ /* only raw8 grbg is supported by TPG */ fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *fmt; + sd_state->pads->try_fmt = *fmt; return 0; } return 0; @@ -65,7 +65,7 @@ } static int tpg_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { /*to fake*/ @@ -73,7 +73,7 @@ } static int tpg_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { /*to fake*/ @@ -81,7 +81,7 @@ } static int tpg_enum_frame_ival(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { /*to fake*/ diff -Naur --no-dereference a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c --- a/drivers/staging/media/imx/imx6-mipi-csi2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c 2022-01-06 12:45:53.830318172 -0500 @@ -447,17 +447,17 @@ } static struct v4l2_mbus_framefmt * -__csi2_get_fmt(struct csi2_dev *csi2, struct v4l2_subdev_pad_config *cfg, +__csi2_get_fmt(struct csi2_dev *csi2, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->sd, cfg, pad); + return v4l2_subdev_get_try_format(&csi2->sd, sd_state, pad); else return &csi2->format_mbus; } static int csi2_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct csi2_dev *csi2 = sd_to_dev(sd); @@ -465,7 +465,7 @@ mutex_lock(&csi2->lock); - fmt = __csi2_get_fmt(csi2, cfg, sdformat->pad, sdformat->which); + fmt = __csi2_get_fmt(csi2, sd_state, sdformat->pad, sdformat->which); sdformat->format = *fmt; @@ -475,7 +475,7 @@ } static int csi2_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct csi2_dev *csi2 = sd_to_dev(sd); @@ -496,7 +496,7 @@ if (sdformat->pad != CSI2_SINK_PAD) sdformat->format = csi2->format_mbus; - fmt = __csi2_get_fmt(csi2, cfg, sdformat->pad, sdformat->which); + fmt = __csi2_get_fmt(csi2, sd_state, sdformat->pad, sdformat->which); *fmt = sdformat->format; out: diff -Naur --no-dereference a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c --- a/drivers/staging/media/imx/imx7-media-csi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx7-media-csi.c 2022-01-06 12:45:53.830318172 -0500 @@ -899,18 +899,18 @@ static struct v4l2_mbus_framefmt * imx7_csi_get_format(struct imx7_csi *csi, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi->sd, cfg, pad); + return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad); return &csi->format_mbus[pad]; } static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx7_csi *csi = v4l2_get_subdevdata(sd); @@ -919,7 +919,8 @@ mutex_lock(&csi->lock); - in_fmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SINK, code->which); + in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK, + code->which); switch (code->pad) { case IMX7_CSI_PAD_SINK: @@ -945,7 +946,7 @@ } static int imx7_csi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct imx7_csi *csi = v4l2_get_subdevdata(sd); @@ -954,7 +955,8 @@ mutex_lock(&csi->lock); - fmt = imx7_csi_get_format(csi, cfg, sdformat->pad, sdformat->which); + fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad, + sdformat->which); if (!fmt) { ret = -EINVAL; goto out_unlock; @@ -969,7 +971,7 @@ } static int imx7_csi_try_fmt(struct imx7_csi *csi, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat, const struct imx_media_pixfmt **cc) { @@ -977,7 +979,7 @@ struct v4l2_mbus_framefmt *in_fmt; u32 code; - in_fmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SINK, + in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK, sdformat->which); if (!in_fmt) return -EINVAL; @@ -1022,7 +1024,7 @@ } static int imx7_csi_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct imx7_csi *csi = v4l2_get_subdevdata(sd); @@ -1043,11 +1045,12 @@ goto out_unlock; } - ret = imx7_csi_try_fmt(csi, cfg, sdformat, &cc); + ret = imx7_csi_try_fmt(csi, sd_state, sdformat, &cc); if (ret < 0) goto out_unlock; - fmt = imx7_csi_get_format(csi, cfg, sdformat->pad, sdformat->which); + fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad, + sdformat->which); if (!fmt) { ret = -EINVAL; goto out_unlock; @@ -1060,11 +1063,11 @@ format.pad = IMX7_CSI_PAD_SRC; format.which = sdformat->which; format.format = sdformat->format; - if (imx7_csi_try_fmt(csi, cfg, &format, &outcc)) { + if (imx7_csi_try_fmt(csi, sd_state, &format, &outcc)) { ret = -EINVAL; goto out_unlock; } - outfmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SRC, + outfmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SRC, sdformat->which); *outfmt = format.format; @@ -1121,7 +1124,7 @@ } static int imx7_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct imx7_csi *csi = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; @@ -1129,7 +1132,7 @@ int i; for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { - mf = v4l2_subdev_get_try_format(sd, cfg, i); + mf = v4l2_subdev_get_try_format(sd, sd_state, i); ret = imx_media_init_mbus_fmt(mf, 800, 600, 0, V4L2_FIELD_NONE, &csi->cc[i]); diff -Naur --no-dereference a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c --- a/drivers/staging/media/imx/imx7-mipi-csis.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx7-mipi-csis.c 2022-01-06 12:45:53.830318172 -0500 @@ -700,26 +700,27 @@ static struct v4l2_mbus_framefmt * mipi_csis_get_format(struct csi_state *state, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which, unsigned int pad) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&state->mipi_sd, cfg, pad); + return v4l2_subdev_get_try_format(&state->mipi_sd, sd_state, + pad); return &state->format_mbus; } static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); struct v4l2_mbus_framefmt *fmt_sink; struct v4l2_mbus_framefmt *fmt_source; enum v4l2_subdev_format_whence which; - which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - fmt_sink = mipi_csis_get_format(state, cfg, which, CSIS_PAD_SINK); + which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink = mipi_csis_get_format(state, sd_state, which, CSIS_PAD_SINK); fmt_sink->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt_sink->width = MIPI_CSIS_DEF_PIX_WIDTH; @@ -738,24 +739,26 @@ * configuration, cfg is NULL, which indicates there's no source pad * configuration to set. */ - if (!cfg) + if (!sd_state) return 0; - fmt_source = mipi_csis_get_format(state, cfg, which, CSIS_PAD_SOURCE); + fmt_source = mipi_csis_get_format(state, sd_state, which, + CSIS_PAD_SOURCE); *fmt_source = *fmt_sink; return 0; } static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); struct v4l2_mbus_framefmt *fmt; mutex_lock(&state->lock); - fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad); + fmt = mipi_csis_get_format(state, sd_state, sdformat->which, + sdformat->pad); sdformat->format = *fmt; mutex_unlock(&state->lock); @@ -763,7 +766,7 @@ } static int mipi_csis_enum_mbus_code(struct v4l2_subdev *mipi_sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); @@ -778,7 +781,8 @@ if (code->index > 0) return -EINVAL; - fmt = mipi_csis_get_format(state, cfg, code->which, code->pad); + fmt = mipi_csis_get_format(state, sd_state, code->which, + code->pad); code->code = fmt->code; return 0; } @@ -795,7 +799,7 @@ } static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); @@ -808,12 +812,13 @@ * modified. */ if (sdformat->pad == CSIS_PAD_SOURCE) - return mipi_csis_get_fmt(mipi_sd, cfg, sdformat); + return mipi_csis_get_fmt(mipi_sd, sd_state, sdformat); if (sdformat->pad != CSIS_PAD_SINK) return -EINVAL; - fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad); + fmt = mipi_csis_get_format(state, sd_state, sdformat->which, + sdformat->pad); mutex_lock(&state->lock); @@ -856,7 +861,7 @@ sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = mipi_csis_get_format(state, cfg, sdformat->which, + fmt = mipi_csis_get_format(state, sd_state, sdformat->which, CSIS_PAD_SOURCE); *fmt = sdformat->format; diff -Naur --no-dereference a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c --- a/drivers/staging/media/imx/imx-ic-prp.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx-ic-prp.c 2022-01-06 12:45:53.826318157 -0500 @@ -79,13 +79,13 @@ } static struct v4l2_mbus_framefmt * -__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_pad_config *cfg, +__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { struct imx_ic_priv *ic_priv = priv->ic_priv; if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ic_priv->sd, cfg, pad); + return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); else return &priv->format_mbus; } @@ -95,7 +95,7 @@ */ static int prp_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct prp_priv *priv = sd_to_priv(sd); @@ -115,7 +115,8 @@ ret = -EINVAL; goto out; } - infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, code->which); + infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, + code->which); code->code = infmt->code; break; default: @@ -127,7 +128,7 @@ } static int prp_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct prp_priv *priv = sd_to_priv(sd); @@ -139,7 +140,7 @@ mutex_lock(&priv->lock); - fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); if (!fmt) { ret = -EINVAL; goto out; @@ -152,7 +153,7 @@ } static int prp_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct prp_priv *priv = sd_to_priv(sd); @@ -171,7 +172,7 @@ goto out; } - infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, sdformat->which); + infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, sdformat->which); switch (sdformat->pad) { case PRP_SINK_PAD: @@ -201,7 +202,7 @@ imx_media_try_colorimetry(&sdformat->format, true); - fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); *fmt = sdformat->format; out: mutex_unlock(&priv->lock); diff -Naur --no-dereference a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c --- a/drivers/staging/media/imx/imx-ic-prpencvf.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c 2022-01-06 12:45:53.826318157 -0500 @@ -790,13 +790,13 @@ } static struct v4l2_mbus_framefmt * -__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_pad_config *cfg, +__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { struct imx_ic_priv *ic_priv = priv->ic_priv; if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ic_priv->sd, cfg, pad); + return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); else return &priv->format_mbus[pad]; } @@ -844,7 +844,7 @@ */ static int prp_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad >= PRPENCVF_NUM_PADS) @@ -855,7 +855,7 @@ } static int prp_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct prp_priv *priv = sd_to_priv(sd); @@ -867,7 +867,7 @@ mutex_lock(&priv->lock); - fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); if (!fmt) { ret = -EINVAL; goto out; @@ -880,7 +880,7 @@ } static void prp_try_fmt(struct prp_priv *priv, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat, const struct imx_media_pixfmt **cc) { @@ -897,7 +897,8 @@ sdformat->format.code = (*cc)->codes[0]; } - infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); + infmt = __prp_get_fmt(priv, sd_state, PRPENCVF_SINK_PAD, + sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { sdformat->format.field = infmt->field; @@ -923,7 +924,7 @@ } static int prp_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct prp_priv *priv = sd_to_priv(sd); @@ -941,9 +942,9 @@ goto out; } - prp_try_fmt(priv, cfg, sdformat, &cc); + prp_try_fmt(priv, sd_state, sdformat, &cc); - fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); *fmt = sdformat->format; /* propagate a default format to source pad */ @@ -955,9 +956,9 @@ format.pad = PRPENCVF_SRC_PAD; format.which = sdformat->which; format.format = sdformat->format; - prp_try_fmt(priv, cfg, &format, &outcc); + prp_try_fmt(priv, sd_state, &format, &outcc); - outfmt = __prp_get_fmt(priv, cfg, PRPENCVF_SRC_PAD, + outfmt = __prp_get_fmt(priv, sd_state, PRPENCVF_SRC_PAD, sdformat->which); *outfmt = format.format; if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) @@ -973,7 +974,7 @@ } static int prp_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct prp_priv *priv = sd_to_priv(sd); @@ -991,7 +992,7 @@ format.format.code = fse->code; format.format.width = 1; format.format.height = 1; - prp_try_fmt(priv, cfg, &format, &cc); + prp_try_fmt(priv, sd_state, &format, &cc); fse->min_width = format.format.width; fse->min_height = format.format.height; @@ -1003,7 +1004,7 @@ format.format.code = fse->code; format.format.width = -1; format.format.height = -1; - prp_try_fmt(priv, cfg, &format, &cc); + prp_try_fmt(priv, sd_state, &format, &cc); fse->max_width = format.format.width; fse->max_height = format.format.height; out: diff -Naur --no-dereference a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c --- a/drivers/staging/media/imx/imx-media-csi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx-media-csi.c 2022-01-06 12:45:53.830318172 -0500 @@ -1162,31 +1162,32 @@ } static struct v4l2_mbus_framefmt * -__csi_get_fmt(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg, +__csi_get_fmt(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); + return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); else return &priv->format_mbus[pad]; } static struct v4l2_rect * -__csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg, +__csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&priv->sd, cfg, CSI_SINK_PAD); + return v4l2_subdev_get_try_crop(&priv->sd, sd_state, + CSI_SINK_PAD); else return &priv->crop; } static struct v4l2_rect * -__csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg, +__csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&priv->sd, cfg, + return v4l2_subdev_get_try_compose(&priv->sd, sd_state, CSI_SINK_PAD); else return &priv->compose; @@ -1194,7 +1195,7 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_rect *crop, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { @@ -1233,7 +1234,7 @@ } static int csi_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1244,7 +1245,7 @@ mutex_lock(&priv->lock); - infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, code->which); + infmt = __csi_get_fmt(priv, sd_state, CSI_SINK_PAD, code->which); incc = imx_media_find_mbus_format(infmt->code, PIXFMT_SEL_ANY); switch (code->pad) { @@ -1286,7 +1287,7 @@ } static int csi_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1305,7 +1306,7 @@ fse->min_height = MIN_H; fse->max_height = MAX_H; } else { - crop = __csi_get_crop(priv, cfg, fse->which); + crop = __csi_get_crop(priv, sd_state, fse->which); fse->min_width = fse->index & 1 ? crop->width / 2 : crop->width; @@ -1320,7 +1321,7 @@ } static int csi_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1336,7 +1337,7 @@ mutex_lock(&priv->lock); input_fi = &priv->frame_interval[CSI_SINK_PAD]; - crop = __csi_get_crop(priv, cfg, fie->which); + crop = __csi_get_crop(priv, sd_state, fie->which); if ((fie->width != crop->width && fie->width != crop->width / 2) || (fie->height != crop->height && fie->height != crop->height / 2)) { @@ -1356,7 +1357,7 @@ } static int csi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1368,7 +1369,7 @@ mutex_lock(&priv->lock); - fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __csi_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); if (!fmt) { ret = -EINVAL; goto out; @@ -1381,11 +1382,11 @@ } static void csi_try_field(struct csi_priv *priv, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct v4l2_mbus_framefmt *infmt = - __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which); + __csi_get_fmt(priv, sd_state, CSI_SINK_PAD, sdformat->which); /* * no restrictions on sink pad field type except must @@ -1431,7 +1432,7 @@ static void csi_try_fmt(struct csi_priv *priv, struct v4l2_fwnode_endpoint *upstream_ep, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat, struct v4l2_rect *crop, struct v4l2_rect *compose, @@ -1441,7 +1442,7 @@ struct v4l2_mbus_framefmt *infmt; u32 code; - infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which); + infmt = __csi_get_fmt(priv, sd_state, CSI_SINK_PAD, sdformat->which); switch (sdformat->pad) { case CSI_SRC_PAD_DIRECT: @@ -1468,7 +1469,7 @@ } } - csi_try_field(priv, cfg, sdformat); + csi_try_field(priv, sd_state, sdformat); /* propagate colorimetry from sink */ sdformat->format.colorspace = infmt->colorspace; @@ -1492,7 +1493,7 @@ sdformat->format.code = (*cc)->codes[0]; } - csi_try_field(priv, cfg, sdformat); + csi_try_field(priv, sd_state, sdformat); /* Reset crop and compose rectangles */ crop->left = 0; @@ -1501,7 +1502,8 @@ crop->height = sdformat->format.height; if (sdformat->format.field == V4L2_FIELD_ALTERNATE) crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + csi_try_crop(priv, crop, sd_state, &sdformat->format, + upstream_ep); compose->left = 0; compose->top = 0; compose->width = crop->width; @@ -1515,7 +1517,7 @@ } static int csi_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1541,12 +1543,13 @@ goto out; } - crop = __csi_get_crop(priv, cfg, sdformat->which); - compose = __csi_get_compose(priv, cfg, sdformat->which); + crop = __csi_get_crop(priv, sd_state, sdformat->which); + compose = __csi_get_compose(priv, sd_state, sdformat->which); - csi_try_fmt(priv, &upstream_ep, cfg, sdformat, crop, compose, &cc); + csi_try_fmt(priv, &upstream_ep, sd_state, sdformat, crop, compose, + &cc); - fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __csi_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); *fmt = sdformat->format; if (sdformat->pad == CSI_SINK_PAD) { @@ -1561,10 +1564,11 @@ format.pad = pad; format.which = sdformat->which; format.format = sdformat->format; - csi_try_fmt(priv, &upstream_ep, cfg, &format, + csi_try_fmt(priv, &upstream_ep, sd_state, &format, NULL, compose, &outcc); - outfmt = __csi_get_fmt(priv, cfg, pad, sdformat->which); + outfmt = __csi_get_fmt(priv, sd_state, pad, + sdformat->which); *outfmt = format.format; if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) @@ -1581,7 +1585,7 @@ } static int csi_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1594,9 +1598,9 @@ mutex_lock(&priv->lock); - infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sel->which); - crop = __csi_get_crop(priv, cfg, sel->which); - compose = __csi_get_compose(priv, cfg, sel->which); + infmt = __csi_get_fmt(priv, sd_state, CSI_SINK_PAD, sel->which); + crop = __csi_get_crop(priv, sd_state, sel->which); + compose = __csi_get_compose(priv, sd_state, sel->which); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -1645,7 +1649,7 @@ } static int csi_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); @@ -1670,9 +1674,9 @@ goto out; } - infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sel->which); - crop = __csi_get_crop(priv, cfg, sel->which); - compose = __csi_get_compose(priv, cfg, sel->which); + infmt = __csi_get_fmt(priv, sd_state, CSI_SINK_PAD, sel->which); + crop = __csi_get_crop(priv, sd_state, sel->which); + compose = __csi_get_compose(priv, sd_state, sel->which); switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -1688,7 +1692,7 @@ goto out; } - csi_try_crop(priv, &sel->r, cfg, infmt, &upstream_ep); + csi_try_crop(priv, &sel->r, sd_state, infmt, &upstream_ep); *crop = sel->r; @@ -1729,7 +1733,7 @@ for (pad = CSI_SINK_PAD + 1; pad < CSI_NUM_PADS; pad++) { struct v4l2_mbus_framefmt *outfmt; - outfmt = __csi_get_fmt(priv, cfg, pad, sel->which); + outfmt = __csi_get_fmt(priv, sd_state, pad, sel->which); outfmt->width = compose->width; outfmt->height = compose->height; } diff -Naur --no-dereference a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h --- a/drivers/staging/media/imx/imx-media.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx-media.h 2022-01-06 12:45:53.830318172 -0500 @@ -190,7 +190,7 @@ u32 width, u32 height, u32 code, u32 field, const struct imx_media_pixfmt **cc); int imx_media_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg); + struct v4l2_subdev_state *sd_state); void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt, bool ic_route); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, diff -Naur --no-dereference a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c --- a/drivers/staging/media/imx/imx-media-utils.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx-media-utils.c 2022-01-06 12:45:53.830318172 -0500 @@ -408,7 +408,7 @@ * of a subdev. Can be used as the .init_cfg pad operation. */ int imx_media_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf_try; struct v4l2_subdev_format format; @@ -424,7 +424,7 @@ if (ret) continue; - mf_try = v4l2_subdev_get_try_format(sd, cfg, pad); + mf_try = v4l2_subdev_get_try_format(sd, sd_state, pad); *mf_try = format.format; } @@ -884,16 +884,16 @@ mutex_lock(&imxmd->md.graph_mutex); if (on) { - ret = __media_pipeline_start(entity, &imxmd->pipe); + ret = __media_pipeline_start(entity->pads, &imxmd->pipe); if (ret) goto out; ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) - __media_pipeline_stop(entity); + __media_pipeline_stop(entity->pads); } else { v4l2_subdev_call(sd, video, s_stream, 0); - if (entity->pipe) - __media_pipeline_stop(entity); + if (entity->pads->pipe) + __media_pipeline_stop(entity->pads); } out: diff -Naur --no-dereference a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c --- a/drivers/staging/media/imx/imx-media-vdic.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/imx/imx-media-vdic.c 2022-01-06 12:45:53.830318172 -0500 @@ -532,17 +532,17 @@ } static struct v4l2_mbus_framefmt * -__vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_pad_config *cfg, +__vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); + return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); else return &priv->format_mbus[pad]; } static int vdic_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad >= VDIC_NUM_PADS) @@ -553,7 +553,7 @@ } static int vdic_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct vdic_priv *priv = v4l2_get_subdevdata(sd); @@ -565,7 +565,7 @@ mutex_lock(&priv->lock); - fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __vdic_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); if (!fmt) { ret = -EINVAL; goto out; @@ -578,7 +578,7 @@ } static void vdic_try_fmt(struct vdic_priv *priv, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat, const struct imx_media_pixfmt **cc) { @@ -594,7 +594,7 @@ sdformat->format.code = (*cc)->codes[0]; } - infmt = __vdic_get_fmt(priv, cfg, priv->active_input_pad, + infmt = __vdic_get_fmt(priv, sd_state, priv->active_input_pad, sdformat->which); switch (sdformat->pad) { @@ -620,7 +620,7 @@ } static int vdic_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) { struct vdic_priv *priv = v4l2_get_subdevdata(sd); @@ -638,9 +638,9 @@ goto out; } - vdic_try_fmt(priv, cfg, sdformat, &cc); + vdic_try_fmt(priv, sd_state, sdformat, &cc); - fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which); + fmt = __vdic_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); *fmt = sdformat->format; /* propagate format to source pad */ @@ -653,9 +653,9 @@ format.pad = VDIC_SRC_PAD_DIRECT; format.which = sdformat->which; format.format = sdformat->format; - vdic_try_fmt(priv, cfg, &format, &outcc); + vdic_try_fmt(priv, sd_state, &format, &outcc); - outfmt = __vdic_get_fmt(priv, cfg, VDIC_SRC_PAD_DIRECT, + outfmt = __vdic_get_fmt(priv, sd_state, VDIC_SRC_PAD_DIRECT, sdformat->which); *outfmt = format.format; if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) diff -Naur --no-dereference a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c --- a/drivers/staging/media/ipu3/ipu3-v4l2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c 2022-01-06 12:45:53.830318172 -0500 @@ -36,7 +36,7 @@ /* Initialize try_fmt */ for (i = 0; i < IMGU_NODE_NUM; i++) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, i); + v4l2_subdev_get_try_format(sd, fh->state, i); try_fmt->width = try_crop.width; try_fmt->height = try_crop.height; @@ -44,8 +44,8 @@ try_fmt->field = V4L2_FIELD_NONE; } - *v4l2_subdev_get_try_crop(sd, fh->pad, IMGU_NODE_IN) = try_crop; - *v4l2_subdev_get_try_compose(sd, fh->pad, IMGU_NODE_IN) = try_crop; + *v4l2_subdev_get_try_crop(sd, fh->state, IMGU_NODE_IN) = try_crop; + *v4l2_subdev_get_try_compose(sd, fh->state, IMGU_NODE_IN) = try_crop; return 0; } @@ -120,7 +120,7 @@ } static int imgu_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imgu_device *imgu = v4l2_get_subdevdata(sd); @@ -136,7 +136,7 @@ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { fmt->format = imgu_pipe->nodes[pad].pad_fmt; } else { - mf = v4l2_subdev_get_try_format(sd, cfg, pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, pad); fmt->format = *mf; } @@ -144,7 +144,7 @@ } static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imgu_media_pipe *imgu_pipe; @@ -161,7 +161,7 @@ imgu_pipe = &imgu->imgu_pipe[pipe]; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mf = v4l2_subdev_get_try_format(sd, cfg, pad); + mf = v4l2_subdev_get_try_format(sd, sd_state, pad); else mf = &imgu_pipe->nodes[pad].pad_fmt; @@ -189,7 +189,7 @@ } static int imgu_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct v4l2_rect *try_sel, *r; @@ -202,11 +202,11 @@ switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); r = &imgu_sd->rect.eff; break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); r = &imgu_sd->rect.bds; break; default: @@ -222,7 +222,7 @@ } static int imgu_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct imgu_device *imgu = v4l2_get_subdevdata(sd); @@ -241,11 +241,11 @@ switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); rect = &imgu_sd->rect.eff; break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); rect = &imgu_sd->rect.bds; break; default: @@ -485,7 +485,7 @@ pipe = node->pipe; imgu_pipe = &imgu->imgu_pipe[pipe]; - r = media_pipeline_start(&node->vdev.entity, &imgu_pipe->pipeline); + r = media_pipeline_start(node->vdev.entity.pads, &imgu_pipe->pipeline); if (r < 0) goto fail_return_bufs; @@ -510,7 +510,7 @@ return 0; fail_stop_pipeline: - media_pipeline_stop(&node->vdev.entity); + media_pipeline_stop(node->vdev.entity.pads); fail_return_bufs: imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); @@ -550,7 +550,7 @@ imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); mutex_unlock(&imgu->streaming_lock); - media_pipeline_stop(&node->vdev.entity); + media_pipeline_stop(node->vdev.entity.pads); } /******************** v4l2_ioctl_ops ********************/ diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c --- a/drivers/staging/media/omap4iss/iss.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss.c 2022-01-06 12:45:53.830318172 -0500 @@ -543,7 +543,7 @@ struct iss_pipeline *pipe; struct media_pad *pad; - if (!me->pipe) + if (!me->pads->pipe) return 0; pipe = to_iss_pipeline(me); if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED) diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c --- a/drivers/staging/media/omap4iss/iss_csi2.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss_csi2.c 2022-01-06 12:45:53.830318172 -0500 @@ -825,19 +825,20 @@ static struct v4l2_mbus_framefmt * __csi2_get_format(struct iss_csi2_device *csi2, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, + pad); return &csi2->formats[pad]; } static void csi2_try_format(struct iss_csi2_device *csi2, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) @@ -868,7 +869,8 @@ * compression. */ pixelcode = fmt->code; - format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which); + format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); /* @@ -894,7 +896,7 @@ * return -EINVAL or zero on success */ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -907,7 +909,7 @@ code->code = csi2_input_fmts[code->index]; } else { - format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, + format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SINK, code->which); switch (code->index) { case 0: @@ -931,7 +933,7 @@ } static int csi2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -943,7 +945,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - csi2_try_format(csi2, cfg, fse->pad, &format, fse->which); + csi2_try_format(csi2, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -953,7 +955,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - csi2_try_format(csi2, cfg, fse->pad, &format, fse->which); + csi2_try_format(csi2, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -968,13 +970,13 @@ * return -EINVAL or zero on success */ static int csi2_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; @@ -990,25 +992,26 @@ * return -EINVAL or zero on success */ static int csi2_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; - csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which); + csi2_try_format(csi2, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CSI2_PAD_SINK) { - format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE, + format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SOURCE, fmt->which); *format = fmt->format; - csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which); + csi2_try_format(csi2, sd_state, CSI2_PAD_SOURCE, format, + fmt->which); } return 0; @@ -1050,7 +1053,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - csi2_set_format(sd, fh ? fh->pad : NULL, &format); + csi2_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c --- a/drivers/staging/media/omap4iss/iss_ipipe.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss_ipipe.c 2022-01-06 12:45:53.830318172 -0500 @@ -21,7 +21,7 @@ static struct v4l2_mbus_framefmt * __ipipe_get_format(struct iss_ipipe_device *ipipe, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which); @@ -175,12 +175,13 @@ static struct v4l2_mbus_framefmt * __ipipe_get_format(struct iss_ipipe_device *ipipe, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ipipe->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&ipipe->subdev, sd_state, + pad); return &ipipe->formats[pad]; } @@ -194,7 +195,7 @@ */ static void ipipe_try_format(struct iss_ipipe_device *ipipe, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) @@ -222,7 +223,8 @@ break; case IPIPE_PAD_SOURCE_VP: - format = __ipipe_get_format(ipipe, cfg, IPIPE_PAD_SINK, which); + format = __ipipe_get_format(ipipe, sd_state, IPIPE_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; @@ -243,7 +245,7 @@ * return -EINVAL or zero on success */ static int ipipe_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -270,7 +272,7 @@ } static int ipipe_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); @@ -282,7 +284,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - ipipe_try_format(ipipe, cfg, fse->pad, &format, fse->which); + ipipe_try_format(ipipe, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -292,7 +294,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - ipipe_try_format(ipipe, cfg, fse->pad, &format, fse->which); + ipipe_try_format(ipipe, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -309,13 +311,13 @@ * to the format type. */ static int ipipe_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which); + format = __ipipe_get_format(ipipe, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; @@ -333,25 +335,26 @@ * to the format type. */ static int ipipe_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which); + format = __ipipe_get_format(ipipe, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; - ipipe_try_format(ipipe, cfg, fmt->pad, &fmt->format, fmt->which); + ipipe_try_format(ipipe, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == IPIPE_PAD_SINK) { - format = __ipipe_get_format(ipipe, cfg, IPIPE_PAD_SOURCE_VP, + format = __ipipe_get_format(ipipe, sd_state, + IPIPE_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ipipe_try_format(ipipe, cfg, IPIPE_PAD_SOURCE_VP, format, + ipipe_try_format(ipipe, sd_state, IPIPE_PAD_SOURCE_VP, format, fmt->which); } @@ -392,7 +395,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ipipe_set_format(sd, fh ? fh->pad : NULL, &format); + ipipe_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c --- a/drivers/staging/media/omap4iss/iss_ipipeif.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c 2022-01-06 12:45:53.830318172 -0500 @@ -357,11 +357,12 @@ static struct v4l2_mbus_framefmt * __ipipeif_get_format(struct iss_ipipeif_device *ipipeif, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ipipeif->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&ipipeif->subdev, sd_state, + pad); return &ipipeif->formats[pad]; } @@ -374,7 +375,7 @@ */ static void ipipeif_try_format(struct iss_ipipeif_device *ipipeif, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -403,7 +404,8 @@ break; case IPIPEIF_PAD_SOURCE_ISIF_SF: - format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK, + format = __ipipeif_get_format(ipipeif, sd_state, + IPIPEIF_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); @@ -418,7 +420,8 @@ break; case IPIPEIF_PAD_SOURCE_VP: - format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK, + format = __ipipeif_get_format(ipipeif, sd_state, + IPIPEIF_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); @@ -442,7 +445,7 @@ * return -EINVAL or zero on success */ static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); @@ -462,7 +465,8 @@ if (code->index != 0) return -EINVAL; - format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK, + format = __ipipeif_get_format(ipipeif, sd_state, + IPIPEIF_PAD_SINK, code->which); code->code = format->code; @@ -476,7 +480,7 @@ } static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); @@ -488,7 +492,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - ipipeif_try_format(ipipeif, cfg, fse->pad, &format, fse->which); + ipipeif_try_format(ipipeif, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -498,7 +502,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - ipipeif_try_format(ipipeif, cfg, fse->pad, &format, fse->which); + ipipeif_try_format(ipipeif, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -515,13 +519,13 @@ * to the format type. */ static int ipipeif_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which); + format = __ipipeif_get_format(ipipeif, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; @@ -539,33 +543,36 @@ * to the format type. */ static int ipipeif_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which); + format = __ipipeif_get_format(ipipeif, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; - ipipeif_try_format(ipipeif, cfg, fmt->pad, &fmt->format, fmt->which); + ipipeif_try_format(ipipeif, sd_state, fmt->pad, &fmt->format, + fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == IPIPEIF_PAD_SINK) { - format = __ipipeif_get_format(ipipeif, cfg, + format = __ipipeif_get_format(ipipeif, sd_state, IPIPEIF_PAD_SOURCE_ISIF_SF, fmt->which); *format = fmt->format; - ipipeif_try_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_ISIF_SF, + ipipeif_try_format(ipipeif, sd_state, + IPIPEIF_PAD_SOURCE_ISIF_SF, format, fmt->which); - format = __ipipeif_get_format(ipipeif, cfg, + format = __ipipeif_get_format(ipipeif, sd_state, IPIPEIF_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ipipeif_try_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_VP, format, + ipipeif_try_format(ipipeif, sd_state, IPIPEIF_PAD_SOURCE_VP, + format, fmt->which); } @@ -608,7 +615,7 @@ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ipipeif_set_format(sd, fh ? fh->pad : NULL, &format); + ipipeif_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c --- a/drivers/staging/media/omap4iss/iss_resizer.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss_resizer.c 2022-01-06 12:45:53.830318172 -0500 @@ -416,11 +416,12 @@ static struct v4l2_mbus_framefmt * __resizer_get_format(struct iss_resizer_device *resizer, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&resizer->subdev, cfg, pad); + return v4l2_subdev_get_try_format(&resizer->subdev, sd_state, + pad); return &resizer->formats[pad]; } @@ -433,7 +434,7 @@ */ static void resizer_try_format(struct iss_resizer_device *resizer, - struct v4l2_subdev_pad_config *cfg, unsigned int pad, + struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -461,7 +462,8 @@ case RESIZER_PAD_SOURCE_MEM: pixelcode = fmt->code; - format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK, + format = __resizer_get_format(resizer, sd_state, + RESIZER_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); @@ -492,7 +494,7 @@ * return -EINVAL or zero on success */ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); @@ -507,7 +509,8 @@ break; case RESIZER_PAD_SOURCE_MEM: - format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK, + format = __resizer_get_format(resizer, sd_state, + RESIZER_PAD_SINK, code->which); if (code->index == 0) { @@ -537,7 +540,7 @@ } static int resizer_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); @@ -549,7 +552,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(resizer, cfg, fse->pad, &format, fse->which); + resizer_try_format(resizer, sd_state, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -559,7 +562,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(resizer, cfg, fse->pad, &format, fse->which); + resizer_try_format(resizer, sd_state, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -576,13 +579,13 @@ * to the format type. */ static int resizer_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which); + format = __resizer_get_format(resizer, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; @@ -600,26 +603,28 @@ * to the format type. */ static int resizer_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which); + format = __resizer_get_format(resizer, sd_state, fmt->pad, fmt->which); if (!format) return -EINVAL; - resizer_try_format(resizer, cfg, fmt->pad, &fmt->format, fmt->which); + resizer_try_format(resizer, sd_state, fmt->pad, &fmt->format, + fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == RESIZER_PAD_SINK) { - format = __resizer_get_format(resizer, cfg, + format = __resizer_get_format(resizer, sd_state, RESIZER_PAD_SOURCE_MEM, fmt->which); *format = fmt->format; - resizer_try_format(resizer, cfg, RESIZER_PAD_SOURCE_MEM, format, + resizer_try_format(resizer, sd_state, RESIZER_PAD_SOURCE_MEM, + format, fmt->which); } @@ -662,7 +667,7 @@ format.format.code = MEDIA_BUS_FMT_UYVY8_1X16; format.format.width = 4096; format.format.height = 4096; - resizer_set_format(sd, fh ? fh->pad : NULL, &format); + resizer_set_format(sd, fh ? fh->state : NULL, &format); return 0; } diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c --- a/drivers/staging/media/omap4iss/iss_video.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss_video.c 2022-01-06 12:45:53.830318172 -0500 @@ -206,8 +206,8 @@ iss_video_far_end(struct iss_video *video) { struct media_graph graph; - struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->graph_obj.mdev; + struct media_pad *pad = video->video.entity.pads; + struct media_device *mdev = video->video.entity.graph_obj.mdev; struct iss_video *far_end = NULL; mutex_lock(&mdev->graph_mutex); @@ -217,16 +217,17 @@ return NULL; } - media_graph_walk_start(&graph, entity); + media_graph_walk_start(&graph, pad); - while ((entity = media_graph_walk_next(&graph))) { - if (entity == &video->video.entity) + while ((pad = media_graph_walk_next(&graph))) { + if (pad->entity == &video->video.entity) continue; - if (!is_media_entity_v4l2_video_device(entity)) + if (!is_media_entity_v4l2_video_device(pad->entity)) continue; - far_end = to_iss_video(media_entity_to_video_device(entity)); + far_end = to_iss_video(media_entity_to_video_device( + pad->entity)); if (far_end->type != video->type) break; @@ -853,7 +854,7 @@ struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); struct media_graph graph; - struct media_entity *entity = &video->video.entity; + struct media_pad *pad = video->video.entity.pads; enum iss_pipeline_state state; struct iss_pipeline *pipe; struct iss_video *far_end; @@ -869,30 +870,31 @@ * Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ - pipe = entity->pipe - ? to_iss_pipeline(entity) : &video->pipe; + pipe = pad->pipe + ? to_iss_pipeline(pad->entity) : &video->pipe; pipe->external = NULL; pipe->external_rate = 0; pipe->external_bpp = 0; - ret = media_entity_enum_init(&pipe->ent_enum, entity->graph_obj.mdev); + ret = media_entity_enum_init(&pipe->ent_enum, + pad->entity->graph_obj.mdev); if (ret) goto err_graph_walk_init; - ret = media_graph_walk_init(&graph, entity->graph_obj.mdev); + ret = media_graph_walk_init(&graph, pad->entity->graph_obj.mdev); if (ret) goto err_graph_walk_init; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, true); - ret = media_pipeline_start(entity, &pipe->pipe); + ret = media_pipeline_start(pad, &pipe->pipe); if (ret < 0) goto err_media_pipeline_start; - media_graph_walk_start(&graph, entity); - while ((entity = media_graph_walk_next(&graph))) - media_entity_enum_set(&pipe->ent_enum, entity); + media_graph_walk_start(&graph, pad); + while ((pad = media_graph_walk_next(&graph))) + media_entity_enum_set(&pipe->ent_enum, pad->entity); /* * Verify that the currently configured format matches the output of @@ -975,7 +977,7 @@ err_omap4iss_set_stream: vb2_streamoff(&vfh->queue, type); err_iss_video_check_format: - media_pipeline_stop(&video->video.entity); + media_pipeline_stop(video->video.entity.pads); err_media_pipeline_start: if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); @@ -1029,7 +1031,7 @@ if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); - media_pipeline_stop(&video->video.entity); + media_pipeline_stop(video->video.entity.pads); done: mutex_unlock(&video->stream_lock); diff -Naur --no-dereference a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h --- a/drivers/staging/media/omap4iss/iss_video.h 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/omap4iss/iss_video.h 2022-01-06 12:45:53.830318172 -0500 @@ -92,7 +92,7 @@ }; #define to_iss_pipeline(__e) \ - container_of((__e)->pipe, struct iss_pipeline, pipe) + container_of((__e)->pads->pipe, struct iss_pipeline, pipe) static inline int iss_pipeline_ready(struct iss_pipeline *pipe) { diff -Naur --no-dereference a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c --- a/drivers/staging/media/rkisp1/rkisp1-capture.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c 2022-01-06 12:45:53.830318172 -0500 @@ -921,7 +921,7 @@ mutex_lock(&cap->rkisp1->stream_lock); rkisp1_stream_stop(cap); - media_pipeline_stop(&node->vdev.entity); + media_pipeline_stop(node->vdev.entity.pads); ret = rkisp1_pipeline_sink_walk(&node->vdev.entity, NULL, rkisp1_pipeline_disable_cb); if (ret) @@ -1010,7 +1010,7 @@ if (ret) goto err_stop_stream; - ret = media_pipeline_start(entity, &cap->rkisp1->pipe); + ret = media_pipeline_start(entity->pads, &cap->rkisp1->pipe); if (ret) { dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret); goto err_pipe_disable; diff -Naur --no-dereference a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c --- a/drivers/staging/media/rkisp1/rkisp1-isp.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c 2022-01-06 12:45:53.830318172 -0500 @@ -208,24 +208,30 @@ static struct v4l2_mbus_framefmt * rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { + struct v4l2_subdev_state state = { + .pads = isp->pad_cfg + }; if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&isp->sd, cfg, pad); + return v4l2_subdev_get_try_format(&isp->sd, sd_state, pad); else - return v4l2_subdev_get_try_format(&isp->sd, isp->pad_cfg, pad); + return v4l2_subdev_get_try_format(&isp->sd, &state, pad); } static struct v4l2_rect * rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { + struct v4l2_subdev_state state = { + .pads = isp->pad_cfg + }; if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&isp->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&isp->sd, sd_state, pad); else - return v4l2_subdev_get_try_crop(&isp->sd, isp->pad_cfg, pad); + return v4l2_subdev_get_try_crop(&isp->sd, &state, pad); } /* ---------------------------------------------------------------------------- @@ -561,7 +567,7 @@ */ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { unsigned int i, dir; @@ -601,37 +607,37 @@ } static int rkisp1_isp_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop, *src_crop; - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; - sink_crop = v4l2_subdev_get_try_crop(sd, cfg, + sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_try_format(sd, cfg, + src_fmt = v4l2_subdev_get_try_format(sd, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); *src_fmt = *sink_fmt; src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; - src_crop = v4l2_subdev_get_try_crop(sd, cfg, + src_crop = v4l2_subdev_get_try_crop(sd, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); *src_crop = *sink_crop; - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, RKISP1_ISP_PAD_SINK_PARAMS); - src_fmt = v4l2_subdev_get_try_format(sd, cfg, + src_fmt = v4l2_subdev_get_try_format(sd, sd_state, RKISP1_ISP_PAD_SOURCE_STATS); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -643,7 +649,7 @@ } static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_mbus_framefmt *format, unsigned int which) { @@ -651,9 +657,9 @@ struct v4l2_mbus_framefmt *src_fmt; const struct v4l2_rect *src_crop; - src_fmt = rkisp1_isp_get_pad_fmt(isp, cfg, + src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO, which); - src_crop = rkisp1_isp_get_pad_crop(isp, cfg, + src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO, which); src_fmt->code = format->code; @@ -684,17 +690,17 @@ } static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_rect *r, unsigned int which) { struct v4l2_mbus_framefmt *src_fmt; const struct v4l2_rect *sink_crop; struct v4l2_rect *src_crop; - src_crop = rkisp1_isp_get_pad_crop(isp, cfg, + src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO, which); - sink_crop = rkisp1_isp_get_pad_crop(isp, cfg, + sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, RKISP1_ISP_PAD_SINK_VIDEO, which); @@ -707,21 +713,23 @@ *r = *src_crop; /* Propagate to out format */ - src_fmt = rkisp1_isp_get_pad_fmt(isp, cfg, + src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO, which); - rkisp1_isp_set_src_fmt(isp, cfg, src_fmt, which); + rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt, which); } static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_rect *r, unsigned int which) { struct v4l2_rect *sink_crop, *src_crop; struct v4l2_mbus_framefmt *sink_fmt; - sink_crop = rkisp1_isp_get_pad_crop(isp, cfg, RKISP1_ISP_PAD_SINK_VIDEO, + sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO, which); - sink_fmt = rkisp1_isp_get_pad_fmt(isp, cfg, RKISP1_ISP_PAD_SINK_VIDEO, + sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO, which); sink_crop->left = ALIGN(r->left, 2); @@ -733,13 +741,13 @@ *r = *sink_crop; /* Propagate to out crop */ - src_crop = rkisp1_isp_get_pad_crop(isp, cfg, + src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO, which); - rkisp1_isp_set_src_crop(isp, cfg, src_crop, which); + rkisp1_isp_set_src_crop(isp, sd_state, src_crop, which); } static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_mbus_framefmt *format, unsigned int which) { @@ -747,7 +755,8 @@ struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = rkisp1_isp_get_pad_fmt(isp, cfg, RKISP1_ISP_PAD_SINK_VIDEO, + sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); @@ -768,36 +777,40 @@ *format = *sink_fmt; /* Propagate to in crop */ - sink_crop = rkisp1_isp_get_pad_crop(isp, cfg, RKISP1_ISP_PAD_SINK_VIDEO, + sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO, which); - rkisp1_isp_set_sink_crop(isp, cfg, sink_crop, which); + rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop, which); } static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); mutex_lock(&isp->ops_lock); - fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, + fmt->which); mutex_unlock(&isp->ops_lock); return 0; } static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) - rkisp1_isp_set_sink_fmt(isp, cfg, &fmt->format, fmt->which); + rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format, + fmt->which); else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) - rkisp1_isp_set_src_fmt(isp, cfg, &fmt->format, fmt->which); + rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format, + fmt->which); else - fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, + fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, fmt->which); mutex_unlock(&isp->ops_lock); @@ -805,7 +818,7 @@ } static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); @@ -821,20 +834,20 @@ if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { struct v4l2_mbus_framefmt *fmt; - fmt = rkisp1_isp_get_pad_fmt(isp, cfg, sel->pad, + fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, sel->pad, sel->which); sel->r.height = fmt->height; sel->r.width = fmt->width; sel->r.left = 0; sel->r.top = 0; } else { - sel->r = *rkisp1_isp_get_pad_crop(isp, cfg, - RKISP1_ISP_PAD_SINK_VIDEO, - sel->which); + sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO, + sel->which); } break; case V4L2_SEL_TGT_CROP: - sel->r = *rkisp1_isp_get_pad_crop(isp, cfg, sel->pad, + sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, sel->pad, sel->which); break; default: @@ -845,7 +858,7 @@ } static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct rkisp1_device *rkisp1 = @@ -860,9 +873,9 @@ sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); mutex_lock(&isp->ops_lock); if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) - rkisp1_isp_set_sink_crop(isp, cfg, &sel->r, sel->which); + rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r, sel->which); else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) - rkisp1_isp_set_src_crop(isp, cfg, &sel->r, sel->which); + rkisp1_isp_set_src_crop(isp, sd_state, &sel->r, sel->which); else ret = -EINVAL; @@ -1003,6 +1016,9 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) { + struct v4l2_subdev_state state = { + .pads = rkisp1->isp.pad_cfg + }; struct rkisp1_isp *isp = &rkisp1->isp; struct media_pad *pads = isp->pads; struct v4l2_subdev *sd = &isp->sd; @@ -1035,7 +1051,7 @@ goto err_cleanup_media_entity; } - rkisp1_isp_init_config(sd, rkisp1->isp.pad_cfg); + rkisp1_isp_init_config(sd, &state); return 0; err_cleanup_media_entity: diff -Naur --no-dereference a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c 2022-01-06 12:45:53.830318172 -0500 @@ -180,24 +180,30 @@ static struct v4l2_mbus_framefmt * rkisp1_rsz_get_pad_fmt(struct rkisp1_resizer *rsz, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { + struct v4l2_subdev_state state = { + .pads = rsz->pad_cfg + }; if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&rsz->sd, cfg, pad); + return v4l2_subdev_get_try_format(&rsz->sd, sd_state, pad); else - return v4l2_subdev_get_try_format(&rsz->sd, rsz->pad_cfg, pad); + return v4l2_subdev_get_try_format(&rsz->sd, &state, pad); } static struct v4l2_rect * rkisp1_rsz_get_pad_crop(struct rkisp1_resizer *rsz, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, u32 which) { + struct v4l2_subdev_state state = { + .pads = rsz->pad_cfg + }; if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&rsz->sd, cfg, pad); + return v4l2_subdev_get_try_crop(&rsz->sd, sd_state, pad); else - return v4l2_subdev_get_try_crop(&rsz->sd, rsz->pad_cfg, pad); + return v4l2_subdev_get_try_crop(&rsz->sd, &state, pad); } /* ---------------------------------------------------------------------------- @@ -451,12 +457,15 @@ */ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); struct v4l2_subdev_pad_config dummy_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &dummy_cfg + }; u32 pad = code->pad; int ret; @@ -481,7 +490,7 @@ /* supported mbus codes on the sink pad are the same as isp src pad */ code->pad = RKISP1_ISP_PAD_SOURCE_VIDEO; ret = v4l2_subdev_call(&rsz->rkisp1->isp.sd, pad, enum_mbus_code, - &dummy_cfg, code); + &pad_state, code); /* restore pad */ code->pad = pad; @@ -490,24 +499,27 @@ } static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, + RKISP1_RSZ_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; sink_fmt->code = RKISP1_DEF_FMT; - sink_crop = v4l2_subdev_get_try_crop(sd, cfg, RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, + RKISP1_RSZ_PAD_SINK); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_try_format(sd, cfg, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_get_try_format(sd, sd_state, + RKISP1_RSZ_PAD_SINK); *src_fmt = *sink_fmt; /* NOTE: there is no crop in the source pad, only in the sink */ @@ -516,15 +528,17 @@ } static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_mbus_framefmt *format, unsigned int which) { const struct rkisp1_isp_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, which); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SRC, which); + sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, + which); + src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, + which); sink_mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); /* for YUV formats, userspace can change the mbus code on the src pad if it is supported */ @@ -543,7 +557,7 @@ } static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_rect *r, unsigned int which) { @@ -551,8 +565,10 @@ struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, which); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, cfg, RKISP1_RSZ_PAD_SINK, + sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, + which); + sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state, + RKISP1_RSZ_PAD_SINK, which); /* Not crop for MP bayer raw data */ @@ -579,7 +595,7 @@ } static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_mbus_framefmt *format, unsigned int which) { @@ -587,9 +603,12 @@ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, which); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SRC, which); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, cfg, RKISP1_RSZ_PAD_SINK, + sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, + which); + src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, + which); + sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state, + RKISP1_RSZ_PAD_SINK, which); if (rsz->id == RKISP1_SELFPATH) sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; @@ -617,24 +636,25 @@ *format = *sink_fmt; /* Update sink crop */ - rkisp1_rsz_set_sink_crop(rsz, cfg, sink_crop, which); + rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which); } static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); mutex_lock(&rsz->ops_lock); - fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, cfg, fmt->pad, fmt->which); + fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, sd_state, fmt->pad, + fmt->which); mutex_unlock(&rsz->ops_lock); return 0; } static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct rkisp1_resizer *rsz = @@ -642,16 +662,18 @@ mutex_lock(&rsz->ops_lock); if (fmt->pad == RKISP1_RSZ_PAD_SINK) - rkisp1_rsz_set_sink_fmt(rsz, cfg, &fmt->format, fmt->which); + rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format, + fmt->which); else - rkisp1_rsz_set_src_fmt(rsz, cfg, &fmt->format, fmt->which); + rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format, + fmt->which); mutex_unlock(&rsz->ops_lock); return 0; } static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct rkisp1_resizer *rsz = @@ -665,7 +687,8 @@ mutex_lock(&rsz->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - mf_sink = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, + mf_sink = rkisp1_rsz_get_pad_fmt(rsz, sd_state, + RKISP1_RSZ_PAD_SINK, sel->which); sel->r.height = mf_sink->height; sel->r.width = mf_sink->width; @@ -673,7 +696,8 @@ sel->r.top = 0; break; case V4L2_SEL_TGT_CROP: - sel->r = *rkisp1_rsz_get_pad_crop(rsz, cfg, RKISP1_RSZ_PAD_SINK, + sel->r = *rkisp1_rsz_get_pad_crop(rsz, sd_state, + RKISP1_RSZ_PAD_SINK, sel->which); break; default: @@ -685,7 +709,7 @@ } static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct rkisp1_resizer *rsz = @@ -698,7 +722,7 @@ sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); mutex_lock(&rsz->ops_lock); - rkisp1_rsz_set_sink_crop(rsz, cfg, &sel->r, sel->which); + rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r, sel->which); mutex_unlock(&rsz->ops_lock); return 0; @@ -764,6 +788,9 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) { + struct v4l2_subdev_state state = { + .pads = rsz->pad_cfg + }; const char * const dev_names[] = {RKISP1_RSZ_MP_DEV_NAME, RKISP1_RSZ_SP_DEV_NAME}; struct media_pad *pads = rsz->pads; @@ -800,7 +827,7 @@ goto err_cleanup_media_entity; } - rkisp1_rsz_init_config(sd, rsz->pad_cfg); + rkisp1_rsz_init_config(sd, &state); return 0; err_cleanup_media_entity: diff -Naur --no-dereference a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c --- a/drivers/staging/media/tegra-video/csi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/tegra-video/csi.c 2022-01-06 12:45:53.830318172 -0500 @@ -64,7 +64,7 @@ * V4L2 Subdevice Pad Operations */ static int csi_enum_bus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) @@ -79,7 +79,7 @@ } static int csi_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct tegra_csi_channel *csi_chan = to_csi_chan(subdev); @@ -127,7 +127,7 @@ } static int csi_enum_framesizes(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { unsigned int i; @@ -154,7 +154,7 @@ } static int csi_enum_frameintervals(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { struct tegra_csi_channel *csi_chan = to_csi_chan(subdev); @@ -181,7 +181,7 @@ } static int csi_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct tegra_csi_channel *csi_chan = to_csi_chan(subdev); diff -Naur --no-dereference a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c --- a/drivers/staging/media/tegra-video/tegra210.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/tegra-video/tegra210.c 2022-01-06 12:45:53.830318172 -0500 @@ -459,7 +459,7 @@ VI_INCR_SYNCPT_NO_STALL); /* start the pipeline */ - ret = media_pipeline_start(&chan->video.entity, pipe); + ret = media_pipeline_start(chan->video.entity.pads, pipe); if (ret < 0) goto error_pipeline_start; @@ -500,7 +500,7 @@ error_kthread_start: tegra_channel_set_stream(chan, false); error_set_stream: - media_pipeline_stop(&chan->video.entity); + media_pipeline_stop(chan->video.entity.pads); error_pipeline_start: tegra_channel_release_buffers(chan, VB2_BUF_STATE_QUEUED); return ret; @@ -522,7 +522,7 @@ tegra_channel_release_buffers(chan, VB2_BUF_STATE_ERROR); tegra_channel_set_stream(chan, false); - media_pipeline_stop(&chan->video.entity); + media_pipeline_stop(chan->video.entity.pads); } /* diff -Naur --no-dereference a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c --- a/drivers/staging/media/tegra-video/vi.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/staging/media/tegra-video/vi.c 2022-01-06 12:45:53.830318172 -0500 @@ -492,7 +492,7 @@ const struct tegra_video_format *fmtinfo; struct v4l2_subdev *subdev; struct v4l2_subdev_format fmt; - struct v4l2_subdev_pad_config *pad_cfg; + struct v4l2_subdev_state *sd_state; struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -506,8 +506,8 @@ if (!subdev) return -ENODEV; - pad_cfg = v4l2_subdev_alloc_pad_config(subdev); - if (!pad_cfg) + sd_state = v4l2_subdev_alloc_state(subdev); + if (!sd_state) return -ENOMEM; /* * Retrieve the format information and if requested format isn't @@ -531,26 +531,26 @@ * If not available, try to get crop boundary from subdev. */ fse.code = fmtinfo->code; - ret = v4l2_subdev_call(subdev, pad, enum_frame_size, pad_cfg, &fse); + ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse); if (ret) { ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); if (ret) return -EINVAL; - pad_cfg->try_crop.width = sdsel.r.width; - pad_cfg->try_crop.height = sdsel.r.height; + sd_state->pads->try_crop.width = sdsel.r.width; + sd_state->pads->try_crop.height = sdsel.r.height; } else { - pad_cfg->try_crop.width = fse.max_width; - pad_cfg->try_crop.height = fse.max_height; + sd_state->pads->try_crop.width = fse.max_width; + sd_state->pads->try_crop.height = fse.max_height; } - ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &fmt); + ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt); if (ret < 0) return ret; v4l2_fill_pix_format(pix, &fmt.format); tegra_channel_fmt_align(chan, pix, fmtinfo->bpp); - v4l2_subdev_free_pad_config(pad_cfg); + v4l2_subdev_free_state(sd_state); return 0; } diff -Naur --no-dereference a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c --- a/drivers/tty/serial/8250/8250_omap.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/tty/serial/8250/8250_omap.c 2022-01-06 12:45:53.830318172 -0500 @@ -1686,10 +1686,10 @@ } add_preferred_console("ttyS", idx, options); - pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n", + pr_info("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n", idx, idx); - pr_err("This ensures that you still see kernel messages. Please\n"); - pr_err("update your kernel commandline.\n"); + pr_info("This ensures that you still see kernel messages. Please\n"); + pr_info("update your kernel commandline.\n"); return 0; } console_initcall(omap8250_console_fixup); diff -Naur --no-dereference a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c --- a/drivers/tty/serial/8250/8250_port.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/tty/serial/8250/8250_port.c 2022-01-06 12:45:53.830318172 -0500 @@ -555,11 +555,30 @@ */ static void serial8250_clear_fifos(struct uart_8250_port *p) { + unsigned char fcr; + unsigned char clr_mask = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; + if (p->capabilities & UART_CAP_FIFO) { - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(p, UART_FCR, 0); + /* + * Make sure to avoid changing FCR[7:3] and ENABLE_FIFO bits. + * In case ENABLE_FIFO is not set, there is nothing to flush + * so just return. Furthermore, on certain implementations of + * the 8250 core, the FCR[7:3] bits may only be changed under + * specific conditions and changing them if those conditions + * are not met can have nasty side effects. One such core is + * the 8250-omap present in TI AM335x. + */ + fcr = serial_in(p, UART_FCR); + + /* FIFO is not enabled, there's nothing to clear. */ + if (!(fcr & UART_FCR_ENABLE_FIFO)) + return; + + fcr |= clr_mask; + serial_out(p, UART_FCR, fcr); + + fcr &= ~clr_mask; + serial_out(p, UART_FCR, fcr); } } @@ -1463,7 +1482,7 @@ * Enable previously disabled RX interrupts. */ if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { - serial8250_clear_and_reinit_fifos(p); + serial8250_clear_fifos(p); p->ier |= UART_IER_RLSI | UART_IER_RDI; serial_port_out(&p->port, UART_IER, p->ier); diff -Naur --no-dereference a/drivers/tty/serial/8250/8250_pruss.c b/drivers/tty/serial/8250/8250_pruss.c --- a/drivers/tty/serial/8250/8250_pruss.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/tty/serial/8250/8250_pruss.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Serial Port driver for PRUSS UART on TI platforms + * + * Copyright (C) 2020-2021 by Texas Instruments Incorporated - http://www.ti.com/ + * Author: Bin Liu + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "8250.h" + +#define DEFAULT_CLK_SPEED 192000000 + +/* extra registers */ +#define PRUSS_UART_PEREMU_MGMT 12 +#define PRUSS_UART_TX_EN BIT(14) +#define PRUSS_UART_RX_EN BIT(13) +#define PRUSS_UART_FREE_RUN BIT(0) + +#define PRUSS_UART_MDR 13 +#define PRUSS_UART_MDR_OSM_SEL_MASK BIT(0) +#define PRUSS_UART_MDR_16X_MODE 0 +#define PRUSS_UART_MDR_13X_MODE 1 + +struct pruss8250_info { + int type; + int line; +}; + +static inline void uart_writel(struct uart_port *p, u32 offset, int value) +{ + writel(value, p->membase + (offset << p->regshift)); +} + +static int pruss8250_startup(struct uart_port *port) +{ + int ret; + + uart_writel(port, PRUSS_UART_PEREMU_MGMT, 0); + + ret = serial8250_do_startup(port); + if (!ret) + uart_writel(port, PRUSS_UART_PEREMU_MGMT, PRUSS_UART_TX_EN | + PRUSS_UART_RX_EN | + PRUSS_UART_FREE_RUN); + return ret; +} + +static unsigned int pruss8250_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) +{ + unsigned int uartclk = port->uartclk; + unsigned int div_13, div_16; + unsigned int abs_d13, abs_d16; + u16 quot; + + /* Old custom speed handling */ + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { + quot = port->custom_divisor & UART_DIV_MAX; + if (port->custom_divisor & (1 << 16)) + *frac = PRUSS_UART_MDR_13X_MODE; + else + *frac = PRUSS_UART_MDR_16X_MODE; + + return quot; + } + + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); + div_13 = div_13 ? : 1; + div_16 = div_16 ? : 1; + + abs_d13 = abs(baud - uartclk / 13 / div_13); + abs_d16 = abs(baud - uartclk / 16 / div_16); + + if (abs_d13 >= abs_d16) { + *frac = PRUSS_UART_MDR_16X_MODE; + quot = div_16; + } else { + *frac = PRUSS_UART_MDR_13X_MODE; + quot = div_13; + } + + return quot; +} + +static void pruss8250_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + serial8250_do_set_divisor(port, baud, quot, quot_frac); + /* + * quot_frac holds the MDR over-sampling mode + * which is set in pruss8250_get_divisor() + */ + quot_frac &= PRUSS_UART_MDR_OSM_SEL_MASK; + serial_port_out(port, PRUSS_UART_MDR, quot_frac); +} + +static int pruss8250_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct uart_8250_port port8250; + struct uart_port *up = &port8250.port; + struct pruss8250_info *info; + struct resource resource; + unsigned int port_type; + struct clk *clk; + int ret; + + port_type = (unsigned long)of_device_get_match_data(&pdev->dev); + if (port_type == PORT_UNKNOWN) + return -EINVAL; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + memset(&port8250, 0, sizeof(port8250)); + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_err(&pdev->dev, "invalid address\n"); + return ret; + } + + ret = of_alias_get_id(np, "serial"); + if (ret > 0) + up->line = ret; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + up->uartclk = DEFAULT_CLK_SPEED; + } else { + up->uartclk = clk_get_rate(clk); + devm_clk_put(&pdev->dev, clk); + } + + up->dev = &pdev->dev; + up->mapbase = resource.start; + up->mapsize = resource_size(&resource); + up->type = port_type; + up->iotype = UPIO_MEM; + up->regshift = 2; + up->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | + UPF_FIXED_TYPE | UPF_IOREMAP; + up->irqflags |= IRQF_SHARED; + up->startup = pruss8250_startup; + up->rs485_config = serial8250_em485_config; + up->get_divisor = pruss8250_get_divisor; + up->set_divisor = pruss8250_set_divisor; + + ret = of_irq_get(np, 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "missing irq\n"); + return ret; + } + + up->irq = ret; + spin_lock_init(&port8250.port.lock); + port8250.capabilities = UART_CAP_FIFO | UART_CAP_AFE; + + ret = serial8250_register_8250_port(&port8250); + if (ret < 0) + goto err_dispose; + + info->type = port_type; + info->line = ret; + platform_set_drvdata(pdev, info); + + return 0; + +err_dispose: + irq_dispose_mapping(port8250.port.irq); + return ret; +} + +static int pruss8250_remove(struct platform_device *pdev) +{ + struct pruss8250_info *info = platform_get_drvdata(pdev); + + serial8250_unregister_port(info->line); + return 0; +} + +static const struct of_device_id pruss8250_table[] = { + { .compatible = "ti,pruss-uart", .data = (void *)PORT_16550A, }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, pruss8250_table); + +static struct platform_driver pruss8250_driver = { + .driver = { + .name = "pruss8250", + .of_match_table = pruss8250_table, + }, + .probe = pruss8250_probe, + .remove = pruss8250_remove, +}; + +module_platform_driver(pruss8250_driver); + +MODULE_AUTHOR("Bin Liu + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PSUART_NAME "ttySPRU" + +#define PSUART_FW_MAGIC_NUMBER 0x54524155 /* "UART" */ +#define MAX_UART_PORTS 3 +#define DRV_TOTAL_PORTS (MAX_UART_PORTS * 2) /* 2 PRUs */ +#define PORT_MMR_BASE 0x14 +#define PORT_MMR_LEN 0x14 +#define FIFO_BASE 0x100 +#define FIFO_SIZE 256 +#define BPC 2 /* bytes per char */ + +/* hw flow control threshold */ +#define RX_FIFO_THRES_SHIFT 16 +#define RX_FIFO_THRES ((FIFO_SIZE - 16) << RX_FIFO_THRES_SHIFT) +#define RX_FIFO_THRES_MASK (0xff << RX_FIFO_THRES_SHIFT) + +/* global registers */ +#define PSUART_FW_MAGIC 0x00 +#define PSUART_FW_VERSION 0x08 +#define PSUART_FW_GCFG 0x50 +#define PSUART_FW_INITED BIT(1) + +/* uart port registers */ +#define PPORT_ENABLE 0x00 +#define PPORT_STATUS 0x01 +#define PPORT_ENABLED ((BIT(1)) | (BIT(0))) + +#define PPORT_CFG 0x04 +#define PPORT_FIFO_POS 0x08 +#define PPORT_TXFIFO_POS 0x08 /* 16-bit register */ +#define PPORT_TXFIFO_WRITE 0x09 /* 8-bit register */ +#define PPORT_RXFIFO_POS 0x0a /* 16-bit register */ +#define PPORT_RXFIFO_READ 0x0a /* 8-bit register */ +#define PPORT_TX_CFG 0x0c +#define PPORT_TX_INTR_CTRL 0x0d /* 8-bit register */ +#define PPORT_RX_CFG 0x10 +#define PPORT_RX_INTR_CTRL 0x11 /* 8-bit register */ + +/* PPORT_CFG register bits */ +#define PPORT_CFG_HWFLOW_EN BIT(12) +#define PPORT_CFG_PARADD BIT(10) +#define PPORT_CFG_PAR_EN BIT(9) +#define PPORT_CFG_CSTOPB BIT(8) + +#define PPORT_CFG_CS6 0x1 +#define PPORT_CFG_CS7 0x2 +#define PPORT_CFG_CS8 0x3 +#define PPORT_CFG_CSSHIFT 4 + +#define PPORT_CFG_B600 0x1 +#define PPORT_CFG_B1200 0x2 +#define PPORT_CFG_B2400 0x3 +#define PPORT_CFG_B4800 0x4 +#define PPORT_CFG_B9600 0x5 +#define PPORT_CFG_B19200 0x7 +#define PPORT_CFG_B38400 0x9 +#define PPORT_CFG_B57600 0xa +#define PPORT_CFG_B115200 0xb +#define PPORT_CFG_BSHIFT 0 +#define PPORT_MAX_BAUD 115200 + +/* rx character info bits */ +#define PPORT_RX_CHAR_PE BIT(15) +#define PPORT_RX_CHAR_FE BIT(14) + +struct psuart_port { + struct uart_port port; + void __iomem *mbase; + void __iomem *tx_fifo; + void __iomem *rx_fifo; +}; + +struct pru_swuart { + struct device *dev; + struct rproc *pru; + struct pruss *pruss; + enum pruss_pru_id pru_id; + struct pruss_mem_region mem; +}; + +struct pport_pins { + u8 tx; + u8 rx; + u8 cts; + u8 rts; +}; + +union fifo_pos { + u16 pos; + struct { + u8 tail; /* read pointer */ + u8 head; /* write pointer */ + } s; +}; + +static struct psuart_port pports[DRV_TOTAL_PORTS]; + +static inline struct psuart_port *up_to_pport(struct uart_port *up) +{ + return container_of(up, struct psuart_port, port); +} + +static inline u32 psuart_readl(struct pru_swuart *pu, u32 reg) +{ + return readl(pu->mem.va + reg); +} + +static inline void psuart_writel(struct pru_swuart *pu, u32 reg, u32 val) +{ + writel(val, pu->mem.va + reg); +} + +static inline u8 pport_readb(struct psuart_port *pp, u32 reg) +{ + return readb(pp->mbase + reg); +} + +static inline void pport_writeb(struct psuart_port *pp, u32 reg, u8 val) +{ + writeb(val, pp->mbase + reg); +} + +static inline u16 pport_readw(struct psuart_port *pp, u32 reg) +{ + return readw(pp->mbase + reg); +} + +static inline u32 pport_readl(struct psuart_port *pp, u32 reg) +{ + return readl(pp->mbase + reg); +} + +static inline void pport_writel(struct psuart_port *pp, u32 reg, u32 val) +{ + writel(val, pp->mbase + reg); +} + +static inline int pport_is_fifo_empty(union fifo_pos *pos) +{ + return pos->s.head == pos->s.tail; +} + +static void pport_rx_chars(struct psuart_port *pp) +{ + struct uart_port *up = &pp->port; + union fifo_pos fifo; + u16 ch; + int i, total; + + fifo.pos = pport_readw(pp, PPORT_RXFIFO_POS); + total = CIRC_CNT(fifo.s.head, fifo.s.tail, FIFO_SIZE) / BPC; + if (!total) + return; + + for (i = 0; i < total; i++) { + ch = readw(pp->rx_fifo + fifo.s.tail); + fifo.s.tail += BPC; + + if (ch & PPORT_RX_CHAR_PE) + up->icount.parity++; + if (ch & PPORT_RX_CHAR_FE) + up->icount.frame++; + + uart_insert_char(up, 0, 0, ch, TTY_NORMAL); + } + + up->icount.rx += total; + pport_writeb(pp, PPORT_RXFIFO_READ, fifo.s.tail); + tty_flip_buffer_push(&up->state->port); +} + +static void pport_tx_chars(struct psuart_port *pp) +{ + struct uart_port *up = &pp->port; + union fifo_pos fifo; + struct circ_buf *xmit = &up->state->xmit; + int count; + + fifo.pos = pport_readw(pp, PPORT_TXFIFO_POS); + count = CIRC_SPACE(fifo.s.head, fifo.s.tail, FIFO_SIZE) / BPC; + if (!count) + return; + + if (up->x_char) { + writew(up->x_char, pp->tx_fifo + fifo.s.head); + fifo.s.head += BPC; + pport_writeb(pp, PPORT_TXFIFO_WRITE, fifo.s.head); + up->icount.tx++; + up->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(up)) + return; + + do { + writew(xmit->buf[xmit->tail], pp->tx_fifo + fifo.s.head); + fifo.s.head += BPC; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + pport_writeb(pp, PPORT_TXFIFO_WRITE, fifo.s.head); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(up); +} + +static irqreturn_t pport_handle_irq(int irq, void *pru_port) +{ + struct psuart_port *pp = (struct psuart_port *)pru_port; + int rx, tx; + + rx = pport_readb(pp, PPORT_RX_INTR_CTRL); + if (rx) { + pport_writeb(pp, PPORT_RX_INTR_CTRL, 0); + pport_rx_chars(pp); + } + + tx = pport_readb(pp, PPORT_TX_INTR_CTRL); + if (tx) { + pport_tx_chars(pp); + pport_writeb(pp, PPORT_TX_INTR_CTRL, 1); + } + + if (rx) + pport_writeb(pp, PPORT_RX_INTR_CTRL, 1); + + return IRQ_HANDLED; +} + +static unsigned int pport_tx_empty(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + union fifo_pos tx; + + tx.pos = pport_readw(pp, PPORT_TXFIFO_POS); + + return pport_is_fifo_empty(&tx) ? TIOCSER_TEMT : 0; +} + +static unsigned int pport_get_mctrl(struct uart_port *up) +{ + return up->mctrl; +} + +/* the hardware flow control doesn't require any software assistance */ +static void pport_set_mctrl(struct uart_port *up, unsigned int mctrl) +{ +}; + +static void pport_stop_tx(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + + pport_writeb(pp, PPORT_TX_INTR_CTRL, 0); +} + +static void pport_start_tx(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + + pport_writeb(pp, PPORT_TX_INTR_CTRL, 0); + pport_tx_chars(pp); + pport_writeb(pp, PPORT_TX_INTR_CTRL, 1); +} + +static void pport_stop_rx(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + + pport_writeb(pp, PPORT_RX_INTR_CTRL, 0); +} + +static void pport_start_rx(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + + pport_writeb(pp, PPORT_RX_INTR_CTRL, 0); + pport_rx_chars(pp); + pport_writeb(pp, PPORT_RX_INTR_CTRL, 1); +} + +static void pport_throttle(struct uart_port *up) +{ + pport_stop_rx(up); +} + +static void pport_unthrottle(struct uart_port *up) +{ + pport_start_rx(up); +} + +/* line break is not supported */ +static void pport_break_ctl(struct uart_port *up, int break_state) +{ +} + +/* software flow control currently not supported */ +static void pport_set_termios(struct uart_port *up, struct ktermios *termios, + struct ktermios *old) +{ + struct psuart_port *pp = up_to_pport(up); + tcflag_t cflag; + unsigned int baud; + u32 cfg; + + /* reset all fields except hw flow control settings */ + cfg = pport_readl(pp, PPORT_CFG); + cfg &= RX_FIFO_THRES_MASK; + + cflag = termios->c_cflag; + switch (cflag & CSIZE) { + case CS5: + break; + case CS6: + cfg |= (PPORT_CFG_CS6 << PPORT_CFG_CSSHIFT); + break; + case CS7: + cfg |= (PPORT_CFG_CS7 << PPORT_CFG_CSSHIFT); + break; + case CS8: + default: + cfg |= (PPORT_CFG_CS8 << PPORT_CFG_CSSHIFT); + break; + } + + if (cflag & PARENB) { + cfg |= PPORT_CFG_PAR_EN; + cfg |= (cflag & PARODD) ? PPORT_CFG_PARADD : 0; + } + + cfg |= (cflag & CSTOPB) ? PPORT_CFG_CSTOPB : 0; + + if (cflag & CRTSCTS) { + cfg |= PPORT_CFG_HWFLOW_EN; + /* + * Setting TIOCM_CTS here to prevent core uart_change_speed() + * calls ops->stop_tx() when hw flow control is enabled. + */ + up->mctrl |= TIOCM_CTS; + } else { + up->mctrl &= ~TIOCM_CTS; + } + + switch (cflag & CBAUD) { + case B300: + break; + case B600: + cfg |= (PPORT_CFG_B600 << PPORT_CFG_BSHIFT); + break; + case B1200: + cfg |= (PPORT_CFG_B1200 << PPORT_CFG_BSHIFT); + break; + case B2400: + cfg |= (PPORT_CFG_B2400 << PPORT_CFG_BSHIFT); + break; + case B4800: + cfg |= (PPORT_CFG_B4800 << PPORT_CFG_BSHIFT); + break; + case B9600: + default: + cfg |= (PPORT_CFG_B9600 << PPORT_CFG_BSHIFT); + break; + case B19200: + cfg |= (PPORT_CFG_B19200 << PPORT_CFG_BSHIFT); + break; + case B38400: + cfg |= (PPORT_CFG_B38400 << PPORT_CFG_BSHIFT); + break; + case B57600: + cfg |= (PPORT_CFG_B57600 << PPORT_CFG_BSHIFT); + break; + case B115200: + cfg |= (PPORT_CFG_B115200 << PPORT_CFG_BSHIFT); + break; + } + + baud = uart_get_baud_rate(up, termios, old, 0, PPORT_MAX_BAUD); + uart_update_timeout(up, cflag, baud); + + pport_writeb(pp, PPORT_ENABLE, 0); + pport_writel(pp, PPORT_CFG, cfg); + pport_writeb(pp, PPORT_ENABLE, 1); +} + +static int pport_startup(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + int timeout = 100; + + if (up->flags & UPF_HARD_FLOW) { + /* + * CTS is a input-only pin in the firmware, so AUTOCTS is + * not supported. + */ + up->status |= UPSTAT_AUTORTS; + pport_writel(pp, PPORT_CFG, RX_FIFO_THRES); + } + + pport_writeb(pp, PPORT_ENABLE, 1); + while (!(pport_readb(pp, PPORT_STATUS) & PPORT_ENABLED)) { + if (--timeout < 0) { + dev_err(up->dev, "failed to enable port\n"); + return 1; + } + } + + pport_start_rx(up); + return 0; +}; + +static void pport_shutdown(struct uart_port *up) +{ + struct psuart_port *pp = up_to_pport(up); + + pport_writeb(pp, PPORT_ENABLE, 0); +}; + +static void pport_config_port(struct uart_port *up, int flags) +{ + up->type = PORT_PSUART; + up->flags |= UPF_HARD_FLOW; +} + +/* rs485 is unsupported */ +static int pport_rs485_config(struct uart_port *up, struct serial_rs485 *rs485) +{ + return rs485->flags & SER_RS485_ENABLED ? -EOPNOTSUPP : 0; +} + +static const struct uart_ops psuart_port_ops = { + .tx_empty = pport_tx_empty, + .get_mctrl = pport_get_mctrl, + .set_mctrl = pport_set_mctrl, + .stop_tx = pport_stop_tx, + .start_tx = pport_start_tx, + .throttle = pport_throttle, + .unthrottle = pport_unthrottle, + .stop_rx = pport_stop_rx, + .break_ctl = pport_break_ctl, + .startup = pport_startup, + .shutdown = pport_shutdown, + .set_termios = pport_set_termios, + .config_port = pport_config_port, +}; + +static struct uart_driver psuart_port_drv = { + .owner = THIS_MODULE, + .driver_name = "PRU-SWUART", + .dev_name = PSUART_NAME, + .nr = DRV_TOTAL_PORTS, +}; + +static int pport_config_port_pins(struct psuart_port *pp, + struct device_node *np) +{ + struct device *dev = pp->port.dev; + struct pport_pins *pins; + int nr_pins; + int ret; + u32 val; + + nr_pins = of_property_count_u8_elems(np, "ti,pru-swuart-pins"); + + /* CTS/RTS pins are optional */ + if (nr_pins != 2 && nr_pins != 4) { + dev_err(dev, "unexpected number of pins\n"); + return -EINVAL; + } + pp->port.flags = (nr_pins == 4) ? UPF_HARD_FLOW : 0; + + pins = devm_kmalloc(dev, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + /* set non-configured pin value to 0xff */ + memset(pins, 0xff, sizeof(*pins)); + ret = of_property_read_u8_array(np, "ti,pru-swuart-pins", + (u8 *)pins, nr_pins); + if (ret) + return ret; + + ret = of_property_read_u32(np, "interrupts", &val); + if (ret) + return ret; + + val = pins->cts << 24 | pins->tx << 16 | (val & 0xff); + pport_writel(pp, PPORT_TX_CFG, val); + + val = pins->rts << 24 | pins->rx << 16 | (val & 0xff); + pport_writel(pp, PPORT_RX_CFG, val); + + return 0; +} + +static int psuart_init_port(struct pru_swuart *pu, struct device_node *np, + int index) +{ + struct psuart_port *pp; + int port_id; + int ret = 0; + + port_id = pu->pru_id * MAX_UART_PORTS + index; + pp = &pports[port_id]; + if (pp->mbase) { + dev_err(pu->dev, "Error: port[%d] is already initialized\n", + index); + return -EEXIST; + } + + ret = of_irq_get(np, 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(pu->dev, "port[%d]: failed to get irq (%d)\n", + index, ret); + return ret; + } + pp->port.irq = ret; + + ret = request_irq(pp->port.irq, pport_handle_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + dev_name(pu->dev), pp); + if (ret) { + dev_err(pu->dev, "port[%d]: failed to request irq (%d)\n", + index, ret); + return ret; + } + + pp->mbase = pu->mem.va + PORT_MMR_BASE + PORT_MMR_LEN * index; + pp->tx_fifo = pu->mem.va + FIFO_BASE + FIFO_SIZE * 2 * index; + pp->rx_fifo = pp->tx_fifo + FIFO_SIZE; + + pp->port.dev = pu->dev; + pp->port.type = PORT_PSUART; + pp->port.iotype = UPIO_MEM; + pp->port.fifosize = FIFO_SIZE / BPC; + pp->port.ops = &psuart_port_ops; + pp->port.line = port_id; + pp->port.rs485_config = pport_rs485_config; + + ret = pport_config_port_pins(pp, np); + if (ret) { + free_irq(pp->port.irq, pp); + return ret; + } + + ret = uart_add_one_port(&psuart_port_drv, &pp->port); + if (ret) { + dev_err(pu->dev, "adding port[%d] failed (%d)\n", index, ret); + pp->mbase = NULL; + free_irq(pp->port.irq, pp); + } + + return ret; +} + +static int psuart_init_pruss(struct device_node *np, struct pru_swuart *pu) +{ + u32 reg; + int ret = 0; + + pu->pru = pru_rproc_get(np, 0, &pu->pru_id); + if (IS_ERR(pu->pru)) { + ret = PTR_ERR(pu->pru); + if (ret != -EPROBE_DEFER) + dev_err(pu->dev, "failed to get pru (%d)\n", ret); + return ret; + } + + pu->pruss = pruss_get(pu->pru); + if (IS_ERR(pu->pruss)) { + ret = PTR_ERR(pu->pruss); + dev_err(pu->dev, "failed to get pruss handle (%d)\n", ret); + goto put_pru; + } + + ret = pruss_cfg_ocp_master_ports(pu->pruss, 1); + if (ret) { + dev_err(pu->dev, "failed to enable ocp master port (%d)\n", + ret); + goto put_pruss; + } + + if (pu->pru_id >= PRUSS_NUM_PRUS) { + dev_err(pu->dev, "invalid pru id (%d)\n", pu->pru_id); + ret = -EINVAL; + goto put_ocp; + } + + ret = pruss_request_mem_region(pu->pruss, + pu->pru_id ? PRUSS_MEM_DRAM1 : PRUSS_MEM_DRAM0, + &pu->mem); + if (ret) { + dev_err(pu->dev, "failed to get pruss mem region (%d)\n", ret); + goto put_ocp; + } + + /* clear the mem region before firmware runs by rproc_boot() */ + memset_io(pu->mem.va, 0, pu->mem.size); + + ret = rproc_boot(pu->pru); + if (ret) { + dev_err(pu->dev, "failed to boot pru (%d)\n", ret); + goto put_mem; + } + + reg = psuart_readl(pu, PSUART_FW_MAGIC); + if (reg != PSUART_FW_MAGIC_NUMBER) { + dev_err(pu->dev, "invalid firmware magic number\n"); + ret = -EINVAL; + goto put_rproc; + } + + reg = psuart_readl(pu, PSUART_FW_VERSION); + if (reg > 0x01000000) { + dev_err(pu->dev, "unsupported firmware version(0x%x)\n", + reg); + ret = -EINVAL; + goto put_rproc; + } + + reg = psuart_readl(pu, PSUART_FW_GCFG); + if (!(reg & PSUART_FW_INITED)) { + dev_err(pu->dev, "failed to initialize firmware\n"); + ret = -EINVAL; + goto put_rproc; + } + + return ret; + +put_rproc: + rproc_shutdown(pu->pru); +put_mem: + pruss_release_mem_region(pu->pruss, &pu->mem); +put_ocp: + pruss_cfg_ocp_master_ports(pu->pruss, 0); +put_pruss: + pruss_put(pu->pruss); +put_pru: + pru_rproc_put(pu->pru); + + return ret; +} + +static void psuart_free_pruss(struct pru_swuart *pu) +{ + rproc_shutdown(pu->pru); + pruss_release_mem_region(pu->pruss, &pu->mem); + pruss_cfg_ocp_master_ports(pu->pruss, 0); + pruss_put(pu->pruss); + pru_rproc_put(pu->pru); +} + +static const struct of_device_id psuart_dt_ids[] = { + { .compatible = "ti,pru-swuart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, psuart_dt_ids); + +static int psuart_probe(struct platform_device *pdev) +{ + struct pru_swuart *pu; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + int id, ret; + + if (!np) + return -ENODEV; /* we don't support non DT */ + + pu = devm_kzalloc(dev, sizeof(*pu), GFP_KERNEL); + if (!pu) + return -ENOMEM; + + platform_set_drvdata(pdev, pu); + pu->dev = dev; + + ret = psuart_init_pruss(np, pu); + if (ret < 0) + return ret; + + for_each_available_child_of_node(np, child) { + ret = of_property_read_u32(child, "reg", &id); + + if (ret || id < 0 || id >= MAX_UART_PORTS) + continue; + + ret = psuart_init_port(pu, child, id); + if (ret == -EPROBE_DEFER) { + /* + * -EPROBE_DEFER could only happen on the first port + * so no initialized ports to free. + */ + psuart_free_pruss(pu); + return ret; + } else if (ret) { + dev_err(pu->dev, "init port[%d] failed(%d)\n", id, ret); + } + + } + return 0; +} + +static int psuart_remove(struct platform_device *pdev) +{ + struct pru_swuart *pu = platform_get_drvdata(pdev); + struct psuart_port *pp; + int i; + + for (i = 0; i < MAX_UART_PORTS; i++) { + pp = &pports[pu->pru_id * MAX_UART_PORTS + i]; + if (!pp->mbase) + continue; + + uart_remove_one_port(&psuart_port_drv, &pp->port); + pp->mbase = NULL; + free_irq(pp->port.irq, pp); + } + + psuart_free_pruss(pu); + return 0; +} + +static struct platform_driver psuart_driver = { + .probe = psuart_probe, + .remove = psuart_remove, + .driver = { + .name = "pru_swuart", + .of_match_table = psuart_dt_ids, + }, +}; + +static int __init psuart_init(void) +{ + int ret; + + ret = uart_register_driver(&psuart_port_drv); + if (ret) + return ret; + + ret = platform_driver_register(&psuart_driver); + if (ret) + uart_unregister_driver(&psuart_port_drv); + + return ret; +} +module_init(psuart_init); + +static void __exit psuart_exit(void) +{ + platform_driver_unregister(&psuart_driver); + uart_unregister_driver(&psuart_port_drv); +} +module_exit(psuart_exit); + +MODULE_AUTHOR("Bin Liu "); +MODULE_DESCRIPTION("PRU SWUART Driver"); +MODULE_LICENSE("GPL v2"); diff -Naur --no-dereference a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c --- a/drivers/usb/cdns3/cdns3-ti.c 2021-12-17 04:14:42.000000000 -0500 +++ b/drivers/usb/cdns3/cdns3-ti.c 2022-01-06 12:45:53.830318172 -0500 @@ -214,6 +214,7 @@ static const struct of_device_id cdns_ti_of_match[] = { { .compatible = "ti,j721e-usb", }, + { .compatible = "ti,am64-usb", }, {}, }; MODULE_DEVICE_TABLE(of, cdns_ti_of_match); diff -Naur --no-dereference a/firmware/am335x-bone-scale-data.bin b/firmware/am335x-bone-scale-data.bin --- a/firmware/am335x-bone-scale-data.bin 1969-12-31 19:00:00.000000000 -0500 +++ b/firmware/am335x-bone-scale-data.bin 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1 @@ + W#d$ m$$ m$$ l$†$ l$†d$ m$$ m$$ l$†$ l$† \ No newline at end of file diff -Naur --no-dereference a/firmware/am335x-evm-scale-data.bin b/firmware/am335x-evm-scale-data.bin --- a/firmware/am335x-evm-scale-data.bin 1969-12-31 19:00:00.000000000 -0500 +++ b/firmware/am335x-evm-scale-data.bin 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1 @@ + Wd-%d-%+ \ No newline at end of file diff -Naur --no-dereference a/firmware/am335x-pm-firmware.bin b/firmware/am335x-pm-firmware.bin --- a/firmware/am335x-pm-firmware.bin 1969-12-31 19:00:00.000000000 -0500 +++ b/firmware/am335x-pm-firmware.bin 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,34 @@ +™ é í ñ õ ù ý   • • • • • • • • • • • • • • • • • É • • • • • • • • • • • • • • å • E Y ± • • • Å Ù í )=Qey¡µÉJh CôÉs`pG0áDð<¹KD›hpG¿ÊD8K D›`pG¿ÊD8KJh›²`pG,áDœKhZ* ØJëƒëƒ˜h0¿ pG pG¿œ#J$Khhð´#Mð"J/`"NÃóÀp!L"HÃó!JÃó€%7`ÃóÀ#p%pHhJh `Sh ÐHI€è0P`˜D냛hHð¼GiðÑI h)ИIÄë„dh±T`éçI˜ÄQø$@,öÑáç<áD8áD°™¬˜¨0áD4áDœ¸Oðÿ3´HLIJ#``]øK ``pG4áD0áD8áD<áDJh CôÉs`pG0áDJh›²Cê@`pG¿,áDJh#ðÿC`pG¿@áDKJhëƒëƒ|pG¿œKJhëƒëƒX|pG¿œµð×ü$ðÜü F4ð¨ü4,ùÑKöŒJhšBÐÿ÷œÿðrøðòùðþù ðü5 ð~üÿ÷¤ÿ ðøOôÉrI ð½ø ½ Fð‚üà翼X$µÿ÷Çÿ0¿ý翵ÿ÷‘ÿ@¿½¿µÿ÷‹ÿ@¿" ð}ü½è@" ðP¼µÿ÷ÿ" ðrü½è@" ðE¼¿µ$ FðSü F4ðcü4,öÑKöŒJhšBÐðøð›ù ð,ü5 ð)ü ðÜÿ ÿ÷Uÿ@¿½ Fð2ü FðCüæç¼pG¿pG¿pG¿pG¿pG¿2±FɲDø“BûÑpG¿xC!±ø+*úÑFpGøµ LF!h +MI±K«B#`¿Kp¿#` ø½Oê8Fÿ÷×ÿ9Fîç¿ 8µFx8±%Fÿ÷Ûÿø(ùÑ`8½¿øµFxx±i±Fû«FD˜“ðøD + ÿ÷—ÿ…óˆ F°½è0@°pG¿T€$”$K`pG¿T-éðO$F‹F'F‘°®Feà(tÐÿ÷rÿ¶FvFø ë%(!FòÑ/òÑ;FOð :Fžø— ñ# u¼ñWòèßèðÿææææææ æ%.........ææææææææææææææææææææææææææææææPææææææææææUËæææÞË^æcæm|Žæ“Æ\ææ÷æú F°½èð + Þ#ô€iñÚÚ@ñé ñ#ðÓø ñ ô€oкñ¼¿IôyÊñ + ô€^#rFø0ŽF ñSF”FàfF³ûðòû3·Lã\Y±£ña ¼ñŒ¿Oð Oð £ë Û²´F ø;F*åÑ«óœ“ô€sqF“ +кñЛ+ðï›+ðïôs“ðE›ð€ ñ“ð‹- Lÿ÷“þ›S±ºñЛ+ðù›+ð¹ñðM0xP±ÿ÷~þø 4(øÑ¹ñ?ôü®›+?ôø®š£ë ¹ñ÷ñ® ñÿ6>@Fÿ÷dþsùÑLDææ cçpxCð@.F÷æpxCô€s.Fòæÿ÷Qþ4ÕæñcÛø ñ (Àò¸px.FàæpxCô€c.FÛæpxCð€.FÖæpxCôc.FÑæôj@ðcpxOð0.FÈæ–ø ñ0 ©ñ0 (˜¿ôjò¹€.F ëŒ ëL øŸ¬ñ0 ©ñ0 (óÙHFºñ ¿eFbF•¦æ Cô€SçÛø ñ4ÿ÷ûý³FæpxCð.F“æ˜@ñ3#ðpxCð.F‰æÞñì€Ø@ñC"FÛøãÁé# ñ aæ Þæš*@ðCô€s Ûø #ô€i ñ ßæpxCð.FcæÛø ñ‘¹ñð&HF“’ÿ÷¬ýš›ÕB’FØF‚F™¡ë"êâvñÀñÿ;>± ñÿ;@Fÿ÷ý»ñÿ?÷ÑQFHFÿ÷Âý4DDÝø°æpxCð .F+æÞCô€iñ’€Ú@ñÓ€ + ñ#ðñ Óø æ@ñˆ€#ðCð,ç¥ëpFÿ÷˜ý'DïåHÿ÷„ýDÝæHF.FôjOç¿Ä$ô$ð€ AѸñ0Лƒ±š›+“ Ýñÿ9 ñÿ9@Fÿ÷Fý¹ñÿ?÷Ñ›D›+@ðÓ€›+±ºñÐOð ¦æ›ƒ±š›+“ Ýñÿ9 ñÿ9@Fÿ÷'ý¹ñÿ?÷Ñ›D0x(?ô¥­Oð šæÛø  ñ +æ›+?ô®ºñ?ô‹®}æ¸ñ0´ÑpæžëÔšéÔ^çÔ{Õ»ø  ñ æÛø0 ñ `{å + Ûø  ñ æpxCð.FˆåpxÛø .F ñ åHFQFÿ÷ +ýñÿ9D>± ñÿ9@Fÿ÷Õü¹ñÿ?÷Ñ4DÝø°Tå#ô€søævx ñ0 ¦ñ0 (±Fö¥®0FbF.F]åpxCð.FXå›3“æ›3“ æž¹Ô˜·ÔZµÔOð +*Õ»ù  ñ ·å™£ÔZ¡Ô'ÕÛø0 ñ €å ßøXÜæ0 4ÿ÷üæpxžƒð€vB–.F#å^õg¯›ø  ñ ’åZõ^¯›ø  ñ ‰åXõu¯Ûø0 ñ pðä!FOð Îåì$´µƒ°«Sø F“ÿ÷Úü°]øë°pG¿þç¿JKšB ÒÑCD H!ð1FDSøƒBBøùÑH IOðˆB¸¿@ø+úÛÿ÷Ç»¿Ä4*Àþç¿þç¿þç¿þç¿þç¿þç¿þç¿þ翵 ð6ú¹þç ½ñŠA¡õá`pGñŠ@ õáhpG#ABððX¿JB(¸¿0@€ñ`@“@õa@`pG#ABððX¿JB(úó¸¿0J@Bø 0pG€áà#ABððX¿JB(úó¸¿0J@Bø 0pG€âàJhCð`pGíàJhCð`pGíൠÿ÷Äÿ ðùûKh`½¿ ßDµ ÿ÷¶ÿÿ÷0úÿ÷8ú¨±ÿ÷áúFH¹ÿ÷ëú°¹ÿ÷Dú½è@ ÿ÷¿ ÿ÷0û½è@ ÿ÷‡¿ ÿ÷¶ú½è@ ÿ÷¿ Fÿ÷,û½è@ ÿ÷w¿¿µ! ÿ÷†ÿ½è@! 𹻿µ$ Fÿ÷{ÿ F4ÿ÷‹ÿ4,öÑKöŒ JhšBнè@ÿ÷º Fÿ÷hÿ Fÿ÷yÿ½è@ÿ÷ý¹¿¼µ# ÿ÷Zÿ½è@# 𻿵$ ÿ÷Pÿ½è@$ ðƒ»¿µ( ÿ÷Fÿ½è@( ðy»¿µ) ÿ÷<ÿ½è@) ðo»¿µ* ÿ÷2ÿ½è@* ðe»¿µ+ ÿ÷(ÿ½è@+ ð[»¿µ, ÿ÷ÿ½è@, ðQ»¿µ- ÿ÷ÿ½è@- ðG»¿µ. ÿ÷ +ÿ½è@. ð=»¿µ/ ÿ÷ÿ½è@/ ð3»¿µ0 ÿ÷öþ½è@0 ð)»¿µ1 ÿ÷ìþ½è@1 𻿵2 ÿ÷âþ½è@2 𻿵3 ÿ÷Øþ½è@3 ð »¿µ4 ÿ÷Îþ½è@4 𻿵5 ÿ÷Äþÿ÷>ùÿ÷Fùˆ±ÿ÷ïù0¹ÿ÷Vù½è@5 ÿ÷¡¾ ÿ÷Bú½è@5 ÿ÷™¾ ÿ÷Èù½è@5 ÿ÷‘¾¿KhhhÛüÔpG¿KhhhÛüÕpG¿KhSø h#ð`pG¿KhSø hCð`pG¿KöDKh“BÐKöŒ“BÐpGKJ`pGKJ`pG¿¼)ˆ&"K`Z`š`Ú`aZapG¿ KöD Kh“B ÐKöŒ“BÐpG K H +I +J˜aÙabpGKH I J˜aÙabpG¿¼ `)())à)¨)˜) +Jp´jëAëÁmhTø1›h-hÔÐPf`¥``p¼ pG¿ Kp´Ûi„ˆEyYk˜iÚiÄóÁŒ@Åó@$ê‘@hXh!CÅó€!ê‚@žhÜh +CÅóÀi"ê @C1±[iÅó@ ê(Cp¼pG Jy‘ip´Êj hHhð”@Ãó€$êiMi‚@"CÃóÀŒh"ê¨@ÉhCÃó ê‹@Cp¼pG Kë@jëRø0 [h`pG¿ KjëÀChhðpG¿  +Kj“è hhJ@’Ð pGÚh™h hhX@ð¿ pG¿ µDh‰ð¡üÿ÷Yý x˜!¿ðOô€0¿$ÿ÷Uý€ ÿ÷Xý€! Dÿ÷NýKh,нðSùð£ÿ Fð„ÿ½è@ ð¿¿œ8µ7K7JhFëƒëƒ›|Dh+YÑ(‰ðhüàˆÀóðû h.K@³ÀóOðÝú Fÿ÷.ÿF Fÿ÷Xÿ!ÿ÷ÿ!(Fÿ÷ ÿðùðùðÍý ðHÿðbÿ#xÛÕKx+³þçFöu ð¹úÚçð¢úKöDKh“BÐKöŒ“BêÑ ÿ÷]ÿ(åÑJ hCð`ÿ÷{þ ÿ÷bþ Kx+Ùѽè8@ ð¿ð›û¢çKh+ßÑÉ翜þÿ¨¼ áD´8µ"K"JhFëƒëƒ›|Dh‹»(‰ðëû#xÛ(ÕàˆÀóð…ú hK@Ó±ÀóOð]ú Fÿ÷®þF Fÿ÷Øþ!ÿ÷þ!(Fÿ÷‹þðQý ðÌþ Kx[±þçFöu ðBúãçð+úÓçðFûÊç ½è8@𸾜þÿ¨8µ!K!JhFëƒëƒ›|Dhs»(‰ðû#xÛ%ÕàˆÀóð7ú hK@»±ÀóOðú Fÿ÷`þF Fÿ÷Šþ!ÿ÷Aþ!(Fÿ÷=þðý Kx[±þçFöu ð÷ùæçðàùÖçðûúÍç ½è8@ðm¾¿œþÿ¨8µKJhFëƒëƒ›|DhS»(‰ðQûàˆÀóðîù hK@˱ÀóOðÆù Fÿ÷þF Fÿ÷Aþ!ÿ÷øý!(Fÿ÷ôý ð7þ Kx±þç8½Föu ð¬ùäçð³úÑçœþÿ¨µCh؈Àóð¹ù½è@ ð¾8µF lhð ý(‰ð ûàˆÀóð¦ù Fÿ÷×ýF Fÿ÷þ!ÿ÷¸ý(F!ÿ÷´ý#yð+Ð ð„ÿ½è8@ ðí½¿pG¿pµFþ÷Jþ(EÐ(M)L+hëƒëƒÛh+;ИG0Fþ÷ÜþkhxÛÔðDùh‰ð«ú+hëƒëƒ›|C» ðCÿ$ Fÿ÷û F4ÿ÷­û4,öÑKöŒJhšBÑ Fÿ÷Žû Fÿ÷Ÿûþ÷_ÿðßø ÿ÷pû5 ÿ÷mû½èp@ ð¿þçðGú+hëƒëƒ£|+ÏÑË眼pG¿KöDKµh“B!ÐKöŒ“B!Ðÿ÷»ýF ÿ÷ý ÿ÷šý ðýð“ýð5üðAÿð+ÿ Fþ÷dþð ù½è@ ðo½Kh+ÝÐ ÿ÷¨ü ÿ÷‘üÖç¼´µÿ÷ýF ÿ÷oý ÿ÷lý ðSýð üðÿ Fþ÷:þðvù½è@ ðE½¿µÿ÷qýF ÿ÷Sý ÿ÷Pý ð7ýðíû Fþ÷ þ@¿½è@ðY¹¿µÿ÷YýF ÿ÷;ý ÿ÷8ýðÒþð8ý Fþ÷ þðEù½è@ ð½µð=ù½è@ ð ½µÿ÷9ýF ÿ÷ý ÿ÷ýðýð°þ Fþ÷éýð%ù ðöü½è@ 𠼿KFhAð€q`h›üÔpG$@ßDKFh!ð€q`h›üÕpG$@ßD"µK`Z`ÿ÷Eü½è@𠼿œ KHhIµJÃó4` `h JÃó#`ðHüðþýÿ÷4üðüû½è@ÿ÷¼áD¼´@áD¸J hCô3`pG¿páDJ h#ô3`pG¿páDJh C`pG¿páDOôqKJ`h C`pG¿ ßDßDJh#ôs`pGßD8µIKñˆt`d#h+,Ñch+/Ñ#j+2Ñ#i+5Ñci+8Ñ£i+;Ñ£h+>Ñãh+AÑãi+DÑKöŒ9M*hšBGУj+dÑ#k+]Ñãj+MÑKöŒ*hšBPÐ8½! ÿ÷óùch+ÏÐ( ÿ÷íù#j+ÌÐ3 ÿ÷çù#i+ÉÐ- ÿ÷áùci+ÆÐ. ÿ÷Ûù£i+ÃÐ/ ÿ÷Õù£h+ÀÐ* ÿ÷Ïùãh+½Ð, ÿ÷Éùãi+ºÐ2 ÿ÷ÃùKöŒM*hšB·Ñck+´Ð ÿ÷·ùOôqKJ`h C`¨ç0 ÿ÷«ùKöŒ*hšB®Ñ½è8@4 ÿ÷¡¹$ ÿ÷žùç# ÿ÷šù–ç¿D¼ ßDßD"8µ"K! `ÿ÷ù( ÿ÷šù) ÿ÷—ù* ÿ÷”ù+ ÿ÷‘ù, ÿ÷Žù- ÿ÷‹ù. ÿ÷ˆù/ ÿ÷…ù0 ÿ÷‚ù1 ÿ÷ù2 ÿ÷|ù3 ÿ÷yù# ÿ÷vù$ ÿ÷sùKöŒ M,hœBÐ8½ J h#ôs`ÿ÷dù+h£Bóѽè8@4 ÿ÷\¹D¼ßDµðÛùð%ùð½ù Kx ±ÿ÷ŠþðŒù ÿ÷Áú ðtú ðqú ðnú ðkú½è@ ðfº˜µ ðzú ðwú ðtú ðqú ðnú ÿ÷úðIù ð¼üðlù KxC¹ðùøKöDJhšBнÿ÷Vþóç½è@𛹿˜¼8µOöÿs˜BÐF ð¿üF ð•üõ ð?ûF ± F8½ ðœü F8½$ F8½¿ÿ÷à¿Kx±æpG¿˜Kx±æpG¿˜KöŒIð´hINMLH h6hK-h$hh‘B`^``Ü`aÑ I J hhYašað¼pG¿áD¼áD áD@áDDáDHHáDLáDøµÿ÷ËÿKh#¹ Kh;+ Ù J K O NMLIH:`2`+`#``ø½ JhCð€S`ëç¿´°ð?‹àÿ@áDDáDáDáD áD‹  ÿáD8µÿ÷•ÿMKLHIJ+`#`` `h+ Ð+Ð8½Oð`oðbIK``8½%Oðÿ0 JhCô€s`Dø<\Aø@ 8½¿@áDð?DáDHáDLáD°áD áD`áDKöDKh“BÐKöŒ“BÐpGÿ÷·¿ÿ÷¿¿¼KöDJð´h™BÐKöŒKHh]hLŸh`Þh%`iM‡`.`dhŠBÑšiXiIK``ð¼pGKh+àÑ Kh;+ÛØ I h#ð€S `Õ翼HáDáD@áDHáDLáD´°áD ! KF``hAð@`hðAð``h›üÕpG¿ áDKh+¿"KJK`pG° áDµ Kx¹½ ðFû#IJ h ‹@`½è@ðM»¿™¬”qàDµ Kx¹½ ð,û#IJ h ‹@`½è@ð3»¿™¬qàDKh+ÑJhCðC`pG°áDKh+ÑJh#ðC`pG°áD Jð´h,:Ð#hY7ÐPhÃëÃPø3PëÃ.hJh2C*`Pø3PJi.h2C*`Pø3`hÍh2h=C*C2`Pø3P +i.h2C*`Pø3PŽi*h"ê*`Pø3 Íih#ê`KjŠj jC hBüÑTø?ZÈÑð¼pGd#Jðµh.@Ð3hY=жFUhÃëÃUø4 ëÄhÃi C`j‚j hBüÐUø4 ƒih C`jBj hBüÐUø4i +h^ø?"ê +`Uø4pÁh†h:h1C"ê:`Uø4Gi +h"ê +`Uø4@h +h"ê +`ZÃÑð½¿d KÀëÀZh´ëÁÔjë€ h ðAð!`k˜`h+üÑ]øKpGd +KÀëÀZh뀛hëÁÐjð)`ÐpGkh+üÐpG¿dKÀëÀ[hëÀCkhÀ²pGd"Kš`Ú`aZašapGdKöD Kh“B +ÐKöŒ“BÐpG K I +JY``pG JK IhY`*ñÐJ`pG¿¼d' '´p%`%µ K„° ñËéLª#h+´¿ÃóAsÃóSëƒSø °½¿ü&@áDKöD Kh“B ÐKöŒ“BÐpGKH I JƒèpGKHI JƒèpG¿¼€d(<(4(Ø(¬(¤(K(hOðSø! h#ð ¿CðCð`pG¿€8µ F hJ ÐFà8¹(Fÿ÷áÿTøK÷Ñ8½8½ ûç¿KhSø 8±hð ñ°ú€ð@ pG€KhSø h#ðCð`pG¿€KhSø h#ðCð`pG¿€K Yh»ç€K Yhµç€K ™h¯ç€-éðOOð FFhJiKÒø€ßø°á³øÀfMgHgI¾øà(ð-ˆ‡°ˆ ˆ`úŒò£ø’úŽò’ª²sxø{’‚²’вGê'’ÿ÷%ÿFHFÿ÷áþ-¸¿5/¿;F#­•ûóóëÓrRQñ€ÿ)@ó€ÿ!Oô‚r›;ñ€ÿ+@ó…€ÿ#%OôBOöÿwBH€BM€8+€ øˆ>KAI€€€²x*DМFHòkßøø ßøøŽFl09Oôzsà;PÐ ˆíúÔ®øpfxUªø`’²¦©ø 2Fí²ë¬ø°#D®øpøKšB€úÑ5Oôzrtà[Ô:Ð ˆ›²ðöÐOöÿrOðÿ0 K€°½èð®øpr]*ÄÑ ½ø PKI€J €½øPL€½øP%€½ø@€½ø0!ø„ÿ ½ŒBooted version 0x%x of CM3 PM Firmware. $¤$¬$´$¼$[%08x]%s: ERR WARN INFO DEBUG0123456789abcdefghijklmnopqrstuvwxyz(null)0x + ÿÿÿÿ ÿÿÿÿLàD(àDÐàDàD¸àDäàDààDÜàDdàD àD`àDàD,àDÿÿÿÿáD€  áDŒàDpàDœàDáD€  áD˜àDHàDTàDáD€  áD”àD4àD@àDˆàD àD,àDàD\àDhàDàDàD + ÿÿÿÿ ÿÿÿÿ8*ßD ßD0ßDh+ßD@+ßD(ŠßD ˆßD@ˆßD¨ˆßD ˆßD ŒßD ƒßDPˆßD8ßD ”ßDKÀ]¨aeÿÿÿÿáD€  áDà-ßDä-ßDì-ßDáD€  áD .ßD$.ßD,.ßDáD€  áD -ßD¤-ßD¬-ßD`-ßDd-ßDl-ßD -ßD$-ßD,-ßDÿÿÿÿ  ÿÿÿÿ)ßDƒßD“ßD’ßDßD‹ßDˆßDŠßDŒßDßD”ßD+ßDÿÿÿÿ   + ÿÿÿÿàDPàDDàD@àD àDàDàDàDàDHàD,àDàDàDßDßDßDßD  +  + @ßD@ßDàDàD àD àD €€@GNUe‚‰â½geÛWÓ…;°¨q2W¸I…¸I…¬ ¥‰¬ ¥‰”ˆ¡ù”ˆ¡ù|=1|=1ÅpÕapÕaXq¡-dY‘€ ƒ ëÔ€üÿêÔƒüßêÔ…üßêÔ€üßêÔ…üßêÔ€üßlog_buf \ No newline at end of file diff -Naur --no-dereference a/firmware/am335x-pm-firmware.elf b/firmware/am335x-pm-firmware.elf --- a/firmware/am335x-pm-firmware.elf 1969-12-31 19:00:00.000000000 -0500 +++ b/firmware/am335x-pm-firmware.elf 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,388 @@ +ELF(4M4 (4*4*4*ÀQåtd™ é í ñ õ ù ý   • • • • • • • • • • • • • • • • • É • • • • • • • • • • • • • • å • E Y ± • • • Å Ù í )=Qey¡µÉJh CôÉs`pG0áDð<¹KD›hpG¿ÊD8K D›`pG¿ÊD8KJh›²`pG,áDœKhZ* ØJëƒëƒ˜h0¿ pG pG¿œ#J$Khhð´#Mð"J/`"NÃóÀp!L"HÃó!JÃó€%7`ÃóÀ#p%pHhJh `Sh ÐHI€è0P`˜D냛hHð¼GiðÑI h)ИIÄë„dh±T`éçI˜ÄQø$@,öÑáç<áD8áD°™¬˜¨0áD4áDœ¸Oðÿ3´HLIJ#``]øK ``pG4áD0áD8áD<áDJh CôÉs`pG0áDJh›²Cê@`pG¿,áDJh#ðÿC`pG¿@áDKJhëƒëƒ|pG¿œKJhëƒëƒX|pG¿œµð×ü$ðÜü F4ð¨ü4,ùÑKöŒJhšBÐÿ÷œÿðrøðòùðþù ðü5 ð~üÿ÷¤ÿ ðøOôÉrI ð½ø ½ Fð‚üà翼X$µÿ÷Çÿ0¿ý翵ÿ÷‘ÿ@¿½¿µÿ÷‹ÿ@¿" ð}ü½è@" ðP¼µÿ÷ÿ" ðrü½è@" ðE¼¿µ$ FðSü F4ðcü4,öÑKöŒJhšBÐðøð›ù ð,ü5 ð)ü ðÜÿ ÿ÷Uÿ@¿½ Fð2ü FðCüæç¼pG¿pG¿pG¿pG¿pG¿2±FɲDø“BûÑpG¿xC!±ø+*úÑFpGøµ LF!h +MI±K«B#`¿Kp¿#` ø½Oê8Fÿ÷×ÿ9Fîç¿ 8µFx8±%Fÿ÷Ûÿø(ùÑ`8½¿øµFxx±i±Fû«FD˜“ðøD + ÿ÷—ÿ…óˆ F°½è0@°pG¿T€$”$K`pG¿T-éðO$F‹F'F‘°®Feà(tÐÿ÷rÿ¶FvFø ë%(!FòÑ/òÑ;FOð :Fžø— ñ# u¼ñWòèßèðÿææææææ æ%.........ææææææææææææææææææææææææææææææPææææææææææUËæææÞË^æcæm|Žæ“Æ\ææ÷æú F°½èð + Þ#ô€iñÚÚ@ñé ñ#ðÓø ñ ô€oкñ¼¿IôyÊñ + ô€^#rFø0ŽF ñSF”FàfF³ûðòû3·Lã\Y±£ña ¼ñŒ¿Oð Oð £ë Û²´F ø;F*åÑ«óœ“ô€sqF“ +кñЛ+ðï›+ðïôs“ðE›ð€ ñ“ð‹- Lÿ÷“þ›S±ºñЛ+ðù›+ð¹ñðM0xP±ÿ÷~þø 4(øÑ¹ñ?ôü®›+?ôø®š£ë ¹ñ÷ñ® ñÿ6>@Fÿ÷dþsùÑLDææ cçpxCð@.F÷æpxCô€s.Fòæÿ÷Qþ4ÕæñcÛø ñ (Àò¸px.FàæpxCô€c.FÛæpxCð€.FÖæpxCôc.FÑæôj@ðcpxOð0.FÈæ–ø ñ0 ©ñ0 (˜¿ôjò¹€.F ëŒ ëL øŸ¬ñ0 ©ñ0 (óÙHFºñ ¿eFbF•¦æ Cô€SçÛø ñ4ÿ÷ûý³FæpxCð.F“æ˜@ñ3#ðpxCð.F‰æÞñì€Ø@ñC"FÛøãÁé# ñ aæ Þæš*@ðCô€s Ûø #ô€i ñ ßæpxCð.FcæÛø ñ‘¹ñð&HF“’ÿ÷¬ýš›ÕB’FØF‚F™¡ë"êâvñÀñÿ;>± ñÿ;@Fÿ÷ý»ñÿ?÷ÑQFHFÿ÷Âý4DDÝø°æpxCð .F+æÞCô€iñ’€Ú@ñÓ€ + ñ#ðñ Óø æ@ñˆ€#ðCð,ç¥ëpFÿ÷˜ý'DïåHÿ÷„ýDÝæHF.FôjOç¿Ä$ô$ð€ AѸñ0Лƒ±š›+“ Ýñÿ9 ñÿ9@Fÿ÷Fý¹ñÿ?÷Ñ›D›+@ðÓ€›+±ºñÐOð ¦æ›ƒ±š›+“ Ýñÿ9 ñÿ9@Fÿ÷'ý¹ñÿ?÷Ñ›D0x(?ô¥­Oð šæÛø  ñ +æ›+?ô®ºñ?ô‹®}æ¸ñ0´ÑpæžëÔšéÔ^çÔ{Õ»ø  ñ æÛø0 ñ `{å + Ûø  ñ æpxCð.FˆåpxÛø .F ñ åHFQFÿ÷ +ýñÿ9D>± ñÿ9@Fÿ÷Õü¹ñÿ?÷Ñ4DÝø°Tå#ô€søævx ñ0 ¦ñ0 (±Fö¥®0FbF.F]åpxCð.FXå›3“æ›3“ æž¹Ô˜·ÔZµÔOð +*Õ»ù  ñ ·å™£ÔZ¡Ô'ÕÛø0 ñ €å ßøXÜæ0 4ÿ÷üæpxžƒð€vB–.F#å^õg¯›ø  ñ ’åZõ^¯›ø  ñ ‰åXõu¯Ûø0 ñ pðä!FOð Îåì$´µƒ°«Sø F“ÿ÷Úü°]øë°pG¿þç¿JKšB ÒÑCD H!ð1FDSøƒBBøùÑH IOðˆB¸¿@ø+úÛÿ÷Ç»¿Ä4*Àþç¿þç¿þç¿þç¿þç¿þç¿þç¿þ翵 ð6ú¹þç ½ñŠA¡õá`pGñŠ@ õáhpG#ABððX¿JB(¸¿0@€ñ`@“@õa@`pG#ABððX¿JB(úó¸¿0J@Bø 0pG€áà#ABððX¿JB(úó¸¿0J@Bø 0pG€âàJhCð`pGíàJhCð`pGíൠÿ÷Äÿ ðùûKh`½¿ ßDµ ÿ÷¶ÿÿ÷0úÿ÷8ú¨±ÿ÷áúFH¹ÿ÷ëú°¹ÿ÷Dú½è@ ÿ÷¿ ÿ÷0û½è@ ÿ÷‡¿ ÿ÷¶ú½è@ ÿ÷¿ Fÿ÷,û½è@ ÿ÷w¿¿µ! ÿ÷†ÿ½è@! 𹻿µ$ Fÿ÷{ÿ F4ÿ÷‹ÿ4,öÑKöŒ JhšBнè@ÿ÷º Fÿ÷hÿ Fÿ÷yÿ½è@ÿ÷ý¹¿¼µ# ÿ÷Zÿ½è@# 𻿵$ ÿ÷Pÿ½è@$ ðƒ»¿µ( ÿ÷Fÿ½è@( ðy»¿µ) ÿ÷<ÿ½è@) ðo»¿µ* ÿ÷2ÿ½è@* ðe»¿µ+ ÿ÷(ÿ½è@+ ð[»¿µ, ÿ÷ÿ½è@, ðQ»¿µ- ÿ÷ÿ½è@- ðG»¿µ. ÿ÷ +ÿ½è@. ð=»¿µ/ ÿ÷ÿ½è@/ ð3»¿µ0 ÿ÷öþ½è@0 ð)»¿µ1 ÿ÷ìþ½è@1 𻿵2 ÿ÷âþ½è@2 𻿵3 ÿ÷Øþ½è@3 ð »¿µ4 ÿ÷Îþ½è@4 𻿵5 ÿ÷Äþÿ÷>ùÿ÷Fùˆ±ÿ÷ïù0¹ÿ÷Vù½è@5 ÿ÷¡¾ ÿ÷Bú½è@5 ÿ÷™¾ ÿ÷Èù½è@5 ÿ÷‘¾¿KhhhÛüÔpG¿KhhhÛüÕpG¿KhSø h#ð`pG¿KhSø hCð`pG¿KöDKh“BÐKöŒ“BÐpGKJ`pGKJ`pG¿¼)ˆ&"K`Z`š`Ú`aZapG¿ KöD Kh“B ÐKöŒ“BÐpG K H +I +J˜aÙabpGKH I J˜aÙabpG¿¼ `)())à)¨)˜) +Jp´jëAëÁmhTø1›h-hÔÐPf`¥``p¼ pG¿ Kp´Ûi„ˆEyYk˜iÚiÄóÁŒ@Åó@$ê‘@hXh!CÅó€!ê‚@žhÜh +CÅóÀi"ê @C1±[iÅó@ ê(Cp¼pG Jy‘ip´Êj hHhð”@Ãó€$êiMi‚@"CÃóÀŒh"ê¨@ÉhCÃó ê‹@Cp¼pG Kë@jëRø0 [h`pG¿ KjëÀChhðpG¿  +Kj“è hhJ@’Ð pGÚh™h hhX@ð¿ pG¿ µDh‰ð¡üÿ÷Yý x˜!¿ðOô€0¿$ÿ÷Uý€ ÿ÷Xý€! Dÿ÷NýKh,нðSùð£ÿ Fð„ÿ½è@ ð¿¿œ8µ7K7JhFëƒëƒ›|Dh+YÑ(‰ðhüàˆÀóðû h.K@³ÀóOðÝú Fÿ÷.ÿF Fÿ÷Xÿ!ÿ÷ÿ!(Fÿ÷ ÿðùðùðÍý ðHÿðbÿ#xÛÕKx+³þçFöu ð¹úÚçð¢úKöDKh“BÐKöŒ“BêÑ ÿ÷]ÿ(åÑJ hCð`ÿ÷{þ ÿ÷bþ Kx+Ùѽè8@ ð¿ð›û¢çKh+ßÑÉ翜þÿ¨¼ áD´8µ"K"JhFëƒëƒ›|Dh‹»(‰ðëû#xÛ(ÕàˆÀóð…ú hK@Ó±ÀóOð]ú Fÿ÷®þF Fÿ÷Øþ!ÿ÷þ!(Fÿ÷‹þðQý ðÌþ Kx[±þçFöu ðBúãçð+úÓçðFûÊç ½è8@𸾜þÿ¨8µ!K!JhFëƒëƒ›|Dhs»(‰ðû#xÛ%ÕàˆÀóð7ú hK@»±ÀóOðú Fÿ÷`þF Fÿ÷Šþ!ÿ÷Aþ!(Fÿ÷=þðý Kx[±þçFöu ð÷ùæçðàùÖçðûúÍç ½è8@ðm¾¿œþÿ¨8µKJhFëƒëƒ›|DhS»(‰ðQûàˆÀóðîù hK@˱ÀóOðÆù Fÿ÷þF Fÿ÷Aþ!ÿ÷øý!(Fÿ÷ôý ð7þ Kx±þç8½Föu ð¬ùäçð³úÑçœþÿ¨µCh؈Àóð¹ù½è@ ð¾8µF lhð ý(‰ð ûàˆÀóð¦ù Fÿ÷×ýF Fÿ÷þ!ÿ÷¸ý(F!ÿ÷´ý#yð+Ð ð„ÿ½è8@ ðí½¿pG¿pµFþ÷Jþ(EÐ(M)L+hëƒëƒÛh+;ИG0Fþ÷ÜþkhxÛÔðDùh‰ð«ú+hëƒëƒ›|C» ðCÿ$ Fÿ÷û F4ÿ÷­û4,öÑKöŒJhšBÑ Fÿ÷Žû Fÿ÷Ÿûþ÷_ÿðßø ÿ÷pû5 ÿ÷mû½èp@ ð¿þçðGú+hëƒëƒ£|+ÏÑË眼pG¿KöDKµh“B!ÐKöŒ“B!Ðÿ÷»ýF ÿ÷ý ÿ÷šý ðýð“ýð5üðAÿð+ÿ Fþ÷dþð ù½è@ ðo½Kh+ÝÐ ÿ÷¨ü ÿ÷‘üÖç¼´µÿ÷ýF ÿ÷oý ÿ÷lý ðSýð üðÿ Fþ÷:þðvù½è@ ðE½¿µÿ÷qýF ÿ÷Sý ÿ÷Pý ð7ýðíû Fþ÷ þ@¿½è@ðY¹¿µÿ÷YýF ÿ÷;ý ÿ÷8ýðÒþð8ý Fþ÷ þðEù½è@ ð½µð=ù½è@ ð ½µÿ÷9ýF ÿ÷ý ÿ÷ýðýð°þ Fþ÷éýð%ù ðöü½è@ 𠼿KFhAð€q`h›üÔpG$@ßDKFh!ð€q`h›üÕpG$@ßD"µK`Z`ÿ÷Eü½è@𠼿œ KHhIµJÃó4` `h JÃó#`ðHüðþýÿ÷4üðüû½è@ÿ÷¼áD¼´@áD¸J hCô3`pG¿páDJ h#ô3`pG¿páDJh C`pG¿páDOôqKJ`h C`pG¿ ßDßDJh#ôs`pGßD8µIKñˆt`d#h+,Ñch+/Ñ#j+2Ñ#i+5Ñci+8Ñ£i+;Ñ£h+>Ñãh+AÑãi+DÑKöŒ9M*hšBGУj+dÑ#k+]Ñãj+MÑKöŒ*hšBPÐ8½! ÿ÷óùch+ÏÐ( ÿ÷íù#j+ÌÐ3 ÿ÷çù#i+ÉÐ- ÿ÷áùci+ÆÐ. ÿ÷Ûù£i+ÃÐ/ ÿ÷Õù£h+ÀÐ* ÿ÷Ïùãh+½Ð, ÿ÷Éùãi+ºÐ2 ÿ÷ÃùKöŒM*hšB·Ñck+´Ð ÿ÷·ùOôqKJ`h C`¨ç0 ÿ÷«ùKöŒ*hšB®Ñ½è8@4 ÿ÷¡¹$ ÿ÷žùç# ÿ÷šù–ç¿D¼ ßDßD"8µ"K! `ÿ÷ù( ÿ÷šù) ÿ÷—ù* ÿ÷”ù+ ÿ÷‘ù, ÿ÷Žù- ÿ÷‹ù. ÿ÷ˆù/ ÿ÷…ù0 ÿ÷‚ù1 ÿ÷ù2 ÿ÷|ù3 ÿ÷yù# ÿ÷vù$ ÿ÷sùKöŒ M,hœBÐ8½ J h#ôs`ÿ÷dù+h£Bóѽè8@4 ÿ÷\¹D¼ßDµðÛùð%ùð½ù Kx ±ÿ÷ŠþðŒù ÿ÷Áú ðtú ðqú ðnú ðkú½è@ ðfº˜µ ðzú ðwú ðtú ðqú ðnú ÿ÷úðIù ð¼üðlù KxC¹ðùøKöDJhšBнÿ÷Vþóç½è@𛹿˜¼8µOöÿs˜BÐF ð¿üF ð•üõ ð?ûF ± F8½ ðœü F8½$ F8½¿ÿ÷à¿Kx±æpG¿˜Kx±æpG¿˜KöŒIð´hINMLH h6hK-h$hh‘B`^``Ü`aÑ I J hhYašað¼pG¿áD¼áD áD@áDDáDHHáDLáDøµÿ÷ËÿKh#¹ Kh;+ Ù J K O NMLIH:`2`+`#``ø½ JhCð€S`ëç¿´°ð?‹àÿ@áDDáDáDáD áD‹  ÿáD8µÿ÷•ÿMKLHIJ+`#`` `h+ Ð+Ð8½Oð`oðbIK``8½%Oðÿ0 JhCô€s`Dø<\Aø@ 8½¿@áDð?DáDHáDLáD°áD áD`áDKöDKh“BÐKöŒ“BÐpGÿ÷·¿ÿ÷¿¿¼KöDJð´h™BÐKöŒKHh]hLŸh`Þh%`iM‡`.`dhŠBÑšiXiIK``ð¼pGKh+àÑ Kh;+ÛØ I h#ð€S `Õ翼HáDáD@áDHáDLáD´°áD ! KF``hAð@`hðAð``h›üÕpG¿ áDKh+¿"KJK`pG° áDµ Kx¹½ ðFû#IJ h ‹@`½è@ðM»¿™¬”qàDµ Kx¹½ ð,û#IJ h ‹@`½è@ð3»¿™¬qàDKh+ÑJhCðC`pG°áDKh+ÑJh#ðC`pG°áD Jð´h,:Ð#hY7ÐPhÃëÃPø3PëÃ.hJh2C*`Pø3PJi.h2C*`Pø3`hÍh2h=C*C2`Pø3P +i.h2C*`Pø3PŽi*h"ê*`Pø3 Íih#ê`KjŠj jC hBüÑTø?ZÈÑð¼pGd#Jðµh.@Ð3hY=жFUhÃëÃUø4 ëÄhÃi C`j‚j hBüÐUø4 ƒih C`jBj hBüÐUø4i +h^ø?"ê +`Uø4pÁh†h:h1C"ê:`Uø4Gi +h"ê +`Uø4@h +h"ê +`ZÃÑð½¿d KÀëÀZh´ëÁÔjë€ h ðAð!`k˜`h+üÑ]øKpGd +KÀëÀZh뀛hëÁÐjð)`ÐpGkh+üÐpG¿dKÀëÀ[hëÀCkhÀ²pGd"Kš`Ú`aZašapGdKöD Kh“B +ÐKöŒ“BÐpG K I +JY``pG JK IhY`*ñÐJ`pG¿¼d' '´p%`%µ K„° ñËéLª#h+´¿ÃóAsÃóSëƒSø °½¿ü&@áDKöD Kh“B ÐKöŒ“BÐpGKH I JƒèpGKHI JƒèpG¿¼€d(<(4(Ø(¬(¤(K(hOðSø! h#ð ¿CðCð`pG¿€8µ F hJ ÐFà8¹(Fÿ÷áÿTøK÷Ñ8½8½ ûç¿KhSø 8±hð ñ°ú€ð@ pG€KhSø h#ðCð`pG¿€KhSø h#ðCð`pG¿€K Yh»ç€K Yhµç€K ™h¯ç€-éðOOð FFhJiKÒø€ßø°á³øÀfMgHgI¾øà(ð-ˆ‡°ˆ ˆ`úŒò£ø’úŽò’ª²sxø{’‚²’вGê'’ÿ÷%ÿFHFÿ÷áþ-¸¿5/¿;F#­•ûóóëÓrRQñ€ÿ)@ó€ÿ!Oô‚r›;ñ€ÿ+@ó…€ÿ#%OôBOöÿwBH€BM€8+€ øˆ>KAI€€€²x*DМFHòkßøø ßøøŽFl09Oôzsà;PÐ ˆíúÔ®øpfxUªø`’²¦©ø 2Fí²ë¬ø°#D®øpøKšB€úÑ5Oôzrtà[Ô:Ð ˆ›²ðöÐOöÿrOðÿ0 K€°½èð®øpr]*ÄÑ ½ø PKI€J €½øPL€½øP%€½ø@€½ø0!ø„ÿ ½ŒBooted version 0x%x of CM3 PM Firmware. $¤$¬$´$¼$[%08x]%s: ERR WARN INFO DEBUG0123456789abcdefghijklmnopqrstuvwxyz(null)0x + ÿÿÿÿ ÿÿÿÿLàD(àDÐàDàD¸àDäàDààDÜàDdàD àD`àDàD,àDÿÿÿÿáD€  áDŒàDpàDœàDáD€  áD˜àDHàDTàDáD€  áD”àD4àD@àDˆàD àD,àDàD\àDhàDàDàD + ÿÿÿÿ ÿÿÿÿ8*ßD ßD0ßDh+ßD@+ßD(ŠßD ˆßD@ˆßD¨ˆßD ˆßD ŒßD ƒßDPˆßD8ßD ”ßDKÀ]¨aeÿÿÿÿáD€  áDà-ßDä-ßDì-ßDáD€  áD .ßD$.ßD,.ßDáD€  áD -ßD¤-ßD¬-ßD`-ßDd-ßDl-ßD -ßD$-ßD,-ßDÿÿÿÿ  ÿÿÿÿ)ßDƒßD“ßD’ßDßD‹ßDˆßDŠßDŒßDßD”ßD+ßDÿÿÿÿ   + ÿÿÿÿàDPàDDàD@àD àDàDàDàDàDHàD,àDàDàDßDßDßDßD  +  + @ßD@ßDàDàD àD àD €€@GNUe‚‰â½geÛWÓ…;°¨q2W¸I…¸I…¬ ¥‰¬ ¥‰”ˆ¡ù”ˆ¡ù|=1|=1ÅpÕapÕaXq¡-dY‘€ ƒ ëÔ€üÿêÔƒüßêÔ…üßêÔ€üßêÔ…üßêÔ€üßlog_bufЇ ö‡Üá0*PP%`GP9Î! Æolz¯…@5 ] N  [xSíMêÕNP QçúRPgSPCVPÿXPYPÍZPO[P`\P¦^P)_P0`PzaPßbPecPìePTjP p +³qP +crP H m6 nê rtcoÎ rawsç vs +EwW +4xs +Cyy +–zy +ß}á +~s +Ìs +£€ò +Mù +¸‚% +íƒ% +h„%ìì6áøš‡Preg‡QEïPreg®×]ïÙP”œâ4ìPE!QÿÛ½"$Þ9'.;`$,œ4$ìN0‚ ÿ0œ-o "@œO!.P!9Q"0•Pœ~#ÿP˜$õŸ%d,œ"©ÄœZ%id«Z¤³¬Pc­Pj®Pq¯P&ÿ±ÿÑ&ÿ’0¶'&ÿ´H½1'&ÿÄ`¾J'(ò)Pœ*int"½ÏT,œ &"TxÑœ9å.ù&"Z˜ÒÂ9."&"^°Óè97.K#"`ÈÔ9`.t E€œe+QSÿ€ÛB‰"ŒÞ9.±"€á”œØæáZÄ,ïãPåÿ”åµ"ž +è9.+"먜KjëZ>,ïíP_ÿ¨ï(}"² +ò9‘.¥$°ú%¼œ$åÿ%Øœ-A.§7P/X6œ0€²1²~/q)¢/öZ°/Ô%™/Z¬/º%˜/Â%¨.Ç.‰.0.O.x.L.Ö.9.42jjFÀ‡ ‡àU*`J£intÿØ0aHm3,VÑ·verhnum hT!·Ÿ"ÎhÇ Ç~hÝ +Ç%«&h4'V +ÇÀ,*Mda+hlen,hT-h .M V] ÇÍD1š»2zŸ4š*5Ýà6hª Ç ÍHôlœ‘ iH¸ úÐ ÛæöPt ñ ü $ (.-PO4AP5 8(>3]P0J>€P3QX$R +’TæPt­GH` +œ´ fªv5,÷9]Ĉˆ+àà,²²)½½’}}²²9¨¨:88(®®‘ÃÖ– + é‡ ú‡l–·*`ê±±UJáe“€;0¬ · +‰ ž  $×intÃÐæ± ¸l +œþ +Äî rÖ PóP óxœu汸zP +Ä0 ~Ö PóP†ád P"ì P" Â&œÓæ&±Q–Ö® PóPœá P"¦ì P" j/¨Xœºi1±r¸ä +N! +Ä´ êÖ P0²÷5 PtºáI PtÎÒ Øìn POÞì‚ P5ä• P;ô÷© Pt úá PtïRœv5,€€œqq*88(²²)}}²²9¥¥(91‡ ‡z2!%œßf‡ O‡ø¾H0[g‹œ‹s‹PcÈn%écs”  int +šQ` %(œ×s ×'ret %R +Ý šbý‡ ‚‡<†H0[n ~<<œ~c ~‚f\PwRuwint¬~xœÆstrƶ ™Æá +†> ÓQ ̦~”.œ(strÆ Ÿ%s ™Æ  +®>±9 ?H‘?v?gg¯Ü‡ ؇Äx“[–íÄlœílvlí¾fmtô‘t¼%øretí  ap‘d +ø‘ P”$ Qt R óP2$€$" +œÝ P‘\ Q‘` § P: intQú  )0 œBlvl)íPÌíT÷íôtt~茀$dœœïïnno‡ Q ‡<V[á>*^^%`1 R——padRžqR— retT— +intQ BÿÿL BÿnumB^»B—n B C3 pEÿ cEž ž — ïZ—<8œ‡ fmtZ‡) apZ’–L `«‘´pa‡´‚ a‡chb—nb—& +numc^} +»d—+ tmpd—ï h d—£ g e—Ñ b fž¬9 g3Îreth—¼i%¶Š y%  +* > ÷J“ j¥‚R ’áÿÖ4ËLÀaµ ‚RìÏõJehAÔ€]uh‹tFPxeò䀓uò‹ FPxe¢ ,W€Éu¢ ‹° FPxeà 9™€ÿuà ‹î FPxe€ +çÖ€5u‹’ +FPx XF!FóP- @F šF FF!ÐQ"Py! \<PyQz d \!p g\Pô$!€ +\vPyQz" FP0 "ž#©$ ©%&ž»'» ~(œQ—t œ )fmtQ‡‘p*+apS’‘d+retT—Pˆ P‘PQ‘T&0'»$s ;AÄ$" ,nn,``,¦¦,¬¬áO +‡ ¨ ‡” L÷*`š È” œc§˜ Hœ}Á ©}`Ò ©}Ò Ù, ç , È , ò , þ , +Æ¿ ¿E~  Ú \¯­­ª" ‡  +‡è N  +è œ^ +ì œL +ð œ; + ô œ +&ø œ +,ü œp +2 œ, +8 œCQ ‡ p ‡ (¨*`… °°¬ +Q Ý + 9 í +[ ¦ Ç +ø +  +Ž  À f . Ñ +$ int¯ ° œà ; P> +C  œ val,P reg°‹º + ,$ œ; reg °À ™ ™ .5 ‡ Þ ‡0 ˜ 80 (œfÏ fó f$þ mSint`²X (œµÏ fMó fnþ mSq&€ (œöÏ &f—ó (f¸þ )mSˆ1¨ œÖ 3mSà;¸ œÖ =mS»£ ‡ ‡È Fj *`ø È œ= RÐ :oP@Ö EP@int( 'ä ^œ^ì :®PO ð P ô [ ú f  q  | + ‡îPO ’P2 +" ‡PO( 'P1 +2 ‡:PO8 ¨NPt B ‡POJ CD œ˜L :‡P! V EP![ JX Dœ iLáb :ÎPtj ³âPt ‚ |ˆ :ÿPtŽ ³Pt – |l ]œ œW¤ :FP# ® EP#} d° œ‘¸ :€P$  EP$g kÄ œËÌ :ºP( Ö EP(x rØ œà :ôP) ê EP)‰ yì œ?ô :.P* þ EP* €œy:hP+ EP+ ‡œ³:¢P, &EP,9 Ž(œí0:ÜP- :EP-V •<œ'D:P. NEP.E œPœaX:PP/ bEP/, £dœ›l:ŠP0 vEP0Ö ªxœÕ€:ÄP1 ŠEP1£ ±Œœ”:þP2 žEP2´ ¸ œI¨:8P3 ²EP3Å ¿´œƒ¼:rP4 ÆEP4ç ÆÈFœ/Ð:¬P5 ÔP Ø[ Þf ä| +î‡äP5ô’÷P2 +þ‡ P5P1 ‡P5v5,²²)Ž Ž 00—õõ˜°°™ååš›88(ó󀀜ÂÂqq*Q‚ ‡ Ç ‡š + HHÿ Ý `§ œqid%P'!$œ“id!%Pš '8œÂid'%Pval)HSç 1Pœñid1%Pval3HSö ;h8œ  + H v5H 4 ? ) · O ))I‡ y‡9 *`ꢢUJáe“€;0¬ · +‰ ž  $×intÀ¹¹~ $ +PÖ$% ©ëë¹ + +8!% Ûë¹ +^)'ø$ ÿ¼‡ ñ‡ ¨ *` Q0úR,gS,CV,ÿX,Y,ÍZ,O[,`\,¦^,)_,0`,za,ßb,ec,ìe,Tj,C,T§ŽŸ0"ñ•#,ç$,ý%,«&, Ë',¿(,),ÿ*,’+, #,,$m-,(ñ.,,÷81¦¼2,3,<4,45, L6,`7,,8,®9,:, ÿ;,$’<,(#=,,m>,0ñ?,4–B˘C,qD,o üá,º,‡,,. var, +‰, +),Em,S valm, vm,Rg,x valg, vg,Ìa, vala, va,å[, val[, v[,Ît,ç valt, vt,¤U,  valU, vU,ÙO,1 valO, vO,TI,V valI, vI,\z,{ valz, vz,.¤¬¬ pd¤7 À¦, t§, int 1( œ C2¸Xœ‚?,0œ val?,.pd?7M€,@\œî4€înv‚,Â@ „cÝÒSJ8‡¬nc£üJPi"Ñ  .zhˆI> ü†p"B 3Ê,œHœL4îUv,vVœ€‘qvfŠ ™äœnpd™7Pž,œ–pdž7¯l²¬0œ$ à´¬{¶ó‹Ð•ä ÷{.º‹.•S  +¢58; TúQ<W ñm@s ¦!ˈ"ˆ~Â!x #v5,#;#¢W!sÌ$#× Á#²;# W#xý Á|²‡ =‡È *`”ffäIJ¼€int£8"Ï$÷$Y $˜!$ í"$Ç#$1$$'%$­'$ „($$ )$(+$,y,$0‰-$4 92 +2~ m œOp% " dd +2 + eOz`% TvC‡ ͇  @@ÿ Ý `^WW~@tˆ&G¢‡ ì‡HŽj á 0*`G79µ! Æolz¯…@5 ] N  [xSíMÑÕN7 QÎúR7gS7CV7ÿX7Y7ÍZ7O[7`\7¦^7)_70`7za7ßb7ec7ìe7Tj7pï ³q7 cr7 +H m nÑ rtcoµ rawsÎ vZ Ew> 4xZ Cy` –z` + ïß}È ~Z ÌZ £€Ù Mà ¸‚% íƒ% h„%ÓÓ  È ß… cc¬ +Q Ý + 9 í +[ ¦ Ç +ø +  +Ž  À f . Ñ +$ intêcáUJáe“€;0¬ · +‰ ž  $×C7þ§Ž”c9äIJ¼€ 7\ÿ Ý Ý!H\œ)4!Óâ#)>$7QRV"n-ËP@<$Q˜t8ßP€|-óQ€ŠCŽN”YPtžYP? µºP¤üœ©4PÓlâR©Õ‰S7óDT7'ØUc:ÄÎdÞoäz¾Ptì…ÒPtòåQ0úþPuQ1þ›C¦ Y,P1N&oJP +uj,±F¼fP1ZÇyP0`ÒŒP0rYŸP?vÝ Ñ™ œœÂ4™Ó\⛩·‰œ7ÊD7ó¾ÎdÞoäz/Ptì…CPtòVQ0úoPuQ1þ¦Y‹P1o P +uj± Ý,YP?KË<˜œÂ4ËÓâÍ©a‰Î7tDÏ7Zjdzo€zBPtˆ…VPtŽiQ0–‚PuQ1š¦ªo P +uj°±¶ÝÂYP?kõÔ„œ² 4õÓ°â÷©TDø7ý‰ù7òüd oz@ Pt…T Pt g Q0(€ PuQ1.Y“ P1@o¨ P +ujFÝüXœ +4Ó.â©OfdpYP1ŒpRœó +4Órâ!©ž‰"7±D#7Ï|èj +P3‚Œd’z +Ptš…¤ +Pt · +Q0¨Ð +PuQ1¸óã +P5ÂYP1¤;Äœ 4;ÓP Ž Aȼœ4 hAcâ!iCcÐþð i Pvü*Ž P15¢ Pt&@¶ Pt<5Ê PtB@Þ PtFKJVPa POVa P5`** P;fl"$}„œÙˆˆpœ% àŠc[¢wª‚ˆ P1°‚› P0¶® P?º˜¾£Â®Æ¹ÌÄæ PtÐÏÚ P1èÚ P0#îåP0‰®ø6œÌ à°cyþw‚g P1 ‚z P0 P?£¹ ij Pt$Ï.P1èÊ0.œ[àÌc—6w>‚P1D‚!P0J4P?N£TÄQPt$^Ïä`0œïàæcµfwn‚P1t‚°P0x¹|ð‚ÄÖPt†ÏP1,úœ–Ï P1 6œÅàcÓ¦w®‚`P1´‚sP0¸˜¼¹ÂÄ™PtÆÏ̵P1ÖûP3%ÊÐ&%'v57'¸67'X‡(g)'qˆö'Â%*wwF*¯ ¯ G*C C I*º +º + H*íí,*¢¢+*WW)*//@*]]?*N*ÊÊO*‚‚J*88-*ºº0*QQ<*L*ç ç *''*''C*~~3*––)*õõ˜**ÀÀ=*FFG*¥¥(*²² +)*qq +**}} *²²9*88 +(*¯¯D*llQ*  K***ÌÌ,*ZZ1*  +*ïï.*€€œ*vvA*š š *§ § *-*ÙÙ4ì ó‡ ‡Øàªá 0*P P%`êÎÎUJáe“€;0¬ · +‰ ž  $×int”ÎäIJ¼€ P3ÿ Ý GP9ª! Æolz¯…@5 ] N  [xS íMÆ +ÕNP  Qà +úRP +gSP +CVP +ÿXP +YP +ÍZP +O[P +`\P +¦^P +)_P +0`P +zaP +ßbP +ecP +ìeP +TjP pä ³qP crP H m nÆrtcoªrawsà  vO Ew3 4xO CyU –zU +äPŽvarP‰P)P~u¦= wÎ&¾= ƒÎAAØØCÎWOòØQξ؜ËñØðœ,å²;œTÜ ç ¨D$Lœ¦varFP;Hò Lý P T \ QVÎpœøvXPY\vZ ‚l wl lYÀa΄œJvcP‚\Še ‚• w© l‚]l˜œ§!ùlοvnPà\œp ‚ó w làŽ¬œÄš¦Èœá²//‘Ø8œ@!ì‘ÎS"ŽÂ³,#šœ$J) @%P!$V) T%P($b) h%P3$n) |%P-$z) %P.$†) ¤%P/$’) ¸%P*$ž) Ì%P,$ª) à%P2$Â) ó%P@$Ú) %P0&î) %P4$ô) /%P$'ü) %P#vΜœÈ(¦€˜éx)˜²¯$4 Œ%P!$$4  %P($*4 ´%P)$04 È%P*$64 Ü%P+$<4 ð%P,$B4 %P-$H4 %P.$N4 ,%P/$T4 @%P0$Z4 T%P1$`4 h%P2$f4 |%P3$l4 %P#$r4 ¤%P$$4 ·%P@* 4 %P4'ó¬Hœœ "¾º +û #º ++ËÄò²? ¶J ºU È` $Îk @ %P1$Ôv S %P4$Úv f %P2$àv y %P1$æv Œ %P0*ðv %P3,¯ôdœ… +-Ø(°Û )°+åD$ü î %P3$  +%P0$  +%P1$ ' +%P2$ : +%P4$Œ M +%P1— $$¢ i +%P1(­ 2¸ Nà .F1ÎX:œ1 /–1UÓ0n31 1ret4Î )È2«7%N$jÎ ÷ +%P4$r¢ + %P4$zÙ %Pt€€ 'ˆä %P47 H¾˜œ` +Ë òبœ‚ +å°3è4PD4v6P¼4¸7P´4§8P¸5X‡5ºŽ%611G6ss86××(6ÃÃ'6CCH6ˆˆ96ö ö 688 (6²² )6(( +6’’ +6 +6·· +6ç ç 6~~36ÙÙ46š š 666 +6¥¥(6ÎÎ +6 + + +6 +6ll*6¡¡ 6––)‡ L‡*`ꢢUJáe“€;0¬ · +‰ ž  $×intÀ¹¹~ $ +iÖÀ& ©ëë¹ + +w#¨& Û +1,& Ûnއ Ò‡¸èäá0*PP%`êÎÎUJáe“€;0¬ · +‰ ž  $×int "= +:#= +¥$= +è%= +¸'= +(= +F)= +Å*=[ ô/ Zd var\PD¸dœ$>lœŸvar?PS"DLˆxœØXÀ@ÎXaŽD’z$œLs +‚$Œœ#var„PS6°0œHvarŸPt·¶à œÎ¾4œ‘[P3&fP3Ê44œÉD[¹P3ZfP3(×hœîvarÙP’愜varèP¥±-ÜHv5P¸6Pö‹ÎÔŒ%Î¥¥(––)ÚD‡ º‡ 8;*`”nnäIJ¼€int£8*Ï,÷,Y ,˜!, í",Ç#,1$,'%,­', „(,$ ),(+,,y,,0‰-,4 a!M +Ñ!3 var#, ºv ˆœœ ixn¸*²f{6̲fAß SJ¿ +ÑJ3 varL, Z(”œ + inX œ<à„¨l à³ ~‡¼4œ/ч3¾ Ù‘ð0œTÑ‘3ß ˆš, œŒÑš3! valœ,!! s¥8œ¯ i§n9!ˆ­LTœÜº, 8œ÷¯¼‘hÖ½n‰!~n÷£)h/upEdK3,`•Plv5,¸6,/’œ‡K­e¸¢žÈ‡qØ¢|܇ ð‡U*`”ffäIJ¼€int£8"Ï$÷$Y $˜!$ í"$Ç#$1$$'%$­'$ „($$ )$(+$,y,$0‰-$4 92 +2~ m žO' " dd +2 + qOz ' T0m‡ ‡§`*íMGÕN QDúRgSCVÿXYÍZO[`\¦^)_0`zaßbecìeTjpe³qcrH m“ nG rtco+ rawsD +Çe¸ +‰nep +0e¬ +O-e  +x@e” +LQeˆ +Öae| +9~ed +4‡eX‡ º‡Ø üá0*PP%`… ÔÔ¬ +Q Ý + 9 í +[ ¦ Ç +ø +  +Ž  À f . Ñ +$ int P  +varP ‰P )P V,0 +reg,Ô var.P î5S +reg5Ô var7P…>Ôy ÿ>Ô +id>WרTœS, (œUc¾!nQøcn 6 A6 $ß!Û< +1öëß!0F C<F GSÛF :öëqHÔT &œ¹ÿHÔò!idsH¹B"retJÔy"iKÔÆ"j SPu ¿!W"™ Q%| œûidQWæ"varSP##W^œ œt$id^WP   `6#  $S#Û¦ 1f#öz#ëS##c¸ œí$idcWP0¼ e<Ž#¼ G«#Û :¾#öÒ#ë«##¢hÔ œ%Ü UP1#Ìmà œ7%è UP2#rì œ\%ô UP2&³m€ s!P&­¹„&3¹ˆ'v5P(s°)'˜»!¥(¿Ë)'¦Ö!À',æ!À'cö!¥'B!À'Ø!À/9‡ ü‡*`… ¨¨¬ +Q Ý + 9 í +[ ¦ Ç +ø +  +Ž  À f . Ñ +$ intÆ¿¿~ $ +cÜd( ¯ññ¿ + +B!<( áñ¿ +Ø/-4( /¬‡ ‡z*`… ¨¨¬ +Q Ý + 9 í +[ ¦ Ç +ø +  +Ž  À f . Ñ +$ intÆ¿¿~ $ +˜ÜØ( ¯ññ¿ + + +¦!¬( áñ¿ +,0-¤( ‡ B‡ÚC::§Ž`Ÿ0"Þ•#:ç$:ý%:«&: Ë':¿(:):ÿ*:’+: #,:$m-:(ñ.:,÷81“¼2:3:<4:45: L6:`7:,8:®9::: ÿ;:$’<:(#=:,m>:0ñ?:4–B¸˜C:qD:²É`)A #ß()Þ ûô +ô~“x4)ä䜇 釸 Ô:*`”nnäIJ¼€into,˜val,˜reg,n 1˜»reg1n +D6 }=nè ret?n i@n ¿Nn retPn iQn ÛT˜3bÑbn ÿdn ™en ©fn àgn sclhn %in jn¡nø Ôœ¸¶¸æ#len‘¾o$Ñ’˜Ž$e“ÊX *”˜ •˜  –˜ —˜ ˜˜uü ¦'ŒŸ!0œD¯¬$Ÿ !Xa¯Á$Ÿ!xž~¯Ö$Ÿ! Ÿ›¯ë$Ÿ!À ¸¯%R!諚&%è1?%<G]%Rp%]Ž%h¬%sâ%uš!Š;Œ?&T&u¨!0‹]Œh&u®!ŒŒ}&V!Ñ^!ÜPyuœ!H®ÀŒ’&§&u !h±æŒ½&Ò&»´!ˆ²u´!ˆ9Œç&ü&uÂ! ÀBŒ'&'uÈ!¸º`ŒuÌ!н~ŒÃÐ!è¶õèÓ<'Þ]'»Ð!IÞuÐ!9Œ©'Ë'ŸÔ!@C¯uÒ!XÇŒí'(»"ÃQu"9Œ<(Q(è"pÉ×pøf(‡( £! Ÿ&"U¯Ó(»0"¨^u0"¨9Œè( +)uL"ÈÎýŒ,)A)uN"èÑ#ŒU)j)uX"ÒIŒ~)“)u`"(ÓoŒ§)¼)uj"Ô•ŒÐ)å)up"ÕŒù)*"ÅH#¾[$ÜÜ;$ˆˆ6vi!‡ P‡p @@ÿ Ý `^WW~@· t)G È!‡ }‡Ì"ŒÀá0*`êµµUJáe“€;0¬ · +‰ ž  $×int•9Ý reg9µ +i;µ¤/þ reg/µ +i1µ kC% regCµ ²HµÌ"|œ» ÿHµ"*idsH»\*iJµ•*¼Þ"NŽÈË*Þ"Óä*Ý#Ré7+#ôQ+Á>ÃH#Tœ¥Wœ#$œ!idW>PÝœ#@Yé¤+@ôÁ+–\À#(œkid\>P¼À#`^È,`Ó1,la%è# œ¬ida>„,þì#c¥,8kµ$œÕ$P0ïsµ$œþ($P2í{µ0$œ'<$P0 ‚µD$œPP$P2IaŒg7?»e»” v57!g¤" P¯™!Á¿" 8Ê´ ^Ú´ iê™ wú´ 1 +´ª#‡ ƇÃC::§Ž`Ÿ0"Þ•#:ç$:ý%:«&: Ë':¿(:):ÿ*:’+: #,:$m-:(ñ.:,÷81“¼2:3:<4:45: L6:`7:,8:®9::: ÿ;:$’<:(#=:,m>:0ñ?:4–B¸˜C:qD:Éà)A¢#ߨ)Þ ûô +ô~“2˜)ä% : ; I$ >  I: ; (  I: ;  : ;  : ; I 8  : ;  + : ; I8  : ;  : ; I : ; I I'I'.?: ; 'I : ; I.?: ; ' : ; I4: ; I.: ; '@—B: ; I1X Y  411X Y 1: ; I‰‚•B1 .1@—B!1".?: ; '@—B#1RUX Y $.?: ; 'I@—B%4: ; I&1RUX Y '1(‰‚•B)Š‚‘B*$ > +41,4: ; I-4: ; I.4: ; I?</4: ; I?0I1!I/ 2.?<n: ; % U$ > $ > : ; I : ;  : ; I8  : ; I8 I !I/ +!I .?: ; 'I@—B 4: ; I ‰‚1‰‚1Š‚‘B‰‚1.?: ; 'I‡@—B4: ; I?<4: ; I?.?<n: ; % $ >  I: ; ( ( $ > .?: ; ' : ; I .1@—B +1 ‰‚1 Š‚‘B .?: ; '@—B: ; I1X Y ‰‚1‰‚•B14: ; I‰‚1.?: ; '@—B4: ; I?<.?<n: ; % .?: ; ' .1@—B% U: ; I$ > .?: ; 'I@—B: ; I: ; I4: ; I $ >  + I &I% : ; I$ > .?: ; 'I@—B: ; I‰‚1Š‚‘B$ >  4: ; I +‰‚1  I &I : ; I4: ; I4: ; I?<.?<n% $ > .?: ; 'I@—B: ; I: ; I4: ; I4: ; I 4: ; I +‰‚1 Š‚‘B ‰‚1 $ >  I&I : ;  I8 4 .?: ; '@—B4: ; II!I/ .?<n: ; % $ > : ; I I: ; ( .: ; 'I : ; I: ; I 4: ; I +$ >   I .?: ; 'I@—B : ; I4: ; I4: ; I4: ; I4: ; I +: ;  +: ; +: ; 1X Y1 41141‰‚1Š‚‘B1X Y 1RUX Y  U ‰‚1!‰‚1"&I# : ; $ I8 4% &I'!I/ (.?: ;'I@—B): ;I*+4: ;I,.?<n: ; % $ > .?: ; '‡@—B.?: ; '@—B4: ; I4: ; I‰‚•B1 I 4: ; I?< +I !I/  4: ; I?.?<n: ; % .?: ; '‡@—B% $ >  I: ; ( ( $ > .?: ; 'I@—B‰‚1 Š‚‘B +.?: ; '@—B : ; I : ; I .?<n: ; % .?: ; '@—B: ; I4: ; I4: ; I$ > $ > .?: ; '@—B% $ > .?: ; '@—B4: ; I‰‚1Š‚‘B‰‚1$ >  ‰‚1 +‰‚•B1 ‰‚•B1 4: ; I ‰‚•B14: ; I?<.?<n: ; %  I: ; ( $ > .?: ; '@—B: ; I4: ; I.?: ; '@—B 4: ; I + I &I 4: ; I?< I!% $ >  I: ; ( ( $ > I!I/ &I +4: ; I?% $ >  : ;  : ; I 8  I: ; (  : ; I8 .: ; 'I  : ; I +: ; I 4: ; I $ >  .?: ; '@—B.?: ; 'I@—B: ; I: ; I4: ; I1RUX Y 111RUX Y 1X Y  I.?: ; '@—B: ; I1X Y  411 414: ; I &I!I"!I/ #4: ; I?<$!% $ >  I: ; ( ( $ >  : ;  : ; I8 I +!I/ &I 4: ; I?%  I: ; ( $ > I!I/ &I4: ; I?% : ; I$ >  I: ; (  : ;  : ; I 8  : ;  : ; I8 + : ;  : ; I : ; I  I'I'( $ > .?: ; '@—B: ; I4: ; I‰‚1‰‚1Š‚‘B‰‚•B1‰‚•B14: ; I.?: ;'@—B: ;I4: ;I: ;I .?: ;'@–B!4: ;I".?: ;'@—B#‰‚1$‰‚•B1%4: ; I&&I'4: ; I?<(I)!*.?<n: ; % : ; I$ >  I: ; (  I: ; ( $ >   : ;  + : ; I 8  : ;  : ; I8  : ;  : ; I : ; I I.: ; 'I : ; I: ; I.?: ; ' 4: ; I.?: ;' 4: ;I.1@—B41.?: ; '@—B‰‚1‰‚•B14: ; I.?: ; 'I@—B1X Y 1!: ; I"1X Y # $‰‚1%Š‚‘B&‰‚•B1'‰‚1(1RUX Y ) U*‰‚•B1+41,.?: ;'@—B-1RUX Y..?: ;'I@—B/: ;I04: ;I14: ;I24: ;I34: ; I44: ; I?54: ; I?<6.?<n: ; % $ >  I: ; ( ( $ > I!I/ &I +4: ; I?% : ; I$ >  I: ; (  I: ; ( $ >   : ;  + : ; I8 .?: ; ' .?: ; '  4: ; I.1@—B.?: ; '@—B4: ; I‰‚1.1@—B41 41‰‚•B14: ; I.?: ; '@—B‰‚1Š‚‘B‰‚•B14: ; I4: ; I?<.?<n: ; % $ >  I: ; ( ( $ >  : ;  : ; I8 .: ; '  +: ; I 4: ; I .?: ; '@—B 4: ; I1X Y 1 411RUX Y  U: ; I.?: ; 'I@—B.?: ; '@—B4: ; I4: ; II!I/ &I I4: ; I?<!% $ >  I: ; ( ( $ >  : ;  : ; I8 I +!I/ &I 4: ; I?% $ >  : ;  : ; I 8  : ;  : ; I8  : ;  : ; I : ; I +4: ; I?% : ; I$ >  I: ; (  I: ; ( $ >  .: ; 'I  +: ; I : ; I .: ; '  4: ; I.?: ; 'I .?: ; '@—B.1@—B11 U1 11X Y  4141.: ; 'I@—B: ; I: ; I4: ; I‰‚1Š‚‘B  I!&I".?: ; 'I@—B#.?: ; '@—B$: ; I%‰‚•B1&4: ; I'4: ; I?<(I)!% $ >  I: ; ( ( $ > I!I/ &I +4: ; I?% $ >  I: ; ( ( $ > I!I/ &I +4: ; I?%  I: ; ( $ >  : ;  : ; I8 4: ; I?&I I +!I/ % $ >  I: ; ( ( $ > .: ; ' : ; I .: ; 'I  +.: ; ' 4: ; I  4: ; I: ; I.?: ; 'I@—B: ; I4: ; I4: ; I4: ; I1RUX Y 11 U4141 1X Y ‰‚1‰‚1Š‚‘B1RUX Y 1X Y U!41" I#&I$.?<n: ; %  I: ; ( $ > I!I/ &I4: ; I?% : ; I$ >  I: ; ( ( $ > .: ; '  : ; I +4: ; I .: ; 'I  .: ; 'I@—B : ; I: ; I4: ; I1X Y 1 411X Y  I&I.?: ; '@—B.?: ; '@—B: ; I1RUX Y  U.?: ; 'I@—B‰‚1Š‚‘B4: ; I 4: ; I?<!I"!%  I: ; ( $ >  : ;  : ; I8 4: ; I?&I I +!I/ S2Ÿ 2Ÿ SPóPŸ P (óPŸ8>1Ÿ¾ÎSÎÙœÚ<Sx~5Ÿ<L2Ÿ<L ÿŸLN3ŸLN ÿŸNT4ŸNT ÿŸTV5ŸTV ÿŸhl2Ÿtv2ŸtvS|†P†óPŸ‚†S†P|€1Ÿ†ˆ1Ÿ†ˆPšPš¤óPŸ˜šSš¤P”6Ÿšœ6ŸšœPTPtŸ`TP +óPŸ P$óPŸ PóPŸ$)P):óPŸ<@0Ÿ@JTJMPMNtŸN”Tx€0ŸQ&óQŸRrŸP$S(,P,4S48sŸ(.0Ÿ.0p s"Ÿ08Q P V &P&<V<BPBNTNTtŸ@BPBFTFTUTVóPŸX^P^jWjntŸnvTv~tŸ~‚W‚†tŸX`Q`jqŸ~€qŸ\^P^†WPóPŸ.P.lóPŸ VU:VTVlPPU^@FVFU +ÈUÈÔVÔâvŸò8UQ,QüVvŸV˜ÞY¼ÊV<HYP,>P>@~”ÿŸFP +~”ÿŸ + P \|#Ÿ@BPBF|#ŸFHPHP|#ŸPRPRZ|#ŸZ]PbdPd~|#Ÿ~€P€ˆ|#ŸˆŠPŠ’|#Ÿ’”P”œ|#Ÿœ¦P¦®|#Ÿ®¶P¶ÔYØâYâôPôü|#ŸüP |#ŸP|#ŸP,|#Ÿ,4P4J|#ŸJLPLP|#ŸP^P^n|#ŸnpPpx|#ŸxŒPŒ“|#ŸÞàPàè|#ŸèúPú|#ŸP|#Ÿ$P$'|#Ÿ8LYÊÖ|#Ÿð<|#ŸdtPtx|0Ÿx‚V‚„P„Ž|#Ÿž¤P¤Ö|#ŸÖØPØÞ|#Ÿè,|#Ÿ®¶0Ÿ¶Ì\Øò\¦®R8L\jp0Ÿp„\ +0Ÿ.@ZhnZ +Z.8ZLÊZÐðZ +Z$ZŽžZ¶¾ZÞèZZZ,8Z@‘¤Pn@Ÿè:Ÿ.8‘¤LÊ‘¤ÊÌ:ŸÖ +‘¤$:Ÿdj@ŸŽž‘¤ž¾:ŸÞ葤ø‘¤:Ÿ,8‘¤˜œSœÎ‘ ÎÒSÒ@‘ .8‘ LÊ‘ Öè‘ èðSŽ”‘ ”–S–œ‘ œžSÞè‘ ,8‘ @0Ÿ@‘œ +0Ÿ +䑜ü‘œ$@Y@p‘œpxPxð‘œò²‘œ²ÞVÞ.‘œ8d‘œdjSj†‘œ¢¨S¨‘œ<‘œ<dVdÞ‘œèêPêô‘œôöVö8‘œ@R +R@]RbîRò R:RJRRn“R“˜‘¤˜¤RÞôR'R8LR$<Rj€R„ŽR¾ÄRÖÞRèøR@ Ÿ + Ÿ +0Ÿ*W,.1Ÿ.8W +0ŸQT,ZQZ\T\¢^¢ÙQÙÚtŸÚäT +tŸ +T<>ty"Ÿ>@T@]Q]bTb Q  +tŸ +T>Q>JTJ€Q€ÞTÞ"Q".T68T8jQ¸¼t‘œ"ŸÊÖQÖèTè +Q +T<Q<dTdÀQÀÞTèQ,T@S +SÈY@]Sb S@SJhShnYn“S“˜‘¨˜ªSÞîSîYS'S8LSLPYÊÖYð +Y +S$Y$<SdŽS޾Y¾ÎSÖÞSèøSøY$SFP~0.ŸP\r0.Ÿ\˜q0.ŸF˜òÖF˜‘¤F\Z\^R^fSŒS˜RF’‘´Ÿ’”S”˜‘´ŸF\‘´Ÿ\^V^jvŸjV.8VLÊVÖðVŽžVÞèV,8Vj‚S,0Y02V28vŸ8<V¶ºVºÀ[ÀÆ{ŸÆÌ[fjSjnYntyŸtzY¤¨S¨¬Y¬²yŸ²¸YJPYPVyŸV\Y S &s|Ÿ&(SQq€€€Ø{Ÿq€ÀðØ{Ÿ P $p€€€Ø{Ÿ$&p€ÀðØ{Ÿ&(óPŸP(óPŸp Ÿ(óP Ÿ(BPBPóPŸ(Bp ŸBPóP ŸPjPjxóPŸPjp ŸjxóP Ÿ”0Ÿ”žTž¡P¡¢tŸ¢¶TºÊTp˜P˜ qp„Q„ óQŸ ®P®üóPŸ ¦0Ÿ¦® +p”3&ÿŸ®äu3&ÿŸ¦®p”3&ÿs $Ÿ®äóP#”3&ÿs $Ÿ¦äsäôóP#” ÿ$3&ÿŸæôQü +P +DóPŸü0Ÿüp”ÿŸS`hPhxóPŸxŽ0Ÿ‚ŽQ„†R–˜PP\óPŸT,0 tÿp"Ÿ\vPvžUžÖóPŸÖàUà*óPŸ*-P-0U0XóPŸr$T*XTž P ÖUà$U0XU¤©P s2!Ÿ SXpPpžUžÄóPŸÄÔUÔ×P×ÚUÚôóPŸnàTž P ÄUÚàU¤©Pô P :U:ZóPŸZjUjmPmpUpŒóPŸ +vT:<P<ZUpvU@EPŒ¤P¤ÌUÌðóPŸðúUúýPýUÒ×PÌÎPÎðUP(óPŸpóP#(.P.LULzóPŸ0tTLNPNtURWP€‡P‡VóPŸ<VÎÐ0ŸÐÚTÚÝPÝÞtŸÞT\^P^ŒT¸ºPºàTðòPòT "P"BT`bPbˆT +q@D$!Ÿ +Q" qÿÿÿwŸ"0QR^SdjSž¢Sž¢@=$Ÿ²¶S²¶0Ÿ²¶@=$ŸÀÊPÊÔóPŸÄÆSÄÊPÄÊ +ÿÿŸàâSôøs ÿýŸøSPlóPŸlnPnqDq8óPŸöøS®²s ÿýŸ²´S€ŽPŽ´óPŸ´¸P¸ºóPŸ€¤0Ÿ¤¬P¬´T´¶0Ÿ¶ºT”´U"S +QQ¼ÂSØÞS0ŸS $V$,R.0V0HRHJvJLVL`R`budlSlnrˆœ0Ÿœ¬Sª®Q®´SÂÄQÄÊSØR2P2PóPŸPdPd€óPŸ€†P†˜óPŸ’pÿŸ˜ž0Ÿž 1Ÿ ¢2Ÿ¢¤3Ÿ¤¦4Ÿ¦¬5ŸsHJ$Ÿ"sHC$Ÿ"&ST^P^|óPŸdhS|ŠPŠšUšœóPŸœžUž P ¢óPŸ|‚Q‚ŠTŠžóQŸž¢T|Š0ŸŠŒPŒ’0Ÿ’šPœžPž¢0Ÿ|Š0Ÿž¢0Ÿ¤¬P¬ÄóPŸ°´p3Ÿ´¸P¸¼pŸÄØ p2$€"ÎÒSÎÖ1ŸÎÖ3Ÿàô p2$€"êîSêò2Ÿêò3Ÿ P LTLZVZÄTÄtŸRrŸ"RNTtŸŠœtŸœÔTÆÞRPTtZ¬WœÔW>¤Ÿ>D,ŸDN°ŸNR´ŸRZ¸ŸZ¬w +ÿÿŸœÔw +ÿÿŸ`bPblUflUv¤UœÔUz”S ªS”–syŸ–¢SœžSª¬S€‚r{Ÿ‚ŽQŽ’r{Ÿ ¢Q¢¤r{Ÿ¦¨Q¨ªr{Ÿ¢°°Ÿ¢°0Ÿ°¶´Ÿ¶º¸ŸºÀ¤ŸºÀ €ŸÀÂ0ŸÀ ÿŸÂÄ(ŸÂÄ ÿŸ ¤Ÿ  †Ÿîò0ŸŠ– ÿŸäæ +ésŸæè +èsŸèò +ésŸŠ” +èsŸîò(ŸŠ–(Ÿîò ÿŸŠ– ÿŸTœŸ$T$@p@D œ°àDJNp (Ÿ  ÿŸ8D ÿŸJN0Ÿ&, +érŸ,. +èrŸ.8 +érŸJN +érŸ.2$Ÿ8D(ŸJN(Ÿ8D ÿŸJN ÿŸT`¤ŸT`0Ÿ`d°Ÿ`d‘Ldl´Ÿdl‘Hlr¸Ÿlr‘Drx¤Ÿrx‘Tx€,Ÿx€‘PnPnróPŸrtPt|óPŸBQBpuŸpróQŸr|Q0Ÿ2U2BRr|0Ÿs2$w"0Ÿ1Ÿ @<$rŸ " +ÿÿrŸ",@<$rŸLXt2$w"LX0ŸXZ1ŸZ\@<$sŸ\^ +ÿÿsŸ^l@<$sŸÐà p2$Œ"Ðà0Ÿàâ1Ÿâä@<$sŸäæ +ÿÿsŸæî@<$sŸô p2$Œ"ô0Ÿ1Ÿ@<$rŸ + +ÿÿrŸ +@<$rŸ&P&<óPŸ& p2$Œ"Ü$Žôl` +Ø l–ü9(<†‚Äx5<V¨” Lè ; (‚0 ˜»È Fz#Ï$ü% ¨ÿ-/ù/HŽBØàðN P¸èS 8]WÝX[Ø -``a“bªdø Ô’k lÌ"Œp8:<>xz|~z|~€œž®°¬®²´<@DFJLBDLNFHRTHJTVô``j<DHJ\ ¢¤¦ªÚÜäªÚÜäÚÜäôüþ¨ª¬´PVhnŒ¦ª°œØÜTVXZ^n8<48<>"&>D*,DFLN .0NR "02RTXZZ¤¬²´ºÞäœÔ¢¤¬°°²´¶¤¨º¼¾À¨¬²´À¼¾ÂÄÊÐÐÔöúÔØþØÚÜÞäòŠŽ’–ØÚîòŠŽ’–ÜÞèêÚÜ&<@DJN&*.88<@DJNTVZ\^`VZ\^bd`bdhjlhjlrÐÒÔØÚôôöøüþQvû src/sys_execsrc/includemsg.cmsg.hprcm_core.hpm_state_data.hstddef.hsync.héÓ./µ.“Jâ.KQ†s q  |\œ#j_!J =">!P.1 -";O ).W *  r  "&//1Yu /JwI äB>.B Ãf½.Ã.“±Ó./µ.ÐJ§Ý.!«.ÚXç./¡ éXuwu^ û src/sys_exec/home/a0274052local/toolchains/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/includesrc/includesys_init.cstdint.hrsc_table.hprcm_core.hcm3.hmsg.htrace.hsync.hdebug.hô + 22!-2j0001=>3>ke. `Ç'2¿mû src/sys_execsrc/includesync.cmsg.hcm3.htrace.hprcm_core.hhwmod.hl!/Az / + >-@!1>-N$"=#+5k01=CJ<=$.=f@+û src/sys_exectrace.c!X>û src/libsrc/includestring.ctypes.hK-11…u<û src/libsrc/includeputs.ctypes.h< !!"$!","0z. Ö "=-[1 "t/-w„Vû src/libsrc/includedebug.cprintf.hputs.hÄ%%%/ sX?Ÿ!"!>?0!ÙØcû src/libsrc/includeprintf.cputs.hstring.hstddef.h<Ú .s  .t  =0+. tÖaª~Jô  /0= ‚</=0¨~.°gó>ÓJX=LMY",>2!1.=MMM/-03JJ•~t=;ì.¸~.ù ?„.ý ˆ.)&(/!"=K!l´.K>.C.> "==¾.Ç.>=¶× 2K3y &-//0.ä "ó.</"Lò~.Y;0CMA2-v< <-s<<-y-O$"=#+3l-*=@-]!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>-O!>4?B2-y=M>=MY/\Y/\hYH PJû src/pm_servicessrc/includehwmod_335x.chwmod.h7û src/pm_servicessrc/includepowerdomain.cio.hmsg.hpowerdomain.hprcm_core.hpowerdomain_335x.hpowerdomain_43xx.h (=!!!!\jY/!!!z./!!! ."//-!!/!!#:fu u  !“ ×t© ×J© ótMn k  (Ÿ[Úgfv/-!"V/"XNHû src/includesrc/pm_servicesdpll.hdpll_335x.cLFû src/includesrc/pm_servicesldo.hldo_335x.c<Áû src/pm_servicessrc/includepm_handlers.cmsg.hclockdomain.hhwmod.hpowerdomain.hdpll.hldo.hprcm_core.hrtc.hcm3.htrace.hstddef.hH!!#?0&z !Ah>LZ11 <y.f&z<&S%1>[KNKA?M010>1<UJX0fLZ$">/AN-H<'<<%7%T$#??[KNK??M0>pJxXz< <#+ ž%7%T$#??[KNK??M0rJxXz<<#+²%7%T$#>[KNK??L>sXvX¬#i-@ %$0>ZK??LY?-O4  K2">K0> ta<$ =",4g=?01=@-`J5ä .9#<P>=>>0000>1-g=>>00>1- +J#>=>>0?#- <#>=>/0>1-C!1- <#>=>00>0?-â¶û src/pm_servicessrc/includeprcm_core.cio.hhwmod.hdpll.hldo.hmsg.hpowerdomain.hclockdomain.hcm3.hddr.hi2c.hstddef.hØÀ$">=>$">=á}<!/"0-\"#&/!"!L///0-¿'y @ Ä.#M'y µ Ï.#Mª.Ú<\?#!†1 f!#+#M????????f???Mf>=>=>=>=>=>=>=>=>f=HZ-zžtä œ #á<1@+–!00Ì.¸X0>===>-\!====>>1><.H<0 -t$YK=Y%*@u. [ ‚PJû src/pm_servicessrc/includehwmod_43xx.chwmod.hSgû src/pm_servicessrc/includeddr.cprcm_core.hmsg.hhwmod.hstddef.h¸/y.!'z !"!"z ""!"y !!"!"!!!!"L#1<`$!#!&u.!#!&o.//‚#1/!!"!!!"D>4z.&z &v.!)//"/XhY,^8$<y.=/$$"!">/!"m.KL/0f# p ##!?"/-!#=N?,0”!M>L1-”!M>L1-•L//kL//pû src/pm_servicessrc/includedpll.cdpll.hprcm_core.hdpll_335x.hdpll_43xx.h ö#¬tu/#/!!#/-!/#/!!#/!/#/!/[6x‡M¢kY/!)/!/  !![(x !"?0/ NHû src/includesrc/pm_servicesdpll.hdpll_43xx.cQKû src/includesrc/pm_servicesmsg.hpm_state_data.c û src/pm_servicessrc/includeclockdomain.cio.hprcm_core.hclockdomain_335x.hclockdomain_43xx.hstddef.hclockdomain.hØjY/!zJ/! J!%j.h<#t M #X!;M)D[#u?P.h<J2XT._<#J.Xjj\Vû src/pm_servicessrc/includeclockdomain_43xx.cclockdomain.h\Vû src/pm_servicessrc/includeclockdomain_335x.cclockdomain.h\Vû src/includesrc/pm_servicespowerdomain.hpowerdomain_43xx.c2Bû src/pm_servicessrc/includei2c.cdpll.hø ž.â. s ˜ è ˜.ð.Ý £ ð. )3õX‹<õJ‹.: > <>H"/=>h!>¨J†ú~<•Ó ¼<q. k.Œ.ô~..ô~ ˜.è~<™X-è~ ˜ ’f)`.#.X<œ.ä~.«.Õ~<†.ú~<¨X1Õ~<‰.÷~.«.«<)LFû src/includesrc/pm_servicesldo.hldo_43xx.cÿ€û src/pm_servicessrc/includehwmod.cprcm_core.hhwmod_335x.hhwmod_43xx.hstddef.hhwmod.hÌ"È93;o h/J¾//` h/J3G‚jY/!zJ/!8JZ' Y '.Y L/)f_" ^ ".^ L/$‚b.€  ˆ p  t Ä$ºÄ$%ì$0Éñÿ è ÔÞñÿ  däñÿ 0 |  € ¤  ¨ ´  ¸ Ä ¬êñÿ È à  ä ˜  œ  þñÿ   $4 8L Pd hx ñÿø$ñÿ  ´ ¸ð < @˜ œà äü  D ( 28;<D@ØLñÿ`%Xñÿˆ&cñÿ H  ¤„  , <Ä ÔH Xx „ð ø˜qñÿ Ø}Ùì ðšñ   $\ p€ „” ˜¨ ¬À ÈÔ Ø   ¬ð ôP X¤ ¨´D¸D„ +Éñÿ&Öñÿ ¸ø \ ˆÜ   $ˆ °Ü àô ( 4\ h| „˜HÜH ãñÿ  $ (¸ ¼ì ð 4 8H L„  Ðü&dêdúhl,  ñÿ 'ñÿX)ñÿ Ø  , P  T 7U &˜  œ ´  ¸ Ð  Ô Ü  à è  ì ô €K€R„_ˆü nñÿ4(ñÿ¤(”ñÿ)§ñÿ ø ¨"¨­ñÿ)¸ñÿ Ì"ÀÍ"|D# H#|# œ#¼# À#ä# è#$ $$ $,$ 0$@$ D$T$ŒÔŒÛì”ôñÿ˜)=<&6ÉAÅWá c0y• "‰”¨)8¤¥ü¶™ HÄ)Ó ÜõdçHýu ')%” .>TK˜)Z`%u$¡œŸ!¬• "½´Åí Ù± êlX  • "0.ñ07Y DHeYù Ôcq)”‹Ù œ©³É‰xß• "ï‘• "%Ø(@3• "Cõ TqnI#Tyă1 “)¤œ·Õ„Ê• "Úyß• "ï½4ú 'ˆ Å .• "?1H• "YynU,}ˆp •ñ §• "¸Ì Ù‰pí• "ý $‘.˜;å ^LY (]¡8qe,‚4‹• "œÙT­É ¾Êý ÖY:êÉFûm + • " ½+ á 7 • "H iV 4*` …p Q Ù8˜ 1$´ • "Å • Ó ¹Xä   ð µ +p% +¸ +( +=80 +%Œ> +• "N +()8^ +¸ l +• "} + +• "ž +¹ µ +QÄ +$%<Ò + (á +¹ ì +• "ý +• " ¤($ Y7 © L • "] 9h í y ©– • "§ µ É¼Ê •.Ð 54Ø • "é ø$  9 à)0/ • "@  Q ñÿ\ ¹dp  ~ ù6’ y£ ¨&» a0Ó =ä ë ¼ò #$ÿ a + %L   . ? QO ©Xe d o • "€ é# ‘ ™œ E$· œ Ç - (Ú ù í • "ý ±0}  !¬ *¡; G• "X• "i­Hqˆ&1 (‘¡£$²åèÎõlÙ4(ïd(@ý¬ +…2 HqR^•r%ƒÀŒí ži8§² ÃÙØ%ð• "A é I\)Õ 6¡ˆFE W• "h™~=˜| ™•®¬(,™ÜÄDä¡6ÿ`)0Ål)#1.7A\M% Z• "k• "|Ä’£­­…Æ• "Ö<((êÀ&<øÁ#(‘ÄMT#°,• "startup.c$tzero_loop$dmsg.ca8_version_handlera8_reset_handlercustom_state_datasys_init.csync.ctrace.cstring.cputs.clogbuf_posdebug.clevelsdebug_leveldebug_idxprintf.chex2ascii_dataexception_handlers.crtc.ccm3.cext_intr_handlers.cldo.cldo_regshwmod_335x.cpowerdomain.cpd_statesmpu_bitsper_bitspd_regsdpll_335x.cldo_335x.cpm_handlers.cprcm_core.cprcm_enable_isolation.part.0prcm_disable_isolation.part.1cmd_wake_sourceshwmod_43xx.cddr.cio_ctxdpll.cpower_down_pllsdpll_regspll_modedpll_43xx.cpm_state_data.cclockdomain.cclkdms_state_changeclkdmssleep_clkdmsstandby_clkdmsclockdomain_43xx.cclockdomain_335x.cpowerdomain_43xx.ci2c.cldo_43xx.chwmod.chwmods_state_changehwmodsessential_hwmodsinterconnect_hwmodspowerdomain_335x.cputcharam43xx_interconnect_hwmodsdisable_ioa8_standalone_handlervtp_disableverify_pd_transitionsextint8_handlertimer_syncam335x_per_bitsa8_lp_ds0_handlerreset_handleram43xx_pd_regspm_resetds_restoreget_pd_mpu_stctrl_valprintfam43xx_dpll_regsam43xx_ldo_regsds1_data_aux_data_startcmd_handlersam335x_pd_regsam335x_pg2_power_down_pllsessential_hwmods_enablea8_lp_ds1_handlerdpll_get_divextint27_handlersoc_revclkdms_standby_wakeextint36_handlerddr_am335x_io_suspendidle_v2_dataextint26_handlerpd_state_changepll_lockextint34_handlerextint48_handleri2c_writepd_read_stateplls_power_up_start_dataextint41_handlerprcm_disable_isolationtrace_set_current_posddr_am437x_io_suspendextint4_handlera8_m3_low_power_fastextint50_handlerextint0_handleram335x_clkdmsextint7_handlerbusfault_handlerdisable_master_oscillatorhwmod_init_end_datadebug_set_levelextint45_handlerclear_wake_sourcesa8_standby_handlerextint9_handlerputssystick_handlerpll_bypassam43xx_power_down_pllsds1_data_hsextint40_handlerextint37_handlermsg_readextint12_handlera8_m3_low_power_syncm3_param_resettrace_initstandby_datamemmanage_handlerextint30_handlerm3_firmware_versiontrace_updatea8_wake_ds0_handlerextint5_handleressential_hwmods_disablea8_wake_cpuidle_handlerio_isolationextint31_handlernvic_disable_irqget_master_xtal_khzmsg_cmd_is_validvtt_highextint19_handlerclockdomain_initextint16_handler_start_textsvc_handlera8_i2c_wake_handlerextint53_handlera8_notifyextint1_handlermsg_cmd_needs_triggerclkdms_wakeextint15_handlerset_ddr_reset_end_textclear_ddr_resetextint47_handlerconfigure_wake_sourcesinterconnect_hwmods_disableextint18_handlerdummy_handlerpowerdomain_initds0_data_hsextint52_handleram335x_dpll_regssoc_typevector_tablevprintfddr_io_resumeextint6_handleram43xx_per_bitsrtc_mode_dataextint23_handlerldo_wait_for_onextint20_handlerscr_enable_sleeponexitldo_power_downam335x_hwmodsnvic_clear_irqclkdm_wakeextint38_handlerextint22_handleram335x_standby_clkdmsa8_cpuidle_handlerscr_enable_sleepdeepextint39_handlerdpll_resetextint42_handlermsg_cmd_wakeup_reason_updateextint21_handler_logbuf_startgeneric_wake_handlerputsnvtt_lowextint32_handleram335x_interconnect_hwmods_aux_data_endldo_power_upam335x_mpu_bitsextint28_handlerdebugmon_handler_end_stackddr_io_save_contextrtc_reg_writea8_wake_ds1_handlerextint49_handleram43xx_essential_hwmodsa8_wake_standby_handlerextint46_handlermemsetsoc_idhwmod_enablemainsetup_socpendsv_handlerextint43_handlerrtc_enable_checkmsg_cmd_read_idinit_m3_state_machineidle_dataextint25_handlerhwmod_is_enabledvtt_toggleinterconnect_hwmods_enablecmd_global_dataclkdm_state_changeusagefault_handlerextint3_handlervtp_enableclkdm_active_logbuf_endds0_dataextint51_handlerclkdm_sleepextint24_handlerextint10_handlerds_saveam335x_ldo_regsnvic_enable_irqpowerdomain_resetddr_io_suspendpd_state_restoredebug_haltam335_initam43xx_standby_clkdmsam43xx_clkdmsvtt_gpio_pin_end_resource_tablea8_wake_rtc_handlertrace_get_current_posa8_cpuidle_v2_handlermsg_cmd_stat_updateldo_wait_for_ret_end_bsshardfault_handlerldo_init_start_bssextint35_handlermsg_cmd_fast_triggeram335x_essential_hwmodsextint14_handlermsg_writenmi_handlera8_lp_rtc_handlerclkdms_sleepplls_power_downextint33_handlerextint13_handlerprcm_enable_isolationa8_lp_ds2_handlerds2_dataa8_i2c_sleep_handleram335x_sleep_clkdmsconfigure_deepsleep_countrsc_taba8_wake_cpuidle_v2_handleram43xx_mpu_bitsdebug_printfstrlena8_wake_ds2_handlerget_pd_per_stctrl_valrtc_reg_readextint29_handlerextint11_handler_start_resource_tableextint44_handlerenable_ioenable_master_oscillatorextint2_handleram43xx_sleep_clkdmsam43xx_hwmodshwmod_disablemsg_cmd_dispatcherdpll_initmem_typeextint17_handler4*!Ä'ÄÄD7¸<FN3rZ;t'$hb˜Â,s$ÅЂôÈ€tÎ#œ0—èé§0€-°p­7À东Ílp$U Ü:= \ No newline at end of file diff -Naur --no-dereference a/firmware/am43x-evm-scale-data.bin b/firmware/am43x-evm-scale-data.bin --- a/firmware/am43x-evm-scale-data.bin 1969-12-31 19:00:00.000000000 -0500 +++ b/firmware/am43x-evm-scale-data.bin 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1 @@ + Wd$k$Š$g$†d$k$™$g$† \ No newline at end of file diff -Naur --no-dereference a/firmware/regulatory.db b/firmware/regulatory.db --- a/firmware/regulatory.db 1969-12-31 19:00:00.000000000 -0500 +++ b/firmware/regulatory.db 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,15 @@ +RGDB00rAD. + */ + +/* + * all header files + */ + +#ifndef __AUFS_H__ +#define __AUFS_H__ + +#ifdef __KERNEL__ + +#define AuStub(type, name, body, ...) \ + static inline type name(__VA_ARGS__) { body; } + +#define AuStubVoid(name, ...) \ + AuStub(void, name, , __VA_ARGS__) +#define AuStubInt0(name, ...) \ + AuStub(int, name, return 0, __VA_ARGS__) + +#include "debug.h" + +#include "branch.h" +#include "cpup.h" +#include "dcsub.h" +#include "dbgaufs.h" +#include "dentry.h" +#include "dir.h" +#include "dirren.h" +#include "dynop.h" +#include "file.h" +#include "fstype.h" +#include "hbl.h" +#include "inode.h" +#include "lcnt.h" +#include "loop.h" +#include "module.h" +#include "opts.h" +#include "rwsem.h" +#include "super.h" +#include "sysaufs.h" +#include "vfsub.h" +#include "whout.h" +#include "wkq.h" + +#endif /* __KERNEL__ */ +#endif /* __AUFS_H__ */ diff -Naur --no-dereference a/fs/aufs/branch.c b/fs/aufs/branch.c --- a/fs/aufs/branch.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/branch.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * branch management + */ + +#include +#include +#include "aufs.h" + +/* + * free a single branch + */ +static void au_br_do_free(struct au_branch *br) +{ + int i; + struct au_wbr *wbr; + struct au_dykey **key; + + au_hnotify_fin_br(br); + /* always, regardless the mount option */ + au_dr_hino_free(&br->br_dirren); + au_xino_put(br); + + AuLCntZero(au_lcnt_read(&br->br_nfiles, /*do_rev*/0)); + au_lcnt_fin(&br->br_nfiles, /*do_sync*/0); + AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0)); + au_lcnt_fin(&br->br_count, /*do_sync*/0); + + wbr = br->br_wbr; + if (wbr) { + for (i = 0; i < AuBrWh_Last; i++) + dput(wbr->wbr_wh[i]); + AuDebugOn(atomic_read(&wbr->wbr_wh_running)); + AuRwDestroy(&wbr->wbr_wh_rwsem); + } + + if (br->br_fhsm) { + au_br_fhsm_fin(br->br_fhsm); + au_kfree_try_rcu(br->br_fhsm); + } + + key = br->br_dykey; + for (i = 0; i < AuBrDynOp; i++, key++) + if (*key) + au_dy_put(*key); + else + break; + + /* recursive lock, s_umount of branch's */ + /* synchronize_rcu(); */ /* why? */ + lockdep_off(); + path_put(&br->br_path); + lockdep_on(); + au_kfree_rcu(wbr); + au_lcnt_wait_for_fin(&br->br_nfiles); + au_lcnt_wait_for_fin(&br->br_count); + /* I don't know why, but percpu_refcount requires this */ + /* synchronize_rcu(); */ + au_kfree_rcu(br); +} + +/* + * frees all branches + */ +void au_br_free(struct au_sbinfo *sbinfo) +{ + aufs_bindex_t bmax; + struct au_branch **br; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + bmax = sbinfo->si_bbot + 1; + br = sbinfo->si_branch; + while (bmax--) + au_br_do_free(*br++); +} + +/* + * find the index of a branch which is specified by @br_id. + */ +int au_br_index(struct super_block *sb, aufs_bindex_t br_id) +{ + aufs_bindex_t bindex, bbot; + + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) + if (au_sbr_id(sb, bindex) == br_id) + return bindex; + return -1; +} + +/* ---------------------------------------------------------------------- */ + +/* + * add a branch + */ + +static int test_overlap(struct super_block *sb, struct dentry *h_adding, + struct dentry *h_root) +{ + if (unlikely(h_adding == h_root + || au_test_loopback_overlap(sb, h_adding))) + return 1; + if (h_adding->d_sb != h_root->d_sb) + return 0; + return au_test_subdir(h_adding, h_root) + || au_test_subdir(h_root, h_adding); +} + +/* + * returns a newly allocated branch. @new_nbranch is a number of branches + * after adding a branch. + */ +static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, + int perm) +{ + struct au_branch *add_branch; + struct dentry *root; + struct inode *inode; + int err; + + err = -ENOMEM; + add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS); + if (unlikely(!add_branch)) + goto out; + add_branch->br_xino = au_xino_alloc(/*nfile*/1); + if (unlikely(!add_branch->br_xino)) + goto out_br; + err = au_hnotify_init_br(add_branch, perm); + if (unlikely(err)) + goto out_xino; + + if (au_br_writable(perm)) { + /* may be freed separately at changing the branch permission */ + add_branch->br_wbr = kzalloc(sizeof(*add_branch->br_wbr), + GFP_NOFS); + if (unlikely(!add_branch->br_wbr)) + goto out_hnotify; + } + + if (au_br_fhsm(perm)) { + err = au_fhsm_br_alloc(add_branch); + if (unlikely(err)) + goto out_wbr; + } + + root = sb->s_root; + err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0); + if (!err) + err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0); + if (!err) { + inode = d_inode(root); + err = au_hinode_realloc(au_ii(inode), new_nbranch, + /*may_shrink*/0); + } + if (!err) + return add_branch; /* success */ + +out_wbr: + au_kfree_rcu(add_branch->br_wbr); +out_hnotify: + au_hnotify_fin_br(add_branch); +out_xino: + au_xino_put(add_branch); +out_br: + au_kfree_rcu(add_branch); +out: + return ERR_PTR(err); +} + +/* + * test if the branch permission is legal or not. + */ +static int test_br(struct inode *inode, int brperm, char *path) +{ + int err; + + err = (au_br_writable(brperm) && IS_RDONLY(inode)); + if (!err) + goto out; + + err = -EINVAL; + pr_err("write permission for readonly mount or inode, %s\n", path); + +out: + return err; +} + +/* + * returns: + * 0: success, the caller will add it + * plus: success, it is already unified, the caller should ignore it + * minus: error + */ +static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; + aufs_bindex_t bbot, bindex; + struct dentry *root, *h_dentry; + struct inode *inode, *h_inode; + + root = sb->s_root; + bbot = au_sbbot(sb); + if (unlikely(bbot >= 0 + && au_find_dbindex(root, add->path.dentry) >= 0)) { + err = 1; + if (!remount) { + err = -EINVAL; + pr_err("%s duplicated\n", add->pathname); + } + goto out; + } + + err = -ENOSPC; /* -E2BIG; */ + if (unlikely(AUFS_BRANCH_MAX <= add->bindex + || AUFS_BRANCH_MAX - 1 <= bbot)) { + pr_err("number of branches exceeded %s\n", add->pathname); + goto out; + } + + err = -EDOM; + if (unlikely(add->bindex < 0 || bbot + 1 < add->bindex)) { + pr_err("bad index %d\n", add->bindex); + goto out; + } + + inode = d_inode(add->path.dentry); + err = -ENOENT; + if (unlikely(!inode->i_nlink)) { + pr_err("no existence %s\n", add->pathname); + goto out; + } + + err = -EINVAL; + if (unlikely(inode->i_sb == sb)) { + pr_err("%s must be outside\n", add->pathname); + goto out; + } + + if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { + pr_err("unsupported filesystem, %s (%s)\n", + add->pathname, au_sbtype(inode->i_sb)); + goto out; + } + + if (unlikely(inode->i_sb->s_stack_depth)) { + pr_err("already stacked, %s (%s)\n", + add->pathname, au_sbtype(inode->i_sb)); + goto out; + } + + err = test_br(d_inode(add->path.dentry), add->perm, add->pathname); + if (unlikely(err)) + goto out; + + if (bbot < 0) + return 0; /* success */ + + err = -EINVAL; + for (bindex = 0; bindex <= bbot; bindex++) + if (unlikely(test_overlap(sb, add->path.dentry, + au_h_dptr(root, bindex)))) { + pr_err("%s is overlapped\n", add->pathname); + goto out; + } + + err = 0; + if (au_opt_test(au_mntflags(sb), WARN_PERM)) { + h_dentry = au_h_dptr(root, 0); + h_inode = d_inode(h_dentry); + if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) + || !uid_eq(h_inode->i_uid, inode->i_uid) + || !gid_eq(h_inode->i_gid, inode->i_gid)) + pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", + add->pathname, + i_uid_read(inode), i_gid_read(inode), + (inode->i_mode & S_IALLUGO), + i_uid_read(h_inode), i_gid_read(h_inode), + (h_inode->i_mode & S_IALLUGO)); + } + +out: + return err; +} + +/* + * initialize or clean the whiteouts for an adding branch + */ +static int au_br_init_wh(struct super_block *sb, struct au_branch *br, + int new_perm) +{ + int err, old_perm; + aufs_bindex_t bindex; + struct inode *h_inode; + struct au_wbr *wbr; + struct au_hinode *hdir; + struct dentry *h_dentry; + + err = vfsub_mnt_want_write(au_br_mnt(br)); + if (unlikely(err)) + goto out; + + wbr = br->br_wbr; + old_perm = br->br_perm; + br->br_perm = new_perm; + hdir = NULL; + h_inode = NULL; + bindex = au_br_index(sb, br->br_id); + if (0 <= bindex) { + hdir = au_hi(d_inode(sb->s_root), bindex); + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + } else { + h_dentry = au_br_dentry(br); + h_inode = d_inode(h_dentry); + inode_lock_nested(h_inode, AuLsc_I_PARENT); + } + if (!wbr) + err = au_wh_init(br, sb); + else { + wbr_wh_write_lock(wbr); + err = au_wh_init(br, sb); + wbr_wh_write_unlock(wbr); + } + if (hdir) + au_hn_inode_unlock(hdir); + else + inode_unlock(h_inode); + vfsub_mnt_drop_write(au_br_mnt(br)); + br->br_perm = old_perm; + + if (!err && wbr && !au_br_writable(new_perm)) { + au_kfree_rcu(wbr); + br->br_wbr = NULL; + } + +out: + return err; +} + +static int au_wbr_init(struct au_branch *br, struct super_block *sb, + int perm) +{ + int err; + struct kstatfs kst; + struct au_wbr *wbr; + + wbr = br->br_wbr; + au_rw_init(&wbr->wbr_wh_rwsem); + atomic_set(&wbr->wbr_wh_running, 0); + + /* + * a limit for rmdir/rename a dir + * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h + */ + err = vfs_statfs(&br->br_path, &kst); + if (unlikely(err)) + goto out; + err = -EINVAL; + if (kst.f_namelen >= NAME_MAX) + err = au_br_init_wh(sb, br, perm); + else + pr_err("%pd(%s), unsupported namelen %ld\n", + au_br_dentry(br), + au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); + +out: + return err; +} + +/* initialize a new branch */ +static int au_br_init(struct au_branch *br, struct super_block *sb, + struct au_opt_add *add) +{ + int err; + struct au_branch *brbase; + struct file *xf; + struct inode *h_inode; + + err = 0; + br->br_perm = add->perm; + br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); + au_lcnt_init(&br->br_nfiles, /*release*/NULL); + au_lcnt_init(&br->br_count, /*release*/NULL); + br->br_id = au_new_br_id(sb); + AuDebugOn(br->br_id < 0); + + /* always, regardless the given option */ + err = au_dr_br_init(sb, br, &add->path); + if (unlikely(err)) + goto out_err; + + if (au_br_writable(add->perm)) { + err = au_wbr_init(br, sb, add->perm); + if (unlikely(err)) + goto out_err; + } + + if (au_opt_test(au_mntflags(sb), XINO)) { + brbase = au_sbr(sb, 0); + xf = au_xino_file(brbase->br_xino, /*idx*/-1); + AuDebugOn(!xf); + h_inode = d_inode(add->path.dentry); + err = au_xino_init_br(sb, br, h_inode->i_ino, &xf->f_path); + if (unlikely(err)) { + AuDebugOn(au_xino_file(br->br_xino, /*idx*/-1)); + goto out_err; + } + } + + sysaufs_br_init(br); + path_get(&br->br_path); + goto out; /* success */ + +out_err: + memset(&br->br_path, 0, sizeof(br->br_path)); +out: + return err; +} + +static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, + struct au_branch *br, aufs_bindex_t bbot, + aufs_bindex_t amount) +{ + struct au_branch **brp; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + brp = sbinfo->si_branch + bindex; + memmove(brp + 1, brp, sizeof(*brp) * amount); + *brp = br; + sbinfo->si_bbot++; + if (unlikely(bbot < 0)) + sbinfo->si_bbot = 0; +} + +static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, + aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hdentry *hdp; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + hdp = au_hdentry(dinfo, bindex); + memmove(hdp + 1, hdp, sizeof(*hdp) * amount); + au_h_dentry_init(hdp); + dinfo->di_bbot++; + if (unlikely(bbot < 0)) + dinfo->di_btop = 0; +} + +static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, + aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + hip = au_hinode(iinfo, bindex); + memmove(hip + 1, hip, sizeof(*hip) * amount); + au_hinode_init(hip); + iinfo->ii_bbot++; + if (unlikely(bbot < 0)) + iinfo->ii_btop = 0; +} + +static void au_br_do_add(struct super_block *sb, struct au_branch *br, + aufs_bindex_t bindex) +{ + struct dentry *root, *h_dentry; + struct inode *root_inode, *h_inode; + aufs_bindex_t bbot, amount; + + root = sb->s_root; + root_inode = d_inode(root); + bbot = au_sbbot(sb); + amount = bbot + 1 - bindex; + h_dentry = au_br_dentry(br); + au_sbilist_lock(); + au_br_do_add_brp(au_sbi(sb), bindex, br, bbot, amount); + au_br_do_add_hdp(au_di(root), bindex, bbot, amount); + au_br_do_add_hip(au_ii(root_inode), bindex, bbot, amount); + au_set_h_dptr(root, bindex, dget(h_dentry)); + h_inode = d_inode(h_dentry); + au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /*flags*/0); + au_sbilist_unlock(); +} + +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; + aufs_bindex_t bbot, add_bindex; + struct dentry *root, *h_dentry; + struct inode *root_inode; + struct au_branch *add_branch; + + root = sb->s_root; + root_inode = d_inode(root); + IMustLock(root_inode); + IiMustWriteLock(root_inode); + err = test_add(sb, add, remount); + if (unlikely(err < 0)) + goto out; + if (err) { + err = 0; + goto out; /* success */ + } + + bbot = au_sbbot(sb); + add_branch = au_br_alloc(sb, bbot + 2, add->perm); + err = PTR_ERR(add_branch); + if (IS_ERR(add_branch)) + goto out; + + err = au_br_init(add_branch, sb, add); + if (unlikely(err)) { + au_br_do_free(add_branch); + goto out; + } + + add_bindex = add->bindex; + sysaufs_brs_del(sb, add_bindex); /* remove successors */ + au_br_do_add(sb, add_branch, add_bindex); + sysaufs_brs_add(sb, add_bindex); /* append successors */ + dbgaufs_brs_add(sb, add_bindex, /*topdown*/0); /* rename successors */ + + h_dentry = add->path.dentry; + if (!add_bindex) { + au_cpup_attr_all(root_inode, /*force*/1); + sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; + } else + au_add_nlink(root_inode, d_inode(h_dentry)); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static unsigned long long au_farray_cb(struct super_block *sb, void *a, + unsigned long long max __maybe_unused, + void *arg) +{ + unsigned long long n; + struct file **p, *f; + struct hlist_bl_head *files; + struct hlist_bl_node *pos; + struct au_finfo *finfo; + + n = 0; + p = a; + files = &au_sbi(sb)->si_files; + hlist_bl_lock(files); + hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) { + f = finfo->fi_file; + if (file_count(f) + && !special_file(file_inode(f)->i_mode)) { + get_file(f); + *p++ = f; + n++; + AuDebugOn(n > max); + } + } + hlist_bl_unlock(files); + + return n; +} + +static struct file **au_farray_alloc(struct super_block *sb, + unsigned long long *max) +{ + struct au_sbinfo *sbi; + + sbi = au_sbi(sb); + *max = au_lcnt_read(&sbi->si_nfiles, /*do_rev*/1); + return au_array_alloc(max, au_farray_cb, sb, /*arg*/NULL); +} + +static void au_farray_free(struct file **a, unsigned long long max) +{ + unsigned long long ull; + + for (ull = 0; ull < max; ull++) + if (a[ull]) + fput(a[ull]); + kvfree(a); +} + +/* ---------------------------------------------------------------------- */ + +/* + * delete a branch + */ + +/* to show the line number, do not make it inlined function */ +#define AuVerbose(do_info, fmt, ...) do { \ + if (do_info) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + +static int au_test_ibusy(struct inode *inode, aufs_bindex_t btop, + aufs_bindex_t bbot) +{ + return (inode && !S_ISDIR(inode->i_mode)) || btop == bbot; +} + +static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t btop, + aufs_bindex_t bbot) +{ + return au_test_ibusy(d_inode(dentry), btop, bbot); +} + +/* + * test if the branch is deletable or not. + */ +static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, + unsigned int sigen, const unsigned int verbose) +{ + int err, i, j, ndentry; + aufs_bindex_t btop, bbot; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *d; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, root, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + for (i = 0; !err && i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = 0; !err && j < ndentry; j++) { + d = dpage->dentries[j]; + AuDebugOn(au_dcount(d) <= 0); + if (!au_digen_test(d, sigen)) { + di_read_lock_child(d, AuLock_IR); + if (unlikely(au_dbrange_test(d))) { + di_read_unlock(d, AuLock_IR); + continue; + } + } else { + di_write_lock_child(d); + if (unlikely(au_dbrange_test(d))) { + di_write_unlock(d); + continue; + } + err = au_reval_dpath(d, sigen); + if (!err) + di_downgrade_lock(d, AuLock_IR); + else { + di_write_unlock(d); + break; + } + } + + /* AuDbgDentry(d); */ + btop = au_dbtop(d); + bbot = au_dbbot(d); + if (btop <= bindex + && bindex <= bbot + && au_h_dptr(d, bindex) + && au_test_dbusy(d, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy %pd\n", d); + AuDbgDentry(d); + } + di_read_unlock(d, AuLock_IR); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, + unsigned int sigen, const unsigned int verbose) +{ + int err; + unsigned long long max, ull; + struct inode *i, **array; + aufs_bindex_t btop, bbot; + + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + err = 0; + AuDbg("b%d\n", bindex); + for (ull = 0; !err && ull < max; ull++) { + i = array[ull]; + if (unlikely(!i)) + break; + if (i->i_ino == AUFS_ROOT_INO) + continue; + + /* AuDbgInode(i); */ + if (au_iigen(i, NULL) == sigen) + ii_read_lock_child(i); + else { + ii_write_lock_child(i); + err = au_refresh_hinode_self(i); + au_iigen_dec(i); + if (!err) + ii_downgrade_lock(i); + else { + ii_write_unlock(i); + break; + } + } + + btop = au_ibtop(i); + bbot = au_ibbot(i); + if (btop <= bindex + && bindex <= bbot + && au_h_iptr(i, bindex) + && au_test_ibusy(i, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy i%lu\n", i->i_ino); + AuDbgInode(i); + } + ii_read_unlock(i); + } + au_iarray_free(array, max); + +out: + return err; +} + +static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, + const unsigned int verbose) +{ + int err; + unsigned int sigen; + + sigen = au_sigen(root->d_sb); + DiMustNoWaiters(root); + IiMustNoWaiters(d_inode(root)); + di_write_unlock(root); + err = test_dentry_busy(root, bindex, sigen, verbose); + if (!err) + err = test_inode_busy(root->d_sb, bindex, sigen, verbose); + di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ + + return err; +} + +static int test_dir_busy(struct file *file, aufs_bindex_t br_id, + struct file **to_free, int *idx) +{ + int err; + unsigned char matched, root; + aufs_bindex_t bindex, bbot; + struct au_fidir *fidir; + struct au_hfile *hfile; + + err = 0; + root = IS_ROOT(file->f_path.dentry); + if (root) { + get_file(file); + to_free[*idx] = file; + (*idx)++; + goto out; + } + + matched = 0; + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; + + if (hfile->hf_br->br_id == br_id) { + matched = 1; + break; + } + } + if (matched) + err = -EBUSY; + +out: + return err; +} + +static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, + struct file **to_free, int opened) +{ + int err, idx; + unsigned long long ull, max; + aufs_bindex_t btop; + struct file *file, **array; + struct dentry *root; + struct au_hfile *hfile; + + array = au_farray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + err = 0; + idx = 0; + root = sb->s_root; + di_write_unlock(root); + for (ull = 0; ull < max; ull++) { + file = array[ull]; + if (unlikely(!file)) + break; + + /* AuDbg("%pD\n", file); */ + fi_read_lock(file); + btop = au_fbtop(file); + if (!d_is_dir(file->f_path.dentry)) { + hfile = &au_fi(file)->fi_htop; + if (hfile->hf_br->br_id == br_id) + err = -EBUSY; + } else + err = test_dir_busy(file, br_id, to_free, &idx); + fi_read_unlock(file); + if (unlikely(err)) + break; + } + di_write_lock_child(root); + au_farray_free(array, max); + AuDebugOn(idx > opened); + +out: + return err; +} + +static void br_del_file(struct file **to_free, unsigned long long opened, + aufs_bindex_t br_id) +{ + unsigned long long ull; + aufs_bindex_t bindex, btop, bbot, bfound; + struct file *file; + struct au_fidir *fidir; + struct au_hfile *hfile; + + for (ull = 0; ull < opened; ull++) { + file = to_free[ull]; + if (unlikely(!file)) + break; + + /* AuDbg("%pD\n", file); */ + AuDebugOn(!d_is_dir(file->f_path.dentry)); + bfound = -1; + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); + fi_write_lock(file); + btop = au_fbtop(file); + bbot = au_fbbot_dir(file); + for (bindex = btop; bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; + + if (hfile->hf_br->br_id == br_id) { + bfound = bindex; + break; + } + } + AuDebugOn(bfound < 0); + au_set_h_fptr(file, bfound, NULL); + if (bfound == btop) { + for (btop++; btop <= bbot; btop++) + if (au_hf_dir(file, btop)) { + au_set_fbtop(file, btop); + break; + } + } + fi_write_unlock(file); + } +} + +static void au_br_do_del_brp(struct au_sbinfo *sbinfo, + const aufs_bindex_t bindex, + const aufs_bindex_t bbot) +{ + struct au_branch **brp, **p; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + brp = sbinfo->si_branch + bindex; + if (bindex < bbot) + memmove(brp, brp + 1, sizeof(*brp) * (bbot - bindex)); + sbinfo->si_branch[0 + bbot] = NULL; + sbinfo->si_bbot--; + + p = au_krealloc(sbinfo->si_branch, sizeof(*p) * bbot, AuGFP_SBILIST, + /*may_shrink*/1); + if (p) + sbinfo->si_branch = p; + /* harmless error */ +} + +static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, + const aufs_bindex_t bbot) +{ + struct au_hdentry *hdp, *p; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + hdp = au_hdentry(dinfo, bindex); + if (bindex < bbot) + memmove(hdp, hdp + 1, sizeof(*hdp) * (bbot - bindex)); + /* au_h_dentry_init(au_hdentry(dinfo, bbot); */ + dinfo->di_bbot--; + + p = au_krealloc(dinfo->di_hdentry, sizeof(*p) * bbot, AuGFP_SBILIST, + /*may_shrink*/1); + if (p) + dinfo->di_hdentry = p; + /* harmless error */ +} + +static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, + const aufs_bindex_t bbot) +{ + struct au_hinode *hip, *p; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + hip = au_hinode(iinfo, bindex); + if (bindex < bbot) + memmove(hip, hip + 1, sizeof(*hip) * (bbot - bindex)); + /* au_hinode_init(au_hinode(iinfo, bbot)); */ + iinfo->ii_bbot--; + + p = au_krealloc(iinfo->ii_hinode, sizeof(*p) * bbot, AuGFP_SBILIST, + /*may_shrink*/1); + if (p) + iinfo->ii_hinode = p; + /* harmless error */ +} + +static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br) +{ + aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct dentry *root, *h_root; + struct inode *inode, *h_inode; + struct au_hinode *hinode; + + SiMustWriteLock(sb); + + root = sb->s_root; + inode = d_inode(root); + sbinfo = au_sbi(sb); + bbot = sbinfo->si_bbot; + + h_root = au_h_dptr(root, bindex); + hinode = au_hi(inode, bindex); + h_inode = au_igrab(hinode->hi_inode); + au_hiput(hinode); + + au_sbilist_lock(); + au_br_do_del_brp(sbinfo, bindex, bbot); + au_br_do_del_hdp(au_di(root), bindex, bbot); + au_br_do_del_hip(au_ii(inode), bindex, bbot); + au_sbilist_unlock(); + + /* ignore an error */ + au_dr_br_fin(sb, br); /* always, regardless the mount option */ + + dput(h_root); + iput(h_inode); + au_br_do_free(br); +} + +static unsigned long long empty_cb(struct super_block *sb, void *array, + unsigned long long max, void *arg) +{ + return max; +} + +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) +{ + int err, rerr, i; + unsigned long long opened; + unsigned int mnt_flags; + aufs_bindex_t bindex, bbot, br_id; + unsigned char do_wh, verbose; + struct au_branch *br; + struct au_wbr *wbr; + struct dentry *root; + struct file **to_free; + + err = 0; + opened = 0; + to_free = NULL; + root = sb->s_root; + bindex = au_find_dbindex(root, del->h_path.dentry); + if (bindex < 0) { + if (remount) + goto out; /* success */ + err = -ENOENT; + pr_err("%s no such branch\n", del->pathname); + goto out; + } + AuDbg("bindex b%d\n", bindex); + + err = -EBUSY; + mnt_flags = au_mntflags(sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); + bbot = au_sbbot(sb); + if (unlikely(!bbot)) { + AuVerbose(verbose, "no more branches left\n"); + goto out; + } + + br = au_sbr(sb, bindex); + AuDebugOn(!path_equal(&br->br_path, &del->h_path)); + if (unlikely(au_lcnt_read(&br->br_count, /*do_rev*/1))) { + AuVerbose(verbose, "br %pd2 is busy now\n", del->h_path.dentry); + goto out; + } + + br_id = br->br_id; + opened = au_lcnt_read(&br->br_nfiles, /*do_rev*/1); + if (unlikely(opened)) { + to_free = au_array_alloc(&opened, empty_cb, sb, NULL); + err = PTR_ERR(to_free); + if (IS_ERR(to_free)) + goto out; + + err = test_file_busy(sb, br_id, to_free, opened); + if (unlikely(err)) { + AuVerbose(verbose, "%llu file(s) opened\n", opened); + goto out; + } + } + + wbr = br->br_wbr; + do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); + if (do_wh) { + /* instead of WbrWhMustWriteLock(wbr) */ + SiMustWriteLock(sb); + for (i = 0; i < AuBrWh_Last; i++) { + dput(wbr->wbr_wh[i]); + wbr->wbr_wh[i] = NULL; + } + } + + err = test_children_busy(root, bindex, verbose); + if (unlikely(err)) { + if (do_wh) + goto out_wh; + goto out; + } + + err = 0; + if (to_free) { + /* + * now we confirmed the branch is deletable. + * let's free the remaining opened dirs on the branch. + */ + di_write_unlock(root); + br_del_file(to_free, opened, br_id); + di_write_lock_child(root); + } + + sysaufs_brs_del(sb, bindex); /* remove successors */ + dbgaufs_xino_del(br); /* remove one */ + au_br_do_del(sb, bindex, br); + sysaufs_brs_add(sb, bindex); /* append successors */ + dbgaufs_brs_add(sb, bindex, /*topdown*/1); /* rename successors */ + + if (!bindex) { + au_cpup_attr_all(d_inode(root), /*force*/1); + sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; + } else + au_sub_nlink(d_inode(root), d_inode(del->h_path.dentry)); + if (au_opt_test(mnt_flags, PLINK)) + au_plink_half_refresh(sb, br_id); + + goto out; /* success */ + +out_wh: + /* revert */ + rerr = au_br_init_wh(sb, br, br->br_perm); + if (rerr) + pr_warn("failed re-creating base whiteout, %s. (%d)\n", + del->pathname, rerr); +out: + if (to_free) + au_farray_free(to_free, opened); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) +{ + int err; + aufs_bindex_t btop, bbot; + struct aufs_ibusy ibusy; + struct inode *inode, *h_inode; + + err = -EPERM; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = copy_from_user(&ibusy, arg, sizeof(ibusy)); + if (!err) + /* VERIFY_WRITE */ + err = !access_ok(&arg->h_ino, sizeof(arg->h_ino)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + + err = -EINVAL; + si_read_lock(sb, AuLock_FLUSH); + if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbbot(sb))) + goto out_unlock; + + err = 0; + ibusy.h_ino = 0; /* invalid */ + inode = ilookup(sb, ibusy.ino); + if (!inode + || inode->i_ino == AUFS_ROOT_INO + || au_is_bad_inode(inode)) + goto out_unlock; + + ii_read_lock_child(inode); + btop = au_ibtop(inode); + bbot = au_ibbot(inode); + if (btop <= ibusy.bindex && ibusy.bindex <= bbot) { + h_inode = au_h_iptr(inode, ibusy.bindex); + if (h_inode && au_test_ibusy(inode, btop, bbot)) + ibusy.h_ino = h_inode->i_ino; + } + ii_read_unlock(inode); + iput(inode); + +out_unlock: + si_read_unlock(sb); + if (!err) { + err = __put_user(ibusy.h_ino, &arg->h_ino); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + } + } +out: + return err; +} + +long au_ibusy_ioctl(struct file *file, unsigned long arg) +{ + return au_ibusy(file->f_path.dentry->d_sb, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) +{ + return au_ibusy(file->f_path.dentry->d_sb, compat_ptr(arg)); +} +#endif + +/* ---------------------------------------------------------------------- */ + +/* + * change a branch permission + */ + +static void au_warn_ima(void) +{ +#ifdef CONFIG_IMA + /* since it doesn't support mark_files_ro() */ + AuWarn1("RW -> RO makes IMA to produce wrong message\n"); +#endif +} + +static int do_need_sigen_inc(int a, int b) +{ + return au_br_whable(a) && !au_br_whable(b); +} + +static int need_sigen_inc(int old, int new) +{ + return do_need_sigen_inc(old, new) + || do_need_sigen_inc(new, old); +} + +static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) +{ + int err, do_warn; + unsigned int mnt_flags; + unsigned long long ull, max; + aufs_bindex_t br_id; + unsigned char verbose, writer; + struct file *file, *hf, **array; + struct au_hfile *hfile; + struct inode *h_inode; + + mnt_flags = au_mntflags(sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); + + array = au_farray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + do_warn = 0; + br_id = au_sbr_id(sb, bindex); + for (ull = 0; ull < max; ull++) { + file = array[ull]; + if (unlikely(!file)) + break; + + /* AuDbg("%pD\n", file); */ + fi_read_lock(file); + if (unlikely(au_test_mmapped(file))) { + err = -EBUSY; + AuVerbose(verbose, "mmapped %pD\n", file); + AuDbgFile(file); + FiMustNoWaiters(file); + fi_read_unlock(file); + goto out_array; + } + + hfile = &au_fi(file)->fi_htop; + hf = hfile->hf_file; + if (!d_is_reg(file->f_path.dentry) + || !(file->f_mode & FMODE_WRITE) + || hfile->hf_br->br_id != br_id + || !(hf->f_mode & FMODE_WRITE)) + array[ull] = NULL; + else { + do_warn = 1; + get_file(file); + } + + FiMustNoWaiters(file); + fi_read_unlock(file); + fput(file); + } + + err = 0; + if (do_warn) + au_warn_ima(); + + for (ull = 0; ull < max; ull++) { + file = array[ull]; + if (!file) + continue; + + /* todo: already flushed? */ + /* + * fs/super.c:mark_files_ro() is gone, but aufs keeps its + * approach which resets f_mode and calls mnt_drop_write() and + * file_release_write() for each file, because the branch + * attribute in aufs world is totally different from the native + * fs rw/ro mode. + */ + /* fi_read_lock(file); */ + hfile = &au_fi(file)->fi_htop; + hf = hfile->hf_file; + /* fi_read_unlock(file); */ + spin_lock(&hf->f_lock); + writer = !!(hf->f_mode & FMODE_WRITER); + hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); + spin_unlock(&hf->f_lock); + if (writer) { + h_inode = file_inode(hf); + if (hf->f_mode & FMODE_READ) + i_readcount_inc(h_inode); + put_write_access(h_inode); + __mnt_drop_write(hf->f_path.mnt); + } + } + +out_array: + au_farray_free(array, max); +out: + AuTraceErr(err); + return err; +} + +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, + int *do_refresh) +{ + int err, rerr; + aufs_bindex_t bindex; + struct dentry *root; + struct au_branch *br; + struct au_br_fhsm *bf; + + root = sb->s_root; + bindex = au_find_dbindex(root, mod->h_root); + if (bindex < 0) { + if (remount) + return 0; /* success */ + err = -ENOENT; + pr_err("%s no such branch\n", mod->path); + goto out; + } + AuDbg("bindex b%d\n", bindex); + + err = test_br(d_inode(mod->h_root), mod->perm, mod->path); + if (unlikely(err)) + goto out; + + br = au_sbr(sb, bindex); + AuDebugOn(mod->h_root != au_br_dentry(br)); + if (br->br_perm == mod->perm) + return 0; /* success */ + + /* pre-allocate for non-fhsm --> fhsm */ + bf = NULL; + if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { + err = au_fhsm_br_alloc(br); + if (unlikely(err)) + goto out; + bf = br->br_fhsm; + br->br_fhsm = NULL; + } + + if (au_br_writable(br->br_perm)) { + /* remove whiteout base */ + err = au_br_init_wh(sb, br, mod->perm); + if (unlikely(err)) + goto out_bf; + + if (!au_br_writable(mod->perm)) { + /* rw --> ro, file might be mmapped */ + DiMustNoWaiters(root); + IiMustNoWaiters(d_inode(root)); + di_write_unlock(root); + err = au_br_mod_files_ro(sb, bindex); + /* aufs_write_lock() calls ..._child() */ + di_write_lock_child(root); + + if (unlikely(err)) { + rerr = -ENOMEM; + br->br_wbr = kzalloc(sizeof(*br->br_wbr), + GFP_NOFS); + if (br->br_wbr) + rerr = au_wbr_init(br, sb, br->br_perm); + if (unlikely(rerr)) { + AuIOErr("nested error %d (%d)\n", + rerr, err); + br->br_perm = mod->perm; + } + } + } + } else if (au_br_writable(mod->perm)) { + /* ro --> rw */ + err = -ENOMEM; + br->br_wbr = kzalloc(sizeof(*br->br_wbr), GFP_NOFS); + if (br->br_wbr) { + err = au_wbr_init(br, sb, mod->perm); + if (unlikely(err)) { + au_kfree_rcu(br->br_wbr); + br->br_wbr = NULL; + } + } + } + if (unlikely(err)) + goto out_bf; + + if (au_br_fhsm(br->br_perm)) { + if (!au_br_fhsm(mod->perm)) { + /* fhsm --> non-fhsm */ + au_br_fhsm_fin(br->br_fhsm); + au_kfree_rcu(br->br_fhsm); + br->br_fhsm = NULL; + } + } else if (au_br_fhsm(mod->perm)) + /* non-fhsm --> fhsm */ + br->br_fhsm = bf; + + *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); + br->br_perm = mod->perm; + goto out; /* success */ + +out_bf: + au_kfree_try_rcu(bf); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) +{ + int err; + struct kstatfs kstfs; + + err = vfs_statfs(&br->br_path, &kstfs); + if (!err) { + stfs->f_blocks = kstfs.f_blocks; + stfs->f_bavail = kstfs.f_bavail; + stfs->f_files = kstfs.f_files; + stfs->f_ffree = kstfs.f_ffree; + } + + return err; +} diff -Naur --no-dereference a/fs/aufs/branch.h b/fs/aufs/branch.h --- a/fs/aufs/branch.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/branch.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,364 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * branch filesystems and xino for them + */ + +#ifndef __AUFS_BRANCH_H__ +#define __AUFS_BRANCH_H__ + +#ifdef __KERNEL__ + +#include +#include "dirren.h" +#include "dynop.h" +#include "lcnt.h" +#include "rwsem.h" +#include "super.h" + +/* ---------------------------------------------------------------------- */ + +/* a xino file */ +struct au_xino { + struct file **xi_file; + unsigned int xi_nfile; + + struct { + spinlock_t spin; + ino_t *array; + int total; + /* reserved for future use */ + /* unsigned long *bitmap; */ + wait_queue_head_t wqh; + } xi_nondir; + + struct mutex xi_mtx; /* protects xi_file array */ + struct hlist_bl_head xi_writing; + + atomic_t xi_truncating; + + struct kref xi_kref; +}; + +/* File-based Hierarchical Storage Management */ +struct au_br_fhsm { +#ifdef CONFIG_AUFS_FHSM + struct mutex bf_lock; + unsigned long bf_jiffy; + struct aufs_stfs bf_stfs; + int bf_readable; +#endif +}; + +/* members for writable branch only */ +enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; +struct au_wbr { + struct au_rwsem wbr_wh_rwsem; + struct dentry *wbr_wh[AuBrWh_Last]; + atomic_t wbr_wh_running; +#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ +#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ +#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ + + /* mfs mode */ + unsigned long long wbr_bytes; +}; + +/* ext2 has 3 types of operations at least, ext3 has 4 */ +#define AuBrDynOp (AuDyLast * 4) + +#ifdef CONFIG_AUFS_HFSNOTIFY +/* support for asynchronous destruction */ +struct au_br_hfsnotify { + struct fsnotify_group *hfsn_group; +}; +#endif + +/* sysfs entries */ +struct au_brsysfs { + char name[16]; + struct attribute attr; +}; + +enum { + AuBrSysfs_BR, + AuBrSysfs_BRID, + AuBrSysfs_Last +}; + +/* protected by superblock rwsem */ +struct au_branch { + struct au_xino *br_xino; + + aufs_bindex_t br_id; + + int br_perm; + struct path br_path; + spinlock_t br_dykey_lock; + struct au_dykey *br_dykey[AuBrDynOp]; + au_lcnt_t br_nfiles; /* opened files */ + au_lcnt_t br_count; /* in-use for other */ + + struct au_wbr *br_wbr; + struct au_br_fhsm *br_fhsm; + +#ifdef CONFIG_AUFS_HFSNOTIFY + struct au_br_hfsnotify *br_hfsn; +#endif + +#ifdef CONFIG_SYSFS + /* entries under sysfs per mount-point */ + struct au_brsysfs br_sysfs[AuBrSysfs_Last]; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *br_dbgaufs; /* xino */ +#endif + + struct au_dr_br br_dirren; +}; + +/* ---------------------------------------------------------------------- */ + +static inline struct vfsmount *au_br_mnt(struct au_branch *br) +{ + return br->br_path.mnt; +} + +static inline struct dentry *au_br_dentry(struct au_branch *br) +{ + return br->br_path.dentry; +} + +static inline struct super_block *au_br_sb(struct au_branch *br) +{ + return au_br_mnt(br)->mnt_sb; +} + +static inline int au_br_rdonly(struct au_branch *br) +{ + return (sb_rdonly(au_br_sb(br)) + || !au_br_writable(br->br_perm)) + ? -EROFS : 0; +} + +static inline int au_br_hnotifyable(int brperm __maybe_unused) +{ +#ifdef CONFIG_AUFS_HNOTIFY + return !(brperm & AuBrPerm_RR); +#else + return 0; +#endif +} + +static inline int au_br_test_oflag(int oflag, struct au_branch *br) +{ + int err, exec_flag; + + err = 0; + exec_flag = oflag & __FMODE_EXEC; + if (unlikely(exec_flag && path_noexec(&br->br_path))) + err = -EACCES; + + return err; +} + +static inline void au_xino_get(struct au_branch *br) +{ + struct au_xino *xi; + + xi = br->br_xino; + if (xi) + kref_get(&xi->xi_kref); +} + +static inline int au_xino_count(struct au_branch *br) +{ + int v; + struct au_xino *xi; + + v = 0; + xi = br->br_xino; + if (xi) + v = kref_read(&xi->xi_kref); + + return v; +} + +/* ---------------------------------------------------------------------- */ + +/* branch.c */ +struct au_sbinfo; +void au_br_free(struct au_sbinfo *sinfo); +int au_br_index(struct super_block *sb, aufs_bindex_t br_id); +struct au_opt_add; +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); +struct au_opt_del; +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); +long au_ibusy_ioctl(struct file *file, unsigned long arg); +#ifdef CONFIG_COMPAT +long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); +#endif +struct au_opt_mod; +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, + int *do_refresh); +struct aufs_stfs; +int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); + +/* xino.c */ +static const loff_t au_loff_max = LLONG_MAX; + +aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry); +struct file *au_xino_create(struct super_block *sb, char *fpath, int silent, + int wbrtop); +struct file *au_xino_create2(struct super_block *sb, struct path *base, + struct file *copy_src); +struct au_xi_new { + struct au_xino *xi; /* switch between xino and xigen */ + int idx; + struct path *base; + struct file *copy_src; +}; +struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew); + +int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t *ino); +int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t ino); +ssize_t xino_fread(struct file *file, void *buf, size_t size, loff_t *pos); +ssize_t xino_fwrite(struct file *file, void *buf, size_t size, loff_t *pos); + +int au_xib_trunc(struct super_block *sb); +int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin); + +struct au_xino *au_xino_alloc(unsigned int nfile); +int au_xino_put(struct au_branch *br); +struct file *au_xino_file1(struct au_xino *xi); + +struct au_opt_xino; +void au_xino_clr(struct super_block *sb); +int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount); +struct file *au_xino_def(struct super_block *sb); +int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino, + struct path *base); + +ino_t au_xino_new_ino(struct super_block *sb); +void au_xino_delete_inode(struct inode *inode, const int unlinked); + +void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, + ino_t h_ino, int idx); +int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + int *idx); + +int au_xino_path(struct seq_file *seq, struct file *file); + +/* ---------------------------------------------------------------------- */ + +/* @idx is signed to accept -1 meaning the first file */ +static inline struct file *au_xino_file(struct au_xino *xi, int idx) +{ + struct file *file; + + file = NULL; + if (!xi) + goto out; + + if (idx >= 0) { + if (idx < xi->xi_nfile) + file = xi->xi_file[idx]; + } else + file = au_xino_file1(xi); + +out: + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* Superblock to branch */ +static inline +aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_id; +} + +static inline +struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_mnt(au_sbr(sb, bindex)); +} + +static inline +struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_sb(au_sbr(sb, bindex)); +} + +static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_perm; +} + +static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_whable(au_sbr_perm(sb, bindex)); +} + +/* ---------------------------------------------------------------------- */ + +#define wbr_wh_read_lock(wbr) au_rw_read_lock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_lock(wbr) au_rw_write_lock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_read_trylock(wbr) au_rw_read_trylock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_trylock(wbr) au_rw_write_trylock(&(wbr)->wbr_wh_rwsem) +/* +#define wbr_wh_read_trylock_nested(wbr) \ + au_rw_read_trylock_nested(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_trylock_nested(wbr) \ + au_rw_write_trylock_nested(&(wbr)->wbr_wh_rwsem) +*/ + +#define wbr_wh_read_unlock(wbr) au_rw_read_unlock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_unlock(wbr) au_rw_write_unlock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_downgrade_lock(wbr) au_rw_dgrade_lock(&(wbr)->wbr_wh_rwsem) + +#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&(wbr)->wbr_wh_rwsem) +#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&(wbr)->wbr_wh_rwsem) +#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&(wbr)->wbr_wh_rwsem) + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_FHSM +static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) +{ + mutex_init(&brfhsm->bf_lock); + brfhsm->bf_jiffy = 0; + brfhsm->bf_readable = 0; +} + +static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) +{ + mutex_destroy(&brfhsm->bf_lock); +} +#else +AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) +AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_BRANCH_H__ */ diff -Naur --no-dereference a/fs/aufs/conf.mk b/fs/aufs/conf.mk --- a/fs/aufs/conf.mk 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/conf.mk 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 + +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} + +define AuConf +ifdef ${1} +AuConfStr += ${1}=${${1}} +endif +endef + +AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ + SBILIST \ + HNOTIFY HFSNOTIFY \ + EXPORT INO_T_64 \ + XATTR \ + FHSM \ + RDU \ + DIRREN \ + SHWH \ + BR_RAMFS \ + BR_FUSE POLL \ + BR_HFSPLUS \ + BDEV_LOOP \ + DEBUG MAGIC_SYSRQ +$(foreach i, ${AuConfAll}, \ + $(eval $(call AuConf,CONFIG_AUFS_${i}))) + +AuConfName = ${obj}/conf.str +${AuConfName}.tmp: FORCE + @echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@ +${AuConfName}: ${AuConfName}.tmp + @diff -q $< $@ > /dev/null 2>&1 || { \ + echo ' GEN ' $@; \ + cp -p $< $@; \ + } +FORCE: +clean-files += ${AuConfName} ${AuConfName}.tmp +${obj}/sysfs.o: ${AuConfName} + +-include ${srctree}/${src}/conf_priv.mk diff -Naur --no-dereference a/fs/aufs/cpup.c b/fs/aufs/cpup.c --- a/fs/aufs/cpup.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/cpup.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1447 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * copy-up functions, see wbr_policy.c for copy-down + */ + +#include +#include +#include +#include "aufs.h" + +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) +{ + const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE + | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; + + BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); + + dst->i_flags |= iflags & ~mask; + if (au_test_fs_notime(dst->i_sb)) + dst->i_flags |= S_NOATIME | S_NOCMTIME; +} + +void au_cpup_attr_timesizes(struct inode *inode) +{ + struct inode *h_inode; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + fsstack_copy_attr_times(inode, h_inode); + fsstack_copy_inode_size(inode, h_inode); +} + +void au_cpup_attr_nlink(struct inode *inode, int force) +{ + struct inode *h_inode; + struct super_block *sb; + aufs_bindex_t bindex, bbot; + + sb = inode->i_sb; + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (!force + && !S_ISDIR(h_inode->i_mode) + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode)) + return; + + /* + * 0 can happen in revalidating. + * h_inode->i_mutex may not be held here, but it is harmless since once + * i_nlink reaches 0, it will never become positive except O_TMPFILE + * case. + * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause + * the incorrect link count. + */ + set_nlink(inode, h_inode->i_nlink); + + /* + * fewer nlink makes find(1) noisy, but larger nlink doesn't. + * it may includes whplink directory. + */ + if (S_ISDIR(h_inode->i_mode)) { + bbot = au_ibbot(inode); + for (bindex++; bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) + au_add_nlink(inode, h_inode); + } + } +} + +void au_cpup_attr_changeable(struct inode *inode) +{ + struct inode *h_inode; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + inode->i_mode = h_inode->i_mode; + inode->i_uid = h_inode->i_uid; + inode->i_gid = h_inode->i_gid; + au_cpup_attr_timesizes(inode); + au_cpup_attr_flags(inode, h_inode->i_flags); +} + +void au_cpup_igen(struct inode *inode, struct inode *h_inode) +{ + struct au_iinfo *iinfo = au_ii(inode); + + IiMustWriteLock(inode); + + iinfo->ii_higen = h_inode->i_generation; + iinfo->ii_hsb1 = h_inode->i_sb; +} + +void au_cpup_attr_all(struct inode *inode, int force) +{ + struct inode *h_inode; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + au_cpup_attr_changeable(inode); + if (inode->i_nlink > 0) + au_cpup_attr_nlink(inode, force); + inode->i_rdev = h_inode->i_rdev; + inode->i_blkbits = h_inode->i_blkbits; + au_cpup_igen(inode, h_inode); +} + +/* ---------------------------------------------------------------------- */ + +/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ + +/* keep the timestamps of the parent dir when cpup */ +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct path *h_path) +{ + struct inode *h_inode; + + dt->dt_dentry = dentry; + dt->dt_h_path = *h_path; + h_inode = d_inode(h_path->dentry); + dt->dt_atime = h_inode->i_atime; + dt->dt_mtime = h_inode->i_mtime; + /* smp_mb(); */ +} + +void au_dtime_revert(struct au_dtime *dt) +{ + struct iattr attr; + int err; + + attr.ia_atime = dt->dt_atime; + attr.ia_mtime = dt->dt_mtime; + attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET + | ATTR_ATIME | ATTR_ATIME_SET; + + /* no delegation since this is a directory */ + err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); + if (unlikely(err)) + pr_warn("restoring timestamps failed(%d). ignored\n", err); +} + +/* ---------------------------------------------------------------------- */ + +/* internal use only */ +struct au_cpup_reg_attr { + int valid; + struct kstat st; + unsigned int iflags; /* inode->i_flags */ +}; + +static noinline_for_stack +int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, + struct au_cpup_reg_attr *h_src_attr) +{ + int err, sbits, icex; + unsigned int mnt_flags; + unsigned char verbose; + struct iattr ia; + struct path h_path; + struct inode *h_isrc, *h_idst; + struct kstat *h_st; + struct au_branch *br; + + h_path.dentry = au_h_dptr(dst, bindex); + h_idst = d_inode(h_path.dentry); + br = au_sbr(dst->d_sb, bindex); + h_path.mnt = au_br_mnt(br); + h_isrc = d_inode(h_src); + ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID + | ATTR_ATIME | ATTR_MTIME + | ATTR_ATIME_SET | ATTR_MTIME_SET; + if (h_src_attr && h_src_attr->valid) { + h_st = &h_src_attr->st; + ia.ia_uid = h_st->uid; + ia.ia_gid = h_st->gid; + ia.ia_atime = h_st->atime; + ia.ia_mtime = h_st->mtime; + if (h_idst->i_mode != h_st->mode + && !S_ISLNK(h_idst->i_mode)) { + ia.ia_valid |= ATTR_MODE; + ia.ia_mode = h_st->mode; + } + sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(h_idst, h_src_attr->iflags); + } else { + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + ia.ia_atime = h_isrc->i_atime; + ia.ia_mtime = h_isrc->i_mtime; + if (h_idst->i_mode != h_isrc->i_mode + && !S_ISLNK(h_idst->i_mode)) { + ia.ia_valid |= ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + } + sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(h_idst, h_isrc->i_flags); + } + /* no delegation since it is just created */ + err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); + + /* is this nfs only? */ + if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { + ia.ia_valid = ATTR_FORCE | ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); + } + + icex = br->br_perm & AuBrAttr_ICEX; + if (!err) { + mnt_flags = au_mntflags(dst->d_sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); + err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, + char *buf, unsigned long blksize) +{ + int err; + size_t sz, rbytes, wbytes; + unsigned char all_zero; + char *p, *zp; + struct inode *h_inode; + /* reduce stack usage */ + struct iattr *ia; + + zp = page_address(ZERO_PAGE(0)); + if (unlikely(!zp)) + return -ENOMEM; /* possible? */ + + err = 0; + all_zero = 0; + while (len) { + AuDbg("len %lld\n", len); + sz = blksize; + if (len < blksize) + sz = len; + + rbytes = 0; + /* todo: signal_pending? */ + while (!rbytes || err == -EAGAIN || err == -EINTR) { + rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); + err = rbytes; + } + if (unlikely(err < 0)) + break; + + all_zero = 0; + if (len >= rbytes && rbytes == blksize) + all_zero = !memcmp(buf, zp, rbytes); + if (!all_zero) { + wbytes = rbytes; + p = buf; + while (wbytes) { + size_t b; + + b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); + err = b; + /* todo: signal_pending? */ + if (unlikely(err == -EAGAIN || err == -EINTR)) + continue; + if (unlikely(err < 0)) + break; + wbytes -= b; + p += b; + } + if (unlikely(err < 0)) + break; + } else { + loff_t res; + + AuLabel(hole); + res = vfsub_llseek(dst, rbytes, SEEK_CUR); + err = res; + if (unlikely(res < 0)) + break; + } + len -= rbytes; + err = 0; + } + + /* the last block may be a hole */ + if (!err && all_zero) { + AuLabel(last hole); + + err = 1; + if (au_test_nfs(dst->f_path.dentry->d_sb)) { + /* nfs requires this step to make last hole */ + /* is this only nfs? */ + do { + /* todo: signal_pending? */ + err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); + } while (err == -EAGAIN || err == -EINTR); + if (err == 1) + dst->f_pos--; + } + + if (err == 1) { + ia = (void *)buf; + ia->ia_size = dst->f_pos; + ia->ia_valid = ATTR_SIZE | ATTR_FILE; + ia->ia_file = dst; + h_inode = file_inode(dst); + inode_lock_nested(h_inode, AuLsc_I_CHILD2); + /* no delegation since it is just created */ + err = vfsub_notify_change(&dst->f_path, ia, + /*delegated*/NULL); + inode_unlock(h_inode); + } + } + + return err; +} + +int au_copy_file(struct file *dst, struct file *src, loff_t len) +{ + int err; + unsigned long blksize; + unsigned char do_kfree; + char *buf; + struct super_block *h_sb; + + err = -ENOMEM; + h_sb = file_inode(dst)->i_sb; + blksize = h_sb->s_blocksize; + if (!blksize || PAGE_SIZE < blksize) + blksize = PAGE_SIZE; + AuDbg("blksize %lu\n", blksize); + do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); + if (do_kfree) + buf = kmalloc(blksize, GFP_NOFS); + else + buf = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!buf)) + goto out; + + if (len > (1 << 22)) + AuDbg("copying a large file %lld\n", (long long)len); + + src->f_pos = 0; + dst->f_pos = 0; + err = au_do_copy_file(dst, src, len, buf, blksize); + if (do_kfree) { + AuDebugOn(!au_kfree_do_sz_test(blksize)); + au_kfree_do_rcu(buf); + } else + free_page((unsigned long)buf); + +out: + return err; +} + +static int au_do_copy(struct file *dst, struct file *src, loff_t len) +{ + int err; + struct super_block *h_src_sb; + struct inode *h_src_inode; + + h_src_inode = file_inode(src); + h_src_sb = h_src_inode->i_sb; + + /* XFS acquires inode_lock */ + if (!au_test_xfs(h_src_sb)) + err = au_copy_file(dst, src, len); + else { + inode_unlock_shared(h_src_inode); + err = au_copy_file(dst, src, len); + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } + + return err; +} + +static int au_clone_or_copy(struct file *dst, struct file *src, loff_t len) +{ + int err; + loff_t lo; + struct super_block *h_src_sb; + struct inode *h_src_inode; + + h_src_inode = file_inode(src); + h_src_sb = h_src_inode->i_sb; + if (h_src_sb != file_inode(dst)->i_sb + || !dst->f_op->remap_file_range) { + err = au_do_copy(dst, src, len); + goto out; + } + + if (!au_test_nfs(h_src_sb)) { + inode_unlock_shared(h_src_inode); + lo = vfsub_clone_file_range(src, dst, len); + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } else + lo = vfsub_clone_file_range(src, dst, len); + if (lo == len) { + err = 0; + goto out; /* success */ + } else if (lo >= 0) + /* todo: possible? */ + /* paritially succeeded */ + AuDbg("lo %lld, len %lld. Retrying.\n", lo, len); + else if (lo != -EOPNOTSUPP) { + /* older XFS has a condition in cloning */ + err = lo; + goto out; + } + + /* the backend fs on NFS may not support cloning */ + err = au_do_copy(dst, src, len); + +out: + AuTraceErr(err); + return err; +} + +/* + * to support a sparse file which is opened with O_APPEND, + * we need to close the file. + */ +static int au_cp_regular(struct au_cp_generic *cpg) +{ + int err, i; + enum { SRC, DST }; + struct { + aufs_bindex_t bindex; + unsigned int flags; + struct dentry *dentry; + int force_wr; + struct file *file; + } *f, file[] = { + { + .bindex = cpg->bsrc, + .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, + }, + { + .bindex = cpg->bdst, + .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, + .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), + } + }; + struct au_branch *br; + struct super_block *sb, *h_src_sb; + struct inode *h_src_inode; + struct task_struct *tsk = current; + + /* bsrc branch can be ro/rw. */ + sb = cpg->dentry->d_sb; + f = file; + for (i = 0; i < 2; i++, f++) { + f->dentry = au_h_dptr(cpg->dentry, f->bindex); + f->file = au_h_open(cpg->dentry, f->bindex, f->flags, + /*file*/NULL, f->force_wr); + if (IS_ERR(f->file)) { + err = PTR_ERR(f->file); + if (i == SRC) + goto out; + else + goto out_src; + } + } + + /* try stopping to update while we copyup */ + h_src_inode = d_inode(file[SRC].dentry); + h_src_sb = h_src_inode->i_sb; + if (!au_test_nfs(h_src_sb)) + IMustLock(h_src_inode); + err = au_clone_or_copy(file[DST].file, file[SRC].file, cpg->len); + + /* i wonder if we had O_NO_DELAY_FPUT flag */ + if (tsk->flags & PF_KTHREAD) + __fput_sync(file[DST].file); + else { + /* it happened actually */ + fput(file[DST].file); + /* + * too bad. + * we have to call both since we don't know which place the file + * was added to. + */ + task_work_run(); + flush_delayed_fput(); + } + br = au_sbr(sb, file[DST].bindex); + au_lcnt_dec(&br->br_nfiles); + +out_src: + fput(file[SRC].file); + br = au_sbr(sb, file[SRC].bindex); + au_lcnt_dec(&br->br_nfiles); +out: + return err; +} + +static int au_do_cpup_regular(struct au_cp_generic *cpg, + struct au_cpup_reg_attr *h_src_attr) +{ + int err, rerr; + loff_t l; + struct path h_path; + struct inode *h_src_inode, *h_dst_inode; + + err = 0; + h_src_inode = au_h_iptr(d_inode(cpg->dentry), cpg->bsrc); + l = i_size_read(h_src_inode); + if (cpg->len == -1 || l < cpg->len) + cpg->len = l; + if (cpg->len) { + /* try stopping to update while we are referencing */ + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + au_pin_hdir_unlock(cpg->pin); + + h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); + h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); + h_src_attr->iflags = h_src_inode->i_flags; + if (!au_test_nfs(h_src_inode->i_sb)) + err = vfsub_getattr(&h_path, &h_src_attr->st); + else { + inode_unlock_shared(h_src_inode); + err = vfsub_getattr(&h_path, &h_src_attr->st); + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } + if (unlikely(err)) { + inode_unlock_shared(h_src_inode); + goto out; + } + h_src_attr->valid = 1; + if (!au_test_nfs(h_src_inode->i_sb)) { + err = au_cp_regular(cpg); + inode_unlock_shared(h_src_inode); + } else { + inode_unlock_shared(h_src_inode); + err = au_cp_regular(cpg); + } + rerr = au_pin_hdir_relock(cpg->pin); + if (!err && rerr) + err = rerr; + } + if (!err && (h_src_inode->i_state & I_LINKABLE)) { + h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); + h_dst_inode = d_inode(h_path.dentry); + spin_lock(&h_dst_inode->i_lock); + h_dst_inode->i_state |= I_LINKABLE; + spin_unlock(&h_dst_inode->i_lock); + } + +out: + return err; +} + +static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, + struct inode *h_dir) +{ + int err; + DEFINE_DELAYED_CALL(done); + const char *sym; + + sym = vfs_get_link(h_src, &done); + err = PTR_ERR(sym); + if (IS_ERR(sym)) + goto out; + + err = vfsub_symlink(h_dir, h_path, sym); + +out: + do_delayed_call(&done); + return err; +} + +/* + * regardless 'acl' option, reset all ACL. + * All ACL will be copied up later from the original entry on the lower branch. + */ +static int au_reset_acl(struct inode *h_dir, struct path *h_path, umode_t mode) +{ + int err; + struct dentry *h_dentry; + struct inode *h_inode; + + h_dentry = h_path->dentry; + h_inode = d_inode(h_dentry); + /* forget_all_cached_acls(h_inode)); */ + err = vfsub_removexattr(h_dentry, XATTR_NAME_POSIX_ACL_ACCESS); + AuTraceErr(err); + if (err == -EOPNOTSUPP) + err = 0; + if (!err) + err = vfsub_acl_chmod(h_inode, mode); + + AuTraceErr(err); + return err; +} + +static int au_do_cpup_dir(struct au_cp_generic *cpg, struct dentry *dst_parent, + struct inode *h_dir, struct path *h_path) +{ + int err; + struct inode *dir, *inode; + + err = vfsub_removexattr(h_path->dentry, XATTR_NAME_POSIX_ACL_DEFAULT); + AuTraceErr(err); + if (err == -EOPNOTSUPP) + err = 0; + if (unlikely(err)) + goto out; + + /* + * strange behaviour from the users view, + * particularly setattr case + */ + dir = d_inode(dst_parent); + if (au_ibtop(dir) == cpg->bdst) + au_cpup_attr_nlink(dir, /*force*/1); + inode = d_inode(cpg->dentry); + au_cpup_attr_nlink(inode, /*force*/1); + +out: + return err; +} + +static noinline_for_stack +int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, + struct au_cpup_reg_attr *h_src_attr) +{ + int err; + umode_t mode; + unsigned int mnt_flags; + unsigned char isdir, isreg, force; + const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); + struct au_dtime dt; + struct path h_path; + struct dentry *h_src, *h_dst, *h_parent; + struct inode *h_inode, *h_dir; + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ + h_src = au_h_dptr(cpg->dentry, cpg->bsrc); + h_inode = d_inode(h_src); + AuDebugOn(h_inode != au_h_iptr(d_inode(cpg->dentry), cpg->bsrc)); + + /* try stopping to be referenced while we are creating */ + h_dst = au_h_dptr(cpg->dentry, cpg->bdst); + if (au_ftest_cpup(cpg->flags, RENAME)) + AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, + AUFS_WH_PFX_LEN)); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + AuDebugOn(h_parent != h_dst->d_parent); + + sb = cpg->dentry->d_sb; + h_path.mnt = au_sbr_mnt(sb, cpg->bdst); + if (do_dt) { + h_path.dentry = h_parent; + au_dtime_store(&dt, dst_parent, &h_path); + } + h_path.dentry = h_dst; + + isreg = 0; + isdir = 0; + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: + isreg = 1; + err = vfsub_create(h_dir, &h_path, 0600, /*want_excl*/true); + if (!err) + err = au_do_cpup_regular(cpg, h_src_attr); + break; + case S_IFDIR: + isdir = 1; + err = vfsub_mkdir(h_dir, &h_path, mode); + if (!err) + err = au_do_cpup_dir(cpg, dst_parent, h_dir, &h_path); + break; + case S_IFLNK: + err = au_do_cpup_symlink(&h_path, h_src, h_dir); + break; + case S_IFCHR: + case S_IFBLK: + AuDebugOn(!capable(CAP_MKNOD)); + fallthrough; + case S_IFIFO: + case S_IFSOCK: + err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); + break; + default: + AuIOErr("Unknown inode type 0%o\n", mode); + err = -EIO; + } + if (!err) + err = au_reset_acl(h_dir, &h_path, mode); + + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, UDBA_NONE) + && !isdir + && au_opt_test(mnt_flags, XINO) + && (h_inode->i_nlink == 1 + || (h_inode->i_state & I_LINKABLE)) + /* todo: unnecessary? */ + /* && d_inode(cpg->dentry)->i_nlink == 1 */ + && cpg->bdst < cpg->bsrc + && !au_ftest_cpup(cpg->flags, KEEPLINO)) + au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); + /* ignore this error */ + + if (!err) { + force = 0; + if (isreg) { + force = !!cpg->len; + if (cpg->len == -1) + force = !!i_size_read(h_inode); + } + au_fhsm_wrote(sb, cpg->bdst, force); + } + + if (do_dt) + au_dtime_revert(&dt); + return err; +} + +static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) +{ + int err; + struct dentry *dentry, *h_dentry, *h_parent, *parent; + struct path h_ppath; + struct inode *h_dir; + aufs_bindex_t bdst; + + dentry = cpg->dentry; + bdst = cpg->bdst; + h_ppath.mnt = au_sbr_mnt(dentry->d_sb, bdst); + h_dentry = au_h_dptr(dentry, bdst); + if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { + dget(h_dentry); + au_set_h_dptr(dentry, bdst, NULL); + err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (!err) + h_path->dentry = dget(au_h_dptr(dentry, bdst)); + au_set_h_dptr(dentry, bdst, h_dentry); + } else { + err = 0; + parent = dget_parent(dentry); + h_ppath.dentry = au_h_dptr(parent, bdst); + dput(parent); + h_path->dentry = vfsub_lkup_one(&dentry->d_name, &h_ppath); + if (IS_ERR(h_path->dentry)) + err = PTR_ERR(h_path->dentry); + } + if (unlikely(err)) + goto out; + + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + AuDbg("%pd %pd\n", h_dentry, h_path->dentry); + /* no delegation since it is just created */ + err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL, + /*flags*/0); + dput(h_path->dentry); + +out: + return err; +} + +/* + * copyup the @dentry from @bsrc to @bdst. + * the caller must set the both of lower dentries. + * @len is for truncating when it is -1 copyup the entire file. + * in link/rename cases, @dst_parent may be different from the real one. + * basic->bsrc can be larger than basic->bdst. + * aufs doesn't touch the credential so + * security_inode_copy_up{,_xattr}() are unnecessary. + */ +static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ + int err, rerr; + aufs_bindex_t old_ibtop; + unsigned char isdir, plink; + struct dentry *h_src, *h_dst, *h_parent; + struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode; + struct super_block *sb; + struct au_branch *br; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct path h_path; + struct au_cpup_reg_attr h_src_attr; + } *a; + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->h_src_attr.valid = 0; + + sb = cpg->dentry->d_sb; + br = au_sbr(sb, cpg->bdst); + a->h_path.mnt = au_br_mnt(br); + h_dst = au_h_dptr(cpg->dentry, cpg->bdst); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + + h_src = au_h_dptr(cpg->dentry, cpg->bsrc); + inode = d_inode(cpg->dentry); + + if (!dst_parent) + dst_parent = dget_parent(cpg->dentry); + else + dget(dst_parent); + + plink = !!au_opt_test(au_mntflags(sb), PLINK); + dst_inode = au_h_iptr(inode, cpg->bdst); + if (dst_inode) { + if (unlikely(!plink)) { + err = -EIO; + AuIOErr("hi%lu(i%lu) exists on b%d " + "but plink is disabled\n", + dst_inode->i_ino, inode->i_ino, cpg->bdst); + goto out_parent; + } + + if (dst_inode->i_nlink) { + const int do_dt = au_ftest_cpup(cpg->flags, DTIME); + + h_src = au_plink_lkup(inode, cpg->bdst); + err = PTR_ERR(h_src); + if (IS_ERR(h_src)) + goto out_parent; + if (unlikely(d_is_negative(h_src))) { + err = -EIO; + AuIOErr("i%lu exists on b%d " + "but not pseudo-linked\n", + inode->i_ino, cpg->bdst); + dput(h_src); + goto out_parent; + } + + if (do_dt) { + a->h_path.dentry = h_parent; + au_dtime_store(&a->dt, dst_parent, &a->h_path); + } + + a->h_path.dentry = h_dst; + delegated = NULL; + err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); + if (!err && au_ftest_cpup(cpg->flags, RENAME)) + err = au_do_ren_after_cpup(cpg, &a->h_path); + if (do_dt) + au_dtime_revert(&a->dt); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + dput(h_src); + goto out_parent; + } else + /* todo: cpup_wh_file? */ + /* udba work */ + au_update_ibrange(inode, /*do_put_zero*/1); + } + + isdir = S_ISDIR(inode->i_mode); + old_ibtop = au_ibtop(inode); + err = cpup_entry(cpg, dst_parent, &a->h_src_attr); + if (unlikely(err)) + goto out_rev; + dst_inode = d_inode(h_dst); + inode_lock_nested(dst_inode, AuLsc_I_CHILD2); + /* todo: necessary? */ + /* au_pin_hdir_unlock(cpg->pin); */ + + err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); + if (unlikely(err)) { + /* todo: necessary? */ + /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ + inode_unlock(dst_inode); + goto out_rev; + } + + if (cpg->bdst < old_ibtop) { + if (S_ISREG(inode->i_mode)) { + err = au_dy_iaop(inode, cpg->bdst, dst_inode); + if (unlikely(err)) { + /* ignore an error */ + /* au_pin_hdir_relock(cpg->pin); */ + inode_unlock(dst_inode); + goto out_rev; + } + } + au_set_ibtop(inode, cpg->bdst); + } else + au_set_ibbot(inode, cpg->bdst); + au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), + au_hi_flags(inode, isdir)); + + /* todo: necessary? */ + /* err = au_pin_hdir_relock(cpg->pin); */ + inode_unlock(dst_inode); + if (unlikely(err)) + goto out_rev; + + src_inode = d_inode(h_src); + if (!isdir + && (src_inode->i_nlink > 1 + || src_inode->i_state & I_LINKABLE) + && plink) + au_plink_append(inode, cpg->bdst, h_dst); + + if (au_ftest_cpup(cpg->flags, RENAME)) { + a->h_path.dentry = h_dst; + err = au_do_ren_after_cpup(cpg, &a->h_path); + } + if (!err) + goto out_parent; /* success */ + + /* revert */ +out_rev: + a->h_path.dentry = h_parent; + au_dtime_store(&a->dt, dst_parent, &a->h_path); + a->h_path.dentry = h_dst; + rerr = 0; + if (d_is_positive(h_dst)) { + if (!isdir) { + /* no delegation since it is just created */ + rerr = vfsub_unlink(h_dir, &a->h_path, + /*delegated*/NULL, /*force*/0); + } else + rerr = vfsub_rmdir(h_dir, &a->h_path); + } + au_dtime_revert(&a->dt); + if (rerr) { + AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); + err = -EIO; + } +out_parent: + dput(dst_parent); + au_kfree_rcu(a); +out: + return err; +} + +#if 0 /* reserved */ +struct au_cpup_single_args { + int *errp; + struct au_cp_generic *cpg; + struct dentry *dst_parent; +}; + +static void au_call_cpup_single(void *args) +{ + struct au_cpup_single_args *a = args; + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_single(a->cpg, a->dst_parent); + au_pin_hdir_release(a->cpg->pin); +} +#endif + +/* + * prevent SIGXFSZ in copy-up. + * testing CAP_MKNOD is for generic fs, + * but CAP_FSETID is for xfs only, currently. + */ +static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) +{ + int do_sio; + struct super_block *sb; + struct inode *h_dir; + + do_sio = 0; + sb = au_pinned_parent(pin)->d_sb; + if (!au_wkq_test() + && (!au_sbi(sb)->si_plink_maint_pid + || au_plink_maint(sb, AuLock_NOPLM))) { + switch (mode & S_IFMT) { + case S_IFREG: + /* no condition about RLIMIT_FSIZE and the file size */ + do_sio = 1; + break; + case S_IFCHR: + case S_IFBLK: + do_sio = !capable(CAP_MKNOD); + break; + } + if (!do_sio) + do_sio = ((mode & (S_ISUID | S_ISGID)) + && !capable(CAP_FSETID)); + /* this workaround may be removed in the future */ + if (!do_sio) { + h_dir = au_pinned_h_dir(pin); + do_sio = h_dir->i_mode & S_ISVTX; + } + } + + return do_sio; +} + +#if 0 /* reserved */ +int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ + int err, wkq_err; + struct dentry *h_dentry; + + h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); + if (!au_cpup_sio_test(pin, d_inode(h_dentry)->i_mode)) + err = au_cpup_single(cpg, dst_parent); + else { + struct au_cpup_single_args args = { + .errp = &err, + .cpg = cpg, + .dst_parent = dst_parent + }; + wkq_err = au_wkq_wait(au_call_cpup_single, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} +#endif + +/* + * copyup the @dentry from the first active lower branch to @bdst, + * using au_cpup_single(). + */ +static int au_cpup_simple(struct au_cp_generic *cpg) +{ + int err; + unsigned int flags_orig; + struct dentry *dentry; + + AuDebugOn(cpg->bsrc < 0); + + dentry = cpg->dentry; + DiMustWriteLock(dentry); + + err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); + if (!err) { + flags_orig = cpg->flags; + au_fset_cpup(cpg->flags, RENAME); + err = au_cpup_single(cpg, NULL); + cpg->flags = flags_orig; + if (!err) + return 0; /* success */ + + /* revert */ + au_set_h_dptr(dentry, cpg->bdst, NULL); + au_set_dbtop(dentry, cpg->bsrc); + } + + return err; +} + +struct au_cpup_simple_args { + int *errp; + struct au_cp_generic *cpg; +}; + +static void au_call_cpup_simple(void *args) +{ + struct au_cpup_simple_args *a = args; + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_simple(a->cpg); + au_pin_hdir_release(a->cpg->pin); +} + +static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) +{ + int err, wkq_err; + struct dentry *dentry, *parent; + struct file *h_file; + struct inode *h_dir; + + dentry = cpg->dentry; + h_file = NULL; + if (au_ftest_cpup(cpg->flags, HOPEN)) { + AuDebugOn(cpg->bsrc < 0); + h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + } + + parent = dget_parent(dentry); + h_dir = au_h_iptr(d_inode(parent), cpg->bdst); + if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) + err = au_cpup_simple(cpg); + else { + struct au_cpup_simple_args args = { + .errp = &err, + .cpg = cpg + }; + wkq_err = au_wkq_wait(au_call_cpup_simple, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + dput(parent); + if (h_file) + au_h_open_post(dentry, cpg->bsrc, h_file); + +out: + return err; +} + +int au_sio_cpup_simple(struct au_cp_generic *cpg) +{ + aufs_bindex_t bsrc, bbot; + struct dentry *dentry, *h_dentry; + + if (cpg->bsrc < 0) { + dentry = cpg->dentry; + bbot = au_dbbot(dentry); + for (bsrc = cpg->bdst + 1; bsrc <= bbot; bsrc++) { + h_dentry = au_h_dptr(dentry, bsrc); + if (h_dentry) { + AuDebugOn(d_is_negative(h_dentry)); + break; + } + } + AuDebugOn(bsrc > bbot); + cpg->bsrc = bsrc; + } + AuDebugOn(cpg->bsrc <= cpg->bdst); + return au_do_sio_cpup_simple(cpg); +} + +int au_sio_cpdown_simple(struct au_cp_generic *cpg) +{ + AuDebugOn(cpg->bdst <= cpg->bsrc); + return au_do_sio_cpup_simple(cpg); +} + +/* ---------------------------------------------------------------------- */ + +/* + * copyup the deleted file for writing. + */ +static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, + struct file *file) +{ + int err; + unsigned int flags_orig; + aufs_bindex_t bsrc_orig; + struct au_dinfo *dinfo; + struct { + struct au_hdentry *hd; + struct dentry *h_dentry; + } hdst, hsrc; + + dinfo = au_di(cpg->dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + + bsrc_orig = cpg->bsrc; + cpg->bsrc = dinfo->di_btop; + hdst.hd = au_hdentry(dinfo, cpg->bdst); + hdst.h_dentry = hdst.hd->hd_dentry; + hdst.hd->hd_dentry = wh_dentry; + dinfo->di_btop = cpg->bdst; + + hsrc.h_dentry = NULL; + if (file) { + hsrc.hd = au_hdentry(dinfo, cpg->bsrc); + hsrc.h_dentry = hsrc.hd->hd_dentry; + hsrc.hd->hd_dentry = au_hf_top(file)->f_path.dentry; + } + flags_orig = cpg->flags; + cpg->flags = !AuCpup_DTIME; + err = au_cpup_single(cpg, /*h_parent*/NULL); + cpg->flags = flags_orig; + if (file) { + if (!err) + err = au_reopen_nondir(file); + hsrc.hd->hd_dentry = hsrc.h_dentry; + } + hdst.hd->hd_dentry = hdst.h_dentry; + dinfo->di_btop = cpg->bsrc; + cpg->bsrc = bsrc_orig; + + return err; +} + +static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) +{ + int err; + aufs_bindex_t bdst; + struct au_dtime dt; + struct dentry *dentry, *parent, *h_parent, *wh_dentry; + struct au_branch *br; + struct path h_path; + + dentry = cpg->dentry; + bdst = cpg->bdst; + br = au_sbr(dentry->d_sb, bdst); + parent = dget_parent(dentry); + h_parent = au_h_dptr(parent, bdst); + wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + h_path.dentry = h_parent; + h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, parent, &h_path); + err = au_do_cpup_wh(cpg, wh_dentry, file); + if (unlikely(err)) + goto out_wh; + + dget(wh_dentry); + h_path.dentry = wh_dentry; + if (!d_is_dir(wh_dentry)) { + /* no delegation since it is just created */ + err = vfsub_unlink(d_inode(h_parent), &h_path, + /*delegated*/NULL, /*force*/0); + } else + err = vfsub_rmdir(d_inode(h_parent), &h_path); + if (unlikely(err)) { + AuIOErr("failed remove copied-up tmp file %pd(%d)\n", + wh_dentry, err); + err = -EIO; + } + au_dtime_revert(&dt); + au_set_hi_wh(d_inode(dentry), bdst, wh_dentry); + +out_wh: + dput(wh_dentry); +out: + dput(parent); + return err; +} + +struct au_cpup_wh_args { + int *errp; + struct au_cp_generic *cpg; + struct file *file; +}; + +static void au_call_cpup_wh(void *args) +{ + struct au_cpup_wh_args *a = args; + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_wh(a->cpg, a->file); + au_pin_hdir_release(a->cpg->pin); +} + +int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) +{ + int err, wkq_err; + aufs_bindex_t bdst; + struct dentry *dentry, *parent, *h_orph, *h_parent; + struct inode *dir, *h_dir, *h_tmpdir; + struct au_wbr *wbr; + struct au_pin wh_pin, *pin_orig; + + dentry = cpg->dentry; + bdst = cpg->bdst; + parent = dget_parent(dentry); + dir = d_inode(parent); + h_orph = NULL; + h_parent = NULL; + h_dir = au_igrab(au_h_iptr(dir, bdst)); + h_tmpdir = h_dir; + pin_orig = NULL; + if (!h_dir->i_nlink) { + wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; + h_orph = wbr->wbr_orph; + + h_parent = dget(au_h_dptr(parent, bdst)); + au_set_h_dptr(parent, bdst, dget(h_orph)); + h_tmpdir = d_inode(h_orph); + au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); + + inode_lock_nested(h_tmpdir, AuLsc_I_PARENT3); + /* todo: au_h_open_pre()? */ + + pin_orig = cpg->pin; + au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, + AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); + cpg->pin = &wh_pin; + } + + if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) + err = au_cpup_wh(cpg, file); + else { + struct au_cpup_wh_args args = { + .errp = &err, + .cpg = cpg, + .file = file + }; + wkq_err = au_wkq_wait(au_call_cpup_wh, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + if (h_orph) { + inode_unlock(h_tmpdir); + /* todo: au_h_open_post()? */ + au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); + au_set_h_dptr(parent, bdst, h_parent); + AuDebugOn(!pin_orig); + cpg->pin = pin_orig; + } + iput(h_dir); + dput(parent); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * generic routine for both of copy-up and copy-down. + */ +/* cf. revalidate function in file.c */ +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg) +{ + int err; + struct au_pin pin; + struct dentry *d, *parent, *h_parent, *real_parent, *h_dentry; + + err = 0; + parent = dget_parent(dentry); + if (IS_ROOT(parent)) + goto out; + + au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, + au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); + + /* do not use au_dpage */ + real_parent = parent; + while (1) { + dput(parent); + parent = dget_parent(dentry); + h_parent = au_h_dptr(parent, bdst); + if (h_parent) + goto out; /* success */ + + /* find top dir which is necessary to cpup */ + do { + d = parent; + dput(parent); + parent = dget_parent(d); + di_read_lock_parent3(parent, !AuLock_IR); + h_parent = au_h_dptr(parent, bdst); + di_read_unlock(parent, !AuLock_IR); + } while (!h_parent); + + if (d != real_parent) + di_write_lock_child3(d); + + /* somebody else might create while we were sleeping */ + h_dentry = au_h_dptr(d, bdst); + if (!h_dentry || d_is_negative(h_dentry)) { + if (h_dentry) + au_update_dbtop(d); + + au_pin_set_dentry(&pin, d); + err = au_do_pin(&pin); + if (!err) { + err = cp(d, bdst, &pin, h_parent, arg); + au_unpin(&pin); + } + } + + if (d != real_parent) + di_write_unlock(d); + if (unlikely(err)) + break; + } + +out: + dput(parent); + return err; +} + +static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent __maybe_unused, + void *arg __maybe_unused) +{ + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = bdst, + .bsrc = -1, + .len = 0, + .pin = pin, + .flags = AuCpup_DTIME + }; + return au_sio_cpup_simple(&cpg); +} + +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) +{ + return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); +} + +int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) +{ + int err; + struct dentry *parent; + struct inode *dir; + + parent = dget_parent(dentry); + dir = d_inode(parent); + err = 0; + if (au_h_iptr(dir, bdst)) + goto out; + + di_read_unlock(parent, AuLock_IR); + di_write_lock_parent(parent); + /* someone else might change our inode while we were sleeping */ + if (!au_h_iptr(dir, bdst)) + err = au_cpup_dirs(dentry, bdst); + di_downgrade_lock(parent, AuLock_IR); + +out: + dput(parent); + return err; +} diff -Naur --no-dereference a/fs/aufs/cpup.h b/fs/aufs/cpup.h --- a/fs/aufs/cpup.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/cpup.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * copy-up/down functions + */ + +#ifndef __AUFS_CPUP_H__ +#define __AUFS_CPUP_H__ + +#ifdef __KERNEL__ + +#include + +struct inode; +struct file; +struct au_pin; + +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); +void au_cpup_attr_timesizes(struct inode *inode); +void au_cpup_attr_nlink(struct inode *inode, int force); +void au_cpup_attr_changeable(struct inode *inode); +void au_cpup_igen(struct inode *inode, struct inode *h_inode); +void au_cpup_attr_all(struct inode *inode, int force); + +/* ---------------------------------------------------------------------- */ + +struct au_cp_generic { + struct dentry *dentry; + aufs_bindex_t bdst, bsrc; + loff_t len; + struct au_pin *pin; + unsigned int flags; +}; + +/* cpup flags */ +#define AuCpup_DTIME 1 /* do dtime_store/revert */ +#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, + for link(2) */ +#define AuCpup_RENAME (1 << 2) /* rename after cpup */ +#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in + cpup */ +#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the + existing entry */ +#define AuCpup_RWDST (1 << 5) /* force write target even if + the branch is marked as RO */ + +#ifndef CONFIG_AUFS_BR_HFSPLUS +#undef AuCpup_HOPEN +#define AuCpup_HOPEN 0 +#endif + +#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) +#define au_fset_cpup(flags, name) \ + do { (flags) |= AuCpup_##name; } while (0) +#define au_fclr_cpup(flags, name) \ + do { (flags) &= ~AuCpup_##name; } while (0) + +int au_copy_file(struct file *dst, struct file *src, loff_t len); +int au_sio_cpup_simple(struct au_cp_generic *cpg); +int au_sio_cpdown_simple(struct au_cp_generic *cpg); +int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); + +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg); +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); +int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); + +/* ---------------------------------------------------------------------- */ + +/* keep timestamps when copyup */ +struct au_dtime { + struct dentry *dt_dentry; + struct path dt_h_path; + struct timespec64 dt_atime, dt_mtime; +}; +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct path *h_path); +void au_dtime_revert(struct au_dtime *dt); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_CPUP_H__ */ diff -Naur --no-dereference a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c --- a/fs/aufs/dbgaufs.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dbgaufs.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debugfs interface + */ + +#include +#include "aufs.h" + +#ifndef CONFIG_SYSFS +#error DEBUG_FS depends upon SYSFS +#endif + +static struct dentry *dbgaufs; +static const mode_t dbgaufs_mode = 0444; + +/* 20 is max digits length of ulong 64 */ +struct dbgaufs_arg { + int n; + char a[20 * 4]; +}; + +/* + * common function for all XINO files + */ +static int dbgaufs_xi_release(struct inode *inode __maybe_unused, + struct file *file) +{ + void *p; + + p = file->private_data; + if (p) { + /* this is struct dbgaufs_arg */ + AuDebugOn(!au_kfree_sz_test(p)); + au_kfree_do_rcu(p); + } + return 0; +} + +static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt, + int cnt) +{ + int err; + struct kstat st; + struct dbgaufs_arg *p; + + err = -ENOMEM; + p = kmalloc(sizeof(*p), GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = 0; + p->n = 0; + file->private_data = p; + if (!xf) + goto out; + + err = vfsub_getattr(&xf->f_path, &st); + if (!err) { + if (do_fcnt) + p->n = snprintf + (p->a, sizeof(p->a), "%d, %llux%u %lld\n", + cnt, st.blocks, st.blksize, + (long long)st.size); + else + p->n = snprintf(p->a, sizeof(p->a), "%llux%u %lld\n", + st.blocks, st.blksize, + (long long)st.size); + AuDebugOn(p->n >= sizeof(p->a)); + } else { + p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); + err = 0; + } + +out: + return err; +} + +static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dbgaufs_arg *p; + + p = file->private_data; + return simple_read_from_buffer(buf, count, ppos, p->a, p->n); +} + +/* ---------------------------------------------------------------------- */ + +struct dbgaufs_plink_arg { + int n; + char a[]; +}; + +static int dbgaufs_plink_release(struct inode *inode __maybe_unused, + struct file *file) +{ + free_page((unsigned long)file->private_data); + return 0; +} + +static int dbgaufs_plink_open(struct inode *inode, struct file *file) +{ + int err, i, limit; + unsigned long n, sum; + struct dbgaufs_plink_arg *p; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct hlist_bl_head *hbl; + + err = -ENOMEM; + p = (void *)get_zeroed_page(GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = -EFBIG; + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + if (au_opt_test(au_mntflags(sb), PLINK)) { + limit = PAGE_SIZE - sizeof(p->n); + + /* the number of buckets */ + n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); + p->n += n; + limit -= n; + + sum = 0; + for (i = 0, hbl = sbinfo->si_plink; i < AuPlink_NHASH; + i++, hbl++) { + n = au_hbl_count(hbl); + sum += n; + + n = snprintf(p->a + p->n, limit, "%lu ", n); + p->n += n; + limit -= n; + if (unlikely(limit <= 0)) + goto out_free; + } + p->a[p->n - 1] = '\n'; + + /* the sum of plinks */ + n = snprintf(p->a + p->n, limit, "%lu\n", sum); + p->n += n; + limit -= n; + if (unlikely(limit <= 0)) + goto out_free; + } else { +#define str "1\n0\n0\n" + p->n = sizeof(str) - 1; + strcpy(p->a, str); +#undef str + } + si_read_unlock(sb); + + err = 0; + file->private_data = p; + goto out; /* success */ + +out_free: + free_page((unsigned long)p); +out: + return err; +} + +static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dbgaufs_plink_arg *p; + + p = file->private_data; + return simple_read_from_buffer(buf, count, ppos, p->a, p->n); +} + +static const struct file_operations dbgaufs_plink_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_plink_open, + .release = dbgaufs_plink_release, + .read = dbgaufs_plink_read +}; + +/* ---------------------------------------------------------------------- */ + +static int dbgaufs_xib_open(struct inode *inode, struct file *file) +{ + int err; + struct au_sbinfo *sbinfo; + struct super_block *sb; + + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0, /*cnt*/0); + si_read_unlock(sb); + return err; +} + +static const struct file_operations dbgaufs_xib_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_xib_open, + .release = dbgaufs_xi_release, + .read = dbgaufs_xi_read +}; + +/* ---------------------------------------------------------------------- */ + +#define DbgaufsXi_PREFIX "xi" + +static int dbgaufs_xino_open(struct inode *inode, struct file *file) +{ + int err, idx; + long l; + aufs_bindex_t bindex; + char *p, a[sizeof(DbgaufsXi_PREFIX) + 8]; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct au_xino *xi; + struct file *xf; + struct qstr *name; + struct au_branch *br; + + err = -ENOENT; + name = &file->f_path.dentry->d_name; + if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) + || memcmp(name->name, DbgaufsXi_PREFIX, + sizeof(DbgaufsXi_PREFIX) - 1))) + goto out; + + AuDebugOn(name->len >= sizeof(a)); + memcpy(a, name->name, name->len); + a[name->len] = '\0'; + p = strchr(a, '-'); + if (p) + *p = '\0'; + err = kstrtol(a + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); + if (unlikely(err)) + goto out; + bindex = l; + idx = 0; + if (p) { + err = kstrtol(p + 1, 10, &l); + if (unlikely(err)) + goto out; + idx = l; + } + + err = -ENOENT; + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + if (unlikely(bindex < 0 || bindex > au_sbbot(sb))) + goto out_si; + br = au_sbr(sb, bindex); + xi = br->br_xino; + if (unlikely(idx >= xi->xi_nfile)) + goto out_si; + xf = au_xino_file(xi, idx); + if (xf) + err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1, + au_xino_count(br)); + +out_si: + si_read_unlock(sb); +out: + AuTraceErr(err); + return err; +} + +static const struct file_operations dbgaufs_xino_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_xino_open, + .release = dbgaufs_xi_release, + .read = dbgaufs_xi_read +}; + +void dbgaufs_xino_del(struct au_branch *br) +{ + struct dentry *dbgaufs; + + dbgaufs = br->br_dbgaufs; + if (!dbgaufs) + return; + + br->br_dbgaufs = NULL; + /* debugfs acquires the parent i_mutex */ + lockdep_off(); + debugfs_remove(dbgaufs); + lockdep_on(); +} + +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ + aufs_bindex_t bbot; + struct au_branch *br; + + if (!au_sbi(sb)->si_dbgaufs) + return; + + bbot = au_sbbot(sb); + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + dbgaufs_xino_del(br); + } +} + +static void dbgaufs_br_do_add(struct super_block *sb, aufs_bindex_t bindex, + unsigned int idx, struct dentry *parent, + struct au_sbinfo *sbinfo) +{ + struct au_branch *br; + struct dentry *d; + /* "xi" bindex(5) "-" idx(2) NULL */ + char name[sizeof(DbgaufsXi_PREFIX) + 8]; + + if (!idx) + snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); + else + snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d-%u", + bindex, idx); + br = au_sbr(sb, bindex); + if (br->br_dbgaufs) { + struct qstr qstr = QSTR_INIT(name, strlen(name)); + + if (!au_qstreq(&br->br_dbgaufs->d_name, &qstr)) { + /* debugfs acquires the parent i_mutex */ + lockdep_off(); + d = debugfs_rename(parent, br->br_dbgaufs, parent, + name); + lockdep_on(); + if (unlikely(!d)) + pr_warn("failed renaming %pd/%s, ignored.\n", + parent, name); + } + } else { + lockdep_off(); + br->br_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, + sbinfo, &dbgaufs_xino_fop); + lockdep_on(); + if (unlikely(!br->br_dbgaufs)) + pr_warn("failed creating %pd/%s, ignored.\n", + parent, name); + } +} + +static void dbgaufs_br_add(struct super_block *sb, aufs_bindex_t bindex, + struct dentry *parent, struct au_sbinfo *sbinfo) +{ + struct au_branch *br; + struct au_xino *xi; + unsigned int u; + + br = au_sbr(sb, bindex); + xi = br->br_xino; + for (u = 0; u < xi->xi_nfile; u++) + dbgaufs_br_do_add(sb, bindex, u, parent, sbinfo); +} + +void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex, int topdown) +{ + struct au_sbinfo *sbinfo; + struct dentry *parent; + aufs_bindex_t bbot; + + if (!au_opt_test(au_mntflags(sb), XINO)) + return; + + sbinfo = au_sbi(sb); + parent = sbinfo->si_dbgaufs; + if (!parent) + return; + + bbot = au_sbbot(sb); + if (topdown) + for (; bindex <= bbot; bindex++) + dbgaufs_br_add(sb, bindex, parent, sbinfo); + else + for (; bbot >= bindex; bbot--) + dbgaufs_br_add(sb, bbot, parent, sbinfo); +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_EXPORT +static int dbgaufs_xigen_open(struct inode *inode, struct file *file) +{ + int err; + struct au_sbinfo *sbinfo; + struct super_block *sb; + + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0, /*cnt*/0); + si_read_unlock(sb); + return err; +} + +static const struct file_operations dbgaufs_xigen_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_xigen_open, + .release = dbgaufs_xi_release, + .read = dbgaufs_xi_read +}; + +static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) +{ + int err; + + /* + * This function is a dynamic '__init' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ + + err = -EIO; + sbinfo->si_dbgaufs_xigen = debugfs_create_file + ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_xigen_fop); + if (sbinfo->si_dbgaufs_xigen) + err = 0; + + return err; +} +#else +static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) +{ + return 0; +} +#endif /* CONFIG_AUFS_EXPORT */ + +/* ---------------------------------------------------------------------- */ + +void dbgaufs_si_fin(struct au_sbinfo *sbinfo) +{ + /* + * This function is a dynamic '__fin' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ + + debugfs_remove_recursive(sbinfo->si_dbgaufs); + sbinfo->si_dbgaufs = NULL; +} + +int dbgaufs_si_init(struct au_sbinfo *sbinfo) +{ + int err; + char name[SysaufsSiNameLen]; + + /* + * This function is a dynamic '__init' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ + + err = -ENOENT; + if (!dbgaufs) { + AuErr1("/debug/aufs is uninitialized\n"); + goto out; + } + + err = -EIO; + sysaufs_name(sbinfo, name); + sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); + if (unlikely(!sbinfo->si_dbgaufs)) + goto out; + + /* regardless plink/noplink option */ + sbinfo->si_dbgaufs_plink = debugfs_create_file + ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_plink_fop); + if (unlikely(!sbinfo->si_dbgaufs_plink)) + goto out_dir; + + /* regardless xino/noxino option */ + sbinfo->si_dbgaufs_xib = debugfs_create_file + ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_xib_fop); + if (unlikely(!sbinfo->si_dbgaufs_xib)) + goto out_dir; + + err = dbgaufs_xigen_init(sbinfo); + if (!err) + goto out; /* success */ + +out_dir: + dbgaufs_si_fin(sbinfo); +out: + if (unlikely(err)) + pr_err("debugfs/aufs failed\n"); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void dbgaufs_fin(void) +{ + debugfs_remove(dbgaufs); +} + +int __init dbgaufs_init(void) +{ + int err; + + err = -EIO; + dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); + if (dbgaufs) + err = 0; + return err; +} diff -Naur --no-dereference a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h --- a/fs/aufs/dbgaufs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dbgaufs.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debugfs interface + */ + +#ifndef __DBGAUFS_H__ +#define __DBGAUFS_H__ + +#ifdef __KERNEL__ + +struct super_block; +struct au_sbinfo; +struct au_branch; + +#ifdef CONFIG_DEBUG_FS +/* dbgaufs.c */ +void dbgaufs_xino_del(struct au_branch *br); +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); +void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex, int topdown); +void dbgaufs_si_fin(struct au_sbinfo *sbinfo); +int dbgaufs_si_init(struct au_sbinfo *sbinfo); +void dbgaufs_fin(void); +int __init dbgaufs_init(void); +#else +AuStubVoid(dbgaufs_xino_del, struct au_branch *br) +AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) +AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex, + int topdown) +AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) +AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) +AuStubVoid(dbgaufs_fin, void) +AuStubInt0(__init dbgaufs_init, void) +#endif /* CONFIG_DEBUG_FS */ + +#endif /* __KERNEL__ */ +#endif /* __DBGAUFS_H__ */ diff -Naur --no-dereference a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c --- a/fs/aufs/dcsub.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dcsub.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for dentry cache + */ + +#include "aufs.h" + +static void au_dpage_free(struct au_dpage *dpage) +{ + int i; + struct dentry **p; + + p = dpage->dentries; + for (i = 0; i < dpage->ndentry; i++) + dput(*p++); + free_page((unsigned long)dpage->dentries); +} + +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) +{ + int err; + void *p; + + err = -ENOMEM; + dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); + if (unlikely(!dpages->dpages)) + goto out; + + p = (void *)__get_free_page(gfp); + if (unlikely(!p)) + goto out_dpages; + + dpages->dpages[0].ndentry = 0; + dpages->dpages[0].dentries = p; + dpages->ndpage = 1; + return 0; /* success */ + +out_dpages: + au_kfree_try_rcu(dpages->dpages); +out: + return err; +} + +void au_dpages_free(struct au_dcsub_pages *dpages) +{ + int i; + struct au_dpage *p; + + p = dpages->dpages; + for (i = 0; i < dpages->ndpage; i++) + au_dpage_free(p++); + au_kfree_try_rcu(dpages->dpages); +} + +static int au_dpages_append(struct au_dcsub_pages *dpages, + struct dentry *dentry, gfp_t gfp) +{ + int err, sz; + struct au_dpage *dpage; + void *p; + + dpage = dpages->dpages + dpages->ndpage - 1; + sz = PAGE_SIZE / sizeof(dentry); + if (unlikely(dpage->ndentry >= sz)) { + AuLabel(new dpage); + err = -ENOMEM; + sz = dpages->ndpage * sizeof(*dpages->dpages); + p = au_kzrealloc(dpages->dpages, sz, + sz + sizeof(*dpages->dpages), gfp, + /*may_shrink*/0); + if (unlikely(!p)) + goto out; + + dpages->dpages = p; + dpage = dpages->dpages + dpages->ndpage; + p = (void *)__get_free_page(gfp); + if (unlikely(!p)) + goto out; + + dpage->ndentry = 0; + dpage->dentries = p; + dpages->ndpage++; + } + + AuDebugOn(au_dcount(dentry) <= 0); + dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); + return 0; /* success */ + +out: + return err; +} + +/* todo: BAD approach */ +/* copied from linux/fs/dcache.c */ +enum d_walk_ret { + D_WALK_CONTINUE, + D_WALK_QUIT, + D_WALK_NORETRY, + D_WALK_SKIP, +}; + +extern void d_walk(struct dentry *parent, void *data, + enum d_walk_ret (*enter)(void *, struct dentry *)); + +struct ac_dpages_arg { + int err; + struct au_dcsub_pages *dpages; + struct super_block *sb; + au_dpages_test test; + void *arg; +}; + +static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) +{ + enum d_walk_ret ret; + struct ac_dpages_arg *arg = _arg; + + ret = D_WALK_CONTINUE; + if (dentry->d_sb == arg->sb + && !IS_ROOT(dentry) + && au_dcount(dentry) > 0 + && au_di(dentry) + && (!arg->test || arg->test(dentry, arg->arg))) { + arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); + if (unlikely(arg->err)) + ret = D_WALK_QUIT; + } + + return ret; +} + +int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, + au_dpages_test test, void *arg) +{ + struct ac_dpages_arg args = { + .err = 0, + .dpages = dpages, + .sb = root->d_sb, + .test = test, + .arg = arg + }; + + d_walk(root, &args, au_call_dpages_append); + + return args.err; +} + +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, + int do_include, au_dpages_test test, void *arg) +{ + int err; + + err = 0; + write_seqlock(&rename_lock); + spin_lock(&dentry->d_lock); + if (do_include + && au_dcount(dentry) > 0 + && (!test || test(dentry, arg))) + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + spin_unlock(&dentry->d_lock); + if (unlikely(err)) + goto out; + + /* + * RCU for vfsmount is unnecessary since this is a traverse in a single + * mount + */ + while (!IS_ROOT(dentry)) { + dentry = dentry->d_parent; /* rename_lock is locked */ + spin_lock(&dentry->d_lock); + if (au_dcount(dentry) > 0 + && (!test || test(dentry, arg))) + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + spin_unlock(&dentry->d_lock); + if (unlikely(err)) + break; + } + +out: + write_sequnlock(&rename_lock); + return err; +} + +static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) +{ + return au_di(dentry) && dentry->d_sb == arg; +} + +int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, + struct dentry *dentry, int do_include) +{ + return au_dcsub_pages_rev(dpages, dentry, do_include, + au_dcsub_dpages_aufs, dentry->d_sb); +} + +int au_test_subdir(struct dentry *d1, struct dentry *d2) +{ + struct path path[2] = { + { + .dentry = d1 + }, + { + .dentry = d2 + } + }; + + return path_is_under(path + 0, path + 1); +} diff -Naur --no-dereference a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h --- a/fs/aufs/dcsub.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dcsub.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for dentry cache + */ + +#ifndef __AUFS_DCSUB_H__ +#define __AUFS_DCSUB_H__ + +#ifdef __KERNEL__ + +#include +#include + +struct au_dpage { + int ndentry; + struct dentry **dentries; +}; + +struct au_dcsub_pages { + int ndpage; + struct au_dpage *dpages; +}; + +/* ---------------------------------------------------------------------- */ + +/* dcsub.c */ +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); +void au_dpages_free(struct au_dcsub_pages *dpages); +typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); +int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, + au_dpages_test test, void *arg); +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, + int do_include, au_dpages_test test, void *arg); +int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, + struct dentry *dentry, int do_include); +int au_test_subdir(struct dentry *d1, struct dentry *d2); + +/* ---------------------------------------------------------------------- */ + +/* + * todo: in linux-3.13, several similar (but faster) helpers are added to + * include/linux/dcache.h. Try them (in the future). + */ + +static inline int au_d_hashed_positive(struct dentry *d) +{ + int err; + struct inode *inode = d_inode(d); + + err = 0; + if (unlikely(d_unhashed(d) + || d_is_negative(d) + || !inode->i_nlink)) + err = -ENOENT; + return err; +} + +static inline int au_d_linkable(struct dentry *d) +{ + int err; + struct inode *inode = d_inode(d); + + err = au_d_hashed_positive(d); + if (err + && d_is_positive(d) + && (inode->i_state & I_LINKABLE)) + err = 0; + return err; +} + +static inline int au_d_alive(struct dentry *d) +{ + int err; + struct inode *inode; + + err = 0; + if (!IS_ROOT(d)) + err = au_d_hashed_positive(d); + else { + inode = d_inode(d); + if (unlikely(d_unlinked(d) + || d_is_negative(d) + || !inode->i_nlink)) + err = -ENOENT; + } + return err; +} + +static inline int au_alive_dir(struct dentry *d) +{ + int err; + + err = au_d_alive(d); + if (unlikely(err || IS_DEADDIR(d_inode(d)))) + err = -ENOENT; + return err; +} + +static inline int au_qstreq(struct qstr *a, struct qstr *b) +{ + return a->len == b->len + && !memcmp(a->name, b->name, a->len); +} + +/* + * by the commit + * 360f547 2015-01-25 dcache: let the dentry count go down to zero without + * taking d_lock + * the type of d_lockref.count became int, but the inlined function d_count() + * still returns unsigned int. + * I don't know why. Maybe it is for every d_count() users? + * Anyway au_dcount() lives on. + */ +static inline int au_dcount(struct dentry *d) +{ + return (int)d_count(d); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DCSUB_H__ */ diff -Naur --no-dereference a/fs/aufs/debug.c b/fs/aufs/debug.c --- a/fs/aufs/debug.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/debug.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debug print functions + */ + +#include +#include "aufs.h" + +/* Returns 0, or -errno. arg is in kp->arg. */ +static int param_atomic_t_set(const char *val, const struct kernel_param *kp) +{ + int err, n; + + err = kstrtoint(val, 0, &n); + if (!err) { + if (n > 0) + au_debug_on(); + else + au_debug_off(); + } + return err; +} + +/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ +static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) +{ + atomic_t *a; + + a = kp->arg; + return sprintf(buffer, "%d", atomic_read(a)); +} + +static struct kernel_param_ops param_ops_atomic_t = { + .set = param_atomic_t_set, + .get = param_atomic_t_get + /* void (*free)(void *arg) */ +}; + +atomic_t aufs_debug = ATOMIC_INIT(0); +MODULE_PARM_DESC(debug, "debug print"); +module_param_named(debug, aufs_debug, atomic_t, 0664); + +DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ +char *au_plevel = KERN_DEBUG; +#define dpri(fmt, ...) do { \ + if ((au_plevel \ + && strcmp(au_plevel, KERN_DEBUG)) \ + || au_debug_test()) \ + printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ +} while (0) + +/* ---------------------------------------------------------------------- */ + +void au_dpri_whlist(struct au_nhash *whlist) +{ + unsigned long ul, n; + struct hlist_head *head; + struct au_vdir_wh *pos; + + n = whlist->nh_num; + head = whlist->nh_head; + for (ul = 0; ul < n; ul++) { + hlist_for_each_entry(pos, head, wh_hash) + dpri("b%d, %.*s, %d\n", + pos->wh_bindex, + pos->wh_str.len, pos->wh_str.name, + pos->wh_str.len); + head++; + } +} + +void au_dpri_vdir(struct au_vdir *vdir) +{ + unsigned long ul; + union au_vdir_deblk_p p; + unsigned char *o; + + if (!vdir || IS_ERR(vdir)) { + dpri("err %ld\n", PTR_ERR(vdir)); + return; + } + + dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %llu\n", + vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, + vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); + for (ul = 0; ul < vdir->vd_nblk; ul++) { + p.deblk = vdir->vd_deblk[ul]; + o = p.deblk; + dpri("[%lu]: %p\n", ul, o); + } +} + +static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, + struct dentry *wh) +{ + char *n = NULL; + int l = 0; + + if (!inode || IS_ERR(inode)) { + dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); + return -1; + } + + /* the type of i_blocks depends upon CONFIG_LBDAF */ + BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) + && sizeof(inode->i_blocks) != sizeof(u64)); + if (wh) { + n = (void *)wh->d_name.name; + l = wh->d_name.len; + } + + dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," + " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", + bindex, inode, + inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", + atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, + i_size_read(inode), (unsigned long long)inode->i_blocks, + hn, (long long)timespec64_to_ns(&inode->i_ctime) & 0x0ffff, + inode->i_mapping ? inode->i_mapping->nrpages : 0, + inode->i_state, inode->i_flags, inode_peek_iversion(inode), + inode->i_generation, + l ? ", wh " : "", l, n); + return 0; +} + +void au_dpri_inode(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct au_hinode *hi; + aufs_bindex_t bindex; + int err, hn; + + err = do_pri_inode(-1, inode, -1, NULL); + if (err || !au_test_aufs(inode->i_sb) || au_is_bad_inode(inode)) + return; + + iinfo = au_ii(inode); + dpri("i-1: btop %d, bbot %d, gen %d\n", + iinfo->ii_btop, iinfo->ii_bbot, au_iigen(inode, NULL)); + if (iinfo->ii_btop < 0) + return; + hn = 0; + for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++) { + hi = au_hinode(iinfo, bindex); + hn = !!au_hn(hi); + do_pri_inode(bindex, hi->hi_inode, hn, hi->hi_whdentry); + } +} + +void au_dpri_dalias(struct inode *inode) +{ + struct dentry *d; + + spin_lock(&inode->i_lock); + hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) + au_dpri_dentry(d); + spin_unlock(&inode->i_lock); +} + +static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) +{ + struct dentry *wh = NULL; + int hn; + struct inode *inode; + struct au_iinfo *iinfo; + struct au_hinode *hi; + + if (!dentry || IS_ERR(dentry)) { + dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); + return -1; + } + /* do not call dget_parent() here */ + /* note: access d_xxx without d_lock */ + dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", + bindex, dentry, dentry, + dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", + au_dcount(dentry), dentry->d_flags, + d_unhashed(dentry) ? "un" : ""); + hn = -1; + inode = NULL; + if (d_is_positive(dentry)) + inode = d_inode(dentry); + if (inode + && au_test_aufs(dentry->d_sb) + && bindex >= 0 + && !au_is_bad_inode(inode)) { + iinfo = au_ii(inode); + hi = au_hinode(iinfo, bindex); + hn = !!au_hn(hi); + wh = hi->hi_whdentry; + } + do_pri_inode(bindex, inode, hn, wh); + return 0; +} + +void au_dpri_dentry(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + aufs_bindex_t bindex; + int err; + + err = do_pri_dentry(-1, dentry); + if (err || !au_test_aufs(dentry->d_sb)) + return; + + dinfo = au_di(dentry); + if (!dinfo) + return; + dpri("d-1: btop %d, bbot %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", + dinfo->di_btop, dinfo->di_bbot, + dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), + dinfo->di_tmpfile); + if (dinfo->di_btop < 0) + return; + for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++) + do_pri_dentry(bindex, au_hdentry(dinfo, bindex)->hd_dentry); +} + +static int do_pri_file(aufs_bindex_t bindex, struct file *file) +{ + char a[32]; + + if (!file || IS_ERR(file)) { + dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); + return -1; + } + a[0] = 0; + if (bindex < 0 + && !IS_ERR_OR_NULL(file->f_path.dentry) + && au_test_aufs(file->f_path.dentry->d_sb) + && au_fi(file)) + snprintf(a, sizeof(a), ", gen %d, mmapped %d", + au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); + dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", + bindex, file->f_mode, file->f_flags, (long)file_count(file), + file->f_version, file->f_pos, a); + if (!IS_ERR_OR_NULL(file->f_path.dentry)) + do_pri_dentry(bindex, file->f_path.dentry); + return 0; +} + +void au_dpri_file(struct file *file) +{ + struct au_finfo *finfo; + struct au_fidir *fidir; + struct au_hfile *hfile; + aufs_bindex_t bindex; + int err; + + err = do_pri_file(-1, file); + if (err + || IS_ERR_OR_NULL(file->f_path.dentry) + || !au_test_aufs(file->f_path.dentry->d_sb)) + return; + + finfo = au_fi(file); + if (!finfo) + return; + if (finfo->fi_btop < 0) + return; + fidir = finfo->fi_hdir; + if (!fidir) + do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); + else + for (bindex = finfo->fi_btop; + bindex >= 0 && bindex <= fidir->fd_bbot; + bindex++) { + hfile = fidir->fd_hfile + bindex; + do_pri_file(bindex, hfile ? hfile->hf_file : NULL); + } +} + +static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) +{ + struct vfsmount *mnt; + struct super_block *sb; + + if (!br || IS_ERR(br)) + goto out; + mnt = au_br_mnt(br); + if (!mnt || IS_ERR(mnt)) + goto out; + sb = mnt->mnt_sb; + if (!sb || IS_ERR(sb)) + goto out; + + dpri("s%d: {perm 0x%x, id %d, wbr %p}, " + "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " + "xino %d\n", + bindex, br->br_perm, br->br_id, br->br_wbr, + au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), + sb->s_flags, sb->s_count, + atomic_read(&sb->s_active), + !!au_xino_file(br->br_xino, /*idx*/-1)); + return 0; + +out: + dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); + return -1; +} + +void au_dpri_sb(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + aufs_bindex_t bindex; + int err; + /* to reduce stack size */ + struct { + struct vfsmount mnt; + struct au_branch fake; + } *a; + + /* this function can be called from magic sysrq */ + a = kzalloc(sizeof(*a), GFP_ATOMIC); + if (unlikely(!a)) { + dpri("no memory\n"); + return; + } + + a->mnt.mnt_sb = sb; + a->fake.br_path.mnt = &a->mnt; + err = do_pri_br(-1, &a->fake); + au_kfree_rcu(a); + dpri("dev 0x%x\n", sb->s_dev); + if (err || !au_test_aufs(sb)) + return; + + sbinfo = au_sbi(sb); + if (!sbinfo) + return; + dpri("nw %d, gen %u, kobj %d\n", + atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, + kref_read(&sbinfo->si_kobj.kref)); + for (bindex = 0; bindex <= sbinfo->si_bbot; bindex++) + do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); +} + +/* ---------------------------------------------------------------------- */ + +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) +{ + struct inode *h_inode, *inode = d_inode(dentry); + struct dentry *h_dentry; + aufs_bindex_t bindex, bbot, bi; + + if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) + return; + + bbot = au_dbbot(dentry); + bi = au_ibbot(inode); + if (bi < bbot) + bbot = bi; + bindex = au_dbtop(dentry); + bi = au_ibtop(inode); + if (bi > bindex) + bindex = bi; + + for (; bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_inode = au_h_iptr(inode, bindex); + if (unlikely(h_inode != d_inode(h_dentry))) { + au_debug_on(); + AuDbg("b%d, %s:%d\n", bindex, func, line); + AuDbgDentry(dentry); + AuDbgInode(inode); + au_debug_off(); + if (au_test_fuse(h_inode->i_sb)) + WARN_ON_ONCE(1); + else + BUG(); + } + } +} + +void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) +{ + int err, i, j; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + err = au_dpages_init(&dpages, GFP_NOFS); + AuDebugOn(err); + err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); + AuDebugOn(err); + for (i = dpages.ndpage - 1; !err && i >= 0; i--) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + for (j = dpage->ndentry - 1; !err && j >= 0; j--) + AuDebugOn(au_digen_test(dentries[j], sigen)); + } + au_dpages_free(&dpages); +} + +void au_dbg_verify_kthread(void) +{ + if (au_wkq_test()) { + au_dbg_blocked(); + /* + * It may be recursive, but udba=notify between two aufs mounts, + * where a single ro branch is shared, is not a problem. + */ + /* WARN_ON(1); */ + } +} + +/* ---------------------------------------------------------------------- */ + +int __init au_debug_init(void) +{ + aufs_bindex_t bindex; + struct au_vdir_destr destr; + + bindex = -1; + AuDebugOn(bindex >= 0); + + destr.len = -1; + AuDebugOn(destr.len < NAME_MAX); + +#ifdef CONFIG_4KSTACKS + pr_warn("CONFIG_4KSTACKS is defined.\n"); +#endif + + return 0; +} diff -Naur --no-dereference a/fs/aufs/debug.h b/fs/aufs/debug.h --- a/fs/aufs/debug.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/debug.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debug print functions + */ + +#ifndef __AUFS_DEBUG_H__ +#define __AUFS_DEBUG_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +#ifdef CONFIG_AUFS_DEBUG +#define AuDebugOn(a) BUG_ON(a) + +/* module parameter */ +extern atomic_t aufs_debug; +static inline void au_debug_on(void) +{ + atomic_inc(&aufs_debug); +} +static inline void au_debug_off(void) +{ + atomic_dec_if_positive(&aufs_debug); +} + +static inline int au_debug_test(void) +{ + return atomic_read(&aufs_debug) > 0; +} +#else +#define AuDebugOn(a) do {} while (0) +AuStubVoid(au_debug_on, void) +AuStubVoid(au_debug_off, void) +AuStubInt0(au_debug_test, void) +#endif /* CONFIG_AUFS_DEBUG */ + +#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) + +/* ---------------------------------------------------------------------- */ + +/* debug print */ + +#define AuDbg(fmt, ...) do { \ + if (au_debug_test()) \ + pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ +} while (0) +#define AuLabel(l) AuDbg(#l "\n") +#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) +#define AuWarn1(fmt, ...) do { \ + static unsigned char _c; \ + if (!_c++) \ + pr_warn(fmt, ##__VA_ARGS__); \ +} while (0) + +#define AuErr1(fmt, ...) do { \ + static unsigned char _c; \ + if (!_c++) \ + pr_err(fmt, ##__VA_ARGS__); \ +} while (0) + +#define AuIOErr1(fmt, ...) do { \ + static unsigned char _c; \ + if (!_c++) \ + AuIOErr(fmt, ##__VA_ARGS__); \ +} while (0) + +#define AuUnsupportMsg "This operation is not supported." \ + " Please report this application to aufs-users ML." +#define AuUnsupport(fmt, ...) do { \ + pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ + dump_stack(); \ +} while (0) + +#define AuTraceErr(e) do { \ + if (unlikely((e) < 0)) \ + AuDbg("err %d\n", (int)(e)); \ +} while (0) + +#define AuTraceErrPtr(p) do { \ + if (IS_ERR(p)) \ + AuDbg("err %ld\n", PTR_ERR(p)); \ +} while (0) + +/* dirty macros for debug print, use with "%.*s" and caution */ +#define AuLNPair(qstr) (qstr)->len, (qstr)->name + +/* ---------------------------------------------------------------------- */ + +struct dentry; +#ifdef CONFIG_AUFS_DEBUG +extern struct mutex au_dbg_mtx; +extern char *au_plevel; +struct au_nhash; +void au_dpri_whlist(struct au_nhash *whlist); +struct au_vdir; +void au_dpri_vdir(struct au_vdir *vdir); +struct inode; +void au_dpri_inode(struct inode *inode); +void au_dpri_dalias(struct inode *inode); +void au_dpri_dentry(struct dentry *dentry); +struct file; +void au_dpri_file(struct file *filp); +struct super_block; +void au_dpri_sb(struct super_block *sb); + +#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); +void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); +void au_dbg_verify_kthread(void); + +int __init au_debug_init(void); + +#define AuDbgWhlist(w) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#w "\n"); \ + au_dpri_whlist(w); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgVdir(v) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#v "\n"); \ + au_dpri_vdir(v); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgInode(i) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#i "\n"); \ + au_dpri_inode(i); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgDAlias(i) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#i "\n"); \ + au_dpri_dalias(i); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgDentry(d) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#d "\n"); \ + au_dpri_dentry(d); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgFile(f) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#f "\n"); \ + au_dpri_file(f); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgSb(sb) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#sb "\n"); \ + au_dpri_sb(sb); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgSym(addr) do { \ + char sym[KSYM_SYMBOL_LEN]; \ + sprint_symbol(sym, (unsigned long)addr); \ + AuDbg("%s\n", sym); \ +} while (0) +#else +AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) +AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) +AuStubVoid(au_dbg_verify_kthread, void) +AuStubInt0(__init au_debug_init, void) + +#define AuDbgWhlist(w) do {} while (0) +#define AuDbgVdir(v) do {} while (0) +#define AuDbgInode(i) do {} while (0) +#define AuDbgDAlias(i) do {} while (0) +#define AuDbgDentry(d) do {} while (0) +#define AuDbgFile(f) do {} while (0) +#define AuDbgSb(sb) do {} while (0) +#define AuDbgSym(addr) do {} while (0) +#endif /* CONFIG_AUFS_DEBUG */ + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_MAGIC_SYSRQ +int __init au_sysrq_init(void); +void au_sysrq_fin(void); + +#ifdef CONFIG_HW_CONSOLE +#define au_dbg_blocked() do { \ + WARN_ON(1); \ + handle_sysrq('w'); \ +} while (0) +#else +AuStubVoid(au_dbg_blocked, void) +#endif + +#else +AuStubInt0(__init au_sysrq_init, void) +AuStubVoid(au_sysrq_fin, void) +AuStubVoid(au_dbg_blocked, void) +#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DEBUG_H__ */ diff -Naur --no-dereference a/fs/aufs/dentry.c b/fs/aufs/dentry.c --- a/fs/aufs/dentry.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dentry.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * lookup and dentry operations + */ + +#include +#include +#include "aufs.h" + +/* + * returns positive/negative dentry, NULL or an error. + * NULL means whiteout-ed or not-found. + */ +static struct dentry* +au_do_lookup(struct dentry *h_parent, struct dentry *dentry, + aufs_bindex_t bindex, struct au_do_lookup_args *args) +{ + struct dentry *h_dentry; + struct inode *h_inode; + struct au_branch *br; + struct path h_path; + int wh_found, opq; + unsigned char wh_able; + const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); + const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, + IGNORE_PERM); + + wh_found = 0; + br = au_sbr(dentry->d_sb, bindex); + h_path.dentry = h_parent; + h_path.mnt = au_br_mnt(br); + wh_able = !!au_br_whable(br->br_perm); + if (wh_able) + wh_found = au_wh_test(&h_path, &args->whname, ignore_perm); + h_dentry = ERR_PTR(wh_found); + if (!wh_found) + goto real_lookup; + if (unlikely(wh_found < 0)) + goto out; + + /* We found a whiteout */ + /* au_set_dbbot(dentry, bindex); */ + au_set_dbwh(dentry, bindex); + if (!allow_neg) + return NULL; /* success */ + +real_lookup: + if (!ignore_perm) + h_dentry = vfsub_lkup_one(args->name, &h_path); + else + h_dentry = au_sio_lkup_one(args->name, &h_path); + if (IS_ERR(h_dentry)) { + if (PTR_ERR(h_dentry) == -ENAMETOOLONG + && !allow_neg) + h_dentry = NULL; + goto out; + } + + h_inode = d_inode(h_dentry); + if (d_is_negative(h_dentry)) { + if (!allow_neg) + goto out_neg; + } else if (wh_found + || (args->type && args->type != (h_inode->i_mode & S_IFMT))) + goto out_neg; + else if (au_ftest_lkup(args->flags, DIRREN) + /* && h_inode */ + && !au_dr_lkup_h_ino(args, bindex, h_inode->i_ino)) { + AuDbg("b%d %pd ignored hi%llu\n", bindex, h_dentry, + (unsigned long long)h_inode->i_ino); + goto out_neg; + } + + if (au_dbbot(dentry) <= bindex) + au_set_dbbot(dentry, bindex); + if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) + au_set_dbtop(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + + if (!d_is_dir(h_dentry) + || !wh_able + || (d_really_is_positive(dentry) && !d_is_dir(dentry))) + goto out; /* success */ + + h_path.dentry = h_dentry; + inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + opq = au_diropq_test(&h_path); + inode_unlock_shared(h_inode); + if (opq > 0) + au_set_dbdiropq(dentry, bindex); + else if (unlikely(opq < 0)) { + au_set_h_dptr(dentry, bindex, NULL); + h_dentry = ERR_PTR(opq); + } + goto out; + +out_neg: + dput(h_dentry); + h_dentry = NULL; +out: + return h_dentry; +} + +static int au_test_shwh(struct super_block *sb, const struct qstr *name) +{ + if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) + && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) + return -EPERM; + return 0; +} + +/* + * returns the number of lower positive dentries, + * otherwise an error. + * can be called at unlinking with @type is zero. + */ +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, + unsigned int flags) +{ + int npositive, err; + aufs_bindex_t bindex, btail, bdiropq; + unsigned char isdir, dirperm1, dirren; + struct au_do_lookup_args args = { + .flags = flags, + .name = &dentry->d_name + }; + struct dentry *parent; + struct super_block *sb; + + sb = dentry->d_sb; + err = au_test_shwh(sb, args.name); + if (unlikely(err)) + goto out; + + err = au_wh_name_alloc(&args.whname, args.name); + if (unlikely(err)) + goto out; + + isdir = !!d_is_dir(dentry); + dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); + dirren = !!au_opt_test(au_mntflags(sb), DIRREN); + if (dirren) + au_fset_lkup(args.flags, DIRREN); + + npositive = 0; + parent = dget_parent(dentry); + btail = au_dbtaildir(parent); + for (bindex = btop; bindex <= btail; bindex++) { + struct dentry *h_parent, *h_dentry; + struct inode *h_inode, *h_dir; + struct au_branch *br; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) { + if (d_is_positive(h_dentry)) + npositive++; + break; + } + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || !d_is_dir(h_parent)) + continue; + + if (dirren) { + /* if the inum matches, then use the prepared name */ + err = au_dr_lkup_name(&args, bindex); + if (unlikely(err)) + goto out_parent; + } + + h_dir = d_inode(h_parent); + inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + h_dentry = au_do_lookup(h_parent, dentry, bindex, &args); + inode_unlock_shared(h_dir); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out_parent; + if (h_dentry) + au_fclr_lkup(args.flags, ALLOW_NEG); + if (dirperm1) + au_fset_lkup(args.flags, IGNORE_PERM); + + if (au_dbwh(dentry) == bindex) + break; + if (!h_dentry) + continue; + if (d_is_negative(h_dentry)) + continue; + h_inode = d_inode(h_dentry); + npositive++; + if (!args.type) + args.type = h_inode->i_mode & S_IFMT; + if (args.type != S_IFDIR) + break; + else if (isdir) { + /* the type of lower may be different */ + bdiropq = au_dbdiropq(dentry); + if (bdiropq >= 0 && bdiropq <= bindex) + break; + } + br = au_sbr(sb, bindex); + if (dirren + && au_dr_hino_test_add(&br->br_dirren, h_inode->i_ino, + /*add_ent*/NULL)) { + /* prepare next name to lookup */ + err = au_dr_lkup(&args, dentry, bindex); + if (unlikely(err)) + goto out_parent; + } + } + + if (npositive) { + AuLabel(positive); + au_update_dbtop(dentry); + } + err = npositive; + if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) + && au_dbtop(dentry) < 0)) { + err = -EIO; + AuIOErr("both of real entry and whiteout found, %pd, err %d\n", + dentry, err); + } + +out_parent: + dput(parent); + au_kfree_try_rcu(args.whname.name); + if (dirren) + au_dr_lkup_fin(&args); +out: + return err; +} + +struct dentry *au_sio_lkup_one(struct qstr *name, struct path *ppath) +{ + struct dentry *dentry; + int wkq_err; + + if (!au_test_h_perm_sio(d_inode(ppath->dentry), MAY_EXEC)) + dentry = vfsub_lkup_one(name, ppath); + else { + struct vfsub_lkup_one_args args = { + .errp = &dentry, + .name = name, + .ppath = ppath + }; + + wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); + if (unlikely(wkq_err)) + dentry = ERR_PTR(wkq_err); + } + + return dentry; +} + +/* + * lookup @dentry on @bindex which should be negative. + */ +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) +{ + int err; + struct dentry *parent, *h_dentry; + struct au_branch *br; + struct path h_ppath; + + parent = dget_parent(dentry); + br = au_sbr(dentry->d_sb, bindex); + h_ppath.dentry = au_h_dptr(parent, bindex); + h_ppath.mnt = au_br_mnt(br); + if (wh) + h_dentry = au_whtmp_lkup(h_ppath.dentry, br, &dentry->d_name); + else + h_dentry = au_sio_lkup_one(&dentry->d_name, &h_ppath); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out; + if (unlikely(d_is_positive(h_dentry))) { + err = -EIO; + AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); + dput(h_dentry); + goto out; + } + + err = 0; + if (bindex < au_dbtop(dentry)) + au_set_dbtop(dentry, bindex); + if (au_dbbot(dentry) < bindex) + au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + +out: + dput(parent); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* subset of struct inode */ +struct au_iattr { + unsigned long i_ino; + /* unsigned int i_nlink; */ + kuid_t i_uid; + kgid_t i_gid; + u64 i_version; +/* + loff_t i_size; + blkcnt_t i_blocks; +*/ + umode_t i_mode; +}; + +static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) +{ + ia->i_ino = h_inode->i_ino; + /* ia->i_nlink = h_inode->i_nlink; */ + ia->i_uid = h_inode->i_uid; + ia->i_gid = h_inode->i_gid; + ia->i_version = inode_query_iversion(h_inode); +/* + ia->i_size = h_inode->i_size; + ia->i_blocks = h_inode->i_blocks; +*/ + ia->i_mode = (h_inode->i_mode & S_IFMT); +} + +static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) +{ + return ia->i_ino != h_inode->i_ino + /* || ia->i_nlink != h_inode->i_nlink */ + || !uid_eq(ia->i_uid, h_inode->i_uid) + || !gid_eq(ia->i_gid, h_inode->i_gid) + || !inode_eq_iversion(h_inode, ia->i_version) +/* + || ia->i_size != h_inode->i_size + || ia->i_blocks != h_inode->i_blocks +*/ + || ia->i_mode != (h_inode->i_mode & S_IFMT); +} + +static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, + struct au_branch *br) +{ + int err; + struct au_iattr ia; + struct inode *h_inode; + struct dentry *h_d; + struct super_block *h_sb; + struct path h_ppath; + + err = 0; + memset(&ia, -1, sizeof(ia)); + h_sb = h_dentry->d_sb; + h_inode = NULL; + if (d_is_positive(h_dentry)) { + h_inode = d_inode(h_dentry); + au_iattr_save(&ia, h_inode); + } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) + /* nfs d_revalidate may return 0 for negative dentry */ + /* fuse d_revalidate always return 0 for negative dentry */ + goto out; + + /* main purpose is namei.c:cached_lookup() and d_revalidate */ + h_ppath.dentry = h_parent; + h_ppath.mnt = au_br_mnt(br); + h_d = vfsub_lkup_one(&h_dentry->d_name, &h_ppath); + err = PTR_ERR(h_d); + if (IS_ERR(h_d)) + goto out; + + err = 0; + if (unlikely(h_d != h_dentry + || d_inode(h_d) != h_inode + || (h_inode && au_iattr_test(&ia, h_inode)))) + err = au_busy_or_stale(); + dput(h_d); + +out: + AuTraceErr(err); + return err; +} + +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, + struct dentry *h_parent, struct au_branch *br) +{ + int err; + + err = 0; + if (udba == AuOpt_UDBA_REVAL + && !au_test_fs_remote(h_dentry->d_sb)) { + IMustLock(h_dir); + err = (d_inode(h_dentry->d_parent) != h_dir); + } else if (udba != AuOpt_UDBA_NONE) + err = au_h_verify_dentry(h_dentry, h_parent, br); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) +{ + int err; + aufs_bindex_t new_bindex, bindex, bbot, bwh, bdiropq; + struct au_hdentry tmp, *p, *q; + struct au_dinfo *dinfo; + struct super_block *sb; + + DiMustWriteLock(dentry); + + sb = dentry->d_sb; + dinfo = au_di(dentry); + bbot = dinfo->di_bbot; + bwh = dinfo->di_bwh; + bdiropq = dinfo->di_bdiropq; + bindex = dinfo->di_btop; + p = au_hdentry(dinfo, bindex); + for (; bindex <= bbot; bindex++, p++) { + if (!p->hd_dentry) + continue; + + new_bindex = au_br_index(sb, p->hd_id); + if (new_bindex == bindex) + continue; + + if (dinfo->di_bwh == bindex) + bwh = new_bindex; + if (dinfo->di_bdiropq == bindex) + bdiropq = new_bindex; + if (new_bindex < 0) { + au_hdput(p); + p->hd_dentry = NULL; + continue; + } + + /* swap two lower dentries, and loop again */ + q = au_hdentry(dinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hd_dentry) { + bindex--; + p--; + } + } + + dinfo->di_bwh = -1; + if (bwh >= 0 && bwh <= au_sbbot(sb) && au_sbr_whable(sb, bwh)) + dinfo->di_bwh = bwh; + + dinfo->di_bdiropq = -1; + if (bdiropq >= 0 + && bdiropq <= au_sbbot(sb) + && au_sbr_whable(sb, bdiropq)) + dinfo->di_bdiropq = bdiropq; + + err = -EIO; + dinfo->di_btop = -1; + dinfo->di_bbot = -1; + bbot = au_dbbot(parent); + bindex = 0; + p = au_hdentry(dinfo, bindex); + for (; bindex <= bbot; bindex++, p++) + if (p->hd_dentry) { + dinfo->di_btop = bindex; + break; + } + + if (dinfo->di_btop >= 0) { + bindex = bbot; + p = au_hdentry(dinfo, bindex); + for (; bindex >= 0; bindex--, p--) + if (p->hd_dentry) { + dinfo->di_bbot = bindex; + err = 0; + break; + } + } + + return err; +} + +static void au_do_hide(struct dentry *dentry) +{ + struct inode *inode; + + if (d_really_is_positive(dentry)) { + inode = d_inode(dentry); + if (!d_is_dir(dentry)) { + if (inode->i_nlink && !d_unhashed(dentry)) + drop_nlink(inode); + } else { + clear_nlink(inode); + /* stop next lookup */ + inode->i_flags |= S_DEAD; + } + smp_mb(); /* necessary? */ + } + d_drop(dentry); +} + +static int au_hide_children(struct dentry *parent) +{ + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *dentry; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, parent, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + /* in reverse order */ + for (i = dpages.ndpage - 1; i >= 0; i--) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = ndentry - 1; j >= 0; j--) { + dentry = dpage->dentries[j]; + if (dentry != parent) + au_do_hide(dentry); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static void au_hide(struct dentry *dentry) +{ + int err; + + AuDbgDentry(dentry); + if (d_is_dir(dentry)) { + /* shrink_dcache_parent(dentry); */ + err = au_hide_children(dentry); + if (unlikely(err)) + AuIOErr("%pd, failed hiding children, ignored %d\n", + dentry, err); + } + au_do_hide(dentry); +} + +/* + * By adding a dirty branch, a cached dentry may be affected in various ways. + * + * a dirty branch is added + * - on the top of layers + * - in the middle of layers + * - to the bottom of layers + * + * on the added branch there exists + * - a whiteout + * - a diropq + * - a same named entry + * + exist + * * negative --> positive + * * positive --> positive + * - type is unchanged + * - type is changed + * + doesn't exist + * * negative --> negative + * * positive --> negative (rejected by au_br_del() for non-dir case) + * - none + */ +static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, + struct au_dinfo *tmp) +{ + int err; + aufs_bindex_t bindex, bbot; + struct { + struct dentry *dentry; + struct inode *inode; + mode_t mode; + } orig_h, tmp_h = { + .dentry = NULL + }; + struct au_hdentry *hd; + struct inode *inode, *h_inode; + struct dentry *h_dentry; + + err = 0; + AuDebugOn(dinfo->di_btop < 0); + orig_h.mode = 0; + orig_h.dentry = au_hdentry(dinfo, dinfo->di_btop)->hd_dentry; + orig_h.inode = NULL; + if (d_is_positive(orig_h.dentry)) { + orig_h.inode = d_inode(orig_h.dentry); + orig_h.mode = orig_h.inode->i_mode & S_IFMT; + } + if (tmp->di_btop >= 0) { + tmp_h.dentry = au_hdentry(tmp, tmp->di_btop)->hd_dentry; + if (d_is_positive(tmp_h.dentry)) { + tmp_h.inode = d_inode(tmp_h.dentry); + tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; + } + } + + inode = NULL; + if (d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (!orig_h.inode) { + AuDbg("negative originally\n"); + if (inode) { + au_hide(dentry); + goto out; + } + AuDebugOn(inode); + AuDebugOn(dinfo->di_btop != dinfo->di_bbot); + AuDebugOn(dinfo->di_bdiropq != -1); + + if (!tmp_h.inode) { + AuDbg("negative --> negative\n"); + /* should have only one negative lower */ + if (tmp->di_btop >= 0 + && tmp->di_btop < dinfo->di_btop) { + AuDebugOn(tmp->di_btop != tmp->di_bbot); + AuDebugOn(dinfo->di_btop != dinfo->di_bbot); + au_set_h_dptr(dentry, dinfo->di_btop, NULL); + au_di_cp(dinfo, tmp); + hd = au_hdentry(tmp, tmp->di_btop); + au_set_h_dptr(dentry, tmp->di_btop, + dget(hd->hd_dentry)); + } + au_dbg_verify_dinode(dentry); + } else { + AuDbg("negative --> positive\n"); + /* + * similar to the behaviour of creating with bypassing + * aufs. + * unhash it in order to force an error in the + * succeeding create operation. + * we should not set S_DEAD here. + */ + d_drop(dentry); + /* au_di_swap(tmp, dinfo); */ + au_dbg_verify_dinode(dentry); + } + } else { + AuDbg("positive originally\n"); + /* inode may be NULL */ + AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); + if (!tmp_h.inode) { + AuDbg("positive --> negative\n"); + /* or bypassing aufs */ + au_hide(dentry); + if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_btop) + dinfo->di_bwh = tmp->di_bwh; + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } else if (orig_h.mode == tmp_h.mode) { + AuDbg("positive --> positive, same type\n"); + if (!S_ISDIR(orig_h.mode) + && dinfo->di_btop > tmp->di_btop) { + /* + * similar to the behaviour of removing and + * creating. + */ + au_hide(dentry); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } else { + /* fill empty slots */ + if (dinfo->di_btop > tmp->di_btop) + dinfo->di_btop = tmp->di_btop; + if (dinfo->di_bbot < tmp->di_bbot) + dinfo->di_bbot = tmp->di_bbot; + dinfo->di_bwh = tmp->di_bwh; + dinfo->di_bdiropq = tmp->di_bdiropq; + bbot = dinfo->di_bbot; + bindex = tmp->di_btop; + hd = au_hdentry(tmp, bindex); + for (; bindex <= bbot; bindex++, hd++) { + if (au_h_dptr(dentry, bindex)) + continue; + h_dentry = hd->hd_dentry; + if (!h_dentry) + continue; + AuDebugOn(d_is_negative(h_dentry)); + h_inode = d_inode(h_dentry); + AuDebugOn(orig_h.mode + != (h_inode->i_mode + & S_IFMT)); + au_set_h_dptr(dentry, bindex, + dget(h_dentry)); + } + if (inode) + err = au_refresh_hinode(inode, dentry); + au_dbg_verify_dinode(dentry); + } + } else { + AuDbg("positive --> positive, different type\n"); + /* similar to the behaviour of removing and creating */ + au_hide(dentry); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } + } + +out: + return err; +} + +void au_refresh_dop(struct dentry *dentry, int force_reval) +{ + const struct dentry_operations *dop + = force_reval ? &aufs_dop : dentry->d_sb->s_d_op; + static const unsigned int mask + = DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE; + + BUILD_BUG_ON(sizeof(mask) != sizeof(dentry->d_flags)); + + if (dentry->d_op == dop) + return; + + AuDbg("%pd\n", dentry); + spin_lock(&dentry->d_lock); + if (dop == &aufs_dop) + dentry->d_flags |= mask; + else + dentry->d_flags &= ~mask; + dentry->d_op = dop; + spin_unlock(&dentry->d_lock); +} + +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) +{ + int err, ebrange, nbr; + unsigned int sigen; + struct au_dinfo *dinfo, *tmp; + struct super_block *sb; + struct inode *inode; + + DiMustWriteLock(dentry); + AuDebugOn(IS_ROOT(dentry)); + AuDebugOn(d_really_is_negative(parent)); + + sb = dentry->d_sb; + sigen = au_sigen(sb); + err = au_digen_test(parent, sigen); + if (unlikely(err)) + goto out; + + nbr = au_sbbot(sb) + 1; + dinfo = au_di(dentry); + err = au_di_realloc(dinfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + ebrange = au_dbrange_test(dentry); + if (!ebrange) + ebrange = au_do_refresh_hdentry(dentry, parent); + + if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { + AuDebugOn(au_dbtop(dentry) < 0 && au_dbbot(dentry) >= 0); + if (d_really_is_positive(dentry)) { + inode = d_inode(dentry); + err = au_refresh_hinode_self(inode); + } + au_dbg_verify_dinode(dentry); + if (!err) + goto out_dgen; /* success */ + goto out; + } + + /* temporary dinfo */ + AuDbgDentry(dentry); + err = -ENOMEM; + tmp = au_di_alloc(sb, AuLsc_DI_TMP); + if (unlikely(!tmp)) + goto out; + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ + /* + * if current working dir is removed, it returns an error. + * but the dentry is legal. + */ + err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG); + AuDbgDentry(dentry); + au_di_swap(tmp, dinfo); + if (err == -ENOENT) + err = 0; + if (err >= 0) { + /* compare/refresh by dinfo */ + AuDbgDentry(dentry); + err = au_refresh_by_dinfo(dentry, dinfo, tmp); + au_dbg_verify_dinode(dentry); + AuTraceErr(err); + } + au_di_realloc(dinfo, nbr, /*may_shrink*/1); /* harmless if err */ + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + if (unlikely(err)) + goto out; + +out_dgen: + au_update_digen(dentry); +out: + if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { + AuIOErr("failed refreshing %pd, %d\n", dentry, err); + AuDbgDentry(dentry); + } + AuTraceErr(err); + return err; +} + +static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, + struct dentry *dentry, aufs_bindex_t bindex) +{ + int err, valid; + + err = 0; + if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) + goto out; + + AuDbg("b%d\n", bindex); + /* + * gave up supporting LOOKUP_CREATE/OPEN for lower fs, + * due to whiteout and branch permission. + */ + flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE + | LOOKUP_FOLLOW | LOOKUP_EXCL); + /* it may return tri-state */ + valid = h_dentry->d_op->d_revalidate(h_dentry, flags); + + if (unlikely(valid < 0)) + err = valid; + else if (!valid) + err = -EINVAL; + +out: + AuTraceErr(err); + return err; +} + +/* todo: remove this */ +static int h_d_revalidate(struct dentry *dentry, struct inode *inode, + unsigned int flags, int do_udba, int dirren) +{ + int err; + umode_t mode, h_mode; + aufs_bindex_t bindex, btail, btop, ibs, ibe; + unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; + struct inode *h_inode, *h_cached_inode; + struct dentry *h_dentry; + struct qstr *name, *h_name; + + err = 0; + plus = 0; + mode = 0; + ibs = -1; + ibe = -1; + unhashed = !!d_unhashed(dentry); + is_root = !!IS_ROOT(dentry); + name = &dentry->d_name; + tmpfile = au_di(dentry)->di_tmpfile; + + /* + * Theoretically, REVAL test should be unnecessary in case of + * {FS,I}NOTIFY. + * But {fs,i}notify doesn't fire some necessary events, + * IN_ATTRIB for atime/nlink/pageio + * Let's do REVAL test too. + */ + if (do_udba && inode) { + mode = (inode->i_mode & S_IFMT); + plus = (inode->i_nlink > 0); + ibs = au_ibtop(inode); + ibe = au_ibbot(inode); + } + + btop = au_dbtop(dentry); + btail = btop; + if (inode && S_ISDIR(inode->i_mode)) + btail = au_dbtaildir(dentry); + for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + + AuDbg("b%d, %pd\n", bindex, h_dentry); + h_nfs = !!au_test_nfs(h_dentry->d_sb); + spin_lock(&h_dentry->d_lock); + h_name = &h_dentry->d_name; + if (unlikely(do_udba + && !is_root + && ((!h_nfs + && (unhashed != !!d_unhashed(h_dentry) + || (!tmpfile && !dirren + && !au_qstreq(name, h_name)) + )) + || (h_nfs + && !(flags & LOOKUP_OPEN) + && (h_dentry->d_flags + & DCACHE_NFSFS_RENAMED))) + )) { + int h_unhashed; + + h_unhashed = d_unhashed(h_dentry); + spin_unlock(&h_dentry->d_lock); + AuDbg("unhash 0x%x 0x%x, %pd %pd\n", + unhashed, h_unhashed, dentry, h_dentry); + goto err; + } + spin_unlock(&h_dentry->d_lock); + + err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); + if (unlikely(err)) + /* do not goto err, to keep the errno */ + break; + + /* todo: plink too? */ + if (!do_udba) + continue; + + /* UDBA tests */ + if (unlikely(!!inode != d_is_positive(h_dentry))) + goto err; + + h_inode = NULL; + if (d_is_positive(h_dentry)) + h_inode = d_inode(h_dentry); + h_plus = plus; + h_mode = mode; + h_cached_inode = h_inode; + if (h_inode) { + h_mode = (h_inode->i_mode & S_IFMT); + h_plus = (h_inode->i_nlink > 0); + } + if (inode && ibs <= bindex && bindex <= ibe) + h_cached_inode = au_h_iptr(inode, bindex); + + if (!h_nfs) { + if (unlikely(plus != h_plus && !tmpfile)) + goto err; + } else { + if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) + && !is_root + && !IS_ROOT(h_dentry) + && unhashed != d_unhashed(h_dentry))) + goto err; + } + if (unlikely(mode != h_mode + || h_cached_inode != h_inode)) + goto err; + continue; + +err: + err = -EINVAL; + break; + } + + AuTraceErr(err); + return err; +} + +/* todo: consolidate with do_refresh() and au_reval_for_attr() */ +static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) +{ + int err; + struct dentry *parent; + + if (!au_digen_test(dentry, sigen)) + return 0; + + parent = dget_parent(dentry); + di_read_lock_parent(parent, AuLock_IR); + AuDebugOn(au_digen_test(parent, sigen)); + au_dbg_verify_gen(parent, sigen); + err = au_refresh_dentry(dentry, parent); + di_read_unlock(parent, AuLock_IR); + dput(parent); + AuTraceErr(err); + return err; +} + +int au_reval_dpath(struct dentry *dentry, unsigned int sigen) +{ + int err; + struct dentry *d, *parent; + + if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) + return simple_reval_dpath(dentry, sigen); + + /* slow loop, keep it simple and stupid */ + /* cf: au_cpup_dirs() */ + err = 0; + parent = NULL; + while (au_digen_test(dentry, sigen)) { + d = dentry; + while (1) { + dput(parent); + parent = dget_parent(d); + if (!au_digen_test(parent, sigen)) + break; + d = parent; + } + + if (d != dentry) + di_write_lock_child2(d); + + /* someone might update our dentry while we were sleeping */ + if (au_digen_test(d, sigen)) { + /* + * todo: consolidate with simple_reval_dpath(), + * do_refresh() and au_reval_for_attr(). + */ + di_read_lock_parent(parent, AuLock_IR); + err = au_refresh_dentry(d, parent); + di_read_unlock(parent, AuLock_IR); + } + + if (d != dentry) + di_write_unlock(d); + dput(parent); + if (unlikely(err)) + break; + } + + return err; +} + +/* + * if valid returns 1, otherwise 0. + */ +static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + int valid, err; + unsigned int sigen; + unsigned char do_udba, dirren; + struct super_block *sb; + struct inode *inode; + + /* todo: support rcu-walk? */ + if (flags & LOOKUP_RCU) + return -ECHILD; + + valid = 0; + if (unlikely(!au_di(dentry))) + goto out; + + valid = 1; + sb = dentry->d_sb; + /* + * todo: very ugly + * i_mutex of parent dir may be held, + * but we should not return 'invalid' due to busy. + */ + err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); + if (unlikely(err)) { + valid = err; + AuTraceErr(err); + goto out; + } + inode = NULL; + if (d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (unlikely(inode && au_is_bad_inode(inode))) { + err = -EINVAL; + AuTraceErr(err); + goto out_dgrade; + } + if (unlikely(au_dbrange_test(dentry))) { + err = -EINVAL; + AuTraceErr(err); + goto out_dgrade; + } + + sigen = au_sigen(sb); + if (au_digen_test(dentry, sigen)) { + AuDebugOn(IS_ROOT(dentry)); + err = au_reval_dpath(dentry, sigen); + if (unlikely(err)) { + AuTraceErr(err); + goto out_dgrade; + } + } + di_downgrade_lock(dentry, AuLock_IR); + + err = -EINVAL; + if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) + && inode + && !(inode->i_state && I_LINKABLE) + && (IS_DEADDIR(inode) || !inode->i_nlink)) { + AuTraceErr(err); + goto out_inval; + } + + do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); + if (do_udba && inode) { + aufs_bindex_t btop = au_ibtop(inode); + struct inode *h_inode; + + if (btop >= 0) { + h_inode = au_h_iptr(inode, btop); + if (h_inode && au_test_higen(inode, h_inode)) { + AuTraceErr(err); + goto out_inval; + } + } + } + + dirren = !!au_opt_test(au_mntflags(sb), DIRREN); + err = h_d_revalidate(dentry, inode, flags, do_udba, dirren); + if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) { + err = -EIO; + AuDbg("both of real entry and whiteout found, %p, err %d\n", + dentry, err); + } + goto out_inval; + +out_dgrade: + di_downgrade_lock(dentry, AuLock_IR); +out_inval: + aufs_read_unlock(dentry, AuLock_IR); + AuTraceErr(err); + valid = !err; +out: + if (!valid) { + AuDbg("%pd invalid, %d\n", dentry, valid); + d_drop(dentry); + } + return valid; +} + +static void aufs_d_release(struct dentry *dentry) +{ + if (au_di(dentry)) { + au_di_fin(dentry); + au_hn_di_reinit(dentry); + } +} + +const struct dentry_operations aufs_dop = { + .d_revalidate = aufs_d_revalidate, + .d_weak_revalidate = aufs_d_revalidate, + .d_release = aufs_d_release +}; + +/* aufs_dop without d_revalidate */ +const struct dentry_operations aufs_dop_noreval = { + .d_release = aufs_d_release +}; diff -Naur --no-dereference a/fs/aufs/dentry.h b/fs/aufs/dentry.h --- a/fs/aufs/dentry.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dentry.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * lookup and dentry operations + */ + +#ifndef __AUFS_DENTRY_H__ +#define __AUFS_DENTRY_H__ + +#ifdef __KERNEL__ + +#include +#include "dirren.h" +#include "rwsem.h" + +struct au_hdentry { + struct dentry *hd_dentry; + aufs_bindex_t hd_id; +}; + +struct au_dinfo { + atomic_t di_generation; + + struct au_rwsem di_rwsem; + aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq; + unsigned char di_tmpfile; /* to allow the different name */ + struct au_hdentry *di_hdentry; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + +/* flags for au_lkup_dentry() */ +#define AuLkup_ALLOW_NEG 1 +#define AuLkup_IGNORE_PERM (1 << 1) +#define AuLkup_DIRREN (1 << 2) +#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) +#define au_fset_lkup(flags, name) \ + do { (flags) |= AuLkup_##name; } while (0) +#define au_fclr_lkup(flags, name) \ + do { (flags) &= ~AuLkup_##name; } while (0) + +#ifndef CONFIG_AUFS_DIRREN +#undef AuLkup_DIRREN +#define AuLkup_DIRREN 0 +#endif + +struct au_do_lookup_args { + unsigned int flags; + mode_t type; + struct qstr whname, *name; + struct au_dr_lookup dirren; +}; + +/* ---------------------------------------------------------------------- */ + +/* dentry.c */ +extern const struct dentry_operations aufs_dop, aufs_dop_noreval; +struct au_branch; +struct dentry *au_sio_lkup_one(struct qstr *name, struct path *ppath); +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, + struct dentry *h_parent, struct au_branch *br); + +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, + unsigned int flags); +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); +int au_reval_dpath(struct dentry *dentry, unsigned int sigen); +void au_refresh_dop(struct dentry *dentry, int force_reval); + +/* dinfo.c */ +void au_di_init_once(void *_di); +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); +void au_di_free(struct au_dinfo *dinfo); +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); +int au_di_init(struct dentry *dentry); +void au_di_fin(struct dentry *dentry); +int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink); + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc); +void di_read_unlock(struct dentry *d, int flags); +void di_downgrade_lock(struct dentry *d, int flags); +void di_write_lock(struct dentry *d, unsigned int lsc); +void di_write_unlock(struct dentry *d); +void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); +void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); +void di_write_unlock2(struct dentry *d1, struct dentry *d2); + +struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); +struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); +aufs_bindex_t au_dbtail(struct dentry *dentry); +aufs_bindex_t au_dbtaildir(struct dentry *dentry); + +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry); +int au_digen_test(struct dentry *dentry, unsigned int sigen); +int au_dbrange_test(struct dentry *dentry); +void au_update_digen(struct dentry *dentry); +void au_update_dbrange(struct dentry *dentry, int do_put_zero); +void au_update_dbtop(struct dentry *dentry); +void au_update_dbbot(struct dentry *dentry); +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); + +/* ---------------------------------------------------------------------- */ + +static inline struct au_dinfo *au_di(struct dentry *dentry) +{ + return dentry->d_fsdata; +} + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for dinfo */ +enum { + AuLsc_DI_CHILD, /* child first */ + AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ + AuLsc_DI_CHILD3, /* copyup dirs */ + AuLsc_DI_PARENT, + AuLsc_DI_PARENT2, + AuLsc_DI_PARENT3, + AuLsc_DI_TMP /* temp for replacing dinfo */ +}; + +/* + * di_read_lock_child, di_write_lock_child, + * di_read_lock_child2, di_write_lock_child2, + * di_read_lock_child3, di_write_lock_child3, + * di_read_lock_parent, di_write_lock_parent, + * di_read_lock_parent2, di_write_lock_parent2, + * di_read_lock_parent3, di_write_lock_parent3, + */ +#define AuReadLockFunc(name, lsc) \ +static inline void di_read_lock_##name(struct dentry *d, int flags) \ +{ di_read_lock(d, flags, AuLsc_DI_##lsc); } + +#define AuWriteLockFunc(name, lsc) \ +static inline void di_write_lock_##name(struct dentry *d) \ +{ di_write_lock(d, AuLsc_DI_##lsc); } + +#define AuRWLockFuncs(name, lsc) \ + AuReadLockFunc(name, lsc) \ + AuWriteLockFunc(name, lsc) + +AuRWLockFuncs(child, CHILD); +AuRWLockFuncs(child2, CHILD2); +AuRWLockFuncs(child3, CHILD3); +AuRWLockFuncs(parent, PARENT); +AuRWLockFuncs(parent2, PARENT2); +AuRWLockFuncs(parent3, PARENT3); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) +#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) +#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) + +/* ---------------------------------------------------------------------- */ + +/* todo: memory barrier? */ +static inline unsigned int au_digen(struct dentry *d) +{ + return atomic_read(&au_di(d)->di_generation); +} + +static inline void au_h_dentry_init(struct au_hdentry *hdentry) +{ + hdentry->hd_dentry = NULL; +} + +static inline struct au_hdentry *au_hdentry(struct au_dinfo *di, + aufs_bindex_t bindex) +{ + return di->di_hdentry + bindex; +} + +static inline void au_hdput(struct au_hdentry *hd) +{ + if (hd) + dput(hd->hd_dentry); +} + +static inline aufs_bindex_t au_dbtop(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_btop; +} + +static inline aufs_bindex_t au_dbbot(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bbot; +} + +static inline aufs_bindex_t au_dbwh(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bwh; +} + +static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bdiropq; +} + +/* todo: hard/soft set? */ +static inline void au_set_dbtop(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + au_di(dentry)->di_btop = bindex; +} + +static inline void au_set_dbbot(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + au_di(dentry)->di_bbot = bindex; +} + +static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + /* dbwh can be outside of btop - bbot range */ + au_di(dentry)->di_bwh = bindex; +} + +static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + au_di(dentry)->di_bdiropq = bindex; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_HNOTIFY +static inline void au_digen_dec(struct dentry *d) +{ + atomic_dec(&au_di(d)->di_generation); +} + +static inline void au_hn_di_reinit(struct dentry *dentry) +{ + dentry->d_fsdata = NULL; +} +#else +AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) +#endif /* CONFIG_AUFS_HNOTIFY */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DENTRY_H__ */ diff -Naur --no-dereference a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c --- a/fs/aufs/dinfo.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dinfo.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * dentry private data + */ + +#include "aufs.h" + +void au_di_init_once(void *_dinfo) +{ + struct au_dinfo *dinfo = _dinfo; + + au_rw_init(&dinfo->di_rwsem); +} + +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) +{ + struct au_dinfo *dinfo; + int nbr, i; + + dinfo = au_cache_alloc_dinfo(); + if (unlikely(!dinfo)) + goto out; + + nbr = au_sbbot(sb) + 1; + if (nbr <= 0) + nbr = 1; + dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); + if (dinfo->di_hdentry) { + au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); + dinfo->di_btop = -1; + dinfo->di_bbot = -1; + dinfo->di_bwh = -1; + dinfo->di_bdiropq = -1; + dinfo->di_tmpfile = 0; + for (i = 0; i < nbr; i++) + dinfo->di_hdentry[i].hd_id = -1; + goto out; + } + + au_cache_free_dinfo(dinfo); + dinfo = NULL; + +out: + return dinfo; +} + +void au_di_free(struct au_dinfo *dinfo) +{ + struct au_hdentry *p; + aufs_bindex_t bbot, bindex; + + /* dentry may not be revalidated */ + bindex = dinfo->di_btop; + if (bindex >= 0) { + bbot = dinfo->di_bbot; + p = au_hdentry(dinfo, bindex); + while (bindex++ <= bbot) + au_hdput(p++); + } + au_kfree_try_rcu(dinfo->di_hdentry); + au_cache_free_dinfo(dinfo); +} + +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) +{ + struct au_hdentry *p; + aufs_bindex_t bi; + + AuRwMustWriteLock(&a->di_rwsem); + AuRwMustWriteLock(&b->di_rwsem); + +#define DiSwap(v, name) \ + do { \ + v = a->di_##name; \ + a->di_##name = b->di_##name; \ + b->di_##name = v; \ + } while (0) + + DiSwap(p, hdentry); + DiSwap(bi, btop); + DiSwap(bi, bbot); + DiSwap(bi, bwh); + DiSwap(bi, bdiropq); + /* smp_mb(); */ + +#undef DiSwap +} + +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) +{ + AuRwMustWriteLock(&dst->di_rwsem); + AuRwMustWriteLock(&src->di_rwsem); + + dst->di_btop = src->di_btop; + dst->di_bbot = src->di_bbot; + dst->di_bwh = src->di_bwh; + dst->di_bdiropq = src->di_bdiropq; + /* smp_mb(); */ +} + +int au_di_init(struct dentry *dentry) +{ + int err; + struct super_block *sb; + struct au_dinfo *dinfo; + + err = 0; + sb = dentry->d_sb; + dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); + if (dinfo) { + atomic_set(&dinfo->di_generation, au_sigen(sb)); + /* smp_mb(); */ /* atomic_set */ + dentry->d_fsdata = dinfo; + } else + err = -ENOMEM; + + return err; +} + +void au_di_fin(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + + dinfo = au_di(dentry); + AuRwDestroy(&dinfo->di_rwsem); + au_di_free(dinfo); +} + +int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink) +{ + int err, sz; + struct au_hdentry *hdp; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + err = -ENOMEM; + sz = sizeof(*hdp) * (dinfo->di_bbot + 1); + if (!sz) + sz = sizeof(*hdp); + hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS, + may_shrink); + if (hdp) { + dinfo->di_hdentry = hdp; + err = 0; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void do_ii_write_lock(struct inode *inode, unsigned int lsc) +{ + switch (lsc) { + case AuLsc_DI_CHILD: + ii_write_lock_child(inode); + break; + case AuLsc_DI_CHILD2: + ii_write_lock_child2(inode); + break; + case AuLsc_DI_CHILD3: + ii_write_lock_child3(inode); + break; + case AuLsc_DI_PARENT: + ii_write_lock_parent(inode); + break; + case AuLsc_DI_PARENT2: + ii_write_lock_parent2(inode); + break; + case AuLsc_DI_PARENT3: + ii_write_lock_parent3(inode); + break; + default: + BUG(); + } +} + +static void do_ii_read_lock(struct inode *inode, unsigned int lsc) +{ + switch (lsc) { + case AuLsc_DI_CHILD: + ii_read_lock_child(inode); + break; + case AuLsc_DI_CHILD2: + ii_read_lock_child2(inode); + break; + case AuLsc_DI_CHILD3: + ii_read_lock_child3(inode); + break; + case AuLsc_DI_PARENT: + ii_read_lock_parent(inode); + break; + case AuLsc_DI_PARENT2: + ii_read_lock_parent2(inode); + break; + case AuLsc_DI_PARENT3: + ii_read_lock_parent3(inode); + break; + default: + BUG(); + } +} + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc) +{ + struct inode *inode; + + au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); + if (d_really_is_positive(d)) { + inode = d_inode(d); + if (au_ftest_lock(flags, IW)) + do_ii_write_lock(inode, lsc); + else if (au_ftest_lock(flags, IR)) + do_ii_read_lock(inode, lsc); + } +} + +void di_read_unlock(struct dentry *d, int flags) +{ + struct inode *inode; + + if (d_really_is_positive(d)) { + inode = d_inode(d); + if (au_ftest_lock(flags, IW)) { + au_dbg_verify_dinode(d); + ii_write_unlock(inode); + } else if (au_ftest_lock(flags, IR)) { + au_dbg_verify_dinode(d); + ii_read_unlock(inode); + } + } + au_rw_read_unlock(&au_di(d)->di_rwsem); +} + +void di_downgrade_lock(struct dentry *d, int flags) +{ + if (d_really_is_positive(d) && au_ftest_lock(flags, IR)) + ii_downgrade_lock(d_inode(d)); + au_rw_dgrade_lock(&au_di(d)->di_rwsem); +} + +void di_write_lock(struct dentry *d, unsigned int lsc) +{ + au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); + if (d_really_is_positive(d)) + do_ii_write_lock(d_inode(d), lsc); +} + +void di_write_unlock(struct dentry *d) +{ + au_dbg_verify_dinode(d); + if (d_really_is_positive(d)) + ii_write_unlock(d_inode(d)); + au_rw_write_unlock(&au_di(d)->di_rwsem); +} + +void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) +{ + AuDebugOn(d1 == d2 + || d_inode(d1) == d_inode(d2) + || d1->d_sb != d2->d_sb); + + if ((isdir && au_test_subdir(d1, d2)) + || d1 < d2) { + di_write_lock_child(d1); + di_write_lock_child2(d2); + } else { + di_write_lock_child(d2); + di_write_lock_child2(d1); + } +} + +void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) +{ + AuDebugOn(d1 == d2 + || d_inode(d1) == d_inode(d2) + || d1->d_sb != d2->d_sb); + + if ((isdir && au_test_subdir(d1, d2)) + || d1 < d2) { + di_write_lock_parent(d1); + di_write_lock_parent2(d2); + } else { + di_write_lock_parent(d2); + di_write_lock_parent2(d1); + } +} + +void di_write_unlock2(struct dentry *d1, struct dentry *d2) +{ + di_write_unlock(d1); + if (d_inode(d1) == d_inode(d2)) + au_rw_write_unlock(&au_di(d2)->di_rwsem); + else + di_write_unlock(d2); +} + +/* ---------------------------------------------------------------------- */ + +struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) +{ + struct dentry *d; + + DiMustAnyLock(dentry); + + if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) + return NULL; + AuDebugOn(bindex < 0); + d = au_hdentry(au_di(dentry), bindex)->hd_dentry; + AuDebugOn(d && au_dcount(d) <= 0); + return d; +} + +/* + * extended version of au_h_dptr(). + * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or + * error. + */ +struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) +{ + struct dentry *h_dentry; + struct inode *inode, *h_inode; + + AuDebugOn(d_really_is_negative(dentry)); + + h_dentry = NULL; + if (au_dbtop(dentry) <= bindex + && bindex <= au_dbbot(dentry)) + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && !au_d_linkable(h_dentry)) { + dget(h_dentry); + goto out; /* success */ + } + + inode = d_inode(dentry); + AuDebugOn(bindex < au_ibtop(inode)); + AuDebugOn(au_ibbot(inode) < bindex); + h_inode = au_h_iptr(inode, bindex); + h_dentry = d_find_alias(h_inode); + if (h_dentry) { + if (!IS_ERR(h_dentry)) { + if (!au_d_linkable(h_dentry)) + goto out; /* success */ + dput(h_dentry); + } else + goto out; + } + + if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { + h_dentry = au_plink_lkup(inode, bindex); + AuDebugOn(!h_dentry); + if (!IS_ERR(h_dentry)) { + if (!au_d_hashed_positive(h_dentry)) + goto out; /* success */ + dput(h_dentry); + h_dentry = NULL; + } + } + +out: + AuDbgDentry(h_dentry); + return h_dentry; +} + +aufs_bindex_t au_dbtail(struct dentry *dentry) +{ + aufs_bindex_t bbot, bwh; + + bbot = au_dbbot(dentry); + if (0 <= bbot) { + bwh = au_dbwh(dentry); + if (!bwh) + return bwh; + if (0 < bwh && bwh < bbot) + return bwh - 1; + } + return bbot; +} + +aufs_bindex_t au_dbtaildir(struct dentry *dentry) +{ + aufs_bindex_t bbot, bopq; + + bbot = au_dbtail(dentry); + if (0 <= bbot) { + bopq = au_dbdiropq(dentry); + if (0 <= bopq && bopq < bbot) + bbot = bopq; + } + return bbot; +} + +/* ---------------------------------------------------------------------- */ + +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry) +{ + struct au_dinfo *dinfo; + struct au_hdentry *hd; + struct au_branch *br; + + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); + hd = au_hdentry(dinfo, bindex); + au_hdput(hd); + hd->hd_dentry = h_dentry; + if (h_dentry) { + br = au_sbr(dentry->d_sb, bindex); + hd->hd_id = br->br_id; + } +} + +int au_dbrange_test(struct dentry *dentry) +{ + int err; + aufs_bindex_t btop, bbot; + + err = 0; + btop = au_dbtop(dentry); + bbot = au_dbbot(dentry); + if (btop >= 0) + AuDebugOn(bbot < 0 && btop > bbot); + else { + err = -EIO; + AuDebugOn(bbot >= 0); + } + + return err; +} + +int au_digen_test(struct dentry *dentry, unsigned int sigen) +{ + int err; + + err = 0; + if (unlikely(au_digen(dentry) != sigen + || au_iigen_test(d_inode(dentry), sigen))) + err = -EIO; + + return err; +} + +void au_update_digen(struct dentry *dentry) +{ + atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); + /* smp_mb(); */ /* atomic_set */ +} + +void au_update_dbrange(struct dentry *dentry, int do_put_zero) +{ + struct au_dinfo *dinfo; + struct dentry *h_d; + struct au_hdentry *hdp; + aufs_bindex_t bindex, bbot; + + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); + if (!dinfo || dinfo->di_btop < 0) + return; + + if (do_put_zero) { + bbot = dinfo->di_bbot; + bindex = dinfo->di_btop; + hdp = au_hdentry(dinfo, bindex); + for (; bindex <= bbot; bindex++, hdp++) { + h_d = hdp->hd_dentry; + if (h_d && d_is_negative(h_d)) + au_set_h_dptr(dentry, bindex, NULL); + } + } + + dinfo->di_btop = 0; + hdp = au_hdentry(dinfo, dinfo->di_btop); + for (; dinfo->di_btop <= dinfo->di_bbot; dinfo->di_btop++, hdp++) + if (hdp->hd_dentry) + break; + if (dinfo->di_btop > dinfo->di_bbot) { + dinfo->di_btop = -1; + dinfo->di_bbot = -1; + return; + } + + hdp = au_hdentry(dinfo, dinfo->di_bbot); + for (; dinfo->di_bbot >= 0; dinfo->di_bbot--, hdp--) + if (hdp->hd_dentry) + break; + AuDebugOn(dinfo->di_btop > dinfo->di_bbot || dinfo->di_bbot < 0); +} + +void au_update_dbtop(struct dentry *dentry) +{ + aufs_bindex_t bindex, bbot; + struct dentry *h_dentry; + + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { + au_set_dbtop(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } +} + +void au_update_dbbot(struct dentry *dentry) +{ + aufs_bindex_t bindex, btop; + struct dentry *h_dentry; + + btop = au_dbtop(dentry); + for (bindex = au_dbbot(dentry); bindex >= btop; bindex--) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { + au_set_dbbot(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } +} + +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) +{ + aufs_bindex_t bindex, bbot; + + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) + if (au_h_dptr(dentry, bindex) == h_dentry) + return bindex; + return -1; +} diff -Naur --no-dereference a/fs/aufs/dir.c b/fs/aufs/dir.c --- a/fs/aufs/dir.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dir.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * directory operations + */ + +#include +#include +#include "aufs.h" + +void au_add_nlink(struct inode *dir, struct inode *h_dir) +{ + unsigned int nlink; + + AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); + + nlink = dir->i_nlink; + nlink += h_dir->i_nlink - 2; + if (h_dir->i_nlink < 2) + nlink += 2; + smp_mb(); /* for i_nlink */ + /* 0 can happen in revaliding */ + set_nlink(dir, nlink); +} + +void au_sub_nlink(struct inode *dir, struct inode *h_dir) +{ + unsigned int nlink; + + AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); + + nlink = dir->i_nlink; + nlink -= h_dir->i_nlink - 2; + if (h_dir->i_nlink < 2) + nlink -= 2; + smp_mb(); /* for i_nlink */ + /* nlink == 0 means the branch-fs is broken */ + set_nlink(dir, nlink); +} + +loff_t au_dir_size(struct file *file, struct dentry *dentry) +{ + loff_t sz; + aufs_bindex_t bindex, bbot; + struct file *h_file; + struct dentry *h_dentry; + + sz = 0; + if (file) { + AuDebugOn(!d_is_dir(file->f_path.dentry)); + + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); + bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file && file_inode(h_file)) + sz += vfsub_f_size_read(h_file); + } + } else { + AuDebugOn(!dentry); + AuDebugOn(!d_is_dir(dentry)); + + bbot = au_dbtaildir(dentry); + for (bindex = au_dbtop(dentry); + bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) + sz += i_size_read(d_inode(h_dentry)); + } + } + if (sz < KMALLOC_MAX_SIZE) + sz = roundup_pow_of_two(sz); + if (sz > KMALLOC_MAX_SIZE) + sz = KMALLOC_MAX_SIZE; + else if (sz < NAME_MAX) { + BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); + sz = AUFS_RDBLK_DEF; + } + return sz; +} + +struct au_dir_ts_arg { + struct dentry *dentry; + aufs_bindex_t brid; +}; + +static void au_do_dir_ts(void *arg) +{ + struct au_dir_ts_arg *a = arg; + struct au_dtime dt; + struct path h_path; + struct inode *dir, *h_dir; + struct super_block *sb; + struct au_branch *br; + struct au_hinode *hdir; + int err; + aufs_bindex_t btop, bindex; + + sb = a->dentry->d_sb; + if (d_really_is_negative(a->dentry)) + goto out; + /* no dir->i_mutex lock */ + aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ + + dir = d_inode(a->dentry); + btop = au_ibtop(dir); + bindex = au_br_index(sb, a->brid); + if (bindex < btop) + goto out_unlock; + + br = au_sbr(sb, bindex); + h_path.dentry = au_h_dptr(a->dentry, bindex); + if (!h_path.dentry) + goto out_unlock; + h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, a->dentry, &h_path); + + br = au_sbr(sb, btop); + if (!au_br_writable(br->br_perm)) + goto out_unlock; + h_path.dentry = au_h_dptr(a->dentry, btop); + h_path.mnt = au_br_mnt(br); + err = vfsub_mnt_want_write(h_path.mnt); + if (err) + goto out_unlock; + hdir = au_hi(dir, btop); + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + h_dir = au_h_iptr(dir, btop); + if (h_dir->i_nlink + && timespec64_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { + dt.dt_h_path = h_path; + au_dtime_revert(&dt); + } + au_hn_inode_unlock(hdir); + vfsub_mnt_drop_write(h_path.mnt); + au_cpup_attr_timesizes(dir); + +out_unlock: + aufs_read_unlock(a->dentry, AuLock_DW); +out: + dput(a->dentry); + au_nwt_done(&au_sbi(sb)->si_nowait); + au_kfree_try_rcu(arg); +} + +void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) +{ + int perm, wkq_err; + aufs_bindex_t btop; + struct au_dir_ts_arg *arg; + struct dentry *dentry; + struct super_block *sb; + + IMustLock(dir); + + dentry = d_find_any_alias(dir); + AuDebugOn(!dentry); + sb = dentry->d_sb; + btop = au_ibtop(dir); + if (btop == bindex) { + au_cpup_attr_timesizes(dir); + goto out; + } + + perm = au_sbr_perm(sb, btop); + if (!au_br_writable(perm)) + goto out; + + arg = kmalloc(sizeof(*arg), GFP_NOFS); + if (!arg) + goto out; + + arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ + arg->brid = au_sbr_id(sb, bindex); + wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); + if (unlikely(wkq_err)) { + pr_err("wkq %d\n", wkq_err); + dput(dentry); + au_kfree_try_rcu(arg); + } + +out: + dput(dentry); +} + +/* ---------------------------------------------------------------------- */ + +static int reopen_dir(struct file *file) +{ + int err; + unsigned int flags; + aufs_bindex_t bindex, btail, btop; + struct dentry *dentry, *h_dentry; + struct file *h_file; + + /* open all lower dirs */ + dentry = file->f_path.dentry; + btop = au_dbtop(dentry); + for (bindex = au_fbtop(file); bindex < btop; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbtop(file, btop); + + btail = au_dbtaildir(dentry); + for (bindex = au_fbbot_dir(file); btail < bindex; bindex--) + au_set_h_fptr(file, bindex, NULL); + au_set_fbbot_dir(file, btail); + + flags = vfsub_file_flags(file); + for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_file = au_hf_dir(file, bindex); + if (h_file) + continue; + + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; /* close all? */ + au_set_h_fptr(file, bindex, h_file); + } + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + err = 0; + +out: + return err; +} + +static int do_open_dir(struct file *file, int flags, struct file *h_file) +{ + int err; + aufs_bindex_t bindex, btail; + struct dentry *dentry, *h_dentry; + struct vfsmount *mnt; + + FiMustWriteLock(file); + AuDebugOn(h_file); + + err = 0; + mnt = file->f_path.mnt; + dentry = file->f_path.dentry; + file->f_version = inode_query_iversion(d_inode(dentry)); + bindex = au_dbtop(dentry); + au_set_fbtop(file, bindex); + btail = au_dbtaildir(dentry); + au_set_fbbot_dir(file, btail); + for (; !err && bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + + err = vfsub_test_mntns(mnt, h_dentry->d_sb); + if (unlikely(err)) + break; + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + if (IS_ERR(h_file)) { + err = PTR_ERR(h_file); + break; + } + au_set_h_fptr(file, bindex, h_file); + } + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + if (!err) + return 0; /* success */ + + /* close all */ + for (bindex = au_fbtop(file); bindex <= btail; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbtop(file, -1); + au_set_fbbot_dir(file, -1); + + return err; +} + +static int aufs_open_dir(struct inode *inode __maybe_unused, + struct file *file) +{ + int err; + struct super_block *sb; + struct au_fidir *fidir; + + err = -ENOMEM; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + fidir = au_fidir_alloc(sb); + if (fidir) { + struct au_do_open_args args = { + .open = do_open_dir, + .fidir = fidir + }; + err = au_do_open(file, &args); + if (unlikely(err)) + au_kfree_rcu(fidir); + } + si_read_unlock(sb); + return err; +} + +static int aufs_release_dir(struct inode *inode __maybe_unused, + struct file *file) +{ + struct au_vdir *vdir_cache; + struct au_finfo *finfo; + struct au_fidir *fidir; + struct au_hfile *hf; + aufs_bindex_t bindex, bbot; + + finfo = au_fi(file); + fidir = finfo->fi_hdir; + if (fidir) { + au_hbl_del(&finfo->fi_hlist, + &au_sbi(file->f_path.dentry->d_sb)->si_files); + vdir_cache = fidir->fd_vdir_cache; /* lock-free */ + if (vdir_cache) + au_vdir_free(vdir_cache); + + bindex = finfo->fi_btop; + if (bindex >= 0) { + hf = fidir->fd_hfile + bindex; + /* + * calls fput() instead of filp_close(), + * since no dnotify or lock for the lower file. + */ + bbot = fidir->fd_bbot; + for (; bindex <= bbot; bindex++, hf++) + if (hf->hf_file) + au_hfput(hf, /*execed*/0); + } + au_kfree_rcu(fidir); + finfo->fi_hdir = NULL; + } + au_finfo_fin(file); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_flush_dir(struct file *file, fl_owner_t id) +{ + int err; + aufs_bindex_t bindex, bbot; + struct file *h_file; + + err = 0; + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file) + err = vfsub_flush(h_file, id); + } + return err; +} + +static int aufs_flush_dir(struct file *file, fl_owner_t id) +{ + return au_do_flush(file, id, au_do_flush_dir); +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) +{ + int err; + aufs_bindex_t bbot, bindex; + struct inode *inode; + struct super_block *sb; + + err = 0; + sb = dentry->d_sb; + inode = d_inode(dentry); + IMustLock(inode); + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); !err && bindex <= bbot; bindex++) { + struct path h_path; + + if (au_test_ro(sb, bindex, inode)) + continue; + h_path.dentry = au_h_dptr(dentry, bindex); + if (!h_path.dentry) + continue; + + h_path.mnt = au_sbr_mnt(sb, bindex); + err = vfsub_fsync(NULL, &h_path, datasync); + } + + return err; +} + +static int au_do_fsync_dir(struct file *file, int datasync) +{ + int err; + aufs_bindex_t bbot, bindex; + struct file *h_file; + struct super_block *sb; + struct inode *inode; + + err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0); + if (unlikely(err)) + goto out; + + inode = file_inode(file); + sb = inode->i_sb; + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (!h_file || au_test_ro(sb, bindex, inode)) + continue; + + err = vfsub_fsync(h_file, &h_file->f_path, datasync); + } + +out: + return err; +} + +/* + * @file may be NULL + */ +static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, + int datasync) +{ + int err; + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; + + err = 0; + dentry = file->f_path.dentry; + inode = d_inode(dentry); + inode_lock(inode); + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (file) + err = au_do_fsync_dir(file, datasync); + else { + di_write_lock_child(dentry); + err = au_do_fsync_dir_no_file(dentry, datasync); + } + au_cpup_attr_timesizes(inode); + di_write_unlock(dentry); + if (file) + fi_write_unlock(file); + + si_read_unlock(sb); + inode_unlock(inode); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_iterate_shared(struct file *file, struct dir_context *ctx) +{ + int err; + struct dentry *dentry; + struct inode *inode, *h_inode; + struct super_block *sb; + + AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos); + + dentry = file->f_path.dentry; + inode = d_inode(dentry); + IMustLock(inode); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0); + if (unlikely(err)) + goto out; + err = au_alive_dir(dentry); + if (!err) + err = au_vdir_init(file); + di_downgrade_lock(dentry, AuLock_IR); + if (unlikely(err)) + goto out_unlock; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (!au_test_nfsd()) { + err = au_vdir_fill_de(file, ctx); + fsstack_copy_attr_atime(inode, h_inode); + } else { + /* + * nfsd filldir may call lookup_one_len(), vfs_getattr(), + * encode_fh() and others. + */ + atomic_inc(&h_inode->i_count); + di_read_unlock(dentry, AuLock_IR); + si_read_unlock(sb); + err = au_vdir_fill_de(file, ctx); + fsstack_copy_attr_atime(inode, h_inode); + fi_write_unlock(file); + iput(h_inode); + + AuTraceErr(err); + return err; + } + +out_unlock: + di_read_unlock(dentry, AuLock_IR); + fi_write_unlock(file); +out: + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +#define AuTestEmpty_WHONLY 1 +#define AuTestEmpty_CALLED (1 << 1) +#define AuTestEmpty_SHWH (1 << 2) +#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) +#define au_fset_testempty(flags, name) \ + do { (flags) |= AuTestEmpty_##name; } while (0) +#define au_fclr_testempty(flags, name) \ + do { (flags) &= ~AuTestEmpty_##name; } while (0) + +#ifndef CONFIG_AUFS_SHWH +#undef AuTestEmpty_SHWH +#define AuTestEmpty_SHWH 0 +#endif + +struct test_empty_arg { + struct dir_context ctx; + struct au_nhash *whlist; + unsigned int flags; + int err; + aufs_bindex_t bindex; +}; + +static int test_empty_cb(struct dir_context *ctx, const char *__name, + int namelen, loff_t offset __maybe_unused, u64 ino, + unsigned int d_type) +{ + struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, + ctx); + char *name = (void *)__name; + + arg->err = 0; + au_fset_testempty(arg->flags, CALLED); + /* smp_mb(); */ + if (name[0] == '.' + && (namelen == 1 || (name[1] == '.' && namelen == 2))) + goto out; /* success */ + + if (namelen <= AUFS_WH_PFX_LEN + || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + if (au_ftest_testempty(arg->flags, WHONLY) + && !au_nhash_test_known_wh(arg->whlist, name, namelen)) + arg->err = -ENOTEMPTY; + goto out; + } + + name += AUFS_WH_PFX_LEN; + namelen -= AUFS_WH_PFX_LEN; + if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) + arg->err = au_nhash_append_wh + (arg->whlist, name, namelen, ino, d_type, arg->bindex, + au_ftest_testempty(arg->flags, SHWH)); + +out: + /* smp_mb(); */ + AuTraceErr(arg->err); + return arg->err; +} + +static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) +{ + int err; + struct file *h_file; + struct au_branch *br; + + h_file = au_h_open(dentry, arg->bindex, + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, + /*file*/NULL, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = 0; + if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) + && !file_inode(h_file)->i_nlink) + goto out_put; + + do { + arg->err = 0; + au_fclr_testempty(arg->flags, CALLED); + /* smp_mb(); */ + err = vfsub_iterate_dir(h_file, &arg->ctx); + if (err >= 0) + err = arg->err; + } while (!err && au_ftest_testempty(arg->flags, CALLED)); + +out_put: + fput(h_file); + br = au_sbr(dentry->d_sb, arg->bindex); + au_lcnt_dec(&br->br_nfiles); +out: + return err; +} + +struct do_test_empty_args { + int *errp; + struct dentry *dentry; + struct test_empty_arg *arg; +}; + +static void call_do_test_empty(void *args) +{ + struct do_test_empty_args *a = args; + *a->errp = do_test_empty(a->dentry, a->arg); +} + +static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) +{ + int err, wkq_err; + struct dentry *h_dentry; + struct inode *h_inode; + + h_dentry = au_h_dptr(dentry, arg->bindex); + h_inode = d_inode(h_dentry); + /* todo: i_mode changes anytime? */ + inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); + inode_unlock_shared(h_inode); + if (!err) + err = do_test_empty(dentry, arg); + else { + struct do_test_empty_args args = { + .errp = &err, + .dentry = dentry, + .arg = arg + }; + unsigned int flags = arg->flags; + + wkq_err = au_wkq_wait(call_do_test_empty, &args); + if (unlikely(wkq_err)) + err = wkq_err; + arg->flags = flags; + } + + return err; +} + +int au_test_empty_lower(struct dentry *dentry) +{ + int err; + unsigned int rdhash; + aufs_bindex_t bindex, btop, btail; + struct au_nhash whlist; + struct test_empty_arg arg = { + .ctx = { + .actor = test_empty_cb + } + }; + int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); + + SiMustAnyLock(dentry->d_sb); + + rdhash = au_sbi(dentry->d_sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); + err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + + arg.flags = 0; + arg.whlist = &whlist; + btop = au_dbtop(dentry); + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + test_empty = do_test_empty; + if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) + test_empty = sio_test_empty; + arg.bindex = btop; + err = test_empty(dentry, &arg); + if (unlikely(err)) + goto out_whlist; + + au_fset_testempty(arg.flags, WHONLY); + btail = au_dbtaildir(dentry); + for (bindex = btop + 1; !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) { + arg.bindex = bindex; + err = test_empty(dentry, &arg); + } + } + +out_whlist: + au_nhash_wh_free(&whlist); +out: + return err; +} + +int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) +{ + int err; + struct test_empty_arg arg = { + .ctx = { + .actor = test_empty_cb + } + }; + aufs_bindex_t bindex, btail; + + err = 0; + arg.whlist = whlist; + arg.flags = AuTestEmpty_WHONLY; + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + btail = au_dbtaildir(dentry); + for (bindex = au_dbtop(dentry); !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) { + arg.bindex = bindex; + err = sio_test_empty(dentry, &arg); + } + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +const struct file_operations aufs_dir_fop = { + .owner = THIS_MODULE, + .llseek = default_llseek, + .read = generic_read_dir, + .iterate_shared = aufs_iterate_shared, + .unlocked_ioctl = aufs_ioctl_dir, +#ifdef CONFIG_COMPAT + .compat_ioctl = aufs_compat_ioctl_dir, +#endif + .open = aufs_open_dir, + .release = aufs_release_dir, + .flush = aufs_flush_dir, + .fsync = aufs_fsync_dir +}; diff -Naur --no-dereference a/fs/aufs/dir.h b/fs/aufs/dir.h --- a/fs/aufs/dir.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dir.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * directory operations + */ + +#ifndef __AUFS_DIR_H__ +#define __AUFS_DIR_H__ + +#ifdef __KERNEL__ + +#include + +/* ---------------------------------------------------------------------- */ + +/* need to be faster and smaller */ + +struct au_nhash { + unsigned int nh_num; + struct hlist_head *nh_head; +}; + +struct au_vdir_destr { + unsigned char len; + unsigned char name[]; +} __packed; + +struct au_vdir_dehstr { + struct hlist_node hash; + struct au_vdir_destr *str; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +struct au_vdir_de { + ino_t de_ino; + unsigned char de_type; + /* caution: packed */ + struct au_vdir_destr de_str; +} __packed; + +struct au_vdir_wh { + struct hlist_node wh_hash; +#ifdef CONFIG_AUFS_SHWH + ino_t wh_ino; + aufs_bindex_t wh_bindex; + unsigned char wh_type; +#else + aufs_bindex_t wh_bindex; +#endif + /* caution: packed */ + struct au_vdir_destr wh_str; +} __packed; + +union au_vdir_deblk_p { + unsigned char *deblk; + struct au_vdir_de *de; +}; + +struct au_vdir { + unsigned char **vd_deblk; + unsigned long vd_nblk; + struct { + unsigned long ul; + union au_vdir_deblk_p p; + } vd_last; + + u64 vd_version; + unsigned int vd_deblk_sz; + unsigned long vd_jiffy; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + +/* dir.c */ +extern const struct file_operations aufs_dir_fop; +void au_add_nlink(struct inode *dir, struct inode *h_dir); +void au_sub_nlink(struct inode *dir, struct inode *h_dir); +loff_t au_dir_size(struct file *file, struct dentry *dentry); +void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); +int au_test_empty_lower(struct dentry *dentry); +int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); + +/* vdir.c */ +unsigned int au_rdhash_est(loff_t sz); +int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); +void au_nhash_wh_free(struct au_nhash *whlist); +int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, + int limit); +int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); +int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, + unsigned int d_type, aufs_bindex_t bindex, + unsigned char shwh); +void au_vdir_free(struct au_vdir *vdir); +int au_vdir_init(struct file *file); +int au_vdir_fill_de(struct file *file, struct dir_context *ctx); + +/* ioctl.c */ +long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); + +#ifdef CONFIG_AUFS_RDU +/* rdu.c */ +long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif +#else +AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, + unsigned int cmd, unsigned long arg) +#ifdef CONFIG_COMPAT +AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, + unsigned int cmd, unsigned long arg) +#endif +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ diff -Naur --no-dereference a/fs/aufs/dirren.c b/fs/aufs/dirren.c --- a/fs/aufs/dirren.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dirren.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * special handling in renaming a directory + * in order to support looking-up the before-renamed name on the lower readonly + * branches + */ + +#include +#include "aufs.h" + +static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent) +{ + int idx; + + idx = au_dr_ihash(ent->dr_h_ino); + au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx); +} + +static int au_dr_hino_test_empty(struct au_dr_br *dr) +{ + int ret, i; + struct hlist_bl_head *hbl; + + ret = 1; + for (i = 0; ret && i < AuDirren_NHASH; i++) { + hbl = dr->dr_h_ino + i; + hlist_bl_lock(hbl); + ret &= hlist_bl_empty(hbl); + hlist_bl_unlock(hbl); + } + + return ret; +} + +static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino) +{ + struct au_dr_hino *found, *ent; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + int idx; + + found = NULL; + idx = au_dr_ihash(ino); + hbl = dr->dr_h_ino + idx; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode) + if (ent->dr_h_ino == ino) { + found = ent; + break; + } + hlist_bl_unlock(hbl); + + return found; +} + +int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino, + struct au_dr_hino *add_ent) +{ + int found, idx; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_dr_hino *ent; + + found = 0; + idx = au_dr_ihash(ino); + hbl = dr->dr_h_ino + idx; +#if 0 /* debug print */ + { + struct hlist_bl_node *tmp; + + hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) + AuDbg("hi%llu\n", (unsigned long long)ent->dr_h_ino); + } +#endif + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode) + if (ent->dr_h_ino == ino) { + found = 1; + break; + } + if (!found && add_ent) + hlist_bl_add_head(&add_ent->dr_hnode, hbl); + hlist_bl_unlock(hbl); + + if (!found && add_ent) + AuDbg("i%llu added\n", (unsigned long long)add_ent->dr_h_ino); + + return found; +} + +void au_dr_hino_free(struct au_dr_br *dr) +{ + int i; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *tmp; + struct au_dr_hino *ent; + + /* SiMustWriteLock(sb); */ + + for (i = 0; i < AuDirren_NHASH; i++) { + hbl = dr->dr_h_ino + i; + /* no spinlock since sbinfo must be write-locked */ + hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) + au_kfree_rcu(ent); + INIT_HLIST_BL_HEAD(hbl); + } +} + +/* returns the number of inodes or an error */ +static int au_dr_hino_store(struct super_block *sb, struct au_branch *br, + struct file *hinofile) +{ + int err, i; + ssize_t ssz; + loff_t pos, oldsize; + __be64 u64; + struct inode *hinoinode; + struct hlist_bl_head *hbl; + struct hlist_bl_node *n1, *n2; + struct au_dr_hino *ent; + + SiMustWriteLock(sb); + AuDebugOn(!au_br_writable(br->br_perm)); + + hinoinode = file_inode(hinofile); + oldsize = i_size_read(hinoinode); + + err = 0; + pos = 0; + hbl = br->br_dirren.dr_h_ino; + for (i = 0; !err && i < AuDirren_NHASH; i++, hbl++) { + /* no bit-lock since sbinfo must be write-locked */ + hlist_bl_for_each_entry_safe(ent, n1, n2, hbl, dr_hnode) { + AuDbg("hi%llu, %pD2\n", + (unsigned long long)ent->dr_h_ino, hinofile); + u64 = cpu_to_be64(ent->dr_h_ino); + ssz = vfsub_write_k(hinofile, &u64, sizeof(u64), &pos); + if (ssz == sizeof(u64)) + continue; + + /* write error */ + pr_err("ssz %zd, %pD2\n", ssz, hinofile); + err = -ENOSPC; + if (ssz < 0) + err = ssz; + break; + } + } + /* regardless the error */ + if (pos < oldsize) { + err = vfsub_trunc(&hinofile->f_path, pos, /*attr*/0, hinofile); + AuTraceErr(err); + } + + AuTraceErr(err); + return err; +} + +static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile) +{ + int err, hidx; + ssize_t ssz; + size_t sz, n; + loff_t pos; + uint64_t u64; + struct au_dr_hino *ent; + struct inode *hinoinode; + struct hlist_bl_head *hbl; + + err = 0; + pos = 0; + hbl = dr->dr_h_ino; + hinoinode = file_inode(hinofile); + sz = i_size_read(hinoinode); + AuDebugOn(sz % sizeof(u64)); + n = sz / sizeof(u64); + while (n--) { + ssz = vfsub_read_k(hinofile, &u64, sizeof(u64), &pos); + if (unlikely(ssz != sizeof(u64))) { + pr_err("ssz %zd, %pD2\n", ssz, hinofile); + err = -EINVAL; + if (ssz < 0) + err = ssz; + goto out_free; + } + + ent = kmalloc(sizeof(*ent), GFP_NOFS); + if (!ent) { + err = -ENOMEM; + AuTraceErr(err); + goto out_free; + } + ent->dr_h_ino = be64_to_cpu((__force __be64)u64); + AuDbg("hi%llu, %pD2\n", + (unsigned long long)ent->dr_h_ino, hinofile); + hidx = au_dr_ihash(ent->dr_h_ino); + au_hbl_add(&ent->dr_hnode, hbl + hidx); + } + goto out; /* success */ + +out_free: + au_dr_hino_free(dr); +out: + AuTraceErr(err); + return err; +} + +/* + * @bindex/@br is a switch to distinguish whether suspending hnotify or not. + * @path is a switch to distinguish load and store. + */ +static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br, const struct path *path) +{ + int err, flags; + unsigned char load, suspend; + struct file *hinofile; + struct au_hinode *hdir; + struct inode *dir, *delegated; + struct path hinopath; + struct qstr hinoname = QSTR_INIT(AUFS_WH_DR_BRHINO, + sizeof(AUFS_WH_DR_BRHINO) - 1); + + AuDebugOn(bindex < 0 && !br); + AuDebugOn(bindex >= 0 && br); + + err = -EINVAL; + suspend = !br; + if (suspend) + br = au_sbr(sb, bindex); + load = !!path; + if (!load) { + path = &br->br_path; + AuDebugOn(!au_br_writable(br->br_perm)); + if (unlikely(!au_br_writable(br->br_perm))) + goto out; + } + + hdir = NULL; + if (suspend) { + dir = d_inode(sb->s_root); + hdir = au_hinode(au_ii(dir), bindex); + dir = hdir->hi_inode; + au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD); + } else { + dir = d_inode(path->dentry); + inode_lock_nested(dir, AuLsc_I_CHILD); + } + hinopath.mnt = path->mnt; + hinopath.dentry = vfsub_lkup_one(&hinoname, (struct path *)path); + err = PTR_ERR(hinopath.dentry); + if (IS_ERR(hinopath.dentry)) + goto out_unlock; + + err = 0; + flags = O_RDONLY; + if (load) { + if (d_is_negative(hinopath.dentry)) + goto out_dput; /* success */ + } else { + if (au_dr_hino_test_empty(&br->br_dirren)) { + if (d_is_positive(hinopath.dentry)) { + delegated = NULL; + err = vfsub_unlink(dir, &hinopath, &delegated, + /*force*/0); + AuTraceErr(err); + if (unlikely(err)) + pr_err("ignored err %d, %pd2\n", + err, hinopath.dentry); + if (unlikely(err == -EWOULDBLOCK)) + iput(delegated); + err = 0; + } + goto out_dput; + } else if (!d_is_positive(hinopath.dentry)) { + err = vfsub_create(dir, &hinopath, 0600, + /*want_excl*/false); + AuTraceErr(err); + if (unlikely(err)) + goto out_dput; + } + flags = O_WRONLY; + } + hinofile = vfsub_dentry_open(&hinopath, flags); + if (suspend) + au_hn_inode_unlock(hdir); + else + inode_unlock(dir); + dput(hinopath.dentry); + AuTraceErrPtr(hinofile); + if (IS_ERR(hinofile)) { + err = PTR_ERR(hinofile); + goto out; + } + + if (load) + err = au_dr_hino_load(&br->br_dirren, hinofile); + else + err = au_dr_hino_store(sb, br, hinofile); + fput(hinofile); + goto out; + +out_dput: + dput(hinopath.dentry); +out_unlock: + if (suspend) + au_hn_inode_unlock(hdir); + else + inode_unlock(dir); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_dr_brid_init(struct au_dr_brid *brid, const struct path *path) +{ + int err; + struct kstatfs kstfs; + dev_t dev; + struct dentry *dentry; + struct super_block *sb; + + err = vfs_statfs((void *)path, &kstfs); + AuTraceErr(err); + if (unlikely(err)) + goto out; + + /* todo: support for UUID */ + + if (kstfs.f_fsid.val[0] || kstfs.f_fsid.val[1]) { + brid->type = AuBrid_FSID; + brid->fsid = kstfs.f_fsid; + } else { + dentry = path->dentry; + sb = dentry->d_sb; + dev = sb->s_dev; + if (dev) { + brid->type = AuBrid_DEV; + brid->dev = dev; + } + } + +out: + return err; +} + +int au_dr_br_init(struct super_block *sb, struct au_branch *br, + const struct path *path) +{ + int err, i; + struct au_dr_br *dr; + struct hlist_bl_head *hbl; + + dr = &br->br_dirren; + hbl = dr->dr_h_ino; + for (i = 0; i < AuDirren_NHASH; i++, hbl++) + INIT_HLIST_BL_HEAD(hbl); + + err = au_dr_brid_init(&dr->dr_brid, path); + if (unlikely(err)) + goto out; + + if (au_opt_test(au_mntflags(sb), DIRREN)) + err = au_dr_hino(sb, /*bindex*/-1, br, path); + +out: + AuTraceErr(err); + return err; +} + +int au_dr_br_fin(struct super_block *sb, struct au_branch *br) +{ + int err; + + err = 0; + if (au_br_writable(br->br_perm)) + err = au_dr_hino(sb, /*bindex*/-1, br, /*path*/NULL); + if (!err) + au_dr_hino_free(&br->br_dirren); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_brid_str(struct au_dr_brid *brid, struct inode *h_inode, + char *buf, size_t sz) +{ + int err; + unsigned int major, minor; + char *p; + + p = buf; + err = snprintf(p, sz, "%d_", brid->type); + AuDebugOn(err > sz); + p += err; + sz -= err; + switch (brid->type) { + case AuBrid_Unset: + return -EINVAL; + case AuBrid_UUID: + err = snprintf(p, sz, "%pU", brid->uuid.b); + break; + case AuBrid_FSID: + err = snprintf(p, sz, "%08x-%08x", + brid->fsid.val[0], brid->fsid.val[1]); + break; + case AuBrid_DEV: + major = MAJOR(brid->dev); + minor = MINOR(brid->dev); + if (major <= 0xff && minor <= 0xff) + err = snprintf(p, sz, "%02x%02x", major, minor); + else + err = snprintf(p, sz, "%03x:%05x", major, minor); + break; + } + AuDebugOn(err > sz); + p += err; + sz -= err; + err = snprintf(p, sz, "_%llu", (unsigned long long)h_inode->i_ino); + AuDebugOn(err > sz); + p += err; + sz -= err; + + return p - buf; +} + +static int au_drinfo_name(struct au_branch *br, char *name, int len) +{ + int rlen; + struct dentry *br_dentry; + struct inode *br_inode; + + br_dentry = au_br_dentry(br); + br_inode = d_inode(br_dentry); + rlen = au_brid_str(&br->br_dirren.dr_brid, br_inode, name, len); + AuDebugOn(rlen >= AUFS_DIRREN_ENV_VAL_SZ); + AuDebugOn(rlen > len); + + return rlen; +} + +/* ---------------------------------------------------------------------- */ + +/* + * from the given @h_dentry, construct drinfo at @*fdata. + * when the size of @*fdata is not enough, reallocate and return new @fdata and + * @allocated. + */ +static int au_drinfo_construct(struct au_drinfo_fdata **fdata, + struct dentry *h_dentry, + unsigned char *allocated) +{ + int err, v; + struct au_drinfo_fdata *f, *p; + struct au_drinfo *drinfo; + struct inode *h_inode; + struct qstr *qname; + + err = 0; + f = *fdata; + h_inode = d_inode(h_dentry); + qname = &h_dentry->d_name; + drinfo = &f->drinfo; + drinfo->ino = (__force uint64_t)cpu_to_be64(h_inode->i_ino); + drinfo->oldnamelen = qname->len; + if (*allocated < sizeof(*f) + qname->len) { + v = roundup_pow_of_two(*allocated + qname->len); + p = au_krealloc(f, v, GFP_NOFS, /*may_shrink*/0); + if (unlikely(!p)) { + err = -ENOMEM; + AuTraceErr(err); + goto out; + } + f = p; + *fdata = f; + *allocated = v; + drinfo = &f->drinfo; + } + memcpy(drinfo->oldname, qname->name, qname->len); + AuDbg("i%llu, %.*s\n", + be64_to_cpu((__force __be64)drinfo->ino), drinfo->oldnamelen, + drinfo->oldname); + +out: + AuTraceErr(err); + return err; +} + +/* callers have to free the return value */ +static struct au_drinfo *au_drinfo_read_k(struct file *file, ino_t h_ino) +{ + struct au_drinfo *ret, *drinfo; + struct au_drinfo_fdata fdata; + int len; + loff_t pos; + ssize_t ssz; + + ret = ERR_PTR(-EIO); + pos = 0; + ssz = vfsub_read_k(file, &fdata, sizeof(fdata), &pos); + if (unlikely(ssz != sizeof(fdata))) { + AuIOErr("ssz %zd, %u, %pD2\n", + ssz, (unsigned int)sizeof(fdata), file); + goto out; + } + + fdata.magic = ntohl((__force __be32)fdata.magic); + switch (fdata.magic) { + case AUFS_DRINFO_MAGIC_V1: + break; + default: + AuIOErr("magic-num 0x%x, 0x%x, %pD2\n", + fdata.magic, AUFS_DRINFO_MAGIC_V1, file); + goto out; + } + + drinfo = &fdata.drinfo; + len = drinfo->oldnamelen; + if (!len) { + AuIOErr("broken drinfo %pD2\n", file); + goto out; + } + + ret = NULL; + drinfo->ino = be64_to_cpu((__force __be64)drinfo->ino); + if (unlikely(h_ino && drinfo->ino != h_ino)) { + AuDbg("ignored i%llu, i%llu, %pD2\n", + (unsigned long long)drinfo->ino, + (unsigned long long)h_ino, file); + goto out; /* success */ + } + + ret = kmalloc(sizeof(*ret) + len, GFP_NOFS); + if (unlikely(!ret)) { + ret = ERR_PTR(-ENOMEM); + AuTraceErrPtr(ret); + goto out; + } + + *ret = *drinfo; + ssz = vfsub_read_k(file, (void *)ret->oldname, len, &pos); + if (unlikely(ssz != len)) { + au_kfree_rcu(ret); + ret = ERR_PTR(-EIO); + AuIOErr("ssz %zd, %u, %pD2\n", ssz, len, file); + goto out; + } + + AuDbg("oldname %.*s\n", ret->oldnamelen, ret->oldname); + +out: + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* in order to be revertible */ +struct au_drinfo_rev_elm { + int created; + struct dentry *info_dentry; + struct au_drinfo *info_last; +}; + +struct au_drinfo_rev { + unsigned char already; + aufs_bindex_t nelm; + struct au_drinfo_rev_elm elm[]; +}; + +/* todo: isn't it too large? */ +struct au_drinfo_store { + struct path h_ppath; + struct dentry *h_dentry; + struct au_drinfo_fdata *fdata; + char *infoname; /* inside of whname, just after PFX */ + char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ]; + aufs_bindex_t btgt, btail; + unsigned char no_sio, + allocated, /* current size of *fdata */ + infonamelen, /* room size for p */ + whnamelen, /* length of the generated name */ + renameback; /* renamed back */ +}; + +/* on rename(2) error, the caller should revert it using @elm */ +static int au_drinfo_do_store(struct au_drinfo_store *w, + struct au_drinfo_rev_elm *elm) +{ + int err, len; + ssize_t ssz; + loff_t pos; + struct path infopath = { + .mnt = w->h_ppath.mnt + }; + struct inode *h_dir, *h_inode, *delegated; + struct file *infofile; + struct qstr *qname; + + AuDebugOn(elm + && memcmp(elm, page_address(ZERO_PAGE(0)), sizeof(*elm))); + + infopath.dentry = vfsub_lookup_one_len(w->whname, &w->h_ppath, + w->whnamelen); + AuTraceErrPtr(infopath.dentry); + if (IS_ERR(infopath.dentry)) { + err = PTR_ERR(infopath.dentry); + goto out; + } + + err = 0; + h_dir = d_inode(w->h_ppath.dentry); + if (elm && d_is_negative(infopath.dentry)) { + err = vfsub_create(h_dir, &infopath, 0600, /*want_excl*/true); + AuTraceErr(err); + if (unlikely(err)) + goto out_dput; + elm->created = 1; + elm->info_dentry = dget(infopath.dentry); + } + + infofile = vfsub_dentry_open(&infopath, O_RDWR); + AuTraceErrPtr(infofile); + if (IS_ERR(infofile)) { + err = PTR_ERR(infofile); + goto out_dput; + } + + h_inode = d_inode(infopath.dentry); + if (elm && i_size_read(h_inode)) { + h_inode = d_inode(w->h_dentry); + elm->info_last = au_drinfo_read_k(infofile, h_inode->i_ino); + AuTraceErrPtr(elm->info_last); + if (IS_ERR(elm->info_last)) { + err = PTR_ERR(elm->info_last); + elm->info_last = NULL; + AuDebugOn(elm->info_dentry); + goto out_fput; + } + } + + if (elm && w->renameback) { + delegated = NULL; + err = vfsub_unlink(h_dir, &infopath, &delegated, /*force*/0); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) + iput(delegated); + goto out_fput; + } + + pos = 0; + qname = &w->h_dentry->d_name; + len = sizeof(*w->fdata) + qname->len; + if (!elm) + len = sizeof(*w->fdata) + w->fdata->drinfo.oldnamelen; + ssz = vfsub_write_k(infofile, w->fdata, len, &pos); + if (ssz == len) { + AuDbg("hi%llu, %.*s\n", w->fdata->drinfo.ino, + w->fdata->drinfo.oldnamelen, w->fdata->drinfo.oldname); + goto out_fput; /* success */ + } else { + err = -EIO; + if (ssz < 0) + err = ssz; + /* the caller should revert it using @elm */ + } + +out_fput: + fput(infofile); +out_dput: + dput(infopath.dentry); +out: + AuTraceErr(err); + return err; +} + +struct au_call_drinfo_do_store_args { + int *errp; + struct au_drinfo_store *w; + struct au_drinfo_rev_elm *elm; +}; + +static void au_call_drinfo_do_store(void *args) +{ + struct au_call_drinfo_do_store_args *a = args; + + *a->errp = au_drinfo_do_store(a->w, a->elm); +} + +static int au_drinfo_store_sio(struct au_drinfo_store *w, + struct au_drinfo_rev_elm *elm) +{ + int err, wkq_err; + + if (w->no_sio) + err = au_drinfo_do_store(w, elm); + else { + struct au_call_drinfo_do_store_args a = { + .errp = &err, + .w = w, + .elm = elm + }; + wkq_err = au_wkq_wait(au_call_drinfo_do_store, &a); + if (unlikely(wkq_err)) + err = wkq_err; + } + AuTraceErr(err); + + return err; +} + +static int au_drinfo_store_work_init(struct au_drinfo_store *w, + aufs_bindex_t btgt) +{ + int err; + + memset(w, 0, sizeof(*w)); + w->allocated = roundup_pow_of_two(sizeof(*w->fdata) + 40); + strcpy(w->whname, AUFS_WH_DR_INFO_PFX); + w->infoname = w->whname + sizeof(AUFS_WH_DR_INFO_PFX) - 1; + w->infonamelen = sizeof(w->whname) - sizeof(AUFS_WH_DR_INFO_PFX); + w->btgt = btgt; + w->no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID); + + err = -ENOMEM; + w->fdata = kcalloc(1, w->allocated, GFP_NOFS); + if (unlikely(!w->fdata)) { + AuTraceErr(err); + goto out; + } + w->fdata->magic = (__force uint32_t)htonl(AUFS_DRINFO_MAGIC_V1); + err = 0; + +out: + return err; +} + +static void au_drinfo_store_work_fin(struct au_drinfo_store *w) +{ + au_kfree_rcu(w->fdata); +} + +static void au_drinfo_store_rev(struct au_drinfo_rev *rev, + struct au_drinfo_store *w) +{ + struct au_drinfo_rev_elm *elm; + struct inode *h_dir, *delegated; + int err, nelm; + struct path infopath = { + .mnt = w->h_ppath.mnt + }; + + h_dir = d_inode(w->h_ppath.dentry); + IMustLock(h_dir); + + err = 0; + elm = rev->elm; + for (nelm = rev->nelm; nelm > 0; nelm--, elm++) { + AuDebugOn(elm->created && elm->info_last); + if (elm->created) { + AuDbg("here\n"); + delegated = NULL; + infopath.dentry = elm->info_dentry; + err = vfsub_unlink(h_dir, &infopath, &delegated, + !w->no_sio); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) + iput(delegated); + dput(elm->info_dentry); + } else if (elm->info_last) { + AuDbg("here\n"); + w->fdata->drinfo = *elm->info_last; + memcpy(w->fdata->drinfo.oldname, + elm->info_last->oldname, + elm->info_last->oldnamelen); + err = au_drinfo_store_sio(w, /*elm*/NULL); + au_kfree_rcu(elm->info_last); + } + if (unlikely(err)) + AuIOErr("%d, %s\n", err, w->whname); + /* go on even if err */ + } +} + +/* caller has to call au_dr_rename_fin() later */ +static int au_drinfo_store(struct dentry *dentry, aufs_bindex_t btgt, + struct qstr *dst_name, void *_rev) +{ + int err, sz, nelm; + aufs_bindex_t bindex, btail; + struct au_drinfo_store work; + struct au_drinfo_rev *rev, **p; + struct au_drinfo_rev_elm *elm; + struct super_block *sb; + struct au_branch *br; + struct au_hinode *hdir; + + err = au_drinfo_store_work_init(&work, btgt); + AuTraceErr(err); + if (unlikely(err)) + goto out; + + err = -ENOMEM; + btail = au_dbtaildir(dentry); + nelm = btail - btgt; + sz = sizeof(*rev) + sizeof(*elm) * nelm; + rev = kcalloc(1, sz, GFP_NOFS); + if (unlikely(!rev)) { + AuTraceErr(err); + goto out_args; + } + rev->nelm = nelm; + elm = rev->elm; + p = _rev; + *p = rev; + + err = 0; + sb = dentry->d_sb; + work.h_ppath.dentry = au_h_dptr(dentry, btgt); + work.h_ppath.mnt = au_sbr_mnt(sb, btgt); + hdir = au_hi(d_inode(dentry), btgt); + au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD); + for (bindex = btgt + 1; bindex <= btail; bindex++, elm++) { + work.h_dentry = au_h_dptr(dentry, bindex); + if (!work.h_dentry) + continue; + + err = au_drinfo_construct(&work.fdata, work.h_dentry, + &work.allocated); + AuTraceErr(err); + if (unlikely(err)) + break; + + work.renameback = au_qstreq(&work.h_dentry->d_name, dst_name); + br = au_sbr(sb, bindex); + work.whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1; + work.whnamelen += au_drinfo_name(br, work.infoname, + work.infonamelen); + AuDbg("whname %.*s, i%llu, %.*s\n", + work.whnamelen, work.whname, + be64_to_cpu((__force __be64)work.fdata->drinfo.ino), + work.fdata->drinfo.oldnamelen, + work.fdata->drinfo.oldname); + + err = au_drinfo_store_sio(&work, elm); + AuTraceErr(err); + if (unlikely(err)) + break; + } + if (unlikely(err)) { + /* revert all drinfo */ + au_drinfo_store_rev(rev, &work); + au_kfree_try_rcu(rev); + *p = NULL; + } + au_hn_inode_unlock(hdir); + +out_args: + au_drinfo_store_work_fin(&work); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_dr_rename(struct dentry *src, aufs_bindex_t bindex, + struct qstr *dst_name, void *_rev) +{ + int err, already; + ino_t ino; + struct super_block *sb; + struct au_branch *br; + struct au_dr_br *dr; + struct dentry *h_dentry; + struct inode *h_inode; + struct au_dr_hino *ent; + struct au_drinfo_rev *rev, **p; + + AuDbg("bindex %d\n", bindex); + + err = -ENOMEM; + ent = kmalloc(sizeof(*ent), GFP_NOFS); + if (unlikely(!ent)) + goto out; + + sb = src->d_sb; + br = au_sbr(sb, bindex); + dr = &br->br_dirren; + h_dentry = au_h_dptr(src, bindex); + h_inode = d_inode(h_dentry); + ino = h_inode->i_ino; + ent->dr_h_ino = ino; + already = au_dr_hino_test_add(dr, ino, ent); + AuDbg("b%d, hi%llu, already %d\n", + bindex, (unsigned long long)ino, already); + + err = au_drinfo_store(src, bindex, dst_name, _rev); + AuTraceErr(err); + if (!err) { + p = _rev; + rev = *p; + rev->already = already; + goto out; /* success */ + } + + /* revert */ + if (!already) + au_dr_hino_del(dr, ent); + au_kfree_rcu(ent); + +out: + AuTraceErr(err); + return err; +} + +void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *_rev) +{ + struct au_drinfo_rev *rev; + struct au_drinfo_rev_elm *elm; + int nelm; + + rev = _rev; + elm = rev->elm; + for (nelm = rev->nelm; nelm > 0; nelm--, elm++) { + dput(elm->info_dentry); + au_kfree_rcu(elm->info_last); + } + au_kfree_try_rcu(rev); +} + +void au_dr_rename_rev(struct dentry *src, aufs_bindex_t btgt, void *_rev) +{ + int err; + struct au_drinfo_store work; + struct au_drinfo_rev *rev = _rev; + struct super_block *sb; + struct au_branch *br; + struct inode *h_inode; + struct au_dr_br *dr; + struct au_dr_hino *ent; + + err = au_drinfo_store_work_init(&work, btgt); + if (unlikely(err)) + goto out; + + sb = src->d_sb; + br = au_sbr(sb, btgt); + work.h_ppath.dentry = au_h_dptr(src, btgt); + work.h_ppath.mnt = au_br_mnt(br); + au_drinfo_store_rev(rev, &work); + au_drinfo_store_work_fin(&work); + if (rev->already) + goto out; + + dr = &br->br_dirren; + h_inode = d_inode(work.h_ppath.dentry); + ent = au_dr_hino_find(dr, h_inode->i_ino); + BUG_ON(!ent); + au_dr_hino_del(dr, ent); + au_kfree_rcu(ent); + +out: + au_kfree_try_rcu(rev); + if (unlikely(err)) + pr_err("failed to remove dirren info\n"); +} + +/* ---------------------------------------------------------------------- */ + +static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath, + char *whname, int whnamelen, + struct dentry **info_dentry) +{ + struct au_drinfo *drinfo; + struct file *f; + struct inode *h_dir; + struct path infopath; + int unlocked; + + AuDbg("%pd/%.*s\n", h_ppath->dentry, whnamelen, whname); + + *info_dentry = NULL; + drinfo = NULL; + unlocked = 0; + h_dir = d_inode(h_ppath->dentry); + inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + infopath.dentry = vfsub_lookup_one_len(whname, h_ppath, whnamelen); + if (IS_ERR(infopath.dentry)) { + drinfo = (void *)infopath.dentry; + goto out; + } + + if (d_is_negative(infopath.dentry)) + goto out_dput; /* success */ + + infopath.mnt = h_ppath->mnt; + f = vfsub_dentry_open(&infopath, O_RDONLY); + inode_unlock_shared(h_dir); + unlocked = 1; + if (IS_ERR(f)) { + drinfo = (void *)f; + goto out_dput; + } + + drinfo = au_drinfo_read_k(f, /*h_ino*/0); + if (IS_ERR_OR_NULL(drinfo)) + goto out_fput; + + AuDbg("oldname %.*s\n", drinfo->oldnamelen, drinfo->oldname); + *info_dentry = dget(infopath.dentry); /* keep it alive */ + +out_fput: + fput(f); +out_dput: + dput(infopath.dentry); +out: + if (!unlocked) + inode_unlock_shared(h_dir); + AuTraceErrPtr(drinfo); + return drinfo; +} + +struct au_drinfo_do_load_args { + struct au_drinfo **drinfop; + struct path *h_ppath; + char *whname; + int whnamelen; + struct dentry **info_dentry; +}; + +static void au_call_drinfo_do_load(void *args) +{ + struct au_drinfo_do_load_args *a = args; + + *a->drinfop = au_drinfo_do_load(a->h_ppath, a->whname, a->whnamelen, + a->info_dentry); +} + +struct au_drinfo_load { + struct path h_ppath; + struct qstr *qname; + unsigned char no_sio; + + aufs_bindex_t ninfo; + struct au_drinfo **drinfo; +}; + +static int au_drinfo_load(struct au_drinfo_load *w, aufs_bindex_t bindex, + struct au_branch *br) +{ + int err, wkq_err, whnamelen, e; + char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ] + = AUFS_WH_DR_INFO_PFX; + struct au_drinfo *drinfo; + struct qstr oldname; + struct inode *h_dir, *delegated; + struct dentry *info_dentry; + struct path infopath; + + whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1; + whnamelen += au_drinfo_name(br, whname + whnamelen, + sizeof(whname) - whnamelen); + if (w->no_sio) + drinfo = au_drinfo_do_load(&w->h_ppath, whname, whnamelen, + &info_dentry); + else { + struct au_drinfo_do_load_args args = { + .drinfop = &drinfo, + .h_ppath = &w->h_ppath, + .whname = whname, + .whnamelen = whnamelen, + .info_dentry = &info_dentry + }; + wkq_err = au_wkq_wait(au_call_drinfo_do_load, &args); + if (unlikely(wkq_err)) + drinfo = ERR_PTR(wkq_err); + } + err = PTR_ERR(drinfo); + if (IS_ERR_OR_NULL(drinfo)) + goto out; + + err = 0; + oldname.len = drinfo->oldnamelen; + oldname.name = drinfo->oldname; + if (au_qstreq(w->qname, &oldname)) { + /* the name is renamed back */ + au_kfree_rcu(drinfo); + drinfo = NULL; + + infopath.dentry = info_dentry; + infopath.mnt = w->h_ppath.mnt; + h_dir = d_inode(w->h_ppath.dentry); + delegated = NULL; + inode_lock_nested(h_dir, AuLsc_I_PARENT); + e = vfsub_unlink(h_dir, &infopath, &delegated, !w->no_sio); + inode_unlock(h_dir); + if (unlikely(e)) + AuIOErr("ignored %d, %pd2\n", e, &infopath.dentry); + if (unlikely(e == -EWOULDBLOCK)) + iput(delegated); + } + au_kfree_rcu(w->drinfo[bindex]); + w->drinfo[bindex] = drinfo; + dput(info_dentry); + +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_dr_lkup_free(struct au_drinfo **drinfo, int n) +{ + struct au_drinfo **p = drinfo; + + while (n-- > 0) + au_kfree_rcu(*drinfo++); + au_kfree_try_rcu(p); +} + +int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry, + aufs_bindex_t btgt) +{ + int err, ninfo; + struct au_drinfo_load w; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + struct inode *h_dir; + struct au_dr_hino *ent; + struct super_block *sb; + + AuDbg("%.*s, name %.*s, whname %.*s, b%d\n", + AuLNPair(&dentry->d_name), AuLNPair(&lkup->dirren.dr_name), + AuLNPair(&lkup->whname), btgt); + + sb = dentry->d_sb; + bbot = au_sbbot(sb); + w.ninfo = bbot + 1; + if (!lkup->dirren.drinfo) { + lkup->dirren.drinfo = kcalloc(w.ninfo, + sizeof(*lkup->dirren.drinfo), + GFP_NOFS); + if (unlikely(!lkup->dirren.drinfo)) { + err = -ENOMEM; + goto out; + } + lkup->dirren.ninfo = w.ninfo; + } + w.drinfo = lkup->dirren.drinfo; + w.no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID); + w.h_ppath.dentry = au_h_dptr(dentry, btgt); + AuDebugOn(!w.h_ppath.dentry); + w.h_ppath.mnt = au_sbr_mnt(sb, btgt); + w.qname = &dentry->d_name; + + ninfo = 0; + for (bindex = btgt + 1; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_drinfo_load(&w, bindex, br); + if (unlikely(err)) + goto out_free; + if (w.drinfo[bindex]) + ninfo++; + } + if (!ninfo) { + br = au_sbr(sb, btgt); + h_dir = d_inode(w.h_ppath.dentry); + ent = au_dr_hino_find(&br->br_dirren, h_dir->i_ino); + AuDebugOn(!ent); + au_dr_hino_del(&br->br_dirren, ent); + au_kfree_rcu(ent); + } + goto out; /* success */ + +out_free: + au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo); + lkup->dirren.ninfo = 0; + lkup->dirren.drinfo = NULL; +out: + AuTraceErr(err); + return err; +} + +void au_dr_lkup_fin(struct au_do_lookup_args *lkup) +{ + au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo); +} + +int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt) +{ + int err; + struct au_drinfo *drinfo; + + err = 0; + if (!lkup->dirren.drinfo) + goto out; + AuDebugOn(lkup->dirren.ninfo <= btgt); + drinfo = lkup->dirren.drinfo[btgt]; + if (!drinfo) + goto out; + + au_kfree_try_rcu(lkup->whname.name); + lkup->whname.name = NULL; + lkup->dirren.dr_name.len = drinfo->oldnamelen; + lkup->dirren.dr_name.name = drinfo->oldname; + lkup->name = &lkup->dirren.dr_name; + err = au_wh_name_alloc(&lkup->whname, lkup->name); + if (!err) + AuDbg("name %.*s, whname %.*s, b%d\n", + AuLNPair(lkup->name), AuLNPair(&lkup->whname), + btgt); + +out: + AuTraceErr(err); + return err; +} + +int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex, + ino_t h_ino) +{ + int match; + struct au_drinfo *drinfo; + + match = 1; + if (!lkup->dirren.drinfo) + goto out; + AuDebugOn(lkup->dirren.ninfo <= bindex); + drinfo = lkup->dirren.drinfo[bindex]; + if (!drinfo) + goto out; + + match = (drinfo->ino == h_ino); + AuDbg("match %d\n", match); + +out: + return match; +} + +/* ---------------------------------------------------------------------- */ + +int au_dr_opt_set(struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + err = 0; + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_dr_hino(sb, bindex, /*br*/NULL, &br->br_path); + } + + return err; +} + +int au_dr_opt_flush(struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + err = 0; + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm)) + err = au_dr_hino(sb, bindex, /*br*/NULL, /*path*/NULL); + } + + return err; +} + +int au_dr_opt_clr(struct super_block *sb, int no_flush) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + err = 0; + if (!no_flush) { + err = au_dr_opt_flush(sb); + if (unlikely(err)) + goto out; + } + + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + au_dr_hino_free(&br->br_dirren); + } + +out: + return err; +} diff -Naur --no-dereference a/fs/aufs/dirren.h b/fs/aufs/dirren.h --- a/fs/aufs/dirren.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dirren.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * renamed dir info + */ + +#ifndef __AUFS_DIRREN_H__ +#define __AUFS_DIRREN_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include "hbl.h" + +#define AuDirren_NHASH 100 + +#ifdef CONFIG_AUFS_DIRREN +enum au_brid_type { + AuBrid_Unset, + AuBrid_UUID, + AuBrid_FSID, + AuBrid_DEV +}; + +struct au_dr_brid { + enum au_brid_type type; + union { + uuid_t uuid; /* unimplemented yet */ + fsid_t fsid; + dev_t dev; + }; +}; + +/* 20 is the max digits length of ulong 64 */ +/* brid-type "_" uuid "_" inum */ +#define AUFS_DIRREN_FNAME_SZ (1 + 1 + UUID_STRING_LEN + 20) +#define AUFS_DIRREN_ENV_VAL_SZ (AUFS_DIRREN_FNAME_SZ + 1 + 20) + +struct au_dr_hino { + struct hlist_bl_node dr_hnode; + ino_t dr_h_ino; +}; + +struct au_dr_br { + struct hlist_bl_head dr_h_ino[AuDirren_NHASH]; + struct au_dr_brid dr_brid; +}; + +struct au_dr_lookup { + /* dr_name is pointed by struct au_do_lookup_args.name */ + struct qstr dr_name; /* subset of dr_info */ + aufs_bindex_t ninfo; + struct au_drinfo **drinfo; +}; +#else +struct au_dr_hino; +/* empty */ +struct au_dr_br { }; +struct au_dr_lookup { }; +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_branch; +struct au_do_lookup_args; +struct au_hinode; +#ifdef CONFIG_AUFS_DIRREN +int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino, + struct au_dr_hino *add_ent); +void au_dr_hino_free(struct au_dr_br *dr); +int au_dr_br_init(struct super_block *sb, struct au_branch *br, + const struct path *path); +int au_dr_br_fin(struct super_block *sb, struct au_branch *br); +int au_dr_rename(struct dentry *src, aufs_bindex_t bindex, + struct qstr *dst_name, void *_rev); +void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev); +void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev); +int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry, + aufs_bindex_t bindex); +int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt); +int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex, + ino_t h_ino); +void au_dr_lkup_fin(struct au_do_lookup_args *lkup); +int au_dr_opt_set(struct super_block *sb); +int au_dr_opt_flush(struct super_block *sb); +int au_dr_opt_clr(struct super_block *sb, int no_flush); +#else +AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino, + struct au_dr_hino *add_ent); +AuStubVoid(au_dr_hino_free, struct au_dr_br *dr); +AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br, + const struct path *path); +AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br); +AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex, + struct qstr *dst_name, void *_rev); +AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev); +AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex, + void *rev); +AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry, + aufs_bindex_t bindex); +AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt); +AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup, + aufs_bindex_t bindex, ino_t h_ino); +AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup); +AuStubInt0(au_dr_opt_set, struct super_block *sb); +AuStubInt0(au_dr_opt_flush, struct super_block *sb); +AuStubInt0(au_dr_opt_clr, struct super_block *sb, int no_flush); +#endif + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_DIRREN +static inline int au_dr_ihash(ino_t h_ino) +{ + return h_ino % AuDirren_NHASH; +} +#else +AuStubInt0(au_dr_ihash, ino_t h_ino); +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIRREN_H__ */ diff -Naur --no-dereference a/fs/aufs/dynop.c b/fs/aufs/dynop.c --- a/fs/aufs/dynop.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dynop.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * dynamically customizable operations for regular files + */ + +#include "aufs.h" + +#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) + +/* + * How large will these lists be? + * Usually just a few elements, 20-30 at most for each, I guess. + */ +static struct hlist_bl_head dynop[AuDyLast]; + +static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl, + const void *h_op) +{ + struct au_dykey *key, *tmp; + struct hlist_bl_node *pos; + + key = NULL; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode) + if (tmp->dk_op.dy_hop == h_op) { + if (kref_get_unless_zero(&tmp->dk_kref)) + key = tmp; + break; + } + hlist_bl_unlock(hbl); + + return key; +} + +static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) +{ + struct au_dykey **k, *found; + const void *h_op = key->dk_op.dy_hop; + int i; + + found = NULL; + k = br->br_dykey; + for (i = 0; i < AuBrDynOp; i++) + if (k[i]) { + if (k[i]->dk_op.dy_hop == h_op) { + found = k[i]; + break; + } + } else + break; + if (!found) { + spin_lock(&br->br_dykey_lock); + for (; i < AuBrDynOp; i++) + if (k[i]) { + if (k[i]->dk_op.dy_hop == h_op) { + found = k[i]; + break; + } + } else { + k[i] = key; + break; + } + spin_unlock(&br->br_dykey_lock); + BUG_ON(i == AuBrDynOp); /* expand the array */ + } + + return found; +} + +/* kref_get() if @key is already added */ +static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key) +{ + struct au_dykey *tmp, *found; + struct hlist_bl_node *pos; + const void *h_op = key->dk_op.dy_hop; + + found = NULL; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode) + if (tmp->dk_op.dy_hop == h_op) { + if (kref_get_unless_zero(&tmp->dk_kref)) + found = tmp; + break; + } + if (!found) + hlist_bl_add_head(&key->dk_hnode, hbl); + hlist_bl_unlock(hbl); + + if (!found) + DyPrSym(key); + return found; +} + +static void dy_free_rcu(struct rcu_head *rcu) +{ + struct au_dykey *key; + + key = container_of(rcu, struct au_dykey, dk_rcu); + DyPrSym(key); + kfree(key); +} + +static void dy_free(struct kref *kref) +{ + struct au_dykey *key; + struct hlist_bl_head *hbl; + + key = container_of(kref, struct au_dykey, dk_kref); + hbl = dynop + key->dk_op.dy_type; + au_hbl_del(&key->dk_hnode, hbl); + call_rcu(&key->dk_rcu, dy_free_rcu); +} + +void au_dy_put(struct au_dykey *key) +{ + kref_put(&key->dk_kref, dy_free); +} + +/* ---------------------------------------------------------------------- */ + +#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) + +#ifdef CONFIG_AUFS_DEBUG +#define DyDbgDeclare(cnt) unsigned int cnt = 0 +#define DyDbgInc(cnt) do { cnt++; } while (0) +#else +#define DyDbgDeclare(cnt) do {} while (0) +#define DyDbgInc(cnt) do {} while (0) +#endif + +#define DySet(func, dst, src, h_op, h_sb) do { \ + DyDbgInc(cnt); \ + if (h_op->func) { \ + if (src.func) \ + dst.func = src.func; \ + else \ + AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ + } \ +} while (0) + +#define DySetForce(func, dst, src) do { \ + AuDebugOn(!src.func); \ + DyDbgInc(cnt); \ + dst.func = src.func; \ +} while (0) + +#define DySetAop(func) \ + DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) +#define DySetAopForce(func) \ + DySetForce(func, dyaop->da_op, aufs_aop) + +static void dy_aop(struct au_dykey *key, const void *h_op, + struct super_block *h_sb __maybe_unused) +{ + struct au_dyaop *dyaop = (void *)key; + const struct address_space_operations *h_aop = h_op; + DyDbgDeclare(cnt); + + AuDbg("%s\n", au_sbtype(h_sb)); + + DySetAop(writepage); + DySetAopForce(readpage); /* force */ + DySetAop(writepages); + DySetAop(set_page_dirty); + DySetAop(readpages); + DySetAop(readahead); + DySetAop(write_begin); + DySetAop(write_end); + DySetAop(bmap); + DySetAop(invalidatepage); + DySetAop(releasepage); + DySetAop(freepage); + /* this one will be changed according to an aufs mount option */ + DySetAop(direct_IO); + DySetAop(migratepage); + DySetAop(isolate_page); + DySetAop(putback_page); + DySetAop(launder_page); + DySetAop(is_partially_uptodate); + DySetAop(is_dirty_writeback); + DySetAop(error_remove_page); + DySetAop(swap_activate); + DySetAop(swap_deactivate); + + DyDbgSize(cnt, *h_aop); +} + +/* ---------------------------------------------------------------------- */ + +static void dy_bug(struct kref *kref) +{ + BUG(); +} + +static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) +{ + struct au_dykey *key, *old; + struct hlist_bl_head *hbl; + struct op { + unsigned int sz; + void (*set)(struct au_dykey *key, const void *h_op, + struct super_block *h_sb __maybe_unused); + }; + static const struct op a[] = { + [AuDy_AOP] = { + .sz = sizeof(struct au_dyaop), + .set = dy_aop + } + }; + const struct op *p; + + hbl = dynop + op->dy_type; + key = dy_gfind_get(hbl, op->dy_hop); + if (key) + goto out_add; /* success */ + + p = a + op->dy_type; + key = kzalloc(p->sz, GFP_NOFS); + if (unlikely(!key)) { + key = ERR_PTR(-ENOMEM); + goto out; + } + + key->dk_op.dy_hop = op->dy_hop; + kref_init(&key->dk_kref); + p->set(key, op->dy_hop, au_br_sb(br)); + old = dy_gadd(hbl, key); + if (old) { + au_kfree_rcu(key); + key = old; + } + +out_add: + old = dy_bradd(br, key); + if (old) + /* its ref-count should never be zero here */ + kref_put(&key->dk_kref, dy_bug); +out: + return key; +} + +/* ---------------------------------------------------------------------- */ +/* + * Aufs prohibits O_DIRECT by default even if the branch supports it. + * This behaviour is necessary to return an error from open(O_DIRECT) instead + * of the succeeding I/O. The dio mount option enables O_DIRECT and makes + * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. + * See the aufs manual in detail. + */ +static void dy_adx(struct au_dyaop *dyaop, int do_dx) +{ + if (!do_dx) + dyaop->da_op.direct_IO = NULL; + else + dyaop->da_op.direct_IO = aufs_aop.direct_IO; +} + +static struct au_dyaop *dy_aget(struct au_branch *br, + const struct address_space_operations *h_aop, + int do_dx) +{ + struct au_dyaop *dyaop; + struct au_dynop op; + + op.dy_type = AuDy_AOP; + op.dy_haop = h_aop; + dyaop = (void *)dy_get(&op, br); + if (IS_ERR(dyaop)) + goto out; + dy_adx(dyaop, do_dx); + +out: + return dyaop; +} + +int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode) +{ + int err, do_dx; + struct super_block *sb; + struct au_branch *br; + struct au_dyaop *dyaop; + + AuDebugOn(!S_ISREG(h_inode->i_mode)); + IiMustWriteLock(inode); + + sb = inode->i_sb; + br = au_sbr(sb, bindex); + do_dx = !!au_opt_test(au_mntflags(sb), DIO); + dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); + err = PTR_ERR(dyaop); + if (IS_ERR(dyaop)) + /* unnecessary to call dy_fput() */ + goto out; + + err = 0; + inode->i_mapping->a_ops = &dyaop->da_op; + +out: + return err; +} + +/* + * Is it safe to replace a_ops during the inode/file is in operation? + * Yes, I hope so. + */ +int au_dy_irefresh(struct inode *inode) +{ + int err; + aufs_bindex_t btop; + struct inode *h_inode; + + err = 0; + if (S_ISREG(inode->i_mode)) { + btop = au_ibtop(inode); + h_inode = au_h_iptr(inode, btop); + err = au_dy_iaop(inode, btop, h_inode); + } + return err; +} + +void au_dy_arefresh(int do_dx) +{ + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_dykey *key; + + hbl = dynop + AuDy_AOP; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(key, pos, hbl, dk_hnode) + dy_adx((void *)key, do_dx); + hlist_bl_unlock(hbl); +} + +/* ---------------------------------------------------------------------- */ + +void __init au_dy_init(void) +{ + int i; + + for (i = 0; i < AuDyLast; i++) + INIT_HLIST_BL_HEAD(dynop + i); +} + +void au_dy_fin(void) +{ + int i; + + for (i = 0; i < AuDyLast; i++) + WARN_ON(!hlist_bl_empty(dynop + i)); +} diff -Naur --no-dereference a/fs/aufs/dynop.h b/fs/aufs/dynop.h --- a/fs/aufs/dynop.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/dynop.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * dynamically customizable operations (for regular files only) + */ + +#ifndef __AUFS_DYNOP_H__ +#define __AUFS_DYNOP_H__ + +#ifdef __KERNEL__ + +#include +#include + +enum {AuDy_AOP, AuDyLast}; + +struct au_dynop { + int dy_type; + union { + const void *dy_hop; + const struct address_space_operations *dy_haop; + }; +}; + +struct au_dykey { + union { + struct hlist_bl_node dk_hnode; + struct rcu_head dk_rcu; + }; + struct au_dynop dk_op; + + /* + * during I am in the branch local array, kref is gotten. when the + * branch is removed, kref is put. + */ + struct kref dk_kref; +}; + +/* stop unioning since their sizes are very different from each other */ +struct au_dyaop { + struct au_dykey da_key; + struct address_space_operations da_op; /* not const */ +}; +/* make sure that 'struct au_dykey *' can be any type */ +static_assert(!offsetof(struct au_dyaop, da_key)); + +/* ---------------------------------------------------------------------- */ + +/* dynop.c */ +struct au_branch; +void au_dy_put(struct au_dykey *key); +int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode); +int au_dy_irefresh(struct inode *inode); +void au_dy_arefresh(int do_dio); + +void __init au_dy_init(void); +void au_dy_fin(void); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DYNOP_H__ */ diff -Naur --no-dereference a/fs/aufs/export.c b/fs/aufs/export.c --- a/fs/aufs/export.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/export.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,837 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * export via nfs + */ + +#include +#include +#include +#include +#include +#include +#include "aufs.h" + +union conv { +#ifdef CONFIG_AUFS_INO_T_64 + __u32 a[2]; +#else + __u32 a[1]; +#endif + ino_t ino; +}; + +static ino_t decode_ino(__u32 *a) +{ + union conv u; + + BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); + u.a[0] = a[0]; +#ifdef CONFIG_AUFS_INO_T_64 + u.a[1] = a[1]; +#endif + return u.ino; +} + +static void encode_ino(__u32 *a, ino_t ino) +{ + union conv u; + + u.ino = ino; + a[0] = u.a[0]; +#ifdef CONFIG_AUFS_INO_T_64 + a[1] = u.a[1]; +#endif +} + +/* NFS file handle */ +enum { + Fh_br_id, + Fh_sigen, +#ifdef CONFIG_AUFS_INO_T_64 + /* support 64bit inode number */ + Fh_ino1, + Fh_ino2, + Fh_dir_ino1, + Fh_dir_ino2, +#else + Fh_ino1, + Fh_dir_ino1, +#endif + Fh_igen, + Fh_h_type, + Fh_tail, + + Fh_ino = Fh_ino1, + Fh_dir_ino = Fh_dir_ino1 +}; + +static int au_test_anon(struct dentry *dentry) +{ + /* note: read d_flags without d_lock */ + return !!(dentry->d_flags & DCACHE_DISCONNECTED); +} + +int au_test_nfsd(void) +{ + int ret; + struct task_struct *tsk = current; + char comm[sizeof(tsk->comm)]; + + ret = 0; + if (tsk->flags & PF_KTHREAD) { + get_task_comm(comm, tsk); + ret = !strcmp(comm, "nfsd"); + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ +/* inode generation external table */ + +void au_xigen_inc(struct inode *inode) +{ + loff_t pos; + ssize_t sz; + __u32 igen; + struct super_block *sb; + struct au_sbinfo *sbinfo; + + sb = inode->i_sb; + AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + + sbinfo = au_sbi(sb); + pos = inode->i_ino; + pos *= sizeof(igen); + igen = inode->i_generation + 1; + sz = xino_fwrite(sbinfo->si_xigen, &igen, sizeof(igen), &pos); + if (sz == sizeof(igen)) + return; /* success */ + + if (unlikely(sz >= 0)) + AuIOErr("xigen error (%zd)\n", sz); +} + +int au_xigen_new(struct inode *inode) +{ + int err; + loff_t pos; + ssize_t sz; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct file *file; + + err = 0; + /* todo: dirty, at mount time */ + if (inode->i_ino == AUFS_ROOT_INO) + goto out; + sb = inode->i_sb; + SiMustAnyLock(sb); + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + goto out; + + err = -EFBIG; + pos = inode->i_ino; + if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { + AuIOErr1("too large i%lld\n", pos); + goto out; + } + pos *= sizeof(inode->i_generation); + + err = 0; + sbinfo = au_sbi(sb); + file = sbinfo->si_xigen; + BUG_ON(!file); + + if (vfsub_f_size_read(file) + < pos + sizeof(inode->i_generation)) { + inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); + sz = xino_fwrite(file, &inode->i_generation, + sizeof(inode->i_generation), &pos); + } else + sz = xino_fread(file, &inode->i_generation, + sizeof(inode->i_generation), &pos); + if (sz == sizeof(inode->i_generation)) + goto out; /* success */ + + err = sz; + if (unlikely(sz >= 0)) { + err = -EIO; + AuIOErr("xigen error (%zd)\n", sz); + } + +out: + return err; +} + +int au_xigen_set(struct super_block *sb, struct path *path) +{ + int err; + struct au_sbinfo *sbinfo; + struct file *file; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + file = au_xino_create2(sb, path, sbinfo->si_xigen); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + err = 0; + if (sbinfo->si_xigen) + fput(sbinfo->si_xigen); + sbinfo->si_xigen = file; + +out: + AuTraceErr(err); + return err; +} + +void au_xigen_clr(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + if (sbinfo->si_xigen) { + fput(sbinfo->si_xigen); + sbinfo->si_xigen = NULL; + } +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, + ino_t dir_ino) +{ + struct dentry *dentry, *d; + struct inode *inode; + unsigned int sigen; + + dentry = NULL; + inode = ilookup(sb, ino); + if (!inode) + goto out; + + dentry = ERR_PTR(-ESTALE); + sigen = au_sigen(sb); + if (unlikely(au_is_bad_inode(inode) + || IS_DEADDIR(inode) + || sigen != au_iigen(inode, NULL))) + goto out_iput; + + dentry = NULL; + if (!dir_ino || S_ISDIR(inode->i_mode)) + dentry = d_find_alias(inode); + else { + spin_lock(&inode->i_lock); + hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { + spin_lock(&d->d_lock); + if (!au_test_anon(d) + && d_inode(d->d_parent)->i_ino == dir_ino) { + dentry = dget_dlock(d); + spin_unlock(&d->d_lock); + break; + } + spin_unlock(&d->d_lock); + } + spin_unlock(&inode->i_lock); + } + if (unlikely(dentry && au_digen_test(dentry, sigen))) { + /* need to refresh */ + dput(dentry); + dentry = NULL; + } + +out_iput: + iput(inode); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +/* todo: dirty? */ +/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ + +struct au_compare_mnt_args { + /* input */ + struct super_block *sb; + + /* output */ + struct vfsmount *mnt; +}; + +static int au_compare_mnt(struct vfsmount *mnt, void *arg) +{ + struct au_compare_mnt_args *a = arg; + + if (mnt->mnt_sb != a->sb) + return 0; + a->mnt = mntget(mnt); + return 1; +} + +static struct vfsmount *au_mnt_get(struct super_block *sb) +{ + int err; + struct path root; + struct au_compare_mnt_args args = { + .sb = sb + }; + + get_fs_root(current->fs, &root); + rcu_read_lock(); + err = iterate_mounts(au_compare_mnt, &args, root.mnt); + rcu_read_unlock(); + path_put(&root); + AuDebugOn(!err); + AuDebugOn(!args.mnt); + return args.mnt; +} + +struct au_nfsd_si_lock { + unsigned int sigen; + aufs_bindex_t bindex, br_id; + unsigned char force_lock; +}; + +static int si_nfsd_read_lock(struct super_block *sb, + struct au_nfsd_si_lock *nsi_lock) +{ + int err; + aufs_bindex_t bindex; + + si_read_lock(sb, AuLock_FLUSH); + + /* branch id may be wrapped around */ + err = 0; + bindex = au_br_index(sb, nsi_lock->br_id); + if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) + goto out; /* success */ + + err = -ESTALE; + bindex = -1; + if (!nsi_lock->force_lock) + si_read_unlock(sb); + +out: + nsi_lock->bindex = bindex; + return err; +} + +struct find_name_by_ino { + struct dir_context ctx; + int called, found; + ino_t ino; + char *name; + int namelen; +}; + +static int +find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, + ctx); + + a->called++; + if (a->ino != ino) + return 0; + + memcpy(a->name, name, namelen); + a->namelen = namelen; + a->found = 1; + return 1; +} + +static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, + struct au_nfsd_si_lock *nsi_lock) +{ + struct dentry *dentry, *parent; + struct file *file; + struct inode *dir; + struct find_name_by_ino arg = { + .ctx = { + .actor = find_name_by_ino + } + }; + int err; + + parent = path->dentry; + if (nsi_lock) + si_read_unlock(parent->d_sb); + file = vfsub_dentry_open(path, au_dir_roflags); + dentry = (void *)file; + if (IS_ERR(file)) + goto out; + + dentry = ERR_PTR(-ENOMEM); + arg.name = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!arg.name)) + goto out_file; + arg.ino = ino; + arg.found = 0; + do { + arg.called = 0; + /* smp_mb(); */ + err = vfsub_iterate_dir(file, &arg.ctx); + } while (!err && !arg.found && arg.called); + dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_name; + /* instead of ENOENT */ + dentry = ERR_PTR(-ESTALE); + if (!arg.found) + goto out_name; + + /* do not call vfsub_lkup_one() */ + dir = d_inode(parent); + dentry = vfsub_lookup_one_len_unlocked(arg.name, path, arg.namelen); + AuTraceErrPtr(dentry); + if (IS_ERR(dentry)) + goto out_name; + AuDebugOn(au_test_anon(dentry)); + if (unlikely(d_really_is_negative(dentry))) { + dput(dentry); + dentry = ERR_PTR(-ENOENT); + } + +out_name: + free_page((unsigned long)arg.name); +out_file: + fput(file); +out: + if (unlikely(nsi_lock + && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) + if (!IS_ERR(dentry)) { + dput(dentry); + dentry = ERR_PTR(-ESTALE); + } + AuTraceErrPtr(dentry); + return dentry; +} + +static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, + ino_t dir_ino, + struct au_nfsd_si_lock *nsi_lock) +{ + struct dentry *dentry; + struct path path; + + if (dir_ino != AUFS_ROOT_INO) { + path.dentry = decode_by_ino(sb, dir_ino, 0); + dentry = path.dentry; + if (!path.dentry || IS_ERR(path.dentry)) + goto out; + AuDebugOn(au_test_anon(path.dentry)); + } else + path.dentry = dget(sb->s_root); + + path.mnt = au_mnt_get(sb); + dentry = au_lkup_by_ino(&path, ino, nsi_lock); + path_put(&path); + +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +static int h_acceptable(void *expv, struct dentry *dentry) +{ + return 1; +} + +static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, + char *buf, int len, struct super_block *sb) +{ + char *p; + int n; + struct path path; + + p = d_path(h_rootpath, buf, len); + if (IS_ERR(p)) + goto out; + n = strlen(p); + + path.mnt = h_rootpath->mnt; + path.dentry = h_parent; + p = d_path(&path, buf, len); + if (IS_ERR(p)) + goto out; + if (n != 1) + p += n; + + path.mnt = au_mnt_get(sb); + path.dentry = sb->s_root; + p = d_path(&path, buf, len - strlen(p)); + mntput(path.mnt); + if (IS_ERR(p)) + goto out; + if (n != 1) + p[strlen(p)] = '/'; + +out: + AuTraceErrPtr(p); + return p; +} + +static +struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, + int fh_len, struct au_nfsd_si_lock *nsi_lock) +{ + struct dentry *dentry, *h_parent, *root; + struct super_block *h_sb; + char *pathname, *p; + struct vfsmount *h_mnt; + struct au_branch *br; + int err; + struct path path; + + br = au_sbr(sb, nsi_lock->bindex); + h_mnt = au_br_mnt(br); + h_sb = h_mnt->mnt_sb; + /* todo: call lower fh_to_dentry()? fh_to_parent()? */ + lockdep_off(); + h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), + fh_len - Fh_tail, fh[Fh_h_type], + h_acceptable, /*context*/NULL); + lockdep_on(); + dentry = h_parent; + if (unlikely(!h_parent || IS_ERR(h_parent))) { + AuWarn1("%s decode_fh failed, %ld\n", + au_sbtype(h_sb), PTR_ERR(h_parent)); + goto out; + } + dentry = NULL; + if (unlikely(au_test_anon(h_parent))) { + AuWarn1("%s decode_fh returned a disconnected dentry\n", + au_sbtype(h_sb)); + goto out_h_parent; + } + + dentry = ERR_PTR(-ENOMEM); + pathname = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!pathname)) + goto out_h_parent; + + root = sb->s_root; + path.mnt = h_mnt; + di_read_lock_parent(root, !AuLock_IR); + path.dentry = au_h_dptr(root, nsi_lock->bindex); + di_read_unlock(root, !AuLock_IR); + p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); + dentry = (void *)p; + if (IS_ERR(p)) + goto out_pathname; + + si_read_unlock(sb); + err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); + dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_relock; + + dentry = ERR_PTR(-ENOENT); + AuDebugOn(au_test_anon(path.dentry)); + if (unlikely(d_really_is_negative(path.dentry))) + goto out_path; + + if (ino != d_inode(path.dentry)->i_ino) + dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); + else + dentry = dget(path.dentry); + +out_path: + path_put(&path); +out_relock: + if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) + if (!IS_ERR(dentry)) { + dput(dentry); + dentry = ERR_PTR(-ESTALE); + } +out_pathname: + free_page((unsigned long)pathname); +out_h_parent: + dput(h_parent); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry * +aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, + int fh_type) +{ + struct dentry *dentry; + __u32 *fh = fid->raw; + struct au_branch *br; + ino_t ino, dir_ino; + struct au_nfsd_si_lock nsi_lock = { + .force_lock = 0 + }; + + dentry = ERR_PTR(-ESTALE); + /* it should never happen, but the file handle is unreliable */ + if (unlikely(fh_len < Fh_tail)) + goto out; + nsi_lock.sigen = fh[Fh_sigen]; + nsi_lock.br_id = fh[Fh_br_id]; + + /* branch id may be wrapped around */ + br = NULL; + if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) + goto out; + nsi_lock.force_lock = 1; + + /* is this inode still cached? */ + ino = decode_ino(fh + Fh_ino); + /* it should never happen */ + if (unlikely(ino == AUFS_ROOT_INO)) + goto out_unlock; + + dir_ino = decode_ino(fh + Fh_dir_ino); + dentry = decode_by_ino(sb, ino, dir_ino); + if (IS_ERR(dentry)) + goto out_unlock; + if (dentry) + goto accept; + + /* is the parent dir cached? */ + br = au_sbr(sb, nsi_lock.bindex); + au_lcnt_inc(&br->br_nfiles); + dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); + if (IS_ERR(dentry)) + goto out_unlock; + if (dentry) + goto accept; + + /* lookup path */ + dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); + if (IS_ERR(dentry)) + goto out_unlock; + if (unlikely(!dentry)) + /* todo?: make it ESTALE */ + goto out_unlock; + +accept: + if (!au_digen_test(dentry, au_sigen(sb)) + && d_inode(dentry)->i_generation == fh[Fh_igen]) + goto out_unlock; /* success */ + + dput(dentry); + dentry = ERR_PTR(-ESTALE); +out_unlock: + if (br) + au_lcnt_dec(&br->br_nfiles); + si_read_unlock(sb); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +#if 0 /* reserved for future use */ +/* support subtreecheck option */ +static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + struct dentry *parent; + __u32 *fh = fid->raw; + ino_t dir_ino; + + dir_ino = decode_ino(fh + Fh_dir_ino); + parent = decode_by_ino(sb, dir_ino, 0); + if (IS_ERR(parent)) + goto out; + if (!parent) + parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), + dir_ino, fh, fh_len); + +out: + AuTraceErrPtr(parent); + return parent; +} +#endif + +/* ---------------------------------------------------------------------- */ + +static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, + struct inode *dir) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb, *h_sb; + struct dentry *dentry, *parent, *h_parent; + struct inode *h_dir; + struct au_branch *br; + + err = -ENOSPC; + if (unlikely(*max_len <= Fh_tail)) { + AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); + goto out; + } + + err = FILEID_ROOT; + if (inode->i_ino == AUFS_ROOT_INO) { + AuDebugOn(inode->i_ino != AUFS_ROOT_INO); + goto out; + } + + h_parent = NULL; + sb = inode->i_sb; + err = si_read_lock(sb, AuLock_FLUSH); + if (unlikely(err)) + goto out; + +#ifdef CONFIG_AUFS_DEBUG + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + AuWarn1("NFS-exporting requires xino\n"); +#endif + err = -EIO; + parent = NULL; + ii_read_lock_child(inode); + bindex = au_ibtop(inode); + if (!dir) { + dentry = d_find_any_alias(inode); + if (unlikely(!dentry)) + goto out_unlock; + AuDebugOn(au_test_anon(dentry)); + parent = dget_parent(dentry); + dput(dentry); + if (unlikely(!parent)) + goto out_unlock; + if (d_really_is_positive(parent)) + dir = d_inode(parent); + } + + ii_read_lock_parent(dir); + h_dir = au_h_iptr(dir, bindex); + ii_read_unlock(dir); + if (unlikely(!h_dir)) + goto out_parent; + h_parent = d_find_any_alias(h_dir); + if (unlikely(!h_parent)) + goto out_hparent; + + err = -EPERM; + br = au_sbr(sb, bindex); + h_sb = au_br_sb(br); + if (unlikely(!h_sb->s_export_op)) { + AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); + goto out_hparent; + } + + fh[Fh_br_id] = br->br_id; + fh[Fh_sigen] = au_sigen(sb); + encode_ino(fh + Fh_ino, inode->i_ino); + encode_ino(fh + Fh_dir_ino, dir->i_ino); + fh[Fh_igen] = inode->i_generation; + + *max_len -= Fh_tail; + fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), + max_len, + /*connectable or subtreecheck*/0); + err = fh[Fh_h_type]; + *max_len += Fh_tail; + /* todo: macros? */ + if (err != FILEID_INVALID) + err = 99; + else + AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); + +out_hparent: + dput(h_parent); +out_parent: + dput(parent); +out_unlock: + ii_read_unlock(inode); + si_read_unlock(sb); +out: + if (unlikely(err < 0)) + err = FILEID_INVALID; + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_commit_metadata(struct inode *inode) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct inode *h_inode; + int (*f)(struct inode *inode); + + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + ii_write_lock_child(inode); + bindex = au_ibtop(inode); + AuDebugOn(bindex < 0); + h_inode = au_h_iptr(inode, bindex); + + f = h_inode->i_sb->s_export_op->commit_metadata; + if (f) + err = f(h_inode); + else { + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 0 /* metadata only */ + }; + + err = sync_inode(h_inode, &wbc); + } + + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct export_operations aufs_export_op = { + .fh_to_dentry = aufs_fh_to_dentry, + /* .fh_to_parent = aufs_fh_to_parent, */ + .encode_fh = aufs_encode_fh, + .commit_metadata = aufs_commit_metadata +}; + +void au_export_init(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + __u32 u; + + BUILD_BUG_ON_MSG(IS_BUILTIN(CONFIG_AUFS_FS) + && IS_MODULE(CONFIG_EXPORTFS), + AUFS_NAME ": unsupported configuration " + "CONFIG_EXPORTFS=m and CONFIG_AUFS_FS=y"); + + sb->s_export_op = &aufs_export_op; + sbinfo = au_sbi(sb); + sbinfo->si_xigen = NULL; + get_random_bytes(&u, sizeof(u)); + BUILD_BUG_ON(sizeof(u) != sizeof(int)); + atomic_set(&sbinfo->si_xigen_next, u); +} diff -Naur --no-dereference a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c --- a/fs/aufs/fhsm.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/fhsm.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * File-based Hierarchy Storage Management + */ + +#include +#include +#include +#include +#include "aufs.h" + +static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + AuDebugOn(!fhsm); + return fhsm->fhsm_bottom; +} + +void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + AuDebugOn(!fhsm); + fhsm->fhsm_bottom = bindex; +} + +/* ---------------------------------------------------------------------- */ + +static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) +{ + struct au_br_fhsm *bf; + + bf = br->br_fhsm; + MtxMustLock(&bf->bf_lock); + + return !bf->bf_readable + || time_after(jiffies, + bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); +} + +/* ---------------------------------------------------------------------- */ + +static void au_fhsm_notify(struct super_block *sb, int val) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + if (au_fhsm_pid(fhsm) + && atomic_read(&fhsm->fhsm_readable) != -1) { + atomic_set(&fhsm->fhsm_readable, val); + if (val) + wake_up(&fhsm->fhsm_wqh); + } +} + +static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, + struct aufs_stfs *rstfs, int do_lock, int do_notify) +{ + int err; + struct au_branch *br; + struct au_br_fhsm *bf; + + br = au_sbr(sb, bindex); + AuDebugOn(au_br_rdonly(br)); + bf = br->br_fhsm; + AuDebugOn(!bf); + + if (do_lock) + mutex_lock(&bf->bf_lock); + else + MtxMustLock(&bf->bf_lock); + + /* sb->s_root for NFS is unreliable */ + err = au_br_stfs(br, &bf->bf_stfs); + if (unlikely(err)) { + AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); + goto out; + } + + bf->bf_jiffy = jiffies; + bf->bf_readable = 1; + if (do_notify) + au_fhsm_notify(sb, /*val*/1); + if (rstfs) + *rstfs = bf->bf_stfs; + +out: + if (do_lock) + mutex_unlock(&bf->bf_lock); + au_fhsm_notify(sb, /*val*/1); + + return err; +} + +void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) +{ + int err; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + struct au_branch *br; + struct au_br_fhsm *bf; + + AuDbg("b%d, force %d\n", bindex, force); + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + if (!au_ftest_si(sbinfo, FHSM) + || fhsm->fhsm_bottom == bindex) + return; + + br = au_sbr(sb, bindex); + bf = br->br_fhsm; + AuDebugOn(!bf); + mutex_lock(&bf->bf_lock); + if (force + || au_fhsm_pid(fhsm) + || au_fhsm_test_jiffy(sbinfo, br)) + err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, + /*do_notify*/1); + mutex_unlock(&bf->bf_lock); +} + +void au_fhsm_wrote_all(struct super_block *sb, int force) +{ + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + /* exclude the bottom */ + bbot = au_fhsm_bottom(sb); + for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + au_fhsm_wrote(sb, bindex, force); + } +} + +/* ---------------------------------------------------------------------- */ + +static __poll_t au_fhsm_poll(struct file *file, struct poll_table_struct *wait) +{ + __poll_t mask; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + mask = 0; + sbinfo = file->private_data; + fhsm = &sbinfo->si_fhsm; + poll_wait(file, &fhsm->fhsm_wqh, wait); + if (atomic_read(&fhsm->fhsm_readable)) + mask = EPOLLIN /* | EPOLLRDNORM */; + + if (!mask) + AuDbg("mask 0x%x\n", mask); + return mask; +} + +static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, + struct aufs_stfs *stfs, __s16 brid) +{ + int err; + + err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); + if (!err) + err = __put_user(brid, &stbr->brid); + if (unlikely(err)) + err = -EFAULT; + + return err; +} + +static ssize_t au_fhsm_do_read(struct super_block *sb, + struct aufs_stbr __user *stbr, size_t count) +{ + ssize_t err; + int nstbr; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + struct au_br_fhsm *bf; + + /* except the bottom branch */ + err = 0; + nstbr = 0; + bbot = au_fhsm_bottom(sb); + for (bindex = 0; !err && bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_fhsm(br->br_perm)) + continue; + + bf = br->br_fhsm; + mutex_lock(&bf->bf_lock); + if (bf->bf_readable) { + err = -EFAULT; + if (count >= sizeof(*stbr)) + err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, + br->br_id); + if (!err) { + bf->bf_readable = 0; + count -= sizeof(*stbr); + nstbr++; + } + } + mutex_unlock(&bf->bf_lock); + } + if (!err) + err = sizeof(*stbr) * nstbr; + + return err; +} + +static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + ssize_t err; + int readable; + aufs_bindex_t nfhsm, bindex, bbot; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + struct au_branch *br; + struct super_block *sb; + + err = 0; + sbinfo = file->private_data; + fhsm = &sbinfo->si_fhsm; +need_data: + spin_lock_irq(&fhsm->fhsm_wqh.lock); + if (!atomic_read(&fhsm->fhsm_readable)) { + if (vfsub_file_flags(file) & O_NONBLOCK) + err = -EAGAIN; + else + err = wait_event_interruptible_locked_irq + (fhsm->fhsm_wqh, + atomic_read(&fhsm->fhsm_readable)); + } + spin_unlock_irq(&fhsm->fhsm_wqh.lock); + if (unlikely(err)) + goto out; + + /* sb may already be dead */ + au_rw_read_lock(&sbinfo->si_rwsem); + readable = atomic_read(&fhsm->fhsm_readable); + if (readable > 0) { + sb = sbinfo->si_sb; + AuDebugOn(!sb); + /* exclude the bottom branch */ + nfhsm = 0; + bbot = au_fhsm_bottom(sb); + for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + nfhsm++; + } + err = -EMSGSIZE; + if (nfhsm * sizeof(struct aufs_stbr) <= count) { + atomic_set(&fhsm->fhsm_readable, 0); + err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, + count); + } + } + au_rw_read_unlock(&sbinfo->si_rwsem); + if (!readable) + goto need_data; + +out: + return err; +} + +static int au_fhsm_release(struct inode *inode, struct file *file) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + /* sb may already be dead */ + sbinfo = file->private_data; + fhsm = &sbinfo->si_fhsm; + spin_lock(&fhsm->fhsm_spin); + fhsm->fhsm_pid = 0; + spin_unlock(&fhsm->fhsm_spin); + kobject_put(&sbinfo->si_kobj); + + return 0; +} + +static const struct file_operations au_fhsm_fops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .read = au_fhsm_read, + .poll = au_fhsm_poll, + .release = au_fhsm_release +}; + +int au_fhsm_fd(struct super_block *sb, int oflags) +{ + int err, fd; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + err = -EPERM; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = -EINVAL; + if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) + goto out; + + err = 0; + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + spin_lock(&fhsm->fhsm_spin); + if (!fhsm->fhsm_pid) + fhsm->fhsm_pid = current->pid; + else + err = -EBUSY; + spin_unlock(&fhsm->fhsm_spin); + if (unlikely(err)) + goto out; + + oflags |= O_RDONLY; + /* oflags |= FMODE_NONOTIFY; */ + fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); + err = fd; + if (unlikely(fd < 0)) + goto out_pid; + + /* succeed regardless 'fhsm' status */ + kobject_get(&sbinfo->si_kobj); + si_noflush_read_lock(sb); + if (au_ftest_si(sbinfo, FHSM)) + au_fhsm_wrote_all(sb, /*force*/0); + si_read_unlock(sb); + goto out; /* success */ + +out_pid: + spin_lock(&fhsm->fhsm_spin); + fhsm->fhsm_pid = 0; + spin_unlock(&fhsm->fhsm_spin); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_fhsm_br_alloc(struct au_branch *br) +{ + int err; + + err = 0; + br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); + if (br->br_fhsm) + au_br_fhsm_init(br->br_fhsm); + else + err = -ENOMEM; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_fhsm_fin(struct super_block *sb) +{ + au_fhsm_notify(sb, /*val*/-1); +} + +void au_fhsm_init(struct au_sbinfo *sbinfo) +{ + struct au_fhsm *fhsm; + + fhsm = &sbinfo->si_fhsm; + spin_lock_init(&fhsm->fhsm_spin); + init_waitqueue_head(&fhsm->fhsm_wqh); + atomic_set(&fhsm->fhsm_readable, 0); + fhsm->fhsm_expire + = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); + fhsm->fhsm_bottom = -1; +} + +void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) +{ + sbinfo->si_fhsm.fhsm_expire + = msecs_to_jiffies(sec * MSEC_PER_SEC); +} + +void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) +{ + unsigned int u; + + if (!au_ftest_si(sbinfo, FHSM)) + return; + + u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; + if (u != AUFS_FHSM_CACHE_DEF_SEC) + seq_printf(seq, ",fhsm_sec=%u", u); +} diff -Naur --no-dereference a/fs/aufs/file.c b/fs/aufs/file.c --- a/fs/aufs/file.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/file.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,863 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * handling file/dir, and address_space operation + */ + +#ifdef CONFIG_AUFS_DEBUG +#include +#endif +#include +#include "aufs.h" + +/* drop flags for writing */ +unsigned int au_file_roflags(unsigned int flags) +{ + flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); + flags |= O_RDONLY | O_NOATIME; + return flags; +} + +/* common functions to regular file and dir */ +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file, int force_wr) +{ + struct file *h_file; + struct dentry *h_dentry; + struct inode *h_inode; + struct super_block *sb; + struct au_branch *br; + struct path h_path; + int err; + + /* a race condition can happen between open and unlink/rmdir */ + h_file = ERR_PTR(-ENOENT); + h_dentry = au_h_dptr(dentry, bindex); + if (au_test_nfsd() && (!h_dentry || d_is_negative(h_dentry))) + goto out; + h_inode = d_inode(h_dentry); + spin_lock(&h_dentry->d_lock); + err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) + /* || !d_inode(dentry)->i_nlink */ + ; + spin_unlock(&h_dentry->d_lock); + if (unlikely(err)) + goto out; + + sb = dentry->d_sb; + br = au_sbr(sb, bindex); + err = au_br_test_oflag(flags, br); + h_file = ERR_PTR(err); + if (unlikely(err)) + goto out; + + /* drop flags for writing */ + if (au_test_ro(sb, bindex, d_inode(dentry))) { + if (force_wr && !(flags & O_WRONLY)) + force_wr = 0; + flags = au_file_roflags(flags); + if (force_wr) { + h_file = ERR_PTR(-EROFS); + flags = au_file_roflags(flags); + if (unlikely(vfsub_native_ro(h_inode) + || IS_APPEND(h_inode))) + goto out; + flags &= ~O_ACCMODE; + flags |= O_WRONLY; + } + } + flags &= ~O_CREAT; + au_lcnt_inc(&br->br_nfiles); + h_path.dentry = h_dentry; + h_path.mnt = au_br_mnt(br); + h_file = vfsub_dentry_open(&h_path, flags); + if (IS_ERR(h_file)) + goto out_br; + + if (flags & __FMODE_EXEC) { + err = deny_write_access(h_file); + if (unlikely(err)) { + fput(h_file); + h_file = ERR_PTR(err); + goto out_br; + } + } + fsnotify_open(h_file); + goto out; /* success */ + +out_br: + au_lcnt_dec(&br->br_nfiles); +out: + return h_file; +} + +static int au_cmoo(struct dentry *dentry) +{ + int err, cmoo, matched; + unsigned int udba; + struct path h_path; + struct au_pin pin; + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = -1, + .bsrc = -1, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + struct inode *delegated; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + pid_t pid; + struct au_branch *br; + struct dentry *parent; + struct au_hinode *hdir; + + DiMustWriteLock(dentry); + IiMustWriteLock(d_inode(dentry)); + + err = 0; + if (IS_ROOT(dentry)) + goto out; + cpg.bsrc = au_dbtop(dentry); + if (!cpg.bsrc) + goto out; + + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + pid = au_fhsm_pid(fhsm); + rcu_read_lock(); + matched = (pid + && (current->pid == pid + || rcu_dereference(current->real_parent)->pid == pid)); + rcu_read_unlock(); + if (matched) + goto out; + + br = au_sbr(sb, cpg.bsrc); + cmoo = au_br_cmoo(br->br_perm); + if (!cmoo) + goto out; + if (!d_is_reg(dentry)) + cmoo &= AuBrAttr_COO_ALL; + if (!cmoo) + goto out; + + parent = dget_parent(dentry); + di_write_lock_parent(parent); + err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); + cpg.bdst = err; + if (unlikely(err < 0)) { + err = 0; /* there is no upper writable branch */ + goto out_dgrade; + } + AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); + + /* do not respect the coo attrib for the target branch */ + err = au_cpup_dirs(dentry, cpg.bdst); + if (unlikely(err)) + goto out_dgrade; + + di_downgrade_lock(parent, AuLock_IR); + udba = au_opt_udba(sb); + err = au_pin(&pin, dentry, cpg.bdst, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_parent; + + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + if (unlikely(err)) + goto out_parent; + if (!(cmoo & AuBrWAttr_MOO)) + goto out_parent; /* success */ + + err = au_pin(&pin, dentry, cpg.bsrc, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_parent; + + h_path.mnt = au_br_mnt(br); + h_path.dentry = au_h_dptr(dentry, cpg.bsrc); + hdir = au_hi(d_inode(parent), cpg.bsrc); + delegated = NULL; + err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); + au_unpin(&pin); + /* todo: keep h_dentry or not? */ + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) { + pr_err("unlink %pd after coo failed (%d), ignored\n", + dentry, err); + err = 0; + } + goto out_parent; /* success */ + +out_dgrade: + di_downgrade_lock(parent, AuLock_IR); +out_parent: + di_read_unlock(parent, AuLock_IR); + dput(parent); +out: + AuTraceErr(err); + return err; +} + +int au_do_open(struct file *file, struct au_do_open_args *args) +{ + int err, aopen = args->aopen; + struct dentry *dentry; + struct au_finfo *finfo; + + if (!aopen) + err = au_finfo_init(file, args->fidir); + else { + lockdep_off(); + err = au_finfo_init(file, args->fidir); + lockdep_on(); + } + if (unlikely(err)) + goto out; + + dentry = file->f_path.dentry; + AuDebugOn(IS_ERR_OR_NULL(dentry)); + di_write_lock_child(dentry); + err = au_cmoo(dentry); + di_downgrade_lock(dentry, AuLock_IR); + if (!err) { + if (!aopen) + err = args->open(file, vfsub_file_flags(file), NULL); + else { + lockdep_off(); + err = args->open(file, vfsub_file_flags(file), + args->h_file); + lockdep_on(); + } + } + di_read_unlock(dentry, AuLock_IR); + + finfo = au_fi(file); + if (!err) { + finfo->fi_file = file; + au_hbl_add(&finfo->fi_hlist, + &au_sbi(file->f_path.dentry->d_sb)->si_files); + } + if (!aopen) + fi_write_unlock(file); + else { + lockdep_off(); + fi_write_unlock(file); + lockdep_on(); + } + if (unlikely(err)) { + finfo->fi_hdir = NULL; + au_finfo_fin(file); + } + +out: + AuTraceErr(err); + return err; +} + +int au_reopen_nondir(struct file *file) +{ + int err; + aufs_bindex_t btop; + struct dentry *dentry; + struct au_branch *br; + struct file *h_file, *h_file_tmp; + + dentry = file->f_path.dentry; + btop = au_dbtop(dentry); + br = au_sbr(dentry->d_sb, btop); + h_file_tmp = NULL; + if (au_fbtop(file) == btop) { + h_file = au_hf_top(file); + if (file->f_mode == h_file->f_mode) + return 0; /* success */ + h_file_tmp = h_file; + get_file(h_file_tmp); + au_lcnt_inc(&br->br_nfiles); + au_set_h_fptr(file, btop, NULL); + } + AuDebugOn(au_fi(file)->fi_hdir); + /* + * it can happen + * file exists on both of rw and ro + * open --> dbtop and fbtop are both 0 + * prepend a branch as rw, "rw" become ro + * remove rw/file + * delete the top branch, "rw" becomes rw again + * --> dbtop is 1, fbtop is still 0 + * write --> fbtop is 0 but dbtop is 1 + */ + /* AuDebugOn(au_fbtop(file) < btop); */ + + h_file = au_h_open(dentry, btop, vfsub_file_flags(file) & ~O_TRUNC, + file, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) { + if (h_file_tmp) { + /* revert */ + au_set_h_fptr(file, btop, h_file_tmp); + h_file_tmp = NULL; + } + goto out; /* todo: close all? */ + } + + err = 0; + au_set_fbtop(file, btop); + au_set_h_fptr(file, btop, h_file); + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + +out: + if (h_file_tmp) { + fput(h_file_tmp); + au_lcnt_dec(&br->br_nfiles); + } + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, + struct dentry *hi_wh) +{ + int err; + aufs_bindex_t btop; + struct au_dinfo *dinfo; + struct dentry *h_dentry; + struct au_hdentry *hdp; + + dinfo = au_di(file->f_path.dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + + btop = dinfo->di_btop; + dinfo->di_btop = btgt; + hdp = au_hdentry(dinfo, btgt); + h_dentry = hdp->hd_dentry; + hdp->hd_dentry = hi_wh; + err = au_reopen_nondir(file); + hdp->hd_dentry = h_dentry; + dinfo->di_btop = btop; + + return err; +} + +static int au_ready_to_write_wh(struct file *file, loff_t len, + aufs_bindex_t bcpup, struct au_pin *pin) +{ + int err; + struct inode *inode, *h_inode; + struct dentry *h_dentry, *hi_wh; + struct au_cp_generic cpg = { + .dentry = file->f_path.dentry, + .bdst = bcpup, + .bsrc = -1, + .len = len, + .pin = pin + }; + + au_update_dbtop(cpg.dentry); + inode = d_inode(cpg.dentry); + h_inode = NULL; + if (au_dbtop(cpg.dentry) <= bcpup + && au_dbbot(cpg.dentry) >= bcpup) { + h_dentry = au_h_dptr(cpg.dentry, bcpup); + if (h_dentry && d_is_positive(h_dentry)) + h_inode = d_inode(h_dentry); + } + hi_wh = au_hi_wh(inode, bcpup); + if (!hi_wh && !h_inode) + err = au_sio_cpup_wh(&cpg, file); + else + /* already copied-up after unlink */ + err = au_reopen_wh(file, bcpup, hi_wh); + + if (!err + && (inode->i_nlink > 1 + || (inode->i_state & I_LINKABLE)) + && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) + au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); + + return err; +} + +/* + * prepare the @file for writing. + */ +int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) +{ + int err; + aufs_bindex_t dbtop; + struct dentry *parent; + struct inode *inode; + struct super_block *sb; + struct file *h_file; + struct au_cp_generic cpg = { + .dentry = file->f_path.dentry, + .bdst = -1, + .bsrc = -1, + .len = len, + .pin = pin, + .flags = AuCpup_DTIME + }; + + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); + cpg.bsrc = au_fbtop(file); + err = au_test_ro(sb, cpg.bsrc, inode); + if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { + err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, + /*flags*/0); + goto out; + } + + /* need to cpup or reopen */ + parent = dget_parent(cpg.dentry); + di_write_lock_parent(parent); + err = AuWbrCopyup(au_sbi(sb), cpg.dentry); + cpg.bdst = err; + if (unlikely(err < 0)) + goto out_dgrade; + err = 0; + + if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { + err = au_cpup_dirs(cpg.dentry, cpg.bdst); + if (unlikely(err)) + goto out_dgrade; + } + + err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_dgrade; + + dbtop = au_dbtop(cpg.dentry); + if (dbtop <= cpg.bdst) + cpg.bsrc = cpg.bdst; + + if (dbtop <= cpg.bdst /* just reopen */ + || !d_unhashed(cpg.dentry) /* copyup and reopen */ + ) { + h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); + if (IS_ERR(h_file)) + err = PTR_ERR(h_file); + else { + di_downgrade_lock(parent, AuLock_IR); + if (dbtop > cpg.bdst) + err = au_sio_cpup_simple(&cpg); + if (!err) + err = au_reopen_nondir(file); + au_h_open_post(cpg.dentry, cpg.bsrc, h_file); + } + } else { /* copyup as wh and reopen */ + /* + * since writable hfsplus branch is not supported, + * h_open_pre/post() are unnecessary. + */ + err = au_ready_to_write_wh(file, len, cpg.bdst, pin); + di_downgrade_lock(parent, AuLock_IR); + } + + if (!err) { + au_pin_set_parent_lflag(pin, /*lflag*/0); + goto out_dput; /* success */ + } + au_unpin(pin); + goto out_unlock; + +out_dgrade: + di_downgrade_lock(parent, AuLock_IR); +out_unlock: + di_read_unlock(parent, AuLock_IR); +out_dput: + dput(parent); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_do_flush(struct file *file, fl_owner_t id, + int (*flush)(struct file *file, fl_owner_t id)) +{ + int err; + struct super_block *sb; + struct inode *inode; + + inode = file_inode(file); + sb = inode->i_sb; + si_noflush_read_lock(sb); + fi_read_lock(file); + ii_read_lock_child(inode); + + err = flush(file, id); + au_cpup_attr_timesizes(inode); + + ii_read_unlock(inode); + fi_read_unlock(file); + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_file_refresh_by_inode(struct file *file, int *need_reopen) +{ + int err; + struct au_pin pin; + struct au_finfo *finfo; + struct dentry *parent, *hi_wh; + struct inode *inode; + struct super_block *sb; + struct au_cp_generic cpg = { + .dentry = file->f_path.dentry, + .bdst = -1, + .bsrc = -1, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME + }; + + FiMustWriteLock(file); + + err = 0; + finfo = au_fi(file); + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); + cpg.bdst = au_ibtop(inode); + if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) + goto out; + + parent = dget_parent(cpg.dentry); + if (au_test_ro(sb, cpg.bdst, inode)) { + di_read_lock_parent(parent, !AuLock_IR); + err = AuWbrCopyup(au_sbi(sb), cpg.dentry); + cpg.bdst = err; + di_read_unlock(parent, !AuLock_IR); + if (unlikely(err < 0)) + goto out_parent; + err = 0; + } + + di_read_lock_parent(parent, AuLock_IR); + hi_wh = au_hi_wh(inode, cpg.bdst); + if (!S_ISDIR(inode->i_mode) + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode) + && !d_unhashed(cpg.dentry) + && cpg.bdst < au_dbtop(cpg.dentry)) { + err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); + if (unlikely(err)) + goto out_unlock; + + /* always superio. */ + err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + } else if (hi_wh) { + /* already copied-up after unlink */ + err = au_reopen_wh(file, cpg.bdst, hi_wh); + *need_reopen = 0; + } + +out_unlock: + di_read_unlock(parent, AuLock_IR); +out_parent: + dput(parent); +out: + return err; +} + +static void au_do_refresh_dir(struct file *file) +{ + aufs_bindex_t bindex, bbot, new_bindex, brid; + struct au_hfile *p, tmp, *q; + struct au_finfo *finfo; + struct super_block *sb; + struct au_fidir *fidir; + + FiMustWriteLock(file); + + sb = file->f_path.dentry->d_sb; + finfo = au_fi(file); + fidir = finfo->fi_hdir; + AuDebugOn(!fidir); + p = fidir->fd_hfile + finfo->fi_btop; + brid = p->hf_br->br_id; + bbot = fidir->fd_bbot; + for (bindex = finfo->fi_btop; bindex <= bbot; bindex++, p++) { + if (!p->hf_file) + continue; + + new_bindex = au_br_index(sb, p->hf_br->br_id); + if (new_bindex == bindex) + continue; + if (new_bindex < 0) { + au_set_h_fptr(file, bindex, NULL); + continue; + } + + /* swap two lower inode, and loop again */ + q = fidir->fd_hfile + new_bindex; + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hf_file) { + bindex--; + p--; + } + } + + p = fidir->fd_hfile; + if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { + bbot = au_sbbot(sb); + for (finfo->fi_btop = 0; finfo->fi_btop <= bbot; + finfo->fi_btop++, p++) + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; + au_hfput(p, /*execed*/0); + } + } else { + bbot = au_br_index(sb, brid); + for (finfo->fi_btop = 0; finfo->fi_btop < bbot; + finfo->fi_btop++, p++) + if (p->hf_file) + au_hfput(p, /*execed*/0); + bbot = au_sbbot(sb); + } + + p = fidir->fd_hfile + bbot; + for (fidir->fd_bbot = bbot; fidir->fd_bbot >= finfo->fi_btop; + fidir->fd_bbot--, p--) + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; + au_hfput(p, /*execed*/0); + } + AuDebugOn(fidir->fd_bbot < finfo->fi_btop); +} + +/* + * after branch manipulating, refresh the file. + */ +static int refresh_file(struct file *file, int (*reopen)(struct file *file)) +{ + int err, need_reopen, nbr; + aufs_bindex_t bbot, bindex; + struct dentry *dentry; + struct super_block *sb; + struct au_finfo *finfo; + struct au_hfile *hfile; + + dentry = file->f_path.dentry; + sb = dentry->d_sb; + nbr = au_sbbot(sb) + 1; + finfo = au_fi(file); + if (!finfo->fi_hdir) { + hfile = &finfo->fi_htop; + AuDebugOn(!hfile->hf_file); + bindex = au_br_index(sb, hfile->hf_br->br_id); + AuDebugOn(bindex < 0); + if (bindex != finfo->fi_btop) + au_set_fbtop(file, bindex); + } else { + err = au_fidir_realloc(finfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + au_do_refresh_dir(file); + } + + err = 0; + need_reopen = 1; + if (!au_test_mmapped(file)) + err = au_file_refresh_by_inode(file, &need_reopen); + if (finfo->fi_hdir) + /* harmless if err */ + au_fidir_realloc(finfo, nbr, /*may_shrink*/1); + if (!err && need_reopen && !d_unlinked(dentry)) + err = reopen(file); + if (!err) { + au_update_figen(file); + goto out; /* success */ + } + + /* error, close all lower files */ + if (finfo->fi_hdir) { + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); bindex <= bbot; bindex++) + au_set_h_fptr(file, bindex, NULL); + } + +out: + return err; +} + +/* common function to regular file and dir */ +int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), + int wlock, unsigned int fi_lsc) +{ + int err; + unsigned int sigen, figen; + aufs_bindex_t btop; + unsigned char pseudo_link; + struct dentry *dentry; + struct inode *inode; + + err = 0; + dentry = file->f_path.dentry; + inode = d_inode(dentry); + sigen = au_sigen(dentry->d_sb); + fi_write_lock_nested(file, fi_lsc); + figen = au_figen(file); + if (!fi_lsc) + di_write_lock_child(dentry); + else + di_write_lock_child2(dentry); + btop = au_dbtop(dentry); + pseudo_link = (btop != au_ibtop(inode)); + if (sigen == figen && !pseudo_link && au_fbtop(file) == btop) { + if (!wlock) { + di_downgrade_lock(dentry, AuLock_IR); + fi_downgrade_lock(file); + } + goto out; /* success */ + } + + AuDbg("sigen %d, figen %d\n", sigen, figen); + if (au_digen_test(dentry, sigen)) { + err = au_reval_dpath(dentry, sigen); + AuDebugOn(!err && au_digen_test(dentry, sigen)); + } + + if (!err) + err = refresh_file(file, reopen); + if (!err) { + if (!wlock) { + di_downgrade_lock(dentry, AuLock_IR); + fi_downgrade_lock(file); + } + } else { + di_write_unlock(dentry); + fi_write_unlock(file); + } + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* cf. aufs_nopage() */ +/* for madvise(2) */ +static int aufs_readpage(struct file *file __maybe_unused, struct page *page) +{ + unlock_page(page); + return 0; +} + +/* it will never be called, but necessary to support O_DIRECT */ +static ssize_t aufs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +{ BUG(); return 0; } + +/* they will never be called. */ +#ifdef CONFIG_AUFS_DEBUG +static int aufs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ AuUnsupport(); return 0; } +static int aufs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ AuUnsupport(); return 0; } +static int aufs_writepage(struct page *page, struct writeback_control *wbc) +{ AuUnsupport(); return 0; } + +static int aufs_set_page_dirty(struct page *page) +{ AuUnsupport(); return 0; } +static void aufs_invalidatepage(struct page *page, unsigned int offset, + unsigned int length) +{ AuUnsupport(); } +static int aufs_releasepage(struct page *page, gfp_t gfp) +{ AuUnsupport(); return 0; } +#if 0 /* called by memory compaction regardless file */ +static int aufs_migratepage(struct address_space *mapping, struct page *newpage, + struct page *page, enum migrate_mode mode) +{ AuUnsupport(); return 0; } +#endif +static bool aufs_isolate_page(struct page *page, isolate_mode_t mode) +{ AuUnsupport(); return true; } +static void aufs_putback_page(struct page *page) +{ AuUnsupport(); } +static int aufs_launder_page(struct page *page) +{ AuUnsupport(); return 0; } +static int aufs_is_partially_uptodate(struct page *page, + unsigned long from, + unsigned long count) +{ AuUnsupport(); return 0; } +static void aufs_is_dirty_writeback(struct page *page, bool *dirty, + bool *writeback) +{ AuUnsupport(); } +static int aufs_error_remove_page(struct address_space *mapping, + struct page *page) +{ AuUnsupport(); return 0; } +static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, + sector_t *span) +{ AuUnsupport(); return 0; } +static void aufs_swap_deactivate(struct file *file) +{ AuUnsupport(); } +#endif /* CONFIG_AUFS_DEBUG */ + +const struct address_space_operations aufs_aop = { + .readpage = aufs_readpage, + .direct_IO = aufs_direct_IO, +#ifdef CONFIG_AUFS_DEBUG + .writepage = aufs_writepage, + /* no writepages, because of writepage */ + .set_page_dirty = aufs_set_page_dirty, + /* no readpages, because of readpage */ + .write_begin = aufs_write_begin, + .write_end = aufs_write_end, + /* no bmap, no block device */ + .invalidatepage = aufs_invalidatepage, + .releasepage = aufs_releasepage, + /* is fallback_migrate_page ok? */ + /* .migratepage = aufs_migratepage, */ + .isolate_page = aufs_isolate_page, + .putback_page = aufs_putback_page, + .launder_page = aufs_launder_page, + .is_partially_uptodate = aufs_is_partially_uptodate, + .is_dirty_writeback = aufs_is_dirty_writeback, + .error_remove_page = aufs_error_remove_page, + .swap_activate = aufs_swap_activate, + .swap_deactivate = aufs_swap_deactivate +#endif /* CONFIG_AUFS_DEBUG */ +}; diff -Naur --no-dereference a/fs/aufs/file.h b/fs/aufs/file.h --- a/fs/aufs/file.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/file.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * file operations + */ + +#ifndef __AUFS_FILE_H__ +#define __AUFS_FILE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "rwsem.h" + +struct au_branch; +struct au_hfile { + struct file *hf_file; + struct au_branch *hf_br; +}; + +struct au_vdir; +struct au_fidir { + aufs_bindex_t fd_bbot; + aufs_bindex_t fd_nent; + struct au_vdir *fd_vdir_cache; + struct au_hfile fd_hfile[]; +}; + +static inline int au_fidir_sz(int nent) +{ + AuDebugOn(nent < 0); + return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; +} + +struct au_finfo { + atomic_t fi_generation; + + struct au_rwsem fi_rwsem; + aufs_bindex_t fi_btop; + + /* do not union them */ + struct { /* for non-dir */ + struct au_hfile fi_htop; + atomic_t fi_mmapped; + }; + struct au_fidir *fi_hdir; /* for dir only */ + + struct hlist_bl_node fi_hlist; + struct file *fi_file; /* very ugly */ + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + +/* file.c */ +extern const struct address_space_operations aufs_aop; +unsigned int au_file_roflags(unsigned int flags); +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file, int force_wr); +struct au_do_open_args { + int aopen; + int (*open)(struct file *file, int flags, + struct file *h_file); + struct au_fidir *fidir; + struct file *h_file; +}; +int au_do_open(struct file *file, struct au_do_open_args *args); +int au_reopen_nondir(struct file *file); +struct au_pin; +int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); +int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), + int wlock, unsigned int fi_lsc); +int au_do_flush(struct file *file, fl_owner_t id, + int (*flush)(struct file *file, fl_owner_t id)); + +/* poll.c */ +#ifdef CONFIG_AUFS_POLL +__poll_t aufs_poll(struct file *file, struct poll_table_struct *pt); +#endif + +#ifdef CONFIG_AUFS_BR_HFSPLUS +/* hfsplus.c */ +struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, + int force_wr); +void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file); +#else +AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, + aufs_bindex_t bindex, int force_wr) +AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file); +#endif + +/* f_op.c */ +extern const struct file_operations aufs_file_fop; +int au_do_open_nondir(struct file *file, int flags, struct file *h_file); +int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); +struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc); + +/* finfo.c */ +void au_hfput(struct au_hfile *hf, int execed); +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, + struct file *h_file); + +void au_update_figen(struct file *file); +struct au_fidir *au_fidir_alloc(struct super_block *sb); +int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink); + +void au_fi_init_once(void *_fi); +void au_finfo_fin(struct file *file); +int au_finfo_init(struct file *file, struct au_fidir *fidir); + +/* ioctl.c */ +long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, + unsigned long arg); +long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, + unsigned long arg); +#endif + +/* ---------------------------------------------------------------------- */ + +static inline struct au_finfo *au_fi(struct file *file) +{ + return file->private_data; +} + +/* ---------------------------------------------------------------------- */ + +#define fi_read_lock(f) au_rw_read_lock(&au_fi(f)->fi_rwsem) +#define fi_write_lock(f) au_rw_write_lock(&au_fi(f)->fi_rwsem) +#define fi_read_trylock(f) au_rw_read_trylock(&au_fi(f)->fi_rwsem) +#define fi_write_trylock(f) au_rw_write_trylock(&au_fi(f)->fi_rwsem) +/* +#define fi_read_trylock_nested(f) \ + au_rw_read_trylock_nested(&au_fi(f)->fi_rwsem) +#define fi_write_trylock_nested(f) \ + au_rw_write_trylock_nested(&au_fi(f)->fi_rwsem) +*/ + +#define fi_read_unlock(f) au_rw_read_unlock(&au_fi(f)->fi_rwsem) +#define fi_write_unlock(f) au_rw_write_unlock(&au_fi(f)->fi_rwsem) +#define fi_downgrade_lock(f) au_rw_dgrade_lock(&au_fi(f)->fi_rwsem) + +/* lock subclass for finfo */ +enum { + AuLsc_FI_1, + AuLsc_FI_2 +}; + +static inline void fi_read_lock_nested(struct file *f, unsigned int lsc) +{ + au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc); +} + +static inline void fi_write_lock_nested(struct file *f, unsigned int lsc) +{ + au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc); +} + +/* + * fi_read_lock_1, fi_write_lock_1, + * fi_read_lock_2, fi_write_lock_2 + */ +#define AuReadLockFunc(name) \ +static inline void fi_read_lock_##name(struct file *f) \ +{ fi_read_lock_nested(f, AuLsc_FI_##name); } + +#define AuWriteLockFunc(name) \ +static inline void fi_write_lock_##name(struct file *f) \ +{ fi_write_lock_nested(f, AuLsc_FI_##name); } + +#define AuRWLockFuncs(name) \ + AuReadLockFunc(name) \ + AuWriteLockFunc(name) + +AuRWLockFuncs(1); +AuRWLockFuncs(2); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) +#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) +#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) + +/* ---------------------------------------------------------------------- */ + +/* todo: hard/soft set? */ +static inline aufs_bindex_t au_fbtop(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_btop; +} + +static inline aufs_bindex_t au_fbbot_dir(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + return au_fi(file)->fi_hdir->fd_bbot; +} + +static inline struct au_vdir *au_fvdir_cache(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + return au_fi(file)->fi_hdir->fd_vdir_cache; +} + +static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + au_fi(file)->fi_btop = bindex; +} + +static inline void au_set_fbbot_dir(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + au_fi(file)->fi_hdir->fd_bbot = bindex; +} + +static inline void au_set_fvdir_cache(struct file *file, + struct au_vdir *vdir_cache) +{ + FiMustWriteLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; +} + +static inline struct file *au_hf_top(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(au_fi(file)->fi_hdir); + return au_fi(file)->fi_htop.hf_file; +} + +static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; +} + +/* todo: memory barrier? */ +static inline unsigned int au_figen(struct file *f) +{ + return atomic_read(&au_fi(f)->fi_generation); +} + +static inline void au_set_mmapped(struct file *f) +{ + if (atomic_inc_return(&au_fi(f)->fi_mmapped)) + return; + pr_warn("fi_mmapped wrapped around\n"); + while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) + ; +} + +static inline void au_unset_mmapped(struct file *f) +{ + atomic_dec(&au_fi(f)->fi_mmapped); +} + +static inline int au_test_mmapped(struct file *f) +{ + return atomic_read(&au_fi(f)->fi_mmapped); +} + +/* customize vma->vm_file */ + +static inline void au_do_vm_file_reset(struct vm_area_struct *vma, + struct file *file) +{ + struct file *f; + + f = vma->vm_file; + get_file(file); + vma->vm_file = file; + fput(f); +} + +#ifdef CONFIG_MMU +#define AuDbgVmRegion(file, vma) do {} while (0) + +static inline void au_vm_file_reset(struct vm_area_struct *vma, + struct file *file) +{ + au_do_vm_file_reset(vma, file); +} +#else +#define AuDbgVmRegion(file, vma) \ + AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) + +static inline void au_vm_file_reset(struct vm_area_struct *vma, + struct file *file) +{ + struct file *f; + + au_do_vm_file_reset(vma, file); + f = vma->vm_region->vm_file; + get_file(file); + vma->vm_region->vm_file = file; + fput(f); +} +#endif /* CONFIG_MMU */ + +/* handle vma->vm_prfile */ +static inline void au_vm_prfile_set(struct vm_area_struct *vma, + struct file *file) +{ + get_file(file); + vma->vm_prfile = file; +#ifndef CONFIG_MMU + get_file(file); + vma->vm_region->vm_prfile = file; +#endif +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FILE_H__ */ diff -Naur --no-dereference a/fs/aufs/finfo.c b/fs/aufs/finfo.c --- a/fs/aufs/finfo.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/finfo.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * file private data + */ + +#include "aufs.h" + +void au_hfput(struct au_hfile *hf, int execed) +{ + if (execed) + allow_write_access(hf->hf_file); + fput(hf->hf_file); + hf->hf_file = NULL; + au_lcnt_dec(&hf->hf_br->br_nfiles); + hf->hf_br = NULL; +} + +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) +{ + struct au_finfo *finfo = au_fi(file); + struct au_hfile *hf; + struct au_fidir *fidir; + + fidir = finfo->fi_hdir; + if (!fidir) { + AuDebugOn(finfo->fi_btop != bindex); + hf = &finfo->fi_htop; + } else + hf = fidir->fd_hfile + bindex; + + if (hf && hf->hf_file) + au_hfput(hf, vfsub_file_execed(file)); + if (val) { + FiMustWriteLock(file); + AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); + hf->hf_file = val; + hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex); + } +} + +void au_update_figen(struct file *file) +{ + atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry)); + /* smp_mb(); */ /* atomic_set */ +} + +/* ---------------------------------------------------------------------- */ + +struct au_fidir *au_fidir_alloc(struct super_block *sb) +{ + struct au_fidir *fidir; + int nbr; + + nbr = au_sbbot(sb) + 1; + if (nbr < 2) + nbr = 2; /* initial allocate for 2 branches */ + fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); + if (fidir) { + fidir->fd_bbot = -1; + fidir->fd_nent = nbr; + } + + return fidir; +} + +int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink) +{ + int err; + struct au_fidir *fidir, *p; + + AuRwMustWriteLock(&finfo->fi_rwsem); + fidir = finfo->fi_hdir; + AuDebugOn(!fidir); + + err = -ENOMEM; + p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), + GFP_NOFS, may_shrink); + if (p) { + p->fd_nent = nbr; + finfo->fi_hdir = p; + err = 0; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_finfo_fin(struct file *file) +{ + struct au_finfo *finfo; + + au_lcnt_dec(&au_sbi(file->f_path.dentry->d_sb)->si_nfiles); + + finfo = au_fi(file); + AuDebugOn(finfo->fi_hdir); + AuRwDestroy(&finfo->fi_rwsem); + au_cache_free_finfo(finfo); +} + +void au_fi_init_once(void *_finfo) +{ + struct au_finfo *finfo = _finfo; + + au_rw_init(&finfo->fi_rwsem); +} + +int au_finfo_init(struct file *file, struct au_fidir *fidir) +{ + int err; + struct au_finfo *finfo; + struct dentry *dentry; + + err = -ENOMEM; + dentry = file->f_path.dentry; + finfo = au_cache_alloc_finfo(); + if (unlikely(!finfo)) + goto out; + + err = 0; + au_lcnt_inc(&au_sbi(dentry->d_sb)->si_nfiles); + au_rw_write_lock(&finfo->fi_rwsem); + finfo->fi_btop = -1; + finfo->fi_hdir = fidir; + atomic_set(&finfo->fi_generation, au_digen(dentry)); + /* smp_mb(); */ /* atomic_set */ + + file->private_data = finfo; + +out: + return err; +} diff -Naur --no-dereference a/fs/aufs/f_op.c b/fs/aufs/f_op.c --- a/fs/aufs/f_op.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/f_op.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,771 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * file and vm operations + */ + +#include +#include +#include +#include +#include "aufs.h" + +int au_do_open_nondir(struct file *file, int flags, struct file *h_file) +{ + int err; + aufs_bindex_t bindex; + struct dentry *dentry, *h_dentry; + struct au_finfo *finfo; + struct inode *h_inode; + + FiMustWriteLock(file); + + err = 0; + dentry = file->f_path.dentry; + AuDebugOn(IS_ERR_OR_NULL(dentry)); + finfo = au_fi(file); + memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); + atomic_set(&finfo->fi_mmapped, 0); + bindex = au_dbtop(dentry); + if (!h_file) { + h_dentry = au_h_dptr(dentry, bindex); + err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); + if (unlikely(err)) + goto out; + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + if (IS_ERR(h_file)) { + err = PTR_ERR(h_file); + goto out; + } + } else { + h_dentry = h_file->f_path.dentry; + err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); + if (unlikely(err)) + goto out; + /* br ref is already inc-ed */ + } + + if ((flags & __O_TMPFILE) + && !(flags & O_EXCL)) { + h_inode = file_inode(h_file); + spin_lock(&h_inode->i_lock); + h_inode->i_state |= I_LINKABLE; + spin_unlock(&h_inode->i_lock); + } + au_set_fbtop(file, bindex); + au_set_h_fptr(file, bindex, h_file); + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + +out: + return err; +} + +static int aufs_open_nondir(struct inode *inode __maybe_unused, + struct file *file) +{ + int err; + struct super_block *sb; + struct au_do_open_args args = { + .open = au_do_open_nondir + }; + + AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", + file, vfsub_file_flags(file), file->f_mode); + + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_do_open(file, &args); + si_read_unlock(sb); + return err; +} + +int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) +{ + struct au_finfo *finfo; + aufs_bindex_t bindex; + + finfo = au_fi(file); + au_hbl_del(&finfo->fi_hlist, + &au_sbi(file->f_path.dentry->d_sb)->si_files); + bindex = finfo->fi_btop; + if (bindex >= 0) + au_set_h_fptr(file, bindex, NULL); + + au_finfo_fin(file); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_flush_nondir(struct file *file, fl_owner_t id) +{ + int err; + struct file *h_file; + + err = 0; + h_file = au_hf_top(file); + if (h_file) + err = vfsub_flush(h_file, id); + return err; +} + +static int aufs_flush_nondir(struct file *file, fl_owner_t id) +{ + return au_do_flush(file, id, au_do_flush_nondir); +} + +/* ---------------------------------------------------------------------- */ +/* + * read and write functions acquire [fdi]_rwsem once, but release before + * mmap_sem. This is because to stop a race condition between mmap(2). + * Releasing these aufs-rwsem should be safe, no branch-management (by keeping + * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in + * read functions after [fdi]_rwsem are released, but it should be harmless. + */ + +/* Callers should call au_read_post() or fput() in the end */ +struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc) +{ + struct file *h_file; + int err; + + err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0, lsc); + if (!err) { + di_read_unlock(file->f_path.dentry, AuLock_IR); + h_file = au_hf_top(file); + get_file(h_file); + if (!keep_fi) + fi_read_unlock(file); + } else + h_file = ERR_PTR(err); + + return h_file; +} + +static void au_read_post(struct inode *inode, struct file *h_file) +{ + /* update without lock, I don't think it a problem */ + fsstack_copy_attr_atime(inode, file_inode(h_file)); + fput(h_file); +} + +struct au_write_pre { + /* input */ + unsigned int lsc; + + /* output */ + blkcnt_t blks; + aufs_bindex_t btop; +}; + +/* + * return with iinfo is write-locked + * callers should call au_write_post() or iinfo_write_unlock() + fput() in the + * end + */ +static struct file *au_write_pre(struct file *file, int do_ready, + struct au_write_pre *wpre) +{ + struct file *h_file; + struct dentry *dentry; + int err; + unsigned int lsc; + struct au_pin pin; + + lsc = 0; + if (wpre) + lsc = wpre->lsc; + err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, lsc); + h_file = ERR_PTR(err); + if (unlikely(err)) + goto out; + + dentry = file->f_path.dentry; + if (do_ready) { + err = au_ready_to_write(file, -1, &pin); + if (unlikely(err)) { + h_file = ERR_PTR(err); + di_write_unlock(dentry); + goto out_fi; + } + } + + di_downgrade_lock(dentry, /*flags*/0); + if (wpre) + wpre->btop = au_fbtop(file); + h_file = au_hf_top(file); + get_file(h_file); + if (wpre) + wpre->blks = file_inode(h_file)->i_blocks; + if (do_ready) + au_unpin(&pin); + di_read_unlock(dentry, /*flags*/0); + +out_fi: + fi_write_unlock(file); +out: + return h_file; +} + +static void au_write_post(struct inode *inode, struct file *h_file, + struct au_write_pre *wpre, ssize_t written) +{ + struct inode *h_inode; + + au_cpup_attr_timesizes(inode); + AuDebugOn(au_ibtop(inode) != wpre->btop); + h_inode = file_inode(h_file); + inode->i_mode = h_inode->i_mode; + ii_write_unlock(inode); + /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ + if (written > 0) + au_fhsm_wrote(inode->i_sb, wpre->btop, + /*force*/h_inode->i_blocks > wpre->blks); + fput(h_file); +} + +/* + * todo: very ugly + * it locks both of i_mutex and si_rwsem for read in safe. + * if the plink maintenance mode continues forever (that is the problem), + * may loop forever. + */ +static void au_mtx_and_read_lock(struct inode *inode) +{ + int err; + struct super_block *sb = inode->i_sb; + + while (1) { + inode_lock(inode); + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (!err) + break; + inode_unlock(inode); + si_read_lock(sb, AuLock_NOPLMW); + si_read_unlock(sb); + } +} + +static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, + struct iov_iter *iov_iter) +{ + ssize_t err; + struct file *file; + ssize_t (*iter)(struct kiocb *, struct iov_iter *); + + err = security_file_permission(h_file, rw); + if (unlikely(err)) + goto out; + + err = -ENOSYS; /* the branch doesn't have its ->(read|write)_iter() */ + iter = NULL; + if (rw == MAY_READ) + iter = h_file->f_op->read_iter; + else if (rw == MAY_WRITE) + iter = h_file->f_op->write_iter; + + file = kio->ki_filp; + kio->ki_filp = h_file; + if (iter) { + lockdep_off(); + err = iter(kio, iov_iter); + lockdep_on(); + } else + /* currently there is no such fs */ + WARN_ON_ONCE(1); + kio->ki_filp = file; + +out: + return err; +} + +static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) +{ + ssize_t err; + struct file *file, *h_file; + struct inode *inode; + struct super_block *sb; + + file = kio->ki_filp; + inode = file_inode(file); + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/1, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + if (au_test_loopback_kthread()) { + au_warn_loopback(h_file->f_path.dentry->d_sb); + if (file->f_mapping != h_file->f_mapping) { + file->f_mapping = h_file->f_mapping; + smp_mb(); /* unnecessary? */ + } + } + fi_read_unlock(file); + + err = au_do_iter(h_file, MAY_READ, kio, iov_iter); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + au_read_post(inode, h_file); + +out: + si_read_unlock(sb); + return err; +} + +static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) +{ + ssize_t err; + struct au_write_pre wpre; + struct inode *inode; + struct file *file, *h_file; + + file = kio->ki_filp; + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); + au_write_post(inode, h_file, &wpre, err); + +out: + si_read_unlock(inode->i_sb); + inode_unlock(inode); + return err; +} + +/* + * We may be able to remove aufs_splice_{read,write}() since almost all FSes + * don't have their own .splice_{read,write} implimentations, and they use + * generic_file_splice_read() and iter_file_splice_write() who can act like the + * simple converters to f_op->iter_read() and ->iter_write(). + * But we keep our own implementations because some non-mainlined FSes may have + * their own .splice_{read,write} implimentations and aufs doesn't want to take + * away an opportunity to co-work with aufs from them. + */ +static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + ssize_t err; + struct file *h_file; + struct inode *inode; + struct super_block *sb; + + inode = file_inode(file); + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = vfsub_splice_to(h_file, ppos, pipe, len, flags); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + au_read_post(inode, h_file); + +out: + si_read_unlock(sb); + return err; +} + +static ssize_t +aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, + size_t len, unsigned int flags) +{ + ssize_t err; + struct au_write_pre wpre; + struct inode *inode; + struct file *h_file; + + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = vfsub_splice_from(pipe, h_file, ppos, len, flags); + au_write_post(inode, h_file, &wpre, err); + +out: + si_read_unlock(inode->i_sb); + inode_unlock(inode); + return err; +} + +static long aufs_fallocate(struct file *file, int mode, loff_t offset, + loff_t len) +{ + long err; + struct au_write_pre wpre; + struct inode *inode; + struct file *h_file; + + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + lockdep_off(); + err = vfs_fallocate(h_file, mode, offset, len); + lockdep_on(); + au_write_post(inode, h_file, &wpre, /*written*/1); + +out: + si_read_unlock(inode->i_sb); + inode_unlock(inode); + return err; +} + +static ssize_t aufs_copy_file_range(struct file *src, loff_t src_pos, + struct file *dst, loff_t dst_pos, + size_t len, unsigned int flags) +{ + ssize_t err; + struct au_write_pre wpre; + enum { SRC, DST }; + struct { + struct inode *inode; + struct file *h_file; + struct super_block *h_sb; + } a[2]; +#define a_src a[SRC] +#define a_dst a[DST] + + err = -EINVAL; + a_src.inode = file_inode(src); + if (unlikely(!S_ISREG(a_src.inode->i_mode))) + goto out; + a_dst.inode = file_inode(dst); + if (unlikely(!S_ISREG(a_dst.inode->i_mode))) + goto out; + + au_mtx_and_read_lock(a_dst.inode); + /* + * in order to match the order in di_write_lock2_{child,parent}(), + * use f_path.dentry for this comparison. + */ + if (src->f_path.dentry < dst->f_path.dentry) { + a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1); + err = PTR_ERR(a_src.h_file); + if (IS_ERR(a_src.h_file)) + goto out_si; + + wpre.lsc = AuLsc_FI_2; + a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre); + err = PTR_ERR(a_dst.h_file); + if (IS_ERR(a_dst.h_file)) { + au_read_post(a_src.inode, a_src.h_file); + goto out_si; + } + } else { + wpre.lsc = AuLsc_FI_1; + a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre); + err = PTR_ERR(a_dst.h_file); + if (IS_ERR(a_dst.h_file)) + goto out_si; + + a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_2); + err = PTR_ERR(a_src.h_file); + if (IS_ERR(a_src.h_file)) { + au_write_post(a_dst.inode, a_dst.h_file, &wpre, + /*written*/0); + goto out_si; + } + } + + err = -EXDEV; + a_src.h_sb = file_inode(a_src.h_file)->i_sb; + a_dst.h_sb = file_inode(a_dst.h_file)->i_sb; + if (unlikely(a_src.h_sb != a_dst.h_sb)) { + AuDbgFile(src); + AuDbgFile(dst); + goto out_file; + } + + err = vfsub_copy_file_range(a_src.h_file, src_pos, a_dst.h_file, + dst_pos, len, flags); + +out_file: + au_write_post(a_dst.inode, a_dst.h_file, &wpre, err); + fi_read_unlock(src); + au_read_post(a_src.inode, a_src.h_file); +out_si: + si_read_unlock(a_dst.inode->i_sb); + inode_unlock(a_dst.inode); +out: + return err; +#undef a_src +#undef a_dst +} + +/* ---------------------------------------------------------------------- */ + +/* + * The locking order around current->mmap_sem. + * - in most and regular cases + * file I/O syscall -- aufs_read() or something + * -- si_rwsem for read -- mmap_sem + * (Note that [fdi]i_rwsem are released before mmap_sem). + * - in mmap case + * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem + * This AB-BA order is definitely bad, but is not a problem since "si_rwsem for + * read" allows multiple processes to acquire it and [fdi]i_rwsem are not held + * in file I/O. Aufs needs to stop lockdep in aufs_mmap() though. + * It means that when aufs acquires si_rwsem for write, the process should never + * acquire mmap_sem. + * + * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a + * problem either since any directory is not able to be mmap-ed. + * The similar scenario is applied to aufs_readlink() too. + */ + +#if 0 /* stop calling security_file_mmap() */ +/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ +#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) + +static unsigned long au_arch_prot_conv(unsigned long flags) +{ + /* currently ppc64 only */ +#ifdef CONFIG_PPC64 + /* cf. linux/arch/powerpc/include/asm/mman.h */ + AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); + return AuConv_VM_PROT(flags, SAO); +#else + AuDebugOn(arch_calc_vm_prot_bits(-1)); + return 0; +#endif +} + +static unsigned long au_prot_conv(unsigned long flags) +{ + return AuConv_VM_PROT(flags, READ) + | AuConv_VM_PROT(flags, WRITE) + | AuConv_VM_PROT(flags, EXEC) + | au_arch_prot_conv(flags); +} + +/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ +#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) + +static unsigned long au_flag_conv(unsigned long flags) +{ + return AuConv_VM_MAP(flags, GROWSDOWN) + | AuConv_VM_MAP(flags, DENYWRITE) + | AuConv_VM_MAP(flags, LOCKED); +} +#endif + +static int aufs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err; + const unsigned char wlock + = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); + struct super_block *sb; + struct file *h_file; + struct inode *inode; + + AuDbgVmRegion(file, vma); + + inode = file_inode(file); + sb = inode->i_sb; + lockdep_off(); + si_read_lock(sb, AuLock_NOPLMW); + + h_file = au_write_pre(file, wlock, /*wpre*/NULL); + lockdep_on(); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = 0; + au_set_mmapped(file); + au_vm_file_reset(vma, h_file); + /* + * we cannot call security_mmap_file() here since it may acquire + * mmap_sem or i_mutex. + * + * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), + * au_flag_conv(vma->vm_flags)); + */ + if (!err) + err = call_mmap(h_file, vma); + if (!err) { + au_vm_prfile_set(vma, file); + fsstack_copy_attr_atime(inode, file_inode(h_file)); + goto out_fput; /* success */ + } + au_unset_mmapped(file); + au_vm_file_reset(vma, file); + +out_fput: + lockdep_off(); + ii_write_unlock(inode); + lockdep_on(); + fput(h_file); +out: + lockdep_off(); + si_read_unlock(sb); + lockdep_on(); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, + int datasync) +{ + int err; + struct au_write_pre wpre; + struct inode *inode; + struct file *h_file; + + err = 0; /* -EBADF; */ /* posix? */ + if (unlikely(!(file->f_mode & FMODE_WRITE))) + goto out; + + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out_unlock; + + err = vfsub_fsync(h_file, &h_file->f_path, datasync); + au_write_post(inode, h_file, &wpre, /*written*/0); + +out_unlock: + si_read_unlock(inode->i_sb); + inode_unlock(inode); +out: + return err; +} + +static int aufs_fasync(int fd, struct file *file, int flag) +{ + int err; + struct file *h_file; + struct super_block *sb; + + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + if (h_file->f_op->fasync) + err = h_file->f_op->fasync(fd, h_file, flag); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); + return err; +} + +static int aufs_setfl(struct file *file, unsigned long arg) +{ + int err; + struct file *h_file; + struct super_block *sb; + + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + /* stop calling h_file->fasync */ + arg |= vfsub_file_flags(file) & FASYNC; + err = setfl(/*unused fd*/-1, h_file, arg); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* no one supports this operation, currently */ +#if 0 /* reserved for future use */ +static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, + size_t len, loff_t *pos, int more) +{ +} +#endif + +/* ---------------------------------------------------------------------- */ + +const struct file_operations aufs_file_fop = { + .owner = THIS_MODULE, + + .llseek = default_llseek, + + .read_iter = aufs_read_iter, + .write_iter = aufs_write_iter, + +#ifdef CONFIG_AUFS_POLL + .poll = aufs_poll, +#endif + .unlocked_ioctl = aufs_ioctl_nondir, +#ifdef CONFIG_COMPAT + .compat_ioctl = aufs_compat_ioctl_nondir, +#endif + .mmap = aufs_mmap, + .open = aufs_open_nondir, + .flush = aufs_flush_nondir, + .release = aufs_release_nondir, + .fsync = aufs_fsync_nondir, + .fasync = aufs_fasync, + /* .sendpage = aufs_sendpage, */ + .setfl = aufs_setfl, + .splice_write = aufs_splice_write, + .splice_read = aufs_splice_read, +#if 0 /* reserved for future use */ + .aio_splice_write = aufs_aio_splice_write, + .aio_splice_read = aufs_aio_splice_read, +#endif + .fallocate = aufs_fallocate, + .copy_file_range = aufs_copy_file_range +}; diff -Naur --no-dereference a/fs/aufs/fstype.h b/fs/aufs/fstype.h --- a/fs/aufs/fstype.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/fstype.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * judging filesystem type + */ + +#ifndef __AUFS_FSTYPE_H__ +#define __AUFS_FSTYPE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +static inline int au_test_aufs(struct super_block *sb) +{ + return sb->s_magic == AUFS_SUPER_MAGIC; +} + +static inline const char *au_sbtype(struct super_block *sb) +{ + return sb->s_type->name; +} + +static inline int au_test_iso9660(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_ISO9660_FS) + return sb->s_magic == ISOFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_romfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_ROMFS_FS) + return sb->s_magic == ROMFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_cramfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_CRAMFS) + return sb->s_magic == CRAMFS_MAGIC; +#endif + return 0; +} + +static inline int au_test_nfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_NFS_FS) + return sb->s_magic == NFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_fuse(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_FUSE_FS) + return sb->s_magic == FUSE_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_xfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_XFS_FS) + return sb->s_magic == XFS_SB_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_TMPFS + return sb->s_magic == TMPFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_ECRYPT_FS) + return !strcmp(au_sbtype(sb), "ecryptfs"); +#else + return 0; +#endif +} + +static inline int au_test_ramfs(struct super_block *sb) +{ + return sb->s_magic == RAMFS_MAGIC; +} + +static inline int au_test_ubifs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_UBIFS_FS) + return sb->s_magic == UBIFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_procfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_PROC_FS + return sb->s_magic == PROC_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_sysfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_SYSFS + return sb->s_magic == SYSFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_configfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_CONFIGFS_FS) + return sb->s_magic == CONFIGFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_minix(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_MINIX_FS) + return sb->s_magic == MINIX3_SUPER_MAGIC + || sb->s_magic == MINIX2_SUPER_MAGIC + || sb->s_magic == MINIX2_SUPER_MAGIC2 + || sb->s_magic == MINIX_SUPER_MAGIC + || sb->s_magic == MINIX_SUPER_MAGIC2; +#else + return 0; +#endif +} + +static inline int au_test_fat(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_FAT_FS) + return sb->s_magic == MSDOS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_msdos(struct super_block *sb) +{ + return au_test_fat(sb); +} + +static inline int au_test_vfat(struct super_block *sb) +{ + return au_test_fat(sb); +} + +static inline int au_test_securityfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_SECURITYFS + return sb->s_magic == SECURITYFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_squashfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_SQUASHFS) + return sb->s_magic == SQUASHFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_btrfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_BTRFS_FS) + return sb->s_magic == BTRFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_xenfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_XENFS) + return sb->s_magic == XENFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_debugfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_DEBUG_FS + return sb->s_magic == DEBUGFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_nilfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_NILFS) + return sb->s_magic == NILFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_HFSPLUS_FS) + return sb->s_magic == HFSPLUS_SUPER_MAGIC; +#else + return 0; +#endif +} + +/* ---------------------------------------------------------------------- */ +/* + * they can't be an aufs branch. + */ +static inline int au_test_fs_unsuppoted(struct super_block *sb) +{ + return +#ifndef CONFIG_AUFS_BR_RAMFS + au_test_ramfs(sb) || +#endif + au_test_procfs(sb) + || au_test_sysfs(sb) + || au_test_configfs(sb) + || au_test_debugfs(sb) + || au_test_securityfs(sb) + || au_test_xenfs(sb) + || au_test_ecryptfs(sb) + /* || !strcmp(au_sbtype(sb), "unionfs") */ + || au_test_aufs(sb); /* will be supported in next version */ +} + +static inline int au_test_fs_remote(struct super_block *sb) +{ + return !au_test_tmpfs(sb) +#ifdef CONFIG_AUFS_BR_RAMFS + && !au_test_ramfs(sb) +#endif + && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); +} + +/* ---------------------------------------------------------------------- */ + +/* + * Note: these functions (below) are created after reading ->getattr() in all + * filesystems under linux/fs. it means we have to do so in every update... + */ + +/* + * some filesystems require getattr to refresh the inode attributes before + * referencing. + * in most cases, we can rely on the inode attribute in NFS (or every remote fs) + * and leave the work for d_revalidate() + */ +static inline int au_test_fs_refresh_iattr(struct super_block *sb) +{ + return au_test_nfs(sb) + || au_test_fuse(sb) + /* || au_test_btrfs(sb) */ /* untested */ + ; +} + +/* + * filesystems which don't maintain i_size or i_blocks. + */ +static inline int au_test_fs_bad_iattr_size(struct super_block *sb) +{ + return au_test_xfs(sb) + || au_test_btrfs(sb) + || au_test_ubifs(sb) + || au_test_hfsplus(sb) /* maintained, but incorrect */ + /* || au_test_minix(sb) */ /* untested */ + ; +} + +/* + * filesystems which don't store the correct value in some of their inode + * attributes. + */ +static inline int au_test_fs_bad_iattr(struct super_block *sb) +{ + return au_test_fs_bad_iattr_size(sb) + || au_test_fat(sb) + || au_test_msdos(sb) + || au_test_vfat(sb); +} + +/* they don't check i_nlink in link(2) */ +static inline int au_test_fs_no_limit_nlink(struct super_block *sb) +{ + return au_test_tmpfs(sb) +#ifdef CONFIG_AUFS_BR_RAMFS + || au_test_ramfs(sb) +#endif + || au_test_ubifs(sb) + || au_test_hfsplus(sb); +} + +/* + * filesystems which sets S_NOATIME and S_NOCMTIME. + */ +static inline int au_test_fs_notime(struct super_block *sb) +{ + return au_test_nfs(sb) + || au_test_fuse(sb) + || au_test_ubifs(sb) + ; +} + +/* temporary support for i#1 in cramfs */ +static inline int au_test_fs_unique_ino(struct inode *inode) +{ + if (au_test_cramfs(inode->i_sb)) + return inode->i_ino != 1; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +/* + * the filesystem where the xino files placed must support i/o after unlink and + * maintain i_size and i_blocks. + */ +static inline int au_test_fs_bad_xino(struct super_block *sb) +{ + return au_test_fs_remote(sb) + || au_test_fs_bad_iattr_size(sb) + /* don't want unnecessary work for xino */ + || au_test_aufs(sb) + || au_test_ecryptfs(sb) + || au_test_nilfs(sb); +} + +static inline int au_test_fs_trunc_xino(struct super_block *sb) +{ + return au_test_tmpfs(sb) + || au_test_ramfs(sb); +} + +/* + * test if the @sb is real-readonly. + */ +static inline int au_test_fs_rr(struct super_block *sb) +{ + return au_test_squashfs(sb) + || au_test_iso9660(sb) + || au_test_cramfs(sb) + || au_test_romfs(sb); +} + +/* + * test if the @inode is nfs with 'noacl' option + * NFS always sets SB_POSIXACL regardless its mount option 'noacl.' + */ +static inline int au_test_nfs_noacl(struct inode *inode) +{ + return au_test_nfs(inode->i_sb) + /* && IS_POSIXACL(inode) */ + && !nfs_server_capable(inode, NFS_CAP_ACLS); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FSTYPE_H__ */ diff -Naur --no-dereference a/fs/aufs/hbl.h b/fs/aufs/hbl.h --- a/fs/aufs/hbl.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/hbl.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * helpers for hlist_bl.h + */ + +#ifndef __AUFS_HBL_H__ +#define __AUFS_HBL_H__ + +#ifdef __KERNEL__ + +#include + +static inline void au_hbl_add(struct hlist_bl_node *node, + struct hlist_bl_head *hbl) +{ + hlist_bl_lock(hbl); + hlist_bl_add_head(node, hbl); + hlist_bl_unlock(hbl); +} + +static inline void au_hbl_del(struct hlist_bl_node *node, + struct hlist_bl_head *hbl) +{ + hlist_bl_lock(hbl); + hlist_bl_del(node); + hlist_bl_unlock(hbl); +} + +#define au_hbl_for_each(pos, head) \ + for (pos = hlist_bl_first(head); \ + pos; \ + pos = pos->next) + +static inline unsigned long au_hbl_count(struct hlist_bl_head *hbl) +{ + unsigned long cnt; + struct hlist_bl_node *pos; + + cnt = 0; + hlist_bl_lock(hbl); + au_hbl_for_each(pos, hbl) + cnt++; + hlist_bl_unlock(hbl); + return cnt; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_HBL_H__ */ diff -Naur --no-dereference a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c --- a/fs/aufs/hfsnotify.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/hfsnotify.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * fsnotify for the lower directories + */ + +#include "aufs.h" + +/* FS_IN_IGNORED is unnecessary */ +static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE + | FS_CREATE | FS_EVENT_ON_CHILD); +static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); +static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); + +static void au_hfsn_free_mark(struct fsnotify_mark *mark) +{ + struct au_hnotify *hn = container_of(mark, struct au_hnotify, + hn_mark); + /* AuDbg("here\n"); */ + au_cache_free_hnotify(hn); + smp_mb__before_atomic(); /* for atomic64_dec */ + if (atomic64_dec_and_test(&au_hfsn_ifree)) + wake_up(&au_hfsn_wq); +} + +static int au_hfsn_alloc(struct au_hinode *hinode) +{ + int err; + struct au_hnotify *hn; + struct super_block *sb; + struct au_branch *br; + struct fsnotify_mark *mark; + aufs_bindex_t bindex; + + hn = hinode->hi_notify; + sb = hn->hn_aufs_inode->i_sb; + bindex = au_br_index(sb, hinode->hi_id); + br = au_sbr(sb, bindex); + AuDebugOn(!br->br_hfsn); + + mark = &hn->hn_mark; + fsnotify_init_mark(mark, br->br_hfsn->hfsn_group); + mark->mask = AuHfsnMask; + /* + * by udba rename or rmdir, aufs assign a new inode to the known + * h_inode, so specify 1 to allow dups. + */ + lockdep_off(); + err = fsnotify_add_inode_mark(mark, hinode->hi_inode, /*allow_dups*/1); + lockdep_on(); + + return err; +} + +static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) +{ + struct fsnotify_mark *mark; + unsigned long long ull; + struct fsnotify_group *group; + + ull = atomic64_inc_return(&au_hfsn_ifree); + BUG_ON(!ull); + + mark = &hn->hn_mark; + spin_lock(&mark->lock); + group = mark->group; + fsnotify_get_group(group); + spin_unlock(&mark->lock); + lockdep_off(); + fsnotify_destroy_mark(mark, group); + fsnotify_put_mark(mark); + fsnotify_put_group(group); + lockdep_on(); + + /* free hn by myself */ + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) +{ + struct fsnotify_mark *mark; + + mark = &hinode->hi_notify->hn_mark; + spin_lock(&mark->lock); + if (do_set) { + AuDebugOn(mark->mask & AuHfsnMask); + mark->mask |= AuHfsnMask; + } else { + AuDebugOn(!(mark->mask & AuHfsnMask)); + mark->mask &= ~AuHfsnMask; + } + spin_unlock(&mark->lock); + /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ +} + +/* ---------------------------------------------------------------------- */ + +/* #define AuDbgHnotify */ +#ifdef AuDbgHnotify +static char *au_hfsn_name(u32 mask) +{ +#ifdef CONFIG_AUFS_DEBUG +#define test_ret(flag) \ + do { \ + if (mask & flag) \ + return #flag; \ + } while (0) + test_ret(FS_ACCESS); + test_ret(FS_MODIFY); + test_ret(FS_ATTRIB); + test_ret(FS_CLOSE_WRITE); + test_ret(FS_CLOSE_NOWRITE); + test_ret(FS_OPEN); + test_ret(FS_MOVED_FROM); + test_ret(FS_MOVED_TO); + test_ret(FS_CREATE); + test_ret(FS_DELETE); + test_ret(FS_DELETE_SELF); + test_ret(FS_MOVE_SELF); + test_ret(FS_UNMOUNT); + test_ret(FS_Q_OVERFLOW); + test_ret(FS_IN_IGNORED); + test_ret(FS_ISDIR); + test_ret(FS_IN_ONESHOT); + test_ret(FS_EVENT_ON_CHILD); + return ""; +#undef test_ret +#else + return "??"; +#endif +} +#endif + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_free_group(struct fsnotify_group *group) +{ + struct au_br_hfsnotify *hfsn = group->private; + + /* AuDbg("here\n"); */ + au_kfree_try_rcu(hfsn); +} + +static int au_hfsn_handle_event(struct fsnotify_group *group, + u32 mask, const void *data, int data_type, + struct inode *dir, + const struct qstr *file_name, u32 cookie, + struct fsnotify_iter_info *iter_info) +{ + int err; + struct au_hnotify *hnotify; + struct inode *h_dir, *h_inode; + struct fsnotify_mark *inode_mark; + + AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); + + err = 0; + /* if FS_UNMOUNT happens, there must be another bug */ + AuDebugOn(mask & FS_UNMOUNT); + if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) + goto out; + + h_dir = dir; + h_inode = NULL; +#ifdef AuDbgHnotify + au_debug_on(); + if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 + || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { + AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", + h_dir->i_ino, mask, au_hfsn_name(mask), + AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); + /* WARN_ON(1); */ + } + au_debug_off(); +#endif + + inode_mark = fsnotify_iter_inode_mark(iter_info); + AuDebugOn(!inode_mark); + hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); + err = au_hnotify(h_dir, hnotify, mask, file_name, h_inode); + +out: + return err; +} + +static struct fsnotify_ops au_hfsn_ops = { + .handle_event = au_hfsn_handle_event, + .free_group_priv = au_hfsn_free_group, + .free_mark = au_hfsn_free_mark +}; + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_fin_br(struct au_branch *br) +{ + struct au_br_hfsnotify *hfsn; + + hfsn = br->br_hfsn; + if (hfsn) { + lockdep_off(); + fsnotify_put_group(hfsn->hfsn_group); + lockdep_on(); + } +} + +static int au_hfsn_init_br(struct au_branch *br, int perm) +{ + int err; + struct fsnotify_group *group; + struct au_br_hfsnotify *hfsn; + + err = 0; + br->br_hfsn = NULL; + if (!au_br_hnotifyable(perm)) + goto out; + + err = -ENOMEM; + hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); + if (unlikely(!hfsn)) + goto out; + + err = 0; + group = fsnotify_alloc_group(&au_hfsn_ops); + if (IS_ERR(group)) { + err = PTR_ERR(group); + pr_err("fsnotify_alloc_group() failed, %d\n", err); + goto out_hfsn; + } + + group->private = hfsn; + hfsn->hfsn_group = group; + br->br_hfsn = hfsn; + goto out; /* success */ + +out_hfsn: + au_kfree_try_rcu(hfsn); +out: + return err; +} + +static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) +{ + int err; + + err = 0; + if (!br->br_hfsn) + err = au_hfsn_init_br(br, perm); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_fin(void) +{ + AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); + wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); +} + +const struct au_hnotify_op au_hnotify_op = { + .ctl = au_hfsn_ctl, + .alloc = au_hfsn_alloc, + .free = au_hfsn_free, + + .fin = au_hfsn_fin, + + .reset_br = au_hfsn_reset_br, + .fin_br = au_hfsn_fin_br, + .init_br = au_hfsn_init_br +}; diff -Naur --no-dereference a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c --- a/fs/aufs/hfsplus.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/hfsplus.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * special support for filesystems which acquires an inode mutex + * at final closing a file, eg, hfsplus. + * + * This trick is very simple and stupid, just to open the file before really + * necessary open to tell hfsplus that this is not the final closing. + * The caller should call au_h_open_pre() after acquiring the inode mutex, + * and au_h_open_post() after releasing it. + */ + +#include "aufs.h" + +struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, + int force_wr) +{ + struct file *h_file; + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + AuDebugOn(!h_dentry); + AuDebugOn(d_is_negative(h_dentry)); + + h_file = NULL; + if (au_test_hfsplus(h_dentry->d_sb) + && d_is_reg(h_dentry)) + h_file = au_h_open(dentry, bindex, + O_RDONLY | O_NOATIME | O_LARGEFILE, + /*file*/NULL, force_wr); + return h_file; +} + +void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file) +{ + struct au_branch *br; + + if (h_file) { + fput(h_file); + br = au_sbr(dentry->d_sb, bindex); + au_lcnt_dec(&br->br_nfiles); + } +} diff -Naur --no-dereference a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c --- a/fs/aufs/hnotify.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/hnotify.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * abstraction to notify the direct changes on lower directories + */ + +/* #include */ +#include "aufs.h" + +int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) +{ + int err; + struct au_hnotify *hn; + + err = -ENOMEM; + hn = au_cache_alloc_hnotify(); + if (hn) { + hn->hn_aufs_inode = inode; + hinode->hi_notify = hn; + err = au_hnotify_op.alloc(hinode); + AuTraceErr(err); + if (unlikely(err)) { + hinode->hi_notify = NULL; + au_cache_free_hnotify(hn); + /* + * The upper dir was removed by udba, but the same named + * dir left. In this case, aufs assigns a new inode + * number and set the monitor again. + * For the lower dir, the old monitor is still left. + */ + if (err == -EEXIST) + err = 0; + } + } + + AuTraceErr(err); + return err; +} + +void au_hn_free(struct au_hinode *hinode) +{ + struct au_hnotify *hn; + + hn = hinode->hi_notify; + if (hn) { + hinode->hi_notify = NULL; + if (au_hnotify_op.free(hinode, hn)) + au_cache_free_hnotify(hn); + } +} + +/* ---------------------------------------------------------------------- */ + +void au_hn_ctl(struct au_hinode *hinode, int do_set) +{ + if (hinode->hi_notify) + au_hnotify_op.ctl(hinode, do_set); +} + +void au_hn_reset(struct inode *inode, unsigned int flags) +{ + aufs_bindex_t bindex, bbot; + struct inode *hi; + struct dentry *iwhdentry; + + bbot = au_ibbot(inode); + for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + hi = au_h_iptr(inode, bindex); + if (!hi) + continue; + + /* inode_lock_nested(hi, AuLsc_I_CHILD); */ + iwhdentry = au_hi_wh(inode, bindex); + if (iwhdentry) + dget(iwhdentry); + au_igrab(hi); + au_set_h_iptr(inode, bindex, NULL, 0); + au_set_h_iptr(inode, bindex, au_igrab(hi), + flags & ~AuHi_XINO); + iput(hi); + dput(iwhdentry); + /* inode_unlock(hi); */ + } +} + +/* ---------------------------------------------------------------------- */ + +static int hn_xino(struct inode *inode, struct inode *h_inode) +{ + int err; + aufs_bindex_t bindex, bbot, bfound, btop; + struct inode *h_i; + + err = 0; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + pr_warn("branch root dir was changed\n"); + goto out; + } + + bfound = -1; + bbot = au_ibbot(inode); + btop = au_ibtop(inode); +#if 0 /* reserved for future use */ + if (bindex == bbot) { + /* keep this ino in rename case */ + goto out; + } +#endif + for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex) == h_inode) { + bfound = bindex; + break; + } + if (bfound < 0) + goto out; + + for (bindex = btop; bindex <= bbot; bindex++) { + h_i = au_h_iptr(inode, bindex); + if (!h_i) + continue; + + err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); + /* ignore this error */ + /* bad action? */ + } + + /* children inode number will be broken */ + +out: + AuTraceErr(err); + return err; +} + +static int hn_gen_tree(struct dentry *dentry) +{ + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, dentry, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + struct dentry *d; + + d = dentries[j]; + if (IS_ROOT(d)) + continue; + + au_digen_dec(d); + if (d_really_is_positive(d)) + /* todo: reset children xino? + cached children only? */ + au_iigen_dec(d_inode(d)); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +/* + * return 0 if processed. + */ +static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, + const unsigned int isdir) +{ + int err; + struct dentry *d; + struct qstr *dname; + + err = 1; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + pr_warn("branch root dir was changed\n"); + err = 0; + goto out; + } + + if (!isdir) { + AuDebugOn(!name); + au_iigen_dec(inode); + spin_lock(&inode->i_lock); + hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { + spin_lock(&d->d_lock); + dname = &d->d_name; + if (dname->len != nlen + && memcmp(dname->name, name, nlen)) { + spin_unlock(&d->d_lock); + continue; + } + err = 0; + au_digen_dec(d); + spin_unlock(&d->d_lock); + break; + } + spin_unlock(&inode->i_lock); + } else { + au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); + d = d_find_any_alias(inode); + if (!d) { + au_iigen_dec(inode); + goto out; + } + + spin_lock(&d->d_lock); + dname = &d->d_name; + if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { + spin_unlock(&d->d_lock); + err = hn_gen_tree(d); + spin_lock(&d->d_lock); + } + spin_unlock(&d->d_lock); + dput(d); + } + +out: + AuTraceErr(err); + return err; +} + +static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) +{ + int err; + + if (IS_ROOT(dentry)) { + pr_warn("branch root dir was changed\n"); + return 0; + } + + err = 0; + if (!isdir) { + au_digen_dec(dentry); + if (d_really_is_positive(dentry)) + au_iigen_dec(d_inode(dentry)); + } else { + au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); + if (d_really_is_positive(dentry)) + err = hn_gen_tree(dentry); + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* hnotify job flags */ +#define AuHnJob_XINO0 1 +#define AuHnJob_GEN (1 << 1) +#define AuHnJob_DIRENT (1 << 2) +#define AuHnJob_ISDIR (1 << 3) +#define AuHnJob_TRYXINO0 (1 << 4) +#define AuHnJob_MNTPNT (1 << 5) +#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) +#define au_fset_hnjob(flags, name) \ + do { (flags) |= AuHnJob_##name; } while (0) +#define au_fclr_hnjob(flags, name) \ + do { (flags) &= ~AuHnJob_##name; } while (0) + +enum { + AuHn_CHILD, + AuHn_PARENT, + AuHnLast +}; + +struct au_hnotify_args { + struct inode *h_dir, *dir, *h_child_inode; + u32 mask; + unsigned int flags[AuHnLast]; + unsigned int h_child_nlen; + char h_child_name[]; +}; + +struct hn_job_args { + unsigned int flags; + struct inode *inode, *h_inode, *dir, *h_dir; + struct dentry *dentry; + char *h_name; + int h_nlen; +}; + +static int hn_job(struct hn_job_args *a) +{ + const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); + int e; + + /* reset xino */ + if (au_ftest_hnjob(a->flags, XINO0) && a->inode) + hn_xino(a->inode, a->h_inode); /* ignore this error */ + + if (au_ftest_hnjob(a->flags, TRYXINO0) + && a->inode + && a->h_inode) { + inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (!a->h_inode->i_nlink + && !(a->h_inode->i_state & I_LINKABLE)) + hn_xino(a->inode, a->h_inode); /* ignore this error */ + inode_unlock_shared(a->h_inode); + } + + /* make the generation obsolete */ + if (au_ftest_hnjob(a->flags, GEN)) { + e = -1; + if (a->inode) + e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, + isdir); + if (e && a->dentry) + hn_gen_by_name(a->dentry, isdir); + /* ignore this error */ + } + + /* make dir entries obsolete */ + if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { + struct au_vdir *vdir; + + vdir = au_ivdir(a->inode); + if (vdir) + vdir->vd_jiffy = 0; + /* IMustLock(a->inode); */ + /* inode_inc_iversion(a->inode); */ + } + + /* can do nothing but warn */ + if (au_ftest_hnjob(a->flags, MNTPNT) + && a->dentry + && d_mountpoint(a->dentry)) + pr_warn("mount-point %pd is removed or renamed\n", a->dentry); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, + struct inode *dir) +{ + struct dentry *dentry, *d, *parent; + struct qstr *dname; + + parent = d_find_any_alias(dir); + if (!parent) + return NULL; + + dentry = NULL; + spin_lock(&parent->d_lock); + list_for_each_entry(d, &parent->d_subdirs, d_child) { + /* AuDbg("%pd\n", d); */ + spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); + dname = &d->d_name; + if (dname->len != nlen || memcmp(dname->name, name, nlen)) + goto cont_unlock; + if (au_di(d)) + au_digen_dec(d); + else + goto cont_unlock; + if (au_dcount(d) > 0) { + dentry = dget_dlock(d); + spin_unlock(&d->d_lock); + break; + } + +cont_unlock: + spin_unlock(&d->d_lock); + } + spin_unlock(&parent->d_lock); + dput(parent); + + if (dentry) + di_write_lock_child(dentry); + + return dentry; +} + +static struct inode *lookup_wlock_by_ino(struct super_block *sb, + aufs_bindex_t bindex, ino_t h_ino) +{ + struct inode *inode; + ino_t ino; + int err; + + inode = NULL; + err = au_xino_read(sb, bindex, h_ino, &ino); + if (!err && ino) + inode = ilookup(sb, ino); + if (!inode) + goto out; + + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + pr_warn("wrong root branch\n"); + iput(inode); + inode = NULL; + goto out; + } + + ii_write_lock_child(inode); + +out: + return inode; +} + +static void au_hn_bh(void *_args) +{ + struct au_hnotify_args *a = _args; + struct super_block *sb; + aufs_bindex_t bindex, bbot, bfound; + unsigned char xino, try_iput; + int err; + struct inode *inode; + ino_t h_ino; + struct hn_job_args args; + struct dentry *dentry; + struct au_sbinfo *sbinfo; + + AuDebugOn(!_args); + AuDebugOn(!a->h_dir); + AuDebugOn(!a->dir); + AuDebugOn(!a->mask); + AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", + a->mask, a->dir->i_ino, a->h_dir->i_ino, + a->h_child_inode ? a->h_child_inode->i_ino : 0); + + inode = NULL; + dentry = NULL; + /* + * do not lock a->dir->i_mutex here + * because of d_revalidate() may cause a deadlock. + */ + sb = a->dir->i_sb; + AuDebugOn(!sb); + sbinfo = au_sbi(sb); + AuDebugOn(!sbinfo); + si_write_lock(sb, AuLock_NOPLMW); + + if (au_opt_test(sbinfo->si_mntflags, DIRREN)) + switch (a->mask & FS_EVENTS_POSS_ON_CHILD) { + case FS_MOVED_FROM: + case FS_MOVED_TO: + AuWarn1("DIRREN with UDBA may not work correctly " + "for the direct rename(2)\n"); + } + + ii_read_lock_parent(a->dir); + bfound = -1; + bbot = au_ibbot(a->dir); + for (bindex = au_ibtop(a->dir); bindex <= bbot; bindex++) + if (au_h_iptr(a->dir, bindex) == a->h_dir) { + bfound = bindex; + break; + } + ii_read_unlock(a->dir); + if (unlikely(bfound < 0)) + goto out; + + xino = !!au_opt_test(au_mntflags(sb), XINO); + h_ino = 0; + if (a->h_child_inode) + h_ino = a->h_child_inode->i_ino; + + if (a->h_child_nlen + && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) + || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) + dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, + a->dir); + try_iput = 0; + if (dentry && d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (xino && !inode && h_ino + && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) + || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) + || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { + inode = lookup_wlock_by_ino(sb, bfound, h_ino); + try_iput = 1; + } + + args.flags = a->flags[AuHn_CHILD]; + args.dentry = dentry; + args.inode = inode; + args.h_inode = a->h_child_inode; + args.dir = a->dir; + args.h_dir = a->h_dir; + args.h_name = a->h_child_name; + args.h_nlen = a->h_child_nlen; + err = hn_job(&args); + if (dentry) { + if (au_di(dentry)) + di_write_unlock(dentry); + dput(dentry); + } + if (inode && try_iput) { + ii_write_unlock(inode); + iput(inode); + } + + ii_write_lock_parent(a->dir); + args.flags = a->flags[AuHn_PARENT]; + args.dentry = NULL; + args.inode = a->dir; + args.h_inode = a->h_dir; + args.dir = NULL; + args.h_dir = NULL; + args.h_name = NULL; + args.h_nlen = 0; + err = hn_job(&args); + ii_write_unlock(a->dir); + +out: + iput(a->h_child_inode); + iput(a->h_dir); + iput(a->dir); + si_write_unlock(sb); + au_nwt_done(&sbinfo->si_nowait); + au_kfree_rcu(a); +} + +/* ---------------------------------------------------------------------- */ + +int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, + const struct qstr *h_child_qstr, struct inode *h_child_inode) +{ + int err, len; + unsigned int flags[AuHnLast], f; + unsigned char isdir, isroot, wh; + struct inode *dir; + struct au_hnotify_args *args; + char *p, *h_child_name; + + err = 0; + AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); + dir = igrab(hnotify->hn_aufs_inode); + if (!dir) + goto out; + + isroot = (dir->i_ino == AUFS_ROOT_INO); + wh = 0; + h_child_name = (void *)h_child_qstr->name; + len = h_child_qstr->len; + if (h_child_name) { + if (len > AUFS_WH_PFX_LEN + && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + h_child_name += AUFS_WH_PFX_LEN; + len -= AUFS_WH_PFX_LEN; + wh = 1; + } + } + + isdir = 0; + if (h_child_inode) + isdir = !!S_ISDIR(h_child_inode->i_mode); + flags[AuHn_PARENT] = AuHnJob_ISDIR; + flags[AuHn_CHILD] = 0; + if (isdir) + flags[AuHn_CHILD] = AuHnJob_ISDIR; + au_fset_hnjob(flags[AuHn_PARENT], DIRENT); + au_fset_hnjob(flags[AuHn_CHILD], GEN); + switch (mask & ALL_FSNOTIFY_DIRENT_EVENTS) { + case FS_MOVED_FROM: + case FS_MOVED_TO: + au_fset_hnjob(flags[AuHn_CHILD], XINO0); + au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); + fallthrough; + case FS_CREATE: + AuDebugOn(!h_child_name); + break; + + case FS_DELETE: + /* + * aufs never be able to get this child inode. + * revalidation should be in d_revalidate() + * by checking i_nlink, i_generation or d_unhashed(). + */ + AuDebugOn(!h_child_name); + au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); + au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); + break; + + default: + AuDebugOn(1); + } + + if (wh) + h_child_inode = NULL; + + err = -ENOMEM; + /* iput() and kfree() will be called in au_hnotify() */ + args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); + if (unlikely(!args)) { + AuErr1("no memory\n"); + iput(dir); + goto out; + } + args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; + args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; + args->mask = mask; + args->dir = dir; + args->h_dir = igrab(h_dir); + if (h_child_inode) + h_child_inode = igrab(h_child_inode); /* can be NULL */ + args->h_child_inode = h_child_inode; + args->h_child_nlen = len; + if (len) { + p = (void *)args; + p += sizeof(*args); + memcpy(p, h_child_name, len); + p[len] = 0; + } + + /* NFS fires the event for silly-renamed one from kworker */ + f = 0; + if (!dir->i_nlink + || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) + f = AuWkq_NEST; + err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); + if (unlikely(err)) { + pr_err("wkq %d\n", err); + iput(args->h_child_inode); + iput(args->h_dir); + iput(args->dir); + au_kfree_rcu(args); + } + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) +{ + int err; + + AuDebugOn(!(udba & AuOptMask_UDBA)); + + err = 0; + if (au_hnotify_op.reset_br) + err = au_hnotify_op.reset_br(udba, br, perm); + + return err; +} + +int au_hnotify_init_br(struct au_branch *br, int perm) +{ + int err; + + err = 0; + if (au_hnotify_op.init_br) + err = au_hnotify_op.init_br(br, perm); + + return err; +} + +void au_hnotify_fin_br(struct au_branch *br) +{ + if (au_hnotify_op.fin_br) + au_hnotify_op.fin_br(br); +} + +static void au_hn_destroy_cache(void) +{ + kmem_cache_destroy(au_cache[AuCache_HNOTIFY]); + au_cache[AuCache_HNOTIFY] = NULL; +} + +int __init au_hnotify_init(void) +{ + int err; + + err = -ENOMEM; + au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify); + if (au_cache[AuCache_HNOTIFY]) { + err = 0; + if (au_hnotify_op.init) + err = au_hnotify_op.init(); + if (unlikely(err)) + au_hn_destroy_cache(); + } + AuTraceErr(err); + return err; +} + +void au_hnotify_fin(void) +{ + if (au_hnotify_op.fin) + au_hnotify_op.fin(); + + /* cf. au_cache_fin() */ + if (au_cache[AuCache_HNOTIFY]) + au_hn_destroy_cache(); +} diff -Naur --no-dereference a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c --- a/fs/aufs/iinfo.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/iinfo.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode private data + */ + +#include "aufs.h" + +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) +{ + struct inode *h_inode; + struct au_hinode *hinode; + + IiMustAnyLock(inode); + + hinode = au_hinode(au_ii(inode), bindex); + h_inode = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + return h_inode; +} + +/* todo: hard/soft set? */ +void au_hiput(struct au_hinode *hinode) +{ + au_hn_free(hinode); + dput(hinode->hi_whdentry); + iput(hinode->hi_inode); +} + +unsigned int au_hi_flags(struct inode *inode, int isdir) +{ + unsigned int flags; + const unsigned int mnt_flags = au_mntflags(inode->i_sb); + + flags = 0; + if (au_opt_test(mnt_flags, XINO)) + au_fset_hi(flags, XINO); + if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) + au_fset_hi(flags, HNOTIFY); + return flags; +} + +void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode, unsigned int flags) +{ + struct au_hinode *hinode; + struct inode *hi; + struct au_iinfo *iinfo = au_ii(inode); + + IiMustWriteLock(inode); + + hinode = au_hinode(iinfo, bindex); + hi = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + + if (hi) + au_hiput(hinode); + hinode->hi_inode = h_inode; + if (h_inode) { + int err; + struct super_block *sb = inode->i_sb; + struct au_branch *br; + + AuDebugOn(inode->i_mode + && (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)); + if (bindex == iinfo->ii_btop) + au_cpup_igen(inode, h_inode); + br = au_sbr(sb, bindex); + hinode->hi_id = br->br_id; + if (au_ftest_hi(flags, XINO)) { + err = au_xino_write(sb, bindex, h_inode->i_ino, + inode->i_ino); + if (unlikely(err)) + AuIOErr1("failed au_xino_write() %d\n", err); + } + + if (au_ftest_hi(flags, HNOTIFY) + && au_br_hnotifyable(br->br_perm)) { + err = au_hn_alloc(hinode, inode); + if (unlikely(err)) + AuIOErr1("au_hn_alloc() %d\n", err); + } + } +} + +void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_wh) +{ + struct au_hinode *hinode; + + IiMustWriteLock(inode); + + hinode = au_hinode(au_ii(inode), bindex); + AuDebugOn(hinode->hi_whdentry); + hinode->hi_whdentry = h_wh; +} + +void au_update_iigen(struct inode *inode, int half) +{ + struct au_iinfo *iinfo; + struct au_iigen *iigen; + unsigned int sigen; + + sigen = au_sigen(inode->i_sb); + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); + iigen->ig_generation = sigen; + if (half) + au_ig_fset(iigen->ig_flags, HALF_REFRESHED); + else + au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); + spin_unlock(&iigen->ig_spin); +} + +/* it may be called at remount time, too */ +void au_update_ibrange(struct inode *inode, int do_put_zero) +{ + struct au_iinfo *iinfo; + aufs_bindex_t bindex, bbot; + + AuDebugOn(au_is_bad_inode(inode)); + IiMustWriteLock(inode); + + iinfo = au_ii(inode); + if (do_put_zero && iinfo->ii_btop >= 0) { + for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++) { + struct inode *h_i; + + h_i = au_hinode(iinfo, bindex)->hi_inode; + if (h_i + && !h_i->i_nlink + && !(h_i->i_state & I_LINKABLE)) + au_set_h_iptr(inode, bindex, NULL, 0); + } + } + + iinfo->ii_btop = -1; + iinfo->ii_bbot = -1; + bbot = au_sbbot(inode->i_sb); + for (bindex = 0; bindex <= bbot; bindex++) + if (au_hinode(iinfo, bindex)->hi_inode) { + iinfo->ii_btop = bindex; + break; + } + if (iinfo->ii_btop >= 0) + for (bindex = bbot; bindex >= iinfo->ii_btop; bindex--) + if (au_hinode(iinfo, bindex)->hi_inode) { + iinfo->ii_bbot = bindex; + break; + } + AuDebugOn(iinfo->ii_btop > iinfo->ii_bbot); +} + +/* ---------------------------------------------------------------------- */ + +void au_icntnr_init_once(void *_c) +{ + struct au_icntnr *c = _c; + struct au_iinfo *iinfo = &c->iinfo; + + spin_lock_init(&iinfo->ii_generation.ig_spin); + au_rw_init(&iinfo->ii_rwsem); + inode_init_once(&c->vfs_inode); +} + +void au_hinode_init(struct au_hinode *hinode) +{ + hinode->hi_inode = NULL; + hinode->hi_id = -1; + au_hn_init(hinode); + hinode->hi_whdentry = NULL; +} + +int au_iinfo_init(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct super_block *sb; + struct au_hinode *hi; + int nbr, i; + + sb = inode->i_sb; + iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); + nbr = au_sbbot(sb) + 1; + if (unlikely(nbr <= 0)) + nbr = 1; + hi = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); + if (hi) { + au_lcnt_inc(&au_sbi(sb)->si_ninodes); + + iinfo->ii_hinode = hi; + for (i = 0; i < nbr; i++, hi++) + au_hinode_init(hi); + + iinfo->ii_generation.ig_generation = au_sigen(sb); + iinfo->ii_btop = -1; + iinfo->ii_bbot = -1; + iinfo->ii_vdir = NULL; + return 0; + } + return -ENOMEM; +} + +int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink) +{ + int err, i; + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + err = -ENOMEM; + hip = au_krealloc(iinfo->ii_hinode, sizeof(*hip) * nbr, GFP_NOFS, + may_shrink); + if (hip) { + iinfo->ii_hinode = hip; + i = iinfo->ii_bbot + 1; + hip += i; + for (; i < nbr; i++, hip++) + au_hinode_init(hip); + err = 0; + } + + return err; +} + +void au_iinfo_fin(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct au_hinode *hi; + struct super_block *sb; + aufs_bindex_t bindex, bbot; + const unsigned char unlinked = !inode->i_nlink; + + AuDebugOn(au_is_bad_inode(inode)); + + sb = inode->i_sb; + au_lcnt_dec(&au_sbi(sb)->si_ninodes); + if (si_pid_test(sb)) + au_xino_delete_inode(inode, unlinked); + else { + /* + * it is safe to hide the dependency between sbinfo and + * sb->s_umount. + */ + lockdep_off(); + si_noflush_read_lock(sb); + au_xino_delete_inode(inode, unlinked); + si_read_unlock(sb); + lockdep_on(); + } + + iinfo = au_ii(inode); + if (iinfo->ii_vdir) + au_vdir_free(iinfo->ii_vdir); + + bindex = iinfo->ii_btop; + if (bindex >= 0) { + hi = au_hinode(iinfo, bindex); + bbot = iinfo->ii_bbot; + while (bindex++ <= bbot) { + if (hi->hi_inode) + au_hiput(hi); + hi++; + } + } + au_kfree_rcu(iinfo->ii_hinode); + AuRwDestroy(&iinfo->ii_rwsem); +} diff -Naur --no-dereference a/fs/aufs/inode.c b/fs/aufs/inode.c --- a/fs/aufs/inode.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/inode.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode functions + */ + +#include +#include "aufs.h" + +struct inode *au_igrab(struct inode *inode) +{ + if (inode) { + AuDebugOn(!atomic_read(&inode->i_count)); + ihold(inode); + } + return inode; +} + +static void au_refresh_hinode_attr(struct inode *inode, int do_version) +{ + au_cpup_attr_all(inode, /*force*/0); + au_update_iigen(inode, /*half*/1); + if (do_version) + inode_inc_iversion(inode); +} + +static int au_ii_refresh(struct inode *inode, int *update) +{ + int err, e, nbr; + umode_t type; + aufs_bindex_t bindex, new_bindex; + struct super_block *sb; + struct au_iinfo *iinfo; + struct au_hinode *p, *q, tmp; + + AuDebugOn(au_is_bad_inode(inode)); + IiMustWriteLock(inode); + + *update = 0; + sb = inode->i_sb; + nbr = au_sbbot(sb) + 1; + type = inode->i_mode & S_IFMT; + iinfo = au_ii(inode); + err = au_hinode_realloc(iinfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + + AuDebugOn(iinfo->ii_btop < 0); + p = au_hinode(iinfo, iinfo->ii_btop); + for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++, p++) { + if (!p->hi_inode) + continue; + + AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); + new_bindex = au_br_index(sb, p->hi_id); + if (new_bindex == bindex) + continue; + + if (new_bindex < 0) { + *update = 1; + au_hiput(p); + p->hi_inode = NULL; + continue; + } + + if (new_bindex < iinfo->ii_btop) + iinfo->ii_btop = new_bindex; + if (iinfo->ii_bbot < new_bindex) + iinfo->ii_bbot = new_bindex; + /* swap two lower inode, and loop again */ + q = au_hinode(iinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hi_inode) { + bindex--; + p--; + } + } + au_update_ibrange(inode, /*do_put_zero*/0); + au_hinode_realloc(iinfo, nbr, /*may_shrink*/1); /* harmless if err */ + e = au_dy_irefresh(inode); + if (unlikely(e && !err)) + err = e; + +out: + AuTraceErr(err); + return err; +} + +void au_refresh_iop(struct inode *inode, int force_getattr) +{ + int type; + struct au_sbinfo *sbi = au_sbi(inode->i_sb); + const struct inode_operations *iop + = force_getattr ? aufs_iop : sbi->si_iop_array; + + if (inode->i_op == iop) + return; + + switch (inode->i_mode & S_IFMT) { + case S_IFDIR: + type = AuIop_DIR; + break; + case S_IFLNK: + type = AuIop_SYMLINK; + break; + default: + type = AuIop_OTHER; + break; + } + + inode->i_op = iop + type; + /* unnecessary smp_wmb() */ +} + +int au_refresh_hinode_self(struct inode *inode) +{ + int err, update; + + err = au_ii_refresh(inode, &update); + if (!err) + au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); + + AuTraceErr(err); + return err; +} + +int au_refresh_hinode(struct inode *inode, struct dentry *dentry) +{ + int err, e, update; + unsigned int flags; + umode_t mode; + aufs_bindex_t bindex, bbot; + unsigned char isdir; + struct au_hinode *p; + struct au_iinfo *iinfo; + + err = au_ii_refresh(inode, &update); + if (unlikely(err)) + goto out; + + update = 0; + iinfo = au_ii(inode); + p = au_hinode(iinfo, iinfo->ii_btop); + mode = (inode->i_mode & S_IFMT); + isdir = S_ISDIR(mode); + flags = au_hi_flags(inode, isdir); + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + struct inode *h_i, *h_inode; + struct dentry *h_d; + + h_d = au_h_dptr(dentry, bindex); + if (!h_d || d_is_negative(h_d)) + continue; + + h_inode = d_inode(h_d); + AuDebugOn(mode != (h_inode->i_mode & S_IFMT)); + if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) { + h_i = au_h_iptr(inode, bindex); + if (h_i) { + if (h_i == h_inode) + continue; + err = -EIO; + break; + } + } + if (bindex < iinfo->ii_btop) + iinfo->ii_btop = bindex; + if (iinfo->ii_bbot < bindex) + iinfo->ii_bbot = bindex; + au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags); + update = 1; + } + au_update_ibrange(inode, /*do_put_zero*/0); + e = au_dy_irefresh(inode); + if (unlikely(e && !err)) + err = e; + if (!err) + au_refresh_hinode_attr(inode, update && isdir); + +out: + AuTraceErr(err); + return err; +} + +static int set_inode(struct inode *inode, struct dentry *dentry) +{ + int err; + unsigned int flags; + umode_t mode; + aufs_bindex_t bindex, btop, btail; + unsigned char isdir; + struct dentry *h_dentry; + struct inode *h_inode; + struct au_iinfo *iinfo; + const struct inode_operations *iop; + + IiMustWriteLock(inode); + + err = 0; + isdir = 0; + iop = au_sbi(inode->i_sb)->si_iop_array; + btop = au_dbtop(dentry); + h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_OTHER; + inode->i_fop = &aufs_file_fop; + err = au_dy_iaop(inode, btop, h_inode); + if (unlikely(err)) + goto out; + break; + case S_IFDIR: + isdir = 1; + btail = au_dbtaildir(dentry); + inode->i_op = iop + AuIop_DIR; + inode->i_fop = &aufs_dir_fop; + break; + case S_IFLNK: + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_SYMLINK; + break; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + case S_IFSOCK: + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_OTHER; + init_special_inode(inode, mode, h_inode->i_rdev); + break; + default: + AuIOErr("Unknown file type 0%o\n", mode); + err = -EIO; + goto out; + } + + /* do not set hnotify for whiteouted dirs (SHWH mode) */ + flags = au_hi_flags(inode, isdir); + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) + && au_ftest_hi(flags, HNOTIFY) + && dentry->d_name.len > AUFS_WH_PFX_LEN + && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) + au_fclr_hi(flags, HNOTIFY); + iinfo = au_ii(inode); + iinfo->ii_btop = btop; + iinfo->ii_bbot = btail; + for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + au_set_h_iptr(inode, bindex, + au_igrab(d_inode(h_dentry)), flags); + } + au_cpup_attr_all(inode, /*force*/1); + /* + * to force calling aufs_get_acl() every time, + * do not call cache_no_acl() for aufs inode. + */ + +out: + return err; +} + +/* + * successful returns with iinfo write_locked + * minus: errno + * zero: success, matched + * plus: no error, but unmatched + */ +static int reval_inode(struct inode *inode, struct dentry *dentry) +{ + int err; + unsigned int gen, igflags; + aufs_bindex_t bindex, bbot; + struct inode *h_inode, *h_dinode; + struct dentry *h_dentry; + + /* + * before this function, if aufs got any iinfo lock, it must be only + * one, the parent dir. + * it can happen by UDBA and the obsoleted inode number. + */ + err = -EIO; + if (unlikely(inode->i_ino == parent_ino(dentry))) + goto out; + + err = 1; + ii_write_lock_new_child(inode); + h_dentry = au_h_dptr(dentry, au_dbtop(dentry)); + h_dinode = d_inode(h_dentry); + bbot = au_ibbot(inode); + for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (!h_inode || h_inode != h_dinode) + continue; + + err = 0; + gen = au_iigen(inode, &igflags); + if (gen == au_digen(dentry) + && !au_ig_ftest(igflags, HALF_REFRESHED)) + break; + + /* fully refresh inode using dentry */ + err = au_refresh_hinode(inode, dentry); + if (!err) + au_update_iigen(inode, /*half*/0); + break; + } + + if (unlikely(err)) + ii_write_unlock(inode); +out: + return err; +} + +int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + unsigned int d_type, ino_t *ino) +{ + int err, idx; + const int isnondir = d_type != DT_DIR; + + /* prevent hardlinked inode number from race condition */ + if (isnondir) { + err = au_xinondir_enter(sb, bindex, h_ino, &idx); + if (unlikely(err)) + goto out; + } + + err = au_xino_read(sb, bindex, h_ino, ino); + if (unlikely(err)) + goto out_xinondir; + + if (!*ino) { + err = -EIO; + *ino = au_xino_new_ino(sb); + if (unlikely(!*ino)) + goto out_xinondir; + err = au_xino_write(sb, bindex, h_ino, *ino); + if (unlikely(err)) + goto out_xinondir; + } + +out_xinondir: + if (isnondir && idx >= 0) + au_xinondir_leave(sb, bindex, h_ino, idx); +out: + return err; +} + +/* successful returns with iinfo write_locked */ +/* todo: return with unlocked? */ +struct inode *au_new_inode(struct dentry *dentry, int must_new) +{ + struct inode *inode, *h_inode; + struct dentry *h_dentry; + struct super_block *sb; + ino_t h_ino, ino; + int err, idx, hlinked; + aufs_bindex_t btop; + + sb = dentry->d_sb; + btop = au_dbtop(dentry); + h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + h_ino = h_inode->i_ino; + hlinked = !d_is_dir(h_dentry) && h_inode->i_nlink > 1; + +new_ino: + /* + * stop 'race'-ing between hardlinks under different + * parents. + */ + if (hlinked) { + err = au_xinondir_enter(sb, btop, h_ino, &idx); + inode = ERR_PTR(err); + if (unlikely(err)) + goto out; + } + + err = au_xino_read(sb, btop, h_ino, &ino); + inode = ERR_PTR(err); + if (unlikely(err)) + goto out_xinondir; + + if (!ino) { + ino = au_xino_new_ino(sb); + if (unlikely(!ino)) { + inode = ERR_PTR(-EIO); + goto out_xinondir; + } + } + + AuDbg("i%lu\n", (unsigned long)ino); + inode = au_iget_locked(sb, ino); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out_xinondir; + + AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); + if (inode->i_state & I_NEW) { + ii_write_lock_new_child(inode); + err = set_inode(inode, dentry); + if (!err) { + unlock_new_inode(inode); + goto out_xinondir; /* success */ + } + + /* + * iget_failed() calls iput(), but we need to call + * ii_write_unlock() after iget_failed(). so dirty hack for + * i_count. + */ + atomic_inc(&inode->i_count); + iget_failed(inode); + ii_write_unlock(inode); + au_xino_write(sb, btop, h_ino, /*ino*/0); + /* ignore this error */ + goto out_iput; + } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { + /* + * horrible race condition between lookup, readdir and copyup + * (or something). + */ + if (hlinked && idx >= 0) + au_xinondir_leave(sb, btop, h_ino, idx); + err = reval_inode(inode, dentry); + if (unlikely(err < 0)) { + hlinked = 0; + goto out_iput; + } + if (!err) + goto out; /* success */ + else if (hlinked && idx >= 0) { + err = au_xinondir_enter(sb, btop, h_ino, &idx); + if (unlikely(err)) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + } + } + + if (unlikely(au_test_fs_unique_ino(h_inode))) + AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," + " b%d, %s, %pd, hi%lu, i%lu.\n", + btop, au_sbtype(h_dentry->d_sb), dentry, + (unsigned long)h_ino, (unsigned long)ino); + ino = 0; + err = au_xino_write(sb, btop, h_ino, /*ino*/0); + if (!err) { + iput(inode); + if (hlinked && idx >= 0) + au_xinondir_leave(sb, btop, h_ino, idx); + goto new_ino; + } + +out_iput: + iput(inode); + inode = ERR_PTR(err); +out_xinondir: + if (hlinked && idx >= 0) + au_xinondir_leave(sb, btop, h_ino, idx); +out: + return inode; +} + +/* ---------------------------------------------------------------------- */ + +int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode) +{ + int err; + struct inode *hi; + + err = au_br_rdonly(au_sbr(sb, bindex)); + + /* pseudo-link after flushed may happen out of bounds */ + if (!err + && inode + && au_ibtop(inode) <= bindex + && bindex <= au_ibbot(inode)) { + /* + * permission check is unnecessary since vfsub routine + * will be called later + */ + hi = au_h_iptr(inode, bindex); + if (hi) + err = IS_IMMUTABLE(hi) ? -EROFS : 0; + } + + return err; +} + +int au_test_h_perm(struct inode *h_inode, int mask) +{ + if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) + return 0; + return inode_permission(h_inode, mask); +} + +int au_test_h_perm_sio(struct inode *h_inode, int mask) +{ + if (au_test_nfs(h_inode->i_sb) + && (mask & MAY_WRITE) + && S_ISDIR(h_inode->i_mode)) + mask |= MAY_READ; /* force permission check */ + return au_test_h_perm(h_inode, mask); +} diff -Naur --no-dereference a/fs/aufs/inode.h b/fs/aufs/inode.h --- a/fs/aufs/inode.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/inode.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,698 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations + */ + +#ifndef __AUFS_INODE_H__ +#define __AUFS_INODE_H__ + +#ifdef __KERNEL__ + +#include +#include "rwsem.h" + +struct vfsmount; + +struct au_hnotify { +#ifdef CONFIG_AUFS_HNOTIFY +#ifdef CONFIG_AUFS_HFSNOTIFY + /* never use fsnotify_add_vfsmount_mark() */ + struct fsnotify_mark hn_mark; +#endif + struct inode *hn_aufs_inode; /* no get/put */ + struct rcu_head rcu; +#endif +} ____cacheline_aligned_in_smp; + +struct au_hinode { + struct inode *hi_inode; + aufs_bindex_t hi_id; +#ifdef CONFIG_AUFS_HNOTIFY + struct au_hnotify *hi_notify; +#endif + + /* reference to the copied-up whiteout with get/put */ + struct dentry *hi_whdentry; +}; + +/* ig_flags */ +#define AuIG_HALF_REFRESHED 1 +#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) +#define au_ig_fset(flags, name) \ + do { (flags) |= AuIG_##name; } while (0) +#define au_ig_fclr(flags, name) \ + do { (flags) &= ~AuIG_##name; } while (0) + +struct au_iigen { + spinlock_t ig_spin; + __u32 ig_generation, ig_flags; +}; + +struct au_vdir; +struct au_iinfo { + struct au_iigen ii_generation; + struct super_block *ii_hsb1; /* no get/put */ + + struct au_rwsem ii_rwsem; + aufs_bindex_t ii_btop, ii_bbot; + __u32 ii_higen; + struct au_hinode *ii_hinode; + struct au_vdir *ii_vdir; +}; + +struct au_icntnr { + struct au_iinfo iinfo; + struct inode vfs_inode; + struct hlist_bl_node plink; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* au_pin flags */ +#define AuPin_DI_LOCKED 1 +#define AuPin_MNT_WRITE (1 << 1) +#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) +#define au_fset_pin(flags, name) \ + do { (flags) |= AuPin_##name; } while (0) +#define au_fclr_pin(flags, name) \ + do { (flags) &= ~AuPin_##name; } while (0) + +struct au_pin { + /* input */ + struct dentry *dentry; + unsigned int udba; + unsigned char lsc_di, lsc_hi, flags; + aufs_bindex_t bindex; + + /* output */ + struct dentry *parent; + struct au_hinode *hdir; + struct vfsmount *h_mnt; + + /* temporary unlock/relock for copyup */ + struct dentry *h_dentry, *h_parent; + struct au_branch *br; + struct task_struct *task; +}; + +void au_pin_hdir_unlock(struct au_pin *p); +int au_pin_hdir_lock(struct au_pin *p); +int au_pin_hdir_relock(struct au_pin *p); +void au_pin_hdir_acquire_nest(struct au_pin *p); +void au_pin_hdir_release(struct au_pin *p); + +/* ---------------------------------------------------------------------- */ + +static inline struct au_iinfo *au_ii(struct inode *inode) +{ + BUG_ON(is_bad_inode(inode)); + return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); +} + +/* ---------------------------------------------------------------------- */ + +/* inode.c */ +struct inode *au_igrab(struct inode *inode); +void au_refresh_iop(struct inode *inode, int force_getattr); +int au_refresh_hinode_self(struct inode *inode); +int au_refresh_hinode(struct inode *inode, struct dentry *dentry); +int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + unsigned int d_type, ino_t *ino); +struct inode *au_new_inode(struct dentry *dentry, int must_new); +int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode); +int au_test_h_perm(struct inode *h_inode, int mask); +int au_test_h_perm_sio(struct inode *h_inode, int mask); + +static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, + ino_t h_ino, unsigned int d_type, ino_t *ino) +{ +#ifdef CONFIG_AUFS_SHWH + return au_ino(sb, bindex, h_ino, d_type, ino); +#else + return 0; +#endif +} + +/* i_op.c */ +enum { + AuIop_SYMLINK, + AuIop_DIR, + AuIop_OTHER, + AuIop_Last +}; +extern struct inode_operations aufs_iop[AuIop_Last], /* not const */ + aufs_iop_nogetattr[AuIop_Last]; + +/* au_wr_dir flags */ +#define AuWrDir_ADD_ENTRY 1 +#define AuWrDir_ISDIR (1 << 1) +#define AuWrDir_TMPFILE (1 << 2) +#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) +#define au_fset_wrdir(flags, name) \ + do { (flags) |= AuWrDir_##name; } while (0) +#define au_fclr_wrdir(flags, name) \ + do { (flags) &= ~AuWrDir_##name; } while (0) + +struct au_wr_dir_args { + aufs_bindex_t force_btgt; + unsigned char flags; +}; +int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, + struct au_wr_dir_args *args); + +struct dentry *au_pinned_h_parent(struct au_pin *pin); +void au_pin_init(struct au_pin *pin, struct dentry *dentry, + aufs_bindex_t bindex, int lsc_di, int lsc_hi, + unsigned int udba, unsigned char flags); +int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, + unsigned int udba, unsigned char flags) __must_check; +int au_do_pin(struct au_pin *pin) __must_check; +void au_unpin(struct au_pin *pin); +int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); + +#define AuIcpup_DID_CPUP 1 +#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) +#define au_fset_icpup(flags, name) \ + do { (flags) |= AuIcpup_##name; } while (0) +#define au_fclr_icpup(flags, name) \ + do { (flags) &= ~AuIcpup_##name; } while (0) + +struct au_icpup_args { + unsigned char flags; + unsigned char pin_flags; + aufs_bindex_t btgt; + unsigned int udba; + struct au_pin pin; + struct path h_path; + struct inode *h_inode; +}; + +int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, + struct au_icpup_args *a); + +int au_h_path_getattr(struct dentry *dentry, struct inode *inode, int force, + struct path *h_path, int locked); + +/* i_op_add.c */ +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir); +int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t dev); +int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); +int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl); +struct vfsub_aopen_args; +int au_aopen_or_create(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *args); +int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); +int aufs_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry); +int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); + +/* i_op_del.c */ +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); +int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir); +int aufs_unlink(struct inode *dir, struct dentry *dentry); +int aufs_rmdir(struct inode *dir, struct dentry *dentry); + +/* i_op_ren.c */ +int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); +int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, + unsigned int flags); + +/* iinfo.c */ +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); +void au_hiput(struct au_hinode *hinode); +void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_wh); +unsigned int au_hi_flags(struct inode *inode, int isdir); + +/* hinode flags */ +#define AuHi_XINO 1 +#define AuHi_HNOTIFY (1 << 1) +#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) +#define au_fset_hi(flags, name) \ + do { (flags) |= AuHi_##name; } while (0) +#define au_fclr_hi(flags, name) \ + do { (flags) &= ~AuHi_##name; } while (0) + +#ifndef CONFIG_AUFS_HNOTIFY +#undef AuHi_HNOTIFY +#define AuHi_HNOTIFY 0 +#endif + +void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode, unsigned int flags); + +void au_update_iigen(struct inode *inode, int half); +void au_update_ibrange(struct inode *inode, int do_put_zero); + +void au_icntnr_init_once(void *_c); +void au_hinode_init(struct au_hinode *hinode); +int au_iinfo_init(struct inode *inode); +void au_iinfo_fin(struct inode *inode); +int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink); + +#ifdef CONFIG_PROC_FS +/* plink.c */ +int au_plink_maint(struct super_block *sb, int flags); +struct au_sbinfo; +void au_plink_maint_leave(struct au_sbinfo *sbinfo); +int au_plink_maint_enter(struct super_block *sb); +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb); +#else +AuStubVoid(au_plink_list, struct super_block *sb) +#endif +int au_plink_test(struct inode *inode); +struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); +void au_plink_append(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_dentry); +void au_plink_put(struct super_block *sb, int verbose); +void au_plink_clean(struct super_block *sb, int verbose); +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); +#else +AuStubInt0(au_plink_maint, struct super_block *sb, int flags); +AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); +AuStubInt0(au_plink_maint_enter, struct super_block *sb); +AuStubVoid(au_plink_list, struct super_block *sb); +AuStubInt0(au_plink_test, struct inode *inode); +AuStub(struct dentry *, au_plink_lkup, return NULL, + struct inode *inode, aufs_bindex_t bindex); +AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_dentry); +AuStubVoid(au_plink_put, struct super_block *sb, int verbose); +AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); +AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_AUFS_XATTR +/* xattr.c */ +int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, + unsigned int verbose); +ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); +void au_xattr_init(struct super_block *sb); +#else +AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, + int ignore_flags, unsigned int verbose); +AuStubVoid(au_xattr_init, struct super_block *sb); +#endif + +#ifdef CONFIG_FS_POSIX_ACL +struct posix_acl *aufs_get_acl(struct inode *inode, int type); +int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); +#endif + +#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) +enum { + AU_XATTR_SET, + AU_ACL_SET +}; + +struct au_sxattr { + int type; + union { + struct { + const char *name; + const void *value; + size_t size; + int flags; + } set; + struct { + struct posix_acl *acl; + int type; + } acl_set; + } u; +}; +ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, + struct au_sxattr *arg); +#endif + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for iinfo */ +enum { + AuLsc_II_CHILD, /* child first */ + AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ + AuLsc_II_CHILD3, /* copyup dirs */ + AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ + AuLsc_II_PARENT2, + AuLsc_II_PARENT3, /* copyup dirs */ + AuLsc_II_NEW_CHILD +}; + +/* + * ii_read_lock_child, ii_write_lock_child, + * ii_read_lock_child2, ii_write_lock_child2, + * ii_read_lock_child3, ii_write_lock_child3, + * ii_read_lock_parent, ii_write_lock_parent, + * ii_read_lock_parent2, ii_write_lock_parent2, + * ii_read_lock_parent3, ii_write_lock_parent3, + * ii_read_lock_new_child, ii_write_lock_new_child, + */ +#define AuReadLockFunc(name, lsc) \ +static inline void ii_read_lock_##name(struct inode *i) \ +{ \ + au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ +} + +#define AuWriteLockFunc(name, lsc) \ +static inline void ii_write_lock_##name(struct inode *i) \ +{ \ + au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ +} + +#define AuRWLockFuncs(name, lsc) \ + AuReadLockFunc(name, lsc) \ + AuWriteLockFunc(name, lsc) + +AuRWLockFuncs(child, CHILD); +AuRWLockFuncs(child2, CHILD2); +AuRWLockFuncs(child3, CHILD3); +AuRWLockFuncs(parent, PARENT); +AuRWLockFuncs(parent2, PARENT2); +AuRWLockFuncs(parent3, PARENT3); +AuRWLockFuncs(new_child, NEW_CHILD); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +#define ii_read_unlock(i) au_rw_read_unlock(&au_ii(i)->ii_rwsem) +#define ii_write_unlock(i) au_rw_write_unlock(&au_ii(i)->ii_rwsem) +#define ii_downgrade_lock(i) au_rw_dgrade_lock(&au_ii(i)->ii_rwsem) + +#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) +#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) +#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) + +/* ---------------------------------------------------------------------- */ + +static inline void au_icntnr_init(struct au_icntnr *c) +{ +#ifdef CONFIG_AUFS_DEBUG + c->vfs_inode.i_mode = 0; +#endif +} + +static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags) +{ + unsigned int gen; + struct au_iinfo *iinfo; + struct au_iigen *iigen; + + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); + if (igflags) + *igflags = iigen->ig_flags; + gen = iigen->ig_generation; + spin_unlock(&iigen->ig_spin); + + return gen; +} + +/* tiny test for inode number */ +/* tmpfs generation is too rough */ +static inline int au_test_higen(struct inode *inode, struct inode *h_inode) +{ + struct au_iinfo *iinfo; + + iinfo = au_ii(inode); + AuRwMustAnyLock(&iinfo->ii_rwsem); + return !(iinfo->ii_hsb1 == h_inode->i_sb + && iinfo->ii_higen == h_inode->i_generation); +} + +static inline void au_iigen_dec(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct au_iigen *iigen; + + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); + iigen->ig_generation--; + spin_unlock(&iigen->ig_spin); +} + +static inline int au_iigen_test(struct inode *inode, unsigned int sigen) +{ + int err; + + err = 0; + if (unlikely(inode && au_iigen(inode, NULL) != sigen)) + err = -EIO; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo, + aufs_bindex_t bindex) +{ + return iinfo->ii_hinode + bindex; +} + +static inline int au_is_bad_inode(struct inode *inode) +{ + return !!(is_bad_inode(inode) || !au_hinode(au_ii(inode), 0)); +} + +static inline aufs_bindex_t au_ii_br_id(struct inode *inode, + aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + return au_hinode(au_ii(inode), bindex)->hi_id; +} + +static inline aufs_bindex_t au_ibtop(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_btop; +} + +static inline aufs_bindex_t au_ibbot(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_bbot; +} + +static inline struct au_vdir *au_ivdir(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_vdir; +} + +static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + return au_hinode(au_ii(inode), bindex)->hi_whdentry; +} + +static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); + au_ii(inode)->ii_btop = bindex; +} + +static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); + au_ii(inode)->ii_bbot = bindex; +} + +static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) +{ + IiMustWriteLock(inode); + au_ii(inode)->ii_vdir = vdir; +} + +static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + return au_hinode(au_ii(inode), bindex); +} + +/* ---------------------------------------------------------------------- */ + +static inline struct dentry *au_pinned_parent(struct au_pin *pin) +{ + if (pin) + return pin->parent; + return NULL; +} + +static inline struct inode *au_pinned_h_dir(struct au_pin *pin) +{ + if (pin && pin->hdir) + return pin->hdir->hi_inode; + return NULL; +} + +static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) +{ + if (pin) + return pin->hdir; + return NULL; +} + +static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) +{ + if (pin) + pin->dentry = dentry; +} + +static inline void au_pin_set_parent_lflag(struct au_pin *pin, + unsigned char lflag) +{ + if (pin) { + if (lflag) + au_fset_pin(pin->flags, DI_LOCKED); + else + au_fclr_pin(pin->flags, DI_LOCKED); + } +} + +#if 0 /* reserved */ +static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) +{ + if (pin) { + dput(pin->parent); + pin->parent = dget(parent); + } +} +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_branch; +#ifdef CONFIG_AUFS_HNOTIFY +struct au_hnotify_op { + void (*ctl)(struct au_hinode *hinode, int do_set); + int (*alloc)(struct au_hinode *hinode); + + /* + * if it returns true, the caller should free hinode->hi_notify, + * otherwise ->free() frees it. + */ + int (*free)(struct au_hinode *hinode, + struct au_hnotify *hn) __must_check; + + void (*fin)(void); + int (*init)(void); + + int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); + void (*fin_br)(struct au_branch *br); + int (*init_br)(struct au_branch *br, int perm); +}; + +/* hnotify.c */ +int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); +void au_hn_free(struct au_hinode *hinode); +void au_hn_ctl(struct au_hinode *hinode, int do_set); +void au_hn_reset(struct inode *inode, unsigned int flags); +int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, + const struct qstr *h_child_qstr, struct inode *h_child_inode); +int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); +int au_hnotify_init_br(struct au_branch *br, int perm); +void au_hnotify_fin_br(struct au_branch *br); +int __init au_hnotify_init(void); +void au_hnotify_fin(void); + +/* hfsnotify.c */ +extern const struct au_hnotify_op au_hnotify_op; + +static inline +void au_hn_init(struct au_hinode *hinode) +{ + hinode->hi_notify = NULL; +} + +static inline struct au_hnotify *au_hn(struct au_hinode *hinode) +{ + return hinode->hi_notify; +} + +#else +AuStub(int, au_hn_alloc, return -EOPNOTSUPP, + struct au_hinode *hinode __maybe_unused, + struct inode *inode __maybe_unused) +AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) +AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) +AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, + int do_set __maybe_unused) +AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, + unsigned int flags __maybe_unused) +AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, + struct au_branch *br __maybe_unused, + int perm __maybe_unused) +AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, + int perm __maybe_unused) +AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) +AuStubInt0(__init au_hnotify_init, void) +AuStubVoid(au_hnotify_fin, void) +AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) +#endif /* CONFIG_AUFS_HNOTIFY */ + +static inline void au_hn_suspend(struct au_hinode *hdir) +{ + au_hn_ctl(hdir, /*do_set*/0); +} + +static inline void au_hn_resume(struct au_hinode *hdir) +{ + au_hn_ctl(hdir, /*do_set*/1); +} + +static inline void au_hn_inode_lock(struct au_hinode *hdir) +{ + inode_lock(hdir->hi_inode); + au_hn_suspend(hdir); +} + +static inline void au_hn_inode_lock_nested(struct au_hinode *hdir, + unsigned int sc __maybe_unused) +{ + inode_lock_nested(hdir->hi_inode, sc); + au_hn_suspend(hdir); +} + +#if 0 /* unused */ +#include "vfsub.h" +static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir, + unsigned int sc) +{ + inode_lock_shared_nested(hdir->hi_inode, sc); + au_hn_suspend(hdir); +} +#endif + +static inline void au_hn_inode_unlock(struct au_hinode *hdir) +{ + au_hn_resume(hdir); + inode_unlock(hdir->hi_inode); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_INODE_H__ */ diff -Naur --no-dereference a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c --- a/fs/aufs/ioctl.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/ioctl.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * ioctl + * plink-management and readdir in userspace. + * assist the pathconf(3) wrapper library. + * move-down + * File-based Hierarchical Storage Management. + */ + +#include +#include +#include "aufs.h" + +static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) +{ + int err, fd; + aufs_bindex_t wbi, bindex, bbot; + struct file *h_file; + struct super_block *sb; + struct dentry *root; + struct au_branch *br; + struct aufs_wbr_fd wbrfd = { + .oflags = au_dir_roflags, + .brid = -1 + }; + const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY + | O_NOATIME | O_CLOEXEC; + + AuDebugOn(wbrfd.oflags & ~valid); + + if (arg) { + err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); + if (unlikely(err)) { + err = -EFAULT; + goto out; + } + + err = -EINVAL; + AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); + wbrfd.oflags |= au_dir_roflags; + AuDbg("0%o\n", wbrfd.oflags); + if (unlikely(wbrfd.oflags & ~valid)) + goto out; + } + + fd = get_unused_fd_flags(0); + err = fd; + if (unlikely(fd < 0)) + goto out; + + h_file = ERR_PTR(-EINVAL); + wbi = 0; + br = NULL; + sb = path->dentry->d_sb; + root = sb->s_root; + aufs_read_lock(root, AuLock_IR); + bbot = au_sbbot(sb); + if (wbrfd.brid >= 0) { + wbi = au_br_index(sb, wbrfd.brid); + if (unlikely(wbi < 0 || wbi > bbot)) + goto out_unlock; + } + + h_file = ERR_PTR(-ENOENT); + br = au_sbr(sb, wbi); + if (!au_br_writable(br->br_perm)) { + if (arg) + goto out_unlock; + + bindex = wbi + 1; + wbi = -1; + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm)) { + wbi = bindex; + br = au_sbr(sb, wbi); + break; + } + } + } + AuDbg("wbi %d\n", wbi); + if (wbi >= 0) + h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, + /*force_wr*/0); + +out_unlock: + aufs_read_unlock(root, AuLock_IR); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out_fd; + + au_lcnt_dec(&br->br_nfiles); /* cf. au_h_open() */ + fd_install(fd, h_file); + err = fd; + goto out; /* success */ + +out_fd: + put_unused_fd(fd); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err; + struct dentry *dentry; + + switch (cmd) { + case AUFS_CTL_RDU: + case AUFS_CTL_RDU_INO: + err = au_rdu_ioctl(file, cmd, arg); + break; + + case AUFS_CTL_WBR_FD: + err = au_wbr_fd(&file->f_path, (void __user *)arg); + break; + + case AUFS_CTL_IBUSY: + err = au_ibusy_ioctl(file, arg); + break; + + case AUFS_CTL_BRINFO: + err = au_brinfo_ioctl(file, arg); + break; + + case AUFS_CTL_FHSM_FD: + dentry = file->f_path.dentry; + if (IS_ROOT(dentry)) + err = au_fhsm_fd(dentry->d_sb, arg); + else + err = -ENOTTY; + break; + + default: + /* do not call the lower */ + AuDbg("0x%x\n", cmd); + err = -ENOTTY; + } + + AuTraceErr(err); + return err; +} + +long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err; + + switch (cmd) { + case AUFS_CTL_MVDOWN: + err = au_mvdown(file->f_path.dentry, (void __user *)arg); + break; + + case AUFS_CTL_WBR_FD: + err = au_wbr_fd(&file->f_path, (void __user *)arg); + break; + + default: + /* do not call the lower */ + AuDbg("0x%x\n", cmd); + err = -ENOTTY; + } + + AuTraceErr(err); + return err; +} + +#ifdef CONFIG_COMPAT +long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err; + + switch (cmd) { + case AUFS_CTL_RDU: + case AUFS_CTL_RDU_INO: + err = au_rdu_compat_ioctl(file, cmd, arg); + break; + + case AUFS_CTL_IBUSY: + err = au_ibusy_compat_ioctl(file, arg); + break; + + case AUFS_CTL_BRINFO: + err = au_brinfo_compat_ioctl(file, arg); + break; + + default: + err = aufs_ioctl_dir(file, cmd, arg); + } + + AuTraceErr(err); + return err; +} + +long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif diff -Naur --no-dereference a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c --- a/fs/aufs/i_op_add.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/i_op_add.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,936 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations (add entry) + */ + +#include +#include "aufs.h" + +/* + * final procedure of adding a new entry, except link(2). + * remove whiteout, instantiate, copyup the parent dir's times and size + * and update version. + * if it failed, re-create the removed whiteout. + */ +static int epilog(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct dentry *dentry) +{ + int err, rerr; + aufs_bindex_t bwh; + struct path h_path; + struct super_block *sb; + struct inode *inode, *h_dir; + struct dentry *wh; + + bwh = -1; + sb = dir->i_sb; + if (wh_dentry) { + h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ + IMustLock(h_dir); + AuDebugOn(au_h_iptr(dir, bindex) != h_dir); + bwh = au_dbwh(dentry); + h_path.dentry = wh_dentry; + h_path.mnt = au_sbr_mnt(sb, bindex); + err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, + dentry); + if (unlikely(err)) + goto out; + } + + inode = au_new_inode(dentry, /*must_new*/1); + if (!IS_ERR(inode)) { + d_instantiate(dentry, inode); + dir = d_inode(dentry->d_parent); /* dir inode is locked */ + IMustLock(dir); + au_dir_ts(dir, bindex); + inode_inc_iversion(dir); + au_fhsm_wrote(sb, bindex, /*force*/0); + return 0; /* success */ + } + + err = PTR_ERR(inode); + if (!wh_dentry) + goto out; + + /* revert */ + /* dir inode is locked */ + wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); + rerr = PTR_ERR(wh); + if (IS_ERR(wh)) { + AuIOErr("%pd reverting whiteout failed(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } else + dput(wh); + +out: + return err; +} + +static int au_d_may_add(struct dentry *dentry) +{ + int err; + + err = 0; + if (unlikely(d_unhashed(dentry))) + err = -ENOENT; + if (unlikely(d_really_is_positive(dentry))) + err = -EEXIST; + return err; +} + +/* + * simple tests for the adding inode operations. + * following the checks in vfs, plus the parent-child relationship. + */ +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir) +{ + int err; + umode_t h_mode; + struct dentry *h_dentry; + struct inode *h_inode; + + err = -ENAMETOOLONG; + if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + goto out; + + h_dentry = au_h_dptr(dentry, bindex); + if (d_really_is_negative(dentry)) { + err = -EEXIST; + if (unlikely(d_is_positive(h_dentry))) + goto out; + } else { + /* rename(2) case */ + err = -EIO; + if (unlikely(d_is_negative(h_dentry))) + goto out; + h_inode = d_inode(h_dentry); + if (unlikely(!h_inode->i_nlink)) + goto out; + + h_mode = h_inode->i_mode; + if (!isdir) { + err = -EISDIR; + if (unlikely(S_ISDIR(h_mode))) + goto out; + } else if (unlikely(!S_ISDIR(h_mode))) { + err = -ENOTDIR; + goto out; + } + } + + err = 0; + /* expected parent dir is locked */ + if (unlikely(h_parent != h_dentry->d_parent)) + err = -EIO; + +out: + AuTraceErr(err); + return err; +} + +/* + * initial procedure of adding a new entry. + * prepare writable branch and the parent dir, lock it, + * and lookup whiteout for the new entry. + */ +static struct dentry* +lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, + struct dentry *src_dentry, struct au_pin *pin, + struct au_wr_dir_args *wr_dir_args) +{ + struct dentry *wh_dentry, *h_parent; + struct super_block *sb; + struct au_branch *br; + int err; + unsigned int udba; + aufs_bindex_t bcpup; + + AuDbg("%pd\n", dentry); + + err = au_wr_dir(dentry, src_dentry, wr_dir_args); + bcpup = err; + wh_dentry = ERR_PTR(err); + if (unlikely(err < 0)) + goto out; + + sb = dentry->d_sb; + udba = au_opt_udba(sb); + err = au_pin(pin, dentry, bcpup, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out; + + h_parent = au_pinned_h_parent(pin); + if (udba != AuOpt_UDBA_NONE + && au_dbtop(dentry) == bcpup) + err = au_may_add(dentry, bcpup, h_parent, + au_ftest_wrdir(wr_dir_args->flags, ISDIR)); + else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + err = -ENAMETOOLONG; + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_unpin; + + br = au_sbr(sb, bcpup); + if (dt) { + struct path tmp = { + .dentry = h_parent, + .mnt = au_br_mnt(br) + }; + au_dtime_store(dt, au_pinned_parent(pin), &tmp); + } + + wh_dentry = NULL; + if (bcpup != au_dbwh(dentry)) + goto out; /* success */ + + /* + * ENAMETOOLONG here means that if we allowed create such name, then it + * would not be able to removed in the future. So we don't allow such + * name here and we don't handle ENAMETOOLONG differently here. + */ + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); + +out_unpin: + if (IS_ERR(wh_dentry)) + au_unpin(pin); +out: + return wh_dentry; +} + +/* ---------------------------------------------------------------------- */ + +enum { Mknod, Symlink, Creat }; +struct simple_arg { + int type; + union { + struct { + umode_t mode; + bool want_excl; + bool try_aopen; + struct vfsub_aopen_args *aopen; + } c; + struct { + const char *symname; + } s; + struct { + umode_t mode; + dev_t dev; + } m; + } u; +}; + +static int add_simple(struct inode *dir, struct dentry *dentry, + struct simple_arg *arg) +{ + int err, rerr; + aufs_bindex_t btop; + unsigned char created; + const unsigned char try_aopen + = (arg->type == Creat && arg->u.c.try_aopen); + struct vfsub_aopen_args *aopen = arg->u.c.aopen; + struct dentry *wh_dentry, *parent; + struct inode *h_dir; + struct super_block *sb; + struct au_branch *br; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + struct path h_path; + struct au_wr_dir_args wr_dir_args; + } *a; + + AuDbg("%pd\n", dentry); + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->wr_dir_args.force_btgt = -1; + a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; + + parent = dentry->d_parent; /* dir inode is locked */ + if (!try_aopen) { + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + } + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + if (!try_aopen) + di_write_lock_parent(parent); + wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, + &a->pin, &a->wr_dir_args); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + btop = au_dbtop(dentry); + sb = dentry->d_sb; + br = au_sbr(sb, btop); + a->h_path.dentry = au_h_dptr(dentry, btop); + a->h_path.mnt = au_br_mnt(br); + h_dir = au_pinned_h_dir(&a->pin); + switch (arg->type) { + case Creat: + if (!try_aopen || !h_dir->i_op->atomic_open) { + err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, + arg->u.c.want_excl); + created = !err; + if (!err && try_aopen) + aopen->file->f_mode |= FMODE_CREATED; + } else { + aopen->br = br; + err = vfsub_atomic_open(h_dir, a->h_path.dentry, aopen); + AuDbg("err %d\n", err); + AuDbgFile(aopen->file); + created = err >= 0 + && !!(aopen->file->f_mode & FMODE_CREATED); + } + break; + case Symlink: + err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); + created = !err; + break; + case Mknod: + err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, + arg->u.m.dev); + created = !err; + break; + default: + BUG(); + } + if (unlikely(err < 0)) + goto out_unpin; + + err = epilog(dir, btop, wh_dentry, dentry); + if (!err) + goto out_unpin; /* success */ + + /* revert */ + if (created /* && d_is_positive(a->h_path.dentry) */) { + /* no delegation since it is just created */ + rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, + /*force*/0); + if (rerr) { + AuIOErr("%pd revert failure(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } + au_dtime_revert(&a->dt); + } + if (try_aopen && h_dir->i_op->atomic_open + && (aopen->file->f_mode & FMODE_OPENED)) + /* aopen->file is still opened */ + au_lcnt_dec(&aopen->br->br_nfiles); + +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); +out_parent: + if (!try_aopen) + di_write_unlock(parent); +out_unlock: + if (unlikely(err)) { + au_update_dbtop(dentry); + d_drop(dentry); + } + if (!try_aopen) + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + return err; +} + +int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t dev) +{ + struct simple_arg arg = { + .type = Mknod, + .u.m = { + .mode = mode, + .dev = dev + } + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct simple_arg arg = { + .type = Symlink, + .u.s.symname = symname + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl) +{ + struct simple_arg arg = { + .type = Creat, + .u.c = { + .mode = mode, + .want_excl = want_excl + } + }; + return add_simple(dir, dentry, &arg); +} + +int au_aopen_or_create(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *aopen_args) +{ + struct simple_arg arg = { + .type = Creat, + .u.c = { + .mode = aopen_args->create_mode, + .want_excl = aopen_args->open_flag & O_EXCL, + .try_aopen = true, + .aopen = aopen_args + } + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct dentry *parent, *h_parent, *h_dentry; + struct inode *h_dir, *inode; + struct vfsmount *h_mnt; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = AuWrDir_TMPFILE + }; + + /* copy-up may happen */ + inode_lock(dir); + + sb = dir->i_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + + err = au_di_init(dentry); + if (unlikely(err)) + goto out_si; + + err = -EBUSY; + parent = d_find_any_alias(dir); + AuDebugOn(!parent); + di_write_lock_parent(parent); + if (unlikely(d_inode(parent) != dir)) + goto out_parent; + + err = au_digen_test(parent, au_sigen(sb)); + if (unlikely(err)) + goto out_parent; + + bindex = au_dbtop(parent); + au_set_dbtop(dentry, bindex); + au_set_dbbot(dentry, bindex); + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + bindex = err; + if (unlikely(err < 0)) + goto out_parent; + + err = -EOPNOTSUPP; + h_dir = au_h_iptr(dir, bindex); + if (unlikely(!h_dir->i_op->tmpfile)) + goto out_parent; + + h_mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mnt_want_write(h_mnt); + if (unlikely(err)) + goto out_parent; + + h_parent = au_h_dptr(parent, bindex); + h_dentry = vfs_tmpfile(h_parent, mode, /*open_flag*/0); + if (IS_ERR(h_dentry)) { + err = PTR_ERR(h_dentry); + goto out_mnt; + } + + au_set_dbtop(dentry, bindex); + au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, dget(h_dentry)); + inode = au_new_inode(dentry, /*must_new*/1); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + au_set_h_dptr(dentry, bindex, NULL); + au_set_dbtop(dentry, -1); + au_set_dbbot(dentry, -1); + } else { + if (!inode->i_nlink) + set_nlink(inode, 1); + d_tmpfile(dentry, inode); + au_di(dentry)->di_tmpfile = 1; + + /* update without i_mutex */ + if (au_ibtop(dir) == au_dbtop(dentry)) + au_cpup_attr_timesizes(dir); + } + dput(h_dentry); + +out_mnt: + vfsub_mnt_drop_write(h_mnt); +out_parent: + di_write_unlock(parent); + dput(parent); + di_write_unlock(dentry); + if (unlikely(err)) { + au_di_fin(dentry); + dentry->d_fsdata = NULL; + } +out_si: + si_read_unlock(sb); +out: + inode_unlock(dir); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_link_args { + aufs_bindex_t bdst, bsrc; + struct au_pin pin; + struct path h_path; + struct dentry *src_parent, *parent; +}; + +static int au_cpup_before_link(struct dentry *src_dentry, + struct au_link_args *a) +{ + int err; + struct dentry *h_src_dentry; + struct au_cp_generic cpg = { + .dentry = src_dentry, + .bdst = a->bdst, + .bsrc = a->bsrc, + .len = -1, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ + }; + + di_read_lock_parent(a->src_parent, AuLock_IR); + err = au_test_and_cpup_dirs(src_dentry, a->bdst); + if (unlikely(err)) + goto out; + + h_src_dentry = au_h_dptr(src_dentry, a->bsrc); + err = au_pin(&a->pin, src_dentry, a->bdst, + au_opt_udba(src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out; + + err = au_sio_cpup_simple(&cpg); + au_unpin(&a->pin); + +out: + di_read_unlock(a->src_parent, AuLock_IR); + return err; +} + +static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, + struct au_link_args *a) +{ + int err; + unsigned char plink; + aufs_bindex_t bbot; + struct dentry *h_src_dentry; + struct inode *h_inode, *inode, *delegated; + struct super_block *sb; + struct file *h_file; + + plink = 0; + h_inode = NULL; + sb = src_dentry->d_sb; + inode = d_inode(src_dentry); + if (au_ibtop(inode) <= a->bdst) + h_inode = au_h_iptr(inode, a->bdst); + if (!h_inode || !h_inode->i_nlink) { + /* copyup src_dentry as the name of dentry. */ + bbot = au_dbbot(dentry); + if (bbot < a->bsrc) + au_set_dbbot(dentry, a->bsrc); + au_set_h_dptr(dentry, a->bsrc, + dget(au_h_dptr(src_dentry, a->bsrc))); + dget(a->h_path.dentry); + au_set_h_dptr(dentry, a->bdst, NULL); + AuDbg("temporary d_inode...\n"); + spin_lock(&dentry->d_lock); + dentry->d_inode = d_inode(src_dentry); /* tmp */ + spin_unlock(&dentry->d_lock); + h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); + if (IS_ERR(h_file)) + err = PTR_ERR(h_file); + else { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->bdst, + .bsrc = -1, + .len = -1, + .pin = &a->pin, + .flags = AuCpup_KEEPLINO + }; + err = au_sio_cpup_simple(&cpg); + au_h_open_post(dentry, a->bsrc, h_file); + if (!err) { + dput(a->h_path.dentry); + a->h_path.dentry = au_h_dptr(dentry, a->bdst); + } else + au_set_h_dptr(dentry, a->bdst, + a->h_path.dentry); + } + spin_lock(&dentry->d_lock); + dentry->d_inode = NULL; /* restore */ + spin_unlock(&dentry->d_lock); + AuDbg("temporary d_inode...done\n"); + au_set_h_dptr(dentry, a->bsrc, NULL); + au_set_dbbot(dentry, bbot); + } else { + /* the inode of src_dentry already exists on a.bdst branch */ + h_src_dentry = d_find_alias(h_inode); + if (!h_src_dentry && au_plink_test(inode)) { + plink = 1; + h_src_dentry = au_plink_lkup(inode, a->bdst); + err = PTR_ERR(h_src_dentry); + if (IS_ERR(h_src_dentry)) + goto out; + + if (unlikely(d_is_negative(h_src_dentry))) { + dput(h_src_dentry); + h_src_dentry = NULL; + } + + } + if (h_src_dentry) { + delegated = NULL; + err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), + &a->h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + dput(h_src_dentry); + } else { + AuIOErr("no dentry found for hi%lu on b%d\n", + h_inode->i_ino, a->bdst); + err = -EIO; + } + } + + if (!err && !plink) + au_plink_append(inode, a->bdst, a->h_path.dentry); + +out: + AuTraceErr(err); + return err; +} + +int aufs_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry) +{ + int err, rerr; + struct au_dtime dt; + struct au_link_args *a; + struct dentry *wh_dentry, *h_src_dentry; + struct inode *inode, *delegated; + struct super_block *sb; + struct au_wr_dir_args wr_dir_args = { + /* .force_btgt = -1, */ + .flags = AuWrDir_ADD_ENTRY + }; + + IMustLock(dir); + inode = d_inode(src_dentry); + IMustLock(inode); + + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + a->parent = dentry->d_parent; /* dir inode is locked */ + err = aufs_read_and_write_lock2(dentry, src_dentry, + AuLock_NOPLM | AuLock_GEN); + if (unlikely(err)) + goto out_kfree; + err = au_d_linkable(src_dentry); + if (unlikely(err)) + goto out_unlock; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + + a->src_parent = dget_parent(src_dentry); + wr_dir_args.force_btgt = au_ibtop(inode); + + di_write_lock_parent(a->parent); + wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); + wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, + &wr_dir_args); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + err = 0; + sb = dentry->d_sb; + a->bdst = au_dbtop(dentry); + a->h_path.dentry = au_h_dptr(dentry, a->bdst); + a->h_path.mnt = au_sbr_mnt(sb, a->bdst); + a->bsrc = au_ibtop(inode); + h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); + if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) + h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); + if (!h_src_dentry) { + a->bsrc = au_dbtop(src_dentry); + h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); + AuDebugOn(!h_src_dentry); + } else if (IS_ERR(h_src_dentry)) { + err = PTR_ERR(h_src_dentry); + goto out_parent; + } + + /* + * aufs doesn't touch the credential so + * security_dentry_create_files_as() is unnecessary. + */ + if (au_opt_test(au_mntflags(sb), PLINK)) { + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) + err = au_cpup_or_link(src_dentry, dentry, a); + else { + delegated = NULL; + err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), + &a->h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + } + dput(h_src_dentry); + } else { + /* + * copyup src_dentry to the branch we process, + * and then link(2) to it. + */ + dput(h_src_dentry); + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { + au_unpin(&a->pin); + di_write_unlock(a->parent); + err = au_cpup_before_link(src_dentry, a); + di_write_lock_parent(a->parent); + if (!err) + err = au_pin(&a->pin, dentry, a->bdst, + au_opt_udba(sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_wh; + } + if (!err) { + h_src_dentry = au_h_dptr(src_dentry, a->bdst); + err = -ENOENT; + if (h_src_dentry && d_is_positive(h_src_dentry)) { + delegated = NULL; + err = vfsub_link(h_src_dentry, + au_pinned_h_dir(&a->pin), + &a->h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry" + " for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + } + } + } + if (unlikely(err)) + goto out_unpin; + + if (wh_dentry) { + a->h_path.dentry = wh_dentry; + err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, + dentry); + if (unlikely(err)) + goto out_revert; + } + + au_dir_ts(dir, a->bdst); + inode_inc_iversion(dir); + inc_nlink(inode); + inode->i_ctime = dir->i_ctime; + d_instantiate(dentry, au_igrab(inode)); + if (d_unhashed(a->h_path.dentry)) + /* some filesystem calls d_drop() */ + d_drop(dentry); + /* some filesystems consume an inode even hardlink */ + au_fhsm_wrote(sb, a->bdst, /*force*/0); + goto out_unpin; /* success */ + +out_revert: + /* no delegation since it is just created */ + rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, + /*delegated*/NULL, /*force*/0); + if (unlikely(rerr)) { + AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); + err = -EIO; + } + au_dtime_revert(&dt); +out_unpin: + au_unpin(&a->pin); +out_wh: + dput(wh_dentry); +out_parent: + di_write_unlock(a->parent); + dput(a->src_parent); +out_unlock: + if (unlikely(err)) { + au_update_dbtop(dentry); + d_drop(dentry); + } + aufs_read_and_write_unlock2(dentry, src_dentry); +out_kfree: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} + +int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int err, rerr; + aufs_bindex_t bindex; + unsigned char diropq; + struct path h_path; + struct dentry *wh_dentry, *parent, *opq_dentry; + struct inode *h_inode; + struct super_block *sb; + struct { + struct au_pin pin; + struct au_dtime dt; + } *a; /* reduce the stack usage */ + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR + }; + + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, + &a->pin, &wr_dir_args); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + sb = dentry->d_sb; + bindex = au_dbtop(dentry); + h_path.dentry = au_h_dptr(dentry, bindex); + h_path.mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); + if (unlikely(err)) + goto out_unpin; + + /* make the dir opaque */ + diropq = 0; + h_inode = d_inode(h_path.dentry); + if (wh_dentry + || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { + inode_lock_nested(h_inode, AuLsc_I_CHILD); + opq_dentry = au_diropq_create(dentry, bindex); + inode_unlock(h_inode); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) + goto out_dir; + dput(opq_dentry); + diropq = 1; + } + + err = epilog(dir, bindex, wh_dentry, dentry); + if (!err) { + inc_nlink(dir); + goto out_unpin; /* success */ + } + + /* revert */ + if (diropq) { + AuLabel(revert opq); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bindex); + inode_unlock(h_inode); + if (rerr) { + AuIOErr("%pd reverting diropq failed(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } + } + +out_dir: + AuLabel(revert dir); + rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); + if (rerr) { + AuIOErr("%pd reverting dir failed(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } + au_dtime_revert(&a->dt); +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); +out_parent: + di_write_unlock(parent); +out_unlock: + if (unlikely(err)) { + au_update_dbtop(dentry); + d_drop(dentry); + } + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + return err; +} diff -Naur --no-dereference a/fs/aufs/i_op.c b/fs/aufs/i_op.c --- a/fs/aufs/i_op.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/i_op.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1506 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations (except add/del/rename) + */ + +#include +#include +#include +#include +#include +#include "aufs.h" + +static int h_permission(struct inode *h_inode, int mask, + struct path *h_path, int brperm) +{ + int err; + const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); + + err = -EPERM; + if (write_mask && IS_IMMUTABLE(h_inode)) + goto out; + + err = -EACCES; + if (((mask & MAY_EXEC) + && S_ISREG(h_inode->i_mode) + && (path_noexec(h_path) + || !(h_inode->i_mode & 0111)))) + goto out; + + /* + * - skip the lower fs test in the case of write to ro branch. + * - nfs dir permission write check is optimized, but a policy for + * link/rename requires a real check. + * - nfs always sets SB_POSIXACL regardless its mount option 'noacl.' + * in this case, generic_permission() returns -EOPNOTSUPP. + */ + if ((write_mask && !au_br_writable(brperm)) + || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) + && write_mask && !(mask & MAY_READ)) + || !h_inode->i_op->permission) { + /* AuLabel(generic_permission); */ + /* AuDbg("get_acl %ps\n", h_inode->i_op->get_acl); */ + err = generic_permission(h_inode, mask); + if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) + err = h_inode->i_op->permission(h_inode, mask); + AuTraceErr(err); + } else { + /* AuLabel(h_inode->permission); */ + err = h_inode->i_op->permission(h_inode, mask); + AuTraceErr(err); + } + + if (!err) + err = devcgroup_inode_permission(h_inode, mask); + if (!err) + err = security_inode_permission(h_inode, mask); + +out: + return err; +} + +static int aufs_permission(struct inode *inode, int mask) +{ + int err; + aufs_bindex_t bindex, bbot; + const unsigned char isdir = !!S_ISDIR(inode->i_mode), + write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); + struct inode *h_inode; + struct super_block *sb; + struct au_branch *br; + + /* todo: support rcu-walk? */ + if (mask & MAY_NOT_BLOCK) + return -ECHILD; + + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); +#if 0 /* reserved for future use */ + /* + * This test may be rather 'too much' since the test is essentially done + * in the aufs_lookup(). Theoretically it is possible that the inode + * generation doesn't match to the superblock's here. But it isn't a + * big deal I suppose. + */ + err = au_iigen_test(inode, au_sigen(sb)); + if (unlikely(err)) + goto out; +#endif + + if (!isdir + || write_mask + || au_opt_test(au_mntflags(sb), DIRPERM1)) { + err = au_busy_or_stale(); + h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (unlikely(!h_inode + || (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT))) + goto out; + + err = 0; + bindex = au_ibtop(inode); + br = au_sbr(sb, bindex); + err = h_permission(h_inode, mask, &br->br_path, br->br_perm); + if (write_mask + && !err + && !special_file(h_inode->i_mode)) { + /* test whether the upper writable branch exists */ + err = -EROFS; + for (; bindex >= 0; bindex--) + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = 0; + break; + } + } + goto out; + } + + /* non-write to dir */ + err = 0; + bbot = au_ibbot(inode); + for (bindex = au_ibtop(inode); !err && bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) { + err = au_busy_or_stale(); + if (unlikely(!S_ISDIR(h_inode->i_mode))) + break; + + br = au_sbr(sb, bindex); + err = h_permission(h_inode, mask, &br->br_path, + br->br_perm); + } + } + +out: + ii_read_unlock(inode); + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct dentry *ret, *parent; + struct inode *inode; + struct super_block *sb; + int err, npositive; + + IMustLock(dir); + + /* todo: support rcu-walk? */ + ret = ERR_PTR(-ECHILD); + if (flags & LOOKUP_RCU) + goto out; + + ret = ERR_PTR(-ENAMETOOLONG); + if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + goto out; + + sb = dir->i_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + ret = ERR_PTR(err); + if (unlikely(err)) + goto out; + + err = au_di_init(dentry); + ret = ERR_PTR(err); + if (unlikely(err)) + goto out_si; + + inode = NULL; + npositive = 0; /* suppress a warning */ + parent = dentry->d_parent; /* dir inode is locked */ + di_read_lock_parent(parent, AuLock_IR); + err = au_alive_dir(parent); + if (!err) + err = au_digen_test(parent, au_sigen(sb)); + if (!err) { + /* regardless LOOKUP_CREATE, always ALLOW_NEG */ + npositive = au_lkup_dentry(dentry, au_dbtop(parent), + AuLkup_ALLOW_NEG); + err = npositive; + } + di_read_unlock(parent, AuLock_IR); + ret = ERR_PTR(err); + if (unlikely(err < 0)) + goto out_unlock; + + if (npositive) { + inode = au_new_inode(dentry, /*must_new*/0); + if (IS_ERR(inode)) { + ret = (void *)inode; + inode = NULL; + goto out_unlock; + } + } + + if (inode) + atomic_inc(&inode->i_count); + ret = d_splice_alias(inode, dentry); +#if 0 /* reserved for future use */ + if (unlikely(d_need_lookup(dentry))) { + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_NEED_LOOKUP; + spin_unlock(&dentry->d_lock); + } else +#endif + if (inode) { + if (!IS_ERR(ret)) { + iput(inode); + if (ret && ret != dentry) + ii_write_unlock(inode); + } else { + ii_write_unlock(inode); + iput(inode); + inode = NULL; + } + } + +out_unlock: + di_write_unlock(dentry); +out_si: + si_read_unlock(sb); +out: + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* + * very dirty and complicated aufs ->atomic_open(). + * aufs_atomic_open() + * + au_aopen_or_create() + * + add_simple() + * + vfsub_atomic_open() + * + branch fs ->atomic_open() + * may call the actual 'open' for h_file + * + inc br_nfiles only if opened + * + au_aopen_no_open() or au_aopen_do_open() + * + * au_aopen_do_open() + * + finish_open() + * + au_do_aopen() + * + au_do_open() the body of all 'open' + * + au_do_open_nondir() + * set the passed h_file + * + * au_aopen_no_open() + * + finish_no_open() + */ + +struct aopen_node { + struct hlist_bl_node hblist; + struct file *file, *h_file; +}; + +static int au_do_aopen(struct inode *inode, struct file *file) +{ + struct hlist_bl_head *aopen; + struct hlist_bl_node *pos; + struct aopen_node *node; + struct au_do_open_args args = { + .aopen = 1, + .open = au_do_open_nondir + }; + + aopen = &au_sbi(inode->i_sb)->si_aopen; + hlist_bl_lock(aopen); + hlist_bl_for_each_entry(node, pos, aopen, hblist) + if (node->file == file) { + args.h_file = node->h_file; + break; + } + hlist_bl_unlock(aopen); + /* AuDebugOn(!args.h_file); */ + + return au_do_open(file, &args); +} + +static int au_aopen_do_open(struct file *file, struct dentry *dentry, + struct aopen_node *aopen_node) +{ + int err; + struct hlist_bl_head *aopen; + + AuLabel(here); + aopen = &au_sbi(dentry->d_sb)->si_aopen; + au_hbl_add(&aopen_node->hblist, aopen); + err = finish_open(file, dentry, au_do_aopen); + au_hbl_del(&aopen_node->hblist, aopen); + /* AuDbgFile(file); */ + AuDbg("%pd%s%s\n", dentry, + (file->f_mode & FMODE_CREATED) ? " created" : "", + (file->f_mode & FMODE_OPENED) ? " opened" : ""); + + AuTraceErr(err); + return err; +} + +static int au_aopen_no_open(struct file *file, struct dentry *dentry) +{ + int err; + + AuLabel(here); + dget(dentry); + err = finish_no_open(file, dentry); + + AuTraceErr(err); + return err; +} + +static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, + struct file *file, unsigned int open_flag, + umode_t create_mode) +{ + int err, did_open; + unsigned int lkup_flags; + aufs_bindex_t bindex; + struct super_block *sb; + struct dentry *parent, *d; + struct vfsub_aopen_args args = { + .open_flag = open_flag, + .create_mode = create_mode + }; + struct aopen_node aopen_node = { + .file = file + }; + + IMustLock(dir); + AuDbg("open_flag 0%o\n", open_flag); + AuDbgDentry(dentry); + + err = 0; + if (!au_di(dentry)) { + lkup_flags = LOOKUP_OPEN; + if (open_flag & O_CREAT) + lkup_flags |= LOOKUP_CREATE; + d = aufs_lookup(dir, dentry, lkup_flags); + if (IS_ERR(d)) { + err = PTR_ERR(d); + AuTraceErr(err); + goto out; + } else if (d) { + /* + * obsoleted dentry found. + * another error will be returned later. + */ + d_drop(d); + AuDbgDentry(d); + dput(d); + } + AuDbgDentry(dentry); + } + + if (d_is_positive(dentry) + || d_unhashed(dentry) + || d_unlinked(dentry) + || !(open_flag & O_CREAT)) { + err = au_aopen_no_open(file, dentry); + goto out; /* success */ + } + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) + goto out; + + sb = dentry->d_sb; + parent = dentry->d_parent; /* dir is locked */ + di_write_lock_parent(parent); + err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG); + if (unlikely(err < 0)) + goto out_parent; + + AuDbgDentry(dentry); + if (d_is_positive(dentry)) { + err = au_aopen_no_open(file, dentry); + goto out_parent; /* success */ + } + + args.file = alloc_empty_file(file->f_flags, current_cred()); + err = PTR_ERR(args.file); + if (IS_ERR(args.file)) + goto out_parent; + + bindex = au_dbtop(dentry); + err = au_aopen_or_create(dir, dentry, &args); + AuTraceErr(err); + AuDbgFile(args.file); + file->f_mode = args.file->f_mode & ~FMODE_OPENED; + did_open = !!(args.file->f_mode & FMODE_OPENED); + if (!did_open) { + fput(args.file); + args.file = NULL; + } + di_write_unlock(parent); + di_write_unlock(dentry); + if (unlikely(err < 0)) { + if (args.file) + fput(args.file); + goto out_sb; + } + + if (!did_open) + err = au_aopen_no_open(file, dentry); + else { + aopen_node.h_file = args.file; + err = au_aopen_do_open(file, dentry, &aopen_node); + } + if (unlikely(err < 0)) { + if (args.file) + fput(args.file); + if (did_open) + au_lcnt_dec(&args.br->br_nfiles); + } + goto out_sb; /* success */ + +out_parent: + di_write_unlock(parent); + di_write_unlock(dentry); +out_sb: + si_read_unlock(sb); +out: + AuTraceErr(err); + AuDbgFile(file); + return err; +} + + +/* ---------------------------------------------------------------------- */ + +static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, + const unsigned char add_entry, aufs_bindex_t bcpup, + aufs_bindex_t btop) +{ + int err; + struct dentry *h_parent; + struct inode *h_dir; + + if (add_entry) + IMustLock(d_inode(parent)); + else + di_write_lock_parent(parent); + + err = 0; + if (!au_h_dptr(parent, bcpup)) { + if (btop > bcpup) + err = au_cpup_dirs(dentry, bcpup); + else if (btop < bcpup) + err = au_cpdown_dirs(dentry, bcpup); + else + BUG(); + } + if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { + h_parent = au_h_dptr(parent, bcpup); + h_dir = d_inode(h_parent); + inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + err = au_lkup_neg(dentry, bcpup, /*wh*/0); + /* todo: no unlock here */ + inode_unlock_shared(h_dir); + + AuDbg("bcpup %d\n", bcpup); + if (!err) { + if (d_really_is_negative(dentry)) + au_set_h_dptr(dentry, btop, NULL); + au_update_dbrange(dentry, /*do_put_zero*/0); + } + } + + if (!add_entry) + di_write_unlock(parent); + if (!err) + err = bcpup; /* success */ + + AuTraceErr(err); + return err; +} + +/* + * decide the branch and the parent dir where we will create a new entry. + * returns new bindex or an error. + * copyup the parent dir if needed. + */ +int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, + struct au_wr_dir_args *args) +{ + int err; + unsigned int flags; + aufs_bindex_t bcpup, btop, src_btop; + const unsigned char add_entry + = au_ftest_wrdir(args->flags, ADD_ENTRY) + | au_ftest_wrdir(args->flags, TMPFILE); + struct super_block *sb; + struct dentry *parent; + struct au_sbinfo *sbinfo; + + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + parent = dget_parent(dentry); + btop = au_dbtop(dentry); + bcpup = btop; + if (args->force_btgt < 0) { + if (src_dentry) { + src_btop = au_dbtop(src_dentry); + if (src_btop < btop) + bcpup = src_btop; + } else if (add_entry) { + flags = 0; + if (au_ftest_wrdir(args->flags, ISDIR)) + au_fset_wbr(flags, DIR); + err = AuWbrCreate(sbinfo, dentry, flags); + bcpup = err; + } + + if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) { + if (add_entry) + err = AuWbrCopyup(sbinfo, dentry); + else { + if (!IS_ROOT(dentry)) { + di_read_lock_parent(parent, !AuLock_IR); + err = AuWbrCopyup(sbinfo, dentry); + di_read_unlock(parent, !AuLock_IR); + } else + err = AuWbrCopyup(sbinfo, dentry); + } + bcpup = err; + if (unlikely(err < 0)) + goto out; + } + } else { + bcpup = args->force_btgt; + AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); + } + + AuDbg("btop %d, bcpup %d\n", btop, bcpup); + err = bcpup; + if (bcpup == btop) + goto out; /* success */ + + /* copyup the new parent into the branch we process */ + err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, btop); + if (err >= 0) { + if (d_really_is_negative(dentry)) { + au_set_h_dptr(dentry, btop, NULL); + au_set_dbtop(dentry, bcpup); + au_set_dbbot(dentry, bcpup); + } + AuDebugOn(add_entry + && !au_ftest_wrdir(args->flags, TMPFILE) + && !au_h_dptr(dentry, bcpup)); + } + +out: + dput(parent); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_pin_hdir_unlock(struct au_pin *p) +{ + if (p->hdir) + au_hn_inode_unlock(p->hdir); +} + +int au_pin_hdir_lock(struct au_pin *p) +{ + int err; + + err = 0; + if (!p->hdir) + goto out; + + /* even if an error happens later, keep this lock */ + au_hn_inode_lock_nested(p->hdir, p->lsc_hi); + + err = -EBUSY; + if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) + goto out; + + err = 0; + if (p->h_dentry) + err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, + p->h_parent, p->br); + +out: + return err; +} + +int au_pin_hdir_relock(struct au_pin *p) +{ + int err, i; + struct inode *h_i; + struct dentry *h_d[] = { + p->h_dentry, + p->h_parent + }; + + err = au_pin_hdir_lock(p); + if (unlikely(err)) + goto out; + + for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { + if (!h_d[i]) + continue; + if (d_is_positive(h_d[i])) { + h_i = d_inode(h_d[i]); + err = !h_i->i_nlink; + } + } + +out: + return err; +} + +static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) +{ +#if IS_ENABLED(CONFIG_PREEMPT_RT) + p->hdir->hi_inode->i_rwsem.rtmutex.owner = task; +#else + atomic_long_set(&p->hdir->hi_inode->i_rwsem.owner, (long)task); +#endif +} + +void au_pin_hdir_acquire_nest(struct au_pin *p) +{ + if (p->hdir) { + rwsem_acquire_nest(&p->hdir->hi_inode->i_rwsem.dep_map, + p->lsc_hi, 0, NULL, _RET_IP_); + au_pin_hdir_set_owner(p, current); + } +} + +void au_pin_hdir_release(struct au_pin *p) +{ + if (p->hdir) { + au_pin_hdir_set_owner(p, p->task); + rwsem_release(&p->hdir->hi_inode->i_rwsem.dep_map, _RET_IP_); + } +} + +struct dentry *au_pinned_h_parent(struct au_pin *pin) +{ + if (pin && pin->parent) + return au_h_dptr(pin->parent, pin->bindex); + return NULL; +} + +void au_unpin(struct au_pin *p) +{ + if (p->hdir) + au_pin_hdir_unlock(p); + if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) + vfsub_mnt_drop_write(p->h_mnt); + if (!p->hdir) + return; + + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + iput(p->hdir->hi_inode); + dput(p->parent); + p->parent = NULL; + p->hdir = NULL; + p->h_mnt = NULL; + /* do not clear p->task */ +} + +int au_do_pin(struct au_pin *p) +{ + int err; + struct super_block *sb; + struct inode *h_dir; + + err = 0; + sb = p->dentry->d_sb; + p->br = au_sbr(sb, p->bindex); + if (IS_ROOT(p->dentry)) { + if (au_ftest_pin(p->flags, MNT_WRITE)) { + p->h_mnt = au_br_mnt(p->br); + err = vfsub_mnt_want_write(p->h_mnt); + if (unlikely(err)) { + au_fclr_pin(p->flags, MNT_WRITE); + goto out_err; + } + } + goto out; + } + + p->h_dentry = NULL; + if (p->bindex <= au_dbbot(p->dentry)) + p->h_dentry = au_h_dptr(p->dentry, p->bindex); + + p->parent = dget_parent(p->dentry); + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_lock(p->parent, AuLock_IR, p->lsc_di); + + h_dir = NULL; + p->h_parent = au_h_dptr(p->parent, p->bindex); + p->hdir = au_hi(d_inode(p->parent), p->bindex); + if (p->hdir) + h_dir = p->hdir->hi_inode; + + /* + * udba case, or + * if DI_LOCKED is not set, then p->parent may be different + * and h_parent can be NULL. + */ + if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { + err = -EBUSY; + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + dput(p->parent); + p->parent = NULL; + goto out_err; + } + + if (au_ftest_pin(p->flags, MNT_WRITE)) { + p->h_mnt = au_br_mnt(p->br); + err = vfsub_mnt_want_write(p->h_mnt); + if (unlikely(err)) { + au_fclr_pin(p->flags, MNT_WRITE); + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + dput(p->parent); + p->parent = NULL; + goto out_err; + } + } + + au_igrab(h_dir); + err = au_pin_hdir_lock(p); + if (!err) + goto out; /* success */ + + au_unpin(p); + +out_err: + pr_err("err %d\n", err); + err = au_busy_or_stale(); +out: + return err; +} + +void au_pin_init(struct au_pin *p, struct dentry *dentry, + aufs_bindex_t bindex, int lsc_di, int lsc_hi, + unsigned int udba, unsigned char flags) +{ + p->dentry = dentry; + p->udba = udba; + p->lsc_di = lsc_di; + p->lsc_hi = lsc_hi; + p->flags = flags; + p->bindex = bindex; + + p->parent = NULL; + p->hdir = NULL; + p->h_mnt = NULL; + + p->h_dentry = NULL; + p->h_parent = NULL; + p->br = NULL; + p->task = current; +} + +int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, + unsigned int udba, unsigned char flags) +{ + au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, + udba, flags); + return au_do_pin(pin); +} + +/* ---------------------------------------------------------------------- */ + +/* + * ->setattr() and ->getattr() are called in various cases. + * chmod, stat: dentry is revalidated. + * fchmod, fstat: file and dentry are not revalidated, additionally they may be + * unhashed. + * for ->setattr(), ia->ia_file is passed from ftruncate only. + */ +/* todo: consolidate with do_refresh() and simple_reval_dpath() */ +int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) +{ + int err; + struct dentry *parent; + + err = 0; + if (au_digen_test(dentry, sigen)) { + parent = dget_parent(dentry); + di_read_lock_parent(parent, AuLock_IR); + err = au_refresh_dentry(dentry, parent); + di_read_unlock(parent, AuLock_IR); + dput(parent); + } + + AuTraceErr(err); + return err; +} + +int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, + struct au_icpup_args *a) +{ + int err; + loff_t sz; + aufs_bindex_t btop, ibtop; + struct dentry *hi_wh, *parent; + struct inode *inode; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = 0 + }; + + if (d_is_dir(dentry)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + /* plink or hi_wh() case */ + btop = au_dbtop(dentry); + inode = d_inode(dentry); + ibtop = au_ibtop(inode); + if (btop != ibtop && !au_test_ro(inode->i_sb, ibtop, inode)) + wr_dir_args.force_btgt = ibtop; + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + if (unlikely(err < 0)) + goto out; + a->btgt = err; + if (err != btop) + au_fset_icpup(a->flags, DID_CPUP); + + err = 0; + a->pin_flags = AuPin_MNT_WRITE; + parent = NULL; + if (!IS_ROOT(dentry)) { + au_fset_pin(a->pin_flags, DI_LOCKED); + parent = dget_parent(dentry); + di_write_lock_parent(parent); + } + + err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); + if (unlikely(err)) + goto out_parent; + + sz = -1; + a->h_path.dentry = au_h_dptr(dentry, btop); + a->h_inode = d_inode(a->h_path.dentry); + if (ia && (ia->ia_valid & ATTR_SIZE)) { + inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (ia->ia_size < i_size_read(a->h_inode)) + sz = ia->ia_size; + inode_unlock_shared(a->h_inode); + } + + hi_wh = NULL; + if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { + hi_wh = au_hi_wh(inode, a->btgt); + if (!hi_wh) { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, + .bsrc = -1, + .len = sz, + .pin = &a->pin + }; + err = au_sio_cpup_wh(&cpg, /*file*/NULL); + if (unlikely(err)) + goto out_unlock; + hi_wh = au_hi_wh(inode, a->btgt); + /* todo: revalidate hi_wh? */ + } + } + + if (parent) { + au_pin_set_parent_lflag(&a->pin, /*lflag*/0); + di_downgrade_lock(parent, AuLock_IR); + dput(parent); + parent = NULL; + } + if (!au_ftest_icpup(a->flags, DID_CPUP)) + goto out; /* success */ + + if (!d_unhashed(dentry)) { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, + .bsrc = btop, + .len = sz, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + err = au_sio_cpup_simple(&cpg); + if (!err) + a->h_path.dentry = au_h_dptr(dentry, a->btgt); + } else if (!hi_wh) + a->h_path.dentry = au_h_dptr(dentry, a->btgt); + else + a->h_path.dentry = hi_wh; /* do not dget here */ + +out_unlock: + a->h_inode = d_inode(a->h_path.dentry); + if (!err) + goto out; /* success */ + au_unpin(&a->pin); +out_parent: + if (parent) { + di_write_unlock(parent); + dput(parent); + } +out: + if (!err) + inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + return err; +} + +static int aufs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err; + struct inode *inode, *delegated; + struct super_block *sb; + struct file *file; + struct au_icpup_args *a; + + inode = d_inode(dentry); + IMustLock(inode); + + err = setattr_prepare(dentry, ia); + if (unlikely(err)) + goto out; + + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + ia->ia_valid &= ~ATTR_MODE; + + file = NULL; + sb = dentry->d_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_kfree; + + if (ia->ia_valid & ATTR_FILE) { + /* currently ftruncate(2) only */ + AuDebugOn(!d_is_reg(dentry)); + file = ia->ia_file; + err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, + /*fi_lsc*/0); + if (unlikely(err)) + goto out_si; + ia->ia_file = au_hf_top(file); + a->udba = AuOpt_UDBA_NONE; + } else { + /* fchmod() doesn't pass ia_file */ + a->udba = au_opt_udba(sb); + di_write_lock_child(dentry); + /* no d_unlinked(), to set UDBA_NONE for root */ + if (d_unhashed(dentry)) + a->udba = AuOpt_UDBA_NONE; + if (a->udba != AuOpt_UDBA_NONE) { + AuDebugOn(IS_ROOT(dentry)); + err = au_reval_for_attr(dentry, au_sigen(sb)); + if (unlikely(err)) + goto out_dentry; + } + } + + err = au_pin_and_icpup(dentry, ia, a); + if (unlikely(err < 0)) + goto out_dentry; + if (au_ftest_icpup(a->flags, DID_CPUP)) { + ia->ia_file = NULL; + ia->ia_valid &= ~ATTR_FILE; + } + + a->h_path.mnt = au_sbr_mnt(sb, a->btgt); + if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) + == (ATTR_MODE | ATTR_CTIME)) { + err = security_path_chmod(&a->h_path, ia->ia_mode); + if (unlikely(err)) + goto out_unlock; + } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) + && (ia->ia_valid & ATTR_CTIME)) { + err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); + if (unlikely(err)) + goto out_unlock; + } + + if (ia->ia_valid & ATTR_SIZE) { + struct file *f; + + if (ia->ia_size < i_size_read(inode)) + /* unmap only */ + truncate_setsize(inode, ia->ia_size); + + f = NULL; + if (ia->ia_valid & ATTR_FILE) + f = ia->ia_file; + inode_unlock(a->h_inode); + err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); + inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + } else { + delegated = NULL; + while (1) { + err = vfsub_notify_change(&a->h_path, ia, &delegated); + if (delegated) { + err = break_deleg_wait(&delegated); + if (!err) + continue; + } + break; + } + } + /* + * regardless aufs 'acl' option setting. + * why don't all acl-aware fs call this func from their ->setattr()? + */ + if (!err && (ia->ia_valid & ATTR_MODE)) + err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); + if (!err) + au_cpup_attr_changeable(inode); + +out_unlock: + inode_unlock(a->h_inode); + au_unpin(&a->pin); + if (unlikely(err)) + au_update_dbtop(dentry); +out_dentry: + di_write_unlock(dentry); + if (file) { + fi_write_unlock(file); + ia->ia_file = file; + ia->ia_valid |= ATTR_FILE; + } +out_si: + si_read_unlock(sb); +out_kfree: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} + +#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) +static int au_h_path_to_set_attr(struct dentry *dentry, + struct au_icpup_args *a, struct path *h_path) +{ + int err; + struct super_block *sb; + + sb = dentry->d_sb; + a->udba = au_opt_udba(sb); + /* no d_unlinked(), to set UDBA_NONE for root */ + if (d_unhashed(dentry)) + a->udba = AuOpt_UDBA_NONE; + if (a->udba != AuOpt_UDBA_NONE) { + AuDebugOn(IS_ROOT(dentry)); + err = au_reval_for_attr(dentry, au_sigen(sb)); + if (unlikely(err)) + goto out; + } + err = au_pin_and_icpup(dentry, /*ia*/NULL, a); + if (unlikely(err < 0)) + goto out; + + h_path->dentry = a->h_path.dentry; + h_path->mnt = au_sbr_mnt(sb, a->btgt); + +out: + return err; +} + +ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, + struct au_sxattr *arg) +{ + int err; + struct path h_path; + struct super_block *sb; + struct au_icpup_args *a; + struct inode *h_inode; + + IMustLock(inode); + + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + sb = dentry->d_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_kfree; + + h_path.dentry = NULL; /* silence gcc */ + di_write_lock_child(dentry); + err = au_h_path_to_set_attr(dentry, a, &h_path); + if (unlikely(err)) + goto out_di; + + inode_unlock(a->h_inode); + switch (arg->type) { + case AU_XATTR_SET: + AuDebugOn(d_is_negative(h_path.dentry)); + err = vfsub_setxattr(h_path.dentry, + arg->u.set.name, arg->u.set.value, + arg->u.set.size, arg->u.set.flags); + break; + case AU_ACL_SET: + err = -EOPNOTSUPP; + h_inode = d_inode(h_path.dentry); + if (h_inode->i_op->set_acl) + /* this will call posix_acl_update_mode */ + err = h_inode->i_op->set_acl(h_inode, + arg->u.acl_set.acl, + arg->u.acl_set.type); + break; + } + if (!err) + au_cpup_attr_timesizes(inode); + + au_unpin(&a->pin); + if (unlikely(err)) + au_update_dbtop(dentry); + +out_di: + di_write_unlock(dentry); + si_read_unlock(sb); +out_kfree: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} +#endif + +static void au_refresh_iattr(struct inode *inode, struct kstat *st, + unsigned int nlink) +{ + unsigned int n; + + inode->i_mode = st->mode; + /* don't i_[ug]id_write() here */ + inode->i_uid = st->uid; + inode->i_gid = st->gid; + inode->i_atime = st->atime; + inode->i_mtime = st->mtime; + inode->i_ctime = st->ctime; + + au_cpup_attr_nlink(inode, /*force*/0); + if (S_ISDIR(inode->i_mode)) { + n = inode->i_nlink; + n -= nlink; + n += st->nlink; + smp_mb(); /* for i_nlink */ + /* 0 can happen */ + set_nlink(inode, n); + } + + spin_lock(&inode->i_lock); + inode->i_blocks = st->blocks; + i_size_write(inode, st->size); + spin_unlock(&inode->i_lock); +} + +/* + * common routine for aufs_getattr() and au_getxattr(). + * returns zero or negative (an error). + * @dentry will be read-locked in success. + */ +int au_h_path_getattr(struct dentry *dentry, struct inode *inode, int force, + struct path *h_path, int locked) +{ + int err; + unsigned int mnt_flags, sigen; + unsigned char udba_none; + aufs_bindex_t bindex; + struct super_block *sb, *h_sb; + + h_path->mnt = NULL; + h_path->dentry = NULL; + + err = 0; + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); + + if (unlikely(locked)) + goto body; /* skip locking dinfo */ + + /* support fstat(2) */ + if (!d_unlinked(dentry) && !udba_none) { + sigen = au_sigen(sb); + err = au_digen_test(dentry, sigen); + if (!err) { + di_read_lock_child(dentry, AuLock_IR); + err = au_dbrange_test(dentry); + if (unlikely(err)) { + di_read_unlock(dentry, AuLock_IR); + goto out; + } + } else { + AuDebugOn(IS_ROOT(dentry)); + di_write_lock_child(dentry); + err = au_dbrange_test(dentry); + if (!err) + err = au_reval_for_attr(dentry, sigen); + if (!err) + di_downgrade_lock(dentry, AuLock_IR); + else { + di_write_unlock(dentry); + goto out; + } + } + } else + di_read_lock_child(dentry, AuLock_IR); + +body: + if (!inode) { + inode = d_inode(dentry); + if (unlikely(!inode)) + goto out; + } + bindex = au_ibtop(inode); + h_path->mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path->mnt->mnt_sb; + if (!force + && !au_test_fs_bad_iattr(h_sb) + && udba_none) + goto out; /* success */ + + if (au_dbtop(dentry) == bindex) + h_path->dentry = au_h_dptr(dentry, bindex); + else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { + h_path->dentry = au_plink_lkup(inode, bindex); + if (IS_ERR(h_path->dentry)) + /* pretending success */ + h_path->dentry = NULL; + else + dput(h_path->dentry); + } + +out: + return err; +} + +static int aufs_getattr(const struct path *path, struct kstat *st, + u32 request, unsigned int query) +{ + int err; + unsigned char positive; + struct path h_path; + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; + + dentry = path->dentry; + inode = d_inode(dentry); + sb = dentry->d_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + err = au_h_path_getattr(dentry, /*inode*/NULL, /*force*/0, &h_path, + /*locked*/0); + if (unlikely(err)) + goto out_si; + if (unlikely(!h_path.dentry)) + /* illegally overlapped or something */ + goto out_fill; /* pretending success */ + + positive = d_is_positive(h_path.dentry); + if (positive) + /* no vfsub version */ + err = vfs_getattr(&h_path, st, request, query); + if (!err) { + if (positive) + au_refresh_iattr(inode, st, + d_inode(h_path.dentry)->i_nlink); + goto out_fill; /* success */ + } + AuTraceErr(err); + goto out_di; + +out_fill: + generic_fillattr(inode, st); +out_di: + di_read_unlock(dentry, AuLock_IR); +out_si: + si_read_unlock(sb); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static const char *aufs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + const char *ret; + struct dentry *h_dentry; + struct inode *h_inode; + int err; + aufs_bindex_t bindex; + + ret = NULL; /* suppress a warning */ + err = -ECHILD; + if (!dentry) + goto out; + + err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); + if (unlikely(err)) + goto out; + + err = au_d_hashed_positive(dentry); + if (unlikely(err)) + goto out_unlock; + + err = -EINVAL; + inode = d_inode(dentry); + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (unlikely(!h_inode->i_op->get_link)) + goto out_unlock; + + err = -EBUSY; + h_dentry = NULL; + if (au_dbtop(dentry) <= bindex) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + dget(h_dentry); + } + if (!h_dentry) { + h_dentry = d_find_any_alias(h_inode); + if (IS_ERR(h_dentry)) { + err = PTR_ERR(h_dentry); + goto out_unlock; + } + } + if (unlikely(!h_dentry)) + goto out_unlock; + + err = 0; + AuDbg("%ps\n", h_inode->i_op->get_link); + AuDbgDentry(h_dentry); + ret = vfs_get_link(h_dentry, done); + dput(h_dentry); + if (IS_ERR(ret)) + err = PTR_ERR(ret); + +out_unlock: + aufs_read_unlock(dentry, AuLock_IR); +out: + if (unlikely(err)) + ret = ERR_PTR(err); + AuTraceErrPtr(ret); + return ret; +} + +/* ---------------------------------------------------------------------- */ + +static int au_is_special(struct inode *inode) +{ + return (inode->i_mode & (S_IFBLK | S_IFCHR | S_IFIFO | S_IFSOCK)); +} + +static int aufs_update_time(struct inode *inode, struct timespec64 *ts, + int flags) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct inode *h_inode; + struct vfsmount *h_mnt; + + sb = inode->i_sb; + WARN_ONCE((flags & S_ATIME) && !IS_NOATIME(inode), + "unexpected s_flags 0x%lx", sb->s_flags); + + /* mmap_sem might be acquired already, cf. aufs_mmap() */ + lockdep_off(); + si_read_lock(sb, AuLock_FLUSH); + ii_write_lock_child(inode); + + err = 0; + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (!au_test_ro(sb, bindex, inode)) { + h_mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mnt_want_write(h_mnt); + if (!err) { + err = vfsub_update_time(h_inode, ts, flags); + vfsub_mnt_drop_write(h_mnt); + } + } else if (au_is_special(h_inode)) { + /* + * Never copy-up here. + * These special files may already be opened and used for + * communicating. If we copied it up, then the communication + * would be corrupted. + */ + AuWarn1("timestamps for i%lu are ignored " + "since it is on readonly branch (hi%lu).\n", + inode->i_ino, h_inode->i_ino); + } else if (flags & ~S_ATIME) { + err = -EIO; + AuIOErr1("unexpected flags 0x%x\n", flags); + AuDebugOn(1); + } + + if (!err) + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + si_read_unlock(sb); + lockdep_on(); + + if (!err && (flags & S_VERSION)) + inode_inc_iversion(inode); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* no getattr version will be set by module.c:aufs_init() */ +struct inode_operations aufs_iop_nogetattr[AuIop_Last], + aufs_iop[] = { + [AuIop_SYMLINK] = { + .permission = aufs_permission, +#ifdef CONFIG_FS_POSIX_ACL + .get_acl = aufs_get_acl, + .set_acl = aufs_set_acl, /* unsupport for symlink? */ +#endif + + .setattr = aufs_setattr, + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR + .listxattr = aufs_listxattr, +#endif + + .get_link = aufs_get_link, + + /* .update_time = aufs_update_time */ + }, + [AuIop_DIR] = { + .create = aufs_create, + .lookup = aufs_lookup, + .link = aufs_link, + .unlink = aufs_unlink, + .symlink = aufs_symlink, + .mkdir = aufs_mkdir, + .rmdir = aufs_rmdir, + .mknod = aufs_mknod, + .rename = aufs_rename, + + .permission = aufs_permission, +#ifdef CONFIG_FS_POSIX_ACL + .get_acl = aufs_get_acl, + .set_acl = aufs_set_acl, +#endif + + .setattr = aufs_setattr, + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR + .listxattr = aufs_listxattr, +#endif + + .update_time = aufs_update_time, + .atomic_open = aufs_atomic_open, + .tmpfile = aufs_tmpfile + }, + [AuIop_OTHER] = { + .permission = aufs_permission, +#ifdef CONFIG_FS_POSIX_ACL + .get_acl = aufs_get_acl, + .set_acl = aufs_set_acl, +#endif + + .setattr = aufs_setattr, + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR + .listxattr = aufs_listxattr, +#endif + + .update_time = aufs_update_time + } +}; diff -Naur --no-dereference a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c --- a/fs/aufs/i_op_del.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/i_op_del.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations (del entry) + */ + +#include +#include "aufs.h" + +/* + * decide if a new whiteout for @dentry is necessary or not. + * when it is necessary, prepare the parent dir for the upper branch whose + * branch index is @bcpup for creation. the actual creation of the whiteout will + * be done by caller. + * return value: + * 0: wh is unnecessary + * plus: wh is necessary + * minus: error + */ +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) +{ + int need_wh, err; + aufs_bindex_t btop; + struct super_block *sb; + + sb = dentry->d_sb; + btop = au_dbtop(dentry); + if (*bcpup < 0) { + *bcpup = btop; + if (au_test_ro(sb, btop, d_inode(dentry))) { + err = AuWbrCopyup(au_sbi(sb), dentry); + *bcpup = err; + if (unlikely(err < 0)) + goto out; + } + } else + AuDebugOn(btop < *bcpup + || au_test_ro(sb, *bcpup, d_inode(dentry))); + AuDbg("bcpup %d, btop %d\n", *bcpup, btop); + + if (*bcpup != btop) { + err = au_cpup_dirs(dentry, *bcpup); + if (unlikely(err)) + goto out; + need_wh = 1; + } else { + struct au_dinfo *dinfo, *tmp; + + need_wh = -ENOMEM; + dinfo = au_di(dentry); + tmp = au_di_alloc(sb, AuLsc_DI_TMP); + if (tmp) { + au_di_cp(tmp, dinfo); + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ + need_wh = au_lkup_dentry(dentry, btop + 1, + /* AuLkup_IGNORE_PERM */ 0); + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + } + } + AuDbg("need_wh %d\n", need_wh); + err = need_wh; + +out: + return err; +} + +/* + * simple tests for the del-entry operations. + * following the checks in vfs, plus the parent-child relationship. + */ +int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir) +{ + int err; + umode_t h_mode; + struct dentry *h_dentry, *h_latest; + struct inode *h_inode; + struct path h_ppath; + struct super_block *sb; + struct au_branch *br; + + h_dentry = au_h_dptr(dentry, bindex); + if (d_really_is_positive(dentry)) { + err = -ENOENT; + if (unlikely(d_is_negative(h_dentry))) + goto out; + h_inode = d_inode(h_dentry); + if (unlikely(!h_inode->i_nlink)) + goto out; + + h_mode = h_inode->i_mode; + if (!isdir) { + err = -EISDIR; + if (unlikely(S_ISDIR(h_mode))) + goto out; + } else if (unlikely(!S_ISDIR(h_mode))) { + err = -ENOTDIR; + goto out; + } + } else { + /* rename(2) case */ + err = -EIO; + if (unlikely(d_is_positive(h_dentry))) + goto out; + } + + err = -ENOENT; + /* expected parent dir is locked */ + if (unlikely(h_parent != h_dentry->d_parent)) + goto out; + err = 0; + + /* + * rmdir a dir may break the consistency on some filesystem. + * let's try heavy test. + */ + err = -EACCES; + sb = dentry->d_sb; + br = au_sbr(sb, bindex); + if (unlikely(!au_opt_test(au_mntflags(sb), DIRPERM1) + && au_test_h_perm(d_inode(h_parent), + MAY_EXEC | MAY_WRITE))) + goto out; + + h_ppath.dentry = h_parent; + h_ppath.mnt = au_br_mnt(br); + h_latest = au_sio_lkup_one(&dentry->d_name, &h_ppath); + err = -EIO; + if (IS_ERR(h_latest)) + goto out; + if (h_latest == h_dentry) + err = 0; + dput(h_latest); + +out: + return err; +} + +/* + * decide the branch where we operate for @dentry. the branch index will be set + * @rbcpup. after deciding it, 'pin' it and store the timestamps of the parent + * dir for reverting. + * when a new whiteout is necessary, create it. + */ +static struct dentry* +lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, + struct au_dtime *dt, struct au_pin *pin) +{ + struct dentry *wh_dentry; + struct super_block *sb; + struct path h_path; + int err, need_wh; + unsigned int udba; + aufs_bindex_t bcpup; + + need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); + wh_dentry = ERR_PTR(need_wh); + if (unlikely(need_wh < 0)) + goto out; + + sb = dentry->d_sb; + udba = au_opt_udba(sb); + bcpup = *rbcpup; + err = au_pin(pin, dentry, bcpup, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out; + + h_path.dentry = au_pinned_h_parent(pin); + if (udba != AuOpt_UDBA_NONE + && au_dbtop(dentry) == bcpup) { + err = au_may_del(dentry, bcpup, h_path.dentry, isdir); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_unpin; + } + + h_path.mnt = au_sbr_mnt(sb, bcpup); + au_dtime_store(dt, au_pinned_parent(pin), &h_path); + wh_dentry = NULL; + if (!need_wh) + goto out; /* success, no need to create whiteout */ + + wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); + if (IS_ERR(wh_dentry)) + goto out_unpin; + + /* returns with the parent is locked and wh_dentry is dget-ed */ + goto out; /* success */ + +out_unpin: + au_unpin(pin); +out: + return wh_dentry; +} + +/* + * when removing a dir, rename it to a unique temporary whiteout-ed name first + * in order to be revertible and save time for removing many child whiteouts + * under the dir. + * returns 1 when there are too many child whiteout and caller should remove + * them asynchronously. returns 0 when the number of children is enough small to + * remove now or the branch fs is a remote fs. + * otherwise return an error. + */ +static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, + struct au_nhash *whlist, struct inode *dir) +{ + int rmdir_later, err, dirwh; + struct dentry *h_dentry; + struct super_block *sb; + struct inode *inode; + + sb = dentry->d_sb; + SiMustAnyLock(sb); + h_dentry = au_h_dptr(dentry, bindex); + err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); + if (unlikely(err)) + goto out; + + /* stop monitoring */ + inode = d_inode(dentry); + au_hn_free(au_hi(inode, bindex)); + + if (!au_test_fs_remote(h_dentry->d_sb)) { + dirwh = au_sbi(sb)->si_dirwh; + rmdir_later = (dirwh <= 1); + if (!rmdir_later) + rmdir_later = au_nhash_test_longer_wh(whlist, bindex, + dirwh); + if (rmdir_later) + return rmdir_later; + } + + err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); + if (unlikely(err)) { + AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", + h_dentry, bindex, err); + err = 0; + } + +out: + AuTraceErr(err); + return err; +} + +/* + * final procedure for deleting a entry. + * maintain dentry and iattr. + */ +static void epilog(struct inode *dir, struct dentry *dentry, + aufs_bindex_t bindex) +{ + struct inode *inode; + + inode = d_inode(dentry); + d_drop(dentry); + inode->i_ctime = dir->i_ctime; + + au_dir_ts(dir, bindex); + inode_inc_iversion(dir); +} + +/* + * when an error happened, remove the created whiteout and revert everything. + */ +static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, + aufs_bindex_t bwh, struct dentry *wh_dentry, + struct dentry *dentry, struct au_dtime *dt) +{ + int rerr; + struct path h_path = { + .dentry = wh_dentry, + .mnt = au_sbr_mnt(dir->i_sb, bindex) + }; + + rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); + if (!rerr) { + au_set_dbwh(dentry, bwh); + au_dtime_revert(dt); + return 0; + } + + AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); + return -EIO; +} + +/* ---------------------------------------------------------------------- */ + +int aufs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; + aufs_bindex_t bwh, bindex, btop; + struct inode *inode, *h_dir, *delegated; + struct dentry *parent, *wh_dentry; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + struct path h_path; + } *a; + + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_d_hashed_positive(dentry); + if (unlikely(err)) + goto out_unlock; + inode = d_inode(dentry); + IMustLock(inode); + err = -EISDIR; + if (unlikely(d_is_dir(dentry))) + goto out_unlock; /* possible? */ + + btop = au_dbtop(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, + &a->pin); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + a->h_path.mnt = au_sbr_mnt(dentry->d_sb, btop); + a->h_path.dentry = au_h_dptr(dentry, btop); + dget(a->h_path.dentry); + if (bindex == btop) { + h_dir = au_pinned_h_dir(&a->pin); + delegated = NULL; + err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + } else { + /* dir inode is locked */ + h_dir = d_inode(wh_dentry->d_parent); + IMustLock(h_dir); + err = 0; + } + + if (!err) { + vfsub_drop_nlink(inode); + epilog(dir, dentry, bindex); + + /* update target timestamps */ + if (bindex == btop) { + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); + /*ignore*/ + inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + } else + /* todo: this timestamp may be reverted later */ + inode->i_ctime = h_dir->i_ctime; + goto out_unpin; /* success */ + } + + /* revert */ + if (wh_dentry) { + int rerr; + + rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, + &a->dt); + if (rerr) + err = rerr; + } + +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); + dput(a->h_path.dentry); +out_parent: + di_write_unlock(parent); +out_unlock: + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + return err; +} + +int aufs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err, rmdir_later; + aufs_bindex_t bwh, bindex, btop; + struct inode *inode; + struct dentry *parent, *wh_dentry, *h_dentry; + struct au_whtmp_rmdir *args; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + } *a; + + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_alive_dir(dentry); + if (unlikely(err)) + goto out_unlock; + inode = d_inode(dentry); + IMustLock(inode); + err = -ENOTDIR; + if (unlikely(!d_is_dir(dentry))) + goto out_unlock; /* possible? */ + + err = -ENOMEM; + args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); + if (unlikely(!args)) + goto out_unlock; + + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + err = au_test_empty(dentry, &args->whlist); + if (unlikely(err)) + goto out_parent; + + btop = au_dbtop(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, + &a->pin); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + h_dentry = au_h_dptr(dentry, btop); + dget(h_dentry); + rmdir_later = 0; + if (bindex == btop) { + err = renwh_and_rmdir(dentry, btop, &args->whlist, dir); + if (err > 0) { + rmdir_later = err; + err = 0; + } + } else { + /* stop monitoring */ + au_hn_free(au_hi(inode, btop)); + + /* dir inode is locked */ + IMustLock(d_inode(wh_dentry->d_parent)); + err = 0; + } + + if (!err) { + vfsub_dead_dir(inode); + au_set_dbdiropq(dentry, -1); + epilog(dir, dentry, bindex); + + if (rmdir_later) { + au_whtmp_kick_rmdir(dir, btop, h_dentry, args); + args = NULL; + } + + goto out_unpin; /* success */ + } + + /* revert */ + AuLabel(revert); + if (wh_dentry) { + int rerr; + + rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, + &a->dt); + if (rerr) + err = rerr; + } + +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); + dput(h_dentry); +out_parent: + di_write_unlock(parent); + if (args) + au_whtmp_rmdir_free(args); +out_unlock: + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} diff -Naur --no-dereference a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c --- a/fs/aufs/i_op_ren.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/i_op_ren.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operation (rename entry) + * todo: this is crazy monster + */ + +#include +#include "aufs.h" + +enum { AuSRC, AuDST, AuSrcDst }; +enum { AuPARENT, AuCHILD, AuParentChild }; + +#define AuRen_ISDIR_SRC 1 +#define AuRen_ISDIR_DST (1 << 1) +#define AuRen_ISSAMEDIR (1 << 2) +#define AuRen_WHSRC (1 << 3) +#define AuRen_WHDST (1 << 4) +#define AuRen_MNT_WRITE (1 << 5) +#define AuRen_DT_DSTDIR (1 << 6) +#define AuRen_DIROPQ_SRC (1 << 7) +#define AuRen_DIROPQ_DST (1 << 8) +#define AuRen_DIRREN (1 << 9) +#define AuRen_DROPPED_SRC (1 << 10) +#define AuRen_DROPPED_DST (1 << 11) +#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) +#define au_fset_ren(flags, name) \ + do { (flags) |= AuRen_##name; } while (0) +#define au_fclr_ren(flags, name) \ + do { (flags) &= ~AuRen_##name; } while (0) + +#ifndef CONFIG_AUFS_DIRREN +#undef AuRen_DIRREN +#define AuRen_DIRREN 0 +#endif + +struct au_ren_args { + struct { + struct dentry *dentry, *h_dentry, *parent, *h_parent, + *wh_dentry; + struct inode *dir, *inode; + struct au_hinode *hdir, *hinode; + struct au_dtime dt[AuParentChild]; + aufs_bindex_t btop, bdiropq; + } sd[AuSrcDst]; + +#define src_dentry sd[AuSRC].dentry +#define src_dir sd[AuSRC].dir +#define src_inode sd[AuSRC].inode +#define src_h_dentry sd[AuSRC].h_dentry +#define src_parent sd[AuSRC].parent +#define src_h_parent sd[AuSRC].h_parent +#define src_wh_dentry sd[AuSRC].wh_dentry +#define src_hdir sd[AuSRC].hdir +#define src_hinode sd[AuSRC].hinode +#define src_h_dir sd[AuSRC].hdir->hi_inode +#define src_dt sd[AuSRC].dt +#define src_btop sd[AuSRC].btop +#define src_bdiropq sd[AuSRC].bdiropq + +#define dst_dentry sd[AuDST].dentry +#define dst_dir sd[AuDST].dir +#define dst_inode sd[AuDST].inode +#define dst_h_dentry sd[AuDST].h_dentry +#define dst_parent sd[AuDST].parent +#define dst_h_parent sd[AuDST].h_parent +#define dst_wh_dentry sd[AuDST].wh_dentry +#define dst_hdir sd[AuDST].hdir +#define dst_hinode sd[AuDST].hinode +#define dst_h_dir sd[AuDST].hdir->hi_inode +#define dst_dt sd[AuDST].dt +#define dst_btop sd[AuDST].btop +#define dst_bdiropq sd[AuDST].bdiropq + + struct dentry *h_trap; + struct au_branch *br; + struct path h_path; + struct au_nhash whlist; + aufs_bindex_t btgt, src_bwh; + + struct { + unsigned short auren_flags; + unsigned char flags; /* syscall parameter */ + unsigned char exchange; + } __packed; + + struct au_whtmp_rmdir *thargs; + struct dentry *h_dst; + struct au_hinode *h_root; +}; + +/* ---------------------------------------------------------------------- */ + +/* + * functions for reverting. + * when an error happened in a single rename systemcall, we should revert + * everything as if nothing happened. + * we don't need to revert the copied-up/down the parent dir since they are + * harmless. + */ + +#define RevertFailure(fmt, ...) do { \ + AuIOErr("revert failure: " fmt " (%d, %d)\n", \ + ##__VA_ARGS__, err, rerr); \ + err = -EIO; \ +} while (0) + +static void au_ren_do_rev_diropq(int err, struct au_ren_args *a, int idx) +{ + int rerr; + struct dentry *d; +#define src_or_dst(member) a->sd[idx].member + + d = src_or_dst(dentry); /* {src,dst}_dentry */ + au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD); + rerr = au_diropq_remove(d, a->btgt); + au_hn_inode_unlock(src_or_dst(hinode)); + au_set_dbdiropq(d, src_or_dst(bdiropq)); + if (rerr) + RevertFailure("remove diropq %pd", d); + +#undef src_or_dst_ +} + +static void au_ren_rev_diropq(int err, struct au_ren_args *a) +{ + if (au_ftest_ren(a->auren_flags, DIROPQ_SRC)) + au_ren_do_rev_diropq(err, a, AuSRC); + if (au_ftest_ren(a->auren_flags, DIROPQ_DST)) + au_ren_do_rev_diropq(err, a, AuDST); +} + +static void au_ren_rev_rename(int err, struct au_ren_args *a) +{ + int rerr; + struct inode *delegated; + struct path h_ppath = { + .dentry = a->src_h_parent, + .mnt = a->h_path.mnt + }; + + a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, &h_ppath); + rerr = PTR_ERR(a->h_path.dentry); + if (IS_ERR(a->h_path.dentry)) { + RevertFailure("lkup one %pd", a->src_dentry); + return; + } + + delegated = NULL; + rerr = vfsub_rename(a->dst_h_dir, + au_h_dptr(a->src_dentry, a->btgt), + a->src_h_dir, &a->h_path, &delegated, a->flags); + if (unlikely(rerr == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + d_drop(a->h_path.dentry); + dput(a->h_path.dentry); + /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ + if (rerr) + RevertFailure("rename %pd", a->src_dentry); +} + +static void au_ren_rev_whtmp(int err, struct au_ren_args *a) +{ + int rerr; + struct inode *delegated; + struct path h_ppath = { + .dentry = a->dst_h_parent, + .mnt = a->h_path.mnt + }; + + a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, &h_ppath); + rerr = PTR_ERR(a->h_path.dentry); + if (IS_ERR(a->h_path.dentry)) { + RevertFailure("lkup one %pd", a->dst_dentry); + return; + } + if (d_is_positive(a->h_path.dentry)) { + d_drop(a->h_path.dentry); + dput(a->h_path.dentry); + return; + } + + delegated = NULL; + rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, + &delegated, a->flags); + if (unlikely(rerr == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + d_drop(a->h_path.dentry); + dput(a->h_path.dentry); + if (!rerr) + au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); + else + RevertFailure("rename %pd", a->h_dst); +} + +static void au_ren_rev_whsrc(int err, struct au_ren_args *a) +{ + int rerr; + + a->h_path.dentry = a->src_wh_dentry; + rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); + au_set_dbwh(a->src_dentry, a->src_bwh); + if (rerr) + RevertFailure("unlink %pd", a->src_wh_dentry); +} +#undef RevertFailure + +/* ---------------------------------------------------------------------- */ + +/* + * when we have to copyup the renaming entry, do it with the rename-target name + * in order to minimize the cost (the later actual rename is unnecessary). + * otherwise rename it on the target branch. + */ +static int au_ren_or_cpup(struct au_ren_args *a) +{ + int err; + struct dentry *d; + struct inode *delegated; + + d = a->src_dentry; + if (au_dbtop(d) == a->btgt) { + a->h_path.dentry = a->dst_h_dentry; + AuDebugOn(au_dbtop(d) != a->btgt); + delegated = NULL; + err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), + a->dst_h_dir, &a->h_path, &delegated, + a->flags); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + } else + BUG(); + + if (!err && a->h_dst) + /* it will be set to dinfo later */ + dget(a->h_dst); + + return err; +} + +/* cf. aufs_rmdir() */ +static int au_ren_del_whtmp(struct au_ren_args *a) +{ + int err; + struct inode *dir; + + dir = a->dst_dir; + SiMustAnyLock(dir->i_sb); + if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, + au_sbi(dir->i_sb)->si_dirwh) + || au_test_fs_remote(a->h_dst->d_sb)) { + err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); + if (unlikely(err)) + pr_warn("failed removing whtmp dir %pd (%d), " + "ignored.\n", a->h_dst, err); + } else { + au_nhash_wh_free(&a->thargs->whlist); + a->thargs->whlist = a->whlist; + a->whlist.nh_num = 0; + au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); + dput(a->h_dst); + a->thargs = NULL; + } + + return 0; +} + +/* make it 'opaque' dir. */ +static int au_ren_do_diropq(struct au_ren_args *a, int idx) +{ + int err; + struct dentry *d, *diropq; +#define src_or_dst(member) a->sd[idx].member + + err = 0; + d = src_or_dst(dentry); /* {src,dst}_dentry */ + src_or_dst(bdiropq) = au_dbdiropq(d); + src_or_dst(hinode) = au_hi(src_or_dst(inode), a->btgt); + au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD); + diropq = au_diropq_create(d, a->btgt); + au_hn_inode_unlock(src_or_dst(hinode)); + if (IS_ERR(diropq)) + err = PTR_ERR(diropq); + else + dput(diropq); + +#undef src_or_dst_ + return err; +} + +static int au_ren_diropq(struct au_ren_args *a) +{ + int err; + unsigned char always; + struct dentry *d; + + err = 0; + d = a->dst_dentry; /* already renamed on the branch */ + always = !!au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC) + && !au_ftest_ren(a->auren_flags, DIRREN) + && a->btgt != au_dbdiropq(a->src_dentry) + && (a->dst_wh_dentry + || a->btgt <= au_dbdiropq(d) + /* hide the lower to keep xino */ + /* the lowers may not be a dir, but we hide them anyway */ + || a->btgt < au_dbbot(d) + || always)) { + AuDbg("here\n"); + err = au_ren_do_diropq(a, AuSRC); + if (unlikely(err)) + goto out; + au_fset_ren(a->auren_flags, DIROPQ_SRC); + } + if (!a->exchange) + goto out; /* success */ + + d = a->src_dentry; /* already renamed on the branch */ + if (au_ftest_ren(a->auren_flags, ISDIR_DST) + && a->btgt != au_dbdiropq(a->dst_dentry) + && (a->btgt < au_dbdiropq(d) + || a->btgt < au_dbbot(d) + || always)) { + AuDbgDentry(a->src_dentry); + AuDbgDentry(a->dst_dentry); + err = au_ren_do_diropq(a, AuDST); + if (unlikely(err)) + goto out_rev_src; + au_fset_ren(a->auren_flags, DIROPQ_DST); + } + goto out; /* success */ + +out_rev_src: + AuDbg("err %d, reverting src\n", err); + au_ren_rev_diropq(err, a); +out: + return err; +} + +static int do_rename(struct au_ren_args *a) +{ + int err; + struct dentry *d, *h_d; + + if (!a->exchange) { + /* prepare workqueue args for asynchronous rmdir */ + h_d = a->dst_h_dentry; + if (au_ftest_ren(a->auren_flags, ISDIR_DST) + /* && !au_ftest_ren(a->auren_flags, DIRREN) */ + && d_is_positive(h_d)) { + err = -ENOMEM; + a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, + GFP_NOFS); + if (unlikely(!a->thargs)) + goto out; + a->h_dst = dget(h_d); + } + + /* create whiteout for src_dentry */ + if (au_ftest_ren(a->auren_flags, WHSRC)) { + a->src_bwh = au_dbwh(a->src_dentry); + AuDebugOn(a->src_bwh >= 0); + a->src_wh_dentry = au_wh_create(a->src_dentry, a->btgt, + a->src_h_parent); + err = PTR_ERR(a->src_wh_dentry); + if (IS_ERR(a->src_wh_dentry)) + goto out_thargs; + } + + /* lookup whiteout for dentry */ + if (au_ftest_ren(a->auren_flags, WHDST)) { + h_d = au_wh_lkup(a->dst_h_parent, + &a->dst_dentry->d_name, a->br); + err = PTR_ERR(h_d); + if (IS_ERR(h_d)) + goto out_whsrc; + if (d_is_negative(h_d)) + dput(h_d); + else + a->dst_wh_dentry = h_d; + } + + /* rename dentry to tmpwh */ + if (a->thargs) { + err = au_whtmp_ren(a->dst_h_dentry, a->br); + if (unlikely(err)) + goto out_whdst; + + d = a->dst_dentry; + au_set_h_dptr(d, a->btgt, NULL); + err = au_lkup_neg(d, a->btgt, /*wh*/0); + if (unlikely(err)) + goto out_whtmp; + a->dst_h_dentry = au_h_dptr(d, a->btgt); + } + } + + BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_btop != a->btgt); +#if 0 /* debugging */ + BUG_ON(!au_ftest_ren(a->auren_flags, DIRREN) + && d_is_positive(a->dst_h_dentry) + && a->src_btop != a->btgt); +#endif + + /* rename by vfs_rename or cpup */ + err = au_ren_or_cpup(a); + if (unlikely(err)) + /* leave the copied-up one */ + goto out_whtmp; + + /* make dir opaque */ + err = au_ren_diropq(a); + if (unlikely(err)) + goto out_rename; + + /* update target timestamps */ + if (a->exchange) { + AuDebugOn(au_dbtop(a->dst_dentry) != a->btgt); + a->h_path.dentry = au_h_dptr(a->dst_dentry, a->btgt); + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ + a->dst_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + } + AuDebugOn(au_dbtop(a->src_dentry) != a->btgt); + a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ + a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + + if (!a->exchange) { + /* remove whiteout for dentry */ + if (a->dst_wh_dentry) { + a->h_path.dentry = a->dst_wh_dentry; + err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, + a->dst_dentry); + if (unlikely(err)) + goto out_diropq; + } + + /* remove whtmp */ + if (a->thargs) + au_ren_del_whtmp(a); /* ignore this error */ + + au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); + } + err = 0; + goto out_success; + +out_diropq: + au_ren_rev_diropq(err, a); +out_rename: + au_ren_rev_rename(err, a); + dput(a->h_dst); +out_whtmp: + if (a->thargs) + au_ren_rev_whtmp(err, a); +out_whdst: + dput(a->dst_wh_dentry); + a->dst_wh_dentry = NULL; +out_whsrc: + if (a->src_wh_dentry) + au_ren_rev_whsrc(err, a); +out_success: + dput(a->src_wh_dentry); + dput(a->dst_wh_dentry); +out_thargs: + if (a->thargs) { + dput(a->h_dst); + au_whtmp_rmdir_free(a->thargs); + a->thargs = NULL; + } +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * test if @dentry dir can be rename destination or not. + * success means, it is a logically empty dir. + */ +static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) +{ + return au_test_empty(dentry, whlist); +} + +/* + * test if @a->src_dentry dir can be rename source or not. + * if it can, return 0. + * success means, + * - it is a logically empty dir. + * - or, it exists on writable branch and has no children including whiteouts + * on the lower branch unless DIRREN is on. + */ +static int may_rename_srcdir(struct au_ren_args *a) +{ + int err; + unsigned int rdhash; + aufs_bindex_t btop, btgt; + struct dentry *dentry; + struct super_block *sb; + struct au_sbinfo *sbinfo; + + dentry = a->src_dentry; + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + if (au_opt_test(sbinfo->si_mntflags, DIRREN)) + au_fset_ren(a->auren_flags, DIRREN); + + btgt = a->btgt; + btop = au_dbtop(dentry); + if (btop != btgt) { + struct au_nhash whlist; + + SiMustAnyLock(sb); + rdhash = sbinfo->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, + dentry)); + err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_test_empty(dentry, &whlist); + au_nhash_wh_free(&whlist); + goto out; + } + + if (btop == au_dbtaildir(dentry)) + return 0; /* success */ + + err = au_test_empty_lower(dentry); + +out: + if (err == -ENOTEMPTY) { + if (au_ftest_ren(a->auren_flags, DIRREN)) { + err = 0; + } else { + AuWarn1("renaming dir who has child(ren) on multiple " + "branches, is not supported\n"); + err = -EXDEV; + } + } + return err; +} + +/* side effect: sets whlist and h_dentry */ +static int au_ren_may_dir(struct au_ren_args *a) +{ + int err; + unsigned int rdhash; + struct dentry *d; + + d = a->dst_dentry; + SiMustAnyLock(d->d_sb); + + err = 0; + if (au_ftest_ren(a->auren_flags, ISDIR_DST) && a->dst_inode) { + rdhash = au_sbi(d->d_sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); + err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + + if (!a->exchange) { + au_set_dbtop(d, a->dst_btop); + err = may_rename_dstdir(d, &a->whlist); + au_set_dbtop(d, a->btgt); + } else + err = may_rename_srcdir(a); + } + a->dst_h_dentry = au_h_dptr(d, au_dbtop(d)); + if (unlikely(err)) + goto out; + + d = a->src_dentry; + a->src_h_dentry = au_h_dptr(d, au_dbtop(d)); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) { + err = may_rename_srcdir(a); + if (unlikely(err)) { + au_nhash_wh_free(&a->whlist); + a->whlist.nh_num = 0; + } + } +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * simple tests for rename. + * following the checks in vfs, plus the parent-child relationship. + */ +static int au_may_ren(struct au_ren_args *a) +{ + int err, isdir; + struct inode *h_inode; + + if (a->src_btop == a->btgt) { + err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, + au_ftest_ren(a->auren_flags, ISDIR_SRC)); + if (unlikely(err)) + goto out; + err = -EINVAL; + if (unlikely(a->src_h_dentry == a->h_trap)) + goto out; + } + + err = 0; + if (a->dst_btop != a->btgt) + goto out; + + err = -ENOTEMPTY; + if (unlikely(a->dst_h_dentry == a->h_trap)) + goto out; + + err = -EIO; + isdir = !!au_ftest_ren(a->auren_flags, ISDIR_DST); + if (d_really_is_negative(a->dst_dentry)) { + if (d_is_negative(a->dst_h_dentry)) + err = au_may_add(a->dst_dentry, a->btgt, + a->dst_h_parent, isdir); + } else { + if (unlikely(d_is_negative(a->dst_h_dentry))) + goto out; + h_inode = d_inode(a->dst_h_dentry); + if (h_inode->i_nlink) + err = au_may_del(a->dst_dentry, a->btgt, + a->dst_h_parent, isdir); + } + +out: + if (unlikely(err == -ENOENT || err == -EEXIST)) + err = -EIO; + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * locking order + * (VFS) + * - src_dir and dir by lock_rename() + * - inode if exists + * (aufs) + * - lock all + * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, + * + si_read_lock + * + di_write_lock2_child() + * + di_write_lock_child() + * + ii_write_lock_child() + * + di_write_lock_child2() + * + ii_write_lock_child2() + * + src_parent and parent + * + di_write_lock_parent() + * + ii_write_lock_parent() + * + di_write_lock_parent2() + * + ii_write_lock_parent2() + * + lower src_dir and dir by vfsub_lock_rename() + * + verify the every relationships between child and parent. if any + * of them failed, unlock all and return -EBUSY. + */ +static void au_ren_unlock(struct au_ren_args *a) +{ + vfsub_unlock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); + if (au_ftest_ren(a->auren_flags, DIRREN) + && a->h_root) + au_hn_inode_unlock(a->h_root); + if (au_ftest_ren(a->auren_flags, MNT_WRITE)) + vfsub_mnt_drop_write(au_br_mnt(a->br)); +} + +static int au_ren_lock(struct au_ren_args *a) +{ + int err; + unsigned int udba; + + err = 0; + a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); + a->src_hdir = au_hi(a->src_dir, a->btgt); + a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); + a->dst_hdir = au_hi(a->dst_dir, a->btgt); + + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out; + au_fset_ren(a->auren_flags, MNT_WRITE); + if (au_ftest_ren(a->auren_flags, DIRREN)) { + struct dentry *root; + struct inode *dir; + + /* + * sbinfo is already locked, so this ii_read_lock is + * unnecessary. but our debugging feature checks it. + */ + root = a->src_inode->i_sb->s_root; + if (root != a->src_parent && root != a->dst_parent) { + dir = d_inode(root); + ii_read_lock_parent3(dir); + a->h_root = au_hi(dir, a->btgt); + ii_read_unlock(dir); + au_hn_inode_lock_nested(a->h_root, AuLsc_I_PARENT3); + } + } + a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); + udba = au_opt_udba(a->src_dentry->d_sb); + if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) + || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) + err = au_busy_or_stale(); + if (!err && au_dbtop(a->src_dentry) == a->btgt) + err = au_h_verify(a->src_h_dentry, udba, + d_inode(a->src_h_parent), a->src_h_parent, + a->br); + if (!err && au_dbtop(a->dst_dentry) == a->btgt) + err = au_h_verify(a->dst_h_dentry, udba, + d_inode(a->dst_h_parent), a->dst_h_parent, + a->br); + if (!err) + goto out; /* success */ + + err = au_busy_or_stale(); + au_ren_unlock(a); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_ren_refresh_dir(struct au_ren_args *a) +{ + struct inode *dir; + + dir = a->dst_dir; + inode_inc_iversion(dir); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) { + /* is this updating defined in POSIX? */ + au_cpup_attr_timesizes(a->src_inode); + au_cpup_attr_nlink(dir, /*force*/1); + } + au_dir_ts(dir, a->btgt); + + if (a->exchange) { + dir = a->src_dir; + inode_inc_iversion(dir); + if (au_ftest_ren(a->auren_flags, ISDIR_DST)) { + /* is this updating defined in POSIX? */ + au_cpup_attr_timesizes(a->dst_inode); + au_cpup_attr_nlink(dir, /*force*/1); + } + au_dir_ts(dir, a->btgt); + } + + if (au_ftest_ren(a->auren_flags, ISSAMEDIR)) + return; + + dir = a->src_dir; + inode_inc_iversion(dir); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) + au_cpup_attr_nlink(dir, /*force*/1); + au_dir_ts(dir, a->btgt); +} + +static void au_ren_refresh(struct au_ren_args *a) +{ + aufs_bindex_t bbot, bindex; + struct dentry *d, *h_d; + struct inode *i, *h_i; + struct super_block *sb; + + d = a->dst_dentry; + d_drop(d); + if (a->h_dst) + /* already dget-ed by au_ren_or_cpup() */ + au_set_h_dptr(d, a->btgt, a->h_dst); + + i = a->dst_inode; + if (i) { + if (!a->exchange) { + if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) + vfsub_drop_nlink(i); + else { + vfsub_dead_dir(i); + au_cpup_attr_timesizes(i); + } + au_update_dbrange(d, /*do_put_zero*/1); + } else + au_cpup_attr_nlink(i, /*force*/1); + } else { + bbot = a->btgt; + for (bindex = au_dbtop(d); bindex < bbot; bindex++) + au_set_h_dptr(d, bindex, NULL); + bbot = au_dbbot(d); + for (bindex = a->btgt + 1; bindex <= bbot; bindex++) + au_set_h_dptr(d, bindex, NULL); + au_update_dbrange(d, /*do_put_zero*/0); + } + + if (a->exchange + || au_ftest_ren(a->auren_flags, DIRREN)) { + d_drop(a->src_dentry); + if (au_ftest_ren(a->auren_flags, DIRREN)) + au_set_dbwh(a->src_dentry, -1); + return; + } + + d = a->src_dentry; + au_set_dbwh(d, -1); + bbot = au_dbbot(d); + for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { + h_d = au_h_dptr(d, bindex); + if (h_d) + au_set_h_dptr(d, bindex, NULL); + } + au_set_dbbot(d, a->btgt); + + sb = d->d_sb; + i = a->src_inode; + if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) + return; /* success */ + + bbot = au_ibbot(i); + for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { + h_i = au_h_iptr(i, bindex); + if (h_i) { + au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); + /* ignore this error */ + au_set_h_iptr(i, bindex, NULL, 0); + } + } + au_set_ibbot(i, a->btgt); +} + +/* ---------------------------------------------------------------------- */ + +/* mainly for link(2) and rename(2) */ +int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) +{ + aufs_bindex_t bdiropq, bwh; + struct dentry *parent; + struct au_branch *br; + + parent = dentry->d_parent; + IMustLock(d_inode(parent)); /* dir is locked */ + + bdiropq = au_dbdiropq(parent); + bwh = au_dbwh(dentry); + br = au_sbr(dentry->d_sb, btgt); + if (au_br_rdonly(br) + || (0 <= bdiropq && bdiropq < btgt) + || (0 <= bwh && bwh < btgt)) + btgt = -1; + + AuDbg("btgt %d\n", btgt); + return btgt; +} + +/* sets src_btop, dst_btop and btgt */ +static int au_ren_wbr(struct au_ren_args *a) +{ + int err; + struct au_wr_dir_args wr_dir_args = { + /* .force_btgt = -1, */ + .flags = AuWrDir_ADD_ENTRY + }; + + a->src_btop = au_dbtop(a->src_dentry); + a->dst_btop = au_dbtop(a->dst_dentry); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC) + || au_ftest_ren(a->auren_flags, ISDIR_DST)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + wr_dir_args.force_btgt = a->src_btop; + if (a->dst_inode && a->dst_btop < a->src_btop) + wr_dir_args.force_btgt = a->dst_btop; + wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); + err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); + a->btgt = err; + if (a->exchange) + au_update_dbtop(a->dst_dentry); + + return err; +} + +static void au_ren_dt(struct au_ren_args *a) +{ + a->h_path.dentry = a->src_h_parent; + au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); + if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) { + a->h_path.dentry = a->dst_h_parent; + au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); + } + + au_fclr_ren(a->auren_flags, DT_DSTDIR); + if (!au_ftest_ren(a->auren_flags, ISDIR_SRC) + && !a->exchange) + return; + + a->h_path.dentry = a->src_h_dentry; + au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); + if (d_is_positive(a->dst_h_dentry)) { + au_fset_ren(a->auren_flags, DT_DSTDIR); + a->h_path.dentry = a->dst_h_dentry; + au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); + } +} + +static void au_ren_rev_dt(int err, struct au_ren_args *a) +{ + struct dentry *h_d; + struct inode *h_inode; + + au_dtime_revert(a->src_dt + AuPARENT); + if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) + au_dtime_revert(a->dst_dt + AuPARENT); + + if (au_ftest_ren(a->auren_flags, ISDIR_SRC) && err != -EIO) { + h_d = a->src_dt[AuCHILD].dt_h_path.dentry; + h_inode = d_inode(h_d); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->src_dt + AuCHILD); + inode_unlock(h_inode); + + if (au_ftest_ren(a->auren_flags, DT_DSTDIR)) { + h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; + h_inode = d_inode(h_d); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->dst_dt + AuCHILD); + inode_unlock(h_inode); + } + } +} + +/* ---------------------------------------------------------------------- */ + +int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, + struct inode *_dst_dir, struct dentry *_dst_dentry, + unsigned int _flags) +{ + int err, lock_flags; + void *rev; + /* reduce stack space */ + struct au_ren_args *a; + struct au_pin pin; + + AuDbg("%pd, %pd, 0x%x\n", _src_dentry, _dst_dentry, _flags); + IMustLock(_src_dir); + IMustLock(_dst_dir); + + err = -EINVAL; + if (unlikely(_flags & RENAME_WHITEOUT)) + goto out; + + err = -ENOMEM; + BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + a->flags = _flags; + BUILD_BUG_ON(sizeof(a->exchange) == sizeof(u8) + && RENAME_EXCHANGE > U8_MAX); + a->exchange = _flags & RENAME_EXCHANGE; + a->src_dir = _src_dir; + a->src_dentry = _src_dentry; + a->src_inode = NULL; + if (d_really_is_positive(a->src_dentry)) + a->src_inode = d_inode(a->src_dentry); + a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ + a->dst_dir = _dst_dir; + a->dst_dentry = _dst_dentry; + a->dst_inode = NULL; + if (d_really_is_positive(a->dst_dentry)) + a->dst_inode = d_inode(a->dst_dentry); + a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ + if (a->dst_inode) { + /* + * if EXCHANGE && src is non-dir && dst is dir, + * dst is not locked. + */ + /* IMustLock(a->dst_inode); */ + au_igrab(a->dst_inode); + } + + err = -ENOTDIR; + lock_flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; + if (d_is_dir(a->src_dentry)) { + au_fset_ren(a->auren_flags, ISDIR_SRC); + if (unlikely(!a->exchange + && d_really_is_positive(a->dst_dentry) + && !d_is_dir(a->dst_dentry))) + goto out_free; + lock_flags |= AuLock_DIRS; + } + if (a->dst_inode && d_is_dir(a->dst_dentry)) { + au_fset_ren(a->auren_flags, ISDIR_DST); + if (unlikely(!a->exchange + && d_really_is_positive(a->src_dentry) + && !d_is_dir(a->src_dentry))) + goto out_free; + lock_flags |= AuLock_DIRS; + } + err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, + lock_flags); + if (unlikely(err)) + goto out_free; + + err = au_d_hashed_positive(a->src_dentry); + if (unlikely(err)) + goto out_unlock; + err = -ENOENT; + if (a->dst_inode) { + /* + * If it is a dir, VFS unhash it before this + * function. It means we cannot rely upon d_unhashed(). + */ + if (unlikely(!a->dst_inode->i_nlink)) + goto out_unlock; + if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) { + err = au_d_hashed_positive(a->dst_dentry); + if (unlikely(err && !a->exchange)) + goto out_unlock; + } else if (unlikely(IS_DEADDIR(a->dst_inode))) + goto out_unlock; + } else if (unlikely(d_unhashed(a->dst_dentry))) + goto out_unlock; + + /* + * is it possible? + * yes, it happened (in linux-3.3-rcN) but I don't know why. + * there may exist a problem somewhere else. + */ + err = -EINVAL; + if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) + goto out_unlock; + + au_fset_ren(a->auren_flags, ISSAMEDIR); /* temporary */ + di_write_lock_parent(a->dst_parent); + + /* which branch we process */ + err = au_ren_wbr(a); + if (unlikely(err < 0)) + goto out_parent; + a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); + a->h_path.mnt = au_br_mnt(a->br); + + /* are they available to be renamed */ + err = au_ren_may_dir(a); + if (unlikely(err)) + goto out_children; + + /* prepare the writable parent dir on the same branch */ + if (a->dst_btop == a->btgt) { + au_fset_ren(a->auren_flags, WHDST); + } else { + err = au_cpup_dirs(a->dst_dentry, a->btgt); + if (unlikely(err)) + goto out_children; + } + + err = 0; + if (!a->exchange) { + if (a->src_dir != a->dst_dir) { + /* + * this temporary unlock is safe, + * because both dir->i_mutex are locked. + */ + di_write_unlock(a->dst_parent); + di_write_lock_parent(a->src_parent); + err = au_wr_dir_need_wh(a->src_dentry, + au_ftest_ren(a->auren_flags, + ISDIR_SRC), + &a->btgt); + di_write_unlock(a->src_parent); + di_write_lock2_parent(a->src_parent, a->dst_parent, + /*isdir*/1); + au_fclr_ren(a->auren_flags, ISSAMEDIR); + } else + err = au_wr_dir_need_wh(a->src_dentry, + au_ftest_ren(a->auren_flags, + ISDIR_SRC), + &a->btgt); + } + if (unlikely(err < 0)) + goto out_children; + if (err) + au_fset_ren(a->auren_flags, WHSRC); + + /* cpup src */ + if (a->src_btop != a->btgt) { + err = au_pin(&pin, a->src_dentry, a->btgt, + au_opt_udba(a->src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + struct au_cp_generic cpg = { + .dentry = a->src_dentry, + .bdst = a->btgt, + .bsrc = a->src_btop, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + AuDebugOn(au_dbtop(a->src_dentry) != a->src_btop); + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + if (unlikely(err)) + goto out_children; + a->src_btop = a->btgt; + a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); + if (!a->exchange) + au_fset_ren(a->auren_flags, WHSRC); + } + + /* cpup dst */ + if (a->exchange && a->dst_inode + && a->dst_btop != a->btgt) { + err = au_pin(&pin, a->dst_dentry, a->btgt, + au_opt_udba(a->dst_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + struct au_cp_generic cpg = { + .dentry = a->dst_dentry, + .bdst = a->btgt, + .bsrc = a->dst_btop, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + if (unlikely(err)) + goto out_children; + a->dst_btop = a->btgt; + a->dst_h_dentry = au_h_dptr(a->dst_dentry, a->btgt); + } + + /* lock them all */ + err = au_ren_lock(a); + if (unlikely(err)) + /* leave the copied-up one */ + goto out_children; + + if (!a->exchange) { + if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) + err = au_may_ren(a); + else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) + err = -ENAMETOOLONG; + if (unlikely(err)) + goto out_hdir; + } + + /* store timestamps to be revertible */ + au_ren_dt(a); + + /* store dirren info */ + if (au_ftest_ren(a->auren_flags, DIRREN)) { + err = au_dr_rename(a->src_dentry, a->btgt, + &a->dst_dentry->d_name, &rev); + AuTraceErr(err); + if (unlikely(err)) + goto out_dt; + } + + /* here we go */ + err = do_rename(a); + if (unlikely(err)) + goto out_dirren; + + if (au_ftest_ren(a->auren_flags, DIRREN)) + au_dr_rename_fin(a->src_dentry, a->btgt, rev); + + /* update dir attributes */ + au_ren_refresh_dir(a); + + /* dput/iput all lower dentries */ + au_ren_refresh(a); + + goto out_hdir; /* success */ + +out_dirren: + if (au_ftest_ren(a->auren_flags, DIRREN)) + au_dr_rename_rev(a->src_dentry, a->btgt, rev); +out_dt: + au_ren_rev_dt(err, a); +out_hdir: + au_ren_unlock(a); +out_children: + au_nhash_wh_free(&a->whlist); + if (err && a->dst_inode && a->dst_btop != a->btgt) { + AuDbg("btop %d, btgt %d\n", a->dst_btop, a->btgt); + au_set_h_dptr(a->dst_dentry, a->btgt, NULL); + au_set_dbtop(a->dst_dentry, a->dst_btop); + } +out_parent: + if (!err) { + if (d_unhashed(a->src_dentry)) + au_fset_ren(a->auren_flags, DROPPED_SRC); + if (d_unhashed(a->dst_dentry)) + au_fset_ren(a->auren_flags, DROPPED_DST); + if (!a->exchange) + d_move(a->src_dentry, a->dst_dentry); + else { + d_exchange(a->src_dentry, a->dst_dentry); + if (au_ftest_ren(a->auren_flags, DROPPED_DST)) + d_drop(a->dst_dentry); + } + if (au_ftest_ren(a->auren_flags, DROPPED_SRC)) + d_drop(a->src_dentry); + } else { + au_update_dbtop(a->dst_dentry); + if (!a->dst_inode) + d_drop(a->dst_dentry); + } + if (au_ftest_ren(a->auren_flags, ISSAMEDIR)) + di_write_unlock(a->dst_parent); + else + di_write_unlock2(a->src_parent, a->dst_parent); +out_unlock: + aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); +out_free: + iput(a->dst_inode); + if (a->thargs) + au_whtmp_rmdir_free(a->thargs); + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} diff -Naur --no-dereference a/fs/aufs/Kconfig b/fs/aufs/Kconfig --- a/fs/aufs/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/Kconfig 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,199 @@ +# SPDX-License-Identifier: GPL-2.0 +config AUFS_FS + tristate "Aufs (Advanced multi layered unification filesystem) support" + help + Aufs is a stackable unification filesystem such as Unionfs, + which unifies several directories and provides a merged single + directory. + In the early days, aufs was entirely re-designed and + re-implemented Unionfs Version 1.x series. Introducing many + original ideas, approaches and improvements, it becomes totally + different from Unionfs while keeping the basic features. + +if AUFS_FS +choice + prompt "Maximum number of branches" + default AUFS_BRANCH_MAX_127 + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_127 + bool "127" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_511 + bool "511" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_1023 + bool "1023" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_32767 + bool "32767" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +endchoice + +config AUFS_SBILIST + bool + depends on AUFS_MAGIC_SYSRQ || PROC_FS + default y + help + Automatic configuration for internal use. + When aufs supports Magic SysRq or /proc, enabled automatically. + +config AUFS_HNOTIFY + bool "Detect direct branch access (bypassing aufs)" + help + If you want to modify files on branches directly, eg. bypassing aufs, + and want aufs to detect the changes of them fully, then enable this + option and use 'udba=notify' mount option. + Currently there is only one available configuration, "fsnotify". + It will have a negative impact to the performance. + See detail in aufs.5. + +choice + prompt "method" if AUFS_HNOTIFY + default AUFS_HFSNOTIFY +config AUFS_HFSNOTIFY + bool "fsnotify" + select FSNOTIFY +endchoice + +config AUFS_EXPORT + bool "NFS-exportable aufs" + depends on EXPORTFS + help + If you want to export your mounted aufs via NFS, then enable this + option. There are several requirements for this configuration. + See detail in aufs.5. + +config AUFS_INO_T_64 + bool + depends on AUFS_EXPORT + depends on 64BIT && !(ALPHA || S390) + default y + help + Automatic configuration for internal use. + /* typedef unsigned long/int __kernel_ino_t */ + /* alpha and s390x are int */ + +config AUFS_XATTR + bool "support for XATTR/EA (including Security Labels)" + help + If your branch fs supports XATTR/EA and you want to make them + available in aufs too, then enable this opsion and specify the + branch attributes for EA. + See detail in aufs.5. + +config AUFS_FHSM + bool "File-based Hierarchical Storage Management" + help + Hierarchical Storage Management (or HSM) is a well-known feature + in the storage world. Aufs provides this feature as file-based. + with multiple branches. + These multiple branches are prioritized, ie. the topmost one + should be the fastest drive and be used heavily. + +config AUFS_RDU + bool "Readdir in userspace" + help + Aufs has two methods to provide a merged view for a directory, + by a user-space library and by kernel-space natively. The latter + is always enabled but sometimes large and slow. + If you enable this option, install the library in aufs2-util + package, and set some environment variables for your readdir(3), + then the work will be handled in user-space which generally + shows better performance in most cases. + See detail in aufs.5. + +config AUFS_DIRREN + bool "Workaround for rename(2)-ing a directory" + help + By default, aufs returns EXDEV error in renameing a dir who has + his child on the lower branch, since it is a bad idea to issue + rename(2) internally for every lower branch. But user may not + accept this behaviour. So here is a workaround to allow such + rename(2) and store some extra infromation on the writable + branch. Obviously this costs high (and I don't like it). + To use this feature, you need to enable this configuration AND + to specify the mount option `dirren.' + See details in aufs.5 and the design documents. + +config AUFS_SHWH + bool "Show whiteouts" + help + If you want to make the whiteouts in aufs visible, then enable + this option and specify 'shwh' mount option. Although it may + sounds like philosophy or something, but in technically it + simply shows the name of whiteout with keeping its behaviour. + +config AUFS_BR_RAMFS + bool "Ramfs (initramfs/rootfs) as an aufs branch" + help + If you want to use ramfs as an aufs branch fs, then enable this + option. Generally tmpfs is recommended. + Aufs prohibited them to be a branch fs by default, because + initramfs becomes unusable after switch_root or something + generally. If you sets initramfs as an aufs branch and boot your + system by switch_root, you will meet a problem easily since the + files in initramfs may be inaccessible. + Unless you are going to use ramfs as an aufs branch fs without + switch_root or something, leave it N. + +config AUFS_BR_FUSE + bool "Fuse fs as an aufs branch" + depends on FUSE_FS + select AUFS_POLL + help + If you want to use fuse-based userspace filesystem as an aufs + branch fs, then enable this option. + It implements the internal poll(2) operation which is + implemented by fuse only (curretnly). + +config AUFS_POLL + bool + help + Automatic configuration for internal use. + +config AUFS_BR_HFSPLUS + bool "Hfsplus as an aufs branch" + depends on HFSPLUS_FS + default y + help + If you want to use hfsplus fs as an aufs branch fs, then enable + this option. This option introduces a small overhead at + copying-up a file on hfsplus. + +config AUFS_BDEV_LOOP + bool + depends on BLK_DEV_LOOP + default y + help + Automatic configuration for internal use. + Convert =[ym] into =y. + +config AUFS_DEBUG + bool "Debug aufs" + help + Enable this to compile aufs internal debug code. + It will have a negative impact to the performance. + +config AUFS_MAGIC_SYSRQ + bool + depends on AUFS_DEBUG && MAGIC_SYSRQ + default y + help + Automatic configuration for internal use. + When aufs supports Magic SysRq, enabled automatically. +endif diff -Naur --no-dereference a/fs/aufs/lcnt.h b/fs/aufs/lcnt.h --- a/fs/aufs/lcnt.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/lcnt.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * simple long counter wrapper + */ + +#ifndef __AUFS_LCNT_H__ +#define __AUFS_LCNT_H__ + +#ifdef __KERNEL__ + +#include "debug.h" + +#define AuLCntATOMIC 1 +#define AuLCntPCPUCNT 2 +/* + * why does percpu_refcount require extra synchronize_rcu()s in + * au_br_do_free() + */ +#define AuLCntPCPUREF 3 + +/* #define AuLCntChosen AuLCntATOMIC */ +#define AuLCntChosen AuLCntPCPUCNT +/* #define AuLCntChosen AuLCntPCPUREF */ + +#if AuLCntChosen == AuLCntATOMIC +#include + +typedef atomic_long_t au_lcnt_t; + +static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) +{ + atomic_long_set(cnt, 0); + return 0; +} + +static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) +{ + /* empty */ +} + +static inline void au_lcnt_fin(au_lcnt_t *cnt __maybe_unused, + int do_sync __maybe_unused) +{ + /* empty */ +} + +static inline void au_lcnt_inc(au_lcnt_t *cnt) +{ + atomic_long_inc(cnt); +} + +static inline void au_lcnt_dec(au_lcnt_t *cnt) +{ + atomic_long_dec(cnt); +} + +static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) +{ + return atomic_long_read(cnt); +} +#endif + +#if AuLCntChosen == AuLCntPCPUCNT +#include + +typedef struct percpu_counter au_lcnt_t; + +static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) +{ + return percpu_counter_init(cnt, 0, GFP_NOFS); +} + +static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) +{ + /* empty */ +} + +static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync __maybe_unused) +{ + percpu_counter_destroy(cnt); +} + +static inline void au_lcnt_inc(au_lcnt_t *cnt) +{ + percpu_counter_inc(cnt); +} + +static inline void au_lcnt_dec(au_lcnt_t *cnt) +{ + percpu_counter_dec(cnt); +} + +static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) +{ + s64 n; + + n = percpu_counter_sum(cnt); + BUG_ON(n < 0); + if (LONG_MAX != LLONG_MAX + && n > LONG_MAX) + AuWarn1("%s\n", "wrap-around"); + + return n; +} +#endif + +#if AuLCntChosen == AuLCntPCPUREF +#include + +typedef struct percpu_ref au_lcnt_t; + +static inline int au_lcnt_init(au_lcnt_t *cnt, percpu_ref_func_t *release) +{ + if (!release) + release = percpu_ref_exit; + return percpu_ref_init(cnt, release, /*percpu mode*/0, GFP_NOFS); +} + +static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) +{ + synchronize_rcu(); +} + +static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync) +{ + percpu_ref_kill(cnt); + if (do_sync) + au_lcnt_wait_for_fin(cnt); +} + +static inline void au_lcnt_inc(au_lcnt_t *cnt) +{ + percpu_ref_get(cnt); +} + +static inline void au_lcnt_dec(au_lcnt_t *cnt) +{ + percpu_ref_put(cnt); +} + +/* + * avoid calling this func as possible. + */ +static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev) +{ + long l; + + percpu_ref_switch_to_atomic_sync(cnt); + l = atomic_long_read(&cnt->count); + if (do_rev) + percpu_ref_switch_to_percpu(cnt); + + /* percpu_ref is initialized by 1 instead of 0 */ + return l - 1; +} +#endif + +#ifdef CONFIG_AUFS_DEBUG +#define AuLCntZero(val) do { \ + long l = val; \ + if (l) \ + AuDbg("%s = %ld\n", #val, l); \ +} while (0) +#else +#define AuLCntZero(val) do {} while (0) +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_LCNT_H__ */ diff -Naur --no-dereference a/fs/aufs/loop.c b/fs/aufs/loop.c --- a/fs/aufs/loop.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/loop.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * support for loopback block device as a branch + */ + +#include "aufs.h" + +/* added into drivers/block/loop.c */ +static struct file *(*backing_file_func)(struct super_block *sb); + +/* + * test if two lower dentries have overlapping branches. + */ +int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) +{ + struct super_block *h_sb; + struct file *backing_file; + + if (unlikely(!backing_file_func)) { + /* don't load "loop" module here */ + backing_file_func = symbol_get(loop_backing_file); + if (unlikely(!backing_file_func)) + /* "loop" module is not loaded */ + return 0; + } + + h_sb = h_adding->d_sb; + backing_file = backing_file_func(h_sb); + if (!backing_file) + return 0; + + h_adding = backing_file->f_path.dentry; + /* + * h_adding can be local NFS. + * in this case aufs cannot detect the loop. + */ + if (unlikely(h_adding->d_sb == sb)) + return 1; + return !!au_test_subdir(h_adding, sb->s_root); +} + +/* true if a kernel thread named 'loop[0-9].*' accesses a file */ +int au_test_loopback_kthread(void) +{ + int ret; + struct task_struct *tsk = current; + char c, comm[sizeof(tsk->comm)]; + + ret = 0; + if (tsk->flags & PF_KTHREAD) { + get_task_comm(comm, tsk); + c = comm[4]; + ret = ('0' <= c && c <= '9' + && !strncmp(comm, "loop", 4)); + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +#define au_warn_loopback_step 16 +static int au_warn_loopback_nelem = au_warn_loopback_step; +static unsigned long *au_warn_loopback_array; + +void au_warn_loopback(struct super_block *h_sb) +{ + int i, new_nelem; + unsigned long *a, magic; + static DEFINE_SPINLOCK(spin); + + magic = h_sb->s_magic; + spin_lock(&spin); + a = au_warn_loopback_array; + for (i = 0; i < au_warn_loopback_nelem && *a; i++) + if (a[i] == magic) { + spin_unlock(&spin); + return; + } + + /* h_sb is new to us, print it */ + if (i < au_warn_loopback_nelem) { + a[i] = magic; + goto pr; + } + + /* expand the array */ + new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; + a = au_kzrealloc(au_warn_loopback_array, + au_warn_loopback_nelem * sizeof(unsigned long), + new_nelem * sizeof(unsigned long), GFP_ATOMIC, + /*may_shrink*/0); + if (a) { + au_warn_loopback_nelem = new_nelem; + au_warn_loopback_array = a; + a[i] = magic; + goto pr; + } + + spin_unlock(&spin); + AuWarn1("realloc failed, ignored\n"); + return; + +pr: + spin_unlock(&spin); + pr_warn("you may want to try another patch for loopback file " + "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); +} + +int au_loopback_init(void) +{ + int err; + struct super_block *sb __maybe_unused; + + BUILD_BUG_ON(sizeof(sb->s_magic) != sizeof(*au_warn_loopback_array)); + + err = 0; + au_warn_loopback_array = kcalloc(au_warn_loopback_step, + sizeof(unsigned long), GFP_NOFS); + if (unlikely(!au_warn_loopback_array)) + err = -ENOMEM; + + return err; +} + +void au_loopback_fin(void) +{ + if (backing_file_func) + symbol_put(loop_backing_file); + au_kfree_try_rcu(au_warn_loopback_array); +} diff -Naur --no-dereference a/fs/aufs/loop.h b/fs/aufs/loop.h --- a/fs/aufs/loop.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/loop.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * support for loopback mount as a branch + */ + +#ifndef __AUFS_LOOP_H__ +#define __AUFS_LOOP_H__ + +#ifdef __KERNEL__ + +struct dentry; +struct super_block; + +#ifdef CONFIG_AUFS_BDEV_LOOP +/* drivers/block/loop.c */ +struct file *loop_backing_file(struct super_block *sb); + +/* loop.c */ +int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); +int au_test_loopback_kthread(void); +void au_warn_loopback(struct super_block *h_sb); + +int au_loopback_init(void); +void au_loopback_fin(void); +#else +AuStub(struct file *, loop_backing_file, return NULL, struct super_block *sb) + +AuStubInt0(au_test_loopback_overlap, struct super_block *sb, + struct dentry *h_adding) +AuStubInt0(au_test_loopback_kthread, void) +AuStubVoid(au_warn_loopback, struct super_block *h_sb) + +AuStubInt0(au_loopback_init, void) +AuStubVoid(au_loopback_fin, void) +#endif /* BLK_DEV_LOOP */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_LOOP_H__ */ diff -Naur --no-dereference a/fs/aufs/magic.mk b/fs/aufs/magic.mk --- a/fs/aufs/magic.mk 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/magic.mk 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 + +# defined in ${srctree}/fs/fuse/inode.c +# tristate +ifdef CONFIG_FUSE_FS +ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 +endif + +# defined in ${srctree}/fs/xfs/xfs_sb.h +# tristate +ifdef CONFIG_XFS_FS +ccflags-y += -DXFS_SB_MAGIC=0x58465342 +endif + +# defined in ${srctree}/fs/configfs/mount.c +# tristate +ifdef CONFIG_CONFIGFS_FS +ccflags-y += -DCONFIGFS_MAGIC=0x62656570 +endif + +# defined in ${srctree}/fs/ubifs/ubifs.h +# tristate +ifdef CONFIG_UBIFS_FS +ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 +endif + +# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h +# tristate +ifdef CONFIG_HFSPLUS_FS +ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b +endif diff -Naur --no-dereference a/fs/aufs/Makefile b/fs/aufs/Makefile --- a/fs/aufs/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/Makefile 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0 + +include ${src}/magic.mk +ifeq (${CONFIG_AUFS_FS},m) +include ${src}/conf.mk +endif +-include ${src}/priv_def.mk + +# cf. include/linux/kernel.h +# enable pr_debug +ccflags-y += -DDEBUG +# sparse requires the full pathname +ifdef M +ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h +else +ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h +endif + +obj-$(CONFIG_AUFS_FS) += aufs.o +aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ + wkq.o vfsub.o dcsub.o \ + cpup.o whout.o wbr_policy.o \ + dinfo.o dentry.o \ + dynop.o \ + finfo.o file.o f_op.o \ + dir.o vdir.o \ + iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ + mvdown.o ioctl.o + +# all are boolean +aufs-$(CONFIG_PROC_FS) += procfs.o plink.o +aufs-$(CONFIG_SYSFS) += sysfs.o +aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o +aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o +aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o +aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o +aufs-$(CONFIG_AUFS_EXPORT) += export.o +aufs-$(CONFIG_AUFS_XATTR) += xattr.o +aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o +aufs-$(CONFIG_AUFS_DIRREN) += dirren.o +aufs-$(CONFIG_AUFS_FHSM) += fhsm.o +aufs-$(CONFIG_AUFS_POLL) += poll.o +aufs-$(CONFIG_AUFS_RDU) += rdu.o +aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o +aufs-$(CONFIG_AUFS_DEBUG) += debug.o +aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o diff -Naur --no-dereference a/fs/aufs/module.c b/fs/aufs/module.c --- a/fs/aufs/module.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/module.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * module global variables and operations + */ + +#include +#include +#include "aufs.h" + +/* shrinkable realloc */ +void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink) +{ + size_t sz; + int diff; + + sz = 0; + diff = -1; + if (p) { +#if 0 /* unused */ + if (!new_sz) { + au_kfree_rcu(p); + p = NULL; + goto out; + } +#else + AuDebugOn(!new_sz); +#endif + sz = ksize(p); + diff = au_kmidx_sub(sz, new_sz); + } + if (sz && !diff) + goto out; + + if (sz < new_sz) + /* expand or SLOB */ + p = krealloc(p, new_sz, gfp); + else if (new_sz < sz && may_shrink) { + /* shrink */ + void *q; + + q = kmalloc(new_sz, gfp); + if (q) { + if (p) { + memcpy(q, p, new_sz); + au_kfree_try_rcu(p); + } + p = q; + } else + p = NULL; + } + +out: + return p; +} + +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, + int may_shrink) +{ + p = au_krealloc(p, new_sz, gfp, may_shrink); + if (p && new_sz > nused) + memset(p + nused, 0, new_sz - nused); + return p; +} + +/* ---------------------------------------------------------------------- */ +/* + * aufs caches + */ +struct kmem_cache *au_cache[AuCache_Last]; + +static void au_cache_fin(void) +{ + int i; + + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + + /* excluding AuCache_HNOTIFY */ + BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); + for (i = 0; i < AuCache_HNOTIFY; i++) { + kmem_cache_destroy(au_cache[i]); + au_cache[i] = NULL; + } +} + +static int __init au_cache_init(void) +{ + au_cache[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); + if (au_cache[AuCache_DINFO]) + /* SLAB_DESTROY_BY_RCU */ + au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, + au_icntnr_init_once); + if (au_cache[AuCache_ICNTNR]) + au_cache[AuCache_FINFO] = AuCacheCtor(au_finfo, + au_fi_init_once); + if (au_cache[AuCache_FINFO]) + au_cache[AuCache_VDIR] = AuCache(au_vdir); + if (au_cache[AuCache_VDIR]) + au_cache[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); + if (au_cache[AuCache_DEHSTR]) + return 0; + + au_cache_fin(); + return -ENOMEM; +} + +/* ---------------------------------------------------------------------- */ + +int au_dir_roflags; + +#ifdef CONFIG_AUFS_SBILIST +/* + * iterate_supers_type() doesn't protect us from + * remounting (branch management) + */ +struct hlist_bl_head au_sbilist; +#endif + +/* + * functions for module interface. + */ +MODULE_LICENSE("GPL"); +/* MODULE_LICENSE("GPL v2"); */ +MODULE_AUTHOR("Junjiro R. Okajima "); +MODULE_DESCRIPTION(AUFS_NAME + " -- Advanced multi layered unification filesystem"); +MODULE_VERSION(AUFS_VERSION); +MODULE_ALIAS_FS(AUFS_NAME); + +/* this module parameter has no meaning when SYSFS is disabled */ +int sysaufs_brs = 1; +MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); +module_param_named(brs, sysaufs_brs, int, 0444); + +/* this module parameter has no meaning when USER_NS is disabled */ +bool au_userns; +MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); +module_param_named(allow_userns, au_userns, bool, 0444); + +/* ---------------------------------------------------------------------- */ + +static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ + +int au_seq_path(struct seq_file *seq, struct path *path) +{ + int err; + + err = seq_path(seq, path, au_esc_chars); + if (err >= 0) + err = 0; + else + err = -ENOMEM; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int __init aufs_init(void) +{ + int err, i; + char *p; + + p = au_esc_chars; + for (i = 1; i <= ' '; i++) + *p++ = i; + *p++ = '\\'; + *p++ = '\x7f'; + *p = 0; + + au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); + + memcpy(aufs_iop_nogetattr, aufs_iop, sizeof(aufs_iop)); + for (i = 0; i < AuIop_Last; i++) + aufs_iop_nogetattr[i].getattr = NULL; + + memset(au_cache, 0, sizeof(au_cache)); /* including hnotify */ + + au_sbilist_init(); + sysaufs_brs_init(); + au_debug_init(); + au_dy_init(); + err = sysaufs_init(); + if (unlikely(err)) + goto out; + err = dbgaufs_init(); + if (unlikely(err)) + goto out_sysaufs; + err = au_procfs_init(); + if (unlikely(err)) + goto out_dbgaufs; + err = au_wkq_init(); + if (unlikely(err)) + goto out_procfs; + err = au_loopback_init(); + if (unlikely(err)) + goto out_wkq; + err = au_hnotify_init(); + if (unlikely(err)) + goto out_loopback; + err = au_sysrq_init(); + if (unlikely(err)) + goto out_hin; + err = au_cache_init(); + if (unlikely(err)) + goto out_sysrq; + + aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; + err = register_filesystem(&aufs_fs_type); + if (unlikely(err)) + goto out_cache; + + /* since we define pr_fmt, call printk directly */ + printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); + goto out; /* success */ + +out_cache: + au_cache_fin(); +out_sysrq: + au_sysrq_fin(); +out_hin: + au_hnotify_fin(); +out_loopback: + au_loopback_fin(); +out_wkq: + au_wkq_fin(); +out_procfs: + au_procfs_fin(); +out_dbgaufs: + dbgaufs_fin(); +out_sysaufs: + sysaufs_fin(); + au_dy_fin(); +out: + return err; +} + +static void __exit aufs_exit(void) +{ + unregister_filesystem(&aufs_fs_type); + au_cache_fin(); + au_sysrq_fin(); + au_hnotify_fin(); + au_loopback_fin(); + au_wkq_fin(); + au_procfs_fin(); + dbgaufs_fin(); + sysaufs_fin(); + au_dy_fin(); +} + +module_init(aufs_init); +module_exit(aufs_exit); diff -Naur --no-dereference a/fs/aufs/module.h b/fs/aufs/module.h --- a/fs/aufs/module.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/module.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * module initialization and module-global + */ + +#ifndef __AUFS_MODULE_H__ +#define __AUFS_MODULE_H__ + +#ifdef __KERNEL__ + +#include +#include "debug.h" +#include "dentry.h" +#include "dir.h" +#include "file.h" +#include "inode.h" + +struct path; +struct seq_file; + +/* module parameters */ +extern int sysaufs_brs; +extern bool au_userns; + +/* ---------------------------------------------------------------------- */ + +extern int au_dir_roflags; + +void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink); +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, + int may_shrink); + +/* + * Comparing the size of the object with sizeof(struct rcu_head) + * case 1: object is always larger + * --> au_kfree_rcu() or au_kfree_do_rcu() + * case 2: object is always smaller + * --> au_kfree_small() + * case 3: object can be any size + * --> au_kfree_try_rcu() + */ + +static inline void au_kfree_do_rcu(const void *p) +{ + struct { + struct rcu_head rcu; + } *a = (void *)p; + + kfree_rcu(a, rcu); +} + +#define au_kfree_rcu(_p) do { \ + typeof(_p) p = (_p); \ + BUILD_BUG_ON(sizeof(*p) < sizeof(struct rcu_head)); \ + if (p) \ + au_kfree_do_rcu(p); \ + } while (0) + +#define au_kfree_do_sz_test(sz) (sz >= sizeof(struct rcu_head)) +#define au_kfree_sz_test(p) (p && au_kfree_do_sz_test(ksize(p))) + +static inline void au_kfree_try_rcu(const void *p) +{ + if (!p) + return; + if (au_kfree_sz_test(p)) + au_kfree_do_rcu(p); + else + kfree(p); +} + +static inline void au_kfree_small(const void *p) +{ + if (!p) + return; + AuDebugOn(au_kfree_sz_test(p)); + kfree(p); +} + +static inline int au_kmidx_sub(size_t sz, size_t new_sz) +{ +#ifndef CONFIG_SLOB + return kmalloc_index(sz) - kmalloc_index(new_sz); +#else + return -1; /* SLOB is untested */ +#endif +} + +int au_seq_path(struct seq_file *seq, struct path *path); + +#ifdef CONFIG_PROC_FS +/* procfs.c */ +int __init au_procfs_init(void); +void au_procfs_fin(void); +#else +AuStubInt0(au_procfs_init, void); +AuStubVoid(au_procfs_fin, void); +#endif + +/* ---------------------------------------------------------------------- */ + +/* kmem cache */ +enum { + AuCache_DINFO, + AuCache_ICNTNR, + AuCache_FINFO, + AuCache_VDIR, + AuCache_DEHSTR, + AuCache_HNOTIFY, /* must be last */ + AuCache_Last +}; + +extern struct kmem_cache *au_cache[AuCache_Last]; + +#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) +#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) +#define AuCacheCtor(type, ctor) \ + kmem_cache_create(#type, sizeof(struct type), \ + __alignof__(struct type), AuCacheFlags, ctor) + +#define AuCacheFuncs(name, index) \ + static inline struct au_##name *au_cache_alloc_##name(void) \ + { return kmem_cache_alloc(au_cache[AuCache_##index], GFP_NOFS); } \ + static inline void au_cache_free_##name##_norcu(struct au_##name *p) \ + { kmem_cache_free(au_cache[AuCache_##index], p); } \ + \ + static inline void au_cache_free_##name##_rcu_cb(struct rcu_head *rcu) \ + { void *p = rcu; \ + p -= offsetof(struct au_##name, rcu); \ + kmem_cache_free(au_cache[AuCache_##index], p); } \ + static inline void au_cache_free_##name##_rcu(struct au_##name *p) \ + { BUILD_BUG_ON(sizeof(struct au_##name) < sizeof(struct rcu_head)); \ + call_rcu(&p->rcu, au_cache_free_##name##_rcu_cb); } \ + \ + static inline void au_cache_free_##name(struct au_##name *p) \ + { /* au_cache_free_##name##_norcu(p); */ \ + au_cache_free_##name##_rcu(p); } + +AuCacheFuncs(dinfo, DINFO); +AuCacheFuncs(icntnr, ICNTNR); +AuCacheFuncs(finfo, FINFO); +AuCacheFuncs(vdir, VDIR); +AuCacheFuncs(vdir_dehstr, DEHSTR); +#ifdef CONFIG_AUFS_HNOTIFY +AuCacheFuncs(hnotify, HNOTIFY); +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ diff -Naur --no-dereference a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c --- a/fs/aufs/mvdown.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/mvdown.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,706 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * move-down, opposite of copy-up + */ + +#include "aufs.h" + +struct au_mvd_args { + struct { + struct super_block *h_sb; + struct dentry *h_parent; + struct au_hinode *hdir; + struct inode *h_dir, *h_inode; + struct au_pin pin; + } info[AUFS_MVDOWN_NARRAY]; + + struct aufs_mvdown mvdown; + struct dentry *dentry, *parent; + struct inode *inode, *dir; + struct super_block *sb; + aufs_bindex_t bopq, bwh, bfound; + unsigned char rename_lock; +}; + +#define mvd_errno mvdown.au_errno +#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex +#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid +#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex +#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid + +#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb +#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent +#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir +#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir +#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode +#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin + +#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb +#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent +#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir +#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir +#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode +#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin + +#define AU_MVD_PR(flag, ...) do { \ + if (flag) \ + pr_err(__VA_ARGS__); \ + } while (0) + +static int find_lower_writable(struct au_mvd_args *a) +{ + struct super_block *sb; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + sb = a->sb; + bindex = a->mvd_bsrc; + bbot = au_sbbot(sb); + if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) + for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm) + && !sb_rdonly(au_br_sb(br))) + return bindex; + } + else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) + for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br)) + return bindex; + } + else + for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!sb_rdonly(au_br_sb(br))) { + if (au_br_rdonly(br)) + a->mvdown.flags + |= AUFS_MVDOWN_ROLOWER_R; + return bindex; + } + } + + return -1; +} + +/* make the parent dir on bdst */ +static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + + err = 0; + a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); + a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); + a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); + a->mvd_h_dst_parent = NULL; + if (au_dbbot(a->parent) >= a->mvd_bdst) + a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); + if (!a->mvd_h_dst_parent) { + err = au_cpdown_dirs(a->dentry, a->mvd_bdst); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); + goto out; + } + a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); + } + +out: + AuTraceErr(err); + return err; +} + +/* lock them all */ +static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct dentry *h_trap; + + a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); + a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); + err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, + au_opt_udba(a->sb), + AuPin_MNT_WRITE | AuPin_DI_LOCKED); + AuTraceErr(err); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "pin_dst failed\n"); + goto out; + } + + if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { + a->rename_lock = 0; + au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, + AuLsc_DI_PARENT, AuLsc_I_PARENT3, + au_opt_udba(a->sb), + AuPin_MNT_WRITE | AuPin_DI_LOCKED); + err = au_do_pin(&a->mvd_pin_src); + AuTraceErr(err); + a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "pin_src failed\n"); + goto out_dst; + } + goto out; /* success */ + } + + a->rename_lock = 1; + au_pin_hdir_unlock(&a->mvd_pin_dst); + err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, + au_opt_udba(a->sb), + AuPin_MNT_WRITE | AuPin_DI_LOCKED); + AuTraceErr(err); + a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "pin_src failed\n"); + au_pin_hdir_lock(&a->mvd_pin_dst); + goto out_dst; + } + au_pin_hdir_unlock(&a->mvd_pin_src); + h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, + a->mvd_h_dst_parent, a->mvd_hdir_dst); + if (h_trap) { + err = (h_trap != a->mvd_h_src_parent); + if (err) + err = (h_trap != a->mvd_h_dst_parent); + } + BUG_ON(err); /* it should never happen */ + if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { + err = -EBUSY; + AuTraceErr(err); + vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, + a->mvd_h_dst_parent, a->mvd_hdir_dst); + au_pin_hdir_lock(&a->mvd_pin_src); + au_unpin(&a->mvd_pin_src); + au_pin_hdir_lock(&a->mvd_pin_dst); + goto out_dst; + } + goto out; /* success */ + +out_dst: + au_unpin(&a->mvd_pin_dst); +out: + AuTraceErr(err); + return err; +} + +static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) +{ + if (!a->rename_lock) + au_unpin(&a->mvd_pin_src); + else { + vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, + a->mvd_h_dst_parent, a->mvd_hdir_dst); + au_pin_hdir_lock(&a->mvd_pin_src); + au_unpin(&a->mvd_pin_src); + au_pin_hdir_lock(&a->mvd_pin_dst); + } + au_unpin(&a->mvd_pin_dst); +} + +/* copy-down the file */ +static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct au_cp_generic cpg = { + .dentry = a->dentry, + .bdst = a->mvd_bdst, + .bsrc = a->mvd_bsrc, + .len = -1, + .pin = &a->mvd_pin_dst, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + + AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); + if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) + au_fset_cpup(cpg.flags, OVERWRITE); + if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) + au_fset_cpup(cpg.flags, RWDST); + err = au_sio_cpdown_simple(&cpg); + if (unlikely(err)) + AU_MVD_PR(dmsg, "cpdown failed\n"); + + AuTraceErr(err); + return err; +} + +/* + * unlink the whiteout on bdst if exist which may be created by UDBA while we + * were sleeping + */ +static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct path h_path; + struct au_branch *br; + struct inode *delegated; + + br = au_sbr(a->sb, a->mvd_bdst); + h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) { + AU_MVD_PR(dmsg, "wh_lkup failed\n"); + goto out; + } + + err = 0; + if (d_is_positive(h_path.dentry)) { + h_path.mnt = au_br_mnt(br); + delegated = NULL; + err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, + &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) + AU_MVD_PR(dmsg, "wh_unlink failed\n"); + } + dput(h_path.dentry); + +out: + AuTraceErr(err); + return err; +} + +/* + * unlink the topmost h_dentry + */ +static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct path h_path; + struct inode *delegated; + + h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); + h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); + delegated = NULL; + err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) + AU_MVD_PR(dmsg, "unlink failed\n"); + + AuTraceErr(err); + return err; +} + +/* Since mvdown succeeded, we ignore an error of this function */ +static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct au_branch *br; + + a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; + br = au_sbr(a->sb, a->mvd_bsrc); + err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); + if (!err) { + br = au_sbr(a->sb, a->mvd_bdst); + a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; + err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); + } + if (!err) + a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; + else + AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); +} + +/* + * copy-down the file and unlink the bsrc file. + * - unlink the bdst whout if exist + * - copy-down the file (with whtmp name and rename) + * - unlink the bsrc file + */ +static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + + err = au_do_mkdir(dmsg, a); + if (!err) + err = au_do_lock(dmsg, a); + if (unlikely(err)) + goto out; + + /* + * do not revert the activities we made on bdst since they should be + * harmless in aufs. + */ + + err = au_do_cpdown(dmsg, a); + if (!err) + err = au_do_unlink_wh(dmsg, a); + if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) + err = au_do_unlink(dmsg, a); + if (unlikely(err)) + goto out_unlock; + + AuDbg("%pd2, 0x%x, %d --> %d\n", + a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); + if (find_lower_writable(a) < 0) + a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; + + if (a->mvdown.flags & AUFS_MVDOWN_STFS) + au_do_stfs(dmsg, a); + + /* maintain internal array */ + if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { + au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); + au_set_dbtop(a->dentry, a->mvd_bdst); + au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); + au_set_ibtop(a->inode, a->mvd_bdst); + } else { + /* hide the lower */ + au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); + au_set_dbbot(a->dentry, a->mvd_bsrc); + au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); + au_set_ibbot(a->inode, a->mvd_bsrc); + } + if (au_dbbot(a->dentry) < a->mvd_bdst) + au_set_dbbot(a->dentry, a->mvd_bdst); + if (au_ibbot(a->inode) < a->mvd_bdst) + au_set_ibbot(a->inode, a->mvd_bdst); + +out_unlock: + au_do_unlock(dmsg, a); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* make sure the file is idle */ +static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err, plinked; + + err = 0; + plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); + if (au_dbtop(a->dentry) == a->mvd_bsrc + && au_dcount(a->dentry) == 1 + && atomic_read(&a->inode->i_count) == 1 + /* && a->mvd_h_src_inode->i_nlink == 1 */ + && (!plinked || !au_plink_test(a->inode)) + && a->inode->i_nlink == 1) + goto out; + + err = -EBUSY; + AU_MVD_PR(dmsg, + "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", + a->mvd_bsrc, au_dbtop(a->dentry), au_dcount(a->dentry), + atomic_read(&a->inode->i_count), a->inode->i_nlink, + a->mvd_h_src_inode->i_nlink, + plinked, plinked ? au_plink_test(a->inode) : 0); + +out: + AuTraceErr(err); + return err; +} + +/* make sure the parent dir is fine */ +static int au_mvd_args_parent(const unsigned char dmsg, + struct au_mvd_args *a) +{ + int err; + aufs_bindex_t bindex; + + err = 0; + if (unlikely(au_alive_dir(a->parent))) { + err = -ENOENT; + AU_MVD_PR(dmsg, "parent dir is dead\n"); + goto out; + } + + a->bopq = au_dbdiropq(a->parent); + bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); + AuDbg("b%d\n", bindex); + if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) + || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { + err = -EINVAL; + a->mvd_errno = EAU_MVDOWN_OPAQUE; + AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", + a->bopq, a->mvd_bdst); + } + +out: + AuTraceErr(err); + return err; +} + +static int au_mvd_args_intermediate(const unsigned char dmsg, + struct au_mvd_args *a) +{ + int err; + struct au_dinfo *dinfo, *tmp; + + /* lookup the next lower positive entry */ + err = -ENOMEM; + tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); + if (unlikely(!tmp)) + goto out; + + a->bfound = -1; + a->bwh = -1; + dinfo = au_di(a->dentry); + au_di_cp(tmp, dinfo); + au_di_swap(tmp, dinfo); + + /* returns the number of positive dentries */ + err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, + /* AuLkup_IGNORE_PERM */ 0); + if (!err) + a->bwh = au_dbwh(a->dentry); + else if (err > 0) + a->bfound = au_dbtop(a->dentry); + + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + if (unlikely(err < 0)) + AU_MVD_PR(dmsg, "failed look-up lower\n"); + + /* + * here, we have these cases. + * bfound == -1 + * no positive dentry under bsrc. there are more sub-cases. + * bwh < 0 + * there no whiteout, we can safely move-down. + * bwh <= bsrc + * impossible + * bsrc < bwh && bwh < bdst + * there is a whiteout on RO branch. cannot proceed. + * bwh == bdst + * there is a whiteout on the RW target branch. it should + * be removed. + * bdst < bwh + * there is a whiteout somewhere unrelated branch. + * -1 < bfound && bfound <= bsrc + * impossible. + * bfound < bdst + * found, but it is on RO branch between bsrc and bdst. cannot + * proceed. + * bfound == bdst + * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return + * error. + * bdst < bfound + * found, after we create the file on bdst, it will be hidden. + */ + + AuDebugOn(a->bfound == -1 + && a->bwh != -1 + && a->bwh <= a->mvd_bsrc); + AuDebugOn(-1 < a->bfound + && a->bfound <= a->mvd_bsrc); + + err = -EINVAL; + if (a->bfound == -1 + && a->mvd_bsrc < a->bwh + && a->bwh != -1 + && a->bwh < a->mvd_bdst) { + a->mvd_errno = EAU_MVDOWN_WHITEOUT; + AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", + a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); + goto out; + } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { + a->mvd_errno = EAU_MVDOWN_UPPER; + AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", + a->mvd_bdst, a->bfound); + goto out; + } + + err = 0; /* success */ + +out: + AuTraceErr(err); + return err; +} + +static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + + err = 0; + if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) + && a->bfound == a->mvd_bdst) + err = -EEXIST; + AuTraceErr(err); + return err; +} + +static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct au_branch *br; + + err = -EISDIR; + if (unlikely(S_ISDIR(a->inode->i_mode))) + goto out; + + err = -EINVAL; + if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) + a->mvd_bsrc = au_ibtop(a->inode); + else { + a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); + if (unlikely(a->mvd_bsrc < 0 + || (a->mvd_bsrc < au_dbtop(a->dentry) + || au_dbbot(a->dentry) < a->mvd_bsrc + || !au_h_dptr(a->dentry, a->mvd_bsrc)) + || (a->mvd_bsrc < au_ibtop(a->inode) + || au_ibbot(a->inode) < a->mvd_bsrc + || !au_h_iptr(a->inode, a->mvd_bsrc)))) { + a->mvd_errno = EAU_MVDOWN_NOUPPER; + AU_MVD_PR(dmsg, "no upper\n"); + goto out; + } + } + if (unlikely(a->mvd_bsrc == au_sbbot(a->sb))) { + a->mvd_errno = EAU_MVDOWN_BOTTOM; + AU_MVD_PR(dmsg, "on the bottom\n"); + goto out; + } + a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); + br = au_sbr(a->sb, a->mvd_bsrc); + err = au_br_rdonly(br); + if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { + if (unlikely(err)) + goto out; + } else if (!(vfsub_native_ro(a->mvd_h_src_inode) + || IS_APPEND(a->mvd_h_src_inode))) { + if (err) + a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; + /* go on */ + } else + goto out; + + err = -EINVAL; + if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { + a->mvd_bdst = find_lower_writable(a); + if (unlikely(a->mvd_bdst < 0)) { + a->mvd_errno = EAU_MVDOWN_BOTTOM; + AU_MVD_PR(dmsg, "no writable lower branch\n"); + goto out; + } + } else { + a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); + if (unlikely(a->mvd_bdst < 0 + || au_sbbot(a->sb) < a->mvd_bdst)) { + a->mvd_errno = EAU_MVDOWN_NOLOWERBR; + AU_MVD_PR(dmsg, "no lower brid\n"); + goto out; + } + } + + err = au_mvd_args_busy(dmsg, a); + if (!err) + err = au_mvd_args_parent(dmsg, a); + if (!err) + err = au_mvd_args_intermediate(dmsg, a); + if (!err) + err = au_mvd_args_exist(dmsg, a); + if (!err) + AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); + +out: + AuTraceErr(err); + return err; +} + +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) +{ + int err, e; + unsigned char dmsg; + struct au_mvd_args *args; + struct inode *inode; + + inode = d_inode(dentry); + err = -EPERM; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = -ENOMEM; + args = kmalloc(sizeof(*args), GFP_NOFS); + if (unlikely(!args)) + goto out; + + err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); + if (!err) + /* VERIFY_WRITE */ + err = !access_ok(uarg, sizeof(*uarg)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out_free; + } + AuDbg("flags 0x%x\n", args->mvdown.flags); + args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); + args->mvdown.au_errno = 0; + args->dentry = dentry; + args->inode = inode; + args->sb = dentry->d_sb; + + err = -ENOENT; + dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); + args->parent = dget_parent(dentry); + args->dir = d_inode(args->parent); + inode_lock_nested(args->dir, I_MUTEX_PARENT); + dput(args->parent); + if (unlikely(args->parent != dentry->d_parent)) { + AU_MVD_PR(dmsg, "parent dir is moved\n"); + goto out_dir; + } + + inode_lock_nested(inode, I_MUTEX_CHILD); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); + if (unlikely(err)) + goto out_inode; + + di_write_lock_parent(args->parent); + err = au_mvd_args(dmsg, args); + if (unlikely(err)) + goto out_parent; + + err = au_do_mvdown(dmsg, args); + if (unlikely(err)) + goto out_parent; + + au_cpup_attr_timesizes(args->dir); + au_cpup_attr_timesizes(inode); + if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER)) + au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst)); + /* au_digen_dec(dentry); */ + +out_parent: + di_write_unlock(args->parent); + aufs_read_unlock(dentry, AuLock_DW); +out_inode: + inode_unlock(inode); +out_dir: + inode_unlock(args->dir); +out_free: + e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); + if (unlikely(e)) + err = -EFAULT; + au_kfree_rcu(args); +out: + AuTraceErr(err); + return err; +} diff -Naur --no-dereference a/fs/aufs/opts.c b/fs/aufs/opts.c --- a/fs/aufs/opts.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/opts.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1880 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * mount options/flags + */ + +#include +#include /* a distribution requires */ +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +enum { + Opt_br, + Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, + Opt_idel, Opt_imod, + Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, + Opt_rdblk_def, Opt_rdhash_def, + Opt_xino, Opt_noxino, + Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, + Opt_trunc_xino_path, Opt_itrunc_xino, + Opt_trunc_xib, Opt_notrunc_xib, + Opt_shwh, Opt_noshwh, + Opt_plink, Opt_noplink, Opt_list_plink, + Opt_udba, + Opt_dio, Opt_nodio, + Opt_diropq_a, Opt_diropq_w, + Opt_warn_perm, Opt_nowarn_perm, + Opt_wbr_copyup, Opt_wbr_create, + Opt_fhsm_sec, + Opt_verbose, Opt_noverbose, + Opt_sum, Opt_nosum, Opt_wsum, + Opt_dirperm1, Opt_nodirperm1, + Opt_dirren, Opt_nodirren, + Opt_acl, Opt_noacl, + Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err +}; + +static match_table_t options = { + {Opt_br, "br=%s"}, + {Opt_br, "br:%s"}, + + {Opt_add, "add=%d:%s"}, + {Opt_add, "add:%d:%s"}, + {Opt_add, "ins=%d:%s"}, + {Opt_add, "ins:%d:%s"}, + {Opt_append, "append=%s"}, + {Opt_append, "append:%s"}, + {Opt_prepend, "prepend=%s"}, + {Opt_prepend, "prepend:%s"}, + + {Opt_del, "del=%s"}, + {Opt_del, "del:%s"}, + /* {Opt_idel, "idel:%d"}, */ + {Opt_mod, "mod=%s"}, + {Opt_mod, "mod:%s"}, + /* {Opt_imod, "imod:%d:%s"}, */ + + {Opt_dirwh, "dirwh=%d"}, + + {Opt_xino, "xino=%s"}, + {Opt_noxino, "noxino"}, + {Opt_trunc_xino, "trunc_xino"}, + {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, + {Opt_notrunc_xino, "notrunc_xino"}, + {Opt_trunc_xino_path, "trunc_xino=%s"}, + {Opt_itrunc_xino, "itrunc_xino=%d"}, + /* {Opt_zxino, "zxino=%s"}, */ + {Opt_trunc_xib, "trunc_xib"}, + {Opt_notrunc_xib, "notrunc_xib"}, + +#ifdef CONFIG_PROC_FS + {Opt_plink, "plink"}, +#else + {Opt_ignore_silent, "plink"}, +#endif + + {Opt_noplink, "noplink"}, + +#ifdef CONFIG_AUFS_DEBUG + {Opt_list_plink, "list_plink"}, +#endif + + {Opt_udba, "udba=%s"}, + + {Opt_dio, "dio"}, + {Opt_nodio, "nodio"}, + +#ifdef CONFIG_AUFS_DIRREN + {Opt_dirren, "dirren"}, + {Opt_nodirren, "nodirren"}, +#else + {Opt_ignore, "dirren"}, + {Opt_ignore_silent, "nodirren"}, +#endif + +#ifdef CONFIG_AUFS_FHSM + {Opt_fhsm_sec, "fhsm_sec=%d"}, +#else + {Opt_ignore, "fhsm_sec=%d"}, +#endif + + {Opt_diropq_a, "diropq=always"}, + {Opt_diropq_a, "diropq=a"}, + {Opt_diropq_w, "diropq=whiteouted"}, + {Opt_diropq_w, "diropq=w"}, + + {Opt_warn_perm, "warn_perm"}, + {Opt_nowarn_perm, "nowarn_perm"}, + + /* keep them temporary */ + {Opt_ignore_silent, "nodlgt"}, + {Opt_ignore, "clean_plink"}, + +#ifdef CONFIG_AUFS_SHWH + {Opt_shwh, "shwh"}, +#endif + {Opt_noshwh, "noshwh"}, + + {Opt_dirperm1, "dirperm1"}, + {Opt_nodirperm1, "nodirperm1"}, + + {Opt_verbose, "verbose"}, + {Opt_verbose, "v"}, + {Opt_noverbose, "noverbose"}, + {Opt_noverbose, "quiet"}, + {Opt_noverbose, "q"}, + {Opt_noverbose, "silent"}, + + {Opt_sum, "sum"}, + {Opt_nosum, "nosum"}, + {Opt_wsum, "wsum"}, + + {Opt_rdcache, "rdcache=%d"}, + {Opt_rdblk, "rdblk=%d"}, + {Opt_rdblk_def, "rdblk=def"}, + {Opt_rdhash, "rdhash=%d"}, + {Opt_rdhash_def, "rdhash=def"}, + + {Opt_wbr_create, "create=%s"}, + {Opt_wbr_create, "create_policy=%s"}, + {Opt_wbr_copyup, "cpup=%s"}, + {Opt_wbr_copyup, "copyup=%s"}, + {Opt_wbr_copyup, "copyup_policy=%s"}, + + /* generic VFS flag */ +#ifdef CONFIG_FS_POSIX_ACL + {Opt_acl, "acl"}, + {Opt_noacl, "noacl"}, +#else + {Opt_ignore, "acl"}, + {Opt_ignore_silent, "noacl"}, +#endif + + /* internal use for the scripts */ + {Opt_ignore_silent, "si=%s"}, + + {Opt_br, "dirs=%s"}, + {Opt_ignore, "debug=%d"}, + {Opt_ignore, "delete=whiteout"}, + {Opt_ignore, "delete=all"}, + {Opt_ignore, "imap=%s"}, + + /* temporary workaround, due to old mount(8)? */ + {Opt_ignore_silent, "relatime"}, + + {Opt_err, NULL} +}; + +/* ---------------------------------------------------------------------- */ + +static const char *au_parser_pattern(int val, match_table_t tbl) +{ + struct match_token *p; + + p = tbl; + while (p->pattern) { + if (p->token == val) + return p->pattern; + p++; + } + BUG(); + return "??"; +} + +static const char *au_optstr(int *val, match_table_t tbl) +{ + struct match_token *p; + int v; + + v = *val; + if (!v) + goto out; + p = tbl; + while (p->pattern) { + if (p->token + && (v & p->token) == p->token) { + *val &= ~p->token; + return p->pattern; + } + p++; + } + +out: + return NULL; +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t brperm = { + {AuBrPerm_RO, AUFS_BRPERM_RO}, + {AuBrPerm_RR, AUFS_BRPERM_RR}, + {AuBrPerm_RW, AUFS_BRPERM_RW}, + {0, NULL} +}; + +static match_table_t brattr = { + /* general */ + {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, + {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, + /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ + {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, +#ifdef CONFIG_AUFS_FHSM + {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, +#endif +#ifdef CONFIG_AUFS_XATTR + {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, + {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, + {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, + {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, + {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, + {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, +#endif + + /* ro/rr branch */ + {AuBrRAttr_WH, AUFS_BRRATTR_WH}, + + /* rw branch */ + {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, + {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, + + {0, NULL} +}; + +static int br_attr_val(char *str, match_table_t table, substring_t args[]) +{ + int attr, v; + char *p; + + attr = 0; + do { + p = strchr(str, '+'); + if (p) + *p = 0; + v = match_token(str, table, args); + if (v) { + if (v & AuBrAttr_CMOO_Mask) + attr &= ~AuBrAttr_CMOO_Mask; + attr |= v; + } else { + if (p) + *p = '+'; + pr_warn("ignored branch attribute %s\n", str); + break; + } + if (p) + str = p + 1; + } while (p); + + return attr; +} + +static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) +{ + int sz; + const char *p; + char *q; + + q = str->a; + *q = 0; + p = au_optstr(&perm, brattr); + if (p) { + sz = strlen(p); + memcpy(q, p, sz + 1); + q += sz; + } else + goto out; + + do { + p = au_optstr(&perm, brattr); + if (p) { + *q++ = '+'; + sz = strlen(p); + memcpy(q, p, sz + 1); + q += sz; + } + } while (p); + +out: + return q - str->a; +} + +static int noinline_for_stack br_perm_val(char *perm) +{ + int val, bad, sz; + char *p; + substring_t args[MAX_OPT_ARGS]; + au_br_perm_str_t attr; + + p = strchr(perm, '+'); + if (p) + *p = 0; + val = match_token(perm, brperm, args); + if (!val) { + if (p) + *p = '+'; + pr_warn("ignored branch permission %s\n", perm); + val = AuBrPerm_RO; + goto out; + } + if (!p) + goto out; + + val |= br_attr_val(p + 1, brattr, args); + + bad = 0; + switch (val & AuBrPerm_Mask) { + case AuBrPerm_RO: + case AuBrPerm_RR: + bad = val & AuBrWAttr_Mask; + val &= ~AuBrWAttr_Mask; + break; + case AuBrPerm_RW: + bad = val & AuBrRAttr_Mask; + val &= ~AuBrRAttr_Mask; + break; + } + + /* + * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs + * does not treat it as an error, just warning. + * this is a tiny guard for the user operation. + */ + if (val & AuBrAttr_UNPIN) { + bad |= AuBrAttr_UNPIN; + val &= ~AuBrAttr_UNPIN; + } + + if (unlikely(bad)) { + sz = au_do_optstr_br_attr(&attr, bad); + AuDebugOn(!sz); + pr_warn("ignored branch attribute %s\n", attr.a); + } + +out: + return val; +} + +void au_optstr_br_perm(au_br_perm_str_t *str, int perm) +{ + au_br_perm_str_t attr; + const char *p; + char *q; + int sz; + + q = str->a; + p = au_optstr(&perm, brperm); + AuDebugOn(!p || !*p); + sz = strlen(p); + memcpy(q, p, sz + 1); + q += sz; + + sz = au_do_optstr_br_attr(&attr, perm); + if (sz) { + *q++ = '+'; + memcpy(q, attr.a, sz + 1); + } + + AuDebugOn(strlen(str->a) >= sizeof(str->a)); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t udbalevel = { + {AuOpt_UDBA_REVAL, "reval"}, + {AuOpt_UDBA_NONE, "none"}, +#ifdef CONFIG_AUFS_HNOTIFY + {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ +#ifdef CONFIG_AUFS_HFSNOTIFY + {AuOpt_UDBA_HNOTIFY, "fsnotify"}, +#endif +#endif + {-1, NULL} +}; + +static int noinline_for_stack udba_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + + return match_token(str, udbalevel, args); +} + +const char *au_optstr_udba(int udba) +{ + return au_parser_pattern(udba, udbalevel); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t au_wbr_create_policy = { + {AuWbrCreate_TDP, "tdp"}, + {AuWbrCreate_TDP, "top-down-parent"}, + {AuWbrCreate_RR, "rr"}, + {AuWbrCreate_RR, "round-robin"}, + {AuWbrCreate_MFS, "mfs"}, + {AuWbrCreate_MFS, "most-free-space"}, + {AuWbrCreate_MFSV, "mfs:%d"}, + {AuWbrCreate_MFSV, "most-free-space:%d"}, + + /* top-down regardless the parent, and then mfs */ + {AuWbrCreate_TDMFS, "tdmfs:%d"}, + {AuWbrCreate_TDMFSV, "tdmfs:%d:%d"}, + + {AuWbrCreate_MFSRR, "mfsrr:%d"}, + {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, + {AuWbrCreate_PMFS, "pmfs"}, + {AuWbrCreate_PMFSV, "pmfs:%d"}, + {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, + {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, + + {-1, NULL} +}; + +static int au_wbr_mfs_wmark(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ + int err; + unsigned long long ull; + + err = 0; + if (!match_u64(arg, &ull)) + create->mfsrr_watermark = ull; + else { + pr_err("bad integer in %s\n", str); + err = -EINVAL; + } + + return err; +} + +static int au_wbr_mfs_sec(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ + int n, err; + + err = 0; + if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) + create->mfs_second = n; + else { + pr_err("bad integer in %s\n", str); + err = -EINVAL; + } + + return err; +} + +static int noinline_for_stack +au_wbr_create_val(char *str, struct au_opt_wbr_create *create) +{ + int err, e; + substring_t args[MAX_OPT_ARGS]; + + err = match_token(str, au_wbr_create_policy, args); + create->wbr_create = err; + switch (err) { + case AuWbrCreate_MFSRRV: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (!e) + e = au_wbr_mfs_sec(&args[1], str, create); + if (unlikely(e)) + err = e; + break; + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + case AuWbrCreate_PMFSRR: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (unlikely(e)) { + err = e; + break; + } + fallthrough; + case AuWbrCreate_MFS: + case AuWbrCreate_PMFS: + create->mfs_second = AUFS_MFS_DEF_SEC; + break; + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + e = au_wbr_mfs_sec(&args[0], str, create); + if (unlikely(e)) + err = e; + break; + } + + return err; +} + +const char *au_optstr_wbr_create(int wbr_create) +{ + return au_parser_pattern(wbr_create, au_wbr_create_policy); +} + +static match_table_t au_wbr_copyup_policy = { + {AuWbrCopyup_TDP, "tdp"}, + {AuWbrCopyup_TDP, "top-down-parent"}, + {AuWbrCopyup_BUP, "bup"}, + {AuWbrCopyup_BUP, "bottom-up-parent"}, + {AuWbrCopyup_BU, "bu"}, + {AuWbrCopyup_BU, "bottom-up"}, + {-1, NULL} +}; + +static int noinline_for_stack au_wbr_copyup_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + + return match_token(str, au_wbr_copyup_policy, args); +} + +const char *au_optstr_wbr_copyup(int wbr_copyup) +{ + return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); +} + +/* ---------------------------------------------------------------------- */ + +static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; + +static void dump_opts(struct au_opts *opts) +{ +#ifdef CONFIG_AUFS_DEBUG + /* reduce stack space */ + union { + struct au_opt_add *add; + struct au_opt_del *del; + struct au_opt_mod *mod; + struct au_opt_xino *xino; + struct au_opt_xino_itrunc *xino_itrunc; + struct au_opt_wbr_create *create; + } u; + struct au_opt *opt; + + opt = opts->opt; + while (opt->type != Opt_tail) { + switch (opt->type) { + case Opt_add: + u.add = &opt->add; + AuDbg("add {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->pathname, u.add->perm, + u.add->path.dentry); + break; + case Opt_del: + case Opt_idel: + u.del = &opt->del; + AuDbg("del {%s, %p}\n", + u.del->pathname, u.del->h_path.dentry); + break; + case Opt_mod: + case Opt_imod: + u.mod = &opt->mod; + AuDbg("mod {%s, 0x%x, %p}\n", + u.mod->path, u.mod->perm, u.mod->h_root); + break; + case Opt_append: + u.add = &opt->add; + AuDbg("append {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->pathname, u.add->perm, + u.add->path.dentry); + break; + case Opt_prepend: + u.add = &opt->add; + AuDbg("prepend {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->pathname, u.add->perm, + u.add->path.dentry); + break; + case Opt_dirwh: + AuDbg("dirwh %d\n", opt->dirwh); + break; + case Opt_rdcache: + AuDbg("rdcache %d\n", opt->rdcache); + break; + case Opt_rdblk: + AuDbg("rdblk %u\n", opt->rdblk); + break; + case Opt_rdblk_def: + AuDbg("rdblk_def\n"); + break; + case Opt_rdhash: + AuDbg("rdhash %u\n", opt->rdhash); + break; + case Opt_rdhash_def: + AuDbg("rdhash_def\n"); + break; + case Opt_xino: + u.xino = &opt->xino; + AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); + break; + case Opt_trunc_xino: + AuLabel(trunc_xino); + break; + case Opt_notrunc_xino: + AuLabel(notrunc_xino); + break; + case Opt_trunc_xino_path: + case Opt_itrunc_xino: + u.xino_itrunc = &opt->xino_itrunc; + AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); + break; + case Opt_noxino: + AuLabel(noxino); + break; + case Opt_trunc_xib: + AuLabel(trunc_xib); + break; + case Opt_notrunc_xib: + AuLabel(notrunc_xib); + break; + case Opt_shwh: + AuLabel(shwh); + break; + case Opt_noshwh: + AuLabel(noshwh); + break; + case Opt_dirperm1: + AuLabel(dirperm1); + break; + case Opt_nodirperm1: + AuLabel(nodirperm1); + break; + case Opt_plink: + AuLabel(plink); + break; + case Opt_noplink: + AuLabel(noplink); + break; + case Opt_list_plink: + AuLabel(list_plink); + break; + case Opt_udba: + AuDbg("udba %d, %s\n", + opt->udba, au_optstr_udba(opt->udba)); + break; + case Opt_dio: + AuLabel(dio); + break; + case Opt_nodio: + AuLabel(nodio); + break; + case Opt_diropq_a: + AuLabel(diropq_a); + break; + case Opt_diropq_w: + AuLabel(diropq_w); + break; + case Opt_warn_perm: + AuLabel(warn_perm); + break; + case Opt_nowarn_perm: + AuLabel(nowarn_perm); + break; + case Opt_verbose: + AuLabel(verbose); + break; + case Opt_noverbose: + AuLabel(noverbose); + break; + case Opt_sum: + AuLabel(sum); + break; + case Opt_nosum: + AuLabel(nosum); + break; + case Opt_wsum: + AuLabel(wsum); + break; + case Opt_wbr_create: + u.create = &opt->wbr_create; + AuDbg("create %d, %s\n", u.create->wbr_create, + au_optstr_wbr_create(u.create->wbr_create)); + switch (u.create->wbr_create) { + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + AuDbg("%d sec\n", u.create->mfs_second); + break; + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + AuDbg("%llu watermark\n", + u.create->mfsrr_watermark); + break; + case AuWbrCreate_MFSRRV: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + AuDbg("%llu watermark, %d sec\n", + u.create->mfsrr_watermark, + u.create->mfs_second); + break; + } + break; + case Opt_wbr_copyup: + AuDbg("copyup %d, %s\n", opt->wbr_copyup, + au_optstr_wbr_copyup(opt->wbr_copyup)); + break; + case Opt_fhsm_sec: + AuDbg("fhsm_sec %u\n", opt->fhsm_second); + break; + case Opt_dirren: + AuLabel(dirren); + break; + case Opt_nodirren: + AuLabel(nodirren); + break; + case Opt_acl: + AuLabel(acl); + break; + case Opt_noacl: + AuLabel(noacl); + break; + default: + BUG(); + } + opt++; + } +#endif +} + +void au_opts_free(struct au_opts *opts) +{ + struct au_opt *opt; + + opt = opts->opt; + while (opt->type != Opt_tail) { + switch (opt->type) { + case Opt_add: + case Opt_append: + case Opt_prepend: + path_put(&opt->add.path); + break; + case Opt_del: + case Opt_idel: + path_put(&opt->del.h_path); + break; + case Opt_mod: + case Opt_imod: + dput(opt->mod.h_root); + break; + case Opt_xino: + fput(opt->xino.file); + break; + } + opt++; + } +} + +static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, + aufs_bindex_t bindex) +{ + int err; + struct au_opt_add *add = &opt->add; + char *p; + + add->bindex = bindex; + add->perm = AuBrPerm_RO; + add->pathname = opt_str; + p = strchr(opt_str, '='); + if (p) { + *p++ = 0; + if (*p) + add->perm = br_perm_val(p); + } + + err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); + if (!err) { + if (!p) { + add->perm = AuBrPerm_RO; + if (au_test_fs_rr(add->path.dentry->d_sb)) + add->perm = AuBrPerm_RR; + else if (!bindex && !(sb_flags & SB_RDONLY)) + add->perm = AuBrPerm_RW; + } + opt->type = Opt_add; + goto out; + } + pr_err("lookup failed %s (%d)\n", add->pathname, err); + err = -EINVAL; + +out: + return err; +} + +static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) +{ + int err; + + del->pathname = args[0].from; + AuDbg("del path %s\n", del->pathname); + + err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); + if (unlikely(err)) + pr_err("lookup failed %s (%d)\n", del->pathname, err); + + return err; +} + +#if 0 /* reserved for future use */ +static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, + struct au_opt_del *del, substring_t args[]) +{ + int err; + struct dentry *root; + + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); + if (bindex < 0 || au_sbbot(sb) < bindex) { + pr_err("out of bounds, %d\n", bindex); + goto out; + } + + err = 0; + del->h_path.dentry = dget(au_h_dptr(root, bindex)); + del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); + +out: + aufs_read_unlock(root, !AuLock_IR); + return err; +} +#endif + +static int noinline_for_stack +au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) +{ + int err; + struct path path; + char *p; + + err = -EINVAL; + mod->path = args[0].from; + p = strchr(mod->path, '='); + if (unlikely(!p)) { + pr_err("no permission %s\n", args[0].from); + goto out; + } + + *p++ = 0; + err = vfsub_kern_path(mod->path, lkup_dirflags, &path); + if (unlikely(err)) { + pr_err("lookup failed %s (%d)\n", mod->path, err); + goto out; + } + + mod->perm = br_perm_val(p); + AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); + mod->h_root = dget(path.dentry); + path_put(&path); + +out: + return err; +} + +#if 0 /* reserved for future use */ +static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, + struct au_opt_mod *mod, substring_t args[]) +{ + int err; + struct dentry *root; + + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); + if (bindex < 0 || au_sbbot(sb) < bindex) { + pr_err("out of bounds, %d\n", bindex); + goto out; + } + + err = 0; + mod->perm = br_perm_val(args[1].from); + AuDbg("mod path %s, perm 0x%x, %s\n", + mod->path, mod->perm, args[1].from); + mod->h_root = dget(au_h_dptr(root, bindex)); + +out: + aufs_read_unlock(root, !AuLock_IR); + return err; +} +#endif + +static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, + substring_t args[]) +{ + int err; + struct file *file; + + file = au_xino_create(sb, args[0].from, /*silent*/0, /*wbrtop*/0); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + + err = -EINVAL; + if (unlikely(file->f_path.dentry->d_sb == sb)) { + fput(file); + pr_err("%s must be outside\n", args[0].from); + goto out; + } + + err = 0; + xino->file = file; + xino->path = args[0].from; + +out: + return err; +} + +static int noinline_for_stack +au_opts_parse_xino_itrunc_path(struct super_block *sb, + struct au_opt_xino_itrunc *xino_itrunc, + substring_t args[]) +{ + int err; + aufs_bindex_t bbot, bindex; + struct path path; + struct dentry *root; + + err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); + if (unlikely(err)) { + pr_err("lookup failed %s (%d)\n", args[0].from, err); + goto out; + } + + xino_itrunc->bindex = -1; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + if (au_h_dptr(root, bindex) == path.dentry) { + xino_itrunc->bindex = bindex; + break; + } + } + aufs_read_unlock(root, !AuLock_IR); + path_put(&path); + + if (unlikely(xino_itrunc->bindex < 0)) { + pr_err("no such branch %s\n", args[0].from); + err = -EINVAL; + } + +out: + return err; +} + +/* called without aufs lock */ +int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) +{ + int err, n, token; + aufs_bindex_t bindex; + unsigned char skipped; + struct dentry *root; + struct au_opt *opt, *opt_tail; + char *opt_str; + /* reduce the stack space */ + union { + struct au_opt_xino_itrunc *xino_itrunc; + struct au_opt_wbr_create *create; + } u; + struct { + substring_t args[MAX_OPT_ARGS]; + } *a; + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + root = sb->s_root; + err = 0; + bindex = 0; + opt = opts->opt; + opt_tail = opt + opts->max_opt - 1; + opt->type = Opt_tail; + while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { + err = -EINVAL; + skipped = 0; + token = match_token(opt_str, options, a->args); + switch (token) { + case Opt_br: + err = 0; + while (!err && (opt_str = strsep(&a->args[0].from, ":")) + && *opt_str) { + err = opt_add(opt, opt_str, opts->sb_flags, + bindex++); + if (unlikely(!err && ++opt > opt_tail)) { + err = -E2BIG; + break; + } + opt->type = Opt_tail; + skipped = 1; + } + break; + case Opt_add: + if (unlikely(match_int(&a->args[0], &n))) { + pr_err("bad integer in %s\n", opt_str); + break; + } + bindex = n; + err = opt_add(opt, a->args[1].from, opts->sb_flags, + bindex); + if (!err) + opt->type = token; + break; + case Opt_append: + err = opt_add(opt, a->args[0].from, opts->sb_flags, + /*dummy bindex*/1); + if (!err) + opt->type = token; + break; + case Opt_prepend: + err = opt_add(opt, a->args[0].from, opts->sb_flags, + /*bindex*/0); + if (!err) + opt->type = token; + break; + case Opt_del: + err = au_opts_parse_del(&opt->del, a->args); + if (!err) + opt->type = token; + break; +#if 0 /* reserved for future use */ + case Opt_idel: + del->pathname = "(indexed)"; + if (unlikely(match_int(&args[0], &n))) { + pr_err("bad integer in %s\n", opt_str); + break; + } + err = au_opts_parse_idel(sb, n, &opt->del, a->args); + if (!err) + opt->type = token; + break; +#endif + case Opt_mod: + err = au_opts_parse_mod(&opt->mod, a->args); + if (!err) + opt->type = token; + break; +#ifdef IMOD /* reserved for future use */ + case Opt_imod: + u.mod->path = "(indexed)"; + if (unlikely(match_int(&a->args[0], &n))) { + pr_err("bad integer in %s\n", opt_str); + break; + } + err = au_opts_parse_imod(sb, n, &opt->mod, a->args); + if (!err) + opt->type = token; + break; +#endif + case Opt_xino: + err = au_opts_parse_xino(sb, &opt->xino, a->args); + if (!err) + opt->type = token; + break; + + case Opt_trunc_xino_path: + err = au_opts_parse_xino_itrunc_path + (sb, &opt->xino_itrunc, a->args); + if (!err) + opt->type = token; + break; + + case Opt_itrunc_xino: + u.xino_itrunc = &opt->xino_itrunc; + if (unlikely(match_int(&a->args[0], &n))) { + pr_err("bad integer in %s\n", opt_str); + break; + } + u.xino_itrunc->bindex = n; + aufs_read_lock(root, AuLock_FLUSH); + if (n < 0 || au_sbbot(sb) < n) { + pr_err("out of bounds, %d\n", n); + aufs_read_unlock(root, !AuLock_IR); + break; + } + aufs_read_unlock(root, !AuLock_IR); + err = 0; + opt->type = token; + break; + + case Opt_dirwh: + if (unlikely(match_int(&a->args[0], &opt->dirwh))) + break; + err = 0; + opt->type = token; + break; + + case Opt_rdcache: + if (unlikely(match_int(&a->args[0], &n))) { + pr_err("bad integer in %s\n", opt_str); + break; + } + if (unlikely(n > AUFS_RDCACHE_MAX)) { + pr_err("rdcache must be smaller than %d\n", + AUFS_RDCACHE_MAX); + break; + } + opt->rdcache = n; + err = 0; + opt->type = token; + break; + case Opt_rdblk: + if (unlikely(match_int(&a->args[0], &n) + || n < 0 + || n > KMALLOC_MAX_SIZE)) { + pr_err("bad integer in %s\n", opt_str); + break; + } + if (unlikely(n && n < NAME_MAX)) { + pr_err("rdblk must be larger than %d\n", + NAME_MAX); + break; + } + opt->rdblk = n; + err = 0; + opt->type = token; + break; + case Opt_rdhash: + if (unlikely(match_int(&a->args[0], &n) + || n < 0 + || n * sizeof(struct hlist_head) + > KMALLOC_MAX_SIZE)) { + pr_err("bad integer in %s\n", opt_str); + break; + } + opt->rdhash = n; + err = 0; + opt->type = token; + break; + + case Opt_trunc_xino: + case Opt_notrunc_xino: + case Opt_noxino: + case Opt_trunc_xib: + case Opt_notrunc_xib: + case Opt_shwh: + case Opt_noshwh: + case Opt_dirperm1: + case Opt_nodirperm1: + case Opt_plink: + case Opt_noplink: + case Opt_list_plink: + case Opt_dio: + case Opt_nodio: + case Opt_diropq_a: + case Opt_diropq_w: + case Opt_warn_perm: + case Opt_nowarn_perm: + case Opt_verbose: + case Opt_noverbose: + case Opt_sum: + case Opt_nosum: + case Opt_wsum: + case Opt_rdblk_def: + case Opt_rdhash_def: + case Opt_dirren: + case Opt_nodirren: + case Opt_acl: + case Opt_noacl: + err = 0; + opt->type = token; + break; + + case Opt_udba: + opt->udba = udba_val(a->args[0].from); + if (opt->udba >= 0) { + err = 0; + opt->type = token; + } else + pr_err("wrong value, %s\n", opt_str); + break; + + case Opt_wbr_create: + u.create = &opt->wbr_create; + u.create->wbr_create + = au_wbr_create_val(a->args[0].from, u.create); + if (u.create->wbr_create >= 0) { + err = 0; + opt->type = token; + } else + pr_err("wrong value, %s\n", opt_str); + break; + case Opt_wbr_copyup: + opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); + if (opt->wbr_copyup >= 0) { + err = 0; + opt->type = token; + } else + pr_err("wrong value, %s\n", opt_str); + break; + + case Opt_fhsm_sec: + if (unlikely(match_int(&a->args[0], &n) + || n < 0)) { + pr_err("bad integer in %s\n", opt_str); + break; + } + if (sysaufs_brs) { + opt->fhsm_second = n; + opt->type = token; + } else + pr_warn("ignored %s\n", opt_str); + err = 0; + break; + + case Opt_ignore: + pr_warn("ignored %s\n", opt_str); + fallthrough; + case Opt_ignore_silent: + skipped = 1; + err = 0; + break; + case Opt_err: + pr_err("unknown option %s\n", opt_str); + break; + } + + if (!err && !skipped) { + if (unlikely(++opt > opt_tail)) { + err = -E2BIG; + opt--; + opt->type = Opt_tail; + break; + } + opt->type = Opt_tail; + } + } + + au_kfree_rcu(a); + dump_opts(opts); + if (unlikely(err)) + au_opts_free(opts); + +out: + return err; +} + +static int au_opt_wbr_create(struct super_block *sb, + struct au_opt_wbr_create *create) +{ + int err; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + err = 1; /* handled */ + sbinfo = au_sbi(sb); + if (sbinfo->si_wbr_create_ops->fin) { + err = sbinfo->si_wbr_create_ops->fin(sb); + if (!err) + err = 1; + } + + sbinfo->si_wbr_create = create->wbr_create; + sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; + switch (create->wbr_create) { + case AuWbrCreate_MFSRRV: + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRR: + case AuWbrCreate_PMFSRRV: + sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; + fallthrough; + case AuWbrCreate_MFS: + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFS: + case AuWbrCreate_PMFSV: + sbinfo->si_wbr_mfs.mfs_expire + = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); + break; + } + + if (sbinfo->si_wbr_create_ops->init) + sbinfo->si_wbr_create_ops->init(sb); /* ignore */ + + return err; +} + +/* + * returns, + * plus: processed without an error + * zero: unprocessed + */ +static int au_opt_simple(struct super_block *sb, struct au_opt *opt, + struct au_opts *opts) +{ + int err; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + err = 1; /* handled */ + sbinfo = au_sbi(sb); + switch (opt->type) { + case Opt_udba: + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= opt->udba; + opts->given_udba |= opt->udba; + break; + + case Opt_plink: + au_opt_set(sbinfo->si_mntflags, PLINK); + break; + case Opt_noplink: + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb, /*verbose*/1); + au_opt_clr(sbinfo->si_mntflags, PLINK); + break; + case Opt_list_plink: + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_list(sb); + break; + + case Opt_dio: + au_opt_set(sbinfo->si_mntflags, DIO); + au_fset_opts(opts->flags, REFRESH_DYAOP); + break; + case Opt_nodio: + au_opt_clr(sbinfo->si_mntflags, DIO); + au_fset_opts(opts->flags, REFRESH_DYAOP); + break; + + case Opt_fhsm_sec: + au_fhsm_set(sbinfo, opt->fhsm_second); + break; + + case Opt_diropq_a: + au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); + break; + case Opt_diropq_w: + au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); + break; + + case Opt_warn_perm: + au_opt_set(sbinfo->si_mntflags, WARN_PERM); + break; + case Opt_nowarn_perm: + au_opt_clr(sbinfo->si_mntflags, WARN_PERM); + break; + + case Opt_verbose: + au_opt_set(sbinfo->si_mntflags, VERBOSE); + break; + case Opt_noverbose: + au_opt_clr(sbinfo->si_mntflags, VERBOSE); + break; + + case Opt_sum: + au_opt_set(sbinfo->si_mntflags, SUM); + break; + case Opt_wsum: + au_opt_clr(sbinfo->si_mntflags, SUM); + au_opt_set(sbinfo->si_mntflags, SUM_W); + break; + case Opt_nosum: + au_opt_clr(sbinfo->si_mntflags, SUM); + au_opt_clr(sbinfo->si_mntflags, SUM_W); + break; + + case Opt_wbr_create: + err = au_opt_wbr_create(sb, &opt->wbr_create); + break; + case Opt_wbr_copyup: + sbinfo->si_wbr_copyup = opt->wbr_copyup; + sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; + break; + + case Opt_dirwh: + sbinfo->si_dirwh = opt->dirwh; + break; + + case Opt_rdcache: + sbinfo->si_rdcache + = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); + break; + case Opt_rdblk: + sbinfo->si_rdblk = opt->rdblk; + break; + case Opt_rdblk_def: + sbinfo->si_rdblk = AUFS_RDBLK_DEF; + break; + case Opt_rdhash: + sbinfo->si_rdhash = opt->rdhash; + break; + case Opt_rdhash_def: + sbinfo->si_rdhash = AUFS_RDHASH_DEF; + break; + + case Opt_shwh: + au_opt_set(sbinfo->si_mntflags, SHWH); + break; + case Opt_noshwh: + au_opt_clr(sbinfo->si_mntflags, SHWH); + break; + + case Opt_dirperm1: + au_opt_set(sbinfo->si_mntflags, DIRPERM1); + break; + case Opt_nodirperm1: + au_opt_clr(sbinfo->si_mntflags, DIRPERM1); + break; + + case Opt_trunc_xino: + au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); + break; + case Opt_notrunc_xino: + au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); + break; + + case Opt_trunc_xino_path: + case Opt_itrunc_xino: + err = au_xino_trunc(sb, opt->xino_itrunc.bindex, + /*idx_begin*/0); + if (!err) + err = 1; + break; + + case Opt_trunc_xib: + au_fset_opts(opts->flags, TRUNC_XIB); + break; + case Opt_notrunc_xib: + au_fclr_opts(opts->flags, TRUNC_XIB); + break; + + case Opt_dirren: + err = 1; + if (!au_opt_test(sbinfo->si_mntflags, DIRREN)) { + err = au_dr_opt_set(sb); + if (!err) + err = 1; + } + if (err == 1) + au_opt_set(sbinfo->si_mntflags, DIRREN); + break; + case Opt_nodirren: + err = 1; + if (au_opt_test(sbinfo->si_mntflags, DIRREN)) { + err = au_dr_opt_clr(sb, au_ftest_opts(opts->flags, + DR_FLUSHED)); + if (!err) + err = 1; + } + if (err == 1) + au_opt_clr(sbinfo->si_mntflags, DIRREN); + break; + + case Opt_acl: + sb->s_flags |= SB_POSIXACL; + break; + case Opt_noacl: + sb->s_flags &= ~SB_POSIXACL; + break; + + default: + err = 0; + break; + } + + return err; +} + +/* + * returns tri-state. + * plus: processed without an error + * zero: unprocessed + * minus: error + */ +static int au_opt_br(struct super_block *sb, struct au_opt *opt, + struct au_opts *opts) +{ + int err, do_refresh; + + err = 0; + switch (opt->type) { + case Opt_append: + opt->add.bindex = au_sbbot(sb) + 1; + if (opt->add.bindex < 0) + opt->add.bindex = 0; + goto add; + /* Always goto add, not fallthrough */ + case Opt_prepend: + opt->add.bindex = 0; + fallthrough; + add: /* indented label */ + case Opt_add: + err = au_br_add(sb, &opt->add, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) { + err = 1; + au_fset_opts(opts->flags, REFRESH); + } + break; + + case Opt_del: + case Opt_idel: + err = au_br_del(sb, &opt->del, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) { + err = 1; + au_fset_opts(opts->flags, TRUNC_XIB); + au_fset_opts(opts->flags, REFRESH); + } + break; + + case Opt_mod: + case Opt_imod: + err = au_br_mod(sb, &opt->mod, + au_ftest_opts(opts->flags, REMOUNT), + &do_refresh); + if (!err) { + err = 1; + if (do_refresh) + au_fset_opts(opts->flags, REFRESH); + } + break; + } + return err; +} + +static int au_opt_xino(struct super_block *sb, struct au_opt *opt, + struct au_opt_xino **opt_xino, + struct au_opts *opts) +{ + int err; + + err = 0; + switch (opt->type) { + case Opt_xino: + err = au_xino_set(sb, &opt->xino, + !!au_ftest_opts(opts->flags, REMOUNT)); + if (unlikely(err)) + break; + + *opt_xino = &opt->xino; + break; + + case Opt_noxino: + au_xino_clr(sb); + *opt_xino = (void *)-1; + break; + } + + return err; +} + +int au_opts_verify(struct super_block *sb, unsigned long sb_flags, + unsigned int pending) +{ + int err, fhsm; + aufs_bindex_t bindex, bbot; + unsigned char do_plink, skip, do_free, can_no_dreval; + struct au_branch *br; + struct au_wbr *wbr; + struct dentry *root, *dentry; + struct inode *dir, *h_dir; + struct au_sbinfo *sbinfo; + struct au_hinode *hdir; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); + + if (!(sb_flags & SB_RDONLY)) { + if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) + pr_warn("first branch should be rw\n"); + if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) + pr_warn_once("shwh should be used with ro\n"); + } + + if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) + && !au_opt_test(sbinfo->si_mntflags, XINO)) + pr_warn_once("udba=*notify requires xino\n"); + + if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) + pr_warn_once("dirperm1 breaks the protection" + " by the permission bits on the lower branch\n"); + + err = 0; + fhsm = 0; + root = sb->s_root; + dir = d_inode(root); + do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); + can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending), + UDBA_NONE); + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) { + skip = 0; + h_dir = au_h_iptr(dir, bindex); + br = au_sbr(sb, bindex); + + if ((br->br_perm & AuBrAttr_ICEX) + && !h_dir->i_op->listxattr) + br->br_perm &= ~AuBrAttr_ICEX; +#if 0 /* untested */ + if ((br->br_perm & AuBrAttr_ICEX_SEC) + && (au_br_sb(br)->s_flags & SB_NOSEC)) + br->br_perm &= ~AuBrAttr_ICEX_SEC; +#endif + + do_free = 0; + wbr = br->br_wbr; + if (wbr) + wbr_wh_read_lock(wbr); + + if (!au_br_writable(br->br_perm)) { + do_free = !!wbr; + skip = (!wbr + || (!wbr->wbr_whbase + && !wbr->wbr_plink + && !wbr->wbr_orph)); + } else if (!au_br_wh_linkable(br->br_perm)) { + /* skip = (!br->br_whbase && !br->br_orph); */ + skip = (!wbr || !wbr->wbr_whbase); + if (skip && wbr) { + if (do_plink) + skip = !!wbr->wbr_plink; + else + skip = !wbr->wbr_plink; + } + } else { + /* skip = (br->br_whbase && br->br_ohph); */ + skip = (wbr && wbr->wbr_whbase); + if (skip) { + if (do_plink) + skip = !!wbr->wbr_plink; + else + skip = !wbr->wbr_plink; + } + } + if (wbr) + wbr_wh_read_unlock(wbr); + + if (can_no_dreval) { + dentry = br->br_path.dentry; + spin_lock(&dentry->d_lock); + if (dentry->d_flags & + (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE)) + can_no_dreval = 0; + spin_unlock(&dentry->d_lock); + } + + if (au_br_fhsm(br->br_perm)) { + fhsm++; + AuDebugOn(!br->br_fhsm); + } + + if (skip) + continue; + + hdir = au_hi(dir, bindex); + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + if (wbr) + wbr_wh_write_lock(wbr); + err = au_wh_init(br, sb); + if (wbr) + wbr_wh_write_unlock(wbr); + au_hn_inode_unlock(hdir); + + if (!err && do_free) { + au_kfree_rcu(wbr); + br->br_wbr = NULL; + } + } + + if (can_no_dreval) + au_fset_si(sbinfo, NO_DREVAL); + else + au_fclr_si(sbinfo, NO_DREVAL); + + if (fhsm >= 2) { + au_fset_si(sbinfo, FHSM); + for (bindex = bbot; bindex >= 0; bindex--) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) { + au_fhsm_set_bottom(sb, bindex); + break; + } + } + } else { + au_fclr_si(sbinfo, FHSM); + au_fhsm_set_bottom(sb, -1); + } + + return err; +} + +int au_opts_mount(struct super_block *sb, struct au_opts *opts) +{ + int err; + unsigned int tmp; + aufs_bindex_t bindex, bbot; + struct au_opt *opt; + struct au_opt_xino *opt_xino, xino; + struct au_sbinfo *sbinfo; + struct au_branch *br; + struct inode *dir; + + SiMustWriteLock(sb); + + err = 0; + opt_xino = NULL; + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) + err = au_opt_simple(sb, opt++, opts); + if (err > 0) + err = 0; + else if (unlikely(err < 0)) + goto out; + + /* disable xino and udba temporary */ + sbinfo = au_sbi(sb); + tmp = sbinfo->si_mntflags; + au_opt_clr(sbinfo->si_mntflags, XINO); + au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); + + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) + err = au_opt_br(sb, opt++, opts); + if (err > 0) + err = 0; + else if (unlikely(err < 0)) + goto out; + + bbot = au_sbbot(sb); + if (unlikely(bbot < 0)) { + err = -EINVAL; + pr_err("no branches\n"); + goto out; + } + + if (au_opt_test(tmp, XINO)) + au_opt_set(sbinfo->si_mntflags, XINO); + opt = opts->opt; + while (!err && opt->type != Opt_tail) + err = au_opt_xino(sb, opt++, &opt_xino, opts); + if (unlikely(err)) + goto out; + + err = au_opts_verify(sb, sb->s_flags, tmp); + if (unlikely(err)) + goto out; + + /* restore xino */ + if (au_opt_test(tmp, XINO) && !opt_xino) { + xino.file = au_xino_def(sb); + err = PTR_ERR(xino.file); + if (IS_ERR(xino.file)) + goto out; + + err = au_xino_set(sb, &xino, /*remount*/0); + fput(xino.file); + if (unlikely(err)) + goto out; + } + + /* restore udba */ + tmp &= AuOptMask_UDBA; + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= tmp; + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(tmp, br, br->br_perm); + if (unlikely(err)) + AuIOErr("hnotify failed on br %d, %d, ignored\n", + bindex, err); + /* go on even if err */ + } + if (au_opt_test(tmp, UDBA_HNOTIFY)) { + dir = d_inode(sb->s_root); + au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); + } + +out: + return err; +} + +int au_opts_remount(struct super_block *sb, struct au_opts *opts) +{ + int err, rerr; + unsigned char no_dreval; + struct inode *dir; + struct au_opt_xino *opt_xino; + struct au_opt *opt; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + err = au_dr_opt_flush(sb); + if (unlikely(err)) + goto out; + au_fset_opts(opts->flags, DR_FLUSHED); + + dir = d_inode(sb->s_root); + sbinfo = au_sbi(sb); + opt_xino = NULL; + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) { + err = au_opt_simple(sb, opt, opts); + if (!err) + err = au_opt_br(sb, opt, opts); + if (!err) + err = au_opt_xino(sb, opt, &opt_xino, opts); + opt++; + } + if (err > 0) + err = 0; + AuTraceErr(err); + /* go on even err */ + + no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL); + rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); + if (unlikely(rerr && !err)) + err = rerr; + + if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL)) + au_fset_opts(opts->flags, REFRESH_IDOP); + + if (au_ftest_opts(opts->flags, TRUNC_XIB)) { + rerr = au_xib_trunc(sb); + if (unlikely(rerr && !err)) + err = rerr; + } + + /* will be handled by the caller */ + if (!au_ftest_opts(opts->flags, REFRESH) + && (opts->given_udba + || au_opt_test(sbinfo->si_mntflags, XINO) + || au_ftest_opts(opts->flags, REFRESH_IDOP) + )) + au_fset_opts(opts->flags, REFRESH); + + AuDbg("status 0x%x\n", opts->flags); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +unsigned int au_opt_udba(struct super_block *sb) +{ + return au_mntflags(sb) & AuOptMask_UDBA; +} diff -Naur --no-dereference a/fs/aufs/opts.h b/fs/aufs/opts.h --- a/fs/aufs/opts.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/opts.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * mount options/flags + */ + +#ifndef __AUFS_OPTS_H__ +#define __AUFS_OPTS_H__ + +#ifdef __KERNEL__ + +#include + +struct file; + +/* ---------------------------------------------------------------------- */ + +/* mount flags */ +#define AuOpt_XINO 1 /* external inode number bitmap + and translation table */ +#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ +#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ +#define AuOpt_UDBA_REVAL (1 << 3) +#define AuOpt_UDBA_HNOTIFY (1 << 4) +#define AuOpt_SHWH (1 << 5) /* show whiteout */ +#define AuOpt_PLINK (1 << 6) /* pseudo-link */ +#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm + bits */ +#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ +#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ +#define AuOpt_SUM_W (1 << 11) /* unimplemented */ +#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ +#define AuOpt_VERBOSE (1 << 13) /* print the cause of error */ +#define AuOpt_DIO (1 << 14) /* direct io */ +#define AuOpt_DIRREN (1 << 15) /* directory rename */ + +#ifndef CONFIG_AUFS_HNOTIFY +#undef AuOpt_UDBA_HNOTIFY +#define AuOpt_UDBA_HNOTIFY 0 +#endif +#ifndef CONFIG_AUFS_DIRREN +#undef AuOpt_DIRREN +#define AuOpt_DIRREN 0 +#endif +#ifndef CONFIG_AUFS_SHWH +#undef AuOpt_SHWH +#define AuOpt_SHWH 0 +#endif + +#define AuOpt_Def (AuOpt_XINO \ + | AuOpt_UDBA_REVAL \ + | AuOpt_PLINK \ + /* | AuOpt_DIRPERM1 */ \ + | AuOpt_WARN_PERM) +#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ + | AuOpt_UDBA_REVAL \ + | AuOpt_UDBA_HNOTIFY) + +#define au_opt_test(flags, name) (flags & AuOpt_##name) +#define au_opt_set(flags, name) do { \ + BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ + ((flags) |= AuOpt_##name); \ +} while (0) +#define au_opt_set_udba(flags, name) do { \ + (flags) &= ~AuOptMask_UDBA; \ + ((flags) |= AuOpt_##name); \ +} while (0) +#define au_opt_clr(flags, name) do { \ + ((flags) &= ~AuOpt_##name); \ +} while (0) + +static inline unsigned int au_opts_plink(unsigned int mntflags) +{ +#ifdef CONFIG_PROC_FS + return mntflags; +#else + return mntflags & ~AuOpt_PLINK; +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* policies to select one among multiple writable branches */ +enum { + AuWbrCreate_TDP, /* top down parent */ + AuWbrCreate_RR, /* round robin */ + AuWbrCreate_MFS, /* most free space */ + AuWbrCreate_MFSV, /* mfs with seconds */ + AuWbrCreate_MFSRR, /* mfs then rr */ + AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ + AuWbrCreate_TDMFS, /* top down regardless parent and mfs */ + AuWbrCreate_TDMFSV, /* top down regardless parent and mfs */ + AuWbrCreate_PMFS, /* parent and mfs */ + AuWbrCreate_PMFSV, /* parent and mfs with seconds */ + AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ + AuWbrCreate_PMFSRRV, /* plus seconds */ + + AuWbrCreate_Def = AuWbrCreate_TDP +}; + +enum { + AuWbrCopyup_TDP, /* top down parent */ + AuWbrCopyup_BUP, /* bottom up parent */ + AuWbrCopyup_BU, /* bottom up */ + + AuWbrCopyup_Def = AuWbrCopyup_TDP +}; + +/* ---------------------------------------------------------------------- */ + +struct au_opt_add { + aufs_bindex_t bindex; + char *pathname; + int perm; + struct path path; +}; + +struct au_opt_del { + char *pathname; + struct path h_path; +}; + +struct au_opt_mod { + char *path; + int perm; + struct dentry *h_root; +}; + +struct au_opt_xino { + char *path; + struct file *file; +}; + +struct au_opt_xino_itrunc { + aufs_bindex_t bindex; +}; + +struct au_opt_wbr_create { + int wbr_create; + int mfs_second; + unsigned long long mfsrr_watermark; +}; + +struct au_opt { + int type; + union { + struct au_opt_xino xino; + struct au_opt_xino_itrunc xino_itrunc; + struct au_opt_add add; + struct au_opt_del del; + struct au_opt_mod mod; + int dirwh; + int rdcache; + unsigned int rdblk; + unsigned int rdhash; + int udba; + struct au_opt_wbr_create wbr_create; + int wbr_copyup; + unsigned int fhsm_second; + }; +}; + +/* opts flags */ +#define AuOpts_REMOUNT 1 +#define AuOpts_REFRESH (1 << 1) +#define AuOpts_TRUNC_XIB (1 << 2) +#define AuOpts_REFRESH_DYAOP (1 << 3) +#define AuOpts_REFRESH_IDOP (1 << 4) +#define AuOpts_DR_FLUSHED (1 << 5) +#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) +#define au_fset_opts(flags, name) \ + do { (flags) |= AuOpts_##name; } while (0) +#define au_fclr_opts(flags, name) \ + do { (flags) &= ~AuOpts_##name; } while (0) + +#ifndef CONFIG_AUFS_DIRREN +#undef AuOpts_DR_FLUSHED +#define AuOpts_DR_FLUSHED 0 +#endif + +struct au_opts { + struct au_opt *opt; + int max_opt; + + unsigned int given_udba; + unsigned int flags; + unsigned long sb_flags; +}; + +/* ---------------------------------------------------------------------- */ + +/* opts.c */ +void au_optstr_br_perm(au_br_perm_str_t *str, int perm); +const char *au_optstr_udba(int udba); +const char *au_optstr_wbr_copyup(int wbr_copyup); +const char *au_optstr_wbr_create(int wbr_create); + +void au_opts_free(struct au_opts *opts); +struct super_block; +int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); +int au_opts_verify(struct super_block *sb, unsigned long sb_flags, + unsigned int pending); +int au_opts_mount(struct super_block *sb, struct au_opts *opts); +int au_opts_remount(struct super_block *sb, struct au_opts *opts); + +unsigned int au_opt_udba(struct super_block *sb); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_OPTS_H__ */ diff -Naur --no-dereference a/fs/aufs/plink.c b/fs/aufs/plink.c --- a/fs/aufs/plink.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/plink.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * pseudo-link + */ + +#include "aufs.h" + +/* + * the pseudo-link maintenance mode. + * during a user process maintains the pseudo-links, + * prohibit adding a new plink and branch manipulation. + * + * Flags + * NOPLM: + * For entry functions which will handle plink, and i_mutex is already held + * in VFS. + * They cannot wait and should return an error at once. + * Callers has to check the error. + * NOPLMW: + * For entry functions which will handle plink, but i_mutex is not held + * in VFS. + * They can wait the plink maintenance mode to finish. + * + * They behave like F_SETLK and F_SETLKW. + * If the caller never handle plink, then both flags are unnecessary. + */ + +int au_plink_maint(struct super_block *sb, int flags) +{ + int err; + pid_t pid, ppid; + struct task_struct *parent, *prev; + struct au_sbinfo *sbi; + + SiMustAnyLock(sb); + + err = 0; + if (!au_opt_test(au_mntflags(sb), PLINK)) + goto out; + + sbi = au_sbi(sb); + pid = sbi->si_plink_maint_pid; + if (!pid || pid == current->pid) + goto out; + + /* todo: it highly depends upon /sbin/mount.aufs */ + prev = NULL; + parent = current; + ppid = 0; + rcu_read_lock(); + while (1) { + parent = rcu_dereference(parent->real_parent); + if (parent == prev) + break; + ppid = task_pid_vnr(parent); + if (pid == ppid) { + rcu_read_unlock(); + goto out; + } + prev = parent; + } + rcu_read_unlock(); + + if (au_ftest_lock(flags, NOPLMW)) { + /* if there is no i_mutex lock in VFS, we don't need to wait */ + /* AuDebugOn(!lockdep_depth(current)); */ + while (sbi->si_plink_maint_pid) { + si_read_unlock(sb); + /* gave up wake_up_bit() */ + wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); + + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&sbi->si_nowait); + si_noflush_read_lock(sb); + } + } else if (au_ftest_lock(flags, NOPLM)) { + AuDbg("ppid %d, pid %d\n", ppid, pid); + err = -EAGAIN; + } + +out: + return err; +} + +void au_plink_maint_leave(struct au_sbinfo *sbinfo) +{ + spin_lock(&sbinfo->si_plink_maint_lock); + sbinfo->si_plink_maint_pid = 0; + spin_unlock(&sbinfo->si_plink_maint_lock); + wake_up_all(&sbinfo->si_plink_wq); +} + +int au_plink_maint_enter(struct super_block *sb) +{ + int err; + struct au_sbinfo *sbinfo; + + err = 0; + sbinfo = au_sbi(sb); + /* make sure i am the only one in this fs */ + si_write_lock(sb, AuLock_FLUSH); + if (au_opt_test(au_mntflags(sb), PLINK)) { + spin_lock(&sbinfo->si_plink_maint_lock); + if (!sbinfo->si_plink_maint_pid) + sbinfo->si_plink_maint_pid = current->pid; + else + err = -EBUSY; + spin_unlock(&sbinfo->si_plink_maint_lock); + } + si_write_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb) +{ + int i; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + for (i = 0; i < AuPlink_NHASH; i++) { + hbl = sbinfo->si_plink + i; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(icntnr, pos, hbl, plink) + AuDbg("%lu\n", icntnr->vfs_inode.i_ino); + hlist_bl_unlock(hbl); + } +} +#endif + +/* is the inode pseudo-linked? */ +int au_plink_test(struct inode *inode) +{ + int found, i; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + + sbinfo = au_sbi(inode->i_sb); + AuRwMustAnyLock(&sbinfo->si_rwsem); + AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); + AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); + + found = 0; + i = au_plink_hash(inode->i_ino); + hbl = sbinfo->si_plink + i; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(icntnr, pos, hbl, plink) + if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } + hlist_bl_unlock(hbl); + return found; +} + +/* ---------------------------------------------------------------------- */ + +/* + * generate a name for plink. + * the file will be stored under AUFS_WH_PLINKDIR. + */ +/* 20 is max digits length of ulong 64 */ +#define PLINK_NAME_LEN ((20 + 1) * 2) + +static int plink_name(char *name, int len, struct inode *inode, + aufs_bindex_t bindex) +{ + int rlen; + struct inode *h_inode; + + h_inode = au_h_iptr(inode, bindex); + rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); + return rlen; +} + +struct au_do_plink_lkup_args { + struct dentry **errp; + struct qstr *tgtname; + struct path *h_ppath; +}; + +static struct dentry *au_do_plink_lkup(struct qstr *tgtname, + struct path *h_ppath) +{ + struct dentry *h_dentry; + struct inode *h_inode; + + h_inode = d_inode(h_ppath->dentry); + inode_lock_shared_nested(h_inode, AuLsc_I_CHILD2); + h_dentry = vfsub_lkup_one(tgtname, h_ppath); + inode_unlock_shared(h_inode); + + return h_dentry; +} + +static void au_call_do_plink_lkup(void *args) +{ + struct au_do_plink_lkup_args *a = args; + *a->errp = au_do_plink_lkup(a->tgtname, a->h_ppath); +} + +/* lookup the plink-ed @inode under the branch at @bindex */ +struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) +{ + struct dentry *h_dentry; + struct au_branch *br; + struct path h_ppath; + int wkq_err; + char a[PLINK_NAME_LEN]; + struct qstr tgtname = QSTR_INIT(a, 0); + + AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); + + br = au_sbr(inode->i_sb, bindex); + h_ppath.dentry = br->br_wbr->wbr_plink; + h_ppath.mnt = au_br_mnt(br); + tgtname.len = plink_name(a, sizeof(a), inode, bindex); + + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { + struct au_do_plink_lkup_args args = { + .errp = &h_dentry, + .tgtname = &tgtname, + .h_ppath = &h_ppath + }; + + wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); + if (unlikely(wkq_err)) + h_dentry = ERR_PTR(wkq_err); + } else + h_dentry = au_do_plink_lkup(&tgtname, &h_ppath); + + return h_dentry; +} + +/* create a pseudo-link */ +static int do_whplink(struct qstr *tgt, struct path *h_ppath, + struct dentry *h_dentry) +{ + int err; + struct path h_path; + struct inode *h_dir, *delegated; + + h_dir = d_inode(h_ppath->dentry); + inode_lock_nested(h_dir, AuLsc_I_CHILD2); + h_path.mnt = h_ppath->mnt; +again: + h_path.dentry = vfsub_lkup_one(tgt, h_ppath); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) + goto out; + + err = 0; + /* wh.plink dir is not monitored */ + /* todo: is it really safe? */ + if (d_is_positive(h_path.dentry) + && d_inode(h_path.dentry) != d_inode(h_dentry)) { + delegated = NULL; + err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + dput(h_path.dentry); + h_path.dentry = NULL; + if (!err) + goto again; + } + if (!err && d_is_negative(h_path.dentry)) { + delegated = NULL; + err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + } + dput(h_path.dentry); + +out: + inode_unlock(h_dir); + return err; +} + +struct do_whplink_args { + int *errp; + struct qstr *tgt; + struct path *h_ppath; + struct dentry *h_dentry; +}; + +static void call_do_whplink(void *args) +{ + struct do_whplink_args *a = args; + *a->errp = do_whplink(a->tgt, a->h_ppath, a->h_dentry); +} + +static int whplink(struct dentry *h_dentry, struct inode *inode, + aufs_bindex_t bindex) +{ + int err, wkq_err; + struct au_branch *br; + struct au_wbr *wbr; + struct path h_ppath; + char a[PLINK_NAME_LEN]; + struct qstr tgtname = QSTR_INIT(a, 0); + + br = au_sbr(inode->i_sb, bindex); + wbr = br->br_wbr; + h_ppath.dentry = wbr->wbr_plink; + h_ppath.mnt = au_br_mnt(br); + tgtname.len = plink_name(a, sizeof(a), inode, bindex); + + /* always superio. */ + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { + struct do_whplink_args args = { + .errp = &err, + .tgt = &tgtname, + .h_ppath = &h_ppath, + .h_dentry = h_dentry + }; + wkq_err = au_wkq_wait(call_do_whplink, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } else + err = do_whplink(&tgtname, &h_ppath, h_dentry); + + return err; +} + +/* + * create a new pseudo-link for @h_dentry on @bindex. + * the linked inode is held in aufs @inode. + */ +void au_plink_append(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_dentry) +{ + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + int found, err, cnt, i; + + sb = inode->i_sb; + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + found = au_plink_test(inode); + if (found) + return; + + i = au_plink_hash(inode->i_ino); + hbl = sbinfo->si_plink + i; + au_igrab(inode); + + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(icntnr, pos, hbl, plink) { + if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } + } + if (!found) { + icntnr = container_of(inode, struct au_icntnr, vfs_inode); + hlist_bl_add_head(&icntnr->plink, hbl); + } + hlist_bl_unlock(hbl); + if (!found) { + cnt = au_hbl_count(hbl); +#define msg "unexpectedly unbalanced or too many pseudo-links" + if (cnt > AUFS_PLINK_WARN) + AuWarn1(msg ", %d\n", cnt); +#undef msg + err = whplink(h_dentry, inode, bindex); + if (unlikely(err)) { + pr_warn("err %d, damaged pseudo link.\n", err); + au_hbl_del(&icntnr->plink, hbl); + iput(&icntnr->vfs_inode); + } + } else + iput(&icntnr->vfs_inode); +} + +/* free all plinks */ +void au_plink_put(struct super_block *sb, int verbose) +{ + int i, warned; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *tmp; + struct au_icntnr *icntnr; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + /* no spin_lock since sbinfo is write-locked */ + warned = 0; + for (i = 0; i < AuPlink_NHASH; i++) { + hbl = sbinfo->si_plink + i; + if (!warned && verbose && !hlist_bl_empty(hbl)) { + pr_warn("pseudo-link is not flushed"); + warned = 1; + } + hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) + iput(&icntnr->vfs_inode); + INIT_HLIST_BL_HEAD(hbl); + } +} + +void au_plink_clean(struct super_block *sb, int verbose) +{ + struct dentry *root; + + root = sb->s_root; + aufs_write_lock(root); + if (au_opt_test(au_mntflags(sb), PLINK)) + au_plink_put(sb, verbose); + aufs_write_unlock(root); +} + +static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) +{ + int do_put; + aufs_bindex_t btop, bbot, bindex; + + do_put = 0; + btop = au_ibtop(inode); + bbot = au_ibbot(inode); + if (btop >= 0) { + for (bindex = btop; bindex <= bbot; bindex++) { + if (!au_h_iptr(inode, bindex) + || au_ii_br_id(inode, bindex) != br_id) + continue; + au_set_h_iptr(inode, bindex, NULL, 0); + do_put = 1; + break; + } + if (do_put) + for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex)) { + do_put = 0; + break; + } + } else + do_put = 1; + + return do_put; +} + +/* free the plinks on a branch specified by @br_id */ +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) +{ + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *tmp; + struct au_icntnr *icntnr; + struct inode *inode; + int i, do_put; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + /* no bit_lock since sbinfo is write-locked */ + for (i = 0; i < AuPlink_NHASH; i++) { + hbl = sbinfo->si_plink + i; + hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) { + inode = au_igrab(&icntnr->vfs_inode); + ii_write_lock_child(inode); + do_put = au_plink_do_half_refresh(inode, br_id); + if (do_put) { + hlist_bl_del(&icntnr->plink); + iput(inode); + } + ii_write_unlock(inode); + iput(inode); + } + } +} diff -Naur --no-dereference a/fs/aufs/poll.c b/fs/aufs/poll.c --- a/fs/aufs/poll.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/poll.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * poll operation + * There is only one filesystem which implements ->poll operation, currently. + */ + +#include "aufs.h" + +__poll_t aufs_poll(struct file *file, struct poll_table_struct *pt) +{ + __poll_t mask; + struct file *h_file; + struct super_block *sb; + + /* We should pretend an error happened. */ + mask = EPOLLERR /* | EPOLLIN | EPOLLOUT */; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + if (IS_ERR(h_file)) { + AuDbg("h_file %ld\n", PTR_ERR(h_file)); + goto out; + } + + mask = vfs_poll(h_file, pt); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); + if (mask & EPOLLERR) + AuDbg("mask 0x%x\n", mask); + return mask; +} diff -Naur --no-dereference a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c --- a/fs/aufs/posix_acl.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/posix_acl.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * posix acl operations + */ + +#include +#include "aufs.h" + +struct posix_acl *aufs_get_acl(struct inode *inode, int type) +{ + struct posix_acl *acl; + int err; + aufs_bindex_t bindex; + struct inode *h_inode; + struct super_block *sb; + + acl = NULL; + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); + if (!(sb->s_flags & SB_POSIXACL)) + goto out; + + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (unlikely(!h_inode + || ((h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)))) { + err = au_busy_or_stale(); + acl = ERR_PTR(err); + goto out; + } + + /* always topmost only */ + acl = get_acl(h_inode, type); + if (IS_ERR(acl)) + forget_cached_acl(inode, type); + else + set_cached_acl(inode, type, acl); + +out: + ii_read_unlock(inode); + si_read_unlock(sb); + + AuTraceErrPtr(acl); + return acl; +} + +int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) +{ + int err; + ssize_t ssz; + struct dentry *dentry; + struct au_sxattr arg = { + .type = AU_ACL_SET, + .u.acl_set = { + .acl = acl, + .type = type + }, + }; + + IMustLock(inode); + + if (inode->i_ino == AUFS_ROOT_INO) + dentry = dget(inode->i_sb->s_root); + else { + dentry = d_find_alias(inode); + if (!dentry) + dentry = d_find_any_alias(inode); + if (!dentry) { + pr_warn("cannot handle this inode, " + "please report to aufs-users ML\n"); + err = -ENOENT; + goto out; + } + } + + ssz = au_sxattr(dentry, inode, &arg); + /* forget even it if succeeds since the branch might set differently */ + forget_cached_acl(inode, type); + dput(dentry); + err = ssz; + if (ssz >= 0) + err = 0; + +out: + return err; +} diff -Naur --no-dereference a/fs/aufs/procfs.c b/fs/aufs/procfs.c --- a/fs/aufs/procfs.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/procfs.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * procfs interfaces + */ + +#include +#include "aufs.h" + +static int au_procfs_plm_release(struct inode *inode, struct file *file) +{ + struct au_sbinfo *sbinfo; + + sbinfo = file->private_data; + if (sbinfo) { + au_plink_maint_leave(sbinfo); + kobject_put(&sbinfo->si_kobj); + } + + return 0; +} + +static void au_procfs_plm_write_clean(struct file *file) +{ + struct au_sbinfo *sbinfo; + + sbinfo = file->private_data; + if (sbinfo) + au_plink_clean(sbinfo->si_sb, /*verbose*/0); +} + +static int au_procfs_plm_write_si(struct file *file, unsigned long id) +{ + int err; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct hlist_bl_node *pos; + + err = -EBUSY; + if (unlikely(file->private_data)) + goto out; + + sb = NULL; + /* don't use au_sbilist_lock() here */ + hlist_bl_lock(&au_sbilist); + hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list) + if (id == sysaufs_si_id(sbinfo)) { + if (kobject_get_unless_zero(&sbinfo->si_kobj)) + sb = sbinfo->si_sb; + break; + } + hlist_bl_unlock(&au_sbilist); + + err = -EINVAL; + if (unlikely(!sb)) + goto out; + + err = au_plink_maint_enter(sb); + if (!err) + /* keep kobject_get() */ + file->private_data = sbinfo; + else + kobject_put(&sbinfo->si_kobj); +out: + return err; +} + +/* + * Accept a valid "si=xxxx" only. + * Once it is accepted successfully, accept "clean" too. + */ +static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + ssize_t err; + unsigned long id; + /* last newline is allowed */ + char buf[3 + sizeof(unsigned long) * 2 + 1]; + + err = -EACCES; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = -EINVAL; + if (unlikely(count > sizeof(buf))) + goto out; + + err = copy_from_user(buf, ubuf, count); + if (unlikely(err)) { + err = -EFAULT; + goto out; + } + buf[count] = 0; + + err = -EINVAL; + if (!strcmp("clean", buf)) { + au_procfs_plm_write_clean(file); + goto out_success; + } else if (unlikely(strncmp("si=", buf, 3))) + goto out; + + err = kstrtoul(buf + 3, 16, &id); + if (unlikely(err)) + goto out; + + err = au_procfs_plm_write_si(file, id); + if (unlikely(err)) + goto out; + +out_success: + err = count; /* success */ +out: + return err; +} + +static const struct proc_ops au_procfs_plm_op = { + .proc_write = au_procfs_plm_write, + .proc_release = au_procfs_plm_release +}; + +/* ---------------------------------------------------------------------- */ + +static struct proc_dir_entry *au_procfs_dir; + +void au_procfs_fin(void) +{ + remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); + remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); +} + +int __init au_procfs_init(void) +{ + int err; + struct proc_dir_entry *entry; + + err = -ENOMEM; + au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); + if (unlikely(!au_procfs_dir)) + goto out; + + entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | 0200, + au_procfs_dir, &au_procfs_plm_op); + if (unlikely(!entry)) + goto out_dir; + + err = 0; + goto out; /* success */ + + +out_dir: + remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); +out: + return err; +} diff -Naur --no-dereference a/fs/aufs/rdu.c b/fs/aufs/rdu.c --- a/fs/aufs/rdu.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/rdu.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * readdir in userspace. + */ + +#include +#include +#include +#include "aufs.h" + +/* bits for struct aufs_rdu.flags */ +#define AuRdu_CALLED 1 +#define AuRdu_CONT (1 << 1) +#define AuRdu_FULL (1 << 2) +#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) +#define au_fset_rdu(flags, name) \ + do { (flags) |= AuRdu_##name; } while (0) +#define au_fclr_rdu(flags, name) \ + do { (flags) &= ~AuRdu_##name; } while (0) + +struct au_rdu_arg { + struct dir_context ctx; + struct aufs_rdu *rdu; + union au_rdu_ent_ul ent; + unsigned long end; + + struct super_block *sb; + int err; +}; + +static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, + loff_t offset, u64 h_ino, unsigned int d_type) +{ + int err, len; + struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); + struct aufs_rdu *rdu = arg->rdu; + struct au_rdu_ent ent; + + err = 0; + arg->err = 0; + au_fset_rdu(rdu->cookie.flags, CALLED); + len = au_rdu_len(nlen); + if (arg->ent.ul + len < arg->end) { + ent.ino = h_ino; + ent.bindex = rdu->cookie.bindex; + ent.type = d_type; + ent.nlen = nlen; + if (unlikely(nlen > AUFS_MAX_NAMELEN)) + ent.type = DT_UNKNOWN; + + /* unnecessary to support mmap_sem since this is a dir */ + err = -EFAULT; + if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) + goto out; + if (copy_to_user(arg->ent.e->name, name, nlen)) + goto out; + /* the terminating NULL */ + if (__put_user(0, arg->ent.e->name + nlen)) + goto out; + err = 0; + /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ + arg->ent.ul += len; + rdu->rent++; + } else { + err = -EFAULT; + au_fset_rdu(rdu->cookie.flags, FULL); + rdu->full = 1; + rdu->tail = arg->ent; + } + +out: + /* AuTraceErr(err); */ + return err; +} + +static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) +{ + int err; + loff_t offset; + struct au_rdu_cookie *cookie = &arg->rdu->cookie; + + /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ + offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); + err = offset; + if (unlikely(offset != cookie->h_pos)) + goto out; + + err = 0; + do { + arg->err = 0; + au_fclr_rdu(cookie->flags, CALLED); + /* smp_mb(); */ + err = vfsub_iterate_dir(h_file, &arg->ctx); + if (err >= 0) + err = arg->err; + } while (!err + && au_ftest_rdu(cookie->flags, CALLED) + && !au_ftest_rdu(cookie->flags, FULL)); + cookie->h_pos = h_file->f_pos; + +out: + AuTraceErr(err); + return err; +} + +static int au_rdu(struct file *file, struct aufs_rdu *rdu) +{ + int err; + aufs_bindex_t bbot; + struct au_rdu_arg arg = { + .ctx = { + .actor = au_rdu_fill + } + }; + struct dentry *dentry; + struct inode *inode; + struct file *h_file; + struct au_rdu_cookie *cookie = &rdu->cookie; + + /* VERIFY_WRITE */ + err = !access_ok(rdu->ent.e, rdu->sz); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + rdu->rent = 0; + rdu->tail = rdu->ent; + rdu->full = 0; + arg.rdu = rdu; + arg.ent = rdu->ent; + arg.end = arg.ent.ul; + arg.end += rdu->sz; + + err = -ENOTDIR; + if (unlikely(!file->f_op->iterate && !file->f_op->iterate_shared)) + goto out; + + err = security_file_permission(file, MAY_READ); + AuTraceErr(err); + if (unlikely(err)) + goto out; + + dentry = file->f_path.dentry; + inode = d_inode(dentry); + inode_lock_shared(inode); + + arg.sb = inode->i_sb; + err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_mtx; + err = au_alive_dir(dentry); + if (unlikely(err)) + goto out_si; + /* todo: reval? */ + fi_read_lock(file); + + err = -EAGAIN; + if (unlikely(au_ftest_rdu(cookie->flags, CONT) + && cookie->generation != au_figen(file))) + goto out_unlock; + + err = 0; + if (!rdu->blk) { + rdu->blk = au_sbi(arg.sb)->si_rdblk; + if (!rdu->blk) + rdu->blk = au_dir_size(file, /*dentry*/NULL); + } + bbot = au_fbtop(file); + if (cookie->bindex < bbot) + cookie->bindex = bbot; + bbot = au_fbbot_dir(file); + /* AuDbg("b%d, b%d\n", cookie->bindex, bbot); */ + for (; !err && cookie->bindex <= bbot; + cookie->bindex++, cookie->h_pos = 0) { + h_file = au_hf_dir(file, cookie->bindex); + if (!h_file) + continue; + + au_fclr_rdu(cookie->flags, FULL); + err = au_rdu_do(h_file, &arg); + AuTraceErr(err); + if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) + break; + } + AuDbg("rent %llu\n", rdu->rent); + + if (!err && !au_ftest_rdu(cookie->flags, CONT)) { + rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); + au_fset_rdu(cookie->flags, CONT); + cookie->generation = au_figen(file); + } + + ii_read_lock_child(inode); + fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibtop(inode))); + ii_read_unlock(inode); + +out_unlock: + fi_read_unlock(file); +out_si: + si_read_unlock(arg.sb); +out_mtx: + inode_unlock_shared(inode); +out: + AuTraceErr(err); + return err; +} + +static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) +{ + int err; + ino_t ino; + unsigned long long nent; + union au_rdu_ent_ul *u; + struct au_rdu_ent ent; + struct super_block *sb; + + err = 0; + nent = rdu->nent; + u = &rdu->ent; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + while (nent-- > 0) { + /* unnecessary to support mmap_sem since this is a dir */ + err = copy_from_user(&ent, u->e, sizeof(ent)); + if (!err) + /* VERIFY_WRITE */ + err = !access_ok(&u->e->ino, sizeof(ino)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + break; + } + + /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ + if (!ent.wh) + err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); + else + err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, + &ino); + if (unlikely(err)) { + AuTraceErr(err); + break; + } + + err = __put_user(ino, &u->e->ino); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + break; + } + u->ul += au_rdu_len(ent.nlen); + } + si_read_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_rdu_verify(struct aufs_rdu *rdu) +{ + AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " + "%llu, b%d, 0x%x, g%u}\n", + rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], + rdu->blk, + rdu->rent, rdu->shwh, rdu->full, + rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, + rdu->cookie.generation); + + if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) + return 0; + + AuDbg("%u:%u\n", + rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); + return -EINVAL; +} + +long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err, e; + struct aufs_rdu rdu; + void __user *p = (void __user *)arg; + + err = copy_from_user(&rdu, p, sizeof(rdu)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + err = au_rdu_verify(&rdu); + if (unlikely(err)) + goto out; + + switch (cmd) { + case AUFS_CTL_RDU: + err = au_rdu(file, &rdu); + if (unlikely(err)) + break; + + e = copy_to_user(p, &rdu, sizeof(rdu)); + if (unlikely(e)) { + err = -EFAULT; + AuTraceErr(err); + } + break; + case AUFS_CTL_RDU_INO: + err = au_rdu_ino(file, &rdu); + break; + + default: + /* err = -ENOTTY; */ + err = -EINVAL; + } + +out: + AuTraceErr(err); + return err; +} + +#ifdef CONFIG_COMPAT +long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err, e; + struct aufs_rdu rdu; + void __user *p = compat_ptr(arg); + + /* todo: get_user()? */ + err = copy_from_user(&rdu, p, sizeof(rdu)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + rdu.ent.e = compat_ptr(rdu.ent.ul); + err = au_rdu_verify(&rdu); + if (unlikely(err)) + goto out; + + switch (cmd) { + case AUFS_CTL_RDU: + err = au_rdu(file, &rdu); + if (unlikely(err)) + break; + + rdu.ent.ul = ptr_to_compat(rdu.ent.e); + rdu.tail.ul = ptr_to_compat(rdu.tail.e); + e = copy_to_user(p, &rdu, sizeof(rdu)); + if (unlikely(e)) { + err = -EFAULT; + AuTraceErr(err); + } + break; + case AUFS_CTL_RDU_INO: + err = au_rdu_ino(file, &rdu); + break; + + default: + /* err = -ENOTTY; */ + err = -EINVAL; + } + +out: + AuTraceErr(err); + return err; +} +#endif diff -Naur --no-dereference a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h --- a/fs/aufs/rwsem.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/rwsem.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * simple read-write semaphore wrappers + */ + +#ifndef __AUFS_RWSEM_H__ +#define __AUFS_RWSEM_H__ + +#ifdef __KERNEL__ + +#include "debug.h" + +/* in the future, the name 'au_rwsem' will be totally gone */ +#define au_rwsem rw_semaphore + +/* to debug easier, do not make them inlined functions */ +#define AuRwMustNoWaiters(rw) AuDebugOn(rwsem_is_contended(rw)) +/* rwsem_is_locked() is unusable */ +#define AuRwMustReadLock(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && !lockdep_is_held_type(rw, 1)) +#define AuRwMustWriteLock(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && !lockdep_is_held_type(rw, 0)) +#define AuRwMustAnyLock(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && !lockdep_is_held(rw)) +#define AuRwDestroy(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && lockdep_is_held(rw)) + +#define au_rw_init(rw) init_rwsem(rw) + +#define au_rw_init_wlock(rw) do { \ + au_rw_init(rw); \ + down_write(rw); \ + } while (0) + +#define au_rw_init_wlock_nested(rw, lsc) do { \ + au_rw_init(rw); \ + down_write_nested(rw, lsc); \ + } while (0) + +#define au_rw_read_lock(rw) down_read(rw) +#define au_rw_read_lock_nested(rw, lsc) down_read_nested(rw, lsc) +#define au_rw_read_unlock(rw) up_read(rw) +#define au_rw_dgrade_lock(rw) downgrade_write(rw) +#define au_rw_write_lock(rw) down_write(rw) +#define au_rw_write_lock_nested(rw, lsc) down_write_nested(rw, lsc) +#define au_rw_write_unlock(rw) up_write(rw) +/* why is not _nested version defined? */ +#define au_rw_read_trylock(rw) down_read_trylock(rw) +#define au_rw_write_trylock(rw) down_write_trylock(rw) + +#endif /* __KERNEL__ */ +#endif /* __AUFS_RWSEM_H__ */ diff -Naur --no-dereference a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c --- a/fs/aufs/sbinfo.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/sbinfo.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * superblock private data + */ + +#include +#include "aufs.h" + +/* + * they are necessary regardless sysfs is disabled. + */ +void au_si_free(struct kobject *kobj) +{ + int i; + struct au_sbinfo *sbinfo; + char *locked __maybe_unused; /* debug only */ + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + for (i = 0; i < AuPlink_NHASH; i++) + AuDebugOn(!hlist_bl_empty(sbinfo->si_plink + i)); + AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); + + AuLCntZero(au_lcnt_read(&sbinfo->si_ninodes, /*do_rev*/0)); + au_lcnt_fin(&sbinfo->si_ninodes, /*do_sync*/0); + AuLCntZero(au_lcnt_read(&sbinfo->si_nfiles, /*do_rev*/0)); + au_lcnt_fin(&sbinfo->si_nfiles, /*do_sync*/0); + + dbgaufs_si_fin(sbinfo); + au_rw_write_lock(&sbinfo->si_rwsem); + au_br_free(sbinfo); + au_rw_write_unlock(&sbinfo->si_rwsem); + + au_kfree_try_rcu(sbinfo->si_branch); + mutex_destroy(&sbinfo->si_xib_mtx); + AuRwDestroy(&sbinfo->si_rwsem); + + au_lcnt_wait_for_fin(&sbinfo->si_ninodes); + /* si_nfiles is waited too */ + au_kfree_rcu(sbinfo); +} + +int au_si_alloc(struct super_block *sb) +{ + int err, i; + struct au_sbinfo *sbinfo; + + err = -ENOMEM; + sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); + if (unlikely(!sbinfo)) + goto out; + + /* will be reallocated separately */ + sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); + if (unlikely(!sbinfo->si_branch)) + goto out_sbinfo; + + err = sysaufs_si_init(sbinfo); + if (!err) { + dbgaufs_si_null(sbinfo); + err = dbgaufs_si_init(sbinfo); + if (unlikely(err)) + kobject_put(&sbinfo->si_kobj); + } + if (unlikely(err)) + goto out_br; + + au_nwt_init(&sbinfo->si_nowait); + au_rw_init_wlock(&sbinfo->si_rwsem); + + au_lcnt_init(&sbinfo->si_ninodes, /*release*/NULL); + au_lcnt_init(&sbinfo->si_nfiles, /*release*/NULL); + + sbinfo->si_bbot = -1; + sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; + + sbinfo->si_wbr_copyup = AuWbrCopyup_Def; + sbinfo->si_wbr_create = AuWbrCreate_Def; + sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; + sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; + + au_fhsm_init(sbinfo); + + sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); + + sbinfo->si_xino_jiffy = jiffies; + sbinfo->si_xino_expire + = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); + mutex_init(&sbinfo->si_xib_mtx); + /* leave si_xib_last_pindex and si_xib_next_bit */ + + INIT_HLIST_BL_HEAD(&sbinfo->si_aopen); + + sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); + sbinfo->si_rdblk = AUFS_RDBLK_DEF; + sbinfo->si_rdhash = AUFS_RDHASH_DEF; + sbinfo->si_dirwh = AUFS_DIRWH_DEF; + + for (i = 0; i < AuPlink_NHASH; i++) + INIT_HLIST_BL_HEAD(sbinfo->si_plink + i); + init_waitqueue_head(&sbinfo->si_plink_wq); + spin_lock_init(&sbinfo->si_plink_maint_lock); + + INIT_HLIST_BL_HEAD(&sbinfo->si_files); + + /* with getattr by default */ + sbinfo->si_iop_array = aufs_iop; + + /* leave other members for sysaufs and si_mnt. */ + sbinfo->si_sb = sb; + sb->s_fs_info = sbinfo; + si_pid_set(sb); + return 0; /* success */ + +out_br: + au_kfree_try_rcu(sbinfo->si_branch); +out_sbinfo: + au_kfree_rcu(sbinfo); +out: + return err; +} + +int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink) +{ + int err, sz; + struct au_branch **brp; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + err = -ENOMEM; + sz = sizeof(*brp) * (sbinfo->si_bbot + 1); + if (unlikely(!sz)) + sz = sizeof(*brp); + brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS, + may_shrink); + if (brp) { + sbinfo->si_branch = brp; + err = 0; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +unsigned int au_sigen_inc(struct super_block *sb) +{ + unsigned int gen; + struct inode *inode; + + SiMustWriteLock(sb); + + gen = ++au_sbi(sb)->si_generation; + au_update_digen(sb->s_root); + inode = d_inode(sb->s_root); + au_update_iigen(inode, /*half*/0); + inode_inc_iversion(inode); + return gen; +} + +aufs_bindex_t au_new_br_id(struct super_block *sb) +{ + aufs_bindex_t br_id; + int i; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + for (i = 0; i <= AUFS_BRANCH_MAX; i++) { + br_id = ++sbinfo->si_last_br_id; + AuDebugOn(br_id < 0); + if (br_id && au_br_index(sb, br_id) < 0) + return br_id; + } + + return -1; +} + +/* ---------------------------------------------------------------------- */ + +/* it is ok that new 'nwt' tasks are appended while we are sleeping */ +int si_read_lock(struct super_block *sb, int flags) +{ + int err; + + err = 0; + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + + si_noflush_read_lock(sb); + err = au_plink_maint(sb, flags); + if (unlikely(err)) + si_read_unlock(sb); + + return err; +} + +int si_write_lock(struct super_block *sb, int flags) +{ + int err; + + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + + si_noflush_write_lock(sb); + err = au_plink_maint(sb, flags); + if (unlikely(err)) + si_write_unlock(sb); + + return err; +} + +/* dentry and super_block lock. call at entry point */ +int aufs_read_lock(struct dentry *dentry, int flags) +{ + int err; + struct super_block *sb; + + sb = dentry->d_sb; + err = si_read_lock(sb, flags); + if (unlikely(err)) + goto out; + + if (au_ftest_lock(flags, DW)) + di_write_lock_child(dentry); + else + di_read_lock_child(dentry, flags); + + if (au_ftest_lock(flags, GEN)) { + err = au_digen_test(dentry, au_sigen(sb)); + if (!au_opt_test(au_mntflags(sb), UDBA_NONE)) + AuDebugOn(!err && au_dbrange_test(dentry)); + else if (!err) + err = au_dbrange_test(dentry); + if (unlikely(err)) + aufs_read_unlock(dentry, flags); + } + +out: + return err; +} + +void aufs_read_unlock(struct dentry *dentry, int flags) +{ + if (au_ftest_lock(flags, DW)) + di_write_unlock(dentry); + else + di_read_unlock(dentry, flags); + si_read_unlock(dentry->d_sb); +} + +void aufs_write_lock(struct dentry *dentry) +{ + si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); + di_write_lock_child(dentry); +} + +void aufs_write_unlock(struct dentry *dentry) +{ + di_write_unlock(dentry); + si_write_unlock(dentry->d_sb); +} + +int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) +{ + int err; + unsigned int sigen; + struct super_block *sb; + + sb = d1->d_sb; + err = si_read_lock(sb, flags); + if (unlikely(err)) + goto out; + + di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIRS)); + + if (au_ftest_lock(flags, GEN)) { + sigen = au_sigen(sb); + err = au_digen_test(d1, sigen); + AuDebugOn(!err && au_dbrange_test(d1)); + if (!err) { + err = au_digen_test(d2, sigen); + AuDebugOn(!err && au_dbrange_test(d2)); + } + if (unlikely(err)) + aufs_read_and_write_unlock2(d1, d2); + } + +out: + return err; +} + +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) +{ + di_write_unlock2(d1, d2); + si_read_unlock(d1->d_sb); +} diff -Naur --no-dereference a/fs/aufs/super.c b/fs/aufs/super.c --- a/fs/aufs/super.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/super.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1047 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * mount and super_block operations + */ + +#include +#include +#include +#include +#include +#include "aufs.h" + +/* + * super_operations + */ +static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) +{ + struct au_icntnr *c; + + c = au_cache_alloc_icntnr(); + if (c) { + au_icntnr_init(c); + inode_set_iversion(&c->vfs_inode, 1); /* sigen(sb); */ + c->iinfo.ii_hinode = NULL; + return &c->vfs_inode; + } + return NULL; +} + +static void aufs_destroy_inode(struct inode *inode) +{ + if (!au_is_bad_inode(inode)) + au_iinfo_fin(inode); +} + +static void aufs_free_inode(struct inode *inode) +{ + au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); +} + +struct inode *au_iget_locked(struct super_block *sb, ino_t ino) +{ + struct inode *inode; + int err; + + inode = iget_locked(sb, ino); + if (unlikely(!inode)) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + if (!(inode->i_state & I_NEW)) + goto out; + + err = au_xigen_new(inode); + if (!err) + err = au_iinfo_init(inode); + if (!err) + inode_inc_iversion(inode); + else { + iget_failed(inode); + inode = ERR_PTR(err); + } + +out: + /* never return NULL */ + AuDebugOn(!inode); + AuTraceErrPtr(inode); + return inode; +} + +/* lock free root dinfo */ +static int au_show_brs(struct seq_file *seq, struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bbot; + struct path path; + struct au_hdentry *hdp; + struct au_branch *br; + au_br_perm_str_t perm; + + err = 0; + bbot = au_sbbot(sb); + bindex = 0; + hdp = au_hdentry(au_di(sb->s_root), bindex); + for (; !err && bindex <= bbot; bindex++, hdp++) { + br = au_sbr(sb, bindex); + path.mnt = au_br_mnt(br); + path.dentry = hdp->hd_dentry; + err = au_seq_path(seq, &path); + if (!err) { + au_optstr_br_perm(&perm, br->br_perm); + seq_printf(seq, "=%s", perm.a); + if (bindex != bbot) + seq_putc(seq, ':'); + } + } + if (unlikely(err || seq_has_overflowed(seq))) + err = -E2BIG; + + return err; +} + +static void au_gen_fmt(char *fmt, int len __maybe_unused, const char *pat, + const char *append) +{ + char *p; + + p = fmt; + while (*pat != ':') + *p++ = *pat++; + *p++ = *pat++; + strcpy(p, append); + AuDebugOn(strlen(fmt) >= len); +} + +static void au_show_wbr_create(struct seq_file *m, int v, + struct au_sbinfo *sbinfo) +{ + const char *pat; + char fmt[32]; + struct au_wbr_mfs *mfs; + + AuRwMustAnyLock(&sbinfo->si_rwsem); + + seq_puts(m, ",create="); + pat = au_optstr_wbr_create(v); + mfs = &sbinfo->si_wbr_mfs; + switch (v) { + case AuWbrCreate_TDP: + case AuWbrCreate_RR: + case AuWbrCreate_MFS: + case AuWbrCreate_PMFS: + seq_puts(m, pat); + break; + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + case AuWbrCreate_PMFSRR: + au_gen_fmt(fmt, sizeof(fmt), pat, "%llu"); + seq_printf(m, fmt, mfs->mfsrr_watermark); + break; + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + au_gen_fmt(fmt, sizeof(fmt), pat, "%lu"); + seq_printf(m, fmt, + jiffies_to_msecs(mfs->mfs_expire) + / MSEC_PER_SEC); + break; + case AuWbrCreate_MFSRRV: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + au_gen_fmt(fmt, sizeof(fmt), pat, "%llu:%lu"); + seq_printf(m, fmt, mfs->mfsrr_watermark, + jiffies_to_msecs(mfs->mfs_expire) / MSEC_PER_SEC); + break; + default: + BUG(); + } +} + +static int au_show_xino(struct seq_file *seq, struct super_block *sb) +{ +#ifdef CONFIG_SYSFS + return 0; +#else + int err; + const int len = sizeof(AUFS_XINO_FNAME) - 1; + aufs_bindex_t bindex, brid; + struct qstr *name; + struct file *f; + struct dentry *d, *h_root; + struct au_branch *br; + + AuRwMustAnyLock(&sbinfo->si_rwsem); + + err = 0; + f = au_sbi(sb)->si_xib; + if (!f) + goto out; + + /* stop printing the default xino path on the first writable branch */ + h_root = NULL; + bindex = au_xi_root(sb, f->f_path.dentry); + if (bindex >= 0) { + br = au_sbr_sb(sb, bindex); + h_root = au_br_dentry(br); + } + + d = f->f_path.dentry; + name = &d->d_name; + /* safe ->d_parent because the file is unlinked */ + if (d->d_parent == h_root + && name->len == len + && !memcmp(name->name, AUFS_XINO_FNAME, len)) + goto out; + + seq_puts(seq, ",xino="); + err = au_xino_path(seq, f); + +out: + return err; +#endif +} + +/* seq_file will re-call me in case of too long string */ +static int aufs_show_options(struct seq_file *m, struct dentry *dentry) +{ + int err; + unsigned int mnt_flags, v; + struct super_block *sb; + struct au_sbinfo *sbinfo; + +#define AuBool(name, str) do { \ + v = au_opt_test(mnt_flags, name); \ + if (v != au_opt_test(AuOpt_Def, name)) \ + seq_printf(m, ",%s" #str, v ? "" : "no"); \ +} while (0) + +#define AuStr(name, str) do { \ + v = mnt_flags & AuOptMask_##name; \ + if (v != (AuOpt_Def & AuOptMask_##name)) \ + seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ +} while (0) + +#define AuUInt(name, str, val) do { \ + if (val != AUFS_##name##_DEF) \ + seq_printf(m, "," #str "=%u", val); \ +} while (0) + + sb = dentry->d_sb; + if (sb->s_flags & SB_POSIXACL) + seq_puts(m, ",acl"); +#if 0 /* reserved for future use */ + if (sb->s_flags & SB_I_VERSION) + seq_puts(m, ",i_version"); +#endif + + /* lock free root dinfo */ + si_noflush_read_lock(sb); + sbinfo = au_sbi(sb); + seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); + + mnt_flags = au_mntflags(sb); + if (au_opt_test(mnt_flags, XINO)) { + err = au_show_xino(m, sb); + if (unlikely(err)) + goto out; + } else + seq_puts(m, ",noxino"); + + AuBool(TRUNC_XINO, trunc_xino); + AuStr(UDBA, udba); + AuBool(SHWH, shwh); + AuBool(PLINK, plink); + AuBool(DIO, dio); + AuBool(DIRPERM1, dirperm1); + + v = sbinfo->si_wbr_create; + if (v != AuWbrCreate_Def) + au_show_wbr_create(m, v, sbinfo); + + v = sbinfo->si_wbr_copyup; + if (v != AuWbrCopyup_Def) + seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); + + v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); + if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) + seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); + + AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); + + v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; + AuUInt(RDCACHE, rdcache, v); + + AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); + AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); + + au_fhsm_show(m, sbinfo); + + AuBool(DIRREN, dirren); + AuBool(SUM, sum); + /* AuBool(SUM_W, wsum); */ + AuBool(WARN_PERM, warn_perm); + AuBool(VERBOSE, verbose); + +out: + /* be sure to print "br:" last */ + if (!sysaufs_brs) { + seq_puts(m, ",br:"); + au_show_brs(m, sb); + } + si_read_unlock(sb); + return 0; + +#undef AuBool +#undef AuStr +#undef AuUInt +} + +/* ---------------------------------------------------------------------- */ + +/* sum mode which returns the summation for statfs(2) */ + +static u64 au_add_till_max(u64 a, u64 b) +{ + u64 old; + + old = a; + a += b; + if (old <= a) + return a; + return ULLONG_MAX; +} + +static u64 au_mul_till_max(u64 a, long mul) +{ + u64 old; + + old = a; + a *= mul; + if (old <= a) + return a; + return ULLONG_MAX; +} + +static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) +{ + int err; + long bsize, factor; + u64 blocks, bfree, bavail, files, ffree; + aufs_bindex_t bbot, bindex, i; + unsigned char shared; + struct path h_path; + struct super_block *h_sb; + + err = 0; + bsize = LONG_MAX; + files = 0; + ffree = 0; + blocks = 0; + bfree = 0; + bavail = 0; + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + h_path.mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path.mnt->mnt_sb; + shared = 0; + for (i = 0; !shared && i < bindex; i++) + shared = (au_sbr_sb(sb, i) == h_sb); + if (shared) + continue; + + /* sb->s_root for NFS is unreliable */ + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, buf); + if (unlikely(err)) + goto out; + + if (bsize > buf->f_bsize) { + /* + * we will reduce bsize, so we have to expand blocks + * etc. to match them again + */ + factor = (bsize / buf->f_bsize); + blocks = au_mul_till_max(blocks, factor); + bfree = au_mul_till_max(bfree, factor); + bavail = au_mul_till_max(bavail, factor); + bsize = buf->f_bsize; + } + + factor = (buf->f_bsize / bsize); + blocks = au_add_till_max(blocks, + au_mul_till_max(buf->f_blocks, factor)); + bfree = au_add_till_max(bfree, + au_mul_till_max(buf->f_bfree, factor)); + bavail = au_add_till_max(bavail, + au_mul_till_max(buf->f_bavail, factor)); + files = au_add_till_max(files, buf->f_files); + ffree = au_add_till_max(ffree, buf->f_ffree); + } + + buf->f_bsize = bsize; + buf->f_blocks = blocks; + buf->f_bfree = bfree; + buf->f_bavail = bavail; + buf->f_files = files; + buf->f_ffree = ffree; + buf->f_frsize = 0; + +out: + return err; +} + +static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err; + struct path h_path; + struct super_block *sb; + + /* lock free root dinfo */ + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (!au_opt_test(au_mntflags(sb), SUM)) { + /* sb->s_root for NFS is unreliable */ + h_path.mnt = au_sbr_mnt(sb, 0); + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, buf); + } else + err = au_statfs_sum(sb, buf); + si_read_unlock(sb); + + if (!err) { + buf->f_type = AUFS_SUPER_MAGIC; + buf->f_namelen = AUFS_MAX_NAMELEN; + memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); + } + /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_sync_fs(struct super_block *sb, int wait) +{ + int err, e; + aufs_bindex_t bbot, bindex; + struct au_branch *br; + struct super_block *h_sb; + + err = 0; + si_noflush_read_lock(sb); + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_writable(br->br_perm)) + continue; + + h_sb = au_sbr_sb(sb, bindex); + e = vfsub_sync_filesystem(h_sb, wait); + if (unlikely(e && !err)) + err = e; + /* go on even if an error happens */ + } + si_read_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* final actions when unmounting a file system */ +static void aufs_put_super(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + sbinfo = au_sbi(sb); + if (sbinfo) + kobject_put(&sbinfo->si_kobj); +} + +/* ---------------------------------------------------------------------- */ + +void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, + struct super_block *sb, void *arg) +{ + void *array; + unsigned long long n, sz; + + array = NULL; + n = 0; + if (!*hint) + goto out; + + if (*hint > ULLONG_MAX / sizeof(array)) { + array = ERR_PTR(-EMFILE); + pr_err("hint %llu\n", *hint); + goto out; + } + + sz = sizeof(array) * *hint; + array = kzalloc(sz, GFP_NOFS); + if (unlikely(!array)) + array = vzalloc(sz); + if (unlikely(!array)) { + array = ERR_PTR(-ENOMEM); + goto out; + } + + n = cb(sb, array, *hint, arg); + AuDebugOn(n > *hint); + +out: + *hint = n; + return array; +} + +static unsigned long long au_iarray_cb(struct super_block *sb, void *a, + unsigned long long max __maybe_unused, + void *arg) +{ + unsigned long long n; + struct inode **p, *inode; + struct list_head *head; + + n = 0; + p = a; + head = arg; + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(inode, head, i_sb_list) { + if (!au_is_bad_inode(inode) + && au_ii(inode)->ii_btop >= 0) { + spin_lock(&inode->i_lock); + if (atomic_read(&inode->i_count)) { + au_igrab(inode); + *p++ = inode; + n++; + AuDebugOn(n > max); + } + spin_unlock(&inode->i_lock); + } + } + spin_unlock(&sb->s_inode_list_lock); + + return n; +} + +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) +{ + struct au_sbinfo *sbi; + + sbi = au_sbi(sb); + *max = au_lcnt_read(&sbi->si_ninodes, /*do_rev*/1); + return au_array_alloc(max, au_iarray_cb, sb, &sb->s_inodes); +} + +void au_iarray_free(struct inode **a, unsigned long long max) +{ + unsigned long long ull; + + for (ull = 0; ull < max; ull++) + iput(a[ull]); + kvfree(a); +} + +/* ---------------------------------------------------------------------- */ + +/* + * refresh dentry and inode at remount time. + */ +/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ +static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, + struct dentry *parent) +{ + int err; + + di_write_lock_child(dentry); + di_read_lock_parent(parent, AuLock_IR); + err = au_refresh_dentry(dentry, parent); + if (!err && dir_flags) + au_hn_reset(d_inode(dentry), dir_flags); + di_read_unlock(parent, AuLock_IR); + di_write_unlock(dentry); + + return err; +} + +static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, + struct au_sbinfo *sbinfo, + const unsigned int dir_flags, unsigned int do_idop) +{ + int err; + struct dentry *parent; + + err = 0; + parent = dget_parent(dentry); + if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { + if (d_really_is_positive(dentry)) { + if (!d_is_dir(dentry)) + err = au_do_refresh(dentry, /*dir_flags*/0, + parent); + else { + err = au_do_refresh(dentry, dir_flags, parent); + if (unlikely(err)) + au_fset_si(sbinfo, FAILED_REFRESH_DIR); + } + } else + err = au_do_refresh(dentry, /*dir_flags*/0, parent); + AuDbgDentry(dentry); + } + dput(parent); + + if (!err) { + if (do_idop) + au_refresh_dop(dentry, /*force_reval*/0); + } else + au_refresh_dop(dentry, /*force_reval*/1); + + AuTraceErr(err); + return err; +} + +static int au_refresh_d(struct super_block *sb, unsigned int do_idop) +{ + int err, i, j, ndentry, e; + unsigned int sigen; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries, *d; + struct au_sbinfo *sbinfo; + struct dentry *root = sb->s_root; + const unsigned int dir_flags = au_hi_flags(d_inode(root), /*isdir*/1); + + if (do_idop) + au_refresh_dop(root, /*force_reval*/0); + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, root, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + sigen = au_sigen(sb); + sbinfo = au_sbi(sb); + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + d = dentries[j]; + e = au_do_refresh_d(d, sigen, sbinfo, dir_flags, + do_idop); + if (unlikely(e && !err)) + err = e; + /* go on even err */ + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static int au_refresh_i(struct super_block *sb, unsigned int do_idop) +{ + int err, e; + unsigned int sigen; + unsigned long long max, ull; + struct inode *inode, **array; + + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + err = 0; + sigen = au_sigen(sb); + for (ull = 0; ull < max; ull++) { + inode = array[ull]; + if (unlikely(!inode)) + break; + + e = 0; + ii_write_lock_child(inode); + if (au_iigen(inode, NULL) != sigen) { + e = au_refresh_hinode_self(inode); + if (unlikely(e)) { + au_refresh_iop(inode, /*force_getattr*/1); + pr_err("error %d, i%lu\n", e, inode->i_ino); + if (!err) + err = e; + /* go on even if err */ + } + } + if (!e && do_idop) + au_refresh_iop(inode, /*force_getattr*/0); + ii_write_unlock(inode); + } + + au_iarray_free(array, max); + +out: + return err; +} + +static void au_remount_refresh(struct super_block *sb, unsigned int do_idop) +{ + int err, e; + unsigned int udba; + aufs_bindex_t bindex, bbot; + struct dentry *root; + struct inode *inode; + struct au_branch *br; + struct au_sbinfo *sbi; + + au_sigen_inc(sb); + sbi = au_sbi(sb); + au_fclr_si(sbi, FAILED_REFRESH_DIR); + + root = sb->s_root; + DiMustNoWaiters(root); + inode = d_inode(root); + IiMustNoWaiters(inode); + + udba = au_opt_udba(sb); + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(udba, br, br->br_perm); + if (unlikely(err)) + AuIOErr("hnotify failed on br %d, %d, ignored\n", + bindex, err); + /* go on even if err */ + } + au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); + + if (do_idop) { + if (au_ftest_si(sbi, NO_DREVAL)) { + AuDebugOn(sb->s_d_op == &aufs_dop_noreval); + sb->s_d_op = &aufs_dop_noreval; + AuDebugOn(sbi->si_iop_array == aufs_iop_nogetattr); + sbi->si_iop_array = aufs_iop_nogetattr; + } else { + AuDebugOn(sb->s_d_op == &aufs_dop); + sb->s_d_op = &aufs_dop; + AuDebugOn(sbi->si_iop_array == aufs_iop); + sbi->si_iop_array = aufs_iop; + } + pr_info("reset to %ps and %ps\n", + sb->s_d_op, sbi->si_iop_array); + } + + di_write_unlock(root); + err = au_refresh_d(sb, do_idop); + e = au_refresh_i(sb, do_idop); + if (unlikely(e && !err)) + err = e; + /* aufs_write_lock() calls ..._child() */ + di_write_lock_child(root); + + au_cpup_attr_all(inode, /*force*/1); + + if (unlikely(err)) + AuIOErr("refresh failed, ignored, %d\n", err); +} + +/* stop extra interpretation of errno in mount(8), and strange error messages */ +static int cvt_err(int err) +{ + AuTraceErr(err); + + switch (err) { + case -ENOENT: + case -ENOTDIR: + case -EEXIST: + case -EIO: + err = -EINVAL; + } + return err; +} + +static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + int err, do_dx; + unsigned int mntflags; + struct au_opts opts = { + .opt = NULL + }; + struct dentry *root; + struct inode *inode; + struct au_sbinfo *sbinfo; + + err = 0; + root = sb->s_root; + if (!data || !*data) { + err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (!err) { + di_write_lock_child(root); + err = au_opts_verify(sb, *flags, /*pending*/0); + aufs_write_unlock(root); + } + goto out; + } + + err = -ENOMEM; + opts.opt = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!opts.opt)) + goto out; + opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); + opts.flags = AuOpts_REMOUNT; + opts.sb_flags = *flags; + + /* parse it before aufs lock */ + err = au_opts_parse(sb, data, &opts); + if (unlikely(err)) + goto out_opts; + + sbinfo = au_sbi(sb); + inode = d_inode(root); + inode_lock(inode); + err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_mtx; + di_write_lock_child(root); + + /* au_opts_remount() may return an error */ + err = au_opts_remount(sb, &opts); + au_opts_free(&opts); + + if (au_ftest_opts(opts.flags, REFRESH)) + au_remount_refresh(sb, au_ftest_opts(opts.flags, REFRESH_IDOP)); + + if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { + mntflags = au_mntflags(sb); + do_dx = !!au_opt_test(mntflags, DIO); + au_dy_arefresh(do_dx); + } + + au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ + aufs_write_unlock(root); + +out_mtx: + inode_unlock(inode); +out_opts: + free_page((unsigned long)opts.opt); +out: + err = cvt_err(err); + AuTraceErr(err); + return err; +} + +static const struct super_operations aufs_sop = { + .alloc_inode = aufs_alloc_inode, + .destroy_inode = aufs_destroy_inode, + .free_inode = aufs_free_inode, + /* always deleting, no clearing */ + .drop_inode = generic_delete_inode, + .show_options = aufs_show_options, + .statfs = aufs_statfs, + .put_super = aufs_put_super, + .sync_fs = aufs_sync_fs, + .remount_fs = aufs_remount_fs +}; + +/* ---------------------------------------------------------------------- */ + +static int alloc_root(struct super_block *sb) +{ + int err; + struct inode *inode; + struct dentry *root; + + err = -ENOMEM; + inode = au_iget_locked(sb, AUFS_ROOT_INO); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out; + + inode->i_op = aufs_iop + AuIop_DIR; /* with getattr by default */ + inode->i_fop = &aufs_dir_fop; + inode->i_mode = S_IFDIR; + set_nlink(inode, 2); + unlock_new_inode(inode); + + root = d_make_root(inode); + if (unlikely(!root)) + goto out; + err = PTR_ERR(root); + if (IS_ERR(root)) + goto out; + + err = au_di_init(root); + if (!err) { + sb->s_root = root; + return 0; /* success */ + } + dput(root); + +out: + return err; +} + +static int aufs_fill_super(struct super_block *sb, void *raw_data, + int silent __maybe_unused) +{ + int err; + struct au_opts opts = { + .opt = NULL + }; + struct au_sbinfo *sbinfo; + struct dentry *root; + struct inode *inode; + char *arg = raw_data; + + if (unlikely(!arg || !*arg)) { + err = -EINVAL; + pr_err("no arg\n"); + goto out; + } + + err = -ENOMEM; + opts.opt = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!opts.opt)) + goto out; + opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); + opts.sb_flags = sb->s_flags; + + err = au_si_alloc(sb); + if (unlikely(err)) + goto out_opts; + sbinfo = au_sbi(sb); + + /* all timestamps always follow the ones on the branch */ + sb->s_flags |= SB_NOATIME | SB_NODIRATIME; + sb->s_flags |= SB_I_VERSION; /* do we really need this? */ + sb->s_op = &aufs_sop; + sb->s_d_op = &aufs_dop; + sb->s_magic = AUFS_SUPER_MAGIC; + sb->s_maxbytes = 0; + sb->s_stack_depth = 1; + au_export_init(sb); + au_xattr_init(sb); + + err = alloc_root(sb); + if (unlikely(err)) { + si_write_unlock(sb); + goto out_info; + } + root = sb->s_root; + inode = d_inode(root); + + /* + * actually we can parse options regardless aufs lock here. + * but at remount time, parsing must be done before aufs lock. + * so we follow the same rule. + */ + ii_write_lock_parent(inode); + aufs_write_unlock(root); + err = au_opts_parse(sb, arg, &opts); + if (unlikely(err)) + goto out_root; + + /* lock vfs_inode first, then aufs. */ + inode_lock(inode); + aufs_write_lock(root); + err = au_opts_mount(sb, &opts); + au_opts_free(&opts); + if (!err && au_ftest_si(sbinfo, NO_DREVAL)) { + sb->s_d_op = &aufs_dop_noreval; + pr_info("%ps\n", sb->s_d_op); + au_refresh_dop(root, /*force_reval*/0); + sbinfo->si_iop_array = aufs_iop_nogetattr; + au_refresh_iop(inode, /*force_getattr*/0); + } + aufs_write_unlock(root); + inode_unlock(inode); + if (!err) + goto out_opts; /* success */ + +out_root: + dput(root); + sb->s_root = NULL; +out_info: + kobject_put(&sbinfo->si_kobj); + sb->s_fs_info = NULL; +out_opts: + free_page((unsigned long)opts.opt); +out: + AuTraceErr(err); + err = cvt_err(err); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name __maybe_unused, + void *raw_data) +{ + struct dentry *root; + + /* all timestamps always follow the ones on the branch */ + /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ + root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); + if (IS_ERR(root)) + goto out; + + au_sbilist_add(root->d_sb); + +out: + return root; +} + +static void aufs_kill_sb(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + sbinfo = au_sbi(sb); + if (sbinfo) { + au_sbilist_del(sb); + aufs_write_lock(sb->s_root); + au_fhsm_fin(sb); + if (sbinfo->si_wbr_create_ops->fin) + sbinfo->si_wbr_create_ops->fin(sb); + if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { + au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); + au_remount_refresh(sb, /*do_idop*/0); + } + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb, /*verbose*/1); + au_xino_clr(sb); + au_dr_opt_flush(sb); + sbinfo->si_sb = NULL; + aufs_write_unlock(sb->s_root); + au_nwt_flush(&sbinfo->si_nowait); + } + kill_anon_super(sb); +} + +struct file_system_type aufs_fs_type = { + .name = AUFS_FSTYPE, + /* a race between rename and others */ + .fs_flags = FS_RENAME_DOES_D_MOVE, + .mount = aufs_mount, + .kill_sb = aufs_kill_sb, + /* no need to __module_get() and module_put(). */ + .owner = THIS_MODULE, +}; diff -Naur --no-dereference a/fs/aufs/super.h b/fs/aufs/super.h --- a/fs/aufs/super.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/super.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,587 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * super_block operations + */ + +#ifndef __AUFS_SUPER_H__ +#define __AUFS_SUPER_H__ + +#ifdef __KERNEL__ + +#include +#include +#include "hbl.h" +#include "lcnt.h" +#include "rwsem.h" +#include "wkq.h" + +/* policies to select one among multiple writable branches */ +struct au_wbr_copyup_operations { + int (*copyup)(struct dentry *dentry); +}; + +#define AuWbr_DIR 1 /* target is a dir */ +#define AuWbr_PARENT (1 << 1) /* always require a parent */ + +#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) +#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } +#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } + +struct au_wbr_create_operations { + int (*create)(struct dentry *dentry, unsigned int flags); + int (*init)(struct super_block *sb); + int (*fin)(struct super_block *sb); +}; + +struct au_wbr_mfs { + struct mutex mfs_lock; /* protect this structure */ + unsigned long mfs_jiffy; + unsigned long mfs_expire; + aufs_bindex_t mfs_bindex; + + unsigned long long mfsrr_bytes; + unsigned long long mfsrr_watermark; +}; + +#define AuPlink_NHASH 100 +static inline int au_plink_hash(ino_t ino) +{ + return ino % AuPlink_NHASH; +} + +/* File-based Hierarchical Storage Management */ +struct au_fhsm { +#ifdef CONFIG_AUFS_FHSM + /* allow only one process who can receive the notification */ + spinlock_t fhsm_spin; + pid_t fhsm_pid; + wait_queue_head_t fhsm_wqh; + atomic_t fhsm_readable; + + /* these are protected by si_rwsem */ + unsigned long fhsm_expire; + aufs_bindex_t fhsm_bottom; +#endif +}; + +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ + struct au_nowait_tasks si_nowait; + + /* + * tried sb->s_umount, but failed due to the dependency between i_mutex. + * rwsem for au_sbinfo is necessary. + */ + struct au_rwsem si_rwsem; + + /* + * dirty approach to protect sb->sb_inodes and ->s_files (gone) from + * remount. + */ + au_lcnt_t si_ninodes, si_nfiles; + + /* branch management */ + unsigned int si_generation; + + /* see AuSi_ flags */ + unsigned char au_si_status; + + aufs_bindex_t si_bbot; + + /* dirty trick to keep br_id plus */ + unsigned int si_last_br_id : + sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; + struct au_branch **si_branch; + + /* policy to select a writable branch */ + unsigned char si_wbr_copyup; + unsigned char si_wbr_create; + struct au_wbr_copyup_operations *si_wbr_copyup_ops; + struct au_wbr_create_operations *si_wbr_create_ops; + + /* round robin */ + atomic_t si_wbr_rr_next; + + /* most free space */ + struct au_wbr_mfs si_wbr_mfs; + + /* File-based Hierarchical Storage Management */ + struct au_fhsm si_fhsm; + + /* mount flags */ + /* include/asm-ia64/siginfo.h defines a macro named si_flags */ + unsigned int si_mntflags; + + /* external inode number (bitmap and translation table) */ + loff_t si_ximaxent; /* max entries in a xino */ + + struct file *si_xib; + struct mutex si_xib_mtx; /* protect xib members */ + unsigned long *si_xib_buf; + unsigned long si_xib_last_pindex; + int si_xib_next_bit; + + unsigned long si_xino_jiffy; + unsigned long si_xino_expire; + /* reserved for future use */ + /* unsigned long long si_xib_limit; */ /* Max xib file size */ + +#ifdef CONFIG_AUFS_EXPORT + /* i_generation */ + /* todo: make xigen file an array to support many inode numbers */ + struct file *si_xigen; + atomic_t si_xigen_next; +#endif + + /* dirty trick to support atomic_open */ + struct hlist_bl_head si_aopen; + + /* vdir parameters */ + unsigned long si_rdcache; /* max cache time in jiffies */ + unsigned int si_rdblk; /* deblk size */ + unsigned int si_rdhash; /* hash size */ + + /* + * If the number of whiteouts are larger than si_dirwh, leave all of + * them after au_whtmp_ren to reduce the cost of rmdir(2). + * future fsck.aufs or kernel thread will remove them later. + * Otherwise, remove all whiteouts and the dir in rmdir(2). + */ + unsigned int si_dirwh; + + /* pseudo_link list */ + struct hlist_bl_head si_plink[AuPlink_NHASH]; + wait_queue_head_t si_plink_wq; + spinlock_t si_plink_maint_lock; + pid_t si_plink_maint_pid; + + /* file list */ + struct hlist_bl_head si_files; + + /* with/without getattr, brother of sb->s_d_op */ + const struct inode_operations *si_iop_array; + + /* + * sysfs and lifetime management. + * this is not a small structure and it may be a waste of memory in case + * of sysfs is disabled, particularly when many aufs-es are mounted. + * but using sysfs is majority. + */ + struct kobject si_kobj; +#ifdef CONFIG_DEBUG_FS + struct dentry *si_dbgaufs; + struct dentry *si_dbgaufs_plink; + struct dentry *si_dbgaufs_xib; +#ifdef CONFIG_AUFS_EXPORT + struct dentry *si_dbgaufs_xigen; +#endif +#endif + +#ifdef CONFIG_AUFS_SBILIST + struct hlist_bl_node si_list; +#endif + + /* dirty, necessary for unmounting, sysfs and sysrq */ + struct super_block *si_sb; +}; + +/* sbinfo status flags */ +/* + * set true when refresh_dirs() failed at remount time. + * then try refreshing dirs at access time again. + * if it is false, refreshing dirs at access time is unnecessary + */ +#define AuSi_FAILED_REFRESH_DIR 1 +#define AuSi_FHSM (1 << 1) /* fhsm is active now */ +#define AuSi_NO_DREVAL (1 << 2) /* disable all d_revalidate */ + +#ifndef CONFIG_AUFS_FHSM +#undef AuSi_FHSM +#define AuSi_FHSM 0 +#endif + +static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, + unsigned int flag) +{ + AuRwMustAnyLock(&sbi->si_rwsem); + return sbi->au_si_status & flag; +} +#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) +#define au_fset_si(sbinfo, name) do { \ + AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ + (sbinfo)->au_si_status |= AuSi_##name; \ +} while (0) +#define au_fclr_si(sbinfo, name) do { \ + AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ + (sbinfo)->au_si_status &= ~AuSi_##name; \ +} while (0) + +/* ---------------------------------------------------------------------- */ + +/* policy to select one among writable branches */ +#define AuWbrCopyup(sbinfo, ...) \ + ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) +#define AuWbrCreate(sbinfo, ...) \ + ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) + +/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ +#define AuLock_DW 1 /* write-lock dentry */ +#define AuLock_IR (1 << 1) /* read-lock inode */ +#define AuLock_IW (1 << 2) /* write-lock inode */ +#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ +#define AuLock_DIRS (1 << 4) /* target is a pair of dirs */ + /* except RENAME_EXCHANGE */ +#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ +#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ +#define AuLock_GEN (1 << 7) /* test digen/iigen */ +#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) +#define au_fset_lock(flags, name) \ + do { (flags) |= AuLock_##name; } while (0) +#define au_fclr_lock(flags, name) \ + do { (flags) &= ~AuLock_##name; } while (0) + +/* ---------------------------------------------------------------------- */ + +/* super.c */ +extern struct file_system_type aufs_fs_type; +struct inode *au_iget_locked(struct super_block *sb, ino_t ino); +typedef unsigned long long (*au_arraycb_t)(struct super_block *sb, void *array, + unsigned long long max, void *arg); +void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, + struct super_block *sb, void *arg); +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); +void au_iarray_free(struct inode **a, unsigned long long max); + +/* sbinfo.c */ +void au_si_free(struct kobject *kobj); +int au_si_alloc(struct super_block *sb); +int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink); + +unsigned int au_sigen_inc(struct super_block *sb); +aufs_bindex_t au_new_br_id(struct super_block *sb); + +int si_read_lock(struct super_block *sb, int flags); +int si_write_lock(struct super_block *sb, int flags); +int aufs_read_lock(struct dentry *dentry, int flags); +void aufs_read_unlock(struct dentry *dentry, int flags); +void aufs_write_lock(struct dentry *dentry); +void aufs_write_unlock(struct dentry *dentry); +int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); + +/* wbr_policy.c */ +extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; +extern struct au_wbr_create_operations au_wbr_create_ops[]; +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); +int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); +int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop); + +/* mvdown.c */ +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); + +#ifdef CONFIG_AUFS_FHSM +/* fhsm.c */ + +static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) +{ + pid_t pid; + + spin_lock(&fhsm->fhsm_spin); + pid = fhsm->fhsm_pid; + spin_unlock(&fhsm->fhsm_spin); + + return pid; +} + +void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); +void au_fhsm_wrote_all(struct super_block *sb, int force); +int au_fhsm_fd(struct super_block *sb, int oflags); +int au_fhsm_br_alloc(struct au_branch *br); +void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); +void au_fhsm_fin(struct super_block *sb); +void au_fhsm_init(struct au_sbinfo *sbinfo); +void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); +void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); +#else +AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, + int force) +AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) +AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) +AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) +AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) +AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) +AuStubVoid(au_fhsm_fin, struct super_block *sb) +AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) +AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) +AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) +#endif + +/* ---------------------------------------------------------------------- */ + +static inline struct au_sbinfo *au_sbi(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_EXPORT +int au_test_nfsd(void); +void au_export_init(struct super_block *sb); +void au_xigen_inc(struct inode *inode); +int au_xigen_new(struct inode *inode); +int au_xigen_set(struct super_block *sb, struct path *path); +void au_xigen_clr(struct super_block *sb); + +static inline int au_busy_or_stale(void) +{ + if (!au_test_nfsd()) + return -EBUSY; + return -ESTALE; +} +#else +AuStubInt0(au_test_nfsd, void) +AuStubVoid(au_export_init, struct super_block *sb) +AuStubVoid(au_xigen_inc, struct inode *inode) +AuStubInt0(au_xigen_new, struct inode *inode) +AuStubInt0(au_xigen_set, struct super_block *sb, struct path *path) +AuStubVoid(au_xigen_clr, struct super_block *sb) +AuStub(int, au_busy_or_stale, return -EBUSY, void) +#endif /* CONFIG_AUFS_EXPORT */ + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_SBILIST +/* module.c */ +extern struct hlist_bl_head au_sbilist; + +static inline void au_sbilist_init(void) +{ + INIT_HLIST_BL_HEAD(&au_sbilist); +} + +static inline void au_sbilist_add(struct super_block *sb) +{ + au_hbl_add(&au_sbi(sb)->si_list, &au_sbilist); +} + +static inline void au_sbilist_del(struct super_block *sb) +{ + au_hbl_del(&au_sbi(sb)->si_list, &au_sbilist); +} + +#ifdef CONFIG_AUFS_MAGIC_SYSRQ +static inline void au_sbilist_lock(void) +{ + hlist_bl_lock(&au_sbilist); +} + +static inline void au_sbilist_unlock(void) +{ + hlist_bl_unlock(&au_sbilist); +} +#define AuGFP_SBILIST GFP_ATOMIC +#else +AuStubVoid(au_sbilist_lock, void) +AuStubVoid(au_sbilist_unlock, void) +#define AuGFP_SBILIST GFP_NOFS +#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ +#else +AuStubVoid(au_sbilist_init, void) +AuStubVoid(au_sbilist_add, struct super_block *sb) +AuStubVoid(au_sbilist_del, struct super_block *sb) +AuStubVoid(au_sbilist_lock, void) +AuStubVoid(au_sbilist_unlock, void) +#define AuGFP_SBILIST GFP_NOFS +#endif + +/* ---------------------------------------------------------------------- */ + +static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) +{ + /* + * This function is a dynamic '__init' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ +#ifdef CONFIG_DEBUG_FS + sbinfo->si_dbgaufs = NULL; + sbinfo->si_dbgaufs_plink = NULL; + sbinfo->si_dbgaufs_xib = NULL; +#ifdef CONFIG_AUFS_EXPORT + sbinfo->si_dbgaufs_xigen = NULL; +#endif +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* current->atomic_flags */ +/* this value should never corrupt the ones defined in linux/sched.h */ +#define PFA_AUFS 0x10 + +TASK_PFA_TEST(AUFS, test_aufs) /* task_test_aufs */ +TASK_PFA_SET(AUFS, aufs) /* task_set_aufs */ +TASK_PFA_CLEAR(AUFS, aufs) /* task_clear_aufs */ + +static inline int si_pid_test(struct super_block *sb) +{ + return !!task_test_aufs(current); +} + +static inline void si_pid_clr(struct super_block *sb) +{ + AuDebugOn(!task_test_aufs(current)); + task_clear_aufs(current); +} + +static inline void si_pid_set(struct super_block *sb) +{ + AuDebugOn(task_test_aufs(current)); + task_set_aufs(current); +} + +/* ---------------------------------------------------------------------- */ + +/* lock superblock. mainly for entry point functions */ +#define __si_read_lock(sb) au_rw_read_lock(&au_sbi(sb)->si_rwsem) +#define __si_write_lock(sb) au_rw_write_lock(&au_sbi(sb)->si_rwsem) +#define __si_read_trylock(sb) au_rw_read_trylock(&au_sbi(sb)->si_rwsem) +#define __si_write_trylock(sb) au_rw_write_trylock(&au_sbi(sb)->si_rwsem) +/* +#define __si_read_trylock_nested(sb) \ + au_rw_read_trylock_nested(&au_sbi(sb)->si_rwsem) +#define __si_write_trylock_nested(sb) \ + au_rw_write_trylock_nested(&au_sbi(sb)->si_rwsem) +*/ + +#define __si_read_unlock(sb) au_rw_read_unlock(&au_sbi(sb)->si_rwsem) +#define __si_write_unlock(sb) au_rw_write_unlock(&au_sbi(sb)->si_rwsem) +#define __si_downgrade_lock(sb) au_rw_dgrade_lock(&au_sbi(sb)->si_rwsem) + +#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) +#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) +#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) + +static inline void si_noflush_read_lock(struct super_block *sb) +{ + __si_read_lock(sb); + si_pid_set(sb); +} + +static inline int si_noflush_read_trylock(struct super_block *sb) +{ + int locked; + + locked = __si_read_trylock(sb); + if (locked) + si_pid_set(sb); + return locked; +} + +static inline void si_noflush_write_lock(struct super_block *sb) +{ + __si_write_lock(sb); + si_pid_set(sb); +} + +static inline int si_noflush_write_trylock(struct super_block *sb) +{ + int locked; + + locked = __si_write_trylock(sb); + if (locked) + si_pid_set(sb); + return locked; +} + +#if 0 /* reserved */ +static inline int si_read_trylock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + return si_noflush_read_trylock(sb); +} +#endif + +static inline void si_read_unlock(struct super_block *sb) +{ + si_pid_clr(sb); + __si_read_unlock(sb); +} + +#if 0 /* reserved */ +static inline int si_write_trylock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + return si_noflush_write_trylock(sb); +} +#endif + +static inline void si_write_unlock(struct super_block *sb) +{ + si_pid_clr(sb); + __si_write_unlock(sb); +} + +#if 0 /* reserved */ +static inline void si_downgrade_lock(struct super_block *sb) +{ + __si_downgrade_lock(sb); +} +#endif + +/* ---------------------------------------------------------------------- */ + +static inline aufs_bindex_t au_sbbot(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_bbot; +} + +static inline unsigned int au_mntflags(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_mntflags; +} + +static inline unsigned int au_sigen(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_generation; +} + +static inline struct au_branch *au_sbr(struct super_block *sb, + aufs_bindex_t bindex) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_branch[0 + bindex]; +} + +static inline loff_t au_xi_maxent(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_ximaxent; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_SUPER_H__ */ diff -Naur --no-dereference a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c --- a/fs/aufs/sysaufs.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/sysaufs.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sysfs interface and lifetime management + * they are necessary regardless sysfs is disabled. + */ + +#include +#include "aufs.h" + +unsigned long sysaufs_si_mask; +struct kset *sysaufs_kset; + +#define AuSiAttr(_name) { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = sysaufs_si_##_name, \ +} + +static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); +struct attribute *sysaufs_si_attrs[] = { + &sysaufs_si_attr_xi_path.attr, + NULL, +}; + +static const struct sysfs_ops au_sbi_ops = { + .show = sysaufs_si_show +}; + +static struct kobj_type au_sbi_ktype = { + .release = au_si_free, + .sysfs_ops = &au_sbi_ops, + .default_attrs = sysaufs_si_attrs +}; + +/* ---------------------------------------------------------------------- */ + +int sysaufs_si_init(struct au_sbinfo *sbinfo) +{ + int err; + + sbinfo->si_kobj.kset = sysaufs_kset; + /* cf. sysaufs_name() */ + err = kobject_init_and_add + (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, + SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); + + return err; +} + +void sysaufs_fin(void) +{ + sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); + kset_unregister(sysaufs_kset); +} + +int __init sysaufs_init(void) +{ + int err; + + do { + get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); + } while (!sysaufs_si_mask); + + err = -EINVAL; + sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); + if (unlikely(!sysaufs_kset)) + goto out; + err = PTR_ERR(sysaufs_kset); + if (IS_ERR(sysaufs_kset)) + goto out; + err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); + if (unlikely(err)) + kset_unregister(sysaufs_kset); + +out: + return err; +} diff -Naur --no-dereference a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h --- a/fs/aufs/sysaufs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/sysaufs.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sysfs interface and mount lifetime management + */ + +#ifndef __SYSAUFS_H__ +#define __SYSAUFS_H__ + +#ifdef __KERNEL__ + +#include +#include "module.h" + +struct super_block; +struct au_sbinfo; + +struct sysaufs_si_attr { + struct attribute attr; + int (*show)(struct seq_file *seq, struct super_block *sb); +}; + +/* ---------------------------------------------------------------------- */ + +/* sysaufs.c */ +extern unsigned long sysaufs_si_mask; +extern struct kset *sysaufs_kset; +extern struct attribute *sysaufs_si_attrs[]; +int sysaufs_si_init(struct au_sbinfo *sbinfo); +int __init sysaufs_init(void); +void sysaufs_fin(void); + +/* ---------------------------------------------------------------------- */ + +/* some people doesn't like to show a pointer in kernel */ +static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) +{ + return sysaufs_si_mask ^ (unsigned long)sbinfo; +} + +#define SysaufsSiNamePrefix "si_" +#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) +static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) +{ + snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", + sysaufs_si_id(sbinfo)); +} + +struct au_branch; +#ifdef CONFIG_SYSFS +/* sysfs.c */ +extern struct attribute_group *sysaufs_attr_group; + +int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); +ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, + char *buf); +long au_brinfo_ioctl(struct file *file, unsigned long arg); +#ifdef CONFIG_COMPAT +long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); +#endif + +void sysaufs_br_init(struct au_branch *br); +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); +void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); + +#define sysaufs_brs_init() do {} while (0) + +#else +#define sysaufs_attr_group NULL + +AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) +AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, + struct attribute *attr, char *buf) +AuStubVoid(sysaufs_br_init, struct au_branch *br) +AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) +AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) + +static inline void sysaufs_brs_init(void) +{ + sysaufs_brs = 0; +} + +#endif /* CONFIG_SYSFS */ + +#endif /* __KERNEL__ */ +#endif /* __SYSAUFS_H__ */ diff -Naur --no-dereference a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c --- a/fs/aufs/sysfs.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/sysfs.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sysfs interface + */ + +#include +#include +#include "aufs.h" + +#ifdef CONFIG_AUFS_FS_MODULE +/* this entry violates the "one line per file" policy of sysfs */ +static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + ssize_t err; + static char *conf = +/* this file is generated at compiling */ +#include "conf.str" + ; + + err = snprintf(buf, PAGE_SIZE, conf); + if (unlikely(err >= PAGE_SIZE)) + err = -EFBIG; + return err; +} + +static struct kobj_attribute au_config_attr = __ATTR_RO(config); +#endif + +static struct attribute *au_attr[] = { +#ifdef CONFIG_AUFS_FS_MODULE + &au_config_attr.attr, +#endif + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct attribute_group sysaufs_attr_group_body = { + .attrs = au_attr +}; + +struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; + +/* ---------------------------------------------------------------------- */ + +int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) +{ + int err; + + SiMustAnyLock(sb); + + err = 0; + if (au_opt_test(au_mntflags(sb), XINO)) { + err = au_xino_path(seq, au_sbi(sb)->si_xib); + seq_putc(seq, '\n'); + } + return err; +} + +/* + * the lifetime of branch is independent from the entry under sysfs. + * sysfs handles the lifetime of the entry, and never call ->show() after it is + * unlinked. + */ +static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, + aufs_bindex_t bindex, int idx) +{ + int err; + struct path path; + struct dentry *root; + struct au_branch *br; + au_br_perm_str_t perm; + + AuDbg("b%d\n", bindex); + + err = 0; + root = sb->s_root; + di_read_lock_parent(root, !AuLock_IR); + br = au_sbr(sb, bindex); + + switch (idx) { + case AuBrSysfs_BR: + path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(root, bindex); + err = au_seq_path(seq, &path); + if (!err) { + au_optstr_br_perm(&perm, br->br_perm); + seq_printf(seq, "=%s\n", perm.a); + } + break; + case AuBrSysfs_BRID: + seq_printf(seq, "%d\n", br->br_id); + break; + } + di_read_unlock(root, !AuLock_IR); + if (unlikely(err || seq_has_overflowed(seq))) + err = -E2BIG; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct seq_file *au_seq(char *p, ssize_t len) +{ + struct seq_file *seq; + + seq = kzalloc(sizeof(*seq), GFP_NOFS); + if (seq) { + /* mutex_init(&seq.lock); */ + seq->buf = p; + seq->size = len; + return seq; /* success */ + } + + seq = ERR_PTR(-ENOMEM); + return seq; +} + +#define SysaufsBr_PREFIX "br" +#define SysaufsBrid_PREFIX "brid" + +/* todo: file size may exceed PAGE_SIZE */ +ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + ssize_t err; + int idx; + long l; + aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct seq_file *seq; + char *name; + struct attribute **cattr; + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + sb = sbinfo->si_sb; + + /* + * prevent a race condition between sysfs and aufs. + * for instance, sysfs_file_read() calls sysfs_get_active_two() which + * prohibits maintaining the sysfs entries. + * hew we acquire read lock after sysfs_get_active_two(). + * on the other hand, the remount process may maintain the sysfs/aufs + * entries after acquiring write lock. + * it can cause a deadlock. + * simply we gave up processing read here. + */ + err = -EBUSY; + if (unlikely(!si_noflush_read_trylock(sb))) + goto out; + + seq = au_seq(buf, PAGE_SIZE); + err = PTR_ERR(seq); + if (IS_ERR(seq)) + goto out_unlock; + + name = (void *)attr->name; + cattr = sysaufs_si_attrs; + while (*cattr) { + if (!strcmp(name, (*cattr)->name)) { + err = container_of(*cattr, struct sysaufs_si_attr, attr) + ->show(seq, sb); + goto out_seq; + } + cattr++; + } + + if (!strncmp(name, SysaufsBrid_PREFIX, + sizeof(SysaufsBrid_PREFIX) - 1)) { + idx = AuBrSysfs_BRID; + name += sizeof(SysaufsBrid_PREFIX) - 1; + } else if (!strncmp(name, SysaufsBr_PREFIX, + sizeof(SysaufsBr_PREFIX) - 1)) { + idx = AuBrSysfs_BR; + name += sizeof(SysaufsBr_PREFIX) - 1; + } else + BUG(); + + err = kstrtol(name, 10, &l); + if (!err) { + bbot = au_sbbot(sb); + if (l <= bbot) + err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); + else + err = -ENOENT; + } + +out_seq: + if (!err) { + err = seq->count; + /* sysfs limit */ + if (unlikely(err == PAGE_SIZE)) + err = -EFBIG; + } + au_kfree_rcu(seq); +out_unlock: + si_read_unlock(sb); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) +{ + int err; + int16_t brid; + aufs_bindex_t bindex, bbot; + size_t sz; + char *buf; + struct seq_file *seq; + struct au_branch *br; + + si_read_lock(sb, AuLock_FLUSH); + bbot = au_sbbot(sb); + err = bbot + 1; + if (!arg) + goto out; + + err = -ENOMEM; + buf = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!buf)) + goto out; + + seq = au_seq(buf, PAGE_SIZE); + err = PTR_ERR(seq); + if (IS_ERR(seq)) + goto out_buf; + + sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); + for (bindex = 0; bindex <= bbot; bindex++, arg++) { + /* VERIFY_WRITE */ + err = !access_ok(arg, sizeof(*arg)); + if (unlikely(err)) + break; + + br = au_sbr(sb, bindex); + brid = br->br_id; + BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); + err = __put_user(brid, &arg->id); + if (unlikely(err)) + break; + + BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); + err = __put_user(br->br_perm, &arg->perm); + if (unlikely(err)) + break; + + err = au_seq_path(seq, &br->br_path); + if (unlikely(err)) + break; + seq_putc(seq, '\0'); + if (!seq_has_overflowed(seq)) { + err = copy_to_user(arg->path, seq->buf, seq->count); + seq->count = 0; + if (unlikely(err)) + break; + } else { + err = -E2BIG; + goto out_seq; + } + } + if (unlikely(err)) + err = -EFAULT; + +out_seq: + au_kfree_rcu(seq); +out_buf: + free_page((unsigned long)buf); +out: + si_read_unlock(sb); + return err; +} + +long au_brinfo_ioctl(struct file *file, unsigned long arg) +{ + return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) +{ + return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); +} +#endif + +/* ---------------------------------------------------------------------- */ + +void sysaufs_br_init(struct au_branch *br) +{ + int i; + struct au_brsysfs *br_sysfs; + struct attribute *attr; + + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + attr = &br_sysfs->attr; + sysfs_attr_init(attr); + attr->name = br_sysfs->name; + attr->mode = 0444; + br_sysfs++; + } +} + +void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ + struct au_branch *br; + struct kobject *kobj; + struct au_brsysfs *br_sysfs; + int i; + aufs_bindex_t bbot; + + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; + bbot = au_sbbot(sb); + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + sysfs_remove_file(kobj, &br_sysfs->attr); + br_sysfs++; + } + } +} + +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) +{ + int err, i; + aufs_bindex_t bbot; + struct kobject *kobj; + struct au_branch *br; + struct au_brsysfs *br_sysfs; + + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; + bbot = au_sbbot(sb); + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), + SysaufsBr_PREFIX "%d", bindex); + snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), + SysaufsBrid_PREFIX "%d", bindex); + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + err = sysfs_create_file(kobj, &br_sysfs->attr); + if (unlikely(err)) + pr_warn("failed %s under sysfs(%d)\n", + br_sysfs->name, err); + br_sysfs++; + } + } +} diff -Naur --no-dereference a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c --- a/fs/aufs/sysrq.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/sysrq.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * magic sysrq handler + */ + +/* #include */ +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +static void sysrq_sb(struct super_block *sb) +{ + char *plevel; + struct au_sbinfo *sbinfo; + struct file *file; + struct hlist_bl_head *files; + struct hlist_bl_node *pos; + struct au_finfo *finfo; + struct inode *i; + + plevel = au_plevel; + au_plevel = KERN_WARNING; + + /* since we define pr_fmt, call printk directly */ +#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) + + sbinfo = au_sbi(sb); + printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); + pr("superblock\n"); + au_dpri_sb(sb); + +#if 0 /* reserved */ + do { + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + + err = au_dpages_init(&dpages, GFP_ATOMIC); + if (unlikely(err)) + break; + err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); + if (!err) + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) + au_dpri_dentry(dpage->dentries[j]); + } + au_dpages_free(&dpages); + } while (0); +#endif + + pr("isolated inode\n"); + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(i, &sb->s_inodes, i_sb_list) { + spin_lock(&i->i_lock); + if (hlist_empty(&i->i_dentry)) + au_dpri_inode(i); + spin_unlock(&i->i_lock); + } + spin_unlock(&sb->s_inode_list_lock); + + pr("files\n"); + files = &au_sbi(sb)->si_files; + hlist_bl_lock(files); + hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) { + umode_t mode; + + file = finfo->fi_file; + mode = file_inode(file)->i_mode; + if (!special_file(mode)) + au_dpri_file(file); + } + hlist_bl_unlock(files); + pr("done\n"); + +#undef pr + au_plevel = plevel; +} + +/* ---------------------------------------------------------------------- */ + +/* module parameter */ +static char *aufs_sysrq_key = "a"; +module_param_named(sysrq, aufs_sysrq_key, charp, 0444); +MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); + +static void au_sysrq(int key __maybe_unused) +{ + struct au_sbinfo *sbinfo; + struct hlist_bl_node *pos; + + lockdep_off(); + au_sbilist_lock(); + hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list) + sysrq_sb(sbinfo->si_sb); + au_sbilist_unlock(); + lockdep_on(); +} + +static struct sysrq_key_op au_sysrq_op = { + .handler = au_sysrq, + .help_msg = "Aufs", + .action_msg = "Aufs", + .enable_mask = SYSRQ_ENABLE_DUMP +}; + +/* ---------------------------------------------------------------------- */ + +int __init au_sysrq_init(void) +{ + int err; + char key; + + err = -1; + key = *aufs_sysrq_key; + if ('a' <= key && key <= 'z') + err = register_sysrq_key(key, &au_sysrq_op); + if (unlikely(err)) + pr_err("err %d, sysrq=%c\n", err, key); + return err; +} + +void au_sysrq_fin(void) +{ + int err; + + err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); + if (unlikely(err)) + pr_err("err %d (ignored)\n", err); +} diff -Naur --no-dereference a/fs/aufs/vdir.c b/fs/aufs/vdir.c --- a/fs/aufs/vdir.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/vdir.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,896 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * virtual or vertical directory + */ + +#include +#include "aufs.h" + +static unsigned int calc_size(int nlen) +{ + return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); +} + +static int set_deblk_end(union au_vdir_deblk_p *p, + union au_vdir_deblk_p *deblk_end) +{ + if (calc_size(0) <= deblk_end->deblk - p->deblk) { + p->de->de_str.len = 0; + /* smp_mb(); */ + return 0; + } + return -1; /* error */ +} + +/* returns true or false */ +static int is_deblk_end(union au_vdir_deblk_p *p, + union au_vdir_deblk_p *deblk_end) +{ + if (calc_size(0) <= deblk_end->deblk - p->deblk) + return !p->de->de_str.len; + return 1; +} + +static unsigned char *last_deblk(struct au_vdir *vdir) +{ + return vdir->vd_deblk[vdir->vd_nblk - 1]; +} + +/* ---------------------------------------------------------------------- */ + +/* estimate the appropriate size for name hash table */ +unsigned int au_rdhash_est(loff_t sz) +{ + unsigned int n; + + n = UINT_MAX; + sz >>= 10; + if (sz < n) + n = sz; + if (sz < AUFS_RDHASH_DEF) + n = AUFS_RDHASH_DEF; + /* pr_info("n %u\n", n); */ + return n; +} + +/* + * the allocated memory has to be freed by + * au_nhash_wh_free() or au_nhash_de_free(). + */ +int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) +{ + struct hlist_head *head; + unsigned int u; + size_t sz; + + sz = sizeof(*nhash->nh_head) * num_hash; + head = kmalloc(sz, gfp); + if (head) { + nhash->nh_num = num_hash; + nhash->nh_head = head; + for (u = 0; u < num_hash; u++) + INIT_HLIST_HEAD(head++); + return 0; /* success */ + } + + return -ENOMEM; +} + +static void nhash_count(struct hlist_head *head) +{ +#if 0 /* debugging */ + unsigned long n; + struct hlist_node *pos; + + n = 0; + hlist_for_each(pos, head) + n++; + pr_info("%lu\n", n); +#endif +} + +static void au_nhash_wh_do_free(struct hlist_head *head) +{ + struct au_vdir_wh *pos; + struct hlist_node *node; + + hlist_for_each_entry_safe(pos, node, head, wh_hash) + au_kfree_rcu(pos); +} + +static void au_nhash_de_do_free(struct hlist_head *head) +{ + struct au_vdir_dehstr *pos; + struct hlist_node *node; + + hlist_for_each_entry_safe(pos, node, head, hash) + au_cache_free_vdir_dehstr(pos); +} + +static void au_nhash_do_free(struct au_nhash *nhash, + void (*free)(struct hlist_head *head)) +{ + unsigned int n; + struct hlist_head *head; + + n = nhash->nh_num; + if (!n) + return; + + head = nhash->nh_head; + while (n-- > 0) { + nhash_count(head); + free(head++); + } + au_kfree_try_rcu(nhash->nh_head); +} + +void au_nhash_wh_free(struct au_nhash *whlist) +{ + au_nhash_do_free(whlist, au_nhash_wh_do_free); +} + +static void au_nhash_de_free(struct au_nhash *delist) +{ + au_nhash_do_free(delist, au_nhash_de_do_free); +} + +/* ---------------------------------------------------------------------- */ + +int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, + int limit) +{ + int num; + unsigned int u, n; + struct hlist_head *head; + struct au_vdir_wh *pos; + + num = 0; + n = whlist->nh_num; + head = whlist->nh_head; + for (u = 0; u < n; u++, head++) + hlist_for_each_entry(pos, head, wh_hash) + if (pos->wh_bindex == btgt && ++num > limit) + return 1; + return 0; +} + +static struct hlist_head *au_name_hash(struct au_nhash *nhash, + unsigned char *name, + unsigned int len) +{ + unsigned int v; + /* const unsigned int magic_bit = 12; */ + + AuDebugOn(!nhash->nh_num || !nhash->nh_head); + + v = 0; + if (len > 8) + len = 8; + while (len--) + v += *name++; + /* v = hash_long(v, magic_bit); */ + v %= nhash->nh_num; + return nhash->nh_head + v; +} + +static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, + int nlen) +{ + return str->len == nlen && !memcmp(str->name, name, nlen); +} + +/* returns found or not */ +int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) +{ + struct hlist_head *head; + struct au_vdir_wh *pos; + struct au_vdir_destr *str; + + head = au_name_hash(whlist, name, nlen); + hlist_for_each_entry(pos, head, wh_hash) { + str = &pos->wh_str; + AuDbg("%.*s\n", str->len, str->name); + if (au_nhash_test_name(str, name, nlen)) + return 1; + } + return 0; +} + +/* returns found(true) or not */ +static int test_known(struct au_nhash *delist, char *name, int nlen) +{ + struct hlist_head *head; + struct au_vdir_dehstr *pos; + struct au_vdir_destr *str; + + head = au_name_hash(delist, name, nlen); + hlist_for_each_entry(pos, head, hash) { + str = pos->str; + AuDbg("%.*s\n", str->len, str->name); + if (au_nhash_test_name(str, name, nlen)) + return 1; + } + return 0; +} + +static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, + unsigned char d_type) +{ +#ifdef CONFIG_AUFS_SHWH + wh->wh_ino = ino; + wh->wh_type = d_type; +#endif +} + +/* ---------------------------------------------------------------------- */ + +int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, + unsigned int d_type, aufs_bindex_t bindex, + unsigned char shwh) +{ + int err; + struct au_vdir_destr *str; + struct au_vdir_wh *wh; + + AuDbg("%.*s\n", nlen, name); + AuDebugOn(!whlist->nh_num || !whlist->nh_head); + + err = -ENOMEM; + wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); + if (unlikely(!wh)) + goto out; + + err = 0; + wh->wh_bindex = bindex; + if (shwh) + au_shwh_init_wh(wh, ino, d_type); + str = &wh->wh_str; + str->len = nlen; + memcpy(str->name, name, nlen); + hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); + /* smp_mb(); */ + +out: + return err; +} + +static int append_deblk(struct au_vdir *vdir) +{ + int err; + unsigned long ul; + const unsigned int deblk_sz = vdir->vd_deblk_sz; + union au_vdir_deblk_p p, deblk_end; + unsigned char **o; + + err = -ENOMEM; + o = au_krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), + GFP_NOFS, /*may_shrink*/0); + if (unlikely(!o)) + goto out; + + vdir->vd_deblk = o; + p.deblk = kmalloc(deblk_sz, GFP_NOFS); + if (p.deblk) { + ul = vdir->vd_nblk++; + vdir->vd_deblk[ul] = p.deblk; + vdir->vd_last.ul = ul; + vdir->vd_last.p.deblk = p.deblk; + deblk_end.deblk = p.deblk + deblk_sz; + err = set_deblk_end(&p, &deblk_end); + } + +out: + return err; +} + +static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, + unsigned int d_type, struct au_nhash *delist) +{ + int err; + unsigned int sz; + const unsigned int deblk_sz = vdir->vd_deblk_sz; + union au_vdir_deblk_p p, *room, deblk_end; + struct au_vdir_dehstr *dehstr; + + p.deblk = last_deblk(vdir); + deblk_end.deblk = p.deblk + deblk_sz; + room = &vdir->vd_last.p; + AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk + || !is_deblk_end(room, &deblk_end)); + + sz = calc_size(nlen); + if (unlikely(sz > deblk_end.deblk - room->deblk)) { + err = append_deblk(vdir); + if (unlikely(err)) + goto out; + + p.deblk = last_deblk(vdir); + deblk_end.deblk = p.deblk + deblk_sz; + /* smp_mb(); */ + AuDebugOn(room->deblk != p.deblk); + } + + err = -ENOMEM; + dehstr = au_cache_alloc_vdir_dehstr(); + if (unlikely(!dehstr)) + goto out; + + dehstr->str = &room->de->de_str; + hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); + room->de->de_ino = ino; + room->de->de_type = d_type; + room->de->de_str.len = nlen; + memcpy(room->de->de_str.name, name, nlen); + + err = 0; + room->deblk += sz; + if (unlikely(set_deblk_end(room, &deblk_end))) + err = append_deblk(vdir); + /* smp_mb(); */ + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_vdir_free(struct au_vdir *vdir) +{ + unsigned char **deblk; + + deblk = vdir->vd_deblk; + while (vdir->vd_nblk--) + au_kfree_try_rcu(*deblk++); + au_kfree_try_rcu(vdir->vd_deblk); + au_cache_free_vdir(vdir); +} + +static struct au_vdir *alloc_vdir(struct file *file) +{ + struct au_vdir *vdir; + struct super_block *sb; + int err; + + sb = file->f_path.dentry->d_sb; + SiMustAnyLock(sb); + + err = -ENOMEM; + vdir = au_cache_alloc_vdir(); + if (unlikely(!vdir)) + goto out; + + vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); + if (unlikely(!vdir->vd_deblk)) + goto out_free; + + vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; + if (!vdir->vd_deblk_sz) { + /* estimate the appropriate size for deblk */ + vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); + /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ + } + vdir->vd_nblk = 0; + vdir->vd_version = 0; + vdir->vd_jiffy = 0; + err = append_deblk(vdir); + if (!err) + return vdir; /* success */ + + au_kfree_try_rcu(vdir->vd_deblk); + +out_free: + au_cache_free_vdir(vdir); +out: + vdir = ERR_PTR(err); + return vdir; +} + +static int reinit_vdir(struct au_vdir *vdir) +{ + int err; + union au_vdir_deblk_p p, deblk_end; + + while (vdir->vd_nblk > 1) { + au_kfree_try_rcu(vdir->vd_deblk[vdir->vd_nblk - 1]); + /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ + vdir->vd_nblk--; + } + p.deblk = vdir->vd_deblk[0]; + deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; + err = set_deblk_end(&p, &deblk_end); + /* keep vd_dblk_sz */ + vdir->vd_last.ul = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + vdir->vd_version = 0; + vdir->vd_jiffy = 0; + /* smp_mb(); */ + return err; +} + +/* ---------------------------------------------------------------------- */ + +#define AuFillVdir_CALLED 1 +#define AuFillVdir_WHABLE (1 << 1) +#define AuFillVdir_SHWH (1 << 2) +#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) +#define au_fset_fillvdir(flags, name) \ + do { (flags) |= AuFillVdir_##name; } while (0) +#define au_fclr_fillvdir(flags, name) \ + do { (flags) &= ~AuFillVdir_##name; } while (0) + +#ifndef CONFIG_AUFS_SHWH +#undef AuFillVdir_SHWH +#define AuFillVdir_SHWH 0 +#endif + +struct fillvdir_arg { + struct dir_context ctx; + struct file *file; + struct au_vdir *vdir; + struct au_nhash delist; + struct au_nhash whlist; + aufs_bindex_t bindex; + unsigned int flags; + int err; +}; + +static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, + loff_t offset __maybe_unused, u64 h_ino, + unsigned int d_type) +{ + struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); + char *name = (void *)__name; + struct super_block *sb; + ino_t ino; + const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); + + arg->err = 0; + sb = arg->file->f_path.dentry->d_sb; + au_fset_fillvdir(arg->flags, CALLED); + /* smp_mb(); */ + if (nlen <= AUFS_WH_PFX_LEN + || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + if (test_known(&arg->delist, name, nlen) + || au_nhash_test_known_wh(&arg->whlist, name, nlen)) + goto out; /* already exists or whiteouted */ + + arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); + if (!arg->err) { + if (unlikely(nlen > AUFS_MAX_NAMELEN)) + d_type = DT_UNKNOWN; + arg->err = append_de(arg->vdir, name, nlen, ino, + d_type, &arg->delist); + } + } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { + name += AUFS_WH_PFX_LEN; + nlen -= AUFS_WH_PFX_LEN; + if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) + goto out; /* already whiteouted */ + + ino = 0; /* just to suppress a warning */ + if (shwh) + arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, + &ino); + if (!arg->err) { + if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) + d_type = DT_UNKNOWN; + arg->err = au_nhash_append_wh + (&arg->whlist, name, nlen, ino, d_type, + arg->bindex, shwh); + } + } + +out: + if (!arg->err) + arg->vdir->vd_jiffy = jiffies; + /* smp_mb(); */ + AuTraceErr(arg->err); + return arg->err; +} + +static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, + struct au_nhash *whlist, struct au_nhash *delist) +{ +#ifdef CONFIG_AUFS_SHWH + int err; + unsigned int nh, u; + struct hlist_head *head; + struct au_vdir_wh *pos; + struct hlist_node *n; + char *p, *o; + struct au_vdir_destr *destr; + + AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); + + err = -ENOMEM; + o = p = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = 0; + nh = whlist->nh_num; + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + p += AUFS_WH_PFX_LEN; + for (u = 0; u < nh; u++) { + head = whlist->nh_head + u; + hlist_for_each_entry_safe(pos, n, head, wh_hash) { + destr = &pos->wh_str; + memcpy(p, destr->name, destr->len); + err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, + pos->wh_ino, pos->wh_type, delist); + if (unlikely(err)) + break; + } + } + + free_page((unsigned long)o); + +out: + AuTraceErr(err); + return err; +#else + return 0; +#endif +} + +static int au_do_read_vdir(struct fillvdir_arg *arg) +{ + int err; + unsigned int rdhash; + loff_t offset; + aufs_bindex_t bbot, bindex, btop; + unsigned char shwh; + struct file *hf, *file; + struct super_block *sb; + + file = arg->file; + sb = file->f_path.dentry->d_sb; + SiMustAnyLock(sb); + + rdhash = au_sbi(sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); + err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out_delist; + + err = 0; + arg->flags = 0; + shwh = 0; + if (au_opt_test(au_mntflags(sb), SHWH)) { + shwh = 1; + au_fset_fillvdir(arg->flags, SHWH); + } + btop = au_fbtop(file); + bbot = au_fbbot_dir(file); + for (bindex = btop; !err && bindex <= bbot; bindex++) { + hf = au_hf_dir(file, bindex); + if (!hf) + continue; + + offset = vfsub_llseek(hf, 0, SEEK_SET); + err = offset; + if (unlikely(offset)) + break; + + arg->bindex = bindex; + au_fclr_fillvdir(arg->flags, WHABLE); + if (shwh + || (bindex != bbot + && au_br_whable(au_sbr_perm(sb, bindex)))) + au_fset_fillvdir(arg->flags, WHABLE); + do { + arg->err = 0; + au_fclr_fillvdir(arg->flags, CALLED); + /* smp_mb(); */ + err = vfsub_iterate_dir(hf, &arg->ctx); + if (err >= 0) + err = arg->err; + } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); + + /* + * dir_relax() may be good for concurrency, but aufs should not + * use it since it will cause a lockdep problem. + */ + } + + if (!err && shwh) + err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); + + au_nhash_wh_free(&arg->whlist); + +out_delist: + au_nhash_de_free(&arg->delist); +out: + return err; +} + +static int read_vdir(struct file *file, int may_read) +{ + int err; + unsigned long expire; + unsigned char do_read; + struct fillvdir_arg arg = { + .ctx = { + .actor = fillvdir + } + }; + struct inode *inode; + struct au_vdir *vdir, *allocated; + + err = 0; + inode = file_inode(file); + IMustLock(inode); + IiMustWriteLock(inode); + SiMustAnyLock(inode->i_sb); + + allocated = NULL; + do_read = 0; + expire = au_sbi(inode->i_sb)->si_rdcache; + vdir = au_ivdir(inode); + if (!vdir) { + do_read = 1; + vdir = alloc_vdir(file); + err = PTR_ERR(vdir); + if (IS_ERR(vdir)) + goto out; + err = 0; + allocated = vdir; + } else if (may_read + && (!inode_eq_iversion(inode, vdir->vd_version) + || time_after(jiffies, vdir->vd_jiffy + expire))) { + do_read = 1; + err = reinit_vdir(vdir); + if (unlikely(err)) + goto out; + } + + if (!do_read) + return 0; /* success */ + + arg.file = file; + arg.vdir = vdir; + err = au_do_read_vdir(&arg); + if (!err) { + /* file->f_pos = 0; */ /* todo: ctx->pos? */ + vdir->vd_version = inode_query_iversion(inode); + vdir->vd_last.ul = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + if (allocated) + au_set_ivdir(inode, allocated); + } else if (allocated) + au_vdir_free(allocated); + +out: + return err; +} + +static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) +{ + int err, rerr; + unsigned long ul, n; + const unsigned int deblk_sz = src->vd_deblk_sz; + + AuDebugOn(tgt->vd_nblk != 1); + + err = -ENOMEM; + if (tgt->vd_nblk < src->vd_nblk) { + unsigned char **p; + + p = au_krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, + GFP_NOFS, /*may_shrink*/0); + if (unlikely(!p)) + goto out; + tgt->vd_deblk = p; + } + + if (tgt->vd_deblk_sz != deblk_sz) { + unsigned char *p; + + tgt->vd_deblk_sz = deblk_sz; + p = au_krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS, + /*may_shrink*/1); + if (unlikely(!p)) + goto out; + tgt->vd_deblk[0] = p; + } + memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); + tgt->vd_version = src->vd_version; + tgt->vd_jiffy = src->vd_jiffy; + + n = src->vd_nblk; + for (ul = 1; ul < n; ul++) { + tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, + GFP_NOFS); + if (unlikely(!tgt->vd_deblk[ul])) + goto out; + tgt->vd_nblk++; + } + tgt->vd_nblk = n; + tgt->vd_last.ul = tgt->vd_last.ul; + tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; + tgt->vd_last.p.deblk += src->vd_last.p.deblk + - src->vd_deblk[src->vd_last.ul]; + /* smp_mb(); */ + return 0; /* success */ + +out: + rerr = reinit_vdir(tgt); + BUG_ON(rerr); + return err; +} + +int au_vdir_init(struct file *file) +{ + int err; + struct inode *inode; + struct au_vdir *vdir_cache, *allocated; + + /* test file->f_pos here instead of ctx->pos */ + err = read_vdir(file, !file->f_pos); + if (unlikely(err)) + goto out; + + allocated = NULL; + vdir_cache = au_fvdir_cache(file); + if (!vdir_cache) { + vdir_cache = alloc_vdir(file); + err = PTR_ERR(vdir_cache); + if (IS_ERR(vdir_cache)) + goto out; + allocated = vdir_cache; + } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { + /* test file->f_pos here instead of ctx->pos */ + err = reinit_vdir(vdir_cache); + if (unlikely(err)) + goto out; + } else + return 0; /* success */ + + inode = file_inode(file); + err = copy_vdir(vdir_cache, au_ivdir(inode)); + if (!err) { + file->f_version = inode_query_iversion(inode); + if (allocated) + au_set_fvdir_cache(file, allocated); + } else if (allocated) + au_vdir_free(allocated); + +out: + return err; +} + +static loff_t calc_offset(struct au_vdir *vdir) +{ + loff_t offset; + union au_vdir_deblk_p p; + + p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; + offset = vdir->vd_last.p.deblk - p.deblk; + offset += vdir->vd_deblk_sz * vdir->vd_last.ul; + return offset; +} + +/* returns true or false */ +static int seek_vdir(struct file *file, struct dir_context *ctx) +{ + int valid; + unsigned int deblk_sz; + unsigned long ul, n; + loff_t offset; + union au_vdir_deblk_p p, deblk_end; + struct au_vdir *vdir_cache; + + valid = 1; + vdir_cache = au_fvdir_cache(file); + offset = calc_offset(vdir_cache); + AuDbg("offset %lld\n", offset); + if (ctx->pos == offset) + goto out; + + vdir_cache->vd_last.ul = 0; + vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; + if (!ctx->pos) + goto out; + + valid = 0; + deblk_sz = vdir_cache->vd_deblk_sz; + ul = div64_u64(ctx->pos, deblk_sz); + AuDbg("ul %lu\n", ul); + if (ul >= vdir_cache->vd_nblk) + goto out; + + n = vdir_cache->vd_nblk; + for (; ul < n; ul++) { + p.deblk = vdir_cache->vd_deblk[ul]; + deblk_end.deblk = p.deblk + deblk_sz; + offset = ul; + offset *= deblk_sz; + while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { + unsigned int l; + + l = calc_size(p.de->de_str.len); + offset += l; + p.deblk += l; + } + if (!is_deblk_end(&p, &deblk_end)) { + valid = 1; + vdir_cache->vd_last.ul = ul; + vdir_cache->vd_last.p = p; + break; + } + } + +out: + /* smp_mb(); */ + if (!valid) + AuDbg("valid %d\n", !valid); + return valid; +} + +int au_vdir_fill_de(struct file *file, struct dir_context *ctx) +{ + unsigned int l, deblk_sz; + union au_vdir_deblk_p deblk_end; + struct au_vdir *vdir_cache; + struct au_vdir_de *de; + + if (!seek_vdir(file, ctx)) + return 0; + + vdir_cache = au_fvdir_cache(file); + deblk_sz = vdir_cache->vd_deblk_sz; + while (1) { + deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; + deblk_end.deblk += deblk_sz; + while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { + de = vdir_cache->vd_last.p.de; + AuDbg("%.*s, off%lld, i%lu, dt%d\n", + de->de_str.len, de->de_str.name, ctx->pos, + (unsigned long)de->de_ino, de->de_type); + if (unlikely(!dir_emit(ctx, de->de_str.name, + de->de_str.len, de->de_ino, + de->de_type))) { + /* todo: ignore the error caused by udba? */ + /* return err; */ + return 0; + } + + l = calc_size(de->de_str.len); + vdir_cache->vd_last.p.deblk += l; + ctx->pos += l; + } + if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { + vdir_cache->vd_last.ul++; + vdir_cache->vd_last.p.deblk + = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; + ctx->pos = deblk_sz * vdir_cache->vd_last.ul; + continue; + } + break; + } + + /* smp_mb(); */ + return 0; +} diff -Naur --no-dereference a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c --- a/fs/aufs/vfsub.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/vfsub.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,890 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for VFS + */ + +#include +#include +#include +#include +#include +#include "aufs.h" + +#ifdef CONFIG_AUFS_BR_FUSE +int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb) +{ + if (!au_test_fuse(h_sb) || !au_userns) + return 0; + + return is_current_mnt_ns(mnt) ? 0 : -EACCES; +} +#endif + +int vfsub_sync_filesystem(struct super_block *h_sb, int wait) +{ + int err; + + lockdep_off(); + down_read(&h_sb->s_umount); + err = __sync_filesystem(h_sb, wait); + up_read(&h_sb->s_umount); + lockdep_on(); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +int vfsub_update_h_iattr(struct path *h_path, int *did) +{ + int err; + struct kstat st; + struct super_block *h_sb; + + /* + * Always needs h_path->mnt for LSM or FUSE branch. + */ + AuDebugOn(!h_path->mnt); + + /* for remote fs, leave work for its getattr or d_revalidate */ + /* for bad i_attr fs, handle them in aufs_getattr() */ + /* still some fs may acquire i_mutex. we need to skip them */ + err = 0; + if (!did) + did = &err; + h_sb = h_path->dentry->d_sb; + *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); + if (*did) + err = vfsub_getattr(h_path, &st); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct file *vfsub_dentry_open(struct path *path, int flags) +{ + return dentry_open(path, flags /* | __FMODE_NONOTIFY */, + current_cred()); +} + +struct file *vfsub_filp_open(const char *path, int oflags, int mode) +{ + struct file *file; + + lockdep_off(); + file = filp_open(path, + oflags /* | __FMODE_NONOTIFY */, + mode); + lockdep_on(); + if (IS_ERR(file)) + goto out; + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + +out: + return file; +} + +/* + * Ideally this function should call VFS:do_last() in order to keep all its + * checkings. But it is very hard for aufs to regenerate several VFS internal + * structure such as nameidata. This is a second (or third) best approach. + * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). + */ +int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *args) +{ + int err; + struct au_branch *br = args->br; + struct file *file = args->file; + /* copied from linux/fs/namei.c:atomic_open() */ + struct dentry *const DENTRY_NOT_SET = (void *)-1UL; + + IMustLock(dir); + AuDebugOn(!dir->i_op->atomic_open); + + err = au_br_test_oflag(args->open_flag, br); + if (unlikely(err)) + goto out; + + au_lcnt_inc(&br->br_nfiles); + file->f_path.dentry = DENTRY_NOT_SET; + file->f_path.mnt = au_br_mnt(br); + AuDbg("%ps\n", dir->i_op->atomic_open); + err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, + args->create_mode); + if (unlikely(err < 0)) { + au_lcnt_dec(&br->br_nfiles); + goto out; + } + + /* temporary workaround for nfsv4 branch */ + if (au_test_nfs(dir->i_sb)) + nfs_mark_for_revalidate(dir); + + if (file->f_mode & FMODE_CREATED) + fsnotify_create(dir, dentry); + if (!(file->f_mode & FMODE_OPENED)) { + au_lcnt_dec(&br->br_nfiles); + goto out; + } + + /* todo: call VFS:may_open() here */ + /* todo: ima_file_check() too? */ + if (!err && (args->open_flag & __FMODE_EXEC)) + err = deny_write_access(file); + if (!err) + fsnotify_open(file); + else + au_lcnt_dec(&br->br_nfiles); + /* note that the file is created and still opened */ + +out: + return err; +} + +int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) +{ + int err; + + err = kern_path(name, flags, path); + if (!err && d_is_positive(path->dentry)) + vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ + return err; +} + +struct dentry *vfsub_lookup_one_len_unlocked(const char *name, + struct path *ppath, int len) +{ + struct path path; + + path.dentry = lookup_one_len_unlocked(name, ppath->dentry, len); + if (IS_ERR(path.dentry)) + goto out; + if (d_is_positive(path.dentry)) { + path.mnt = ppath->mnt; + vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ + } + +out: + AuTraceErrPtr(path.dentry); + return path.dentry; +} + +struct dentry *vfsub_lookup_one_len(const char *name, struct path *ppath, + int len) +{ + struct path path; + + /* VFS checks it too, but by WARN_ON_ONCE() */ + IMustLock(d_inode(ppath->dentry)); + + path.dentry = lookup_one_len(name, ppath->dentry, len); + if (IS_ERR(path.dentry)) + goto out; + if (d_is_positive(path.dentry)) { + path.mnt = ppath->mnt; + vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ + } + +out: + AuTraceErrPtr(path.dentry); + return path.dentry; +} + +void vfsub_call_lkup_one(void *args) +{ + struct vfsub_lkup_one_args *a = args; + *a->errp = vfsub_lkup_one(a->name, a->ppath); +} + +/* ---------------------------------------------------------------------- */ + +struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2) +{ + struct dentry *d; + + lockdep_off(); + d = lock_rename(d1, d2); + lockdep_on(); + au_hn_suspend(hdir1); + if (hdir1 != hdir2) + au_hn_suspend(hdir2); + + return d; +} + +void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2) +{ + au_hn_resume(hdir1); + if (hdir1 != hdir2) + au_hn_resume(hdir2); + lockdep_off(); + unlock_rename(d1, d2); + lockdep_on(); +} + +/* ---------------------------------------------------------------------- */ + +int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_mknod(path, d, mode, 0); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_create(dir, path->dentry, mode, want_excl); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_symlink(path, d, symname); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_symlink(dir, path->dentry, symname); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_mknod(path, d, mode, new_encode_dev(dev)); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_mknod(dir, path->dentry, mode, dev); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +static int au_test_nlink(struct inode *inode) +{ + const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ + + if (!au_test_fs_no_limit_nlink(inode->i_sb) + || inode->i_nlink < link_max) + return 0; + return -EMLINK; +} + +int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, + struct inode **delegated_inode) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + err = au_test_nlink(d_inode(src_dentry)); + if (unlikely(err)) + return err; + + /* we don't call may_linkat() */ + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_link(src_dentry, path, d); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + /* fuse has different memory inode for the same inumber */ + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + tmp.dentry = src_dentry; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct path *path, + struct inode **delegated_inode, unsigned int flags) +{ + int err; + struct path tmp = { + .mnt = path->mnt + }; + struct dentry *d; + + IMustLock(dir); + IMustLock(src_dir); + + d = path->dentry; + path->dentry = d->d_parent; + tmp.dentry = src_dentry->d_parent; + err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_rename(src_dir, src_dentry, dir, path->dentry, + delegated_inode, flags); + lockdep_on(); + if (!err) { + int did; + + tmp.dentry = d->d_parent; + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = src_dentry; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + tmp.dentry = src_dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_mkdir(struct inode *dir, struct path *path, int mode) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_mkdir(path, d, mode); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_mkdir(dir, path->dentry, mode); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_rmdir(struct inode *dir, struct path *path) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_rmdir(path, d); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_rmdir(dir, path->dentry); + lockdep_on(); + if (!err) { + struct path tmp = { + .dentry = path->dentry->d_parent, + .mnt = path->mnt + }; + + vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ + } + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* todo: support mmap_sem? */ +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = vfs_read(file, ubuf, count, ppos); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = kernel_read(file, kbuf, count, ppos); + lockdep_on(); + AuTraceErr(err); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = vfs_write(file, ubuf, count, ppos); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = kernel_write(file, kbuf, count, ppos); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +int vfsub_flush(struct file *file, fl_owner_t id) +{ + int err; + + err = 0; + if (file->f_op->flush) { + if (!au_test_nfs(file->f_path.dentry->d_sb)) + err = file->f_op->flush(file, id); + else { + lockdep_off(); + err = file->f_op->flush(file, id); + lockdep_on(); + } + if (!err) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); + /*ignore*/ + } + return err; +} + +int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) +{ + int err; + + AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos); + + lockdep_off(); + err = iterate_dir(file, ctx); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + + return err; +} + +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + long err; + + lockdep_off(); + err = do_splice_to(in, ppos, pipe, len, flags); + lockdep_on(); + file_accessed(in); + if (err >= 0) + vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + long err; + + lockdep_off(); + err = do_splice_from(pipe, out, ppos, len, flags); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +int vfsub_fsync(struct file *file, struct path *path, int datasync) +{ + int err; + + /* file can be NULL */ + lockdep_off(); + err = vfs_fsync(file, datasync); + lockdep_on(); + if (!err) { + if (!path) { + AuDebugOn(!file); + path = &file->f_path; + } + vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ + } + return err; +} + +/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ +int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, + struct file *h_file) +{ + int err; + struct inode *h_inode; + struct super_block *h_sb; + + if (!h_file) { + err = vfsub_truncate(h_path, length); + goto out; + } + + h_inode = d_inode(h_path->dentry); + h_sb = h_inode->i_sb; + lockdep_off(); + sb_start_write(h_sb); + lockdep_on(); + err = locks_verify_truncate(h_inode, h_file, length); + if (!err) + err = security_path_truncate(h_path); + if (!err) { + lockdep_off(); + err = do_truncate(h_path->dentry, length, attr, h_file); + lockdep_on(); + } + lockdep_off(); + sb_end_write(h_sb); + lockdep_on(); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_vfsub_mkdir_args { + int *errp; + struct inode *dir; + struct path *path; + int mode; +}; + +static void au_call_vfsub_mkdir(void *args) +{ + struct au_vfsub_mkdir_args *a = args; + *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); +} + +int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) +{ + int err, do_sio, wkq_err; + + do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); + if (!do_sio) { + lockdep_off(); + err = vfsub_mkdir(dir, path, mode); + lockdep_on(); + } else { + struct au_vfsub_mkdir_args args = { + .errp = &err, + .dir = dir, + .path = path, + .mode = mode + }; + wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} + +struct au_vfsub_rmdir_args { + int *errp; + struct inode *dir; + struct path *path; +}; + +static void au_call_vfsub_rmdir(void *args) +{ + struct au_vfsub_rmdir_args *a = args; + *a->errp = vfsub_rmdir(a->dir, a->path); +} + +int vfsub_sio_rmdir(struct inode *dir, struct path *path) +{ + int err, do_sio, wkq_err; + + do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); + if (!do_sio) { + lockdep_off(); + err = vfsub_rmdir(dir, path); + lockdep_on(); + } else { + struct au_vfsub_rmdir_args args = { + .errp = &err, + .dir = dir, + .path = path + }; + wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct notify_change_args { + int *errp; + struct path *path; + struct iattr *ia; + struct inode **delegated_inode; +}; + +static void call_notify_change(void *args) +{ + struct notify_change_args *a = args; + struct inode *h_inode; + + h_inode = d_inode(a->path->dentry); + IMustLock(h_inode); + + *a->errp = -EPERM; + if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { + lockdep_off(); + *a->errp = notify_change(a->path->dentry, a->ia, + a->delegated_inode); + lockdep_on(); + if (!*a->errp) + vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ + } + AuTraceErr(*a->errp); +} + +int vfsub_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode) +{ + int err; + struct notify_change_args args = { + .errp = &err, + .path = path, + .ia = ia, + .delegated_inode = delegated_inode + }; + + call_notify_change(&args); + + return err; +} + +int vfsub_sio_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode) +{ + int err, wkq_err; + struct notify_change_args args = { + .errp = &err, + .path = path, + .ia = ia, + .delegated_inode = delegated_inode + }; + + wkq_err = au_wkq_wait(call_notify_change, &args); + if (unlikely(wkq_err)) + err = wkq_err; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct unlink_args { + int *errp; + struct inode *dir; + struct path *path; + struct inode **delegated_inode; +}; + +static void call_unlink(void *args) +{ + struct unlink_args *a = args; + struct dentry *d = a->path->dentry; + struct inode *h_inode; + const int stop_sillyrename = (au_test_nfs(d->d_sb) + && au_dcount(d) == 1); + + IMustLock(a->dir); + + a->path->dentry = d->d_parent; + *a->errp = security_path_unlink(a->path, d); + a->path->dentry = d; + if (unlikely(*a->errp)) + return; + + if (!stop_sillyrename) + dget(d); + h_inode = NULL; + if (d_is_positive(d)) { + h_inode = d_inode(d); + ihold(h_inode); + } + + lockdep_off(); + *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); + lockdep_on(); + if (!*a->errp) { + struct path tmp = { + .dentry = d->d_parent, + .mnt = a->path->mnt + }; + vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ + } + + if (!stop_sillyrename) + dput(d); + if (h_inode) + iput(h_inode); + + AuTraceErr(*a->errp); +} + +/* + * @dir: must be locked. + * @dentry: target dentry. + */ +int vfsub_unlink(struct inode *dir, struct path *path, + struct inode **delegated_inode, int force) +{ + int err; + struct unlink_args args = { + .errp = &err, + .dir = dir, + .path = path, + .delegated_inode = delegated_inode + }; + + if (!force) + call_unlink(&args); + else { + int wkq_err; + + wkq_err = au_wkq_wait(call_unlink, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} diff -Naur --no-dereference a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h --- a/fs/aufs/vfsub.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/vfsub.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,354 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for VFS + */ + +#ifndef __AUFS_VFSUB_H__ +#define __AUFS_VFSUB_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "debug.h" + +/* copied from linux/fs/internal.h */ +/* todo: BAD approach!! */ +extern void __mnt_drop_write(struct vfsmount *); +extern struct file *alloc_empty_file(int, const struct cred *); + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for lower inode */ +/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ +/* reduce? gave up. */ +enum { + AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ + AuLsc_I_PARENT, /* lower inode, parent first */ + AuLsc_I_PARENT2, /* copyup dirs */ + AuLsc_I_PARENT3, /* copyup wh */ + AuLsc_I_CHILD, + AuLsc_I_CHILD2, + AuLsc_I_End +}; + +/* to debug easier, do not make them inlined functions */ +#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) +#define IMustLock(i) AuDebugOn(!inode_is_locked(i)) + +/* ---------------------------------------------------------------------- */ + +static inline void vfsub_drop_nlink(struct inode *inode) +{ + AuDebugOn(!inode->i_nlink); + drop_nlink(inode); +} + +static inline void vfsub_dead_dir(struct inode *inode) +{ + AuDebugOn(!S_ISDIR(inode->i_mode)); + inode->i_flags |= S_DEAD; + clear_nlink(inode); +} + +static inline int vfsub_native_ro(struct inode *inode) +{ + return sb_rdonly(inode->i_sb) + || IS_RDONLY(inode) + /* || IS_APPEND(inode) */ + || IS_IMMUTABLE(inode); +} + +#ifdef CONFIG_AUFS_BR_FUSE +int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb); +#else +AuStubInt0(vfsub_test_mntns, struct vfsmount *mnt, struct super_block *h_sb); +#endif + +int vfsub_sync_filesystem(struct super_block *h_sb, int wait); + +/* ---------------------------------------------------------------------- */ + +int vfsub_update_h_iattr(struct path *h_path, int *did); +struct file *vfsub_dentry_open(struct path *path, int flags); +struct file *vfsub_filp_open(const char *path, int oflags, int mode); +struct au_branch; +struct vfsub_aopen_args { + struct file *file; + unsigned int open_flag; + umode_t create_mode; + struct au_branch *br; +}; +int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *args); +int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); + +struct dentry *vfsub_lookup_one_len_unlocked(const char *name, + struct path *ppath, int len); +struct dentry *vfsub_lookup_one_len(const char *name, struct path *ppath, + int len); + +struct vfsub_lkup_one_args { + struct dentry **errp; + struct qstr *name; + struct path *ppath; +}; + +static inline struct dentry *vfsub_lkup_one(struct qstr *name, + struct path *ppath) +{ + return vfsub_lookup_one_len(name->name, ppath, name->len); +} + +void vfsub_call_lkup_one(void *args); + +/* ---------------------------------------------------------------------- */ + +static inline int vfsub_mnt_want_write(struct vfsmount *mnt) +{ + int err; + + lockdep_off(); + err = mnt_want_write(mnt); + lockdep_on(); + return err; +} + +static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) +{ + lockdep_off(); + mnt_drop_write(mnt); + lockdep_on(); +} + +#if 0 /* reserved */ +static inline void vfsub_mnt_drop_write_file(struct file *file) +{ + lockdep_off(); + mnt_drop_write_file(file); + lockdep_on(); +} +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_hinode; +struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2); +void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2); + +int vfsub_create(struct inode *dir, struct path *path, int mode, + bool want_excl); +int vfsub_symlink(struct inode *dir, struct path *path, + const char *symname); +int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); +int vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct path *path, struct inode **delegated_inode); +int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, + struct inode *hdir, struct path *path, + struct inode **delegated_inode, unsigned int flags); +int vfsub_mkdir(struct inode *dir, struct path *path, int mode); +int vfsub_rmdir(struct inode *dir, struct path *path); + +/* ---------------------------------------------------------------------- */ + +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos); +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos); +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos); +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos); +int vfsub_flush(struct file *file, fl_owner_t id); +int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); + +static inline loff_t vfsub_f_size_read(struct file *file) +{ + return i_size_read(file_inode(file)); +} + +static inline unsigned int vfsub_file_flags(struct file *file) +{ + unsigned int flags; + + spin_lock(&file->f_lock); + flags = file->f_flags; + spin_unlock(&file->f_lock); + + return flags; +} + +static inline int vfsub_file_execed(struct file *file) +{ + /* todo: direct access f_flags */ + return !!(vfsub_file_flags(file) & __FMODE_EXEC); +} + +#if 0 /* reserved */ +static inline void vfsub_file_accessed(struct file *h_file) +{ + file_accessed(h_file); + vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ +} +#endif + +#if 0 /* reserved */ +static inline void vfsub_touch_atime(struct vfsmount *h_mnt, + struct dentry *h_dentry) +{ + struct path h_path = { + .dentry = h_dentry, + .mnt = h_mnt + }; + touch_atime(&h_path); + vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ +} +#endif + +static inline int vfsub_update_time(struct inode *h_inode, + struct timespec64 *ts, int flags) +{ + return inode_update_time(h_inode, ts, flags); + /* no vfsub_update_h_iattr() since we don't have struct path */ +} + +#ifdef CONFIG_FS_POSIX_ACL +static inline int vfsub_acl_chmod(struct inode *h_inode, umode_t h_mode) +{ + int err; + + err = posix_acl_chmod(h_inode, h_mode); + if (err == -EOPNOTSUPP) + err = 0; + return err; +} +#else +AuStubInt0(vfsub_acl_chmod, struct inode *h_inode, umode_t h_mode); +#endif + +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); + +static inline long vfsub_truncate(struct path *path, loff_t length) +{ + long err; + + lockdep_off(); + err = vfs_truncate(path, length); + lockdep_on(); + return err; +} + +int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, + struct file *h_file); +int vfsub_fsync(struct file *file, struct path *path, int datasync); + +/* + * re-use branch fs's ioctl(FICLONE) while aufs itself doesn't support such + * ioctl. + */ +static inline loff_t vfsub_clone_file_range(struct file *src, struct file *dst, + loff_t len) +{ + loff_t err; + + lockdep_off(); + err = vfs_clone_file_range(src, 0, dst, 0, len, /*remap_flags*/0); + lockdep_on(); + + return err; +} + +/* copy_file_range(2) is a systemcall */ +static inline ssize_t vfsub_copy_file_range(struct file *src, loff_t src_pos, + struct file *dst, loff_t dst_pos, + size_t len, unsigned int flags) +{ + ssize_t ssz; + + lockdep_off(); + ssz = vfs_copy_file_range(src, src_pos, dst, dst_pos, len, flags); + lockdep_on(); + + return ssz; +} + +/* ---------------------------------------------------------------------- */ + +static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t err; + + lockdep_off(); + err = vfs_llseek(file, offset, origin); + lockdep_on(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); +int vfsub_sio_rmdir(struct inode *dir, struct path *path); +int vfsub_sio_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode); +int vfsub_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode); +int vfsub_unlink(struct inode *dir, struct path *path, + struct inode **delegated_inode, int force); + +static inline int vfsub_getattr(const struct path *path, struct kstat *st) +{ + return vfs_getattr(path, st, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); +} + +/* ---------------------------------------------------------------------- */ + +static inline int vfsub_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + int err; + + lockdep_off(); + err = vfs_setxattr(dentry, name, value, size, flags); + lockdep_on(); + + return err; +} + +static inline int vfsub_removexattr(struct dentry *dentry, const char *name) +{ + int err; + + lockdep_off(); + err = vfs_removexattr(dentry, name); + lockdep_on(); + + return err; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_VFSUB_H__ */ diff -Naur --no-dereference a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c --- a/fs/aufs/wbr_policy.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/wbr_policy.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,830 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * policies for selecting one among multiple writable branches + */ + +#include +#include "aufs.h" + +/* subset of cpup_attr() */ +static noinline_for_stack +int au_cpdown_attr(struct path *h_path, struct dentry *h_src) +{ + int err, sbits; + struct iattr ia; + struct inode *h_isrc; + + h_isrc = d_inode(h_src); + ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; + ia.ia_mode = h_isrc->i_mode; + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(d_inode(h_path->dentry), h_isrc->i_flags); + /* no delegation since it is just created */ + err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); + + /* is this nfs only? */ + if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { + ia.ia_valid = ATTR_FORCE | ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); + } + + return err; +} + +#define AuCpdown_PARENT_OPQ 1 +#define AuCpdown_WHED (1 << 1) +#define AuCpdown_MADE_DIR (1 << 2) +#define AuCpdown_DIROPQ (1 << 3) +#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) +#define au_fset_cpdown(flags, name) \ + do { (flags) |= AuCpdown_##name; } while (0) +#define au_fclr_cpdown(flags, name) \ + do { (flags) &= ~AuCpdown_##name; } while (0) + +static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, + unsigned int *flags) +{ + int err; + struct dentry *opq_dentry; + + opq_dentry = au_diropq_create(dentry, bdst); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) + goto out; + dput(opq_dentry); + au_fset_cpdown(*flags, DIROPQ); + +out: + return err; +} + +static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, + struct inode *dir, aufs_bindex_t bdst) +{ + int err; + struct path h_path; + struct au_branch *br; + + br = au_sbr(dentry->d_sb, bdst); + h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) + goto out; + + err = 0; + if (d_is_positive(h_path.dentry)) { + h_path.mnt = au_br_mnt(br); + err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, + dentry); + } + dput(h_path.dentry); + +out: + return err; +} + +static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent, void *arg) +{ + int err, rerr; + aufs_bindex_t bopq, btop; + struct path h_path; + struct dentry *parent; + struct inode *h_dir, *h_inode, *inode, *dir; + unsigned int *flags = arg; + + btop = au_dbtop(dentry); + /* dentry is di-locked */ + parent = dget_parent(dentry); + dir = d_inode(parent); + h_dir = d_inode(h_parent); + AuDebugOn(h_dir != au_h_iptr(dir, bdst)); + IMustLock(h_dir); + + err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (unlikely(err < 0)) + goto out; + h_path.dentry = au_h_dptr(dentry, bdst); + h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); + err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, 0755); + if (unlikely(err)) + goto out_put; + au_fset_cpdown(*flags, MADE_DIR); + + bopq = au_dbdiropq(dentry); + au_fclr_cpdown(*flags, WHED); + au_fclr_cpdown(*flags, DIROPQ); + if (au_dbwh(dentry) == bdst) + au_fset_cpdown(*flags, WHED); + if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) + au_fset_cpdown(*flags, PARENT_OPQ); + h_inode = d_inode(h_path.dentry); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_opq(dentry, bdst, flags); + if (unlikely(err)) { + inode_unlock(h_inode); + goto out_dir; + } + } + + err = au_cpdown_attr(&h_path, au_h_dptr(dentry, btop)); + inode_unlock(h_inode); + if (unlikely(err)) + goto out_opq; + + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); + if (unlikely(err)) + goto out_opq; + } + + inode = d_inode(dentry); + if (au_ibbot(inode) < bdst) + au_set_ibbot(inode, bdst); + au_set_h_iptr(inode, bdst, au_igrab(h_inode), + au_hi_flags(inode, /*isdir*/1)); + au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); + goto out; /* success */ + + /* revert */ +out_opq: + if (au_ftest_cpdown(*flags, DIROPQ)) { + inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bdst); + inode_unlock(h_inode); + if (unlikely(rerr)) { + AuIOErr("failed removing diropq for %pd b%d (%d)\n", + dentry, bdst, rerr); + err = -EIO; + goto out; + } + } +out_dir: + if (au_ftest_cpdown(*flags, MADE_DIR)) { + rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); + if (unlikely(rerr)) { + AuIOErr("failed removing %pd b%d (%d)\n", + dentry, bdst, rerr); + err = -EIO; + } + } +out_put: + au_set_h_dptr(dentry, bdst, NULL); + if (au_dbbot(dentry) == bdst) + au_update_dbbot(dentry); +out: + dput(parent); + return err; +} + +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) +{ + int err; + unsigned int flags; + + flags = 0; + err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* policies for create */ + +int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) +{ + int err, i, j, ndentry; + aufs_bindex_t bopq; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries, *parent, *d; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + parent = dget_parent(dentry); + err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); + if (unlikely(err)) + goto out_free; + + err = bindex; + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + d = dentries[j]; + di_read_lock_parent2(d, !AuLock_IR); + bopq = au_dbdiropq(d); + di_read_unlock(d, !AuLock_IR); + if (bopq >= 0 && bopq < err) + err = bopq; + } + } + +out_free: + dput(parent); + au_dpages_free(&dpages); +out: + return err; +} + +static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) +{ + for (; bindex >= 0; bindex--) + if (!au_br_rdonly(au_sbr(sb, bindex))) + return bindex; + return -EROFS; +} + +/* top down parent */ +static int au_wbr_create_tdp(struct dentry *dentry, + unsigned int flags __maybe_unused) +{ + int err; + aufs_bindex_t btop, bindex; + struct super_block *sb; + struct dentry *parent, *h_parent; + + sb = dentry->d_sb; + btop = au_dbtop(dentry); + err = btop; + if (!au_br_rdonly(au_sbr(sb, btop))) + goto out; + + err = -EROFS; + parent = dget_parent(dentry); + for (bindex = au_dbtop(parent); bindex < btop; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = bindex; + break; + } + } + dput(parent); + + /* bottom up here */ + if (unlikely(err < 0)) { + err = au_wbr_bu(sb, btop - 1); + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + } + +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* an exception for the policy other than tdp */ +static int au_wbr_create_exp(struct dentry *dentry) +{ + int err; + aufs_bindex_t bwh, bdiropq; + struct dentry *parent; + + err = -1; + bwh = au_dbwh(dentry); + parent = dget_parent(dentry); + bdiropq = au_dbdiropq(parent); + if (bwh >= 0) { + if (bdiropq >= 0) + err = min(bdiropq, bwh); + else + err = bwh; + AuDbg("%d\n", err); + } else if (bdiropq >= 0) { + err = bdiropq; + AuDbg("%d\n", err); + } + dput(parent); + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + + if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) + err = -1; + + AuDbg("%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* round robin */ +static int au_wbr_create_init_rr(struct super_block *sb) +{ + int err; + + err = au_wbr_bu(sb, au_sbbot(sb)); + atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ + /* smp_mb(); */ + + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) +{ + int err, nbr; + unsigned int u; + aufs_bindex_t bindex, bbot; + struct super_block *sb; + atomic_t *next; + + err = au_wbr_create_exp(dentry); + if (err >= 0) + goto out; + + sb = dentry->d_sb; + next = &au_sbi(sb)->si_wbr_rr_next; + bbot = au_sbbot(sb); + nbr = bbot + 1; + for (bindex = 0; bindex <= bbot; bindex++) { + if (!au_ftest_wbr(flags, DIR)) { + err = atomic_dec_return(next) + 1; + /* modulo for 0 is meaningless */ + if (unlikely(!err)) + err = atomic_dec_return(next) + 1; + } else + err = atomic_read(next); + AuDbg("%d\n", err); + u = err; + err = u % nbr; + AuDbg("%d\n", err); + if (!au_br_rdonly(au_sbr(sb, err))) + break; + err = -EROFS; + } + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + +out: + AuDbg("%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* most free space */ +static void au_mfs(struct dentry *dentry, struct dentry *parent) +{ + struct super_block *sb; + struct au_branch *br; + struct au_wbr_mfs *mfs; + struct dentry *h_parent; + aufs_bindex_t bindex, bbot; + int err; + unsigned long long b, bavail; + struct path h_path; + /* reduce the stack usage */ + struct kstatfs *st; + + st = kmalloc(sizeof(*st), GFP_NOFS); + if (unlikely(!st)) { + AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); + return; + } + + bavail = 0; + sb = dentry->d_sb; + mfs = &au_sbi(sb)->si_wbr_mfs; + MtxMustLock(&mfs->mfs_lock); + mfs->mfs_bindex = -EROFS; + mfs->mfsrr_bytes = 0; + if (!parent) { + bindex = 0; + bbot = au_sbbot(sb); + } else { + bindex = au_dbtop(parent); + bbot = au_dbtaildir(parent); + } + + for (; bindex <= bbot; bindex++) { + if (parent) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + } + br = au_sbr(sb, bindex); + if (au_br_rdonly(br)) + continue; + + /* sb->s_root for NFS is unreliable */ + h_path.mnt = au_br_mnt(br); + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, st); + if (unlikely(err)) { + AuWarn1("failed statfs, b%d, %d\n", bindex, err); + continue; + } + + /* when the available size is equal, select the lower one */ + BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) + || sizeof(b) < sizeof(st->f_bsize)); + b = st->f_bavail * st->f_bsize; + br->br_wbr->wbr_bytes = b; + if (b >= bavail) { + bavail = b; + mfs->mfs_bindex = bindex; + mfs->mfs_jiffy = jiffies; + } + } + + mfs->mfsrr_bytes = bavail; + AuDbg("b%d\n", mfs->mfs_bindex); + au_kfree_rcu(st); +} + +static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) +{ + int err; + struct dentry *parent; + struct super_block *sb; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_exp(dentry); + if (err >= 0) + goto out; + + sb = dentry->d_sb; + parent = NULL; + if (au_ftest_wbr(flags, PARENT)) + parent = dget_parent(dentry); + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) + || mfs->mfs_bindex < 0 + || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) + au_mfs(dentry, parent); + mutex_unlock(&mfs->mfs_lock); + err = mfs->mfs_bindex; + dput(parent); + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + +out: + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_create_init_mfs(struct super_block *sb) +{ + struct au_wbr_mfs *mfs; + + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_init(&mfs->mfs_lock); + mfs->mfs_jiffy = 0; + mfs->mfs_bindex = -EROFS; + + return 0; +} + +static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) +{ + mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +/* top down regardless parent, and then mfs */ +static int au_wbr_create_tdmfs(struct dentry *dentry, + unsigned int flags __maybe_unused) +{ + int err; + aufs_bindex_t bwh, btail, bindex, bfound, bmfs; + unsigned long long watermark; + struct super_block *sb; + struct au_wbr_mfs *mfs; + struct au_branch *br; + struct dentry *parent; + + sb = dentry->d_sb; + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) + || mfs->mfs_bindex < 0) + au_mfs(dentry, /*parent*/NULL); + watermark = mfs->mfsrr_watermark; + bmfs = mfs->mfs_bindex; + mutex_unlock(&mfs->mfs_lock); + + /* another style of au_wbr_create_exp() */ + bwh = au_dbwh(dentry); + parent = dget_parent(dentry); + btail = au_dbtaildir(parent); + if (bwh >= 0 && bwh < btail) + btail = bwh; + + err = au_wbr_nonopq(dentry, btail); + if (unlikely(err < 0)) + goto out; + btail = err; + bfound = -1; + for (bindex = 0; bindex <= btail; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_rdonly(br)) + continue; + if (br->br_wbr->wbr_bytes > watermark) { + bfound = bindex; + break; + } + } + err = bfound; + if (err < 0) + err = bmfs; + +out: + dput(parent); + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* most free space and then round robin */ +static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) +{ + int err; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_mfs(dentry, flags); + if (err >= 0) { + mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) + err = au_wbr_create_rr(dentry, flags); + mutex_unlock(&mfs->mfs_lock); + } + + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_create_init_mfsrr(struct super_block *sb) +{ + int err; + + au_wbr_create_init_mfs(sb); /* ignore */ + err = au_wbr_create_init_rr(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* top down parent and most free space */ +static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) +{ + int err, e2; + unsigned long long b; + aufs_bindex_t bindex, btop, bbot; + struct super_block *sb; + struct dentry *parent, *h_parent; + struct au_branch *br; + + err = au_wbr_create_tdp(dentry, flags); + if (unlikely(err < 0)) + goto out; + parent = dget_parent(dentry); + btop = au_dbtop(parent); + bbot = au_dbtaildir(parent); + if (btop == bbot) + goto out_parent; /* success */ + + e2 = au_wbr_create_mfs(dentry, flags); + if (e2 < 0) + goto out_parent; /* success */ + + /* when the available size is equal, select upper one */ + sb = dentry->d_sb; + br = au_sbr(sb, err); + b = br->br_wbr->wbr_bytes; + AuDbg("b%d, %llu\n", err, b); + + for (bindex = btop; bindex <= bbot; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { + b = br->br_wbr->wbr_bytes; + err = bindex; + AuDbg("b%d, %llu\n", err, b); + } + } + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + +out_parent: + dput(parent); +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * - top down parent + * - most free space with parent + * - most free space round-robin regardless parent + */ +static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) +{ + int err; + unsigned long long watermark; + struct super_block *sb; + struct au_branch *br; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); + if (unlikely(err < 0)) + goto out; + + sb = dentry->d_sb; + br = au_sbr(sb, err); + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + watermark = mfs->mfsrr_watermark; + mutex_unlock(&mfs->mfs_lock); + if (br->br_wbr->wbr_bytes < watermark) + /* regardless the parent dir */ + err = au_wbr_create_mfsrr(dentry, flags); + +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* policies for copyup */ + +/* top down parent */ +static int au_wbr_copyup_tdp(struct dentry *dentry) +{ + return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); +} + +/* bottom up parent */ +static int au_wbr_copyup_bup(struct dentry *dentry) +{ + int err; + aufs_bindex_t bindex, btop; + struct dentry *parent, *h_parent; + struct super_block *sb; + + err = -EROFS; + sb = dentry->d_sb; + parent = dget_parent(dentry); + btop = au_dbtop(parent); + for (bindex = au_dbtop(dentry); bindex >= btop; bindex--) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = bindex; + break; + } + } + dput(parent); + + /* bottom up here */ + if (unlikely(err < 0)) + err = au_wbr_bu(sb, btop - 1); + + AuDbg("b%d\n", err); + return err; +} + +/* bottom up */ +int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop) +{ + int err; + + err = au_wbr_bu(dentry->d_sb, btop); + AuDbg("b%d\n", err); + if (err > btop) + err = au_wbr_nonopq(dentry, err); + + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_copyup_bu(struct dentry *dentry) +{ + int err; + aufs_bindex_t btop; + + btop = au_dbtop(dentry); + err = au_wbr_do_copyup_bu(dentry, btop); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { + [AuWbrCopyup_TDP] = { + .copyup = au_wbr_copyup_tdp + }, + [AuWbrCopyup_BUP] = { + .copyup = au_wbr_copyup_bup + }, + [AuWbrCopyup_BU] = { + .copyup = au_wbr_copyup_bu + } +}; + +struct au_wbr_create_operations au_wbr_create_ops[] = { + [AuWbrCreate_TDP] = { + .create = au_wbr_create_tdp + }, + [AuWbrCreate_RR] = { + .create = au_wbr_create_rr, + .init = au_wbr_create_init_rr + }, + [AuWbrCreate_MFS] = { + .create = au_wbr_create_mfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSV] = { + .create = au_wbr_create_mfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSRR] = { + .create = au_wbr_create_mfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSRRV] = { + .create = au_wbr_create_mfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_TDMFS] = { + .create = au_wbr_create_tdmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_TDMFSV] = { + .create = au_wbr_create_tdmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFS] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSV] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSRR] = { + .create = au_wbr_create_pmfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSRRV] = { + .create = au_wbr_create_pmfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + } +}; diff -Naur --no-dereference a/fs/aufs/whout.c b/fs/aufs/whout.c --- a/fs/aufs/whout.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/whout.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1064 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * whiteout for logical deletion and opaque directory + */ + +#include "aufs.h" + +#define WH_MASK 0444 + +/* + * If a directory contains this file, then it is opaque. We start with the + * .wh. flag so that it is blocked by lookup. + */ +static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, + sizeof(AUFS_WH_DIROPQ) - 1); + +/* + * generate whiteout name, which is NOT terminated by NULL. + * @name: original d_name.name + * @len: original d_name.len + * @wh: whiteout qstr + * returns zero when succeeds, otherwise error. + * succeeded value as wh->name should be freed by kfree(). + */ +int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) +{ + char *p; + + if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) + return -ENAMETOOLONG; + + wh->len = name->len + AUFS_WH_PFX_LEN; + p = kmalloc(wh->len, GFP_NOFS); + wh->name = p; + if (p) { + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); + /* smp_mb(); */ + return 0; + } + return -ENOMEM; +} + +/* ---------------------------------------------------------------------- */ + +/* + * test if the @wh_name exists under @h_ppath. + * @try_sio specifies the necessary of super-io. + */ +int au_wh_test(struct path *h_ppath, struct qstr *wh_name, int try_sio) +{ + int err; + struct dentry *wh_dentry; + + if (!try_sio) + wh_dentry = vfsub_lkup_one(wh_name, h_ppath); + else + wh_dentry = au_sio_lkup_one(wh_name, h_ppath); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) { + if (err == -ENAMETOOLONG) + err = 0; + goto out; + } + + err = 0; + if (d_is_negative(wh_dentry)) + goto out_wh; /* success */ + + err = 1; + if (d_is_reg(wh_dentry)) + goto out_wh; /* success */ + + err = -EIO; + AuIOErr("%pd Invalid whiteout entry type 0%o.\n", + wh_dentry, d_inode(wh_dentry)->i_mode); + +out_wh: + dput(wh_dentry); +out: + return err; +} + +/* + * test if the @h_path->dentry sets opaque or not. + */ +int au_diropq_test(struct path *h_path) +{ + int err; + struct inode *h_dir; + + h_dir = d_inode(h_path->dentry); + err = au_wh_test(h_path, &diropq_name, + au_test_h_perm_sio(h_dir, MAY_EXEC)); + return err; +} + +/* + * returns a negative dentry whose name is unique and temporary. + */ +struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, + struct qstr *prefix) +{ + struct dentry *dentry; + int i; + char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], + *name, *p; + /* strict atomic_t is unnecessary here */ + static unsigned short cnt; + struct qstr qs; + struct path h_ppath; + + BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); + + name = defname; + qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; + if (unlikely(prefix->len > DNAME_INLINE_LEN)) { + dentry = ERR_PTR(-ENAMETOOLONG); + if (unlikely(qs.len > NAME_MAX)) + goto out; + dentry = ERR_PTR(-ENOMEM); + name = kmalloc(qs.len + 1, GFP_NOFS); + if (unlikely(!name)) + goto out; + } + + /* doubly whiteout-ed */ + memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); + p = name + AUFS_WH_PFX_LEN * 2; + memcpy(p, prefix->name, prefix->len); + p += prefix->len; + *p++ = '.'; + AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); + + h_ppath.dentry = h_parent; + h_ppath.mnt = au_br_mnt(br); + qs.name = name; + for (i = 0; i < 3; i++) { + sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); + dentry = au_sio_lkup_one(&qs, &h_ppath); + if (IS_ERR(dentry) || d_is_negative(dentry)) + goto out_name; + dput(dentry); + } + /* pr_warn("could not get random name\n"); */ + dentry = ERR_PTR(-EEXIST); + AuDbg("%.*s\n", AuLNPair(&qs)); + BUG(); + +out_name: + if (name != defname) + au_kfree_try_rcu(name); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* + * rename the @h_dentry on @br to the whiteouted temporary name. + */ +int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) +{ + int err; + struct path h_path = { + .mnt = au_br_mnt(br) + }; + struct inode *h_dir, *delegated; + struct dentry *h_parent; + + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + + h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) + goto out; + + /* under the same dir, no need to lock_rename() */ + delegated = NULL; + err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated, + /*flags*/0); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + dput(h_path.dentry); + +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ +/* + * functions for removing a whiteout + */ + +static int do_unlink_wh(struct inode *h_dir, struct path *h_path) +{ + int err, force; + struct inode *delegated; + + /* + * forces superio when the dir has a sticky bit. + * this may be a violation of unix fs semantics. + */ + force = (h_dir->i_mode & S_ISVTX) + && !uid_eq(current_fsuid(), d_inode(h_path->dentry)->i_uid); + delegated = NULL; + err = vfsub_unlink(h_dir, h_path, &delegated, force); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + return err; +} + +int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, + struct dentry *dentry) +{ + int err; + + err = do_unlink_wh(h_dir, h_path); + if (!err && dentry) + au_set_dbwh(dentry, -1); + + return err; +} + +static int unlink_wh_name(struct path *h_ppath, struct qstr *wh) +{ + int err; + struct path h_path; + + err = 0; + h_path.dentry = vfsub_lkup_one(wh, h_ppath); + if (IS_ERR(h_path.dentry)) + err = PTR_ERR(h_path.dentry); + else { + if (d_is_reg(h_path.dentry)) { + h_path.mnt = h_ppath->mnt; + err = do_unlink_wh(d_inode(h_ppath->dentry), &h_path); + } + dput(h_path.dentry); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ +/* + * initialize/clean whiteout for a branch + */ + +static void au_wh_clean(struct inode *h_dir, struct path *whpath, + const int isdir) +{ + int err; + struct inode *delegated; + + if (d_is_negative(whpath->dentry)) + return; + + if (isdir) + err = vfsub_rmdir(h_dir, whpath); + else { + delegated = NULL; + err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + } + if (unlikely(err)) + pr_warn("failed removing %pd (%d), ignored.\n", + whpath->dentry, err); +} + +static int test_linkable(struct dentry *h_root) +{ + struct inode *h_dir = d_inode(h_root); + + if (h_dir->i_op->link) + return 0; + + pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", + h_root, au_sbtype(h_root->d_sb)); + return -ENOSYS; /* the branch doesn't have its ->link() */ +} + +/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ +static int au_whdir(struct inode *h_dir, struct path *path) +{ + int err; + + err = -EEXIST; + if (d_is_negative(path->dentry)) { + int mode = 0700; + + if (au_test_nfs(path->dentry->d_sb)) + mode |= 0111; + err = vfsub_mkdir(h_dir, path, mode); + } else if (d_is_dir(path->dentry)) + err = 0; + else + pr_err("unknown %pd exists\n", path->dentry); + + return err; +} + +struct au_wh_base { + const struct qstr *name; + struct dentry *dentry; +}; + +static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], + struct path *h_path) +{ + h_path->dentry = base[AuBrWh_BASE].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/0); + h_path->dentry = base[AuBrWh_PLINK].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/1); + h_path->dentry = base[AuBrWh_ORPH].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/1); +} + +/* + * returns tri-state, + * minus: error, caller should print the message + * zero: success + * plus: error, caller should NOT print the message + */ +static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, + int do_plink, struct au_wh_base base[], + struct path *h_path) +{ + int err; + struct inode *h_dir; + + h_dir = d_inode(h_root); + h_path->dentry = base[AuBrWh_BASE].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/0); + h_path->dentry = base[AuBrWh_PLINK].dentry; + if (do_plink) { + err = test_linkable(h_root); + if (unlikely(err)) { + err = 1; + goto out; + } + + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); + } else + au_wh_clean(h_dir, h_path, /*isdir*/1); + h_path->dentry = base[AuBrWh_ORPH].dentry; + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); + +out: + return err; +} + +/* + * for the moment, aufs supports the branch filesystem which does not support + * link(2). testing on FAT which does not support i_op->setattr() fully either, + * copyup failed. finally, such filesystem will not be used as the writable + * branch. + * + * returns tri-state, see above. + */ +static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, + int do_plink, struct au_wh_base base[], + struct path *h_path) +{ + int err; + struct inode *h_dir; + + WbrWhMustWriteLock(wbr); + + err = test_linkable(h_root); + if (unlikely(err)) { + err = 1; + goto out; + } + + /* + * todo: should this create be done in /sbin/mount.aufs helper? + */ + err = -EEXIST; + h_dir = d_inode(h_root); + if (d_is_negative(base[AuBrWh_BASE].dentry)) { + h_path->dentry = base[AuBrWh_BASE].dentry; + err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); + } else if (d_is_reg(base[AuBrWh_BASE].dentry)) + err = 0; + else + pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); + if (unlikely(err)) + goto out; + + h_path->dentry = base[AuBrWh_PLINK].dentry; + if (do_plink) { + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); + } else + au_wh_clean(h_dir, h_path, /*isdir*/1); + wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); + + h_path->dentry = base[AuBrWh_ORPH].dentry; + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); + +out: + return err; +} + +/* + * initialize the whiteout base file/dir for @br. + */ +int au_wh_init(struct au_branch *br, struct super_block *sb) +{ + int err, i; + const unsigned char do_plink + = !!au_opt_test(au_mntflags(sb), PLINK); + struct inode *h_dir; + struct path path = br->br_path; + struct dentry *h_root = path.dentry; + struct au_wbr *wbr = br->br_wbr; + static const struct qstr base_name[] = { + [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, + sizeof(AUFS_BASE_NAME) - 1), + [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, + sizeof(AUFS_PLINKDIR_NAME) - 1), + [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, + sizeof(AUFS_ORPHDIR_NAME) - 1) + }; + struct au_wh_base base[] = { + [AuBrWh_BASE] = { + .name = base_name + AuBrWh_BASE, + .dentry = NULL + }, + [AuBrWh_PLINK] = { + .name = base_name + AuBrWh_PLINK, + .dentry = NULL + }, + [AuBrWh_ORPH] = { + .name = base_name + AuBrWh_ORPH, + .dentry = NULL + } + }; + + if (wbr) + WbrWhMustWriteLock(wbr); + + for (i = 0; i < AuBrWh_Last; i++) { + /* doubly whiteouted */ + struct dentry *d; + + d = au_wh_lkup(h_root, (void *)base[i].name, br); + err = PTR_ERR(d); + if (IS_ERR(d)) + goto out; + + base[i].dentry = d; + AuDebugOn(wbr + && wbr->wbr_wh[i] + && wbr->wbr_wh[i] != base[i].dentry); + } + + if (wbr) + for (i = 0; i < AuBrWh_Last; i++) { + dput(wbr->wbr_wh[i]); + wbr->wbr_wh[i] = NULL; + } + + err = 0; + if (!au_br_writable(br->br_perm)) { + h_dir = d_inode(h_root); + au_wh_init_ro(h_dir, base, &path); + } else if (!au_br_wh_linkable(br->br_perm)) { + err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); + if (err > 0) + goto out; + else if (err) + goto out_err; + } else { + err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); + if (err > 0) + goto out; + else if (err) + goto out_err; + } + goto out; /* success */ + +out_err: + pr_err("an error(%d) on the writable branch %pd(%s)\n", + err, h_root, au_sbtype(h_root->d_sb)); +out: + for (i = 0; i < AuBrWh_Last; i++) + dput(base[i].dentry); + return err; +} + +/* ---------------------------------------------------------------------- */ +/* + * whiteouts are all hard-linked usually. + * when its link count reaches a ceiling, we create a new whiteout base + * asynchronously. + */ + +struct reinit_br_wh { + struct super_block *sb; + struct au_branch *br; +}; + +static void reinit_br_wh(void *arg) +{ + int err; + aufs_bindex_t bindex; + struct path h_path; + struct reinit_br_wh *a = arg; + struct au_wbr *wbr; + struct inode *dir, *delegated; + struct dentry *h_root; + struct au_hinode *hdir; + + err = 0; + wbr = a->br->br_wbr; + /* big aufs lock */ + si_noflush_write_lock(a->sb); + if (!au_br_writable(a->br->br_perm)) + goto out; + bindex = au_br_index(a->sb, a->br->br_id); + if (unlikely(bindex < 0)) + goto out; + + di_read_lock_parent(a->sb->s_root, AuLock_IR); + dir = d_inode(a->sb->s_root); + hdir = au_hi(dir, bindex); + h_root = au_h_dptr(a->sb->s_root, bindex); + AuDebugOn(h_root != au_br_dentry(a->br)); + + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + wbr_wh_write_lock(wbr); + err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, + h_root, a->br); + if (!err) { + h_path.dentry = wbr->wbr_whbase; + h_path.mnt = au_br_mnt(a->br); + delegated = NULL; + err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, + /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + } else { + pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); + err = 0; + } + dput(wbr->wbr_whbase); + wbr->wbr_whbase = NULL; + if (!err) + err = au_wh_init(a->br, a->sb); + wbr_wh_write_unlock(wbr); + au_hn_inode_unlock(hdir); + di_read_unlock(a->sb->s_root, AuLock_IR); + if (!err) + au_fhsm_wrote(a->sb, bindex, /*force*/0); + +out: + if (wbr) + atomic_dec(&wbr->wbr_wh_running); + au_lcnt_dec(&a->br->br_count); + si_write_unlock(a->sb); + au_nwt_done(&au_sbi(a->sb)->si_nowait); + au_kfree_rcu(a); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} + +static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) +{ + int do_dec, wkq_err; + struct reinit_br_wh *arg; + + do_dec = 1; + if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) + goto out; + + /* ignore ENOMEM */ + arg = kmalloc(sizeof(*arg), GFP_NOFS); + if (arg) { + /* + * dec(wh_running), kfree(arg) and dec(br_count) + * in reinit function + */ + arg->sb = sb; + arg->br = br; + au_lcnt_inc(&br->br_count); + wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); + if (unlikely(wkq_err)) { + atomic_dec(&br->br_wbr->wbr_wh_running); + au_lcnt_dec(&br->br_count); + au_kfree_rcu(arg); + } + do_dec = 0; + } + +out: + if (do_dec) + atomic_dec(&br->br_wbr->wbr_wh_running); +} + +/* ---------------------------------------------------------------------- */ + +/* + * create the whiteout @wh. + */ +static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, + struct dentry *wh) +{ + int err; + struct path h_path = { + .dentry = wh + }; + struct au_branch *br; + struct au_wbr *wbr; + struct dentry *h_parent; + struct inode *h_dir, *delegated; + + h_parent = wh->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + + br = au_sbr(sb, bindex); + h_path.mnt = au_br_mnt(br); + wbr = br->br_wbr; + wbr_wh_read_lock(wbr); + if (wbr->wbr_whbase) { + delegated = NULL; + err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + if (!err || err != -EMLINK) + goto out; + + /* link count full. re-initialize br_whbase. */ + kick_reinit_br_wh(sb, br); + } + + /* return this error in this context */ + err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); + if (!err) + au_fhsm_wrote(sb, bindex, /*force*/0); + +out: + wbr_wh_read_unlock(wbr); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * create or remove the diropq. + */ +static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags) +{ + struct dentry *opq_dentry; + struct super_block *sb; + struct au_branch *br; + struct path h_path; + int err; + + sb = dentry->d_sb; + br = au_sbr(sb, bindex); + h_path.dentry = au_h_dptr(dentry, bindex); + h_path.mnt = au_br_mnt(br); + opq_dentry = vfsub_lkup_one(&diropq_name, &h_path); + if (IS_ERR(opq_dentry)) + goto out; + + if (au_ftest_diropq(flags, CREATE)) { + err = link_or_create_wh(sb, bindex, opq_dentry); + if (!err) { + au_set_dbdiropq(dentry, bindex); + goto out; /* success */ + } + } else { + h_path.dentry = opq_dentry; + err = do_unlink_wh(au_h_iptr(d_inode(dentry), bindex), &h_path); + if (!err) + au_set_dbdiropq(dentry, -1); + } + dput(opq_dentry); + opq_dentry = ERR_PTR(err); + +out: + return opq_dentry; +} + +struct do_diropq_args { + struct dentry **errp; + struct dentry *dentry; + aufs_bindex_t bindex; + unsigned int flags; +}; + +static void call_do_diropq(void *args) +{ + struct do_diropq_args *a = args; + *a->errp = do_diropq(a->dentry, a->bindex, a->flags); +} + +struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags) +{ + struct dentry *diropq, *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + if (!au_test_h_perm_sio(d_inode(h_dentry), MAY_EXEC | MAY_WRITE)) + diropq = do_diropq(dentry, bindex, flags); + else { + int wkq_err; + struct do_diropq_args args = { + .errp = &diropq, + .dentry = dentry, + .bindex = bindex, + .flags = flags + }; + + wkq_err = au_wkq_wait(call_do_diropq, &args); + if (unlikely(wkq_err)) + diropq = ERR_PTR(wkq_err); + } + + return diropq; +} + +/* ---------------------------------------------------------------------- */ + +/* + * lookup whiteout dentry. + * @h_parent: lower parent dentry which must exist and be locked + * @base_name: name of dentry which will be whiteouted + * returns dentry for whiteout. + */ +struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, + struct au_branch *br) +{ + int err; + struct qstr wh_name; + struct dentry *wh_dentry; + struct path h_path; + + err = au_wh_name_alloc(&wh_name, base_name); + wh_dentry = ERR_PTR(err); + if (!err) { + h_path.dentry = h_parent; + h_path.mnt = au_br_mnt(br); + wh_dentry = vfsub_lkup_one(&wh_name, &h_path); + au_kfree_try_rcu(wh_name.name); + } + return wh_dentry; +} + +/* + * link/create a whiteout for @dentry on @bindex. + */ +struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent) +{ + struct dentry *wh_dentry; + struct super_block *sb; + int err; + + sb = dentry->d_sb; + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); + if (!IS_ERR(wh_dentry) && d_is_negative(wh_dentry)) { + err = link_or_create_wh(sb, bindex, wh_dentry); + if (!err) { + au_set_dbwh(dentry, bindex); + au_fhsm_wrote(sb, bindex, /*force*/0); + } else { + dput(wh_dentry); + wh_dentry = ERR_PTR(err); + } + } + + return wh_dentry; +} + +/* ---------------------------------------------------------------------- */ + +/* Delete all whiteouts in this directory on branch bindex. */ +static int del_wh_children(struct path *h_path, struct au_nhash *whlist, + aufs_bindex_t bindex) +{ + int err; + unsigned long ul, n; + struct qstr wh_name; + char *p; + struct hlist_head *head; + struct au_vdir_wh *pos; + struct au_vdir_destr *str; + + err = -ENOMEM; + p = (void *)__get_free_page(GFP_NOFS); + wh_name.name = p; + if (unlikely(!wh_name.name)) + goto out; + + err = 0; + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + p += AUFS_WH_PFX_LEN; + n = whlist->nh_num; + head = whlist->nh_head; + for (ul = 0; !err && ul < n; ul++, head++) { + hlist_for_each_entry(pos, head, wh_hash) { + if (pos->wh_bindex != bindex) + continue; + + str = &pos->wh_str; + if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { + memcpy(p, str->name, str->len); + wh_name.len = AUFS_WH_PFX_LEN + str->len; + err = unlink_wh_name(h_path, &wh_name); + if (!err) + continue; + break; + } + AuIOErr("whiteout name too long %.*s\n", + str->len, str->name); + err = -EIO; + break; + } + } + free_page((unsigned long)wh_name.name); + +out: + return err; +} + +struct del_wh_children_args { + int *errp; + struct path *h_path; + struct au_nhash *whlist; + aufs_bindex_t bindex; +}; + +static void call_del_wh_children(void *args) +{ + struct del_wh_children_args *a = args; + *a->errp = del_wh_children(a->h_path, a->whlist, a->bindex); +} + +/* ---------------------------------------------------------------------- */ + +struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) +{ + struct au_whtmp_rmdir *whtmp; + int err; + unsigned int rdhash; + + SiMustAnyLock(sb); + + whtmp = kzalloc(sizeof(*whtmp), gfp); + if (unlikely(!whtmp)) { + whtmp = ERR_PTR(-ENOMEM); + goto out; + } + + /* no estimation for dir size */ + rdhash = au_sbi(sb)->si_rdhash; + if (!rdhash) + rdhash = AUFS_RDHASH_DEF; + err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); + if (unlikely(err)) { + au_kfree_rcu(whtmp); + whtmp = ERR_PTR(err); + } + +out: + return whtmp; +} + +void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) +{ + if (whtmp->br) + au_lcnt_dec(&whtmp->br->br_count); + dput(whtmp->wh_dentry); + iput(whtmp->dir); + au_nhash_wh_free(&whtmp->whlist); + au_kfree_rcu(whtmp); +} + +/* + * rmdir the whiteouted temporary named dir @h_dentry. + * @whlist: whiteouted children. + */ +int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_nhash *whlist) +{ + int err; + unsigned int h_nlink; + struct path wh_path; + struct inode *wh_inode, *h_dir; + struct au_branch *br; + + h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ + IMustLock(h_dir); + + br = au_sbr(dir->i_sb, bindex); + wh_path.dentry = wh_dentry; + wh_path.mnt = au_br_mnt(br); + wh_inode = d_inode(wh_dentry); + inode_lock_nested(wh_inode, AuLsc_I_CHILD); + + /* + * someone else might change some whiteouts while we were sleeping. + * it means this whlist may have an obsoleted entry. + */ + if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) + err = del_wh_children(&wh_path, whlist, bindex); + else { + int wkq_err; + struct del_wh_children_args args = { + .errp = &err, + .h_path = &wh_path, + .whlist = whlist, + .bindex = bindex + }; + + wkq_err = au_wkq_wait(call_del_wh_children, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + inode_unlock(wh_inode); + + if (!err) { + h_nlink = h_dir->i_nlink; + err = vfsub_rmdir(h_dir, &wh_path); + /* some fs doesn't change the parent nlink in some cases */ + h_nlink -= h_dir->i_nlink; + } + + if (!err) { + if (au_ibtop(dir) == bindex) { + /* todo: dir->i_mutex is necessary */ + au_cpup_attr_timesizes(dir); + if (h_nlink) + vfsub_drop_nlink(dir); + } + return 0; /* success */ + } + + pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); + return err; +} + +static void call_rmdir_whtmp(void *args) +{ + int err; + aufs_bindex_t bindex; + struct au_whtmp_rmdir *a = args; + struct super_block *sb; + struct dentry *h_parent; + struct inode *h_dir; + struct au_hinode *hdir; + + /* rmdir by nfsd may cause deadlock with this i_mutex */ + /* inode_lock(a->dir); */ + err = -EROFS; + sb = a->dir->i_sb; + si_read_lock(sb, !AuLock_FLUSH); + if (!au_br_writable(a->br->br_perm)) + goto out; + bindex = au_br_index(sb, a->br->br_id); + if (unlikely(bindex < 0)) + goto out; + + err = -EIO; + ii_write_lock_parent(a->dir); + h_parent = dget_parent(a->wh_dentry); + h_dir = d_inode(h_parent); + hdir = au_hi(a->dir, bindex); + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out_mnt; + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, + a->br); + if (!err) + err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); + au_hn_inode_unlock(hdir); + vfsub_mnt_drop_write(au_br_mnt(a->br)); + +out_mnt: + dput(h_parent); + ii_write_unlock(a->dir); +out: + /* inode_unlock(a->dir); */ + au_whtmp_rmdir_free(a); + si_read_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} + +void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_whtmp_rmdir *args) +{ + int wkq_err; + struct super_block *sb; + + IMustLock(dir); + + /* all post-process will be done in do_rmdir_whtmp(). */ + sb = dir->i_sb; + args->dir = au_igrab(dir); + args->br = au_sbr(sb, bindex); + au_lcnt_inc(&args->br->br_count); + args->wh_dentry = dget(wh_dentry); + wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); + if (unlikely(wkq_err)) { + pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); + au_whtmp_rmdir_free(args); + } +} diff -Naur --no-dereference a/fs/aufs/whout.h b/fs/aufs/whout.h --- a/fs/aufs/whout.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/whout.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * whiteout for logical deletion and opaque directory + */ + +#ifndef __AUFS_WHOUT_H__ +#define __AUFS_WHOUT_H__ + +#ifdef __KERNEL__ + +#include "dir.h" + +/* whout.c */ +int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); +int au_wh_test(struct path *h_ppath, struct qstr *wh_name, int try_sio); +int au_diropq_test(struct path *h_path); +struct au_branch; +struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, + struct qstr *prefix); +int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); +int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, + struct dentry *dentry); +int au_wh_init(struct au_branch *br, struct super_block *sb); + +/* diropq flags */ +#define AuDiropq_CREATE 1 +#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) +#define au_fset_diropq(flags, name) \ + do { (flags) |= AuDiropq_##name; } while (0) +#define au_fclr_diropq(flags, name) \ + do { (flags) &= ~AuDiropq_##name; } while (0) + +struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags); +struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, + struct au_branch *br); +struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent); + +/* real rmdir for the whiteout-ed dir */ +struct au_whtmp_rmdir { + struct inode *dir; + struct au_branch *br; + struct dentry *wh_dentry; + struct au_nhash whlist; +}; + +struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); +void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); +int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_nhash *whlist); +void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_whtmp_rmdir *args); + +/* ---------------------------------------------------------------------- */ + +static inline struct dentry *au_diropq_create(struct dentry *dentry, + aufs_bindex_t bindex) +{ + return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); +} + +static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) +{ + return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WHOUT_H__ */ diff -Naur --no-dereference a/fs/aufs/wkq.c b/fs/aufs/wkq.c --- a/fs/aufs/wkq.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/wkq.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * workqueue for asynchronous/super-io operations + * todo: try new credential scheme + */ + +#include +#include "aufs.h" + +/* internal workqueue named AUFS_WKQ_NAME */ + +static struct workqueue_struct *au_wkq; + +struct au_wkinfo { + struct work_struct wk; + struct kobject *kobj; + + unsigned int flags; /* see wkq.h */ + + au_wkq_func_t func; + void *args; + +#ifdef CONFIG_LOCKDEP + int dont_check; + struct held_lock **hlock; +#endif + + struct completion *comp; +}; + +/* ---------------------------------------------------------------------- */ +/* + * Aufs passes some operations to the workqueue such as the internal copyup. + * This scheme looks rather unnatural for LOCKDEP debugging feature, since the + * job run by workqueue depends upon the locks acquired in the other task. + * Delegating a small operation to the workqueue, aufs passes its lockdep + * information too. And the job in the workqueue restores the info in order to + * pretend as if it acquired those locks. This is just to make LOCKDEP work + * correctly and expectedly. + */ + +#ifndef CONFIG_LOCKDEP +AuStubInt0(au_wkq_lockdep_alloc, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_free, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_pre, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_post, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_init, struct au_wkinfo *wkinfo); +#else +static void au_wkq_lockdep_init(struct au_wkinfo *wkinfo) +{ + wkinfo->hlock = NULL; + wkinfo->dont_check = 0; +} + +/* + * 1: matched + * 0: unmatched + */ +static int au_wkq_lockdep_test(struct lock_class_key *key, const char *name) +{ + static DEFINE_SPINLOCK(spin); + static struct { + char *name; + struct lock_class_key *key; + } a[] = { + { .name = "&sbinfo->si_rwsem" }, + { .name = "&finfo->fi_rwsem" }, + { .name = "&dinfo->di_rwsem" }, + { .name = "&iinfo->ii_rwsem" } + }; + static int set; + int i; + + /* lockless read from 'set.' see below */ + if (set == ARRAY_SIZE(a)) { + for (i = 0; i < ARRAY_SIZE(a); i++) + if (a[i].key == key) + goto match; + goto unmatch; + } + + spin_lock(&spin); + if (set) + for (i = 0; i < ARRAY_SIZE(a); i++) + if (a[i].key == key) { + spin_unlock(&spin); + goto match; + } + for (i = 0; i < ARRAY_SIZE(a); i++) { + if (a[i].key) { + if (unlikely(a[i].key == key)) { /* rare but possible */ + spin_unlock(&spin); + goto match; + } else + continue; + } + if (strstr(a[i].name, name)) { + /* + * the order of these three lines is important for the + * lockless read above. + */ + a[i].key = key; + spin_unlock(&spin); + set++; + /* AuDbg("%d, %s\n", set, name); */ + goto match; + } + } + spin_unlock(&spin); + goto unmatch; + +match: + return 1; +unmatch: + return 0; +} + +static int au_wkq_lockdep_alloc(struct au_wkinfo *wkinfo) +{ + int err, n; + struct task_struct *curr; + struct held_lock **hl, *held_locks, *p; + + err = 0; + curr = current; + wkinfo->dont_check = lockdep_recursing(curr); + if (wkinfo->dont_check) + goto out; + n = curr->lockdep_depth; + if (!n) + goto out; + + err = -ENOMEM; + wkinfo->hlock = kmalloc_array(n + 1, sizeof(*wkinfo->hlock), GFP_NOFS); + if (unlikely(!wkinfo->hlock)) + goto out; + + err = 0; +#if 0 /* left for debugging */ + if (0 && au_debug_test()) + lockdep_print_held_locks(curr); +#endif + held_locks = curr->held_locks; + hl = wkinfo->hlock; + while (n--) { + p = held_locks++; + if (au_wkq_lockdep_test(p->instance->key, p->instance->name)) + *hl++ = p; + } + *hl = NULL; + +out: + return err; +} + +static void au_wkq_lockdep_free(struct au_wkinfo *wkinfo) +{ + au_kfree_try_rcu(wkinfo->hlock); +} + +static void au_wkq_lockdep_pre(struct au_wkinfo *wkinfo) +{ + struct held_lock *p, **hl = wkinfo->hlock; + int subclass; + + if (wkinfo->dont_check) + lockdep_off(); + if (!hl) + return; + while ((p = *hl++)) { /* assignment */ + subclass = lockdep_hlock_class(p)->subclass; + /* AuDbg("%s, %d\n", p->instance->name, subclass); */ + if (p->read) + rwsem_acquire_read(p->instance, subclass, 0, + /*p->acquire_ip*/_RET_IP_); + else + rwsem_acquire(p->instance, subclass, 0, + /*p->acquire_ip*/_RET_IP_); + } +} + +static void au_wkq_lockdep_post(struct au_wkinfo *wkinfo) +{ + struct held_lock *p, **hl = wkinfo->hlock; + + if (wkinfo->dont_check) + lockdep_on(); + if (!hl) + return; + while ((p = *hl++)) /* assignment */ + rwsem_release(p->instance, /*p->acquire_ip*/_RET_IP_); +} +#endif + +static void wkq_func(struct work_struct *wk) +{ + struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); + + AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); + AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); + + au_wkq_lockdep_pre(wkinfo); + wkinfo->func(wkinfo->args); + au_wkq_lockdep_post(wkinfo); + if (au_ftest_wkq(wkinfo->flags, WAIT)) + complete(wkinfo->comp); + else { + kobject_put(wkinfo->kobj); + module_put(THIS_MODULE); /* todo: ?? */ + au_kfree_rcu(wkinfo); + } +} + +/* + * Since struct completion is large, try allocating it dynamically. + */ +#define AuWkqCompDeclare(name) struct completion *comp = NULL + +static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) +{ + *comp = kmalloc(sizeof(**comp), GFP_NOFS); + if (*comp) { + init_completion(*comp); + wkinfo->comp = *comp; + return 0; + } + return -ENOMEM; +} + +static void au_wkq_comp_free(struct completion *comp) +{ + au_kfree_rcu(comp); +} + +static void au_wkq_run(struct au_wkinfo *wkinfo) +{ + if (au_ftest_wkq(wkinfo->flags, NEST)) { + if (au_wkq_test()) { + AuWarn1("wkq from wkq, unless silly-rename on NFS," + " due to a dead dir by UDBA," + " or async xino write?\n"); + AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); + } + } else + au_dbg_verify_kthread(); + + if (au_ftest_wkq(wkinfo->flags, WAIT)) { + INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); + queue_work(au_wkq, &wkinfo->wk); + } else { + INIT_WORK(&wkinfo->wk, wkq_func); + schedule_work(&wkinfo->wk); + } +} + +/* + * Be careful. It is easy to make deadlock happen. + * processA: lock, wkq and wait + * processB: wkq and wait, lock in wkq + * --> deadlock + */ +int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) +{ + int err; + AuWkqCompDeclare(comp); + struct au_wkinfo wkinfo = { + .flags = flags, + .func = func, + .args = args + }; + + err = au_wkq_comp_alloc(&wkinfo, &comp); + if (unlikely(err)) + goto out; + err = au_wkq_lockdep_alloc(&wkinfo); + if (unlikely(err)) + goto out_comp; + if (!err) { + au_wkq_run(&wkinfo); + /* no timeout, no interrupt */ + wait_for_completion(wkinfo.comp); + } + au_wkq_lockdep_free(&wkinfo); + +out_comp: + au_wkq_comp_free(comp); +out: + destroy_work_on_stack(&wkinfo.wk); + return err; +} + +/* + * Note: dget/dput() in func for aufs dentries are not supported. It will be a + * problem in a concurrent umounting. + */ +int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags) +{ + int err; + struct au_wkinfo *wkinfo; + + atomic_inc(&au_sbi(sb)->si_nowait.nw_len); + + /* + * wkq_func() must free this wkinfo. + * it highly depends upon the implementation of workqueue. + */ + err = 0; + wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); + if (wkinfo) { + wkinfo->kobj = &au_sbi(sb)->si_kobj; + wkinfo->flags = flags & ~AuWkq_WAIT; + wkinfo->func = func; + wkinfo->args = args; + wkinfo->comp = NULL; + au_wkq_lockdep_init(wkinfo); + kobject_get(wkinfo->kobj); + __module_get(THIS_MODULE); /* todo: ?? */ + + au_wkq_run(wkinfo); + } else { + err = -ENOMEM; + au_nwt_done(&au_sbi(sb)->si_nowait); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_nwt_init(struct au_nowait_tasks *nwt) +{ + atomic_set(&nwt->nw_len, 0); + /* smp_mb(); */ /* atomic_set */ + init_waitqueue_head(&nwt->nw_wq); +} + +void au_wkq_fin(void) +{ + destroy_workqueue(au_wkq); +} + +int __init au_wkq_init(void) +{ + int err; + + err = 0; + au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); + if (IS_ERR(au_wkq)) + err = PTR_ERR(au_wkq); + else if (!au_wkq) + err = -ENOMEM; + + return err; +} diff -Naur --no-dereference a/fs/aufs/wkq.h b/fs/aufs/wkq.h --- a/fs/aufs/wkq.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/wkq.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * workqueue for asynchronous/super-io operations + * todo: try new credentials management scheme + */ + +#ifndef __AUFS_WKQ_H__ +#define __AUFS_WKQ_H__ + +#ifdef __KERNEL__ + +#include + +struct super_block; + +/* ---------------------------------------------------------------------- */ + +/* + * in the next operation, wait for the 'nowait' tasks in system-wide workqueue + */ +struct au_nowait_tasks { + atomic_t nw_len; + wait_queue_head_t nw_wq; +}; + +/* ---------------------------------------------------------------------- */ + +typedef void (*au_wkq_func_t)(void *args); + +/* wkq flags */ +#define AuWkq_WAIT 1 +#define AuWkq_NEST (1 << 1) +#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) +#define au_fset_wkq(flags, name) \ + do { (flags) |= AuWkq_##name; } while (0) +#define au_fclr_wkq(flags, name) \ + do { (flags) &= ~AuWkq_##name; } while (0) + +/* wkq.c */ +int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); +int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags); +void au_nwt_init(struct au_nowait_tasks *nwt); +int __init au_wkq_init(void); +void au_wkq_fin(void); + +/* ---------------------------------------------------------------------- */ + +static inline int au_wkq_test(void) +{ + return current->flags & PF_WQ_WORKER; +} + +static inline int au_wkq_wait(au_wkq_func_t func, void *args) +{ + return au_wkq_do_wait(AuWkq_WAIT, func, args); +} + +static inline void au_nwt_done(struct au_nowait_tasks *nwt) +{ + if (atomic_dec_and_test(&nwt->nw_len)) + wake_up_all(&nwt->nw_wq); +} + +static inline int au_nwt_flush(struct au_nowait_tasks *nwt) +{ + wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); + return 0; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WKQ_H__ */ diff -Naur --no-dereference a/fs/aufs/xattr.c b/fs/aufs/xattr.c --- a/fs/aufs/xattr.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/xattr.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * handling xattr functions + */ + +#include +#include +#include +#include "aufs.h" + +static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) +{ + if (!ignore_flags) + goto out; + switch (err) { + case -ENOMEM: + case -EDQUOT: + goto out; + } + + if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { + err = 0; + goto out; + } + +#define cmp(brattr, prefix) do { \ + if (!strncmp(name, XATTR_##prefix##_PREFIX, \ + XATTR_##prefix##_PREFIX_LEN)) { \ + if (ignore_flags & AuBrAttr_ICEX_##brattr) \ + err = 0; \ + goto out; \ + } \ + } while (0) + + cmp(SEC, SECURITY); + cmp(SYS, SYSTEM); + cmp(TR, TRUSTED); + cmp(USR, USER); +#undef cmp + + if (ignore_flags & AuBrAttr_ICEX_OTH) + err = 0; + +out: + return err; +} + +static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; + +static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, + char *name, char **buf, unsigned int ignore_flags, + unsigned int verbose) +{ + int err; + ssize_t ssz; + struct inode *h_idst; + + ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); + err = ssz; + if (unlikely(err <= 0)) { + if (err == -ENODATA + || (err == -EOPNOTSUPP + && ((ignore_flags & au_xattr_out_of_list) + || (au_test_nfs_noacl(d_inode(h_src)) + && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) + || !strcmp(name, + XATTR_NAME_POSIX_ACL_DEFAULT)))) + )) + err = 0; + if (err && (verbose || au_debug_test())) + pr_err("%s, err %d\n", name, err); + goto out; + } + + /* unlock it temporary */ + h_idst = d_inode(h_dst); + inode_unlock(h_idst); + err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); + inode_lock_nested(h_idst, AuLsc_I_CHILD2); + if (unlikely(err)) { + if (verbose || au_debug_test()) + pr_err("%s, err %d\n", name, err); + err = au_xattr_ignore(err, name, ignore_flags); + } + +out: + return err; +} + +int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, + unsigned int verbose) +{ + int err, unlocked, acl_access, acl_default; + ssize_t ssz; + struct inode *h_isrc, *h_idst; + char *value, *p, *o, *e; + + /* try stopping to update the source inode while we are referencing */ + /* there should not be the parent-child relationship between them */ + h_isrc = d_inode(h_src); + h_idst = d_inode(h_dst); + inode_unlock(h_idst); + inode_lock_shared_nested(h_isrc, AuLsc_I_CHILD); + inode_lock_nested(h_idst, AuLsc_I_CHILD2); + unlocked = 0; + + /* some filesystems don't list POSIX ACL, for example tmpfs */ + ssz = vfs_listxattr(h_src, NULL, 0); + err = ssz; + if (unlikely(err < 0)) { + AuTraceErr(err); + if (err == -ENODATA + || err == -EOPNOTSUPP) + err = 0; /* ignore */ + goto out; + } + + err = 0; + p = NULL; + o = NULL; + if (ssz) { + err = -ENOMEM; + p = kmalloc(ssz, GFP_NOFS); + o = p; + if (unlikely(!p)) + goto out; + err = vfs_listxattr(h_src, p, ssz); + } + inode_unlock_shared(h_isrc); + unlocked = 1; + AuDbg("err %d, ssz %zd\n", err, ssz); + if (unlikely(err < 0)) + goto out_free; + + err = 0; + e = p + ssz; + value = NULL; + acl_access = 0; + acl_default = 0; + while (!err && p < e) { + acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); + acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) + - 1); + err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, + verbose); + p += strlen(p) + 1; + } + AuTraceErr(err); + ignore_flags |= au_xattr_out_of_list; + if (!err && !acl_access) { + err = au_do_cpup_xattr(h_dst, h_src, + XATTR_NAME_POSIX_ACL_ACCESS, &value, + ignore_flags, verbose); + AuTraceErr(err); + } + if (!err && !acl_default) { + err = au_do_cpup_xattr(h_dst, h_src, + XATTR_NAME_POSIX_ACL_DEFAULT, &value, + ignore_flags, verbose); + AuTraceErr(err); + } + + au_kfree_try_rcu(value); + +out_free: + au_kfree_try_rcu(o); +out: + if (!unlocked) + inode_unlock_shared(h_isrc); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_smack_reentering(struct super_block *sb) +{ +#if IS_ENABLED(CONFIG_SECURITY_SMACK) || IS_ENABLED(CONFIG_SECURITY_SELINUX) + /* + * as a part of lookup, smack_d_instantiate() is called, and it calls + * i_op->getxattr(). ouch. + */ + return si_pid_test(sb); +#else + return 0; +#endif +} + +enum { + AU_XATTR_LIST, + AU_XATTR_GET +}; + +struct au_lgxattr { + int type; + union { + struct { + char *list; + size_t size; + } list; + struct { + const char *name; + void *value; + size_t size; + } get; + } u; +}; + +static ssize_t au_lgxattr(struct dentry *dentry, struct inode *inode, + struct au_lgxattr *arg) +{ + ssize_t err; + int reenter; + struct path h_path; + struct super_block *sb; + + sb = dentry->d_sb; + reenter = au_smack_reentering(sb); + if (!reenter) { + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + } + err = au_h_path_getattr(dentry, inode, /*force*/1, &h_path, reenter); + if (unlikely(err)) + goto out_si; + if (unlikely(!h_path.dentry)) + /* illegally overlapped or something */ + goto out_di; /* pretending success */ + + /* always topmost entry only */ + switch (arg->type) { + case AU_XATTR_LIST: + err = vfs_listxattr(h_path.dentry, + arg->u.list.list, arg->u.list.size); + break; + case AU_XATTR_GET: + AuDebugOn(d_is_negative(h_path.dentry)); + err = vfs_getxattr(h_path.dentry, + arg->u.get.name, arg->u.get.value, + arg->u.get.size); + break; + } + +out_di: + if (!reenter) + di_read_unlock(dentry, AuLock_IR); +out_si: + if (!reenter) + si_read_unlock(sb); +out: + AuTraceErr(err); + return err; +} + +ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) +{ + struct au_lgxattr arg = { + .type = AU_XATTR_LIST, + .u.list = { + .list = list, + .size = size + }, + }; + + return au_lgxattr(dentry, /*inode*/NULL, &arg); +} + +static ssize_t au_getxattr(struct dentry *dentry, struct inode *inode, + const char *name, void *value, size_t size) +{ + struct au_lgxattr arg = { + .type = AU_XATTR_GET, + .u.get = { + .name = name, + .value = value, + .size = size + }, + }; + + return au_lgxattr(dentry, inode, &arg); +} + +static int au_setxattr(struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + struct au_sxattr arg = { + .type = AU_XATTR_SET, + .u.set = { + .name = name, + .value = value, + .size = size, + .flags = flags + }, + }; + + return au_sxattr(dentry, inode, &arg); +} + +/* ---------------------------------------------------------------------- */ + +static int au_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + return au_getxattr(dentry, inode, name, buffer, size); +} + +static int au_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + return au_setxattr(dentry, inode, name, value, size, flags); +} + +static const struct xattr_handler au_xattr_handler = { + .name = "", + .prefix = "", + .get = au_xattr_get, + .set = au_xattr_set +}; + +static const struct xattr_handler *au_xattr_handlers[] = { +#ifdef CONFIG_FS_POSIX_ACL + &posix_acl_access_xattr_handler, + &posix_acl_default_xattr_handler, +#endif + &au_xattr_handler, /* must be last */ + NULL +}; + +void au_xattr_init(struct super_block *sb) +{ + sb->s_xattr = au_xattr_handlers; +} diff -Naur --no-dereference a/fs/aufs/xino.c b/fs/aufs/xino.c --- a/fs/aufs/xino.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/aufs/xino.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1926 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * external inode number translation table and bitmap + * + * things to consider + * - the lifetime + * + au_xino object + * + XINO files (xino, xib, xigen) + * + dynamic debugfs entries (xiN) + * + static debugfs entries (xib, xigen) + * + static sysfs entry (xi_path) + * - several entry points to handle them. + * + mount(2) without xino option (default) + * + mount(2) with xino option + * + mount(2) with noxino option + * + umount(2) + * + remount with add/del branches + * + remount with xino/noxino options + */ + +#include +#include +#include "aufs.h" + +static aufs_bindex_t sbr_find_shared(struct super_block *sb, aufs_bindex_t btop, + aufs_bindex_t bbot, + struct super_block *h_sb) +{ + /* todo: try binary-search if the branches are many */ + for (; btop <= bbot; btop++) + if (h_sb == au_sbr_sb(sb, btop)) + return btop; + return -1; +} + +/* + * find another branch who is on the same filesystem of the specified + * branch{@btgt}. search until @bbot. + */ +static aufs_bindex_t is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, + aufs_bindex_t bbot) +{ + aufs_bindex_t bindex; + struct super_block *tgt_sb; + + tgt_sb = au_sbr_sb(sb, btgt); + bindex = sbr_find_shared(sb, /*btop*/0, btgt - 1, tgt_sb); + if (bindex < 0) + bindex = sbr_find_shared(sb, btgt + 1, bbot, tgt_sb); + + return bindex; +} + +/* ---------------------------------------------------------------------- */ + +/* + * stop unnecessary notify events at creating xino files + */ + +aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry) +{ + aufs_bindex_t bfound, bindex, bbot; + struct dentry *parent; + struct au_branch *br; + + bfound = -1; + parent = dentry->d_parent; /* safe d_parent access */ + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_dentry(br) == parent) { + bfound = bindex; + break; + } + } + + AuDbg("bfound b%d\n", bfound); + return bfound; +} + +struct au_xino_lock_dir { + struct au_hinode *hdir; + struct dentry *parent; + struct inode *dir; +}; + +static struct dentry *au_dget_parent_lock(struct dentry *dentry, + unsigned int lsc) +{ + struct dentry *parent; + struct inode *dir; + + parent = dget_parent(dentry); + dir = d_inode(parent); + inode_lock_nested(dir, lsc); +#if 0 /* it should not happen */ + spin_lock(&dentry->d_lock); + if (unlikely(dentry->d_parent != parent)) { + spin_unlock(&dentry->d_lock); + inode_unlock(dir); + dput(parent); + parent = NULL; + goto out; + } + spin_unlock(&dentry->d_lock); + +out: +#endif + return parent; +} + +static void au_xino_lock_dir(struct super_block *sb, struct path *xipath, + struct au_xino_lock_dir *ldir) +{ + aufs_bindex_t bindex; + + ldir->hdir = NULL; + bindex = au_xi_root(sb, xipath->dentry); + if (bindex >= 0) { + /* rw branch root */ + ldir->hdir = au_hi(d_inode(sb->s_root), bindex); + au_hn_inode_lock_nested(ldir->hdir, AuLsc_I_PARENT); + } else { + /* other */ + ldir->parent = au_dget_parent_lock(xipath->dentry, + AuLsc_I_PARENT); + ldir->dir = d_inode(ldir->parent); + } +} + +static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) +{ + if (ldir->hdir) + au_hn_inode_unlock(ldir->hdir); + else { + inode_unlock(ldir->dir); + dput(ldir->parent); + } +} + +/* ---------------------------------------------------------------------- */ + +/* + * create and set a new xino file + */ +struct file *au_xino_create(struct super_block *sb, char *fpath, int silent, + int wbrtop) +{ + struct file *file; + struct dentry *h_parent, *d; + struct inode *h_dir, *inode; + int err; + static DEFINE_MUTEX(mtx); + + /* + * at mount-time, and the xino file is the default path, + * hnotify is disabled so we have no notify events to ignore. + * when a user specified the xino, we cannot get au_hdir to be ignored. + */ + if (!wbrtop) + mutex_lock(&mtx); + file = vfsub_filp_open(fpath, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE + /* | __FMODE_NONOTIFY */, + 0666); + if (IS_ERR(file)) { + if (!wbrtop) + mutex_unlock(&mtx); + if (!silent) + pr_err("open %s(%ld)\n", fpath, PTR_ERR(file)); + return file; + } + + /* keep file count */ + err = 0; + d = file->f_path.dentry; + h_parent = au_dget_parent_lock(d, AuLsc_I_PARENT); + if (!wbrtop) + mutex_unlock(&mtx); + /* mnt_want_write() is unnecessary here */ + h_dir = d_inode(h_parent); + inode = file_inode(file); + /* no delegation since it is just created */ + if (inode->i_nlink) + err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, + /*force*/0); + inode_unlock(h_dir); + dput(h_parent); + if (unlikely(err)) { + if (!silent) + pr_err("unlink %s(%d)\n", fpath, err); + goto out; + } + + err = -EINVAL; + if (unlikely(sb == d->d_sb)) { + if (!silent) + pr_err("%s must be outside\n", fpath); + goto out; + } + if (unlikely(au_test_fs_bad_xino(d->d_sb))) { + if (!silent) + pr_err("xino doesn't support %s(%s)\n", + fpath, au_sbtype(d->d_sb)); + goto out; + } + return file; /* success */ + +out: + fput(file); + file = ERR_PTR(err); + return file; +} + +/* + * create a new xinofile at the same place/path as @base. + */ +struct file *au_xino_create2(struct super_block *sb, struct path *base, + struct file *copy_src) +{ + struct file *file; + struct dentry *dentry; + struct inode *dir, *delegated; + struct qstr *name; + struct path ppath, path; + int err, do_unlock; + struct au_xino_lock_dir ldir; + + do_unlock = 1; + au_xino_lock_dir(sb, base, &ldir); + dentry = base->dentry; + ppath.dentry = dentry->d_parent; /* dir inode is locked */ + ppath.mnt = base->mnt; + dir = d_inode(ppath.dentry); + IMustLock(dir); + + name = &dentry->d_name; + path.dentry = vfsub_lookup_one_len(name->name, &ppath, name->len); + if (IS_ERR(path.dentry)) { + file = (void *)path.dentry; + pr_err("%pd lookup err %ld\n", dentry, PTR_ERR(path.dentry)); + goto out; + } + + /* no need to mnt_want_write() since we call dentry_open() later */ + err = vfs_create(dir, path.dentry, 0666, NULL); + if (unlikely(err)) { + file = ERR_PTR(err); + pr_err("%pd create err %d\n", dentry, err); + goto out_dput; + } + + path.mnt = base->mnt; + file = vfsub_dentry_open(&path, + O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE + /* | __FMODE_NONOTIFY */); + if (IS_ERR(file)) { + pr_err("%pd open err %ld\n", dentry, PTR_ERR(file)); + goto out_dput; + } + + delegated = NULL; + err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); + au_xino_unlock_dir(&ldir); + do_unlock = 0; + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) { + pr_err("%pd unlink err %d\n", dentry, err); + goto out_fput; + } + + if (copy_src) { + /* no one can touch copy_src xino */ + err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); + if (unlikely(err)) { + pr_err("%pd copy err %d\n", dentry, err); + goto out_fput; + } + } + goto out_dput; /* success */ + +out_fput: + fput(file); + file = ERR_PTR(err); +out_dput: + dput(path.dentry); +out: + if (do_unlock) + au_xino_unlock_dir(&ldir); + return file; +} + +struct file *au_xino_file1(struct au_xino *xi) +{ + struct file *file; + unsigned int u, nfile; + + file = NULL; + nfile = xi->xi_nfile; + for (u = 0; u < nfile; u++) { + file = xi->xi_file[u]; + if (file) + break; + } + + return file; +} + +static int au_xino_file_set(struct au_xino *xi, int idx, struct file *file) +{ + int err; + struct file *f; + void *p; + + if (file) + get_file(file); + + err = 0; + f = NULL; + if (idx < xi->xi_nfile) { + f = xi->xi_file[idx]; + if (f) + fput(f); + } else { + p = au_kzrealloc(xi->xi_file, + sizeof(*xi->xi_file) * xi->xi_nfile, + sizeof(*xi->xi_file) * (idx + 1), + GFP_NOFS, /*may_shrink*/0); + if (p) { + MtxMustLock(&xi->xi_mtx); + xi->xi_file = p; + xi->xi_nfile = idx + 1; + } else { + err = -ENOMEM; + if (file) + fput(file); + goto out; + } + } + xi->xi_file[idx] = file; + +out: + return err; +} + +/* + * if @xinew->xi is not set, then create new xigen file. + */ +struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew) +{ + struct file *file; + int err; + + SiMustAnyLock(sb); + + file = au_xino_create2(sb, xinew->base, xinew->copy_src); + if (IS_ERR(file)) { + err = PTR_ERR(file); + pr_err("%s[%d], err %d\n", + xinew->xi ? "xino" : "xigen", + xinew->idx, err); + goto out; + } + + if (xinew->xi) + err = au_xino_file_set(xinew->xi, xinew->idx, file); + else { + BUG(); + /* todo: make xigen file an array */ + /* err = au_xigen_file_set(sb, xinew->idx, file); */ + } + fput(file); + if (unlikely(err)) + file = ERR_PTR(err); + +out: + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* + * truncate xino files + */ +static int au_xino_do_trunc(struct super_block *sb, aufs_bindex_t bindex, + int idx, struct kstatfs *st) +{ + int err; + blkcnt_t blocks; + struct file *file, *new_xino; + struct au_xi_new xinew = { + .idx = idx + }; + + err = 0; + xinew.xi = au_sbr(sb, bindex)->br_xino; + file = au_xino_file(xinew.xi, idx); + if (!file) + goto out; + + xinew.base = &file->f_path; + err = vfs_statfs(xinew.base, st); + if (unlikely(err)) { + AuErr1("statfs err %d, ignored\n", err); + err = 0; + goto out; + } + + blocks = file_inode(file)->i_blocks; + pr_info("begin truncating xino(b%d-%d), ib%llu, %llu/%llu free blks\n", + bindex, idx, (u64)blocks, st->f_bfree, st->f_blocks); + + xinew.copy_src = file; + new_xino = au_xi_new(sb, &xinew); + if (IS_ERR(new_xino)) { + err = PTR_ERR(new_xino); + pr_err("xino(b%d-%d), err %d, ignored\n", bindex, idx, err); + goto out; + } + + err = vfs_statfs(&new_xino->f_path, st); + if (!err) + pr_info("end truncating xino(b%d-%d), ib%llu, %llu/%llu free blks\n", + bindex, idx, (u64)file_inode(new_xino)->i_blocks, + st->f_bfree, st->f_blocks); + else { + AuErr1("statfs err %d, ignored\n", err); + err = 0; + } + +out: + return err; +} + +int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin) +{ + int err, i; + unsigned long jiffy; + aufs_bindex_t bbot; + struct kstatfs *st; + struct au_branch *br; + struct au_xino *xi; + + err = -ENOMEM; + st = kmalloc(sizeof(*st), GFP_NOFS); + if (unlikely(!st)) + goto out; + + err = -EINVAL; + bbot = au_sbbot(sb); + if (unlikely(bindex < 0 || bbot < bindex)) + goto out_st; + + err = 0; + jiffy = jiffies; + br = au_sbr(sb, bindex); + xi = br->br_xino; + for (i = idx_begin; !err && i < xi->xi_nfile; i++) + err = au_xino_do_trunc(sb, bindex, i, st); + if (!err) + au_sbi(sb)->si_xino_jiffy = jiffy; + +out_st: + au_kfree_rcu(st); +out: + return err; +} + +struct xino_do_trunc_args { + struct super_block *sb; + struct au_branch *br; + int idx; +}; + +static void xino_do_trunc(void *_args) +{ + struct xino_do_trunc_args *args = _args; + struct super_block *sb; + struct au_branch *br; + struct inode *dir; + int err, idx; + aufs_bindex_t bindex; + + err = 0; + sb = args->sb; + dir = d_inode(sb->s_root); + br = args->br; + idx = args->idx; + + si_noflush_write_lock(sb); + ii_read_lock_parent(dir); + bindex = au_br_index(sb, br->br_id); + err = au_xino_trunc(sb, bindex, idx); + ii_read_unlock(dir); + if (unlikely(err)) + pr_warn("err b%d, (%d)\n", bindex, err); + atomic_dec(&br->br_xino->xi_truncating); + au_lcnt_dec(&br->br_count); + si_write_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); + au_kfree_rcu(args); +} + +/* + * returns the index in the xi_file array whose corresponding file is necessary + * to truncate, or -1 which means no need to truncate. + */ +static int xino_trunc_test(struct super_block *sb, struct au_branch *br) +{ + int err; + unsigned int u; + struct kstatfs st; + struct au_sbinfo *sbinfo; + struct au_xino *xi; + struct file *file; + + /* todo: si_xino_expire and the ratio should be customizable */ + sbinfo = au_sbi(sb); + if (time_before(jiffies, + sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) + return -1; + + /* truncation border */ + xi = br->br_xino; + for (u = 0; u < xi->xi_nfile; u++) { + file = au_xino_file(xi, u); + if (!file) + continue; + + err = vfs_statfs(&file->f_path, &st); + if (unlikely(err)) { + AuErr1("statfs err %d, ignored\n", err); + return -1; + } + if (div64_u64(st.f_bfree * 100, st.f_blocks) + >= AUFS_XINO_DEF_TRUNC) + return u; + } + + return -1; +} + +static void xino_try_trunc(struct super_block *sb, struct au_branch *br) +{ + int idx; + struct xino_do_trunc_args *args; + int wkq_err; + + idx = xino_trunc_test(sb, br); + if (idx < 0) + return; + + if (atomic_inc_return(&br->br_xino->xi_truncating) > 1) + goto out; + + /* lock and kfree() will be called in trunc_xino() */ + args = kmalloc(sizeof(*args), GFP_NOFS); + if (unlikely(!args)) { + AuErr1("no memory\n"); + goto out; + } + + au_lcnt_inc(&br->br_count); + args->sb = sb; + args->br = br; + args->idx = idx; + wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); + if (!wkq_err) + return; /* success */ + + pr_err("wkq %d\n", wkq_err); + au_lcnt_dec(&br->br_count); + au_kfree_rcu(args); + +out: + atomic_dec(&br->br_xino->xi_truncating); +} + +/* ---------------------------------------------------------------------- */ + +struct au_xi_calc { + int idx; + loff_t pos; +}; + +static void au_xi_calc(struct super_block *sb, ino_t h_ino, + struct au_xi_calc *calc) +{ + loff_t maxent; + + maxent = au_xi_maxent(sb); + calc->idx = div64_u64_rem(h_ino, maxent, &calc->pos); + calc->pos *= sizeof(ino_t); +} + +static int au_xino_do_new_async(struct super_block *sb, struct au_branch *br, + struct au_xi_calc *calc) +{ + int err; + struct file *file; + struct au_xino *xi = br->br_xino; + struct au_xi_new xinew = { + .xi = xi + }; + + SiMustAnyLock(sb); + + err = 0; + if (!xi) + goto out; + + mutex_lock(&xi->xi_mtx); + file = au_xino_file(xi, calc->idx); + if (file) + goto out_mtx; + + file = au_xino_file(xi, /*idx*/-1); + AuDebugOn(!file); + xinew.idx = calc->idx; + xinew.base = &file->f_path; + /* xinew.copy_src = NULL; */ + file = au_xi_new(sb, &xinew); + if (IS_ERR(file)) + err = PTR_ERR(file); + +out_mtx: + mutex_unlock(&xi->xi_mtx); +out: + return err; +} + +struct au_xino_do_new_async_args { + struct super_block *sb; + struct au_branch *br; + struct au_xi_calc calc; + ino_t ino; +}; + +struct au_xi_writing { + struct hlist_bl_node node; + ino_t h_ino, ino; +}; + +static int au_xino_do_write(struct file *file, struct au_xi_calc *calc, + ino_t ino); + +static void au_xino_call_do_new_async(void *args) +{ + struct au_xino_do_new_async_args *a = args; + struct au_branch *br; + struct super_block *sb; + struct au_sbinfo *sbi; + struct inode *root; + struct file *file; + struct au_xi_writing *del, *p; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + int err; + + br = a->br; + sb = a->sb; + sbi = au_sbi(sb); + si_noflush_read_lock(sb); + root = d_inode(sb->s_root); + ii_read_lock_child(root); + err = au_xino_do_new_async(sb, br, &a->calc); + if (unlikely(err)) { + AuIOErr("err %d\n", err); + goto out; + } + + file = au_xino_file(br->br_xino, a->calc.idx); + AuDebugOn(!file); + err = au_xino_do_write(file, &a->calc, a->ino); + if (unlikely(err)) { + AuIOErr("err %d\n", err); + goto out; + } + + del = NULL; + hbl = &br->br_xino->xi_writing; + hlist_bl_lock(hbl); + au_hbl_for_each(pos, hbl) { + p = container_of(pos, struct au_xi_writing, node); + if (p->ino == a->ino) { + del = p; + hlist_bl_del(&p->node); + break; + } + } + hlist_bl_unlock(hbl); + au_kfree_rcu(del); + +out: + au_lcnt_dec(&br->br_count); + ii_read_unlock(root); + si_read_unlock(sb); + au_nwt_done(&sbi->si_nowait); + au_kfree_rcu(a); +} + +/* + * create a new xino file asynchronously + */ +static int au_xino_new_async(struct super_block *sb, struct au_branch *br, + struct au_xi_calc *calc, ino_t ino) +{ + int err; + struct au_xino_do_new_async_args *arg; + + err = -ENOMEM; + arg = kmalloc(sizeof(*arg), GFP_NOFS); + if (unlikely(!arg)) + goto out; + + arg->sb = sb; + arg->br = br; + arg->calc = *calc; + arg->ino = ino; + au_lcnt_inc(&br->br_count); + err = au_wkq_nowait(au_xino_call_do_new_async, arg, sb, AuWkq_NEST); + if (unlikely(err)) { + pr_err("wkq %d\n", err); + au_lcnt_dec(&br->br_count); + au_kfree_rcu(arg); + } + +out: + return err; +} + +/* + * read @ino from xinofile for the specified branch{@sb, @bindex} + * at the position of @h_ino. + */ +int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t *ino) +{ + int err; + ssize_t sz; + struct au_xi_calc calc; + struct au_sbinfo *sbinfo; + struct file *file; + struct au_xino *xi; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_xi_writing *p; + + *ino = 0; + if (!au_opt_test(au_mntflags(sb), XINO)) + return 0; /* no xino */ + + err = 0; + au_xi_calc(sb, h_ino, &calc); + xi = au_sbr(sb, bindex)->br_xino; + file = au_xino_file(xi, calc.idx); + if (!file) { + hbl = &xi->xi_writing; + hlist_bl_lock(hbl); + au_hbl_for_each(pos, hbl) { + p = container_of(pos, struct au_xi_writing, node); + if (p->h_ino == h_ino) { + AuDbg("hi%llu, i%llu, found\n", + (u64)p->h_ino, (u64)p->ino); + *ino = p->ino; + break; + } + } + hlist_bl_unlock(hbl); + return 0; + } else if (vfsub_f_size_read(file) < calc.pos + sizeof(*ino)) + return 0; /* no xino */ + + sbinfo = au_sbi(sb); + sz = xino_fread(file, ino, sizeof(*ino), &calc.pos); + if (sz == sizeof(*ino)) + return 0; /* success */ + + err = sz; + if (unlikely(sz >= 0)) { + err = -EIO; + AuIOErr("xino read error (%zd)\n", sz); + } + return err; +} + +static int au_xino_do_write(struct file *file, struct au_xi_calc *calc, + ino_t ino) +{ + ssize_t sz; + + sz = xino_fwrite(file, &ino, sizeof(ino), &calc->pos); + if (sz == sizeof(ino)) + return 0; /* success */ + + AuIOErr("write failed (%zd)\n", sz); + return -EIO; +} + +/* + * write @ino to the xinofile for the specified branch{@sb, @bindex} + * at the position of @h_ino. + * even if @ino is zero, it is written to the xinofile and means no entry. + * if the size of the xino file on a specific filesystem exceeds the watermark, + * try truncating it. + */ +int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t ino) +{ + int err; + unsigned int mnt_flags; + struct au_xi_calc calc; + struct file *file; + struct au_branch *br; + struct au_xino *xi; + struct au_xi_writing *p; + + SiMustAnyLock(sb); + + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, XINO)) + return 0; + + au_xi_calc(sb, h_ino, &calc); + br = au_sbr(sb, bindex); + xi = br->br_xino; + file = au_xino_file(xi, calc.idx); + if (!file) { + /* store the inum pair into the list */ + p = kmalloc(sizeof(*p), GFP_NOFS | __GFP_NOFAIL); + p->h_ino = h_ino; + p->ino = ino; + au_hbl_add(&p->node, &xi->xi_writing); + + /* create and write a new xino file asynchronously */ + err = au_xino_new_async(sb, br, &calc, ino); + if (!err) + return 0; /* success */ + goto out; + } + + err = au_xino_do_write(file, &calc, ino); + if (!err) { + br = au_sbr(sb, bindex); + if (au_opt_test(mnt_flags, TRUNC_XINO) + && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + return 0; /* success */ + } + +out: + AuIOErr("write failed (%d)\n", err); + return -EIO; +} + +static ssize_t xino_fread_wkq(struct file *file, void *buf, size_t size, + loff_t *pos); + +/* todo: unnecessary to support mmap_sem since kernel-space? */ +ssize_t xino_fread(struct file *file, void *kbuf, size_t size, loff_t *pos) +{ + ssize_t err; + int i; + const int prevent_endless = 10; + + i = 0; + do { + err = vfsub_read_k(file, kbuf, size, pos); + if (err == -EINTR + && !au_wkq_test() + && fatal_signal_pending(current)) { + err = xino_fread_wkq(file, kbuf, size, pos); + BUG_ON(err == -EINTR); + } + } while (i++ < prevent_endless + && (err == -EAGAIN || err == -EINTR)); + +#if 0 /* reserved for future use */ + if (err > 0) + fsnotify_access(file->f_path.dentry); +#endif + + return err; +} + +struct xino_fread_args { + ssize_t *errp; + struct file *file; + void *buf; + size_t size; + loff_t *pos; +}; + +static void call_xino_fread(void *args) +{ + struct xino_fread_args *a = args; + *a->errp = xino_fread(a->file, a->buf, a->size, a->pos); +} + +static ssize_t xino_fread_wkq(struct file *file, void *buf, size_t size, + loff_t *pos) +{ + ssize_t err; + int wkq_err; + struct xino_fread_args args = { + .errp = &err, + .file = file, + .buf = buf, + .size = size, + .pos = pos + }; + + wkq_err = au_wkq_wait(call_xino_fread, &args); + if (unlikely(wkq_err)) + err = wkq_err; + + return err; +} + +static ssize_t xino_fwrite_wkq(struct file *file, void *buf, size_t size, + loff_t *pos); + +static ssize_t do_xino_fwrite(struct file *file, void *kbuf, size_t size, + loff_t *pos) +{ + ssize_t err; + int i; + const int prevent_endless = 10; + + i = 0; + do { + err = vfsub_write_k(file, kbuf, size, pos); + if (err == -EINTR + && !au_wkq_test() + && fatal_signal_pending(current)) { + err = xino_fwrite_wkq(file, kbuf, size, pos); + BUG_ON(err == -EINTR); + } + } while (i++ < prevent_endless + && (err == -EAGAIN || err == -EINTR)); + +#if 0 /* reserved for future use */ + if (err > 0) + fsnotify_modify(file->f_path.dentry); +#endif + + return err; +} + +struct do_xino_fwrite_args { + ssize_t *errp; + struct file *file; + void *buf; + size_t size; + loff_t *pos; +}; + +static void call_do_xino_fwrite(void *args) +{ + struct do_xino_fwrite_args *a = args; + *a->errp = do_xino_fwrite(a->file, a->buf, a->size, a->pos); +} + +static ssize_t xino_fwrite_wkq(struct file *file, void *buf, size_t size, + loff_t *pos) +{ + ssize_t err; + int wkq_err; + struct do_xino_fwrite_args args = { + .errp = &err, + .file = file, + .buf = buf, + .size = size, + .pos = pos + }; + + /* + * it breaks RLIMIT_FSIZE and normal user's limit, + * users should care about quota and real 'filesystem full.' + */ + wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); + if (unlikely(wkq_err)) + err = wkq_err; + + return err; +} + +ssize_t xino_fwrite(struct file *file, void *buf, size_t size, loff_t *pos) +{ + ssize_t err; + + if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { + lockdep_off(); + err = do_xino_fwrite(file, buf, size, pos); + lockdep_on(); + } else { + lockdep_off(); + err = xino_fwrite_wkq(file, buf, size, pos); + lockdep_on(); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * inode number bitmap + */ +static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; +static ino_t xib_calc_ino(unsigned long pindex, int bit) +{ + ino_t ino; + + AuDebugOn(bit < 0 || page_bits <= bit); + ino = AUFS_FIRST_INO + pindex * page_bits + bit; + return ino; +} + +static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) +{ + AuDebugOn(ino < AUFS_FIRST_INO); + ino -= AUFS_FIRST_INO; + *pindex = ino / page_bits; + *bit = ino % page_bits; +} + +static int xib_pindex(struct super_block *sb, unsigned long pindex) +{ + int err; + loff_t pos; + ssize_t sz; + struct au_sbinfo *sbinfo; + struct file *xib; + unsigned long *p; + + sbinfo = au_sbi(sb); + MtxMustLock(&sbinfo->si_xib_mtx); + AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE + || !au_opt_test(sbinfo->si_mntflags, XINO)); + + if (pindex == sbinfo->si_xib_last_pindex) + return 0; + + xib = sbinfo->si_xib; + p = sbinfo->si_xib_buf; + pos = sbinfo->si_xib_last_pindex; + pos *= PAGE_SIZE; + sz = xino_fwrite(xib, p, PAGE_SIZE, &pos); + if (unlikely(sz != PAGE_SIZE)) + goto out; + + pos = pindex; + pos *= PAGE_SIZE; + if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) + sz = xino_fread(xib, p, PAGE_SIZE, &pos); + else { + memset(p, 0, PAGE_SIZE); + sz = xino_fwrite(xib, p, PAGE_SIZE, &pos); + } + if (sz == PAGE_SIZE) { + sbinfo->si_xib_last_pindex = pindex; + return 0; /* success */ + } + +out: + AuIOErr1("write failed (%zd)\n", sz); + err = sz; + if (sz >= 0) + err = -EIO; + return err; +} + +static void au_xib_clear_bit(struct inode *inode) +{ + int err, bit; + unsigned long pindex; + struct super_block *sb; + struct au_sbinfo *sbinfo; + + AuDebugOn(inode->i_nlink); + + sb = inode->i_sb; + xib_calc_bit(inode->i_ino, &pindex, &bit); + AuDebugOn(page_bits <= bit); + sbinfo = au_sbi(sb); + mutex_lock(&sbinfo->si_xib_mtx); + err = xib_pindex(sb, pindex); + if (!err) { + clear_bit(bit, sbinfo->si_xib_buf); + sbinfo->si_xib_next_bit = bit; + } + mutex_unlock(&sbinfo->si_xib_mtx); +} + +/* ---------------------------------------------------------------------- */ + +/* + * truncate a xino bitmap file + */ + +/* todo: slow */ +static int do_xib_restore(struct super_block *sb, struct file *file, void *page) +{ + int err, bit; + ssize_t sz; + unsigned long pindex; + loff_t pos, pend; + struct au_sbinfo *sbinfo; + ino_t *ino; + unsigned long *p; + + err = 0; + sbinfo = au_sbi(sb); + MtxMustLock(&sbinfo->si_xib_mtx); + p = sbinfo->si_xib_buf; + pend = vfsub_f_size_read(file); + pos = 0; + while (pos < pend) { + sz = xino_fread(file, page, PAGE_SIZE, &pos); + err = sz; + if (unlikely(sz <= 0)) + goto out; + + err = 0; + for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { + if (unlikely(*ino < AUFS_FIRST_INO)) + continue; + + xib_calc_bit(*ino, &pindex, &bit); + AuDebugOn(page_bits <= bit); + err = xib_pindex(sb, pindex); + if (!err) + set_bit(bit, p); + else + goto out; + } + } + +out: + return err; +} + +static int xib_restore(struct super_block *sb) +{ + int err, i; + unsigned int nfile; + aufs_bindex_t bindex, bbot; + void *page; + struct au_branch *br; + struct au_xino *xi; + struct file *file; + + err = -ENOMEM; + page = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!page)) + goto out; + + err = 0; + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) + if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) { + br = au_sbr(sb, bindex); + xi = br->br_xino; + nfile = xi->xi_nfile; + for (i = 0; i < nfile; i++) { + file = au_xino_file(xi, i); + if (file) + err = do_xib_restore(sb, file, page); + } + } else + AuDbg("skip shared b%d\n", bindex); + free_page((unsigned long)page); + +out: + return err; +} + +int au_xib_trunc(struct super_block *sb) +{ + int err; + ssize_t sz; + loff_t pos; + struct au_sbinfo *sbinfo; + unsigned long *p; + struct file *file; + + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); + if (!au_opt_test(sbinfo->si_mntflags, XINO)) + goto out; + + file = sbinfo->si_xib; + if (vfsub_f_size_read(file) <= PAGE_SIZE) + goto out; + + file = au_xino_create2(sb, &sbinfo->si_xib->f_path, NULL); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + fput(sbinfo->si_xib); + sbinfo->si_xib = file; + + p = sbinfo->si_xib_buf; + memset(p, 0, PAGE_SIZE); + pos = 0; + sz = xino_fwrite(sbinfo->si_xib, p, PAGE_SIZE, &pos); + if (unlikely(sz != PAGE_SIZE)) { + err = sz; + AuIOErr("err %d\n", err); + if (sz >= 0) + err = -EIO; + goto out; + } + + mutex_lock(&sbinfo->si_xib_mtx); + /* mnt_want_write() is unnecessary here */ + err = xib_restore(sb); + mutex_unlock(&sbinfo->si_xib_mtx); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_xino *au_xino_alloc(unsigned int nfile) +{ + struct au_xino *xi; + + xi = kzalloc(sizeof(*xi), GFP_NOFS); + if (unlikely(!xi)) + goto out; + xi->xi_nfile = nfile; + xi->xi_file = kcalloc(nfile, sizeof(*xi->xi_file), GFP_NOFS); + if (unlikely(!xi->xi_file)) + goto out_free; + + xi->xi_nondir.total = 8; /* initial size */ + xi->xi_nondir.array = kcalloc(xi->xi_nondir.total, sizeof(ino_t), + GFP_NOFS); + if (unlikely(!xi->xi_nondir.array)) + goto out_file; + + spin_lock_init(&xi->xi_nondir.spin); + init_waitqueue_head(&xi->xi_nondir.wqh); + mutex_init(&xi->xi_mtx); + INIT_HLIST_BL_HEAD(&xi->xi_writing); + atomic_set(&xi->xi_truncating, 0); + kref_init(&xi->xi_kref); + goto out; /* success */ + +out_file: + au_kfree_try_rcu(xi->xi_file); +out_free: + au_kfree_rcu(xi); + xi = NULL; +out: + return xi; +} + +static int au_xino_init(struct au_branch *br, int idx, struct file *file) +{ + int err; + struct au_xino *xi; + + err = 0; + xi = au_xino_alloc(idx + 1); + if (unlikely(!xi)) { + err = -ENOMEM; + goto out; + } + + if (file) + get_file(file); + xi->xi_file[idx] = file; + AuDebugOn(br->br_xino); + br->br_xino = xi; + +out: + return err; +} + +static void au_xino_release(struct kref *kref) +{ + struct au_xino *xi; + int i; + unsigned long ul; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *n; + struct au_xi_writing *p; + + xi = container_of(kref, struct au_xino, xi_kref); + for (i = 0; i < xi->xi_nfile; i++) + if (xi->xi_file[i]) + fput(xi->xi_file[i]); + for (i = xi->xi_nondir.total - 1; i >= 0; i--) + AuDebugOn(xi->xi_nondir.array[i]); + mutex_destroy(&xi->xi_mtx); + hbl = &xi->xi_writing; + ul = au_hbl_count(hbl); + if (unlikely(ul)) { + pr_warn("xi_writing %lu\n", ul); + hlist_bl_lock(hbl); + hlist_bl_for_each_entry_safe(p, pos, n, hbl, node) { + hlist_bl_del(&p->node); + /* kmemleak reported au_kfree_rcu() doesn't free it */ + kfree(p); + } + hlist_bl_unlock(hbl); + } + au_kfree_try_rcu(xi->xi_file); + au_kfree_try_rcu(xi->xi_nondir.array); + au_kfree_rcu(xi); +} + +int au_xino_put(struct au_branch *br) +{ + int ret; + struct au_xino *xi; + + ret = 0; + xi = br->br_xino; + if (xi) { + br->br_xino = NULL; + ret = kref_put(&xi->xi_kref, au_xino_release); + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* + * xino mount option handlers + */ + +/* xino bitmap */ +static void xino_clear_xib(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; + if (sbinfo->si_xib_buf) + free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; +} + +static int au_xino_set_xib(struct super_block *sb, struct path *path) +{ + int err; + loff_t pos; + struct au_sbinfo *sbinfo; + struct file *file; + struct super_block *xi_sb; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + file = au_xino_create2(sb, path, sbinfo->si_xib); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = file; + xi_sb = file_inode(file)->i_sb; + sbinfo->si_ximaxent = xi_sb->s_maxbytes; + if (unlikely(sbinfo->si_ximaxent < PAGE_SIZE)) { + err = -EIO; + pr_err("s_maxbytes(%llu) on %s is too small\n", + (u64)sbinfo->si_ximaxent, au_sbtype(xi_sb)); + goto out_unset; + } + sbinfo->si_ximaxent /= sizeof(ino_t); + + err = -ENOMEM; + if (!sbinfo->si_xib_buf) + sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); + if (unlikely(!sbinfo->si_xib_buf)) + goto out_unset; + + sbinfo->si_xib_last_pindex = 0; + sbinfo->si_xib_next_bit = 0; + if (vfsub_f_size_read(file) < PAGE_SIZE) { + pos = 0; + err = xino_fwrite(file, sbinfo->si_xib_buf, PAGE_SIZE, &pos); + if (unlikely(err != PAGE_SIZE)) + goto out_free; + } + err = 0; + goto out; /* success */ + +out_free: + if (sbinfo->si_xib_buf) + free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; + if (err >= 0) + err = -EIO; +out_unset: + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; +out: + AuTraceErr(err); + return err; +} + +/* xino for each branch */ +static void xino_clear_br(struct super_block *sb) +{ + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + AuDebugOn(!br); + au_xino_put(br); + } +} + +static void au_xino_set_br_shared(struct super_block *sb, struct au_branch *br, + aufs_bindex_t bshared) +{ + struct au_branch *brshared; + + brshared = au_sbr(sb, bshared); + AuDebugOn(!brshared->br_xino); + AuDebugOn(!brshared->br_xino->xi_file); + if (br->br_xino != brshared->br_xino) { + au_xino_get(brshared); + au_xino_put(br); + br->br_xino = brshared->br_xino; + } +} + +struct au_xino_do_set_br { + struct au_branch *br; + ino_t h_ino; + aufs_bindex_t bshared; +}; + +static int au_xino_do_set_br(struct super_block *sb, struct path *path, + struct au_xino_do_set_br *args) +{ + int err; + struct au_xi_calc calc; + struct file *file; + struct au_branch *br; + struct au_xi_new xinew = { + .base = path + }; + + br = args->br; + xinew.xi = br->br_xino; + au_xi_calc(sb, args->h_ino, &calc); + xinew.copy_src = au_xino_file(xinew.xi, calc.idx); + if (args->bshared >= 0) + /* shared xino */ + au_xino_set_br_shared(sb, br, args->bshared); + else if (!xinew.xi) { + /* new xino */ + err = au_xino_init(br, calc.idx, xinew.copy_src); + if (unlikely(err)) + goto out; + } + + /* force re-creating */ + xinew.xi = br->br_xino; + xinew.idx = calc.idx; + mutex_lock(&xinew.xi->xi_mtx); + file = au_xi_new(sb, &xinew); + mutex_unlock(&xinew.xi->xi_mtx); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + AuDebugOn(!file); + + err = au_xino_do_write(file, &calc, AUFS_ROOT_INO); + if (unlikely(err)) + au_xino_put(br); + +out: + AuTraceErr(err); + return err; +} + +static int au_xino_set_br(struct super_block *sb, struct path *path) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_xino_do_set_br args; + struct inode *inode; + + SiMustWriteLock(sb); + + bbot = au_sbbot(sb); + inode = d_inode(sb->s_root); + for (bindex = 0; bindex <= bbot; bindex++) { + args.h_ino = au_h_iptr(inode, bindex)->i_ino; + args.br = au_sbr(sb, bindex); + args.bshared = is_sb_shared(sb, bindex, bindex - 1); + err = au_xino_do_set_br(sb, path, &args); + if (unlikely(err)) + break; + } + + AuTraceErr(err); + return err; +} + +void au_xino_clr(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + au_xigen_clr(sb); + xino_clear_xib(sb); + xino_clear_br(sb); + dbgaufs_brs_del(sb, 0); + sbinfo = au_sbi(sb); + /* lvalue, do not call au_mntflags() */ + au_opt_clr(sbinfo->si_mntflags, XINO); +} + +int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount) +{ + int err, skip; + struct dentry *dentry, *parent, *cur_dentry, *cur_parent; + struct qstr *dname, *cur_name; + struct file *cur_xino; + struct au_sbinfo *sbinfo; + struct path *path, *cur_path; + + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); + path = &xiopt->file->f_path; + dentry = path->dentry; + parent = dget_parent(dentry); + if (remount) { + skip = 0; + cur_xino = sbinfo->si_xib; + if (cur_xino) { + cur_path = &cur_xino->f_path; + cur_dentry = cur_path->dentry; + cur_parent = dget_parent(cur_dentry); + cur_name = &cur_dentry->d_name; + dname = &dentry->d_name; + skip = (cur_parent == parent + && au_qstreq(dname, cur_name)); + dput(cur_parent); + } + if (skip) + goto out; + } + + au_opt_set(sbinfo->si_mntflags, XINO); + err = au_xino_set_xib(sb, path); + /* si_x{read,write} are set */ + if (!err) + err = au_xigen_set(sb, path); + if (!err) + err = au_xino_set_br(sb, path); + if (!err) { + dbgaufs_brs_add(sb, 0, /*topdown*/1); + goto out; /* success */ + } + + /* reset all */ + AuIOErr("failed setting xino(%d).\n", err); + au_xino_clr(sb); + +out: + dput(parent); + return err; +} + +/* + * create a xinofile at the default place/path. + */ +struct file *au_xino_def(struct super_block *sb) +{ + struct file *file; + char *page, *p; + struct au_branch *br; + struct super_block *h_sb; + struct path path; + aufs_bindex_t bbot, bindex, bwr; + + br = NULL; + bbot = au_sbbot(sb); + bwr = -1; + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm) + && !au_test_fs_bad_xino(au_br_sb(br))) { + bwr = bindex; + break; + } + } + + if (bwr >= 0) { + file = ERR_PTR(-ENOMEM); + page = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!page)) + goto out; + path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(sb->s_root, bwr); + p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); + file = (void *)p; + if (!IS_ERR(p)) { + strcat(p, "/" AUFS_XINO_FNAME); + AuDbg("%s\n", p); + file = au_xino_create(sb, p, /*silent*/0, /*wbrtop*/1); + } + free_page((unsigned long)page); + } else { + file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0, + /*wbrtop*/0); + if (IS_ERR(file)) + goto out; + h_sb = file->f_path.dentry->d_sb; + if (unlikely(au_test_fs_bad_xino(h_sb))) { + pr_err("xino doesn't support %s(%s)\n", + AUFS_XINO_DEFPATH, au_sbtype(h_sb)); + fput(file); + file = ERR_PTR(-EINVAL); + } + } + +out: + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* + * initialize the xinofile for the specified branch @br + * at the place/path where @base_file indicates. + * test whether another branch is on the same filesystem or not, + * if found then share the xinofile with another branch. + */ +int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, + struct path *base) +{ + int err; + struct au_xino_do_set_br args = { + .h_ino = h_ino, + .br = br + }; + + args.bshared = sbr_find_shared(sb, /*btop*/0, au_sbbot(sb), + au_br_sb(br)); + err = au_xino_do_set_br(sb, base, &args); + if (unlikely(err)) + au_xino_put(br); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * get an unused inode number from bitmap + */ +ino_t au_xino_new_ino(struct super_block *sb) +{ + ino_t ino; + unsigned long *p, pindex, ul, pend; + struct au_sbinfo *sbinfo; + struct file *file; + int free_bit, err; + + if (!au_opt_test(au_mntflags(sb), XINO)) + return iunique(sb, AUFS_FIRST_INO); + + sbinfo = au_sbi(sb); + mutex_lock(&sbinfo->si_xib_mtx); + p = sbinfo->si_xib_buf; + free_bit = sbinfo->si_xib_next_bit; + if (free_bit < page_bits && !test_bit(free_bit, p)) + goto out; /* success */ + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + + pindex = sbinfo->si_xib_last_pindex; + for (ul = pindex - 1; ul < ULONG_MAX; ul--) { + err = xib_pindex(sb, ul); + if (unlikely(err)) + goto out_err; + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + } + + file = sbinfo->si_xib; + pend = vfsub_f_size_read(file) / PAGE_SIZE; + for (ul = pindex + 1; ul <= pend; ul++) { + err = xib_pindex(sb, ul); + if (unlikely(err)) + goto out_err; + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + } + BUG(); + +out: + set_bit(free_bit, p); + sbinfo->si_xib_next_bit = free_bit + 1; + pindex = sbinfo->si_xib_last_pindex; + mutex_unlock(&sbinfo->si_xib_mtx); + ino = xib_calc_ino(pindex, free_bit); + AuDbg("i%lu\n", (unsigned long)ino); + return ino; +out_err: + mutex_unlock(&sbinfo->si_xib_mtx); + AuDbg("i0\n"); + return 0; +} + +/* for s_op->delete_inode() */ +void au_xino_delete_inode(struct inode *inode, const int unlinked) +{ + int err; + unsigned int mnt_flags; + aufs_bindex_t bindex, bbot, bi; + unsigned char try_trunc; + struct au_iinfo *iinfo; + struct super_block *sb; + struct au_hinode *hi; + struct inode *h_inode; + struct au_branch *br; + struct au_xi_calc calc; + struct file *file; + + AuDebugOn(au_is_bad_inode(inode)); + + sb = inode->i_sb; + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, XINO) + || inode->i_ino == AUFS_ROOT_INO) + return; + + if (unlinked) { + au_xigen_inc(inode); + au_xib_clear_bit(inode); + } + + iinfo = au_ii(inode); + bindex = iinfo->ii_btop; + if (bindex < 0) + return; + + try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); + hi = au_hinode(iinfo, bindex); + bbot = iinfo->ii_bbot; + for (; bindex <= bbot; bindex++, hi++) { + h_inode = hi->hi_inode; + if (!h_inode + || (!unlinked && h_inode->i_nlink)) + continue; + + /* inode may not be revalidated */ + bi = au_br_index(sb, hi->hi_id); + if (bi < 0) + continue; + + br = au_sbr(sb, bi); + au_xi_calc(sb, h_inode->i_ino, &calc); + file = au_xino_file(br->br_xino, calc.idx); + if (IS_ERR_OR_NULL(file)) + continue; + + err = au_xino_do_write(file, &calc, /*ino*/0); + if (!err && try_trunc + && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + } +} + +/* ---------------------------------------------------------------------- */ + +static int au_xinondir_find(struct au_xino *xi, ino_t h_ino) +{ + int found, total, i; + + found = -1; + total = xi->xi_nondir.total; + for (i = 0; i < total; i++) { + if (xi->xi_nondir.array[i] != h_ino) + continue; + found = i; + break; + } + + return found; +} + +static int au_xinondir_expand(struct au_xino *xi) +{ + int err, sz; + ino_t *p; + + BUILD_BUG_ON(KMALLOC_MAX_SIZE > INT_MAX); + + err = -ENOMEM; + sz = xi->xi_nondir.total * sizeof(ino_t); + if (unlikely(sz > KMALLOC_MAX_SIZE / 2)) + goto out; + p = au_kzrealloc(xi->xi_nondir.array, sz, sz << 1, GFP_ATOMIC, + /*may_shrink*/0); + if (p) { + xi->xi_nondir.array = p; + xi->xi_nondir.total <<= 1; + AuDbg("xi_nondir.total %d\n", xi->xi_nondir.total); + err = 0; + } + +out: + return err; +} + +void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, + ino_t h_ino, int idx) +{ + struct au_xino *xi; + + AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + xi = au_sbr(sb, bindex)->br_xino; + AuDebugOn(idx < 0 || xi->xi_nondir.total <= idx); + + spin_lock(&xi->xi_nondir.spin); + AuDebugOn(xi->xi_nondir.array[idx] != h_ino); + xi->xi_nondir.array[idx] = 0; + spin_unlock(&xi->xi_nondir.spin); + wake_up_all(&xi->xi_nondir.wqh); +} + +int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + int *idx) +{ + int err, found, empty; + struct au_xino *xi; + + err = 0; + *idx = -1; + if (!au_opt_test(au_mntflags(sb), XINO)) + goto out; /* no xino */ + + xi = au_sbr(sb, bindex)->br_xino; + +again: + spin_lock(&xi->xi_nondir.spin); + found = au_xinondir_find(xi, h_ino); + if (found == -1) { + empty = au_xinondir_find(xi, /*h_ino*/0); + if (empty == -1) { + empty = xi->xi_nondir.total; + err = au_xinondir_expand(xi); + if (unlikely(err)) + goto out_unlock; + } + xi->xi_nondir.array[empty] = h_ino; + *idx = empty; + } else { + spin_unlock(&xi->xi_nondir.spin); + wait_event(xi->xi_nondir.wqh, + xi->xi_nondir.array[found] != h_ino); + goto again; + } + +out_unlock: + spin_unlock(&xi->xi_nondir.spin); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_xino_path(struct seq_file *seq, struct file *file) +{ + int err; + + err = au_seq_path(seq, &file->f_path); + if (unlikely(err)) + goto out; + +#define Deleted "\\040(deleted)" + seq->count -= sizeof(Deleted) - 1; + AuDebugOn(memcmp(seq->buf + seq->count, Deleted, + sizeof(Deleted) - 1)); +#undef Deleted + +out: + return err; +} diff -Naur --no-dereference a/fs/dcache.c b/fs/dcache.c --- a/fs/dcache.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/dcache.c 2022-01-06 12:45:53.830318172 -0500 @@ -1285,7 +1285,7 @@ * * The @enter() callbacks are called with d_lock held. */ -static void d_walk(struct dentry *parent, void *data, +void d_walk(struct dentry *parent, void *data, enum d_walk_ret (*enter)(void *, struct dentry *)) { struct dentry *this_parent; @@ -1390,6 +1390,7 @@ seq = 1; goto again; } +EXPORT_SYMBOL_GPL(d_walk); struct check_mount { struct vfsmount *mnt; @@ -2935,6 +2936,7 @@ write_sequnlock(&rename_lock); } +EXPORT_SYMBOL_GPL(d_exchange); /** * d_ancestor - search for an ancestor diff -Naur --no-dereference a/fs/exec.c b/fs/exec.c --- a/fs/exec.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/exec.c 2022-01-06 12:45:53.830318172 -0500 @@ -113,6 +113,7 @@ return (path->mnt->mnt_flags & MNT_NOEXEC) || (path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC); } +EXPORT_SYMBOL_GPL(path_noexec); #ifdef CONFIG_USELIB /* diff -Naur --no-dereference a/fs/fcntl.c b/fs/fcntl.c --- a/fs/fcntl.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/fcntl.c 2022-01-06 12:45:53.830318172 -0500 @@ -32,7 +32,7 @@ #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) -static int setfl(int fd, struct file * filp, unsigned long arg) +int setfl(int fd, struct file *filp, unsigned long arg) { struct inode * inode = file_inode(filp); int error = 0; @@ -63,6 +63,8 @@ if (filp->f_op->check_flags) error = filp->f_op->check_flags(arg); + if (!error && filp->f_op->setfl) + error = filp->f_op->setfl(filp, arg); if (error) return error; @@ -83,6 +85,7 @@ out: return error; } +EXPORT_SYMBOL_GPL(setfl); static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, int force) diff -Naur --no-dereference a/fs/file_table.c b/fs/file_table.c --- a/fs/file_table.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/file_table.c 2022-01-06 12:45:53.830318172 -0500 @@ -162,6 +162,7 @@ } return ERR_PTR(-ENFILE); } +EXPORT_SYMBOL_GPL(alloc_empty_file); /* * Variant of alloc_empty_file() that doesn't check and modify nr_files. @@ -376,6 +377,7 @@ } EXPORT_SYMBOL(fput); +EXPORT_SYMBOL_GPL(__fput_sync); void __init files_init(void) { diff -Naur --no-dereference a/fs/Kconfig b/fs/Kconfig --- a/fs/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/Kconfig 2022-01-06 12:45:53.830318172 -0500 @@ -288,6 +288,7 @@ source "fs/ufs/Kconfig" source "fs/erofs/Kconfig" source "fs/vboxsf/Kconfig" +source "fs/aufs/Kconfig" endif # MISC_FILESYSTEMS @@ -336,6 +337,7 @@ source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" +source "fs/ksmbd/Kconfig" source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff -Naur --no-dereference a/fs/ksmbd/asn1.c b/fs/ksmbd/asn1.c --- a/fs/ksmbd/asn1.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/asn1.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" + +#include "asn1.h" +#include "connection.h" +#include "auth.h" +#include "ksmbd_spnego_negtokeninit.asn1.h" +#include "ksmbd_spnego_negtokentarg.asn1.h" + +#define SPNEGO_OID_LEN 7 +#define NTLMSSP_OID_LEN 10 +#define KRB5_OID_LEN 7 +#define KRB5U2U_OID_LEN 8 +#define MSKRB5_OID_LEN 7 +static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; +static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; +static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 }; +static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 }; +static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; + +static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a }; + +static bool +asn1_subid_decode(const unsigned char **begin, const unsigned char *end, + unsigned long *subid) +{ + const unsigned char *ptr = *begin; + unsigned char ch; + + *subid = 0; + + do { + if (ptr >= end) + return false; + + ch = *ptr++; + *subid <<= 7; + *subid |= ch & 0x7F; + } while ((ch & 0x80) == 0x80); + + *begin = ptr; + return true; +} + +static bool asn1_oid_decode(const unsigned char *value, size_t vlen, + unsigned long **oid, size_t *oidlen) +{ + const unsigned char *iptr = value, *end = value + vlen; + unsigned long *optr; + unsigned long subid; + + vlen += 1; + if (vlen < 2 || vlen > UINT_MAX / sizeof(unsigned long)) + goto fail_nullify; + + *oid = kmalloc(vlen * sizeof(unsigned long), GFP_KERNEL); + if (!*oid) + return false; + + optr = *oid; + + if (!asn1_subid_decode(&iptr, end, &subid)) + goto fail; + + if (subid < 40) { + optr[0] = 0; + optr[1] = subid; + } else if (subid < 80) { + optr[0] = 1; + optr[1] = subid - 40; + } else { + optr[0] = 2; + optr[1] = subid - 80; + } + + *oidlen = 2; + optr += 2; + + while (iptr < end) { + if (++(*oidlen) > vlen) + goto fail; + + if (!asn1_subid_decode(&iptr, end, optr++)) + goto fail; + } + return true; + +fail: + kfree(*oid); +fail_nullify: + *oid = NULL; + return false; +} + +static bool oid_eq(unsigned long *oid1, unsigned int oid1len, + unsigned long *oid2, unsigned int oid2len) +{ + if (oid1len != oid2len) + return false; + + return memcmp(oid1, oid2, oid1len) == 0; +} + +int +ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, + security_blob, length); +} + +int +ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, + security_blob, length); +} + +static int compute_asn_hdr_len_bytes(int len) +{ + if (len > 0xFFFFFF) + return 4; + else if (len > 0xFFFF) + return 3; + else if (len > 0xFF) + return 2; + else if (len > 0x7F) + return 1; + else + return 0; +} + +static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, + int length) +{ + int i; + int index = *ofs; + char hdr_len = compute_asn_hdr_len_bytes(length); + int len = length + 2 + hdr_len; + + /* insert tag */ + buf[index++] = tag; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + /* insert seq */ + len = len - (index - *ofs); + buf[index++] = seq; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + *ofs += (index - *ofs); +} + +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + + NTLMSSP_OID_LEN; + int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + + ntlm_blob_len; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + + oid_len + ntlmssp_len) * 2 + + neg_result_len + oid_len + ntlmssp_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + + ntlmssp_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + buf[ofs++] = 1; + + /* insert oid */ + encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); + memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); + ofs += NTLMSSP_OID_LEN; + + /* insert response token - ntlmssp blob */ + encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); + memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); + ofs += ntlm_blob_len; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + + neg_result_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + if (neg_result) + buf[ofs++] = 2; + else + buf[ofs++] = 0; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + unsigned long *oid; + size_t oidlen; + int err = 0; + + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) { + err = -EBADMSG; + goto out; + } + + if (!oid_eq(oid, oidlen, SPNEGO_OID, SPNEGO_OID_LEN)) + err = -EBADMSG; + kfree(oid); +out: + if (err) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + } + return err; +} + +int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + unsigned long *oid; + size_t oidlen; + int mech_type; + char buf[50]; + + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) + goto fail; + + if (oid_eq(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN)) + mech_type = KSMBD_AUTH_NTLMSSP; + else if (oid_eq(oid, oidlen, MSKRB5_OID, MSKRB5_OID_LEN)) + mech_type = KSMBD_AUTH_MSKRB5; + else if (oid_eq(oid, oidlen, KRB5_OID, KRB5_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5; + else if (oid_eq(oid, oidlen, KRB5U2U_OID, KRB5U2U_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5U2U; + else + goto fail; + + conn->auth_mechs |= mech_type; + if (conn->preferred_auth_mech == 0) + conn->preferred_auth_mech = mech_type; + + kfree(oid); + return 0; + +fail: + kfree(oid); + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; +} + +int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} + +int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} diff -Naur --no-dereference a/fs/ksmbd/asn1.h b/fs/ksmbd/asn1.h --- a/fs/ksmbd/asn1.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/asn1.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __ASN1_H__ +#define __ASN1_H__ + +int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len); +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result); +#endif /* __ASN1_H__ */ diff -Naur --no-dereference a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c --- a/fs/ksmbd/auth.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/auth.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1436 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "auth.h" +#include "glob.h" + +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "crypto_ctx.h" +#include "transport_ipc.h" + +/* + * Fixed format data defining GSS header and fixed string + * "not_defined_in_RFC4178@please_ignore". + * So sec blob data in neg phase could be generated statically. + */ +static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, + 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, + 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, + 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 +#else + 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, + 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, + 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, + 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65 +#endif +}; + +void ksmbd_copy_gss_neg_header(void *buf) +{ + memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); +} + +static void +str_to_key(unsigned char *str, unsigned char *key) +{ + int i; + + key[0] = str[0] >> 1; + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2); + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3); + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4); + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5); + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6); + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7); + key[7] = str[6] & 0x7F; + for (i = 0; i < 8; i++) + key[i] = (key[i] << 1); +} + +static int +smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) +{ + unsigned char key2[8]; + struct des_ctx ctx; + + if (fips_enabled) { + ksmbd_debug(AUTH, "FIPS compliance enabled: DES not permitted\n"); + return -ENOENT; + } + + str_to_key(key, key2); + des_expand_key(&ctx, key2, DES_KEY_SIZE); + des_encrypt(&ctx, out, in); + memzero_explicit(&ctx, sizeof(ctx)); + return 0; +} + +static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) +{ + int rc; + + rc = smbhash(p24, c8, p21); + if (rc) + return rc; + rc = smbhash(p24 + 8, c8, p21 + 7); + if (rc) + return rc; + return smbhash(p24 + 16, c8, p21 + 14); +} + +/* produce a md4 message digest from data of length n bytes */ +static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str, + int link_len) +{ + int rc; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_md4(); + if (!ctx) { + ksmbd_debug(AUTH, "Crypto md4 allocation error\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_MD4(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init md4 shash\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD4(ctx), link_str, link_len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with link_str\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_MD4(ctx), md4_hash); + if (rc) + ksmbd_debug(AUTH, "Could not generate md4 hash\n"); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce, + char *server_challenge, int len) +{ + int rc; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_md5(); + if (!ctx) { + ksmbd_debug(AUTH, "Crypto md5 allocation error\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_MD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init md5 shash\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), server_challenge, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with challenge\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), nonce, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with nonce\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_MD5(ctx), md5_hash); + if (rc) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_gen_sess_key() - function to generate session key + * @sess: session of connection + * @hash: source hash value to be used for find session key + * @hmac: source hmac value to be used for finding session key + * + */ +static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, + char *hmac) +{ + struct ksmbd_crypto_ctx *ctx; + int rc; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), + hmac, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); + goto out; + } + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, + char *dname) +{ + int ret, len, conv_len; + wchar_t *domain = NULL; + __le16 *uniname = NULL; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); + return -ENOMEM; + } + + ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + user_passkey(sess->user), + CIFS_ENCPWD_SIZE); + if (ret) { + ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); + goto out; + } + + ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (ret) { + ksmbd_debug(AUTH, "could not init hmacmd5\n"); + goto out; + } + + /* convert user_name to unicode */ + len = strlen(user_name(sess->user)); + uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!uniname) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, + sess->conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + UniStrupr(uniname); + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)uniname, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with user\n"); + goto out; + } + + /* Convert domain name or conn name to unicode and uppercase */ + len = strlen(dname); + domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!domain) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, + sess->conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)domain, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with domain\n"); + goto out; + } + + ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); + if (ret) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + kfree(uniname); + kfree(domain); + ksmbd_release_crypto_ctx(ctx); + return ret; +} + +/** + * ksmbd_auth_ntlm() - NTLM authentication handler + * @sess: session of connection + * @pw_buf: NTLM challenge response + * @passkey: user password + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf, char *cryptkey) +{ + int rc; + unsigned char p21[21]; + char key[CIFS_AUTH_RESP_SIZE]; + + memset(p21, '\0', 21); + memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); + rc = ksmbd_enc_p24(p21, cryptkey, key); + if (rc) { + pr_err("password processing failed\n"); + return rc; + } + + ksmbd_enc_md4(sess->sess_key, user_passkey(sess->user), + CIFS_SMB1_SESSKEY_SIZE); + memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key, + CIFS_AUTH_RESP_SIZE); + sess->sequence_number = 1; + + if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { + ksmbd_debug(AUTH, "ntlmv1 authentication failed\n"); + return -EINVAL; + } + + ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); + return 0; +} + +/** + * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler + * @sess: session of connection + * @ntlmv2: NTLMv2 challenge response + * @blen: NTLMv2 blob length + * @domain_name: domain name + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, + int blen, char *domain_name, char *cryptkey) +{ + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + struct ksmbd_crypto_ctx *ctx; + char *construct = NULL; + int rc, len; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = calc_ntlmv2_hash(sess, ntlmv2_hash, domain_name); + if (rc) { + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + goto out; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init hmacmd5\n"); + goto out; + } + + len = CIFS_CRYPTO_KEY_SIZE + blen; + construct = kzalloc(len, GFP_KERNEL); + if (!construct) { + rc = -ENOMEM; + goto out; + } + + memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); + memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + goto out; + } + + rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate sess key\n"); + goto out; + } + + if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) + rc = -EINVAL; +out: + ksmbd_release_crypto_ctx(ctx); + kfree(construct); + return rc; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * __ksmbd_auth_ntlmv2() - NTLM2(extended security) authentication handler + * @sess: session of connection + * @client_nonce: client nonce from LM response. + * @ntlm_resp: ntlm response data from client. + * + * Return: 0 on success, error number on error + */ +static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, + char *client_nonce, + char *ntlm_resp, + char *cryptkey) +{ + char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0}; + int rc; + unsigned char p21[21]; + char key[CIFS_AUTH_RESP_SIZE]; + + rc = ksmbd_enc_update_sess_key(sess_key, client_nonce, cryptkey, 8); + if (rc) { + pr_err("password processing failed\n"); + goto out; + } + + memset(p21, '\0', 21); + memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); + rc = ksmbd_enc_p24(p21, sess_key, key); + if (rc) { + pr_err("password processing failed\n"); + goto out; + } + + if (memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE) != 0) + rc = -EINVAL; +out: + return rc; +} +#endif + +/** + * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct + * authenticate blob + * @authblob: authenticate blob source pointer + * @usr: user details + * @sess: session of connection + * + * Return: 0 on success, error number on error + */ +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + char *domain_name; + unsigned int nt_off, dn_off; + unsigned short nt_len, dn_len; +#ifdef CONFIG_SMB_INSECURE_SERVER + unsigned int lm_off; + unsigned short lm_len; +#endif + int ret; + + if (blob_len < sizeof(struct authenticate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(authblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + authblob->Signature); + return -EINVAL; + } + + nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); + nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); + + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); + dn_len = le16_to_cpu(authblob->DomainName.Length); + + if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len) + return -EINVAL; + +#ifdef CONFIG_SMB_INSECURE_SERVER + lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset); + lm_len = le16_to_cpu(authblob->LmChallengeResponse.Length); + if (blob_len < (u64)lm_off + lm_len) + return -EINVAL; + + /* process NTLM authentication */ + if (nt_len == CIFS_AUTH_RESP_SIZE) { + if (le32_to_cpu(authblob->NegotiateFlags) & + NTLMSSP_NEGOTIATE_EXTENDED_SEC) + return __ksmbd_auth_ntlmv2(sess, + (char *)authblob + lm_off, + (char *)authblob + nt_off, + conn->ntlmssp.cryptkey); + else + return ksmbd_auth_ntlm(sess, (char *)authblob + + nt_off, conn->ntlmssp.cryptkey); + } +#endif + + /* TODO : use domain name that imported from configuration file */ + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, + dn_len, true, conn->local_nls); + if (IS_ERR(domain_name)) + return PTR_ERR(domain_name); + + /* process NTLMv2 authentication */ + ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", + domain_name); + ret = ksmbd_auth_ntlmv2(sess, (struct ntlmv2_resp *)((char *)authblob + nt_off), + nt_len - CIFS_ENCPWD_SIZE, + domain_name, conn->ntlmssp.cryptkey); + kfree(domain_name); + return ret; +} + +/** + * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct + * negotiate blob + * @negblob: negotiate blob source pointer + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn) +{ + if (blob_len < sizeof(struct negotiate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(negblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + negblob->Signature); + return -EINVAL; + } + + conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); + return 0; +} + +/** + * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct + * challenge blob + * @chgblob: challenge blob source pointer to initialize + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn) +{ + struct target_info *tinfo; + wchar_t *name; + __u8 *target_name; + unsigned int flags, blob_off, blob_len, type, target_info_len = 0; + int len, uni_len, conv_len; + int cflags = conn->ntlmssp.client_flags; + + memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); + chgblob->MessageType = NtLmChallenge; + + flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | + NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (cflags & NTLMSSP_NEGOTIATE_SIGN) { + flags |= NTLMSSP_NEGOTIATE_SIGN; + flags |= cflags & (NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_56); + } + + if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + + if (cflags & NTLMSSP_REQUEST_TARGET) + flags |= NTLMSSP_REQUEST_TARGET; + + if (conn->use_spnego && + (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) + flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; + + chgblob->NegotiateFlags = cpu_to_le32(flags); + len = strlen(ksmbd_netbios_name()); + name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!name) + return -ENOMEM; + + conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + kfree(name); + return -EINVAL; + } + + uni_len = UNICODE_LEN(conv_len); + + blob_off = sizeof(struct challenge_message); + blob_len = blob_off + uni_len; + + chgblob->TargetName.Length = cpu_to_le16(uni_len); + chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); + chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); + + /* Initialize random conn challenge */ + get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); + memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + + /* Add Target Information to security buffer */ + chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); + + target_name = (__u8 *)chgblob + blob_off; + memcpy(target_name, name, uni_len); + tinfo = (struct target_info *)(target_name + uni_len); + + chgblob->TargetInfoArray.Length = 0; + /* Add target info list for NetBIOS/DNS settings */ + for (type = NTLMSSP_AV_NB_COMPUTER_NAME; + type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { + tinfo->Type = cpu_to_le16(type); + tinfo->Length = cpu_to_le16(uni_len); + memcpy(tinfo->Content, name, uni_len); + tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); + target_info_len += 4 + uni_len; + } + + /* Add terminator subblock */ + tinfo->Type = 0; + tinfo->Length = 0; + target_info_len += 4; + + chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); + chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); + blob_len += target_info_len; + kfree(name); + ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); + return blob_len; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + struct ksmbd_spnego_authen_response *resp; + struct ksmbd_user *user = NULL; + int retval; + + resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); + if (!resp) { + ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); + return -EINVAL; + } + + if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { + ksmbd_debug(AUTH, "krb5 authentication failure\n"); + retval = -EPERM; + goto out; + } + + if (*out_len <= resp->spnego_blob_len) { + ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", + *out_len, resp->spnego_blob_len); + retval = -EINVAL; + goto out; + } + + if (resp->session_key_len > sizeof(sess->sess_key)) { + ksmbd_debug(AUTH, "session key is too long\n"); + retval = -EINVAL; + goto out; + } + + user = ksmbd_alloc_user(&resp->login_response); + if (!user) { + ksmbd_debug(AUTH, "login failure\n"); + retval = -ENOMEM; + goto out; + } + sess->user = user; + + memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(out_blob, resp->payload + resp->session_key_len, + resp->spnego_blob_len); + *out_len = resp->spnego_blob_len; + retval = 0; +out: + kvfree(resp); + return retval; +} +#else +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + return -EOPNOTSUPP; +} +#endif + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * ksmbd_sign_smb1_pdu() - function to generate SMB1 packet signing + * @sess: session of connection + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb1_pdu(struct ksmbd_session *sess, struct kvec *iov, int n_vec, + char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_md5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc md5\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_MD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "md5 init error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), sess->sess_key, 40); + if (rc) { + ksmbd_debug(AUTH, "md5 update error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_MD5(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "md5 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_MD5(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "md5 generation error %d\n", rc); + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} +#endif + +/** + * ksmbd_sign_smb2_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_sign_smb3_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_cmacaes(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), + key, + SMB2_CMACAES_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); + if (rc) { + ksmbd_debug(AUTH, "cmaces init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_CMACAES(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "cmaces update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; + bool binding; +}; + +static int generate_key(struct ksmbd_session *sess, struct kvec label, + struct kvec context, __u8 *key, unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; + int rc; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct ksmbd_crypto_ctx *ctx; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto smb3signkey_ret; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + label.iov_base, + label.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with label\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); + if (rc) { + ksmbd_debug(AUTH, "could not update with zero\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + context.iov_base, + context.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with context\n"); + goto smb3signkey_ret; + } + + if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + else + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with L\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", + rc); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int generate_smb3signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn, + const struct derivation *signing) +{ + int rc; + struct channel *chann; + char *key; + + chann = lookup_chann_list(sess, conn); + if (!chann) + return 0; + + if (sess->conn->dialect >= SMB30_PROT_ID && signing->binding) + key = chann->smb3signingkey; + else + key = sess->smb3signingkey; + + rc = generate_key(sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + if (!(sess->conn->dialect >= SMB30_PROT_ID && signing->binding)) + memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); + + ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + ksmbd_debug(AUTH, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, key); + return 0; +} + +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMB2AESCMAC"; + d.label.iov_len = 12; + d.context.iov_base = "SmbSign"; + d.context.iov_len = 8; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMBSigningKey"; + d.label.iov_len = 14; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return -ENOENT; + d.context.iov_base = preauth_sess->Preauth_HashValue; + } else { + d.context.iov_base = sess->Preauth_HashValue; + } + d.context.iov_len = 64; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +struct derivation_twin { + struct derivation encryption; + struct derivation decryption; +}; + +static int generate_smb3encryptionkey(struct ksmbd_session *sess, + const struct derivation_twin *ptwin) +{ + int rc; + + rc = generate_key(sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + rc = generate_key(sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); + ksmbd_debug(AUTH, "Cipher type %d\n", sess->conn->cipher_type); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } else { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } + return 0; +} + +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + d = &twin.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + return generate_smb3encryptionkey(sess, &twin); +} + +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + d = &twin.decryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + return generate_smb3encryptionkey(sess, &twin); +} + +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash) +{ + int rc; + struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; + char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; + int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length); + struct ksmbd_crypto_ctx *ctx = NULL; + + if (conn->preauth_info->Preauth_HashId != + SMB2_PREAUTH_INTEGRITY_SHA512) + return -EINVAL; + + ctx = ksmbd_crypto_ctx_find_sha512(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha512\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA512(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash) +{ + int rc; + struct ksmbd_crypto_ctx *ctx = NULL; + + ctx = ksmbd_crypto_ctx_find_sha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha256\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, + int enc, u8 *key) +{ + struct ksmbd_session *sess; + u8 *ses_enc_key; + + sess = ksmbd_session_lookup_all(conn, ses_id); + if (!sess) + return -EINVAL; + + ses_enc_key = enc ? sess->smb3encryptionkey : + sess->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + + return 0; +} + +static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + void *addr; + + if (is_vmalloc_addr(buf)) + addr = vmalloc_to_page(buf); + else + addr = virt_to_page(buf); + sg_set_page(sg, addr, buflen, offset_in_page(buf)); +} + +static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + u8 *sign) +{ + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + + if (!nvec) + return NULL; + + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + + if (is_vmalloc_addr(iov[i + 1].iov_base)) { + nr_entries[i] = ((kaddr + iov[i + 1].iov_len + + PAGE_SIZE - 1) >> PAGE_SHIFT) - + (kaddr >> PAGE_SHIFT); + } else { + nr_entries[i]++; + } + total_entries += nr_entries[i]; + } + + /* Add two entries for transform header and signature */ + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); + if (!sg) + return NULL; + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); + for (i = 0; i < nvec - 1; i++) { + void *data = iov[i + 1].iov_base; + int len = iov[i + 1].iov_len; + + if (is_vmalloc_addr(data)) { + int j, offset = offset_in_page(data); + + for (j = 0; j < nr_entries[i]; j++) { + unsigned int bytes = PAGE_SIZE - offset; + + if (!len) + break; + + if (bytes > len) + bytes = len; + + sg_set_page(&sg[sg_idx++], + vmalloc_to_page(data), bytes, + offset_in_page(data)); + + data += bytes; + len -= bytes; + offset = 0; + } + } else { + sg_set_page(&sg[sg_idx++], virt_to_page(data), len, + offset_in_page(data)); + } + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); + return sg; +} + +int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, + unsigned int nvec, int enc) +{ + struct smb2_transform_hdr *tr_hdr = + (struct smb2_transform_hdr *)iov[0].iov_base; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + int rc; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; + struct aead_request *req; + char *iv; + unsigned int iv_len; + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + struct ksmbd_crypto_ctx *ctx; + + rc = ksmbd_get_encryption_key(conn, + le64_to_cpu(tr_hdr->SessionId), + enc, + key); + if (rc) { + pr_err("Could not get %scryption key\n", enc ? "en" : "de"); + return rc; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + ctx = ksmbd_crypto_ctx_find_gcm(); + else + ctx = ksmbd_crypto_ctx_find_ccm(); + if (!ctx) { + pr_err("crypto alloc failed\n"); + return -ENOMEM; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + tfm = CRYPTO_GCM(ctx); + else + tfm = CRYPTO_CCM(ctx); + + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); + if (rc) { + pr_err("Failed to set aead key %d\n", rc); + goto free_ctx; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + pr_err("Failed to set authsize %d\n", rc); + goto free_ctx; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + rc = -ENOMEM; + goto free_ctx; + } + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + sg = ksmbd_init_sg(iov, nvec, sign); + if (!sg) { + pr_err("Failed to init sg\n"); + rc = -ENOMEM; + goto free_req; + } + + iv_len = crypto_aead_ivsize(tfm); + iv = kzalloc(iv_len, GFP_KERNEL); + if (!iv) { + rc = -ENOMEM; + goto free_sg; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + } else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + } + + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + + if (enc) + rc = crypto_aead_encrypt(req); + else + rc = crypto_aead_decrypt(req); + if (rc) + goto free_iv; + + if (enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + +free_iv: + kfree(iv); +free_sg: + kfree(sg); +free_req: + kfree(req); +free_ctx: + ksmbd_release_crypto_ctx(ctx); + return rc; +} diff -Naur --no-dereference a/fs/ksmbd/auth.h b/fs/ksmbd/auth.h --- a/fs/ksmbd/auth.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/auth.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __AUTH_H__ +#define __AUTH_H__ + +#include "ntlmssp.h" + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +#define AUTH_GSS_LENGTH 96 +#define AUTH_GSS_PADDING 0 +#else +#define AUTH_GSS_LENGTH 74 +#define AUTH_GSS_PADDING 6 +#endif + +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define KSMBD_AUTH_NTLMSSP 0x0001 +#define KSMBD_AUTH_KRB5 0x0002 +#define KSMBD_AUTH_MSKRB5 0x0004 +#define KSMBD_AUTH_KRB5U2U 0x0008 + +struct ksmbd_session; +struct ksmbd_conn; +struct kvec; + +int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, + unsigned int nvec, int enc); +void ksmbd_copy_gss_neg_header(void *buf); +int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf, char *cryptkey); +int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, + int blen, char *domain_name, char *cryptkey); +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn); +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn); +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len); +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_sign_smb1_pdu(struct ksmbd_session *sess, struct kvec *iov, int n_vec, + char *sig); +#endif +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess); +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess); +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash); +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash); +#endif diff -Naur --no-dereference a/fs/ksmbd/build_ksmbd.sh b/fs/ksmbd/build_ksmbd.sh --- a/fs/ksmbd/build_ksmbd.sh 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/build_ksmbd.sh 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2019 Samsung Electronics Co., Ltd. +# + +#!/bin/sh + +KERNEL_SRC='' +COMP_FLAGS='' + +function is_module +{ + local ok=$(cat "$KERNEL_SRC"/.config | grep "CONFIG_SMB_SERVER=m") + + if [ "z$ok" == "z" ]; then + echo "1" + return 1 + fi + + echo "0" + return 0 +} + +function patch_fs_config +{ + local ok=$(pwd | grep -c "fs/smbd") + if [ "z$ok" != "z1" ]; then + echo "ERROR: please ``cd`` to fs/smbd" + exit 1 + fi + + KERNEL_SRC=$(pwd | sed -e 's/fs\/smbd//') + if [ ! -f "$KERNEL_SRC"/fs/Kconfig ]; then + echo "ERROR: please ``cd`` to fs/smbd" + exit 1 + fi + + ok=$(cat "$KERNEL_SRC"/fs/Makefile | grep smbd) + if [ "z$ok" == "z" ]; then + echo 'obj-$(CONFIG_SMB_SERVER) += smbd/' \ + >> "$KERNEL_SRC"/fs/Makefile + fi + + ok=$(cat "$KERNEL_SRC"/fs/Kconfig | grep smbd) + if [ "z$ok" == "z" ]; then + ok=$(cat "$KERNEL_SRC"/fs/Kconfig \ + | sed -e 's/fs\/cifs\/Kconfig/fs\/cifs\/Kconfig\"\nsource \"fs\/smbd\/Kconfig/' \ + > "$KERNEL_SRC"/fs/Kconfig.new) + if [ $? != 0 ]; then + exit 1 + fi + mv "$KERNEL_SRC"/fs/Kconfig.new "$KERNEL_SRC"/fs/Kconfig + fi + + ok=$(cat "$KERNEL_SRC"/.config | grep "CONFIG_NETWORK_FILESYSTEMS=y") + if [ "z$ok" == "z" ]; then + ok=$(echo "CONFIG_NETWORK_FILESYSTEMS=y" \ + >> "$KERNEL_SRC"/.config) + if [ $? != 0 ]; then + exit 1 + fi + fi + + ok=$(is_module) + if [ "z$ok" == "z1" ]; then + ok=$(echo "CONFIG_SMB_SERVER=m" >> "$KERNEL_SRC"/.config) + if [ $? != 0 ]; then + exit 1 + fi + ok=$(echo "CONFIG_SMB_INSECURE_SERVER=y" \ + >> "$KERNEL_SRC"/.config) + if [ $? != 0 ]; then + exit 1 + fi + fi +} + +function ksmbd_module_make +{ + echo "Running smbd make" + + local c="make "$COMP_FLAGS" -C "$KERNEL_SRC" M="$KERNEL_SRC"/fs/smbd" + + rm smbd.ko + + cd "$KERNEL_SRC" + echo $c + $c + cd "$KERNEL_SRC"/fs/smbd + + if [ $? != 0 ]; then + exit 1 + fi +} + +function ksmbd_module_install +{ + echo "Running smbd install" + + local ok=$(lsmod | grep -c smbd) + if [ "z$ok" == "z1" ]; then + sudo rmmod smbd + if [ $? -ne 0 ]; then + echo "ERROR: unable to rmmod smbd" + exit 1 + fi + fi + + ok=$(is_module) + if [ "z$ok" == "z1" ]; then + echo "It doesn't look like SMB_SERVER is as a kernel module" + exit 1 + fi + + if [ ! -f "$KERNEL_SRC"/fs/smbd/smbd.ko ]; then + echo "ERROR: smbd.ko was not found" + exit 1 + fi + + cd "$KERNEL_SRC" + if [ -f "/lib/modules/$(uname -r)/kernel/fs/smbd/smbd.ko*" ]; then + sudo rm /lib/modules/$(uname -r)/kernel/fs/smbd/smbd.ko* + sudo cp "$KERNEL_SRC"/fs/smbd/smbd.ko \ + /lib/modules/$(uname -r)/kernel/fs/smbd/smbd.ko + + local VER=$(make kernelrelease) + sudo depmod -A $VER + else + sudo make -C "$KERNEL_SRC" M="$KERNEL_SRC"/fs/smbd/ \ + modules_install + local VER=$(make kernelrelease) + sudo depmod -A $VER + fi + cd "$KERNEL_SRC"/fs/smbd +} + +function ksmbd_module_clean +{ + echo "Running smbd clean" + + cd "$KERNEL_SRC" + make -C "$KERNEL_SRC" M="$KERNEL_SRC"/fs/smbd/ clean + cd "$KERNEL_SRC"/fs/smbd +} + +function main +{ + patch_fs_config + + COMP_FLAGS="$FLAGS" + + case $1 in + clean) + ksmbd_module_clean + exit 0 + ;; + install) + ksmbd_module_make + ksmbd_module_install + exit 0 + ;; + make) + ksmbd_module_make + exit 0 + ;; + help) + echo "Usage: build_ksmbd.sh [clean | make | install]" + exit 0 + ;; + *) + ksmbd_module_make + exit 0 + ;; + esac +} + +main $1 diff -Naur --no-dereference a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c --- a/fs/ksmbd/connection.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/connection.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#ifdef CONFIG_SMB_INSECURE_SERVER +#include "smb1pdu.h" +#endif +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +static DEFINE_MUTEX(init_lock); + +static struct ksmbd_conn_ops default_conn_ops; + +LIST_HEAD(conn_list); +DEFINE_RWLOCK(conn_list_lock); + +/** + * ksmbd_conn_free() - free resources of the connection instance + * + * @conn: connection instance to be cleand up + * + * During the thread termination, the corresponding conn instance + * resources(sock/memory) are released and finally the conn object is freed. + */ +void ksmbd_conn_free(struct ksmbd_conn *conn) +{ + write_lock(&conn_list_lock); + list_del(&conn->conns_list); + write_unlock(&conn_list_lock); + + kvfree(conn->request_buf); + kfree(conn->preauth_info); + kfree(conn); +} + +/** + * ksmbd_conn_alloc() - initialize a new connection instance + * + * Return: ksmbd_conn struct on success, otherwise NULL + */ +struct ksmbd_conn *ksmbd_conn_alloc(void) +{ + struct ksmbd_conn *conn; + + conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + if (!conn) + return NULL; + + conn->need_neg = true; + conn->status = KSMBD_SESS_NEW; + conn->local_nls = load_nls("utf8"); + if (!conn->local_nls) + conn->local_nls = load_nls_default(); + atomic_set(&conn->req_running, 0); + atomic_set(&conn->r_count, 0); + conn->total_credits = 1; + + init_waitqueue_head(&conn->req_running_q); + INIT_LIST_HEAD(&conn->conns_list); + INIT_LIST_HEAD(&conn->sessions); + INIT_LIST_HEAD(&conn->requests); + INIT_LIST_HEAD(&conn->async_requests); + spin_lock_init(&conn->request_lock); + spin_lock_init(&conn->credits_lock); + ida_init(&conn->async_ida); + + spin_lock_init(&conn->llist_lock); + INIT_LIST_HEAD(&conn->lock_list); + + write_lock(&conn_list_lock); + list_add(&conn->conns_list, &conn_list); + write_unlock(&conn_list_lock); + return conn; +} + +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) +{ + struct ksmbd_conn *t; + bool ret = false; + + read_lock(&conn_list_lock); + list_for_each_entry(t, &conn_list, conns_list) { + if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) + continue; + + ret = true; + break; + } + read_unlock(&conn_list_lock); + return ret; +} + +void ksmbd_conn_enqueue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; +#ifdef CONFIG_SMB_INSECURE_SERVER + struct smb2_hdr *hdr = work->request_buf; + + if (hdr->ProtocolId == SMB2_PROTO_NUMBER) { + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->syncronous = true; + } + } else { + if (conn->ops->get_cmd_val(work) != SMB_COM_NT_CANCEL) + requests_queue = &conn->requests; + } +#else + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->syncronous = true; + } +#endif + + if (requests_queue) { + atomic_inc(&conn->req_running); + spin_lock(&conn->request_lock); + list_add_tail(&work->request_entry, requests_queue); + spin_unlock(&conn->request_lock); + } +} + +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) + return 0; + + if (!work->multiRsp) + atomic_dec(&conn->req_running); + spin_lock(&conn->request_lock); + if (!work->multiRsp) { + list_del_init(&work->request_entry); + if (work->syncronous == false) + list_del_init(&work->async_request_entry); + ret = 0; + } + spin_unlock(&conn->request_lock); + + wake_up_all(&conn->req_running_q); + return ret; +} + +static void ksmbd_conn_lock(struct ksmbd_conn *conn) +{ + mutex_lock(&conn->srv_mutex); +} + +static void ksmbd_conn_unlock(struct ksmbd_conn *conn) +{ + mutex_unlock(&conn->srv_mutex); +} + +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) +{ + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); +} + +int ksmbd_conn_write(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr = work->response_buf; + size_t len = 0; + int sent; + struct kvec iov[3]; + int iov_idx = 0; + + ksmbd_conn_try_dequeue_request(work); + if (!rsp_hdr) { + pr_err("NULL response header\n"); + return -EINVAL; + } + + if (work->tr_buf) { + iov[iov_idx] = (struct kvec) { work->tr_buf, + sizeof(struct smb2_transform_hdr) }; + len += iov[iov_idx++].iov_len; + } + + if (work->aux_payload_sz) { + iov[iov_idx] = (struct kvec) { rsp_hdr, work->resp_hdr_sz }; + len += iov[iov_idx++].iov_len; + iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; + len += iov[iov_idx++].iov_len; + } else { + if (work->tr_buf) + iov[iov_idx].iov_len = work->resp_hdr_sz; + else + iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4; + iov[iov_idx].iov_base = rsp_hdr; + len += iov[iov_idx++].iov_len; + } + + ksmbd_conn_lock(conn); + sent = conn->transport->ops->writev(conn->transport, &iov[0], + iov_idx, len, + work->need_invalidate_rkey, + work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { + pr_err("Failed to send message: %d\n", sent); + return sent; + } + + return 0; +} + +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_read) + ret = conn->transport->ops->rdma_read(conn->transport, + buf, buflen, + remote_key, remote_offset, + remote_len); + return ret; +} + +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, + u64 remote_offset, u32 remote_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_write) + ret = conn->transport->ops->rdma_write(conn->transport, + buf, buflen, + remote_key, remote_offset, + remote_len); + return ret; +} + +bool ksmbd_conn_alive(struct ksmbd_conn *conn) +{ + if (!ksmbd_server_running()) + return false; + + if (conn->status == KSMBD_SESS_EXITING) + return false; + + if (kthread_should_stop()) + return false; + + if (atomic_read(&conn->stats.open_files_count) > 0) + return true; + + /* + * Stop current session if the time that get last request from client + * is bigger than deadtime user configured and opening file count is + * zero. + */ + if (server_conf.deadtime > 0 && + time_after(jiffies, conn->last_active + server_conf.deadtime)) { + ksmbd_debug(CONN, "No response from client in %lu minutes\n", + server_conf.deadtime / SMB_ECHO_INTERVAL); + return false; + } + return true; +} + +/** + * ksmbd_conn_handler_loop() - session thread to listen on new smb requests + * @p: connection instance + * + * One thread each per connection + * + * Return: 0 on success + */ +int ksmbd_conn_handler_loop(void *p) +{ + struct ksmbd_conn *conn = (struct ksmbd_conn *)p; + struct ksmbd_transport *t = conn->transport; + unsigned int pdu_size; + char hdr_buf[4] = {0,}; + int size; + + mutex_init(&conn->srv_mutex); + __module_get(THIS_MODULE); + + if (t->ops->prepare && t->ops->prepare(t)) + goto out; + + conn->last_active = jiffies; + while (ksmbd_conn_alive(conn)) { + if (try_to_freeze()) + continue; + + kvfree(conn->request_buf); + conn->request_buf = NULL; + + size = t->ops->read(t, hdr_buf, sizeof(hdr_buf)); + if (size != sizeof(hdr_buf)) + break; + + pdu_size = get_rfc1002_len(hdr_buf); + ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); + + /* make sure we have enough to get to SMB header end */ + if (!ksmbd_pdu_size_has_room(pdu_size)) { + ksmbd_debug(CONN, "SMB request too short (%u bytes)\n", + pdu_size); + continue; + } + + if (pdu_size > MAX_STREAM_PROT_LEN) + continue; + + /* 4 for rfc1002 length field */ + size = pdu_size + 4; + conn->request_buf = kvmalloc(size, GFP_KERNEL); + if (!conn->request_buf) + continue; + + memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); + if (!ksmbd_smb_request(conn)) + break; + + /* + * We already read 4 bytes to find out PDU size, now + * read in PDU + */ + size = t->ops->read(t, conn->request_buf + 4, pdu_size); + if (size < 0) { + pr_err("sock_read failed: %d\n", size); + break; + } + + if (size != pdu_size) { + pr_err("PDU error. Read: %d, Expected: %d\n", + size, pdu_size); + continue; + } + + if (!default_conn_ops.process_fn) { + pr_err("No connection request callback\n"); + break; + } + + if (default_conn_ops.process_fn(conn)) { + pr_err("Cannot handle request\n"); + break; + } + } + +out: + /* Wait till all reference dropped to the Server object*/ + while (atomic_read(&conn->r_count) > 0) + schedule_timeout(HZ); + + unload_nls(conn->local_nls); + if (default_conn_ops.terminate_fn) + default_conn_ops.terminate_fn(conn); + t->ops->disconnect(t); + module_put(THIS_MODULE); + return 0; +} + +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) +{ + default_conn_ops.process_fn = ops->process_fn; + default_conn_ops.terminate_fn = ops->terminate_fn; +} + +int ksmbd_conn_transport_init(void) +{ + int ret; + + mutex_lock(&init_lock); + ret = ksmbd_tcp_init(); + if (ret) { + pr_err("Failed to init TCP subsystem: %d\n", ret); + goto out; + } + + ret = ksmbd_rdma_init(); + if (ret) { + pr_err("Failed to init RDMA subsystem: %d\n", ret); + goto out; + } +out: + mutex_unlock(&init_lock); + return ret; +} + +static void stop_sessions(void) +{ + struct ksmbd_conn *conn; + +again: + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + struct task_struct *task; + + task = conn->transport->handler; + if (task) + ksmbd_debug(CONN, "Stop session handler %s/%d\n", + task->comm, task_pid_nr(task)); + conn->status = KSMBD_SESS_EXITING; + } + read_unlock(&conn_list_lock); + + if (!list_empty(&conn_list)) { + schedule_timeout_interruptible(HZ / 10); /* 100ms */ + goto again; + } +} + +void ksmbd_conn_transport_destroy(void) +{ + mutex_lock(&init_lock); + ksmbd_tcp_destroy(); + ksmbd_rdma_destroy(); + stop_sessions(); + mutex_unlock(&init_lock); +} diff -Naur --no-dereference a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h --- a/fs/ksmbd/connection.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/connection.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_CONNECTION_H__ +#define __KSMBD_CONNECTION_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smb_common.h" +#include "ksmbd_work.h" + +#define KSMBD_SOCKET_BACKLOG 16 + +/* + * WARNING + * + * This is nothing but a HACK. Session status should move to channel + * or to session. As of now we have 1 tcp_conn : 1 ksmbd_session, but + * we need to change it to 1 tcp_conn : N ksmbd_sessions. + */ +enum { + KSMBD_SESS_NEW = 0, + KSMBD_SESS_GOOD, + KSMBD_SESS_EXITING, + KSMBD_SESS_NEED_RECONNECT, + KSMBD_SESS_NEED_NEGOTIATE +}; + +struct ksmbd_stats { + atomic_t open_files_count; + atomic64_t request_served; +}; + +struct ksmbd_transport; + +struct ksmbd_conn { + struct smb_version_values *vals; + struct smb_version_ops *ops; + struct smb_version_cmds *cmds; + unsigned int max_cmds; + struct mutex srv_mutex; + int status; + unsigned int cli_cap; + char *request_buf; + struct ksmbd_transport *transport; + struct nls_table *local_nls; + struct list_head conns_list; + /* smb session 1 per user */ + struct list_head sessions; + unsigned long last_active; + /* How many request are running currently */ + atomic_t req_running; + /* References which are made for this Server object*/ + atomic_t r_count; + unsigned short total_credits; + unsigned short max_credits; + spinlock_t credits_lock; + wait_queue_head_t req_running_q; + /* Lock to protect requests list*/ + spinlock_t request_lock; + struct list_head requests; + struct list_head async_requests; + int connection_type; + struct ksmbd_stats stats; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + struct ntlmssp_auth ntlmssp; + + spinlock_t llist_lock; + struct list_head lock_list; + + struct preauth_integrity_info *preauth_info; + + bool need_neg; + unsigned int auth_mechs; + unsigned int preferred_auth_mech; + bool sign; + bool use_spnego:1; + __u16 cli_sec_mode; + __u16 srv_sec_mode; + /* dialect index that server chose */ + __u16 dialect; + + char *mechToken; + + struct ksmbd_conn_ops *conn_ops; + + /* Preauth Session Table */ + struct list_head preauth_sess_table; + + struct sockaddr_storage peer_addr; + + /* Identifier for async message */ + struct ida async_ida; + + __le16 cipher_type; + __le16 compress_algorithm; + bool posix_ext_supported; + bool signing_negotiated; + __le16 signing_algorithm; + bool binding; +}; + +struct ksmbd_conn_ops { + int (*process_fn)(struct ksmbd_conn *conn); + int (*terminate_fn)(struct ksmbd_conn *conn); +}; + +struct ksmbd_transport_ops { + int (*prepare)(struct ksmbd_transport *t); + void (*disconnect)(struct ksmbd_transport *t); + int (*read)(struct ksmbd_transport *t, char *buf, unsigned int size); + int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, + int size, bool need_invalidate_rkey, + unsigned int remote_key); + int (*rdma_read)(struct ksmbd_transport *t, void *buf, unsigned int len, + u32 remote_key, u64 remote_offset, u32 remote_len); + int (*rdma_write)(struct ksmbd_transport *t, void *buf, + unsigned int len, u32 remote_key, u64 remote_offset, + u32 remote_len); +}; + +struct ksmbd_transport { + struct ksmbd_conn *conn; + struct ksmbd_transport_ops *ops; + struct task_struct *handler; +}; + +#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) +#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) +#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) + +extern struct list_head conn_list; +extern rwlock_t conn_list_lock; + +bool ksmbd_conn_alive(struct ksmbd_conn *conn); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); +struct ksmbd_conn *ksmbd_conn_alloc(void); +void ksmbd_conn_free(struct ksmbd_conn *conn); +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); +int ksmbd_conn_write(struct ksmbd_work *work); +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len); +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len); +void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); +int ksmbd_conn_handler_loop(void *p); +int ksmbd_conn_transport_init(void); +void ksmbd_conn_transport_destroy(void); + +/* + * WARNING + * + * This is a hack. We will move status to a proper place once we land + * a multi-sessions support. + */ +static inline bool ksmbd_conn_good(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_GOOD; +} + +static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_NEED_RECONNECT; +} + +static inline bool ksmbd_conn_exiting(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_EXITING; +} + +static inline void ksmbd_conn_set_good(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_GOOD; +} + +static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_NEED_RECONNECT; +} + +static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_EXITING; +} +#endif /* __CONNECTION_H__ */ diff -Naur --no-dereference a/fs/ksmbd/crypto_ctx.c b/fs/ksmbd/crypto_ctx.c --- a/fs/ksmbd/crypto_ctx.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/crypto_ctx.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "crypto_ctx.h" + +struct crypto_ctx_list { + spinlock_t ctx_lock; + int avail_ctx; + struct list_head idle_ctx; + wait_queue_head_t ctx_wait; +}; + +static struct crypto_ctx_list ctx_list; + +static inline void free_aead(struct crypto_aead *aead) +{ + if (aead) + crypto_free_aead(aead); +} + +static void free_shash(struct shash_desc *shash) +{ + if (shash) { + crypto_free_shash(shash->tfm); + kfree(shash); + } +} + +static struct crypto_aead *alloc_aead(int id) +{ + struct crypto_aead *tfm = NULL; + + switch (id) { + case CRYPTO_AEAD_AES_GCM: + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + break; + case CRYPTO_AEAD_AES_CCM: + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + break; + default: + pr_err("Does not support encrypt ahead(id : %d)\n", id); + return NULL; + } + + if (IS_ERR(tfm)) { + pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); + return NULL; + } + + return tfm; +} + +static struct shash_desc *alloc_shash_desc(int id) +{ + struct crypto_shash *tfm = NULL; + struct shash_desc *shash; + + switch (id) { + case CRYPTO_SHASH_HMACMD5: + tfm = crypto_alloc_shash("hmac(md5)", 0, 0); + break; + case CRYPTO_SHASH_HMACSHA256: + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + break; + case CRYPTO_SHASH_CMACAES: + tfm = crypto_alloc_shash("cmac(aes)", 0, 0); + break; + case CRYPTO_SHASH_SHA256: + tfm = crypto_alloc_shash("sha256", 0, 0); + break; + case CRYPTO_SHASH_SHA512: + tfm = crypto_alloc_shash("sha512", 0, 0); + break; + case CRYPTO_SHASH_MD4: + tfm = crypto_alloc_shash("md4", 0, 0); + break; + case CRYPTO_SHASH_MD5: + tfm = crypto_alloc_shash("md5", 0, 0); + break; + default: + return NULL; + } + + if (IS_ERR(tfm)) + return NULL; + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) + crypto_free_shash(tfm); + else + shash->tfm = tfm; + return shash; +} + +static void ctx_free(struct ksmbd_crypto_ctx *ctx) +{ + int i; + + for (i = 0; i < CRYPTO_SHASH_MAX; i++) + free_shash(ctx->desc[i]); + for (i = 0; i < CRYPTO_AEAD_MAX; i++) + free_aead(ctx->ccmaes[i]); + kfree(ctx); +} + +static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (1) { + spin_lock(&ctx_list.ctx_lock); + if (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + spin_unlock(&ctx_list.ctx_lock); + return ctx; + } + + if (ctx_list.avail_ctx > num_online_cpus()) { + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + + ctx_list.avail_ctx++; + spin_unlock(&ctx_list.ctx_lock); + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) { + spin_lock(&ctx_list.ctx_lock); + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + break; + } + return ctx; +} + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) +{ + if (!ctx) + return; + + spin_lock(&ctx_list.ctx_lock); + if (ctx_list.avail_ctx <= num_online_cpus()) { + list_add(&ctx->list, &ctx_list.idle_ctx); + spin_unlock(&ctx_list.ctx_lock); + wake_up(&ctx_list.ctx_wait); + return; + } + + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + ctx_free(ctx); +} + +static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_SHASH_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->desc[id]) + return ctx; + + ctx->desc[id] = alloc_shash_desc(id); + if (ctx->desc[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5); +} + +static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_AEAD_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->ccmaes[id]) + return ctx; + + ctx->ccmaes[id] = alloc_aead(id); + if (ctx->ccmaes[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); +} + +void ksmbd_crypto_destroy(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + ctx_free(ctx); + } +} + +int ksmbd_crypto_create(void) +{ + struct ksmbd_crypto_ctx *ctx; + + spin_lock_init(&ctx_list.ctx_lock); + INIT_LIST_HEAD(&ctx_list.idle_ctx); + init_waitqueue_head(&ctx_list.ctx_wait); + ctx_list.avail_ctx = 1; + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + list_add(&ctx->list, &ctx_list.idle_ctx); + return 0; +} diff -Naur --no-dereference a/fs/ksmbd/crypto_ctx.h b/fs/ksmbd/crypto_ctx.h --- a/fs/ksmbd/crypto_ctx.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/crypto_ctx.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __CRYPTO_CTX_H__ +#define __CRYPTO_CTX_H__ + +#include +#include + +enum { + CRYPTO_SHASH_HMACMD5 = 0, + CRYPTO_SHASH_HMACSHA256, + CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_SHA256, + CRYPTO_SHASH_SHA512, + CRYPTO_SHASH_MD4, + CRYPTO_SHASH_MD5, + CRYPTO_SHASH_MAX, +}; + +enum { + CRYPTO_AEAD_AES_GCM = 16, + CRYPTO_AEAD_AES_CCM, + CRYPTO_AEAD_MAX, +}; + +enum { + CRYPTO_BLK_ECBDES = 32, + CRYPTO_BLK_MAX, +}; + +struct ksmbd_crypto_ctx { + struct list_head list; + + struct shash_desc *desc[CRYPTO_SHASH_MAX]; + struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; +}; + +#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) +#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) +#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) +#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) +#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) +#define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4]) +#define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5]) + +#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) +#define CRYPTO_HMACSHA256_TFM(c)\ + ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) +#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) +#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) +#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) +#define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm) +#define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm) + +#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) +#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); +void ksmbd_crypto_destroy(void); +int ksmbd_crypto_create(void); + +#endif /* __CRYPTO_CTX_H__ */ diff -Naur --no-dereference a/fs/ksmbd/glob.h b/fs/ksmbd/glob.h --- a/fs/ksmbd/glob.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/glob.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_GLOB_H +#define __KSMBD_GLOB_H + +#include + +#include "unicode.h" +#include "vfs_cache.h" + +#define KSMBD_VERSION "3.4.3" + +extern int ksmbd_debug_types; + +#define KSMBD_DEBUG_SMB BIT(0) +#define KSMBD_DEBUG_AUTH BIT(1) +#define KSMBD_DEBUG_VFS BIT(2) +#define KSMBD_DEBUG_OPLOCK BIT(3) +#define KSMBD_DEBUG_IPC BIT(4) +#define KSMBD_DEBUG_CONN BIT(5) +#define KSMBD_DEBUG_RDMA BIT(6) +#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ + KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ + KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ + KSMBD_DEBUG_RDMA) + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#ifdef SUBMOD_NAME +#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt +#else +#define pr_fmt(fmt) "ksmbd: " fmt +#endif + +#define ksmbd_debug(type, fmt, ...) \ + do { \ + if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ + pr_info(fmt, ##__VA_ARGS__); \ + } while (0) + +#define UNICODE_LEN(x) ((x) * 2) + +#ifdef CONFIG_SMB_INSECURE_SERVER +/* ksmbd misc functions */ +extern void ntstatus_to_dos(__le32 ntstatus, __u8 *eclass, __le16 *ecode); +#endif + +#ifndef LOOKUP_NO_SYMLINKS +#define LOOKUP_NO_SYMLINKS 0 +#endif + +#endif /* __KSMBD_GLOB_H */ diff -Naur --no-dereference a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig --- a/fs/ksmbd/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/Kconfig 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,79 @@ +config SMB_SERVER + tristate "SMB3 server support (EXPERIMENTAL)" + depends on INET + depends on MULTIUSER + depends on FILE_LOCKING + select NLS + select NLS_UTF8 + select CRYPTO + select CRYPTO_MD4 + select CRYPTO_MD5 + select CRYPTO_HMAC + select CRYPTO_ECB + select CRYPTO_LIB_DES + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_SHA512 + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + select ASN1 + select OID_REGISTRY + select CRC32 + default n + help + Choose Y here if you want to allow SMB3 compliant clients + to access files residing on this system using SMB3 protocol. + To compile the SMB3 server support as a module, + choose M here: the module will be called ksmbd. + + You may choose to use a samba server instead, in which + case you can choose N here. + + You also need to install user space programs which can be found + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is + available via README file + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). + + ksmbd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session + establishment via NTLM or NTLMv2. + +config SMB_INSECURE_SERVER + bool "Support for insecure SMB1/CIFS and SMB2.0 protocols" + depends on SMB_SERVER && INET + select NLS + default n + + help + This enables deprecated insecure protocols dialects: SMB1/CIFS + and SMB2.0 + +config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" + depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + select SG_POOL + default n + + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + +config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER + default y + + help + Prevent unprivileged processes to start the ksmbd kernel server. + +config SMB_SERVER_KERBEROS5 + bool "Support for Kerberos 5" + depends on SMB_SERVER + default n diff -Naur --no-dereference a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h --- a/fs/ksmbd/ksmbd_netlink.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ksmbd_netlink.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,397 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * linux-ksmbd-devel@lists.sourceforge.net + */ + +#ifndef _LINUX_KSMBD_SERVER_H +#define _LINUX_KSMBD_SERVER_H + +#include + +/* + * This is a userspace ABI to communicate data between ksmbd and user IPC + * daemon using netlink. This is added to track and cache user account DB + * and share configuration info from userspace. + * + * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) + * This event is to check whether user IPC daemon is alive. If user IPC + * daemon is dead, ksmbd keep existing connection till disconnecting and + * new connection will be denied. + * + * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) + * This event is to receive the information that initializes the ksmbd + * server from the user IPC daemon and to start the server. The global + * section parameters are given from smb.conf as initialization + * information. + * + * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) + * This event is to shutdown ksmbd server. + * + * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) + * This event is to get user account info to user IPC daemon. + * + * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) + * This event is to get net share configuration info. + * + * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) + * This event is to get session and tree connect info. + * + * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) + * This event is to send tree disconnect info to user IPC daemon. + * + * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) + * This event is to send logout request to user IPC daemon. + * + * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) + * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, + * samr to be processed in userspace. + * + * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) + * This event is to make kerberos authentication to be processed in + * userspace. + */ + +#define KSMBD_GENL_NAME "SMBD_GENL" +#define KSMBD_GENL_VERSION 0x01 + +#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 +#define KSMBD_REQ_MAX_HASH_SZ 18 +#define KSMBD_REQ_MAX_SHARE_NAME 64 + +/* + * IPC heartbeat frame to check whether user IPC daemon is alive. + */ +struct ksmbd_heartbeat { + __u32 handle; +}; + +/* + * Global config flags. + */ +#define KSMBD_GLOBAL_FLAG_INVALID (0) +#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) +#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) + +/* + * IPC request for ksmbd server startup + */ +struct ksmbd_startup_request { + __u32 flags; /* Flags for global config */ + __s32 signing; /* Signing enabled */ + __s8 min_prot[16]; /* The minimum SMB protocol version */ + __s8 max_prot[16]; /* The maximum SMB protocol version */ + __s8 netbios_name[16]; + __s8 work_group[64]; /* Workgroup */ + __s8 server_string[64]; /* Server string */ + __u16 tcp_port; /* tcp port */ + __u16 ipc_timeout; /* + * specifies the number of seconds + * server will wait for the userspace to + * reply to heartbeat frames. + */ + __u32 deadtime; /* Number of minutes of inactivity */ + __u32 file_max; /* Limits the maximum number of open files */ + __u32 smb2_max_write; /* MAX write size */ + __u32 smb2_max_read; /* MAX read size */ + __u32 smb2_max_trans; /* MAX trans size */ + __u32 share_fake_fscaps; /* + * Support some special application that + * makes QFSINFO calls to check whether + * we set the SPARSE_FILES bit (0x40). + */ + __u32 sub_auth[3]; /* Subauth value for Security ID */ + __u32 ifc_list_sz; /* interfaces list size */ + __s8 ____payload[]; +}; + +#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) + +/* + * IPC request to shutdown ksmbd server. + */ +struct ksmbd_shutdown_request { + __s32 reserved; +}; + +/* + * IPC user login request. + */ +struct ksmbd_login_request { + __u32 handle; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ +}; + +/* + * IPC user login response. + */ +struct ksmbd_login_response { + __u32 handle; + __u32 gid; /* group id */ + __u32 uid; /* user id */ + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u16 status; + __u16 hash_sz; /* hash size */ + __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ +}; + +/* + * IPC request to fetch net share config. + */ +struct ksmbd_share_config_request { + __u32 handle; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ +}; + +/* + * IPC response to the net share config request. + */ +struct ksmbd_share_config_response { + __u32 handle; + __u32 flags; + __u16 create_mask; + __u16 directory_mask; + __u16 force_create_mode; + __u16 force_directory_mode; + __u16 force_uid; + __u16 force_gid; + __u32 veto_list_sz; + __s8 ____payload[]; +}; + +#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) + +static inline char * +ksmbd_share_config_path(struct ksmbd_share_config_response *sc) +{ + char *p = sc->____payload; + + if (sc->veto_list_sz) + p += sc->veto_list_sz + 1; + + return p; +} + +/* + * IPC request for tree connection. This request include session and tree + * connect info from client. + */ +struct ksmbd_tree_connect_request { + __u32 handle; + __u16 account_flags; + __u16 flags; + __u64 session_id; + __u64 connect_id; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; + __s8 peer_addr[64]; +}; + +/* + * IPC Response structure for tree connection. + */ +struct ksmbd_tree_connect_response { + __u32 handle; + __u16 status; + __u16 connection_flags; +}; + +/* + * IPC Request struture to disconnect tree connection. + */ +struct ksmbd_tree_disconnect_request { + __u64 session_id; /* session id */ + __u64 connect_id; /* tree connection id */ +}; + +/* + * IPC Response structure to logout user account. + */ +struct ksmbd_logout_request { + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 account_flags; +}; + +/* + * RPC command structure to send rpc request like srvsvc or wkssvc to + * IPC user daemon. + */ +struct ksmbd_rpc_command { + __u32 handle; + __u32 flags; + __u32 payload_sz; + __u8 payload[]; +}; + +/* + * IPC Request Kerberos authentication + */ +struct ksmbd_spnego_authen_request { + __u32 handle; + __u16 spnego_blob_len; /* the length of spnego_blob */ + __u8 spnego_blob[0]; /* + * the GSS token from SecurityBuffer of + * SMB2 SESSION SETUP request + */ +}; + +/* + * Response data which includes the GSS token and the session key generated by + * user daemon. + */ +struct ksmbd_spnego_authen_response { + __u32 handle; + struct ksmbd_login_response login_response; /* + * the login response with + * a user identified by the + * GSS token from a client + */ + __u16 session_key_len; /* the length of the session key */ + __u16 spnego_blob_len; /* + * the length of the GSS token which will be + * stored in SecurityBuffer of SMB2 SESSION + * SETUP response + */ + __u8 payload[]; /* session key + AP_REP */ +}; + +/* + * This also used as NETLINK attribute type value. + * + * NOTE: + * Response message type value should be equal to + * request message type value + 1. + */ +enum ksmbd_event { + KSMBD_EVENT_UNSPEC = 0, + KSMBD_EVENT_HEARTBEAT_REQUEST, + + KSMBD_EVENT_STARTING_UP, + KSMBD_EVENT_SHUTTING_DOWN, + + KSMBD_EVENT_LOGIN_REQUEST, + KSMBD_EVENT_LOGIN_RESPONSE = 5, + + KSMBD_EVENT_SHARE_CONFIG_REQUEST, + KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + + KSMBD_EVENT_TREE_CONNECT_REQUEST, + KSMBD_EVENT_TREE_CONNECT_RESPONSE, + + KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, + + KSMBD_EVENT_LOGOUT_REQUEST, + + KSMBD_EVENT_RPC_REQUEST, + KSMBD_EVENT_RPC_RESPONSE, + + KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, + + KSMBD_EVENT_MAX +}; + +/* + * Enumeration for IPC tree connect status. + */ +enum KSMBD_TREE_CONN_STATUS { + KSMBD_TREE_CONN_STATUS_OK = 0, + KSMBD_TREE_CONN_STATUS_NOMEM, + KSMBD_TREE_CONN_STATUS_NO_SHARE, + KSMBD_TREE_CONN_STATUS_NO_USER, + KSMBD_TREE_CONN_STATUS_INVALID_USER, + KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, + KSMBD_TREE_CONN_STATUS_CONN_EXIST, + KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, + KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, + KSMBD_TREE_CONN_STATUS_ERROR, +}; + +/* + * User config flags. + */ +#define KSMBD_USER_FLAG_INVALID (0) +#define KSMBD_USER_FLAG_OK BIT(0) +#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) +#define KSMBD_USER_FLAG_BAD_UID BIT(2) +#define KSMBD_USER_FLAG_BAD_USER BIT(3) +#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) +#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) + +/* + * Share config flags. + */ +#define KSMBD_SHARE_FLAG_INVALID (0) +#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +#define KSMBD_SHARE_FLAG_READONLY BIT(3) +#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +#define KSMBD_SHARE_FLAG_PIPE BIT(8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) + +/* + * Tree connect request flags. + */ +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) + +/* + * Tree connect flags. + */ +#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) +#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) +#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) +#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) + +/* + * RPC over IPC. + */ +#define KSMBD_RPC_METHOD_RETURN BIT(0) +#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) +#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) +#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_OPEN_METHOD BIT(4) +#define KSMBD_RPC_WRITE_METHOD BIT(5) +#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_CLOSE_METHOD BIT(7) +#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) +#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) +#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) +#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) + +/* + * RPC status definitions. + */ +#define KSMBD_RPC_OK 0 +#define KSMBD_RPC_EBAD_FUNC 0x00000001 +#define KSMBD_RPC_EACCESS_DENIED 0x00000005 +#define KSMBD_RPC_EBAD_FID 0x00000006 +#define KSMBD_RPC_ENOMEM 0x00000008 +#define KSMBD_RPC_EBAD_DATA 0x0000000D +#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 +#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 +#define KSMBD_RPC_EMORE_DATA 0x000000EA +#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C +#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 + +#define KSMBD_CONFIG_OPT_DISABLED 0 +#define KSMBD_CONFIG_OPT_ENABLED 1 +#define KSMBD_CONFIG_OPT_AUTO 2 +#define KSMBD_CONFIG_OPT_MANDATORY 3 + +#endif /* _LINUX_KSMBD_SERVER_H */ diff -Naur --no-dereference a/fs/ksmbd/ksmbd.rst b/fs/ksmbd/ksmbd.rst --- a/fs/ksmbd/ksmbd.rst 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ksmbd.rst 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,165 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +KSMBD - SMB3 Kernel Server +========================== + +ksmbd is a linux kernel server which implements SMB3 protocol in kernel space +for sharing files over network. + +KSMBD architecture +================== + +The subset of performance related operations belong in kernelspace and +the other subset which belong to operations which are not really related with +performance in userspace. So, DCE/RPC management that has historically resulted +into number of buffer overflow issues and dangerous security bugs and user +account management are implemented in user space as ksmbd.mountd. +File operations that are related with performance (open/read/write/close etc.) +in kernel space (ksmbd). This also allows for easier integration with VFS +interface for all file operations. + +ksmbd (kernel daemon) +--------------------- + +When the server daemon is started, It starts up a forker thread +(ksmbd/interface name) at initialization time and open a dedicated port 445 +for listening to SMB requests. Whenever new clients make request, Forker +thread will accept the client connection and fork a new thread for dedicated +communication channel between the client and the server. It allows for parallel +processing of SMB requests(commands) from clients as well as allowing for new +clients to make new connections. Each instance is named ksmbd/1~n(port number) +to indicate connected clients. Depending on the SMB request types, each new +thread can decide to pass through the commands to the user space (ksmbd.mountd), +currently DCE/RPC commands are identified to be handled through the user space. +To further utilize the linux kernel, it has been chosen to process the commands +as workitems and to be executed in the handlers of the ksmbd-io kworker threads. +It allows for multiplexing of the handlers as the kernel take care of initiating +extra worker threads if the load is increased and vice versa, if the load is +decreased it destroys the extra worker threads. So, after connection is +established with client. Dedicated ksmbd/1..n(port number) takes complete +ownership of receiving/parsing of SMB commands. Each received command is worked +in parallel i.e., There can be multiple clients commands which are worked in +parallel. After receiving each command a separated kernel workitem is prepared +for each command which is further queued to be handled by ksmbd-io kworkers. +So, each SMB workitem is queued to the kworkers. This allows the benefit of load +sharing to be managed optimally by the default kernel and optimizing client +performance by handling client commands in parallel. + +ksmbd.mountd (user space daemon) +-------------------------------- + +ksmbd.mountd is userspace process to, transfer user account and password that +are registered using ksmbd.adduser(part of utils for user space). Further it +allows sharing information parameters that parsed from smb.conf to ksmbd in +kernel. For the execution part it has a daemon which is continuously running +and connected to the kernel interface using netlink socket, it waits for the +requests(dcerpc and share/user info). It handles RPC calls (at a minimum few +dozen) that are most important for file server from NetShareEnum and +NetServerGetInfo. Complete DCE/RPC response is prepared from the user space +and passed over to the associated kernel thread for the client. + + +KSMBD Feature Status +==================== + +============================== ================================================= +Feature name Status +============================== ================================================= +Dialects Supported. SMB2.1 SMB3.0, SMB3.1.1 dialects + (intentionally excludes security vulnerable SMB1 + dialect). +Auto Negotiation Supported. +Compound Request Supported. +Oplock Cache Mechanism Supported. +SMB2 leases(v1 lease) Supported. +Directory leases(v2 lease) Planned for future. +Multi-credits Supported. +NTLM/NTLMv2 Supported. +HMAC-SHA256 Signing Supported. +Secure negotiate Supported. +Signing Update Supported. +Pre-authentication integrity Supported. +SMB3 encryption(CCM, GCM) Supported. (CCM and GCM128 supported, GCM256 in + progress) +SMB direct(RDMA) Partially Supported. SMB3 Multi-channel is + required to connect to Windows client. +SMB3 Multi-channel Partially Supported. Planned to implement + replay/retry mechanisms for future. +SMB3.1.1 POSIX extension Supported. +ACLs Partially Supported. only DACLs available, SACLs + (auditing) is planned for the future. For + ownership (SIDs) ksmbd generates random subauth + values(then store it to disk) and use uid/gid + get from inode as RID for local domain SID. + The current acl implementation is limited to + standalone server, not a domain member. + Integration with Samba tools is being worked on + to allow future support for running as a domain + member. +Kerberos Supported. +Durable handle v1,v2 Planned for future. +Persistent handle Planned for future. +SMB2 notify Planned for future. +Sparse file support Supported. +DCE/RPC support Partially Supported. a few calls(NetShareEnumAll, + NetServerGetInfo, SAMR, LSARPC) that are needed + for file server handled via netlink interface + from ksmbd.mountd. Additional integration with + Samba tools and libraries via upcall is being + investigated to allow support for additional + DCE/RPC management calls (and future support + for Witness protocol e.g.) +ksmbd/nfsd interoperability Planned for future. The features that ksmbd + support are Leases, Notify, ACLs and Share modes. +============================== ================================================= + + +How to run +========== + +1. Download ksmbd-tools and compile them. + - https://github.com/cifsd-team/ksmbd-tools + +2. Create user/password for SMB share. + + # mkdir /etc/ksmbd/ + # ksmbd.adduser -a + +3. Create /etc/ksmbd/smb.conf file, add SMB share in smb.conf file + - Refer smb.conf.example and + https://github.com/cifsd-team/ksmbd-tools/blob/master/Documentation/configuration.txt + +4. Insert ksmbd.ko module + + # insmod ksmbd.ko + +5. Start ksmbd user space daemon + # ksmbd.mountd + +6. Access share from Windows or Linux using CIFS + +Shutdown KSMBD +============== + +1. kill user and kernel space daemon + # sudo ksmbd.control -s + +How to turn debug print on +========================== + +Each layer +/sys/class/ksmbd-control/debug + +1. Enable all component prints + # sudo ksmbd.control -d "all" + +2. Enable one of components(smb, auth, vfs, oplock, ipc, conn, rdma) + # sudo ksmbd.control -d "smb" + +3. Show what prints are enable. + # cat/sys/class/ksmbd-control/debug + [smb] auth vfs oplock ipc conn [rdma] + +4. Disable prints: + If you try the selected component once more, It is disabled without brackets. diff -Naur --no-dereference a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 --- a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,31 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegTokenInit ::= + SEQUENCE { + mechTypes + [0] MechTypeList, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit, + negTokenTarg + [1] ANY + } diff -Naur --no-dereference a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 --- a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,19 @@ +GSSAPI ::= + CHOICE { + negTokenInit + [0] ANY, + negTokenTarg + [1] NegTokenTarg + } + +NegTokenTarg ::= + SEQUENCE { + negResult + [0] ENUMERATED OPTIONAL, + supportedMech + [1] OBJECT IDENTIFIER OPTIONAL, + responseToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } diff -Naur --no-dereference a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c --- a/fs/ksmbd/ksmbd_work.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ksmbd_work.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "server.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/ksmbd_ida.h" + +static struct kmem_cache *work_cache; +static struct workqueue_struct *ksmbd_wq; + +struct ksmbd_work *ksmbd_alloc_work_struct(void) +{ + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + + if (work) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + INIT_LIST_HEAD(&work->request_entry); + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); + } + return work; +} + +void ksmbd_free_work_struct(struct ksmbd_work *work) +{ + WARN_ON(work->saved_cred != NULL); + + kvfree(work->response_buf); + kvfree(work->aux_payload_buf); + kfree(work->tr_buf); + kvfree(work->request_buf); + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +} + +void ksmbd_work_pool_destroy(void) +{ + kmem_cache_destroy(work_cache); +} + +int ksmbd_work_pool_init(void) +{ + work_cache = kmem_cache_create("ksmbd_work_cache", + sizeof(struct ksmbd_work), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!work_cache) + return -ENOMEM; + return 0; +} + +int ksmbd_workqueue_init(void) +{ + ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); + if (!ksmbd_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_workqueue_destroy(void) +{ + destroy_workqueue(ksmbd_wq); + ksmbd_wq = NULL; +} + +bool ksmbd_queue_work(struct ksmbd_work *work) +{ + return queue_work(ksmbd_wq, &work->work); +} diff -Naur --no-dereference a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h --- a/fs/ksmbd/ksmbd_work.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ksmbd_work.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_WORK_H__ +#define __KSMBD_WORK_H__ + +#include +#include + +struct ksmbd_conn; +struct ksmbd_session; +struct ksmbd_tree_connect; + +enum { + KSMBD_WORK_ACTIVE = 0, + KSMBD_WORK_CANCELLED, + KSMBD_WORK_CLOSED, +}; + +/* one of these for every pending CIFS request at the connection */ +struct ksmbd_work { + /* Server corresponding to this mid */ + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tcon; + + /* Pointer to received SMB header */ + void *request_buf; + /* Response buffer */ + void *response_buf; + + /* Read data buffer */ + void *aux_payload_buf; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE + * command is present in compound request + */ + u64 compound_fid; + u64 compound_pfid; + u64 compound_sid; + + const struct cred *saved_cred; + + /* Number of granted credits */ + unsigned int credits_granted; + + /* response smb header size */ + unsigned int resp_hdr_sz; + unsigned int response_sz; + /* Read data count */ + unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; + /* Multiple responses for one request e.g. SMB ECHO */ + bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ + bool syncronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; + /* cancel works */ + int async_id; + void **cancel_argv; + void (*cancel_fn)(void **argv); + + struct work_struct work; + /* List head at conn->requests */ + struct list_head request_entry; + /* List head at conn->async_requests */ + struct list_head async_request_entry; + struct list_head fp_entry; + struct list_head interim_entry; +}; + +/** + * ksmbd_resp_buf_next - Get next buffer on compound response. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) +{ + return work->response_buf + work->next_smb2_rsp_hdr_off; +} + +/** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) +{ + return work->request_buf + work->next_smb2_rcv_hdr_off; +} + +struct ksmbd_work *ksmbd_alloc_work_struct(void); +void ksmbd_free_work_struct(struct ksmbd_work *work); + +void ksmbd_work_pool_destroy(void); +int ksmbd_work_pool_init(void); + +int ksmbd_workqueue_init(void); +void ksmbd_workqueue_destroy(void); +bool ksmbd_queue_work(struct ksmbd_work *work); + +#endif /* __KSMBD_WORK_H__ */ diff -Naur --no-dereference a/fs/ksmbd/Makefile b/fs/ksmbd/Makefile --- a/fs/ksmbd/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/Makefile 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for Linux SMB3 kernel server +# +ifneq ($(KERNELRELEASE),) +# For kernel build + +# CONFIG_SMB_SERVER_SMBDIRECT is supported in the kernel above 4.12 version. +SMBDIRECT_SUPPORTED = $(shell [ $(VERSION) -gt 4 -o \( $(VERSION) -eq 4 -a \ + $(PATCHLEVEL) -gt 12 \) ] && echo y) + +ifeq "$(CONFIG_SMB_SERVER_SMBDIRECT)" "y" +ifneq "$(call SMBDIRECT_SUPPORTED)" "y" +$(error CONFIG_SMB_SERVER_SMBDIRECT is supported in the kernel above 4.12 version) +endif +endif + +obj-$(CONFIG_SMB_SERVER) += ksmbd.o + +ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o connection.o crypto_ctx.o \ + server.o misc.o oplock.o ksmbd_work.o smbacl.o ndr.o\ + mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ + mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ + transport_tcp.o transport_ipc.o + +ksmbd-y += smb2pdu.o smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ + ksmbd_spnego_negtokentarg.asn1.o asn1.o + +$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h +$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +ksmbd-$(CONFIG_SMB_INSECURE_SERVER) += smb1pdu.o smb1ops.o smb1misc.o netmisc.o +ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o +else +# For external module build +EXTRA_FLAGS += -I$(PWD) +KDIR ?= /lib/modules/$(shell uname -r)/build +MDIR ?= /lib/modules/$(shell uname -r) +PWD := $(shell pwd) +PWD := $(shell pwd) + +export CONFIG_SMB_SERVER := m + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +install: ksmbd.ko + rm -f ${MDIR}/kernel/fs/ksmbd/ksmbd.ko + install -m644 -b -D ksmbd.ko ${MDIR}/kernel/fs/ksmbd/ksmbd.ko + depmod -a + +uninstall: + rm -rf ${MDIR}/kernel/fs/ksmbd + depmod -a +endif + +.PHONY : all clean install uninstall diff -Naur --no-dereference a/fs/ksmbd/mgmt/ksmbd_ida.c b/fs/ksmbd/mgmt/ksmbd_ida.c --- a/fs/ksmbd/mgmt/ksmbd_ida.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/ksmbd_ida.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "ksmbd_ida.h" + +static inline int __acquire_id(struct ida *ida, int from, int to) +{ + return ida_simple_get(ida, from, to, GFP_KERNEL); +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_tid(struct ida *ida) +{ + return __acquire_id(ida, 1, 0xFFFF); +} +#endif + +int ksmbd_acquire_smb2_tid(struct ida *ida) +{ + return __acquire_id(ida, 1, 0xFFFFFFFF); + +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_uid(struct ida *ida) +{ + return __acquire_id(ida, 1, 0xFFFE); +} +#endif + +int ksmbd_acquire_smb2_uid(struct ida *ida) +{ + int id; + + id = __acquire_id(ida, 1, 0); + if (id == 0xFFFE) + id = __acquire_id(ida, 1, 0); + + return id; +} + +int ksmbd_acquire_async_msg_id(struct ida *ida) +{ + return __acquire_id(ida, 1, 0); +} + +int ksmbd_acquire_id(struct ida *ida) +{ + return __acquire_id(ida, 0, 0); +} + +void ksmbd_release_id(struct ida *ida, int id) +{ + ida_simple_remove(ida, id); +} diff -Naur --no-dereference a/fs/ksmbd/mgmt/ksmbd_ida.h b/fs/ksmbd/mgmt/ksmbd_ida.h --- a/fs/ksmbd/mgmt/ksmbd_ida.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/ksmbd_ida.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_IDA_MANAGEMENT_H__ +#define __KSMBD_IDA_MANAGEMENT_H__ + +#include +#include + +/* + * 2.2.1.6.7 TID Generation + * The value 0xFFFF MUST NOT be used as a valid TID. All other + * possible values for TID, including zero (0x0000), are valid. + * The value 0xFFFF is used to specify all TIDs or no TID, + * depending upon the context in which it is used. + */ +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_tid(struct ida *ida); +#endif +int ksmbd_acquire_smb2_tid(struct ida *ida); + +/* + * 2.2.1.6.8 UID Generation + * The value 0xFFFE was declared reserved in the LAN Manager 1.0 + * documentation, so a value of 0xFFFE SHOULD NOT be used as a + * valid UID.<21> All other possible values for a UID, excluding + * zero (0x0000), are valid. + */ +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_uid(struct ida *ida); +#endif +int ksmbd_acquire_smb2_uid(struct ida *ida); +int ksmbd_acquire_async_msg_id(struct ida *ida); + +int ksmbd_acquire_id(struct ida *ida); + +void ksmbd_release_id(struct ida *ida, int id); +#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff -Naur --no-dereference a/fs/ksmbd/mgmt/share_config.c b/fs/ksmbd/mgmt/share_config.c --- a/fs/ksmbd/mgmt/share_config.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/share_config.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "share_config.h" +#include "user_config.h" +#include "user_session.h" +#include "../transport_ipc.h" + +#define SHARE_HASH_BITS 3 +static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); +static DECLARE_RWSEM(shares_table_lock); + +struct ksmbd_veto_pattern { + char *pattern; + struct list_head list; +}; + +static unsigned int share_name_hash(char *name) +{ + return jhash(name, strlen(name), 0); +} + +static void kill_share(struct ksmbd_share_config *share) +{ + while (!list_empty(&share->veto_list)) { + struct ksmbd_veto_pattern *p; + + p = list_entry(share->veto_list.next, + struct ksmbd_veto_pattern, + list); + list_del(&p->list); + kfree(p->pattern); + kfree(p); + } + + if (share->path) + path_put(&share->vfs_path); + kfree(share->name); + kfree(share->path); + kfree(share); +} + +void __ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + down_write(&shares_table_lock); + hash_del(&share->hlist); + up_write(&shares_table_lock); + + kill_share(share); +} + +static struct ksmbd_share_config * +__get_share_config(struct ksmbd_share_config *share) +{ + if (!atomic_inc_not_zero(&share->refcount)) + return NULL; + return share; +} + +static struct ksmbd_share_config *__share_lookup(char *name) +{ + struct ksmbd_share_config *share; + unsigned int key = share_name_hash(name); + + hash_for_each_possible(shares_table, share, hlist, key) { + if (!strcmp(name, share->name)) + return share; + } + return NULL; +} + +static int parse_veto_list(struct ksmbd_share_config *share, + char *veto_list, + int veto_list_sz) +{ + int sz = 0; + + if (!veto_list_sz) + return 0; + + while (veto_list_sz > 0) { + struct ksmbd_veto_pattern *p; + + sz = strlen(veto_list); + if (!sz) + break; + + p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->pattern = kstrdup(veto_list, GFP_KERNEL); + if (!p->pattern) { + kfree(p); + return -ENOMEM; + } + + list_add(&p->list, &share->veto_list); + + veto_list += sz + 1; + veto_list_sz -= (sz + 1); + } + + return 0; +} + +static struct ksmbd_share_config *share_config_request(char *name) +{ + struct ksmbd_share_config_response *resp; + struct ksmbd_share_config *share = NULL; + struct ksmbd_share_config *lookup; + int ret; + + resp = ksmbd_ipc_share_config_request(name); + if (!resp) + return NULL; + + if (resp->flags == KSMBD_SHARE_FLAG_INVALID) + goto out; + + share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); + if (!share) + goto out; + + share->flags = resp->flags; + atomic_set(&share->refcount, 1); + INIT_LIST_HEAD(&share->veto_list); + share->name = kstrdup(name, GFP_KERNEL); + + if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + share->path = kstrdup(ksmbd_share_config_path(resp), + GFP_KERNEL); + if (share->path) + share->path_sz = strlen(share->path); + share->create_mask = resp->create_mask; + share->directory_mask = resp->directory_mask; + share->force_create_mode = resp->force_create_mode; + share->force_directory_mode = resp->force_directory_mode; + share->force_uid = resp->force_uid; + share->force_gid = resp->force_gid; + ret = parse_veto_list(share, + KSMBD_SHARE_CONFIG_VETO_LIST(resp), + resp->veto_list_sz); + if (!ret && share->path) { + ret = kern_path(share->path, 0, &share->vfs_path); + if (ret) { + ksmbd_debug(SMB, "failed to access '%s'\n", + share->path); + /* Avoid put_path() */ + kfree(share->path); + share->path = NULL; + } + } + if (ret || !share->name) { + kill_share(share); + share = NULL; + goto out; + } + } + + down_write(&shares_table_lock); + lookup = __share_lookup(name); + if (lookup) + lookup = __get_share_config(lookup); + if (!lookup) { + hash_add(shares_table, &share->hlist, share_name_hash(name)); + } else { + kill_share(share); + share = lookup; + } + up_write(&shares_table_lock); + +out: + kvfree(resp); + return share; +} + +static void strtolower(char *share_name) +{ + while (*share_name) { + *share_name = tolower(*share_name); + share_name++; + } +} + +struct ksmbd_share_config *ksmbd_share_config_get(char *name) +{ + struct ksmbd_share_config *share; + + strtolower(name); + + down_read(&shares_table_lock); + share = __share_lookup(name); + if (share) + share = __get_share_config(share); + up_read(&shares_table_lock); + + if (share) + return share; + return share_config_request(name); +} + +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename) +{ + struct ksmbd_veto_pattern *p; + + list_for_each_entry(p, &share->veto_list, list) { + if (match_wildcard(p->pattern, filename)) + return true; + } + return false; +} + +void ksmbd_share_configs_cleanup(void) +{ + struct ksmbd_share_config *share; + struct hlist_node *tmp; + int i; + + down_write(&shares_table_lock); + hash_for_each_safe(shares_table, i, tmp, share, hlist) { + hash_del(&share->hlist); + kill_share(share); + } + up_write(&shares_table_lock); +} diff -Naur --no-dereference a/fs/ksmbd/mgmt/share_config.h b/fs/ksmbd/mgmt/share_config.h --- a/fs/ksmbd/mgmt/share_config.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/share_config.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SHARE_CONFIG_MANAGEMENT_H__ +#define __SHARE_CONFIG_MANAGEMENT_H__ + +#include +#include +#include + +struct ksmbd_share_config { + char *name; + char *path; + + unsigned int path_sz; + unsigned int flags; + struct list_head veto_list; + + struct path vfs_path; + + atomic_t refcount; + struct hlist_node hlist; + unsigned short create_mask; + unsigned short directory_mask; + unsigned short force_create_mode; + unsigned short force_directory_mode; + unsigned short force_uid; + unsigned short force_gid; +}; + +#define KSMBD_SHARE_INVALID_UID ((__u16)-1) +#define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +static inline int share_config_create_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_create_mode) { + if (!posix_mode) + return share->create_mask; + else + return posix_mode & share->create_mask; + } + return share->force_create_mode & share->create_mask; +} + +static inline int share_config_directory_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_directory_mode) { + if (!posix_mode) + return share->directory_mask; + else + return posix_mode & share->directory_mask; + } + + return share->force_directory_mode & share->directory_mask; +} + +static inline int test_share_config_flag(struct ksmbd_share_config *share, + int flag) +{ + return share->flags & flag; +} + +void __ksmbd_share_config_put(struct ksmbd_share_config *share); + +static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + if (!atomic_dec_and_test(&share->refcount)) + return; + __ksmbd_share_config_put(share); +} + +struct ksmbd_share_config *ksmbd_share_config_get(char *name); +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename); +void ksmbd_share_configs_cleanup(void); + +#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff -Naur --no-dereference a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c --- a/fs/ksmbd/mgmt/tree_connect.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/tree_connect.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "../transport_ipc.h" +#include "../connection.h" + +#include "tree_connect.h" +#include "user_config.h" +#include "share_config.h" +#include "user_session.h" + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) +{ + struct ksmbd_tree_conn_status status = {-EINVAL, NULL}; + struct ksmbd_tree_connect_response *resp = NULL; + struct ksmbd_share_config *sc; + struct ksmbd_tree_connect *tree_conn = NULL; + struct sockaddr *peer_addr; + int ret; + + sc = ksmbd_share_config_get(share_name); + if (!sc) + return status; + + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); + if (!tree_conn) { + status.ret = -ENOMEM; + goto out_error; + } + + tree_conn->id = ksmbd_acquire_tree_conn_id(sess); + if (tree_conn->id < 0) { + status.ret = -EINVAL; + goto out_error; + } + + peer_addr = KSMBD_TCP_PEER_SOCKADDR(sess->conn); + resp = ksmbd_ipc_tree_connect_request(sess, + sc, + tree_conn, + peer_addr); + if (!resp) { + status.ret = -EINVAL; + goto out_error; + } + + status.ret = resp->status; + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + goto out_error; + + tree_conn->flags = resp->connection_flags; + tree_conn->user = sess->user; + tree_conn->share_conf = sc; + status.tree_conn = tree_conn; + + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); + if (ret) { + status.ret = -ENOMEM; + goto out_error; + } + kvfree(resp); + return status; + +out_error: + if (tree_conn) + ksmbd_release_tree_conn_id(sess, tree_conn->id); + ksmbd_share_config_put(sc); + kfree(tree_conn); + kvfree(resp); + return status; +} + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + int ret; + + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); + xa_erase(&sess->tree_conns, tree_conn->id); + ksmbd_share_config_put(tree_conn->share_conf); + kfree(tree_conn); + return ret; +} + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id) +{ + return xa_load(&sess->tree_conns, id); +} + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tc; + + tc = ksmbd_tree_conn_lookup(sess, id); + if (tc) + return tc->share_conf; + return NULL; +} + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) +{ + int ret = 0; + struct ksmbd_tree_connect *tc; + unsigned long id; + + xa_for_each(&sess->tree_conns, id, tc) + ret |= ksmbd_tree_conn_disconnect(sess, tc); + xa_destroy(&sess->tree_conns); + return ret; +} diff -Naur --no-dereference a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h --- a/fs/ksmbd/mgmt/tree_connect.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/tree_connect.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __TREE_CONNECT_MANAGEMENT_H__ +#define __TREE_CONNECT_MANAGEMENT_H__ + +#include + +#include "../ksmbd_netlink.h" + +struct ksmbd_share_config; +struct ksmbd_user; + +struct ksmbd_tree_connect { + int id; + + unsigned int flags; + struct ksmbd_share_config *share_conf; + struct ksmbd_user *user; + + struct list_head list; + + int maximal_access; + bool posix_extensions; +}; + +struct ksmbd_tree_conn_status { + unsigned int ret; + struct ksmbd_tree_connect *tree_conn; +}; + +static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, + int flag) +{ + return tree_conn->flags & flag; +} + +struct ksmbd_session; + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name); + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id); + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + +#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff -Naur --no-dereference a/fs/ksmbd/mgmt/user_config.c b/fs/ksmbd/mgmt/user_config.c --- a/fs/ksmbd/mgmt/user_config.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/user_config.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include + +#include "user_config.h" +#include "../transport_ipc.h" + +struct ksmbd_user *ksmbd_login_user(const char *account) +{ + struct ksmbd_login_response *resp; + struct ksmbd_user *user = NULL; + + resp = ksmbd_ipc_login_request(account); + if (!resp) + return NULL; + + if (!(resp->status & KSMBD_USER_FLAG_OK)) + goto out; + + user = ksmbd_alloc_user(resp); +out: + kvfree(resp); + return user; +} + +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) +{ + struct ksmbd_user *user = NULL; + + user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); + if (!user) + return NULL; + + user->name = kstrdup(resp->account, GFP_KERNEL); + user->flags = resp->status; + user->gid = resp->gid; + user->uid = resp->uid; + user->passkey_sz = resp->hash_sz; + user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); + if (user->passkey) + memcpy(user->passkey, resp->hash, resp->hash_sz); + + if (!user->name || !user->passkey) { + kfree(user->name); + kfree(user->passkey); + kfree(user); + user = NULL; + } + return user; +} + +void ksmbd_free_user(struct ksmbd_user *user) +{ + ksmbd_ipc_logout_request(user->name, user->flags); + kfree(user->name); + kfree(user->passkey); + kfree(user); +} + +int ksmbd_anonymous_user(struct ksmbd_user *user) +{ + if (user->name[0] == '\0') + return 1; + return 0; +} + +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) +{ + if (strcmp(u1->name, u2->name)) + return false; + if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz)) + return false; + + return true; +} diff -Naur --no-dereference a/fs/ksmbd/mgmt/user_config.h b/fs/ksmbd/mgmt/user_config.h --- a/fs/ksmbd/mgmt/user_config.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/user_config.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_CONFIG_MANAGEMENT_H__ +#define __USER_CONFIG_MANAGEMENT_H__ + +#include "../glob.h" + +struct ksmbd_user { + unsigned short flags; + + unsigned int uid; + unsigned int gid; + + char *name; + + size_t passkey_sz; + char *passkey; + unsigned int failed_login_count; +}; + +static inline bool user_guest(struct ksmbd_user *user) +{ + return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; +} + +static inline void set_user_flag(struct ksmbd_user *user, int flag) +{ + user->flags |= flag; +} + +static inline int test_user_flag(struct ksmbd_user *user, int flag) +{ + return user->flags & flag; +} + +static inline void set_user_guest(struct ksmbd_user *user) +{ +} + +static inline char *user_passkey(struct ksmbd_user *user) +{ + return user->passkey; +} + +static inline char *user_name(struct ksmbd_user *user) +{ + return user->name; +} + +static inline unsigned int user_uid(struct ksmbd_user *user) +{ + return user->uid; +} + +static inline unsigned int user_gid(struct ksmbd_user *user) +{ + return user->gid; +} + +struct ksmbd_user *ksmbd_login_user(const char *account); +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); +void ksmbd_free_user(struct ksmbd_user *user); +int ksmbd_anonymous_user(struct ksmbd_user *user); +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); +#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff -Naur --no-dereference a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c --- a/fs/ksmbd/mgmt/user_session.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/user_session.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "ksmbd_ida.h" +#include "user_session.h" +#include "user_config.h" +#include "tree_connect.h" +#include "../transport_ipc.h" +#include "../connection.h" +#include "../vfs_cache.h" + +static DEFINE_IDA(session_ida); + +#define SESSION_HASH_BITS 3 +static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); +static DECLARE_RWSEM(sessions_table_lock); + +struct ksmbd_session_rpc { + int id; + unsigned int method; + struct list_head list; +}; + +static void free_channel_list(struct ksmbd_session *sess) +{ + struct channel *chann, *tmp; + + list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, + chann_list) { + list_del(&chann->chann_list); + kfree(chann); + } +} + +static void __session_rpc_close(struct ksmbd_session *sess, + struct ksmbd_session_rpc *entry) +{ + struct ksmbd_rpc_command *resp; + + resp = ksmbd_rpc_close(sess, entry->id); + if (!resp) + pr_err("Unable to close RPC pipe %d\n", entry->id); + + kvfree(resp); + ksmbd_rpc_id_free(entry->id); + kfree(entry); +} + +static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) +{ + struct ksmbd_session_rpc *entry; + + while (!list_empty(&sess->rpc_handle_list)) { + entry = list_entry(sess->rpc_handle_list.next, + struct ksmbd_session_rpc, + list); + + list_del(&entry->list); + __session_rpc_close(sess, entry); + } +} + +static int __rpc_method(char *rpc_name) +{ + if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) + return KSMBD_RPC_SRVSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) + return KSMBD_RPC_WKSSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) + return KSMBD_RPC_RAP_METHOD; + + if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) + return KSMBD_RPC_SAMR_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) + return KSMBD_RPC_LSARPC_METHOD_INVOKE; + + pr_err("Unsupported RPC: %s\n", rpc_name); + return 0; +} + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) +{ + struct ksmbd_session_rpc *entry; + struct ksmbd_rpc_command *resp; + int method; + + method = __rpc_method(rpc_name); + if (!method) + return -EINVAL; + + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + if (!entry) + return -EINVAL; + + list_add(&entry->list, &sess->rpc_handle_list); + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto error; + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) + goto error; + + kvfree(resp); + return entry->id; +error: + list_del(&entry->list); + kfree(entry); + return -EINVAL; +} + +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) { + list_del(&entry->list); + __session_rpc_close(sess, entry); + break; + } + } +} + +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) + return entry->method; + } + return 0; +} + +void ksmbd_session_destroy(struct ksmbd_session *sess) +{ + if (!sess) + return; + + if (!atomic_dec_and_test(&sess->refcnt)) + return; + + list_del(&sess->sessions_entry); + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (IS_SMB2(sess->conn)) { + down_write(&sessions_table_lock); + hash_del(&sess->hlist); + up_write(&sessions_table_lock); + } +#else + down_write(&sessions_table_lock); + hash_del(&sess->hlist); + up_write(&sessions_table_lock); +#endif + + if (sess->user) + ksmbd_free_user(sess->user); + + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(&sess->file_table); + ksmbd_session_rpc_clear_list(sess); + free_channel_list(sess); + kfree(sess->Preauth_HashValue); + ksmbd_release_id(&session_ida, sess->id); + kfree(sess); +} + +static struct ksmbd_session *__session_lookup(unsigned long long id) +{ + struct ksmbd_session *sess; + + hash_for_each_possible(sessions_table, sess, hlist, id) { + if (id == sess->id) + return sess; + } + return NULL; +} + +void ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + sess->conn = conn; + list_add(&sess->sessions_entry, &conn->sessions); +} + +void ksmbd_sessions_deregister(struct ksmbd_conn *conn) +{ + struct ksmbd_session *sess; + + while (!list_empty(&conn->sessions)) { + sess = list_entry(conn->sessions.next, + struct ksmbd_session, + sessions_entry); + + ksmbd_session_destroy(sess); + } +} + +static bool ksmbd_session_id_match(struct ksmbd_session *sess, + unsigned long long id) +{ + return sess->id == id; +} + +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess = NULL; + + list_for_each_entry(sess, &conn->sessions, sessions_entry) { + if (ksmbd_session_id_match(sess, id)) + return sess; + } + return NULL; +} + +int get_session(struct ksmbd_session *sess) +{ + return atomic_inc_not_zero(&sess->refcnt); +} + +void put_session(struct ksmbd_session *sess) +{ + if (atomic_dec_and_test(&sess->refcnt)) + pr_err("get/%s seems to be mismatched.", __func__); +} + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) +{ + struct ksmbd_session *sess; + + down_read(&sessions_table_lock); + sess = __session_lookup(id); + if (sess) { + if (!get_session(sess)) + sess = NULL; + } + up_read(&sessions_table_lock); + + return sess; +} + +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = ksmbd_session_lookup(conn, id); + if (!sess && conn->binding) + sess = ksmbd_session_lookup_slowpath(id); + return sess; +} + +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id) +{ + struct preauth_session *sess; + + sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + if (!sess) + return NULL; + + sess->id = sess_id; + memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE); + list_add(&sess->preauth_entry, &conn->preauth_sess_table); + + return sess; +} + +static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, + unsigned long long id) +{ + return sess->id == id; +} + +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct preauth_session *sess = NULL; + + list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { + if (ksmbd_preauth_session_id_match(sess, id)) + return sess; + } + return NULL; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +static int __init_smb1_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb1_uid(&session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} +#endif + +static int __init_smb2_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb2_uid(&session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} + +static struct ksmbd_session *__session_create(int protocol) +{ + struct ksmbd_session *sess; + int ret; + + sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); + if (!sess) + return NULL; + + if (ksmbd_init_file_table(&sess->file_table)) + goto error; + + set_session_flag(sess, protocol); + INIT_LIST_HEAD(&sess->sessions_entry); + xa_init(&sess->tree_conns); + INIT_LIST_HEAD(&sess->ksmbd_chann_list); + INIT_LIST_HEAD(&sess->rpc_handle_list); + sess->sequence_number = 1; + atomic_set(&sess->refcnt, 1); + + switch (protocol) { +#ifdef CONFIG_SMB_INSECURE_SERVER + case CIFDS_SESSION_FLAG_SMB1: + ret = __init_smb1_session(sess); + break; +#endif + case CIFDS_SESSION_FLAG_SMB2: + ret = __init_smb2_session(sess); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + goto error; + + ida_init(&sess->tree_conn_ida); + + if (protocol == CIFDS_SESSION_FLAG_SMB2) { + down_write(&sessions_table_lock); + hash_add(sessions_table, &sess->hlist, sess->id); + up_write(&sessions_table_lock); + } + return sess; + +error: + ksmbd_session_destroy(sess); + return NULL; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_session *ksmbd_smb1_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB1); +} +#endif + +struct ksmbd_session *ksmbd_smb2_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB2); +} + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) +{ + int id = -EINVAL; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB1)) + id = ksmbd_acquire_smb1_tid(&sess->tree_conn_ida); +#endif + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); + + return id; +} + +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) +{ + if (id >= 0) + ksmbd_release_id(&sess->tree_conn_ida, id); +} diff -Naur --no-dereference a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h --- a/fs/ksmbd/mgmt/user_session.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/mgmt/user_session.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_SESSION_MANAGEMENT_H__ +#define __USER_SESSION_MANAGEMENT_H__ + +#include +#include + +#include "../smb_common.h" +#include "../ntlmssp.h" + +#ifdef CONFIG_SMB_INSECURE_SERVER +#define CIFDS_SESSION_FLAG_SMB1 BIT(0) +#endif +#define CIFDS_SESSION_FLAG_SMB2 BIT(1) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct ksmbd_file_table; + +struct channel { + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + struct ksmbd_conn *conn; + struct list_head chann_list; +}; + +struct preauth_session { + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + u64 id; + struct list_head preauth_entry; +}; + +struct ksmbd_session { + u64 id; + + struct ksmbd_user *user; + struct ksmbd_conn *conn; + unsigned int sequence_number; + unsigned int flags; + + bool sign; + bool enc; + bool is_anonymous; + + int state; + __u8 *Preauth_HashValue; + + char sess_key[CIFS_KEY_SIZE]; + + struct hlist_node hlist; + struct list_head ksmbd_chann_list; + struct xarray tree_conns; + struct ida tree_conn_ida; + struct list_head rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + + struct list_head sessions_entry; + struct ksmbd_file_table file_table; + atomic_t refcnt; +}; + +static inline int test_session_flag(struct ksmbd_session *sess, int bit) +{ + return sess->flags & bit; +} + +static inline void set_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags |= bit; +} + +static inline void clear_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags &= ~bit; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_session *ksmbd_smb1_session_create(void); +#endif +struct ksmbd_session *ksmbd_smb2_session_create(void); + +void ksmbd_session_destroy(struct ksmbd_session *sess); + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); +void ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +void ksmbd_sessions_deregister(struct ksmbd_conn *conn); +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id); +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id); +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +int get_session(struct ksmbd_session *sess); +void put_session(struct ksmbd_session *sess); +#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff -Naur --no-dereference a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c --- a/fs/ksmbd/misc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/misc.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "misc.h" +#include "smb_common.h" +#include "connection.h" +#include "vfs.h" + +#include "mgmt/share_config.h" + +/** + * match_pattern() - compare a string with a pattern which might include + * wildcard '*' and '?' + * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR + * + * @string: string to compare with a pattern + * @len: string length + * @pattern: pattern string which might include wildcard '*' and '?' + * + * Return: 0 if pattern matched with the string, otherwise non zero value + */ +int match_pattern(const char *str, size_t len, const char *pattern) +{ + const char *s = str; + const char *p = pattern; + bool star = false; + + while (*s && len) { + switch (*p) { + case '?': + s++; + len--; + p++; + break; + case '*': + star = true; + str = s; + if (!*++p) + return true; + pattern = p; + break; + default: + if (tolower(*s) == tolower(*p)) { + s++; + len--; + p++; + } else { + if (!star) + return false; + str++; + s = str; + p = pattern; + } + break; + } + } + + if (*p == '*') + ++p; + return !*p; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char ch) +{ + /* check for control chars, wildcards etc. */ + if (!(ch & 0x80) && + (ch <= 0x1f || + ch == '?' || ch == '"' || ch == '<' || + ch == '>' || ch == '|' || ch == '*')) + return 0; + + return 1; +} + +int ksmbd_validate_filename(char *filename) +{ + while (*filename) { + char c = *filename; + + filename++; + if (!is_char_allowed(c)) { + ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); + return -ENOENT; + } + } + + return 0; +} + +static int ksmbd_validate_stream_name(char *stream_name) +{ + while (*stream_name) { + char c = *stream_name; + + stream_name++; + if (c == '/' || c == ':' || c == '\\') { + pr_err("Stream name validation failed: %c\n", c); + return -ENOENT; + } + } + + return 0; +} + +int parse_stream_name(char *filename, char **stream_name, int *s_type) +{ + char *stream_type; + char *s_name; + int rc = 0; + + s_name = filename; + filename = strsep(&s_name, ":"); + ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); + if (strchr(s_name, ':')) { + stream_type = s_name; + s_name = strsep(&stream_type, ":"); + + rc = ksmbd_validate_stream_name(s_name); + if (rc < 0) { + rc = -ENOENT; + goto out; + } + + ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, + stream_type); + if (!strncasecmp("$data", stream_type, 5)) + *s_type = DATA_STREAM; + else if (!strncasecmp("$index_allocation", stream_type, 17)) + *s_type = DIR_STREAM; + else + rc = -ENOENT; + } + + *stream_name = s_name; +out: + return rc; +} + +/** + * convert_to_nt_pathname() - extract and return windows path string + * whose share directory prefix was removed from file path + * @filename : unix filename + * @sharepath: share path string + * + * Return : windows path string or error + */ + +char *convert_to_nt_pathname(char *filename) +{ + char *ab_pathname; + + if (strlen(filename) == 0) + filename = "\\"; + + ab_pathname = kstrdup(filename, GFP_KERNEL); + if (!ab_pathname) + return NULL; + + ksmbd_conv_path_to_windows(ab_pathname); + return ab_pathname; +} + +int get_nlink(struct kstat *st) +{ + int nlink; + + nlink = st->nlink; + if (S_ISDIR(st->mode)) + nlink--; + + return nlink; +} + +void ksmbd_conv_path_to_unix(char *path) +{ + strreplace(path, '\\', '/'); +} + +void ksmbd_strip_last_slash(char *path) +{ + int len = strlen(path); + + while (len && path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } +} + +void ksmbd_conv_path_to_windows(char *path) +{ + strreplace(path, '/', '\\'); +} + +/** + * ksmbd_extract_sharename() - get share name from tree connect request + * @treename: buffer containing tree name and share name + * + * Return: share name on success, otherwise error + */ +char *ksmbd_extract_sharename(char *treename) +{ + char *name = treename; + char *dst; + char *pos = strrchr(name, '\\'); + + if (pos) + name = (pos + 1); + + /* caller has to free the memory */ + dst = kstrdup(name, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + return dst; +} + +/** + * convert_to_unix_name() - convert windows name to unix format + * @path: name to be converted + * @tid: tree id of mathing share + * + * Return: converted name on success, otherwise NULL + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name; + + if (name[0] == '/') + name++; + + path_len = share->path_sz; + name_len = strlen(name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) + return new_name; + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + return new_name; +} +#else +static char *normalize_path(const char *path) +{ + size_t path_len, remain_path_len, out_path_len; + char *out_path, *out_next; + int i, pre_dotdot_cnt = 0, slash_cnt = 0; + bool is_last; + + path_len = strlen(path); + remain_path_len = path_len; + + out_path = kzalloc(path_len + 2, GFP_KERNEL); + if (!out_path) + return ERR_PTR(-ENOMEM); + out_path_len = 0; + out_next = out_path; + + do { + const char *name = path + path_len - remain_path_len; + char *next = strchrnul(name, '/'); + size_t name_len = next - name; + + is_last = !next[0]; + if (name_len == 2 && name[0] == '.' && name[1] == '.') { + pre_dotdot_cnt++; + /* handle the case that path ends with "/.." */ + if (is_last) + goto follow_dotdot; + } else { + if (pre_dotdot_cnt) { +follow_dotdot: + slash_cnt = 0; + for (i = out_path_len - 1; i >= 0; i--) { + if (out_path[i] == '/' && + ++slash_cnt == pre_dotdot_cnt + 1) + break; + } + + if (i < 0 && + slash_cnt != pre_dotdot_cnt) { + kfree(out_path); + return ERR_PTR(-EINVAL); + } + + out_next = &out_path[i+1]; + *out_next = '\0'; + out_path_len = i + 1; + + } + + if (name_len != 0 && + !(name_len == 1 && name[0] == '.') && + !(name_len == 2 && name[0] == '.' && name[1] == '.')) { + next[0] = '\0'; + sprintf(out_next, "%s/", name); + out_next += name_len + 1; + out_path_len += name_len + 1; + if (!is_last) + next[0] = '/'; + } + pre_dotdot_cnt = 0; + } + + remain_path_len -= name_len + 1; + } while (!is_last); + + if (out_path_len > 0) + out_path[out_path_len-1] = '\0'; + return out_path; +} + +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name, *norm_name; + + if (name[0] == '/') + name++; + + norm_name = normalize_path(name); + if (IS_ERR(norm_name)) + return norm_name; + + path_len = share->path_sz; + name_len = strlen(norm_name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) { + kfree(norm_name); + return new_name; + } + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, norm_name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + kfree(norm_name); + + return new_name; +} +#endif + +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len) +{ + char *conv; + int sz = min(4 * d_info->name_len, PATH_MAX); + + if (!sz) + return NULL; + + conv = kmalloc(sz, GFP_KERNEL); + if (!conv) + return NULL; + + /* XXX */ + *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, + d_info->name_len, local_nls, 0); + *conv_len *= 2; + + /* We allocate buffer twice bigger than needed. */ + conv[*conv_len] = 0x00; + conv[*conv_len + 1] = 0x00; + return conv; +} + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} diff -Naur --no-dereference a/fs/ksmbd/misc.h b/fs/ksmbd/misc.h --- a/fs/ksmbd/misc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/misc.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_MISC_H__ +#define __KSMBD_MISC_H__ + +struct ksmbd_share_config; +struct nls_table; +struct kstat; +struct ksmbd_file; + +int match_pattern(const char *str, size_t len, const char *pattern); +int ksmbd_validate_filename(char *filename); +int parse_stream_name(char *filename, char **stream_name, int *s_type); +char *convert_to_nt_pathname(char *filename); +int get_nlink(struct kstat *st); +void ksmbd_conv_path_to_unix(char *path); +void ksmbd_strip_last_slash(char *path); +void ksmbd_conv_path_to_windows(char *path); +char *ksmbd_extract_sharename(char *treename); +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); + +#define KSMBD_DIR_INFO_ALIGNMENT 8 +struct ksmbd_dir_info; +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len); + +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); +u64 ksmbd_UnixTimeToNT(struct timespec64 t); +long long ksmbd_systime(void); +#endif /* __KSMBD_MISC_H__ */ diff -Naur --no-dereference a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c --- a/fs/ksmbd/ndr.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ndr.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include + +#include "glob.h" +#include "ndr.h" + +static inline char *ndr_get_field(struct ndr *n) +{ + return n->data + n->offset; +} + +static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) +{ + char *data; + + data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + if (!data) + return -ENOMEM; + + n->data = data; + n->length += 1024; + memset(n->data + n->offset, 0, 1024); + return 0; +} + +static int ndr_write_int16(struct ndr *n, __u16 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int32(struct ndr *n, __u32 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int64(struct ndr *n, __u64 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + return 0; +} + +static int ndr_write_string(struct ndr *n, char *value) +{ + size_t sz; + + sz = strlen(value) + 1; + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_string(struct ndr *n, void *value, size_t sz) +{ + int len; + + if (n->offset + sz > n->length) + return -EINVAL; + + len = strnlen(ndr_get_field(n), sz); + if (value) + memcpy(value, ndr_get_field(n), len); + len++; + n->offset += len; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->offset + sz > n->length) + return -EINVAL; + + if (value) + memcpy(value, ndr_get_field(n), sz); + n->offset += sz; + return 0; +} + +static int ndr_read_int16(struct ndr *n, __u16 *value) +{ + if (n->offset + sizeof(__u16) > n->length) + return -EINVAL; + + if (value) + *value = le16_to_cpu(*(__le16 *)ndr_get_field(n)); + n->offset += sizeof(__u16); + return 0; +} + +static int ndr_read_int32(struct ndr *n, __u32 *value) +{ + if (n->offset + sizeof(__u32) > n->length) + return -EINVAL; + + if (value) + *value = le32_to_cpu(*(__le32 *)ndr_get_field(n)); + n->offset += sizeof(__u32); + return 0; +} + +static int ndr_read_int64(struct ndr *n, __u64 *value) +{ + if (n->offset + sizeof(__u64) > n->length) + return -EINVAL; + + if (value) + *value = le64_to_cpu(*(__le64 *)ndr_get_field(n)); + n->offset += sizeof(__u64); + return 0; +} + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + int ret; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (da->version == 3) { + snprintf(hex_attr, 10, "0x%x", da->attr); + ret = ndr_write_string(n, hex_attr); + } else { + ret = ndr_write_string(n, ""); + } + if (ret) + return ret; + + ret = ndr_write_int16(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->flags); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->attr); + if (ret) + return ret; + + if (da->version == 3) { + ret = ndr_write_int32(n, da->ea_size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->alloc_size); + } else { + ret = ndr_write_int64(n, da->itime); + } + if (ret) + return ret; + + ret = ndr_write_int64(n, da->create_time); + if (ret) + return ret; + + if (da->version == 3) + ret = ndr_write_int64(n, da->change_time); + return ret; +} + +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12]; + unsigned int version2, ret; + + n->offset = 0; + ret = ndr_read_string(n, hex_attr, sizeof(hex_attr)); + if (ret) + return ret; + + ret = ndr_read_int16(n, &da->version); + if (ret) + return ret; + + if (da->version != 3 && da->version != 4) { + pr_err("v%d version is not supported\n", da->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + + if (da->version != version2) { + pr_err("ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); + return -EINVAL; + } + + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int32(n, &da->attr); + if (ret) + return ret; + + if (da->version == 4) { + ret = ndr_read_int64(n, &da->itime); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + } else { + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + } + + return ret; +} + +static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) +{ + int i, ret; + + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + ret = ndr_write_int32(n, 0); + if (ret) + return ret; + + for (i = 0; i < acl->count; i++) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + if (acl->entries[i].type == SMB_ACL_USER) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].uid); + } else if (acl->entries[i].type == SMB_ACL_GROUP) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].gid); + } + if (ret) + return ret; + + /* push permission */ + ret = ndr_write_int32(n, acl->entries[i].perm); + } + + return ret; +} + +int ndr_encode_posix_acl(struct ndr *n, + struct user_namespace *user_ns, + struct inode *inode, + struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl) +{ + unsigned int ref_id = 0x00020000; + int ret; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (acl) { + /* ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + + if (def_acl) { + /* DEFAULT ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + ret = ndr_write_int64(n, from_kuid(&init_user_ns, i_uid_into_mnt(user_ns, inode))); + if (ret) + return ret; + + ret = ndr_write_int64(n, from_kgid(&init_user_ns, i_gid_into_mnt(user_ns, inode))); + if (ret) + return ret; +#else + ret = ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); + if (ret) + return ret; + + ret = ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); + if (ret) + return ret; +#endif + ret = ndr_write_int32(n, inode->i_mode); + if (ret) + return ret; + + if (acl) { + ret = ndr_encode_posix_acl_entry(n, acl); + if (def_acl && !ret) + ret = ndr_encode_posix_acl_entry(n, def_acl); + } + return ret; +} + +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int ref_id = 0x00020004; + int ret; + + n->offset = 0; + n->length = 2048; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + ret = ndr_write_int16(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int16(n, 2); + if (ret) + return ret; + + ret = ndr_write_int32(n, ref_id); + if (ret) + return ret; + + /* push hash type and hash 64bytes */ + ret = ndr_write_int16(n, acl->hash_type); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->desc, acl->desc_len); + if (ret) + return ret; + + ret = ndr_write_int64(n, acl->current_time); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + /* push ndr for security descriptor */ + ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} + +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int version2; + int ret; + + n->offset = 0; + ret = ndr_read_int16(n, &acl->version); + if (ret) + return ret; + if (acl->version != 4) { + pr_err("v%d version is not supported\n", acl->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + if (acl->version != version2) { + pr_err("ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); + return -EINVAL; + } + + /* Read Level */ + ret = ndr_read_int16(n, NULL); + if (ret) + return ret; + + /* Read Ref Id */ + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int16(n, &acl->hash_type); + if (ret) + return ret; + + ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ndr_read_bytes(n, acl->desc, 10); + if (strncmp(acl->desc, "posix_acl", 9)) { + pr_err("Invalid acl description : %s\n", acl->desc); + return -EINVAL; + } + + /* Read Time */ + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + /* Read Posix ACL hash */ + ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + acl->sd_size = n->length - n->offset; + acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + if (!acl->sd_buf) + return -ENOMEM; + + ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} diff -Naur --no-dereference a/fs/ksmbd/ndr.h b/fs/ksmbd/ndr.h --- a/fs/ksmbd/ndr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ndr.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +struct ndr { + char *data; + int offset; + int length; +}; + +#define NDR_NTSD_OFFSETOF 0xA0 + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, + struct inode *inode, struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl); +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff -Naur --no-dereference a/fs/ksmbd/netmisc.c b/fs/ksmbd/netmisc.c --- a/fs/ksmbd/netmisc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/netmisc.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Error mapping routines from Samba libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + */ + +#include "glob.h" +#include "smberr.h" +#include "nterr.h" +#include "smb_common.h" + +/***************************************************************************** + * convert a NT status code to a dos class/code + *****************************************************************************/ +/* NT status -> dos error map */ +static const struct { + __u8 dos_class; + __u16 dos_code; + __u32 ntstatus; +} ntstatus_to_dos_map[] = { + { + ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { + ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { + ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { + ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { + ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { + ERRDOS, 87, NT_STATUS_INVALID_CID}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { + ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { + ERRDOS, 38, NT_STATUS_END_OF_FILE}, { + ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { + ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { + ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + * during the session setup } + */ + { + ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { + ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { + ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { + ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + * during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { + ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { + ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { + ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { + ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { + ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { + ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { + ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { + /* mapping changed since shell does lookup on * expects FileNotFound */ + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { + ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { + ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { + ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { + ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { + ERRDOS, 23, NT_STATUS_DATA_ERROR}, { + ERRDOS, 23, NT_STATUS_CRC_ERROR}, { + ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { + ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { + ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { + ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { + ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { + ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { + ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { + ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { + ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { + ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { + ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { + ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { + ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { + ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { + ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { + ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { + ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { + ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { + ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { + ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + * during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ + ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + * during the session setup } + */ + { + ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { + ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { + ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { + ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { + ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { + ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { + ERRDOS, 112, NT_STATUS_DISK_FULL}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { + ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { + ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { + ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { + ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { + ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { + ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_INSUFFICIENT_RESOURCES to + * NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } + */ + { + ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { + ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { + ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { + ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { + ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { + ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { + ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { + ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { + ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { + ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { + ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { + ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { + ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { + ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { + ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { + ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { + ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { + ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { + ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { + ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { + ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { + ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { + ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { + ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { + ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { + ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { + ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { + ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { + ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { + ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { + ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { + ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { + ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { + ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { + ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { + ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { + ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { + ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { + ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { + ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { + ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { + ERRDOS, 203, 0xc0000100}, { + ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { + ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { + ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { + ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { + ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { + ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { + ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { + ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { + ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { + ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { + ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { + ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { + ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { + ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { + ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { + ERRDOS, 59, NT_STATUS_LINK_FAILED}, { + ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { + ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { + ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { + ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { + ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { + ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { + ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { + ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { + ERRHRD, ERRgeneral, 0xc000016e}, { + ERRHRD, ERRgeneral, 0xc000016f}, { + ERRHRD, ERRgeneral, 0xc0000170}, { + ERRHRD, ERRgeneral, 0xc0000171}, { + ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { + ERRHRD, ERRgeneral, 0xc0000179}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { + ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { + ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { + ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { + ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { + ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { + ERRDOS, 19, NT_STATUS_TOO_LATE}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_NO_TRUST_SAM_ACCOUNT to + * NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { + ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + * during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { + ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { + ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { + ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { + ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { + ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { + ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { + ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { + ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { + ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { + ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { + ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { + ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { + ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { + ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { + ERRHRD, ERRgeneral, 0xc000024a}, { + ERRHRD, ERRgeneral, 0xc000024b}, { + ERRHRD, ERRgeneral, 0xc000024c}, { + ERRHRD, ERRgeneral, 0xc000024d}, { + ERRHRD, ERRgeneral, 0xc000024e}, { + ERRHRD, ERRgeneral, 0xc000024f}, { + ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { + ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { + ERRHRD, ERRgeneral, 0xc000025d}, { + ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { + ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { + ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { + ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { + ERRDOS, 21, 0xc000026e}, { + ERRDOS, 161, 0xc0000281}, { + ERRDOS, ERRnoaccess, 0xc000028a}, { + ERRDOS, ERRnoaccess, 0xc000028b}, { + ERRHRD, ERRgeneral, 0xc000028c}, { + ERRDOS, ERRnoaccess, 0xc000028d}, { + ERRDOS, ERRnoaccess, 0xc000028e}, { + ERRDOS, ERRnoaccess, 0xc000028f}, { + ERRDOS, ERRnoaccess, 0xc0000290}, { + ERRDOS, ERRbadfunc, 0xc000029c}, { + ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { + ERRDOS, ERRinvlevel, 0x007c0001}, }; + +void +ntstatus_to_dos(__le32 ntstatus, __u8 *eclass, __le16 *ecode) +{ + int i; + + if (ntstatus == 0) { + *eclass = 0; + *ecode = 0; + return; + } + for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { + if (le32_to_cpu(ntstatus) == ntstatus_to_dos_map[i].ntstatus) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = cpu_to_le16(ntstatus_to_dos_map[i].dos_code); + return; + } + } + *eclass = ERRHRD; + *ecode = cpu_to_le16(ERRgeneral); +} diff -Naur --no-dereference a/fs/ksmbd/nterr.h b/fs/ksmbd/nterr.h --- a/fs/ksmbd/nterr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/nterr.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,543 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * NT error code constants + * Copyright (C) Andrew Tridgell 1992-2000 + * Copyright (C) John H Terpstra 1996-2000 + * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + * Copyright (C) Paul Ashton 1998-2000 + */ + +#ifndef _NTERR_H +#define _NTERR_H + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c +#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) +#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) +#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) +#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) +#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) +#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) +#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) +#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) +#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) +#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) +#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) +#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) +#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) +#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) +#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) +#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) +#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) +#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) +#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) +#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) +#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) +#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) +#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) +#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) +#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) +#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) +#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) +#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) +#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) +#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) +#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) +#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) +#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) +#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) +#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) +#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) +#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) +#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) +#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) +#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) +#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) +#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) +#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) +#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) +#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) +#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) +#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) +#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) +#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) +#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) +#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) +#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) +#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) +#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) +#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) +#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) +#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) +#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) +#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) +#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) +#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) +#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) +#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) +#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) +#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) +#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) +#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) +#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) +#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) +#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) +#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) +#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) +#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) +#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) +#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) +#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) +#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) +#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) +#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) +#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) +#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) +#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) +#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) +#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) +#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) +#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) +#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) +#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) +#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) +#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) +#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) +#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) +#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) +#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) +#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) +#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) +#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) +#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) +#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) +#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) +#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) +#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) +#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) +#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) +#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) +#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) +#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) +#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) +#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) +#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) +#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) +#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) +#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) +#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) +#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) +#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) +#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) +#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) +#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) +#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) +#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) +#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) +#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) +#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) +#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) +#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) +#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) +#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) +#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) +#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) +#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) +#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) +#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) +#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) +#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) +#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) +#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) +#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) +#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) +#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) +#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) +#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) +#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) +#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) +#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) +#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) +#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) +#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) +#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) +#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) +#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) +#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) +#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) +#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) +#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) +#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) +#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) +#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) +#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) +#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) +#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) +#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) +#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) +#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) +#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) +#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) +#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) +#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) +#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) +#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) +#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) +#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) +#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) +#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) +#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) +#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) +#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) +#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) +#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) +#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) +#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) +#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) +#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) +#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) +#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) +#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) +#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) +#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) +#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) +#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) +#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) +#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) +#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) +#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) +#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) +#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) +#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) +#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) +#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) +#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) +#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) +#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) +#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) +#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) +#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) +#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) +#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) +#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) +#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) +#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) +#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) +#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) +#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) +#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) +#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) +#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) +#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) +#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) +#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) +#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) +#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) +#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) +#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) +#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) +#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) +#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) +#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) +#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) +#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) +#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) +#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) +#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) +#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) +#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) +#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) +#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) +#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) +#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) +#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) +#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) +#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) +#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) +#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) +#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) +#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) +#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) +#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) +#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) +#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) +#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) +#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) +#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) +#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) +#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) +#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) +#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) +#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) +#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) +#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) +#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) +#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) +#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) +#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) +#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) +#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) +#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) +#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) +#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) +#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) +#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) +#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) +#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) +#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) +#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) +#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) +#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) +#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) +#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) +#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) +#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) +#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) +#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) +#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) +#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) +#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) +#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) +#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) +#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) +#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) +#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) +#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) +#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) +#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) +#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) +#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) +#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) +#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) +#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) +#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) +#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) +#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) +#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) +#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) +#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) +#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) +#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) +#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) +#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) +#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) +#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) +#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) +#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) +#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) +#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) +#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) +#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) +#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) +#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) +#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) +#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) +#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) +#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) +#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) +#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) +#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) +#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) +#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) +#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) +#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) +#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) +#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) +#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) +#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) +#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) +#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) +#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) +#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) +#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) +#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) +#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) +#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) +#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) +#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) +#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) +#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) +#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) +#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) +#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) +#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) +#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) +#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) +#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) +#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) +#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) +#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) +#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) +#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) +#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) +#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) +#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) +#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) +#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) +#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) +#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) +#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) +#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) +#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) +#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) +#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) +#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) +#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) +#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) +#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) +#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) +#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) +#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) +#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) +#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) +#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) +#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) +#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) +#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) +#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) +#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) +#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) +#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) +#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) +#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) +#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) +#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) +#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) +#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) +#define NT_STATUS_RETRY (0xC0000000 | 0x022d) +#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) +#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) +#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) +#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) +#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) +#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) +#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) +#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) +#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) +#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) +#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) +#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) +#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) +#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) +#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) +#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) +#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) +#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) +#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) +#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) +#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) +#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) +#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) +#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) +#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) +#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) +#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) +#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) +#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) +#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) +#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) +#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) +#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) +#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) +#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) +#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) +#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) +#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) +#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) +#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) +#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) +#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) +#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ +#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) +#define NT_STATUS_PENDING 0x00000103 +#endif /* _NTERR_H */ diff -Naur --no-dereference a/fs/ksmbd/ntlmssp.h b/fs/ksmbd/ntlmssp.h --- a/fs/ksmbd/ntlmssp.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/ntlmssp.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +#ifndef __KSMBD_NTLMSSP_H +#define __KSMBD_NTLMSSP_H + +#define NTLMSSP_SIGNATURE "NTLMSSP" + +/* Security blob target info data */ +#define TGT_Name "KSMBD" + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) +#define CIFS_KEY_SIZE (40) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) + +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +struct security_buffer { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __packed; + +struct target_info { + __le16 Type; + __le16 Length; + __u8 Content[0]; +} __packed; + +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + struct security_buffer DomainName; /* RFC 1001 style and ASCII */ + struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char DomainString[0]; + /* followed by WorkstationString */ +} __packed; + +struct challenge_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + struct security_buffer TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + struct security_buffer TargetInfoArray; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ +} __packed; + +struct authenticate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + struct security_buffer LmChallengeResponse; + struct security_buffer NtChallengeResponse; + struct security_buffer DomainName; + struct security_buffer UserName; + struct security_buffer WorkstationName; + struct security_buffer SessionKey; + __le32 NegotiateFlags; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char UserString[0]; +} __packed; + +struct ntlmv2_resp { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __packed; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + /* whether session key is per smb session */ + bool sesskey_per_smbsess; + /* sent by client in type 1 ntlmsssp exchange */ + __u32 client_flags; + /* sent by server in type 2 ntlmssp exchange */ + __u32 conn_flags; + /* sent to server */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; + /* used by ntlmssp */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; +}; +#endif /* __KSMBD_NTLMSSP_H */ diff -Naur --no-dereference a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c --- a/fs/ksmbd/oplock.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/oplock.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1992 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "oplock.h" + +#include "smb_common.h" +#ifdef CONFIG_SMB_INSECURE_SERVER +#include "smb1pdu.h" +#endif +#include "smbstatus.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" + +static LIST_HEAD(lease_table_list); +static DEFINE_RWLOCK(lease_list_lock); + +/** + * alloc_opinfo() - allocate a new opinfo object for oplock info + * @work: smb work + * @id: fid of open file + * @Tid: tree id of connection + * + * Return: allocated opinfo object on success, otherwise NULL + */ +static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, + u64 id, __u16 Tid) +{ + struct ksmbd_session *sess = work->sess; + struct oplock_info *opinfo; + + opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + if (!opinfo) + return NULL; + + opinfo->sess = sess; + opinfo->conn = sess->conn; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + opinfo->pending_break = 0; + opinfo->fid = id; + opinfo->Tid = Tid; +#ifdef CONFIG_SMB_INSECURE_SERVER + opinfo->is_smb2 = IS_SMB2(sess->conn); +#endif + INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->interim_list); + init_waitqueue_head(&opinfo->oplock_q); + init_waitqueue_head(&opinfo->oplock_brk); + atomic_set(&opinfo->refcount, 1); + atomic_set(&opinfo->breaking_cnt, 0); + + return opinfo; +} + +static void lease_add_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + spin_lock(&lb->lb_lock); + list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + spin_unlock(&lb->lb_lock); +} + +static void lease_del_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + if (!lb) + return; + + spin_lock(&lb->lb_lock); + if (list_empty(&opinfo->lease_entry)) { + spin_unlock(&lb->lb_lock); + return; + } + + list_del_init(&opinfo->lease_entry); + opinfo->o_lease->l_lb = NULL; + spin_unlock(&lb->lb_lock); +} + +static void lb_add(struct lease_table *lb) +{ + write_lock(&lease_list_lock); + list_add(&lb->l_entry, &lease_table_list); + write_unlock(&lease_list_lock); +} + +static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) +{ + struct lease *lease; + + lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + if (!lease) + return -ENOMEM; + + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + lease->state = lctx->req_state; + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = 0; + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + + return 0; +} + +static void free_lease(struct oplock_info *opinfo) +{ + struct lease *lease; + + lease = opinfo->o_lease; + kfree(lease); +} + +static void free_opinfo(struct oplock_info *opinfo) +{ + if (opinfo->is_lease) + free_lease(opinfo); + kfree(opinfo); +} + +static inline void opinfo_free_rcu(struct rcu_head *rcu_head) +{ + struct oplock_info *opinfo; + + opinfo = container_of(rcu_head, struct oplock_info, rcu_head); + free_opinfo(opinfo); +} + +struct oplock_info *opinfo_get(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo; + + if (list_empty(&ci->m_op_list)) + return NULL; + + rcu_read_lock(); + opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, + op_entry); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +void opinfo_put(struct oplock_info *opinfo) +{ + if (!atomic_dec_and_test(&opinfo->refcount)) + return; + + call_rcu(&opinfo->rcu_head, opinfo_free_rcu); +} + +static void opinfo_add(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + write_lock(&ci->m_lock); + list_add_rcu(&opinfo->op_entry, &ci->m_op_list); + write_unlock(&ci->m_lock); +} + +static void opinfo_del(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + if (opinfo->is_lease) { + write_lock(&lease_list_lock); + lease_del_list(opinfo); + write_unlock(&lease_list_lock); + } + write_lock(&ci->m_lock); + list_del_rcu(&opinfo->op_entry); + write_unlock(&ci->m_lock); +} + +static unsigned long opinfo_count(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_read(&fp->f_ci->sop_count); + else + return atomic_read(&fp->f_ci->op_count); +} + +static void opinfo_count_inc(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_inc(&fp->f_ci->sop_count); + else + return atomic_inc(&fp->f_ci->op_count); +} + +static void opinfo_count_dec(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_dec(&fp->f_ci->sop_count); + else + return atomic_dec(&fp->f_ci->op_count); +} + +/** + * opinfo_write_to_read() - convert a write oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo->is_smb2) { + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; + } else { + if (!(opinfo->level == OPLOCK_EXCLUSIVE || + opinfo->level == OPLOCK_BATCH)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + return -EINVAL; + } + opinfo->level = OPLOCK_READ; + } +#else + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; +#endif + return 0; +} + +/** + * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_handle_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + lease->state = lease->new_state; + opinfo->level = SMB2_OPLOCK_LEVEL_II; + return 0; +} + +/** + * opinfo_write_to_none() - convert a write oplock to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo->is_smb2) { + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + } else { + if (!(opinfo->level == OPLOCK_EXCLUSIVE || + opinfo->level == OPLOCK_BATCH)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + return -EINVAL; + } + opinfo->level = OPLOCK_NONE; + } +#else + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; +#endif + return 0; +} + +/** + * opinfo_read_to_none() - convert a write read to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo->is_smb2) { + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + } else { + if (opinfo->level != OPLOCK_READ) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + return -EINVAL; + } + opinfo->level = OPLOCK_NONE; + } +#else + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; +#endif + return 0; +} + +/** + * lease_read_to_write() - upgrade lease state from read to write + * @opinfo: current lease info + * + * Return: 0 on success, otherwise -EINVAL + */ +int lease_read_to_write(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state |= SMB2_LEASE_WRITE_CACHING_LE; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + return 0; +} + +/** + * lease_none_upgrade() - upgrade lease state from none + * @opinfo: current lease info + * @new_state: new lease state + * + * Return: 0 on success, otherwise -EINVAL + */ +static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state == SMB2_LEASE_NONE_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state = new_state; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_II; + else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + else if (lease->state & SMB2_LEASE_READ_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + return 0; +} + +/** + * close_id_del_oplock() - release oplock object at file close time + * @fp: ksmbd file pointer + */ +void close_id_del_oplock(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return; + + opinfo = opinfo_get(fp); + if (!opinfo) + return; + + opinfo_del(opinfo); + + rcu_assign_pointer(fp->f_opinfo, NULL); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + opinfo->op_state = OPLOCK_CLOSING; + wake_up_interruptible_all(&opinfo->oplock_q); + if (opinfo->is_lease) { + atomic_set(&opinfo->breaking_cnt, 0); + wake_up_interruptible_all(&opinfo->oplock_brk); + } + } + + opinfo_count_dec(fp); + atomic_dec(&opinfo->refcount); + opinfo_put(opinfo); +} + +/** + * grant_write_oplock() - grant exclusive/batch oplock or write lease + * @opinfo_new: new oplock info object + * @req_oplock: request oplock + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo_new->is_smb2) { + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else { + if (req_oplock == REQ_BATCHOPLOCK) + opinfo_new->level = OPLOCK_BATCH; + else + opinfo_new->level = OPLOCK_EXCLUSIVE; + } +#else + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; +#endif + + if (lctx) { + lease->state = lctx->req_state; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_read_oplock() - grant level2 oplock or read lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_read_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo_new->is_smb2) + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; + else + opinfo_new->level = OPLOCK_READ; +#else + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; +#endif + + if (lctx) { + lease->state = SMB2_LEASE_READ_CACHING_LE; + if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_none_oplock() - grant none oplock or none lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_none_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo_new->is_smb2) + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; + else + opinfo_new->level = OPLOCK_NONE; +#else + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; +#endif + + if (lctx) { + lease->state = 0; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +static inline int compare_guid_key(struct oplock_info *opinfo, + const char *guid1, const char *key1) +{ + const char *guid2, *key2; + + guid2 = opinfo->conn->ClientGUID; + key2 = opinfo->o_lease->lease_key; + if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && + !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) + return 1; + + return 0; +} + +/** + * same_client_has_lease() - check whether current lease request is + * from lease owner of file + * @ci: master file pointer + * @client_guid: Client GUID + * @lctx: lease context information + * + * Return: oplock(lease) object on success, otherwise NULL + */ +static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + char *client_guid, + struct lease_ctx_info *lctx) +{ + int ret; + struct lease *lease; + struct oplock_info *opinfo; + struct oplock_info *m_opinfo = NULL; + + if (!lctx) + return NULL; + + /* + * Compare lease key and client_guid to know request from same owner + * of same client + */ + read_lock(&ci->m_lock); + list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { + if (!opinfo->is_lease) + continue; + read_unlock(&ci->m_lock); + lease = opinfo->o_lease; + + ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); + if (ret) { + m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ + if (atomic_read(&opinfo->breaking_cnt)) { + read_lock(&ci->m_lock); + continue; + } + + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { + if (lease->state == + (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { + if (lctx->req_state == + (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + lease->state = lctx->req_state; + } + + if (lctx->req_state && lease->state == + SMB2_LEASE_NONE_LE) + lease_none_upgrade(opinfo, lctx->req_state); + } + read_lock(&ci->m_lock); + } + read_unlock(&ci->m_lock); + + return m_opinfo; +} + +static void wait_for_break_ack(struct oplock_info *opinfo) +{ + int rc = 0; + + rc = wait_event_interruptible_timeout(opinfo->oplock_q, + opinfo->op_state == OPLOCK_STATE_NONE || + opinfo->op_state == OPLOCK_CLOSING, + OPLOCK_WAIT_TIME); + + /* is this a timeout ? */ + if (!rc) { + if (opinfo->is_lease) + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + } +} + +static void wake_up_oplock_break(struct oplock_info *opinfo) +{ + clear_bit_unlock(0, &opinfo->pending_break); + /* memory barrier is needed for wake_up_bit() */ + smp_mb__after_atomic(); + wake_up_bit(&opinfo->pending_break, 0); +} + +static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) +{ + while (test_and_set_bit(0, &opinfo->pending_break)) { + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); + + /* Not immediately break to none. */ + opinfo->open_trunc = 0; + + if (opinfo->op_state == OPLOCK_CLOSING) + return -ENOENT; + else if (!opinfo->is_lease && opinfo->level <= req_op_level) + return 1; + } + + if (!opinfo->is_lease && opinfo->level <= req_op_level) { + wake_up_oplock_break(opinfo); + return 1; + } + return 0; +} + +static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +{ + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); + if (!work->response_buf) + return -ENOMEM; + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + return 0; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * smb1_oplock_break_noti() - send smb1 oplock break cmd from conn + * to client + * @work: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * work->request_buf contains oplock_info. + */ +static void __smb1_oplock_break_noti(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr; + struct smb_com_lock_req *req; + struct oplock_info *opinfo = work->request_buf; + + if (allocate_oplock_break_buf(work)) { + pr_err("smb_allocate_rsp_buf failed! "); + ksmbd_free_work_struct(work); + return; + } + + /* Init response header */ + rsp_hdr = work->response_buf; + /* wct is 8 for locking andx(18) */ + memset(rsp_hdr, 0, sizeof(struct smb_hdr) + 18); + rsp_hdr->smb_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals) + 18); + rsp_hdr->Protocol[0] = 0xFF; + rsp_hdr->Protocol[1] = 'S'; + rsp_hdr->Protocol[2] = 'M'; + rsp_hdr->Protocol[3] = 'B'; + + rsp_hdr->Command = SMB_COM_LOCKING_ANDX; + /* we know unicode, long file name and use nt error codes */ + rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_KNOWS_LONG_NAMES | + SMBFLG2_ERR_STATUS; + rsp_hdr->Uid = cpu_to_le16(work->sess->id); + rsp_hdr->Pid = cpu_to_le16(0xFFFF); + rsp_hdr->Mid = cpu_to_le16(0xFFFF); + rsp_hdr->Tid = cpu_to_le16(opinfo->Tid); + rsp_hdr->WordCount = 8; + + /* Init locking request */ + req = work->response_buf; + + req->AndXCommand = 0xFF; + req->AndXReserved = 0; + req->AndXOffset = 0; + req->Fid = opinfo->fid; + req->LockType = LOCKING_ANDX_OPLOCK_RELEASE; + if (!opinfo->open_trunc && + (opinfo->level == OPLOCK_BATCH || + opinfo->level == OPLOCK_EXCLUSIVE)) + req->OplockLevel = 1; + else + req->OplockLevel = 0; + req->Timeout = 0; + req->NumberOfUnlocks = 0; + req->ByteCount = 0; + ksmbd_debug(OPLOCK, "sending oplock break for fid %d lock level = %d\n", + req->Fid, req->OplockLevel); + + ksmbd_conn_write(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * smb1_oplock_break() - send smb1 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * @ack_required if requiring ack + * + * Return: 0 on success, otherwise error + */ +static int smb1_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + work->request_buf = (char *)opinfo; + work->conn = conn; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb1_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb1_oplock_break_noti(&work->work); + if (opinfo->level == OPLOCK_READ) + opinfo->level = OPLOCK_NONE; + } + return 0; +} +#endif + +/** + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client + * @wk: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * work->request_buf contains oplock_info. + */ +static void __smb2_oplock_break_noti(struct work_struct *wk) +{ + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_durable_fd(br_info->fid); + if (!fp) { + atomic_dec(&conn->r_count); + ksmbd_free_work_struct(work); + return; + } + + if (allocate_oplock_break_buf(work)) { + pr_err("smb2_allocate_rsp_buf failed! "); + atomic_dec(&conn->r_count); + ksmbd_fd_put(work, fp); + ksmbd_free_work_struct(work); + return; + } + + rsp_hdr = work->response_buf; + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = work->response_buf; + + rsp->StructureSize = cpu_to_le16(24); + if (!br_info->open_trunc && + (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || + br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; + else + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->PersistentFid = cpu_to_le64(fp->persistent_id); + rsp->VolatileFid = cpu_to_le64(fp->volatile_id); + + inc_rfc1001_len(rsp, 24); + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + + ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * + * Return: 0 on success, otherwise error + */ +static int smb2_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct oplock_break_info *br_info; + int ret = 0; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->level = opinfo->level; + br_info->fid = opinfo->fid; + br_info->open_trunc = opinfo->open_trunc; + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb2_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb2_oplock_break_noti(&work->work); + if (opinfo->level == SMB2_OPLOCK_LEVEL_II) + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + } + return ret; +} + +/** + * __smb2_lease_break_noti() - send lease break command from server + * to client + * @wk: smb work object + */ +static void __smb2_lease_break_noti(struct work_struct *wk) +{ + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = work->request_buf; + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + + if (allocate_oplock_break_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); + return; + } + + rsp_hdr = work->response_buf; + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = work->response_buf; + rsp->StructureSize = cpu_to_le16(44); + rsp->Epoch = br_info->epoch; + rsp->Flags = 0; + + if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; + + memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); + rsp->CurrentLeaseState = br_info->curr_state; + rsp->NewLeaseState = br_info->new_state; + rsp->BreakReason = 0; + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + + inc_rfc1001_len(rsp, 44); + + ksmbd_conn_write(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * smb2_lease_break_noti() - break lease when a new client request + * write lease + * @opinfo: conains lease state information + * + * Return: 0 on success, otherwise error + */ +static int smb2_lease_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct list_head *tmp, *t; + struct ksmbd_work *work; + struct lease_break_info *br_info; + struct lease *lease = opinfo->o_lease; + + work = ksmbd_alloc_work_struct(); + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->curr_state = lease->state; + br_info->new_state = lease->new_state; + if (lease->version == 2) + br_info->epoch = cpu_to_le16(++lease->epoch); + else + br_info->epoch = 0; + memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + list_for_each_safe(tmp, t, &opinfo->interim_list) { + struct ksmbd_work *in_work; + + in_work = list_entry(tmp, struct ksmbd_work, + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); + wait_for_break_ack(opinfo); + } else { + __smb2_lease_break_noti(&work->work); + if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + } + } + return 0; +} + +static void wait_lease_breaking(struct oplock_info *opinfo) +{ + if (!opinfo->is_lease) + return; + + wake_up_interruptible_all(&opinfo->oplock_brk); + if (atomic_read(&opinfo->breaking_cnt)) { + int ret = 0; + + ret = wait_event_interruptible_timeout(opinfo->oplock_brk, + atomic_read(&opinfo->breaking_cnt) == 0, + HZ); + if (!ret) + atomic_set(&opinfo->breaking_cnt, 0); + } +} + +static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) +{ + int err = 0; + + /* Need to break exclusive/batch oplock, write lease or overwrite_if */ + ksmbd_debug(OPLOCK, + "request to send oplock(level : 0x%x) break notification\n", + brk_opinfo->level); + + if (brk_opinfo->is_lease) { + struct lease *lease = brk_opinfo->o_lease; + + atomic_inc(&brk_opinfo->breaking_cnt); + + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->open_trunc) { + /* + * Create overwrite break trigger the lease break to + * none. + */ + lease->new_state = SMB2_LEASE_NONE_LE; + } else { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE; + else + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else + lease->new_state = SMB2_LEASE_NONE_LE; + } + } + + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + else + atomic_dec(&brk_opinfo->breaking_cnt); + } else { + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + } + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (brk_opinfo->is_smb2) + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); + else + err = smb1_oplock_break_noti(brk_opinfo); +#else + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); +#endif + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + wake_up_oplock_break(brk_opinfo); + + wait_lease_breaking(brk_opinfo); + + return err; +} + +void destroy_lease_table(struct ksmbd_conn *conn) +{ + struct lease_table *lb, *lbtmp; + struct oplock_info *opinfo; + + write_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + write_unlock(&lease_list_lock); + return; + } + + list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { + if (conn && memcmp(lb->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + continue; +again: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, + lease_entry) { + rcu_read_unlock(); + lease_del_list(opinfo); + goto again; + } + rcu_read_unlock(); + list_del(&lb->l_entry); + kfree(lb); + } + write_unlock(&lease_list_lock); +} + +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + int err = 0; + struct lease_table *lb; + + if (!lctx) + return err; + + read_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + read_unlock(&lease_list_lock); + return 0; + } + + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, sess->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + read_unlock(&lease_list_lock); + + return 0; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (opinfo->o_fp->f_ci == ci) + goto op_next; + err = compare_guid_key(opinfo, sess->conn->ClientGUID, + lctx->lease_key); + if (err) { + err = -EINVAL; + ksmbd_debug(OPLOCK, + "found same lease key is already used in other files\n"); + opinfo_put(opinfo); + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return err; +} + +static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) +{ + struct lease *lease1 = op1->o_lease; + struct lease *lease2 = op2->o_lease; + + op2->level = op1->level; + lease2->state = lease1->state; + memcpy(lease2->lease_key, lease1->lease_key, + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; +} + +static int add_lease_global_list(struct oplock_info *opinfo) +{ + struct lease_table *lb; + + read_lock(&lease_list_lock); + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + read_unlock(&lease_list_lock); + return 0; + } + } + read_unlock(&lease_list_lock); + + lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + memcpy(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + INIT_LIST_HEAD(&lb->lease_list); + spin_lock_init(&lb->lb_lock); + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + lb_add(lb); + return 0; +} + +static void set_oplock_level(struct oplock_info *opinfo, int level, + struct lease_ctx_info *lctx) +{ + switch (level) { +#ifdef CONFIG_SMB_INSECURE_SERVER + case REQ_OPLOCK: + case REQ_BATCHOPLOCK: +#endif + case SMB2_OPLOCK_LEVEL_BATCH: + case SMB2_OPLOCK_LEVEL_EXCLUSIVE: + grant_write_oplock(opinfo, level, lctx); + break; + case SMB2_OPLOCK_LEVEL_II: + grant_read_oplock(opinfo, lctx); + break; + default: + grant_none_oplock(opinfo, lctx); + break; + } +} + +/** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work + * @req_op_level: oplock level + * @pid: id of open file + * @fp: ksmbd file pointer + * @tid: Tree id of connection + * @lctx: lease context information on file open + * @share_ret: share mode + * + * Return: 0 on success, otherwise error + */ +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret) +{ + struct ksmbd_session *sess = work->sess; + int err = 0; + struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; + struct ksmbd_inode *ci = fp->f_ci; + bool prev_op_has_lease; + __le32 prev_op_state = 0; + + /* not support directory lease */ + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return 0; + + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; + + if (lctx) { + err = alloc_lease(opinfo, lctx); + if (err) + goto err_out; + opinfo->is_lease = 1; + } + + /* ci does not have any oplock */ + if (!opinfo_count(fp)) + goto set_lev; + + /* grant none-oplock if second open is trunc */ + if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + fp->cdoption != FILE_OVERWRITE_LE && + fp->cdoption != FILE_SUPERSEDE_LE) { + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + goto set_lev; + } + + if (lctx) { + struct oplock_info *m_opinfo; + + /* is lease already granted ? */ + m_opinfo = same_client_has_lease(ci, sess->conn->ClientGUID, + lctx); + if (m_opinfo) { + copy_lease(m_opinfo, opinfo); + if (atomic_read(&m_opinfo->breaking_cnt)) + opinfo->o_lease->flags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + goto out; + } + } + prev_opinfo = opinfo_get_list(ci); + if (!prev_opinfo || + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) + goto set_lev; + prev_op_has_lease = prev_opinfo->is_lease; + if (prev_op_has_lease) + prev_op_state = prev_opinfo->o_lease->state; + + if (share_ret < 0 && + prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + err = share_ret; + opinfo_put(prev_opinfo); + goto err_out; + } + + if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_put(prev_opinfo); + goto op_break_not_needed; + } + + list_add(&work->interim_entry, &prev_opinfo->interim_list); + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_put(prev_opinfo); + if (err == -ENOENT) + goto set_lev; + /* Check all oplock was freed by close */ + else if (err < 0) + goto err_out; + +op_break_not_needed: + if (share_ret < 0) { + err = share_ret; + goto err_out; + } + + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + /* grant fixed oplock on stacked locking between lease and oplock */ + if (prev_op_has_lease && !lctx) + if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + + if (!prev_op_has_lease && lctx) { + req_op_level = SMB2_OPLOCK_LEVEL_II; + lctx->req_state = SMB2_LEASE_READ_CACHING_LE; + } + +set_lev: + set_oplock_level(opinfo, req_op_level, lctx); + +out: + rcu_assign_pointer(fp->f_opinfo, opinfo); + opinfo->o_fp = fp; + + opinfo_count_inc(fp); + opinfo_add(opinfo); + if (opinfo->is_lease) { + err = add_lease_global_list(opinfo); + if (err) + goto err_out; + } + + return 0; +err_out: + free_opinfo(opinfo); + return err; +} + +/** + * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +static void smb_break_all_write_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + struct oplock_info *brk_opinfo; + + brk_opinfo = opinfo_get_list(fp->f_ci); + if (!brk_opinfo) + return; + if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_put(brk_opinfo); + return; + } + + brk_opinfo->open_trunc = is_trunc; + list_add(&work->interim_entry, &brk_opinfo->interim_list); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_put(brk_opinfo); +} + +/** + * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * from server to client + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) +{ + struct oplock_info *op, *brk_op; + struct ksmbd_inode *ci; + struct ksmbd_conn *conn = work->sess->conn; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + ci = fp->f_ci; + op = opinfo_get(fp); + + rcu_read_lock(); + list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + if (!atomic_inc_not_zero(&brk_op->refcount)) + continue; + rcu_read_unlock(); + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (brk_op->is_smb2) { + if (brk_op->is_lease && (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, + "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != + SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && + atomic_read(&brk_op->breaking_cnt)) + goto next; + } else { + if (brk_op->level != OPLOCK_READ) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + } +#else + if (brk_op->is_lease && + (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && + atomic_read(&brk_op->breaking_cnt)) + goto next; +#endif + + if (op && op->is_lease && brk_op->is_lease && + !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE) && + !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, + SMB2_LEASE_KEY_SIZE)) + goto next; + brk_op->open_trunc = is_trunc; + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); +next: + opinfo_put(brk_op); + rcu_read_lock(); + } + rcu_read_unlock(); + + if (op) + opinfo_put(op); +} + +/** + * smb_break_all_oplock() - break both batch/exclusive and level2 oplock + * @work: smb work + * @fp: ksmbd file pointer + */ +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + smb_break_all_write_oplock(work, fp, 1); + smb_break_all_levII_oplock(work, fp, 1); +} + +/** + * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type + * @lease_state: lease type + * + * Return: 0 if no mapping, otherwise corresponding oplock type + */ +__u8 smb2_map_lease_to_oplock(__le32 lease_state) +{ + if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)) { + return SMB2_OPLOCK_LEVEL_BATCH; + } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && + lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { + return SMB2_OPLOCK_LEVEL_II; + } + return 0; +} + +/** + * create_lease_buf() - create lease context for open cmd response + * @rbuf: buffer to create lease context response + * @lease: buffer to stored parsed lease state information + */ +void create_lease_buf(u8 *rbuf, struct lease *lease) +{ + char *LeaseKey = (char *)&lease->lease_key; + + if (lease->version == 2) { + struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + char *ParentLeaseKey = (char *)&lease->parent_lease_key; + + memset(buf, 0, sizeof(struct create_lease_v2)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->lcontext.ParentLeaseKeyLow = *((__le64 *)ParentLeaseKey); + buf->lcontext.ParentLeaseKeyHigh = *((__le64 *)(ParentLeaseKey + 8)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } else { + struct create_lease *buf = (struct create_lease *)rbuf; + + memset(buf, 0, sizeof(struct create_lease)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } +} + +/** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request + * + * Return: oplock state, -ENOENT if create lease context not found + */ +struct lease_ctx_info *parse_lease_state(void *open_req) +{ + char *data_offset; + struct create_context *cc; + unsigned int next = 0; + char *name; + bool found = false; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info), + GFP_KERNEL); + if (!lreq) + return NULL; + + data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset); + cc = (struct create_context *)data_offset; + do { + cc = (struct create_context *)((char *)cc + next); + name = le16_to_cpu(cc->NameOffset) + (char *)cc; + if (le16_to_cpu(cc->NameLength) != 4 || + strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { + next = le32_to_cpu(cc->Next); + continue; + } + found = true; + break; + } while (next != 0); + + if (found) { + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + *((__le64 *)lreq->parent_lease_key) = lc->lcontext.ParentLeaseKeyLow; + *((__le64 *)(lreq->parent_lease_key + 8)) = lc->lcontext.ParentLeaseKeyHigh; + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; + + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; + } + return lreq; + } + + kfree(lreq); + return NULL; +} + +/** + * smb2_find_context_vals() - find a particular context info in open request + * @open_req: buffer containing smb2 file open(create) request + * @tag: context name to search for + * + * Return: pointer to requested context, NULL if @str context not found + * or error pointer if name length is invalid. + */ +struct create_context *smb2_find_context_vals(void *open_req, const char *tag) +{ + struct create_context *cc; + unsigned int next = 0; + char *name; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + unsigned int remain_len, name_off, name_len, value_off, value_len, + cc_len; + + /* + * CreateContextsOffset and CreateContextsLength are guaranteed to + * be valid because of ksmbd_smb2_check_message(). + */ + cc = (struct create_context *)((char *)req + 4 + + le32_to_cpu(req->CreateContextsOffset)); + remain_len = le32_to_cpu(req->CreateContextsLength); + do { + cc = (struct create_context *)((char *)cc + next); + if (remain_len < offsetof(struct create_context, Buffer)) + return ERR_PTR(-EINVAL); + + next = le32_to_cpu(cc->Next); + name_off = le16_to_cpu(cc->NameOffset); + name_len = le16_to_cpu(cc->NameLength); + value_off = le16_to_cpu(cc->DataOffset); + value_len = le32_to_cpu(cc->DataLength); + cc_len = next ? next : remain_len; + + if ((next & 0x7) != 0 || + next > remain_len || + name_off != offsetof(struct create_context, Buffer) || + name_len < 4 || + name_off + name_len > cc_len || + (value_off & 0x7) != 0 || + (value_off && (value_off < name_off + name_len)) || + ((u64)value_off + value_len > cc_len)) + return ERR_PTR(-EINVAL); + + name = (char *)cc + name_off; + if (memcmp(name, tag, name_len) == 0) + return cc; + + remain_len -= next; + } while (next != 0); + + return NULL; +} + +/** + * create_durable_rsp_buf() - create durable handle context + * @cc: buffer to create durable context response + */ +void create_durable_rsp_buf(char *cc) +{ + struct create_durable_rsp *buf; + + buf = (struct create_durable_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; +} + +/** + * create_durable_v2_rsp_buf() - create durable handle v2 context + * @cc: buffer to create durable context response + * @fp: ksmbd file pointer + */ +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_durable_v2_rsp *buf; + + buf = (struct create_durable_v2_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + + buf->Timeout = cpu_to_le32(fp->durable_timeout); +} + +/** + * create_mxac_rsp_buf() - create query maximal access context + * @cc: buffer to create maximal access context response + * @maximal_access: maximal access + */ +void create_mxac_rsp_buf(char *cc, int maximal_access) +{ + struct create_mxac_rsp *buf; + + buf = (struct create_mxac_rsp *)cc; + memset(buf, 0, sizeof(struct create_mxac_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, QueryStatus)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ + buf->Name[0] = 'M'; + buf->Name[1] = 'x'; + buf->Name[2] = 'A'; + buf->Name[3] = 'c'; + + buf->QueryStatus = STATUS_SUCCESS; + buf->MaximalAccess = cpu_to_le32(maximal_access); +} + +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) +{ + struct create_disk_id_rsp *buf; + + buf = (struct create_disk_id_rsp *)cc; + memset(buf, 0, sizeof(struct create_disk_id_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_disk_id_rsp, DiskFileId)); + buf->ccontext.DataLength = cpu_to_le32(32); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + + buf->DiskFileId = cpu_to_le64(file_id); + buf->VolumeId = cpu_to_le64(vol_id); +} + +/** + * create_posix_rsp_buf() - create posix extension context + * @cc: buffer to create posix on posix response + * @fp: ksmbd file pointer + */ +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_posix_rsp *buf; + struct inode *inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + struct user_namespace *user_ns; +#endif + + buf = (struct create_posix_rsp *)cc; + memset(buf, 0, sizeof(struct create_posix_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, nlink)); + buf->ccontext.DataLength = cpu_to_le32(52); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + + buf->nlink = cpu_to_le32(inode->i_nlink); + buf->reparse_tag = cpu_to_le32(fp->volatile_id); + buf->mode = cpu_to_le32(inode->i_mode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + user_ns = file_mnt_user_ns(fp->filp); + id_to_sid(from_kuid_munged(&init_user_ns, + i_uid_into_mnt(user_ns, inode)), +#else + id_to_sid(from_kuid_munged(&init_user_ns, inode->i_uid), +#endif + SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + id_to_sid(from_kgid_munged(&init_user_ns, + i_gid_into_mnt(user_ns, inode)), +#else + id_to_sid(from_kgid_munged(&init_user_ns, inode->i_gid), +#endif + SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]); +} + +/* + * Find lease object(opinfo) for given lease key/fid from lease + * break/file close path. + */ +/** + * lookup_lease_in_table() - find a matching lease info object + * @conn: connection instance + * @lease_key: lease key to be searched for + * + * Return: opinfo if found matching opinfo, otherwise NULL + */ +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key) +{ + struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease_table *lt; + int ret; + + read_lock(&lease_list_lock); + list_for_each_entry(lt, &lease_table_list, l_entry) { + if (!memcmp(lt->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + + read_unlock(&lease_list_lock); + return NULL; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) + goto op_next; + if (!(opinfo->o_lease->state & + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + goto op_next; + ret = compare_guid_key(opinfo, conn->ClientGUID, + lease_key); + if (ret) { + ksmbd_debug(OPLOCK, "found opinfo\n"); + ret_op = opinfo; + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return ret_op; +} + +int smb2_check_durable_oplock(struct ksmbd_file *fp, + struct lease_ctx_info *lctx, char *name) +{ + struct oplock_info *opinfo = opinfo_get(fp); + int ret = 0; + + if (opinfo && opinfo->is_lease) { + if (!lctx) { + pr_err("open does not include lease\n"); + ret = -EBADF; + goto out; + } + if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE)) { + pr_err("invalid lease key\n"); + ret = -EBADF; + goto out; + } + if (name && strcmp(fp->filename, name)) { + pr_err("invalid name reconnect %s\n", name); + ret = -EINVAL; + goto out; + } + } +out: + if (opinfo) + opinfo_put(opinfo); + return ret; +} diff -Naur --no-dereference a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h --- a/fs/ksmbd/oplock.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/oplock.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_OPLOCK_H +#define __KSMBD_OPLOCK_H + +#include "smb_common.h" + +#define OPLOCK_WAIT_TIME (35 * HZ) + +#ifdef CONFIG_SMB_INSECURE_SERVER +/* SMB Oplock levels */ +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ +#endif + +/* SMB2 Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* Oplock states */ +#define OPLOCK_STATE_NONE 0x00 +#define OPLOCK_ACK_WAIT 0x01 +#define OPLOCK_CLOSING 0x02 + +#define OPLOCK_WRITE_TO_READ 0x01 +#define OPLOCK_READ_HANDLE_TO_READ 0x02 +#define OPLOCK_WRITE_TO_NONE 0x04 +#define OPLOCK_READ_TO_NONE 0x08 + +#define SMB2_LEASE_KEY_SIZE 16 + +struct lease_ctx_info { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; +}; + +struct lease_table { + char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct list_head lease_list; + struct list_head l_entry; + spinlock_t lb_lock; +}; + +struct lease { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 state; + __le32 new_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; + struct lease_table *l_lb; +}; + +struct oplock_info { + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_work *work; + struct ksmbd_file *o_fp; + int level; + int op_state; + unsigned long pending_break; + u64 fid; + atomic_t breaking_cnt; + atomic_t refcount; + __u16 Tid; + bool is_lease; +#ifdef CONFIG_SMB_INSECURE_SERVER + bool is_smb2; +#endif + bool open_trunc; /* truncate on open */ + struct lease *o_lease; + struct list_head interim_list; + struct list_head op_entry; + struct list_head lease_entry; + wait_queue_head_t oplock_q; /* Other server threads */ + wait_queue_head_t oplock_brk; /* oplock breaking wait */ + struct rcu_head rcu_head; +}; + +struct lease_break_info { + __le32 curr_state; + __le32 new_state; + __le16 epoch; + char lease_key[SMB2_LEASE_KEY_SIZE]; +}; + +struct oplock_break_info { + int level; + int open_trunc; + int fid; +}; + +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, + u64 pid, struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret); +void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +int opinfo_write_to_read(struct oplock_info *opinfo); +int opinfo_read_handle_to_read(struct oplock_info *opinfo); +int opinfo_write_to_none(struct oplock_info *opinfo); +int opinfo_read_to_none(struct oplock_info *opinfo); +void close_id_del_oplock(struct ksmbd_file *fp); +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); +struct oplock_info *opinfo_get(struct ksmbd_file *fp); +void opinfo_put(struct oplock_info *opinfo); + +/* Lease related functions */ +void create_lease_buf(u8 *rbuf, struct lease *lease); +struct lease_ctx_info *parse_lease_state(void *open_req); +__u8 smb2_map_lease_to_oplock(__le32 lease_state); +int lease_read_to_write(struct oplock_info *opinfo); + +/* Durable related functions */ +void create_durable_rsp_buf(char *cc); +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); +void create_mxac_rsp_buf(char *cc, int maximal_access); +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); +struct create_context *smb2_find_context_vals(void *open_req, const char *str); +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key); +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); +void destroy_lease_table(struct ksmbd_conn *conn); +int smb2_check_durable_oplock(struct ksmbd_file *fp, + struct lease_ctx_info *lctx, char *name); +#endif /* __KSMBD_OPLOCK_H */ diff -Naur --no-dereference a/fs/ksmbd/README.md b/fs/ksmbd/README.md --- a/fs/ksmbd/README.md 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/README.md 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,180 @@ + +# Content + +- [What is KSMBD?](#KSMBDwhat-is-ksmbd) +- [Under PFIF](#under-pfif) +- [Git](#git) +- [Maintainers](#maintainers) +- [Bug reports or contribution](#Bug-reports-or-contribution) +- [Features](#features) +- [Supported Linux Kernel Versions](#supported-linux-kernel-versions) +- [KSMBD architecture](#ksmbd-architecture) + + +## What is KSMBD? + +KSMBD is an opensource In-kernel CIFS/SMB3 server created by Namjae Jeon for Linux Kernel. It's an implementation of SMB/CIFS protocol in kernel space for sharing files and IPC services over network. Initially the target is to provide improved file I/O performances, but the bigger goal is to have some new features which are much easier to develop and maintain inside the kernel and expose the layers fully. Directions can be attributed to sections where SAMBA is moving to few modules inside the kernel to have features like RDMA(Remote direct memory access) to work with actual performance gain. + + +## Under PFIF + +This code was developed in participation with the Protocol Freedom Information Foundation. + +Please see +* http://protocolfreedom.org/ +* http://samba.org/samba/PFIF/ +for more details. + + +## Git + +The development git tree is available at +* https://github.com/cifsd-team/ksmbd +* https://github.com/cifsd-team/ksmbd-tools + + +## Maintainers + +* Namjae Jeon +* Sergey Senozhatsky + + +## Bug reports or contribution + +For reporting bugs and sending patches, please send the patches to the following mail address: + +* linux-cifsd-devel@lists.sourceforge.net +* linkinjeon@kernel.org +* sergey.senozhatsky@gmail.com + +or open issues/send PRs to [KSMBD](https://github.com/cifsd-team/ksmbd). + +## linux-cifsd-devel mailing list subscription + +For subscribing to maling list, Insert your mail and info after accessing the following address: + +* https://sourceforge.net/projects/linux-cifsd/lists/linux-cifsd-devel + +## Installing as a stand-alone module + +Install prerequisite package for Fedora, RHEL: +``` + yum install kernel-devel-$(uname -r) +``` + +Build step: +``` + make + sudo make install +``` + +To load the driver manually, run this as root: +``` + modprobe ksmbd +``` + + +## Installing as a part of the kernel + +1. Let's take [linux] as the path to your kernel source dir. +``` + cd [linux] + cp -ar ksmbd [linux]/fs/ +``` + +2. edit [linux]/fs/Kconfig +``` + source "fs/cifs/Kconfig" + +source "fs/ksmbd/Kconfig" + source "fs/coda/Kconfig" +``` + +3. edit [linux]/fs/Makefile +``` + obj-$(CONFIG_CIFS) += cifs/ + +obj-$(CONFIG_SMB_SERVER) += ksmbd/ + obj-$(CONFIG_HPFS_FS) += hpfs/ +``` +4. make menuconfig and set ksmbd +``` + [*] Network File Systems ---> + SMB server support +``` + +build your kernel + + +## Features + +*Implemented* +1. SMB1(CIFS), SMB2/3 protocols for basic file sharing +2. Dynamic crediting +3. Compound requests +4. oplock/lease +5. Large MTU +6. NTLM/NTLMv2 +7. Auto negotiation +8. HMAC-SHA256 Signing +9. Secure negotiate +10. Signing Update +11. Pre-authentication integrity(SMB 3.1.1) +12. SMB3 encryption(CCM, GCM) +13. SMB direct(RDMA) +14. Win-ACL +15. Kerberos +16. Multi-channel + +*Planned* +1. Durable handle v1/v2 +2. Persistent handles +3. Directory lease + + +## Supported Linux Kernel Versions + +* Linux Kernel 5.4 or later + + +## KSMBD architecture + +``` + |--- ... + --------|--- ksmbd/3 - Client 3 + |-------|--- ksmbd/2 - Client 2 + | | _____________________________________________________ + | | |- Client 1 | +<--- Socket ---|--- ksmbd/1 <<= Authentication : NTLM/NTLM2, Kerberos(TODO)| + | | | | <<= SMB : SMB1, SMB2, SMB2.1, SMB3, SMB3.0.2, | + | | | | SMB3.1.1 | + | | | |_____________________________________________________| + | | | + | | |--- VFS --- Local Filesystem + | | +KERNEL |--- ksmbd/0(forker kthread) +---------------||--------------------------------------------------------------- +USER || + || communication using NETLINK + || ______________________________________________ + || | | + ksmbd.mountd <<= DCE/RPC, WINREG | + ^ | <<= configure shares setting, user accounts | + | |______________________________________________| + | + |------ smb.conf(config file) + | + |------ ksmbdpwd.db(user account/password file) + ^ + ksmbd.adduser ---------------| + +``` + +## Performance + +1. ksmbd vs samba performance comparison using iozone (Linux Client) +

+ +2. ksmbd vs samba performance comparison using fileop (Linux Client) +

+ +3. ksmbd vs samba performance comparison using CrystalDiskMark (Windows Client) +

![CrystalDiskMark](https://github.com/cifsd-team/cifsd-perf/blob/master/CrystalDiskMark_Performance.JPG) diff -Naur --no-dereference a/fs/ksmbd/server.c b/fs/ksmbd/server.c --- a/fs/ksmbd/server.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/server.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "oplock.h" +#include "misc.h" +#include +#include +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "connection.h" +#include "transport_ipc.h" +#include "mgmt/user_session.h" +#include "crypto_ctx.h" +#include "auth.h" + +int ksmbd_debug_types; + +struct ksmbd_server_config server_conf; + +enum SERVER_CTRL_TYPE { + SERVER_CTRL_TYPE_INIT, + SERVER_CTRL_TYPE_RESET, +}; + +struct server_ctrl_struct { + int type; + struct work_struct ctrl_work; +}; + +static DEFINE_MUTEX(ctrl_lock); + +static int ___server_conf_set(int idx, char *val) +{ + if (idx >= ARRAY_SIZE(server_conf.conf)) + return -EINVAL; + + if (!val || val[0] == 0x00) + return -EINVAL; + + kfree(server_conf.conf[idx]); + server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + if (!server_conf.conf[idx]) + return -ENOMEM; + return 0; +} + +int ksmbd_set_netbios_name(char *v) +{ + return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); +} + +int ksmbd_set_server_string(char *v) +{ + return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); +} + +int ksmbd_set_work_group(char *v) +{ + return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); +} + +char *ksmbd_netbios_name(void) +{ + return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; +} + +char *ksmbd_server_string(void) +{ + return server_conf.conf[SERVER_CONF_SERVER_STRING]; +} + +char *ksmbd_work_group(void) +{ + return server_conf.conf[SERVER_CONF_WORK_GROUP]; +} + +/** + * check_conn_state() - check state of server thread connection + * @work: smb work containing server thread information + * + * Return: 0 on valid connection, otherwise 1 to reconnect + */ +static inline int check_conn_state(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr; + + if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { + rsp_hdr = work->response_buf; + rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; + return 1; + } + return 0; +} + +#define SERVER_HANDLER_CONTINUE 0 +#define SERVER_HANDLER_ABORT 1 + +static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + u16 *cmd) +{ + struct smb_version_cmds *cmds; + u16 command; + int ret; + + if (check_conn_state(work)) + return SERVER_HANDLER_CONTINUE; + + if (ksmbd_verify_smb_message(work)) + return SERVER_HANDLER_ABORT; + + command = conn->ops->get_cmd_val(work); + *cmd = command; + +andx_again: + if (command >= conn->max_cmds) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return SERVER_HANDLER_CONTINUE; + } + + cmds = &conn->cmds[command]; + if (!cmds->proc) { + ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); + conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); + return SERVER_HANDLER_CONTINUE; + } + + if (work->sess && conn->ops->is_sign_req(work, command)) { + ret = conn->ops->check_sign_req(work); + if (!ret) { + conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); + return SERVER_HANDLER_CONTINUE; + } + } + + ret = cmds->proc(work); + + if (ret < 0) + ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); + /* AndX commands - chained request can return positive values */ + else if (ret > 0) { + command = ret; + *cmd = command; + goto andx_again; + } + + if (work->send_no_response) + return SERVER_HANDLER_ABORT; + return SERVER_HANDLER_CONTINUE; +} + +static void __handle_ksmbd_work(struct ksmbd_work *work, + struct ksmbd_conn *conn) +{ + u16 command = 0; + int rc; + + if (conn->ops->allocate_rsp_buf(work)) + return; + + if (conn->ops->is_transform_hdr && + conn->ops->is_transform_hdr(work->request_buf)) { + rc = conn->ops->decrypt_req(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + + work->encrypted = true; + } + + rc = conn->ops->init_rsp_hdr(work); + if (rc) { + /* either uid or tid is not correct */ + conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); + goto send; + } + + if (conn->ops->check_user_session) { + rc = conn->ops->check_user_session(work); + if (rc < 0) { + command = conn->ops->get_cmd_val(work); + conn->ops->set_rsp_status(work, + STATUS_USER_SESSION_DELETED); + goto send; + } else if (rc > 0) { + rc = conn->ops->get_ksmbd_tcon(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_NETWORK_NAME_DELETED); + goto send; + } + } + } + + do { + rc = __process_request(work, conn, &command); + if (rc == SERVER_HANDLER_ABORT) + break; + + /* + * Call smb2_set_rsp_credits() function to set number of credits + * granted in hdr of smb2 response. + */ + if (conn->ops->set_rsp_credits) { + spin_lock(&conn->credits_lock); + rc = conn->ops->set_rsp_credits(work); + spin_unlock(&conn->credits_lock); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + goto send; + } + } + + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); + } while (is_chained_smb2_message(work)); + + if (work->send_no_response) + return; + +send: + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { + rc = conn->ops->encrypt_resp(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + } + + ksmbd_conn_write(work); +} + +/** + * handle_ksmbd_work() - process pending smb work requests + * @wk: smb work containing request command buffer + * + * called by kworker threads to processing remaining smb work requests + */ +static void handle_ksmbd_work(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + + atomic64_inc(&conn->stats.request_served); + + __handle_ksmbd_work(work, conn); + + ksmbd_conn_try_dequeue_request(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * queue_ksmbd_work() - queue a smb request to worker thread queue + * for proccessing smb command and sending response + * @conn: connection instance + * + * read remaining data from socket create and submit work. + */ +static int queue_ksmbd_work(struct ksmbd_conn *conn) +{ + struct ksmbd_work *work; + + work = ksmbd_alloc_work_struct(); + if (!work) { + pr_err("allocation for work failed\n"); + return -ENOMEM; + } + + work->conn = conn; + work->request_buf = conn->request_buf; + conn->request_buf = NULL; + + if (ksmbd_init_smb_server(work)) { + ksmbd_free_work_struct(work); + return -EINVAL; + } + + ksmbd_conn_enqueue_request(work); + atomic_inc(&conn->r_count); + /* update activity on connection */ + conn->last_active = jiffies; + INIT_WORK(&work->work, handle_ksmbd_work); + ksmbd_queue_work(work); + return 0; +} + +static int ksmbd_server_process_request(struct ksmbd_conn *conn) +{ + return queue_ksmbd_work(conn); +} + +static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) +{ + ksmbd_sessions_deregister(conn); + destroy_lease_table(conn); + return 0; +} + +static void ksmbd_server_tcp_callbacks_init(void) +{ + struct ksmbd_conn_ops ops; + + ops.process_fn = ksmbd_server_process_request; + ops.terminate_fn = ksmbd_server_terminate_conn; + + ksmbd_conn_init_server_callbacks(&ops); +} + +static void server_conf_free(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { + kfree(server_conf.conf[i]); + server_conf.conf[i] = NULL; + } +} + +static int server_conf_init(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); + server_conf.enforced_signing = 0; + server_conf.min_protocol = ksmbd_min_protocol(); + server_conf.max_protocol = ksmbd_max_protocol(); + server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | + KSMBD_AUTH_MSKRB5; +#endif + return 0; +} + +static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) +{ + int ret; + + ret = ksmbd_conn_transport_init(); + if (ret) { + server_queue_ctrl_reset_work(); + return; + } + + WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); +} + +static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) +{ + ksmbd_ipc_soft_reset(); + ksmbd_conn_transport_destroy(); + server_conf_free(); + server_conf_init(); + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); +} + +static void server_ctrl_handle_work(struct work_struct *work) +{ + struct server_ctrl_struct *ctrl; + + ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); + + mutex_lock(&ctrl_lock); + switch (ctrl->type) { + case SERVER_CTRL_TYPE_INIT: + server_ctrl_handle_init(ctrl); + break; + case SERVER_CTRL_TYPE_RESET: + server_ctrl_handle_reset(ctrl); + break; + default: + pr_err("Unknown server work type: %d\n", ctrl->type); + } + mutex_unlock(&ctrl_lock); + kfree(ctrl); + module_put(THIS_MODULE); +} + +static int __queue_ctrl_work(int type) +{ + struct server_ctrl_struct *ctrl; + + ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + __module_get(THIS_MODULE); + ctrl->type = type; + INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); + queue_work(system_long_wq, &ctrl->ctrl_work); + return 0; +} + +int server_queue_ctrl_init_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); +} + +int server_queue_ctrl_reset_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); +} + +static ssize_t stats_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + /* + * Inc this each time you change stats output format, + * so user space will know what to do. + */ + static int stats_version = 2; + static const char * const state[] = { + "startup", + "running", + "reset", + "shutdown" + }; + + ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, + state[server_conf.state], server_conf.tcp_port, + server_conf.ipc_last_active / HZ); + return sz; +} + +static ssize_t kill_server_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t len) +{ + if (!sysfs_streq(buf, "hard")) + return len; + + pr_info("kill command received\n"); + mutex_lock(&ctrl_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + __module_get(THIS_MODULE); + server_ctrl_handle_reset(NULL); + module_put(THIS_MODULE); + mutex_unlock(&ctrl_lock); + return len; +} + +static const char * const debug_type_strings[] = {"smb", "auth", "vfs", + "oplock", "ipc", "conn", + "rdma"}; + +static ssize_t debug_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + ssize_t sz = 0; + int i, pos = 0; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "[%s] ", + debug_type_strings[i]); + } else { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "%s ", + debug_type_strings[i]); + } + sz += pos; + } + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +static ssize_t debug_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if (sysfs_streq(buf, "all")) { + if (ksmbd_debug_types == KSMBD_DEBUG_ALL) + ksmbd_debug_types = 0; + else + ksmbd_debug_types = KSMBD_DEBUG_ALL; + break; + } + + if (sysfs_streq(buf, debug_type_strings[i])) { + if (ksmbd_debug_types & (1 << i)) + ksmbd_debug_types &= ~(1 << i); + else + ksmbd_debug_types |= (1 << i); + break; + } + } + + return len; +} + +static CLASS_ATTR_RO(stats); +static CLASS_ATTR_WO(kill_server); +static CLASS_ATTR_RW(debug); + +static struct attribute *ksmbd_control_class_attrs[] = { + &class_attr_stats.attr, + &class_attr_kill_server.attr, + &class_attr_debug.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ksmbd_control_class); + +static struct class ksmbd_control_class = { + .name = "ksmbd-control", + .owner = THIS_MODULE, + .class_groups = ksmbd_control_class_groups, +}; + +static int ksmbd_server_shutdown(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + + class_unregister(&ksmbd_control_class); + ksmbd_workqueue_destroy(); + ksmbd_ipc_release(); + ksmbd_conn_transport_destroy(); + ksmbd_crypto_destroy(); + ksmbd_free_global_file_table(); + destroy_lease_table(NULL); + ksmbd_work_pool_destroy(); + ksmbd_exit_file_cache(); + server_conf_free(); + return 0; +} + +static int __init ksmbd_server_init(void) +{ + int ret; + + ret = class_register(&ksmbd_control_class); + if (ret) { + pr_err("Unable to register ksmbd-control class\n"); + return ret; + } + + ksmbd_server_tcp_callbacks_init(); + + ret = server_conf_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_work_pool_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_init_file_cache(); + if (ret) + goto err_destroy_work_pools; + + ret = ksmbd_ipc_init(); + if (ret) + goto err_exit_file_cache; + + ret = ksmbd_init_global_file_table(); + if (ret) + goto err_ipc_release; + + ret = ksmbd_inode_hash_init(); + if (ret) + goto err_destroy_file_table; + + ret = ksmbd_crypto_create(); + if (ret) + goto err_release_inode_hash; + + ret = ksmbd_workqueue_init(); + if (ret) + goto err_crypto_destroy; + return 0; + +err_crypto_destroy: + ksmbd_crypto_destroy(); +err_release_inode_hash: + ksmbd_release_inode_hash(); +err_destroy_file_table: + ksmbd_free_global_file_table(); +err_ipc_release: + ksmbd_ipc_release(); +err_exit_file_cache: + ksmbd_exit_file_cache(); +err_destroy_work_pools: + ksmbd_work_pool_destroy(); +err_unregister: + class_unregister(&ksmbd_control_class); + + return ret; +} + +/** + * ksmbd_server_exit() - shutdown forker thread and free memory at module exit + */ +static void __exit ksmbd_server_exit(void) +{ + ksmbd_server_shutdown(); + ksmbd_release_inode_hash(); +} + +MODULE_AUTHOR("Namjae Jeon "); +MODULE_VERSION(KSMBD_VERSION); +MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ecb"); +MODULE_SOFTDEP("pre: hmac"); +MODULE_SOFTDEP("pre: md4"); +MODULE_SOFTDEP("pre: md5"); +MODULE_SOFTDEP("pre: nls"); +MODULE_SOFTDEP("pre: aes"); +MODULE_SOFTDEP("pre: cmac"); +MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: sha512"); +MODULE_SOFTDEP("pre: aead2"); +MODULE_SOFTDEP("pre: ccm"); +MODULE_SOFTDEP("pre: gcm"); +MODULE_SOFTDEP("pre: crc32"); +module_init(ksmbd_server_init) +module_exit(ksmbd_server_exit) diff -Naur --no-dereference a/fs/ksmbd/server.h b/fs/ksmbd/server.h --- a/fs/ksmbd/server.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/server.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include "smbacl.h" + +/* + * Server state type + */ +enum { + SERVER_STATE_STARTING_UP, + SERVER_STATE_RUNNING, + SERVER_STATE_RESETTING, + SERVER_STATE_SHUTTING_DOWN, +}; + +/* + * Server global config string index + */ +enum { + SERVER_CONF_NETBIOS_NAME, + SERVER_CONF_SERVER_STRING, + SERVER_CONF_WORK_GROUP, +}; + +struct ksmbd_server_config { + unsigned int flags; + unsigned int state; + short signing; + short enforced_signing; + short min_protocol; + short max_protocol; + unsigned short tcp_port; + unsigned short ipc_timeout; + unsigned long ipc_last_active; + unsigned long deadtime; + unsigned int share_fake_fscaps; + struct smb_sid domain_sid; + unsigned int auth_mechs; + + char *conf[SERVER_CONF_WORK_GROUP + 1]; +}; + +extern struct ksmbd_server_config server_conf; + +int ksmbd_set_netbios_name(char *v); +int ksmbd_set_server_string(char *v); +int ksmbd_set_work_group(char *v); + +char *ksmbd_netbios_name(void); +char *ksmbd_server_string(void); +char *ksmbd_work_group(void); + +static inline int ksmbd_server_running(void) +{ + return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; +} + +static inline int ksmbd_server_configurable(void) +{ + return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; +} + +int server_queue_ctrl_init_work(void); +int server_queue_ctrl_reset_work(void); +#endif /* __SERVER_H__ */ diff -Naur --no-dereference a/fs/ksmbd/smb1misc.c b/fs/ksmbd/smb1misc.c --- a/fs/ksmbd/smb1misc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb1misc.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "asn1.h" +#include "nterr.h" +#include "ksmbd_work.h" +#include "smb_common.h" +#include "smb1pdu.h" +#include "mgmt/user_session.h" + +/** + * check_smb_hdr() - check for valid smb request header + * @smb: smb header to be checked + * + * check for valid smb signature and packet direction(request/response) + * TODO: properly check client authetication and tree authentication + * + * Return: 0 on success, otherwise 1 + */ +static int check_smb1_hdr(struct smb_hdr *smb) +{ + /* does it have the right SMB "signature" ? */ + if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) { + ksmbd_debug(SMB, "Bad protocol string signature header 0x%x\n", + *(unsigned int *)smb->Protocol); + return 1; + } + ksmbd_debug(SMB, "got SMB\n"); + + /* if it's not a response then accept */ + /* TODO : check for oplock break */ + if (!(smb->Flags & SMBFLG_RESPONSE)) + return 0; + + ksmbd_debug(SMB, "Server sent request, not response\n"); + return 1; +} + + +static int smb1_req_struct_size(struct smb_hdr *hdr) +{ + int wc = hdr->WordCount; + + switch (hdr->Command) { + case SMB_COM_CREATE_DIRECTORY: + case SMB_COM_DELETE_DIRECTORY: + case SMB_COM_QUERY_INFORMATION: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_NEGOTIATE: + case SMB_COM_NT_CANCEL: + case SMB_COM_CHECK_DIRECTORY: + case SMB_COM_PROCESS_EXIT: + if (wc != 0x0) + return -EINVAL; + break; + case SMB_COM_FLUSH: + case SMB_COM_DELETE: + case SMB_COM_RENAME: + case SMB_COM_ECHO: + case SMB_COM_FIND_CLOSE2: + if (wc != 0x1) + return -EINVAL; + break; + case SMB_COM_LOGOFF_ANDX: + if (wc != 0x2) + return -EINVAL; + break; + case SMB_COM_CLOSE: + if (wc != 0x3) + return -EINVAL; + break; + case SMB_COM_TREE_CONNECT_ANDX: + case SMB_COM_NT_RENAME: + if (wc != 0x4) + return -EINVAL; + break; + case SMB_COM_WRITE: + if (wc != 0x5) + return -EINVAL; + break; + case SMB_COM_SETATTR: + case SMB_COM_LOCKING_ANDX: + if (wc != 0x8) + return -EINVAL; + break; + case SMB_COM_TRANSACTION: + if (wc < 0xe) + return -EINVAL; + break; + case SMB_COM_SESSION_SETUP_ANDX: + if (wc != 0xc && wc != 0xd) + return -EINVAL; + break; + case SMB_COM_OPEN_ANDX: + case SMB_COM_TRANSACTION2: + if (wc != 0xf) + return -EINVAL; + break; + case SMB_COM_NT_CREATE_ANDX: + if (wc != 0x18) + return -EINVAL; + break; + case SMB_COM_READ_ANDX: + if (wc != 0xa && wc != 0xc) + return -EINVAL; + break; + case SMB_COM_WRITE_ANDX: + if (wc != 0xc && wc != 0xe) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + return wc; +} + +static int smb1_get_byte_count(struct smb_hdr *hdr) +{ + int bc; + + bc = le16_to_cpu(*(__le16 *)((char *)hdr + + sizeof(struct smb_hdr) + hdr->WordCount * 2)); + + switch (hdr->Command) { + case SMB_COM_CLOSE: + case SMB_COM_FLUSH: + case SMB_COM_READ_ANDX: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_LOGOFF_ANDX: + case SMB_COM_NT_CANCEL: + case SMB_COM_PROCESS_EXIT: + case SMB_COM_FIND_CLOSE2: + if (bc != 0x0) + return -EINVAL; + break; + case SMB_COM_LOCKING_ANDX: + case SMB_COM_TRANSACTION: + case SMB_COM_TRANSACTION2: + case SMB_COM_ECHO: + case SMB_COM_SESSION_SETUP_ANDX: + if (bc < 0x0) + return -EINVAL; + break; + case SMB_COM_WRITE_ANDX: + if (bc < 0x1) + return -EINVAL; + break; + case SMB_COM_CREATE_DIRECTORY: + case SMB_COM_DELETE_DIRECTORY: + case SMB_COM_DELETE: + case SMB_COM_RENAME: + case SMB_COM_QUERY_INFORMATION: + case SMB_COM_SETATTR: + case SMB_COM_OPEN_ANDX: + case SMB_COM_NEGOTIATE: + case SMB_COM_CHECK_DIRECTORY: + if (bc < 0x2) + return -EINVAL; + break; + case SMB_COM_TREE_CONNECT_ANDX: + case SMB_COM_WRITE: + if (bc < 0x3) + return -EINVAL; + break; + case SMB_COM_NT_RENAME: + if (bc < 0x4) + return -EINVAL; + break; + case SMB_COM_NT_CREATE_ANDX: + if (hdr->Flags2 & SMBFLG2_UNICODE) { + if (bc < 3) + return -EINVAL; + } else if (bc < 2) + return -EINVAL; + break; + } + + return bc; +} + +static unsigned int smb1_calc_size(struct smb_hdr *hdr) +{ + int len = sizeof(struct smb_hdr) - 4 + 2; + int bc, struct_size = hdr->WordCount * 2; + + len += struct_size; + bc = smb1_get_byte_count(hdr); + if (bc < 0) + return bc; + ksmbd_debug(SMB, "SMB2 byte count %d, struct size : %d\n", bc, + struct_size); + len += bc; + + ksmbd_debug(SMB, "SMB1 len %d\n", len); + return len; +} + +static int smb1_get_data_len(struct smb_hdr *hdr) +{ + int data_len = 0; + + /* data offset check */ + switch (hdr->Command) { + case SMB_COM_WRITE_ANDX: + { + struct smb_com_write_req *req = (struct smb_com_write_req *)hdr; + + data_len = le16_to_cpu(req->DataLengthLow); + data_len |= (le16_to_cpu(req->DataLengthHigh) << 16); + data_len += le16_to_cpu(req->DataOffset); + break; + } + case SMB_COM_TRANSACTION: + { + struct smb_com_trans_req *req = (struct smb_com_trans_req *)hdr; + + data_len = le16_to_cpu(req->DataOffset) + + le16_to_cpu(req->DataCount); + break; + } + case SMB_COM_TRANSACTION2: + { + struct smb_com_trans2_req *req = + (struct smb_com_trans2_req *)hdr; + + data_len = le16_to_cpu(req->DataOffset) + + le16_to_cpu(req->DataCount); + break; + } + } + + return data_len; +} + +int ksmbd_smb1_check_message(struct ksmbd_work *work) +{ + struct smb_hdr *hdr = (struct smb_hdr *)work->request_buf; + char *buf = work->request_buf; + int command = hdr->Command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(buf); + int wc, data_len; + + if (check_smb1_hdr(hdr)) + return 1; + + wc = smb1_req_struct_size(hdr); + if (wc == -EOPNOTSUPP) { + ksmbd_debug(SMB, "Not support cmd %x\n", command); + return 1; + } else if (hdr->WordCount != wc) { + pr_err("Invalid word count, %d not %d. cmd %x\n", + hdr->WordCount, wc, command); + return 1; + } + + data_len = smb1_get_data_len(hdr); + if (len < data_len) { + pr_err("Invalid data area length %u not %u. cmd : %x\n", + len, data_len, command); + return 1; + } + + clc_len = smb1_calc_size(hdr); + if (len != clc_len) { + /* + * smbclient may return wrong byte count in smb header. + * But allow it to avoid write failure with smbclient. + */ + if (command == SMB_COM_WRITE_ANDX) + return 0; + + if (len > clc_len) { + ksmbd_debug(SMB, + "cli req too long, len %d not %d. cmd:%x\n", + len, clc_len, command); + return 0; + } + + pr_err("cli req too short, len %d not %d. cmd:%x\n", + len, clc_len, command); + + return 1; + } + + return 0; +} + +int smb_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); +} diff -Naur --no-dereference a/fs/ksmbd/smb1ops.c b/fs/ksmbd/smb1ops.c --- a/fs/ksmbd/smb1ops.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb1ops.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smb1pdu.h" + +static struct smb_version_values smb1_server_values = { + .version_string = SMB1_VERSION_STRING, + .protocol_id = SMB10_PROT_ID, + .capabilities = SMB1_SERVER_CAPS, + .max_read_size = CIFS_DEFAULT_IOSIZE, + .max_write_size = CIFS_DEFAULT_IOSIZE, + .max_trans_size = CIFS_DEFAULT_IOSIZE, + .large_lock_type = LOCKING_ANDX_LARGE_FILES, + .exclusive_lock_type = 0, + .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, + .unlock_lock_type = 0, + .header_size = sizeof(struct smb_hdr), + .max_header_size = MAX_CIFS_HDR_SIZE, + .read_rsp_size = sizeof(struct smb_com_read_rsp), + .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), + .cap_unix = CAP_UNIX, + .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, + .cap_large_files = CAP_LARGE_FILES, + .signing_enabled = SECMODE_SIGN_ENABLED, + .signing_required = SECMODE_SIGN_REQUIRED, +}; + +static struct smb_version_ops smb1_server_ops = { + .get_cmd_val = get_smb_cmd_val, + .init_rsp_hdr = init_smb_rsp_hdr, + .set_rsp_status = set_smb_rsp_status, + .allocate_rsp_buf = smb_allocate_rsp_buf, + .check_user_session = smb_check_user_session, + .is_sign_req = smb1_is_sign_req, + .check_sign_req = smb1_check_sign_req, + .set_sign_rsp = smb1_set_sign_rsp, + .get_ksmbd_tcon = smb_get_ksmbd_tcon, +}; + +static struct smb_version_cmds smb1_server_cmds[256] = { + [SMB_COM_CREATE_DIRECTORY] = { .proc = smb_mkdir, }, + [SMB_COM_DELETE_DIRECTORY] = { .proc = smb_rmdir, }, + [SMB_COM_CLOSE] = { .proc = smb_close, }, + [SMB_COM_FLUSH] = { .proc = smb_flush, }, + [SMB_COM_DELETE] = { .proc = smb_unlink, }, + [SMB_COM_RENAME] = { .proc = smb_rename, }, + [SMB_COM_QUERY_INFORMATION] = { .proc = smb_query_info, }, + [SMB_COM_SETATTR] = { .proc = smb_setattr, }, + [SMB_COM_LOCKING_ANDX] = { .proc = smb_locking_andx, }, + [SMB_COM_TRANSACTION] = { .proc = smb_trans, }, + [SMB_COM_ECHO] = { .proc = smb_echo, }, + [SMB_COM_OPEN_ANDX] = { .proc = smb_open_andx, }, + [SMB_COM_READ_ANDX] = { .proc = smb_read_andx, }, + [SMB_COM_WRITE_ANDX] = { .proc = smb_write_andx, }, + [SMB_COM_TRANSACTION2] = { .proc = smb_trans2, }, + [SMB_COM_FIND_CLOSE2] = { .proc = smb_closedir, }, + [SMB_COM_TREE_DISCONNECT] = { .proc = smb_tree_disconnect, }, + [SMB_COM_NEGOTIATE] = { .proc = smb_negotiate_request, }, + [SMB_COM_SESSION_SETUP_ANDX] = { .proc = smb_session_setup_andx, }, + [SMB_COM_LOGOFF_ANDX] = { .proc = smb_session_disconnect, }, + [SMB_COM_TREE_CONNECT_ANDX] = { .proc = smb_tree_connect_andx, }, + [SMB_COM_NT_CREATE_ANDX] = { .proc = smb_nt_create_andx, }, + [SMB_COM_NT_CANCEL] = { .proc = smb_nt_cancel, }, + [SMB_COM_NT_RENAME] = { .proc = smb_nt_rename, }, + [SMB_COM_WRITE] = { .proc = smb_write, }, + [SMB_COM_CHECK_DIRECTORY] = { .proc = smb_checkdir, }, + [SMB_COM_PROCESS_EXIT] = { .proc = smb_process_exit, }, +}; + +/** + * init_smb1_server() - initialize a smb server connection with smb1 + * command dispatcher + * @conn: connection instance + */ +int init_smb1_server(struct ksmbd_conn *conn) +{ + if (!conn) + return -EINVAL; + + conn->vals = &smb1_server_values; + conn->ops = &smb1_server_ops; + conn->cmds = smb1_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); + return 0; +} diff -Naur --no-dereference a/fs/ksmbd/smb1pdu.c b/fs/ksmbd/smb1pdu.c --- a/fs/ksmbd/smb1pdu.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb1pdu.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,8505 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "transport_ipc.h" +#include "vfs.h" +#include "misc.h" + +#include "auth.h" +#include "asn1.h" +#include "server.h" +#include "smb_common.h" +#include "smb1pdu.h" +#include "smbstatus.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "ndr.h" +#include "smberr.h" + +static int smb1_oplock_enable = false; + +/* Default: allocation roundup size = 1048576 */ +static unsigned int alloc_roundup_size = 1048576; + +struct ksmbd_dirent { + unsigned long long ino; + unsigned long long offset; + unsigned int namelen; + unsigned int d_type; + char name[]; +}; + +/** + * smb_NTtimeToUnix() - convert NTFS time to unix style time format + * @ntutc: NTFS style time + * + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + * + * Return: timespec containing unix style time + */ +static struct timespec64 smb_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* BB what about the timezone? BB */ + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + /* this has been taken from cifs, ntfs code */ + u64 t; + + t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + ts.tv_nsec = do_div(t, 10000000) * 100; + ts.tv_sec = t; + return ts; +} + +/** + * get_smb_cmd_val() - get smb command value from smb header + * @work: smb work containing smb header + * + * Return: smb command value + */ +u16 get_smb_cmd_val(struct ksmbd_work *work) +{ + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + + return (u16)rcv_hdr->Command; +} + +/** + * is_smbreq_unicode() - check if the smb command is request is unicode or not + * @hdr: pointer to smb_hdr in the the request part + * + * Return: check flags and return true if request is unicode, else false + */ +static inline int is_smbreq_unicode(struct smb_hdr *hdr) +{ + return hdr->Flags2 & SMBFLG2_UNICODE ? 1 : 0; +} + +/** + * set_smb_rsp_status() - set error type in smb response header + * @work: smb work containing smb response header + * @err: error code to set in response + */ +void set_smb_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *) work->response_buf; + + rsp_hdr->Status.CifsError = err; +} + +/** + * init_smb_rsp_hdr() - initialize smb response header + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -EINVAL + */ +int init_smb_rsp_hdr(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr; + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + + rsp_hdr = (struct smb_hdr *) work->response_buf; + memset(rsp_hdr, 0, sizeof(struct smb_hdr) + 2); + + /* remove 4 byte direct TCP header, add 1 byte wc and 2 byte bcc */ + rsp_hdr->smb_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals) + 2); + memcpy(rsp_hdr->Protocol, rcv_hdr->Protocol, 4); + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. Other bits are obsolete. + */ + rsp_hdr->Flags = (SMBFLG_RESPONSE); + + /* + * Lets assume error code are NTLM. True for CIFS and windows 7 + */ + rsp_hdr->Flags2 = rcv_hdr->Flags2; + rsp_hdr->PidHigh = rcv_hdr->PidHigh; + rsp_hdr->Pid = rcv_hdr->Pid; + rsp_hdr->Mid = rcv_hdr->Mid; + rsp_hdr->WordCount = 0; + + /* We can do the above test because we have set maxVCN as 1 */ + rsp_hdr->Uid = rcv_hdr->Uid; + rsp_hdr->Tid = rcv_hdr->Tid; + return 0; +} + +/** + * smb_allocate_rsp_buf() - allocate response buffer for a command + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb_hdr *hdr = (struct smb_hdr *)work->request_buf; + unsigned char cmd = hdr->Command; + size_t large_sz = work->conn->vals->max_read_size + MAX_CIFS_HDR_SIZE; + size_t sz = MAX_CIFS_SMALL_BUFFER_SIZE; + + if (cmd == SMB_COM_TRANSACTION2) { + struct smb_com_trans2_qpi_req *req = work->request_buf; + u16 sub_cmd = le16_to_cpu(req->SubCommand); + u16 infolevel = le16_to_cpu(req->InformationLevel); + + if ((sub_cmd == TRANS2_FIND_FIRST) || + (sub_cmd == TRANS2_FIND_NEXT) || + (sub_cmd == TRANS2_QUERY_PATH_INFORMATION && + (infolevel == SMB_QUERY_FILE_UNIX_LINK || + infolevel == SMB_QUERY_POSIX_ACL || + infolevel == SMB_INFO_QUERY_ALL_EAS))) + sz = large_sz; + } + + if (cmd == SMB_COM_TRANSACTION) + sz = large_sz; + + if (cmd == SMB_COM_ECHO) { + int resp_size; + struct smb_com_echo_req *req = work->request_buf; + + /* + * size of struct smb_com_echo_rsp + Bytecount - Size of Data + * in struct smb_com_echo_rsp + */ + resp_size = sizeof(struct smb_com_echo_rsp) + + le16_to_cpu(req->ByteCount) - 1; + if (resp_size > MAX_CIFS_SMALL_BUFFER_SIZE) + sz = large_sz; + } + + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + work->response_sz = sz; + + if (!work->response_buf) { + pr_err("Failed to allocate %zu bytes buffer\n", sz); + return -ENOMEM; + } + + return 0; +} + +/** + * andx_request_buffer() - return pointer to matching andx command + * @work: buffer containing smb request + * @command: match next command with this command + * + * Return: pointer to matching command buffer on success, otherwise NULL + */ +static char *andx_request_buffer(char *buf, int command) +{ + struct andx_block *andx_ptr = (struct andx_block *)(buf + + sizeof(struct smb_hdr) - 1); + struct andx_block *next; + + while (andx_ptr->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + next = (struct andx_block *) + (buf + 4 + le16_to_cpu(andx_ptr->AndXOffset)); + if (andx_ptr->AndXCommand == command) + return (char *)next; + andx_ptr = next; + } + return NULL; +} + +/** + * andx_response_buffer() - return pointer to andx response buffer + * @buf: buffer containing smb request + * + * Return: pointer to andx command response on success, otherwise NULL + */ +static char *andx_response_buffer(char *buf) +{ + int pdu_length = get_rfc1002_len(buf); + + return buf + 4 + pdu_length; +} + +/** + * smb_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_check_user_session(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + + work->sess = NULL; + if (cmd == SMB_COM_NEGOTIATE || cmd == SMB_COM_SESSION_SETUP_ANDX || + cmd == SMB_COM_ECHO) + return 0; + + if (!ksmbd_conn_good(work)) + return -EINVAL; + + if (list_empty(&conn->sessions)) { + ksmbd_debug(SMB, "NO sessions registered\n"); + return 0; + } + + work->sess = ksmbd_session_lookup(conn, le16_to_cpu(req_hdr->Uid)); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %u\n", + le16_to_cpu(req_hdr->Uid)); + return -EINVAL; +} + +/** + * smb_get_ksmbd_tcon() - get tree connection information for a tree id + * @sess: session containing tree list + * @tid: match tree connection with tree id + * + * Return: matching tree connection on success, otherwise error + */ +int smb_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + u8 cmd = req_hdr->Command; + int tree_id; + + work->tcon = NULL; + if (cmd == SMB_COM_TREE_CONNECT_ANDX || + cmd == SMB_COM_NT_CANCEL || + cmd == SMB_COM_LOGOFF_ANDX) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (xa_empty(&work->sess->tree_conns)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -ENOENT; + } + + tree_id = le16_to_cpu(req_hdr->Tid); + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + pr_err("Invalid tid %d\n", tree_id); + return -EINVAL; + } + + return 1; +} + +/** + * smb_session_disconnect() - LOGOFF request handler + * @work: smb work containing log off request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_session_disconnect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + + /* Got a valid session, set connection state */ + WARN_ON(sess->conn != conn); + + /* setting CifsExiting here may race with start_tcp_sess */ + ksmbd_conn_set_need_reconnect(work); + + ksmbd_free_user(sess->user); + sess->user = NULL; + + ksmbd_conn_wait_idle(conn); + + ksmbd_tree_conn_session_logoff(sess); + ksmbd_session_destroy(sess); + work->sess = NULL; + + /* let start_tcp_sess free conn info now */ + ksmbd_conn_set_exiting(work); + return 0; +} + +/** + * smb_session_disconnect() - tree disconnect request handler + * @work: smb work containing tree disconnect request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_tree_disconnect(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_session *sess = work->sess; + + if (!tcon) { + pr_err("Invalid tid %d\n", req_hdr->Tid); + rsp_hdr->Status.CifsError = STATUS_NO_SUCH_USER; + return -EINVAL; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + return 0; +} + +static void set_service_type(struct ksmbd_conn *conn, + struct ksmbd_share_config *share, + struct smb_com_tconx_rsp_ext *rsp) +{ + int length; + char *buf = rsp->Service; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + length = strlen(SERVICE_IPC_SHARE); + memcpy(buf, SERVICE_IPC_SHARE, length); + rsp->ByteCount = cpu_to_le16(length + 1); + buf += length; + *buf = '\0'; + } else { + int uni_len = 0; + + length = strlen(SERVICE_DISK_SHARE); + memcpy(buf, SERVICE_DISK_SHARE, length); + buf[length] = '\0'; + length += 1; + uni_len = smbConvertToUTF16((__le16 *)(buf + length), + NATIVE_FILE_SYSTEM, + PATH_MAX, conn->local_nls, 0); + uni_len++; + uni_len *= 2; + length += uni_len; + rsp->ByteCount = cpu_to_le16(length); + } +} + +/** + * smb_tree_connect_andx() - tree connect request handler + * @work: smb work containing tree connect request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_tree_connect_andx(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct smb_com_tconx_req *req; + struct smb_com_tconx_rsp_ext *rsp; + int extra_byte = 0; + char *treename = NULL, *name = NULL, *dev_type = NULL; + struct ksmbd_share_config *share; + struct ksmbd_session *sess = work->sess; + int dev_flags = 0; + struct ksmbd_tree_conn_status status; + + /* Is this an ANDX command ? */ + if (req_hdr->Command != SMB_COM_TREE_CONNECT_ANDX) { + ksmbd_debug(SMB, "SMB_COM_TREE_CONNECT_ANDX is part of ANDX"); + req = (struct smb_com_tconx_req *) + andx_request_buffer(work->request_buf, + SMB_COM_TREE_CONNECT_ANDX); + rsp = (struct smb_com_tconx_rsp_ext *) + andx_response_buffer(work->response_buf); + extra_byte = 3; + if (!req) { + status.ret = -EINVAL; + goto out_err; + } + } else { + req = (struct smb_com_tconx_req *)(&req_hdr->WordCount); + rsp = (struct smb_com_tconx_rsp_ext *)(&rsp_hdr->WordCount); + } + + /* check if valid tree name is present in request or not */ + if (!req->PasswordLength) { + treename = smb_strndup_from_utf16(req->Password + 1, + 256, true, conn->local_nls); + dev_type = smb_strndup_from_utf16(req->Password + 1 + + ((strlen(treename) + 1) * 2), 256, false, + conn->local_nls); + } else { + treename = smb_strndup_from_utf16(req->Password + + le16_to_cpu(req->PasswordLength), 256, true, + conn->local_nls); + dev_type = smb_strndup_from_utf16(req->Password + + le16_to_cpu(req->PasswordLength) + + ((strlen(treename) + 1) * 2), + 256, false, conn->local_nls); + } + + if (IS_ERR(treename) || IS_ERR(dev_type)) { + pr_err("Unable to strdup() treename or devtype uid %d\n", + rsp_hdr->Uid); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err; + } + name = ksmbd_extract_sharename(treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err; + } + + ksmbd_debug(SMB, "tree connect request for tree %s, dev_type : %s\n", + name, dev_type); + + if (!strcmp(dev_type, "A:")) + dev_flags = 1; + else if (!strncmp(dev_type, "LPT", 3)) + dev_flags = 2; + else if (!strcmp(dev_type, "IPC")) + dev_flags = 3; + else if (!strcmp(dev_type, "COMM")) + dev_flags = 4; + else if (!strcmp(dev_type, "?????")) + dev_flags = 5; + + if (!strncmp("IPC$", name, 4)) { + if (dev_flags < 3) { + status.ret = -ENODEV; + goto out_err; + } + } else if (!dev_flags || (dev_flags > 1 && dev_flags < 5)) { + status.ret = -ENODEV; + goto out_err; + } + + status = ksmbd_tree_conn_connect(sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp_hdr->Tid = cpu_to_le16(status.tree_conn->id); + else + goto out_err; + + status.ret = 0; + share = status.tree_conn->share_conf; + rsp->WordCount = 7; + rsp->OptionalSupport = 0; + + rsp->OptionalSupport = cpu_to_le16((SMB_SUPPORT_SEARCH_BITS | + SMB_CSC_NO_CACHING | SMB_UNIQUE_FILE_NAME)); + + rsp->MaximalShareAccessRights = cpu_to_le32(FILE_READ_RIGHTS | + FILE_EXEC_RIGHTS); + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) + rsp->MaximalShareAccessRights |= cpu_to_le32(FILE_WRITE_RIGHTS); + rsp->GuestMaximalShareAccessRights = 0; + + set_service_type(conn, share, rsp); + + /* For each extra andx response, we have to add 1 byte, + * for wc and 2 bytes for byte count + */ + inc_rfc1001_len(rsp_hdr, + 7 * 2 + le16_to_cpu(rsp->ByteCount) + extra_byte); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(rsp_hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + /* More processing required */ + status.ret = rsp->AndXCommand; + } else { + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + } + + kfree(treename); + kfree(dev_type); + kfree(name); + + return status.ret; + +out_err: + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(dev_type)) + kfree(dev_type); + if (!IS_ERR(name)) + kfree(name); + + rsp->WordCount = 7; + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(rsp_hdr)); + rsp->OptionalSupport = 0; + rsp->MaximalShareAccessRights = 0; + rsp->GuestMaximalShareAccessRights = 0; + rsp->ByteCount = 0; + ksmbd_debug(SMB, "error while tree connect\n"); + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp_hdr->Status.CifsError = STATUS_BAD_NETWORK_PATH; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -ENODEV: + rsp_hdr->Status.CifsError = STATUS_BAD_DEVICE_TYPE; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + rsp_hdr->Status.CifsError = STATUS_BAD_NETWORK_NAME; + break; + case -EINVAL: + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + break; + default: + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + } + + inc_rfc1001_len(rsp_hdr, (7 * 2 + le16_to_cpu(rsp->ByteCount) + + extra_byte)); + return -EINVAL; +} + +/** + * smb_get_name() - convert filename on smb packet to char string + * @src: source filename, mostly in unicode format + * @maxlen: maxlen of src string to be used for parsing + * @work: smb work containing smb header flag + * @converted: src string already converted to local characterset + * + * Return: pointer to filename string on success, otherwise error ptr + */ +static char * +smb_get_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct ksmbd_work *work, bool converted) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + bool is_unicode = is_smbreq_unicode(req_hdr); + char *name, *wild_card_pos; + + if (converted) + name = (char *)src; + else { + name = smb_strndup_from_utf16(src, maxlen, is_unicode, + work->conn->local_nls); + if (IS_ERR(name)) { + ksmbd_debug(SMB, "failed to get name %ld\n", + PTR_ERR(name)); + if (PTR_ERR(name) == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else + rsp_hdr->Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return name; + } + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + + /*Handling of dir path in FIND_FIRST2 having '*' at end of path*/ + wild_card_pos = strrchr(name, '*'); + + if (wild_card_pos != NULL) + *wild_card_pos = '\0'; + + + if (ksmbd_validate_filename(name) < 0) + return ERR_PTR(-ENOENT); + + if (ksmbd_share_veto_filename(share, name)) { + ksmbd_debug(SMB, + "file(%s) open is not allowed by setting as veto file\n", + name); + if (!converted) + kfree(name); + return ERR_PTR(-ENOENT); + } + + ksmbd_debug(SMB, "file name = %s\n", name); + + return name; +} + +/** + * smb_get_dir_name() - convert directory name on smb packet to char string + * @src: source dir name, mostly in unicode format + * @maxlen: maxlen of src string to be used for parsing + * @work: smb work containing smb header flag + * @srch_ptr: update search pointer in dir for searching dir entries + * + * Return: pointer to dir name string on success, otherwise error ptr + */ +static char *smb_get_dir_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct ksmbd_work *work, char **srch_ptr) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + bool is_unicode = is_smbreq_unicode(req_hdr); + char *name, *pattern_pos, *pattern = NULL; + int pattern_len, rc; + + name = smb_strndup_from_utf16(src, maxlen, is_unicode, + work->conn->local_nls); + if (IS_ERR(name)) { + pr_err("failed to allocate memory\n"); + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + return name; + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + + pattern_pos = strrchr(name, '/'); + + if (!pattern_pos) + pattern_pos = name; + else + pattern_pos += 1; + + pattern_len = strlen(pattern_pos); + if (pattern_len == 0) { + rc = -EINVAL; + goto err_name; + } + ksmbd_debug(SMB, "pattern searched = %s pattern_len = %d\n", + pattern_pos, pattern_len); + pattern = kmalloc(pattern_len + 1, GFP_KERNEL); + if (!pattern) { + rc = -ENOMEM; + goto err_name; + } + memcpy(pattern, pattern_pos, pattern_len); + *(pattern + pattern_len) = '\0'; + *pattern_pos = '\0'; + *srch_ptr = pattern; + + if (ksmbd_validate_filename(name) < 0) { + rc = -ENOENT; + goto err_pattern; + } + + if (ksmbd_share_veto_filename(share, name)) { + ksmbd_debug(SMB, + "file(%s) open is not allowed by setting as veto file\n", + name); + rc = -ENOENT; + goto err_pattern; + } + + ksmbd_debug(SMB, "dir name = %s\n", name); + return name; + +err_pattern: + kfree(pattern); +err_name: + kfree(name); + + if (rc == -EINVAL) + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + else if (rc == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else if (rc == -ENOENT) + rsp_hdr->Status.CifsError = STATUS_OBJECT_NAME_INVALID; + + return ERR_PTR(rc); +} + +/** + * smb_rename() - rename request handler + * @work: smb work containing rename request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_rename(struct ksmbd_work *work) +{ + struct smb_com_rename_req *req = work->request_buf; + struct smb_com_rename_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + bool is_unicode = is_smbreq_unicode(&req->hdr); + char *oldname, *newname; + int oldname_len; + struct path path; + bool file_present = true; + int rc = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + oldname = smb_get_name(share, req->OldFileName, PATH_MAX, work, false); + if (IS_ERR(oldname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(oldname); + } + + if (is_unicode) + oldname_len = smb1_utf16_name_length((__le16 *)req->OldFileName, + PATH_MAX); + else { + oldname_len = strlen(oldname); + oldname_len++; + } + + newname = smb_get_name(share, &req->OldFileName[oldname_len + 2], + PATH_MAX, work, false); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + rc = PTR_ERR(newname); + newname = NULL; + goto out; + } + + rc = ksmbd_vfs_kern_path(work, newname, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) + file_present = false; + else + path_put(&path); + + if (file_present && strncmp(oldname, newname, strlen(oldname))) { + rc = -EEXIST; + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + ksmbd_debug(SMB, "cannot rename already existing file\n"); + goto out; + } + + ksmbd_debug(SMB, "rename %s -> %s\n", oldname, newname); + rc = ksmbd_vfs_rename_slowpath(work, oldname, newname); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + goto out; + } + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; +out: + kfree(oldname); + kfree(newname); + return rc; +} + +/** + * smb_handle_negotiate() - negotiate request handler + * @work: smb work containing negotiate request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_negotiate_rsp *neg_rsp = work->response_buf; + __u64 time; + int rc = 0; + + WARN_ON(ksmbd_conn_good(work)); + + if (conn->dialect == BAD_PROT_ID) { + neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE; + rc = -EINVAL; + goto err_out; + } + + conn->connection_type = 0; + + /* wct 17 for NTLM */ + neg_rsp->hdr.WordCount = 17; + neg_rsp->DialectIndex = cpu_to_le16(conn->dialect); + + neg_rsp->SecurityMode = SMB1_SERVER_SECU; + if (server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + conn->sign = true; + neg_rsp->SecurityMode |= SECMODE_SIGN_ENABLED; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + neg_rsp->SecurityMode |= SECMODE_SIGN_REQUIRED; + } + neg_rsp->MaxMpxCount = cpu_to_le16(SMB1_MAX_MPX_COUNT); + neg_rsp->MaxNumberVcs = cpu_to_le16(SMB1_MAX_VCS); + neg_rsp->MaxBufferSize = cpu_to_le32(conn->vals->max_read_size); + neg_rsp->MaxRawSize = cpu_to_le32(SMB1_MAX_RAW_SIZE); + neg_rsp->SessionKey = 0; + neg_rsp->Capabilities = cpu_to_le32(SMB1_SERVER_CAPS); + + time = ksmbd_systime(); + neg_rsp->SystemTimeLow = cpu_to_le32(time & 0x00000000FFFFFFFF); + neg_rsp->SystemTimeHigh = + cpu_to_le32((time & 0xFFFFFFFF00000000) >> 32); + neg_rsp->ServerTimeZone = 0; + + if (((struct smb_hdr *)work->request_buf)->Flags2 & SMBFLG2_EXT_SEC) + conn->use_spnego = true; + + ksmbd_debug(SMB, "spnego is %s\n", conn->use_spnego ? "on" : "off"); + + if (conn->use_spnego == false) { + neg_rsp->EncryptionKeyLength = CIFS_CRYPTO_KEY_SIZE; + neg_rsp->ByteCount = cpu_to_le16(CIFS_CRYPTO_KEY_SIZE); + /* initialize random server challenge */ + get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); + memcpy((neg_rsp->u.EncryptionKey), conn->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + /* Adjust pdu length, 17 words and 8 bytes added */ + inc_rfc1001_len(neg_rsp, (17 * 2 + 8)); + } else { + neg_rsp->EncryptionKeyLength = 0; + neg_rsp->ByteCount = cpu_to_le16(SMB1_CLIENT_GUID_SIZE + + AUTH_GSS_LENGTH); + get_random_bytes(neg_rsp->u.extended_response.GUID, + SMB1_CLIENT_GUID_SIZE); + ksmbd_copy_gss_neg_header( + neg_rsp->u.extended_response.SecurityBlob); + inc_rfc1001_len(neg_rsp, (17 * 2 + 16 + AUTH_GSS_LENGTH)); + } + + /* Null terminated domain name in unicode */ + + ksmbd_conn_set_need_negotiate(work); + /* Domain name and PC name are ignored by clients, so no need to send. + * We can try sending them later + */ +err_out: + return rc; +} + +static int build_sess_rsp_noextsec(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + struct smb_com_session_setup_req_no_secext *req, + struct smb_com_session_setup_old_resp *rsp) +{ + int offset, err = 0; + char *name; + + /* Build response. We don't use extended security (yet), so wct is 3 */ + rsp->hdr.WordCount = 3; + rsp->Action = 0; + /* The names should be unicode */ + rsp->ByteCount = 0; + /* adjust pdu length. data added 6 bytes */ + inc_rfc1001_len(&rsp->hdr, 6); + + /* check if valid user name is present in request or not */ + offset = le16_to_cpu(req->CaseInsensitivePasswordLength) + + le16_to_cpu(req->CaseSensitivePasswordLength); + + /* 1 byte for padding */ + name = smb_strndup_from_utf16((req->CaseInsensitivePassword + offset + + 1), 256, true, conn->local_nls); + if (IS_ERR(name)) { + pr_err("cannot allocate memory\n"); + err = PTR_ERR(name); + goto out_err; + } + + WARN_ON(sess->user); + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + sess->user = ksmbd_login_user(name); + kfree(name); + if (!sess->user) { + pr_err("user not present in database\n"); + err = -EINVAL; + goto out_err; + } + + if (user_guest(sess->user)) { + rsp->Action = cpu_to_le16(GUEST_LOGIN); + goto no_password_check; + } + + if (le16_to_cpu(req->CaseSensitivePasswordLength) == + CIFS_AUTH_RESP_SIZE) { + err = ksmbd_auth_ntlm(sess, req->CaseInsensitivePassword + + le16_to_cpu(req->CaseInsensitivePasswordLength), + conn->ntlmssp.cryptkey); + if (err) { + pr_err("ntlm authentication failed for user %s\n", + user_name(sess->user)); + goto out_err; + } + } else { + char *ntdomain; + + offset = le16_to_cpu(req->CaseInsensitivePasswordLength) + + le16_to_cpu(req->CaseSensitivePasswordLength) + + ((strlen(user_name(sess->user)) + 1) * 2); + + ntdomain = smb_strndup_from_utf16( + req->CaseInsensitivePassword + + offset + 1, 256, true, conn->local_nls); + if (IS_ERR(ntdomain)) { + pr_err("cannot allocate memory\n"); + err = PTR_ERR(ntdomain); + goto out_err; + } + + err = ksmbd_auth_ntlmv2(sess, + (struct ntlmv2_resp *) ((char *) + req->CaseInsensitivePassword + + le16_to_cpu(req->CaseInsensitivePasswordLength)), + le16_to_cpu(req->CaseSensitivePasswordLength) - + CIFS_ENCPWD_SIZE, ntdomain, + conn->ntlmssp.cryptkey); + kfree(ntdomain); + if (err) { + pr_err("authentication failed for user %s\n", + user_name(sess->user)); + goto out_err; + } + } + +no_password_check: + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + +out_err: + return err; +} + +static int build_sess_rsp_extsec(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + struct smb_com_session_setup_req *req, + struct smb_com_session_setup_resp *rsp) +{ + struct negotiate_message *negblob; + char *neg_blob; + int err = 0, neg_blob_len; + unsigned char *spnego_blob; + u16 spnego_blob_len; + int sz; + + rsp->hdr.WordCount = 4; + rsp->Action = 0; + + /* The names should be unicode */ + rsp->ByteCount = 0; + /* adjust pdu length. data added 6 bytes */ + inc_rfc1001_len(&rsp->hdr, 8); + + negblob = (struct negotiate_message *)req->SecurityBlob; + sz = le16_to_cpu(req->SecurityBlobLength); + + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->use_spnego = false; + } + } + + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + + if (negblob->MessageType == NtLmNegotiate) { + struct challenge_message *chgblob; + + ksmbd_debug(SMB, "negotiate phase\n"); + err = ksmbd_decode_ntlmssp_neg_blob(negblob, + le16_to_cpu(req->SecurityBlobLength), + conn); + if (err) + goto out_err; + + chgblob = (struct challenge_message *)rsp->SecurityBlob; + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (conn->use_spnego) { + int sz; + + sz = sizeof(struct negotiate_message) + + (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + neg_blob = kmalloc(sz, GFP_KERNEL); + if (!neg_blob) { + err = -ENOMEM; + goto out_err; + } + chgblob = (struct challenge_message *)neg_blob; + neg_blob_len = ksmbd_build_ntlmssp_challenge_blob( + chgblob, + conn); + if (neg_blob_len < 0) { + kfree(neg_blob); + err = -ENOMEM; + goto out_err; + } + + if (build_spnego_ntlmssp_neg_blob(&spnego_blob, + &spnego_blob_len, + neg_blob, neg_blob_len)) { + kfree(neg_blob); + err = -ENOMEM; + goto out_err; + } + + memcpy((char *)rsp->SecurityBlob, spnego_blob, + spnego_blob_len); + rsp->SecurityBlobLength = + cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + kfree(neg_blob); + } else { + neg_blob_len = ksmbd_build_ntlmssp_challenge_blob( + chgblob, + conn); + if (neg_blob_len < 0) { + err = -ENOMEM; + goto out_err; + } + + rsp->SecurityBlobLength = cpu_to_le16(neg_blob_len); + } + + rsp->hdr.Status.CifsError = STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an adjustment + * for 0 size blob. + */ + inc_rfc1001_len(rsp, le16_to_cpu(rsp->SecurityBlobLength)); + rsp->ByteCount = rsp->SecurityBlobLength; + } else if (negblob->MessageType == NtLmAuthenticate) { + struct authenticate_message *authblob; + char *username; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego && conn->mechToken) + authblob = + (struct authenticate_message *)conn->mechToken; + else + authblob = (struct authenticate_message *) + req->SecurityBlob; + + username = smb_strndup_from_utf16((const char *)authblob + + le32_to_cpu(authblob->UserName.BufferOffset), + le16_to_cpu(authblob->UserName.Length), true, + conn->local_nls); + + if (IS_ERR(username)) { + pr_err("cannot allocate memory\n"); + err = PTR_ERR(username); + goto out_err; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", + username); + sess->user = ksmbd_login_user(username); + kfree(username); + + if (!sess->user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + err = -EINVAL; + goto out_err; + } + + if (user_guest(sess->user)) { + rsp->Action = cpu_to_le16(GUEST_LOGIN); + goto no_password_check; + } + + err = ksmbd_decode_ntlmssp_auth_blob(authblob, + le16_to_cpu(req->SecurityBlobLength), + conn, sess); + if (err) { + ksmbd_debug(SMB, "authentication failed\n"); + err = -EINVAL; + goto out_err; + } + +no_password_check: + if (conn->use_spnego) { + if (build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, 0)) { + err = -ENOMEM; + goto out_err; + } + + memcpy((char *)rsp->SecurityBlob, spnego_blob, + spnego_blob_len); + rsp->SecurityBlobLength = + cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(rsp, spnego_blob_len); + rsp->ByteCount = rsp->SecurityBlobLength; + } + } else { + pr_err("Invalid phase\n"); + err = -EINVAL; + } + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + +out_err: + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + return err; +} + +/** + * smb_session_setup_andx() - session setup request handler + * @work: smb work containing session setup request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_session_setup_andx(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = NULL; + int rc = 0, cap; + unsigned short uid; + + union smb_com_session_setup_andx *pSMB = work->request_buf; + union smb_com_session_setup_andx *rsp = work->response_buf; + + if (pSMB->req.hdr.WordCount == 12) + cap = le32_to_cpu(pSMB->req.Capabilities); + else if (pSMB->req.hdr.WordCount == 13) + cap = le32_to_cpu(pSMB->req_no_secext.Capabilities); + else { + pr_err("malformed packet\n"); + work->send_no_response = 1; + return 0; + } + + uid = le16_to_cpu(pSMB->req.hdr.Uid); + if (uid != 0) { + sess = ksmbd_session_lookup(conn, uid); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + ksmbd_debug(SMB, "Reuse session ID: %llu, Uid: %u\n", + sess->id, uid); + } else { + sess = ksmbd_smb1_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + + ksmbd_session_register(conn, sess); + rsp->resp.hdr.Uid = cpu_to_le16(sess->id); + ksmbd_debug(SMB, "New session ID: %llu, Uid: %u\n", sess->id, + uid); + } + + if (cap & CAP_EXTENDED_SECURITY) { + ksmbd_debug(SMB, "build response with extend_security\n"); + rc = build_sess_rsp_extsec(conn, sess, &pSMB->req, &rsp->resp); + + } else { + ksmbd_debug(SMB, "build response without extend_security\n"); + rc = build_sess_rsp_noextsec(conn, sess, &pSMB->req_no_secext, + &rsp->old_resp); + } + if (rc < 0) + goto out_err; + + work->sess = sess; + ksmbd_conn_set_good(work); + return 0; + +out_err: + rsp->resp.hdr.Status.CifsError = STATUS_LOGON_FAILURE; + rsp->resp.hdr.WordCount = 0; + rsp->resp.ByteCount = 0; + if (rc < 0 && sess) { + ksmbd_session_destroy(sess); + work->sess = NULL; + } + return rc; +} + +/** + * file_create_dispostion_flags() - convert disposition flags to + * file open flags + * @dispostion: file disposition contained in open request + * @file_present: file already present or not + * + * Return: file open flags after conversion from disposition + */ +static int file_create_dispostion_flags(int dispostion, bool file_present) +{ + int disp_flags = 0; + + switch (dispostion) { + /* + * If the file already exists, it SHOULD be superseded (overwritten). + * If it does not already exist, then it SHOULD be created. + */ + case FILE_SUPERSEDE: + if (file_present) + disp_flags |= O_TRUNC; + else + disp_flags |= O_CREAT; + break; + /* + * If the file already exists, it SHOULD be opened rather than created. + * If the file does not already exist, the operation MUST fail. + */ + case FILE_OPEN: + if (!file_present) + return -ENOENT; + break; + /* + * If the file already exists, the operation MUST fail. + * If the file does not already exist, it SHOULD be created. + */ + case FILE_CREATE: + if (file_present) + return -EEXIST; + disp_flags |= O_CREAT; + break; + /* + * If the file already exists, it SHOULD be opened. If the file + * does not already exist, then it SHOULD be created. + */ + case FILE_OPEN_IF: + if (!file_present) + disp_flags |= O_CREAT; + break; + /* + * If the file already exists, it SHOULD be opened and truncated. + * If the file does not already exist, the operation MUST fail. + */ + case FILE_OVERWRITE: + if (!file_present) + return -ENOENT; + disp_flags |= O_TRUNC; + break; + /* + * If the file already exists, it SHOULD be opened and truncated. + * If the file does not already exist, it SHOULD be created. + */ + case FILE_OVERWRITE_IF: + if (file_present) + disp_flags |= O_TRUNC; + else + disp_flags |= O_CREAT; + break; + default: + return -EINVAL; + } + + return disp_flags; +} + +static inline int ksmbd_openflags_to_mayflags(int open_flags) +{ + int mask = open_flags & O_ACCMODE; + + if (mask == O_WRONLY) + return MAY_OPEN | MAY_WRITE; + else if (mask == O_RDWR) + return MAY_OPEN | MAY_READ | MAY_WRITE; + else + return MAY_OPEN | MAY_READ; +} + +/** + * convert_generic_access_flags() - convert access flags to + * file open flags + * @access_flag: file access flags contained in open request + * @open_flag: file open flags are updated as per access flags + * @may_flags: file may flags are updated with @open_flags + * @attrib: attribute flag indicating posix symantics or not + * + * Return: access flags + */ +static int +convert_generic_access_flags(int access_flag, int *open_flags, + int *may_flags, int attrib) +{ + int aflags = access_flag; + int oflags = *open_flags; + + if (aflags & GENERIC_READ) { + aflags &= ~GENERIC_READ; + aflags |= GENERIC_READ_FLAGS; + } + + if (aflags & GENERIC_WRITE) { + aflags &= ~GENERIC_WRITE; + aflags |= GENERIC_WRITE_FLAGS; + } + + if (aflags & GENERIC_EXECUTE) { + aflags &= ~GENERIC_EXECUTE; + aflags |= GENERIC_EXECUTE_FLAGS; + } + + if (aflags & GENERIC_ALL) { + aflags &= ~GENERIC_ALL; + aflags |= GENERIC_ALL_FLAGS; + } + + if (oflags & O_TRUNC) + aflags |= FILE_WRITE_DATA; + + if (aflags & (FILE_WRITE_DATA | FILE_APPEND_DATA)) { + if (aflags & (FILE_READ_ATTRIBUTES | FILE_READ_DATA | + FILE_READ_EA | FILE_EXECUTE)) { + *open_flags |= O_RDWR; + + } else { + *open_flags |= O_WRONLY; + } + } else { + *open_flags |= O_RDONLY; + } + + if ((attrib & ATTR_POSIX_SEMANTICS) && (aflags & FILE_APPEND_DATA)) + *open_flags |= O_APPEND; + + *may_flags = ksmbd_openflags_to_mayflags(*open_flags); + + return aflags; +} + +/** + * smb_get_dos_attr() - convert unix style stat info to dos attr + * @stat: stat to be converted to dos attr + * + * Return: dos style attribute + */ +static __u32 smb_get_dos_attr(struct kstat *stat) +{ + __u32 attr = 0; + + /* check whether file has attributes ATTR_READONLY, ATTR_HIDDEN, + * ATTR_SYSTEM, ATTR_VOLUME, ATTR_DIRECTORY, ATTR_ARCHIVE, + * ATTR_DEVICE, ATTR_NORMAL, ATTR_TEMPORARY, ATTR_SPARSE, + * ATTR_REPARSE, ATTR_COMPRESSED, ATTR_OFFLINE + */ + + if (stat->mode & S_ISVTX) /* hidden */ + attr |= (ATTR_HIDDEN | ATTR_SYSTEM); + + if (!(stat->mode & 0222)) /* read-only */ + attr |= ATTR_READONLY; + + if (S_ISDIR(stat->mode)) + attr |= ATTR_DIRECTORY; + + if (stat->size > (stat->blksize * stat->blocks)) + attr |= ATTR_SPARSE; + + if (!attr) + attr |= ATTR_NORMAL; + + return attr; +} + +static int +lock_oplock_release(struct ksmbd_file *fp, int type, int oplock_level) +{ + struct oplock_info *opinfo; + int ret; + + ksmbd_debug(SMB, "got oplock brk for level OplockLevel = %d\n", + oplock_level); + + opinfo = fp->f_opinfo; + if (opinfo->op_state == OPLOCK_STATE_NONE) { + pr_err("unexpected oplock state 0x%x\n", opinfo->op_state); + return -EINVAL; + } + + if (oplock_level == OPLOCK_EXCLUSIVE || oplock_level == OPLOCK_BATCH) { + if (opinfo_write_to_none(opinfo) < 0) { + opinfo->op_state = OPLOCK_STATE_NONE; + return -EINVAL; + } + } else if (((opinfo->level == OPLOCK_EXCLUSIVE) || + (opinfo->level == OPLOCK_BATCH)) && + (oplock_level == OPLOCK_READ)) { + ret = opinfo_write_to_read(opinfo); + if (ret) { + opinfo->op_state = OPLOCK_STATE_NONE; + return -EINVAL; + } + } else if ((opinfo->level == OPLOCK_READ) && + (oplock_level == OPLOCK_NONE)) { + ret = opinfo_read_to_none(opinfo); + if (ret) { + opinfo->op_state = OPLOCK_STATE_NONE; + return -EINVAL; + } + } + + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible(&opinfo->oplock_q); + + return 0; +} + +static struct ksmbd_lock *smb_lock_init(struct file_lock *flock, + unsigned int cmd, int mode, unsigned long long offset, + unsigned long long length, struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = offset; + lock->end = offset + length; + lock->flags = mode; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->llist); + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +/** + * smb_locking_andx() - received oplock break response from client + * @work: smb work containing oplock break command + * + * Return: 0 on success, otherwise error + */ +int smb_locking_andx(struct ksmbd_work *work) +{ + struct smb_com_lock_req *req = work->request_buf; + struct smb_com_lock_rsp *rsp = work->response_buf; + struct ksmbd_file *fp; + int err = 0; + struct locking_andx_range32 *lock_ele32 = NULL, *unlock_ele32 = NULL; + struct locking_andx_range64 *lock_ele64 = NULL, *unlock_ele64 = NULL; + struct file *filp = NULL; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + int i, lock_count, unlock_count; + unsigned long long offset, length; + struct file_lock *flock = NULL; + unsigned int cmd = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int locked, timeout; + const unsigned long long loff_max = ~0; + struct ksmbd_conn *conn; + + timeout = le32_to_cpu(req->Timeout); + ksmbd_debug(SMB, "got oplock brk for fid %d lock type = 0x%x, timeout : %d\n", + req->Fid, req->LockType, timeout); + + /* find fid */ + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("cannot obtain fid for %d\n", req->Fid); + return -EINVAL; + } + + if (req->LockType & LOCKING_ANDX_OPLOCK_RELEASE) { + pr_err("lock type is oplock release\n"); + err = lock_oplock_release(fp, req->LockType, req->OplockLevel); + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->NumberOfLocks); + unlock_count = le16_to_cpu(req->NumberOfUnlocks); + + ksmbd_debug(SMB, "lock count is %d, unlock_count : %d\n", + lock_count, unlock_count); + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) + lock_ele64 = (struct locking_andx_range64 *)req->Locks; + else + lock_ele32 = (struct locking_andx_range32 *)req->Locks; + + if (req->LockType & LOCKING_ANDX_CHANGE_LOCKTYPE) { + pr_err("lock type: LOCKING_ANDX_CHANGE_LOCKTYPE\n"); + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = cpu_to_le16(ERRnoatomiclocks); + rsp->hdr.Flags2 &= ~SMBFLG2_ERR_STATUS; + goto out; + } + + if (req->LockType & LOCKING_ANDX_CANCEL_LOCK) + pr_err("lock type: LOCKING_ANDX_CANCEL_LOCK\n"); + + for (i = 0; i < lock_count; i++) { + flock = smb_flock_init(filp); + if (!flock) + goto out; + + if (req->LockType & LOCKING_ANDX_SHARED_LOCK) { + pr_err("received shared request\n"); + if (!(filp->f_mode & FMODE_READ)) { + rsp->hdr.Status.CifsError = + STATUS_ACCESS_DENIED; + goto out; + } + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + } else { + pr_err("received exclusive request\n"); + if (!(filp->f_mode & FMODE_WRITE)) { + rsp->hdr.Status.CifsError = + STATUS_ACCESS_DENIED; + goto out; + } + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + } + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) { + offset = (unsigned long long)le32_to_cpu( + lock_ele64[i].OffsetLow); + length = (unsigned long long)le32_to_cpu( + lock_ele64[i].LengthLow); + offset |= (unsigned long long)le32_to_cpu( + lock_ele64[i].OffsetHigh) << 32; + length |= (unsigned long long)le32_to_cpu( + lock_ele64[i].LengthHigh) << 32; + } else { + offset = (unsigned long long)le32_to_cpu( + lock_ele32[i].Offset); + length = (unsigned long long)le32_to_cpu( + lock_ele32[i].Length); + } + + if (offset > loff_max) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status.CifsError = + STATUS_INVALID_LOCK_RANGE; + goto out; + } + + if (offset > 0 && length > (loff_max - offset) + 1) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status.CifsError = + STATUS_INVALID_LOCK_RANGE; + goto out; + } + + ksmbd_debug(SMB, "locking offset : %llx, length : %llu\n", + offset, length); + + if (offset > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = offset; + if (offset + length > OFFSET_MAX) + flock->fl_end = OFFSET_MAX; + else + flock->fl_end = offset + length; + + smb_lock = smb_lock_init(flock, cmd, req->LockType, offset, + length, &lock_list); + if (!smb_lock) + goto out; + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + int same_zero_lock = 0; + + list_del(&smb_lock->llist); + /* check locks in connections */ + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->zero_len && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end) { + same_zero_lock = 1; + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + goto out_check_cl; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + pr_err("previous lock conflict with zero byte lock range\n"); + err = -EPERM; + } else if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + pr_err("current lock conflict with zero byte lock range\n"); + err = -EPERM; + } else if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + pr_err("Not allow lock operation on exclusive lock range\n"); + err = -EPERM; + } + + if (err) { + /* Clean error cache */ + if ((smb_lock->zero_len && + fp->cflock_cnt > 1) || + (timeout && (fp->llock_fstart == + smb_lock->start))) { + ksmbd_debug(SMB, "clean error cache\n"); + fp->cflock_cnt = 0; + } + + if (timeout > 0 || + (fp->cflock_cnt > 0 && + fp->llock_fstart == smb_lock->start) || + ((smb_lock->start >> 63) == 0 && + smb_lock->start >= 0xEF000000)) { + if (timeout) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + ksmbd_debug(SMB, "waiting error response for timeout : %d\n", + timeout); + msleep(timeout); + } + rsp->hdr.Status.CifsError = + STATUS_FILE_LOCK_CONFLICT; + } else + rsp->hdr.Status.CifsError = + STATUS_LOCK_NOT_GRANTED; + fp->cflock_cnt++; + fp->llock_fstart = smb_lock->start; + + if (timeout <= 0) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + } + goto out; + } + } + spin_unlock(&conn->llist_lock); + } + read_unlock(&conn_list_lock); + +out_check_cl: + if (same_zero_lock) + continue; + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; +retry: + err = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); + if (err == FILE_LOCK_DEFERRED) { + pr_err("would have to wait for getting lock\n"); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); +wait: + err = ksmbd_vfs_posix_lock_wait_timeout(flock, + msecs_to_jiffies(10)); + if (err) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + goto retry; + } else + goto wait; + } else if (!err) { +skip: + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + pr_err("successful in taking lock\n"); + } else if (err < 0) { + rsp->hdr.Status.CifsError = STATUS_LOCK_NOT_GRANTED; + goto out; + } + } + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) + unlock_ele64 = (struct locking_andx_range64 *)(req->Locks + + (sizeof(struct locking_andx_range64) * + lock_count)); + else + unlock_ele32 = (struct locking_andx_range32 *)(req->Locks + + (sizeof(struct locking_andx_range32) * + lock_count)); + + for (i = 0; i < unlock_count; i++) { + flock = smb_flock_init(filp); + if (!flock) + goto out; + + flock->fl_type = F_UNLCK; + cmd = 0; + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) { + offset = (unsigned long long)le32_to_cpu( + unlock_ele64[i].OffsetLow); + length = (unsigned long long)le32_to_cpu( + unlock_ele64[i].LengthLow); + offset |= (unsigned long long)le32_to_cpu( + unlock_ele64[i].OffsetHigh) << 32; + length |= (unsigned long long)le32_to_cpu( + unlock_ele64[i].LengthHigh) << 32; + } else { + offset = (unsigned long long)le32_to_cpu( + unlock_ele32[i].Offset); + length = (unsigned long long)le32_to_cpu( + unlock_ele32[i].Length); + } + + ksmbd_debug(SMB, "unlock offset : %llx, length : %llu\n", + offset, length); + + if (offset > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = offset; + if (offset + length > OFFSET_MAX) + flock->fl_end = OFFSET_MAX; + else + flock->fl_end = offset + length; + + locked = 0; + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry(cmp_lock, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(flock->fl_file)) + continue; + + if ((cmp_lock->start == offset && + cmp_lock->end == offset + length)) { + locked = 1; + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + goto out_check_cl_unlck; + } + } + spin_unlock(&conn->llist_lock); + } + read_unlock(&conn_list_lock); + +out_check_cl_unlck: + if (!locked) { + locks_free_lock(flock); + rsp->hdr.Status.CifsError = STATUS_RANGE_NOT_LOCKED; + goto out; + } + + err = vfs_lock_file(filp, cmd, flock, NULL); + if (!err) { + ksmbd_debug(SMB, "File unlocked\n"); + spin_lock(&conn->llist_lock); + if (!list_empty(&cmp_lock->flist)) + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + fp->cflock_cnt = 0; + } else if (err == -ENOENT) { + rsp->hdr.Status.CifsError = STATUS_RANGE_NOT_LOCKED; + goto out; + } + locks_free_lock(flock); + } + + rsp->hdr.WordCount = 2; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + ksmbd_fd_put(work, fp); + return err; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + err = vfs_lock_file(filp, 0, rlock, NULL); + if (err) + pr_err("rollback unlock fail : %d\n", err); + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } + + ksmbd_fd_put(work, fp); + pr_err("failed in taking lock\n"); + return err; +} + +/** + * smb_trans() - trans2 command dispatcher + * @work: smb work containing trans2 command + * + * Return: 0 on success, otherwise error + */ +int smb_trans(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_com_trans_req *req = work->request_buf; + struct smb_com_trans_rsp *rsp = work->response_buf; + struct smb_com_trans_pipe_req *pipe_req = work->request_buf; + struct ksmbd_rpc_command *rpc_resp; + __u16 subcommand; + char *name, *pipe; + char *pipedata; + int setup_bytes_count = 0; + int pipe_name_offset = 0; + int str_len_uni; + int ret = 0, nbytes = 0; + int param_len = 0; + int id, buf_len; + int padding; + + buf_len = le16_to_cpu(req->MaxDataCount); + buf_len = min((int)(KSMBD_IPC_MAX_PAYLOAD - + sizeof(struct smb_com_trans_rsp)), buf_len); + + if (req->SetupCount) + setup_bytes_count = 2 * req->SetupCount; + + subcommand = le16_to_cpu(req->SubCommand); + name = smb_strndup_from_utf16(req->Data + setup_bytes_count, 256, 1, + conn->local_nls); + + if (IS_ERR(name)) { + pr_err("failed to allocate memory\n"); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return PTR_ERR(name); + } + + ksmbd_debug(SMB, "Obtained string name = %s setupcount = %d\n", + name, setup_bytes_count); + + pipe_name_offset = strlen("\\PIPE"); + if (strncmp("\\PIPE", name, pipe_name_offset) != 0) { + ksmbd_debug(SMB, "Not Pipe request\n"); + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + kfree(name); + return 0; + } + + if (name[pipe_name_offset] == '\\') + pipe_name_offset++; + + pipe = name + pipe_name_offset; + + if (*pipe != '\0' && strncmp(pipe, "LANMAN", sizeof("LANMAN")) != 0) { + ksmbd_debug(SMB, "Pipe %s not supported request\n", pipe); + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + kfree(name); + return 0; + } + + /* Incoming pipe name unicode len */ + str_len_uni = 2 * (strlen(name) + 1); + + ksmbd_debug(SMB, "Pipe name unicode len = %d\n", str_len_uni); + + /* Some clients like Windows may have additional padding. */ + padding = le16_to_cpu(req->ParameterOffset) - + offsetof(struct smb_com_trans_req, Data) + - str_len_uni; + pipedata = req->Data + str_len_uni + setup_bytes_count + padding; + + if (!strncmp(pipe, "LANMAN", sizeof("LANMAN"))) { + rpc_resp = ksmbd_rpc_rap(work->sess, pipedata, + le16_to_cpu(req->TotalParameterCount)); + + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status.CifsError = + STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status.CifsError = + STATUS_INVALID_PARAMETER; + kvfree(rpc_resp); + goto out; + } + + nbytes = rpc_resp->payload_sz; + memcpy((char *)rsp + sizeof(struct smb_com_trans_rsp), + rpc_resp->payload, nbytes); + + kvfree(rpc_resp); + ret = 0; + goto resp_out; + } else { + ret = -EINVAL; + goto out; + } + } + + id = pipe_req->fid; + switch (subcommand) { + case TRANSACT_DCERPCCMD: + + ksmbd_debug(SMB, "GOT TRANSACT_DCERPCCMD\n"); + ret = -EINVAL; + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, pipedata, + le16_to_cpu(req->DataCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status.CifsError = + STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status.CifsError = + STATUS_INVALID_PARAMETER; + kvfree(rpc_resp); + goto out; + } + + nbytes = rpc_resp->payload_sz; + memcpy((char *)rsp + sizeof(struct smb_com_trans_rsp), + rpc_resp->payload, nbytes); + kvfree(rpc_resp); + ret = 0; + } + break; + + default: + ksmbd_debug(SMB, "SMB TRANS subcommand not supported %u\n", + subcommand); + ret = -EOPNOTSUPP; + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + goto out; + } + +resp_out: + + rsp->hdr.WordCount = 10; + rsp->TotalParameterCount = cpu_to_le16(param_len); + rsp->TotalDataCount = cpu_to_le16(nbytes); + rsp->Reserved = 0; + rsp->ParameterCount = cpu_to_le16(param_len); + rsp->ParameterOffset = cpu_to_le16(56); + rsp->ParameterDisplacement = 0; + rsp->DataCount = cpu_to_le16(nbytes); + rsp->DataOffset = cpu_to_le16(56 + param_len); + rsp->DataDisplacement = 0; + rsp->SetupCount = 0; + rsp->Reserved1 = 0; + /* Adding 1 for Pad */ + rsp->ByteCount = cpu_to_le16(nbytes + 1 + param_len); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + +out: + kfree(name); + return ret; +} + +/** + * create_andx_pipe() - create ipc pipe request handler + * @work: smb work containing create command + * + * Return: 0 on success, otherwise error + */ +static int create_andx_pipe(struct ksmbd_work *work) +{ + struct smb_com_open_req *req = work->request_buf; + struct smb_com_open_ext_rsp *rsp = work->response_buf; + char *name; + int rc = 0; + __u16 fid; + + /* one byte pad before unicode file name start */ + if (is_smbreq_unicode(&req->hdr)) + name = smb_strndup_from_utf16(req->fileName + 1, 256, 1, + work->conn->local_nls); + else + name = smb_strndup_from_utf16(req->fileName, 256, 1, + work->conn->local_nls); + + if (IS_ERR(name)) { + rc = -ENOMEM; + goto out; + } + + rc = ksmbd_session_rpc_open(work->sess, name); + if (rc < 0) + goto out; + fid = rc; + + rsp->hdr.WordCount = 42; + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + rsp->AndXReserved = 0; + rsp->OplockLevel = 0; + rsp->Fid = fid; + rsp->CreateAction = cpu_to_le32(1); + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->FileAttributes = cpu_to_le32(ATTR_NORMAL); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndOfFile = 0; + rsp->FileType = cpu_to_le16(2); + rsp->DeviceState = cpu_to_le16(0x05ff); + rsp->DirectoryFlag = 0; + rsp->fid = 0; + rsp->MaxAccess = cpu_to_le32(FILE_GENERIC_ALL); + rsp->GuestAccess = cpu_to_le32(FILE_GENERIC_READ); + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, 100); + +out: + switch (rc) { + case 0: + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + break; + case -EINVAL: + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + default: + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + break; + } + + kfree(name); + return rc; +} + +/** + * smb_nt_create_andx() - file open request handler + * @work: smb work containing nt open command + * + * Return: 0 on success, otherwise error + */ +int smb_nt_create_andx(struct ksmbd_work *work) +{ + struct smb_com_open_req *req = work->request_buf; + struct smb_com_open_rsp *rsp = work->response_buf; + struct smb_com_open_ext_rsp *ext_rsp = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + int oplock_flags, file_info, open_flags, may_flags, access_flags; + char *name; + char *conv_name; + bool file_present = true, extended_reply; + __u64 alloc_size = 0, time; + umode_t mode = 0; + int err; + int create_directory = 0; + char *src; + char *root = NULL; + bool is_unicode; + bool is_relative_root = false; + struct ksmbd_file *fp = NULL; + int oplock_rsp = OPLOCK_NONE; + int share_ret; + + rsp->hdr.Status.CifsError = STATUS_UNSUCCESSFUL; + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "create pipe on IPC\n"); + return create_andx_pipe(work); + } + + if (req->CreateOptions & FILE_OPEN_BY_FILE_ID_LE) { + ksmbd_debug(SMB, "file open with FID is not supported\n"); + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + return -EINVAL; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + if (req->DesiredAccess && + !(le32_to_cpu(req->DesiredAccess) & DELETE)) { + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EPERM; + } + + if (le32_to_cpu(req->FileAttributes) & ATTR_READONLY) { + rsp->hdr.Status.CifsError = STATUS_CANNOT_DELETE; + return -EPERM; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + ksmbd_debug(SMB, "GOT Create Directory via CREATE ANDX\n"); + create_directory = 1; + } + + /* + * Filename is relative to this root directory FID, instead of + * tree connect point. Find root dir name from this FID and + * prepend root dir name in filename. + */ + if (req->RootDirectoryFid) { + ksmbd_debug(SMB, "path lookup relative to RootDirectoryFid\n"); + + is_relative_root = true; + fp = ksmbd_lookup_fd_fast(work, req->RootDirectoryFid); + if (fp) + root = (char *)fp->filp->f_path.dentry->d_name.name; + else { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + memset(&rsp->hdr.WordCount, 0, 3); + return -EINVAL; + } + ksmbd_fd_put(work, fp); + } + + /* here allocated +2 (UNI '\0') length for both ASCII & UNI + * to avoid unnecessary if/else check + */ + src = kzalloc(le16_to_cpu(req->NameLength) + 2, GFP_KERNEL); + if (!src) { + rsp->hdr.Status.CifsError = + STATUS_NO_MEMORY; + + return -ENOMEM; + } + + if (is_smbreq_unicode(&req->hdr)) { + memcpy(src, req->fileName + 1, le16_to_cpu(req->NameLength)); + is_unicode = true; + } else { + memcpy(src, req->fileName, le16_to_cpu(req->NameLength)); + is_unicode = false; + } + + name = smb_strndup_from_utf16(src, PATH_MAX, is_unicode, + conn->local_nls); + kfree(src); + + if (IS_ERR(name)) { + if (PTR_ERR(name) == -ENOMEM) { + pr_err("failed to allocate memory\n"); + rsp->hdr.Status.CifsError = + STATUS_NO_MEMORY; + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + + return PTR_ERR(name); + } + + if (is_relative_root) { + int org_len = strnlen(name, PATH_MAX); + int add_len = strnlen(root, PATH_MAX); + char *full_name; + + /* +3 for: '\''\' & '\0' */ + full_name = kzalloc(org_len + add_len + 3, GFP_KERNEL); + if (!full_name) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + snprintf(full_name, add_len + 3, "\\%s\\", root); + strncat(full_name, name, org_len); + kfree(name); + name = full_name; + } + + root = strrchr(name, '\\'); + if (root) { + root++; + if ((root[0] == '*' || root[0] == '/') && (root[1] == '\0')) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + kfree(name); + return -EINVAL; + } + } + + conv_name = smb_get_name(share, name, PATH_MAX, work, true); + if (IS_ERR(conv_name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(conv_name); + } + + if (ksmbd_override_fsids(work)) { + err = -ENOMEM; + goto out1; + } + + err = ksmbd_vfs_kern_path(work, conv_name, LOOKUP_NO_SYMLINKS, &path, + (req->hdr.Flags & SMBFLG_CASELESS) && + !create_directory); + if (err) { + if (err == -EACCES || err == -EXDEV) + goto out; + file_present = false; + ksmbd_debug(SMB, "can not get linux path for %s, err = %d\n", + conv_name, err); + } else { + if (d_is_symlink(path.dentry)) { + err = -EACCES; + goto free_path; + } + + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("can not stat %s, err = %d\n", + conv_name, err); + goto free_path; + } + } + + if (file_present && (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) && + S_ISDIR(stat.mode)) { + ksmbd_debug(SMB, "Can't open dir %s, request is to open file\n", + conv_name); + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = + cpu_to_le16(ERRfilexists); + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + + memset(&rsp->hdr.WordCount, 0, 3); + kfree(conv_name); + + goto free_path; + } + + if (file_present && create_directory && !S_ISDIR(stat.mode)) { + ksmbd_debug(SMB, "Can't open file %s, request is to open dir\n", + conv_name); + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + ntstatus_to_dos(STATUS_NOT_A_DIRECTORY, + &rsp->hdr.Status.DosError.ErrorClass, + &rsp->hdr.Status.DosError.Error); + } else + rsp->hdr.Status.CifsError = + STATUS_NOT_A_DIRECTORY; + + memset(&rsp->hdr.WordCount, 0, 3); + kfree(conv_name); + + goto free_path; + } + + oplock_flags = le32_to_cpu(req->OpenFlags) & + (REQ_OPLOCK | REQ_BATCHOPLOCK); + extended_reply = le32_to_cpu(req->OpenFlags) & REQ_EXTENDED_INFO; + open_flags = file_create_dispostion_flags( + le32_to_cpu(req->CreateDisposition), file_present); + + if (open_flags < 0) { + ksmbd_debug(SMB, "create_dispostion returned %d\n", open_flags); + if (file_present) { + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = + cpu_to_le16(ERRfilexists); + } else if (open_flags == -EINVAL) + rsp->hdr.Status.CifsError = + STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + memset(&rsp->hdr.WordCount, 0, 3); + kfree(conv_name); + goto free_path; + } else { + err = -ENOENT; + goto out; + } + } else { + if (file_present) { + if (S_ISFIFO(stat.mode)) + open_flags |= O_NONBLOCK; + } + + if (req->CreateOptions & FILE_WRITE_THROUGH_LE) + open_flags |= O_SYNC; + } + + access_flags = convert_generic_access_flags( + le32_to_cpu(req->DesiredAccess), + &open_flags, &may_flags, + le32_to_cpu(req->FileAttributes)); + + mode |= 0777; + if (le32_to_cpu(req->FileAttributes) & ATTR_READONLY) + mode &= ~0222; + + /* TODO: + * - check req->ShareAccess for sharing file among different process + * - check req->FileAttributes for special/readonly file attrib + * - check req->SecurityFlags for client security context tracking + * - check req->ImpersonationLevel + */ + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + err = -EACCES; + goto out; + } + } + + ksmbd_debug(SMB, "filename : %s, open_flags = 0x%x\n", conv_name, + open_flags); + if (!file_present && (open_flags & O_CREAT)) { + + if (!create_directory) { + mode |= S_IFREG; + err = ksmbd_vfs_create(work, conv_name, mode); + if (err) + goto out; + } else { + err = ksmbd_vfs_mkdir(work, conv_name, mode); + if (err) { + pr_err("Can't create directory %s", + conv_name); + goto out; + } + } + + err = ksmbd_vfs_kern_path(work, conv_name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + may_flags); +#else + err = inode_permission(d_inode(path.dentry), + may_flags); +#endif + if (err) + goto free_path; + } + + err = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (err == KSMBD_INODE_STATUS_PENDING_DELETE) { + err = -EBUSY; + goto free_path; + } + + err = 0; + /* open file and get FID */ + fp = ksmbd_vfs_dentry_open(work, + &path, + open_flags, + req->CreateOptions, + file_present); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + fp = NULL; + goto free_path; + } + fp->filename = conv_name; + fp->daccess = req->DesiredAccess; + fp->saccess = req->ShareAccess; + fp->pid = le16_to_cpu(req->hdr.Pid); + + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (smb1_oplock_enable && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS) && + !S_ISDIR(file_inode(fp->filp)->i_mode) && oplock_flags) { + /* Client cannot request levelII oplock directly */ + err = smb_grant_oplock(work, oplock_flags, fp->volatile_id, + fp, le16_to_cpu(req->hdr.Tid), NULL, share_ret); + if (err) + goto free_path; + } else { + if (ksmbd_inode_pending_delete(fp)) { + err = -EBUSY; + goto free_path; + } + + if (share_ret < 0) { + err = -EPERM; + goto free_path; + } + } + + oplock_rsp = fp->f_opinfo != NULL ? fp->f_opinfo->level : 0; + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = F_OPENED; + else + file_info = F_OVERWRITTEN; + } else + file_info = F_CREATED; + + if (le32_to_cpu(req->DesiredAccess) & (DELETE | GENERIC_ALL)) + fp->is_nt_open = 1; + if ((le32_to_cpu(req->DesiredAccess) & DELETE) && + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) + ksmbd_fd_set_delete_on_close(fp, file_info); + + /* open success, send back response */ + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("cannot get stat information\n"); + goto free_path; + } + + alloc_size = le64_to_cpu(req->AllocationSize); + if (alloc_size && (file_info == F_CREATED || + file_info == F_OVERWRITTEN)) { + if (alloc_size > stat.size) { + err = ksmbd_vfs_truncate(work, fp, alloc_size); + if (err) { + pr_err("failed to expand file, err = %d\n", + err); + goto free_path; + } + } + } + + /* prepare response buffer */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + rsp->OplockLevel = oplock_rsp; + rsp->Fid = fp->volatile_id; + + if ((le32_to_cpu(req->CreateDisposition) == FILE_SUPERSEDE) && + (file_info == F_OVERWRITTEN)) + rsp->CreateAction = cpu_to_le32(F_SUPERSEDED); + else + rsp->CreateAction = cpu_to_le32(file_info); + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (file_present) { + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + err = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err > 0) + fp->create_time = da.create_time; + err = 0; + } + } else { + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.attr = smb_get_dos_attr(&stat); + da.create_time = fp->create_time; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in xattr\n"); + err = 0; + } + } + + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + + rsp->FileAttributes = cpu_to_le32(smb_get_dos_attr(&stat)); + rsp->AllocationSize = cpu_to_le64(stat.blocks << 9); + rsp->EndOfFile = cpu_to_le64(stat.size); + /* TODO: is it normal file, named pipe, printer, modem etc*/ + rsp->FileType = 0; + /* status of named pipe*/ + rsp->DeviceState = 0; + rsp->DirectoryFlag = S_ISDIR(stat.mode) ? 1 : 0; + if (extended_reply) { + struct inode *inode; + + rsp->hdr.WordCount = 50; + memset(&ext_rsp->VolId, 0, 16); + if (fp) { + inode = file_inode(fp->filp); + ext_rsp->fid = inode->i_ino; + if (S_ISDIR(inode->i_mode) || + (fp->filp->f_mode & FMODE_WRITE)) + ext_rsp->MaxAccess = FILE_GENERIC_ALL_LE; + else + ext_rsp->MaxAccess = FILE_GENERIC_READ_LE | + FILE_EXECUTE_LE; + } else { + ext_rsp->MaxAccess = FILE_GENERIC_ALL_LE; + ext_rsp->fid = 0; + } + + ext_rsp->ByteCount = 0; + + } else { + rsp->hdr.WordCount = 34; + rsp->ByteCount = 0; + } + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2 + 0)); + +free_path: + path_put(&path); +out: + ksmbd_revert_fsids(work); +out1: + switch (err) { + case 0: + break; + case -ENOSPC: + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + break; + case -EMFILE: + rsp->hdr.Status.CifsError = + STATUS_TOO_MANY_OPENED_FILES; + break; + case -EINVAL: + rsp->hdr.Status.CifsError = STATUS_NO_SUCH_USER; + break; + case -EACCES: + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -EPERM: + rsp->hdr.Status.CifsError = STATUS_SHARING_VIOLATION; + break; + case -ENOENT: + rsp->hdr.Status.CifsError = STATUS_OBJECT_NAME_NOT_FOUND; + break; + case -EBUSY: + rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + break; + default: + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + } + + if (err) { + if (fp) + ksmbd_close_fd(work, fp->volatile_id); + else + kfree(conv_name); + } + + if (!rsp->hdr.WordCount) + return err; + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return err; + +} + +/** + * smb_close_pipe() - ipc pipe close request handler + * @work: smb work containing close command + * + * Return: 0 on success, otherwise error + */ +static int smb_close_pipe(struct ksmbd_work *work) +{ + struct smb_com_close_req *req = work->request_buf; + + ksmbd_session_rpc_close(work->sess, req->FileID); + return 0; +} + +/** + * smb_close() - ipc pipe close request handler + * @work: smb work containing close command + * + * Return: 0 on success, otherwise error + */ +int smb_close(struct ksmbd_work *work) +{ + struct smb_com_close_req *req = work->request_buf; + struct smb_com_close_rsp *rsp = work->response_buf; + int err = 0; + + ksmbd_debug(SMB, "SMB_COM_CLOSE called for fid %u\n", req->FileID); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + err = smb_close_pipe(work); + if (err < 0) + goto out; + goto IPC_out; + } + + /* + * TODO: linux cifs client does not send LastWriteTime, + * need to check if windows client use this field + */ + if (le32_to_cpu(req->LastWriteTime) > 0 && + le32_to_cpu(req->LastWriteTime) < 0xFFFFFFFF) + pr_info("need to set last modified time before close\n"); + + err = ksmbd_close_fd(work, req->FileID); + +IPC_out: + /* file close success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + +out: + if (err) + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_read_andx_pipe() - read from ipc pipe request handler + * @work: smb work containing read command + * + * Return: 0 on success, otherwise error + */ +static int smb_read_andx_pipe(struct ksmbd_work *work) +{ + struct smb_com_read_req *req = work->request_buf; + struct smb_com_read_rsp *rsp = work->response_buf; + struct ksmbd_rpc_command *rpc_resp; + char *data_buf; + int ret = 0, nbytes = 0; + unsigned int count; + unsigned int rsp_buflen = MAX_CIFS_SMALL_BUFFER_SIZE - + sizeof(struct smb_com_read_rsp); + + rsp_buflen = min((unsigned int)(MAX_CIFS_SMALL_BUFFER_SIZE - + sizeof(struct smb_com_read_rsp)), rsp_buflen); + + count = min_t(unsigned int, le16_to_cpu(req->MaxCount), rsp_buflen); + data_buf = (char *) (&rsp->ByteCount) + sizeof(rsp->ByteCount); + + rpc_resp = ksmbd_rpc_read(work->sess, req->Fid); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK || + !rpc_resp->payload_sz) { + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + kvfree(rpc_resp); + return -EINVAL; + } + + nbytes = rpc_resp->payload_sz; + memcpy(data_buf, rpc_resp->payload, rpc_resp->payload_sz); + kvfree(rpc_resp); + } else { + ret = -EINVAL; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 12; + rsp->Remaining = 0; + rsp->DataCompactionMode = 0; + rsp->DataCompactionMode = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le16(nbytes & 0xFFFF); + rsp->DataOffset = cpu_to_le16(sizeof(struct smb_com_read_rsp) - + sizeof(rsp->hdr.smb_buf_length)); + rsp->DataLengthHigh = cpu_to_le16(nbytes >> 16); + rsp->Reserved2 = 0; + + rsp->ByteCount = cpu_to_le16(nbytes); + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2 + nbytes)); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return ret; +} + +/** + * smb_read_andx() - read request handler + * @work: smb work containing read command + * + * Return: 0 on success, otherwise error + */ +int smb_read_andx(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_com_read_req *req = work->request_buf; + struct smb_com_read_rsp *rsp = work->response_buf; + struct ksmbd_file *fp; + loff_t pos; + size_t count; + ssize_t nbytes; + int err = 0; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) + return smb_read_andx_pipe(work); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %d\n", + req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + pos = le32_to_cpu(req->OffsetLow); + if (req->hdr.WordCount == 12) + pos |= ((loff_t)le32_to_cpu(req->OffsetHigh) << 32); + + count = le16_to_cpu(req->MaxCount); + /* + * It probably seems to be set to 0 or 0xFFFF if MaxCountHigh is + * not supported. If it is 0xFFFF, it is set to a too large value + * and a read fail occurs. If it is 0xFFFF, limit it to not set + * the value. + * + * [MS-SMB] 3.2.4.4.1: + * If the CAP_LARGE_READX bit is set in + * Client.Connection.ServerCapabilities, then the client is allowed to + * issue a read of a size larger than Client.Connection.MaxBufferSize + * using an SMB_COM_READ_ANDX request. + */ + if (conn->vals->capabilities & CAP_LARGE_READ_X && + le32_to_cpu(req->MaxCountHigh) < 0xFFFF) + count |= le32_to_cpu(req->MaxCountHigh) << 16; + else if (count > CIFS_DEFAULT_IOSIZE) { + ksmbd_debug(SMB, "read size(%zu) exceeds max size(%u)\n", + count, CIFS_DEFAULT_IOSIZE); + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + CIFS_DEFAULT_IOSIZE); + count = CIFS_DEFAULT_IOSIZE; + } + + ksmbd_debug(SMB, "filename %pd, offset %lld, count %zu\n", + fp->filp->f_path.dentry, pos, count); + + work->aux_payload_buf = kvmalloc(count, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, count, &pos); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + /* read success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 12; + rsp->Remaining = 0; + rsp->DataCompactionMode = 0; + rsp->DataCompactionMode = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le16(nbytes & 0xFFFF); + rsp->DataOffset = cpu_to_le16(sizeof(struct smb_com_read_rsp) - + sizeof(rsp->hdr.smb_buf_length)); + rsp->DataLengthHigh = cpu_to_le16(nbytes >> 16); + rsp->Reserved2 = 0; + + rsp->ByteCount = cpu_to_le16(nbytes); + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(&rsp->hdr, nbytes); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + ksmbd_fd_put(work, fp); + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + +out: + ksmbd_fd_put(work, fp); + if (err) + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_write() - write request handler + * @work: smb work containing write command + * + * Return: 0 on success, otherwise error + */ +int smb_write(struct ksmbd_work *work) +{ + struct smb_com_write_req_32bit *req = work->request_buf; + struct smb_com_write_rsp_32bit *rsp = work->response_buf; + struct ksmbd_file *fp = NULL; + loff_t pos; + size_t count; + char *data_buf; + ssize_t nbytes = 0; + int err = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + pos = le32_to_cpu(req->Offset); + count = le16_to_cpu(req->Length); + data_buf = req->Data; + + ksmbd_debug(SMB, "filename %pd, offset %lld, count %zu\n", + fp->filp->f_path.dentry, pos, count); + if (!count) { + err = ksmbd_vfs_truncate(work, fp, pos); + nbytes = 0; + } else + err = ksmbd_vfs_write(work, fp, data_buf, + count, &pos, 0, &nbytes); + + rsp->hdr.WordCount = 1; + rsp->Written = cpu_to_le16(nbytes & 0xFFFF); + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + ksmbd_fd_put(work, fp); + if (!err) { + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + return 0; + } + + if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_write_andx_pipe() - write on pipe request handler + * @work: smb work containing write command + * + * Return: 0 on success, otherwise error + */ +static int smb_write_andx_pipe(struct ksmbd_work *work) +{ + struct smb_com_write_req *req = work->request_buf; + struct smb_com_write_rsp *rsp = work->response_buf; + struct ksmbd_rpc_command *rpc_resp; + int ret = 0; + size_t count = 0; + + count = le16_to_cpu(req->DataLengthLow); + if (work->conn->vals->capabilities & CAP_LARGE_WRITE_X) + count |= (le16_to_cpu(req->DataLengthHigh) << 16); + + rpc_resp = ksmbd_rpc_write(work->sess, req->Fid, req->Data, count); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + kvfree(rpc_resp); + return -EINVAL; + } + count = rpc_resp->payload_sz; + kvfree(rpc_resp); + } else { + ret = -EINVAL; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 6; + rsp->Count = cpu_to_le16(count & 0xFFFF); + rsp->Remaining = 0; + rsp->CountHigh = cpu_to_le16(count >> 16); + rsp->Reserved = 0; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return ret; +} + +/** + * smb_write_andx() - andx write request handler + * @work: smb work containing write command + * + * Return: 0 on success, otherwise error + */ +int smb_write_andx(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_com_write_req *req = work->request_buf; + struct smb_com_write_rsp *rsp = work->response_buf; + struct ksmbd_file *fp; + bool writethrough = false; + loff_t pos; + size_t count; + ssize_t nbytes = 0; + char *data_buf; + int err = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "Write ANDX called for IPC$"); + return smb_write_andx_pipe(work); + } + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + pos = le32_to_cpu(req->OffsetLow); + if (req->hdr.WordCount == 14) + pos |= ((loff_t)le32_to_cpu(req->OffsetHigh) << 32); + + writethrough = (le16_to_cpu(req->WriteMode) == 1); + + /* + * [MS-SMB] 3.3.5.8: + * If CAP_LARGE_WRITEX is set in Server.Connection.ClientCapabilities, + * then it is possible that the count of bytes to be written is larger + * than the server's MaxBufferSize + */ + count = le16_to_cpu(req->DataLengthLow); + if (conn->vals->capabilities & CAP_LARGE_WRITE_X) + count |= (le16_to_cpu(req->DataLengthHigh) << 16); + else if (count > CIFS_DEFAULT_IOSIZE) { + ksmbd_debug(SMB, "write size(%zu) exceeds max size(%u)\n", + count, CIFS_DEFAULT_IOSIZE); + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + CIFS_DEFAULT_IOSIZE); + count = CIFS_DEFAULT_IOSIZE; + } + + if (le16_to_cpu(req->DataOffset) == + (offsetof(struct smb_com_write_req, Data) - 4)) { + data_buf = (char *)&req->Data[0]; + } else { + if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || + (le16_to_cpu(req->DataOffset) + + count > get_rfc1002_len(req))) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + } + + ksmbd_debug(SMB, "filname %pd, offset %lld, count %zu\n", + fp->filp->f_path.dentry, pos, count); + err = ksmbd_vfs_write(work, fp, data_buf, count, &pos, + writethrough, &nbytes); + if (err < 0) + goto out; + + /* write success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 6; + rsp->Count = cpu_to_le16(nbytes & 0xFFFF); + rsp->Remaining = 0; + rsp->CountHigh = cpu_to_le16(nbytes >> 16); + rsp->Reserved = 0; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + ksmbd_fd_put(work, fp); + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return 0; + +out: + ksmbd_fd_put(work, fp); + if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_echo() - echo(ping) request handler + * @work: smb work containing echo command + * + * Return: 0 on success, otherwise error + */ +int smb_echo(struct ksmbd_work *work) +{ + struct smb_com_echo_req *req = work->request_buf; + struct smb_com_echo_rsp *rsp = work->response_buf; + __u16 data_count; + int i; + + ksmbd_debug(SMB, "SMB_COM_ECHO called with echo count %u\n", + le16_to_cpu(req->EchoCount)); + + if (le16_to_cpu(req->EchoCount) > 1) + work->multiRsp = 1; + + data_count = le16_to_cpu(req->ByteCount); + /* send echo response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 1; + rsp->ByteCount = cpu_to_le16(data_count); + + memcpy(rsp->Data, req->Data, data_count); + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2) + data_count); + + /* Send req->EchoCount - 1 number of ECHO response now & + * if SMB CANCEL for Echo comes don't send response + */ + for (i = 1; i < le16_to_cpu(req->EchoCount) && + !work->send_no_response; i++) { + rsp->SequenceNumber = cpu_to_le16(i); + ksmbd_conn_write(work); + } + + /* Last echo response */ + rsp->SequenceNumber = cpu_to_le16(i); + work->multiRsp = 0; + + return 0; +} + +/** + * smb_flush() - file sync - flush request handler + * @work: smb work containing flush command + * + * Return: 0 on success, otherwise error + */ +int smb_flush(struct ksmbd_work *work) +{ + struct smb_com_flush_req *req = work->request_buf; + struct smb_com_flush_rsp *rsp = work->response_buf; + int err = 0; + + ksmbd_debug(SMB, "SMB_COM_FLUSH called for fid %u\n", req->FileID); + + if (req->FileID == 0xFFFF) { + err = ksmbd_file_table_flush(work); + if (err) + goto out; + } else { + err = ksmbd_vfs_fsync(work, req->FileID, KSMBD_NO_FID); + if (err) + goto out; + } + + /* file fsync success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + return err; + +out: + if (err) + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + + return err; +} + +/***************************************************************************** + * TRANS2 command implementation functions + *****************************************************************************/ + +/** + * get_filetype() - convert file mode to smb file type + * @mode: file mode to be convertd + * + * Return: converted file type + */ +static __u32 get_filetype(mode_t mode) +{ + if (S_ISREG(mode)) + return UNIX_FILE; + else if (S_ISDIR(mode)) + return UNIX_DIR; + else if (S_ISLNK(mode)) + return UNIX_SYMLINK; + else if (S_ISCHR(mode)) + return UNIX_CHARDEV; + else if (S_ISBLK(mode)) + return UNIX_BLOCKDEV; + else if (S_ISFIFO(mode)) + return UNIX_FIFO; + else if (S_ISSOCK(mode)) + return UNIX_SOCKET; + + return UNIX_UNKNOWN; +} + +/** + * init_unix_info() - convert file stat information to smb file info format + * @unix_info: smb file information format + * @stat: unix file/dir stat information + */ +static void init_unix_info(struct file_unix_basic_info *unix_info, + struct user_namespace *user_ns, struct kstat *stat) +{ + u64 time; + + unix_info->EndOfFile = cpu_to_le64(stat->size); + unix_info->NumOfBytes = cpu_to_le64(512 * stat->blocks); + time = ksmbd_UnixTimeToNT(stat->ctime); + unix_info->LastStatusChange = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat->atime); + unix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat->mtime); + unix_info->LastModificationTime = cpu_to_le64(time); + unix_info->Uid = cpu_to_le64(from_kuid(user_ns, stat->uid)); + unix_info->Gid = cpu_to_le64(from_kgid(user_ns, stat->gid)); + unix_info->Type = cpu_to_le32(get_filetype(stat->mode)); + unix_info->DevMajor = cpu_to_le64(MAJOR(stat->rdev)); + unix_info->DevMinor = cpu_to_le64(MINOR(stat->rdev)); + unix_info->UniqueId = cpu_to_le64(stat->ino); + unix_info->Permissions = cpu_to_le64(stat->mode); + unix_info->Nlinks = cpu_to_le64(stat->nlink); +} + +/** + * unix_info_to_attr() - convert smb file info format to unix attr format + * @unix_info: smb file information format + * @attrs: unix file/dir stat information + * + * Return: 0 + */ +static int unix_info_to_attr(struct file_unix_basic_info *unix_info, + struct user_namespace *user_ns, + struct iattr *attrs) +{ + struct timespec64 ts; + + if (le64_to_cpu(unix_info->EndOfFile) != NO_CHANGE_64) { + attrs->ia_size = le64_to_cpu(unix_info->EndOfFile); + attrs->ia_valid |= ATTR_SIZE; + } + + if (le64_to_cpu(unix_info->LastStatusChange) != NO_CHANGE_64) { + ts = smb_NTtimeToUnix(unix_info->LastStatusChange); + attrs->ia_ctime = ts; + attrs->ia_valid |= ATTR_CTIME; + } + + if (le64_to_cpu(unix_info->LastAccessTime) != NO_CHANGE_64) { + ts = smb_NTtimeToUnix(unix_info->LastAccessTime); + attrs->ia_atime = ts; + attrs->ia_valid |= ATTR_ATIME; + } + + if (le64_to_cpu(unix_info->LastModificationTime) != NO_CHANGE_64) { + ts = smb_NTtimeToUnix(unix_info->LastModificationTime); + attrs->ia_mtime = ts; + attrs->ia_valid |= ATTR_MTIME; + } + + if (le64_to_cpu(unix_info->Uid) != NO_CHANGE_64) { + attrs->ia_uid = make_kuid(user_ns, + le64_to_cpu(unix_info->Uid)); + attrs->ia_valid |= ATTR_UID; + } + + if (le64_to_cpu(unix_info->Gid) != NO_CHANGE_64) { + attrs->ia_gid = make_kgid(user_ns, + le64_to_cpu(unix_info->Gid)); + attrs->ia_valid |= ATTR_GID; + } + + if (le64_to_cpu(unix_info->Permissions) != NO_CHANGE_64) { + attrs->ia_mode = le64_to_cpu(unix_info->Permissions); + attrs->ia_valid |= ATTR_MODE; + } + + switch (le32_to_cpu(unix_info->Type)) { + case UNIX_FILE: + attrs->ia_mode |= S_IFREG; + break; + case UNIX_DIR: + attrs->ia_mode |= S_IFDIR; + break; + case UNIX_SYMLINK: + attrs->ia_mode |= S_IFLNK; + break; + case UNIX_CHARDEV: + attrs->ia_mode |= S_IFCHR; + break; + case UNIX_BLOCKDEV: + attrs->ia_mode |= S_IFBLK; + break; + case UNIX_FIFO: + attrs->ia_mode |= S_IFIFO; + break; + case UNIX_SOCKET: + attrs->ia_mode |= S_IFSOCK; + break; + default: + pr_err("unknown file type 0x%x\n", + le32_to_cpu(unix_info->Type)); + } + + return 0; +} + +/** + * unix_to_dos_time() - convert unix time to dos format + * @ts: unix style time + * @time: store dos style time + * @date: store dos style date + */ +static void unix_to_dos_time(struct timespec64 ts, __le16 *time, __le16 *date) +{ + struct tm t; + __u16 val; + + time64_to_tm(ts.tv_sec, (-sys_tz.tz_minuteswest) * 60, &t); + val = (((unsigned int)(t.tm_mon + 1)) >> 3) | ((t.tm_year - 80) << 1); + val = ((val & 0xFF) << 8) | (t.tm_mday | + (((t.tm_mon + 1) & 0x7) << 5)); + *date = cpu_to_le16(val); + + val = ((((unsigned int)t.tm_min >> 3) & 0x7) | + (((unsigned int)t.tm_hour) << 3)); + val = ((val & 0xFF) << 8) | ((t.tm_sec/2) | ((t.tm_min & 0x7) << 5)); + *time = cpu_to_le16(val); +} + +/** + * cifs_convert_ace() - helper function for convert an Access Control Entry + * from cifs wire format to local POSIX xattr format + * @ace: local - unix style Access Control Entry format + * @cifs_ace: cifs wire Access Control Entry format + */ +static void cifs_convert_ace(struct posix_acl_xattr_entry *ace, + struct cifs_posix_ace *cifs_ace) +{ + /* u8 cifs fields do not need le conversion */ + ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); + ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); + ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); +} + +/** + * cifs_copy_posix_acl() - Convert ACL from CIFS POSIX wire format to local + * Linux POSIX ACL xattr + * @trgt: target buffer for storing in local ace format + * @src: source buffer in cifs ace format + * @buflen: target buffer length + * @acl_type: ace type + * @size_of_data_area: max buffer size to store ace xattr + * + * Return: size of convert ace xattr on success, otherwise error + */ +static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, + const int acl_type, const int size_of_data_area) +{ + int size = 0; + int i; + __u16 count; + struct cifs_posix_ace *pACE; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; + struct posix_acl_xattr_entry *ace; + struct posix_acl_xattr_header *local_acl = (void *)trgt; + + if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) + return -EOPNOTSUPP; + + if (acl_type & ACL_TYPE_ACCESS) { + count = le16_to_cpu(cifs_acl->access_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) { + ksmbd_debug(SMB, "bad CIFS POSIX ACL size %d vs. %d\n", + size_of_data_area, size); + return -EINVAL; + } + } else if (acl_type & ACL_TYPE_DEFAULT) { + count = le16_to_cpu(cifs_acl->default_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) + return -EINVAL; + } else { + /* illegal type */ + return -EINVAL; + } + + size = posix_acl_xattr_size(count); + if ((buflen != 0) && local_acl && size > buflen) + return -ERANGE; + + /* buffer big enough */ + ace = (void *)(local_acl + 1); + local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); + for (i = 0; i < count; i++) { + cifs_convert_ace(&ace[i], pACE); + pACE++; + } + + return size; +} + +/** + * convert_ace_to_cifs_ace() - helper function to convert ACL from local + * Linux POSIX ACL xattr to CIFS POSIX wire format to local + * @cifs_ace: target buffer for storing in cifs ace format + * @local_ace: source buffer in Linux POSIX ACL xattr format + * + * Return: 0 + */ +static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, + const struct posix_acl_xattr_entry *local_ace) +{ + __u16 rc = 0; /* 0 = ACL converted ok */ + + cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); + cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); + /* BB is there a better way to handle the large uid? */ + if (local_ace->e_id == cpu_to_le32(-1)) { + /* Probably no need to le convert -1 on any + * arch but can not hurt + */ + cifs_ace->cifs_uid = cpu_to_le64(-1); + } else + cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); + return rc; +} + +/** + * ACL_to_cifs_posix() - ACL from local Linux POSIX xattr to CIFS POSIX ACL + * wire format + * @parm_data: target buffer for storing in cifs ace format + * @pACL: source buffer in cifs ace format + * @buflen: target buffer length + * @acl_type: ace type + * + * Return: 0 on success, otherwise error + */ +static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, + const int buflen, const int acl_type) +{ + __u16 rc = 0; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; + struct posix_acl_xattr_header *local_acl = (void *)pACL; + struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); + int count; + int i, j = 0; + + if ((buflen == 0) || !pACL || !cifs_acl) + return 0; + + count = posix_acl_xattr_count((size_t)buflen); + ksmbd_debug(SMB, "setting acl with %d entries from buf of length %d and version of %d\n", + count, buflen, le32_to_cpu(local_acl->a_version)); + if (le32_to_cpu(local_acl->a_version) != 2) { + ksmbd_debug(SMB, "unknown POSIX ACL version %d\n", + le32_to_cpu(local_acl->a_version)); + return 0; + } + if (acl_type == ACL_TYPE_ACCESS) { + cifs_acl->access_entry_count = cpu_to_le16(count); + j = 0; + } else if (acl_type == ACL_TYPE_DEFAULT) { + cifs_acl->default_entry_count = cpu_to_le16(count); + if (cifs_acl->access_entry_count) + j = le16_to_cpu(cifs_acl->access_entry_count); + } else { + ksmbd_debug(SMB, "unknown ACL type %d\n", acl_type); + return 0; + } + for (i = 0; i < count; i++, j++) { + rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]); + if (rc != 0) { + /* ACE not converted */ + break; + } + } + if (rc == 0) { + rc = (__u16)(count * sizeof(struct cifs_posix_ace)); + /* BB add check to make sure ACL does not overflow SMB */ + } + return rc; +} + +/** + * smb_get_acl() - handler for query posix acl information + * @work: smb work containing posix acl query command + * @path: path of file/dir to query acl + * + * Return: 0 on success, otherwise error + */ +static int smb_get_acl(struct ksmbd_work *work, struct path *path) +{ + struct smb_com_trans2_rsp *rsp = work->response_buf; + char *buf = NULL; + int rc = 0, value_len; + struct cifs_posix_acl *aclbuf; + __u16 rsp_data_cnt = 0; + + aclbuf = (struct cifs_posix_acl *)(work->response_buf + + sizeof(struct smb_com_trans2_rsp) + 4); + + aclbuf->version = cpu_to_le16(CIFS_ACL_VERSION); + aclbuf->default_entry_count = 0; + aclbuf->access_entry_count = 0; + + /* check if POSIX_ACL_XATTR_ACCESS exists */ + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), path->dentry, + XATTR_NAME_POSIX_ACL_ACCESS, + &buf); + if (value_len > 0) { + rsp_data_cnt += ACL_to_cifs_posix((char *)aclbuf, buf, + value_len, ACL_TYPE_ACCESS); + kfree(buf); + buf = NULL; + } + + /* check if POSIX_ACL_XATTR_DEFAULT exists */ + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), path->dentry, + XATTR_NAME_POSIX_ACL_DEFAULT, + &buf); + if (value_len > 0) { + rsp_data_cnt += ACL_to_cifs_posix((char *)aclbuf, buf, + value_len, ACL_TYPE_DEFAULT); + kfree(buf); + buf = NULL; + } + + if (rsp_data_cnt) + rsp_data_cnt += sizeof(struct cifs_posix_acl); + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(rsp_data_cnt); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(rsp_data_cnt + 5); + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + + if (buf) + kfree(buf); + return rc; +} + +/** + * smb_set_acl() - handler for setting posix acl information + * @work: smb work containing posix acl set command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_acl(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cifs_posix_acl *wire_acl_data; + char *fname, *buf = NULL; + int rc = 0, acl_type = 0, value_len; + + fname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(fname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(fname); + } + + buf = vmalloc(XATTR_SIZE_MAX); + if (!buf) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + rc = -ENOMEM; + goto out; + } + + wire_acl_data = (struct cifs_posix_acl *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + if (le16_to_cpu(wire_acl_data->access_entry_count) > 0 && + le16_to_cpu(wire_acl_data->access_entry_count) < 0xFFFF) { + acl_type = ACL_TYPE_ACCESS; + + } else if (le16_to_cpu(wire_acl_data->default_entry_count) > 0 && + le16_to_cpu(wire_acl_data->default_entry_count) < 0xFFFF) { + acl_type = ACL_TYPE_DEFAULT; + } else { + rc = -EINVAL; + goto out; + } + + rc = cifs_copy_posix_acl(buf, + (char *)wire_acl_data, + XATTR_SIZE_MAX, acl_type, XATTR_SIZE_MAX); + if (rc < 0) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + goto out; + } + + value_len = rc; + if (acl_type == ACL_TYPE_ACCESS) { + rc = ksmbd_vfs_fsetxattr(work, + fname, + XATTR_NAME_POSIX_ACL_ACCESS, + buf, value_len, 0); + } else if (acl_type == ACL_TYPE_DEFAULT) { + rc = ksmbd_vfs_fsetxattr(work, + fname, + XATTR_NAME_POSIX_ACL_DEFAULT, + buf, value_len, 0); + } + + if (rc < 0) { + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(0); + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(0); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + if (buf) + vfree(buf); + kfree(fname); + return rc; +} + +static void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz) +{ + size_t sz = min(old_sz, new_sz); + void *nptr; + + nptr = kvmalloc(new_sz, GFP_KERNEL | __GFP_ZERO); + if (!nptr) + return ptr; + memcpy(nptr, ptr, sz); + kvfree(ptr); + return nptr; +} + +/** + * smb_readlink() - handler for reading symlink source path + * @work: smb work containing query link information + * + * Return: 0 on success, otherwise error + */ +static int smb_readlink(struct ksmbd_work *work, struct path *path) +{ + struct smb_com_trans2_qpi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + int err, name_len; + char *buf, *ptr; + + buf = kzalloc((CIFS_MF_SYMLINK_LINK_MAXLEN), GFP_KERNEL); + if (!buf) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_readlink(path, buf, CIFS_MF_SYMLINK_LINK_MAXLEN); + if (err < 0) { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + goto out; + } + + /* + * check if this namelen(unicode) and smb header can fit in small rsp + * buf. If not, switch to large rsp buffer. + */ + err++; + err *= 2; + if (err + MAX_HEADER_SIZE(work->conn) > work->response_sz) { + void *nptr; + size_t nsz = err + MAX_HEADER_SIZE(work->conn); + + nptr = ksmbd_realloc_response(work->response_buf, + work->response_sz, + nsz); + if (nptr == work->response_buf) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + err = -ENOMEM; + goto out; + } + + work->response_buf = nptr; + rsp = (struct smb_com_trans2_rsp *)work->response_buf; + } + err = 0; + + ptr = (char *)&rsp->Buffer[0]; + memset(ptr, 0, 4); + ptr += 4; + + if (is_smbreq_unicode(&req->hdr)) { + name_len = smb_strtoUTF16((__le16 *)ptr, + buf, + CIFS_MF_SYMLINK_LINK_MAXLEN, + work->conn->local_nls); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB add path length overrun check */ + name_len = strscpy(ptr, buf, CIFS_MF_SYMLINK_LINK_MAXLEN - 1); + name_len++; /* trailing null */ + } + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(name_len); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(name_len + 5); + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + +out: + kfree(buf); + return err; +} + +/** + * smb_get_ea() - handler for extended attribute query + * @work: smb work containing query xattr command + * @path: path of file/dir to query xattr command + * + * Return: 0 on success, otherwise error + */ +static int smb_get_ea(struct ksmbd_work *work, struct path *path) +{ + struct smb_com_trans2_rsp *rsp = work->response_buf; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len; + struct fealist *eabuf = (struct fealist *)(work->response_buf + + sizeof(struct smb_com_trans2_rsp) + 4); + struct fea *temp_fea; + ssize_t buf_free_len; + __u16 rsp_data_cnt = 4; + + eabuf->list_len = cpu_to_le32(rsp_data_cnt); + buf_free_len = work->response_sz - (get_rfc1002_len(rsp) + 4) - + sizeof(struct smb_com_trans2_rsp); + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + eabuf->list_len = cpu_to_le32(rsp_data_cnt); + goto done; + } + + xattr_list_len = rc; + rc = 0; + + ptr = (char *)eabuf->list; + temp_fea = (struct fea *)ptr; + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + /* + * CIFS does not support EA other name user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + name_len = strlen(name); + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&temp_fea->name + name_len + 1); + buf_free_len -= (offsetof(struct fea, name) + name_len + 1); + + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), + path->dentry, name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + goto out; + } + + memcpy(ptr, buf, value_len); + kfree(buf); + + temp_fea->EA_flags = 0; + temp_fea->name_len = name_len; + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + memcpy(temp_fea->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(temp_fea->name, name, name_len); + + temp_fea->value_len = cpu_to_le16(value_len); + buf_free_len -= value_len; + rsp_data_cnt += offsetof(struct fea, name) + name_len + 1 + + value_len; + eabuf->list_len += cpu_to_le32(offsetof(struct fea, name) + + name_len + 1 + value_len); + ptr += value_len; + temp_fea = (struct fea *)ptr; + } + +done: + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(rsp_data_cnt); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(rsp_data_cnt + 5); + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); +out: + kvfree(xattr_list); + return rc; +} + +/** + * query_path_info() - handler for query path info + * @work: smb work containing query path info command + * + * Return: 0 on success, otherwise error + */ +static int query_path_info(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct trans2_qpi_req_params *req_params; + char *name = NULL; + struct path path; + struct kstat st; + int rc; + char *ptr; + __u64 create_time = 0, time; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + rsp_hdr->Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + return 0; + } + + req_params = (struct trans2_qpi_req_params *)(work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4); + name = smb_get_name(share, req_params->FileName, PATH_MAX, work, + false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc == -EACCES || rc == -EXDEV) + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp_hdr->Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + ksmbd_debug(SMB, "cannot get linux path for %s, err %d\n", + name, rc); + goto out; + } + + if (d_is_symlink(path.dentry)) { + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + goto err_out; + } + + rc = vfs_getattr(&path, &st, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); + if (rc) { + pr_err("cannot get stat information\n"); + goto err_out; + } + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (rc > 0) + create_time = da.create_time; + rc = 0; + } + + switch (le16_to_cpu(req_params->InformationLevel)) { + case SMB_INFO_STANDARD: + { + struct file_info_standard *infos; + + ksmbd_debug(SMB, "SMB_INFO_STANDARD\n"); + rc = ksmbd_query_inode_status(d_inode(path.dentry)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + infos = (struct file_info_standard *)(ptr + 4); + unix_to_dos_time(ksmbd_NTtimeToUnix(cpu_to_le64(create_time)), + &infos->CreationDate, &infos->CreationTime); + unix_to_dos_time(st.atime, + &infos->LastAccessDate, + &infos->LastAccessTime); + unix_to_dos_time(st.mtime, + &infos->LastWriteDate, + &infos->LastWriteTime); + infos->DataSize = cpu_to_le32(st.size); + infos->AllocationSize = cpu_to_le32(st.blocks << 9); + infos->Attributes = cpu_to_le16(S_ISDIR(st.mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + infos->EASize = 0; + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(22); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(22); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(27); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_STANDARD_INFO: + { + struct file_standard_info *standard_info; + unsigned int del_pending; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_STANDARD_INFO\n"); + del_pending = ksmbd_query_inode_status(d_inode(path.dentry)); + if (del_pending == KSMBD_INODE_STATUS_PENDING_DELETE) + del_pending = 1; + else + del_pending = 0; + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_standard_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + standard_info = (struct file_standard_info *)(ptr + 4); + standard_info->AllocationSize = cpu_to_le64(st.blocks << 9); + standard_info->EndOfFile = cpu_to_le64(st.size); + standard_info->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + del_pending); + standard_info->DeletePending = del_pending; + standard_info->Directory = S_ISDIR(st.mode) ? 1 : 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_BASIC_INFO: + { + struct file_basic_info *basic_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_BASIC_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_basic_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + basic_info = (struct file_basic_info *)(ptr + 4); + basic_info->CreationTime = cpu_to_le64(create_time); + time = ksmbd_UnixTimeToNT(st.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = S_ISDIR(st.mode) ? + ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + basic_info->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_EA_INFO: + { + struct file_ea_info *ea_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_EA_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_ea_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ea_info = (struct file_ea_info *)(ptr + 4); + ea_info->EaSize = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_NAME_INFO: + { + struct file_name_info *name_info; + int uni_filename_len; + char *filename; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_NAME_INFO\n"); + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + name_info = (struct file_name_info *)(ptr + 4); + + filename = convert_to_nt_pathname(name); + if (!filename) { + rc = -ENOMEM; + goto err_out; + } + uni_filename_len = smbConvertToUTF16( + (__le16 *)name_info->FileName, + filename, PATH_MAX, + conn->local_nls, 0); + kfree(filename); + uni_filename_len *= 2; + name_info->FileNameLength = cpu_to_le32(uni_filename_len); + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(2 + uni_filename_len + 4 + 3); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_ALL_INFO: + { + struct file_all_info *ainfo; + unsigned int del_pending; + char *filename; + int uni_filename_len, total_count = 72; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_ALL_INFO\n"); + + del_pending = ksmbd_query_inode_status(d_inode(path.dentry)); + if (del_pending == KSMBD_INODE_STATUS_PENDING_DELETE) + del_pending = 1; + else + del_pending = 0; + + filename = convert_to_nt_pathname(name); + if (!filename) { + rc = -ENOMEM; + goto err_out; + } + + /* + * Observation: sizeof smb_hdr is 33 bytes(including word count) + * After that: trans2 response 22 bytes when stepcount 0 and + * including ByteCount storage. + */ + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ainfo = (struct file_all_info *) (ptr + 4); + + ainfo->CreationTime = cpu_to_le64(create_time); + time = ksmbd_UnixTimeToNT(st.atime); + ainfo->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + ainfo->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + ainfo->ChangeTime = cpu_to_le64(time); + ainfo->Attributes = S_ISDIR(st.mode) ? + ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + ainfo->Pad1 = 0; + ainfo->AllocationSize = cpu_to_le64(st.blocks << 9); + ainfo->EndOfFile = cpu_to_le64(st.size); + ainfo->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + del_pending); + ainfo->DeletePending = del_pending; + ainfo->Directory = S_ISDIR(st.mode) ? 1 : 0; + ainfo->Pad2 = 0; + ainfo->EASize = 0; + uni_filename_len = smbConvertToUTF16( + (__le16 *)ainfo->FileName, + filename, PATH_MAX, + conn->local_nls, 0); + kfree(filename); + uni_filename_len *= 2; + ainfo->FileNameLength = cpu_to_le32(uni_filename_len); + total_count += uni_filename_len; + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + /* add unicode name length of name */ + rsp->t2.TotalDataCount = cpu_to_le16(total_count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(total_count); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /* 2 for parameter count + 72 data count + + * filename length + 3 pad (1pad1 + 2 pad2) + */ + rsp->ByteCount = cpu_to_le16(5 + total_count); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_ALT_NAME_INFO: + { + struct alt_name_info *alt_name_info; + char *base; + int filename_len; + + ksmbd_debug(SMB, "SMB_QUERY_ALT_NAME_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(25); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + alt_name_info = (struct alt_name_info *)(ptr + 4); + + base = strrchr(name, '/'); + if (!base) + base = name; + else + base += 1; + + filename_len = ksmbd_extract_shortname(conn, base, + alt_name_info->FileName); + alt_name_info->FileNameLength = cpu_to_le32(filename_len); + rsp->t2.TotalDataCount = cpu_to_le16(4 + filename_len); + rsp->t2.DataCount = cpu_to_le16(4 + filename_len); + + inc_rfc1001_len(rsp_hdr, (4 + filename_len + 25)); + break; + } + case SMB_QUERY_FILE_UNIX_BASIC: + { + struct file_unix_basic_info *unix_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_BASIC\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = 0; + rsp->t2.TotalDataCount = cpu_to_le16(100); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = 0; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(100); + rsp->t2.DataOffset = cpu_to_le16(56); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(101); /* 100 data count + 1pad */ + rsp->Pad = 0; + unix_info = (struct file_unix_basic_info *)(&rsp->Pad + 1); + init_unix_info(unix_info, mnt_user_ns(path.mnt), &st); + inc_rfc1001_len(rsp_hdr, (10 * 2 + 101)); + break; + } + case SMB_QUERY_FILE_INTERNAL_INFO: + { + struct file_internal_info *iinfo; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_INTERNAL_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(8); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(8); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(13); + rsp->Pad = 0; + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + iinfo = (struct file_internal_info *) (ptr + 4); + iinfo->UniqueId = cpu_to_le64(st.ino); + inc_rfc1001_len(rsp_hdr, (10 * 2 + 13)); + break; + } + case SMB_QUERY_FILE_UNIX_LINK: + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_LINK\n"); + rc = smb_readlink(work, &path); + if (rc < 0) + goto err_out; + break; + case SMB_INFO_QUERY_ALL_EAS: + ksmbd_debug(SMB, "SMB_INFO_QUERY_ALL_EAS\n"); + rc = smb_get_ea(work, &path); + if (rc < 0) + goto err_out; + break; + case SMB_QUERY_POSIX_ACL: + ksmbd_debug(SMB, "SMB_QUERY_POSIX_ACL\n"); + rc = smb_get_acl(work, &path); + if (rc < 0) + goto err_out; + break; + default: + pr_err("query path info not implemnted for %x\n", + le16_to_cpu(req_params->InformationLevel)); + rc = -EINVAL; + goto err_out; + } + +err_out: + path_put(&path); +out: + ksmbd_revert_fsids(work); + kfree(name); + return rc; +} + +/** + * create_trans2_reply() - create response for trans2 request + * @work: smb work containing smb response buffer + * @count: trans2 response buffer size + */ +static void create_trans2_reply(struct ksmbd_work *work, __u16 count) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + + rsp_hdr->WordCount = 0x0A; + rsp->t2.TotalParameterCount = 0; + rsp->t2.TotalDataCount = cpu_to_le16(count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = 0; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(count); + rsp->t2.DataOffset = cpu_to_le16(56); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + rsp->ByteCount = cpu_to_le16(count + 1); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + (count + 1)); +} + +/** + * set_fs_info() - handler for set fs info commands + * @work: smb work containing set fs info command buffer + * + * Return: 0 on success, otherwise error + */ +static int set_fs_info(struct ksmbd_work *work) +{ + struct smb_com_trans2_setfsi_req *req = work->request_buf; + struct smb_com_trans2_setfsi_rsp *rsp = work->response_buf; + int info_level = le16_to_cpu(req->InformationLevel); + + switch (info_level) { + u64 client_cap; + + case SMB_SET_CIFS_UNIX_INFO: + ksmbd_debug(SMB, "SMB_SET_CIFS_UNIX_INFO\n"); + if (le16_to_cpu(req->ClientUnixMajor) != + CIFS_UNIX_MAJOR_VERSION) { + pr_err("Non compatible unix major info\n"); + return -EINVAL; + } + + if (le16_to_cpu(req->ClientUnixMinor) != + CIFS_UNIX_MINOR_VERSION) { + pr_err("Non compatible unix minor info\n"); + return -EINVAL; + } + + client_cap = le64_to_cpu(req->ClientUnixCap); + ksmbd_debug(SMB, "clients unix cap = %llx\n", client_cap); + /* TODO: process caps */ + rsp->t2.TotalDataCount = 0; + break; + default: + ksmbd_debug(SMB, "info level %x not supported\n", info_level); + return -EINVAL; + } + + create_trans2_reply(work, le16_to_cpu(rsp->t2.TotalDataCount)); + return 0; +} + +/** + * query_fs_info() - handler for query fs info commands + * @work: smb work containing query fs info command buffer + * + * Return: 0 on success, otherwise error + */ +static int query_fs_info(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = work->request_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_qfsi_req_params *req_params; + struct ksmbd_conn *conn = work->conn; + struct kstatfs stfs; + struct ksmbd_share_config *share; + int rc; + struct path path; + bool incomplete = false; + int info_level, len = 0; + struct ksmbd_tree_connect *tree_conn; + + req_params = (struct smb_com_trans2_qfsi_req_params *) + (work->request_buf + le16_to_cpu(req->ParameterOffset) + 4); + /* check if more data is coming */ + if (le16_to_cpu(req->TotalParameterCount) != + le16_to_cpu(req->ParameterCount)) { + ksmbd_debug(SMB, "total param = %d, received = %d\n", + le16_to_cpu(req->TotalParameterCount), + le16_to_cpu(req->ParameterCount)); + incomplete = true; + } + + if (le16_to_cpu(req->TotalDataCount) != le16_to_cpu(req->DataCount)) { + ksmbd_debug(SMB, "total data = %d, received = %d\n", + le16_to_cpu(req->TotalDataCount), + le16_to_cpu(req->DataCount)); + incomplete = true; + } + + if (incomplete) { + /* create 1 trans_state structure + * and add to connection list + */ + } + + info_level = le16_to_cpu(req_params->InformationLevel); + + tree_conn = ksmbd_tree_conn_lookup(work->sess, + le16_to_cpu(req_hdr->Tid)); + if (!tree_conn) + return -ENOENT; + share = tree_conn->share_conf; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) + return -ENOENT; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (rc) { + ksmbd_revert_fsids(work); + pr_err("cannot create vfs path\n"); + return rc; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + pr_err("cannot do stat of path %s\n", share->path); + goto err_out; + } + + switch (info_level) { + case SMB_INFO_ALLOCATION: + { + struct filesystem_alloc_info *ainfo; + + ksmbd_debug(SMB, "GOT SMB_INFO_ALLOCATION\n"); + rsp->t2.TotalDataCount = cpu_to_le16(18); + ainfo = (struct filesystem_alloc_info *)(&rsp->Pad + 1); + ainfo->fsid = 0; + ainfo->BytesPerSector = cpu_to_le16(512); + ainfo->SectorsPerAllocationUnit = + cpu_to_le32(stfs.f_bsize/le16_to_cpu(ainfo->BytesPerSector)); + ainfo->TotalAllocationUnits = cpu_to_le32(stfs.f_blocks); + ainfo->FreeAllocationUnits = cpu_to_le32(stfs.f_bfree); + break; + } + case SMB_QUERY_FS_VOLUME_INFO: + { + struct filesystem_vol_info *vinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_VOLUME_INFO\n"); + vinfo = (struct filesystem_vol_info *)(&rsp->Pad + 1); + vinfo->VolumeCreationTime = 0; + /* Taking dummy value of serial number*/ + vinfo->SerialNumber = cpu_to_le32(0xbc3ac512); + len = smbConvertToUTF16((__le16 *)vinfo->VolumeLabel, + share->name, PATH_MAX, conn->local_nls, 0); + vinfo->VolumeLabelSize = cpu_to_le32(len); + vinfo->Reserved = 0; + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct filesystem_vol_info) + + len - 2); + break; + } + case SMB_QUERY_FS_SIZE_INFO: + { + struct filesystem_info *sinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_SIZE_INFO\n"); + rsp->t2.TotalDataCount = cpu_to_le16(24); + sinfo = (struct filesystem_info *)(&rsp->Pad + 1); + sinfo->BytesPerSector = cpu_to_le32(512); + sinfo->SectorsPerAllocationUnit = + cpu_to_le32(stfs.f_bsize/sinfo->BytesPerSector); + sinfo->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + sinfo->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + break; + } + case SMB_QUERY_FS_DEVICE_INFO: + { + struct filesystem_device_info *fdi; + + /* query fs info device info response is 0 word and 8 bytes */ + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_DEVICE_INFO\n"); + if (le16_to_cpu(req->MaxDataCount) < 8) { + pr_err("Insufficient bytes, cannot response()\n"); + rc = -EINVAL; + goto err_out; + } + + rsp->t2.TotalDataCount = cpu_to_le16(18); + fdi = (struct filesystem_device_info *)(&rsp->Pad + 1); + fdi->DeviceType = cpu_to_le32(FILE_DEVICE_DISK); + fdi->DeviceCharacteristics = cpu_to_le32(0x20); + break; + } + case SMB_QUERY_FS_ATTRIBUTE_INFO: + { + struct filesystem_attribute_info *info; + + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_ATTRIBUTE_INFO\n"); + /* constant 12 bytes + variable filesystem name */ + info = (struct filesystem_attribute_info *)(&rsp->Pad + 1); + + if (le16_to_cpu(req->MaxDataCount) < 12) { + pr_err("Insufficient bytes, cannot response()\n"); + rc = -EINVAL; + goto err_out; + } + + info->Attributes = cpu_to_le32(FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_VOLUME_QUOTAS); + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + info->FileSystemNameLen = 0; + rsp->t2.TotalDataCount = cpu_to_le16(12); + break; + } + case SMB_QUERY_CIFS_UNIX_INFO: + { + struct filesystem_unix_info *uinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_CIFS_UNIX_INFO\n"); + /* constant 12 bytes + variable filesystem name */ + uinfo = (struct filesystem_unix_info *)(&rsp->Pad + 1); + + if (le16_to_cpu(req->MaxDataCount) < 12) { + pr_err("Insufficient bytes, cannot response()\n"); + rc = -EINVAL; + goto err_out; + } + uinfo->MajorVersionNumber = + cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); + uinfo->MinorVersionNumber = + cpu_to_le16(CIFS_UNIX_MINOR_VERSION); + uinfo->Capability = cpu_to_le64(SMB_UNIX_CAPS); + rsp->t2.TotalDataCount = cpu_to_le16(12); + break; + } + case SMB_QUERY_POSIX_FS_INFO: + { + struct filesystem_posix_info *pinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_POSIX_FS_INFO\n"); + rsp->t2.TotalDataCount = cpu_to_le16(56); + pinfo = (struct filesystem_posix_info *)(&rsp->Pad + 1); + pinfo->BlockSize = cpu_to_le32(stfs.f_bsize); + pinfo->OptimalTransferSize = cpu_to_le32(stfs.f_blocks); + pinfo->TotalBlocks = cpu_to_le64(stfs.f_blocks); + pinfo->BlocksAvail = cpu_to_le64(stfs.f_bfree); + pinfo->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + pinfo->TotalFileNodes = cpu_to_le64(stfs.f_files); + pinfo->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + pinfo->FileSysIdentifier = 0; + break; + } + default: + ksmbd_debug(SMB, "info level %x not implemented\n", info_level); + rc = -EINVAL; + goto err_out; + } + + create_trans2_reply(work, le16_to_cpu(rsp->t2.TotalDataCount)); + +err_out: + path_put(&path); + ksmbd_revert_fsids(work); + return rc; +} + +/** + * smb_posix_convert_flags() - convert smb posix access flags to open flags + * @flags: smb posix access flags + * + * Return: file open flags + */ +static __u32 smb_posix_convert_flags(__u32 flags, int *may_flags) +{ + __u32 posix_flags = 0; + + if ((flags & SMB_ACCMODE) == SMB_O_RDONLY) + posix_flags = O_RDONLY; + else if ((flags & SMB_ACCMODE) == SMB_O_WRONLY) + posix_flags = O_WRONLY; + else if ((flags & SMB_ACCMODE) == SMB_O_RDWR) + posix_flags = O_RDWR; + + if (flags & SMB_O_SYNC) + posix_flags |= O_DSYNC; + if (flags & SMB_O_DIRECTORY) + posix_flags |= O_DIRECTORY; + if (flags & SMB_O_NOFOLLOW) + posix_flags |= O_NOFOLLOW; + if (flags & SMB_O_APPEND) + posix_flags |= O_APPEND; + + *may_flags = ksmbd_openflags_to_mayflags(posix_flags); + + return posix_flags; +} + +/** + * smb_get_disposition() - convert smb disposition flags to open flags + * @flags: smb file disposition flags + * @file_present: file already present or not + * @stat: file stat information + * @open_flags: open flags should be stored here + * + * Return: file disposition flags + */ +static int smb_get_disposition(unsigned int flags, bool file_present, + struct kstat *stat, unsigned int *open_flags) +{ + int dispostion, disp_flags; + + if ((flags & (SMB_O_CREAT | SMB_O_EXCL)) == (SMB_O_CREAT | SMB_O_EXCL)) + dispostion = FILE_CREATE; + else if ((flags & (SMB_O_CREAT | SMB_O_TRUNC)) == + (SMB_O_CREAT | SMB_O_TRUNC)) + dispostion = FILE_OVERWRITE_IF; + else if ((flags & SMB_O_CREAT) == SMB_O_CREAT) + dispostion = FILE_OPEN_IF; + else if ((flags & SMB_O_TRUNC) == SMB_O_TRUNC) + dispostion = FILE_OVERWRITE; + else if ((flags & (SMB_O_CREAT | SMB_O_EXCL | SMB_O_TRUNC)) == 0) + dispostion = FILE_OPEN; + else + dispostion = FILE_SUPERSEDE; + + disp_flags = file_create_dispostion_flags(dispostion, file_present); + if (disp_flags < 0) + return disp_flags; + + *open_flags |= disp_flags; + return disp_flags; +} + +/** + * smb_posix_open() - handler for smb posix open + * @work: smb work containing posix open command + * + * Return: 0 on success, otherwise error + */ +static int smb_posix_open(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *pSMB_req = work->request_buf; + struct smb_com_trans2_spi_rsp *pSMB_rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct open_psx_req *psx_req; + struct open_psx_rsp *psx_rsp; + struct file_unix_basic_info *unix_info; + struct path path; + struct kstat stat; + __u16 data_offset, rsp_info_level, file_info = 0; + __u32 oplock_flags, posix_open_flags, may_flags; + umode_t mode; + char *name; + bool file_present = true; + int err; + struct ksmbd_file *fp = NULL; + int oplock_rsp = OPLOCK_NONE; + + name = smb_get_name(share, pSMB_req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + pSMB_rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + pSMB_rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + kfree(name); + return -ENOMEM; + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 0); + if (err) { + file_present = false; + ksmbd_debug(SMB, "cannot get linux path for %s, err = %d\n", + name, err); + if (err == -EACCES || err == -EXDEV) + goto out; + } else { + if (d_is_symlink(path.dentry)) { + err = -EACCES; + goto free_path; + } + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("can not stat %s, err = %d\n", name, err); + goto free_path; + } + } + + data_offset = le16_to_cpu(pSMB_req->DataOffset); + psx_req = (struct open_psx_req *)(((char *)&pSMB_req->hdr.Protocol) + + data_offset); + oplock_flags = le32_to_cpu(psx_req->OpenFlags); + + posix_open_flags = smb_posix_convert_flags( + le32_to_cpu(psx_req->PosixOpenFlags), + &may_flags); + err = smb_get_disposition(le32_to_cpu(psx_req->PosixOpenFlags), + file_present, &stat, + &posix_open_flags); + if (err < 0) { + ksmbd_debug(SMB, "create_dispostion returned %d\n", err); + if (file_present) + goto free_path; + else + goto out; + } + + ksmbd_debug(SMB, "filename : %s, posix_open_flags : %x\n", name, + posix_open_flags); + mode = (umode_t) le64_to_cpu(psx_req->Permissions); + rsp_info_level = le16_to_cpu(psx_req->Level); + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (posix_open_flags & O_CREAT) { + err = -EACCES; + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + if (file_present) + goto free_path; + else + goto out; + } + } + + /* posix mkdir command */ + if (posix_open_flags == (O_DIRECTORY | O_CREAT)) { + if (file_present) { + err = -EEXIST; + goto free_path; + } + + err = ksmbd_vfs_mkdir(work, name, mode); + if (err) + goto out; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } + ksmbd_debug(SMB, "mkdir done for %s, inode %lu\n", + name, d_inode(path.dentry)->i_ino); + goto prepare_rsp; + } + + if (!file_present && (posix_open_flags & O_CREAT)) { + err = ksmbd_vfs_create(work, name, mode); + if (err) + goto out; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } + } else if (file_present) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + may_flags); +#else + err = inode_permission(d_inode(path.dentry), + may_flags); +#endif + if (err) + goto free_path; + } + + fp = ksmbd_vfs_dentry_open(work, &path, posix_open_flags, + 0, file_present); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + fp = NULL; + goto free_path; + } + fp->filename = name; + fp->pid = le16_to_cpu(pSMB_req->hdr.Pid); + + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + if (smb1_oplock_enable && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS) && + !S_ISDIR(file_inode(fp->filp)->i_mode)) { + /* Client cannot request levelII oplock directly */ + err = smb_grant_oplock(work, oplock_flags & + (REQ_OPLOCK | REQ_BATCHOPLOCK), fp->volatile_id, fp, + le16_to_cpu(pSMB_req->hdr.Tid), NULL, 0); + if (err) + goto free_path; + } + + oplock_rsp = fp->f_opinfo != NULL ? fp->f_opinfo->level : 0; + +prepare_rsp: + /* open/mkdir success, send back response */ + data_offset = sizeof(struct smb_com_trans2_spi_rsp) - + sizeof(pSMB_rsp->hdr.smb_buf_length) + + 3 /*alignment*/; + psx_rsp = (struct open_psx_rsp *)(((char *)&pSMB_rsp->hdr.Protocol) + + data_offset); + if (data_offset + sizeof(struct open_psx_rsp) > work->response_sz) { + err = -EIO; + goto free_path; + } + + psx_rsp->OplockFlags = cpu_to_le16(oplock_rsp); + psx_rsp->Fid = fp != NULL ? fp->volatile_id : 0; + + if (file_present) { + if (!(posix_open_flags & O_TRUNC)) + file_info = F_OPENED; + else + file_info = F_OVERWRITTEN; + } else + file_info = F_CREATED; + psx_rsp->CreateAction = cpu_to_le32(file_info); + + if (rsp_info_level != SMB_QUERY_FILE_UNIX_BASIC) { + ksmbd_debug(SMB, "returning null information level response"); + rsp_info_level = SMB_NO_INFO_LEVEL_RESPONSE; + } + psx_rsp->ReturnedLevel = cpu_to_le16(rsp_info_level); + + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("cannot get stat information\n"); + goto free_path; + } + + pSMB_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + unix_info = (struct file_unix_basic_info *)((char *)psx_rsp + + sizeof(struct open_psx_rsp)); + init_unix_info(unix_info, mnt_user_ns(path.mnt), &stat); + + pSMB_rsp->hdr.WordCount = 10; + pSMB_rsp->t2.TotalParameterCount = cpu_to_le16(2); + pSMB_rsp->t2.TotalDataCount = cpu_to_le16(sizeof(struct open_psx_rsp) + + sizeof(struct file_unix_basic_info)); + pSMB_rsp->t2.ParameterCount = pSMB_rsp->t2.TotalParameterCount; + pSMB_rsp->t2.Reserved = 0; + pSMB_rsp->t2.ParameterCount = cpu_to_le16(2); + pSMB_rsp->t2.ParameterOffset = cpu_to_le16(56); + pSMB_rsp->t2.ParameterDisplacement = 0; + pSMB_rsp->t2.DataCount = pSMB_rsp->t2.TotalDataCount; + pSMB_rsp->t2.DataOffset = cpu_to_le16(data_offset); + pSMB_rsp->t2.DataDisplacement = 0; + pSMB_rsp->t2.SetupCount = 0; + pSMB_rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 112 data count + 3 pad (1 pad1 + 2 pad2)*/ + pSMB_rsp->ByteCount = cpu_to_le16(117); + pSMB_rsp->Reserved2 = 0; + inc_rfc1001_len(&pSMB_rsp->hdr, + (pSMB_rsp->hdr.WordCount * 2 + 117)); + +free_path: + path_put(&path); +out: + switch (err) { + case 0: + break; + case -ENOSPC: + pSMB_rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + break; + case -EINVAL: + pSMB_rsp->hdr.Status.CifsError = STATUS_NO_SUCH_USER; + break; + case -EACCES: + pSMB_rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -ENOENT: + pSMB_rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + break; + case -EBUSY: + pSMB_rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + break; + default: + pSMB_rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + } + + if (err) { + if (fp) + ksmbd_close_fd(work, fp->volatile_id); + else + kfree(name); + } + ksmbd_revert_fsids(work); + return err; +} + +/** + * smb_posix_unlink() - handler for posix file delete + * @work: smb work containing trans2 posix delete command + * + * Return: 0 on success, otherwise error + */ +static int smb_posix_unlink(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct unlink_psx_rsp *psx_rsp = NULL; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name; + int rc = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + rc = ksmbd_vfs_remove_file(work, name); + if (rc < 0) + goto out; + + psx_rsp = (struct unlink_psx_rsp *)((char *)rsp + + sizeof(struct smb_com_trans2_rsp)); + psx_rsp->EAErrorOffset = cpu_to_le16(0); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(0); + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(0); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + if (rc) + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + + kfree(name); + return rc; +} + +/** + * smb_set_time_pathinfo() - handler for setting time using set path info + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_time_pathinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct file_basic_info *info; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct iattr attrs; + char *name; + int err = 0; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + info = (struct file_basic_info *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + attrs.ia_valid = 0; + if (le64_to_cpu(info->LastAccessTime)) { + attrs.ia_atime = smb_NTtimeToUnix(info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + if (le64_to_cpu(info->ChangeTime)) { + attrs.ia_ctime = smb_NTtimeToUnix(info->ChangeTime); + attrs.ia_valid |= ATTR_CTIME; + } + + if (le64_to_cpu(info->LastWriteTime)) { + attrs.ia_mtime = smb_NTtimeToUnix(info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + /* TODO: check dos mode and acl bits if req->Attributes nonzero */ + + if (!attrs.ia_valid) + goto done; + + err = ksmbd_vfs_setattr(work, name, 0, &attrs); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + +done: + ksmbd_debug(SMB, "%s setattr done\n", name); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + + kfree(name); + return 0; +} + +/** + * smb_set_unix_pathinfo() - handler for setting unix path info(setattr) + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_unix_pathinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct file_unix_basic_info *unix_info; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct iattr attrs; + char *name; + int err = 0; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + err = kern_path(name, 0, &path); + if (err) { + ksmbd_revert_fsids(work); + kfree(name); + return -ENOENT; + } + + unix_info = (struct file_unix_basic_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + attrs.ia_valid = 0; + attrs.ia_mode = 0; + err = unix_info_to_attr(unix_info, mnt_user_ns(path.mnt), &attrs); + path_put(&path); + ksmbd_revert_fsids(work); + if (err) + goto out; + + err = ksmbd_vfs_setattr(work, name, 0, &attrs); + if (err) + goto out; + /* setattr success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + kfree(name); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + return 0; +} + +/** + * smb_set_ea() - handler for setting extended attributes using set path + * info command + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_ea(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct fealist *eabuf; + struct fea *ea; + char *fname, *attr_name = NULL, *value; + int rc = 0, list_len, i, next = 0; + + fname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(fname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(fname); + } + + eabuf = (struct fealist *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + list_len = le32_to_cpu(eabuf->list_len) - 4; + ea = (struct fea *)eabuf->list; + + for (i = 0; list_len >= 0 && ea->name_len != 0; i++, list_len -= next) { + if (ea->name_len > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + goto out; + } + + next = ea->name_len + le16_to_cpu(ea->value_len) + 4; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) { + rc = -ENOMEM; + goto out; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], ea->name, + ea->name_len); + attr_name[XATTR_USER_PREFIX_LEN + ea->name_len] = '\0'; + value = (char *)&ea->name + ea->name_len + 1; + ksmbd_debug(SMB, "name: <%s>, name_len %u, value_len %u\n", + ea->name, ea->name_len, le16_to_cpu(ea->value_len)); + + rc = ksmbd_vfs_fsetxattr(work, fname, attr_name, value, + le16_to_cpu(ea->value_len), + 0); + if (rc < 0) { + kfree(attr_name); + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + kfree(attr_name); + ea += next; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(0); + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(0); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + kfree(fname); + return rc; +} + +/** + * smb_set_file_size_pinfo() - handler for setting eof or truncate using + * trans2 set path info command + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_file_size_pinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct file_end_of_file_info *eofinfo; + struct iattr attr; + char *name = NULL; + loff_t newsize; + int rc = 0; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + eofinfo = (struct file_end_of_file_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + newsize = le64_to_cpu(eofinfo->FileSize); + attr.ia_valid = ATTR_SIZE; + attr.ia_size = newsize; + rc = ksmbd_vfs_setattr(work, name, 0, &attr); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + goto out; + } + ksmbd_debug(SMB, "%s truncated to newsize %lld\n", + name, newsize); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + kfree(name); + return rc; +} + +/** + * smb_creat_hardlink() - handler for creating hardlink + * @work: smb work containing set path info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_creat_hardlink(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *oldname, *newname, *oldname_offset; + int err; + + newname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(newname); + } + + oldname_offset = ((char *)&req->hdr.Protocol) + + le16_to_cpu(req->DataOffset); + oldname = smb_get_name(share, oldname_offset, PATH_MAX, work, false); + if (IS_ERR(oldname)) { + err = PTR_ERR(oldname); + oldname = NULL; + goto out; + } + ksmbd_debug(SMB, "oldname %s, newname %s\n", oldname, newname); + + err = ksmbd_vfs_link(work, oldname, newname); + if (err < 0) { + if (err == -EACCES) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp->hdr.Status.CifsError = STATUS_NOT_SAME_DEVICE; + goto out; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = 0; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); +out: + kfree(newname); + kfree(oldname); + return err; +} + +/** + * smb_creat_symlink() - handler for creating symlink + * @work: smb work containing set path info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_creat_symlink(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_spi_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name, *symname, *name_offset; + bool is_unicode = is_smbreq_unicode(&req->hdr); + int err; + + symname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(symname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(symname); + } + + name_offset = ((char *)&req->hdr.Protocol) + + le16_to_cpu(req->DataOffset); + name = smb_strndup_from_utf16(name_offset, PATH_MAX, is_unicode, + work->conn->local_nls); + if (IS_ERR(name)) { + kfree(symname); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return PTR_ERR(name); + } + ksmbd_debug(SMB, "name %s, symname %s\n", name, symname); + + err = ksmbd_vfs_symlink(work, name, symname); + if (err < 0) { + if (err == -ENOSPC) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else if (err == -EEXIST) + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + else + rsp->hdr.Status.CifsError = STATUS_NOT_SAME_DEVICE; + } else + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = 0; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + kfree(name); + kfree(symname); + return err; +} + +/** + * set_path_info() - handler for trans2 set path info sub commands + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int set_path_info(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *pSMB_req = work->request_buf; + struct smb_com_trans2_spi_rsp *pSMB_rsp = work->response_buf; + __u16 info_level, total_param; + int err = 0; + + info_level = le16_to_cpu(pSMB_req->InformationLevel); + total_param = le16_to_cpu(pSMB_req->TotalParameterCount); + if (total_param < 7) { + pSMB_rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + pr_err("invalid total parameter for info_level 0x%x\n", + total_param); + return -EINVAL; + } + + switch (info_level) { + case SMB_POSIX_OPEN: + err = smb_posix_open(work); + break; + case SMB_POSIX_UNLINK: + err = smb_posix_unlink(work); + break; + case SMB_SET_FILE_UNIX_HLINK: + err = smb_creat_hardlink(work); + break; + case SMB_SET_FILE_UNIX_LINK: + err = smb_creat_symlink(work); + break; + case SMB_SET_FILE_BASIC_INFO: + /* fall through */ + case SMB_SET_FILE_BASIC_INFO2: + err = smb_set_time_pathinfo(work); + break; + case SMB_SET_FILE_UNIX_BASIC: + err = smb_set_unix_pathinfo(work); + break; + case SMB_SET_FILE_EA: + err = smb_set_ea(work); + break; + case SMB_SET_POSIX_ACL: + err = smb_set_acl(work); + break; + case SMB_SET_FILE_END_OF_FILE_INFO2: + /* fall through */ + case SMB_SET_FILE_END_OF_FILE_INFO: + err = smb_set_file_size_pinfo(work); + break; + default: + ksmbd_debug(SMB, "info level = %x not implemented yet\n", + info_level); + pSMB_rsp->hdr.Status.CifsError = STATUS_NOT_IMPLEMENTED; + return -EOPNOTSUPP; + } + + if (err < 0) + ksmbd_debug(SMB, "info_level 0x%x failed, err %d\n", + info_level, err); + return err; +} +static int readdir_info_level_struct_sz(int info_level) +{ + switch (info_level) { + case SMB_FIND_FILE_INFO_STANDARD: + return sizeof(struct find_info_standard); + case SMB_FIND_FILE_QUERY_EA_SIZE: + return sizeof(struct find_info_query_ea_size); + case SMB_FIND_FILE_DIRECTORY_INFO: + return sizeof(struct file_directory_info); + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + return sizeof(struct file_full_directory_info); + case SMB_FIND_FILE_NAMES_INFO: + return sizeof(struct file_names_info); + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + return sizeof(struct file_both_directory_info); + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + return sizeof(struct file_id_full_dir_info); + case SMB_FIND_FILE_ID_BOTH_DIR_INFO: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_UNIX: + return sizeof(struct file_unix_info); + default: + return -EOPNOTSUPP; + } +} + +/** + * smb_populate_readdir_entry() - encode directory entry in smb response buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset; + char *conv_name; + int conv_len; + int struct_sz; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + kfree(conv_name); + d_info->out_buf_len = -1; + return -ENOSPC; + } + + switch (info_level) { + case SMB_FIND_FILE_INFO_STANDARD: + { + struct find_info_standard *fsinfo; + + fsinfo = (struct find_info_standard *)(d_info->wptr); + unix_to_dos_time( + ksmbd_NTtimeToUnix( + cpu_to_le64(ksmbd_kstat->create_time)), + &fsinfo->CreationTime, + &fsinfo->CreationDate); + unix_to_dos_time(ksmbd_kstat->kstat->atime, + &fsinfo->LastAccessTime, + &fsinfo->LastAccessDate); + unix_to_dos_time(ksmbd_kstat->kstat->mtime, + &fsinfo->LastWriteTime, + &fsinfo->LastWriteDate); + fsinfo->DataSize = cpu_to_le32(ksmbd_kstat->kstat->size); + fsinfo->AllocationSize = + cpu_to_le32(ksmbd_kstat->kstat->blocks << 9); + fsinfo->Attributes = + cpu_to_le16(S_ISDIR(ksmbd_kstat->kstat->mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + fsinfo->FileNameLength = cpu_to_le16(conv_len); + memcpy(fsinfo->FileName, conv_name, conv_len); + + break; + } + case SMB_FIND_FILE_QUERY_EA_SIZE: + { + struct find_info_query_ea_size *fesize; + + fesize = (struct find_info_query_ea_size *)(d_info->wptr); + unix_to_dos_time( + ksmbd_NTtimeToUnix( + cpu_to_le64(ksmbd_kstat->create_time)), + &fesize->CreationTime, + &fesize->CreationDate); + unix_to_dos_time(ksmbd_kstat->kstat->atime, + &fesize->LastAccessTime, + &fesize->LastAccessDate); + unix_to_dos_time(ksmbd_kstat->kstat->mtime, + &fesize->LastWriteTime, + &fesize->LastWriteDate); + + fesize->DataSize = + cpu_to_le32(ksmbd_kstat->kstat->size); + fesize->AllocationSize = + cpu_to_le32(ksmbd_kstat->kstat->blocks << 9); + fesize->Attributes = + cpu_to_le16(S_ISDIR(ksmbd_kstat->kstat->mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + fesize->EASize = 0; + fesize->FileNameLength = (__u8)(conv_len); + memcpy(fesize->FileName, conv_name, conv_len); + + break; + } + case SMB_FIND_FILE_DIRECTORY_INFO: + { + struct file_directory_info *fdinfo = NULL; + + fdinfo = (struct file_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + fdinfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + { + struct file_full_directory_info *ffdinfo = NULL; + + ffdinfo = (struct file_full_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = 0; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)ffdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_NAMES_INFO: + { + struct file_names_info *fninfo = NULL; + + fninfo = (struct file_names_info *)(d_info->wptr); + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fninfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + + break; + } + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + { + struct file_both_directory_info *fbdinfo = NULL; + + fbdinfo = (struct file_both_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = 0; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fbdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + { + struct file_id_full_dir_info *dinfo = NULL; + + dinfo = (struct file_id_full_dir_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + dinfo->FileNameLength = cpu_to_le32(conv_len); + dinfo->EaSize = 0; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)dinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_ID_BOTH_DIR_INFO: + { + struct file_id_both_directory_info *fibdinfo = NULL; + + fibdinfo = (struct file_id_both_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = 0; + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = 0; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fibdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + + break; + } + case SMB_FIND_FILE_UNIX: + { + struct file_unix_info *finfo = NULL; + struct file_unix_basic_info *unix_info; + + finfo = (struct file_unix_info *)(d_info->wptr); + finfo->ResumeKey = 0; + unix_info = (struct file_unix_basic_info *)((char *)finfo + 8); + init_unix_info(unix_info, &init_user_ns, ksmbd_kstat->kstat); + /* include null terminator */ + memcpy(finfo->FileName, conv_name, conv_len + 2); + next_entry_offset += 2; + finfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)finfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + } + + d_info->num_entry++; + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr = (char *)(d_info->wptr) + next_entry_offset; + kfree(conv_name); + + ksmbd_debug(SMB, "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + return 0; +} + +/** + * ksmbd_fill_dirent() - populates a dirent details in readdir + * @ctx: dir_context information + * @name: dirent name + * @namelen: dirent name length + * @offset: dirent offset in directory + * @ino: dirent inode number + * @d_type: dirent type + * + * Return: 0 on success, otherwise -EINVAL + */ +static int ksmbd_fill_dirent(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf = + container_of(ctx, struct ksmbd_readdir_data, ctx); + struct ksmbd_dirent *de = (void *)(buf->dirent + buf->used); + unsigned int reclen; + + reclen = ALIGN(sizeof(struct ksmbd_dirent) + namlen, sizeof(u64)); + if (buf->used + reclen > PAGE_SIZE) + return -EINVAL; + + de->namelen = namlen; + de->offset = offset; + de->ino = ino; + de->d_type = d_type; + memcpy(de->name, name, namlen); + buf->used += reclen; + + return 0; +} + +/** + * find_first() - smb readdir command + * @work: smb work containing find first request params + * + * Return: 0 on success, otherwise error + */ +static int find_first(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_ffirst_req_params *req_params; + struct smb_com_trans2_ffirst_rsp_parms *params = NULL; + struct path path; + struct ksmbd_dirent *de; + struct ksmbd_file *dir_fp = NULL; + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct ksmbd_dir_info d_info; + int params_count = sizeof(struct smb_com_trans2_ffirst_rsp_parms); + int data_alignment_offset = 0; + int rc = 0, reclen = 0; + int srch_cnt = 0; + char *dirpath = NULL; + char *srch_ptr = NULL; + int header_size; + int struct_sz; + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + req_params = (struct smb_com_trans2_ffirst_req_params *) + (work->request_buf + le16_to_cpu(req->ParameterOffset) + 4); + dirpath = smb_get_dir_name(share, req_params->FileName, PATH_MAX, + work, &srch_ptr); + if (IS_ERR(dirpath)) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + rc = PTR_ERR(dirpath); + goto err_out; + } + + ksmbd_debug(SMB, "complete dir path = %s\n", dirpath); + rc = ksmbd_vfs_kern_path(work, dirpath, LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path, 0); + if (rc < 0) { + ksmbd_debug(SMB, "cannot create vfs root path <%s> %d\n", + dirpath, rc); + goto err_free_dirpath; + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + MAY_READ | MAY_EXEC)) { +#else + if (inode_permission(d_inode(path.dentry), + MAY_READ | MAY_EXEC)) { +#endif + rc = -EACCES; + path_put(&path); + goto err_free_dirpath; + } + } + + if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_free_dirpath; + } + + dir_fp = ksmbd_vfs_dentry_open(work, &path, O_RDONLY, 0, 1); + if (IS_ERR(dir_fp)) { + ksmbd_debug(SMB, "dir dentry open failed with rc=%d\n", rc); + path_put(&path); + rc = -EINVAL; + goto err_free_dirpath; + } + + write_lock(&dir_fp->f_ci->m_lock); + list_add(&dir_fp->node, &dir_fp->f_ci->m_fp_list); + write_unlock(&dir_fp->f_ci->m_lock); + + set_ctx_actor(&dir_fp->readdir_data.ctx, ksmbd_fill_dirent); + dir_fp->readdir_data.dirent = (void *)__get_free_page(GFP_KERNEL); + if (!dir_fp->readdir_data.dirent) { + rc = -ENOMEM; + goto err_free_dirpath; + } + + dir_fp->filename = dirpath; + dir_fp->readdir_data.used = 0; + dir_fp->dirent_offset = 0; + dir_fp->readdir_data.file_attr = + le16_to_cpu(req_params->SearchAttributes); + + if (params_count % 4) + data_alignment_offset = 4 - params_count % 4; + + d_info.smb1_name = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!d_info.smb1_name) { + rc = -ENOMEM; + goto err_out; + } + d_info.wptr = (char *)((char *)rsp + sizeof(struct smb_com_trans2_rsp) + + params_count + data_alignment_offset); + + header_size = sizeof(struct smb_com_trans2_rsp) + params_count + + data_alignment_offset; + + + struct_sz = readdir_info_level_struct_sz(le16_to_cpu(req_params->InformationLevel)); + + if (struct_sz < 0) { + rc = -EFAULT; + goto err_out; + } + + /* When search count is zero, respond only 1 entry. */ + srch_cnt = le16_to_cpu(req_params->SearchCount); + if (!srch_cnt) + d_info.out_buf_len = struct_sz + header_size; + else + d_info.out_buf_len = min_t(int, srch_cnt * struct_sz + header_size, + MAX_CIFS_LOOKUP_BUFFER_SIZE - header_size); + + + /* reserve dot and dotdot entries in head of buffer in first response */ + if (!*srch_ptr || is_asterisk(srch_ptr)) { + rc = ksmbd_populate_dot_dotdot_entries(work, + le16_to_cpu(req_params->InformationLevel), + dir_fp, + &d_info, + srch_ptr, + smb_populate_readdir_entry); + if (rc) + goto err_out; + } + + do { + if (dir_fp->dirent_offset >= dir_fp->readdir_data.used) { + dir_fp->dirent_offset = 0; + dir_fp->readdir_data.used = 0; + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + if (rc < 0) { + ksmbd_debug(SMB, "err : %d\n", rc); + goto err_out; + } + + if (!dir_fp->readdir_data.used) { + free_page((unsigned long) + (dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + break; + } + + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent); + } else { + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent + + dir_fp->dirent_offset); + } + + reclen = ALIGN(sizeof(struct ksmbd_dirent) + de->namelen, + sizeof(__le64)); + dir_fp->dirent_offset += reclen; + + if (dir_fp->readdir_data.file_attr & + SMB_SEARCH_ATTRIBUTE_DIRECTORY && de->d_type != DT_DIR) + continue; + + ksmbd_kstat.kstat = &kstat; + + if (de->namelen > NAME_MAX) { + pr_err("filename length exceeds 255 bytes.\n"); + continue; + } + + if (!strncmp(de->name, ".", de->namelen) || + !strncmp(de->name, "..", de->namelen)) + continue; + + memcpy(d_info.smb1_name, de->name, de->namelen); + d_info.smb1_name[de->namelen] = '\0'; + d_info.name = (const char *)d_info.smb1_name; + d_info.name_len = de->namelen; + rc = ksmbd_vfs_readdir_name(work, + file_mnt_user_ns(dir_fp->filp), + &ksmbd_kstat, + de->name, + de->namelen, + dirpath); + if (rc) { + ksmbd_debug(SMB, "Cannot read dirent: %d\n", rc); + continue; + } + + if (ksmbd_share_veto_filename(share, d_info.name)) { + ksmbd_debug(SMB, "Veto filename %s\n", d_info.name); + continue; + } + + if (match_pattern(d_info.name, d_info.name_len, srch_ptr)) { + rc = smb_populate_readdir_entry(conn, + le16_to_cpu(req_params->InformationLevel), + &d_info, + &ksmbd_kstat); + if (rc == -ENOSPC) + break; + else if (rc) + goto err_out; + } + } while (d_info.out_buf_len >= 0); + + if (!d_info.data_count && *srch_ptr) { + ksmbd_debug(SMB, "There is no entry matched with the search pattern\n"); + rc = -ENOENT; + goto err_out; + } + + if (d_info.out_buf_len < 0) + dir_fp->dirent_offset -= reclen; + + params = (struct smb_com_trans2_ffirst_rsp_parms *)((char *)rsp + + sizeof(struct smb_com_trans2_rsp)); + params->SearchHandle = dir_fp->volatile_id; + params->SearchCount = cpu_to_le16(d_info.num_entry); + params->LastNameOffset = cpu_to_le16(d_info.last_entry_offset); + + if (d_info.out_buf_len < 0) { + ksmbd_debug(SMB, "continue search\n"); + params->EndofSearch = cpu_to_le16(0); + } else { + ksmbd_debug(SMB, "end of search\n"); + params->EndofSearch = cpu_to_le16(1); + path_put(&(dir_fp->filp->f_path)); + if (le16_to_cpu(req_params->SearchFlags) & + CIFS_SEARCH_CLOSE_AT_END) + ksmbd_close_fd(work, dir_fp->volatile_id); + } + params->EAErrorOffset = cpu_to_le16(0); + + rsp_hdr->WordCount = 0x0A; + rsp->t2.TotalParameterCount = cpu_to_le16(params_count); + rsp->t2.TotalDataCount = cpu_to_le16(d_info.data_count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(params_count); + rsp->t2.ParameterOffset = + cpu_to_le16(sizeof(struct smb_com_trans2_rsp) - 4); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(d_info.data_count); + rsp->t2.DataOffset = cpu_to_le16(sizeof(struct smb_com_trans2_rsp) + + params_count + data_alignment_offset - 4); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->Pad = 0; + rsp->ByteCount = cpu_to_le16(d_info.data_count + + params_count + 1 /*pad*/ + data_alignment_offset); + memset((char *)rsp + sizeof(struct smb_com_trans2_rsp) + params_count, + '\0', 2); + inc_rfc1001_len(rsp_hdr, (10 * 2 + d_info.data_count + + params_count + 1 + data_alignment_offset)); + kfree(srch_ptr); + kfree(d_info.smb1_name); + ksmbd_revert_fsids(work); + return 0; + +err_free_dirpath: + kfree(dirpath); +err_out: + if (rc == -EINVAL) + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES || rc == -EXDEV) + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp_hdr->Status.CifsError = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp_hdr->Status.CifsError = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp_hdr->Status.CifsError = STATUS_INVALID_LEVEL; + if (!rsp->hdr.Status.CifsError) + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + + if (dir_fp) { + if (dir_fp->readdir_data.dirent) { + free_page((unsigned long)(dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + } + path_put(&(dir_fp->filp->f_path)); + ksmbd_close_fd(work, dir_fp->volatile_id); + } + + kfree(srch_ptr); + kfree(d_info.smb1_name); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * find_next() - smb next readdir command + * @work: smb work containing find next request params + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int find_next(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_fnext_req_params *req_params; + struct smb_com_trans2_fnext_rsp_params *params = NULL; + struct ksmbd_dirent *de; + struct ksmbd_file *dir_fp; + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct ksmbd_dir_info d_info; + int params_count = sizeof(struct smb_com_trans2_fnext_rsp_params); + int data_alignment_offset = 0; + int rc = 0, reclen = 0; + __u16 sid; + char *name = NULL; + char *pathname = NULL; + int header_size, srch_cnt, struct_sz; + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + + req_params = (struct smb_com_trans2_fnext_req_params *) + (work->request_buf + le16_to_cpu(req->ParameterOffset) + 4); + sid = req_params->SearchHandle; + + /*Currently no usage of ResumeFilename*/ + name = req_params->ResumeFileName; + name = smb_strndup_from_utf16(name, NAME_MAX, 1, conn->local_nls); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return PTR_ERR(name); + } + ksmbd_debug(SMB, "FileName after unicode conversion %s\n", name); + kfree(name); + + dir_fp = ksmbd_lookup_fd_fast(work, sid); + if (!dir_fp) { + ksmbd_debug(SMB, "error invalid sid\n"); + rc = -EINVAL; + goto err_out; + } + + set_ctx_actor(&dir_fp->readdir_data.ctx, ksmbd_fill_dirent); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) { + rc = -ENOMEM; + goto err_out; + } + + if (params_count % 4) + data_alignment_offset = 4 - params_count % 4; + + d_info.smb1_name = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!d_info.smb1_name) { + rc = -ENOMEM; + goto err_out; + } + d_info.wptr = (char *)((char *)rsp + sizeof(struct smb_com_trans2_rsp) + + params_count + data_alignment_offset); + + header_size = sizeof(struct smb_com_trans2_rsp) + params_count + + data_alignment_offset; + + srch_cnt = le16_to_cpu(req_params->SearchCount); + struct_sz = readdir_info_level_struct_sz(le16_to_cpu(req_params->InformationLevel)); + + if (struct_sz < 0) { + rc = -EFAULT; + goto err_out; + } + + d_info.out_buf_len = min_t(int, srch_cnt * struct_sz + header_size, + MAX_CIFS_LOOKUP_BUFFER_SIZE - header_size); + do { + if (dir_fp->dirent_offset >= dir_fp->readdir_data.used) { + dir_fp->dirent_offset = 0; + dir_fp->readdir_data.used = 0; + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + if (rc < 0) { + ksmbd_debug(SMB, "err : %d\n", rc); + goto err_out; + } + + if (!dir_fp->readdir_data.used) { + free_page((unsigned long) + (dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + break; + } + + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent); + } else { + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent + + dir_fp->dirent_offset); + } + + reclen = ALIGN(sizeof(struct ksmbd_dirent) + de->namelen, + sizeof(__le64)); + dir_fp->dirent_offset += reclen; + + if (dir_fp->readdir_data.file_attr & + SMB_SEARCH_ATTRIBUTE_DIRECTORY && de->d_type != DT_DIR) + continue; + + if (dir_fp->readdir_data.file_attr & + SMB_SEARCH_ATTRIBUTE_ARCHIVE && (de->d_type == DT_DIR || + (!strcmp(de->name, ".") || !strcmp(de->name, "..")))) + continue; + + ksmbd_kstat.kstat = &kstat; + + if (de->namelen > NAME_MAX) { + pr_err("filename length exceeds 255 bytes.\n"); + continue; + } + memcpy(d_info.smb1_name, de->name, de->namelen); + d_info.smb1_name[de->namelen] = '\0'; + d_info.name = (const char *)d_info.smb1_name; + d_info.name_len = de->namelen; + rc = ksmbd_vfs_readdir_name(work, + file_mnt_user_ns(dir_fp->filp), + &ksmbd_kstat, + de->name, + de->namelen, + dir_fp->filename); + if (rc) { + ksmbd_debug(SMB, "Err while dirent read rc = %d\n", rc); + rc = 0; + continue; + } + + if (ksmbd_share_veto_filename(share, d_info.name)) { + ksmbd_debug(SMB, "file(%s) is invisible by setting as veto file\n", + d_info.name); + continue; + } + + ksmbd_debug(SMB, "filename string = %.*s\n", + d_info.name_len, d_info.name); + rc = smb_populate_readdir_entry(conn, + le16_to_cpu(req_params->InformationLevel), &d_info, + &ksmbd_kstat); + if (rc == -ENOSPC) + break; + else if (rc) + goto err_out; + + } while (d_info.out_buf_len >= 0); + + if (d_info.out_buf_len < 0) + dir_fp->dirent_offset -= reclen; + + params = (struct smb_com_trans2_fnext_rsp_params *) + ((char *)rsp + sizeof(struct smb_com_trans_rsp)); + params->SearchCount = cpu_to_le16(d_info.num_entry); + + if (d_info.out_buf_len < 0) { + ksmbd_debug(SMB, "continue search\n"); + params->EndofSearch = cpu_to_le16(0); + params->LastNameOffset = cpu_to_le16(d_info.last_entry_offset); + } else { + ksmbd_debug(SMB, "end of search\n"); + params->EndofSearch = cpu_to_le16(1); + params->LastNameOffset = cpu_to_le16(0); + path_put(&(dir_fp->filp->f_path)); + if (le16_to_cpu(req_params->SearchFlags) & + CIFS_SEARCH_CLOSE_AT_END) + ksmbd_close_fd(work, sid); + } + params->EAErrorOffset = cpu_to_le16(0); + + rsp_hdr->WordCount = 0x0A; + rsp->t2.TotalParameterCount = cpu_to_le16(params_count); + rsp->t2.TotalDataCount = cpu_to_le16(d_info.data_count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(params_count); + rsp->t2.ParameterOffset = + cpu_to_le16(sizeof(struct smb_com_trans_rsp) - 4); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(d_info.data_count); + rsp->t2.DataOffset = cpu_to_le16(sizeof(struct smb_com_trans_rsp) + + params_count + data_alignment_offset - 4); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->Pad = 0; + rsp->ByteCount = cpu_to_le16(d_info.data_count + params_count + 1 + + data_alignment_offset); + memset((char *)rsp + sizeof(struct smb_com_trans_rsp) + + params_count, '\0', data_alignment_offset); + inc_rfc1001_len(rsp_hdr, (10 * 2 + d_info.data_count + + params_count + 1 + data_alignment_offset)); + kfree(pathname); + kfree(d_info.smb1_name); + ksmbd_fd_put(work, dir_fp); + return 0; + +err_out: + if (rc == -EINVAL) + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES || rc == -EXDEV) + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp_hdr->Status.CifsError = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp_hdr->Status.CifsError = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp_hdr->Status.CifsError = STATUS_INVALID_LEVEL; + if (!rsp->hdr.Status.CifsError) + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + + if (dir_fp) { + if (dir_fp->readdir_data.dirent) { + free_page((unsigned long)(dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + } + path_put(&(dir_fp->filp->f_path)); + ksmbd_close_fd(work, sid); + } + + kfree(d_info.smb1_name); + kfree(pathname); + return 0; +} + +/** + * smb_set_alloc_size() - set file truncate method using trans2 + * set file info command - file allocation info level + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_alloc_size(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct file_allocation_info *allocinfo; + struct kstat stat; + struct ksmbd_file *fp = NULL; + loff_t newsize; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + + allocinfo = (struct file_allocation_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + newsize = le64_to_cpu(allocinfo->AllocationSize); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + err = ksmbd_vfs_getattr(&fp->filp->f_path, &stat); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return err; + } + + if (newsize == stat.size) /* nothing to do */ + goto out; + + /* Round up size */ + if (alloc_roundup_size) { + newsize = div64_u64(newsize + alloc_roundup_size - 1, + alloc_roundup_size); + newsize *= alloc_roundup_size; + } + + err = ksmbd_vfs_truncate(work, fp, newsize); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return err; + } + +out: + ksmbd_debug(SMB, "fid %u, truncated to newsize %llu\n", + req->Fid, newsize); + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb_set_file_size_finfo() - set file truncate method using trans2 + * set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_file_size_finfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct file_end_of_file_info *eofinfo; + struct ksmbd_file *fp; + loff_t newsize; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + + eofinfo = (struct file_end_of_file_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + newsize = le64_to_cpu(eofinfo->FileSize); + err = ksmbd_vfs_truncate(work, fp, newsize); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return err; + } + + ksmbd_debug(SMB, "fid %u, truncated to newsize %lld\n", req->Fid, + newsize); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * query_file_info_pipe() - query file info of IPC pipe + * using query file info command + * @work: smb work containing query file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int query_file_info_pipe(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_trans2_qfi_req_params *req_params; + struct file_standard_info *standard_info; + char *ptr; + + req_params = (struct smb_trans2_qfi_req_params *)(work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4); + + if (le16_to_cpu(req_params->InformationLevel) != + SMB_QUERY_FILE_STANDARD_INFO) { + ksmbd_debug(SMB, "query file info for info %u not supported\n", + le16_to_cpu(req_params->InformationLevel)); + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + return -EOPNOTSUPP; + } + + ksmbd_debug(SMB, "SMB_QUERY_FILE_STANDARD_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(2 + sizeof(struct file_standard_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + standard_info = (struct file_standard_info *)(ptr + 4); + standard_info->AllocationSize = cpu_to_le64(4096); + standard_info->EndOfFile = 0; + standard_info->NumberOfLinks = cpu_to_le32(1); + standard_info->DeletePending = 0; + standard_info->Directory = 0; + standard_info->DeletePending = 1; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + + return 0; +} + +/** + * query_file_info() - query file info of file/dir + * using query file info command + * @work: smb work containing query file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int query_file_info(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_trans2_qfi_req_params *req_params; + struct ksmbd_file *fp; + struct kstat st; + char *ptr; + int rc = 0; + u64 time; + + req_params = (struct smb_trans2_qfi_req_params *)(work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "query file info for IPC srvsvc\n"); + return query_file_info_pipe(work); + } + + fp = ksmbd_lookup_fd_fast(work, req_params->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req_params->Fid); + rsp_hdr->Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + rc = -EIO; + goto err_out; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &st); +#else + generic_fillattr(file_inode(fp->filp), &st); +#endif + + switch (le16_to_cpu(req_params->InformationLevel)) { + + case SMB_QUERY_FILE_STANDARD_INFO: + { + struct file_standard_info *standard_info; + unsigned int delete_pending; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_STANDARD_INFO\n"); + delete_pending = ksmbd_inode_pending_delete(fp); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_standard_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + standard_info = (struct file_standard_info *)(ptr + 4); + standard_info->AllocationSize = cpu_to_le64(st.blocks << 9); + standard_info->EndOfFile = cpu_to_le64(st.size); + standard_info->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + delete_pending); + standard_info->DeletePending = delete_pending; + standard_info->Directory = S_ISDIR(st.mode) ? 1 : 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_BASIC_INFO: + { + struct file_basic_info *basic_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_BASIC_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_basic_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + basic_info = (struct file_basic_info *)(ptr + 4); + basic_info->CreationTime = + cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(st.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = S_ISDIR(st.mode) ? + ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + basic_info->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_EA_INFO: + { + struct file_ea_info *ea_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_EA_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_ea_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ea_info = (struct file_ea_info *)(ptr + 4); + ea_info->EaSize = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_UNIX_BASIC: + { + struct file_unix_basic_info *uinfo; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_BASIC\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_unix_basic_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = + cpu_to_le16(sizeof(struct file_unix_basic_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_unix_basic_info) + + 3); + rsp->Pad = 0; + /* lets set unix info info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + uinfo = (struct file_unix_basic_info *)(ptr + 4); + init_unix_info(uinfo, file_mnt_user_ns(fp->filp), &st); + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_NAME_INFO: + { + struct file_name_info *name_info; + int uni_filename_len; + char *filename; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_NAME_INFO\n"); + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + name_info = (struct file_name_info *)(ptr + 4); + + filename = convert_to_nt_pathname(fp->filename); + if (!filename) { + rc = -ENOMEM; + goto err_out; + } + uni_filename_len = smbConvertToUTF16( + (__le16 *)name_info->FileName, + filename, PATH_MAX, + conn->local_nls, 0); + kfree(filename); + uni_filename_len *= 2; + name_info->FileNameLength = cpu_to_le32(uni_filename_len); + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(2 + uni_filename_len + 4 + 3); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_ALL_INFO: + { + struct file_all_info *ainfo; + unsigned int delete_pending; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_BASIC\n"); + delete_pending = ksmbd_inode_pending_delete(fp); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_all_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_all_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_all_info) + 3); + rsp->Pad = 0; + /* lets set all info info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ainfo = (struct file_all_info *)(ptr + 4); + ainfo->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(st.atime); + ainfo->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + ainfo->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + ainfo->ChangeTime = cpu_to_le64(time); + ainfo->Attributes = cpu_to_le32(S_ISDIR(st.mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + ainfo->Pad1 = 0; + ainfo->AllocationSize = cpu_to_le64(st.blocks << 9); + ainfo->EndOfFile = cpu_to_le64(st.size); + ainfo->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + delete_pending); + ainfo->DeletePending = delete_pending; + ainfo->Directory = S_ISDIR(st.mode) ? 1 : 0; + ainfo->Pad2 = 0; + ainfo->EASize = 0; + ainfo->FileNameLength = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + default: + pr_err("query path info not implemnted for %x\n", + le16_to_cpu(req_params->InformationLevel)); + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + + } + +err_out: + ksmbd_fd_put(work, fp); + return rc; +} + +/** + * smb_set_unix_fileinfo() - set smb unix file info(setattr) + * @work: smb work containing unix basic info buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_unix_fileinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req = work->request_buf; + struct smb_com_trans2_sfi_rsp *rsp = work->response_buf; + struct file_unix_basic_info *unix_info; + struct ksmbd_file *fp; + struct iattr attrs; + int err = 0; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + ksmbd_revert_fsids(work); + return -ENOENT; + } + + unix_info = (struct file_unix_basic_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + + attrs.ia_valid = 0; + attrs.ia_mode = 0; + err = unix_info_to_attr(unix_info, + file_mnt_user_ns(fp->filp), &attrs); + ksmbd_fd_put(work, fp); + ksmbd_revert_fsids(work); + if (err) + goto out; + + err = ksmbd_vfs_setattr(work, NULL, (u64)req->Fid, &attrs); + if (err) + goto out; + + /* setattr success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, + rsp->hdr.WordCount * 2 + le16_to_cpu(rsp->ByteCount)); + +out: + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + return 0; +} + +/** + * smb_set_dispostion() - set file dispostion method using trans2 + * using set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_dispostion(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req = work->request_buf; + struct smb_com_trans2_sfi_rsp *rsp = work->response_buf; + char *disp_info; + struct ksmbd_file *fp; + int ret = 0; + + disp_info = (char *) (((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %d\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + if (*disp_info) { + if (!fp->is_nt_open) { + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + ret = -EPERM; + goto err_out; + } + + if (!(file_inode(fp->filp)->i_mode & 0222)) { + rsp->hdr.Status.CifsError = STATUS_CANNOT_DELETE; + ret = -EPERM; + goto err_out; + } + + if (S_ISDIR(file_inode(fp->filp)->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) { + rsp->hdr.Status.CifsError = STATUS_DIRECTORY_NOT_EMPTY; + ret = -ENOTEMPTY; + goto err_out; + } + + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, + rsp->hdr.WordCount * 2 + 3); + +err_out: + ksmbd_fd_put(work, fp); + return ret; +} + +/** + * smb_set_time_fileinfo() - set file time method using trans2 + * using set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_time_fileinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct file_basic_info *info; + struct iattr attrs; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + + info = (struct file_basic_info *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + attrs.ia_valid = 0; + if (le64_to_cpu(info->LastAccessTime)) { + attrs.ia_atime = smb_NTtimeToUnix(info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + if (le64_to_cpu(info->ChangeTime)) { + attrs.ia_ctime = smb_NTtimeToUnix(info->ChangeTime); + attrs.ia_valid |= ATTR_CTIME; + } + + if (le64_to_cpu(info->LastWriteTime)) { + attrs.ia_mtime = smb_NTtimeToUnix(info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + /* TODO: check dos mode and acl bits if req->Attributes nonzero */ + + if (!attrs.ia_valid) + goto done; + + err = ksmbd_vfs_setattr(work, NULL, (u64)req->Fid, &attrs); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + +done: + ksmbd_debug(SMB, "fid %u, setattr done\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, + rsp->hdr.WordCount * 2 + 3); + + return 0; +} + +/** + * smb_fileinfo_rename() - rename method using trans2 set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_fileinfo_rename(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct set_file_rename *info; + struct ksmbd_file *fp; + char *newname; + int rc = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + info = (struct set_file_rename *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + if (info->overwrite) { + rc = ksmbd_vfs_truncate(work, fp, 0); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return rc; + } + } + + newname = smb_get_name(share, info->target_name, PATH_MAX, work, 0); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + ksmbd_fd_put(work, fp); + return PTR_ERR(newname); + } + + ksmbd_debug(SMB, "rename oldname(%s) -> newname(%s)\n", fp->filename, + newname); + rc = ksmbd_vfs_fp_rename(work, fp, newname); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + ksmbd_fd_put(work, fp); + kfree(newname); + return rc; +} + +/** + * set_file_info() - trans2 set file info command dispatcher + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int set_file_info(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + __u16 info_level, total_param; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + info_level = le16_to_cpu(req->InformationLevel); + total_param = le16_to_cpu(req->TotalParameterCount); + if (total_param < 4) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + pr_err("invalid total parameter for info_level 0x%x\n", + total_param); + return -EINVAL; + } + + switch (info_level) { + case SMB_SET_FILE_EA: + err = smb_set_ea(work); + break; + case SMB_SET_FILE_ALLOCATION_INFO2: + /* fall through */ + case SMB_SET_FILE_ALLOCATION_INFO: + err = smb_set_alloc_size(work); + break; + case SMB_SET_FILE_END_OF_FILE_INFO2: + /* fall through */ + case SMB_SET_FILE_END_OF_FILE_INFO: + err = smb_set_file_size_finfo(work); + break; + case SMB_SET_FILE_UNIX_BASIC: + err = smb_set_unix_fileinfo(work); + break; + case SMB_SET_FILE_DISPOSITION_INFO: + case SMB_SET_FILE_DISPOSITION_INFORMATION: + err = smb_set_dispostion(work); + break; + case SMB_SET_FILE_BASIC_INFO2: + /* fall through */ + case SMB_SET_FILE_BASIC_INFO: + err = smb_set_time_fileinfo(work); + break; + case SMB_SET_FILE_RENAME_INFORMATION: + err = smb_fileinfo_rename(work); + break; + default: + ksmbd_debug(SMB, "info level = %x not implemented yet\n", + info_level); + rsp->hdr.Status.CifsError = STATUS_NOT_IMPLEMENTED; + return -EOPNOTSUPP; + } + + if (err < 0) + ksmbd_debug(SMB, "info_level 0x%x failed, err %d\n", + info_level, err); + return err; +} + +/** + * create_dir() - trans2 create directory dispatcher + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int create_dir(struct ksmbd_work *work) +{ + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + mode_t mode = S_IALLUGO; + char *name; + int err; + + name = smb_get_name(share, work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4, + PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_mkdir(work, name, mode); + if (err) { + if (err == -EEXIST) { + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + ntstatus_to_dos(STATUS_OBJECT_NAME_COLLISION, + &rsp->hdr.Status.DosError.ErrorClass, + &rsp->hdr.Status.DosError.Error); + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + } else + rsp->hdr.Status.CifsError = STATUS_DATA_ERROR; + goto out; + } else + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + __u64 ctime; + struct path path; + struct xattr_dos_attrib da = {0}; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 1); + if (!err) { + ctime = ksmbd_UnixTimeToNT(current_time(d_inode(path.dentry))); + + da.version = 4; + da.attr = ATTR_DIRECTORY; + da.itime = da.create_time = ctime; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in EA\n"); + path_put(&path); + } + err = 0; + } + +out: + memset(&rsp->hdr.WordCount, 0, 3); + ksmbd_revert_fsids(work); + kfree(name); + return err; +} + +/** + * get_dfs_referral() - handler for smb dfs referral command + * @work: smb work containing get dfs referral command buffer + * + * Return: 0 on success, otherwise error + */ +static int get_dfs_referral(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + return 0; +} + +/** + * smb_trans2() - handler for trans2 commands + * @work: smb work containing trans2 command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_trans2(struct ksmbd_work *work) +{ + struct smb_com_trans2_req *req = work->request_buf; + struct smb_hdr *rsp_hdr = work->response_buf; + int err = 0; + u16 sub_command = le16_to_cpu(req->SubCommand); + + /* at least one setup word for TRANS2 command + * MS-CIFS, SMB COM TRANSACTION + */ + if (req->SetupCount < 1) { + pr_err("Wrong setup count in SMB_TRANS2 - indicates wrong request\n"); + rsp_hdr->Status.CifsError = STATUS_UNSUCCESSFUL; + return -EINVAL; + } + + switch (sub_command) { + case TRANS2_FIND_FIRST: + err = find_first(work); + break; + case TRANS2_FIND_NEXT: + err = find_next(work); + break; + case TRANS2_QUERY_FS_INFORMATION: + err = query_fs_info(work); + break; + case TRANS2_QUERY_PATH_INFORMATION: + err = query_path_info(work); + break; + case TRANS2_SET_PATH_INFORMATION: + err = set_path_info(work); + break; + case TRANS2_SET_FS_INFORMATION: + err = set_fs_info(work); + break; + case TRANS2_QUERY_FILE_INFORMATION: + err = query_file_info(work); + break; + case TRANS2_SET_FILE_INFORMATION: + err = set_file_info(work); + break; + case TRANS2_CREATE_DIRECTORY: + err = create_dir(work); + break; + case TRANS2_GET_DFS_REFERRAL: + err = get_dfs_referral(work); + break; + default: + ksmbd_debug(SMB, "sub command 0x%x not implemented yet\n", + sub_command); + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + return -EINVAL; + } + + if (err) { + ksmbd_debug(SMB, "%s failed with error %d\n", __func__, err); + if (err == -EBUSY) + rsp_hdr->Status.CifsError = STATUS_DELETE_PENDING; + return err; + } + + return 0; +} + +/** + * smb_mkdir() - handler for smb mkdir + * @work: smb work containing creat directory command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_mkdir(struct ksmbd_work *work) +{ + struct smb_com_create_directory_req *req = work->request_buf; + struct smb_com_create_directory_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + mode_t mode = S_IALLUGO; + char *name; + int err; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->DirName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_mkdir(work, name, mode); + if (err) { + if (err == -EEXIST) { + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = + cpu_to_le16(ERRnoaccess); + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + } else + rsp->hdr.Status.CifsError = STATUS_DATA_ERROR; + goto out; + } else { + /* mkdir success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + __u64 ctime; + struct path path; + struct xattr_dos_attrib da = {0}; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 1); + if (!err) { + ctime = ksmbd_UnixTimeToNT(current_time(d_inode(path.dentry))); + + da.version = 4; + da.attr = ATTR_DIRECTORY; + da.itime = da.create_time = ctime; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in xattr\n"); + path_put(&path); + } + err = 0; + } + +out: + ksmbd_revert_fsids(work); + kfree(name); + return err; +} + +/** + * smb_checkdir() - handler to verify whether a specified + * path resolves to a valid directory or not + * + * @work: smb work containing creat directory command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_checkdir(struct ksmbd_work *work) +{ + struct smb_com_check_directory_req *req = work->request_buf; + struct smb_com_check_directory_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + char *name, *last; + int err; + bool caseless_lookup = req->hdr.Flags & SMBFLG_CASELESS; + + name = smb_get_name(share, req->DirName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, + caseless_lookup); + if (err) { + if (err == -ENOENT) { + /* + * If the parent directory is valid but not the + * last component - then returns + * STATUS_OBJECT_NAME_NOT_FOUND + * for that case and STATUS_OBJECT_PATH_NOT_FOUND + * if the path is invalid. + */ + last = strrchr(name, '/'); + if (last && last[1] != '\0') { + *last = '\0'; + last++; + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_FOLLOW | + LOOKUP_DIRECTORY, &path, + caseless_lookup); + } else { + ksmbd_debug(SMB, "can't lookup parent %s\n", + name); + err = -ENOENT; + } + } + if (err) { + ksmbd_debug(SMB, "look up failed err %d\n", err); + switch (err) { + case -ENOENT: + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + break; + case -ENOMEM: + rsp->hdr.Status.CifsError = + STATUS_INSUFFICIENT_RESOURCES; + break; + case -EACCES: + rsp->hdr.Status.CifsError = + STATUS_ACCESS_DENIED; + break; + case -EIO: + rsp->hdr.Status.CifsError = + STATUS_DATA_ERROR; + break; + default: + rsp->hdr.Status.CifsError = + STATUS_OBJECT_PATH_SYNTAX_BAD; + break; + } + kfree(name); + return err; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + + if (!S_ISDIR(stat.mode)) { + rsp->hdr.Status.CifsError = STATUS_NOT_A_DIRECTORY; + } else { + /* checkdir success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + path_put(&path); + kfree(name); + return err; +} + +/** + * smb_process_exit() - handler for smb process exit + * @work: smb work containing process exit command buffer + * + * Return: 0 on success always + * This command is obsolete now. Starting with the LAN Manager 1.0 dialect, + * FIDs are no longer associated with PIDs.CIFS clients SHOULD NOT send + * SMB_COM_PROCESS_EXIT requests. Instead, CIFS clients SHOULD perform all + * process cleanup operations, sending individual file close operations + * as needed.Here it is implemented very minimally for sake + * of passing smbtorture testcases. + */ +int smb_process_exit(struct ksmbd_work *work) +{ + struct smb_com_process_exit_rsp *rsp = work->response_buf; + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + return 0; +} + +/** + * smb_rmdir() - handler for smb rmdir + * @work: smb work containing delete directory command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_rmdir(struct ksmbd_work *work) +{ + struct smb_com_delete_directory_req *req = work->request_buf; + struct smb_com_delete_directory_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name; + int err; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->DirName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + err = ksmbd_vfs_remove_file(work, name); + if (err) { + if (err == -ENOTEMPTY) + rsp->hdr.Status.CifsError = + STATUS_DIRECTORY_NOT_EMPTY; + else if (err == -ENOENT) + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + else + rsp->hdr.Status.CifsError = STATUS_DATA_ERROR; + } else { + /* rmdir success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + kfree(name); + return err; +} + +/** + * smb_unlink() - handler for smb delete file + * @work: smb work containing delete file command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_unlink(struct ksmbd_work *work) +{ + struct smb_com_delete_file_req *req = work->request_buf; + struct smb_com_delete_file_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name; + int err; + struct ksmbd_file *fp; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->fileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + fp = ksmbd_lookup_fd_filename(work, name); + if (fp) + err = -ESHARE; + else + err = ksmbd_vfs_remove_file(work, name); + + if (err) { + if (err == -EISDIR) + rsp->hdr.Status.CifsError = + STATUS_FILE_IS_A_DIRECTORY; + else if (err == -ESHARE) + rsp->hdr.Status.CifsError = STATUS_SHARING_VIOLATION; + else if (err == -EACCES || err == -EXDEV) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + } else { + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + ksmbd_fd_put(work, fp); + kfree(name); + return err; +} + +/** + * smb_nt_cancel() - handler for smb cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 + */ +int smb_nt_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *work_hdr; + struct ksmbd_work *new_work; + + ksmbd_debug(SMB, "smb cancel called on mid %u\n", hdr->Mid); + + spin_lock(&conn->request_lock); + list_for_each_entry(new_work, &conn->requests, request_entry) { + work_hdr = (struct smb_hdr *)new_work->request_buf; + if (work_hdr->Mid == hdr->Mid) { + ksmbd_debug(SMB, "smb with mid %u cancelled command = 0x%x\n", + hdr->Mid, work_hdr->Command); + new_work->send_no_response = 1; + list_del_init(&new_work->request_entry); + new_work->sess->sequence_number--; + break; + } + } + spin_unlock(&conn->request_lock); + + /* For SMB_COM_NT_CANCEL command itself send no response */ + work->send_no_response = 1; + return 0; +} + +/** + * smb_nt_rename() - handler for smb rename command + * @work: smb work containing nt rename command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_nt_rename(struct ksmbd_work *work) +{ + struct smb_com_nt_rename_req *req = work->request_buf; + struct smb_com_rename_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *oldname, *newname; + int oldname_len, err; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + if (le16_to_cpu(req->Flags) != CREATE_HARD_LINK) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + oldname = smb_get_name(share, req->OldFileName, PATH_MAX, work, false); + if (IS_ERR(oldname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(oldname); + } + + if (is_smbreq_unicode(&req->hdr)) + oldname_len = smb1_utf16_name_length((__le16 *)req->OldFileName, + PATH_MAX); + else { + oldname_len = strlen(oldname); + oldname_len++; + } + + newname = smb_get_name(share, &req->OldFileName[oldname_len + 2], + PATH_MAX, work, false); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + kfree(oldname); + return PTR_ERR(newname); + } + ksmbd_debug(SMB, "oldname %s, newname %s, oldname_len %d, unicode %d\n", + oldname, newname, oldname_len, + is_smbreq_unicode(&req->hdr)); + + err = ksmbd_vfs_link(work, oldname, newname); + if (err == -EACCES) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else if (err < 0) + rsp->hdr.Status.CifsError = STATUS_NOT_SAME_DEVICE; + + kfree(newname); + kfree(oldname); + return err; +} + +static __le32 smb_query_info_pipe(struct ksmbd_share_config *share, + struct kstat *st) +{ + st->mode = S_IFDIR; + return 0; +} + +static __le32 smb_query_info_path(struct ksmbd_work *work, struct kstat *st) +{ + struct smb_com_query_information_req *req = work->request_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + char *name; + __le32 err = 0; + int ret; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) + return STATUS_OBJECT_NAME_INVALID; + + if (ksmbd_override_fsids(work)) { + kfree(name); + return STATUS_NO_MEMORY; + } + + ret = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 0); + if (ret) { + pr_err("look up failed err %d\n", ret); + + if (d_is_symlink(path.dentry)) { + err = STATUS_ACCESS_DENIED; + goto out; + } + err = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), st); +#else + generic_fillattr(d_inode(path.dentry), st); +#endif + path_put(&path); +out: + ksmbd_revert_fsids(work); + kfree(name); + return err; +} + +/** + * smb_query_info() - handler for query information command + * @work: smb work containing query info command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_query_info(struct ksmbd_work *work) +{ + struct smb_com_query_information_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct kstat st = {0,}; + __u16 attr = 0; + int i; + __le32 err; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) + err = smb_query_info_path(work, &st); + else + err = smb_query_info_pipe(share, &st); + + if (le32_to_cpu(err) != 0) { + rsp->hdr.Status.CifsError = err; + return -EINVAL; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + + if (st.mode & S_ISVTX) + attr |= (ATTR_HIDDEN | ATTR_SYSTEM); + if (!(st.mode & 0222)) + attr |= ATTR_READONLY; + if (S_ISDIR(st.mode)) + attr |= ATTR_DIRECTORY; + + rsp->attr = cpu_to_le16(attr); + rsp->last_write_time = cpu_to_le32(st.mtime.tv_sec); + rsp->size = cpu_to_le32((u32)st.size); + for (i = 0; i < 5; i++) + rsp->reserved[i] = 0; + + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2); + return 0; +} + +/** + * smb_closedir() - handler closing dir handle, opened for readdir + * @work: smb work containing find close command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_closedir(struct ksmbd_work *work) +{ + struct smb_com_findclose_req *req = work->request_buf; + struct smb_com_close_rsp *rsp = work->response_buf; + int err; + + ksmbd_debug(SMB, "SMB_COM_FIND_CLOSE2 called for fid %u\n", + req->FileID); + + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + + err = ksmbd_close_fd(work, req->FileID); + if (!err) + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + else + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * convert_open_flags() - convert smb open flags to file open flags + * @file_present: is file already present + * @mode: smp file open mode + * @disposition: smp file disposition information + * + * Return: converted file open flags + */ +static int convert_open_flags(bool file_present, + __u16 mode, __u16 dispostion, + int *may_flags) +{ + int oflags = 0; + + switch (mode & 0x0007) { + case SMBOPEN_READ: + oflags |= O_RDONLY; + break; + case SMBOPEN_WRITE: + oflags |= O_WRONLY; + break; + case SMBOPEN_READWRITE: + oflags |= O_RDWR; + break; + default: + oflags |= O_RDONLY; + break; + } + + if (mode & SMBOPEN_WRITE_THROUGH) + oflags |= O_SYNC; + + if (file_present) { + switch (dispostion & 0x0003) { + case SMBOPEN_DISPOSITION_NONE: + return -EEXIST; + case SMBOPEN_OAPPEND: + oflags |= O_APPEND; + break; + case SMBOPEN_OTRUNC: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (dispostion & 0x0010) { + case SMBOPEN_DISPOSITION_NONE: + return -EINVAL; + case SMBOPEN_OCREATE: + oflags |= O_CREAT; + break; + default: + break; + } + } + + *may_flags = ksmbd_openflags_to_mayflags(oflags); + + return oflags; +} + +/** + * smb_open_andx() - smb andx open method handler + * @work: smb work containing buffer for andx open command buffer + * + * Return: error if there is error while processing current command, + * otherwise pointer to next andx command in the chain + */ +int smb_open_andx(struct ksmbd_work *work) +{ + struct smb_com_openx_req *req = work->request_buf; + struct smb_com_openx_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + int oplock_flags, file_info, open_flags, may_flags; + char *name; + bool file_present = true; + umode_t mode = 0; + int err; + struct ksmbd_file *fp = NULL; + int oplock_rsp = OPLOCK_NONE, share_ret; + + rsp->hdr.Status.CifsError = STATUS_UNSUCCESSFUL; + + /* check for sharing mode flag */ + if ((le16_to_cpu(req->Mode) & SMBOPEN_SHARING_MODE) > + SMBOPEN_DENY_NONE) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = cpu_to_le16(ERRbadaccess); + rsp->hdr.Flags2 &= ~SMBFLG2_ERR_STATUS; + + memset(&rsp->hdr.WordCount, 0, 3); + return -EINVAL; + } + + if (is_smbreq_unicode(&req->hdr)) + name = smb_get_name(share, req->fileName + 1, PATH_MAX, + work, false); + else + name = smb_get_name(share, req->fileName, PATH_MAX, + work, false); + + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, + req->hdr.Flags & SMBFLG_CASELESS); + if (err) { + if (err == -EACCES || err == -EXDEV) + goto out; + file_present = false; + } else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + + oplock_flags = le16_to_cpu(req->OpenFlags) & + (REQ_OPLOCK | REQ_BATCHOPLOCK); + + open_flags = convert_open_flags(file_present, + le16_to_cpu(req->Mode), + le16_to_cpu(req->OpenFunction), + &may_flags); + if (open_flags < 0) { + ksmbd_debug(SMB, "create_dispostion returned %d\n", open_flags); + if (file_present) + goto free_path; + else { + err = -ENOENT; + goto out; + } + } + + if (file_present && !(stat.mode & 0222)) { + if ((open_flags & O_ACCMODE) == O_WRONLY || + (open_flags & O_ACCMODE) == O_RDWR) { + ksmbd_debug(SMB, "readonly file(%s)\n", name); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + memset(&rsp->hdr.WordCount, 0, 3); + goto free_path; + } + } + + if (!file_present && (open_flags & O_CREAT)) { + mode |= 0777; + if (le16_to_cpu(req->FileAttributes) & ATTR_READONLY) + mode &= ~0222; + + mode |= S_IFREG; + err = ksmbd_vfs_create(work, name, mode); + if (err) + goto out; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + } else if (file_present) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + may_flags); +#else + err = inode_permission(d_inode(path.dentry), + may_flags); +#endif + if (err) + goto free_path; + } + + err = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (err == KSMBD_INODE_STATUS_PENDING_DELETE) { + err = -EBUSY; + goto free_path; + } + + err = 0; + ksmbd_debug(SMB, "(%s) open_flags = 0x%x, oplock_flags 0x%x\n", + name, open_flags, oplock_flags); + /* open file and get FID */ + fp = ksmbd_vfs_dentry_open(work, &path, open_flags, + 0, file_present); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + fp = NULL; + goto free_path; + } + fp->filename = name; + fp->pid = le16_to_cpu(req->hdr.Pid); + + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (smb1_oplock_enable && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS) && + !S_ISDIR(file_inode(fp->filp)->i_mode) && + oplock_flags) { + /* Client cannot request levelII oplock directly */ + err = smb_grant_oplock(work, oplock_flags, fp->volatile_id, + fp, le16_to_cpu(req->hdr.Tid), NULL, 0); + if (err) + goto free_path; + } else { + if (ksmbd_inode_pending_delete(fp)) { + err = -EBUSY; + goto free_path; + } + + if (share_ret < 0) { + err = -EPERM; + goto free_path; + } + } + + oplock_rsp = fp->f_opinfo != NULL ? fp->f_opinfo->level : 0; + + /* open success, send back response */ + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = F_OPENED; + else + file_info = F_OVERWRITTEN; + } else + file_info = F_CREATED; + + if (oplock_rsp) + file_info |= SMBOPEN_LOCK_GRANTED; + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (file_present) { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + err = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err > 0) { + fp->create_time = da.create_time; + fp->itime = da.itime; + } + err = 0; + } + } else { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.attr = ATTR_NORMAL; + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in xattr\n"); + err = 0; + } + } + + /* prepare response buffer */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0x0F; + rsp->Fid = fp->volatile_id; + rsp->FileAttributes = cpu_to_le16(ATTR_NORMAL); + rsp->LastWriteTime = cpu_to_le32(stat.mtime.tv_sec); + rsp->EndOfFile = cpu_to_le32(stat.size); + switch (open_flags & O_ACCMODE) { + case O_RDONLY: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_READ); + break; + case O_WRONLY: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_WRITE); + break; + case O_RDWR: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_READ_WRITE); + break; + default: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_READ); + break; + } + + rsp->FileType = 0; + rsp->IPCState = 0; + rsp->Action = cpu_to_le16(file_info); + rsp->Reserved = 0; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2); + +free_path: + path_put(&path); +out: + ksmbd_revert_fsids(work); + if (err) { + if (err == -ENOSPC) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else if (err == -EMFILE) + rsp->hdr.Status.CifsError = + STATUS_TOO_MANY_OPENED_FILES; + else if (err == -EBUSY) + rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + else if (err == -ENOENT) + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + else if (err == -EACCES || err == -EXDEV) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + if (fp) + ksmbd_close_fd(work, fp->volatile_id); + else + kfree(name); + } + + if (!rsp->hdr.WordCount) + return err; + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return err; +} + +/** + * smb_setattr() - set file attributes + * @work: smb work containing setattr command + * + * Return: 0 on success, otherwise error + */ +int smb_setattr(struct ksmbd_work *work) +{ + struct smb_com_setattr_req *req = work->request_buf; + struct smb_com_setattr_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + struct iattr attrs; + int err = 0; + char *name; + __u16 dos_attr; + + name = smb_get_name(share, req->fileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, + req->hdr.Flags & SMBFLG_CASELESS); + if (err) { + ksmbd_debug(SMB, "look up failed err %d\n", err); + rsp->hdr.Status.CifsError = STATUS_OBJECT_NAME_NOT_FOUND; + err = 0; + goto out; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + path_put(&path); + attrs.ia_valid = 0; + attrs.ia_mode = 0; + + dos_attr = le16_to_cpu(req->attr); + if (!dos_attr) + attrs.ia_mode = stat.mode | 0200; + + if (dos_attr & ATTR_READONLY) + attrs.ia_mode = stat.mode & ~0222; + + if (attrs.ia_mode) + attrs.ia_valid |= ATTR_MODE; + + attrs.ia_mtime.tv_sec = le32_to_cpu(req->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + + err = ksmbd_vfs_setattr(work, name, 0, &attrs); + if (err) + goto out; + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + +out: + kfree(name); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + + return 0; +} + +/** + * smb1_is_sign_req() - handler for checking packet signing status + * @work: smb work containing notify command buffer + * + * Return: true if packed is signed, false otherwise + */ +bool smb1_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ +#if 0 + struct smb_hdr *rcv_hdr1 = (struct smb_hdr *)work->request_buf; + + /* + * FIXME: signed tree connect failed by signing error + * with windows XP client. For now, Force to turn off + * signing feature in SMB1. + */ + if ((rcv_hdr1->Flags2 & SMBFLG2_SECURITY_SIGNATURE) && + command != SMB_COM_SESSION_SETUP_ANDX) + return true; + return false; +#else + return false; +#endif +} + +/** + * smb1_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb1_check_sign_req(struct ksmbd_work *work) +{ + struct smb_hdr *rcv_hdr1 = (struct smb_hdr *)work->request_buf; + char signature_req[CIFS_SMB1_SIGNATURE_SIZE]; + char signature[20]; + struct kvec iov[1]; + + memcpy(signature_req, rcv_hdr1->Signature.SecuritySignature, + CIFS_SMB1_SIGNATURE_SIZE); + rcv_hdr1->Signature.Sequence.SequenceNumber = + cpu_to_le32(++work->sess->sequence_number); + rcv_hdr1->Signature.Sequence.Reserved = 0; + + iov[0].iov_base = rcv_hdr1->Protocol; + iov[0].iov_len = be32_to_cpu(rcv_hdr1->smb_buf_length); + + if (ksmbd_sign_smb1_pdu(work->sess, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, CIFS_SMB1_SIGNATURE_SIZE)) { + ksmbd_debug(SMB, "bad smb1 sign\n"); + return 0; + } + + return 1; +} + +/** + * smb1_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb1_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + char signature[20]; + struct kvec iov[2]; + int n_vec = 1; + + rsp_hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + rsp_hdr->Signature.Sequence.SequenceNumber = + cpu_to_le32(++work->sess->sequence_number); + rsp_hdr->Signature.Sequence.Reserved = 0; + + iov[0].iov_base = rsp_hdr->Protocol; + iov[0].iov_len = be32_to_cpu(rsp_hdr->smb_buf_length); + + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (ksmbd_sign_smb1_pdu(work->sess, iov, n_vec, signature)) + memset(rsp_hdr->Signature.SecuritySignature, + 0, CIFS_SMB1_SIGNATURE_SIZE); + else + memcpy(rsp_hdr->Signature.SecuritySignature, + signature, CIFS_SMB1_SIGNATURE_SIZE); +} diff -Naur --no-dereference a/fs/ksmbd/smb1pdu.h b/fs/ksmbd/smb1pdu.h --- a/fs/ksmbd/smb1pdu.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb1pdu.h 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,1600 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB1PDU_H +#define __SMB1PDU_H + +#define MAX_CIFS_HDR_SIZE 0x58 + +#define SMB1_CLIENT_GUID_SIZE (16) +#define SMB1_MAX_MPX_COUNT 10 +#define SMB1_MAX_VCS 1 +#define SMB1_MAX_RAW_SIZE 65536 +#define MAX_CIFS_LOOKUP_BUFFER_SIZE (16*1024) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define SMB1_SERVER_CAPS \ + (CAP_UNICODE | CAP_LARGE_FILES | CAP_EXTENDED_SECURITY |\ + CAP_NT_SMBS | CAP_STATUS32 | CAP_LOCK_AND_READ | \ + CAP_NT_FIND | CAP_UNIX | CAP_LARGE_READ_X | \ + CAP_LARGE_WRITE_X | CAP_LEVEL_II_OPLOCKS) + +#define SMB1_SERVER_SECU (SECMODE_USER | SECMODE_PW_ENCRYPT) + +/* Service Type of TreeConnect*/ +#define SERVICE_DISK_SHARE "A:" +#define SERVICE_IPC_SHARE "IPC" +#define SERVICE_PRINTER_SHARE "LPT1:" +#define SERVICE_COMM "COMM" + +#define NATIVE_FILE_SYSTEM "NTFS" + +#define SMB_NO_MORE_ANDX_COMMAND 0xFF +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) + +/* Transact2 subcommand codes */ +#define TRANS2_OPEN 0x00 +#define TRANS2_FIND_FIRST 0x01 +#define TRANS2_FIND_NEXT 0x02 +#define TRANS2_QUERY_FS_INFORMATION 0x03 +#define TRANS2_SET_FS_INFORMATION 0x04 +#define TRANS2_QUERY_PATH_INFORMATION 0x05 +#define TRANS2_SET_PATH_INFORMATION 0x06 +#define TRANS2_QUERY_FILE_INFORMATION 0x07 +#define TRANS2_SET_FILE_INFORMATION 0x08 +#define TRANS2_CREATE_DIRECTORY 0x0d +#define TRANS2_GET_DFS_REFERRAL 0x10 +#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 + +/* SMB Transact (Named Pipe) subcommand codes */ +#define TRANS_SET_NMPIPE_STATE 0x0001 +#define TRANS_RAW_READ_NMPIPE 0x0011 +#define TRANS_QUERY_NMPIPE_STATE 0x0021 +#define TRANS_QUERY_NMPIPE_INFO 0x0022 +#define TRANS_PEEK_NMPIPE 0x0023 +#define TRANS_TRANSACT_NMPIPE 0x0026 +#define TRANS_RAW_WRITE_NMPIPE 0x0031 +#define TRANS_READ_NMPIPE 0x0036 +#define TRANS_WRITE_NMPIPE 0x0037 +#define TRANS_WAIT_NMPIPE 0x0053 +#define TRANS_CALL_NMPIPE 0x0054 + +/* NT Transact subcommand codes */ +#define NT_TRANSACT_CREATE 0x01 +#define NT_TRANSACT_IOCTL 0x02 +#define NT_TRANSACT_SET_SECURITY_DESC 0x03 +#define NT_TRANSACT_NOTIFY_CHANGE 0x04 +#define NT_TRANSACT_RENAME 0x05 +#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 +#define NT_TRANSACT_GET_USER_QUOTA 0x07 +#define NT_TRANSACT_SET_USER_QUOTA 0x08 + +/* + * SMB flag definitions + */ +#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ +#define SMBFLG_RCV_POSTED 0x02 /* obsolete */ +#define SMBFLG_RSVD 0x04 +#define SMBFLG_CASELESS 0x08 /* + * all pathnames treated as caseless (off + * implies case sensitive file handling + * request) + */ +#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ +#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ +#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +/* + * SMB flag2 definitions + */ +#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* + * can send long (non-8.3) + * path names in response + */ +#define SMBFLG2_KNOWS_EAS cpu_to_le16(2) +#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) +#define SMBFLG2_COMPRESSED (8) +#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_REPARSE_PATH (0x400) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_DFS cpu_to_le16(0x1000) +#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ +#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ +#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ +#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ +#define SMB_COM_DELETE 0x06 /* trivial response */ +#define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ +#define SMB_COM_SETATTR 0x09 /* trivial response */ +#define SMB_COM_WRITE 0x0b +#define SMB_COM_CHECK_DIRECTORY 0x10 /* trivial response */ +#define SMB_COM_PROCESS_EXIT 0x11 /* trivial response */ +#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ +#define SMB_COM_TRANSACTION 0x25 +#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_ECHO 0x2B /* echo request */ +#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ +#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 /* no response */ +#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ + +/* Negotiate response Capabilities */ +#define CAP_RAW_MODE 0x00000001 +#define CAP_MPX_MODE 0x00000002 +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ +#define CAP_RPC_REMOTE_APIS 0x00000020 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_LOCK_AND_READ 0x00000100 +#define CAP_NT_FIND 0x00000200 +#define CAP_DFS 0x00001000 +#define CAP_INFOLEVEL_PASSTHRU 0x00002000 +#define CAP_LARGE_READ_X 0x00004000 +#define CAP_LARGE_WRITE_X 0x00008000 +#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ +#define CAP_UNIX 0x00800000 +#define CAP_COMPRESSED_DATA 0x02000000 +#define CAP_DYNAMIC_REAUTH 0x20000000 +#define CAP_PERSISTENT_HANDLES 0x40000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + +/* RFC 1002 session packet types */ +#define RFC1002_SESSION_MESSAGE 0x00 +#define RFC1002_SESSION_REQUEST 0x81 +#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 +#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 +#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 +#define RFC1002_SESSION_KEEP_ALIVE 0x85 + +/* Action bits */ +#define GUEST_LOGIN 1 + +struct smb_com_read_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 MaxCountHigh; + __le16 Remaining; + __le32 OffsetHigh; + __le16 ByteCount; +} __packed; + +struct smb_com_read_rsp { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Remaining; + __le16 DataCompactionMode; + __le16 Reserved; + __le16 DataLength; + __le16 DataOffset; + __le16 DataLengthHigh; + __u64 Reserved2; + __le16 ByteCount; + /* read response data immediately follows */ +} __packed; + +struct smb_com_write_req { + struct smb_hdr hdr; /* wct = 14 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; + __le16 WriteMode; + __le16 Remaining; + __le16 DataLengthHigh; + __le16 DataLengthLow; + __le16 DataOffset; + __le32 OffsetHigh; + __le16 ByteCount; + __u8 Pad; /* + * BB check for whether padded to DWORD + * boundary and optimum performance here + */ + char Data[0]; +} __packed; + +struct smb_com_write_req_32bit { + struct smb_hdr hdr; /* wct = 5 */ + __u16 Fid; + __le16 Length; + __le32 Offset; + __u16 Estimate; + __le16 ByteCount; /* must be greater than 2 */ + __u8 BufferFormat; + __u16 DataLength; + char Data[0]; +} __packed; + +struct smb_com_write_rsp_32bit { + struct smb_hdr hdr; /* wct = 1 */ + __le16 Written; + __le16 ByteCount; /* must be 0 */ +} __packed; + +struct smb_com_write_rsp { + struct smb_hdr hdr; /* wct = 6 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Count; + __le16 Remaining; + __le16 CountHigh; + __u16 Reserved; + __le16 ByteCount; +} __packed; + +struct smb_com_rename_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __packed; + +struct smb_com_rename_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +/* SecurityMode bits */ +#define SECMODE_USER 0x01 /* off indicates share level security */ +#define SECMODE_PW_ENCRYPT 0x02 +#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ +#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ + +struct smb_com_session_setup_req { /* request format */ + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 SecurityBlobLength; + __u32 Reserved; + __le32 Capabilities; /* see below */ + __le16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ +} __packed; /* NTLM request format (with extended security) */ + +struct smb_com_session_setup_req_no_secext { /* request format */ + struct smb_hdr hdr; /* we will handle this :: wct = 13 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 CaseInsensitivePasswordLength; /* ASCII password len */ + __le16 CaseSensitivePasswordLength; /* Unicode password length*/ + __u32 Reserved; /* see below */ + __le32 Capabilities; + __le16 ByteCount; + unsigned char CaseInsensitivePassword[0]; /* followed by: */ + /* unsigned char * CaseSensitivePassword; */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ +} __packed; /* NTLM request format (without extended security */ + +struct smb_com_session_setup_resp { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 SecurityBlobLength; + __le16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ + /* unsigned char * NativeOS; */ + /* unsigned char * NativeLanMan; */ + /* unsigned char * PrimaryDomain; */ +} __packed; /* NTLM response (with or without extended sec) */ + +struct smb_com_session_setup_old_resp { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 3 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 ByteCount; + unsigned char NativeOS[1]; /* followed by */ + /* unsigned char * NativeLanMan; */ + /* unsigned char * PrimaryDomain; */ +} __packed; /* pre-NTLM (LANMAN2.1) response */ + +union smb_com_session_setup_andx { + struct smb_com_session_setup_req req; + struct smb_com_session_setup_req_no_secext req_no_secext; + struct smb_com_session_setup_resp resp; + struct smb_com_session_setup_old_resp old_resp; +} __packed; + +struct smb_com_tconx_req { + __u8 WordCount; /* wct = 4, it could be ANDX */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Flags; /* see below */ + __le16 PasswordLength; + __le16 ByteCount; + unsigned char Password[1]; /* followed by */ + /* STRING Path *//* \\server\share name */ + /* STRING Service */ +} __packed; + +struct smb_com_tconx_rsp { + __u8 WordCount; /* wct = 3 , not extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __packed; + +struct smb_com_tconx_rsp_ext { + __u8 WordCount; /* wct = 7, extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le32 MaximalShareAccessRights; + __le32 GuestMaximalShareAccessRights; + __le16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __packed; + +struct andx_block { + __u8 WordCount; + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; +} __packed; + +struct locking_andx_range64 { + __le16 Pid; + __le16 Pad; + __le32 OffsetHigh; + __le32 OffsetLow; + __le32 LengthHigh; + __le32 LengthLow; +} __packed; + +struct locking_andx_range32 { + __le16 Pid; + __le32 Offset; + __le32 Length; +} __packed; + +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ + +struct smb_com_lock_req { + struct smb_hdr hdr; /* wct = 8 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __u8 LockType; + __u8 OplockLevel; + __le32 Timeout; + __le16 NumberOfUnlocks; + __le16 NumberOfLocks; + __le16 ByteCount; + char *Locks[1]; +} __packed; + +struct smb_com_lock_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 ByteCount; +} __packed; + +/* tree connect Flags */ +#define DISCONNECT_TID 0x0001 +#define TCON_EXTENDED_SIGNATURES 0x0004 +#define TCON_EXTENDED_SECINFO 0x0008 + +/* OptionalSupport bits */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* + * "must have" directory search bits + * (exclusive searches supported) + */ +#define SMB_SHARE_IS_IN_DFS 0x0002 +#define SMB_CSC_MASK 0x000C +/* CSC flags defined as follows */ +#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 +#define SMB_CSC_CACHE_AUTO_REINT 0x0004 +#define SMB_CSC_CACHE_VDO 0x0008 +#define SMB_CSC_NO_CACHING 0x000C +#define SMB_UNIQUE_FILE_NAME 0x0010 +#define SMB_EXTENDED_SIGNATURES 0x0020 + +/* OpenFlags */ +#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ +#define REQ_OPLOCK 0x00000002 +#define REQ_BATCHOPLOCK 0x00000004 +#define REQ_OPENDIRONLY 0x00000008 +#define REQ_EXTENDED_INFO 0x00000010 + +/* File type */ +#define DISK_TYPE 0x0000 +#define BYTE_PIPE_TYPE 0x0001 +#define MESSAGE_PIPE_TYPE 0x0002 +#define PRINTER_TYPE 0x0003 +#define COMM_DEV_TYPE 0x0004 +#define UNKNOWN_TYPE 0xFFFF + +/* Device Type or File Status Flags */ +#define NO_EAS 0x0001 +#define NO_SUBSTREAMS 0x0002 +#define NO_REPARSETAG 0x0004 +/* following flags can apply if pipe */ +#define ICOUNT_MASK 0x00FF +#define PIPE_READ_MODE 0x0100 +#define NAMED_PIPE_TYPE 0x0400 +#define PIPE_END_POINT 0x4000 +#define BLOCKING_NAMED_PIPE 0x8000 + +/* ShareAccess flags */ +#define FILE_NO_SHARE 0x00000000 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_SHARE_ALL 0x00000007 + +/* CreateDisposition flags, similar to CreateAction as well */ +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 + +/* ImpersonationLevel flags */ +#define SECURITY_ANONYMOUS 0 +#define SECURITY_IDENTIFICATION 1 +#define SECURITY_IMPERSONATION 2 +#define SECURITY_DELEGATION 3 + +/* SecurityFlags */ +#define SECURITY_CONTEXT_TRACKING 0x01 +#define SECURITY_EFFECTIVE_ONLY 0x02 + +struct smb_com_open_req { /* also handles create */ + struct smb_hdr hdr; /* wct = 24 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 Reserved; /* Must Be Zero */ + __le16 NameLength; + __le32 OpenFlags; + __u32 RootDirectoryFid; + __le32 DesiredAccess; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le32 ImpersonationLevel; + __u8 SecurityFlags; + __le16 ByteCount; + char fileName[1]; +} __packed; + +/* open response for CreateAction shifted left */ +#define CIFS_CREATE_ACTION 0x20000 /* file created */ + +/* Basic file attributes */ +#define SMB_FILE_ATTRIBUTE_NORMAL 0x0000 +#define SMB_FILE_ATTRIBUTE_READONLY 0x0001 +#define SMB_FILE_ATTRIBUTE_HIDDEN 0x0002 +#define SMB_FILE_ATTRIBUTE_SYSTEM 0x0004 +#define SMB_FILE_ATTRIBUTE_VOLUME 0x0008 +#define SMB_FILE_ATTRIBUTE_DIRECTORY 0x0010 +#define SMB_FILE_ATTRIBUTE_ARCHIVE 0x0020 +#define SMB_SEARCH_ATTRIBUTE_READONLY 0x0100 +#define SMB_SEARCH_ATTRIBUTE_HIDDEN 0x0200 +#define SMB_SEARCH_ATTRIBUTE_SYSTEM 0x0400 +#define SMB_SEARCH_ATTRIBUTE_DIRECTORY 0x1000 +#define SMB_SEARCH_ATTRIBUTE_ARCHIVE 0x2000 + +struct smb_com_open_rsp { + struct smb_hdr hdr; /* wct = 34 BB */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_open_ext_rsp { + struct smb_hdr hdr; /* wct = 42 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u8 VolId[16]; + __u64 fid; + __le32 MaxAccess; + __le32 GuestAccess; + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_close_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 FileID; + __le32 LastWriteTime; /* should be zero or -1 */ + __le16 ByteCount; /* 0 */ +} __packed; + +struct smb_com_close_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_echo_req { + struct smb_hdr hdr; + __le16 EchoCount; + __le16 ByteCount; + char Data[1]; +} __packed; + +struct smb_com_echo_rsp { + struct smb_hdr hdr; + __le16 SequenceNumber; + __le16 ByteCount; + char Data[1]; +} __packed; + +struct smb_com_flush_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __le16 ByteCount; /* 0 */ +} __packed; + +struct smb_com_flush_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +/* SMB_COM_TRANSACTION */ +struct smb_com_trans_req { + struct smb_hdr hdr; + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; + __u8 Pad; + __u8 Data[1]; +} __packed; + +struct smb_com_trans_pipe_req { + struct smb_hdr hdr; + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __u16 SubCommand; + __u16 fid; + __le16 ByteCount; + __u8 Pad; + __u8 Data[1]; +} __packed; + +struct smb_com_trans_rsp { + struct smb_hdr hdr; /* wct = 10+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + __le16 ByteCount; + __u8 Pad; +} __packed; + +/* SMB_COM_TRANSACTION subcommands */ + +#define TRANSACT_DCERPCCMD 0x26 + +/***************************************************************************** + * TRANS2 command implementation functions + *****************************************************************************/ +#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL + +/* QFSInfo Levels */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 +#define SMB_QUERY_POSIX_FS_INFO 0x201 +#define SMB_QUERY_POSIX_WHO_AM_I 0x202 +#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 +#define SMB_QUERY_FS_PROXY 0x204 /* + * WAFS enabled. Returns structure + * FILE_SYSTEM__UNIX_INFO to tell + * whether new NTIOCTL available + * (0xACE) for WAN friendly SMB + * operations to be carried + */ +#define SMB_QUERY_LABEL_INFO 0x3ea +#define SMB_QUERY_FS_QUOTA_INFO 0x3ee +#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef +#define SMB_QUERY_OBJECTID_INFO 0x3f0 + +struct trans2_resp { + /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + /* + * SetupWords[SetupCount]; + * __u16 ByteCount; + * __u16 Reserved2; + */ + /* data area follows */ +} __packed; + +struct smb_com_trans2_req { + struct smb_hdr hdr; + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ +} __packed; + +struct smb_com_trans2_qfsi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; +} __packed; + +struct smb_com_trans2_qfsi_req_params { + __le16 InformationLevel; +} __packed; + +#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 +#define CIFS_SEARCH_CLOSE_AT_END 0x0002 +#define CIFS_SEARCH_RETURN_RESUME 0x0004 +#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 +#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 + +struct smb_com_trans2_ffirst_req_params { + __le16 SearchAttributes; + __le16 SearchCount; + __le16 SearchFlags; + __le16 InformationLevel; + __le32 SearchStorageType; + char FileName[1]; +} __packed; + +struct smb_com_trans2_ffirst_rsp_parms { + __u16 SearchHandle; + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __packed; + +struct smb_com_trans2_fnext_req_params { + __u16 SearchHandle; + __le16 SearchCount; + __le16 InformationLevel; + __u32 ResumeKey; + __le16 SearchFlags; + char ResumeFileName[1]; +} __packed; + +struct smb_com_trans2_fnext_rsp_params { + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __packed; + +struct smb_com_trans2_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __le16 ByteCount; + __u8 Pad; /* may be three bytes? *//* followed by data area */ + __u8 Buffer[0]; +} __packed; + +struct file_internal_info { + __le64 UniqueId; /* inode number */ +} __packed; /* level 0x3ee */ + +/* DeviceType Flags */ +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 + +/* Filesystem Attributes. */ +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_UNICODE_ON_DISK 0x00000004 +/* According to cifs9f, this is 4, not 8 */ +/* Acconding to testing, this actually sets the security attribute! */ +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FS_LFN_APIS 0x00004000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_READ_ONLY_VOLUME 0x00080000 + +/* PathInfo/FileInfo infolevels */ +#define SMB_INFO_STANDARD 1 +#define SMB_SET_FILE_EA 2 +#define SMB_QUERY_FILE_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ +#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ +#define SMB_QUERY_POSIX_PERMISSION 0x207 +#define SMB_QUERY_POSIX_LOCK 0x208 +/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_UNLINK 0x20a */ +#define SMB_QUERY_FILE__UNIX_INFO2 0x20b +#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee +#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 +#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_MODE_INFO 0x3f8 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 + + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_SET_POSIX_ACL 0x204 +#define SMB_SET_XATTR 0x205 +#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ +#define SMB_SET_POSIX_LOCK 0x208 +#define SMB_POSIX_OPEN 0x209 +#define SMB_POSIX_UNLINK 0x20a +#define SMB_SET_FILE_UNIX_INFO2 0x20b +#define SMB_SET_FILE_BASIC_INFO2 0x3ec +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ +#define SMB_SET_FILE_DISPOSITION_INFORMATION 0x3f5 /* alias for 0x102 */ +#define SMB_FILE_ALL_INFO2 0x3fa +#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb +#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc +#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 +#define SMB_FILE_QUOTA_INFO 0x408 +#define SMB_FILE_REPARSEPOINT_INFO 0x409 +#define SMB_FILE_MAXIMUM_INFO 0x40d + +/* Find File infolevels */ +#define SMB_FIND_FILE_INFO_STANDARD 0x001 +#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 +#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 +#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 +#define SMB_FIND_FILE_UNIX 0x202 + +struct smb_com_trans2_qpi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __packed; + +struct trans2_qpi_req_params { + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __packed; + +/******************************************************************************/ +/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ +/******************************************************************************/ +struct file_basic_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} __packed; /* size info, level 0x101 */ + +struct file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; + +struct file_ea_info { + __le32 EaSize; +} __packed; + +struct alt_name_info { + __le32 FileNameLength; + char FileName[1]; +} __packed; + +struct file_name_info { + __le32 FileNameLength; + char FileName[1]; +} __packed; + +/* data block encoding of response to level 263 QPathInfo */ +struct file_all_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; + __le32 EASize; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0x107 QPathInfo */ + +/* set path info/open file */ +/* defines for enumerating possible values of the Unix type field below */ +#define UNIX_FILE 0 +#define UNIX_DIR 1 +#define UNIX_SYMLINK 2 +#define UNIX_CHARDEV 3 +#define UNIX_BLOCKDEV 4 +#define UNIX_FIFO 5 +#define UNIX_SOCKET 6 +#define UNIX_UNKNOWN 0xFFFFFFFF + +struct file_unix_basic_info { + __le64 EndOfFile; + __le64 NumOfBytes; + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ + __le64 LastAccessTime; + __le64 LastModificationTime; + __le64 Uid; + __le64 Gid; + __le32 Type; + __le64 DevMajor; + __le64 DevMinor; + __le64 UniqueId; + __le64 Permissions; + __le64 Nlinks; +} __packed; /* level 0x200 QPathInfo */ + +struct smb_com_trans2_spi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __packed; + +struct smb_com_trans2_spi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __le16 ByteCount; + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ +} __packed; + +/* POSIX Open Flags */ +#define SMB_O_RDONLY 0x1 +#define SMB_O_WRONLY 0x2 +#define SMB_O_RDWR 0x4 +#define SMB_O_CREAT 0x10 +#define SMB_O_EXCL 0x20 +#define SMB_O_TRUNC 0x40 +#define SMB_O_APPEND 0x80 +#define SMB_O_SYNC 0x100 +#define SMB_O_DIRECTORY 0x200 +#define SMB_O_NOFOLLOW 0x400 +#define SMB_O_DIRECT 0x800 +#define SMB_ACCMODE 0x7 + +/* info level response for SMB_POSIX_PATH_OPEN */ +#define SMB_NO_INFO_LEVEL_RESPONSE 0xFFFF + +struct open_psx_req { + __le32 OpenFlags; /* same as NT CreateX */ + __le32 PosixOpenFlags; + __le64 Permissions; + __le16 Level; /* reply level requested (see QPathInfo levels) */ +} __packed; /* level 0x209 SetPathInfo data */ + +struct open_psx_rsp { + __le16 OplockFlags; + __u16 Fid; + __le32 CreateAction; + __le16 ReturnedLevel; + __le16 Pad; + /* struct following varies based on requested level */ +} __packed; /* level 0x209 SetPathInfo data */ + +struct unlink_psx_rsp { + __le16 EAErrorOffset; +} __packed; /* level 0x209 SetPathInfo data*/ + +/* Version numbers for CIFS UNIX major and minor. */ +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + +struct filesystem_unix_info { + __le16 MajorVersionNumber; + __le16 MinorVersionNumber; + __le64 Capability; +} __packed; /* Unix extension level 0x200*/ + +/* Linux/Unix extensions capability flags */ +#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ +#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ +#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ +#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ +#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ +#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* + * Allow new POSIX path based + * calls including posix open + * and posix unlink + */ +#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* + * support reads >128K (up + * to 0xFFFF00 + */ +#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ +#define CIFS_UNIX_PROXY_CAP 0x00000400 /* + * Proxy cap: 0xACE ioctl and + * QFS PROXY call + */ +#ifdef CONFIG_CIFS_POSIX +/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send + * LockingX instead of posix locking call on unix sess (and we do not expect + * LockingX to use different (ie Windows) semantics than posix locking on + * the same session (if WINE needs to do this later, we can add this cap + * back in later + */ + +/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ +#define CIFS_UNIX_CAP_MASK 0x000003db +#else +#define CIFS_UNIX_CAP_MASK 0x00000013 +#endif /* CONFIG_CIFS_POSIX */ + + +#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ + +/* Our server caps */ + +#define SMB_UNIX_CAPS (CIFS_UNIX_FCNTL_CAP | CIFS_UNIX_POSIX_ACL_CAP | \ + CIFS_UNIX_XATTR_CAP | CIFS_UNIX_POSIX_PATHNAMES_CAP| \ + CIFS_UNIX_POSIX_PATH_OPS_CAP | CIFS_UNIX_LARGE_READ_CAP | \ + CIFS_UNIX_LARGE_WRITE_CAP) + +#define SMB_SET_CIFS_UNIX_INFO 0x200 +/* Level 0x200 request structure follows */ +struct smb_com_trans2_setfsi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 FileNum; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + __le16 ClientUnixMajor; /* Data start. */ + __le16 ClientUnixMinor; + __le64 ClientUnixCap; /* Data end */ +} __packed; + +/* response for setfsinfo levels 0x200 and 0x203 */ +struct smb_com_trans2_setfsi_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __le16 ByteCount; +} __packed; + +struct smb_trans2_qfi_req_params { + __u16 Fid; + __le16 InformationLevel; +} __packed; + +/* FIND FIRST2 and FIND NEXT2 INFORMATION Level Codes*/ + +struct find_info_standard { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le16 FileNameLength; + char FileName[1]; +} __packed; + +struct find_info_query_ea_size { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; + __u8 FileNameLength; + char FileName[1]; +} __packed; + +struct file_unix_info { + __le32 NextEntryOffset; + __u32 ResumeKey; /* as with FileIndex - no need to convert */ + struct file_unix_basic_info basic; + char FileName[1]; +} __packed; /* level 0x202 */ + +struct smb_com_trans2_sfi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __u16 Fid; + __le16 InformationLevel; + __u16 Reserved4; +} __packed; + +struct smb_com_trans2_sfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __le16 ByteCount; + __u16 Reserved2; /* + * parameter word reserved - + * present for infolevels > 100 + */ +} __packed; + +struct file_end_of_file_info { + __le64 FileSize; /* offset to end of file */ +} __packed; /* size info, level 0x104 for set, 0x106 for query */ + +struct smb_com_create_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __packed; + +struct smb_com_create_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_check_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __packed; + +struct smb_com_check_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_process_exit_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_delete_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __packed; + +struct smb_com_delete_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_delete_file_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __packed; + +struct smb_com_delete_file_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +#define CREATE_HARD_LINK 0x103 + +struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ + struct smb_hdr hdr; /* wct = 4 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 Flags; /* spec says Information Level */ + __le32 ClusterCount; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __packed; + +struct smb_com_query_information_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* 1 + namelen + 1 */ + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char FileName[1]; +} __packed; + +struct smb_com_query_information_rsp { + struct smb_hdr hdr; /* wct = 10 */ + __le16 attr; + __le32 last_write_time; + __le32 size; + __u16 reserved[5]; + __le16 ByteCount; /* bcc = 0 */ +} __packed; + +struct smb_com_findclose_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __le16 ByteCount; /* 0 */ +} __packed; + +#define SMBOPEN_DISPOSITION_NONE 0 +#define SMBOPEN_LOCK_GRANTED 0x8000 + +#define SMB_DA_ACCESS_READ 0 +#define SMB_DA_ACCESS_WRITE 0x0001 +#define SMB_DA_ACCESS_READ_WRITE 0x0002 + +/* + * Flags on SMB open + */ +#define SMBOPEN_WRITE_THROUGH 0x4000 +#define SMBOPEN_DENY_ALL 0x0010 +#define SMBOPEN_DENY_WRITE 0x0020 +#define SMBOPEN_DENY_READ 0x0030 +#define SMBOPEN_DENY_NONE 0x0040 +#define SMBOPEN_SHARING_MODE (SMBOPEN_DENY_ALL | \ + SMBOPEN_DENY_WRITE | \ + SMBOPEN_DENY_READ | \ + SMBOPEN_DENY_NONE) +#define SMBOPEN_READ 0x0000 +#define SMBOPEN_WRITE 0x0001 +#define SMBOPEN_READWRITE 0x0002 +#define SMBOPEN_EXECUTE 0x0003 + +#define SMBOPEN_OCREATE 0x0010 +#define SMBOPEN_OTRUNC 0x0002 +#define SMBOPEN_OAPPEND 0x0001 + +/* format of legacy open request */ +struct smb_com_openx_req { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OpenFlags; + __le16 Mode; + __le16 Sattr; /* search attributes */ + __le16 FileAttributes; /* dos attrs */ + __le32 CreateTime; /* os2 format */ + __le16 OpenFunction; + __le32 EndOfFile; + __le32 Timeout; + __le32 Reserved; + __le16 ByteCount; /* file name follows */ + char fileName[1]; +} __packed; + +struct smb_com_openx_rsp { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le16 FileAttributes; + __le32 LastWriteTime; /* os2 format */ + __le32 EndOfFile; + __le16 Access; + __le16 FileType; + __le16 IPCState; + __le16 Action; + __u32 FileId; + __u16 Reserved; + __le16 ByteCount; +} __packed; + +struct filesystem_alloc_info { + __le32 fsid; + __le32 SectorsPerAllocationUnit; + __le32 TotalAllocationUnits; + __le32 FreeAllocationUnits; + __le16 BytesPerSector; +} __packed; + +struct file_allocation_info { + __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ +} __packed; /* size used on disk: 0x103 for set, 0x105 for query */ + +struct file_info_standard { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; +} __packed; /* level 1 SetPath/FileInfo */ + +#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) + +struct set_file_rename { + __le32 overwrite; /* 1 = overwrite dest */ + __u32 root_fid; /* zero */ + __le32 target_name_len; + char target_name[0]; /* Must be unicode */ +} __packed; + +struct fea { + unsigned char EA_flags; + __u8 name_len; + __le16 value_len; + char name[1]; + /* optionally followed by value */ +} __packed; + +struct fealist { + __le32 list_len; + __u8 list[1]; +} __packed; + +/* POSIX ACL set/query path info structures */ +#define CIFS_ACL_VERSION 1 +struct cifs_posix_ace { /* access control entry (ACE) */ + __u8 cifs_e_tag; + __u8 cifs_e_perm; + __le64 cifs_uid; /* or gid */ +} __packed; + +struct cifs_posix_acl { /* access conrol list (ACL) */ + __le16 version; + __le16 access_entry_count; /* access ACL - count of entries */ + __le16 default_entry_count; /* default ACL - count of entries */ + struct cifs_posix_ace ace_array[0]; + /* + * followed by + * struct cifs_posix_ace default_ace_arraay[] + */ +} __packed; /* level 0x204 */ + +struct smb_com_setattr_req { + struct smb_hdr hdr; /* wct = 8 */ + __le16 attr; + __le32 LastWriteTime; + __le16 reserved[5]; /* must be zero */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __packed; + +struct smb_com_setattr_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +extern int init_smb1_server(struct ksmbd_conn *conn); + +/* function prototypes */ +extern int init_smb_rsp_hdr(struct ksmbd_work *work); +extern u16 get_smb_cmd_val(struct ksmbd_work *work); +extern void set_smb_rsp_status(struct ksmbd_work *work, __le32 err); +extern int smb_allocate_rsp_buf(struct ksmbd_work *work); +extern bool smb1_is_sign_req(struct ksmbd_work *work, unsigned int command); +extern int smb1_check_sign_req(struct ksmbd_work *work); +extern void smb1_set_sign_rsp(struct ksmbd_work *work); +extern int smb_check_user_session(struct ksmbd_work *work); +extern int smb_get_ksmbd_tcon(struct ksmbd_work *work); +extern int ksmbd_smb1_check_message(struct ksmbd_work *work); + +/* smb1 command handlers */ +extern int smb_rename(struct ksmbd_work *work); +extern int smb_negotiate_request(struct ksmbd_work *work); +extern int smb_handle_negotiate(struct ksmbd_work *work); +extern int smb_session_setup_andx(struct ksmbd_work *work); +extern int smb_tree_connect_andx(struct ksmbd_work *work); +extern int smb_trans2(struct ksmbd_work *work); +extern int smb_nt_create_andx(struct ksmbd_work *work); +extern int smb_trans(struct ksmbd_work *work); +extern int smb_locking_andx(struct ksmbd_work *work); +extern int smb_close(struct ksmbd_work *work); +extern int smb_read_andx(struct ksmbd_work *work); +extern int smb_tree_disconnect(struct ksmbd_work *work); +extern int smb_session_disconnect(struct ksmbd_work *work); +extern int smb_write_andx(struct ksmbd_work *work); +extern int smb_echo(struct ksmbd_work *work); +extern int smb_flush(struct ksmbd_work *work); +extern int smb_mkdir(struct ksmbd_work *work); +extern int smb_rmdir(struct ksmbd_work *work); +extern int smb_unlink(struct ksmbd_work *work); +extern int smb_nt_cancel(struct ksmbd_work *work); +extern int smb_nt_rename(struct ksmbd_work *work); +extern int smb_query_info(struct ksmbd_work *work); +extern int smb_closedir(struct ksmbd_work *work); +extern int smb_open_andx(struct ksmbd_work *work); +extern int smb_write(struct ksmbd_work *work); +extern int smb_setattr(struct ksmbd_work *work); +extern int smb_checkdir(struct ksmbd_work *work); +extern int smb_process_exit(struct ksmbd_work *work); +#endif /* __SMB1PDU_H */ diff -Naur --no-dereference a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c --- a/fs/ksmbd/smb2misc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb2misc.c 2022-01-06 12:45:53.830318172 -0500 @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "nterr.h" +#include "smb2pdu.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "mgmt/user_session.h" +#include "connection.h" + +static int check_smb2_hdr(struct smb2_hdr *hdr) +{ + /* + * Make sure that this really is an SMB, that it is a response. + */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 1; + return 0; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(36), + /* SMB2_SESSION_SETUP */ cpu_to_le16(25), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(9), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(57), + /* SMB2_CLOSE */ cpu_to_le16(24), + /* SMB2_FLUSH */ cpu_to_le16(24), + /* SMB2_READ */ cpu_to_le16(49), + /* SMB2_WRITE */ cpu_to_le16(49), + /* SMB2_LOCK */ cpu_to_le16(48), + /* SMB2_IOCTL */ cpu_to_le16(57), + /* SMB2_CANCEL */ cpu_to_le16(4), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), + /* SMB2_QUERY_INFO */ cpu_to_le16(41), + /* SMB2_SET_INFO */ cpu_to_le16(33), + /* use 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) +}; + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 requests. SMB2 requests + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ true, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ true, + /* SMB2_LOCK */ true, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ false, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ true, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Set length of the data area and the offset to arguments. + * if they are invalid, return error. + */ +static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + struct smb2_hdr *hdr) +{ + int ret = 0; + + *off = 0; + *len = 0; + + /* error reqeusts do not have data area */ + if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && + (((struct smb2_err_rsp *)hdr)->StructureSize) == SMB2_ERROR_STRUCTURE_SIZE2_LE) + return ret; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (hdr->Command) { + case SMB2_SESSION_SETUP: + *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + break; + case SMB2_TREE_CONNECT: + *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); + *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); + break; + case SMB2_CREATE: + { + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { + *off = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsOffset); + *len = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsLength); + break; + } + + *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); + *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + break; + } + case SMB2_QUERY_INFO: + *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); + break; + case SMB2_SET_INFO: + *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); + *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); + break; + case SMB2_READ: + *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); + break; + case SMB2_WRITE: + if (((struct smb2_write_req *)hdr)->DataOffset) { + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset); + *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); + break; + } + + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); + break; + case SMB2_LOCK: + { + int lock_count; + + /* + * smb2_lock request size is 48 included single + * smb2_lock_element structure size. + */ + lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount) - 1; + if (lock_count > 0) { + *off = __SMB2_HEADER_STRUCTURE_SIZE + 48; + *len = sizeof(struct smb2_lock_element) * lock_count; + } + break; + } + case SMB2_IOCTL: + *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); + break; + default: + ksmbd_debug(SMB, "no length check for command\n"); + break; + } + + if (*off > 4096) { + ksmbd_debug(SMB, "offset %d too large\n", *off); + ret = -EINVAL; + } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { + ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", + MAX_STREAM_PROT_LEN, (u64)*off + *len); + ret = -EINVAL; + } + + return ret; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +static int smb2_calc_size(void *buf, unsigned int *len) +{ + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *hdr = &pdu->hdr; + unsigned int offset; /* the offset from the beginning of SMB to data area */ + unsigned int data_length; /* the length of the variable length data area */ + int ret; + + /* Structure Size has already been checked to make sure it is 64 */ + *len = le16_to_cpu(hdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + *len += le16_to_cpu(pdu->StructureSize2); + /* + * StructureSize2 of smb2_lock pdu is set to 48, indicating + * the size of smb2 lock request with single smb2_lock_element + * regardless of number of locks. Subtract single + * smb2_lock_element for correct buffer size check. + */ + if (hdr->Command == SMB2_LOCK) + *len -= sizeof(struct smb2_lock_element); + + if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) + goto calc_size_exit; + + ret = smb2_get_data_area_len(&offset, &data_length, hdr); + if (ret) + return ret; + ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, + offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < *len) { + ksmbd_debug(SMB, + "data area offset %d overlaps SMB2 header %u\n", + offset + 1, *len); + return -EINVAL; + } + + *len = offset + data_length; + } + +calc_size_exit: + ksmbd_debug(SMB, "SMB2 len %u\n", *len); + return 0; +} + +static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) +{ + return le32_to_cpu(h->InputBufferLength) + + le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) +{ + return le32_to_cpu(h->BufferLength); +} + +static inline int smb2_read_req_len(struct smb2_read_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_write_req_len(struct smb2_write_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) +{ + return le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->InputCount) + + le32_to_cpu(h->OutputCount); +} + +static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->MaxInputResponse) + + le32_to_cpu(h->MaxOutputResponse); +} + +static int smb2_validate_credit_charge(struct ksmbd_conn *conn, + struct smb2_hdr *hdr) +{ + unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; + unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); + void *__hdr = hdr; + int ret; + + switch (hdr->Command) { + case SMB2_QUERY_INFO: + req_len = smb2_query_info_req_len(__hdr); + break; + case SMB2_SET_INFO: + req_len = smb2_set_info_req_len(__hdr); + break; + case SMB2_READ: + req_len = smb2_read_req_len(__hdr); + break; + case SMB2_WRITE: + req_len = smb2_write_req_len(__hdr); + break; + case SMB2_QUERY_DIRECTORY: + req_len = smb2_query_dir_req_len(__hdr); + break; + case SMB2_IOCTL: + req_len = smb2_ioctl_req_len(__hdr); + expect_resp_len = smb2_ioctl_resp_len(__hdr); + break; + case SMB2_CANCEL: + return 0; + default: + req_len = 1; + break; + } + + credit_charge = max_t(unsigned short, credit_charge, 1); + max_len = max_t(unsigned int, req_len, expect_resp_len); + calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); + + if (credit_charge < calc_credit_num) { + ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", + credit_charge, calc_credit_num); + return 1; + } else if (credit_charge > conn->max_credits) { + ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); + return 1; + } + + spin_lock(&conn->credits_lock); + if (credit_charge <= conn->total_credits) { + conn->total_credits -= credit_charge; + ret = 0; + } else { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + ret = 1; + } + spin_unlock(&conn->credits_lock); + return ret; +} + +int ksmbd_smb2_check_message(struct ksmbd_work *work) +{ + struct smb2_pdu *pdu = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = &pdu->hdr; + int command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(pdu); + + if (le32_to_cpu(hdr->NextCommand) > 0) + len = le32_to_cpu(hdr->NextCommand); + else if (work->next_smb2_rcv_hdr_off) + len -= work->next_smb2_rcv_hdr_off; + + if (check_smb2_hdr(hdr)) + return 1; + + if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + ksmbd_debug(SMB, "Illegal structure size %u\n", + le16_to_cpu(hdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(hdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); + return 1; + } + + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { + if (command != SMB2_OPLOCK_BREAK_HE && + (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { + /* error packets have 9 byte structure size */ + ksmbd_debug(SMB, + "Illegal request size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); + return 1; + } else if (command == SMB2_OPLOCK_BREAK_HE && + hdr->Status == 0 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { + /* special case for SMB2.1 lease break message */ + ksmbd_debug(SMB, + "Illegal request size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + if (smb2_calc_size(hdr, &clc_len)) + return 1; + + if (len != clc_len) { + /* client can return one byte more due to implied bcc[0] */ + if (clc_len == len + 1) + goto validate_credit; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(clc_len, 8) == len) + goto validate_credit; + + /* + * windows client also pad up to 8 bytes when compounding. + * If pad is longer than eight bytes, log the server behavior + * (once), since may indicate a problem but allow it and + * continue since the frame is parseable. + */ + if (clc_len < len) { + ksmbd_debug(SMB, + "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + goto validate_credit; + } + + ksmbd_debug(SMB, + "cli req too short, len %d not %d. cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + + return 1; + } + +validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(work->conn, hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + + return 0; +} + +int smb2_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); +} diff -Naur --no-dereference a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c --- a/fs/ksmbd/smb2ops.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb2ops.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include "glob.h" +#include "smb2pdu.h" + +#include "auth.h" +#include "connection.h" +#include "smb_common.h" +#include "server.h" + +#ifdef CONFIG_SMB_INSECURE_SERVER +static struct smb_version_values smb20_server_values = { + .version_string = SMB20_VERSION_STRING, + .protocol_id = SMB20_PROT_ID, + .capabilities = 0, + .max_read_size = CIFS_DEFAULT_IOSIZE, + .max_write_size = CIFS_DEFAULT_IOSIZE, + .max_trans_size = CIFS_DEFAULT_IOSIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; +#endif + +static struct smb_version_values smb21_server_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB21_DEFAULT_IOSIZE, + .max_write_size = SMB21_DEFAULT_IOSIZE, + .max_trans_size = SMB21_DEFAULT_IOSIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb30_server_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb302_server_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb311_server_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_ops smb2_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb2_check_sign_req, + .set_sign_rsp = smb2_set_sign_rsp +}; + +static struct smb_version_ops smb3_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb30_signingkey, + .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_ops smb3_11_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb311_signingkey, + .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { + [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, + [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, + [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, + [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, + [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, + [SMB2_CREATE_HE] = { .proc = smb2_open}, + [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, + [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, + [SMB2_CLOSE_HE] = { .proc = smb2_close}, + [SMB2_ECHO_HE] = { .proc = smb2_echo}, + [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, + [SMB2_READ_HE] = { .proc = smb2_read}, + [SMB2_WRITE_HE] = { .proc = smb2_write}, + [SMB2_FLUSH_HE] = { .proc = smb2_flush}, + [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, + [SMB2_LOCK_HE] = { .proc = smb2_lock}, + [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, + [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, + [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, +}; + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * init_smb2_0_server() - initialize a smb server connection with smb2.0 + * command dispatcher + * @conn: connection instance + */ +int init_smb2_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb20_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + conn->total_credits = 0; + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; + return 0; +} +#else +int init_smb2_0_server(struct ksmbd_conn *conn) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * init_smb2_1_server() - initialize a smb server connection with smb2.1 + * command dispatcher + * @conn: connection instance + */ +void init_smb2_1_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb21_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; +} + +/** + * init_smb3_0_server() - initialize a smb server connection with smb3.0 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb30_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_02_server() - initialize a smb server connection with smb3.02 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_02_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb302_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_11_server() - initialize a smb server connection with smb3.11 + * command dispatcher + * @conn: connection instance + */ +int init_smb3_11_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb311_server_values; + conn->ops = &smb3_11_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + + INIT_LIST_HEAD(&conn->preauth_sess_table); + return 0; +} + +void init_smb2_max_read_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_read_size = sz; + smb30_server_values.max_read_size = sz; + smb302_server_values.max_read_size = sz; + smb311_server_values.max_read_size = sz; +} + +void init_smb2_max_write_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_write_size = sz; + smb30_server_values.max_write_size = sz; + smb302_server_values.max_write_size = sz; + smb311_server_values.max_write_size = sz; +} + +void init_smb2_max_trans_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_trans_size = sz; + smb30_server_values.max_trans_size = sz; + smb302_server_values.max_trans_size = sz; + smb311_server_values.max_trans_size = sz; +} diff -Naur --no-dereference a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c --- a/fs/ksmbd/smb2pdu.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb2pdu.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,8665 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "smb2pdu.h" +#include "smbfsctl.h" +#include "oplock.h" +#include "smbacl.h" + +#include "auth.h" +#include "asn1.h" +#include "connection.h" +#include "transport_ipc.h" +#include "transport_rdma.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "misc.h" + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "ksmbd_work.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/ksmbd_ida.h" +#include "ndr.h" + +static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) +{ + if (work->next_smb2_rcv_hdr_off) { + *req = ksmbd_req_buf_next(work); + *rsp = ksmbd_resp_buf_next(work); + } else { + *req = work->request_buf; + *rsp = work->response_buf; + } +} + +#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) + +/** + * check_session_id() - check for valid session id in smb header + * @conn: connection instance + * @id: session id from smb header + * + * Return: 1 if valid session id, otherwise 0 + */ +static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) +{ + struct ksmbd_session *sess; + + if (id == 0 || id == -1) + return false; + + sess = ksmbd_session_lookup_all(conn, id); + if (sess) + return true; + pr_err("Invalid user session id: %llu\n", id); + return false; +} + +struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) +{ + struct channel *chann; + + list_for_each_entry(chann, &sess->ksmbd_chann_list, chann_list) { + if (chann->conn == conn) + return chann; + } + + return NULL; +} + +/** + * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. + * @work: smb work + * + * Return: 0 if there is a tree connection matched or these are + * skipable commands, otherwise error + */ +int smb2_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = work->request_buf; + unsigned int cmd = le16_to_cpu(req_hdr->Command); + int tree_id; + + work->tcon = NULL; + if (cmd == SMB2_TREE_CONNECT_HE || + cmd == SMB2_CANCEL_HE || + cmd == SMB2_LOGOFF_HE) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (xa_empty(&work->sess->tree_conns)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -ENOENT; + } + + tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + pr_err("Invalid tid %d\n", tree_id); + return -EINVAL; + } + + return 1; +} + +/** + * smb2_set_err_rsp() - set error response code on smb response + * @work: smb work containing response buffer + */ +void smb2_set_err_rsp(struct ksmbd_work *work) +{ + struct smb2_err_rsp *err_rsp; + + if (work->next_smb2_rcv_hdr_off) + err_rsp = ksmbd_resp_buf_next(work); + else + err_rsp = work->response_buf; + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; + inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); + } +} + +/** + * is_smb2_neg_cmd() - is it smb2 negotiation command + * @work: smb work containing smb header + * + * Return: true if smb2 negotiation command, otherwise false + */ +bool is_smb2_neg_cmd(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = work->request_buf; + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is request not response message */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return false; + + if (hdr->Command != SMB2_NEGOTIATE) + return false; + + return true; +} + +/** + * is_smb2_rsp() - is it smb2 response + * @work: smb work containing smb response buffer + * + * Return: true if smb2 response, otherwise false + */ +bool is_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = work->response_buf; + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is response not request message */ + if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) + return false; + + return true; +} + +/** + * get_smb2_cmd_val() - get smb command code from smb header + * @work: smb work containing smb request buffer + * + * Return: smb2 request command value + */ +u16 get_smb2_cmd_val(struct ksmbd_work *work) +{ + struct smb2_hdr *rcv_hdr; + + if (work->next_smb2_rcv_hdr_off) + rcv_hdr = ksmbd_req_buf_next(work); + else + rcv_hdr = work->request_buf; + return le16_to_cpu(rcv_hdr->Command); +} + +/** + * set_smb2_rsp_status() - set error response code on smb2 header + * @work: smb work containing response buffer + * @err: error response code + */ +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb2_hdr *rsp_hdr; + + if (work->next_smb2_rcv_hdr_off) + rsp_hdr = ksmbd_resp_buf_next(work); + else + rsp_hdr = work->response_buf; + rsp_hdr->Status = err; + smb2_set_err_rsp(work); +} + +/** + * init_smb2_neg_rsp() - initialize smb2 response for negotiate command + * @work: smb work containing smb request buffer + * + * smb2 negotiate response is sent in reply of smb1 negotiate command for + * dialect auto-negotiation. + */ +int init_smb2_neg_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + + if (conn->need_neg == false) + return -EINVAL; + if (!(conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID)) + return -EINVAL; + + rsp_hdr = work->response_buf; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(2); + rsp_hdr->Command = SMB2_NEGOTIATE; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = 0; + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = work->response_buf; + + WARN_ON(ksmbd_conn_good(work)); + + rsp->StructureSize = cpu_to_le16(65); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying connection + */ + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + /* Default Max Message Size till SMB2.0, 64K*/ + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + + sizeof(rsp->hdr.smb2_buf_length)) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(work); + return 0; +} + +/** + * smb2_set_rsp_credits() - set number of credits in response buffer + * @work: smb work containing smb response buffer + */ +int smb2_set_rsp_credits(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); + struct ksmbd_conn *conn = work->conn; + unsigned short credits_requested; + unsigned short credit_charge, credits_granted = 0; + unsigned short aux_max, aux_credits; + + if (work->send_no_response) + return 0; + + hdr->CreditCharge = req_hdr->CreditCharge; + + if (conn->total_credits > conn->max_credits) { + hdr->CreditRequest = 0; + pr_err("Total credits overflow: %d\n", conn->total_credits); + return -EINVAL; + } + + credit_charge = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditCharge), 1); + credits_requested = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditRequest), 1); + + /* according to smb2.credits smbtorture, Windows server + * 2016 or later grant up to 8192 credits at once. + * + * TODO: Need to adjuct CreditRequest value according to + * current cpu load + */ + aux_credits = credits_requested - 1; + if (hdr->Command == SMB2_NEGOTIATE) + aux_max = 0; + else + aux_max = conn->max_credits - credit_charge; + aux_credits = min_t(unsigned short, aux_credits, aux_max); + credits_granted = credit_charge + aux_credits; + + if (conn->max_credits - conn->total_credits < credits_granted) + credits_granted = conn->max_credits - + conn->total_credits; + + conn->total_credits += credits_granted; + work->credits_granted += credits_granted; + + if (!req_hdr->NextCommand) { + /* Update CreditRequest in last request */ + hdr->CreditRequest = cpu_to_le16(work->credits_granted); + } + ksmbd_debug(SMB, + "credits: requested[%d] granted[%d] total_granted[%d]\n", + credits_requested, credits_granted, + conn->total_credits); + return 0; +} + +/** + * init_chained_smb2_rsp() - initialize smb2 chained response + * @work: smb work containing smb response buffer + */ +static void init_chained_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *req = ksmbd_req_buf_next(work); + struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); + struct smb2_hdr *rsp_hdr; + struct smb2_hdr *rcv_hdr; + int next_hdr_offset = 0; + int len, new_len; + + /* Len of this response = updated RFC len - offset of previous cmd + * in the compound rsp + */ + + /* Storing the current local FID which may be needed by subsequent + * command in the compound request + */ + if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { + work->compound_fid = + le64_to_cpu(((struct smb2_create_rsp *)rsp)-> + VolatileFileId); + work->compound_pfid = + le64_to_cpu(((struct smb2_create_rsp *)rsp)-> + PersistentFileId); + work->compound_sid = le64_to_cpu(rsp->SessionId); + } + + len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); + inc_rfc1001_len(work->response_buf, ((sizeof(struct smb2_hdr) - 4) + + new_len - len)); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", + new_len, work->next_smb2_rcv_hdr_off, + work->next_smb2_rsp_hdr_off); + + rsp_hdr = ksmbd_resp_buf_next(work); + rcv_hdr = ksmbd_req_buf_next(work); + + if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "related flag should be set\n"); + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + memset((char *)rsp_hdr + 4, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | + SMB2_FLAGS_RELATED_OPERATIONS); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); +} + +/** + * is_chained_smb2_message() - check for chained command + * @work: smb work containing smb request buffer + * + * Return: true if chained request, otherwise false + */ +bool is_chained_smb2_message(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = work->request_buf; + unsigned int len, next_cmd; + + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + hdr = ksmbd_req_buf_next(work); + next_cmd = le32_to_cpu(hdr->NextCommand); + if (next_cmd > 0) { + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + + __SMB2_HEADER_STRUCTURE_SIZE > + get_rfc1002_len(work->request_buf)) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return false; + } + + if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > + work->response_sz) { + pr_err("next response offset exceeds response buffer size\n"); + return false; + } + + ksmbd_debug(SMB, "got SMB2 chained command\n"); + init_chained_smb2_rsp(work); + return true; + } else if (work->next_smb2_rcv_hdr_off) { + /* + * This is last request in chained command, + * align response to 8 byte + */ + len = ALIGN(get_rfc1002_len(work->response_buf), 8); + len = len - get_rfc1002_len(work->response_buf); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); + inc_rfc1001_len(work->response_buf, len); + if (work->aux_payload_sz) + work->aux_payload_sz += len; + } + } + return false; +} + +/** + * init_smb2_rsp_hdr() - initialize smb2 response + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int init_smb2_rsp_hdr(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr = work->response_buf; + struct smb2_hdr *rcv_hdr = work->request_buf; + struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + + work->syncronous = true; + if (work->async_id) { + ksmbd_release_id(&conn->async_ida, work->async_id); + work->async_id = 0; + } + + return 0; +} + +/** + * smb2_allocate_rsp_buf() - allocate smb2 response buffer + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb2_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = work->request_buf; + size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + size_t large_sz = small_sz + work->conn->vals->max_trans_size; + size_t sz = small_sz; + int cmd = le16_to_cpu(hdr->Command); + + if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) + sz = large_sz; + + if (cmd == SMB2_QUERY_INFO_HE) { + struct smb2_query_info_req *req; + + req = work->request_buf; + if (req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) + sz = large_sz; + } + + /* allocate large response buf for chained commands */ + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!work->response_buf) + return -ENOMEM; + + work->response_sz = sz; + return 0; +} + +/** + * smb2_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_check_user_session(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = work->request_buf; + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + unsigned long long sess_id; + + work->sess = NULL; + /* + * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not + * require a session id, so no need to validate user session's for + * these commands. + */ + if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || + cmd == SMB2_SESSION_SETUP_HE) + return 0; + + if (!ksmbd_conn_good(work)) + return -EINVAL; + + sess_id = le64_to_cpu(req_hdr->SessionId); + /* Check for validity of user session */ + work->sess = ksmbd_session_lookup_all(conn, sess_id); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); + return -EINVAL; +} + +static void destroy_previous_session(struct ksmbd_user *user, u64 id) +{ + struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); + struct ksmbd_user *prev_user; + + if (!prev_sess) + return; + + prev_user = prev_sess->user; + + if (!prev_user || + strcmp(user->name, prev_user->name) || + user->passkey_sz != prev_user->passkey_sz || + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) { + put_session(prev_sess); + return; + } + + put_session(prev_sess); + ksmbd_session_destroy(prev_sess); +} + +/** + * smb2_get_name() - get filename string from on the wire smb format + * @share: ksmbd_share_config pointer + * @src: source buffer + * @maxlen: maxlen of source string + * @nls_table: nls_table pointer + * + * Return: matching converted filename on success, otherwise error ptr + */ +static char * +smb2_get_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct nls_table *local_nls) +{ + char *name; + + name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); + if (IS_ERR(name)) { + pr_err("failed to get name %ld\n", PTR_ERR(name)); + return name; + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + return name; +} + +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) +{ + struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + + rsp_hdr = work->response_buf; + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + + id = ksmbd_acquire_async_msg_id(&conn->async_ida); + if (id < 0) { + pr_err("Failed to alloc async message id\n"); + return id; + } + work->syncronous = false; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", + work->async_id); + + work->cancel_fn = fn; + work->cancel_argv = arg; + + if (list_empty(&work->async_request_entry)) { + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + } + + return 0; +} + +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) +{ + struct smb2_hdr *rsp_hdr; + + rsp_hdr = work->response_buf; + smb2_set_err_rsp(work); + rsp_hdr->Status = status; + + work->multiRsp = 1; + ksmbd_conn_write(work); + rsp_hdr->Status = 0; + work->multiRsp = 0; +} + +static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +{ + if (S_ISDIR(mode) || S_ISREG(mode)) + return 0; + + if (S_ISLNK(mode)) + return IO_REPARSE_TAG_LX_SYMLINK_LE; + else if (S_ISFIFO(mode)) + return IO_REPARSE_TAG_LX_FIFO_LE; + else if (S_ISSOCK(mode)) + return IO_REPARSE_TAG_AF_UNIX_LE; + else if (S_ISCHR(mode)) + return IO_REPARSE_TAG_LX_CHR_LE; + else if (S_ISBLK(mode)) + return IO_REPARSE_TAG_LX_BLK_LE; + + return 0; +} + +/** + * smb2_get_dos_mode() - get file mode in dos format from unix mode + * @stat: kstat containing file mode + * @attribute: attribute flags + * + * Return: converted dos mode + */ +static int smb2_get_dos_mode(struct kstat *stat, int attribute) +{ + int attr = 0; + + if (S_ISDIR(stat->mode)) { + attr = ATTR_DIRECTORY | + (attribute & (ATTR_HIDDEN | ATTR_SYSTEM)); + } else { + attr = (attribute & 0x00005137) | ATTR_ARCHIVE; + attr &= ~(ATTR_DIRECTORY); + if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & + FILE_SUPPORTS_SPARSE_FILES)) + attr |= ATTR_SPARSE; + + if (smb2_get_reparse_tag_special_file(stat->mode)) + attr |= ATTR_REPARSE; + } + + return attr; +} + +static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, + __le16 hash_id) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = hash_id; +} + +static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + __le16 cipher_type) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(4); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = cipher_type; +} + +static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, + __le16 comp_algo) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_ctx) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved1 = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +} + +static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) +{ + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SigningAlgorithms[0] = sign_algo; +} + +static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp) +{ + /* +4 is to account for the RFC1001 len field */ + char *pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset) + 4; + int neg_ctxt_cnt = 1; + int ctxt_size; + + ksmbd_debug(SMB, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); + rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(rsp, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, + conn->cipher_type); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += + round_up(sizeof(struct smb2_encryption_neg_context) + 2, + 8); + } + + if (conn->compress_algorithm) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + /* Temporarily set to SMB3_COMPRESS_NONE */ + build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, + conn->compress_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_compression_ctx) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx) + 2, + 8); + } + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_posix_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); + build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, + conn->signing_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; + } + + inc_rfc1001_len(rsp, ctxt_size); +} + +static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, + struct smb2_preauth_neg_context *pneg_ctxt) +{ + __le32 err = STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + + if (pneg_ctxt->HashAlgorithms == SMB2_PREAUTH_INTEGRITY_SHA512) { + conn->preauth_info->Preauth_HashId = + SMB2_PREAUTH_INTEGRITY_SHA512; + err = STATUS_SUCCESS; + } + + return err; +} + +static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + struct smb2_encryption_neg_context *pneg_ctxt, + int len_of_ctxts) +{ + int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + int i, cphs_size = cph_cnt * sizeof(__le16); + + conn->cipher_type = 0; + + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > + len_of_ctxts) { + pr_err("Invalid cipher count(%d)\n", cph_cnt); + return; + } + + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) + return; + + for (i = 0; i < cph_cnt; i++) { + if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(SMB, "Cipher ID = 0x%x\n", + pneg_ctxt->Ciphers[i]); + conn->cipher_type = pneg_ctxt->Ciphers[i]; + break; + } + } +} + +/** + * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption + * @conn: smb connection + * + * Return: true if connection should be encrypted, else false + */ +static bool smb3_encryption_negotiated(struct ksmbd_conn *conn) +{ + if (!conn->ops->generate_encryptionkey) + return false; + + /* + * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. + * SMB 3.1.1 uses the cipher_type field. + */ + return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + conn->cipher_type; +} + +static void decode_compress_ctxt(struct ksmbd_conn *conn, + struct smb2_compression_ctx *pneg_ctxt) +{ + conn->compress_algorithm = SMB3_COMPRESS_NONE; +} + +static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, + struct smb2_signing_capabilities *pneg_ctxt, + int len_of_ctxts) +{ + int sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + int i, sign_alos_size = sign_algo_cnt * sizeof(__le16); + + conn->signing_negotiated = false; + + if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > + len_of_ctxts) { + pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); + return; + } + + for (i = 0; i < sign_algo_cnt; i++) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256 || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC) { + ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", + pneg_ctxt->SigningAlgorithms[i]); + conn->signing_negotiated = true; + conn->signing_algorithm = + pneg_ctxt->SigningAlgorithms[i]; + break; + } + } +} + +static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_req *req) +{ + /* +4 is to account for the RFC1001 len field */ + struct smb2_neg_context *pctx = (struct smb2_neg_context *)((char *)req + 4); + int i = 0, len_of_ctxts; + int offset = le32_to_cpu(req->NegotiateContextOffset); + int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); + int len_of_smb = be32_to_cpu(req->hdr.smb2_buf_length); + __le32 status = STATUS_INVALID_PARAMETER; + + ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); + if (len_of_smb <= offset) { + ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); + return status; + } + + len_of_ctxts = len_of_smb - offset; + + while (i++ < neg_ctxt_cnt) { + int clen; + + /* check that offset is not beyond end of SMB */ + if (len_of_ctxts == 0) + break; + + if (len_of_ctxts < sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)((char *)pctx + offset); + clen = le16_to_cpu(pctx->DataLength); + if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + if (conn->preauth_info->Preauth_HashId) + break; + + status = decode_preauth_ctxt(conn, + (struct smb2_preauth_neg_context *)pctx); + if (status != STATUS_SUCCESS) + break; + } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + if (conn->cipher_type) + break; + + decode_encrypt_ctxt(conn, + (struct smb2_encryption_neg_context *)pctx, + len_of_ctxts); + } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); + if (conn->compress_algorithm) + break; + + decode_compress_ctxt(conn, + (struct smb2_compression_ctx *)pctx); + } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { + ksmbd_debug(SMB, + "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); + } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { + ksmbd_debug(SMB, + "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + conn->posix_ext_supported = true; + } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + decode_sign_cap_ctxt(conn, + (struct smb2_signing_capabilities *)pctx, + len_of_ctxts); + } + + /* offsets must be 8 byte aligned */ + clen = (clen + 7) & ~0x7; + offset = clen + sizeof(struct smb2_neg_context); + len_of_ctxts -= clen + sizeof(struct smb2_neg_context); + } + return status; +} + +/** + * smb2_handle_negotiate() - handler for smb2 negotiate command + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int smb2_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_negotiate_req *req = work->request_buf; + struct smb2_negotiate_rsp *rsp = work->response_buf; + int rc = 0; + unsigned int smb2_buf_len, smb2_neg_size; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); + conn->need_neg = false; + if (ksmbd_conn_good(work)) { + pr_err("conn->tcp_status is already in CifsGood State\n"); + work->send_no_response = 1; + return rc; + } + + if (req->DialectCount == 0) { + pr_err("malformed packet\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + smb2_buf_len = get_rfc1002_len(work->request_buf); + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects) - 4; + if (smb2_neg_size > smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (conn->dialect == SMB311_PROT_ID) { + unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); + + if (smb2_buf_len < nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size > nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } else { + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } + + conn->cli_cap = le32_to_cpu(req->Capabilities); + switch (conn->dialect) { + case SMB311_PROT_ID: + conn->preauth_info = + kzalloc(sizeof(struct preauth_integrity_info), + GFP_KERNEL); + if (!conn->preauth_info) { + rc = -ENOMEM; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + status = deassemble_neg_contexts(conn, req); + if (status != STATUS_SUCCESS) { + pr_err("deassemble_neg_contexts error(0x%x)\n", + status); + rsp->hdr.Status = status; + rc = -EINVAL; + goto err_out; + } + + rc = init_smb3_11_server(conn); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + ksmbd_gen_preauth_integrity_hash(conn, + work->request_buf, + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); + assemble_neg_contexts(conn, rsp); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); + break; + case SMB30_PROT_ID: + init_smb3_0_server(conn); + break; + case SMB21_PROT_ID: + init_smb2_1_server(conn); + break; + case SMB20_PROT_ID: + rc = init_smb2_0_server(conn); + if (rc) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto err_out; + } + break; + case SMB2X_PROT_ID: + case BAD_PROT_ID: + default: + ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", + conn->dialect); + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + } + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + + /* For stats */ + conn->connection_type = conn->dialect; + + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + if (conn->dialect > SMB20_PROT_ID) { + memcpy(conn->ClientGUID, req->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); + } + + rsp->StructureSize = cpu_to_le16(65); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying server + */ + memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", + le32_to_cpu(rsp->NegotiateContextOffset), + le16_to_cpu(rsp->NegotiateContextCount)); + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + + sizeof(rsp->hdr.smb2_buf_length)) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + + if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && + req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) + conn->sign = true; + else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + server_conf.enforced_signing = true; + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->sign = true; + } + + conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); + ksmbd_conn_set_need_negotiate(work); + +err_out: + if (rc < 0) + smb2_set_err_rsp(work); + + return rc; +} + +static int alloc_preauth_hash(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + if (sess->Preauth_HashValue) + return 0; + + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + if (!sess->Preauth_HashValue) + return -ENOMEM; + + return 0; +} + +static int generate_preauth_hash(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + u8 *preauth_hash; + + if (conn->dialect != SMB311_PROT_ID) + return 0; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) { + preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); + if (!preauth_sess) + return -ENOMEM; + } + + preauth_hash = preauth_sess->Preauth_HashValue; + } else { + if (!sess->Preauth_HashValue) + if (alloc_preauth_hash(sess, conn)) + return -ENOMEM; + preauth_hash = sess->Preauth_HashValue; + } + + ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); + return 0; +} + +static int decode_negotiation_token(struct ksmbd_conn *conn, + struct negotiate_message *negblob, + size_t sz) +{ + if (!conn->use_spnego) + return -EINVAL; + + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; + conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; + conn->use_spnego = false; + } + } + return 0; +} + +static int ntlm_negotiate(struct ksmbd_work *work, + struct negotiate_message *negblob, + size_t negblob_len) +{ + struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct challenge_message *chgblob; + unsigned char *spnego_blob = NULL; + u16 spnego_blob_len; + char *neg_blob; + int sz, rc; + + ksmbd_debug(SMB, "negotiate phase\n"); + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); + if (rc) + return rc; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + chgblob = + (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (!work->conn->use_spnego) { + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) + return -ENOMEM; + + rsp->SecurityBufferLength = cpu_to_le16(sz); + return 0; + } + + sz = sizeof(struct challenge_message); + sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + + neg_blob = kzalloc(sz, GFP_KERNEL); + if (!neg_blob) + return -ENOMEM; + + chgblob = (struct challenge_message *)neg_blob; + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) { + rc = -ENOMEM; + goto out; + } + + rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, + neg_blob, sz); + if (rc) { + rc = -ENOMEM; + goto out; + } + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + +out: + kfree(spnego_blob); + kfree(neg_blob); + return rc; +} + +static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + int sz; + + if (conn->use_spnego && conn->mechToken) + return (struct authenticate_message *)conn->mechToken; + + sz = le16_to_cpu(req->SecurityBufferOffset); + return (struct authenticate_message *)((char *)&req->hdr.ProtocolId + + sz); +} + +static struct ksmbd_user *session_user(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + struct authenticate_message *authblob; + struct ksmbd_user *user; + char *name; + unsigned int auth_msg_len, name_off, name_len, secbuf_len; + + secbuf_len = le16_to_cpu(req->SecurityBufferLength); + if (secbuf_len < sizeof(struct authenticate_message)) { + ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); + return NULL; + } + authblob = user_authblob(conn, req); + name_off = le32_to_cpu(authblob->UserName.BufferOffset); + name_len = le16_to_cpu(authblob->UserName.Length); + auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len; + + if (auth_msg_len < (u64)name_off + name_len) + return NULL; + + name = smb_strndup_from_utf16((const char *)authblob + name_off, + name_len, + true, + conn->local_nls); + if (IS_ERR(name)) { + pr_err("cannot allocate memory\n"); + return NULL; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + user = ksmbd_login_user(name); + kfree(name); + return user; +} + +static int ntlm_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct channel *chann = NULL; + struct ksmbd_user *user; + u64 prev_id; + int sz, rc; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego) { + unsigned char *spnego_blob; + u16 spnego_blob_len; + + rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, + 0); + if (rc) + return -ENOMEM; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(rsp, spnego_blob_len - 1); + } + + user = session_user(conn, req); + if (!user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + return -EPERM; + } + + /* Check for previous session */ + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(user, prev_id); + + if (sess->state == SMB2_SESSION_VALID) { + /* + * Reuse session if anonymous try to connect + * on reauthetication. + */ + if (ksmbd_anonymous_user(user)) { + ksmbd_free_user(user); + return 0; + } + + if (!ksmbd_compare_user(sess->user, user)) { + ksmbd_free_user(user); + return -EPERM; + } + ksmbd_free_user(user); + } else { + sess->user = user; + } + + if (user_guest(sess->user)) { + if (conn->sign) { + ksmbd_debug(SMB, "Guest login not allowed when signing enabled\n"); + return -EPERM; + } + + rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; + } else { + struct authenticate_message *authblob; + + authblob = user_authblob(conn, req); + sz = le16_to_cpu(req->SecurityBufferLength); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); + if (rc) { + set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); + ksmbd_debug(SMB, "authentication failed\n"); + return -EPERM; + } + + /* + * If session state is SMB2_SESSION_VALID, We can assume + * that it is reauthentication. And the user/password + * has been verified, so return it here. + */ + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; + return 0; + } + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn) && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + rc = conn->ops->generate_encryptionkey(sess); + if (rc) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session + */ + sess->sign = false; + } + } + +binding_session: + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + INIT_LIST_HEAD(&chann->chann_list); + list_add(&chann->chann_list, &sess->ksmbd_chann_list); + } + } + + if (conn->ops->generate_signingkey) { + rc = conn->ops->generate_signingkey(sess, conn); + if (rc) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (conn->dialect > SMB20_PROT_ID) { + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + } + return 0; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +static int krb5_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + char *in_blob, *out_blob; + struct channel *chann = NULL; + u64 prev_sess_id; + int in_len, out_len; + int retval; + + in_blob = (char *)&req->hdr.ProtocolId + + le16_to_cpu(req->SecurityBufferOffset); + in_len = le16_to_cpu(req->SecurityBufferLength); + out_blob = (char *)&rsp->hdr.ProtocolId + + le16_to_cpu(rsp->SecurityBufferOffset); + out_len = work->response_sz - + offsetof(struct smb2_hdr, smb2_buf_length) - + le16_to_cpu(rsp->SecurityBufferOffset); + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(sess->user, prev_sess_id); + + if (sess->state == SMB2_SESSION_VALID) + ksmbd_free_user(sess->user); + + retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, + out_blob, &out_len); + if (retval) { + ksmbd_debug(SMB, "krb5 authentication failed\n"); + return -EINVAL; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); + inc_rfc1001_len(rsp, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn)) { + retval = conn->ops->generate_encryptionkey(sess); + if (retval) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + INIT_LIST_HEAD(&chann->chann_list); + list_add(&chann->chann_list, &sess->ksmbd_chann_list); + } + } + + if (conn->ops->generate_signingkey) { + retval = conn->ops->generate_signingkey(sess, conn); + if (retval) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (conn->dialect > SMB20_PROT_ID) { + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + } + return 0; +} +#else +static int krb5_authenticate(struct ksmbd_work *work) +{ + return -EOPNOTSUPP; +} +#endif + +int smb2_sess_setup(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct ksmbd_session *sess; + struct negotiate_message *negblob; + unsigned int negblob_len, negblob_off; + int rc = 0; + + ksmbd_debug(SMB, "Received request for session setup\n"); + + rsp->StructureSize = cpu_to_le16(9); + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; + inc_rfc1001_len(rsp, 9); + + if (!req->hdr.SessionId) { + sess = ksmbd_smb2_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + rsp->hdr.SessionId = cpu_to_le64(sess->id); + ksmbd_session_register(conn, sess); + } else if (conn->dialect >= SMB30_PROT_ID && + (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + sess = ksmbd_session_lookup_slowpath(sess_id); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (conn->dialect != sess->conn->dialect) { + rc = -EINVAL; + goto out_err; + } + + if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { + rc = -EINVAL; + goto out_err; + } + + if (strncmp(conn->ClientGUID, sess->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_IN_PROGRESS) { + rc = -EACCES; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_session_lookup(conn, sess_id)) { + rc = -EACCES; + goto out_err; + } + + conn->binding = true; + } else if ((conn->dialect < SMB30_PROT_ID || + server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + sess = NULL; + rc = -EACCES; + goto out_err; + } else { + sess = ksmbd_session_lookup(conn, + le64_to_cpu(req->hdr.SessionId)); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + } + work->sess = sess; + + if (sess->state == SMB2_SESSION_EXPIRED) + sess->state = SMB2_SESSION_IN_PROGRESS; + + negblob_off = le16_to_cpu(req->SecurityBufferOffset); + negblob_len = le16_to_cpu(req->SecurityBufferLength); + if (negblob_off < (offsetof(struct smb2_sess_setup_req, Buffer) - 4) || + negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { + rc = -EINVAL; + goto out_err; + } + + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + + negblob_off); + + if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + } + + if (server_conf.auth_mechs & conn->auth_mechs) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + + if (conn->preferred_auth_mech & + (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { + rc = krb5_authenticate(work); + if (rc) { + rc = -EINVAL; + goto out_err; + } + + ksmbd_conn_set_good(work); + sess->state = SMB2_SESSION_VALID; + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { + if (negblob->MessageType == NtLmNegotiate) { + rc = ntlm_negotiate(work, negblob, negblob_len); + if (rc) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an + * adjustment for 0 size blob + */ + inc_rfc1001_len(rsp, le16_to_cpu(rsp->SecurityBufferLength) - 1); + + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work); + if (rc) + goto out_err; + + ksmbd_conn_set_good(work); + sess->state = SMB2_SESSION_VALID; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = + ksmbd_preauth_session_lookup(conn, sess->id); + if (preauth_sess) { + list_del(&preauth_sess->preauth_entry); + kfree(preauth_sess); + } + } + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } + } else { + /* TODO: need one more negotiation */ + pr_err("Not support the preferred authentication\n"); + rc = -EINVAL; + } + } else { + pr_err("Not support authentication\n"); + rc = -EINVAL; + } + +out_err: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc) + rsp->hdr.Status = STATUS_LOGON_FAILURE; + + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + if (rc < 0) { + /* + * SecurityBufferOffset should be set to zero + * in session setup error response. + */ + rsp->SecurityBufferOffset = 0; + + if (sess) { + bool try_delay = false; + + /* + * To avoid dictionary attacks (repeated session setups rapidly sent) to + * connect to server, ksmbd make a delay of a 5 seconds on session setup + * failure to make it harder to send enough random connection requests + * to break into a server. + */ + if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) + try_delay = true; + + ksmbd_session_destroy(sess); + work->sess = NULL; + if (try_delay) + ssleep(5); + } + } + + return rc; +} + +/** + * smb2_tree_connect() - handler for smb2 tree connect command + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_tree_connect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_tree_connect_req *req = work->request_buf; + struct smb2_tree_connect_rsp *rsp = work->response_buf; + struct ksmbd_session *sess = work->sess; + char *treename = NULL, *name = NULL; + struct ksmbd_tree_conn_status status; + struct ksmbd_share_config *share; + int rc = -EINVAL; + + treename = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->PathLength), true, + conn->local_nls); + if (IS_ERR(treename)) { + pr_err("treename is NULL\n"); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + name = ksmbd_extract_sharename(treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", + name, treename); + + status = ksmbd_tree_conn_connect(sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); + else + goto out_err1; + + share = status.tree_conn->share_conf; + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC share path request\n"); + rsp->ShareType = SMB2_SHARE_TYPE_PIPE; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | + FILE_DELETE_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } else { + rsp->ShareType = SMB2_SHARE_TYPE_DISK; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + rsp->MaximalAccess |= FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | + FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | + FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } + } + + status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + +out_err1: + rsp->StructureSize = cpu_to_le16(16); + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + inc_rfc1001_len(rsp, 16); + + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) + kfree(name); + + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_OK: + rsp->hdr.Status = STATUS_SUCCESS; + rc = 0; + break; + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp->hdr.Status = STATUS_BAD_NETWORK_PATH; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + break; + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + default: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + } + + return rc; +} + +/** + * smb2_create_open_flags() - convert smb open flags to unix open flags + * @file_present: is file already present + * @access: file access flags + * @disposition: file disposition flags + * @may_flags: set with MAY_ flags + * + * Return: file open flags + */ +static int smb2_create_open_flags(bool file_present, __le32 access, + __le32 disposition, + int *may_flags) +{ + int oflags = O_NONBLOCK | O_LARGEFILE; + + if (access & FILE_READ_DESIRED_ACCESS_LE && + access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_RDWR; + *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; + } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_WRONLY; + *may_flags = MAY_OPEN | MAY_WRITE; + } else { + oflags |= O_RDONLY; + *may_flags = MAY_OPEN | MAY_READ; + } + + if (access == FILE_READ_ATTRIBUTES_LE) + oflags |= O_PATH; + + if (file_present) { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_OPEN_LE: + case FILE_CREATE_LE: + break; + case FILE_SUPERSEDE_LE: + case FILE_OVERWRITE_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_SUPERSEDE_LE: + case FILE_CREATE_LE: + case FILE_OPEN_IF_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_CREAT; + break; + case FILE_OPEN_LE: + case FILE_OVERWRITE_LE: + oflags &= ~O_CREAT; + break; + default: + break; + } + } + + return oflags; +} + +/** + * smb2_tree_disconnect() - handler for smb tree connect request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_tree_disconnect(struct ksmbd_work *work) +{ + struct smb2_tree_disconnect_rsp *rsp = work->response_buf; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(rsp, 4); + + ksmbd_debug(SMB, "request\n"); + + if (!tcon) { + struct smb2_tree_disconnect_req *req = work->request_buf; + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + return 0; +} + +/** + * smb2_session_logoff() - handler for session log off request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_session_logoff(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_logoff_rsp *rsp = work->response_buf; + struct ksmbd_session *sess = work->sess; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(rsp, 4); + + ksmbd_debug(SMB, "request\n"); + + /* setting CifsExiting here may race with start_tcp_sess */ + ksmbd_conn_set_need_reconnect(work); + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn); + + if (ksmbd_tree_conn_session_logoff(sess)) { + struct smb2_logoff_req *req = work->request_buf; + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_destroy_file_table(&sess->file_table); + sess->state = SMB2_SESSION_EXPIRED; + + ksmbd_free_user(sess->user); + sess->user = NULL; + + /* let start_tcp_sess free connection info now */ + ksmbd_conn_set_need_negotiate(work); + return 0; +} + +/** + * create_smb2_pipe() - create IPC pipe + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int create_smb2_pipe(struct ksmbd_work *work) +{ + struct smb2_create_rsp *rsp = work->response_buf; + struct smb2_create_req *req = work->request_buf; + int id; + int err; + char *name; + + name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), + 1, work->conn->local_nls); + if (IS_ERR(name)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + err = PTR_ERR(name); + goto out; + } + + id = ksmbd_session_rpc_open(work->sess, name); + if (id < 0) { + pr_err("Unable to open RPC pipe: %d\n", id); + err = id; + goto out; + } + + rsp->hdr.Status = STATUS_SUCCESS; + rsp->StructureSize = cpu_to_le16(89); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->CreateAction = cpu_to_le32(FILE_OPENED); + + rsp->CreationTime = cpu_to_le64(0); + rsp->LastAccessTime = cpu_to_le64(0); + rsp->ChangeTime = cpu_to_le64(0); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndofFile = cpu_to_le64(0); + rsp->FileAttributes = ATTR_NORMAL_LE; + rsp->Reserved2 = 0; + rsp->VolatileFileId = cpu_to_le64(id); + rsp->PersistentFileId = 0; + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + + inc_rfc1001_len(rsp, 88); /* StructureSize - 1*/ + kfree(name); + return 0; + +out: + switch (err) { + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + } + + if (!IS_ERR(name)) + kfree(name); + + smb2_set_err_rsp(work); + return err; +} + +/** + * smb2_set_ea() - handler for setting extended attributes using set + * info command + * @eabuf: set info command buffer + * @buf_len: set info command buffer length + * @path: dentry path for get ea + * + * Return: 0 on success, otherwise error + */ +static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *attr_name = NULL, *value; + int rc = 0; + unsigned int next = 0; + + if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + + le16_to_cpu(eabuf->EaValueLength)) + return -EINVAL; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) + return -ENOMEM; + + do { + if (!eabuf->EaNameLength) + goto next; + + ksmbd_debug(SMB, + "name : <%s>, name_len : %u, value_len : %u, next : %u\n", + eabuf->name, eabuf->EaNameLength, + le16_to_cpu(eabuf->EaValueLength), + le32_to_cpu(eabuf->NextEntryOffset)); + + if (eabuf->EaNameLength > + (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + break; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, + eabuf->EaNameLength); + attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; + value = (char *)&eabuf->name + eabuf->EaNameLength + 1; + + if (!eabuf->EaValueLength) { + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + attr_name, + XATTR_USER_PREFIX_LEN + + eabuf->EaNameLength); + + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(user_ns, + path->dentry, + attr_name); + + if (rc < 0) { + ksmbd_debug(SMB, + "remove xattr failed(%d)\n", + rc); + break; + } + } + + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { + rc = ksmbd_vfs_setxattr(user_ns, + path->dentry, attr_name, value, + le16_to_cpu(eabuf->EaValueLength), 0); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", + rc); + break; + } + } + +next: + next = le32_to_cpu(eabuf->NextEntryOffset); + if (next == 0 || buf_len < next) + break; + buf_len -= next; + eabuf = (struct smb2_ea_info *)((char *)eabuf + next); + if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) + break; + + } while (next != 0); + + kfree(attr_name); + return rc; +} + +static noinline int smb2_set_stream_name_xattr(struct path *path, + struct ksmbd_file *fp, + char *stream_name, int s_type) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + size_t xattr_stream_size; + char *xattr_stream_name; + int rc; + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + return rc; + + fp->stream.name = xattr_stream_name; + fp->stream.size = xattr_stream_size; + + /* Check if there is stream prefix in xattr space */ + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + xattr_stream_name, + xattr_stream_size); + if (rc >= 0) + return 0; + + if (fp->cdoption == FILE_OPEN_LE) { + ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); + return -EBADF; + } + + rc = ksmbd_vfs_setxattr(user_ns, path->dentry, + xattr_stream_name, NULL, 0, 0); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +} + +static int smb2_remove_smb_xattrs(struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + strncmp(&name[XATTR_USER_PREFIX_LEN], DOS_ATTRIBUTE_PREFIX, + DOS_ATTRIBUTE_PREFIX_LEN) && + strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) + continue; + + err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } +out: + kvfree(xattr_list); + return err; +} + +static int smb2_create_truncate(struct path *path) +{ + int rc = vfs_truncate(path, 0); + + if (rc) { + pr_err("vfs_truncate failed, rc %d\n", rc); + return rc; + } + + rc = smb2_remove_smb_xattrs(path); + if (rc == -EOPNOTSUPP) + rc = 0; + if (rc) + ksmbd_debug(SMB, + "ksmbd_truncate_stream_name_xattr failed, rc %d\n", + rc); + return rc; +} + +static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, + struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da = {0}; + int rc; + + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + da.version = 4; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); +} + +static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + struct path *path, struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da; + int rc; + + fp->f_ci->m_fattr &= ~(ATTR_HIDDEN_LE | ATTR_SYSTEM_LE); + + /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc > 0) { + fp->f_ci->m_fattr = cpu_to_le32(da.attr); + fp->create_time = da.create_time; + fp->itime = da.itime; + } +} + +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + int open_flags, umode_t posix_mode, bool is_dir) +{ + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; + umode_t mode; + int rc; + + if (!(open_flags & O_CREAT)) + return -EBADF; + + ksmbd_debug(SMB, "file does not exist, so creating\n"); + if (is_dir == true) { + ksmbd_debug(SMB, "creating directory\n"); + + mode = share_config_directory_mode(share, posix_mode); + rc = ksmbd_vfs_mkdir(work, name, mode); + if (rc) + return rc; + } else { + ksmbd_debug(SMB, "creating regular file\n"); + + mode = share_config_create_mode(share, posix_mode); + rc = ksmbd_vfs_create(work, name, mode); + if (rc) + return rc; + } + + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); + return rc; + } + return 0; +} + +static int smb2_create_sd_buffer(struct ksmbd_work *work, + struct smb2_create_req *req, + struct path *path) +{ + struct create_context *context; + struct create_sd_buf_req *sd_buf; + + if (!req->CreateContextsOffset) + return -ENOENT; + + /* Parse SD BUFFER create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER); + if (!context) + return -ENOENT; + else if (IS_ERR(context)) + return PTR_ERR(context); + + ksmbd_debug(SMB, + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + sd_buf = (struct create_sd_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_sd_buf_req)) + return -EINVAL; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, + le32_to_cpu(sd_buf->ccontext.DataLength), true); +} + +static void ksmbd_acls_fattr(struct smb_fattr *fattr, + struct user_namespace *mnt_userns, + struct inode *inode) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + fattr->cf_uid = i_uid_into_mnt(mnt_userns, inode); + fattr->cf_gid = i_gid_into_mnt(mnt_userns, inode); +#else + fattr->cf_uid = inode->i_uid; + fattr->cf_gid = inode->i_gid; +#endif + fattr->cf_mode = inode->i_mode; + fattr->cf_acls = NULL; + fattr->cf_dacls = NULL; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); + } +} + +/** + * smb2_open() - handler for smb file open request + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_open(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp, *rsp_org; + struct path path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; + struct user_namespace *user_ns = NULL; + struct kstat stat; + struct create_context *context; + struct lease_ctx_info *lc = NULL; + struct create_ea_buf_req *ea_buf = NULL; + struct oplock_info *opinfo; + __le32 *next_ptr = NULL; + int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; + int rc = 0; + int contxt_cnt = 0, query_disk_id = 0; + int maximal_access_ctxt = 0, posix_ctxt = 0; + int s_type = 0; + int next_off = 0; + char *name = NULL; + char *stream_name = NULL; + bool file_present = false, created = false, already_permitted = false; + int share_ret, need_truncate = 0; + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && + (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "invalid flag in chained command\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return -EINVAL; + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe create request\n"); + return create_smb2_pipe(work); + } + + if (req->NameLength) { + if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + *(char *)req->Buffer == '\\') { + pr_err("not allow directory name included leading slash\n"); + rc = -EINVAL; + goto err_out1; + } + + name = smb2_get_name(share, + req->Buffer, + le16_to_cpu(req->NameLength), + work->conn->local_nls); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + if (rc != -ENOMEM) + rc = -ENOENT; + name = NULL; + goto err_out1; + } + + ksmbd_debug(SMB, "converted name = %s\n", name); + if (strchr(name, ':')) { + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; + goto err_out1; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out1; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) + goto err_out1; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); + goto err_out1; + } + } else { + name = kstrdup("", GFP_KERNEL); + if (!name) { + rc = -ENOMEM; + goto err_out1; + } + } + + req_op_level = req->RequestedOplockLevel; + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + lc = parse_lease_state(req); + + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; + goto err_out1; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; + goto err_out1; + } else { + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) + req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); + + if (req->CreateOptions & + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; + goto err_out1; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; + goto err_out1; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } + } + } + + if (le32_to_cpu(req->CreateDisposition) > + le32_to_cpu(FILE_OVERWRITE_IF_LE)) { + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; + goto err_out1; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; + goto err_out1; + } + + if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; + goto err_out1; + } + + if (req->CreateContextsOffset) { + /* Parse non-durable handle create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; + goto err_out1; + } + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; + goto err_out1; + } + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, + "get query maximal access context\n"); + maximal_access_ctxt = 1; + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_TIMEWARP_REQUEST); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; + goto err_out1; + } + + if (tcon->posix_extensions) { + context = smb2_find_context_vals(req, + SMB2_CREATE_TAG_POSIX); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + struct create_posix *posix = + (struct create_posix *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix)) { + rc = -EINVAL; + goto err_out1; + } + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = 1; + } + } + } + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out1; + } + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (!rc) { + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * If file exists with under flags, return access + * denied error. + */ + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + path_put(&path); + goto err_out; + } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + } + + if (rc) { + if (rc != -ENOENT) + goto err_out; + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; + } else { + file_present = true; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + user_ns = mnt_user_ns(path.mnt); + generic_fillattr(user_ns, d_inode(path.dentry), &stat); +#else + user_ns = NULL; + generic_fillattr(d_inode(path.dentry), &stat); +#endif + } + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + } + } else { + if (S_ISDIR(stat.mode) && s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && + req->FileAttributes & ATTR_NORMAL_LE) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + } + + if (rc < 0) + goto err_out; + } + + if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && + S_ISDIR(stat.mode) && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", + name, req->CreateOptions); + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + !(req->CreateDisposition == FILE_CREATE_LE) && + !S_ISDIR(stat.mode)) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (!stream_name && file_present && + req->CreateDisposition == FILE_CREATE_LE) { + rc = -EEXIST; + goto err_out; + } + + daccess = smb_map_generic_desired_access(req->DesiredAccess); + + if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = smb_check_perm_dacl(conn, &path, &daccess, + sess->user->uid); + if (rc) + goto err_out; + } + + if (daccess & FILE_MAXIMAL_ACCESS_LE) { + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { + rc = ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &daccess); + if (rc) + goto err_out; + already_permitted = true; + } + maximal_access = daccess; + } + + open_flags = smb2_create_open_flags(file_present, daccess, + req->CreateDisposition, + &may_flags); + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + } + + /*create file if not present */ + if (!file_present) { + rc = smb2_creat(work, &path, name, open_flags, posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) { + if (rc == -ENOENT) { + rc = -EIO; + rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + goto err_out; + } + + created = true; + user_ns = mnt_user_ns(path.mnt); + if (ea_buf) { + if (le32_to_cpu(ea_buf->ccontext.DataLength) < + sizeof(struct smb2_ea_info)) { + rc = -EINVAL; + goto err_out; + } + + rc = smb2_set_ea(&ea_buf->ea, + le32_to_cpu(ea_buf->ccontext.DataLength), + &path); + if (rc == -EOPNOTSUPP) + rc = 0; + else if (rc) + goto err_out; + } + } else if (!already_permitted) { + /* FILE_READ_ATTRIBUTE is allowed without inode_permission, + * because execute(search) permission on a parent directory, + * is already granted. + */ + if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = inode_permission(user_ns, + d_inode(path.dentry), + may_flags); +#else + rc = inode_permission(d_inode(path.dentry), may_flags); +#endif + if (rc) + goto err_out; + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = ksmbd_vfs_may_delete(user_ns, + path.dentry); + if (rc) + goto err_out; + } + } + } + + rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + filp = dentry_open(&path, open_flags, current_cred()); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + pr_err("dentry open for dir failed, rc %d\n", rc); + goto err_out; + } + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = FILE_OPENED; + else + file_info = FILE_OVERWRITTEN; + + if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == + FILE_SUPERSEDE_LE) + file_info = FILE_SUPERSEDED; + } else if (open_flags & O_CREAT) { + file_info = FILE_CREATED; + } + + ksmbd_vfs_set_fadvise(filp, req->CreateOptions); + + /* Obtain Volatile-ID */ + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + rc = PTR_ERR(fp); + fp = NULL; + goto err_out; + } + + /* Get Persistent-ID */ + ksmbd_open_durable_fd(fp); + if (!has_file_id(fp->persistent_id)) { + rc = -ENOMEM; + goto err_out; + } + + fp->filename = name; + fp->cdoption = req->CreateDisposition; + fp->daccess = daccess; + fp->saccess = req->ShareAccess; + fp->coption = req->CreateOptions; + + /* Set default windows and posix acls if creating new file */ + if (created) { + int posix_acl_rc; + struct inode *inode = d_inode(path.dentry); + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, + inode, + d_inode(path.dentry->d_parent)); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, &path, sess->user->uid, + sess->user->gid); + } + + if (rc) { + rc = smb2_create_sd_buffer(work, req, &path); + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(user_ns, + inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + struct smb_fattr fattr; + struct smb_ntsd *pntsd; + int pntsd_size, ace_num = 0; + + ksmbd_acls_fattr(&fattr, user_ns, inode); + if (fattr.cf_acls) + ace_num = fattr.cf_acls->a_count; + if (fattr.cf_dacls) + ace_num += fattr.cf_dacls->a_count; + + pntsd = kmalloc(sizeof(struct smb_ntsd) + + sizeof(struct smb_sid) * 3 + + sizeof(struct smb_acl) + + sizeof(struct smb_ace) * ace_num * 2, + GFP_KERNEL); + if (!pntsd) + goto err_out; + + rc = build_sec_desc(user_ns, + pntsd, NULL, + OWNER_SECINFO | + GROUP_SECINFO | + DACL_SECINFO, + &pntsd_size, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + if (rc) { + kfree(pntsd); + goto err_out; + } + + rc = ksmbd_vfs_set_sd_xattr(conn, + user_ns, + path.dentry, + pntsd, + pntsd_size); + kfree(pntsd); + if (rc) + pr_err("failed to store ntacl in xattr : %d\n", + rc); + } + } + } + rc = 0; + } + + if (stream_name) { + rc = smb2_set_stream_name_xattr(&path, + fp, + stream_name, + s_type); + if (rc) + goto err_out; + file_info = FILE_CREATED; + } + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && + !fp->attrib_only && !stream_name) { + smb_break_all_oplock(work, fp); + need_truncate = 1; + } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are + * initialized. + */ + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(user_ns, d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + rc = 0; + } + + /* Check delete pending among previous fp before oplock break */ + if (ksmbd_inode_pending_delete(fp)) { + rc = -EBUSY; + goto err_out; + } + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && + !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { + rc = share_ret; + goto err_out; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) + goto err_out; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + rc = smb_grant_oplock(work, req_op_level, + fp->persistent_id, fp, + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) + goto err_out; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + + if (need_truncate) { + rc = smb2_create_truncate(&path); + if (rc) + goto err_out; + } + + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + + az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, + SMB2_CREATE_ALLOCATION_SIZE); + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); + goto err_out; + } else if (az_req) { + loff_t alloc_size; + int err; + + if (le16_to_cpu(az_req->ccontext.DataOffset) + + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; + goto err_out; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); + ksmbd_debug(SMB, + "request smb2 create allocate size : %llu\n", + alloc_size); + smb_break_all_levII_oplock(work, fp, 1); + err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_size); + if (err < 0) + ksmbd_debug(SMB, + "vfs_fallocate is failed : %d\n", + err); + } + + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out; + } else if (context) { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; + } + } + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (req->FileAttributes || fp->f_ci->m_fattr == 0) + fp->f_ci->m_fattr = + cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); + + if (!created) + smb2_update_xattrs(tcon, &path, fp); + else + smb2_new_xattrs(tcon, &path, fp); + + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(user_ns, file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + + rsp->StructureSize = cpu_to_le16(89); + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; + rcu_read_unlock(); + rsp->Reserved = 0; + rsp->CreateAction = cpu_to_le32(file_info); + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.blocks << 9); + rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + rsp->FileAttributes = fp->f_ci->m_fattr; + + rsp->Reserved2 = 0; + + rsp->PersistentFileId = cpu_to_le64(fp->persistent_id); + rsp->VolatileFileId = cpu_to_le64(fp->volatile_id); + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + inc_rfc1001_len(rsp_org, 88); /* StructureSize - 1*/ + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { + struct create_context *lease_ccontext; + + ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", + name, opinfo->o_lease->state); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + + lease_ccontext = (struct create_context *)rsp->Buffer; + contxt_cnt++; + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); + inc_rfc1001_len(rsp_org, conn->vals->create_lease_size); + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } + + if (maximal_access_ctxt) { + struct create_context *mxac_ccontext; + + if (maximal_access == 0) + ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &maximal_access); + mxac_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_mxac_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); + inc_rfc1001_len(rsp_org, conn->vals->create_mxac_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; + next_off = conn->vals->create_mxac_size; + } + + if (query_disk_id) { + struct create_context *disk_id_ccontext; + + disk_id_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_disk_id_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); + inc_rfc1001_len(rsp_org, conn->vals->create_disk_id_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; + next_off = conn->vals->create_disk_id_size; + } + + if (posix_ctxt) { + contxt_cnt++; + create_posix_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); + inc_rfc1001_len(rsp_org, conn->vals->create_posix_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } + + if (contxt_cnt > 0) { + rsp->CreateContextsOffset = + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer) + - 4); + } + +err_out: + if (file_present || created) + path_put(&path); + ksmbd_revert_fsids(work); +err_out1: + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EPERM) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -EBUSY) + rsp->hdr.Status = STATUS_DELETE_PENDING; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (rc == -ENOEXEC) + rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; + else if (rc == -ENXIO) + rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rc == -EMFILE) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + if (!fp || !fp->filename) + kfree(name); + if (fp) + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + + kfree(lc); + + return 0; +} + +static int readdir_info_level_struct_sz(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_full_directory_info); + case FILE_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_both_directory_info); + case FILE_DIRECTORY_INFORMATION: + return sizeof(struct file_directory_info); + case FILE_NAMES_INFORMATION: + return sizeof(struct file_names_info); + case FILEID_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_id_full_dir_info); + case FILEID_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_POSIX_INFO: + return sizeof(struct smb2_posix_info); + default: + return -EOPNOTSUPP; + } +} + +static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); + d_info->name = ffdinfo->FileName; + d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); + return 0; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); + d_info->name = fbdinfo->FileName; + d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); + return 0; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); + d_info->name = fdinfo->FileName; + d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); + return 0; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); + d_info->name = fninfo->FileName; + d_info->name_len = le32_to_cpu(fninfo->FileNameLength); + return 0; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); + d_info->name = dinfo->FileName; + d_info->name_len = le32_to_cpu(dinfo->FileNameLength); + return 0; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); + d_info->name = fibdinfo->FileName; + d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); + return 0; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); + d_info->name = posix_info->name; + d_info->name_len = le32_to_cpu(posix_info->name_len); + return 0; + } + default: + return -EINVAL; + } +} + +/** + * smb2_populate_readdir_entry() - encode directory entry in smb2 response + * buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @user_ns: user namespace + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, + struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset = 0; + char *conv_name; + int conv_len; + void *kstat; + int struct_sz, rc = 0; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + /* Somehow the name has only terminating NULL bytes */ + if (conv_len < 0) { + rc = -EINVAL; + goto free_conv_name; + } + + struct_sz = readdir_info_level_struct_sz(info_level); + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + rc = -ENOSPC; + goto free_conv_name; + } + + kstat = d_info->wptr; + if (info_level != FILE_NAMES_INFORMATION) + kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (ffdinfo->EaSize) + ffdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + ffdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fbdinfo->EaSize) + fbdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + if (d_info->hide_dot_file && d_info->name[0] == '.') + fbdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)kstat; + fdinfo->FileNameLength = cpu_to_le32(conv_len); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)kstat; + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)kstat; + dinfo->FileNameLength = cpu_to_le32(conv_len); + dinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (dinfo->EaSize) + dinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (d_info->hide_dot_file && d_info->name[0] == '.') + dinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)kstat; + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fibdinfo->EaSize) + fibdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = cpu_to_le16(0); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fibdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + u64 time; + + posix_info = (struct smb2_posix_info *)kstat; + posix_info->Ignored = 0; + posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + posix_info->ChangeTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); + posix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); + posix_info->LastWriteTime = cpu_to_le64(time); + posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); + posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); + posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); + posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); + posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode); + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); + posix_info->DosAttributes = + S_ISDIR(ksmbd_kstat->kstat->mode) ? ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + posix_info->DosAttributes |= ATTR_HIDDEN_LE; + id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), + SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); + id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), + SIDNFS_GROUP, (struct smb_sid *)&posix_info->SidBuffer[20]); + memcpy(posix_info->name, conv_name, conv_len); + posix_info->name_len = cpu_to_le32(conv_len); + posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + + } /* switch (info_level) */ + + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + + ksmbd_debug(SMB, + "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + +free_conv_name: + kfree(conv_name); + return rc; +} + +struct smb2_query_dir_private { + struct ksmbd_work *work; + char *search_pattern; + struct ksmbd_file *dir_fp; + + struct ksmbd_dir_info *d_info; + int info_level; +}; + +static void lock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); +} + +static void unlock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_unlock(d_inode(dir)); +} + +static int process_query_dir_entries(struct smb2_query_dir_private *priv) +{ + struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + int rc; + int i; + + for (i = 0; i < priv->d_info->num_entry; i++) { + struct dentry *dent; + + if (dentry_name(priv->d_info, priv->info_level)) + return -EINVAL; + + lock_dir(priv->dir_fp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + dent = lookup_one(user_ns, priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); +#else + dent = lookup_one_len(priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); +#endif + unlock_dir(priv->dir_fp); + + if (IS_ERR(dent)) { + ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", + priv->d_info->name, + PTR_ERR(dent)); + continue; + } + if (unlikely(d_is_negative(dent))) { + dput(dent); + ksmbd_debug(SMB, "Negative dentry `%s'\n", + priv->d_info->name); + continue; + } + + ksmbd_kstat.kstat = &kstat; + if (priv->info_level != FILE_NAMES_INFORMATION) + ksmbd_vfs_fill_dentry_attrs(priv->work, + user_ns, + dent, + &ksmbd_kstat); + + rc = smb2_populate_readdir_entry(priv->work->conn, + priv->info_level, + priv->d_info, + &ksmbd_kstat); + dput(dent); + if (rc) + return rc; + } + return 0; +} + +static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + int info_level) +{ + int struct_sz; + int conv_len; + int next_entry_offset; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + return -ENOSPC; + } + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->wptr; + memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); + ffdinfo->FileName[d_info->name_len] = 0x00; + ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->wptr; + memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); + fbdinfo->FileName[d_info->name_len] = 0x00; + fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->wptr; + memcpy(fdinfo->FileName, d_info->name, d_info->name_len); + fdinfo->FileName[d_info->name_len] = 0x00; + fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->wptr; + memcpy(fninfo->FileName, d_info->name, d_info->name_len); + fninfo->FileName[d_info->name_len] = 0x00; + fninfo->FileNameLength = cpu_to_le32(d_info->name_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->wptr; + memcpy(dinfo->FileName, d_info->name, d_info->name_len); + dinfo->FileName[d_info->name_len] = 0x00; + dinfo->FileNameLength = cpu_to_le32(d_info->name_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; + memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); + fibdinfo->FileName[d_info->name_len] = 0x00; + fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->wptr; + memcpy(posix_info->name, d_info->name, d_info->name_len); + posix_info->name[d_info->name_len] = 0x00; + posix_info->name_len = cpu_to_le32(d_info->name_len); + posix_info->NextEntryOffset = + cpu_to_le32(next_entry_offset); + break; + } + } /* switch (info_level) */ + + d_info->num_entry++; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + return 0; +} + +static int __query_dir(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + struct smb2_query_dir_private *priv; + struct ksmbd_dir_info *d_info; + int rc; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + priv = buf->private; + d_info = priv->d_info; + + /* dot and dotdot entries are already reserved */ + if (!strcmp(".", name) || !strcmp("..", name)) + return 0; + if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) + return 0; + if (!match_pattern(name, namlen, priv->search_pattern)) + return 0; + + d_info->name = name; + d_info->name_len = namlen; + rc = reserve_populate_dentry(d_info, priv->info_level); + if (rc) + return rc; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + return 0; + } + return 0; +} + +static void restart_ctx(struct dir_context *ctx) +{ + ctx->pos = 0; +} + +static int verify_info_level(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + case FILE_BOTH_DIRECTORY_INFORMATION: + case FILE_DIRECTORY_INFORMATION: + case FILE_NAMES_INFORMATION: + case FILEID_FULL_DIRECTORY_INFORMATION: + case FILEID_BOTH_DIRECTORY_INFORMATION: + case SMB_FIND_FILE_POSIX_INFO: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, + unsigned short hdr2_len, + unsigned int out_buf_len) +{ + int free_len; + + if (out_buf_len > work->conn->vals->max_trans_size) + return -EINVAL; + + free_len = (int)(work->response_sz - + (get_rfc1002_len(work->response_buf) + 4)) - + hdr2_len; + if (free_len < 0) + return -EINVAL; + + return min_t(int, out_buf_len, free_len); +} + +int smb2_query_dir(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_query_directory_req *req; + struct smb2_query_directory_rsp *rsp, *rsp_org; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_file *dir_fp = NULL; + struct ksmbd_dir_info d_info; + int rc = 0; + char *srch_ptr = NULL; + unsigned char srch_flag; + int buffer_sz; + struct smb2_query_dir_private query_dir_private = {NULL, }; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + smb2_set_err_rsp(work); + return -ENOMEM; + } + + rc = verify_info_level(req->FileInformationClass); + if (rc) { + rc = -EFAULT; + goto err_out2; + } + + dir_fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!dir_fp) { + rc = -EBADF; + goto err_out2; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(file_mnt_user_ns(dir_fp->filp), + file_inode(dir_fp->filp), + MAY_READ | MAY_EXEC)) { +#else + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { +#endif + pr_err("no right to enumerate directory (%pd)\n", + dir_fp->filp->f_path.dentry); + rc = -EACCES; + goto err_out2; + } + + if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { + pr_err("can't do query dir for a file\n"); + rc = -EINVAL; + goto err_out2; + } + + srch_flag = req->Flags; + srch_ptr = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); + if (IS_ERR(srch_ptr)) { + ksmbd_debug(SMB, "Search Pattern not found\n"); + rc = -EINVAL; + goto err_out2; + } else { + ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); + } + + ksmbd_debug(SMB, "Directory name is %s\n", dir_fp->filename); + + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { + ksmbd_debug(SMB, "Restart directory scan\n"); + generic_file_llseek(dir_fp->filp, 0, SEEK_SET); + restart_ctx(&dir_fp->readdir_data.ctx); + } + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + d_info.wptr = (char *)rsp->Buffer; + d_info.rptr = (char *)rsp->Buffer; + d_info.out_buf_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (d_info.out_buf_len < 0) { + rc = -EINVAL; + goto err_out; + } + d_info.flags = srch_flag; + + /* + * reserve dot and dotdot entries in head of buffer + * in first response + */ + rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, + dir_fp, &d_info, srch_ptr, + smb2_populate_readdir_entry); + if (rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) + d_info.hide_dot_file = true; + + buffer_sz = d_info.out_buf_len; + d_info.rptr = d_info.wptr; + query_dir_private.work = work; + query_dir_private.search_pattern = srch_ptr; + query_dir_private.dir_fp = dir_fp; + query_dir_private.d_info = &d_info; + query_dir_private.info_level = req->FileInformationClass; + dir_fp->readdir_data.private = &query_dir_private; + set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); + + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + if (rc == 0) + restart_ctx(&dir_fp->readdir_data.ctx); + if (rc == -ENOSPC) + rc = 0; + if (rc) + goto err_out; + + d_info.wptr = d_info.rptr; + d_info.out_buf_len = buffer_sz; + rc = process_query_dir_entries(&query_dir_private); + if (rc) + goto err_out; + + if (!d_info.data_count && d_info.out_buf_len >= 0) { + if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + } else { + dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; + rsp->hdr.Status = STATUS_NO_MORE_FILES; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; + inc_rfc1001_len(rsp_org, 9); + } else { + ((struct file_directory_info *) + ((char *)rsp->Buffer + d_info.last_entry_offset)) + ->NextEntryOffset = 0; + + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); + inc_rfc1001_len(rsp_org, 8 + d_info.data_count); + } + + kfree(srch_ptr); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; + +err_out: + pr_err("error while processing smb2 query dir rc = %d\n", rc); + kfree(srch_ptr); + +err_out2: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * buffer_check_err() - helper function to check buffer errors + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ +static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, int infoclass_size) +{ + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { + if (reqOutputBufferLength < infoclass_size) { + pr_err("Invalid Buffer Size Requested\n"); + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4); + return -EINVAL; + } + + ksmbd_debug(SMB, "Buffer Overflow\n"); + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4 + + reqOutputBufferLength); + rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); + } + return 0; +} + +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) +{ + struct smb2_file_standard_info *sinfo; + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + + sinfo->AllocationSize = cpu_to_le64(4096); + sinfo->EndOfFile = cpu_to_le64(0); + sinfo->NumberOfLinks = cpu_to_le32(1); + sinfo->DeletePending = 1; + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp, sizeof(struct smb2_file_standard_info)); +} + +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num) +{ + struct smb2_file_internal_info *file_info; + + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + + /* any unique number */ + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp, sizeof(struct smb2_file_internal_info)); +} + +static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + u64 id; + int rc; + + /* + * Windows can sometime send query file info request on + * pipe without opening it, checking error condition here + */ + id = le64_to_cpu(req->VolatileFileId); + if (!ksmbd_session_rpc_method(sess, id)) + return -ENOENT; + + ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", + req->FileInfoClass, le64_to_cpu(req->VolatileFileId)); + + switch (req->FileInfoClass) { + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, FILE_STANDARD_INFORMATION_SIZE); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, FILE_INTERNAL_INFORMATION_SIZE); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", + req->FileInfoClass); + rc = -EOPNOTSUPP; + } + return rc; +} + +/** + * smb2_get_ea() - handler for smb2 get extended attribute command + * @work: smb work containing query info command buffer + * @fp: ksmbd_file pointer + * @req: get extended attribute request + * @rsp: response buffer pointer + * @rsp_org: base response buffer pointer in case of chained response + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_ea_info *eainfo, *prev_eainfo; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len, idx; + ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; + struct smb2_ea_info_req *ea_req = NULL; + struct path *path; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + + if (!(fp->daccess & FILE_READ_EA_LE)) { + pr_err("Not permitted to read ext attr : 0x%x\n", + fp->daccess); + return -EACCES; + } + + path = &fp->filp->f_path; + /* single EA entry is requested with given user.* name */ + if (req->InputBufferLength) { + if (le32_to_cpu(req->InputBufferLength) < + sizeof(struct smb2_ea_info_req)) + return -EINVAL; + + ea_req = (struct smb2_ea_info_req *)req->Buffer; + } else { + /* need to send all EAs, if no specific EA is requested*/ + if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) + ksmbd_debug(SMB, + "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", + le32_to_cpu(req->Flags)); + } + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + return -EINVAL; + + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + ksmbd_debug(SMB, "no ea data in the file\n"); + goto done; + } + xattr_list_len = rc; + + ptr = (char *)rsp->Buffer; + eainfo = (struct smb2_ea_info *)ptr; + prev_eainfo = eainfo; + idx = 0; + + while (idx < xattr_list_len) { + name = xattr_list + idx; + name_len = strlen(name); + + ksmbd_debug(SMB, "%s, len %d\n", name, name_len); + idx += name_len + 1; + + /* + * CIFS does not support EA other than user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) + continue; + + if (req->InputBufferLength && + strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, + ea_req->EaNameLength)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) + continue; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&eainfo->name + name_len + 1); + buf_free_len -= (offsetof(struct smb2_ea_info, name) + + name_len + 1); + /* bailout if xattr can't fit in buf_free_len */ + value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, + name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } + + buf_free_len -= value_len; + if (buf_free_len < 0) { + kfree(buf); + break; + } + + memcpy(ptr, buf, value_len); + kfree(buf); + + ptr += value_len; + eainfo->Flags = 0; + eainfo->EaNameLength = name_len; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(eainfo->name, name, name_len); + + eainfo->name[name_len] = '\0'; + eainfo->EaValueLength = cpu_to_le16(value_len); + next_offset = offsetof(struct smb2_ea_info, name) + + name_len + 1 + value_len; + + /* align next xattr entry at 4 byte bundary */ + alignment_bytes = ((next_offset + 3) & ~3) - next_offset; + if (alignment_bytes) { + memset(ptr, '\0', alignment_bytes); + ptr += alignment_bytes; + next_offset += alignment_bytes; + buf_free_len -= alignment_bytes; + } + eainfo->NextEntryOffset = cpu_to_le32(next_offset); + prev_eainfo = eainfo; + eainfo = (struct smb2_ea_info *)ptr; + rsp_data_cnt += next_offset; + + if (req->InputBufferLength) { + ksmbd_debug(SMB, "single entry requested\n"); + break; + } + } + + /* no more ea entries */ + prev_eainfo->NextEntryOffset = 0; +done: + rc = 0; + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); + inc_rfc1001_len(rsp_org, rsp_data_cnt); +out: + kvfree(xattr_list); + return rc; +} + +static void get_file_access_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_access_info *file_info; + + file_info = (struct smb2_file_access_info *)rsp->Buffer; + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); +} + +static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_basic_info *basic_info; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + basic_info = (struct smb2_file_basic_info *)rsp->Buffer; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + basic_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = fp->f_ci->m_fattr; + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_basic_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); + return 0; +} + +static unsigned long long get_allocation_size(struct inode *inode, + struct kstat *stat) +{ + unsigned long long alloc_size = 0; + + if (!S_ISDIR(stat->mode)) { + if ((inode->i_blocks << 9) <= stat->size) + alloc_size = stat->size; + else + alloc_size = inode->i_blocks << 9; + } + + return alloc_size; +} + +static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + + inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); +#else + generic_fillattr(inode, &stat); +#endif + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + delete_pending = ksmbd_inode_pending_delete(fp); + + sinfo->AllocationSize = cpu_to_le64(get_allocation_size(inode, &stat)); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); + sinfo->DeletePending = delete_pending; + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_standard_info)); +} + +static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_alignment_info *file_info; + + file_info = (struct smb2_file_alignment_info *)rsp->Buffer; + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_alignment_info)); +} + +static int get_file_all_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_all_info *file_info; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + int conv_len; + char *filename; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + filename = convert_to_nt_pathname(fp->filename); + if (!filename) + return -ENOMEM; + + inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); +#else + generic_fillattr(inode, &stat); +#endif + + ksmbd_debug(SMB, "filename = %s\n", filename); + delete_pending = ksmbd_inode_pending_delete(fp); + file_info = (struct smb2_file_all_info *)rsp->Buffer; + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->Pad1 = 0; + file_info->AllocationSize = + cpu_to_le64(get_allocation_size(inode, &stat)); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->NumberOfLinks = + cpu_to_le32(get_nlink(&stat) - delete_pending); + file_info->DeletePending = delete_pending; + file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; + file_info->Pad2 = 0; + file_info->IndexNumber = cpu_to_le64(stat.ino); + file_info->EASize = 0; + file_info->AccessFlags = fp->daccess; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + file_info->Mode = fp->coption; + file_info->AlignmentRequirement = 0; + conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, + PATH_MAX, conn->local_nls, 0); + conv_len *= 2; + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; +} + +static void get_file_alternate_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_alt_name_info *file_info; + struct dentry *dentry = fp->filp->f_path.dentry; + int conv_len; + + spin_lock(&dentry->d_lock); + file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; + conv_len = ksmbd_extract_shortname(conn, + dentry->d_name.name, + file_info->FileName); + spin_unlock(&dentry->d_lock); + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); +} + +static void get_file_stream_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_stream_info *file_info; + char *stream_name, *xattr_list = NULL, *stream_buf; + struct kstat stat; + struct path *path = &fp->filp->f_path; + ssize_t xattr_list_len; + int nbytes = 0, streamlen, stream_name_len, next, idx = 0; + int buf_free_len; + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + file_info = (struct smb2_file_stream_info *)rsp->Buffer; + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + goto out; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + while (idx < xattr_list_len) { + stream_name = xattr_list + idx; + streamlen = strlen(stream_name); + idx += streamlen + 1; + + ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); + + if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], + STREAM_PREFIX, STREAM_PREFIX_LEN)) + continue; + + stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + + STREAM_PREFIX_LEN); + streamlen = stream_name_len; + + /* plus : size */ + streamlen += 1; + stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + if (!stream_buf) + break; + + streamlen = snprintf(stream_buf, streamlen + 1, + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + + next = sizeof(struct smb2_file_stream_info) + streamlen * 2; + if (next > buf_free_len) + break; + + file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + stream_buf, streamlen, + conn->local_nls, 0); + streamlen *= 2; + kfree(stream_buf); + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stream_name_len); + file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); + + nbytes += next; + buf_free_len -= next; + file_info->NextEntryOffset = cpu_to_le32(next); + } + +out: + if (!S_ISDIR(stat.mode) && + buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + "::$DATA", 7, conn->local_nls, 0); + streamlen *= 2; + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stat.size); + file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); + nbytes += sizeof(struct smb2_file_stream_info) + streamlen; + } + + /* last entry offset should be 0 */ + file_info->NextEntryOffset = 0; + kvfree(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); + inc_rfc1001_len(rsp_org, nbytes); +} + +static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + struct kstat stat; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_ntwrk_info *file_info; + struct inode *inode; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + + inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); +#else + generic_fillattr(inode, &stat); +#endif + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->AllocationSize = + cpu_to_le64(get_allocation_size(inode, &stat)); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; +} + +static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_file_ea_info *file_info; + + file_info = (struct smb2_file_ea_info *)rsp->Buffer; + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); +} + +static void get_file_position_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_pos_info *file_info; + + file_info = (struct smb2_file_pos_info *)rsp->Buffer; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); +} + +static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_mode_info *file_info; + + file_info = (struct smb2_file_mode_info *)rsp->Buffer; + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); +} + +static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_comp_info *file_info; + struct kstat stat; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + + file_info = (struct smb2_file_comp_info *)rsp->Buffer; + file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); + file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; + file_info->CompressionUnitShift = 0; + file_info->ChunkShift = 0; + file_info->ClusterShift = 0; + memset(&file_info->Reserved[0], 0, 3); + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); +} + +static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_attr_tag_info *file_info; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; + file_info->FileAttributes = fp->f_ci->m_fattr; + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); + return 0; +} + +static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb311_posix_qinfo *file_info; + struct inode *inode = file_inode(fp->filp); + u64 time; + + file_info = (struct smb311_posix_qinfo *)rsp->Buffer; + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->DosAttributes = fp->f_ci->m_fattr; + file_info->Inode = cpu_to_le64(inode->i_ino); + file_info->EndOfFile = cpu_to_le64(inode->i_size); + file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + file_info->HardLinks = cpu_to_le32(inode->i_nlink); + file_info->Mode = cpu_to_le32(inode->i_mode); + file_info->DeviceId = cpu_to_le32(inode->i_rdev); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb311_posix_qinfo)); + inc_rfc1001_len(rsp_org, sizeof(struct smb311_posix_qinfo)); + return 0; +} + +static int smb2_get_info_file(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; + int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + /* smb2 info file called for pipe */ + return smb2_get_info_file_pipe(work->sess, req, rsp); + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = le64_to_cpu(req->VolatileFileId); + pid = le64_to_cpu(req->PersistentFileId); + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + fileinfoclass = req->FileInfoClass; + + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, rsp_org); + file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, rsp_org); + file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, rsp_org); + file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, rsp_org); + file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, rsp_org); + file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, rsp_org); + file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + rc = find_file_posix_info(rsp, fp, rsp_org); + file_infoclass_size = sizeof(struct smb311_posix_qinfo); + } + break; + default: + ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", + fileinfoclass); + rc = -EOPNOTSUPP; + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, + file_infoclass_size); + ksmbd_fd_put(work, fp); + return rc; +} + +static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_conn *conn = sess->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + int fsinfoclass = 0; + struct kstatfs stfs; + struct path path; + int rc = 0, len; + int fs_infoclass_size = 0; + + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (rc) { + pr_err("cannot create vfs path\n"); + return -EIO; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + pr_err("cannot do stat of path %s\n", share->path); + path_put(&path); + return -EIO; + } + + fsinfoclass = req->FileInfoClass; + + switch (fsinfoclass) { + case FS_DEVICE_INFORMATION: + { + struct filesystem_device_info *info; + + info = (struct filesystem_device_info *)rsp->Buffer; + + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); + inc_rfc1001_len(rsp_org, 8); + fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: + { + struct filesystem_attribute_info *info; + size_t sz; + + info = (struct filesystem_attribute_info *)rsp->Buffer; + info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | + FILE_PERSISTENT_ACLS | + FILE_UNICODE_ON_DISK | + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_SUPPORTS_BLOCK_REFCOUNTING); + + info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); + + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + len = smbConvertToUTF16((__le16 *)info->FileSystemName, + "NTFS", PATH_MAX, conn->local_nls, 0); + len = len * 2; + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(rsp_org, sz); + fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: + { + struct filesystem_vol_info *info; + size_t sz; + unsigned int serial_crc = 0; + + info = (struct filesystem_vol_info *)(rsp->Buffer); + info->VolumeCreationTime = 0; + serial_crc = crc32_le(serial_crc, share->name, + strlen(share->name)); + serial_crc = crc32_le(serial_crc, share->path, + strlen(share->path)); + serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), + strlen(ksmbd_netbios_name())); + /* Taking dummy value of serial number*/ + info->SerialNumber = cpu_to_le32(serial_crc); + len = smbConvertToUTF16((__le16 *)info->VolumeLabel, + share->name, PATH_MAX, + conn->local_nls, 0); + len = len * 2; + info->VolumeLabelSize = cpu_to_le32(len); + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(rsp_org, sz); + fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: + { + struct filesystem_info *info; + + info = (struct filesystem_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(24); + inc_rfc1001_len(rsp_org, 24); + fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: + { + struct smb2_fs_full_size_info *info; + + info = (struct smb2_fs_full_size_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->CallerAvailableAllocationUnits = + cpu_to_le64(stfs.f_bavail); + info->ActualAvailableAllocationUnits = + cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(32); + inc_rfc1001_len(rsp_org, 32); + fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: + { + struct object_id_info *info; + + info = (struct object_id_info *)(rsp->Buffer); + + if (!user_guest(sess->user)) + memcpy(info->objid, user_passkey(sess->user), 16); + else + memset(info->objid, 0, 16); + + info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); + info->extended_info.version = cpu_to_le32(1); + info->extended_info.release = cpu_to_le32(1); + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); + inc_rfc1001_len(rsp_org, 64); + fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: + { + struct smb3_fs_ss_info *info; + + info = (struct smb3_fs_ss_info *)(rsp->Buffer); + + info->LogicalBytesPerSector = cpu_to_le32(stfs.f_bsize); + info->PhysicalBytesPerSectorForAtomicity = + cpu_to_le32(stfs.f_bsize); + info->PhysicalBytesPerSectorForPerf = cpu_to_le32(stfs.f_bsize); + info->FSEffPhysicalBytesPerSectorForAtomicity = + cpu_to_le32(stfs.f_bsize); + info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | + SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); + inc_rfc1001_len(rsp_org, 28); + fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: + { + /* + * TODO : The current implementation is based on + * test result with win7(NTFS) server. It's need to + * modify this to get valid Quota values + * from Linux kernel + */ + struct smb2_fs_control_info *info; + + info = (struct smb2_fs_control_info *)(rsp->Buffer); + info->FreeSpaceStartFiltering = 0; + info->FreeSpaceThreshold = 0; + info->FreeSpaceStopFiltering = 0; + info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); + inc_rfc1001_len(rsp_org, 48); + fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: + { + struct filesystem_posix_info *info; + + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + info = (struct filesystem_posix_info *)(rsp->Buffer); + info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); + info->BlockSize = cpu_to_le32(stfs.f_bsize); + info->TotalBlocks = cpu_to_le64(stfs.f_blocks); + info->BlocksAvail = cpu_to_le64(stfs.f_bfree); + info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); + inc_rfc1001_len(rsp_org, 56); + fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } + default: + path_put(&path); + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, + fs_infoclass_size); + path_put(&path); + return rc; +} + +static int smb2_get_info_sec(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_fattr fattr = {{0}}; + struct inode *inode; + __u32 secdesclen; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + int addition_info = le32_to_cpu(req->AdditionalInformation); + int rc; + + if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + PROTECTED_DACL_SECINFO | + UNPROTECTED_DACL_SECINFO)) { + ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", + addition_info); + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); + pntsd->osidoffset = 0; + pntsd->gsidoffset = 0; + pntsd->sacloffset = 0; + pntsd->dacloffset = 0; + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(rsp_org, secdesclen); + + return 0; + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = le64_to_cpu(req->VolatileFileId); + pid = le64_to_cpu(req->PersistentFileId); + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + user_ns = file_mnt_user_ns(fp->filp); + inode = file_inode(fp->filp); + ksmbd_acls_fattr(&fattr, user_ns, inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) + ksmbd_vfs_get_sd_xattr(work->conn, user_ns, + fp->filp->f_path.dentry, &ppntsd); + + rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info, + &secdesclen, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + kfree(ppntsd); + ksmbd_fd_put(work, fp); + if (rc) + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(rsp_org, secdesclen); + return 0; +} + +/** + * smb2_query_info() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_query_info(struct ksmbd_work *work) +{ + struct smb2_query_info_req *req; + struct smb2_query_info_rsp *rsp, *rsp_org; + int rc = 0; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "GOT query info request\n"); + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_get_info_file(work, req, rsp, (void *)rsp_org); + break; + case SMB2_O_INFO_FILESYSTEM: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); + rc = smb2_get_info_filesystem(work, req, rsp, (void *)rsp_org); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + rc = smb2_get_info_sec(work, req, rsp, (void *)rsp_org); + break; + default: + ksmbd_debug(SMB, "InfoType %d not supported yet\n", + req->InfoType); + rc = -EOPNOTSUPP; + } + + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", + rc); + return rc; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + inc_rfc1001_len(rsp_org, 8); + return 0; +} + +/** + * smb2_close_pipe() - handler for closing IPC pipe + * @work: smb work containing close request buffer + * + * Return: 0 + */ +static noinline int smb2_close_pipe(struct ksmbd_work *work) +{ + u64 id; + struct smb2_close_req *req = work->request_buf; + struct smb2_close_rsp *rsp = work->response_buf; + + id = le64_to_cpu(req->VolatileFileId); + ksmbd_session_rpc_close(work->sess, id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Flags = 0; + rsp->Reserved = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + inc_rfc1001_len(rsp, 60); + return 0; +} + +/** + * smb2_close() - handler for smb2 close file command + * @work: smb work containing close request buffer + * + * Return: 0 + */ +int smb2_close(struct ksmbd_work *work) +{ + u64 volatile_id = KSMBD_NO_FID; + u64 sess_id; + struct smb2_close_req *req; + struct smb2_close_rsp *rsp; + struct smb2_close_rsp *rsp_org; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_file *fp; + struct inode *inode; + u64 time; + int err = 0; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe close request\n"); + return smb2_close_pipe(work); + } + + sess_id = le64_to_cpu(req->hdr.SessionId); + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + sess_id = work->compound_sid; + + work->compound_sid = 0; + if (check_session_id(conn, sess_id)) { + work->compound_sid = sess_id; + } else { + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EBADF; + goto out; + } + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(le64_to_cpu(req->VolatileFileId))) { + if (!has_file_id(work->compound_fid)) { + /* file already closed, return FILE_CLOSED */ + ksmbd_debug(SMB, "file already closed\n"); + rsp->hdr.Status = STATUS_FILE_CLOSED; + err = -EBADF; + goto out; + } else { + ksmbd_debug(SMB, + "Compound request set FID = %llu:%llu\n", + work->compound_fid, + work->compound_pfid); + volatile_id = work->compound_fid; + + /* file closed, stored id is not valid anymore */ + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + } else { + volatile_id = le64_to_cpu(req->VolatileFileId); + } + ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Reserved = 0; + + if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { + fp = ksmbd_lookup_fd_fast(work, volatile_id); + if (!fp) { + err = -ENOENT; + goto out; + } + + inode = file_inode(fp->filp); + rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : + cpu_to_le64(inode->i_blocks << 9); + rsp->EndOfFile = cpu_to_le64(inode->i_size); + rsp->Attributes = fp->f_ci->m_fattr; + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + rsp->ChangeTime = cpu_to_le64(time); + ksmbd_fd_put(work, fp); + } else { + rsp->Flags = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + } + + err = ksmbd_close_fd(work, volatile_id); +out: + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + } else { + inc_rfc1001_len(rsp_org, 60); + } + + return 0; +} + +/** + * smb2_echo() - handler for smb2 echo(ping) command + * @work: smb work containing echo request buffer + * + * Return: 0 + */ +int smb2_echo(struct ksmbd_work *work) +{ + struct smb2_echo_rsp *rsp = work->response_buf; + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(rsp, 4); + return 0; +} + +static int smb2_rename(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct user_namespace *user_ns, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) +{ + struct ksmbd_share_config *share = fp->tcon->share_conf; + char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; + char *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + abs_oldname = d_path(&fp->filp->f_path, pathname, PATH_MAX); + if (IS_ERR(abs_oldname)) { + rc = -EINVAL; + goto out; + } + old_name = strrchr(abs_oldname, '/'); + if (old_name && old_name[1] != '\0') { + old_name++; + } else { + ksmbd_debug(SMB, "can't get last component in path %s\n", + abs_oldname); + rc = -ENOENT; + goto out; + } + + new_name = smb2_get_name(share, + file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(new_name)) { + rc = PTR_ERR(new_name); + goto out; + } + + if (strchr(new_name, ':')) { + int s_type; + char *xattr_stream_name, *stream_name = NULL; + size_t xattr_stream_size; + int len; + + rc = parse_stream_name(new_name, &stream_name, &s_type); + if (rc < 0) + goto out; + + len = strlen(new_name); + if (len > 0 && new_name[len - 1] != '/') { + pr_err("not allow base filename in rename\n"); + rc = -ESHARE; + goto out; + } + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + goto out; + + rc = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + xattr_stream_name, + NULL, 0, 0); + if (rc < 0) { + pr_err("failed to store stream name in xattr: %d\n", + rc); + rc = -EINVAL; + goto out; + } + + goto out; + } + + ksmbd_debug(SMB, "new name %s\n", new_name); + rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, new_name); + if (rc) { + if (rc != -ENOTEMPTY) + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s, rc %d\n", + new_name, rc); + goto out; + } + } + } else { + if (file_present && + strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { + rc = -EEXIST; + ksmbd_debug(SMB, + "cannot rename already existing file\n"); + goto out; + } + } + + rc = ksmbd_vfs_fp_rename(work, fp, new_name); +out: + kfree(pathname); + if (!IS_ERR(new_name)) + kfree(new_name); + return rc; +} + +static int smb2_create_link(struct ksmbd_work *work, + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, + unsigned int buf_len, struct file *filp, + struct nls_table *local_nls) +{ + char *link_name = NULL, *target_name = NULL, *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + + le32_to_cpu(file_info->FileNameLength)) + return -EINVAL; + + ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + link_name = smb2_get_name(share, + file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "link name is %s\n", link_name); + target_name = d_path(&filp->f_path, pathname, PATH_MAX); + if (IS_ERR(target_name)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); + rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, link_name); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", + link_name); + goto out; + } + } + } else { + if (file_present) { + rc = -EEXIST; + ksmbd_debug(SMB, "link already exists\n"); + goto out; + } + } + + rc = ksmbd_vfs_link(work, target_name, link_name); + if (rc) + rc = -EINVAL; +out: + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); + return rc; +} + +static int set_file_basic_info(struct ksmbd_file *fp, + struct smb2_file_basic_info *file_info, + struct ksmbd_share_config *share) +{ + struct iattr attrs; + struct file *filp; + struct inode *inode; + struct user_namespace *user_ns; + int rc = 0; + + if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) + return -EACCES; + + attrs.ia_valid = 0; + filp = fp->filp; + inode = file_inode(filp); + user_ns = file_mnt_user_ns(filp); + + if (file_info->CreationTime) + fp->create_time = le64_to_cpu(file_info->CreationTime); + + if (file_info->LastAccessTime) { + attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + attrs.ia_valid |= ATTR_CTIME; + if (file_info->ChangeTime) + attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); + else + attrs.ia_ctime = inode->i_ctime; + + if (file_info->LastWriteTime) { + attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + + if (file_info->Attributes) { + if (!S_ISDIR(inode->i_mode) && + file_info->Attributes & ATTR_DIRECTORY_LE) { + pr_err("can't change a file to a directory\n"); + return -EINVAL; + } + + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == ATTR_NORMAL_LE)) + fp->f_ci->m_fattr = file_info->Attributes | + (fp->f_ci->m_fattr & ATTR_DIRECTORY_LE); + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + (file_info->CreationTime || file_info->Attributes)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.itime = fp->itime; + da.create_time = fp->create_time; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + filp->f_path.dentry, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); + rc = 0; + } + + if (attrs.ia_valid) { + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = d_inode(dentry); + + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EACCES; + + inode_lock(inode); + inode->i_ctime = attrs.ia_ctime; + attrs.ia_valid &= ~ATTR_CTIME; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = notify_change(user_ns, dentry, &attrs, NULL); +#else + rc = notify_change(dentry, &attrs, NULL); +#endif + inode_unlock(inode); + if (rc) + return -EINVAL; + } + return rc; +} + +static int set_file_allocation_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct smb2_file_alloc_info *file_alloc_info) +{ + /* + * TODO : It's working fine only when store dos attributes + * is not yes. need to implement a logic which works + * properly with any smb.conf option + */ + + loff_t alloc_blks; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + inode = file_inode(fp->filp); + + if (alloc_blks > inode->i_blocks) { + smb_break_all_levII_oplock(work, fp, 1); + rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_blks * 512); + if (rc && rc != -EOPNOTSUPP) { + pr_err("vfs_fallocate is failed : %d\n", rc); + return rc; + } + } else if (alloc_blks < inode->i_blocks) { + loff_t size; + + /* + * Allocation size could be smaller than original one + * which means allocated blocks in file should be + * deallocated. use truncate to cut out it, but inode + * size is also updated with truncate offset. + * inode size is retained by backup inode size. + */ + size = i_size_read(inode); + rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); + if (rc) { + pr_err("truncate failed! filename : %s, err %d\n", + fp->filename, rc); + return rc; + } + if (size < alloc_blks * 512) + i_size_write(inode, size); + } + return 0; +} + +static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_eof_info *file_eof_info) +{ + loff_t newsize; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + newsize = le64_to_cpu(file_eof_info->EndOfFile); + inode = file_inode(fp->filp); + + /* + * If FILE_END_OF_FILE_INFORMATION of set_info_file is called + * on FAT32 shared device, truncate execution time is too long + * and network error could cause from windows client. because + * truncate of some filesystem like FAT32 fill zero data in + * truncated range. + */ + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n", + fp->filename, newsize); + rc = ksmbd_vfs_truncate(work, fp, newsize); + if (rc) { + ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n", + fp->filename, rc); + if (rc != -EAGAIN) + rc = -EBADF; + return rc; + } + } + return 0; +} + +static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) +{ + struct user_namespace *user_ns; + struct ksmbd_file *parent_fp; + struct dentry *parent; + struct dentry *dentry = fp->filp->f_path.dentry; + int ret; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + + user_ns = file_mnt_user_ns(fp->filp); + if (ksmbd_stream_fd(fp)) + goto next; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); + inode_unlock(d_inode(parent)); + dput(parent); + + if (parent_fp) { + if (parent_fp->daccess & FILE_DELETE_LE) { + pr_err("parent dir is opened with delete access\n"); + return -ESHARE; + } + } +next: + return smb2_rename(work, fp, user_ns, rename_info, + work->sess->conn->local_nls); +} + +static int set_file_disposition_info(struct ksmbd_file *fp, + struct smb2_file_disposition_info *file_info) +{ + struct inode *inode; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + inode = file_inode(fp->filp); + if (file_info->DeletePending) { + if (S_ISDIR(inode->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) + return -EBUSY; + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + return 0; +} + +static int set_file_position_info(struct ksmbd_file *fp, + struct smb2_file_pos_info *file_info) +{ + loff_t current_byte_offset; + unsigned long sector_size; + struct inode *inode; + + inode = file_inode(fp->filp); + current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); + sector_size = inode->i_sb->s_blocksize; + + if (current_byte_offset < 0 || + (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && + current_byte_offset & (sector_size - 1))) { + pr_err("CurrentByteOffset is not valid : %llu\n", + current_byte_offset); + return -EINVAL; + } + + fp->filp->f_pos = current_byte_offset; + return 0; +} + +static int set_file_mode_info(struct ksmbd_file *fp, + struct smb2_file_mode_info *file_info) +{ + __le32 mode; + + mode = file_info->Mode; + + if ((mode & ~FILE_MODE_INFO_MASK) || + (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && + mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { + pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); + return -EINVAL; + } + + /* + * TODO : need to implement consideration for + * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT + */ + ksmbd_vfs_set_fadvise(fp->filp, mode); + fp->coption = mode; + return 0; +} + +/** + * smb2_set_info_file() - handler for smb2 set info command + * @work: smb work containing set info command buffer + * @fp: ksmbd_file pointer + * @info_class: smb2 set info class + * @share: ksmbd_share_config pointer + * + * Return: 0 on success, otherwise error + * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH + */ +static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_set_info_req *req, + struct ksmbd_share_config *share) +{ + unsigned int buf_len = le32_to_cpu(req->BufferLength); + + switch (req->FileInfoClass) { + case FILE_BASIC_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_basic_info)) + return -EINVAL; + + return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); + } + case FILE_ALLOCATION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_alloc_info)) + return -EINVAL; + + return set_file_allocation_info(work, fp, + (struct smb2_file_alloc_info *)req->Buffer); + } + case FILE_END_OF_FILE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_eof_info)) + return -EINVAL; + + return set_end_of_file_info(work, fp, + (struct smb2_file_eof_info *)req->Buffer); + } + case FILE_RENAME_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_rename_info)) + return -EINVAL; + + return set_rename_info(work, fp, + (struct smb2_file_rename_info *)req->Buffer, + buf_len); + } + case FILE_LINK_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_link_info)) + return -EINVAL; + + return smb2_create_link(work, work->tcon->share_conf, + (struct smb2_file_link_info *)req->Buffer, + buf_len, fp->filp, + work->sess->conn->local_nls); + } + case FILE_DISPOSITION_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_disposition_info)) + return -EINVAL; + + return set_file_disposition_info(fp, + (struct smb2_file_disposition_info *)req->Buffer); + } + case FILE_FULL_EA_INFORMATION: + { + if (!(fp->daccess & FILE_WRITE_EA_LE)) { + pr_err("Not permitted to write ext attr: 0x%x\n", + fp->daccess); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_ea_info)) + return -EINVAL; + + return smb2_set_ea((struct smb2_ea_info *)req->Buffer, + buf_len, &fp->filp->f_path); + } + case FILE_POSITION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_pos_info)) + return -EINVAL; + + return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); + } + case FILE_MODE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_mode_info)) + return -EINVAL; + + return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); + } + } + + pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); + return -EOPNOTSUPP; +} + +static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + char *buffer, int buf_len) +{ + struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; + + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, + buf_len, false); +} + +/** + * smb2_set_info() - handler for smb2 set info command handler + * @work: smb work containing set info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_set_info(struct ksmbd_work *work) +{ + struct smb2_set_info_req *req; + struct smb2_set_info_rsp *rsp, *rsp_org; + struct ksmbd_file *fp; + int rc = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + ksmbd_debug(SMB, "Received set info request\n"); + + rsp_org = work->response_buf; + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } else { + req = work->request_buf; + rsp = work->response_buf; + } + + if (!has_file_id(id)) { + id = le64_to_cpu(req->VolatileFileId); + pid = le64_to_cpu(req->PersistentFileId); + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %u\n", id); + rc = -ENOENT; + goto err_out; + } + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } + rc = smb2_set_info_sec(fp, + le32_to_cpu(req->AdditionalInformation), + req->Buffer, + le32_to_cpu(req->BufferLength)); + ksmbd_revert_fsids(work); + break; + default: + rc = -EOPNOTSUPP; + } + + if (rc < 0) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); + inc_rfc1001_len(rsp_org, 2); + ksmbd_fd_put(work, fp); + return 0; + +err_out: + if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EBUSY || rc == -ENOTEMPTY) + rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; + else if (rc == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (rc == -EBADF || rc == -ESTALE) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); + return rc; +} + +/** + * smb2_read_pipe() - handler for smb2 read from IPC pipe + * @work: smb work containing read IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_read_pipe(struct ksmbd_work *work) +{ + int nbytes = 0, err; + u64 id; + struct ksmbd_rpc_command *rpc_resp; + struct smb2_read_req *req = work->request_buf; + struct smb2_read_rsp *rsp = work->response_buf; + + id = le64_to_cpu(req->VolatileFileId); + + inc_rfc1001_len(rsp, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + + work->aux_payload_buf = + kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + memcpy(work->aux_payload_buf, rpc_resp->payload, + rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; + work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; + work->aux_payload_sz = nbytes; + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp, nbytes); + return 0; + +out: + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return err; +} + +static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, + struct smb2_read_req *req, void *data_buf, + size_t length) +{ + struct smb2_buffer_desc_v1 *desc = + (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; + int err; + + if (work->conn->dialect == SMB30_PROT_ID && + req->Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + if (req->ReadChannelInfoOffset == 0 || + le16_to_cpu(req->ReadChannelInfoLength) < sizeof(*desc)) + return -EINVAL; + + work->need_invalidate_rkey = + (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + work->remote_key = le32_to_cpu(desc->token); + + err = ksmbd_conn_rdma_write(work->conn, data_buf, length, + le32_to_cpu(desc->token), + le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); + if (err) + return err; + + return length; +} + +/** + * smb2_read() - handler for smb2 read from file + * @work: smb work containing read command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_read(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_read_req *req; + struct smb2_read_rsp *rsp, *rsp_org; + struct ksmbd_file *fp; + loff_t offset; + size_t length, mincount; + ssize_t nbytes = 0, remain_bytes = 0; + int err = 0; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe read request\n"); + return smb2_read_pipe(work); + } + + fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to read : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + mincount = le32_to_cpu(req->MinimumCount); + + if (length > conn->vals->max_read_size) { + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + conn->vals->max_read_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "filename %pd, offset %lld, len %zu\n", + fp->filp->f_path.dentry, offset, length); + + work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, length, &offset); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return 0; + } + + ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", + nbytes, offset, mincount); + + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, + work->aux_payload_buf, + nbytes); + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp_org, 16); + work->resp_hdr_sz = get_rfc1002_len(rsp_org) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(rsp_org, nbytes); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err) { + if (err == -EISDIR) + rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; + else if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + } + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_write_pipe() - handler for smb2 write on IPC pipe + * @work: smb work containing write IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_write_pipe(struct ksmbd_work *work) +{ + struct smb2_write_req *req = work->request_buf; + struct smb2_write_rsp *rsp = work->response_buf; + struct ksmbd_rpc_command *rpc_resp; + u64 id = 0; + int err = 0, ret = 0; + char *data_buf; + size_t length; + + length = le32_to_cpu(req->Length); + id = le64_to_cpu(req->VolatileFileId); + + if (le16_to_cpu(req->DataOffset) == + (offsetof(struct smb2_write_req, Buffer) - 4)) { + data_buf = (char *)&req->Buffer[0]; + } else { + if ((u64)le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req)) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), get_rfc1002_len(req)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + } + + rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + smb2_set_err_rsp(work); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return ret; + } + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp, 16); + return 0; +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + struct smb2_write_req *req, + struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) +{ + struct smb2_buffer_desc_v1 *desc; + char *data_buf; + int ret; + ssize_t nbytes; + + desc = (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; + + if (work->conn->dialect == SMB30_PROT_ID && + req->Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + if (req->Length != 0 || req->DataOffset != 0) + return -EINVAL; + + if (req->WriteChannelInfoOffset == 0 || + le16_to_cpu(req->WriteChannelInfoLength) < sizeof(*desc)) + return -EINVAL; + + work->need_invalidate_rkey = + (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + work->remote_key = le32_to_cpu(desc->token); + + data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!data_buf) + return -ENOMEM; + + ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, + le32_to_cpu(desc->token), + le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); + if (ret < 0) { + kvfree(data_buf); + return ret; + } + + ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); + kvfree(data_buf); + if (ret < 0) + return ret; + + return nbytes; +} + +/** + * smb2_write() - handler for smb2 write from file + * @work: smb work containing write command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_write(struct ksmbd_work *work) +{ + struct smb2_write_req *req; + struct smb2_write_rsp *rsp, *rsp_org; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length; + ssize_t nbytes; + char *data_buf; + bool writethrough = false; + int err = 0; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe write request\n"); + return smb2_write_pipe(work); + } + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + err = -EACCES; + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to write : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + + if (length > work->conn->vals->max_write_size) { + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + work->conn->vals->max_write_size); + err = -EINVAL; + goto out; + } + + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + if (req->Channel != SMB2_CHANNEL_RDMA_V1 && + req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { + if (le16_to_cpu(req->DataOffset) == + (offsetof(struct smb2_write_req, Buffer) - 4)) { + data_buf = (char *)&req->Buffer[0]; + } else { + if ((u64)le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req)) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), get_rfc1002_len(req)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + } + + ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + ksmbd_debug(SMB, "filename %pd, offset %lld, len %zu\n", + fp->filp->f_path.dentry, offset, length); + err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, + writethrough, &nbytes); + if (err < 0) + goto out; + } else { + /* read data from the client using rdma channel, and + * write the data. + */ + nbytes = smb2_write_rdma_channel(work, req, fp, offset, + le32_to_cpu(req->RemainingBytes), + writethrough); + if (nbytes < 0) { + err = (int)nbytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp_org, 16); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_flush() - handler for smb2 flush file - fsync + * @work: smb work containing flush command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_flush(struct ksmbd_work *work) +{ + struct smb2_flush_req *req; + struct smb2_flush_rsp *rsp, *rsp_org; + int err; + + rsp_org = work->response_buf; + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", + le64_to_cpu(req->VolatileFileId)); + + err = ksmbd_vfs_fsync(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (err) + goto out; + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(rsp_org, 4); + return 0; + +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +/** + * smb2_cancel() - handler for smb2 cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *chdr; + struct ksmbd_work *cancel_work = NULL; + int canceled = 0; + struct list_head *command_list; + + ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", + hdr->MessageId, hdr->Flags); + + if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { + command_list = &conn->async_requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(cancel_work, command_list, + async_request_entry) { + chdr = cancel_work->request_buf; + + if (cancel_work->async_id != + le64_to_cpu(hdr->Id.AsyncId)) + continue; + + ksmbd_debug(SMB, + "smb2 with AsyncId %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->Id.AsyncId), + le16_to_cpu(chdr->Command)); + canceled = 1; + break; + } + spin_unlock(&conn->request_lock); + } else { + command_list = &conn->requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(cancel_work, command_list, request_entry) { + chdr = cancel_work->request_buf; + + if (chdr->MessageId != hdr->MessageId || + cancel_work == work) + continue; + + ksmbd_debug(SMB, + "smb2 with mid %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->MessageId), + le16_to_cpu(chdr->Command)); + canceled = 1; + break; + } + spin_unlock(&conn->request_lock); + } + + if (canceled) { + cancel_work->state = KSMBD_WORK_CANCELLED; + if (cancel_work->cancel_fn) + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + + /* For SMB2_CANCEL command itself send no response*/ + work->send_no_response = 1; + return 0; +} + +struct file_lock *smb_flock_init(struct file *f) +{ + struct file_lock *fl; + + fl = locks_alloc_lock(); + if (!fl) + goto out; + + locks_init_lock(fl); + + fl->fl_owner = f; + fl->fl_pid = current->tgid; + fl->fl_file = f; + fl->fl_flags = FL_POSIX; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; + +out: + return fl; +} + +static int smb2_set_flock_flags(struct file_lock *flock, int flags) +{ + int cmd = -EINVAL; + + /* Checking for wrong flag combination during lock request*/ + switch (flags) { + case SMB2_LOCKFLAG_SHARED: + ksmbd_debug(SMB, "received shared request\n"); + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_EXCLUSIVE: + ksmbd_debug(SMB, "received exclusive request\n"); + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received shared & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_RDLCK; + break; + case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received exclusive & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_WRLCK; + break; + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; + cmd = 0; + break; + } + + return cmd; +} + +static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, + unsigned int cmd, int flags, + struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = flock->fl_start; + lock->end = flock->fl_end; + lock->flags = flags; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); + INIT_LIST_HEAD(&lock->llist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +static void smb2_remove_blocked_lock(void **argv) +{ + struct file_lock *flock = (struct file_lock *)argv[0]; + + ksmbd_vfs_posix_lock_unblock(flock); + wake_up(&flock->fl_wait); +} + +static inline bool lock_defer_pending(struct file_lock *fl) +{ + /* check pending lock waiters */ + return waitqueue_active(&fl->fl_wait); +} + +/** + * smb2_lock() - handler for smb2 file lock command + * @work: smb work containing lock command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_lock(struct ksmbd_work *work) +{ + struct smb2_lock_req *req = work->request_buf; + struct smb2_lock_rsp *rsp = work->response_buf; + struct smb2_lock_element *lock_ele; + struct ksmbd_file *fp = NULL; + struct file_lock *flock = NULL; + struct file *filp = NULL; + int lock_count; + int flags = 0; + int cmd = 0; + int err = -EIO, i, rc = 0; + u64 lock_start, lock_length; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + struct ksmbd_conn *conn; + int nolock = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int prior_lock = 0; + + ksmbd_debug(SMB, "Received lock request\n"); + fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) { + ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", + le64_to_cpu(req->VolatileFileId)); + err = -ENOENT; + goto out2; + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->LockCount); + lock_ele = req->locks; + + ksmbd_debug(SMB, "lock count is %d\n", lock_count); + if (!lock_count) { + err = -EINVAL; + goto out2; + } + + for (i = 0; i < lock_count; i++) { + flags = le32_to_cpu(lock_ele[i].Flags); + + flock = smb_flock_init(filp); + if (!flock) + goto out; + + cmd = smb2_set_flock_flags(flock, flags); + + lock_start = le64_to_cpu(lock_ele[i].Offset); + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_start > U64_MAX - lock_length) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + if (lock_start > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = lock_start; + + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_length > OFFSET_MAX - flock->fl_start) + lock_length = OFFSET_MAX - flock->fl_start; + + flock->fl_end = flock->fl_start + lock_length; + + if (flock->fl_end < flock->fl_start) { + ksmbd_debug(SMB, + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + /* Check conflict locks in one request */ + list_for_each_entry(cmp_lock, &lock_list, llist) { + if (cmp_lock->fl->fl_start <= flock->fl_start && + cmp_lock->fl->fl_end >= flock->fl_end) { + if (cmp_lock->fl->fl_type != F_UNLCK && + flock->fl_type != F_UNLCK) { + pr_err("conflict two locks in one request\n"); + err = -EINVAL; + goto out; + } + } + } + + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + err = -EINVAL; + goto out; + } + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + if (smb_lock->cmd < 0) { + err = -EINVAL; + goto out; + } + + if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { + err = -EINVAL; + goto out; + } + + if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && + smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || + (prior_lock == SMB2_LOCKFLAG_UNLOCK && + !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { + err = -EINVAL; + goto out; + } + + prior_lock = smb_lock->flags; + + if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && + !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) + goto no_check_cl; + + nolock = 1; + /* check locks in connection list */ + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->fl->fl_type == F_UNLCK) { + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { + nolock = 0; + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + goto out_check_cl; + } + continue; + } + + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } else { + if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("previous lock conflict with zero byte lock range\n"); + goto out; + } + + if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("current lock conflict with zero byte lock range\n"); + goto out; + } + + if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("Not allow lock operation on exclusive lock range\n"); + goto out; + } + } + spin_unlock(&conn->llist_lock); + } + read_unlock(&conn_list_lock); +out_check_cl: + if (smb_lock->fl->fl_type == F_UNLCK && nolock) { + pr_err("Try to unlock nolocked range\n"); + rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; + goto out; + } + +no_check_cl: + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; + list_del(&smb_lock->llist); +retry: + rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); +skip: + if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (!rc) { + ksmbd_debug(SMB, "File unlocked\n"); + } else if (rc == -ENOENT) { + rsp->hdr.Status = STATUS_NOT_LOCKED; + goto out; + } + locks_free_lock(flock); + kfree(smb_lock); + } else { + if (rc == FILE_LOCK_DEFERRED) { + void **argv; + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); + if (!argv) { + err = -ENOMEM; + goto out; + } + argv[0] = flock; + + rc = setup_async_work(work, + smb2_remove_blocked_lock, + argv); + if (rc) { + err = -ENOMEM; + goto out; + } + spin_lock(&fp->f_lock); + list_add(&work->fp_entry, &fp->blocked_works); + spin_unlock(&fp->f_lock); + + smb2_send_interim_resp(work, STATUS_PENDING); + + ksmbd_vfs_posix_lock_wait(flock); + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + locks_free_lock(flock); + + if (work->state == KSMBD_WORK_CANCELLED) { + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + rsp->hdr.Status = + STATUS_CANCELLED; + kfree(smb_lock); + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; + } + init_smb2_rsp_hdr(work); + smb2_set_err_rsp(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; + } + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + goto retry; + } else if (!rc) { + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + goto out; + } + } + } + + if (atomic_read(&fp->f_ci->op_count) > 1) + smb_break_all_oplock(work, fp); + + rsp->StructureSize = cpu_to_le16(4); + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; + inc_rfc1001_len(rsp, 4); + ksmbd_fd_put(work, fp); + return 0; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + rc = vfs_lock_file(filp, 0, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } +out2: + ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); + + if (!rsp->hdr.Status) { + if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (err == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + } + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +static int fsctl_copychunk(struct ksmbd_work *work, + struct copychunk_ioctl_req *ci_req, + unsigned int cnt_code, + unsigned int input_count, + unsigned long long volatile_id, + unsigned long long persistent_id, + struct smb2_ioctl_rsp *rsp) +{ + struct copychunk_ioctl_rsp *ci_rsp; + struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; + struct srv_copychunk *chunks; + unsigned int i, chunk_count, chunk_count_written = 0; + unsigned int chunk_size_written = 0; + loff_t total_size_written = 0; + int ret = 0; + + ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; + + rsp->VolatileFileId = cpu_to_le64(volatile_id); + rsp->PersistentFileId = cpu_to_le64(persistent_id); + ci_rsp->ChunksWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); + ci_rsp->ChunkBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); + ci_rsp->TotalBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_total_size()); + + chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; + chunk_count = le32_to_cpu(ci_req->ChunkCount); + if (chunk_count == 0) + goto out; + total_size_written = 0; + + /* verify the SRV_COPYCHUNK_COPY packet */ + if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || + input_count < offsetof(struct copychunk_ioctl_req, Chunks) + + chunk_count * sizeof(struct srv_copychunk)) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + for (i = 0; i < chunk_count; i++) { + if (le32_to_cpu(chunks[i].Length) == 0 || + le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) + break; + total_size_written += le32_to_cpu(chunks[i].Length); + } + + if (i < chunk_count || + total_size_written > ksmbd_server_side_copy_max_total_size()) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + src_fp = ksmbd_lookup_foreign_fd(work, + le64_to_cpu(ci_req->ResumeKey[0])); + dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + ret = -EINVAL; + if (!src_fp || + src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + + if (!dst_fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out; + } + + /* + * FILE_READ_DATA should only be included in + * the FSCTL_COPYCHUNK case + */ + if (cnt_code == FSCTL_COPYCHUNK && + !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + goto out; + } + + ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, + chunks, chunk_count, + &chunk_count_written, + &chunk_size_written, + &total_size_written); + if (ret < 0) { + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + if (ret == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (ret == -EBADF) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (ret == -EFBIG || ret == -ENOSPC) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (ret == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (ret == -EISDIR) + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + else if (ret == -E2BIG) + rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; + else + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + } + + ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); + ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); + ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); +out: + ksmbd_fd_put(work, src_fp); + ksmbd_fd_put(work, dst_fp); + return ret; +} + +static __be32 idev_ipv4_address(struct in_device *idev) +{ + __be32 addr = 0; + + struct in_ifaddr *ifa; + + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, idev) { + if (ifa->ifa_flags & IFA_F_SECONDARY) + continue; + + addr = ifa->ifa_address; + break; + } + rcu_read_unlock(); + return addr; +} + +static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, + struct smb2_ioctl_rsp *rsp, + unsigned int out_buf_len) +{ + struct network_interface_info_ioctl_rsp *nii_rsp = NULL; + int nbytes = 0; + struct net_device *netdev; + struct sockaddr_storage_rsp *sockaddr_storage; + unsigned int flags; + unsigned long long speed; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + bool ipv4_set = false; + + if (netdev->type == ARPHRD_LOOPBACK) + continue; + + flags = dev_get_flags(netdev); + if (!(flags & IFF_RUNNING)) + continue; +ipv6_retry: + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + + nii_rsp = (struct network_interface_info_ioctl_rsp *) + &rsp->Buffer[nbytes]; + nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); + + nii_rsp->Capability = 0; + if (netdev->real_num_tx_queues > 1) + nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + if (ksmbd_rdma_capable_netdev(netdev)) + nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); + + nii_rsp->Next = cpu_to_le32(152); + nii_rsp->Reserved = 0; + + if (netdev->ethtool_ops->get_link_ksettings) { + struct ethtool_link_ksettings cmd; + + netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); + speed = cmd.base.speed; + } else { + ksmbd_debug(SMB, "%s %s\n", netdev->name, + "speed is unknown, defaulting to 1Gb/sec"); + speed = SPEED_1000; + } + + speed *= 1000000; + nii_rsp->LinkSpeed = cpu_to_le64(speed); + + sockaddr_storage = (struct sockaddr_storage_rsp *) + nii_rsp->SockAddr_Storage; + memset(sockaddr_storage, 0, 128); + + if (!ipv4_set) { + struct in_device *idev; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->addr4.Port = 0; + + idev = __in_dev_get_rtnl(netdev); + if (!idev) + continue; + sockaddr_storage->addr4.IPv4address = + idev_ipv4_address(idev); + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + ipv4_set = true; + goto ipv6_retry; + } else { + struct inet6_dev *idev6; + struct inet6_ifaddr *ifa; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->addr6.Port = 0; + sockaddr_storage->addr6.FlowInfo = 0; + + idev6 = __in6_dev_get(netdev); + if (!idev6) + continue; + + list_for_each_entry(ifa, &idev6->addr_list, if_list) { + if (ifa->flags & (IFA_F_TENTATIVE | + IFA_F_DEPRECATED)) + continue; + memcpy(ipv6_addr, ifa->addr.s6_addr, 16); + break; + } + sockaddr_storage->addr6.ScopeId = 0; + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + } + } + rtnl_unlock(); + + /* zero if this is last one */ + if (nii_rsp) + nii_rsp->Next = 0; + + rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); + rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); + return nbytes; +} + +static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp, + unsigned int in_buf_len) +{ + int ret = 0; + int dialect; + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + + le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) + return -EINVAL; + + dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, + neg_req->DialectCount); + if (dialect == BAD_PROT_ID || dialect != conn->dialect) { + ret = -EINVAL; + goto err_out; + } + + if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + ret = -EINVAL; + goto err_out; + } + + if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { + ret = -EINVAL; + goto err_out; + } + + if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { + ret = -EINVAL; + goto err_out; + } + + neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); + neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); + neg_rsp->Dialect = cpu_to_le16(conn->dialect); +err_out: + return ret; +} + +static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + unsigned int in_count, unsigned int *out_count) +{ + struct ksmbd_file *fp; + loff_t start, length; + int ret = 0; + + *out_count = 0; + if (in_count == 0) + return -EINVAL; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + + start = le64_to_cpu(qar_req->file_offset); + length = le64_to_cpu(qar_req->length); + + ret = ksmbd_vfs_fqar_lseek(fp, start, length, + qar_rsp, in_count, out_count); + if (ret && ret != -E2BIG) + *out_count = 0; + + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, + unsigned int out_buf_len, + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) +{ + struct ksmbd_rpc_command *rpc_resp; + char *data_buf = (char *)&req->Buffer[0]; + int nbytes = 0; + + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, + le32_to_cpu(req->InputCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { + /* + * set STATUS_SOME_NOT_MAPPED response + * for unknown domain sid. + */ + rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; + } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + nbytes = rpc_resp->payload_sz; + if (rpc_resp->payload_sz > out_buf_len) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + nbytes = out_buf_len; + } + + if (!rpc_resp->payload_sz) { + rsp->hdr.Status = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); + } +out: + kvfree(rpc_resp); + return nbytes; +} + +static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + struct file_sparse *sparse) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + int ret = 0; + __le32 old_fattr; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + user_ns = file_mnt_user_ns(fp->filp); + + old_fattr = fp->f_ci->m_fattr; + if (sparse->SetSparse) + fp->f_ci->m_fattr |= ATTR_SPARSE_FILE_LE; + else + fp->f_ci->m_fattr &= ~ATTR_SPARSE_FILE_LE; + + if (fp->f_ci->m_fattr != old_fattr && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret <= 0) + goto out; + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } + +out: + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_request_resume_key(struct ksmbd_work *work, + struct smb2_ioctl_req *req, + struct resume_key_ioctl_rsp *key_rsp) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) + return -ENOENT; + + memset(key_rsp, 0, sizeof(*key_rsp)); + key_rsp->ResumeKey[0] = req->VolatileFileId; + key_rsp->ResumeKey[1] = req->PersistentFileId; + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb2_ioctl() - handler for smb2 ioctl command + * @work: smb work containing ioctl command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_ioctl(struct ksmbd_work *work) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp, *rsp_org; + unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; + u64 id = KSMBD_NO_FID; + struct ksmbd_conn *conn = work->conn; + int ret = 0; + + rsp_org = work->response_buf; + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + } + } else { + req = work->request_buf; + rsp = work->response_buf; + } + + if (!has_file_id(id)) + id = le64_to_cpu(req->VolatileFileId); + + if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } + + cnt_code = le32_to_cpu(req->CntCode); + ret = smb2_calc_max_out_buf_len(work, 48, + le32_to_cpu(req->MaxOutputResponse)); + if (ret < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + out_buf_len = (unsigned int)ret; + in_buf_len = le32_to_cpu(req->InputCount); + + switch (cnt_code) { + case FSCTL_DFS_GET_REFERRALS: + case FSCTL_DFS_GET_REFERRALS_EX: + /* Not support DFS yet */ + rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; + goto out; + case FSCTL_CREATE_OR_GET_OBJECT_ID: + { + struct file_object_buf_type1_ioctl_rsp *obj_buf; + + nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); + obj_buf = (struct file_object_buf_type1_ioctl_rsp *) + &rsp->Buffer[0]; + + /* + * TODO: This is dummy implementation to pass smbtorture + * Need to check correct response later + */ + memset(obj_buf->ObjectId, 0x0, 16); + memset(obj_buf->BirthVolumeId, 0x0, 16); + memset(obj_buf->BirthObjectId, 0x0, 16); + memset(obj_buf->DomainId, 0x0, 16); + + break; + } + case FSCTL_PIPE_TRANSCEIVE: + out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); + nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); + break; + case FSCTL_VALIDATE_NEGOTIATE_INFO: + if (conn->dialect < SMB30_PROT_ID) { + ret = -EOPNOTSUPP; + goto out; + } + + if (in_buf_len < sizeof(struct validate_negotiate_info_req)) + return -EINVAL; + + if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) + return -EINVAL; + + ret = fsctl_validate_negotiate_info(conn, + (struct validate_negotiate_info_req *)&req->Buffer[0], + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], + in_buf_len); + if (ret < 0) + goto out; + + nbytes = sizeof(struct validate_negotiate_info_rsp); + rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); + rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); + break; + case FSCTL_QUERY_NETWORK_INTERFACE_INFO: + ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); + if (ret < 0) + goto out; + nbytes = ret; + break; + case FSCTL_REQUEST_RESUME_KEY: + if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_request_resume_key(work, req, + (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); + if (ret < 0) + goto out; + rsp->PersistentFileId = req->PersistentFileId; + rsp->VolatileFileId = req->VolatileFileId; + nbytes = sizeof(struct resume_key_ioctl_rsp); + break; + case FSCTL_COPYCHUNK: + case FSCTL_COPYCHUNK_WRITE: + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + nbytes = sizeof(struct copychunk_ioctl_rsp); + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + fsctl_copychunk(work, + (struct copychunk_ioctl_req *)&req->Buffer[0], + le32_to_cpu(req->CntCode), + le32_to_cpu(req->InputCount), + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId), + rsp); + break; + case FSCTL_SET_SPARSE: + if (in_buf_len < sizeof(struct file_sparse)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_set_sparse(work, id, + (struct file_sparse *)&req->Buffer[0]); + if (ret < 0) + goto out; + break; + case FSCTL_SET_ZERO_DATA: + { + struct file_zero_data_information *zero_data; + struct ksmbd_file *fp; + loff_t off, len; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct file_zero_data_information)) { + ret = -EINVAL; + goto out; + } + + zero_data = + (struct file_zero_data_information *)&req->Buffer[0]; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -ENOENT; + goto out; + } + + off = le64_to_cpu(zero_data->FileOffset); + len = le64_to_cpu(zero_data->BeyondFinalZero) - off; + + ret = ksmbd_vfs_zero_data(work, fp, off, len); + ksmbd_fd_put(work, fp); + if (ret < 0) + goto out; + break; + } + case FSCTL_QUERY_ALLOCATED_RANGES: + if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_query_allocated_ranges(work, id, + (struct file_allocated_range_buffer *)&req->Buffer[0], + (struct file_allocated_range_buffer *)&rsp->Buffer[0], + out_buf_len / + sizeof(struct file_allocated_range_buffer), &nbytes); + if (ret == -E2BIG) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + } else if (ret < 0) { + nbytes = 0; + goto out; + } + + nbytes *= sizeof(struct file_allocated_range_buffer); + break; + case FSCTL_GET_REPARSE_POINT: + { + struct reparse_data_buffer *reparse_ptr; + struct ksmbd_file *fp; + + reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + pr_err("not found fp!!\n"); + ret = -ENOENT; + goto out; + } + + reparse_ptr->ReparseTag = + smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); + reparse_ptr->ReparseDataLength = 0; + ksmbd_fd_put(work, fp); + nbytes = sizeof(struct reparse_data_buffer); + break; + } + case FSCTL_DUPLICATE_EXTENTS_TO_FILE: + { + struct ksmbd_file *fp_in, *fp_out = NULL; + struct duplicate_extents_to_file *dup_ext; + loff_t src_off, dst_off, length, cloned; + + if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { + ret = -EINVAL; + goto out; + } + + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); + if (!fp_in) { + pr_err("not found file handle in duplicate extent to file\n"); + ret = -ENOENT; + goto out; + } + + fp_out = ksmbd_lookup_fd_fast(work, id); + if (!fp_out) { + pr_err("not found fp\n"); + ret = -ENOENT; + goto dup_ext_out; + } + + src_off = le64_to_cpu(dup_ext->SourceFileOffset); + dst_off = le64_to_cpu(dup_ext->TargetFileOffset); + length = le64_to_cpu(dup_ext->ByteCount); + cloned = vfs_clone_file_range(fp_in->filp, src_off, fp_out->filp, + dst_off, length, 0); + if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { + ret = -EOPNOTSUPP; + goto dup_ext_out; + } else if (cloned != length) { + cloned = vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length, 0); + if (cloned != length) { + if (cloned < 0) + ret = cloned; + else + ret = -EINVAL; + } + } + +dup_ext_out: + ksmbd_fd_put(work, fp_in); + ksmbd_fd_put(work, fp_out); + if (ret < 0) + goto out; + break; + } + default: + ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", + cnt_code); + ret = -EOPNOTSUPP; + goto out; + } + + rsp->CntCode = cpu_to_le32(cnt_code); + rsp->InputCount = cpu_to_le32(0); + rsp->InputOffset = cpu_to_le32(112); + rsp->OutputOffset = cpu_to_le32(112); + rsp->OutputCount = cpu_to_le32(nbytes); + rsp->StructureSize = cpu_to_le16(49); + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); + inc_rfc1001_len(rsp_org, 48 + nbytes); + + return 0; + +out: + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (ret == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (ret == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (ret == -ENOSPC) + rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; + else if (ret < 0 || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return 0; +} + +/** + * smb20_oplock_break_ack() - handler for smb2.0 oplock break command + * @work: smb work containing oplock break command buffer + * + * Return: 0 + */ +static void smb20_oplock_break_ack(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = work->request_buf; + struct smb2_oplock_break *rsp = work->response_buf; + struct ksmbd_file *fp; + struct oplock_info *opinfo = NULL; + __le32 err = 0; + int ret = 0; + u64 volatile_id, persistent_id; + char req_oplevel = 0, rsp_oplevel = 0; + unsigned int oplock_change_type; + + volatile_id = le64_to_cpu(req->VolatileFid); + persistent_id = le64_to_cpu(req->PersistentFid); + req_oplevel = req->OplockLevel; + ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", + volatile_id, persistent_id, req_oplevel); + + fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + return; + } + + opinfo = opinfo_get(fp); + if (!opinfo) { + pr_err("unexpected null oplock_info\n"); + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return; + } + + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + (req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_READ_TO_NONE; + } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_DEVICE_STATE; + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_II) { + oplock_change_type = OPLOCK_WRITE_TO_READ; + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_READ_TO_NONE; + } else { + oplock_change_type = 0; + } + } else { + oplock_change_type = 0; + } + + switch (oplock_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_II; + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + default: + pr_err("unknown oplock change 0x%x -> 0x%x\n", + opinfo->level, rsp_oplevel); + } + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + rsp->StructureSize = cpu_to_le16(24); + rsp->OplockLevel = rsp_oplevel; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->VolatileFid = cpu_to_le64(volatile_id); + rsp->PersistentFid = cpu_to_le64(persistent_id); + inc_rfc1001_len(rsp, 24); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); +} + +static int check_lease_state(struct lease *lease, __le32 req_state) +{ + if ((lease->new_state == + (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && + !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { + lease->new_state = req_state; + return 0; + } + + if (lease->new_state == req_state) + return 0; + + return 1; +} + +/** + * smb21_lease_break_ack() - handler for smb2.1 lease break command + * @work: smb work containing lease break command buffer + * + * Return: 0 + */ +static void smb21_lease_break_ack(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_lease_ack *req = work->request_buf; + struct smb2_lease_ack *rsp = work->response_buf; + struct oplock_info *opinfo; + __le32 err = 0; + int ret = 0; + unsigned int lease_change_type; + __le32 lease_state; + struct lease *lease; + + ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", + le32_to_cpu(req->LeaseState)); + opinfo = lookup_lease_in_table(conn, req->LeaseKey); + if (!opinfo) { + ksmbd_debug(OPLOCK, "file not opened\n"); + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + return; + } + lease = opinfo->o_lease; + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + pr_err("unexpected lease break state 0x%x\n", + opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if (check_lease_state(lease, req->LeaseState)) { + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + ksmbd_debug(OPLOCK, + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); + goto err_out; + } + + if (!atomic_read(&opinfo->breaking_cnt)) { + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + /* check for bad lease state */ + if (req->LeaseState & + (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && + req->LeaseState != SMB2_LEASE_NONE_LE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else { + /* valid lease state changes */ + err = STATUS_INVALID_DEVICE_STATE; + if (req->LeaseState == SMB2_LEASE_NONE_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_READ; + else + lease_change_type = OPLOCK_READ_HANDLE_TO_READ; + } else { + lease_change_type = 0; + } + } + + switch (lease_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + break; + case OPLOCK_READ_HANDLE_TO_READ: + ret = opinfo_read_handle_to_read(opinfo); + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + break; + default: + ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } + + lease_state = lease->state; + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + opinfo_put(opinfo); + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; + inc_rfc1001_len(rsp, 36); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + + opinfo_put(opinfo); + smb2_set_err_rsp(work); +} + +/** + * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break + * @work: smb work containing oplock/lease break command buffer + * + * Return: 0 + */ +int smb2_oplock_break(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = work->request_buf; + struct smb2_oplock_break *rsp = work->response_buf; + + switch (le16_to_cpu(req->StructureSize)) { + case OP_BREAK_STRUCT_SIZE_20: + smb20_oplock_break_ack(work); + break; + case OP_BREAK_STRUCT_SIZE_21: + smb21_lease_break_ack(work); + break; + default: + ksmbd_debug(OPLOCK, "invalid break cmd %d\n", + le16_to_cpu(req->StructureSize)); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + } + + return 0; +} + +/** + * smb2_notify() - handler for smb2 notify request + * @work: smb work containing notify command buffer + * + * Return: 0 + */ +int smb2_notify(struct ksmbd_work *work) +{ + struct smb2_notify_req *req; + struct smb2_notify_rsp *rsp; + + WORK_BUFFERS(work, req, rsp); + + if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { + rsp->hdr.Status = STATUS_INTERNAL_ERROR; + smb2_set_err_rsp(work); + return 0; + } + + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; + return 0; +} + +/** + * smb2_is_sign_req() - handler for checking packet signing status + * @work: smb work containing notify command buffer + * @command: SMB2 command id + * + * Return: true if packed is signed, false otherwise + */ +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ + struct smb2_hdr *rcv_hdr2 = work->request_buf; + + if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && + command != SMB2_NEGOTIATE_HE && + command != SMB2_SESSION_SETUP_HE && + command != SMB2_OPLOCK_BREAK_HE) + return true; + + return false; +} + +/** + * smb2_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb2_check_sign_req(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr, *hdr_org; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr_org = hdr = work->request_buf; + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = be32_to_cpu(hdr_org->smb2_buf_length); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = be32_to_cpu(hdr_org->smb2_buf_length) - + work->next_smb2_rcv_hdr_off; + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb2_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb2_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[2]; + size_t len; + int n_vec = 1; + + hdr_org = hdr = work->response_buf; + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(hdr_org); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb3_check_sign_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + char *signing_key; + struct smb2_hdr *hdr, *hdr_org; + struct channel *chann; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr_org = hdr = work->request_buf; + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = be32_to_cpu(hdr_org->smb2_buf_length); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = be32_to_cpu(hdr_org->smb2_buf_length) - + work->next_smb2_rcv_hdr_off; + + if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, conn); + if (!chann) + return 0; + signing_key = chann->smb3signingkey; + } + + if (!signing_key) { + pr_err("SMB3 signing key is not generated\n"); + return 0; + } + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb3_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb3_set_sign_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *req_hdr; + struct smb2_hdr *hdr, *hdr_org; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[2]; + int n_vec = 1; + size_t len; + char *signing_key; + + hdr_org = hdr = work->response_buf; + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(hdr_org); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, work->conn); + if (!chann) + return; + signing_key = chann->smb3signingkey; + } + + if (!signing_key) + return; + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_preauth_hash_rsp() - handler for computing preauth hash on response + * @work: smb work containing response buffer + * + */ +void smb3_preauth_hash_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *req, *rsp; + + if (conn->dialect != SMB311_PROT_ID) + return; + + WORK_BUFFERS(work, req, rsp); + + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && + conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + conn->preauth_info->Preauth_HashValue); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { + __u8 *hash_value; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return; + hash_value = preauth_sess->Preauth_HashValue; + } else { + hash_value = sess->Preauth_HashValue; + if (!hash_value) + return; + } + ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + hash_value); + } +} + +static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, char *old_buf, + __le16 cipher_type) +{ + struct smb2_hdr *hdr = (struct smb2_hdr *)old_buf; + unsigned int orig_len = get_rfc1002_len(old_buf); + + memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(0x01); + if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || + cipher_type == SMB2_ENCRYPTION_AES256_GCM) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); + inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4); + inc_rfc1001_len(tr_hdr, orig_len); +} + +int smb3_encrypt_resp(struct ksmbd_work *work) +{ + char *buf = work->response_buf; + struct smb2_transform_hdr *tr_hdr; + struct kvec iov[3]; + int rc = -ENOMEM; + int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); + + if (ARRAY_SIZE(iov) < rq_nvec) + return -ENOMEM; + + tr_hdr = kzalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); + if (!tr_hdr) + return rc; + + /* fill transform header */ + fill_transform_hdr(tr_hdr, buf, work->conn->cipher_type); + + iov[0].iov_base = tr_hdr; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + buf_size += iov[0].iov_len - 4; + + iov[1].iov_base = buf + 4; + iov[1].iov_len = get_rfc1002_len(buf); + if (work->aux_payload_sz) { + iov[1].iov_len = work->resp_hdr_sz - 4; + + iov[2].iov_base = work->aux_payload_buf; + iov[2].iov_len = work->aux_payload_sz; + buf_size += iov[2].iov_len; + } + buf_size += iov[1].iov_len; + work->resp_hdr_sz = iov[1].iov_len; + + rc = ksmbd_crypt_message(work->conn, iov, rq_nvec, 1); + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, iov[1].iov_len); + tr_hdr->smb2_buf_length = cpu_to_be32(buf_size); + work->tr_buf = tr_hdr; + + return rc; +} + +bool smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = buf; + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +int smb3_decrypt_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess; + char *buf = work->request_buf; + struct smb2_hdr *hdr; + unsigned int pdu_length = get_rfc1002_len(buf); + struct kvec iov[2]; + int buf_data_size = pdu_length + 4 - + sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + int rc = 0; + + if (buf_data_size < sizeof(struct smb2_hdr)) { + pr_err("Transform message is too small (%u)\n", + pdu_length); + return -ECONNABORTED; + } + + if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { + pr_err("Transform message is broken\n"); + return -ECONNABORTED; + } + + sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId)); + if (!sess) { + pr_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); + return -ECONNABORTED; + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[1].iov_len = buf_data_size; + rc = ksmbd_crypt_message(conn, iov, 2, 0); + if (rc) + return rc; + + memmove(buf + 4, iov[1].iov_base, buf_data_size); + hdr = (struct smb2_hdr *)buf; + hdr->smb2_buf_length = cpu_to_be32(buf_data_size); + + return rc; +} + +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp = work->response_buf; + + if (conn->dialect < SMB30_PROT_ID) + return false; + + if (work->next_smb2_rcv_hdr_off) + rsp = ksmbd_resp_buf_next(work); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && + rsp->Status == STATUS_SUCCESS) + return true; + return false; +} diff -Naur --no-dereference a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h --- a/fs/ksmbd/smb2pdu.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb2pdu.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,1709 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include "ntlmssp.h" +#include "smbacl.h" + +/* + * Note that, due to trying to use names similar to the protocol specifications, + * there are many mixed case field names in the structures below. Although + * this does not match typical Linux kernel style, it is necessary to be + * able to match against the protocol specfication. + * + * SMB2 commands + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie no useful data other than the SMB error code itself) and are marked such. + * Knowing this helps avoid response buffer allocations and copy in some cases. + */ + +/* List of commands in host endian */ +#define SMB2_NEGOTIATE_HE 0x0000 +#define SMB2_SESSION_SETUP_HE 0x0001 +#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ +#define SMB2_TREE_CONNECT_HE 0x0003 +#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ +#define SMB2_CREATE_HE 0x0005 +#define SMB2_CLOSE_HE 0x0006 +#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ +#define SMB2_READ_HE 0x0008 +#define SMB2_WRITE_HE 0x0009 +#define SMB2_LOCK_HE 0x000A +#define SMB2_IOCTL_HE 0x000B +#define SMB2_CANCEL_HE 0x000C +#define SMB2_ECHO_HE 0x000D +#define SMB2_QUERY_DIRECTORY_HE 0x000E +#define SMB2_CHANGE_NOTIFY_HE 0x000F +#define SMB2_QUERY_INFO_HE 0x0010 +#define SMB2_SET_INFO_HE 0x0011 +#define SMB2_OPLOCK_BREAK_HE 0x0012 + +/* The same list in little endian */ +#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) +#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) +#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) +#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) +#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) +#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) +#define SMB2_READ cpu_to_le16(SMB2_READ_HE) +#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) +#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) +#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) +#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) +#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) +#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) +#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) +#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) + +/*Create Action Flags*/ +#define FILE_SUPERSEDED 0x00000000 +#define FILE_OPENED 0x00000001 +#define FILE_CREATED 0x00000002 +#define FILE_OVERWRITTEN 0x00000003 + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define SMB2_NTLMV2_SESSKEY_SIZE 16 +#define SMB2_SIGNATURE_SIZE 16 +#define SMB2_HMACSHA256_SIZE 32 +#define SMB2_CMACAES_SIZE 16 +#define SMB3_GCM128_CRYPTKEY_SIZE 16 +#define SMB3_GCM256_CRYPTKEY_SIZE 32 + +/* + * Size of the smb3 encryption/decryption keys + */ +#define SMB3_ENC_DEC_KEY_SIZE 32 + +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE 16 + +#define CIFS_CLIENT_CHALLENGE_SIZE 8 +#define SMB_SERVER_CHALLENGE_SIZE 8 + +/* SMB2 Max Credits */ +#define SMB2_MAX_CREDITS 8192 + +#define SMB2_CLIENT_GUID_SIZE 16 +#define SMB2_CREATE_GUID_SIZE 16 + +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 + +#define NUMBER_OF_SMB2_COMMANDS 0x0013 + +/* BB FIXME - analyze following length BB */ +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ + +#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) /* 'B''M''S' */ +#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) + +#define SMB21_DEFAULT_IOSIZE (1024 * 1024) +#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) +#define SMB3_MIN_IOSIZE (64 * 1024) +#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) + +/* + * SMB2 Header Definition + * + * "MBZ" : Must be Zero + * "BB" : BugBug, Something to check/review/analyze later + * "PDU" : "Protocol Data Unit" (ie a network "frame") + * + */ + +#define __SMB2_HEADER_STRUCTURE_SIZE 64 +#define SMB2_HEADER_STRUCTURE_SIZE \ + cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) + +struct smb2_hdr { + __be32 smb2_buf_length; /* big endian on wire */ + /* + * length is only two or three bytes - with + * one or two byte type preceding it that MBZ + */ + __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ + __le16 StructureSize; /* 64 */ + __le16 CreditCharge; /* MBZ */ + __le32 Status; /* Error from server */ + __le16 Command; + __le16 CreditRequest; /* CreditResponse */ + __le32 Flags; + __le32 NextCommand; + __le64 MessageId; + union { + struct { + __le32 ProcessId; + __le32 TreeId; + } __packed SyncId; + __le64 AsyncId; + } __packed Id; + __le64 SessionId; + __u8 Signature[16]; +} __packed; + +struct smb2_pdu { + struct smb2_hdr hdr; + __le16 StructureSize2; /* size of wct area (varies, request specific) */ +} __packed; + +#define SMB3_AES_CCM_NONCE 11 +#define SMB3_AES_GCM_NONCE 12 + +struct smb2_transform_hdr { + __be32 smb2_buf_length; /* big endian on wire */ + /* + * length is only two or three bytes - with + * one or two byte type preceding it that MBZ + */ + __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ + __u8 Signature[16]; + __u8 Nonce[16]; + __le32 OriginalMessageSize; + __u16 Reserved1; + __le16 Flags; /* EncryptionAlgorithm */ + __le64 SessionId; +} __packed; + +/* + * SMB2 flag definitions + */ +#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) +#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) +#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) +#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +#define SMB2_FLAGS_REPLAY_OPERATIONS cpu_to_le32(0x20000000) + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +#define SMB2_ERROR_STRUCTURE_SIZE2 9 +#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) + +struct smb2_err_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __u8 ErrorContextCount; + __u8 Reserved; + __le32 ByteCount; /* even if zero, at least one byte follows */ + __u8 ErrorData[1]; /* variable length */ +} __packed; + +struct smb2_negotiate_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 DialectCount; + __le16 SecurityMode; + __le16 Reserved; /* MBZ */ + __le32 Capabilities; + __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; + /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ + __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ + __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ + __le16 Reserved2; + __le16 Dialects[1]; /* One dialect (vers=) at a time for now */ +} __packed; + +/* SecurityMode flags */ +#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) +/* Capabilities flags */ +#define SMB2_GLOBAL_CAP_DFS 0x00000001 +#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ +/* Internal types */ +#define SMB2_NT_FIND 0x00100000 +#define SMB2_LARGE_FILES 0x00200000 + +#define SMB311_SALT_SIZE 32 +/* Hash Algorithm Types */ +#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct preauth_integrity_info { + /* PreAuth integrity Hash ID */ + __le16 Preauth_HashId; + /* PreAuth integrity Hash Value */ + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; +}; + +/* offset is sizeof smb2_negotiate_rsp - 4 but rounded up to 8 bytes. */ +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +/* sizeof(struct smb2_negotiate_rsp) - 4 = + * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) + */ +#define OFFSET_OF_NEG_CONTEXT 0xe0 +#else +/* sizeof(struct smb2_negotiate_rsp) - 4 = + * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) + */ +#define OFFSET_OF_NEG_CONTEXT 0xd0 +#endif + +#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) +#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) +#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) +#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) +#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) +#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) + +struct smb2_neg_context { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + /* Followed by array of data */ +} __packed; + +struct smb2_preauth_neg_context { + __le16 ContextType; /* 1 */ + __le16 DataLength; + __le32 Reserved; + __le16 HashAlgorithmCount; /* 1 */ + __le16 SaltLength; + __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ + __u8 Salt[SMB311_SALT_SIZE]; +} __packed; + +/* Encryption Algorithms Ciphers */ +#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) +#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) +#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) +#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) + +struct smb2_encryption_neg_context { + __le16 ContextType; /* 2 */ + __le16 DataLength; + __le32 Reserved; + /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ + __le16 CipherCount; /* AES-128-GCM and AES-128-CCM by default */ + __le16 Ciphers[]; +} __packed; + +#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) +#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) +#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) +#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) + +struct smb2_compression_ctx { + __le16 ContextType; /* 3 */ + __le16 DataLength; + __le32 Reserved; + __le16 CompressionAlgorithmCount; + __u16 Padding; + __le32 Reserved1; + __le16 CompressionAlgorithms[]; +} __packed; + +#define POSIX_CTXT_DATA_LEN 16 +struct smb2_posix_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ +} __packed; + +struct smb2_netname_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __le16 NetName[]; /* hostname of target converted to UCS-2 */ +} __packed; + +/* Signing algorithms */ +#define SIGNING_ALG_HMAC_SHA256 cpu_to_le16(0) +#define SIGNING_ALG_AES_CMAC cpu_to_le16(1) +#define SIGNING_ALG_AES_GMAC cpu_to_le16(2) + +struct smb2_signing_capabilities { + __le16 ContextType; /* 8 */ + __le16 DataLength; + __le32 Reserved; + __le16 SigningAlgorithmCount; + __le16 SigningAlgorithms[]; +} __packed; + +struct smb2_negotiate_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 65 */ + __le16 SecurityMode; + __le16 DialectRevision; + __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ + __u8 ServerGUID[16]; + __le32 Capabilities; + __le32 MaxTransactSize; + __le32 MaxReadSize; + __le32 MaxWriteSize; + __le64 SystemTime; /* MBZ */ + __le64 ServerStartTime; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +#define SMB2_SESSION_EXPIRED (0) +#define SMB2_SESSION_IN_PROGRESS BIT(0) +#define SMB2_SESSION_VALID BIT(1) + +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +struct smb2_sess_setup_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 Flags; + __u8 SecurityMode; + __le32 Capabilities; + __le32 Channel; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Flags/Reserved for SMB3.1.1 */ +#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001 + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) +#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) +#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) +struct smb2_sess_setup_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +struct smb2_logoff_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_connect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 Reserved; /* Flags in SMB3.1.1 */ + __le16 PathOffset; + __le16 PathLength; + __u8 Buffer[1]; /* variable length */ +} __packed; + +struct smb2_tree_connect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 16 */ + __u8 ShareType; /* see below */ + __u8 Reserved; + __le32 ShareFlags; /* see below */ + __le32 Capabilities; /* see below */ + __le32 MaximalAccess; +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SHI1005_FLAGS_DFS 0x00000001 +#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) + +struct smb2_tree_disconnect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) +#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) +#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) +#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) +#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) +#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) +#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) +#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) +#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) +#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) +#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) +#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) +#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) +#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) +#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) + +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF +/* Non-spec internal type */ +#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) +#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) +#define FILE_ADD_FILE_LE cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) +#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) +#define FILE_TRAVERSE_LE cpu_to_le32(0x00000020) +#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) +#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) +#define FILE_DELETE_LE cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) +#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) +#define FILE_OPEN_LE cpu_to_le32(0x00000001) +#define FILE_CREATE_LE cpu_to_le32(0x00000002) +#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) +#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) + +#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ + FILE_READ_EA_LE | \ + FILE_GENERIC_READ_LE) +#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ + FILE_APPEND_DATA_LE | \ + FILE_WRITE_EA_LE | \ + FILE_WRITE_ATTRIBUTES_LE | \ + FILE_GENERIC_WRITE_LE) + +/* Impersonation Levels */ +#define IL_ANONYMOUS_LE cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION_LE cpu_to_le32(0x00000001) +#define IL_IMPERSONATION_LE cpu_to_le32(0x00000002) +#define IL_DELEGATE_LE cpu_to_le32(0x00000003) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE "AlSi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" +#define SMB2_CREATE_REQUEST_LEASE "RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" +#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" + #define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" +#define SVHDX_OPEN_DEVICE_CONTEXT 0x83CE6F1AD851E0986E34401CC9BCFCE9 +#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" + +struct smb2_create_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; + __le32 ImpersonationLevel; + __le64 SmbCreateFlags; + __le64 Reserved; + __le32 DesiredAccess; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le16 NameOffset; + __le16 NameLength; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[0]; +} __packed; + +struct smb2_create_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndofFile; + __le32 FileAttributes; + __le32 Reserved2; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +struct create_context { + __le32 Next; + __le16 NameOffset; + __le16 NameLength; + __le16 Reserved; + __le16 DataOffset; + __le32 DataLength; + __u8 Buffer[0]; +} __packed; + +struct create_durable_req_v2 { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; + __u8 Reserved[8]; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_reconn_req { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __le64 PersistentFileId; + __le64 VolatileFileId; + } Fid; + } Data; +} __packed; + +struct create_durable_reconn_v2_req { + struct create_context ccontext; + __u8 Name[8]; + struct { + __le64 PersistentFileId; + __le64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; +} __packed; + +struct create_app_inst_id { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[8]; + __u8 AppInstanceId[16]; +} __packed; + +struct create_app_inst_id_vers { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[2]; + __u8 Padding[4]; + __le64 AppInstanceVersionHigh; + __le64 AppInstanceVersionLow; +} __packed; + +struct create_mxac_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; +} __packed; + +struct create_alloc_size_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 AllocationSize; +} __packed; + +struct create_posix { + struct create_context ccontext; + __u8 Name[16]; + __le32 Mode; + __u32 Reserved; +} __packed; + +struct create_durable_rsp { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[8]; + __u64 data; + } Data; +} __packed; + +struct create_durable_v2_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +} __packed; + +struct create_mxac_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 QueryStatus; + __le32 MaximalAccess; +} __packed; + +struct create_disk_id_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u8 Reserved[16]; +} __packed; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + struct create_context ccontext; + __u8 Name[16]; + __le32 nlink; + __le32 reparse_tag; + __le32 mode; + u8 SidBuffer[40]; +} __packed; + +#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) +#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) +#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) +#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) + +struct lease_context { + __le64 LeaseKeyLow; + __le64 LeaseKeyHigh; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; +} __packed; + +struct lease_context_v2 { + __le64 LeaseKeyLow; + __le64 LeaseKeyHigh; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; + __le64 ParentLeaseKeyLow; + __le64 ParentLeaseKeyHigh; + __le16 Epoch; + __le16 Reserved; +} __packed; + +struct create_lease { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context lcontext; +} __packed; + +struct create_lease_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +} __packed; + +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) +struct smb2_close_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; + __le64 PersistentFileId; + __le64 VolatileFileId; +} __packed; + +struct smb2_close_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; + __le32 Attributes; +} __packed; + +struct smb2_flush_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Reserved1; + __le32 Reserved2; + __le64 PersistentFileId; + __le64 VolatileFileId; +} __packed; + +struct smb2_flush_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; +} __packed; + +struct smb2_buffer_desc_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) +#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) + +struct smb2_read_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __u8 Padding; /* offset from start of SMB2 header to place read */ + __u8 Reserved; + __le32 Length; + __le64 Offset; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 MinimumCount; + __le32 Channel; /* Reserved MBZ */ + __le32 RemainingBytes; + __le16 ReadChannelInfoOffset; /* Reserved MBZ */ + __le16 ReadChannelInfoLength; /* Reserved MBZ */ + __u8 Buffer[1]; +} __packed; + +struct smb2_read_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + +/* For write request Flags field below the following flag is defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 + +struct smb2_write_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 DataOffset; /* offset from start of SMB2 header to write data */ + __le32 Length; + __le64 Offset; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 Channel; /* Reserved MBZ */ + __le32 RemainingBytes; + __le16 WriteChannelInfoOffset; /* Reserved MBZ */ + __le16 WriteChannelInfoLength; /* Reserved MBZ */ + __le32 Flags; + __u8 Buffer[1]; +} __packed; + +struct smb2_write_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CntCode; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[1]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CntCode; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[1]; +} __packed; + +struct validate_negotiate_info_req { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ +} __packed; + +struct validate_negotiate_info_rsp { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 Dialect; /* Dialect in use for the connection */ +} __packed; + +struct smb_sockaddr_in { + __be16 Port; + __be32 IPv4address; + __u8 Reserved[8]; +} __packed; + +struct smb_sockaddr_in6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6address[16]; + __be32 ScopeId; +} __packed; + +#define INTERNETWORK 0x0002 +#define INTERNETWORKV6 0x0017 + +struct sockaddr_storage_rsp { + __le16 Family; + union { + struct smb_sockaddr_in addr4; + struct smb_sockaddr_in6 addr6; + }; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +struct file_object_buf_type1_ioctl_rsp { + __u8 ObjectId[16]; + __u8 BirthVolumeId[16]; + __u8 BirthObjectId[16]; + __u8 DomainId[16]; +} __packed; + +struct resume_key_ioctl_rsp { + __le64 ResumeKey[3]; + __le32 ContextLength; + __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +struct copychunk_ioctl_req { + __le64 ResumeKey[3]; + __le32 ChunkCount; + __le32 Reserved; + __u8 Chunks[1]; /* array of srv_copychunk */ +} __packed; + +struct srv_copychunk { + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; + __le32 Reserved; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +struct file_sparse { + __u8 SetSparse; +} __packed; + +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + +struct file_allocated_range_buffer { + __le64 file_offset; + __le64 length; +} __packed; + +struct reparse_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +/* Completion Filter flags for Notify */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_NAME 0x00000003 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +/* Flags */ +#define SMB2_WATCH_TREE 0x0001 + +struct smb2_notify_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 32 */ + __le16 Flags; + __le32 OutputBufferLength; + __le64 PersistentFileId; + __le64 VolatileFileId; + __u32 CompletionFileter; + __u32 Reserved; +} __packed; + +struct smb2_notify_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* SMB2 Notify Action Flags */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 +#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 + +#define SMB2_LOCKFLAG_SHARED 0x0001 +#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 +#define SMB2_LOCKFLAG_UNLOCK 0x0004 +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 +#define SMB2_LOCKFLAG_MASK 0x0007 + +struct smb2_lock_element { + __le64 Offset; + __le64 Length; + __le32 Flags; + __le32 Reserved; +} __packed; + +struct smb2_lock_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 48 */ + __le16 LockCount; + __le32 Reserved; + __le64 PersistentFileId; + __le64 VolatileFileId; + /* Followed by at least one */ + struct smb2_lock_element locks[1]; +} __packed; + +struct smb2_lock_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_echo_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +struct smb2_echo_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +/* search (query_directory) Flags field */ +#define SMB2_RESTART_SCANS 0x01 +#define SMB2_RETURN_SINGLE_ENTRY 0x02 +#define SMB2_INDEX_SPECIFIED 0x04 +#define SMB2_REOPEN 0x10 + +struct smb2_query_directory_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 FileInformationClass; + __u8 Flags; + __le32 FileIndex; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le16 FileNameOffset; + __le16 FileNameLength; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_directory_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE 0x01 +#define SMB2_O_INFO_FILESYSTEM 0x02 +#define SMB2_O_INFO_SECURITY 0x03 +#define SMB2_O_INFO_QUOTA 0x04 + +/* Security info type additionalinfo flags. See MS-SMB2 (2.2.37) or MS-DTYP */ +#define OWNER_SECINFO 0x00000001 +#define GROUP_SECINFO 0x00000002 +#define DACL_SECINFO 0x00000004 +#define SACL_SECINFO 0x00000008 +#define LABEL_SECINFO 0x00000010 +#define ATTRIBUTE_SECINFO 0x00000020 +#define SCOPE_SECINFO 0x00000040 +#define BACKUP_SECINFO 0x00010000 +#define UNPROTECTED_SACL_SECINFO 0x10000000 +#define UNPROTECTED_DACL_SECINFO 0x20000000 +#define PROTECTED_SACL_SECINFO 0x40000000 +#define PROTECTED_DACL_SECINFO 0x80000000 + +struct smb2_query_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 OutputBufferLength; + __le16 InputBufferOffset; + __u16 Reserved; + __le32 InputBufferLength; + __le32 AdditionalInformation; + __le32 Flags; + __le64 PersistentFileId; + __le64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 BufferLength; + __le16 BufferOffset; + __u16 Reserved; + __le32 AdditionalInformation; + __le64 PersistentFileId; + __le64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 2 */ +} __packed; + +/* FILE Info response size */ +#define FILE_DIRECTORY_INFORMATION_SIZE 1 +#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 +#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 +#define FILE_BASIC_INFORMATION_SIZE 40 +#define FILE_STANDARD_INFORMATION_SIZE 24 +#define FILE_INTERNAL_INFORMATION_SIZE 8 +#define FILE_EA_INFORMATION_SIZE 4 +#define FILE_ACCESS_INFORMATION_SIZE 4 +#define FILE_NAME_INFORMATION_SIZE 9 +#define FILE_RENAME_INFORMATION_SIZE 10 +#define FILE_LINK_INFORMATION_SIZE 11 +#define FILE_NAMES_INFORMATION_SIZE 12 +#define FILE_DISPOSITION_INFORMATION_SIZE 13 +#define FILE_POSITION_INFORMATION_SIZE 14 +#define FILE_FULL_EA_INFORMATION_SIZE 15 +#define FILE_MODE_INFORMATION_SIZE 4 +#define FILE_ALIGNMENT_INFORMATION_SIZE 4 +#define FILE_ALL_INFORMATION_SIZE 104 +#define FILE_ALLOCATION_INFORMATION_SIZE 19 +#define FILE_END_OF_FILE_INFORMATION_SIZE 20 +#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 +#define FILE_STREAM_INFORMATION_SIZE 32 +#define FILE_PIPE_INFORMATION_SIZE 23 +#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 +#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 +#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 +#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 +#define FILE_COMPRESSION_INFORMATION_SIZE 16 +#define FILE_OBJECT_ID_INFORMATION_SIZE 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 +#define FILE_QUOTA_INFORMATION_SIZE 32 +#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 +#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 +#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 + +/* FS Info response size */ +#define FS_DEVICE_INFORMATION_SIZE 8 +#define FS_ATTRIBUTE_INFORMATION_SIZE 16 +#define FS_VOLUME_INFORMATION_SIZE 24 +#define FS_SIZE_INFORMATION_SIZE 24 +#define FS_FULL_SIZE_INFORMATION_SIZE 32 +#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 +#define FS_OBJECT_ID_INFORMATION_SIZE 64 +#define FS_CONTROL_INFORMATION_SIZE 48 +#define FS_POSIX_INFORMATION_SIZE 56 + +/* FS_ATTRIBUTE_File_System_Name */ +#define FS_TYPE_SUPPORT_SIZE 44 +struct fs_type_info { + char *fs_name; + long magic_number; +} __packed; + +struct smb2_oplock_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 Reserved2; + __le64 PersistentFid; + __le64 VolatileFid; +} __packed; + +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + +struct smb2_lease_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 44 */ + __le16 Epoch; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 CurrentLeaseState; + __le32 NewLeaseState; + __le32 BreakReason; + __le32 AccessMaskHint; + __le32 ShareMaskHint; +} __packed; + +struct smb2_lease_ack { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 Reserved; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 LeaseState; + __le64 LeaseDuration; +} __packed; + +/* + * PDU infolevel structure definitions + * BB consider moving to a different header + */ + +/* File System Information Classes */ +#define FS_VOLUME_INFORMATION 1 /* Query */ +#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_SIZE_INFORMATION 3 /* Query */ +#define FS_DEVICE_INFORMATION 4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ +#define FS_CONTROL_INFORMATION 6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION 7 /* Query */ +#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ +#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ + +struct smb2_fs_full_size_info { + __le64 TotalAllocationUnits; + __le64 CallerAvailableAllocationUnits; + __le64 ActualAvailableAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; + +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FSEffPhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + +/* File System Control Information */ +struct smb2_fs_control_info { + __le64 FreeSpaceStartFiltering; + __le64 FreeSpaceThreshold; + __le64 FreeSpaceStopFiltering; + __le64 DefaultQuotaThreshold; + __le64 DefaultQuotaLimit; + __le32 FileSystemControlFlags; + __le32 Padding; +} __packed; + +/* partial list of QUERY INFO levels */ +#define FILE_DIRECTORY_INFORMATION 1 +#define FILE_FULL_DIRECTORY_INFORMATION 2 +#define FILE_BOTH_DIRECTORY_INFORMATION 3 +#define FILE_BASIC_INFORMATION 4 +#define FILE_STANDARD_INFORMATION 5 +#define FILE_INTERNAL_INFORMATION 6 +#define FILE_EA_INFORMATION 7 +#define FILE_ACCESS_INFORMATION 8 +#define FILE_NAME_INFORMATION 9 +#define FILE_RENAME_INFORMATION 10 +#define FILE_LINK_INFORMATION 11 +#define FILE_NAMES_INFORMATION 12 +#define FILE_DISPOSITION_INFORMATION 13 +#define FILE_POSITION_INFORMATION 14 +#define FILE_FULL_EA_INFORMATION 15 +#define FILE_MODE_INFORMATION 16 +#define FILE_ALIGNMENT_INFORMATION 17 +#define FILE_ALL_INFORMATION 18 +#define FILE_ALLOCATION_INFORMATION 19 +#define FILE_END_OF_FILE_INFORMATION 20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION 22 +#define FILE_PIPE_INFORMATION 23 +#define FILE_PIPE_LOCAL_INFORMATION 24 +#define FILE_PIPE_REMOTE_INFORMATION 25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION 27 +#define FILE_COMPRESSION_INFORMATION 28 +#define FILE_OBJECT_ID_INFORMATION 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION 31 +#define FILE_QUOTA_INFORMATION 32 +#define FILE_REPARSE_POINT_INFORMATION 33 +#define FILE_NETWORK_OPEN_INFORMATION 34 +#define FILE_ATTRIBUTE_TAG_INFORMATION 35 +#define FILE_TRACKING_INFORMATION 36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 +#define FILEID_FULL_DIRECTORY_INFORMATION 38 +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION 40 +#define FILE_SFIO_RESERVE_INFORMATION 44 +#define FILE_SFIO_VOLUME_INFORMATION 45 +#define FILE_HARD_LINK_INFORMATION 46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION 54 + +#define OP_BREAK_STRUCT_SIZE_20 24 +#define OP_BREAK_STRUCT_SIZE_21 36 + +struct smb2_file_access_info { + __le32 AccessFlags; +} __packed; + +struct smb2_file_alignment_info { + __le32 AlignmentRequirement; +} __packed; + +struct smb2_file_internal_info { + __le64 IndexNumber; +} __packed; /* level 6 Query */ + +struct smb2_file_rename_info { /* encoding of request for level 10 */ + __u8 ReplaceIfExists; /* 1 = replace existing target with new */ + /* 0 = fail if target already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[0]; /* New name to be assigned */ +} __packed; /* level 10 Set */ + +struct smb2_file_link_info { /* encoding of request for level 11 */ + __u8 ReplaceIfExists; /* 1 = replace existing link with new */ + /* 0 = fail if link already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[0]; /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 18 Query */ + +struct smb2_file_basic_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ +} __packed; + +struct smb2_file_alt_name_info { + __le32 FileNameLength; + char FileName[0]; +} __packed; + +struct smb2_file_stream_info { + __le32 NextEntryOffset; + __le32 StreamNameLength; + __le64 StreamSize; + __le64 StreamAllocationSize; + char StreamName[0]; +} __packed; + +struct smb2_file_eof_info { /* encoding of request for level 10 */ + __le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + +struct smb2_file_ntwrk_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; + +struct smb2_file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; /* level 18 Query */ + +struct smb2_file_ea_info { + __le32 EASize; +} __packed; + +struct smb2_file_alloc_info { + __le64 AllocationSize; +} __packed; + +struct smb2_file_disposition_info { + __u8 DeletePending; +} __packed; + +struct smb2_file_pos_info { + __le64 CurrentByteOffset; +} __packed; + +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000103e) + +struct smb2_file_mode_info { + __le32 Mode; +} __packed; + +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +struct smb2_file_comp_info { + __le64 CompressedFileSize; + __le16 CompressionFormat; + __u8 CompressionUnitShift; + __u8 ChunkShift; + __u8 ClusterShift; + __u8 Reserved[3]; +} __packed; + +struct smb2_file_attr_tag_info { + __le32 FileAttributes; + __le32 ReparseTag; +} __packed; + +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_ea_info_req { + __le32 NextEntryOffset; + __u8 EaNameLength; + char name[1]; +} __packed; /* level 15 Query */ + +struct smb2_ea_info { + __le32 NextEntryOffset; + __u8 Flags; + __u8 EaNameLength; + __le16 EaValueLength; + char name[1]; + /* optionally followed by value */ +} __packed; /* level 15 Query */ + +struct create_ea_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb2_ea_info ea; +} __packed; + +struct create_sd_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb_ntsd ntsd; +} __packed; + +/* Find File infolevels */ +#define SMB_FIND_FILE_POSIX_INFO 0x064 + +/* Level 100 query info */ +struct smb311_posix_qinfo { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 Sids[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 SidBuffer[40]; + __le32 name_len; + u8 name[1]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* functions */ +int init_smb2_0_server(struct ksmbd_conn *conn); +void init_smb2_1_server(struct ksmbd_conn *conn); +void init_smb3_0_server(struct ksmbd_conn *conn); +void init_smb3_02_server(struct ksmbd_conn *conn); +int init_smb3_11_server(struct ksmbd_conn *conn); + +void init_smb2_max_read_size(unsigned int sz); +void init_smb2_max_write_size(unsigned int sz); +void init_smb2_max_trans_size(unsigned int sz); + +bool is_smb2_neg_cmd(struct ksmbd_work *work); +bool is_smb2_rsp(struct ksmbd_work *work); + +u16 get_smb2_cmd_val(struct ksmbd_work *work); +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); +int init_smb2_rsp_hdr(struct ksmbd_work *work); +int smb2_allocate_rsp_buf(struct ksmbd_work *work); +bool is_chained_smb2_message(struct ksmbd_work *work); +int init_smb2_neg_rsp(struct ksmbd_work *work); +void smb2_set_err_rsp(struct ksmbd_work *work); +int smb2_check_user_session(struct ksmbd_work *work); +int smb2_get_ksmbd_tcon(struct ksmbd_work *work); +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); +int smb2_check_sign_req(struct ksmbd_work *work); +void smb2_set_sign_rsp(struct ksmbd_work *work); +int smb3_check_sign_req(struct ksmbd_work *work); +void smb3_set_sign_rsp(struct ksmbd_work *work); +int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + __le16 dialects_count); +struct file_lock *smb_flock_init(struct file *f); +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); +struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +void smb3_preauth_hash_rsp(struct ksmbd_work *work); +bool smb3_is_transform_hdr(void *buf); +int smb3_decrypt_req(struct ksmbd_work *work); +int smb3_encrypt_resp(struct ksmbd_work *work); +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); +int smb2_set_rsp_credits(struct ksmbd_work *work); + +/* smb2 misc functions */ +int ksmbd_smb2_check_message(struct ksmbd_work *work); + +/* smb2 command handlers */ +int smb2_handle_negotiate(struct ksmbd_work *work); +int smb2_negotiate_request(struct ksmbd_work *work); +int smb2_sess_setup(struct ksmbd_work *work); +int smb2_tree_connect(struct ksmbd_work *work); +int smb2_tree_disconnect(struct ksmbd_work *work); +int smb2_session_logoff(struct ksmbd_work *work); +int smb2_open(struct ksmbd_work *work); +int smb2_query_info(struct ksmbd_work *work); +int smb2_query_dir(struct ksmbd_work *work); +int smb2_close(struct ksmbd_work *work); +int smb2_echo(struct ksmbd_work *work); +int smb2_set_info(struct ksmbd_work *work); +int smb2_read(struct ksmbd_work *work); +int smb2_write(struct ksmbd_work *work); +int smb2_flush(struct ksmbd_work *work); +int smb2_cancel(struct ksmbd_work *work); +int smb2_lock(struct ksmbd_work *work); +int smb2_ioctl(struct ksmbd_work *work); +int smb2_oplock_break(struct ksmbd_work *work); +int smb2_notify(struct ksmbd_work *ksmbd_work); + +#endif /* _SMB2PDU_H */ diff -Naur --no-dereference a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c --- a/fs/ksmbd/smbacl.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smbacl.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,1422 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include +#include +#include + +#include "smbacl.h" +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "mgmt/share_config.h" + +static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* security id for everyone/world system group */ +static const struct smb_sid creator_owner = { + 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; +/* security id for everyone/world system group */ +static const struct smb_sid creator_group = { + 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; + +/* security id for everyone/world system group */ +static const struct smb_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct smb_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if (!ctsid || !cwsid) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) +{ + int i; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; +} + +/* + * change posix mode to reflect permissions + * pmode is the existing mode (we only want to overwrite part of this + * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 + */ +static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, + int type) +{ + __u32 flags = le32_to_cpu(ace_flags); + umode_t mode = 0; + + if (flags & GENERIC_ALL) { + mode = 0777; + ksmbd_debug(SMB, "all perms\n"); + return mode; + } + + if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) + mode = 0444; + if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { + mode |= 0222; + if (S_ISDIR(fattr->cf_mode)) + mode |= 0111; + } + if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) + mode |= 0111; + + if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) + mode = ~mode; + + ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); + + return mode; +} + +/* + * Generate access flags to reflect permissions mode is the existing mode. + * This function is called for every ACE in the DACL whose SID matches + * with either owner or group or everyone. + */ +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* + * check for R/W/X UGO since we do not know whose flags + * is this but we have cleared all the bits sans RWX for + * either user or group or other as per bits_to_use + */ + if (mode & 0444) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & 0222) + *pace_flags |= FILE_WRITE_RIGHTS; + if (mode & 0111) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", + mode, *pace_flags); +} + +static __u16 fill_ace_for_sid(struct smb_ace *pntace, + const struct smb_sid *psid, int type, int flags, + umode_t mode, umode_t bits) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = type; + pntace->flags = flags; + mode_to_access_flags(mode, bits, &access_req); + if (!access_req) + access_req = SET_MINIMUM_RIGHTS; + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) +{ + switch (sidtype) { + case SIDOWNER: + smb_copy_sid(ssid, &server_conf.domain_sid); + break; + case SIDUNIX_USER: + smb_copy_sid(ssid, &sid_unix_users); + break; + case SIDUNIX_GROUP: + smb_copy_sid(ssid, &sid_unix_groups); + break; + case SIDCREATOR_OWNER: + smb_copy_sid(ssid, &creator_owner); + return; + case SIDCREATOR_GROUP: + smb_copy_sid(ssid, &creator_group); + return; + case SIDNFS_USER: + smb_copy_sid(ssid, &sid_unix_NFS_users); + break; + case SIDNFS_GROUP: + smb_copy_sid(ssid, &sid_unix_NFS_groups); + break; + case SIDNFS_MODE: + smb_copy_sid(ssid, &sid_unix_NFS_mode); + break; + default: + return; + } + + /* RID */ + ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); + ssid->num_subauth++; +} + +static int sid_to_id(struct user_namespace *user_ns, + struct smb_sid *psid, uint sidtype, + struct smb_fattr *fattr) +{ + int rc = -EINVAL; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + pr_err("%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if (!compare_sids(psid, &sid_everyone)) + return -EIO; + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + /* + * Translate raw sid into kuid in the server's user + * namespace. + */ + uid = make_kuid(&init_user_ns, id); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + /* If this is an idmapped mount, apply the idmapping. */ + uid = kuid_from_mnt(user_ns, uid); +#endif + if (uid_valid(uid)) { + fattr->cf_uid = uid; + rc = 0; + } + } else { + kgid_t gid; + gid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + /* + * Translate raw sid into kgid in the server's user + * namespace. + */ + gid = make_kgid(&init_user_ns, id); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + /* If this is an idmapped mount, apply the idmapping. */ + gid = kgid_from_mnt(user_ns, gid); +#endif + if (gid_valid(gid)) { + fattr->cf_gid = gid; + rc = 0; + } + } + + return rc; +} + +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace) +{ + int i; + + pace->e_tag = ACL_USER_OBJ; + pace->e_perm = state->owner.allow; + for (i = 0; i < state->users->n; i++) { + pace++; + pace->e_tag = ACL_USER; + pace->e_uid = state->users->aces[i].uid; + pace->e_perm = state->users->aces[i].perms.allow; + } + + pace++; + pace->e_tag = ACL_GROUP_OBJ; + pace->e_perm = state->group.allow; + + for (i = 0; i < state->groups->n; i++) { + pace++; + pace->e_tag = ACL_GROUP; + pace->e_gid = state->groups->aces[i].gid; + pace->e_perm = state->groups->aces[i].perms.allow; + } + + if (state->users->n || state->groups->n) { + pace++; + pace->e_tag = ACL_MASK; + pace->e_perm = state->mask.allow; + } + + pace++; + pace->e_tag = ACL_OTHER; + pace->e_perm = state->other.allow; +} + +int init_acl_state(struct posix_acl_state *state, int cnt) +{ + int alloc; + + memset(state, 0, sizeof(struct posix_acl_state)); + /* + * In the worst case, each individual acl could be for a distinct + * named user or group, but we don't know which, so we allocate + * enough space for either: + */ + alloc = sizeof(struct posix_ace_state_array) + + cnt * sizeof(struct posix_user_ace_state); + state->users = kzalloc(alloc, GFP_KERNEL); + if (!state->users) + return -ENOMEM; + state->groups = kzalloc(alloc, GFP_KERNEL); + if (!state->groups) { + kfree(state->users); + return -ENOMEM; + } + return 0; +} + +void free_acl_state(struct posix_acl_state *state) +{ + kfree(state->users); + kfree(state->groups); +} + +static void parse_dacl(struct user_namespace *user_ns, + struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + int i, ret; + int num_aces = 0; + unsigned int acl_size; + char *acl_base; + struct smb_ace **ppace; + struct posix_acl_entry *cf_pace, *cf_pdace; + struct posix_acl_state acl_state, default_acl_state; + umode_t mode = 0, acl_mode; + bool owner_found = false, group_found = false, others_found = false; + + if (!pdacl) + return; + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || + end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + pr_err("ACL too small to parse DACL\n"); + return; + } + + ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces <= 0) + return; + + if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) + return; + + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); + if (!ppace) + return; + + ret = init_acl_state(&acl_state, num_aces); + if (ret) + return; + ret = init_acl_state(&default_acl_state, num_aces); + if (ret) { + free_acl_state(&acl_state); + return; + } + + /* + * reset rwx permissions for user/group/other. + * Also, if num_aces is 0 i.e. DACL has no ACEs, + * user/group/other have no permissions + */ + for (i = 0; i < num_aces; ++i) { + if (end_of_acl - acl_base < acl_size) + break; + + ppace[i] = (struct smb_ace *)(acl_base + acl_size); + acl_base = (char *)ppace[i]; + acl_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + + if (end_of_acl - acl_base < acl_size || + ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + (end_of_acl - acl_base < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || + (le16_to_cpu(ppace[i]->size) < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) + break; + + acl_size = le16_to_cpu(ppace[i]->size); + ppace[i]->access_req = + smb_map_generic_desired_access(ppace[i]->access_req); + + if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { + fattr->cf_mode = + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else if (!compare_sids(&ppace[i]->sid, pownersid)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0700; + + if (!owner_found) { + mode &= ~(0700); + mode |= acl_mode; + } + owner_found = true; + } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0070; + if (!group_found) { + mode &= ~(0070); + mode |= acl_mode; + } + group_found = true; + } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0007; + if (!others_found) { + mode &= ~(0007); + mode |= acl_mode; + } + others_found = true; + } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { + continue; + } else { + struct smb_fattr temp_fattr; + + acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, + ppace[i]->type); + temp_fattr.cf_uid = INVALID_UID; + ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); + if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); + continue; + } + + acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = + temp_fattr.cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + temp_fattr.cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + } + } + kfree(ppace); + + if (owner_found) { + /* The owner must be set to at least read-only. */ + acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + fattr->cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + } + + if (group_found) { + acl_state.group.allow = (mode & 0070) >> 3; + acl_state.groups->aces[acl_state.groups->n].gid = + fattr->cf_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + default_acl_state.group.allow = (mode & 0070) >> 3; + default_acl_state.groups->aces[default_acl_state.groups->n].gid = + fattr->cf_gid; + default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + } + + if (others_found) { + fattr->cf_mode &= ~(0007); + fattr->cf_mode |= mode & 0007; + + acl_state.other.allow = mode & 0007; + default_acl_state.other.allow = mode & 0007; + } + + if (acl_state.users->n || acl_state.groups->n) { + acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = + posix_acl_alloc(acl_state.users->n + + acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_acls) { + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); + } + } + } + + if (default_acl_state.users->n || default_acl_state.groups->n) { + default_acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_dacls = + posix_acl_alloc(default_acl_state.users->n + + default_acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_dacls) { + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); + } + } + } + free_acl_state(&acl_state); + free_acl_state(&default_acl_state); +} + +static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, + struct smb_ace *pndace, + struct smb_fattr *fattr, u32 *num_aces, + u16 *size, u32 nt_aces_num) +{ + struct posix_acl_entry *pace; + struct smb_sid *sid; + struct smb_ace *ntace; + int i, j; + + if (!fattr->cf_acls) + goto posix_default_acl; + + pace = fattr->cf_acls->a_entries; + for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { + int flags = 0; + + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + unsigned int sid_type = SIDOWNER; + + uid = posix_acl_uid_translate(user_ns, pace); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDUNIX_GROUP, sid); + } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + smb_copy_sid(sid, &sid_everyone); + } else { + kfree(sid); + continue; + } + ntace = pndace; + for (j = 0; j < nt_aces_num; j++) { + if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == + sid->sub_auth[sid->num_subauth - 1]) + goto pass_same_sid; + ntace = (struct smb_ace *)((char *)ntace + + le16_to_cpu(ntace->size)); + } + + if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) + flags = 0x03; + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + + if (S_ISDIR(fattr->cf_mode) && + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + 0x03, pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + } + +pass_same_sid: + kfree(sid); + } + + if (nt_aces_num) + return; + +posix_default_acl: + if (!fattr->cf_dacls) + return; + + pace = fattr->cf_dacls->a_entries; + for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + + uid = posix_acl_uid_translate(user_ns, pace); + id_to_sid(uid, SIDCREATOR_OWNER, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDCREATOR_GROUP, sid); + } else { + kfree(sid); + continue; + } + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + kfree(sid); + } +} + +static void set_ntacl_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, + struct smb_acl *nt_dacl, + const struct smb_sid *pownersid, + const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + struct smb_ace *ntace, *pndace; + int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; + int i; + + pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + if (nt_num_aces) { + ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); + for (i = 0; i < nt_num_aces; i++) { + memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size)); + size += le16_to_cpu(ntace->size); + ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size)); + num_aces++; + } + } + + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, nt_num_aces); + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static void set_mode_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, struct smb_fattr *fattr) +{ + struct smb_ace *pace, *pndace; + u32 num_aces = 0; + u16 size = 0, ace_size = 0; + uid_t uid; + const struct smb_sid *sid; + + pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + + if (fattr->cf_acls) { + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, num_aces); + goto out; + } + + /* owner RID */ + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (uid) + sid = &server_conf.domain_sid; + else + sid = &sid_unix_users; + ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0700); + pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + + /* Group RID */ + ace_size = fill_ace_for_sid(pace, &sid_unix_groups, + ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); + pace->sid.sub_auth[pace->sid.num_subauth++] = + cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 3; + + if (S_ISDIR(fattr->cf_mode)) { + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator owner */ + size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0700); + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator group */ + size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0070); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 5; + } + + /* other */ + size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0007); + +out: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static int parse_sid(struct smb_sid *psid, char *end_of_acl) +{ + /* + * validate that we do not go past end of ACL - sid must be at least 8 + * bytes long (assuming no sub-auths - e.g. the null SID + */ + if (end_of_acl < (char *)psid + 8) { + pr_err("ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + + return 0; +} + +/* Convert CIFS ACL to POSIX form */ +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr) +{ + int rc = 0; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + int pntsd_type; + + if (!pntsd) + return -EIO; + + if (acl_len < sizeof(struct smb_ntsd)) + return -EINVAL; + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + ksmbd_debug(SMB, + "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); + + pntsd_type = le16_to_cpu(pntsd->type); + if (!(pntsd_type & DACL_PRESENT)) { + ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); + return rc; + } + + pntsd->type = cpu_to_le16(DACL_PRESENT); + + if (pntsd->osidoffset) { + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + + rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + owner_sid_ptr = NULL; + } + } + + if (pntsd->gsidoffset) { + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); + if (rc) { + pr_err("%s: Error %d mapping Group SID to gid\n", + __func__, rc); + group_sid_ptr = NULL; + } + } + + if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == + (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + if (pntsd_type & DACL_PROTECTED) + pntsd->type |= cpu_to_le16(DACL_PROTECTED); + + if (dacloffset) { + parse_dacl(user_ns, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); + } + + return 0; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +int build_sec_desc(struct user_namespace *user_ns, + struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int addition_info, __u32 *secdesclen, + struct smb_fattr *fattr) +{ + int rc = 0; + __u32 offset; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; + struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + uid_t uid; + gid_t gid; + unsigned int sid_type = SIDOWNER; + + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!nowner_sid_ptr) + return -ENOMEM; + + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, nowner_sid_ptr); + + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!ngroup_sid_ptr) { + kfree(nowner_sid_ptr); + return -ENOMEM; + } + + gid = from_kgid(&init_user_ns, fattr->cf_gid); + id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); + + offset = sizeof(struct smb_ntsd); + pntsd->sacloffset = 0; + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE); + if (ppntsd) + pntsd->type |= ppntsd->type; + + if (addition_info & OWNER_SECINFO) { + pntsd->osidoffset = cpu_to_le32(offset); + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); + offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); + } + + if (addition_info & GROUP_SECINFO) { + pntsd->gsidoffset = cpu_to_le32(offset); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); + offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); + } + + if (addition_info & DACL_SECINFO) { + pntsd->type |= cpu_to_le16(DACL_PRESENT); + dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); + dacl_ptr->revision = cpu_to_le16(2); + dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); + dacl_ptr->num_aces = 0; + + if (!ppntsd) { + set_mode_dacl(user_ns, dacl_ptr, fattr); + } else if (!ppntsd->dacloffset) { + goto out; + } else { + struct smb_acl *ppdacl_ptr; + + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + + le32_to_cpu(ppntsd->dacloffset)); + set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, + nowner_sid_ptr, ngroup_sid_ptr, fattr); + } + pntsd->dacloffset = cpu_to_le32(offset); + offset += le16_to_cpu(dacl_ptr->size); + } + +out: + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + *secdesclen = offset; + return rc; +} + +static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, + u8 flags, __le32 access_req) +{ + ace->type = type; + ace->flags = flags; + ace->access_req = access_req; + smb_copy_sid(&ace->sid, sid); + ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); +} + +int smb_inherit_dacl(struct ksmbd_conn *conn, + struct path *path, + unsigned int uid, unsigned int gid) +{ + const struct smb_sid *psid, *creator = NULL; + struct smb_ace *parent_aces, *aces; + struct smb_acl *parent_pdacl; + struct smb_ntsd *parent_pntsd = NULL; + struct smb_sid owner_sid, group_sid; + struct dentry *parent = path->dentry->d_parent; + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; + int rc = 0, num_aces, dacloffset, pntsd_type, acl_len; + char *aces_base; + bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); + + acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns, + parent, &parent_pntsd); + if (acl_len <= 0) + return -ENOENT; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); + if (!dacloffset) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + num_aces = le32_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); + + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + if (!aces_base) { + rc = -ENOMEM; + goto free_parent_pntsd; + } + + aces = (struct smb_ace *)aces_base; + parent_aces = (struct smb_ace *)((char *)parent_pdacl + + sizeof(struct smb_acl)); + + if (pntsd_type & DACL_AUTO_INHERITED) + inherited_flags = INHERITED_ACE; + + for (i = 0; i < num_aces; i++) { + flags = parent_aces->flags; + if (!smb_inherit_flags(flags, is_dir)) + goto pass; + if (is_dir) { + flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); + if (!(flags & CONTAINER_INHERIT_ACE)) + flags |= INHERIT_ONLY_ACE; + if (flags & NO_PROPAGATE_INHERIT_ACE) + flags = 0; + } else { + flags = 0; + } + + if (!compare_sids(&creator_owner, &parent_aces->sid)) { + creator = &creator_owner; + id_to_sid(uid, SIDOWNER, &owner_sid); + psid = &owner_sid; + } else if (!compare_sids(&creator_group, &parent_aces->sid)) { + creator = &creator_group; + id_to_sid(gid, SIDUNIX_GROUP, &group_sid); + psid = &group_sid; + } else { + creator = NULL; + psid = &parent_aces->sid; + } + + if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { + smb_set_ace(aces, psid, parent_aces->type, inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + ace_cnt++; + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + flags |= INHERIT_ONLY_ACE; + psid = creator; + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { + psid = &parent_aces->sid; + } + + smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + ace_cnt++; +pass: + parent_aces = + (struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size)); + } + + if (nt_size > 0) { + struct smb_ntsd *pntsd; + struct smb_acl *pdacl; + struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; + int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + + if (parent_pntsd->osidoffset) { + powner_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->osidoffset)); + powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); + } + if (parent_pntsd->gsidoffset) { + pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->gsidoffset)); + pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); + } + + pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + + nt_size, GFP_KERNEL); + if (!pntsd) { + rc = -ENOMEM; + goto free_aces_base; + } + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); + if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + pntsd_size = sizeof(struct smb_ntsd); + pntsd->osidoffset = parent_pntsd->osidoffset; + pntsd->gsidoffset = parent_pntsd->gsidoffset; + pntsd->dacloffset = parent_pntsd->dacloffset; + + if (pntsd->osidoffset) { + struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + memcpy(owner_sid, powner_sid, powner_sid_size); + pntsd_size += powner_sid_size; + } + + if (pntsd->gsidoffset) { + struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + memcpy(group_sid, pgroup_sid, pgroup_sid_size); + pntsd_size += pgroup_sid_size; + } + + if (pntsd->dacloffset) { + struct smb_ace *pace; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); + pdacl->num_aces = cpu_to_le32(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, pntsd_size); + kfree(pntsd); + } + +free_aces_base: + kfree(aces_base); +free_parent_pntsd: + kfree(parent_pntsd); + return rc; +} + +bool smb_inherit_flags(int flags, bool is_dir) +{ + if (!is_dir) + return (flags & OBJECT_INHERIT_ACE) != 0; + + if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) + return true; + + if (flags & CONTAINER_INHERIT_ACE) + return true; + return false; +} + +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, + __le32 *pdaccess, int uid) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct smb_ntsd *pntsd = NULL; + struct smb_acl *pdacl; + struct posix_acl *posix_acls; + int rc = 0, acl_size; + struct smb_sid sid; + int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); + struct smb_ace *ace; + int i, found = 0; + unsigned int access_bits = 0; + struct smb_ace *others_ace = NULL; + struct posix_acl_entry *pa_entry; + unsigned int sid_type = SIDOWNER; + char *end_of_acl; + + ksmbd_debug(SMB, "check permission using windows acl\n"); + acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + path->dentry, &pntsd); + if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { + kfree(pntsd); + return 0; + } + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + end_of_acl = ((char *)pntsd) + acl_size; + if (end_of_acl <= (char *)pdacl) { + kfree(pntsd); + return 0; + } + + if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) || + le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) { + kfree(pntsd); + return 0; + } + + if (!pdacl->num_aces) { + if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) && + *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { + rc = -EACCES; + goto err_out; + } + kfree(pntsd); + return 0; + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + granted |= le32_to_cpu(ace->access_req); + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + if (end_of_acl < (char *)ace) + goto err_out; + } + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, &sid); + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (!compare_sids(&sid, &ace->sid) || + !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { + found = 1; + break; + } + if (!compare_sids(&sid_everyone, &ace->sid)) + others_ace = ace; + + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + if (end_of_acl < (char *)ace) + goto err_out; + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + granted |= le32_to_cpu(ace->access_req); + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); + if (posix_acls && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { + if (pa_entry->e_tag == ACL_USER) + id = posix_acl_uid_translate(user_ns, pa_entry); + else if (pa_entry->e_tag == ACL_GROUP) + id = posix_acl_gid_translate(user_ns, pa_entry); + else + continue; + + if (id == uid) { + mode_to_access_flags(pa_entry->e_perm, + 0777, + &access_bits); + if (!access_bits) + access_bits = + SET_MINIMUM_RIGHTS; + goto check_access_bits; + } + } + } + if (posix_acls) + posix_acl_release(posix_acls); + } + + if (!found) { + if (others_ace) { + ace = others_ace; + } else { + ksmbd_debug(SMB, "Can't find corresponding sid\n"); + rc = -EACCES; + goto err_out; + } + } + + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + access_bits = le32_to_cpu(ace->access_req); + break; + case ACCESS_DENIED_ACE_TYPE: + case ACCESS_DENIED_CALLBACK_ACE_TYPE: + access_bits = le32_to_cpu(~ace->access_req); + break; + } + +check_access_bits: + if (granted & + ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { + ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", + granted, le32_to_cpu(ace->access_req)); + rc = -EACCES; + goto err_out; + } + + *pdaccess = cpu_to_le32(granted); +err_out: + kfree(pntsd); + return rc; +} + +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) +{ + int rc; + struct smb_fattr fattr = {{0}}; + struct inode *inode = d_inode(path->dentry); + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct iattr newattrs; + + fattr.cf_uid = INVALID_UID; + fattr.cf_gid = INVALID_GID; + fattr.cf_mode = inode->i_mode; + + rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); + if (rc) + goto out; + + newattrs.ia_valid = ATTR_CTIME; + if (!uid_eq(fattr.cf_uid, INVALID_UID)) { + newattrs.ia_valid |= ATTR_UID; + newattrs.ia_uid = fattr.cf_uid; + } + if (!gid_eq(fattr.cf_gid, INVALID_GID)) { + inode->i_gid = fattr.cf_gid; + newattrs.ia_valid |= ATTR_GID; + newattrs.ia_gid = fattr.cf_gid; + } + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + + ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); + /* Update posix acls */ + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = set_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS, + fattr.cf_acls); +#else + rc = set_posix_acl(inode, ACL_TYPE_ACCESS, fattr.cf_acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = set_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT, fattr.cf_dacls); +#else + rc = set_posix_acl(inode, ACL_TYPE_DEFAULT, + fattr.cf_dacls); +#endif + if (rc) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + } + + inode_lock(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = notify_change(user_ns, path->dentry, &newattrs, NULL); +#else + rc = notify_change(path->dentry, &newattrs, NULL); +#endif + inode_unlock(inode); + if (rc) + goto out; + + /* Check it only calling from SD BUFFER context */ + if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) + goto out; + + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, ntsd_len); + } + +out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + mark_inode_dirty(inode); + return rc; +} + +void ksmbd_init_domain(u32 *sub_auth) +{ + int i; + + memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); + for (i = 0; i < 3; ++i) + server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); +} diff -Naur --no-dereference a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h --- a/fs/ksmbd/smbacl.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smbacl.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ + +#ifndef _SMBACL_H +#define _SMBACL_H + +#include +#include +#include +#include + +#include "mgmt/tree_connect.h" + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +/* + * ACE types - see MS-DTYP 2.4.4.1 + */ +enum { + ACCESS_ALLOWED, + ACCESS_DENIED, +}; + +/* + * Security ID types + */ +enum { + SIDOWNER = 1, + SIDGROUP, + SIDCREATOR_OWNER, + SIDCREATOR_GROUP, + SIDUNIX_USER, + SIDUNIX_GROUP, + SIDNFS_USER, + SIDNFS_GROUP, + SIDNFS_MODE, +}; + +/* Revision for ACLs */ +#define SD_REVISION 1 + +/* Control flags for Security Descriptor */ +#define OWNER_DEFAULTED 0x0001 +#define GROUP_DEFAULTED 0x0002 +#define DACL_PRESENT 0x0004 +#define DACL_DEFAULTED 0x0008 +#define SACL_PRESENT 0x0010 +#define SACL_DEFAULTED 0x0020 +#define DACL_TRUSTED 0x0040 +#define SERVER_SECURITY 0x0080 +#define DACL_AUTO_INHERIT_REQ 0x0100 +#define SACL_AUTO_INHERIT_REQ 0x0200 +#define DACL_AUTO_INHERITED 0x0400 +#define SACL_AUTO_INHERITED 0x0800 +#define DACL_PROTECTED 0x1000 +#define SACL_PROTECTED 0x2000 +#define RM_CONTROL_VALID 0x4000 +#define SELF_RELATIVE 0x8000 + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +#define DOMAIN_USER_RID_LE cpu_to_le32(513) + +struct ksmbd_conn; + +struct smb_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __packed; + +struct smb_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __packed; + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct smb_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __packed; + +struct smb_ace { + __u8 type; + __u8 flags; + __le16 size; + __le32 access_req; + struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +} __packed; + +struct smb_fattr { + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + __le32 daccess; + struct posix_acl *cf_acls; + struct posix_acl *cf_dacls; +}; + +struct posix_ace_state { + u32 allow; + u32 deny; +}; + +struct posix_user_ace_state { + union { + kuid_t uid; + kgid_t gid; + }; + struct posix_ace_state perms; +}; + +struct posix_ace_state_array { + int n; + struct posix_user_ace_state aces[]; +}; + +/* + * while processing the nfsv4 ace, this maintains the partial permissions + * calculated so far: + */ + +struct posix_acl_state { + struct posix_ace_state owner; + struct posix_ace_state group; + struct posix_ace_state other; + struct posix_ace_state everyone; + struct posix_ace_state mask; /* deny unused in this case */ + struct posix_ace_state_array *users; + struct posix_ace_state_array *groups; +}; + +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr); +int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + struct smb_ntsd *ppntsd, int addition_info, + __u32 *secdesclen, struct smb_fattr *fattr); +int init_acl_state(struct posix_acl_state *state, int cnt); +void free_acl_state(struct posix_acl_state *state); +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace); +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); +bool smb_inherit_flags(int flags, bool is_dir); +int smb_inherit_dacl(struct ksmbd_conn *conn, struct path *path, + unsigned int uid, unsigned int gid); +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, + __le32 *pdaccess, int uid); +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); +void ksmbd_init_domain(u32 *sub_auth); + +static inline uid_t posix_acl_uid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + kuid_t kuid; + + /* If this is an idmapped mount, apply the idmapping. */ + kuid = kuid_into_mnt(mnt_userns, pace->e_uid); + + /* Translate the kuid into a userspace id ksmbd would see. */ + return from_kuid(&init_user_ns, kuid); +#else + return from_kuid(&init_user_ns, pace->e_uid); +#endif +} + +static inline gid_t posix_acl_gid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + kgid_t kgid; + + /* If this is an idmapped mount, apply the idmapping. */ + kgid = kgid_into_mnt(mnt_userns, pace->e_gid); + + /* Translate the kgid into a userspace id ksmbd would see. */ + return from_kgid(&init_user_ns, kgid); +#else + return from_kgid(&init_user_ns, pace->e_gid); +#endif +} + +#endif /* _SMBACL_H */ diff -Naur --no-dereference a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c --- a/fs/ksmbd/smb_common.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb_common.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,737 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * Copyright (C) 2018 Namjae Jeon + */ + +#include "smb_common.h" +#ifdef CONFIG_SMB_INSECURE_SERVER +#include "smb1pdu.h" +#endif +#include "server.h" +#include "misc.h" +#include "smbstatus.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/share_config.h" + +/*for shortname implementation */ +static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) +#define MAGIC_CHAR '~' +#define PERIOD '.' +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) + +#ifdef CONFIG_SMB_INSECURE_SERVER +#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) +#else +#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr)) +#endif + +struct smb_protocol { + int index; + char *name; + char *prot; + __u16 prot_id; +}; + +static struct smb_protocol smb1_protos[] = { +#ifdef CONFIG_SMB_INSECURE_SERVER + { + SMB1_PROT, + "\2NT LM 0.12", + "NT1", + SMB10_PROT_ID + }, + { + SMB2_PROT, + "\2SMB 2.002", + "SMB2_02", + SMB20_PROT_ID + }, +#endif + { + SMB2X_PROT, + "\2SMB 2.???", + "SMB2_22", + SMB2X_PROT_ID + }, +}; + +static struct smb_protocol smb2_protos[] = { + { + SMB2_PROT, + "\2SMB 2.002", + "SMB2_02", + SMB20_PROT_ID + }, + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB30_PROT, + "\2SMB 3.0", + "SMB3_00", + SMB30_PROT_ID + }, + { + SMB302_PROT, + "\2SMB 3.02", + "SMB3_02", + SMB302_PROT_ID + }, + { + SMB311_PROT, + "\2SMB 3.1.1", + "SMB3_11", + SMB311_PROT_ID + }, +}; + +unsigned int ksmbd_server_side_copy_max_chunk_count(void) +{ + return 256; +} + +unsigned int ksmbd_server_side_copy_max_chunk_size(void) +{ + return (2U << 30) - 1; +} + +unsigned int ksmbd_server_side_copy_max_total_size(void) +{ + return (2U << 30) - 1; +} + +inline int ksmbd_min_protocol(void) +{ +#ifdef CONFIG_SMB_INSECURE_SERVER + return SMB1_PROT; +#else + return SMB2_PROT; +#endif +} + +inline int ksmbd_max_protocol(void) +{ + return SMB311_PROT; +} + +int ksmbd_lookup_protocol_idx(char *str) +{ + int offt = ARRAY_SIZE(smb1_protos) - 1; + int len = strlen(str); + + while (offt >= 0) { + if (!strncmp(str, smb1_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb1_protos[offt].prot, offt); + return smb1_protos[offt].index; + } + offt--; + } + + offt = ARRAY_SIZE(smb2_protos) - 1; + while (offt >= 0) { + if (!strncmp(str, smb2_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb2_protos[offt].prot, offt); + return smb2_protos[offt].index; + } + offt--; + } + return -1; +} + +/** + * ksmbd_verify_smb_message() - check for valid smb2 request header + * @work: smb work + * + * check for valid smb signature and packet direction(request/response) + * + * Return: 0 on success, otherwise error + */ +int ksmbd_verify_smb_message(struct ksmbd_work *work) +{ + struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) { + ksmbd_debug(SMB, "got SMB2 command\n"); + return ksmbd_smb2_check_message(work); + } + + return ksmbd_smb1_check_message(work); +#else + struct smb_hdr *hdr; + + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) + return ksmbd_smb2_check_message(work); + + hdr = work->request_buf; + if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && + hdr->Command == SMB_COM_NEGOTIATE) + return 0; + + return -EINVAL; +#endif +} + +/** + * ksmbd_smb_request() - check for valid smb request type + * @conn: connection instance + * + * Return: true on success, otherwise false + */ +bool ksmbd_smb_request(struct ksmbd_conn *conn) +{ + return conn->request_buf[0] == 0; +} + +static bool supported_protocol(int idx) +{ + if (idx == SMB2X_PROT && + (server_conf.min_protocol >= SMB21_PROT || + server_conf.max_protocol <= SMB311_PROT)) + return true; + + return (server_conf.min_protocol <= idx && + idx <= server_conf.max_protocol); +} + +static char *next_dialect(char *dialect, int *next_off, int bcount) +{ + dialect = dialect + *next_off; + *next_off = strnlen(dialect, bcount); + if (dialect[*next_off] != '\0') + return NULL; + return dialect; +} + +static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) +{ + int i, seq_num, bcount, next; + char *dialect; + + for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { + seq_num = 0; + next = 0; + dialect = cli_dialects; + bcount = le16_to_cpu(byte_count); + do { + dialect = next_dialect(dialect, &next, bcount); + if (!dialect) + break; + ksmbd_debug(SMB, "client requested dialect %s\n", + dialect); + if (!strcmp(dialect, smb1_protos[i].name)) { + if (supported_protocol(smb1_protos[i].index)) { + ksmbd_debug(SMB, + "selected %s dialect\n", + smb1_protos[i].name); + if (smb1_protos[i].index == SMB1_PROT) + return seq_num; + return smb1_protos[i].prot_id; + } + } + seq_num++; + bcount -= (++next); + } while (bcount > 0); + } + + return BAD_PROT_ID; +} + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) +{ + int i; + int count; + + for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { + count = le16_to_cpu(dialects_count); + while (--count >= 0) { + ksmbd_debug(SMB, "client requested dialect 0x%x\n", + le16_to_cpu(cli_dialects[count])); + if (le16_to_cpu(cli_dialects[count]) != + smb2_protos[i].prot_id) + continue; + + if (supported_protocol(smb2_protos[i].index)) { + ksmbd_debug(SMB, "selected %s dialect\n", + smb2_protos[i].name); + return smb2_protos[i].prot_id; + } + } + } + + return BAD_PROT_ID; +} + +static int ksmbd_negotiate_smb_dialect(void *buf) +{ + int smb_buf_length = get_rfc1002_len(buf); + __le32 proto = ((struct smb2_hdr *)buf)->ProtocolId; + + if (proto == SMB2_PROTO_NUMBER) { + struct smb2_negotiate_req *req; + int smb2_neg_size = + offsetof(struct smb2_negotiate_req, Dialects) - 4; + + req = (struct smb2_negotiate_req *)buf; + if (smb2_neg_size > smb_buf_length) + goto err_out; + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb_buf_length) + goto err_out; + + return ksmbd_lookup_dialect_by_id(req->Dialects, + req->DialectCount); + } + + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) { + struct smb_negotiate_req *req; + + req = (struct smb_negotiate_req *)buf; + if (le16_to_cpu(req->ByteCount) < 2) + goto err_out; + + if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + + le16_to_cpu(req->ByteCount) > smb_buf_length) { + goto err_out; + } + + return ksmbd_lookup_dialect_by_name(req->DialectsArray, + req->ByteCount); + } + +err_out: + return BAD_PROT_ID; +} + +#define SMB_COM_NEGOTIATE 0x72 +int ksmbd_init_smb_server(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; +#ifdef CONFIG_SMB_INSECURE_SERVER + void *buf = work->request_buf; + __le32 proto; +#endif + + if (conn->need_neg == false) + return 0; + +#ifdef CONFIG_SMB_INSECURE_SERVER + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) + init_smb1_server(conn); + else + init_smb2_0_server(conn); +#else + init_smb3_11_server(conn); +#endif + + if (conn->ops->get_cmd_val(work) != SMB_COM_NEGOTIATE) + conn->need_neg = false; + return 0; +} + +bool ksmbd_pdu_size_has_room(unsigned int pdu) +{ + return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4); +} + +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)) +{ + int i, rc = 0; + struct ksmbd_conn *conn = work->conn; + struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); + + for (i = 0; i < 2; i++) { + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + + if (!dir->dot_dotdot[i]) { /* fill dot entry info */ + if (i == 0) { + d_info->name = "."; + d_info->name_len = 1; + } else { + d_info->name = ".."; + d_info->name_len = 2; + } + + if (!match_pattern(d_info->name, d_info->name_len, + search_pattern)) { + dir->dot_dotdot[i] = 1; + continue; + } + + ksmbd_kstat.kstat = &kstat; + ksmbd_vfs_fill_dentry_attrs(work, + user_ns, + dir->filp->f_path.dentry->d_parent, + &ksmbd_kstat); + rc = fn(conn, info_level, d_info, &ksmbd_kstat); + if (rc) + break; + if (d_info->out_buf_len <= 0) + break; + + dir->dot_dotdot[i] = 1; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + break; + } + } + } + + return rc; +} + +/** + * ksmbd_extract_shortname() - get shortname from long filename + * @conn: connection instance + * @longname: source long filename + * @shortname: destination short filename + * + * Return: shortname length or 0 when source long name is '.' or '..' + * TODO: Though this function comforms the restriction of 8.3 Filename spec, + * but the result is different with Windows 7's one. need to check. + */ +int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, + char *shortname) +{ + const char *p; + char base[9], extension[4]; + char out[13] = {0}; + int baselen = 0; + int extlen = 0, len = 0; + unsigned int csum = 0; + const unsigned char *ptr; + bool dot_present = true; + + p = longname; + if ((*p == '.') || (!(strcmp(p, "..")))) { + /*no mangling required */ + return 0; + } + + p = strrchr(longname, '.'); + if (p == longname) { /*name starts with a dot*/ + strscpy(extension, "___", strlen("___")); + } else { + if (p) { + p++; + while (*p && extlen < 3) { + if (*p != '.') + extension[extlen++] = toupper(*p); + p++; + } + extension[extlen] = '\0'; + } else { + dot_present = false; + } + } + + p = longname; + if (*p == '.') { + p++; + longname++; + } + while (*p && (baselen < 5)) { + if (*p != '.') + base[baselen++] = toupper(*p); + p++; + } + + base[baselen] = MAGIC_CHAR; + memcpy(out, base, baselen + 1); + + ptr = longname; + len = strlen(longname); + for (; len > 0; len--, ptr++) + csum += *ptr; + + csum = csum % (MANGLE_BASE * MANGLE_BASE); + out[baselen + 1] = mangle(csum / MANGLE_BASE); + out[baselen + 2] = mangle(csum); + out[baselen + 3] = PERIOD; + + if (dot_present) + memcpy(&out[baselen + 4], extension, 4); + else + out[baselen + 4] = '\0'; + smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, + conn->local_nls, 0); + len = strlen(out) * 2; + return len; +} + +static int __smb2_negotiate(struct ksmbd_conn *conn) +{ + return (conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID); +} + +#ifndef CONFIG_SMB_INSECURE_SERVER +static int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct smb_negotiate_rsp *neg_rsp = work->response_buf; + + ksmbd_debug(SMB, "Unsupported SMB protocol\n"); + neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE; + return -EINVAL; +} +#endif + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) +{ + struct ksmbd_conn *conn = work->conn; + int ret; + + conn->dialect = ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + + if (command == SMB2_NEGOTIATE_HE) { + struct smb2_hdr *smb2_hdr = work->request_buf; + + if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) { + ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n"); + command = SMB_COM_NEGOTIATE; + } + } + + if (command == SMB2_NEGOTIATE_HE) { + ret = smb2_handle_negotiate(work); + init_smb2_neg_rsp(work); + return ret; + } + + if (command == SMB_COM_NEGOTIATE) { + if (__smb2_negotiate(conn)) { + conn->need_neg = true; + init_smb3_11_server(conn); + init_smb2_neg_rsp(work); + ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); + return 0; + } + return smb_handle_negotiate(work); + } + + pr_err("Unknown SMB negotiation command: %u\n", command); + return -EINVAL; +} + +enum SHARED_MODE_ERRORS { + SHARE_DELETE_ERROR, + SHARE_READ_ERROR, + SHARE_WRITE_ERROR, + FILE_READ_ERROR, + FILE_WRITE_ERROR, + FILE_DELETE_ERROR, +}; + +static const char * const shared_mode_errors[] = { + "Current access mode does not permit SHARE_DELETE", + "Current access mode does not permit SHARE_READ", + "Current access mode does not permit SHARE_WRITE", + "Desired access mode does not permit FILE_READ", + "Desired access mode does not permit FILE_WRITE", + "Desired access mode does not permit FILE_DELETE", +}; + +static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, + struct ksmbd_file *curr_fp) +{ + ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); + ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", + prev_fp->saccess, curr_fp->daccess); +} + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) +{ + int rc = 0; + struct ksmbd_file *prev_fp; + + /* + * Lookup fp in master fp list, and check desired access and + * shared mode between previous open and current open. + */ + read_lock(&curr_fp->f_ci->m_lock); + list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { + if (file_inode(filp) != file_inode(prev_fp->filp)) + continue; + + if (filp == prev_fp->filp) + continue; + + if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) + if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) + continue; + + if (prev_fp->attrib_only != curr_fp->attrib_only) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && + curr_fp->daccess & FILE_DELETE_LE) { + smb_shared_mode_error(SHARE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + /* + * Only check FILE_SHARE_DELETE if stream opened and + * normal file opened. + */ + if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && + curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { + smb_shared_mode_error(SHARE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && + curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { + smb_shared_mode_error(SHARE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_READ_LE)) { + smb_shared_mode_error(FILE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { + smb_shared_mode_error(FILE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & FILE_DELETE_LE && + !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { + smb_shared_mode_error(FILE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + } + read_unlock(&curr_fp->f_ci->m_lock); + + return rc; +} + +bool is_asterisk(char *p) +{ + return p && p[0] == '*'; +} + +int ksmbd_override_fsids(struct ksmbd_work *work) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cred *cred; + struct group_info *gi; + unsigned int uid; + unsigned int gid; + + uid = user_uid(sess->user); + gid = user_gid(sess->user); + if (share->force_uid != KSMBD_SHARE_INVALID_UID) + uid = share->force_uid; + if (share->force_gid != KSMBD_SHARE_INVALID_GID) + gid = share->force_gid; + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + cred->fsuid = make_kuid(current_user_ns(), uid); + cred->fsgid = make_kgid(current_user_ns(), gid); + + gi = groups_alloc(0); + if (!gi) { + abort_creds(cred); + return -ENOMEM; + } + set_groups(cred, gi); + put_group_info(gi); + + if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) + cred->cap_effective = cap_drop_fs_set(cred->cap_effective); + + WARN_ON(work->saved_cred); + work->saved_cred = override_creds(cred); + if (!work->saved_cred) { + abort_creds(cred); + return -EINVAL; + } + return 0; +} + +void ksmbd_revert_fsids(struct ksmbd_work *work) +{ + const struct cred *cred; + + WARN_ON(!work->saved_cred); + + cred = current_cred(); + revert_creds(work->saved_cred); + put_cred(cred); + work->saved_cred = NULL; +} + +__le32 smb_map_generic_desired_access(__le32 daccess) +{ + if (daccess & FILE_GENERIC_READ_LE) { + daccess |= cpu_to_le32(GENERIC_READ_FLAGS); + daccess &= ~FILE_GENERIC_READ_LE; + } + + if (daccess & FILE_GENERIC_WRITE_LE) { + daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); + daccess &= ~FILE_GENERIC_WRITE_LE; + } + + if (daccess & FILE_GENERIC_EXECUTE_LE) { + daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); + daccess &= ~FILE_GENERIC_EXECUTE_LE; + } + + if (daccess & FILE_GENERIC_ALL_LE) { + daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); + daccess &= ~FILE_GENERIC_ALL_LE; + } + + return daccess; +} diff -Naur --no-dereference a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h --- a/fs/ksmbd/smb_common.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smb_common.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB_COMMON_H__ +#define __SMB_COMMON_H__ + +#include + +#include "glob.h" +#include "nterr.h" +#include "smb2pdu.h" + +/* ksmbd's Specific ERRNO */ +#define ESHARE 50000 + +#define SMB1_PROT 0 +#define SMB2_PROT 1 +#define SMB21_PROT 2 +/* multi-protocol negotiate request */ +#define SMB2X_PROT 3 +#define SMB30_PROT 4 +#define SMB302_PROT 5 +#define SMB311_PROT 6 +#define BAD_PROT 0xFFFF + +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#define SMB21_VERSION_STRING "2.1" +#define SMB30_VERSION_STRING "3.0" +#define SMB302_VERSION_STRING "3.02" +#define SMB311_VERSION_STRING "3.1.1" + +/* Dialects */ +#define SMB10_PROT_ID 0x00 +#define SMB20_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +/* multi-protocol negotiate request */ +#define SMB2X_PROT_ID 0x02FF +#define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 +#define SMB311_PROT_ID 0x0311 +#define BAD_PROT_ID 0xFFFF + +#define SMB_ECHO_INTERVAL (60 * HZ) + +#define CIFS_DEFAULT_IOSIZE (64 * 1024) +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ + +#define MAX_STREAM_PROT_LEN 0x00FFFFFF + +#define IS_SMB2(x) ((x)->vals->protocol_id != SMB10_PROT_ID) +#define MAX_HEADER_SIZE(conn) ((conn)->vals->max_header_size) + +/* Responses when opening a file. */ +#define F_SUPERSEDED 0 +#define F_OPENED 1 +#define F_CREATED 2 +#define F_OVERWRITTEN 3 + +/* + * File Attribute flags + */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_DIRECTORY 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_DEVICE 0x0040 +#define ATTR_NORMAL 0x0080 +#define ATTR_TEMPORARY 0x0100 +#define ATTR_SPARSE 0x0200 +#define ATTR_REPARSE 0x0400 +#define ATTR_COMPRESSED 0x0800 +#define ATTR_OFFLINE 0x1000 +#define ATTR_NOT_CONTENT_INDEXED 0x2000 +#define ATTR_ENCRYPTED 0x4000 +#define ATTR_POSIX_SEMANTICS 0x01000000 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) +#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) +#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) +#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) +#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) +#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) +#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) +#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) +#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) +#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) +#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) +#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) +#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) +#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) +#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 + +#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ +#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ +#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ +#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ +/* with the file can be read */ +#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ +/* with the file can be written */ +#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ +/* the file using system paging I/O */ +#define FILE_DELETE_CHILD 0x00000040 +#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ +/* file can be read */ +#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ +/* file can be written */ +#define DELETE 0x00010000 /* The file can be deleted */ +#define READ_CONTROL 0x00020000 /* The access control list and */ +/* ownership associated with the */ +/* file can be read */ +#define WRITE_DAC 0x00040000 /* The access control list and */ +/* ownership associated with the */ +/* file can be written. */ +#define WRITE_OWNER 0x00080000 /* Ownership information associated */ +/* with the file can be written */ +#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ +/* synchronize with the completion */ +/* of an input/output request */ +#define GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 +/* In summary - Relevant file */ +/* access flags from CIFS are */ +/* file_read_data, file_write_data */ +/* file_execute, file_read_attributes*/ +/* write_dac, and delete. */ + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA \ + | FILE_DELETE_CHILD \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + +/* generic flags for file open */ +#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ + FILE_READ_ATTRIBUTES | \ + FILE_READ_EA | SYNCHRONIZE) + +#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ + FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ + FILE_APPEND_DATA | SYNCHRONIZE) + +#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ + FILE_READ_ATTRIBUTES | SYNCHRONIZE) + +#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ + WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ + FILE_WRITE_DATA | FILE_APPEND_DATA | \ + FILE_READ_EA | FILE_WRITE_EA | \ + FILE_EXECUTE | FILE_DELETE_CHILD | \ + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) + +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) +#define SMB_COM_NEGOTIATE 0x72 + +#define SMB1_CLIENT_GUID_SIZE (16) +struct smb_hdr { + __be32 smb_buf_length; + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __packed DosError; + __le32 CifsError; + } __packed Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __packed Sequence; + __u8 SecuritySignature[8]; /* le */ + } __packed Signature; + __u8 pad[2]; + __le16 Tid; + __le16 Pid; + __le16 Uid; + __le16 Mid; + __u8 WordCount; +} __packed; + +struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[1]; +} __packed; + +struct smb_negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __u8 SecurityMode; + __le16 MaxMpxCount; + __le16 MaxNumberVcs; + __le32 MaxBufferSize; + __le32 MaxRawSize; + __le32 SessionKey; + __le32 Capabilities; /* see below */ + __le32 SystemTimeLow; + __le32 SystemTimeHigh; + __le16 ServerTimeZone; + __u8 EncryptionKeyLength; + __le16 ByteCount; + union { + unsigned char EncryptionKey[8]; /* cap extended security off */ + /* followed by Domain name - if extended security is off */ + /* followed by 16 bytes of server GUID */ + /* then security blob if cap_extended_security negotiated */ + struct { + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; + unsigned char SecurityBlob[1]; + } __packed extended_response; + } __packed u; +} __packed; + +struct filesystem_attribute_info { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + __le16 FileSystemName[1]; /* do not have to save this - get subset? */ +} __packed; + +struct filesystem_device_info { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __packed; /* device info level 0x104 */ + +struct filesystem_vol_info { + __le64 VolumeCreationTime; + __le32 SerialNumber; + __le32 VolumeLabelSize; + __le16 Reserved; + __le16 VolumeLabel[1]; +} __packed; + +struct filesystem_info { + __le64 TotalAllocationUnits; + __le64 FreeAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; /* size info, level 0x103 */ + +#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ +#define STRING_LENGTH 28 + +struct fs_extended_info { + __le32 magic; + __le32 version; + __le32 release; + __u64 rel_date; + char version_string[STRING_LENGTH]; +} __packed; + +struct object_id_info { + char objid[16]; + struct fs_extended_info extended_info; +} __packed; + +struct file_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0x101 FF resp data */ + +struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0xc FF resp data */ + +struct file_full_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; + char FileName[1]; +} __packed; /* level 0x102 FF resp */ + +struct file_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[1]; +} __packed; /* level 0x104 FFrsp data */ + +struct file_id_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; + char FileName[1]; +} __packed; + +struct file_id_full_dir_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[1]; +} __packed; /* level 0x105 FF rsp data */ + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __le16 lock_cmd; + __u32 capabilities; + __u32 max_read_size; + __u32 max_write_size; + __u32 max_trans_size; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; + size_t create_durable_size; + size_t create_durable_v2_size; + size_t create_mxac_size; + size_t create_disk_id_size; + size_t create_posix_size; +}; + +struct filesystem_posix_info { + /* For undefined recommended transfer size return -1 in that field */ + __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + * (above). If block size is unknown, 4096 would be a + * reasonable block size for a server to report. + * Note that returning the blocks/blocksavail removes need + * to make a second call (to QFSInfo level 0x103 to get this info. + * UserBlockAvail is typically less than or equal to BlocksAvail, + * if no distinction is made return the same value in each + */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __packed; + +struct smb_version_ops { + u16 (*get_cmd_val)(struct ksmbd_work *swork); + int (*init_rsp_hdr)(struct ksmbd_work *swork); + void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); + int (*allocate_rsp_buf)(struct ksmbd_work *work); + int (*set_rsp_credits)(struct ksmbd_work *work); + int (*check_user_session)(struct ksmbd_work *work); + int (*get_ksmbd_tcon)(struct ksmbd_work *work); + bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); + int (*check_sign_req)(struct ksmbd_work *work); + void (*set_sign_rsp)(struct ksmbd_work *work); + int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); + int (*generate_encryptionkey)(struct ksmbd_session *sess); + bool (*is_transform_hdr)(void *buf); + int (*decrypt_req)(struct ksmbd_work *work); + int (*encrypt_resp)(struct ksmbd_work *work); +}; + +struct smb_version_cmds { + int (*proc)(struct ksmbd_work *swork); +}; + +static inline size_t +smb2_hdr_size_no_buflen(struct smb_version_values *vals) +{ + return vals->header_size - 4; +} + +int ksmbd_min_protocol(void); +int ksmbd_max_protocol(void); + +int ksmbd_lookup_protocol_idx(char *str); + +int ksmbd_verify_smb_message(struct ksmbd_work *work); +bool ksmbd_smb_request(struct ksmbd_conn *conn); + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); + +int ksmbd_init_smb_server(struct ksmbd_work *work); + +bool ksmbd_pdu_size_has_room(unsigned int pdu); + +struct ksmbd_kstat; +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, + int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, + int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)); + +int ksmbd_extract_shortname(struct ksmbd_conn *conn, + const char *longname, + char *shortname); + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); +int ksmbd_override_fsids(struct ksmbd_work *work); +void ksmbd_revert_fsids(struct ksmbd_work *work); + +unsigned int ksmbd_server_side_copy_max_chunk_count(void); +unsigned int ksmbd_server_side_copy_max_chunk_size(void); +unsigned int ksmbd_server_side_copy_max_total_size(void); +bool is_asterisk(char *p); +__le32 smb_map_generic_desired_access(__le32 daccess); + +static inline unsigned int get_rfc1002_len(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} +#endif /* __SMB_COMMON_H__ */ diff -Naur --no-dereference a/fs/ksmbd/smberr.h b/fs/ksmbd/smberr.h --- a/fs/ksmbd/smberr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smberr.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * See Error Codes section of the SNIA CIFS Specification + * for more information + */ +#ifndef __KSMBD_SMBERR_H +#define __KSMBD_SMBERR_H + +#define SUCCESS 0x00 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ +#define ERRSRV 0x02 /* Error is generated by the file server daemon */ +#define ERRHRD 0x03 /* Error is a hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* The following error codes may be generated with the SUCCESS error class.*/ + +/*#define SUCCESS 0 The request was successful. */ + +/* The following error codes may be generated with the ERRDOS error class.*/ + +#define ERRbadfunc 1 /* + * Invalid function. The server did not + * recognize or could not perform a + * system call generated by the server, + * e.g., set the DIRECTORY attribute on + * a data file, invalid seek mode. + */ +#define ERRbadfile 2 /* + * File not found. The last component + * of a file's pathname could not be + * found. + */ +#define ERRbadpath 3 /* + * Directory invalid. A directory + * component in a pathname could not be + * found. + */ +#define ERRnofids 4 /* + * Too many open files. The server has + * no file handles available. + */ +#define ERRnoaccess 5 /* + * Access denied, the client's context + * does not permit the requested + * function. This includes the + * following conditions: invalid rename + * command, write to Fid open for read + * only, read on Fid open for write + * only, attempt to delete a non-empty + * directory + */ +#define ERRbadfid 6 /* + * Invalid file handle. The file handle + * specified was not recognized by the + * server. + */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* + * Insufficient server memory to + * perform the requested function. + */ +#define ERRbadmem 9 /* Invalid memory block address. */ +#define ERRbadenv 10 /* Invalid environment. */ +#define ERRbadformat 11 /* Invalid format. */ +#define ERRbadaccess 12 /* Invalid open mode. */ +#define ERRbaddata 13 /* + * Invalid data (generated only by + * IOCTL calls within the server). + */ +#define ERRbaddrive 15 /* Invalid drive specified. */ +#define ERRremcd 16 /* + * A Delete Directory request attempted + * to remove the server's current + * directory. + */ +#define ERRdiffdevice 17 /* + * Not same device (e.g., a cross + * volume rename was attempted + */ +#define ERRnofiles 18 /* + * A File Search command can find no + * more files matching the specified + * criteria. + */ +#define ERRwriteprot 19 /* media is write protected */ +#define ERRgeneral 31 +#define ERRbadshare 32 /* + * The sharing mode specified for an + * Open conflicts with existing FIDs on + * the file. + */ +#define ERRlock 33 /* + * A Lock request conflicted with an + * existing lock or specified an + * invalid mode, or an Unlock requested + * attempted to remove a lock held by + * another process. + */ +#define ERRunsup 50 +#define ERRnosuchshare 67 +#define ERRfilexists 80 /* + * The file named in the request + * already exists. + */ +#define ERRinvparm 87 +#define ERRdiskfull 112 +#define ERRinvname 123 +#define ERRinvlevel 124 +#define ERRdirnotempty 145 +#define ERRnotlocked 158 +#define ERRcancelviolation 173 +#define ERRnoatomiclocks 174 +#define ERRalreadyexists 183 +#define ERRbadpipe 230 +#define ERRpipebusy 231 +#define ERRpipeclosing 232 +#define ERRnotconnected 233 +#define ERRmoredata 234 +#define ERReasnotsupported 282 +#define ErrQuota 0x200 /* + * The operation would cause a quota + * limit to be exceeded. + */ +#define ErrNotALink 0x201 /* + * A link operation was performed on a + * pathname that was not a link. + */ + +/* + * Below errors are used internally (do not come over the wire) for passthrough + * from STATUS codes to POSIX only + */ +#define ERRsymlink 0xFFFD +#define ErrTooManyLinks 0xFFFE + +/* Following error codes may be generated with the ERRSRV error class.*/ + +#define ERRerror 1 /* + * Non-specific error code. It is + * returned under the following + * conditions: resource other than disk + * space exhausted (e.g. TIDs), first + * SMB command was not negotiate, + * multiple negotiates attempted, and + * internal server error. + */ +#define ERRbadpw 2 /* + * Bad password - name/password pair in + * a TreeConnect or Session Setup are + * invalid. + */ +#define ERRbadtype 3 /* + * used for indicating DFS referral + * needed + */ +#define ERRaccess 4 /* + * The client does not have the + * necessary access rights within the + * specified context for requested + * function. + */ +#define ERRinvtid 5 /* + * The Tid specified in a command was + * invalid. + */ +#define ERRinvnetname 6 /* + * Invalid network name in tree + * connect. + */ +#define ERRinvdevice 7 /* + * Invalid device - printer request + * made to non-printer connection or + * non-printer request made to printer + * connection. + */ +#define ERRqfull 49 /* + * Print queue full (files) -- returned + * by open print file. + */ +#define ERRqtoobig 50 /* Print queue full -- no space. */ +#define ERRqeof 51 /* EOF on print queue dump */ +#define ERRinvpfid 52 /* Invalid print file FID. */ +#define ERRsmbcmd 64 /* + * The server did not recognize the + * command received. + */ +#define ERRsrverror 65 /* + * The server encountered an internal + * error, e.g., system file + * unavailable. + */ +#define ERRbadBID 66 /* (obsolete) */ +#define ERRfilespecs 67 /* + * The Fid and pathname parameters + * contained an invalid combination of + * values. + */ +#define ERRbadLink 68 /* (obsolete) */ +#define ERRbadpermits 69 /* + * The access permissions specified for + * a file or directory are not a valid + * combination. + */ +#define ERRbadPID 70 +#define ERRsetattrmode 71 /* attribute (mode) is invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* reserved - messaging off */ +#define ERRnoroom 83 /* reserved - no room for message */ +#define ERRrmuns 87 /* reserved - too many remote names */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources available for request */ +#define ERRtoomanyuids 90 /* + * Too many UIDs active on this session + */ +#define ERRbaduid 91 /* + * The UID is not known as a valid user + */ +#define ERRusempx 250 /* temporarily unable to use raw */ +#define ERRusestd 251 /* + * temporarily unable to use either raw + * or mpx + */ +#define ERR_NOTIFY_ENUM_DIR 1024 +#define ERRnoSuchUser 2238 /* user account does not exist */ +#define ERRaccountexpired 2239 +#define ERRbadclient 2240 /* can not logon from this client */ +#define ERRbadLogonTime 2241 /* logon hours do not allow this */ +#define ERRpasswordExpired 2242 +#define ERRnetlogonNotStarted 2455 +#define ERRnosupport 0xFFFF + +#endif /* __KSMBD_SMBERR_H */ diff -Naur --no-dereference a/fs/ksmbd/smbfsctl.h b/fs/ksmbd/smbfsctl.h --- a/fs/ksmbd/smbfsctl.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smbfsctl.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2 client. There is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + */ + +#ifndef __KSMBD_SMBFSCTL_H +#define __KSMBD_SMBFSCTL_H + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +#define FSCTL_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC +#define FSCTL_COPYCHUNK 0x001440F2 +#define FSCTL_COPYCHUNK_WRITE 0x001480F2 + +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 + +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) +#endif /* __KSMBD_SMBFSCTL_H */ diff -Naur --no-dereference a/fs/ksmbd/smbstatus.h b/fs/ksmbd/smbstatus.h --- a/fs/ksmbd/smbstatus.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/smbstatus.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,1822 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smb2status.h + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ + cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ + cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ + cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ + cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ + cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ + cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ + cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ + cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ + cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ + cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ + cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ + cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ + cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ + cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ + cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ + cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ + cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ + cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ + cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ + cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ + cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ + cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ + cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ + cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ + cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ + cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ + cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ + cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ + cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ + cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ + cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ + cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ + cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ + cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ + cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ + cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) + +#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) +#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff -Naur --no-dereference a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c --- a/fs/ksmbd/transport_ipc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/transport_ipc.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,874 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfs_cache.h" +#include "transport_ipc.h" +#include "server.h" +#include "smb_common.h" + +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/user_session.h" +#include "mgmt/tree_connect.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" + +#define IPC_WAIT_TIMEOUT (2 * HZ) + +#define IPC_MSG_HASH_BITS 3 +static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); +static DECLARE_RWSEM(ipc_msg_table_lock); +static DEFINE_MUTEX(startup_lock); + +static DEFINE_IDA(ipc_ida); + +static unsigned int ksmbd_tools_pid; + +static bool ksmbd_ipc_validate_version(struct genl_info *m) +{ + if (m->genlhdr->version != KSMBD_GENL_VERSION) { + pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", + "Daemon and kernel module version mismatch", + m->genlhdr->version, + KSMBD_GENL_VERSION, + "User-space ksmbd should terminate"); + return false; + } + return true; +} + +struct ksmbd_ipc_msg { + unsigned int type; + unsigned int sz; + unsigned char payload[]; +}; + +struct ipc_msg_table_entry { + unsigned int handle; + unsigned int type; + wait_queue_head_t wait; + struct hlist_node ipc_table_hlist; + + void *response; +}; + +static struct delayed_work ipc_timer_work; + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); +static int ksmbd_ipc_heartbeat_request(void); + +static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { + [KSMBD_EVENT_UNSPEC] = { + .len = 0, + }, + [KSMBD_EVENT_HEARTBEAT_REQUEST] = { + .len = sizeof(struct ksmbd_heartbeat), + }, + [KSMBD_EVENT_STARTING_UP] = { + .len = sizeof(struct ksmbd_startup_request), + }, + [KSMBD_EVENT_SHUTTING_DOWN] = { + .len = sizeof(struct ksmbd_shutdown_request), + }, + [KSMBD_EVENT_LOGIN_REQUEST] = { + .len = sizeof(struct ksmbd_login_request), + }, + [KSMBD_EVENT_LOGIN_RESPONSE] = { + .len = sizeof(struct ksmbd_login_response), + }, + [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { + .len = sizeof(struct ksmbd_share_config_request), + }, + [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { + .len = sizeof(struct ksmbd_share_config_response), + }, + [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_connect_request), + }, + [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { + .len = sizeof(struct ksmbd_tree_connect_response), + }, + [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_disconnect_request), + }, + [KSMBD_EVENT_LOGOUT_REQUEST] = { + .len = sizeof(struct ksmbd_logout_request), + }, + [KSMBD_EVENT_RPC_REQUEST] = { + }, + [KSMBD_EVENT_RPC_RESPONSE] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { + }, +}; + +static struct genl_ops ksmbd_genl_ops[] = { + { + .cmd = KSMBD_EVENT_UNSPEC, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_STARTING_UP, + .doit = handle_startup_event, + }, + { + .cmd = KSMBD_EVENT_SHUTTING_DOWN, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGOUT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, + .doit = handle_generic_event, + }, +}; + +static struct genl_family ksmbd_genl_family = { + .name = KSMBD_GENL_NAME, + .version = KSMBD_GENL_VERSION, + .hdrsize = 0, + .maxattr = KSMBD_EVENT_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = ksmbd_genl_ops, + .n_ops = ARRAY_SIZE(ksmbd_genl_ops), +}; + +static void ksmbd_nl_init_fixup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) + ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | + GENL_DONT_VALIDATE_DUMP; + + ksmbd_genl_family.policy = ksmbd_nl_policy; +} + +static int rpc_context_flags(struct ksmbd_session *sess) +{ + if (user_guest(sess->user)) + return KSMBD_RPC_RESTRICTED_CONTEXT; + return 0; +} + +static void ipc_update_last_active(void) +{ + if (server_conf.ipc_timeout) + server_conf.ipc_last_active = jiffies; +} + +static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) +{ + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + + msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); + if (msg) + msg->sz = sz; + return msg; +} + +static void ipc_msg_free(struct ksmbd_ipc_msg *msg) +{ + kvfree(msg); +} + +static void ipc_msg_handle_free(int handle) +{ + if (handle >= 0) + ksmbd_release_id(&ipc_ida, handle); +} + +static int handle_response(int type, void *payload, size_t sz) +{ + unsigned int handle = *(unsigned int *)payload; + struct ipc_msg_table_entry *entry; + int ret = 0; + + ipc_update_last_active(); + down_read(&ipc_msg_table_lock); + hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { + if (handle != entry->handle) + continue; + + entry->response = NULL; + /* + * Response message type value should be equal to + * request message type + 1. + */ + if (entry->type + 1 != type) { + pr_err("Waiting for IPC type %d, got %d. Ignore.\n", + entry->type + 1, type); + } + + entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!entry->response) { + ret = -ENOMEM; + break; + } + + memcpy(entry->response, payload, sz); + wake_up_interruptible(&entry->wait); + ret = 0; + break; + } + up_read(&ipc_msg_table_lock); + + return ret; +} + +static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) +{ + int ret; + + ksmbd_set_fd_limit(req->file_max); + server_conf.flags = req->flags; + server_conf.signing = req->signing; + server_conf.tcp_port = req->tcp_port; + server_conf.ipc_timeout = req->ipc_timeout * HZ; + server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; + server_conf.share_fake_fscaps = req->share_fake_fscaps; + ksmbd_init_domain(req->sub_auth); + + if (req->smb2_max_read) + init_smb2_max_read_size(req->smb2_max_read); + if (req->smb2_max_write) + init_smb2_max_write_size(req->smb2_max_write); + if (req->smb2_max_trans) + init_smb2_max_trans_size(req->smb2_max_trans); + + ret = ksmbd_set_netbios_name(req->netbios_name); + ret |= ksmbd_set_server_string(req->server_string); + ret |= ksmbd_set_work_group(req->work_group); + ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), + req->ifc_list_sz); + if (ret) { + pr_err("Server configuration error: %s %s %s\n", + req->netbios_name, req->server_string, + req->work_group); + return ret; + } + + if (req->min_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->min_prot); + if (ret >= 0) + server_conf.min_protocol = ret; + } + if (req->max_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->max_prot); + if (ret >= 0) + server_conf.max_protocol = ret; + } + + if (server_conf.ipc_timeout) + schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); + return 0; +} + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[KSMBD_EVENT_STARTING_UP]) + return -EINVAL; + + mutex_lock(&startup_lock); + if (!ksmbd_server_configurable()) { + mutex_unlock(&startup_lock); + pr_err("Server reset is in progress, can't start daemon\n"); + return -EINVAL; + } + + if (ksmbd_tools_pid) { + if (ksmbd_ipc_heartbeat_request() == 0) { + ret = -EINVAL; + goto out; + } + + pr_err("Reconnect to a new user space daemon\n"); + } else { + struct ksmbd_startup_request *req; + + req = nla_data(info->attrs[info->genlhdr->cmd]); + ret = ipc_server_config_on_startup(req); + if (ret) + goto out; + server_queue_ctrl_init_work(); + } + + ksmbd_tools_pid = info->snd_portid; + ipc_update_last_active(); + +out: + mutex_unlock(&startup_lock); + return ret; +} + +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) +{ + pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); + return -EINVAL; +} + +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) +{ + void *payload; + int sz; + int type = info->genlhdr->cmd; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (type >= KSMBD_EVENT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[type]) + return -EINVAL; + + payload = nla_data(info->attrs[info->genlhdr->cmd]); + sz = nla_len(info->attrs[info->genlhdr->cmd]); + return handle_response(type, payload, sz); +} + +static int ipc_msg_send(struct ksmbd_ipc_msg *msg) +{ + struct genlmsghdr *nlh; + struct sk_buff *skb; + int ret = -EINVAL; + + if (!ksmbd_tools_pid) + return ret; + + skb = genlmsg_new(msg->sz, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); + if (!nlh) + goto out; + + ret = nla_put(skb, msg->type, msg->sz, msg->payload); + if (ret) { + genlmsg_cancel(skb, nlh); + goto out; + } + + genlmsg_end(skb, nlh); + ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); + if (!ret) + ipc_update_last_active(); + return ret; + +out: + nlmsg_free(skb); + return ret; +} + +static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) +{ + struct ipc_msg_table_entry entry; + int ret; + + if ((int)handle < 0) + return NULL; + + entry.type = msg->type; + entry.response = NULL; + init_waitqueue_head(&entry.wait); + + down_write(&ipc_msg_table_lock); + entry.handle = handle; + hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); + up_write(&ipc_msg_table_lock); + + ret = ipc_msg_send(msg); + if (ret) + goto out; + + ret = wait_event_interruptible_timeout(entry.wait, + entry.response != NULL, + IPC_WAIT_TIMEOUT); +out: + down_write(&ipc_msg_table_lock); + hash_del(&entry.ipc_table_hlist); + up_write(&ipc_msg_table_lock); + return entry.response; +} + +static int ksmbd_ipc_heartbeat_request(void) +{ + struct ksmbd_ipc_msg *msg; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); + if (!msg) + return -EINVAL; + + msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_login_request *req; + struct ksmbd_login_response *resp; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_LOGIN_REQUEST; + req = (struct ksmbd_login_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_spnego_authen_request *req; + struct ksmbd_spnego_authen_response *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + + blob_len + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; + req = (struct ksmbd_spnego_authen_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->spnego_blob_len = blob_len; + memcpy(req->spnego_blob, spnego_blob, blob_len); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_connect_request *req; + struct ksmbd_tree_connect_response *resp; + + if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; + req = (struct ksmbd_tree_connect_request *)msg->payload; + + req->handle = ksmbd_acquire_id(&ipc_ida); + req->account_flags = sess->user->flags; + req->session_id = sess->id; + req->connect_id = tree_conn->id; + strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); + snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); + + if (peer_addr->sa_family == AF_INET6) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_disconnect_request *req; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; + req = (struct ksmbd_tree_disconnect_request *)msg->payload; + req->session_id = session_id; + req->connect_id = connect_id; + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +int ksmbd_ipc_logout_request(const char *account, int flags) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_logout_request *req; + int ret; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return -EINVAL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_LOGOUT_REQUEST; + req = (struct ksmbd_logout_request *)msg->payload; + req->account_flags = flags; + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_share_config_request *req; + struct ksmbd_share_config_response *resp; + + if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; + req = (struct ksmbd_share_config_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_OPEN_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_CLOSE_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_WRITE_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_READ_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_IOCTL_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->flags = rpc_context_flags(sess); + req->flags |= KSMBD_RPC_RAP_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +static int __ipc_heartbeat(void) +{ + unsigned long delta; + + if (!ksmbd_server_running()) + return 0; + + if (time_after(jiffies, server_conf.ipc_last_active)) { + delta = (jiffies - server_conf.ipc_last_active); + } else { + ipc_update_last_active(); + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + if (delta < server_conf.ipc_timeout) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout - delta); + return 0; + } + + if (ksmbd_ipc_heartbeat_request() == 0) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + mutex_lock(&startup_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + server_conf.ipc_last_active = 0; + ksmbd_tools_pid = 0; + pr_err("No IPC daemon response for %lus\n", delta / HZ); + mutex_unlock(&startup_lock); + return -EINVAL; +} + +static void ipc_timer_heartbeat(struct work_struct *w) +{ + if (__ipc_heartbeat()) + server_queue_ctrl_reset_work(); +} + +int ksmbd_ipc_id_alloc(void) +{ + return ksmbd_acquire_id(&ipc_ida); +} + +void ksmbd_rpc_id_free(int handle) +{ + ksmbd_release_id(&ipc_ida, handle); +} + +void ksmbd_ipc_release(void) +{ + cancel_delayed_work_sync(&ipc_timer_work); + genl_unregister_family(&ksmbd_genl_family); +} + +void ksmbd_ipc_soft_reset(void) +{ + mutex_lock(&startup_lock); + ksmbd_tools_pid = 0; + cancel_delayed_work_sync(&ipc_timer_work); + mutex_unlock(&startup_lock); +} + +int ksmbd_ipc_init(void) +{ + int ret = 0; + + ksmbd_nl_init_fixup(); + INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); + + ret = genl_register_family(&ksmbd_genl_family); + if (ret) { + pr_err("Failed to register KSMBD netlink interface %d\n", ret); + cancel_delayed_work_sync(&ipc_timer_work); + } + + return ret; +} diff -Naur --no-dereference a/fs/ksmbd/transport_ipc.h b/fs/ksmbd/transport_ipc.h --- a/fs/ksmbd/transport_ipc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/transport_ipc.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_IPC_H__ +#define __KSMBD_TRANSPORT_IPC_H__ + +#include + +#define KSMBD_IPC_MAX_PAYLOAD 4096 + +struct ksmbd_login_response * +ksmbd_ipc_login_request(const char *account); + +struct ksmbd_session; +struct ksmbd_share_config; +struct ksmbd_tree_connect; +struct sockaddr; + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id); +int ksmbd_ipc_logout_request(const char *account, int flags); +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name); +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); +int ksmbd_ipc_id_alloc(void); +void ksmbd_rpc_id_free(int handle); +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz); +void ksmbd_ipc_release(void); +void ksmbd_ipc_soft_reset(void); +int ksmbd_ipc_init(void); +#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff -Naur --no-dereference a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c --- a/fs/ksmbd/transport_rdma.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/transport_rdma.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,2074 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + * + * Author(s): Long Li , + * Hyunchul Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +#define SUBMOD_NAME "smb_direct" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "transport_rdma.h" + +#define SMB_DIRECT_PORT 5445 + +#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) + +/* SMB_DIRECT negotiation timeout in seconds */ +#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 + +#define SMB_DIRECT_MAX_SEND_SGES 8 +#define SMB_DIRECT_MAX_RECV_SGES 1 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 + +/* Maximum number of retries on data transfer operations */ +#define SMB_DIRECT_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ +#define SMB_DIRECT_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMB_DIRECT transport connection + * as defined in [MS-SMBD] 3.1.1.1 + * Those may change after a SMB_DIRECT negotiation + */ +/* The local peer's maximum number of credits to grant to the peer */ +static int smb_direct_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +static int smb_direct_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +static int smb_direct_max_send_size = 8192; + +/* The maximum fragmented upper-layer payload receive size supported */ +static int smb_direct_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +static int smb_direct_max_receive_size = 8192; + +static int smb_direct_max_read_write_size = 1024 * 1024; + +static int smb_direct_max_outstanding_rw_ops = 8; + +static struct smb_direct_listener { + struct rdma_cm_id *cm_id; +} smb_direct_listener; + +static struct workqueue_struct *smb_direct_wq; + +enum smb_direct_status { + SMB_DIRECT_CS_NEW = 0, + SMB_DIRECT_CS_CONNECTED, + SMB_DIRECT_CS_DISCONNECTING, + SMB_DIRECT_CS_DISCONNECTED, +}; + +struct smb_direct_transport { + struct ksmbd_transport transport; + + enum smb_direct_status status; + bool full_packet_received; + wait_queue_head_t wait_status; + + struct rdma_cm_id *cm_id; + struct ib_cq *send_cq; + struct ib_cq *recv_cq; + struct ib_pd *pd; + struct ib_qp *qp; + + int max_send_size; + int max_recv_size; + int max_fragmented_send_size; + int max_fragmented_recv_size; + int max_rdma_rw_size; + + spinlock_t reassembly_queue_lock; + struct list_head reassembly_queue; + int reassembly_data_length; + int reassembly_queue_length; + int first_entry_offset; + wait_queue_head_t wait_reassembly_queue; + + spinlock_t receive_credit_lock; + int recv_credits; + int count_avail_recvmsg; + int recv_credit_max; + int recv_credit_target; + + spinlock_t recvmsg_queue_lock; + struct list_head recvmsg_queue; + + spinlock_t empty_recvmsg_queue_lock; + struct list_head empty_recvmsg_queue; + + int send_credit_target; + atomic_t send_credits; + spinlock_t lock_new_recv_credits; + int new_recv_credits; + atomic_t rw_avail_ops; + + wait_queue_head_t wait_send_credits; + wait_queue_head_t wait_rw_avail_ops; + + mempool_t *sendmsg_mempool; + struct kmem_cache *sendmsg_cache; + mempool_t *recvmsg_mempool; + struct kmem_cache *recvmsg_cache; + + wait_queue_head_t wait_send_payload_pending; + atomic_t send_payload_pending; + wait_queue_head_t wait_send_pending; + atomic_t send_pending; + + struct delayed_work post_recv_credits_work; + struct work_struct send_immediate_work; + struct work_struct disconnect_work; + + bool negotiation_requested; +}; + +#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) + +enum { + SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, + SMB_DIRECT_MSG_DATA_TRANSFER +}; + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; + +struct smb_direct_send_ctx { + struct list_head msg_list; + int wr_cnt; + bool need_invalidate_rkey; + unsigned int remote_key; +}; + +struct smb_direct_sendmsg { + struct smb_direct_transport *transport; + struct ib_send_wr wr; + struct list_head list; + int num_sge; + struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; + struct ib_cqe cqe; + u8 packet[]; +}; + +struct smb_direct_recvmsg { + struct smb_direct_transport *transport; + struct list_head list; + int type; + struct ib_sge sge; + struct ib_cqe cqe; + bool first_segment; + u8 packet[]; +}; + +struct smb_direct_rdma_rw_msg { + struct smb_direct_transport *t; + struct ib_cqe cqe; + struct completion *completion; + struct rdma_rw_ctx rw_ctx; + struct sg_table sgt; + struct scatterlist sg_list[0]; +}; + +static inline int get_buf_page_count(void *buf, int size) +{ + return (int)(DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - + (uintptr_t)buf / PAGE_SIZE); +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *transport); +static void smb_direct_post_recv_credits(struct work_struct *work); +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length); + +static inline struct smb_direct_transport * +smb_trans_direct_transfort(struct ksmbd_transport *t) +{ + return container_of(t, struct smb_direct_transport, transport); +} + +static inline void +*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) +{ + return (void *)recvmsg->packet; +} + +static inline bool is_receive_credit_post_required(int receive_credits, + int avail_recvmsg_count) +{ + return receive_credits <= (smb_direct_receive_credit_max >> 3) && + avail_recvmsg_count >= (receive_credits >> 2); +} + +static struct +smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->recvmsg_queue_lock); + if (!list_empty(&t->recvmsg_queue)) { + recvmsg = list_first_entry(&t->recvmsg_queue, + struct smb_direct_recvmsg, + list); + list_del(&recvmsg->list); + } + spin_unlock(&t->recvmsg_queue_lock); + return recvmsg; +} + +static void put_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->recvmsg_queue_lock); + list_add(&recvmsg->list, &t->recvmsg_queue); + spin_unlock(&t->recvmsg_queue_lock); +} + +static struct +smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->empty_recvmsg_queue_lock); + if (!list_empty(&t->empty_recvmsg_queue)) { + recvmsg = list_first_entry(&t->empty_recvmsg_queue, + struct smb_direct_recvmsg, list); + list_del(&recvmsg->list); + } + spin_unlock(&t->empty_recvmsg_queue_lock); + return recvmsg; +} + +static void put_empty_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->empty_recvmsg_queue_lock); + list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); + spin_unlock(&t->empty_recvmsg_queue_lock); +} + +static void enqueue_reassembly(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg, + int data_length) +{ + spin_lock(&t->reassembly_queue_lock); + list_add_tail(&recvmsg->list, &t->reassembly_queue); + t->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + t->reassembly_data_length += data_length; + spin_unlock(&t->reassembly_queue_lock); +} + +static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) +{ + if (!list_empty(&t->reassembly_queue)) + return list_first_entry(&t->reassembly_queue, + struct smb_direct_recvmsg, list); + else + return NULL; +} + +static void smb_direct_disconnect_rdma_work(struct work_struct *work) +{ + struct smb_direct_transport *t = + container_of(work, struct smb_direct_transport, + disconnect_work); + + if (t->status == SMB_DIRECT_CS_CONNECTED) { + t->status = SMB_DIRECT_CS_DISCONNECTING; + rdma_disconnect(t->cm_id); + } +} + +static void +smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) +{ + if (t->status == SMB_DIRECT_CS_CONNECTED) + queue_work(smb_direct_wq, &t->disconnect_work); +} + +static void smb_direct_send_immediate_work(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, send_immediate_work); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return; + + smb_direct_post_send_data(t, NULL, NULL, 0, 0); +} + +static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) +{ + struct smb_direct_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + + t->cm_id = cm_id; + cm_id->context = t; + + t->status = SMB_DIRECT_CS_NEW; + init_waitqueue_head(&t->wait_status); + + spin_lock_init(&t->reassembly_queue_lock); + INIT_LIST_HEAD(&t->reassembly_queue); + t->reassembly_data_length = 0; + t->reassembly_queue_length = 0; + init_waitqueue_head(&t->wait_reassembly_queue); + init_waitqueue_head(&t->wait_send_credits); + init_waitqueue_head(&t->wait_rw_avail_ops); + + spin_lock_init(&t->receive_credit_lock); + spin_lock_init(&t->recvmsg_queue_lock); + INIT_LIST_HEAD(&t->recvmsg_queue); + + spin_lock_init(&t->empty_recvmsg_queue_lock); + INIT_LIST_HEAD(&t->empty_recvmsg_queue); + + init_waitqueue_head(&t->wait_send_payload_pending); + atomic_set(&t->send_payload_pending, 0); + init_waitqueue_head(&t->wait_send_pending); + atomic_set(&t->send_pending, 0); + + spin_lock_init(&t->lock_new_recv_credits); + + INIT_DELAYED_WORK(&t->post_recv_credits_work, + smb_direct_post_recv_credits); + INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); + INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); + + conn = ksmbd_conn_alloc(); + if (!conn) + goto err; + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; + return t; +err: + kfree(t); + return NULL; +} + +static void free_transport(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + wake_up_interruptible(&t->wait_send_credits); + + ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); + wait_event(t->wait_send_payload_pending, + atomic_read(&t->send_payload_pending) == 0); + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + + cancel_work_sync(&t->disconnect_work); + cancel_delayed_work_sync(&t->post_recv_credits_work); + cancel_work_sync(&t->send_immediate_work); + + if (t->qp) { + ib_drain_qp(t->qp); + ib_destroy_qp(t->qp); + } + + ksmbd_debug(RDMA, "drain the reassembly queue\n"); + do { + spin_lock(&t->reassembly_queue_lock); + recvmsg = get_first_reassembly(t); + if (recvmsg) { + list_del(&recvmsg->list); + spin_unlock(&t->reassembly_queue_lock); + put_recvmsg(t, recvmsg); + } else { + spin_unlock(&t->reassembly_queue_lock); + } + } while (recvmsg); + t->reassembly_data_length = 0; + + if (t->send_cq) + ib_free_cq(t->send_cq); + if (t->recv_cq) + ib_free_cq(t->recv_cq); + if (t->pd) + ib_dealloc_pd(t->pd); + if (t->cm_id) + rdma_destroy_id(t->cm_id); + + smb_direct_destroy_pools(t); + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t); +} + +static struct smb_direct_sendmsg +*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) +{ + struct smb_direct_sendmsg *msg; + + msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->transport = t; + INIT_LIST_HEAD(&msg->list); + msg->num_sge = 0; + return msg; +} + +static void smb_direct_free_sendmsg(struct smb_direct_transport *t, + struct smb_direct_sendmsg *msg) +{ + int i; + + if (msg->num_sge > 0) { + ib_dma_unmap_single(t->cm_id->device, + msg->sge[0].addr, msg->sge[0].length, + DMA_TO_DEVICE); + for (i = 1; i < msg->num_sge; i++) + ib_dma_unmap_page(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + } + mempool_free(msg, t->sendmsg_mempool); +} + +static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) +{ + switch (recvmsg->type) { + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *req = + (struct smb_direct_data_transfer *)recvmsg->packet; + struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + + le32_to_cpu(req->data_offset) - 4); + ksmbd_debug(RDMA, + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", + le16_to_cpu(req->credits_granted), + le16_to_cpu(req->credits_requested), + req->data_length, req->remaining_data_length, + hdr->ProtocolId, hdr->Command); + break; + } + case SMB_DIRECT_MSG_NEGOTIATE_REQ: { + struct smb_direct_negotiate_req *req = + (struct smb_direct_negotiate_req *)recvmsg->packet; + ksmbd_debug(RDMA, + "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", + le16_to_cpu(req->min_version), + le16_to_cpu(req->max_version), + le16_to_cpu(req->credits_requested), + le32_to_cpu(req->preferred_send_size), + le32_to_cpu(req->max_receive_size), + le32_to_cpu(req->max_fragmented_size)); + if (le16_to_cpu(req->min_version) > 0x0100 || + le16_to_cpu(req->max_version) < 0x0100) + return -EOPNOTSUPP; + if (le16_to_cpu(req->credits_requested) <= 0 || + le32_to_cpu(req->max_receive_size) <= 128 || + le32_to_cpu(req->max_fragmented_size) <= + 128 * 1024) + return -ECONNABORTED; + + break; + } + default: + return -EINVAL; + } + return 0; +} + +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_transport *t; + + recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); + t = recvmsg->transport; + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + if (wc->status != IB_WC_WR_FLUSH_ERR) { + pr_err("Recv error. status='%s (%d)' opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + put_empty_recvmsg(t, recvmsg); + return; + } + + ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + switch (recvmsg->type) { + case SMB_DIRECT_MSG_NEGOTIATE_REQ: + if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { + put_empty_recvmsg(t, recvmsg); + return; + } + t->negotiation_requested = true; + t->full_packet_received = true; + wake_up_interruptible(&t->wait_status); + break; + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *data_transfer = + (struct smb_direct_data_transfer *)recvmsg->packet; + unsigned int data_length; + int avail_recvmsg_count, receive_credits; + + if (wc->byte_len < + offsetof(struct smb_direct_data_transfer, padding)) { + put_empty_recvmsg(t, recvmsg); + return; + } + + data_length = le32_to_cpu(data_transfer->data_length); + if (data_length) { + if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + + (u64)data_length) { + put_empty_recvmsg(t, recvmsg); + return; + } + + if (t->full_packet_received) + recvmsg->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + t->full_packet_received = false; + else + t->full_packet_received = true; + + enqueue_reassembly(t, recvmsg, (int)data_length); + wake_up_interruptible(&t->wait_reassembly_queue); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = t->count_avail_recvmsg; + spin_unlock(&t->receive_credit_lock); + } else { + put_empty_recvmsg(t, recvmsg); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = ++(t->count_avail_recvmsg); + spin_unlock(&t->receive_credit_lock); + } + + t->recv_credit_target = + le16_to_cpu(data_transfer->credits_requested); + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &t->send_credits); + + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) + queue_work(smb_direct_wq, &t->send_immediate_work); + + if (atomic_read(&t->send_credits) > 0) + wake_up_interruptible(&t->wait_send_credits); + + if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) + mod_delayed_work(smb_direct_wq, + &t->post_recv_credits_work, 0); + break; + } + default: + break; + } +} + +static int smb_direct_post_recv(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + struct ib_recv_wr wr; + int ret; + + recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, + recvmsg->packet, t->max_recv_size, + DMA_FROM_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); + if (ret) + return ret; + recvmsg->sge.length = t->max_recv_size; + recvmsg->sge.lkey = t->pd->local_dma_lkey; + recvmsg->cqe.done = recv_done; + + wr.wr_cqe = &recvmsg->cqe; + wr.next = NULL; + wr.sg_list = &recvmsg->sge; + wr.num_sge = 1; + + ret = ib_post_recv(t->qp, &wr, NULL); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + ib_dma_unmap_single(t->cm_id->device, + recvmsg->sge.addr, recvmsg->sge.length, + DMA_FROM_DEVICE); + smb_direct_disconnect_rdma_connection(t); + return ret; + } + return ret; +} + +static int smb_direct_read(struct ksmbd_transport *t, char *buf, + unsigned int size) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + +again: + if (st->status != SMB_DIRECT_CS_CONNECTED) { + pr_err("disconnected\n"); + return -ENOTCONN; + } + + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + if (st->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = st->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = st->first_entry_offset; + while (data_read < size) { + recvmsg = get_first_reassembly(st); + data_transfer = smb_direct_recvmsg_payload(recvmsg); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu(data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (recvmsg->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + recvmsg->first_segment = false; + ksmbd_debug(RDMA, + "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) { + list_del(&recvmsg->list); + } else { + spin_lock_irq(&st->reassembly_queue_lock); + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + } + queue_removed++; + put_recvmsg(st, recvmsg); + offset = 0; + } else { + offset += to_copy; + } + + to_read -= to_copy; + data_read += to_copy; + } + + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_data_length -= data_read; + st->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&st->reassembly_queue_lock); + + spin_lock(&st->receive_credit_lock); + st->count_avail_recvmsg += queue_removed; + if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { + spin_unlock(&st->receive_credit_lock); + mod_delayed_work(smb_direct_wq, + &st->post_recv_credits_work, 0); + } else { + spin_unlock(&st->receive_credit_lock); + } + + st->first_entry_offset = offset; + ksmbd_debug(RDMA, + "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, st->reassembly_data_length, + st->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + ksmbd_debug(RDMA, "wait_event on more data\n"); + rc = wait_event_interruptible(st->wait_reassembly_queue, + st->reassembly_data_length >= size || + st->status != SMB_DIRECT_CS_CONNECTED); + if (rc) + return -EINTR; + + goto again; +} + +static void smb_direct_post_recv_credits(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, post_recv_credits_work.work); + struct smb_direct_recvmsg *recvmsg; + int receive_credits, credits = 0; + int ret; + int use_free = 1; + + spin_lock(&t->receive_credit_lock); + receive_credits = t->recv_credits; + spin_unlock(&t->receive_credit_lock); + + if (receive_credits < t->recv_credit_target) { + while (true) { + if (use_free) + recvmsg = get_free_recvmsg(t); + else + recvmsg = get_empty_recvmsg(t); + if (!recvmsg) { + if (use_free) { + use_free = 0; + continue; + } else { + break; + } + } + + recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; + recvmsg->first_segment = false; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + put_recvmsg(t, recvmsg); + break; + } + credits++; + } + } + + spin_lock(&t->receive_credit_lock); + t->recv_credits += credits; + t->count_avail_recvmsg -= credits; + spin_unlock(&t->receive_credit_lock); + + spin_lock(&t->lock_new_recv_credits); + t->new_recv_credits += credits; + spin_unlock(&t->lock_new_recv_credits); + + if (credits) + queue_work(smb_direct_wq, &t->send_immediate_work); +} + +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_sendmsg *sendmsg, *sibling; + struct smb_direct_transport *t; + struct list_head *pos, *prev, *end; + + sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); + t = sendmsg->transport; + + ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + pr_err("Send error. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + + if (sendmsg->num_sge > 1) { + if (atomic_dec_and_test(&t->send_payload_pending)) + wake_up(&t->wait_send_payload_pending); + } else { + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + } + + /* iterate and free the list of messages in reverse. the list's head + * is invalid. + */ + for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; + prev != end; pos = prev, prev = prev->prev) { + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); + } + + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); +} + +static int manage_credits_prior_sending(struct smb_direct_transport *t) +{ + int new_credits; + + spin_lock(&t->lock_new_recv_credits); + new_credits = t->new_recv_credits; + t->new_recv_credits = 0; + spin_unlock(&t->lock_new_recv_credits); + + return new_credits; +} + +static int smb_direct_post_send(struct smb_direct_transport *t, + struct ib_send_wr *wr) +{ + int ret; + + if (wr->num_sge > 1) + atomic_inc(&t->send_payload_pending); + else + atomic_inc(&t->send_pending); + + ret = ib_post_send(t->qp, wr, NULL); + if (ret) { + pr_err("failed to post send: %d\n", ret); + if (wr->num_sge > 1) { + if (atomic_dec_and_test(&t->send_payload_pending)) + wake_up(&t->wait_send_payload_pending); + } else { + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + } + smb_direct_disconnect_rdma_connection(t); + } + return ret; +} + +static void smb_direct_send_ctx_init(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, + unsigned int remote_key) +{ + INIT_LIST_HEAD(&send_ctx->msg_list); + send_ctx->wr_cnt = 0; + send_ctx->need_invalidate_rkey = need_invalidate_rkey; + send_ctx->remote_key = remote_key; +} + +static int smb_direct_flush_send_list(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool is_last) +{ + struct smb_direct_sendmsg *first, *last; + int ret; + + if (list_empty(&send_ctx->msg_list)) + return 0; + + first = list_first_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + if (is_last && send_ctx->need_invalidate_rkey) { + last->wr.opcode = IB_WR_SEND_WITH_INV; + last->wr.ex.invalidate_rkey = send_ctx->remote_key; + } + + ret = smb_direct_post_send(t, &first->wr); + if (!ret) { + smb_direct_send_ctx_init(t, send_ctx, + send_ctx->need_invalidate_rkey, + send_ctx->remote_key); + } else { + atomic_add(send_ctx->wr_cnt, &t->send_credits); + wake_up(&t->wait_send_credits); + list_for_each_entry_safe(first, last, &send_ctx->msg_list, + list) { + smb_direct_free_sendmsg(t, first); + } + } + return ret; +} + +static int wait_for_credits(struct smb_direct_transport *t, + wait_queue_head_t *waitq, atomic_t *credits) +{ + int ret; + + do { + if (atomic_dec_return(credits) >= 0) + return 0; + + atomic_inc(credits); + ret = wait_event_interruptible(*waitq, + atomic_read(credits) > 0 || + t->status != SMB_DIRECT_CS_CONNECTED); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_credits(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx) +{ + int ret; + + if (send_ctx && + (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { + ret = smb_direct_flush_send_list(t, send_ctx, false); + if (ret) + return ret; + } + + return wait_for_credits(t, &t->wait_send_credits, &t->send_credits); +} + +static int smb_direct_create_header(struct smb_direct_transport *t, + int size, int remaining_data_length, + struct smb_direct_sendmsg **sendmsg_out) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_data_transfer *packet; + int header_length; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return PTR_ERR(sendmsg); + + /* Fill in the packet header */ + packet = (struct smb_direct_data_transfer *)sendmsg->packet; + packet->credits_requested = cpu_to_le16(t->send_credit_target); + packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + + packet->flags = 0; + packet->reserved = 0; + if (!size) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(size); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + ksmbd_debug(RDMA, + "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smb_direct_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!size) + header_length = + offsetof(struct smb_direct_data_transfer, padding); + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = header_length; + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + *sendmsg_out = sendmsg; + return 0; +} + +static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) +{ + bool high = is_vmalloc_addr(buf); + struct page *page; + int offset, len; + int i = 0; + + if (nentries < get_buf_page_count(buf, size)) + return -EINVAL; + + offset = offset_in_page(buf); + buf -= offset; + while (size > 0) { + len = min_t(int, PAGE_SIZE - offset, size); + if (high) + page = vmalloc_to_page(buf); + else + page = kmap_to_page(buf); + + if (!sg_list) + return -EINVAL; + sg_set_page(sg_list, page, len, offset); + sg_list = sg_next(sg_list); + + buf += PAGE_SIZE; + size -= len; + offset = 0; + i++; + } + return i; +} + +static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) +{ + int npages; + + npages = get_sg_list(buf, size, sg_list, nentries); + if (npages <= 0) + return -EINVAL; + return ib_dma_map_sg(device, sg_list, npages, dir); +} + +static int post_sendmsg(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) +{ + int i; + + for (i = 0; i < msg->num_sge; i++) + ib_dma_sync_single_for_device(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + + msg->cqe.done = send_done; + msg->wr.opcode = IB_WR_SEND; + msg->wr.sg_list = &msg->sge[0]; + msg->wr.num_sge = msg->num_sge; + msg->wr.next = NULL; + + if (send_ctx) { + msg->wr.wr_cqe = NULL; + msg->wr.send_flags = 0; + if (!list_empty(&send_ctx->msg_list)) { + struct smb_direct_sendmsg *last; + + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last->wr.next = &msg->wr; + } + list_add_tail(&msg->list, &send_ctx->msg_list); + send_ctx->wr_cnt++; + return 0; + } + + msg->wr.wr_cqe = &msg->cqe; + msg->wr.send_flags = IB_SEND_SIGNALED; + return smb_direct_post_send(t, &msg->wr); +} + +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length) +{ + int i, j, ret; + struct smb_direct_sendmsg *msg; + int data_length; + struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; + + ret = wait_for_send_credits(t, send_ctx); + if (ret) + return ret; + + data_length = 0; + for (i = 0; i < niov; i++) + data_length += iov[i].iov_len; + + ret = smb_direct_create_header(t, data_length, remaining_data_length, + &msg); + if (ret) { + atomic_inc(&t->send_credits); + return ret; + } + + for (i = 0; i < niov; i++) { + struct ib_sge *sge; + int sg_cnt; + + sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); + sg_cnt = get_mapped_sg_list(t->cm_id->device, + iov[i].iov_base, iov[i].iov_len, + sg, SMB_DIRECT_MAX_SEND_SGES - 1, + DMA_TO_DEVICE); + if (sg_cnt <= 0) { + pr_err("failed to map buffer\n"); + ret = -ENOMEM; + goto err; + } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("buffer not fitted into sges\n"); + ret = -E2BIG; + ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, + DMA_TO_DEVICE); + goto err; + } + + for (j = 0; j < sg_cnt; j++) { + sge = &msg->sge[msg->num_sge]; + sge->addr = sg_dma_address(&sg[j]); + sge->length = sg_dma_len(&sg[j]); + sge->lkey = t->pd->local_dma_lkey; + msg->num_sge++; + } + } + + ret = post_sendmsg(t, send_ctx, msg); + if (ret) + goto err; + return 0; +err: + smb_direct_free_sendmsg(t, msg); + atomic_inc(&t->send_credits); + return ret; +} + +static int smb_direct_writev(struct ksmbd_transport *t, + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + int remaining_data_length; + int start, i, j; + int max_iov_size = st->max_send_size - + sizeof(struct smb_direct_data_transfer); + int ret; + struct kvec vec; + struct smb_direct_send_ctx send_ctx; + + if (st->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + //FIXME: skip RFC1002 header.. + buflen -= 4; + iov[0].iov_base += 4; + iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); + start = i = 0; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; + if (buflen > max_iov_size) { + if (i > start) { + remaining_data_length -= + (buflen - iov[i].iov_len); + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + } else { + /* iov[start] is too big, break it */ + int nvec = (buflen + max_iov_size - 1) / + max_iov_size; + + for (j = 0; j < nvec; j++) { + vec.iov_base = + (char *)iov[start].iov_base + + j * max_iov_size; + vec.iov_len = + min_t(int, max_iov_size, + buflen - max_iov_size * j); + remaining_data_length -= vec.iov_len; + ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, + remaining_data_length); + if (ret) + goto done; + } + i++; + if (i == niovs) + break; + } + start = i; + buflen = 0; + } else { + i++; + if (i == niovs) { + /* send out all remaining vecs */ + remaining_data_length -= buflen; + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + break; + } + } + } + +done: + ret = smb_direct_flush_send_list(st, &send_ctx, true); + + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(st->wait_send_payload_pending, + atomic_read(&st->send_payload_pending) == 0); + return ret; +} + +static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, + enum dma_data_direction dir) +{ + struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, + struct smb_direct_rdma_rw_msg, cqe); + struct smb_direct_transport *t = msg->t; + + if (wc->status != IB_WC_SUCCESS) { + pr_err("read/write error. opcode = %d, status = %s(%d)\n", + wc->opcode, ib_wc_status_msg(wc->status), wc->status); + smb_direct_disconnect_rdma_connection(t); + } + + if (atomic_inc_return(&t->rw_avail_ops) > 0) + wake_up(&t->wait_rw_avail_ops); + + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sg_list, msg->sgt.nents, dir); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + + complete(msg->completion); + kfree(msg); +} + +static void read_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_FROM_DEVICE); +} + +static void write_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_TO_DEVICE); +} + +static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, + int buf_len, u32 remote_key, u64 remote_offset, + u32 remote_len, bool is_read) +{ + struct smb_direct_rdma_rw_msg *msg; + int ret; + DECLARE_COMPLETION_ONSTACK(completion); + struct ib_send_wr *first_wr = NULL; + + ret = wait_for_credits(t, &t->wait_rw_avail_ops, &t->rw_avail_ops); + if (ret < 0) + return ret; + + /* TODO: mempool */ + msg = kmalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + + sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + if (!msg) { + atomic_inc(&t->rw_avail_ops); + return -ENOMEM; + } + + msg->sgt.sgl = &msg->sg_list[0]; + ret = sg_alloc_table_chained(&msg->sgt, + get_buf_page_count(buf, buf_len), + msg->sg_list, SG_CHUNK_SIZE); + if (ret) { + atomic_inc(&t->rw_avail_ops); + kfree(msg); + return -ENOMEM; + } + + ret = get_sg_list(buf, buf_len, msg->sgt.sgl, msg->sgt.orig_nents); + if (ret <= 0) { + pr_err("failed to get pages\n"); + goto err; + } + + ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, + msg->sg_list, get_buf_page_count(buf, buf_len), + 0, remote_offset, remote_key, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (ret < 0) { + pr_err("failed to init rdma_rw_ctx: %d\n", ret); + goto err; + } + + msg->t = t; + msg->cqe.done = is_read ? read_done : write_done; + msg->completion = &completion; + first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, + &msg->cqe, NULL); + + ret = ib_post_send(t->qp, first_wr, NULL); + if (ret) { + pr_err("failed to post send wr: %d\n", ret); + goto err; + } + + wait_for_completion(&completion); + return 0; + +err: + atomic_inc(&t->rw_avail_ops); + if (first_wr) + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sg_list, msg->sgt.nents, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + return ret; +} + +static int smb_direct_rdma_write(struct ksmbd_transport *t, void *buf, + unsigned int buflen, u32 remote_key, + u64 remote_offset, u32 remote_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + remote_key, remote_offset, + remote_len, false); +} + +static int smb_direct_rdma_read(struct ksmbd_transport *t, void *buf, + unsigned int buflen, u32 remote_key, + u64 remote_offset, u32 remote_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + remote_key, remote_offset, + remote_len, true); +} + +static void smb_direct_disconnect(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); + wait_event_interruptible(st->wait_status, + st->status == SMB_DIRECT_CS_DISCONNECTED); + free_transport(st); +} + +static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct smb_direct_transport *t = cm_id->context; + + ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + + switch (event->event) { + case RDMA_CM_EVENT_ESTABLISHED: { + t->status = SMB_DIRECT_CS_CONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + wake_up_interruptible(&t->wait_reassembly_queue); + wake_up(&t->wait_send_credits); + break; + } + case RDMA_CM_EVENT_CONNECT_ERROR: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + default: + pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), + event->event); + break; + } + return 0; +} + +static void smb_direct_qpair_handler(struct ib_event *event, void *context) +{ + struct smb_direct_transport *t = context; + + ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", + t->cm_id, ib_event_msg(event->event), event->event); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smb_direct_disconnect_rdma_connection(t); + break; + default: + break; + } +} + +static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, + int failed) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_negotiate_resp *resp; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return -ENOMEM; + + resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; + if (failed) { + memset(resp, 0, sizeof(*resp)); + resp->min_version = cpu_to_le16(0x0100); + resp->max_version = cpu_to_le16(0x0100); + resp->status = STATUS_NOT_SUPPORTED; + } else { + resp->status = STATUS_SUCCESS; + resp->min_version = SMB_DIRECT_VERSION_LE; + resp->max_version = SMB_DIRECT_VERSION_LE; + resp->negotiated_version = SMB_DIRECT_VERSION_LE; + resp->reserved = 0; + resp->credits_requested = + cpu_to_le16(t->send_credit_target); + resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); + resp->preferred_send_size = cpu_to_le32(t->max_send_size); + resp->max_receive_size = cpu_to_le32(t->max_recv_size); + resp->max_fragmented_size = + cpu_to_le32(t->max_fragmented_recv_size); + } + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)resp, sizeof(*resp), + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = sizeof(*resp); + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + ret = post_sendmsg(t, NULL, sendmsg); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + return 0; +} + +static int smb_direct_accept_client(struct smb_direct_transport *t) +{ + struct rdma_conn_param conn_param; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + int ret; + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, + SMB_DIRECT_CM_INITIATOR_DEPTH); + conn_param.responder_resources = 0; + + t->cm_id->device->ops.get_port_immutable(t->cm_id->device, + t->cm_id->port_num, + &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = conn_param.responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + conn_param.retry_count = SMB_DIRECT_CM_RETRY; + conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; + conn_param.flow_control = 0; + + ret = rdma_accept(t->cm_id, &conn_param); + if (ret) { + pr_err("error at rdma_accept: %d\n", ret); + return ret; + } + + wait_event_interruptible(t->wait_status, + t->status != SMB_DIRECT_CS_NEW); + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + return 0; +} + +static int smb_direct_negotiate(struct smb_direct_transport *t) +{ + int ret; + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_negotiate_req *req; + + recvmsg = get_free_recvmsg(t); + if (!recvmsg) + return -ENOMEM; + recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + goto out; + } + + t->negotiation_requested = false; + ret = smb_direct_accept_client(t); + if (ret) { + pr_err("Can't accept client\n"); + goto out; + } + + smb_direct_post_recv_credits(&t->post_recv_credits_work.work); + + ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); + ret = wait_event_interruptible_timeout(t->wait_status, + t->negotiation_requested || + t->status == SMB_DIRECT_CS_DISCONNECTED, + SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); + if (ret <= 0 || t->status == SMB_DIRECT_CS_DISCONNECTED) { + ret = ret < 0 ? ret : -ETIMEDOUT; + goto out; + } + + ret = smb_direct_check_recvmsg(recvmsg); + if (ret == -ECONNABORTED) + goto out; + + req = (struct smb_direct_negotiate_req *)recvmsg->packet; + t->max_recv_size = min_t(int, t->max_recv_size, + le32_to_cpu(req->preferred_send_size)); + t->max_send_size = min_t(int, t->max_send_size, + le32_to_cpu(req->max_receive_size)); + t->max_fragmented_send_size = + le32_to_cpu(req->max_fragmented_size); + + ret = smb_direct_send_negotiate_response(t, ret); +out: + if (recvmsg) + put_recvmsg(t, recvmsg); + return ret; +} + +static int smb_direct_init_params(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + struct ib_device *device = t->cm_id->device; + int max_send_sges, max_pages, max_rw_wrs, max_send_wrs; + + /* need 2 more sge. because a SMB_DIRECT header will be mapped, + * and maybe a send buffer could be not page aligned. + */ + t->max_send_size = smb_direct_max_send_size; + max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 2; + if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("max_send_size %d is too large\n", t->max_send_size); + return -EINVAL; + } + + /* + * allow smb_direct_max_outstanding_rw_ops of in-flight RDMA + * read/writes. HCA guarantees at least max_send_sge of sges for + * a RDMA read/write work request, and if memory registration is used, + * we need reg_mr, local_inv wrs for each read/write. + */ + t->max_rdma_rw_size = smb_direct_max_read_write_size; + max_pages = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; + max_rw_wrs = DIV_ROUND_UP(max_pages, SMB_DIRECT_MAX_SEND_SGES); + max_rw_wrs += rdma_rw_mr_factor(device, t->cm_id->port_num, + max_pages) * 2; + max_rw_wrs *= smb_direct_max_outstanding_rw_ops; + + max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; + if (max_send_wrs > device->attrs.max_cqe || + max_send_wrs > device->attrs.max_qp_wr) { + pr_err("consider lowering send_credit_target = %d, or max_outstanding_rw_ops = %d\n", + smb_direct_send_credit_target, + smb_direct_max_outstanding_rw_ops); + pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (smb_direct_receive_credit_max > device->attrs.max_cqe || + smb_direct_receive_credit_max > device->attrs.max_qp_wr) { + pr_err("consider lowering receive_credit_max = %d\n", + smb_direct_receive_credit_max); + pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (device->attrs.max_send_sge < SMB_DIRECT_MAX_SEND_SGES) { + pr_err("warning: device max_send_sge = %d too small\n", + device->attrs.max_send_sge); + return -EINVAL; + } + if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { + pr_err("warning: device max_recv_sge = %d too small\n", + device->attrs.max_recv_sge); + return -EINVAL; + } + + t->recv_credits = 0; + t->count_avail_recvmsg = 0; + + t->recv_credit_max = smb_direct_receive_credit_max; + t->recv_credit_target = 10; + t->new_recv_credits = 0; + + t->send_credit_target = smb_direct_send_credit_target; + atomic_set(&t->send_credits, 0); + atomic_set(&t->rw_avail_ops, smb_direct_max_outstanding_rw_ops); + + t->max_send_size = smb_direct_max_send_size; + t->max_recv_size = smb_direct_max_receive_size; + t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + + cap->max_send_wr = max_send_wrs; + cap->max_recv_wr = t->recv_credit_max; + cap->max_send_sge = SMB_DIRECT_MAX_SEND_SGES; + cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; + cap->max_inline_data = 0; + cap->max_rdma_ctxs = 0; + return 0; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + while ((recvmsg = get_free_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + while ((recvmsg = get_empty_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + + mempool_destroy(t->recvmsg_mempool); + t->recvmsg_mempool = NULL; + + kmem_cache_destroy(t->recvmsg_cache); + t->recvmsg_cache = NULL; + + mempool_destroy(t->sendmsg_mempool); + t->sendmsg_mempool = NULL; + + kmem_cache_destroy(t->sendmsg_cache); + t->sendmsg_cache = NULL; +} + +static int smb_direct_create_pools(struct smb_direct_transport *t) +{ + char name[80]; + int i; + struct smb_direct_recvmsg *recvmsg; + + snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); + t->sendmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_sendmsg) + + sizeof(struct smb_direct_negotiate_resp), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->sendmsg_cache) + return -ENOMEM; + + t->sendmsg_mempool = mempool_create(t->send_credit_target, + mempool_alloc_slab, mempool_free_slab, + t->sendmsg_cache); + if (!t->sendmsg_mempool) + goto err; + + snprintf(name, sizeof(name), "smb_direct_resp_%p", t); + t->recvmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_recvmsg) + + t->max_recv_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->recvmsg_cache) + goto err; + + t->recvmsg_mempool = + mempool_create(t->recv_credit_max, mempool_alloc_slab, + mempool_free_slab, t->recvmsg_cache); + if (!t->recvmsg_mempool) + goto err; + + INIT_LIST_HEAD(&t->recvmsg_queue); + + for (i = 0; i < t->recv_credit_max; i++) { + recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + if (!recvmsg) + goto err; + recvmsg->transport = t; + list_add(&recvmsg->list, &t->recvmsg_queue); + } + t->count_avail_recvmsg = t->recv_credit_max; + + return 0; +err: + smb_direct_destroy_pools(t); + return -ENOMEM; +} + +static int smb_direct_create_qpair(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + int ret; + struct ib_qp_init_attr qp_attr; + + t->pd = ib_alloc_pd(t->cm_id->device, 0); + if (IS_ERR(t->pd)) { + pr_err("Can't create RDMA PD\n"); + ret = PTR_ERR(t->pd); + t->pd = NULL; + return ret; + } + + t->send_cq = ib_alloc_cq(t->cm_id->device, t, + t->send_credit_target, 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->send_cq)) { + pr_err("Can't create RDMA send CQ\n"); + ret = PTR_ERR(t->send_cq); + t->send_cq = NULL; + goto err; + } + + t->recv_cq = ib_alloc_cq(t->cm_id->device, t, + cap->max_send_wr + cap->max_rdma_ctxs, + 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->recv_cq)) { + pr_err("Can't create RDMA recv CQ\n"); + ret = PTR_ERR(t->recv_cq); + t->recv_cq = NULL; + goto err; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smb_direct_qpair_handler; + qp_attr.qp_context = t; + qp_attr.cap = *cap; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = t->send_cq; + qp_attr.recv_cq = t->recv_cq; + qp_attr.port_num = ~0; + + ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); + if (ret) { + pr_err("Can't create RDMA QP: %d\n", ret); + goto err; + } + + t->qp = t->cm_id->qp; + t->cm_id->event_handler = smb_direct_cm_handler; + + return 0; +err: + if (t->qp) { + ib_destroy_qp(t->qp); + t->qp = NULL; + } + if (t->recv_cq) { + ib_destroy_cq(t->recv_cq); + t->recv_cq = NULL; + } + if (t->send_cq) { + ib_destroy_cq(t->send_cq); + t->send_cq = NULL; + } + if (t->pd) { + ib_dealloc_pd(t->pd); + t->pd = NULL; + } + return ret; +} + +static int smb_direct_prepare(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + int ret; + struct ib_qp_cap qp_cap; + + ret = smb_direct_init_params(st, &qp_cap); + if (ret) { + pr_err("Can't configure RDMA parameters\n"); + return ret; + } + + ret = smb_direct_create_pools(st); + if (ret) { + pr_err("Can't init RDMA pool: %d\n", ret); + return ret; + } + + ret = smb_direct_create_qpair(st, &qp_cap); + if (ret) { + pr_err("Can't accept RDMA client: %d\n", ret); + return ret; + } + + ret = smb_direct_negotiate(st); + if (ret) { + pr_err("Can't negotiate: %d\n", ret); + return ret; + } + + st->status = SMB_DIRECT_CS_CONNECTED; + return 0; +} + +static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) +{ + struct smb_direct_transport *t; + + if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { + ksmbd_debug(RDMA, + "Fast Registration Work Requests is not supported. device capabilities=%llx\n", + new_cm_id->device->attrs.device_cap_flags); + return -EPROTONOSUPPORT; + } + + t = alloc_transport(new_cm_id); + if (!t) + return -ENOMEM; + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:r%u", + SMB_DIRECT_PORT); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + int ret = PTR_ERR(KSMBD_TRANS(t)->handler); + + pr_err("Can't start thread\n"); + free_transport(t); + return ret; + } + + return 0; +} + +static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + int ret = smb_direct_handle_connect_request(cm_id); + + if (ret) { + pr_err("Can't create transport: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", + cm_id); + break; + } + default: + pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + break; + } + return 0; +} + +static int smb_direct_listen(int port) +{ + int ret; + struct rdma_cm_id *cm_id; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, + &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(cm_id)) { + pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); + return PTR_ERR(cm_id); + } + + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); + if (ret) { + pr_err("Can't bind: %d\n", ret); + goto err; + } + + smb_direct_listener.cm_id = cm_id; + + ret = rdma_listen(cm_id, 10); + if (ret) { + pr_err("Can't listen: %d\n", ret); + goto err; + } + return 0; +err: + smb_direct_listener.cm_id = NULL; + rdma_destroy_id(cm_id); + return ret; +} + +int ksmbd_rdma_init(void) +{ + int ret; + + smb_direct_listener.cm_id = NULL; + + /* When a client is running out of send credits, the credits are + * granted by the server's sending a packet using this queue. + * This avoids the situation that a clients cannot send packets + * for lack of credits + */ + smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + if (!smb_direct_wq) + return -ENOMEM; + + ret = smb_direct_listen(SMB_DIRECT_PORT); + if (ret) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + pr_err("Can't listen: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", + smb_direct_listener.cm_id); + return 0; +} + +int ksmbd_rdma_destroy(void) +{ + if (smb_direct_listener.cm_id) + rdma_destroy_id(smb_direct_listener.cm_id); + smb_direct_listener.cm_id = NULL; + + if (smb_direct_wq) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + } + return 0; +} + +bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +{ + struct ib_device *ibdev; + bool rdma_capable = false; + + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); + if (ibdev) { + if (rdma_frwr_is_supported(&ibdev->attrs)) + rdma_capable = true; + ib_device_put(ibdev); + } + return rdma_capable; +} + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { + .prepare = smb_direct_prepare, + .disconnect = smb_direct_disconnect, + .writev = smb_direct_writev, + .read = smb_direct_read, + .rdma_read = smb_direct_rdma_read, + .rdma_write = smb_direct_rdma_write, +}; diff -Naur --no-dereference a/fs/ksmbd/transport_rdma.h b/fs/ksmbd/transport_rdma.h --- a/fs/ksmbd/transport_rdma.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/transport_rdma.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + */ + +#ifndef __KSMBD_TRANSPORT_RDMA_H__ +#define __KSMBD_TRANSPORT_RDMA_H__ + +#define SMB_DIRECT_PORT 5445 + +/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ +struct smb_direct_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ +struct smb_direct_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ +struct smb_direct_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +#ifdef CONFIG_SMB_SERVER_SMBDIRECT +int ksmbd_rdma_init(void); +int ksmbd_rdma_destroy(void); +bool ksmbd_rdma_capable_netdev(struct net_device *netdev); +#else +static inline int ksmbd_rdma_init(void) { return 0; } +static inline int ksmbd_rdma_destroy(void) { return 0; } +static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } +#endif + +#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff -Naur --no-dereference a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c --- a/fs/ksmbd/transport_tcp.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/transport_tcp.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "auth.h" +#include "connection.h" +#include "transport_tcp.h" + +#define IFACE_STATE_DOWN BIT(0) +#define IFACE_STATE_CONFIGURED BIT(1) + +struct interface { + struct task_struct *ksmbd_kthread; + struct socket *ksmbd_socket; + struct list_head entry; + char *name; + struct mutex sock_release_lock; + int state; +}; + +static LIST_HEAD(iface_list); + +static int bind_additional_ifaces; + +struct tcp_transport { + struct ksmbd_transport transport; + struct socket *sock; + struct kvec *iov; + unsigned int nr_iov; +}; + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; + +static void tcp_stop_kthread(struct task_struct *kthread); +static struct interface *alloc_iface(char *ifname); + +#define KSMBD_TRANS(t) (&(t)->transport) +#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ + struct tcp_transport, transport)) + +static inline void ksmbd_tcp_nodelay(struct socket *sock) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + int val = 1; + + kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY, (char *)&val, + sizeof(val)); +#else + tcp_sock_set_nodelay(sock->sk); +#endif +} + +static inline void ksmbd_tcp_reuseaddr(struct socket *sock) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + int val = 1; + + kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, + sizeof(val)); +#else + sock_set_reuseaddr(sock->sk); +#endif +} + +static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + struct __kernel_old_timeval tv = { .tv_sec = secs, .tv_usec = 0 }; + + kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_OLD, (char *)&tv, + sizeof(tv)); +#else + lock_sock(sock->sk); + if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) + sock->sk->sk_rcvtimeo = secs * HZ; + else + sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + release_sock(sock->sk); +#endif +} + +static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + struct __kernel_old_timeval tv = { .tv_sec = secs, .tv_usec = 0 }; + + kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_OLD, (char *)&tv, + sizeof(tv)); +#else + sock_set_sndtimeo(sock->sk, secs); +#endif +} + +static struct tcp_transport *alloc_transport(struct socket *client_sk) +{ + struct tcp_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + t->sock = client_sk; + + conn = ksmbd_conn_alloc(); + if (!conn) { + kfree(t); + return NULL; + } + + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; + return t; +} + +static void free_transport(struct tcp_transport *t) +{ + kernel_sock_shutdown(t->sock, SHUT_RDWR); + sock_release(t->sock); + t->sock = NULL; + + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t->iov); + kfree(t); +} + +/** + * kvec_array_init() - initialize a IO vector segment + * @new: IO vector to be initialized + * @iov: base IO vector + * @nr_segs: number of segments in base iov + * @bytes: total iovec length so far for read + * + * Return: Number of IO segments + */ +static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, + unsigned int nr_segs, size_t bytes) +{ + size_t base = 0; + + while (bytes || !iov->iov_len) { + int copy = min(bytes, iov->iov_len); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + nr_segs--; + base = 0; + } + } + + memcpy(new, iov, sizeof(*iov) * nr_segs); + new->iov_base += base; + new->iov_len -= base; + return nr_segs; +} + +/** + * get_conn_iovec() - get connection iovec for reading from socket + * @t: TCP transport instance + * @nr_segs: number of segments in iov + * + * Return: return existing or newly allocate iovec + */ +static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) +{ + struct kvec *new_iov; + + if (t->iov && nr_segs <= t->nr_iov) + return t->iov; + + /* not big enough -- allocate a new one and release the old */ + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + if (new_iov) { + kfree(t->iov); + t->iov = new_iov; + t->nr_iov = nr_segs; + } + return new_iov; +} + +static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)sa)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + } + return 0; +} + +/** + * ksmbd_tcp_new_connection() - create a new tcp session on mount + * @client_sk: socket associated with new connection + * + * whenever a new connection is requested, create a conn thread + * (session thread) to handle new incoming smb requests from the connection + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_tcp_new_connection(struct socket *client_sk) +{ + struct sockaddr *csin; + int rc = 0; + struct tcp_transport *t; + + t = alloc_transport(client_sk); + if (!t) + return -ENOMEM; + + csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); + + if (kernel_getpeername(client_sk, csin) < 0) { + pr_err("client ip resolution failed\n"); + rc = -EINVAL; + goto out_error; + } + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, + "ksmbd:%u", + ksmbd_tcp_get_port(csin)); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + pr_err("cannot start conn thread\n"); + rc = PTR_ERR(KSMBD_TRANS(t)->handler); + free_transport(t); + } + return rc; + +out_error: + free_transport(t); + return rc; +} + +/** + * ksmbd_kthread_fn() - listen to new SMB connections and callback server + * @p: arguments to forker thread + * + * Return: 0 on success, error number otherwise + */ +static int ksmbd_kthread_fn(void *p) +{ + struct socket *client_sk = NULL; + struct interface *iface = (struct interface *)p; + int ret; + + while (!kthread_should_stop()) { + mutex_lock(&iface->sock_release_lock); + if (!iface->ksmbd_socket) { + mutex_unlock(&iface->sock_release_lock); + break; + } + ret = kernel_accept(iface->ksmbd_socket, &client_sk, + O_NONBLOCK); + mutex_unlock(&iface->sock_release_lock); + if (ret) { + if (ret == -EAGAIN) + /* check for new connections every 100 msecs */ + schedule_timeout_interruptible(HZ / 10); + continue; + } + + ksmbd_debug(CONN, "connect success: accepted new connection\n"); + client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ksmbd_tcp_new_connection(client_sk); + } + + ksmbd_debug(CONN, "releasing socket\n"); + return 0; +} + +/** + * ksmbd_tcp_run_kthread() - start forker thread + * @iface: pointer to struct interface + * + * start forker thread(ksmbd/0) at module init time to listen + * on port 445 for new SMB connection requests. It creates per connection + * server threads(ksmbd/x) + * + * Return: 0 on success or error number + */ +static int ksmbd_tcp_run_kthread(struct interface *iface) +{ + int rc; + struct task_struct *kthread; + + kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", + iface->name); + if (IS_ERR(kthread)) { + rc = PTR_ERR(kthread); + return rc; + } + iface->ksmbd_kthread = kthread; + + return 0; +} + +/** + * ksmbd_tcp_readv() - read data from socket in given iovec + * @t: TCP transport instance + * @iov_orig: base IO vector + * @nr_segs: number of segments in base iov + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, + unsigned int nr_segs, unsigned int to_read) +{ + int length = 0; + int total_read; + unsigned int segs; + struct msghdr ksmbd_msg; + struct kvec *iov; + struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; + + iov = get_conn_iovec(t, nr_segs); + if (!iov) + return -ENOMEM; + + ksmbd_msg.msg_control = NULL; + ksmbd_msg.msg_controllen = 0; + + for (total_read = 0; to_read; total_read += length, to_read -= length) { + try_to_freeze(); + + if (!ksmbd_conn_alive(conn)) { + total_read = -ESHUTDOWN; + break; + } + segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + + length = kernel_recvmsg(t->sock, &ksmbd_msg, + iov, segs, to_read, 0); + + if (length == -EINTR) { + total_read = -ESHUTDOWN; + break; + } else if (conn->status == KSMBD_SESS_NEED_RECONNECT) { + total_read = -EAGAIN; + break; + } else if (length == -ERESTARTSYS || length == -EAGAIN) { + usleep_range(1000, 2000); + length = 0; + continue; + } else if (length <= 0) { + total_read = -EAGAIN; + break; + } + } + return total_read; +} + +/** + * ksmbd_tcp_read() - read data from socket in given buffer + * @t: TCP transport instance + * @buf: buffer to store read data from socket + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, unsigned int to_read) +{ + struct kvec iov; + + iov.iov_base = buf; + iov.iov_len = to_read; + + return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read); +} + +static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, + int nvecs, int size, bool need_invalidate, + unsigned int remote_key) + +{ + struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; + + return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); +} + +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) +{ + free_transport(TCP_TRANS(t)); +} + +static void tcp_destroy_socket(struct socket *ksmbd_socket) +{ + int ret; + + if (!ksmbd_socket) + return; + + /* set zero to timeout */ + ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); + ksmbd_tcp_snd_timeout(ksmbd_socket, 0); + + ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); + if (ret) + pr_err("Failed to shutdown socket: %d\n", ret); + sock_release(ksmbd_socket); +} + +/** + * create_socket - create socket for ksmbd/0 + * + * Return: 0 on success, error number otherwise + */ +static int create_socket(struct interface *iface) +{ + int ret; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct socket *ksmbd_socket; + bool ipv4 = false; + + ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); + if (ret) { + pr_err("Can't create socket for ipv6, try ipv4: %d\n", ret); + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, + &ksmbd_socket); + if (ret) { + pr_err("Can't create socket for ipv4: %d\n", ret); + goto out_error; + } + + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(server_conf.tcp_port); + ipv4 = true; + } else { + sin6.sin6_family = PF_INET6; + sin6.sin6_addr = in6addr_any; + sin6.sin6_port = htons(server_conf.tcp_port); + } + + ksmbd_tcp_nodelay(ksmbd_socket); + ksmbd_tcp_reuseaddr(ksmbd_socket); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + ret = kernel_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, + iface->name, + strlen(iface->name)); +#else + ret = sock_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) + (char __user *)iface->name, +#else + KERNEL_SOCKPTR(iface->name), +#endif + strlen(iface->name)); +#endif + if (ret != -ENODEV && ret < 0) { + pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); + goto out_error; + } + + if (ipv4) + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, + sizeof(sin)); + else + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, + sizeof(sin6)); + if (ret) { + pr_err("Failed to bind socket: %d\n", ret); + goto out_error; + } + + ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); + if (ret) { + pr_err("Port listen() error: %d\n", ret); + goto out_error; + } + + iface->ksmbd_socket = ksmbd_socket; + ret = ksmbd_tcp_run_kthread(iface); + if (ret) { + pr_err("Can't start ksmbd main kthread: %d\n", ret); + goto out_error; + } + iface->state = IFACE_STATE_CONFIGURED; + + return 0; + +out_error: + tcp_destroy_socket(ksmbd_socket); + iface->ksmbd_socket = NULL; + return ret; +} + +static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct interface *iface; + int ret, found = 0; + + switch (event) { + case NETDEV_UP: + if (netdev->priv_flags & IFF_BRIDGE_PORT) + return NOTIFY_OK; + + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name)) { + found = 1; + if (iface->state != IFACE_STATE_DOWN) + break; + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; + break; + } + } + if (!found && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface) + return NOTIFY_OK; + ret = create_socket(iface); + if (ret) + break; + } + break; + case NETDEV_DOWN: + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name) && + iface->state == IFACE_STATE_CONFIGURED) { + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + mutex_lock(&iface->sock_release_lock); + tcp_destroy_socket(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + mutex_unlock(&iface->sock_release_lock); + + iface->state = IFACE_STATE_DOWN; + break; + } + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block ksmbd_netdev_notifier = { + .notifier_call = ksmbd_netdev_event, +}; + +int ksmbd_tcp_init(void) +{ + register_netdevice_notifier(&ksmbd_netdev_notifier); + + return 0; +} + +static void tcp_stop_kthread(struct task_struct *kthread) +{ + int ret; + + if (!kthread) + return; + + ret = kthread_stop(kthread); + if (ret) + pr_err("failed to stop forker thread\n"); +} + +void ksmbd_tcp_destroy(void) +{ + struct interface *iface, *tmp; + + unregister_netdevice_notifier(&ksmbd_netdev_notifier); + + list_for_each_entry_safe(iface, tmp, &iface_list, entry) { + list_del(&iface->entry); + kfree(iface->name); + kfree(iface); + } +} + +static struct interface *alloc_iface(char *ifname) +{ + struct interface *iface; + + if (!ifname) + return NULL; + + iface = kzalloc(sizeof(struct interface), GFP_KERNEL); + if (!iface) { + kfree(ifname); + return NULL; + } + + iface->name = ifname; + iface->state = IFACE_STATE_DOWN; + list_add(&iface->entry, &iface_list); + mutex_init(&iface->sock_release_lock); + return iface; +} + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) +{ + int sz = 0; + + if (!ifc_list_sz) { + struct net_device *netdev; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + if (netdev->priv_flags & IFF_BRIDGE_PORT) + continue; + if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) + return -ENOMEM; + } + rtnl_unlock(); + bind_additional_ifaces = 1; + return 0; + } + + while (ifc_list_sz > 0) { + if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + return -ENOMEM; + + sz = strlen(ifc_list); + if (!sz) + break; + + ifc_list += sz + 1; + ifc_list_sz -= (sz + 1); + } + + bind_additional_ifaces = 0; + + return 0; +} + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { + .read = ksmbd_tcp_read, + .writev = ksmbd_tcp_writev, + .disconnect = ksmbd_tcp_disconnect, +}; diff -Naur --no-dereference a/fs/ksmbd/transport_tcp.h b/fs/ksmbd/transport_tcp.h --- a/fs/ksmbd/transport_tcp.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/transport_tcp.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_TCP_H__ +#define __KSMBD_TRANSPORT_TCP_H__ + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); +int ksmbd_tcp_init(void); +void ksmbd_tcp_destroy(void); + +#endif /* __KSMBD_TRANSPORT_TCP_H__ */ diff -Naur --no-dereference a/fs/ksmbd/unicode.c b/fs/ksmbd/unicode.c --- a/fs/ksmbd/unicode.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/unicode.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ +#include +#include +#include +#include "glob.h" +#include "unicode.h" +#include "uniupr.h" +#include "smb_common.h" + +#ifdef CONFIG_SMB_INSECURE_SERVER +int smb1_utf16_name_length(const __le16 *from, int maxbytes) +{ + int i, len = 0; + int maxwords = maxbytes / 2; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + len += 2; + if (ftmp == 0) + break; + } + + return len; +} +#endif + +/* + * smb_utf16_bytes() - how long will a string be after conversion? + * @from: pointer to input string + * @maxbytes: don't go past this many bytes of input string + * @codepage: destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + * + * Return: string length after conversion + */ +static int smb_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; +} + +/* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied + * @src_char: 2 byte host-endian source character + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + * + * Return: string length after conversion + */ +static int +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + + goto out; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char *ch) +{ + /* check for control chars, wildcards etc. */ + if (!(*ch & 0x80) && + (*ch <= 0x1f || + *ch == '?' || *ch == '"' || *ch == '<' || + *ch == '>' || *ch == '|')) + return 0; + + return 1; +} + +/* + * smb_from_utf16() - convert utf16le string to local charset + * @to: destination buffer + * @from: source buffer + * @tolen: destination buffer size (in bytes) + * @fromlen: source buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + * + * Return: string length after conversion + */ +static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * smb_strtoUTF16() - Convert character string to unicode string + * @to: destination buffer + * @from: source buffer + * @len: destination buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * + * Return: string length after conversion + */ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * smb_strndup_from_utf16() - copy a string from wire format to the local + * codepage + * @src: source string + * @maxlen: don't walk past this many bytes in the source string + * @is_unicode: is this a unicode string? + * @codepage: destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + * + * Return: destination string buffer or error ptr + */ +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage) +{ + int len, ret; + char *dst; + + if (is_unicode) { + len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, + false); + if (ret < 0) { + kfree(dst); + return ERR_PTR(-EINVAL); + } + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + strscpy(dst, src, len); + } + + return dst; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +/* + * smbConvertToUTF16() - convert string from local charset to utf16 + * @target: destination buffer + * @source: source buffer + * @srclen: source buffer size (in bytes) + * @cp: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + * + * Return: char length after conversion + */ +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars) +{ + int i, j, charlen; + char src_char; + __le16 dst_char; + wchar_t tmp; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); + + for (i = 0, j = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + switch (src_char) { + case 0: + put_unaligned(0, &target[j]); + return j; + case ':': + dst_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dst_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dst_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dst_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dst_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dst_char = cpu_to_le16(UNI_PIPE); + break; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + default: + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen < 1) { + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + } + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + + return j; +} diff -Naur --no-dereference a/fs/ksmbd/unicode.h b/fs/ksmbd/unicode.h --- a/fs/ksmbd/unicode.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/unicode.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + * + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK ((__u16)('*' + 0xF000)) +#define UNI_QUESTION ((__u16)('?' + 0xF000)) +#define UNI_COLON ((__u16)(':' + 0xF000)) +#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) +#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) +#define UNI_PIPE ((__u16)('|' + 0xF000)) +#define UNI_SLASH ((__u16)('\\' + 0xF000)) + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char SmbUniUpperTable[512]; +extern const struct UniCaseRange SmbUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +#ifdef CONFIG_SMB_INSECURE_SERVER +int smb1_utf16_name_length(const __le16 *from, int maxbytes); +#endif +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage); +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars); +char *ksmbd_extract_sharename(char *treename); +#endif + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ +static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) + /*NULL*/; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) + /*NULL*/; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ +static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *)ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ +static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) + /*NULL*/; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ +static inline size_t UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ +static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ +static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) + /*NULL*/; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return anchor; +} + +/* + * UniStrncmp: Compare length limited string + */ +static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ +static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *)anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *)anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(SmbUniUpperTable)) { + /* Latin characters */ + return uc + SmbUniUpperTable[uc]; /* Use base tables */ + } + + rp = SmbUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ +static inline __le16 *UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ +static inline wchar_t UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } + + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ +static inline wchar_t *UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff -Naur --no-dereference a/fs/ksmbd/uniupr.h b/fs/ksmbd/uniupr.h --- a/fs/ksmbd/uniupr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/uniupr.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/uniupr.h + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges + * + */ +#ifndef __KSMBD_UNIUPR_H +#define __KSMBD_UNIUPR_H + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char SmbUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, + -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, + 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange SmbUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, + 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, + 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, + 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, + 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, + 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif + +#endif /* __KSMBD_UNIUPR_H */ diff -Naur --no-dereference a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c --- a/fs/ksmbd/vfs.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/vfs.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,2520 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "smbacl.h" +#include "ndr.h" +#include "auth.h" +#include "misc.h" + +#include "smb_common.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + const char *, unsigned int, struct path *); +#endif + +static char *extract_last_component(char *path) +{ + char *p = strrchr(path, '/'); + + if (p && p[1] != '\0') { + *p = '\0'; + p++; + } else { + p = NULL; + } + return p; +} + +static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_INHERIT_OWNER)) + return; + + i_uid_write(inode, i_uid_read(parent_inode)); +} + +/** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable + * + * the parent dentry got by dget_parent or @parent could be + * unstable, we try to lock a parent inode and lookup the + * child dentry again. + * + * the reference count of @parent isn't incremented. + */ +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child) +{ + struct dentry *dentry; + int ret = 0; + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + dentry = lookup_one(user_ns, child->d_name.name, parent, + child->d_name.len); +#else + dentry = lookup_one_len(child->d_name.name, parent, + child->d_name.len); +#endif + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out_err; + } + + if (dentry != child) { + ret = -ESTALE; + dput(dentry); + goto out_err; + } + + dput(dentry); + return 0; +out_err: + inode_unlock(d_inode(parent)); + return ret; +} + +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, + struct dentry *dentry) +{ + struct dentry *parent; + int ret; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + ret = inode_permission(user_ns, d_inode(parent), + MAY_EXEC | MAY_WRITE); +#else + ret = inode_permission(d_inode(parent), MAY_EXEC | MAY_WRITE); +#endif + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) +{ + struct dentry *parent; + int ret = 0; + + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) +#else + if (!inode_permission(d_inode(dentry), MAY_OPEN | MAY_WRITE)) +#endif + *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | + FILE_WRITE_DATA | FILE_APPEND_DATA | + FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | + FILE_DELETE_CHILD); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) +#else + if (!inode_permission(d_inode(dentry), MAY_OPEN | MAY_READ)) +#endif + *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) +#else + if (!inode_permission(d_inode(dentry), MAY_OPEN | MAY_EXEC)) +#endif + *daccess |= FILE_EXECUTE_LE; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) +#else + if (!inode_permission(d_inode(parent), MAY_EXEC | MAY_WRITE)) +#endif + *daccess |= FILE_DELETE_LE; + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +/** + * ksmbd_vfs_create() - vfs helper for smb create file + * @work: work + * @name: file name that is relative to share + * @mode: file create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS, &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -ENOENT) + pr_err("path create failed for %s, err %d\n", + name, err); + return err; + } + + mode |= S_IFREG; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), dentry, mode, true); +#else + err = vfs_create(d_inode(path.dentry), dentry, mode, true); +#endif + if (!err) { + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(dentry)); + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } + done_path_create(&path, dentry); + return err; +} + +/** + * ksmbd_vfs_mkdir() - vfs helper for smb create directory + * @work: work + * @name: directory name that is relative to share + * @mode: directory create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -EEXIST) + ksmbd_debug(VFS, "path create failed for %s, err %d\n", + name, err); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); +#else + err = vfs_mkdir(d_inode(path.dentry), dentry, mode); +#endif + if (err) { + goto out; + } else if (d_unhashed(dentry)) { + struct dentry *d; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); +#else + d = lookup_one_len(dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); +#endif + if (IS_ERR(d)) { + err = PTR_ERR(d); + goto out; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; + goto out; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } +out: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); + return err; +} + +static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len, char **attr_value) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_getxattr(user_ns, + dentry, + name, + attr_value); + if (value_len < 0) + pr_err("failed to get xattr in file\n"); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + ssize_t v_len; + char *stream_buf = NULL; + + ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", + *pos, count); + + v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len <= 0) + return (int)v_len; + + if (v_len <= *pos) { + count = -EINVAL; + goto free_buf; + } + + if (v_len - *pos < count) + count = v_len - *pos; + + memcpy(buf, &stream_buf[*pos], count); + +free_buf: + kvfree(stream_buf); + return count; +} + +/** + * check_lock_range() - vfs helper for smb byte range file locking + * @filp: the file to apply the lock to + * @start: lock start byte offset + * @end: lock end byte offset + * @type: byte range type read/write + * + * Return: 0 on success, otherwise error + */ +static int check_lock_range(struct file *filp, loff_t start, loff_t end, + unsigned char type) +{ + struct file_lock *flock; + struct file_lock_context *ctx = file_inode(filp)->i_flctx; + int error = 0; + + if (!ctx || list_empty_careful(&ctx->flc_posix)) + return 0; + + spin_lock(&ctx->flc_lock); + list_for_each_entry(flock, &ctx->flc_posix, fl_list) { + /* check conflict locks */ + if (flock->fl_end >= start && end >= flock->fl_start) { + if (flock->fl_type == F_RDLCK) { + if (type == WRITE) { + pr_err("not allow write by shared lock\n"); + error = 1; + goto out; + } + } else if (flock->fl_type == F_WRLCK) { + /* check owner in lock */ + if (flock->fl_file != filp) { + error = 1; + pr_err("not allow rw access by exclusive lock from other opens\n"); + goto out; + } + } + } + } +out: + spin_unlock(&ctx->flc_lock); + return error; +} + +/** + * ksmbd_vfs_read() - vfs helper for smb file read + * @work: smb work + * @fid: file id of open file + * @count: read byte count + * @pos: file pos + * + * Return: number of read bytes on success, otherwise error + */ +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, + loff_t *pos) +{ + struct file *filp = fp->filp; + ssize_t nbytes = 0; + char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); + + if (S_ISDIR(inode->i_mode)) + return -EISDIR; + + if (unlikely(count == 0)) + return 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pd)\n", + fp->filp->f_path.dentry); + return -EACCES; + } + } + + if (ksmbd_stream_fd(fp)) + return ksmbd_vfs_stream_read(fp, rbuf, pos, count); + + if (!work->tcon->posix_extensions) { + int ret; + + ret = check_lock_range(filp, *pos, *pos + count - 1, READ); + if (ret) { + pr_err("unable to read due to lock\n"); + return -EAGAIN; + } + } + + nbytes = kernel_read(filp, rbuf, count, pos); + if (nbytes < 0) { + pr_err("smb read failed for (%s), err = %zd\n", + fp->filename, nbytes); + return nbytes; + } + + filp->f_pos = *pos; + return nbytes; +} + +static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + size_t size, v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", + *pos, count); + + size = *pos + count; + if (size > XATTR_SIZE_MAX) { + size = XATTR_SIZE_MAX; + count = (*pos + count) - XATTR_SIZE_MAX; + } + + v_len = ksmbd_vfs_getcasexattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len < 0) { + pr_err("not found stream in xattr : %zd\n", v_len); + err = (int)v_len; + goto out; + } + + if (v_len < size) { + wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!wbuf) { + err = -ENOMEM; + goto out; + } + + if (v_len > 0) + memcpy(wbuf, stream_buf, v_len); + kvfree(stream_buf); + stream_buf = wbuf; + } + + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + (void *)stream_buf, + size, + 0); + if (err < 0) + goto out; + + fp->filp->f_pos = *pos; + err = 0; +out: + kvfree(stream_buf); + return err; +} + +/** + * ksmbd_vfs_write() - vfs helper for smb file write + * @work: work + * @fid: file id of open file + * @buf: buf containing data for writing + * @count: read byte count + * @pos: file pos + * @sync: fsync after write + * @written: number of bytes written + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written) +{ + struct ksmbd_session *sess = work->sess; + struct file *filp; + loff_t offset = *pos; + int err = 0; + + if (sess->conn->connection_type) { + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + pr_err("no right to write(%pd)\n", + fp->filp->f_path.dentry); + err = -EACCES; + goto out; + } + } + + filp = fp->filp; + + if (ksmbd_stream_fd(fp)) { + err = ksmbd_vfs_stream_write(fp, buf, pos, count); + if (!err) + *written = count; + goto out; + } + + if (!work->tcon->posix_extensions) { + err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); + if (err) { + pr_err("unable to write due to lock\n"); + err = -EAGAIN; + goto out; + } + } + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + err = kernel_write(filp, buf, count, pos); + if (err < 0) { + ksmbd_debug(VFS, "smb write failed, err = %d\n", err); + goto out; + } + + filp->f_pos = *pos; + *written = err; + err = 0; + if (sync) { + err = vfs_fsync_range(filp, offset, offset + *written, 0); + if (err < 0) + pr_err("fsync failed for filename = %pd, err = %d\n", + fp->filp->f_path.dentry, err); + } + +out: + return err; +} + +/** + * ksmbd_vfs_getattr() - vfs helper for smb getattr + * @work: work + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_getattr(struct path *path, struct kstat *stat) +{ + int err; + + err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + if (err) + pr_err("getattr failed, err %d\n", err); + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * smb_check_attrs() - sanitize inode attributes + * @inode: inode + * @attrs: inode attributes + */ +static void smb_check_attrs(struct inode *inode, struct iattr *attrs) +{ + /* sanitize the mode change */ + if (attrs->ia_valid & ATTR_MODE) { + attrs->ia_mode &= S_IALLUGO; + attrs->ia_mode |= (inode->i_mode & ~S_IALLUGO); + } + + /* Revoke setuid/setgid on chown */ + if (!S_ISDIR(inode->i_mode) && + (((attrs->ia_valid & ATTR_UID) && + !uid_eq(attrs->ia_uid, inode->i_uid)) || + ((attrs->ia_valid & ATTR_GID) && + !gid_eq(attrs->ia_gid, inode->i_gid)))) { + attrs->ia_valid |= ATTR_KILL_PRIV; + if (attrs->ia_valid & ATTR_MODE) { + /* we're setting mode too, just clear the s*id bits */ + attrs->ia_mode &= ~S_ISUID; + if (attrs->ia_mode & 0010) + attrs->ia_mode &= ~S_ISGID; + } else { + /* set ATTR_KILL_* bits and let VFS handle it */ + attrs->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID); + } + } +} + +/** + * ksmbd_vfs_setattr() - vfs helper for smb setattr + * @work: work + * @name: file name + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setattr(struct ksmbd_work *work, const char *name, u64 fid, + struct iattr *attrs) +{ + struct file *filp; + struct dentry *dentry; + struct inode *inode; + struct path path; + bool update_size = false; + int err = 0; + struct ksmbd_file *fp = NULL; + struct user_namespace *user_ns; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + if (name) { + err = kern_path(name, 0, &path); + if (err) { + ksmbd_revert_fsids(work); + ksmbd_debug(VFS, "lookup failed for %s, err = %d\n", + name, err); + return -ENOENT; + } + dentry = path.dentry; + inode = d_inode(dentry); + user_ns = mnt_user_ns(path.mnt); + } else { + fp = ksmbd_lookup_fd_fast(work, fid); + if (!fp) { + ksmbd_revert_fsids(work); + pr_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + + filp = fp->filp; + dentry = filp->f_path.dentry; + inode = d_inode(dentry); + user_ns = file_mnt_user_ns(filp); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(user_ns, d_inode(dentry), MAY_WRITE); +#else + err = inode_permission(d_inode(dentry), MAY_WRITE); +#endif + if (err) + goto out; + + /* no need to update mode of symlink */ + if (S_ISLNK(inode->i_mode)) + attrs->ia_valid &= ~ATTR_MODE; + + /* skip setattr, if nothing to update */ + if (!attrs->ia_valid) { + err = 0; + goto out; + } + + smb_check_attrs(inode, attrs); + if (attrs->ia_valid & ATTR_SIZE) { + err = get_write_access(inode); + if (err) + goto out; + update_size = true; + } + + attrs->ia_valid |= ATTR_CTIME; + + inode_lock(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = notify_change(user_ns, dentry, attrs, NULL); +#else + err = notify_change(dentry, attrs, NULL); +#endif + inode_unlock(inode); + + if (update_size) + put_write_access(inode); + + if (!err) { + sync_inode_metadata(inode, 1); + ksmbd_debug(VFS, "fid %llu, setattr done\n", fid); + } + +out: + if (name) + path_put(&path); + ksmbd_fd_put(work, fp); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_symlink() - vfs helper for creating smb symlink + * @name: source file name + * @symname: symlink name + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_symlink(struct ksmbd_work *work, const char *name, + const char *symname) +{ + struct path path; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + dentry = kern_path_create(AT_FDCWD, symname, &path, 0); + if (IS_ERR(dentry)) { + ksmbd_revert_fsids(work); + err = PTR_ERR(dentry); + pr_err("path create failed for %s, err %d\n", name, err); + return err; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_symlink(mnt_user_ns(path.mnt), d_inode(dentry->d_parent), dentry, name); +#else + err = vfs_symlink(d_inode(dentry->d_parent), dentry, name); +#endif + if (err && (err != -EEXIST || err != -ENOSPC)) + ksmbd_debug(VFS, "failed to create symlink, err %d\n", err); + + done_path_create(&path, dentry); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_readlink() - vfs helper for reading value of symlink + * @path: path of symlink + * @buf: destination buffer for symlink value + * @lenp: destination buffer length + * + * Return: symlink value length on success, otherwise error + */ +int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp) +{ + struct inode *inode; + int err; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + const char *link; + DEFINE_DELAYED_CALL(done); + int len; +#else + mm_segment_t old_fs; +#endif + + if (!path) + return -ENOENT; + + inode = d_inode(path->dentry); + if (!S_ISLNK(inode->i_mode)) + return -EINVAL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + link = vfs_get_link(path->dentry, &done); + if (IS_ERR(link)) { + err = PTR_ERR(link); + pr_err("readlink failed, err = %d\n", err); + return err; + } + + len = strlen(link); + if (len > lenp) + len = lenp; + + memcpy(buf, link, len); + do_delayed_call(&done); + + return 0; +#else + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = inode->i_op->readlink(path->dentry, (char __user *)buf, lenp); + set_fs(old_fs); + if (err < 0) + pr_err("readlink failed, err = %d\n", err); + + return err; +#endif +} + +int ksmbd_vfs_readdir_name(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct ksmbd_kstat *ksmbd_kstat, + const char *de_name, int de_name_len, + const char *dir_path) +{ + struct path path; + int rc, file_pathlen, dir_pathlen; + char *name; + + dir_pathlen = strlen(dir_path); + /* 1 for '/'*/ + file_pathlen = dir_pathlen + de_name_len + 1; + name = kmalloc(file_pathlen + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + memcpy(name, dir_path, dir_pathlen); + memset(name + dir_pathlen, '/', 1); + memcpy(name + dir_pathlen + 1, de_name, de_name_len); + name[file_pathlen] = '\0'; + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + pr_err("lookup failed: %s [%d]\n", name, rc); + kfree(name); + return -ENOMEM; + } + + ksmbd_vfs_fill_dentry_attrs(work, user_ns, path.dentry, ksmbd_kstat); + path_put(&path); + kfree(name); + return 0; +} +#endif + +/** + * ksmbd_vfs_fsync() - vfs helper for smb fsync + * @work: work + * @fid: file id of open file + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) +{ + struct ksmbd_file *fp; + int err; + + fp = ksmbd_lookup_fd_slow(work, fid, p_id); + if (!fp) { + pr_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + err = vfs_fsync(fp->filp, 0); + if (err < 0) + pr_err("smb fsync failed, err = %d\n", err); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink + * @name: directory or file name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *parent; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); + if (err) { + ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); + ksmbd_revert_fsids(work); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + parent = dget_parent(path.dentry); + err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); + if (err) { + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; + } + + if (!d_inode(path.dentry)->i_nlink) { + err = -ENOENT; + goto out_err; + } + + if (S_ISDIR(d_inode(path.dentry)->i_mode)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); +#else + err = vfs_rmdir(d_inode(parent), path.dentry); +#endif + if (err && err != -ENOTEMPTY) + ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, + err); + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); +#else + err = vfs_unlink(d_inode(parent), path.dentry, NULL); +#endif + if (err) + ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, + err); + } + +out_err: + inode_unlock(d_inode(parent)); + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_link() - vfs helper for creating smb hardlink + * @oldname: source file name + * @newname: hardlink name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + const char *newname) +{ + struct path oldpath, newpath; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); + if (err) { + pr_err("cannot get linux path for %s, err = %d\n", + oldname, err); + goto out1; + } + + dentry = ksmbd_vfs_kern_path_create(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, + &newpath); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + pr_err("path create err for %s, err %d\n", newname, err); + goto out2; + } + + err = -EXDEV; + if (oldpath.mnt != newpath.mnt) { + pr_err("vfs_link failed err %d\n", err); + goto out3; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), + dentry, NULL); +#else + err = vfs_link(oldpath.dentry, d_inode(newpath.dentry), dentry, NULL); +#endif + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); + +out3: + done_path_create(&newpath, dentry); +out2: + path_put(&oldpath); +out1: + ksmbd_revert_fsids(work); + return err; +} + +static int ksmbd_validate_entry_in_use(struct dentry *src_dent) +{ + struct dentry *dst_dent; + + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; + + if (d_really_is_negative(dst_dent)) + continue; + + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } + } + spin_unlock(&src_dent->d_lock); + + return 0; +} + +static int __ksmbd_vfs_rename(struct ksmbd_work *work, + struct user_namespace *src_user_ns, + struct dentry *src_dent_parent, + struct dentry *src_dent, + struct user_namespace *dst_user_ns, + struct dentry *dst_dent_parent, + struct dentry *trap_dent, + char *dst_name) +{ + struct dentry *dst_dent; + int err; + + if (!work->tcon->posix_extensions) { + err = ksmbd_validate_entry_in_use(src_dent); + if (err) + return err; + } + + if (d_really_is_negative(src_dent_parent)) + return -ENOENT; + if (d_really_is_negative(dst_dent_parent)) + return -ENOENT; + if (d_really_is_negative(src_dent)) + return -ENOENT; + if (src_dent == trap_dent) + return -EINVAL; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, + strlen(dst_name)); +#else + dst_dent = lookup_one_len(dst_name, dst_dent_parent, + strlen(dst_name)); +#endif + err = PTR_ERR(dst_dent); + if (IS_ERR(dst_dent)) { + pr_err("lookup failed %s [%d]\n", dst_name, err); + goto out; + } + + err = -ENOTEMPTY; + if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + struct renamedata rd = { + .old_mnt_userns = src_user_ns, + .old_dir = d_inode(src_dent_parent), + .old_dentry = src_dent, + .new_mnt_userns = dst_user_ns, + .new_dir = d_inode(dst_dent_parent), + .new_dentry = dst_dent, + }; + err = vfs_rename(&rd); +#else + err = vfs_rename(d_inode(src_dent_parent), + src_dent, + d_inode(dst_dent_parent), + dst_dent, + NULL, + 0); +#endif + } + if (err) + pr_err("vfs_rename failed err %d\n", err); + if (dst_dent) + dput(dst_dent); +out: + ksmbd_revert_fsids(work); + return err; +} + +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname) +{ + struct user_namespace *user_ns; + struct path dst_path; + struct dentry *src_dent_parent, *dst_dent_parent; + struct dentry *src_dent, *trap_dent, *src_child; + char *dst_name; + int err; + + dst_name = extract_last_component(newname); + if (!dst_name) { + dst_name = newname; + newname = ""; + } + + src_dent_parent = dget_parent(fp->filp->f_path.dentry); + src_dent = fp->filp->f_path.dentry; + + err = ksmbd_vfs_kern_path(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &dst_path, false); + if (err) { + ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); + goto out; + } + dst_dent_parent = dst_path.dentry; + + trap_dent = lock_rename(src_dent_parent, dst_dent_parent); + dget(src_dent); + dget(dst_dent_parent); + user_ns = file_mnt_user_ns(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); +#else + src_child = lookup_one_len(src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); +#endif + if (IS_ERR(src_child)) { + err = PTR_ERR(src_child); + goto out_lock; + } + + if (src_child != src_dent) { + err = -ESTALE; + dput(src_child); + goto out_lock; + } + dput(src_child); + + err = __ksmbd_vfs_rename(work, + user_ns, + src_dent_parent, + src_dent, + mnt_user_ns(dst_path.mnt), + dst_dent_parent, + trap_dent, + dst_name); +out_lock: + dput(src_dent); + dput(dst_dent_parent); + unlock_rename(src_dent_parent, dst_dent_parent); + path_put(&dst_path); +out: + dput(src_dent_parent); + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_vfs_rename_slowpath(struct ksmbd_work *work, char *oldname, char *newname) +{ + struct path dst_path, src_path; + struct dentry *src_dent_parent, *dst_dent_parent; + struct dentry *src_dent = NULL, *trap_dent; + char *src_name, *dst_name; + int err; + + src_name = extract_last_component(oldname); + if (!src_name) + return -EINVAL; + dst_name = extract_last_component(newname); + if (!dst_name) + return -EINVAL; + + err = kern_path(oldname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &src_path); + if (err) { + pr_err("Cannot get path for %s [%d]\n", oldname, err); + return err; + } + src_dent_parent = src_path.dentry; + dget(src_dent_parent); + + err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path); + if (err) { + pr_err("Cannot get path for %s [%d]\n", newname, err); + dput(src_dent_parent); + path_put(&src_path); + return err; + } + + dst_dent_parent = dst_path.dentry; + dget(dst_dent_parent); + + trap_dent = lock_rename(src_dent_parent, dst_dent_parent); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + src_dent = lookup_one(mnt_user_ns(src_path.mnt), src_name, + src_dent_parent, strlen(src_name)); +#else + src_dent = lookup_one_len(src_name, src_dent_parent, strlen(src_name)); +#endif + err = PTR_ERR(src_dent); + if (IS_ERR(src_dent)) { + src_dent = NULL; + pr_err("%s lookup failed with error = %d\n", src_name, err); + goto out; + } + + err = __ksmbd_vfs_rename(work, + mnt_user_ns(src_path.mnt), + src_dent_parent, + src_dent, + mnt_user_ns(dst_path.mnt), + dst_dent_parent, + trap_dent, + dst_name); +out: + if (src_dent) + dput(src_dent); + dput(dst_dent_parent); + dput(src_dent_parent); + unlock_rename(src_dent_parent, dst_dent_parent); + path_put(&src_path); + path_put(&dst_path); + return err; +} +#else +int ksmbd_vfs_rename_slowpath(struct ksmbd_work *work, char *oldname, + char *newname) +{ + return 0; +} +#endif + +/** + * ksmbd_vfs_truncate() - vfs helper for smb file truncate + * @work: work + * @name: old filename + * @fid: file id of old file + * @size: truncate to given size + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size) +{ + int err = 0; + struct file *filp; + + filp = fp->filp; + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + if (!work->tcon->posix_extensions) { + struct inode *inode = file_inode(filp); + + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); + } + + if (err) { + pr_err("failed due to lock\n"); + return -EAGAIN; + } + } + + err = vfs_truncate(&filp->f_path, size); + if (err) + pr_err("truncate failed for filename : %s err %d\n", + fp->filename, err); + return err; +} + +/** + * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes + * @dentry: dentry of file for listing xattrs + * @list: destination buffer + * @size: destination buffer length + * + * Return: xattr list length on success, otherwise error + */ +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) +{ + ssize_t size; + char *vlist = NULL; + + size = vfs_listxattr(dentry, NULL, 0); + if (size <= 0) + return size; + + vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!vlist) + return -ENOMEM; + + *list = vlist; + size = vfs_listxattr(dentry, vlist, size); + if (size < 0) { + ksmbd_debug(VFS, "listxattr failed\n"); + kvfree(vlist); + *list = NULL; + } + + return size; +} + +static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *xattr_name) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); +#else + return vfs_getxattr(dentry, xattr_name, NULL, 0); +#endif +} + +/** + * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value ++ @user_ns: user namespace + * @dentry: dentry of file for getting xattrs + * @xattr_name: name of xattr name to query + * @xattr_buf: destination buffer xattr value + * + * Return: read xattr value length on success, otherwise error + */ +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, char **xattr_buf) +{ + ssize_t xattr_len; + char *buf; + + *xattr_buf = NULL; + xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); + if (xattr_len < 0) + return xattr_len; + + buf = kmalloc(xattr_len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, + (void *)buf, xattr_len); +#else + xattr_len = vfs_getxattr(dentry, xattr_name, (void *)buf, xattr_len); +#endif + if (xattr_len > 0) + *xattr_buf = buf; + else + kfree(buf); + return xattr_len; +} + +/** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace + * @dentry: dentry to set XATTR at + * @name: xattr name for setxattr + * @value: xattr value to set + * @size: size of xattr value + * @flags: destination buffer length + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, + const void *attr_value, size_t attr_size, int flags) +{ + int err; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_setxattr(user_ns, + dentry, +#else + err = vfs_setxattr(dentry, +#endif + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_vfs_fsetxattr(struct ksmbd_work *work, const char *filename, + const char *attr_name, const void *attr_value, + size_t attr_size, int flags) +{ + struct path path; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(filename, 0, &path); + if (err) { + ksmbd_revert_fsids(work); + ksmbd_debug(VFS, "cannot get linux path %s, err %d\n", + filename, err); + return err; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_setxattr(mnt_user_ns(path.mnt), path.dentry, +#else + err = vfs_setxattr(path.dentry, +#endif + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + path_put(&path); + ksmbd_revert_fsids(work); + return err; +} +#endif + +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path) +{ + char *abs_name; + struct dentry *dent; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (!abs_name) + return ERR_PTR(-ENOMEM); + + dent = kern_path_create(AT_FDCWD, abs_name, path, flags); + kfree(abs_name); + return dent; +} + +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + int acl_type) +{ + struct xattr_smb_acl *smb_acl = NULL; + struct posix_acl *posix_acls; + struct posix_acl_entry *pa_entry; + struct xattr_acl_entry *xa_entry; + int i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return NULL; + + posix_acls = get_acl(inode, acl_type); + if (!posix_acls) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + + sizeof(struct xattr_acl_entry) * posix_acls->a_count, + GFP_KERNEL); + if (!smb_acl) + goto out; + + smb_acl->count = posix_acls->a_count; + pa_entry = posix_acls->a_entries; + xa_entry = smb_acl->entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { + switch (pa_entry->e_tag) { + case ACL_USER: + xa_entry->type = SMB_ACL_USER; + xa_entry->uid = posix_acl_uid_translate(user_ns, pa_entry); + break; + case ACL_USER_OBJ: + xa_entry->type = SMB_ACL_USER_OBJ; + break; + case ACL_GROUP: + xa_entry->type = SMB_ACL_GROUP; + xa_entry->gid = posix_acl_gid_translate(user_ns, pa_entry); + break; + case ACL_GROUP_OBJ: + xa_entry->type = SMB_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + xa_entry->type = SMB_ACL_OTHER; + break; + case ACL_MASK: + xa_entry->type = SMB_ACL_MASK; + break; + default: + pr_err("unknown type : 0x%x\n", pa_entry->e_tag); + goto out; + } + + if (pa_entry->e_perm & ACL_READ) + xa_entry->perm |= SMB_ACL_READ; + if (pa_entry->e_perm & ACL_WRITE) + xa_entry->perm |= SMB_ACL_WRITE; + if (pa_entry->e_perm & ACL_EXECUTE) + xa_entry->perm |= SMB_ACL_EXECUTE; + } +out: + posix_acl_release(posix_acls); + return smb_acl; +} + +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len) +{ + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; + struct inode *inode = d_inode(dentry); + + acl.version = 4; + acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; + acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); + + memcpy(acl.desc, "posix_acl", 9); + acl.desc_len = 10; + + pntsd->osidoffset = + cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); + pntsd->gsidoffset = + cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); + pntsd->dacloffset = + cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); + + acl.sd_buf = (char *)pntsd; + acl.sd_size = len; + + rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + return rc; + } + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, + smb_acl, def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + acl.posix_acl_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out; + } + + rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_vfs_setxattr(user_ns, dentry, + XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); + if (rc < 0) + pr_err("Failed to store XATTR ntacl :%d\n", rc); + + kfree(sd_ndr.data); +out: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + return rc; +} + +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd) +{ + int rc; + struct ndr n; + struct inode *inode = d_inode(dentry); + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + + rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); + if (rc <= 0) + return rc; + + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + goto free_n_data; + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, + def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out_free; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out_free; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + pr_err("hash value diff\n"); + rc = -EINVAL; + goto out_free; + } + + *pntsd = acl.sd_buf; + (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - + NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out_free: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + if (rc < 0) { + kfree(acl.sd_buf); + *pntsd = NULL; + } + +free_n_data: + kfree(n.data); + return rc; +} + +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ndr_encode_dos_attr(&n, da); + if (err) + return err; + + err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, n.offset, 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); + + return err; +} + +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); + if (err > 0) { + n.length = err; + if (ndr_decode_dos_attr(&n, da)) + err = -EINVAL; + kfree(n.data); + } else { + ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + } + + return err; +} + +/** + * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options + * @filp: file pointer for IO + * @options: smb IO options + */ +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) +{ + struct address_space *mapping; + + mapping = filp->f_mapping; + + if (!option || !mapping) + return; + + if (option & FILE_WRITE_THROUGH_LE) { + filp->f_flags |= O_SYNC; + } else if (option & FILE_SEQUENTIAL_ONLY_LE) { + filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; + spin_lock(&filp->f_lock); + filp->f_mode &= ~FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } else if (option & FILE_RANDOM_ACCESS_LE) { + spin_lock(&filp->f_lock); + filp->f_mode |= FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } +} + +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len) +{ + smb_break_all_levII_oplock(work, fp, 1); + if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) + return vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); + + return vfs_fallocate(fp->filp, FALLOC_FL_ZERO_RANGE, off, len); +} + +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count) +{ + struct file *f = fp->filp; + struct inode *inode = file_inode(fp->filp); + loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; + loff_t extent_start, extent_end; + int ret = 0; + + if (start > maxbytes) + return -EFBIG; + + if (!in_count) + return 0; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if (length > maxbytes || (maxbytes - length) < start) + length = maxbytes - start; + + if (start + length > inode->i_size) + length = inode->i_size - start; + + *out_count = 0; + end = start + length; + while (start < end && *out_count < in_count) { + extent_start = f->f_op->llseek(f, start, SEEK_DATA); + if (extent_start < 0) { + if (extent_start != -ENXIO) + ret = (int)extent_start; + break; + } + + if (extent_start >= end) + break; + + extent_end = f->f_op->llseek(f, extent_start, SEEK_HOLE); + if (extent_end < 0) { + if (extent_end != -ENXIO) + ret = (int)extent_end; + break; + } else if (extent_start >= extent_end) { + break; + } + + ranges[*out_count].file_offset = cpu_to_le64(extent_start); + ranges[(*out_count)++].length = + cpu_to_le64(min(extent_end, end) - extent_start); + + start = extent_end; + } + + return ret; +} + +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return vfs_removexattr(user_ns, dentry, attr_name); +#else + return vfs_removexattr(dentry, attr_name); +#endif +} + +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry) +{ + int err = 0; + + err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); + if (err) + return err; + + dget(dentry); + if (S_ISDIR(d_inode(dentry)->i_mode)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_rmdir(user_ns, d_inode(dir), dentry); + else + err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); +#else + err = vfs_rmdir(d_inode(dir), dentry); + else + err = vfs_unlink(d_inode(dir), dentry, NULL); +#endif + + dput(dentry); + inode_unlock(d_inode(dir)); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); + + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * ksmbd_vfs_dentry_open() - open a dentry and provide fid for it + * @work: smb work ptr + * @path: path of dentry to be opened + * @flags: open flags + * @ret_id: fid returned on this + * @option: file access pattern options for fadvise + * @fexist: file already present or not + * + * Return: allocated struct ksmbd_file on success, otherwise error pointer + */ +struct ksmbd_file *ksmbd_vfs_dentry_open(struct ksmbd_work *work, + const struct path *path, int flags, + __le32 option, int fexist) +{ + struct file *filp; + int err = 0; + struct ksmbd_file *fp = NULL; + + filp = dentry_open(path, flags | O_LARGEFILE, current_cred()); + if (IS_ERR(filp)) { + err = PTR_ERR(filp); + pr_err("dentry open failed, err %d\n", err); + return ERR_PTR(err); + } + + ksmbd_vfs_set_fadvise(filp, option); + + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + err = PTR_ERR(fp); + pr_err("id insert failed\n"); + goto err_out; + } + + if (flags & O_TRUNC) { + if (fexist) + smb_break_all_oplock(work, fp); + err = vfs_truncate((struct path *)path, 0); + if (err) + goto err_out; + } + return fp; + +err_out: + if (!IS_ERR(fp)) + ksmbd_close_fd(work, fp->volatile_id); + if (err) { + fp = ERR_PTR(err); + pr_err("err : %d\n", err); + } + return fp; +} +#endif + +static int __dir_empty(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + buf->dirent_count++; + + if (buf->dirent_count > 2) + return -ENOTEMPTY; + return 0; +} + +/** + * ksmbd_vfs_empty_dir() - check for empty directory + * @fp: ksmbd file pointer + * + * Return: true if directory empty, otherwise false + */ +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) +{ + int err; + struct ksmbd_readdir_data readdir_data; + + memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); + + set_ctx_actor(&readdir_data.ctx, __dir_empty); + readdir_data.dirent_count = 0; + + err = iterate_dir(fp->filp, &readdir_data.ctx); + if (readdir_data.dirent_count > 2) + err = -ENOTEMPTY; + else + err = 0; + return err; +} + +static int __caseless_lookup(struct dir_context *ctx, const char *name, + int namlen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + + if (buf->used != namlen) + return 0; + if (!strncasecmp((char *)buf->private, name, namlen)) { + memcpy((char *)buf->private, name, namlen); + buf->dirent_count = 1; + return -EEXIST; + } + return 0; +} + +/** + * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory + * @dir: path info + * @name: filename to lookup + * @namelen: filename length + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen) +{ + int ret; + struct file *dfilp; + int flags = O_RDONLY | O_LARGEFILE; + struct ksmbd_readdir_data readdir_data = { + .ctx.actor = __caseless_lookup, + .private = name, + .used = namelen, + .dirent_count = 0, + }; + + dfilp = dentry_open(dir, flags, current_cred()); + if (IS_ERR(dfilp)) + return PTR_ERR(dfilp); + + ret = iterate_dir(dfilp, &readdir_data.ctx); + if (readdir_data.dirent_count > 0) + ret = 0; + fput(dfilp); + return ret; +} + +/** + * ksmbd_vfs_kern_path() - lookup a file and get path info + * @name: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) +{ + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; + + flags |= LOOKUP_BENEATH; + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + name, + flags, + path); + if (!err) + return 0; + + if (caseless) { + char *filepath; + struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(name, GFP_KERNEL); + if (!filepath) + return -ENOMEM; + + path_len = strlen(filepath); + remain_len = path_len; + + parent = share_conf->vfs_path; + path_get(&parent); + + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len); + path_put(&parent); + if (err) + goto out; + + next[0] = '\0'; + + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + filepath, + flags, + &parent); + if (err) + goto out; + else if (is_last) { + *path = parent; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; +out: + kfree(filepath); + } + return err; +} +#else +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) +{ + char *abs_name; + int err; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (IS_ERR(abs_name)) + return PTR_ERR(abs_name); + + err = kern_path(abs_name, flags, path); + if (!err) { + err = 0; + goto free_abs_name; + } + + if (caseless) { + char *filepath; + struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(abs_name, GFP_KERNEL); + if (!filepath) { + err = -ENOMEM; + goto free_abs_name; + } + + path_len = strlen(filepath); + remain_len = path_len - 1; + + err = kern_path("/", flags, &parent); + if (err) + goto out; + + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len); + if (err) { + path_put(&parent); + goto out; + } + + path_put(&parent); + next[0] = '\0'; + + err = kern_path(filepath, flags, &parent); + if (err) + goto out; + + if (is_last) { + path->mnt = parent.mnt; + path->dentry = parent.dentry; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; +out: + kfree(filepath); + } + +free_abs_name: + kfree(abs_name); + return err; +} +#endif + +/** + * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format + * @p: destination buffer + * @ksmbd_kstat: ksmbd kstat wrapper + */ +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) +{ + struct file_directory_info *info = (struct file_directory_info *)(*p); + struct kstat *kstat = ksmbd_kstat->kstat; + u64 time; + + info->FileIndex = 0; + info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(kstat->atime); + info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->mtime); + info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->ctime); + info->ChangeTime = cpu_to_le64(time); + + if (ksmbd_kstat->file_attributes & ATTR_DIRECTORY_LE) { + info->EndOfFile = 0; + info->AllocationSize = 0; + } else { + info->EndOfFile = cpu_to_le64(kstat->size); + info->AllocationSize = cpu_to_le64(kstat->blocks << 9); + } + info->ExtFileAttributes = ksmbd_kstat->file_attributes; + + return info; +} + +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat) +{ + u64 time; + int rc; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); +#else + generic_fillattr(d_inode(dentry), ksmbd_kstat->kstat); +#endif + + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + ksmbd_kstat->create_time = time; + + /* + * set default value for the case that store dos attributes is not yes + * or that acl is disable in server's filesystem and the config is yes. + */ + if (S_ISDIR(ksmbd_kstat->kstat->mode)) + ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; + else + ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); + if (rc > 0) { + ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); + ksmbd_kstat->create_time = da.create_time; + } else { + ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } + } + + return 0; +} + +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type) +{ + char *type, *buf; + + if (s_type == DIR_STREAM) + type = ":$INDEX_ALLOCATION"; + else + type = ":$DATA"; + + buf = kasprintf(GFP_KERNEL, "%s%s%s", + XATTR_NAME_STREAM, stream_name, type); + if (!buf) + return -ENOMEM; + + *xattr_stream_name = buf; + *xattr_stream_name_size = strlen(buf) + 1; + + return 0; +} + +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written) +{ + unsigned int i; + loff_t src_off, dst_off, src_file_size; + size_t len; + int ret; + + *chunk_count_written = 0; + *chunk_size_written = 0; + *total_size_written = 0; + + if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pd)\n", src_fp->filp->f_path.dentry); + return -EACCES; + } + if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { + pr_err("no right to write(%pd)\n", dst_fp->filp->f_path.dentry); + return -EACCES; + } + + if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) + return -EBADF; + + smb_break_all_levII_oplock(work, dst_fp, 1); + + if (!work->tcon->posix_extensions) { + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (check_lock_range(src_fp->filp, src_off, + src_off + len - 1, READ)) + return -EAGAIN; + if (check_lock_range(dst_fp->filp, dst_off, + dst_off + len - 1, WRITE)) + return -EAGAIN; + } + } + + src_file_size = i_size_read(file_inode(src_fp->filp)); + + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (src_off + len > src_file_size) + return -E2BIG; + + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, 0); + if (ret < 0) + return ret; + + *chunk_count_written += 1; + *total_size_written += ret; + } + return 0; +} + +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) +{ + wait_event(flock->fl_wait, !flock->fl_blocker); +} + +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) +{ + return wait_event_interruptible_timeout(flock->fl_wait, + !flock->fl_blocker, + timeout); +} + +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) +{ + locks_delete_block(flock); +} + +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode) +{ + struct posix_acl_state acl_state; + struct posix_acl *acls; + int rc; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + ksmbd_debug(SMB, "Set posix acls\n"); + rc = init_acl_state(&acl_state, 1); + if (rc) + return rc; + + /* Set default owner group */ + acl_state.owner.allow = (inode->i_mode & 0700) >> 6; + acl_state.group.allow = (inode->i_mode & 0070) >> 3; + acl_state.other.allow = inode->i_mode & 0007; + acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + acl_state.owner.allow; + acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + acl_state.group.allow; + acl_state.mask.allow = 0x07; + + acls = posix_acl_alloc(6, GFP_KERNEL); + if (!acls) { + free_acl_state(&acl_state); + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); +#else + rc = set_posix_acl(inode, ACL_TYPE_ACCESS, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + else if (S_ISDIR(inode->i_mode)) { + posix_state_to_acl(&acl_state, acls->a_entries); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); +#else + rc = set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +} + +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, struct inode *parent_inode) +{ + struct posix_acl *acls; + struct posix_acl_entry *pace; + int rc, i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); + if (!acls) + return -ENOENT; + pace = acls->a_entries; + + for (i = 0; i < acls->a_count; i++, pace++) { + if (pace->e_tag == ACL_MASK) { + pace->e_perm = 0x07; + break; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); +#else + rc = set_posix_acl(inode, ACL_TYPE_ACCESS, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); +#else + rc = set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + posix_acl_release(acls); + return rc; +} diff -Naur --no-dereference a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c --- a/fs/ksmbd/vfs_cache.c 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/vfs_cache.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "glob.h" +#include "vfs_cache.h" +#include "oplock.h" +#include "vfs.h" +#include "connection.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "smb_common.h" + +#define S_DEL_PENDING 1 +#define S_DEL_ON_CLS 2 +#define S_DEL_ON_CLS_STREAM 8 + +static unsigned int inode_hash_mask __read_mostly; +static unsigned int inode_hash_shift __read_mostly; +static struct hlist_head *inode_hashtable __read_mostly; +static DEFINE_RWLOCK(inode_hash_lock); + +static struct ksmbd_file_table global_ft; +static atomic_long_t fd_limit; +static struct kmem_cache *filp_cache; + +void ksmbd_set_fd_limit(unsigned long limit) +{ + limit = min(limit, get_max_files()); + atomic_long_set(&fd_limit, limit); +} + +static bool fd_limit_depleted(void) +{ + long v = atomic_long_dec_return(&fd_limit); + + if (v >= 0) + return false; + atomic_long_inc(&fd_limit); + return true; +} + +static void fd_limit_close(void) +{ + atomic_long_inc(&fd_limit); +} + +/* + * INODE hash + */ + +static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) +{ + unsigned long tmp; + + tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / + L1_CACHE_BYTES; + tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); + return tmp & inode_hash_mask; +} + +static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) +{ + struct hlist_head *head = inode_hashtable + + inode_hash(inode->i_sb, inode->i_ino); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { + if (ci->m_inode == inode) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; + } + } + return ret_ci; +} + +static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) +{ + return __ksmbd_inode_lookup(file_inode(fp->filp)); +} + +static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +{ + struct ksmbd_inode *ci; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + read_unlock(&inode_hash_lock); + return ci; +} + +int ksmbd_query_inode_status(struct inode *inode) +{ + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & S_DEL_PENDING) + ret = KSMBD_INODE_STATUS_PENDING_DELETE; + atomic_dec(&ci->m_count); + } + read_unlock(&inode_hash_lock); + return ret; +} + +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) +{ + return (fp->f_ci->m_flags & S_DEL_PENDING); +} + +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags |= S_DEL_PENDING; +} + +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags &= ~S_DEL_PENDING; +} + +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info) +{ + if (ksmbd_stream_fd(fp)) { + fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; + return; + } + + fp->f_ci->m_flags |= S_DEL_ON_CLS; +} + +static void ksmbd_inode_hash(struct ksmbd_inode *ci) +{ + struct hlist_head *b = inode_hashtable + + inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); + + hlist_add_head(&ci->m_hash, b); +} + +static void ksmbd_inode_unhash(struct ksmbd_inode *ci) +{ + write_lock(&inode_hash_lock); + hlist_del_init(&ci->m_hash); + write_unlock(&inode_hash_lock); +} + +static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) +{ + ci->m_inode = file_inode(fp->filp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); + ci->m_flags = 0; + ci->m_fattr = 0; + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); + return 0; +} + +static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) +{ + struct ksmbd_inode *ci, *tmpci; + int rc; + + read_lock(&inode_hash_lock); + ci = ksmbd_inode_lookup(fp); + read_unlock(&inode_hash_lock); + if (ci) + return ci; + + ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + if (!ci) + return NULL; + + rc = ksmbd_inode_init(ci, fp); + if (rc) { + pr_err("inode initialized failed\n"); + kfree(ci); + return NULL; + } + + write_lock(&inode_hash_lock); + tmpci = ksmbd_inode_lookup(fp); + if (!tmpci) { + ksmbd_inode_hash(ci); + } else { + kfree(ci); + ci = tmpci; + } + write_unlock(&inode_hash_lock); + return ci; +} + +static void ksmbd_inode_free(struct ksmbd_inode *ci) +{ + ksmbd_inode_unhash(ci); + kfree(ci); +} + +static void ksmbd_inode_put(struct ksmbd_inode *ci) +{ + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +} + +int __init ksmbd_inode_hash_init(void) +{ + unsigned int loop; + unsigned long numentries = 16384; + unsigned long bucketsize = sizeof(struct hlist_head); + unsigned long size; + + inode_hash_shift = ilog2(numentries); + inode_hash_mask = (1 << inode_hash_shift) - 1; + + size = bucketsize << inode_hash_shift; + + /* init master fp hash table */ + inode_hashtable = vmalloc(size); + if (!inode_hashtable) + return -ENOMEM; + + for (loop = 0; loop < (1U << inode_hash_shift); loop++) + INIT_HLIST_HEAD(&inode_hashtable[loop]); + return 0; +} + +void ksmbd_release_inode_hash(void) +{ + vfree(inode_hashtable); +} + +static void __ksmbd_inode_close(struct ksmbd_file *fp) +{ + struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; + + filp = fp->filp; + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), + filp->f_path.dentry, + fp->stream.name); + if (err) + pr_err("remove xattr failed : %s\n", + fp->stream.name); + } + + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { + dentry = filp->f_path.dentry; + dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); + ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); + + ksmbd_inode_free(ci); + } +} + +static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + if (!has_file_id(fp->persistent_id)) + return; + + write_lock(&global_ft.lock); + idr_remove(global_ft.idr, fp->persistent_id); + write_unlock(&global_ft.lock); +} + +static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + if (!has_file_id(fp->volatile_id)) + return; + + write_lock(&fp->f_ci->m_lock); + list_del_init(&fp->node); + write_unlock(&fp->f_ci->m_lock); + + write_lock(&ft->lock); + idr_remove(ft->idr, fp->volatile_id); + write_unlock(&ft->lock); +} + +static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + struct file *filp; + struct ksmbd_lock *smb_lock, *tmp_lock; + + fd_limit_close(); + __ksmbd_remove_durable_fd(fp); + __ksmbd_remove_fd(ft, fp); + + close_id_del_oplock(fp); + filp = fp->filp; + + __ksmbd_inode_close(fp); + if (!IS_ERR_OR_NULL(filp)) + fput(filp); + + /* because the reference count of fp is 0, it is guaranteed that + * there are not accesses to fp->lock_list. + */ + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { + spin_lock(&fp->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + + list_del(&smb_lock->flist); + locks_free_lock(smb_lock->fl); + kfree(smb_lock); + } + + kfree(fp->filename); + if (ksmbd_stream_fd(fp)) + kfree(fp->stream.name); + kmem_cache_free(filp_cache, fp); +} + +static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) +{ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +} + +static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, + u64 id) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) + return NULL; + + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) + fp = ksmbd_fp_get(fp); + read_unlock(&ft->lock); + return fp; +} + +static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + __ksmbd_close_fd(&work->sess->file_table, fp); + atomic_dec(&work->conn->stats.open_files_count); +} + +static void set_close_state_blocked_works(struct ksmbd_file *fp) +{ + struct ksmbd_work *cancel_work, *ctmp; + + spin_lock(&fp->f_lock); + list_for_each_entry_safe(cancel_work, ctmp, &fp->blocked_works, + fp_entry) { + list_del(&cancel_work->fp_entry); + cancel_work->state = KSMBD_WORK_CLOSED; + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + spin_unlock(&fp->f_lock); +} + +int ksmbd_close_fd(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; + + if (!has_file_id(id)) + return 0; + + ft = &work->sess->file_table; + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } + read_unlock(&ft->lock); + + if (!fp) + return -EINVAL; + + __put_fd_final(work, fp); + return 0; +} + +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!fp) + return; + + if (!atomic_dec_and_test(&fp->refcount)) + return; + __put_fd_final(work, fp); +} + +static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) +{ + if (!fp) + return false; + if (fp->tcon != tcon) + return false; + return true; +} + +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) +{ + return __ksmbd_lookup_fd(&work->sess->file_table, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + + if (__sanity_check(work->tcon, fp)) + return fp; + + ksmbd_fd_put(work, fp); + return NULL; +} + +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) { + id = work->compound_fid; + pid = work->compound_pfid; + } + + fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + if (!__sanity_check(work->tcon, fp)) { + ksmbd_fd_put(work, fp); + return NULL; + } + if (fp->persistent_id != pid) { + ksmbd_fd_put(work, fp); + return NULL; + } + return fp; +} + +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +{ + return __ksmbd_lookup_fd(&global_ft, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->create_guid, + cguid, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&work->sess->file_table.lock); + idr_for_each_entry(work->sess->file_table.idr, fp, id) { + if (!strcmp(fp->filename, filename)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&work->sess->file_table.lock); + + return fp; +} +#endif + +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; + + ci = ksmbd_inode_lookup_by_vfsinode(inode); + if (!ci) + return NULL; + + read_lock(&ci->m_lock); + list_for_each_entry(lfp, &ci->m_fp_list, node) { + if (inode == file_inode(lfp->filp)) { + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return lfp; + } + } + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return NULL; +} + +#define OPEN_ID_TYPE_VOLATILE_ID (0) +#define OPEN_ID_TYPE_PERSISTENT_ID (1) + +static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) +{ + if (type == OPEN_ID_TYPE_VOLATILE_ID) + fp->volatile_id = id; + if (type == OPEN_ID_TYPE_PERSISTENT_ID) + fp->persistent_id = id; +} + +static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + int type) +{ + u64 id = 0; + int ret; + + if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { + __open_id_set(fp, KSMBD_NO_FID, type); + return -EMFILE; + } + + idr_preload(GFP_KERNEL); + write_lock(&ft->lock); + ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); + if (ret >= 0) { + id = ret; + ret = 0; + } else { + id = KSMBD_NO_FID; + fd_limit_close(); + } + + __open_id_set(fp, id, type); + write_unlock(&ft->lock); + idr_preload_end(); + return ret; +} + +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) +{ + __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); + return fp->persistent_id; +} + +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) +{ + struct ksmbd_file *fp; + int ret; + + fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); + if (!fp) { + pr_err("Failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&fp->blocked_works); + INIT_LIST_HEAD(&fp->node); + INIT_LIST_HEAD(&fp->lock_list); + spin_lock_init(&fp->f_lock); + atomic_set(&fp->refcount, 1); + + fp->filp = filp; + fp->conn = work->sess->conn; + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { + ret = -ENOMEM; + goto err_out; + } + + ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (ret) { + ksmbd_inode_put(fp->f_ci); + goto err_out; + } + + atomic_inc(&work->conn->stats.open_files_count); + return fp; + +err_out: + kmem_cache_free(filp_cache, fp); + return ERR_PTR(ret); +} + +static int +__close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp)) +{ + unsigned int id; + struct ksmbd_file *fp; + int num = 0; + + idr_for_each_entry(ft->idr, fp, id) { + if (skip(tcon, fp)) + continue; + + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + continue; + __ksmbd_close_fd(ft, fp); + num++; + } + return num; +} + +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return fp->tcon != tcon; +} + +static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return false; +} + +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + tree_conn_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +void ksmbd_close_session_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + session_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +int ksmbd_init_global_file_table(void) +{ + return ksmbd_init_file_table(&global_ft); +} + +void ksmbd_free_global_file_table(void) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + idr_for_each_entry(global_ft.idr, fp, id) { + __ksmbd_remove_durable_fd(fp); + kmem_cache_free(filp_cache, fp); + } + + ksmbd_destroy_file_table(&global_ft); +} + +int ksmbd_file_table_flush(struct ksmbd_work *work) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + int ret; + + read_lock(&work->sess->file_table.lock); + idr_for_each_entry(work->sess->file_table.idr, fp, id) { + ret = ksmbd_vfs_fsync(work, fp->volatile_id, KSMBD_NO_FID); + if (ret) + break; + } + read_unlock(&work->sess->file_table.lock); + return ret; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft) +{ + ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (!ft->idr) + return -ENOMEM; + + idr_init(ft->idr); + rwlock_init(&ft->lock); + return 0; +} + +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +{ + if (!ft->idr) + return; + + __close_file_table_ids(ft, NULL, session_fd_check); + idr_destroy(ft->idr); + kfree(ft->idr); + ft->idr = NULL; +} + +int ksmbd_init_file_cache(void) +{ + filp_cache = kmem_cache_create("ksmbd_file_cache", + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!filp_cache) + goto out; + + return 0; + +out: + pr_err("failed to allocate file cache\n"); + return -ENOMEM; +} + +void ksmbd_exit_file_cache(void) +{ + kmem_cache_destroy(filp_cache); +} diff -Naur --no-dereference a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h --- a/fs/ksmbd/vfs_cache.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/vfs_cache.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __VFS_CACHE_H__ +#define __VFS_CACHE_H__ + +#include +#include +#include +#include +#include +#include + +#include "vfs.h" + +/* Windows style file permissions for extended response */ +#define FILE_GENERIC_ALL 0x1F01FF +#define FILE_GENERIC_READ 0x120089 +#define FILE_GENERIC_WRITE 0x120116 +#define FILE_GENERIC_EXECUTE 0X1200a0 + +#define KSMBD_START_FID 0 +#define KSMBD_NO_FID (INT_MAX) +#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) + +struct ksmbd_conn; +struct ksmbd_session; + +struct ksmbd_lock { + struct file_lock *fl; + struct list_head clist; + struct list_head flist; + struct list_head llist; + unsigned int flags; + int cmd; + int zero_len; + unsigned long long start; + unsigned long long end; +}; + +struct stream { + char *name; + ssize_t size; +}; + +struct ksmbd_inode { + rwlock_t m_lock; + atomic_t m_count; + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; + struct inode *m_inode; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; + struct list_head m_op_list; + struct oplock_info *m_opinfo; + __le32 m_fattr; +}; + +struct ksmbd_file { + struct file *filp; + char *filename; + u64 persistent_id; + u64 volatile_id; + + spinlock_t f_lock; + + struct ksmbd_inode *f_ci; + struct ksmbd_inode *f_parent_ci; + struct oplock_info __rcu *f_opinfo; + struct ksmbd_conn *conn; + struct ksmbd_tree_connect *tcon; + + atomic_t refcount; + __le32 daccess; + __le32 saccess; + __le32 coption; + __le32 cdoption; + __u64 create_time; + __u64 itime; + + bool is_nt_open; + bool attrib_only; + + char client_guid[16]; + char create_guid[16]; + char app_instance_id[16]; + + struct stream stream; + struct list_head node; + struct list_head blocked_works; + struct list_head lock_list; + + int durable_timeout; + +#ifdef CONFIG_SMB_INSECURE_SERVER + /* for SMB1 */ + int pid; + + /* conflict lock fail count for SMB1 */ + unsigned int cflock_cnt; + /* last lock failure start offset for SMB1 */ + unsigned long long llock_fstart; + + int dirent_offset; +#endif + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; +}; + +static inline void set_ctx_actor(struct dir_context *ctx, + filldir_t actor) +{ + ctx->actor = actor; +} + +#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG + +struct ksmbd_file_table { + rwlock_t lock; + struct idr *idr; +}; + +static inline bool has_file_id(u64 id) +{ + return id < KSMBD_NO_FID; +} + +static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) +{ + return fp->stream.name != NULL; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); +int ksmbd_close_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename); +#endif +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +void ksmbd_close_session_fds(struct ksmbd_work *work); +int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); +int ksmbd_init_global_file_table(void); +void ksmbd_free_global_file_table(void); +int ksmbd_file_table_flush(struct ksmbd_work *work); +void ksmbd_set_fd_limit(unsigned long limit); + +/* + * INODE hash + */ +int __init ksmbd_inode_hash_init(void); +void ksmbd_release_inode_hash(void); + +enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_OK, + KSMBD_INODE_STATUS_UNKNOWN, + KSMBD_INODE_STATUS_PENDING_DELETE, +}; + +int ksmbd_query_inode_status(struct inode *inode); +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info); +int ksmbd_init_file_cache(void); +void ksmbd_exit_file_cache(void); +#endif /* __VFS_CACHE_H__ */ diff -Naur --no-dereference a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h --- a/fs/ksmbd/vfs.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/vfs.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_VFS_H__ +#define __KSMBD_VFS_H__ + +#include +#include +#include +#include +#include + +#include "smbacl.h" +#include "xattr.h" + +/* + * Enumeration for stream type. + */ +enum { + DATA_STREAM = 1, /* type $DATA */ + DIR_STREAM /* type $INDEX_ALLOCATION */ +}; + +/* CreateOptions */ +/* Flag is set, it must not be a file , valid for directory only */ +#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) + +/* Should not buffer on server*/ +#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) +/* MBZ */ +#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) +/* MBZ */ +#define FILE_SYNCHRONOUS_IO_NONALERT_LE cpu_to_le32(0x00000020) + +/* Flaf must not be set for directory */ +#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) + +/* Should be zero */ +#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) +#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) +#define FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) + +/** + * Doc says this is obsolete "open for recovery" flag should be zero + * in any case. + */ +#define CREATE_OPEN_FOR_RECOVERY cpu_to_le32(0x00000400) +#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) + +/* Should be zero*/ +#define FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) +#define FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) +#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) +#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) + +/* Should be zero */ +#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) +#define CREATE_OPTIONS_MASK cpu_to_le32(0x00FFFFFF) +#define CREATE_OPTION_READONLY 0x10000000 +/* system. NB not sent over wire */ +#define CREATE_OPTION_SPECIAL 0x20000000 + +struct ksmbd_work; +struct ksmbd_file; +struct ksmbd_conn; + +struct ksmbd_dir_info { + const char *name; +#ifdef CONFIG_SMB_INSECURE_SERVER + char *smb1_name; +#endif + char *wptr; + char *rptr; + int name_len; + int out_buf_len; + int num_entry; + int data_count; + int last_entry_offset; + bool hide_dot_file; + int flags; +}; + +struct ksmbd_readdir_data { + struct dir_context ctx; + union { + void *private; + char *dirent; + }; + + unsigned int used; + unsigned int dirent_count; + unsigned int file_attr; +}; + +/* ksmbd kstat wrapper to get valid create time when reading dir entry */ +struct ksmbd_kstat { + struct kstat *kstat; + unsigned long long create_time; + __le32 file_attributes; +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) +static inline struct user_namespace *mnt_user_ns(const struct vfsmount *mnt) +{ + return &init_user_ns; +} + +static inline struct user_namespace *file_mnt_user_ns(struct file *file) +{ + return &init_user_ns; +} +#endif + +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child); +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos); +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); +int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); +int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_vfs_setattr(struct ksmbd_work *work, const char *name, + u64 fid, struct iattr *attrs); +int ksmbd_vfs_symlink(struct ksmbd_work *work, + const char *name, const char *symname); +int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp); +int ksmbd_vfs_readdir_name(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct ksmbd_kstat *ksmbd_kstat, + const char *de_name, int de_name_len, + const char *dir_path); +#endif +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname); +int ksmbd_vfs_rename_slowpath(struct ksmbd_work *work, + char *oldname, char *newname); +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size); +struct srv_copychunk; +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written); +struct ksmbd_file *ksmbd_vfs_dentry_open(struct ksmbd_work *work, + const struct path *path, int flags, + __le32 option, int fexist); +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, + char **xattr_buf); +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len); +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, + const void *attr_value, size_t attr_size, int flags); +int ksmbd_vfs_fsetxattr(struct ksmbd_work *work, const char *filename, + const char *attr_name, const void *attr_value, + size_t attr_size, int flags); +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name); +int ksmbd_vfs_kern_path(struct ksmbd_work *work, + char *name, unsigned int flags, struct path *path, + bool caseless); +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path); +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len); +struct file_allocated_range_buffer; +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count); +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry); +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat); +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len); +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode); +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + struct inode *parent_inode); +#endif /* __KSMBD_VFS_H__ */ diff -Naur --no-dereference a/fs/ksmbd/xattr.h b/fs/ksmbd/xattr.h --- a/fs/ksmbd/xattr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/fs/ksmbd/xattr.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + */ + +#ifndef __XATTR_H__ +#define __XATTR_H__ + +/* + * These are on-disk structures to store additional metadata into xattr to + * reproduce windows filesystem semantics. And they are encoded with NDR to + * compatible with samba's xattr meta format. The compatibility with samba + * is important because it can lose the information(file attribute, + * creation time, acls) about the existing files when switching between + * ksmbd and samba. + */ + +/* + * Dos attribute flags used for what variable is valid. + */ +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020, + XATTR_DOSINFO_ITIME = 0x00000040 +}; + +/* + * Dos attribute structure which is compatible with samba's one. + * Storing it into the xattr named "DOSATTRIB" separately from inode + * allows ksmbd to faithfully reproduce windows filesystem semantics + * on top of a POSIX filesystem. + */ +struct xattr_dos_attrib { + __u16 version; /* version 3 or version 4 */ + __u32 flags; /* valid flags */ + __u32 attr; /* Dos attribute */ + __u32 ea_size; /* EA size */ + __u64 size; + __u64 alloc_size; + __u64 create_time; /* File creation time */ + __u64 change_time; /* File change time */ + __u64 itime; /* Invented/Initial time */ +}; + +/* + * Enumeration is used for computing posix acl hash. + */ +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +/* + * xattr_smb_acl structure is used for computing posix acl hash. + */ +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[0]; +}; + +/* 64bytes hash in xattr_ntacl is computed with sha256 */ +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +/* + * xattr_ntacl is used for storing ntacl and hashes. + * Hash is used for checking valid posix acl and ntacl in xattr. + */ +struct xattr_ntacl { + __u16 version; /* version 4*/ + void *sd_buf; + __u32 sd_size; + __u16 hash_type; /* hash type */ + __u8 desc[10]; /* posix_acl description */ + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +/* STREAM XATTR PREFIX */ +#define STREAM_PREFIX "DosStream." +#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) +#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) +#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) + +/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ +#define SD_PREFIX "NTACL" +#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) +#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) +#define XATTR_NAME_SD_LEN \ + (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) + +#endif /* __XATTR_H__ */ diff -Naur --no-dereference a/fs/Makefile b/fs/Makefile --- a/fs/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/Makefile 2022-01-06 12:45:53.830318172 -0500 @@ -98,6 +98,7 @@ obj-$(CONFIG_UNICODE) += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ obj-$(CONFIG_CIFS) += cifs/ +obj-$(CONFIG_SMB_SERVER) += ksmbd/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_UFS_FS) += ufs/ @@ -136,3 +137,4 @@ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_VBOXSF_FS) += vboxsf/ obj-$(CONFIG_ZONEFS_FS) += zonefs/ +obj-$(CONFIG_AUFS_FS) += aufs/ diff -Naur --no-dereference a/fs/namespace.c b/fs/namespace.c --- a/fs/namespace.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/namespace.c 2022-01-06 12:45:53.834318188 -0500 @@ -431,6 +431,7 @@ mnt_dec_writers(real_mount(mnt)); preempt_enable(); } +EXPORT_SYMBOL_GPL(__mnt_drop_write); /** * mnt_drop_write - give up write access to a mount @@ -792,6 +793,13 @@ return mnt->mnt_ns == current->nsproxy->mnt_ns; } +/* for aufs, CONFIG_AUFS_BR_FUSE */ +int is_current_mnt_ns(struct vfsmount *mnt) +{ + return check_mnt(real_mount(mnt)); +} +EXPORT_SYMBOL_GPL(is_current_mnt_ns); + /* * vfsmount lock must be held for write */ @@ -1992,6 +2000,7 @@ } return 0; } +EXPORT_SYMBOL_GPL(iterate_mounts); static void lock_mnt_tree(struct mount *mnt) { diff -Naur --no-dereference a/fs/notify/group.c b/fs/notify/group.c --- a/fs/notify/group.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/notify/group.c 2022-01-06 12:45:53.834318188 -0500 @@ -100,6 +100,7 @@ { refcount_inc(&group->refcnt); } +EXPORT_SYMBOL_GPL(fsnotify_get_group); /* * Drop a reference to a group. Free it if it's through. diff -Naur --no-dereference a/fs/open.c b/fs/open.c --- a/fs/open.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/open.c 2022-01-06 12:45:53.834318188 -0500 @@ -65,6 +65,7 @@ inode_unlock(dentry->d_inode); return ret; } +EXPORT_SYMBOL_GPL(do_truncate); long vfs_truncate(const struct path *path, loff_t length) { diff -Naur --no-dereference a/fs/proc/base.c b/fs/proc/base.c --- a/fs/proc/base.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/proc/base.c 2022-01-06 12:45:53.834318188 -0500 @@ -2184,7 +2184,7 @@ rc = -ENOENT; vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { - *path = vma->vm_file->f_path; + *path = vma_pr_or_file(vma)->f_path; path_get(path); rc = 0; } diff -Naur --no-dereference a/fs/proc/nommu.c b/fs/proc/nommu.c --- a/fs/proc/nommu.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/proc/nommu.c 2022-01-06 12:45:53.834318188 -0500 @@ -40,7 +40,10 @@ file = region->vm_file; if (file) { - struct inode *inode = file_inode(region->vm_file); + struct inode *inode; + + file = vmr_pr_or_file(region); + inode = file_inode(file); dev = inode->i_sb->s_dev; ino = inode->i_ino; } diff -Naur --no-dereference a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c --- a/fs/proc/task_mmu.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/proc/task_mmu.c 2022-01-06 12:45:53.834318188 -0500 @@ -280,7 +280,10 @@ const char *name = NULL; if (file) { - struct inode *inode = file_inode(vma->vm_file); + struct inode *inode; + + file = vma_pr_or_file(vma); + inode = file_inode(file); dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; @@ -1863,7 +1866,7 @@ struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; - struct file *file = vma->vm_file; + struct file *file = vma_pr_or_file(vma); struct mm_struct *mm = vma->vm_mm; struct mempolicy *pol; char buffer[64]; diff -Naur --no-dereference a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c --- a/fs/proc/task_nommu.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/proc/task_nommu.c 2022-01-06 12:45:53.834318188 -0500 @@ -155,7 +155,10 @@ file = vma->vm_file; if (file) { - struct inode *inode = file_inode(vma->vm_file); + struct inode *inode; + + file = vma_pr_or_file(vma); + inode = file_inode(file); dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; diff -Naur --no-dereference a/fs/read_write.c b/fs/read_write.c --- a/fs/read_write.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/read_write.c 2022-01-06 12:45:53.834318188 -0500 @@ -503,6 +503,7 @@ inc_syscr(current); return ret; } +EXPORT_SYMBOL_GPL(vfs_read); static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { @@ -613,6 +614,7 @@ file_end_write(file); return ret; } +EXPORT_SYMBOL_GPL(vfs_write); /* file_ppos returns &file->f_pos or NULL if file is stream */ static inline loff_t *file_ppos(struct file *file) diff -Naur --no-dereference a/fs/splice.c b/fs/splice.c --- a/fs/splice.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/splice.c 2022-01-06 12:45:53.834318188 -0500 @@ -756,20 +756,21 @@ /* * Attempt to initiate a splice from pipe to file. */ -static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) +long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) { if (unlikely(!out->f_op->splice_write)) return warn_unsupported(out, "write"); return out->f_op->splice_write(pipe, out, ppos, len, flags); } +EXPORT_SYMBOL_GPL(do_splice_from); /* * Attempt to initiate a splice from a file to a pipe. */ -static long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { int ret; @@ -787,6 +788,7 @@ return warn_unsupported(in, "read"); return in->f_op->splice_read(in, ppos, pipe, len, flags); } +EXPORT_SYMBOL_GPL(do_splice_to); /** * splice_direct_to_actor - splices data directly between two non-pipes diff -Naur --no-dereference a/fs/sync.c b/fs/sync.c --- a/fs/sync.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/sync.c 2022-01-06 12:45:53.834318188 -0500 @@ -28,7 +28,7 @@ * wait == 1 case since in that case write_inode() functions do * sync_dirty_buffer() and thus effectively write one block at a time. */ -static int __sync_filesystem(struct super_block *sb, int wait) +int __sync_filesystem(struct super_block *sb, int wait) { if (wait) sync_inodes_sb(sb); @@ -39,6 +39,7 @@ sb->s_op->sync_fs(sb, wait); return __sync_blockdev(sb->s_bdev, wait); } +EXPORT_SYMBOL_GPL(__sync_filesystem); /* * Write out and wait upon all dirty data associated with this diff -Naur --no-dereference a/fs/xattr.c b/fs/xattr.c --- a/fs/xattr.c 2021-12-17 04:14:42.000000000 -0500 +++ b/fs/xattr.c 2022-01-06 12:45:53.834318188 -0500 @@ -360,6 +360,7 @@ *xattr_value = value; return error; } +EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); ssize_t __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, diff -Naur --no-dereference a/.github/FUNDING.yml b/.github/FUNDING.yml --- a/.github/FUNDING.yml 1969-12-31 19:00:00.000000000 -0500 +++ b/.github/FUNDING.yml 2022-01-06 12:45:53.802318058 -0500 @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: beagleboard # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: beagleboard # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: https://paypal.me/beagleboard # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff -Naur --no-dereference a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md --- a/.github/ISSUE_TEMPLATE/bug_report.md 1969-12-31 19:00:00.000000000 -0500 +++ b/.github/ISSUE_TEMPLATE/bug_report.md 2022-01-06 12:45:53.802318058 -0500 @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Describe how to reproduce the bug** +List all the steps needed to reproduce the bug + +**REQUIRED INFORMATION** +Run this command and paste the output here: +``` +sudo /opt/scripts/tools/version.sh +``` diff -Naur --no-dereference a/.gitignore b/.gitignore --- a/.gitignore 2021-12-17 04:14:42.000000000 -0500 +++ b/.gitignore 2022-01-06 12:45:53.802318058 -0500 @@ -18,6 +18,7 @@ *.c.[012]*.* *.dt.yaml *.dtb +*.dtbo *.dtb.S *.dwo *.elf diff -Naur --no-dereference a/include/crypto/sha.h b/include/crypto/sha.h --- a/include/crypto/sha.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/crypto/sha.h 2022-01-06 12:45:53.834318188 -0500 @@ -95,6 +95,7 @@ struct shash_desc; +extern void sha256_transform(u32 *state, const u8 *input); extern int crypto_sha1_update(struct shash_desc *desc, const u8 *data, unsigned int len); diff -Naur --no-dereference a/include/dt-bindings/board/am335x-bbw-bbb-base.h b/include/dt-bindings/board/am335x-bbw-bbb-base.h --- a/include/dt-bindings/board/am335x-bbw-bbb-base.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/dt-bindings/board/am335x-bbw-bbb-base.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,103 @@ +/* + * This header provides constants for bbw/bbb pinctrl bindings. + * + * Copyright (C) 2014 Robert Nelson + * + * Numbers Based on: https://github.com/derekmolloy/boneDeviceTree/tree/master/docs + */ + +#ifndef _DT_BINDINGS_BOARD_AM335X_BBW_BBB_BASE_H +#define _DT_BINDINGS_BOARD_AM335X_BBW_BBB_BASE_H + +#define BONE_P8_03 0x018 +#define BONE_P8_04 0x01C + +#define BONE_P8_05 0x008 +#define BONE_P8_06 0x00C +#define BONE_P8_07 0x090 +#define BONE_P8_08 0x094 + +#define BONE_P8_09 0x09C +#define BONE_P8_10 0x098 +#define BONE_P8_11 0x034 +#define BONE_P8_12 0x030 + +#define BONE_P8_13 0x024 +#define BONE_P8_14 0x028 +#define BONE_P8_15 0x03C +#define BONE_P8_16 0x038 + +#define BONE_P8_17 0x02C +#define BONE_P8_18 0x08C +#define BONE_P8_19 0x020 +#define BONE_P8_20 0x084 + +#define BONE_P8_21 0x080 +#define BONE_P8_22 0x014 +#define BONE_P8_23 0x010 +#define BONE_P8_24 0x004 + +#define BONE_P8_25 0x000 +#define BONE_P8_26 0x07C +#define BONE_P8_27 0x0E0 +#define BONE_P8_28 0x0E8 + +#define BONE_P8_29 0x0E4 +#define BONE_P8_30 0x0EC +#define BONE_P8_31 0x0D8 +#define BONE_P8_32 0x0DC + +#define BONE_P8_33 0x0D4 +#define BONE_P8_34 0x0CC +#define BONE_P8_35 0x0D0 +#define BONE_P8_36 0x0C8 + +#define BONE_P8_37 0x0C0 +#define BONE_P8_38 0x0C4 +#define BONE_P8_39 0x0B8 +#define BONE_P8_40 0x0BC + +#define BONE_P8_41 0x0B0 +#define BONE_P8_42 0x0B4 +#define BONE_P8_43 0x0A8 +#define BONE_P8_44 0x0AC + +#define BONE_P8_45 0x0A0 +#define BONE_P8_46 0x0A4 + +#define BONE_P9_11 0x070 +#define BONE_P9_12 0x078 + +#define BONE_P9_13 0x074 +#define BONE_P9_14 0x048 +#define BONE_P9_15 0x040 +#define BONE_P9_16 0x04C + +#define BONE_P9_17 0x15C +#define BONE_P9_18 0x158 +#define BONE_P9_19 0x17C +#define BONE_P9_20 0x178 + +#define BONE_P9_21 0x154 +#define BONE_P9_22 0x150 +#define BONE_P9_23 0x044 +#define BONE_P9_24 0x184 + +#define BONE_P9_25 0x1AC +#define BONE_P9_26 0x180 +#define BONE_P9_27 0x1A4 +#define BONE_P9_28 0x19C + +#define BONE_P9_29 0x194 +#define BONE_P9_30 0x198 +#define BONE_P9_31 0x190 + +/* Shared P21 of P11 */ +#define BONE_P9_41A 0x1B4 +#define BONE_P9_41B 0x1A8 + +/* Shared P22 of P11 */ +#define BONE_P9_42A 0x164 +#define BONE_P9_42B 0x1A0 + +#endif diff -Naur --no-dereference a/include/dt-bindings/clock/dra7.h b/include/dt-bindings/clock/dra7.h --- a/include/dt-bindings/clock/dra7.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/dt-bindings/clock/dra7.h 2022-01-06 12:45:53.834318188 -0500 @@ -84,6 +84,10 @@ #define DRA7_L3_MAIN_2_CLKCTRL DRA7_CLKCTRL_INDEX(0x20) #define DRA7_L3_INSTR_CLKCTRL DRA7_CLKCTRL_INDEX(0x28) +/* iva clocks */ +#define DRA7_IVA_CLKCTRL DRA7_CLKCTRL_INDEX(0x20) +#define DRA7_SL2IF_CLKCTRL DRA7_CLKCTRL_INDEX(0x28) + /* dss clocks */ #define DRA7_DSS_CORE_CLKCTRL DRA7_CLKCTRL_INDEX(0x20) #define DRA7_BB2D_CLKCTRL DRA7_CLKCTRL_INDEX(0x30) diff -Naur --no-dereference a/include/dt-bindings/clock/omap5.h b/include/dt-bindings/clock/omap5.h --- a/include/dt-bindings/clock/omap5.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/dt-bindings/clock/omap5.h 2022-01-06 12:45:53.834318188 -0500 @@ -32,6 +32,8 @@ /* l3main2 clocks */ #define OMAP5_L3_MAIN_2_CLKCTRL OMAP5_CLKCTRL_INDEX(0x20) +#define OMAP5_L3_MAIN_2_GPMC_CLKCTRL OMAP5_CLKCTRL_INDEX(0x28) +#define OMAP5_L3_MAIN_2_OCMC_RAM_CLKCTRL OMAP5_CLKCTRL_INDEX(0x30) /* ipu clocks */ #define OMAP5_MMU_IPU_CLKCTRL OMAP5_CLKCTRL_INDEX(0x20) diff -Naur --no-dereference a/include/dt-bindings/mux/ti-serdes.h b/include/dt-bindings/mux/ti-serdes.h --- a/include/dt-bindings/mux/ti-serdes.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/dt-bindings/mux/ti-serdes.h 2022-01-06 12:45:53.834318188 -0500 @@ -90,4 +90,31 @@ #define J7200_SERDES0_LANE3_USB 0x2 #define J7200_SERDES0_LANE3_IP4_UNUSED 0x3 +/* AM64 */ + +#define AM64_SERDES0_LANE0_PCIE0 0x0 +#define AM64_SERDES0_LANE0_USB 0x1 + +/* J721S2 */ + +#define J721S2_SERDES0_LANE0_EDP_LANE0 0x0 +#define J721S2_SERDES0_LANE0_PCIE1_LANE0 0x1 +#define J721S2_SERDES0_LANE0_IP3_UNUSED 0x2 +#define J721S2_SERDES0_LANE0_IP4_UNUSED 0x3 + +#define J721S2_SERDES0_LANE1_EDP_LANE1 0x0 +#define J721S2_SERDES0_LANE1_PCIE1_LANE1 0x1 +#define J721S2_SERDES0_LANE1_USB 0x2 +#define J721S2_SERDES0_LANE1_IP4_UNUSED 0x3 + +#define J721S2_SERDES0_LANE2_EDP_LANE2 0x0 +#define J721S2_SERDES0_LANE2_PCIE1_LANE2 0x1 +#define J721S2_SERDES0_LANE2_IP3_UNUSED 0x2 +#define J721S2_SERDES0_LANE2_IP4_UNUSED 0x3 + +#define J721S2_SERDES0_LANE3_EDP_LANE3 0x0 +#define J721S2_SERDES0_LANE3_PCIE1_LANE3 0x1 +#define J721S2_SERDES0_LANE3_USB 0x2 +#define J721S2_SERDES0_LANE3_IP4_UNUSED 0x3 + #endif /* _DT_BINDINGS_MUX_TI_SERDES */ diff -Naur --no-dereference a/include/dt-bindings/phy/phy-cadence.h b/include/dt-bindings/phy/phy-cadence.h --- a/include/dt-bindings/phy/phy-cadence.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/dt-bindings/phy/phy-cadence.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This header provides constants for Cadence SERDES. + */ + +#ifndef _DT_BINDINGS_CADENCE_SERDES_H +#define _DT_BINDINGS_CADENCE_SERDES_H + +/* Torrent */ +#define TORRENT_SERDES_NO_SSC 0 +#define TORRENT_SERDES_EXTERNAL_SSC 1 +#define TORRENT_SERDES_INTERNAL_SSC 2 + +#define CDNS_TORRENT_REFCLK_DRIVER 0 + +/* Sierra */ +#define CDNS_SIERRA_PLL_CMNLC 0 +#define CDNS_SIERRA_PLL_CMNLC1 1 + +#endif /* _DT_BINDINGS_CADENCE_SERDES_H */ diff -Naur --no-dereference a/include/dt-bindings/phy/phy-cadence-torrent.h b/include/dt-bindings/phy/phy-cadence-torrent.h --- a/include/dt-bindings/phy/phy-cadence-torrent.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/dt-bindings/phy/phy-cadence-torrent.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * This header provides constants for Cadence Torrent SERDES. - */ - -#ifndef _DT_BINDINGS_TORRENT_SERDES_H -#define _DT_BINDINGS_TORRENT_SERDES_H - -#define TORRENT_SERDES_NO_SSC 0 -#define TORRENT_SERDES_EXTERNAL_SSC 1 -#define TORRENT_SERDES_INTERNAL_SSC 2 - -#endif /* _DT_BINDINGS_TORRENT_SERDES_H */ diff -Naur --no-dereference a/include/dt-bindings/phy/phy-ti.h b/include/dt-bindings/phy/phy-ti.h --- a/include/dt-bindings/phy/phy-ti.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/dt-bindings/phy/phy-ti.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This header provides constants for TI SERDES. + */ + +#ifndef _DT_BINDINGS_TI_SERDES +#define _DT_BINDINGS_TI_SERDES + +/* Clock index for output clocks from WIZ */ + +/* MUX Clocks */ +#define TI_WIZ_PLL0_REFCLK 0 +#define TI_WIZ_PLL1_REFCLK 1 +#define TI_WIZ_REFCLK_DIG 2 + +/* Reserve index here for future additions */ + +/* MISC Clocks */ +#define TI_WIZ_PHY_EN_REFCLK 16 + +#endif /* _DT_BINDINGS_TI_SERDES */ diff -Naur --no-dereference a/include/dt-bindings/pinctrl/k3.h b/include/dt-bindings/pinctrl/k3.h --- a/include/dt-bindings/pinctrl/k3.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/dt-bindings/pinctrl/k3.h 2022-01-06 12:45:53.834318188 -0500 @@ -3,7 +3,7 @@ * This header provides constants for pinctrl bindings for TI's K3 SoC * family. * - * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _DT_BINDINGS_PINCTRL_TI_K3_H #define _DT_BINDINGS_PINCTRL_TI_K3_H @@ -35,4 +35,10 @@ #define J721E_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) #define J721E_WKUP_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) +#define AM64X_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) +#define AM64X_MCU_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) + +#define J721S2_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) +#define J721S2_WKUP_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) + #endif diff -Naur --no-dereference a/include/dt-bindings/pinctrl/omap.h b/include/dt-bindings/pinctrl/omap.h --- a/include/dt-bindings/pinctrl/omap.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/dt-bindings/pinctrl/omap.h 2022-01-06 12:45:53.834318188 -0500 @@ -64,8 +64,8 @@ #define OMAP3_WKUP_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x2a00) (val) #define DM814X_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) #define DM816X_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) -#define AM33XX_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) (0) -#define AM33XX_PADCONF(pa, conf, mux) OMAP_IOPAD_OFFSET((pa), 0x0800) (conf) (mux) +#define AM33XX_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) +#define AM33XX_PADCONF(pa, dir, mux) OMAP_IOPAD_OFFSET((pa), 0x0800) ((dir) | (mux)) /* * Macros to allow using the offset from the padconf physical address diff -Naur --no-dereference a/include/linux/crypto.h b/include/linux/crypto.h --- a/include/linux/crypto.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/crypto.h 2022-01-06 12:45:53.834318188 -0500 @@ -593,8 +593,15 @@ switch (err) { case -EINPROGRESS: case -EBUSY: - wait_for_completion(&wait->completion); + err = wait_for_completion_timeout(&wait->completion, + msecs_to_jiffies(1000)); reinit_completion(&wait->completion); + if (!err) { + pr_err("%s: timeout for %p\n", __func__, wait); + err = -ETIMEDOUT; + break; + } + err = wait->err; break; } diff -Naur --no-dereference a/include/linux/dma/k3-event-router.h b/include/linux/dma/k3-event-router.h --- a/include/linux/dma/k3-event-router.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/linux/dma/k3-event-router.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com + */ + +#ifndef K3_EVENT_ROUTER_ +#define K3_EVENT_ROUTER_ + +#include + +struct k3_event_route_data { + void *priv; + int (*set_event)(void *priv, u32 event); +}; + +#endif /* K3_EVENT_ROUTER_ */ diff -Naur --no-dereference a/include/linux/dma/k3-psil.h b/include/linux/dma/k3-psil.h --- a/include/linux/dma/k3-psil.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/dma/k3-psil.h 2022-01-06 12:45:53.834318188 -0500 @@ -42,27 +42,42 @@ /** * struct psil_endpoint_config - PSI-L Endpoint configuration * @ep_type: PSI-L endpoint type + * @channel_tpl: Desired throughput level for the channel * @pkt_mode: If set, the channel must be in Packet mode, otherwise in * TR mode * @notdpkt: TDCM must be suppressed on the TX channel * @needs_epib: Endpoint needs EPIB - * @psd_size: If set, PSdata is used by the endpoint - * @channel_tpl: Desired throughput level for the channel * @pdma_acc32: ACC32 must be enabled on the PDMA side * @pdma_burst: BURST must be enabled on the PDMA side + * @psd_size: If set, PSdata is used by the endpoint + * @mapped_channel_id: PKTDMA thread to channel mapping for mapped channels. + * The thread must be serviced by the specified channel if + * mapped_channel_id is >= 0 in case of PKTDMA + * @flow_start: PKDMA flow range start of mapped channel. Unmapped + * channels use flow_id == chan_id + * @flow_num: PKDMA flow count of mapped channel. Unmapped channels + * use flow_id == chan_id + * @default_flow_id: PKDMA default (r)flow index of mapped channel. + * Must be within the flow range of the mapped channel. */ struct psil_endpoint_config { enum psil_endpoint_type ep_type; + enum udma_tp_level channel_tpl; unsigned pkt_mode:1; unsigned notdpkt:1; unsigned needs_epib:1; - u32 psd_size; - enum udma_tp_level channel_tpl; - /* PDMA properties, valid for PSIL_EP_PDMA_* */ unsigned pdma_acc32:1; unsigned pdma_burst:1; + + u32 psd_size; + /* PKDMA mapped channel */ + s16 mapped_channel_id; + /* PKTDMA tflow and rflow ranges for mapped channel */ + u16 flow_start; + u16 flow_num; + s16 default_flow_id; }; int psil_set_new_ep_config(struct device *dev, const char *name, diff -Naur --no-dereference a/include/linux/dma/k3-udma-glue.h b/include/linux/dma/k3-udma-glue.h --- a/include/linux/dma/k3-udma-glue.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/dma/k3-udma-glue.h 2022-01-06 12:45:53.834318188 -0500 @@ -41,6 +41,12 @@ u32 k3_udma_glue_tx_get_hdesc_size(struct k3_udma_glue_tx_channel *tx_chn); u32 k3_udma_glue_tx_get_txcq_id(struct k3_udma_glue_tx_channel *tx_chn); int k3_udma_glue_tx_get_irq(struct k3_udma_glue_tx_channel *tx_chn); +struct device * + k3_udma_glue_tx_get_dma_device(struct k3_udma_glue_tx_channel *tx_chn); +void k3_udma_glue_tx_dma_to_cppi5_addr(struct k3_udma_glue_tx_channel *tx_chn, + dma_addr_t *addr); +void k3_udma_glue_tx_cppi5_to_dma_addr(struct k3_udma_glue_tx_channel *tx_chn, + dma_addr_t *addr); enum { K3_UDMA_GLUE_SRC_TAG_LO_KEEP = 0, @@ -130,5 +136,11 @@ u32 flow_idx); int k3_udma_glue_rx_flow_disable(struct k3_udma_glue_rx_channel *rx_chn, u32 flow_idx); +struct device * + k3_udma_glue_rx_get_dma_device(struct k3_udma_glue_rx_channel *rx_chn); +void k3_udma_glue_rx_dma_to_cppi5_addr(struct k3_udma_glue_rx_channel *rx_chn, + dma_addr_t *addr); +void k3_udma_glue_rx_cppi5_to_dma_addr(struct k3_udma_glue_rx_channel *rx_chn, + dma_addr_t *addr); #endif /* K3_UDMA_GLUE_H_ */ diff -Naur --no-dereference a/include/linux/dmaengine.h b/include/linux/dmaengine.h --- a/include/linux/dmaengine.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/dmaengine.h 2022-01-06 12:45:53.834318188 -0500 @@ -357,11 +357,14 @@ * @chan: driver channel device * @device: sysfs device * @dev_id: parent dma_device dev_id + * @chan_dma_dev: The channel is using custom/different dma-mapping + * compared to the parent dma_device */ struct dma_chan_dev { struct dma_chan *chan; struct device device; int dev_id; + bool chan_dma_dev; }; /** @@ -737,6 +740,8 @@ DMAENGINE_ALIGN_16_BYTES = 4, DMAENGINE_ALIGN_32_BYTES = 5, DMAENGINE_ALIGN_64_BYTES = 6, + DMAENGINE_ALIGN_128_BYTES = 7, + DMAENGINE_ALIGN_256_BYTES = 8, }; /** @@ -800,6 +805,7 @@ * by tx_status * @device_alloc_chan_resources: allocate resources and return the * number of allocated descriptors + * @device_router_config: optional callback for DMA router configuration * @device_free_chan_resources: release DMA channel's resources * @device_prep_dma_memcpy: prepares a memcpy operation * @device_prep_dma_xor: prepares a xor operation @@ -874,6 +880,7 @@ enum dma_residue_granularity residue_granularity; int (*device_alloc_chan_resources)(struct dma_chan *chan); + int (*device_router_config)(struct dma_chan *chan); void (*device_free_chan_resources)(struct dma_chan *chan); struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)( @@ -1611,4 +1618,13 @@ return "invalid"; } } + +static inline struct device *dmaengine_get_dma_device(struct dma_chan *chan) +{ + if (chan->dev->chan_dma_dev) + return &chan->dev->device; + + return chan->device->dev; +} + #endif /* DMAENGINE_H */ diff -Naur --no-dereference a/include/linux/ethtool.h b/include/linux/ethtool.h --- a/include/linux/ethtool.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/ethtool.h 2022-01-06 12:45:53.834318188 -0500 @@ -189,6 +189,12 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, const unsigned long *src); +bool convert_legacy_settings_to_link_ksettings(struct ethtool_link_ksettings *link_ksettings, + const struct ethtool_cmd *legacy_settings); + +bool convert_link_ksettings_to_legacy_settings(struct ethtool_cmd *legacy_settings, + const struct ethtool_link_ksettings *link_ksettings); + #define ETHTOOL_COALESCE_RX_USECS BIT(0) #define ETHTOOL_COALESCE_RX_MAX_FRAMES BIT(1) #define ETHTOOL_COALESCE_RX_USECS_IRQ BIT(2) diff -Naur --no-dereference a/include/linux/fs.h b/include/linux/fs.h --- a/include/linux/fs.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/fs.h 2022-01-06 12:45:53.834318188 -0500 @@ -1332,6 +1332,7 @@ /* can be called from interrupts */ extern void kill_fasync(struct fasync_struct **, int, int); +extern int setfl(int fd, struct file *filp, unsigned long arg); extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); extern int f_setown(struct file *filp, unsigned long arg, int force); extern void f_delown(struct file *filp); @@ -1843,6 +1844,7 @@ ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); + int (*setfl)(struct file *, unsigned long); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); @@ -2330,6 +2332,7 @@ extern void ihold(struct inode * inode); extern void iput(struct inode *); extern int generic_update_time(struct inode *, struct timespec64 *, int); +extern int update_time(struct inode *, struct timespec64 *, int); /* /sys/fs */ extern struct kobject *fs_kobj; @@ -2566,6 +2569,7 @@ } void emergency_thaw_all(void); +extern int __sync_filesystem(struct super_block *, int); extern int sync_filesystem(struct super_block *); extern const struct file_operations def_blk_fops; extern const struct file_operations def_chr_fops; diff -Naur --no-dereference a/include/linux/if_hsr.h b/include/linux/if_hsr.h --- a/include/linux/if_hsr.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/linux/if_hsr.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_IF_HSR_H_ +#define _LINUX_IF_HSR_H_ + +/* used to differentiate various protocols */ +enum hsr_version { + HSR_V0 = 0, + HSR_V1, + PRP_V1, +}; + +#if IS_ENABLED(CONFIG_HSR) +extern bool is_hsr_master(struct net_device *dev); +extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver); +#else +static inline bool is_hsr_master(struct net_device *dev) +{ + return false; +} +static inline int hsr_get_version(struct net_device *dev, + enum hsr_version *ver) +{ + return -EINVAL; +} +#endif /* CONFIG_HSR */ + +#endif /*_LINUX_IF_HSR_H_*/ diff -Naur --no-dereference a/include/linux/irqdomain.h b/include/linux/irqdomain.h --- a/include/linux/irqdomain.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/irqdomain.h 2022-01-06 12:45:53.834318188 -0500 @@ -387,6 +387,8 @@ extern unsigned int irq_create_mapping_affinity(struct irq_domain *host, irq_hw_number_t hwirq, const struct irq_affinity_desc *affinity); +void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, unsigned int count, + struct irq_fwspec *fwspec); extern unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec); extern void irq_dispose_mapping(unsigned int virq); diff -Naur --no-dereference a/include/linux/lockdep.h b/include/linux/lockdep.h --- a/include/linux/lockdep.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/lockdep.h 2022-01-06 12:45:53.834318188 -0500 @@ -248,6 +248,8 @@ return lock->key == key; } +struct lock_class *lockdep_hlock_class(struct held_lock *hlock); + /* * Acquire a lock. * @@ -384,6 +386,7 @@ #define lockdep_depth(tsk) (0) +#define lockdep_is_held(lock) (1) #define lockdep_is_held_type(l, r) (1) #define lockdep_assert_held(l) do { (void)(l); } while (0) diff -Naur --no-dereference a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h --- a/include/linux/mmc/sdio_ids.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/mmc/sdio_ids.h 2022-01-06 12:45:53.834318188 -0500 @@ -63,9 +63,10 @@ #define SDIO_DEVICE_ID_BROADCOM_4339 0x4339 #define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 -#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359 0x4355 +#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459 0x4355 #define SDIO_DEVICE_ID_BROADCOM_4356 0x4356 #define SDIO_DEVICE_ID_BROADCOM_4359 0x4359 +#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439 0xa9af #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 0x4373 #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 0xa804 #define SDIO_DEVICE_ID_BROADCOM_43143 0xa887 @@ -76,6 +77,12 @@ #define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6 #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf +#define SDIO_VENDOR_ID_CYPRESS 0x04b4 +#define SDIO_DEVICE_ID_CYPRESS_54590 0xbd3a +#define SDIO_DEVICE_ID_CYPRESS_54591 0xbd3b +#define SDIO_DEVICE_ID_CYPRESS_54594 0xbd3c +#define SDIO_DEVICE_ID_CYPRESS_43439 0xbd3d + #define SDIO_VENDOR_ID_MARVELL 0x02df #define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103 #define SDIO_DEVICE_ID_MARVELL_8688_WLAN 0x9104 diff -Naur --no-dereference a/include/linux/mm.h b/include/linux/mm.h --- a/include/linux/mm.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/mm.h 2022-01-06 12:45:53.834318188 -0500 @@ -1730,6 +1730,28 @@ unmap_mapping_range(mapping, holebegin, holelen, 0); } +extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); +extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], + int); +extern void vma_do_get_file(struct vm_area_struct *, const char[], int); +extern void vma_do_fput(struct vm_area_struct *, const char[], int); + +#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ + __LINE__) +#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ + __LINE__) +#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) +#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) + +#ifndef CONFIG_MMU +extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); +extern void vmr_do_fput(struct vm_region *, const char[], int); + +#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ + __LINE__) +#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) +#endif /* !CONFIG_MMU */ + extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, unsigned int gup_flags); extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, diff -Naur --no-dereference a/include/linux/mm_types.h b/include/linux/mm_types.h --- a/include/linux/mm_types.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/mm_types.h 2022-01-06 12:45:53.834318188 -0500 @@ -282,6 +282,7 @@ unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ + struct file *vm_prfile; /* the virtual backing file or NULL */ int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for @@ -361,6 +362,7 @@ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units */ struct file * vm_file; /* File we map to (can be NULL). */ + struct file *vm_prfile; /* shadow of vm_file */ void * vm_private_data; /* was vm_pte (shared mem) */ #ifdef CONFIG_SWAP diff -Naur --no-dereference a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h --- a/include/linux/mnt_namespace.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/mnt_namespace.h 2022-01-06 12:45:53.834318188 -0500 @@ -7,12 +7,15 @@ struct fs_struct; struct user_namespace; struct ns_common; +struct vfsmount; extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, struct user_namespace *, struct fs_struct *); extern void put_mnt_ns(struct mnt_namespace *ns); extern struct ns_common *from_mnt_ns(struct mnt_namespace *); +extern int is_current_mnt_ns(struct vfsmount *mnt); + extern const struct file_operations proc_mounts_operations; extern const struct file_operations proc_mountinfo_operations; extern const struct file_operations proc_mountstats_operations; diff -Naur --no-dereference a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h --- a/include/linux/mtd/spi-nor.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/mtd/spi-nor.h 2022-01-06 12:45:53.834318188 -0500 @@ -51,6 +51,8 @@ #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ #define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ #define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */ +#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */ +#define SPINOR_OP_SRST 0x99 /* Software Reset */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */ @@ -182,6 +184,7 @@ SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2), SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4), SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8), + SNOR_PROTO_8_8_8_DTR = SNOR_PROTO_DTR(8, 8, 8), }; static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto) @@ -228,7 +231,7 @@ * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly * (Slow) Read. */ -#define SNOR_HWCAPS_READ_MASK GENMASK(14, 0) +#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0) #define SNOR_HWCAPS_READ BIT(0) #define SNOR_HWCAPS_READ_FAST BIT(1) #define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2) @@ -245,11 +248,12 @@ #define SNOR_HWCAPS_READ_4_4_4 BIT(9) #define SNOR_HWCAPS_READ_1_4_4_DTR BIT(10) -#define SNOR_HWCAPS_READ_OCTAL GENMASK(14, 11) +#define SNOR_HWCAPS_READ_OCTAL GENMASK(15, 11) #define SNOR_HWCAPS_READ_1_1_8 BIT(11) #define SNOR_HWCAPS_READ_1_8_8 BIT(12) #define SNOR_HWCAPS_READ_8_8_8 BIT(13) #define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14) +#define SNOR_HWCAPS_READ_8_8_8_DTR BIT(15) /* * Page Program capabilities. @@ -260,18 +264,19 @@ * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory * implements such commands. */ -#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16) -#define SNOR_HWCAPS_PP BIT(16) +#define SNOR_HWCAPS_PP_MASK GENMASK(23, 16) +#define SNOR_HWCAPS_PP BIT(16) -#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) -#define SNOR_HWCAPS_PP_1_1_4 BIT(17) -#define SNOR_HWCAPS_PP_1_4_4 BIT(18) -#define SNOR_HWCAPS_PP_4_4_4 BIT(19) - -#define SNOR_HWCAPS_PP_OCTAL GENMASK(22, 20) -#define SNOR_HWCAPS_PP_1_1_8 BIT(20) -#define SNOR_HWCAPS_PP_1_8_8 BIT(21) -#define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) +#define SNOR_HWCAPS_PP_1_1_4 BIT(17) +#define SNOR_HWCAPS_PP_1_4_4 BIT(18) +#define SNOR_HWCAPS_PP_4_4_4 BIT(19) + +#define SNOR_HWCAPS_PP_OCTAL GENMASK(23, 20) +#define SNOR_HWCAPS_PP_1_1_8 BIT(20) +#define SNOR_HWCAPS_PP_1_8_8 BIT(21) +#define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_8_8_8_DTR BIT(23) #define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \ SNOR_HWCAPS_READ_4_4_4 | \ @@ -279,10 +284,14 @@ SNOR_HWCAPS_PP_4_4_4 | \ SNOR_HWCAPS_PP_8_8_8) +#define SNOR_HWCAPS_X_X_X_DTR (SNOR_HWCAPS_READ_8_8_8_DTR | \ + SNOR_HWCAPS_PP_8_8_8_DTR) + #define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \ SNOR_HWCAPS_READ_1_2_2_DTR | \ SNOR_HWCAPS_READ_1_4_4_DTR | \ - SNOR_HWCAPS_READ_1_8_8_DTR) + SNOR_HWCAPS_READ_1_8_8_DTR | \ + SNOR_HWCAPS_READ_8_8_8_DTR) #define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \ SNOR_HWCAPS_PP_MASK) @@ -318,6 +327,22 @@ int (*erase)(struct spi_nor *nor, loff_t offs); }; +/** + * enum spi_nor_cmd_ext - describes the command opcode extension in DTR mode + * @SPI_NOR_EXT_NONE: no extension. This is the default, and is used in Legacy + * SPI mode + * @SPI_NOR_EXT_REPEAT: the extension is same as the opcode + * @SPI_NOR_EXT_INVERT: the extension is the bitwise inverse of the opcode + * @SPI_NOR_EXT_HEX: the extension is any hex value. The command and opcode + * combine to form a 16-bit opcode. + */ +enum spi_nor_cmd_ext { + SPI_NOR_EXT_NONE = 0, + SPI_NOR_EXT_REPEAT, + SPI_NOR_EXT_INVERT, + SPI_NOR_EXT_HEX, +}; + /* * Forward declarations that are used internally by the core and manufacturer * drivers. @@ -345,6 +370,7 @@ * @program_opcode: the program opcode * @sst_write_second: used by the SST write operation * @flags: flag options for the current SPI NOR (SNOR_F_*) + * @cmd_ext_type: the command opcode extension type for DTR mode. * @read_proto: the SPI protocol for read operations * @write_proto: the SPI protocol for write operations * @reg_proto: the SPI protocol for read_reg/write_reg/erase operations @@ -376,6 +402,7 @@ enum spi_nor_protocol reg_proto; bool sst_write_second; u32 flags; + enum spi_nor_cmd_ext cmd_ext_type; const struct spi_nor_controller_ops *controller_ops; diff -Naur --no-dereference a/include/linux/netdev_features.h b/include/linux/netdev_features.h --- a/include/linux/netdev_features.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/netdev_features.h 2022-01-06 12:45:53.834318188 -0500 @@ -85,6 +85,11 @@ NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */ + NETIF_F_HW_HSR_TAG_INS_BIT, /* Offload HSR tag insertion */ + NETIF_F_HW_HSR_TAG_RM_BIT, /* Offload HSR tag removal */ + NETIF_F_HW_HSR_FWD_BIT, /* Offload HSR forwarding */ + NETIF_F_HW_HSR_DUP_BIT, /* Offload HSR duplication */ + /* * Add your fresh new feature above and remember to update * netdev_features_strings[] in net/ethtool/common.c and maybe @@ -157,6 +162,10 @@ #define NETIF_F_GRO_FRAGLIST __NETIF_F(GRO_FRAGLIST) #define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST) #define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC) +#define NETIF_F_HW_HSR_TAG_INS __NETIF_F(HW_HSR_TAG_INS) +#define NETIF_F_HW_HSR_TAG_RM __NETIF_F(HW_HSR_TAG_RM) +#define NETIF_F_HW_HSR_FWD __NETIF_F(HW_HSR_FWD) +#define NETIF_F_HW_HSR_DUP __NETIF_F(HW_HSR_DUP) /* Finds the next feature with the highest number of the range of start till 0. */ diff -Naur --no-dereference a/include/linux/netdevice.h b/include/linux/netdevice.h --- a/include/linux/netdevice.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/netdevice.h 2022-01-06 12:45:53.834318188 -0500 @@ -1922,6 +1922,9 @@ #endif const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; +#if IS_ENABLED(CONFIG_HSR) + const struct lredev_ops *lredev_ops; +#endif #ifdef CONFIG_NET_L3_MASTER_DEV const struct l3mdev_ops *l3mdev_ops; #endif diff -Naur --no-dereference a/include/linux/pci-epc.h b/include/linux/pci-epc.h --- a/include/linux/pci-epc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/pci-epc.h 2022-01-06 12:45:53.834318188 -0500 @@ -13,6 +13,12 @@ struct pci_epc; +enum pci_epc_interface_type { + UNKNOWN_INTERFACE = -1, + PRIMARY_INTERFACE, + SECONDARY_INTERFACE, +}; + enum pci_epc_irq_type { PCI_EPC_IRQ_UNKNOWN, PCI_EPC_IRQ_LEGACY, @@ -20,6 +26,19 @@ PCI_EPC_IRQ_MSIX, }; +static inline const char * +pci_epc_interface_string(enum pci_epc_interface_type type) +{ + switch (type) { + case PRIMARY_INTERFACE: + return "primary"; + case SECONDARY_INTERFACE: + return "secondary"; + default: + return "UNKNOWN interface"; + } +} + /** * struct pci_epc_ops - set of function pointers for performing EPC operations * @write_header: ops to populate configuration space header @@ -36,32 +55,38 @@ * @get_msix: ops to get the number of MSI-X interrupts allocated by the RC * from the MSI-X capability register * @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt + * @map_msi_irq: ops to map physical address to MSI address and return MSI data * @start: ops to start the PCI link * @stop: ops to stop the PCI link * @owner: the module owner containing the ops */ struct pci_epc_ops { - int (*write_header)(struct pci_epc *epc, u8 func_no, + int (*write_header)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr); - int (*set_bar)(struct pci_epc *epc, u8 func_no, + int (*set_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); - void (*clear_bar)(struct pci_epc *epc, u8 func_no, + void (*clear_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); - int (*map_addr)(struct pci_epc *epc, u8 func_no, + int (*map_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t addr, u64 pci_addr, size_t size); - void (*unmap_addr)(struct pci_epc *epc, u8 func_no, + void (*unmap_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t addr); - int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts); - int (*get_msi)(struct pci_epc *epc, u8 func_no); - int (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts, - enum pci_barno, u32 offset); - int (*get_msix)(struct pci_epc *epc, u8 func_no); - int (*raise_irq)(struct pci_epc *epc, u8 func_no, + int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u8 interrupts); + int (*get_msi)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); + int (*set_msix)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u16 interrupts, enum pci_barno, u32 offset); + int (*get_msix)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); + int (*raise_irq)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, enum pci_epc_irq_type type, u16 interrupt_num); + int (*map_msi_irq)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + phys_addr_t phys_addr, u8 interrupt_num, + u32 entry_size, u32 *msi_data, + u32 *msi_addr_offset); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); const struct pci_epc_features* (*get_features)(struct pci_epc *epc, - u8 func_no); + u8 func_no, u8 vfunc_no); struct module *owner; }; @@ -103,6 +128,8 @@ * single window. * @num_windows: number of windows supported by device * @max_functions: max number of functions that can be configured in this EPC + * @max_vfs: Array indicating the maximum number of virtual functions that can + * be associated with each physical function * @group: configfs group representing the PCI EPC device * @lock: mutex to protect pci_epc ops * @function_num_map: bitmap to manage physical function number @@ -116,6 +143,7 @@ struct pci_epc_mem *mem; unsigned int num_windows; u8 max_functions; + u8 *max_vfs; struct config_group *group; /* mutex to protect against concurrent access of EP controller */ struct mutex lock; @@ -175,32 +203,38 @@ struct module *owner); void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc); void pci_epc_destroy(struct pci_epc *epc); -int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf); +int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, + enum pci_epc_interface_type type); void pci_epc_linkup(struct pci_epc *epc); void pci_epc_init_notify(struct pci_epc *epc); -void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); -int pci_epc_write_header(struct pci_epc *epc, u8 func_no, +void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, + enum pci_epc_interface_type type); +int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr); -int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); -int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, +int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr, u64 pci_addr, size_t size); -void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, +void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr); -int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts); -int pci_epc_get_msi(struct pci_epc *epc, u8 func_no); -int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts, - enum pci_barno, u32 offset); -int pci_epc_get_msix(struct pci_epc *epc, u8 func_no); -int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, +int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u8 interrupts); +int pci_epc_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no); +int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u16 interrupts, enum pci_barno, u32 offset); +int pci_epc_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no); +int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + phys_addr_t phys_addr, u8 interrupt_num, + u32 entry_size, u32 *msi_data, u32 *msi_addr_offset); +int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, enum pci_epc_irq_type type, u16 interrupt_num); int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, - u8 func_no); + u8 func_no, u8 vfunc_no); enum pci_barno pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features); enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features diff -Naur --no-dereference a/include/linux/pci-epf.h b/include/linux/pci-epf.h --- a/include/linux/pci-epf.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/pci-epf.h 2022-01-06 12:45:53.834318188 -0500 @@ -9,11 +9,13 @@ #ifndef __LINUX_PCI_EPF_H #define __LINUX_PCI_EPF_H +#include #include #include #include struct pci_epf; +enum pci_epc_interface_type; enum pci_notify_event { CORE_INIT, @@ -61,10 +63,13 @@ * @bind: ops to perform when a EPC device has been bound to EPF device * @unbind: ops to perform when a binding has been lost between a EPC device * and EPF device + * @add_cfs: ops to initialize function specific configfs attributes */ struct pci_epf_ops { int (*bind)(struct pci_epf *epf); void (*unbind)(struct pci_epf *epf); + struct config_group *(*add_cfs)(struct pci_epf *epf, + struct config_group *group); }; /** @@ -113,12 +118,24 @@ * @header: represents standard configuration header * @bar: represents the BAR of EPF device * @msi_interrupts: number of MSI interrupts required by this function - * @func_no: unique function number within this endpoint device + * @func_no: unique (physical) function number within this endpoint device + * @vfunc_no: unique virtual function number within a physical function * @epc: the EPC device to which this EPF device is bound + * @epf_pf: the physical EPF device to which this virtual EPF device is bound * @driver: the EPF driver to which this EPF device is bound * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc * @nb: notifier block to notify EPF of any EPC events (like linkup) * @lock: mutex to protect pci_epf_ops + * @sec_epc: the secondary EPC device to which this EPF device is bound + * @sec_epc_list: to add pci_epf as list of PCI endpoint functions to secondary + * EPC device + * @sec_epc_bar: represents the BAR of EPF device associated with secondary EPC + * @sec_epc_func_no: unique (physical) function number within the secondary EPC + * @group: configfs group associated with the EPF device + * @is_bound: indicates if bind notification to function driver has been invoked + * @is_vf: true - virtual function, false - physical function + * @vfunction_num_map: bitmap to manage virtual function number + * @pci_vepf: list of virtual endpoint functions associated with this function */ struct pci_epf { struct device dev; @@ -128,13 +145,26 @@ u8 msi_interrupts; u16 msix_interrupts; u8 func_no; + u8 vfunc_no; struct pci_epc *epc; + struct pci_epf *epf_pf; struct pci_epf_driver *driver; struct list_head list; struct notifier_block nb; /* mutex to protect against concurrent access of pci_epf_ops */ struct mutex lock; + + /* Below members are to attach secondary EPC to an endpoint function */ + struct pci_epc *sec_epc; + struct list_head sec_epc_list; + struct pci_epf_bar sec_epc_bar[6]; + u8 sec_epc_func_no; + struct config_group *group; + unsigned int is_bound; + unsigned int is_vf; + unsigned long vfunction_num_map; + struct list_head pci_vepf; }; /** @@ -165,16 +195,19 @@ return dev_get_drvdata(&epf->dev); } -const struct pci_epf_device_id * -pci_epf_match_device(const struct pci_epf_device_id *id, struct pci_epf *epf); struct pci_epf *pci_epf_create(const char *name); void pci_epf_destroy(struct pci_epf *epf); int __pci_epf_register_driver(struct pci_epf_driver *driver, struct module *owner); void pci_epf_unregister_driver(struct pci_epf_driver *driver); void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, - size_t align); -void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar); + size_t align, enum pci_epc_interface_type type); +void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar, + enum pci_epc_interface_type type); int pci_epf_bind(struct pci_epf *epf); void pci_epf_unbind(struct pci_epf *epf); +struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, + struct config_group *group); +int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf); +void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf); #endif /* __LINUX_PCI_EPF_H */ diff -Naur --no-dereference a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/pci_ids.h 2022-01-06 12:45:53.834318188 -0500 @@ -880,6 +880,7 @@ #define PCI_DEVICE_ID_TI_X620 0xac8d #define PCI_DEVICE_ID_TI_X420 0xac8e #define PCI_DEVICE_ID_TI_XX20_FM 0xac8f +#define PCI_DEVICE_ID_TI_J721E 0xb00d #define PCI_DEVICE_ID_TI_DRA74x 0xb500 #define PCI_DEVICE_ID_TI_DRA72x 0xb501 diff -Naur --no-dereference a/include/linux/phy/phy.h b/include/linux/phy/phy.h --- a/include/linux/phy/phy.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/phy/phy.h 2022-01-06 12:45:53.834318188 -0500 @@ -115,7 +115,7 @@ /** * struct phy_attrs - represents phy attributes * @bus_width: Data path width implemented by PHY - * @max_link_rate: Maximum link rate supported by PHY (in Mbps) + * @max_link_rate: Maximum link rate supported by PHY (units to be decided by producer and consumer) * @mode: PHY mode */ struct phy_attrs { diff -Naur --no-dereference a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h --- a/include/linux/phy/phy-mipi-dphy.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/phy/phy-mipi-dphy.h 2022-01-06 12:45:53.834318188 -0500 @@ -7,6 +7,19 @@ #define __PHY_MIPI_DPHY_H_ /** + * enum phy_mipi_dphy_submode - MIPI D-PHY sub-mode + * + * A MIPI D-PHY can be used to transmit or receive data. + * Since some controllers can support both, the direction to enable is specified + * with the PHY sub-mode. Transmit is assumed by default with phy_set_mode. + */ + +enum phy_mipi_dphy_submode { + PHY_MIPI_DPHY_SUBMODE_TX = 0, + PHY_MIPI_DPHY_SUBMODE_RX, +}; + +/** * struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set * * This structure is used to represent the configuration state of a diff -Naur --no-dereference a/include/linux/pruss_driver.h b/include/linux/pruss_driver.h --- a/include/linux/pruss_driver.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/pruss_driver.h 2022-01-06 12:45:53.834318188 -0500 @@ -2,43 +2,25 @@ /* * PRU-ICSS sub-system specific definitions * - * Copyright (C) 2014-2020 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2014-2021 Texas Instruments Incorporated - https://www.ti.com/ * Suman Anna */ #ifndef _PRUSS_DRIVER_H_ #define _PRUSS_DRIVER_H_ +#include +#include #include -/* - * enum pruss_mem - PRUSS memory range identifiers - */ -enum pruss_mem { - PRUSS_MEM_DRAM0 = 0, - PRUSS_MEM_DRAM1, - PRUSS_MEM_SHRD_RAM2, - PRUSS_MEM_MAX, -}; - -/** - * struct pruss_mem_region - PRUSS memory region structure - * @va: kernel virtual address of the PRUSS memory region - * @pa: physical (bus) address of the PRUSS memory region - * @size: size of the PRUSS memory region - */ -struct pruss_mem_region { - void __iomem *va; - phys_addr_t pa; - size_t size; -}; - /** * struct pruss - PRUSS parent structure * @dev: pruss device pointer * @cfg_base: base iomap for CFG region * @cfg_regmap: regmap for config region * @mem_regions: data for each of the PRUSS memory regions + * @mem_in_use: to indicate if memory resource is in use + * @lock: mutex to serialize access to resources * @core_clk_mux: clk handle for PRUSS CORE_CLK_MUX * @iep_clk_mux: clk handle for PRUSS IEP_CLK_MUX */ @@ -47,8 +29,54 @@ void __iomem *cfg_base; struct regmap *cfg_regmap; struct pruss_mem_region mem_regions[PRUSS_MEM_MAX]; + struct pruss_mem_region *mem_in_use[PRUSS_MEM_MAX]; + struct mutex lock; /* PRU resource lock */ struct clk *core_clk_mux; struct clk *iep_clk_mux; }; +/** + * pruss_cfg_get_gpmux() - get the current GPMUX value for a PRU device + * @pruss: pruss instance + * @pru_id: PRU identifier (0-1) + * @mux: pointer to store the current mux value into + * + * Return: 0 on success, or an error code otherwise + */ +static inline int pruss_cfg_get_gpmux(struct pruss *pruss, + enum pruss_pru_id pru_id, u8 *mux) +{ + int ret = 0; + u32 val; + + if (pru_id < 0 || pru_id >= PRUSS_NUM_PRUS) + return -EINVAL; + + ret = pruss_cfg_read(pruss, PRUSS_CFG_GPCFG(pru_id), &val); + if (!ret) + *mux = (u8)((val & PRUSS_GPCFG_PRU_MUX_SEL_MASK) >> + PRUSS_GPCFG_PRU_MUX_SEL_SHIFT); + return ret; +} + +/** + * pruss_cfg_set_gpmux() - set the GPMUX value for a PRU device + * @pruss: pruss instance + * @pru_id: PRU identifier (0-1) + * @mux: new mux value for PRU + * + * Return: 0 on success, or an error code otherwise + */ +static inline int pruss_cfg_set_gpmux(struct pruss *pruss, + enum pruss_pru_id pru_id, u8 mux) +{ + if (mux >= PRUSS_GP_MUX_SEL_MAX || + pru_id < 0 || pru_id >= PRUSS_NUM_PRUS) + return -EINVAL; + + return pruss_cfg_update(pruss, PRUSS_CFG_GPCFG(pru_id), + PRUSS_GPCFG_PRU_MUX_SEL_MASK, + (u32)mux << PRUSS_GPCFG_PRU_MUX_SEL_SHIFT); +} + #endif /* _PRUSS_DRIVER_H_ */ diff -Naur --no-dereference a/include/linux/pruss.h b/include/linux/pruss.h --- a/include/linux/pruss.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/linux/pruss.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/** + * PRU-ICSS Subsystem user interfaces + * + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + * Suman Anna + * Tero Kristo + */ + +#ifndef __LINUX_PRUSS_H +#define __LINUX_PRUSS_H + +#include +#include +#include +#include + +#define PRU_RPROC_DRVNAME "pru-rproc" + +/* + * PRU_ICSS_CFG registers + * SYSCFG, ISRP, ISP, IESP, IECP, SCRP applicable on AMxxxx devices only + */ +#define PRUSS_CFG_REVID 0x00 +#define PRUSS_CFG_SYSCFG 0x04 +#define PRUSS_CFG_GPCFG(x) (0x08 + (x) * 4) +#define PRUSS_CFG_CGR 0x10 +#define PRUSS_CFG_ISRP 0x14 +#define PRUSS_CFG_ISP 0x18 +#define PRUSS_CFG_IESP 0x1C +#define PRUSS_CFG_IECP 0x20 +#define PRUSS_CFG_SCRP 0x24 +#define PRUSS_CFG_PMAO 0x28 +#define PRUSS_CFG_MII_RT 0x2C +#define PRUSS_CFG_IEPCLK 0x30 +#define PRUSS_CFG_SPP 0x34 +#define PRUSS_CFG_PIN_MX 0x40 + +/* PRUSS_GPCFG register bits */ +#define PRUSS_GPCFG_PRU_GPO_SH_SEL BIT(25) + +#define PRUSS_GPCFG_PRU_DIV1_SHIFT 20 +#define PRUSS_GPCFG_PRU_DIV1_MASK GENMASK(24, 20) + +#define PRUSS_GPCFG_PRU_DIV0_SHIFT 15 +#define PRUSS_GPCFG_PRU_DIV0_MASK GENMASK(15, 19) + +#define PRUSS_GPCFG_PRU_GPO_MODE BIT(14) +#define PRUSS_GPCFG_PRU_GPO_MODE_DIRECT 0 +#define PRUSS_GPCFG_PRU_GPO_MODE_SERIAL BIT(14) + +#define PRUSS_GPCFG_PRU_GPI_SB BIT(13) + +#define PRUSS_GPCFG_PRU_GPI_DIV1_SHIFT 8 +#define PRUSS_GPCFG_PRU_GPI_DIV1_MASK GENMASK(12, 8) + +#define PRUSS_GPCFG_PRU_GPI_DIV0_SHIFT 3 +#define PRUSS_GPCFG_PRU_GPI_DIV0_MASK GENMASK(7, 3) + +#define PRUSS_GPCFG_PRU_GPI_CLK_MODE_POSITIVE 0 +#define PRUSS_GPCFG_PRU_GPI_CLK_MODE_NEGATIVE BIT(2) +#define PRUSS_GPCFG_PRU_GPI_CLK_MODE BIT(2) + +#define PRUSS_GPCFG_PRU_GPI_MODE_MASK GENMASK(1, 0) +#define PRUSS_GPCFG_PRU_GPI_MODE_SHIFT 0 + +#define PRUSS_GPCFG_PRU_MUX_SEL_SHIFT 26 +#define PRUSS_GPCFG_PRU_MUX_SEL_MASK GENMASK(29, 26) + +/* PRUSS_MII_RT register bits */ +#define PRUSS_MII_RT_EVENT_EN BIT(0) + +/* PRUSS_SPP register bits */ +#define PRUSS_SPP_PRU1_PAD_HP_EN BIT(0) +#define PRUSS_SPP_XFER_SHIFT_EN BIT(1) +#define PRUSS_SPP_XFR_BYTE_SHIFT_EN BIT(2) +#define PRUSS_SPP_RTU_XFR_SHIFT_EN BIT(3) + +/* + * enum pruss_gp_mux_sel - PRUSS GPI/O Mux modes for the + * PRUSS_GPCFG0/1 registers + * + * NOTE: The below defines are the most common values, but there + * are some exceptions like on 66AK2G, where the RESERVED and MII2 + * values are interchanged. Also, this bit-field does not exist on + * AM335x SoCs + */ +enum pruss_gp_mux_sel { + PRUSS_GP_MUX_SEL_GP = 0, + PRUSS_GP_MUX_SEL_ENDAT, + PRUSS_GP_MUX_SEL_RESERVED, + PRUSS_GP_MUX_SEL_SD, + PRUSS_GP_MUX_SEL_MII2, + PRUSS_GP_MUX_SEL_MAX, +}; + +/* + * enum pruss_gpi_mode - PRUSS GPI configuration modes, used + * to program the PRUSS_GPCFG0/1 registers + */ +enum pruss_gpi_mode { + PRUSS_GPI_MODE_DIRECT = 0, + PRUSS_GPI_MODE_PARALLEL, + PRUSS_GPI_MODE_28BIT_SHIFT, + PRUSS_GPI_MODE_MII, +}; + +/* + * enum pruss_pru_id - PRU core identifiers + */ +enum pruss_pru_id { + PRUSS_PRU0 = 0, + PRUSS_PRU1, + PRUSS_NUM_PRUS, +}; + +/* + * enum pru_ctable_idx - Configurable Constant table index identifiers + */ +enum pru_ctable_idx { + PRU_C24 = 0, + PRU_C25, + PRU_C26, + PRU_C27, + PRU_C28, + PRU_C29, + PRU_C30, + PRU_C31, +}; + +/* + * enum pruss_mem - PRUSS memory range identifiers + */ +enum pruss_mem { + PRUSS_MEM_DRAM0 = 0, + PRUSS_MEM_DRAM1, + PRUSS_MEM_SHRD_RAM2, + PRUSS_MEM_MAX, +}; + +/** + * struct pruss_mem_region - PRUSS memory region structure + * @va: kernel virtual address of the PRUSS memory region + * @pa: physical (bus) address of the PRUSS memory region + * @size: size of the PRUSS memory region + */ +struct pruss_mem_region { + void __iomem *va; + phys_addr_t pa; + size_t size; +}; + +struct device_node; +struct rproc; +struct pruss; + +#if IS_ENABLED(CONFIG_TI_PRUSS) + +struct pruss *pruss_get(struct rproc *rproc); +void pruss_put(struct pruss *pruss); +int pruss_request_mem_region(struct pruss *pruss, enum pruss_mem mem_id, + struct pruss_mem_region *region); +int pruss_release_mem_region(struct pruss *pruss, + struct pruss_mem_region *region); +int pruss_cfg_read(struct pruss *pruss, unsigned int reg, unsigned int *val); +int pruss_cfg_update(struct pruss *pruss, unsigned int reg, + unsigned int mask, unsigned int val); +int pruss_cfg_ocp_master_ports(struct pruss *pruss, bool enable); + +#else + +static inline struct pruss *pruss_get(struct rproc *rproc) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void pruss_put(struct pruss *pruss) { } + +static inline int pruss_request_mem_region(struct pruss *pruss, + enum pruss_mem mem_id, + struct pruss_mem_region *region) +{ + return -EOPNOTSUPP; +} + +static inline int pruss_release_mem_region(struct pruss *pruss, + struct pruss_mem_region *region) +{ + return -EOPNOTSUPP; +} + +static inline int pruss_cfg_read(struct pruss *pruss, unsigned int reg, + unsigned int *val) +{ + return -EOPNOTSUPP; +} + +static inline int pruss_cfg_update(struct pruss *pruss, unsigned int reg, + unsigned int mask, unsigned int val) +{ + return -EOPNOTSUPP; +} + +static inline int pruss_cfg_ocp_master_ports(struct pruss *pruss, bool enable) +{ + return -EOPNOTSUPP; +} + +#endif /* CONFIG_TI_PRUSS */ + +#if IS_ENABLED(CONFIG_PRU_REMOTEPROC) + +struct rproc *pru_rproc_get(struct device_node *np, int index, + enum pruss_pru_id *pru_id); +void pru_rproc_put(struct rproc *rproc); +int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr); + +#else + +static inline struct rproc * +pru_rproc_get(struct device_node *np, int index, enum pruss_pru_id *pru_id) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void pru_rproc_put(struct rproc *rproc) { } + +static inline int pru_rproc_set_ctable(struct rproc *rproc, + enum pru_ctable_idx c, u32 addr) +{ + return -EOPNOTSUPP; +} + +#endif /* CONFIG_PRU_REMOTEPROC */ + +static inline bool is_pru_rproc(struct device *dev) +{ + const char *drv_name = dev_driver_string(dev); + + if (strncmp(drv_name, PRU_RPROC_DRVNAME, sizeof(PRU_RPROC_DRVNAME))) + return false; + + return true; +} + +/** + * pruss_cfg_gpimode() - set the GPI mode of the PRU + * @pruss: the pruss instance handle + * @pru_id: id of the PRU core within the PRUSS + * @mode: GPI mode to set + * + * Sets the GPI mode for a given PRU by programming the + * corresponding PRUSS_CFG_GPCFGx register + * + * Return: 0 on success, or an error code otherwise + */ +static inline int pruss_cfg_gpimode(struct pruss *pruss, + enum pruss_pru_id pru_id, + enum pruss_gpi_mode mode) +{ + if (pru_id < 0 || pru_id >= PRUSS_NUM_PRUS) + return -EINVAL; + + return pruss_cfg_update(pruss, PRUSS_CFG_GPCFG(pru_id), + PRUSS_GPCFG_PRU_GPI_MODE_MASK, + mode << PRUSS_GPCFG_PRU_GPI_MODE_SHIFT); +} + +/** + * pruss_cfg_miirt_enable() - Enable/disable MII RT Events + * @pruss: the pruss instance + * @enable: enable/disable + * + * Enable/disable the MII RT Events for the PRUSS. + * + * Return: 0 on success, or an error code otherwise + */ +static inline int pruss_cfg_miirt_enable(struct pruss *pruss, bool enable) +{ + u32 set = enable ? PRUSS_MII_RT_EVENT_EN : 0; + + return pruss_cfg_update(pruss, PRUSS_CFG_MII_RT, + PRUSS_MII_RT_EVENT_EN, set); +} + +/** + * pruss_cfg_xfr_enable() - Enable/disable XIN XOUT shift functionality + * @pruss: the pruss instance + * @enable: enable/disable + * + * Return: 0 on success, or an error code otherwise + */ +static inline int pruss_cfg_xfr_enable(struct pruss *pruss, bool enable) +{ + u32 set = enable ? PRUSS_SPP_XFER_SHIFT_EN : 0; + + return pruss_cfg_update(pruss, PRUSS_CFG_SPP, + PRUSS_SPP_XFER_SHIFT_EN, set); +} + +#endif /* __LINUX_PRUSS_H */ diff -Naur --no-dereference a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h --- a/include/linux/ptp_classify.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/ptp_classify.h 2022-01-06 12:45:53.834318188 -0500 @@ -31,6 +31,11 @@ #define PTP_CLASS_V2_VLAN (PTP_CLASS_V2 | PTP_CLASS_VLAN) #define PTP_CLASS_L4 (PTP_CLASS_IPV4 | PTP_CLASS_IPV6) +#define PTP_MSGTYPE_SYNC 0x0 +#define PTP_MSGTYPE_DELAY_REQ 0x1 +#define PTP_MSGTYPE_PDELAY_REQ 0x2 +#define PTP_MSGTYPE_PDELAY_RESP 0x3 + #define PTP_EV_PORT 319 #define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */ @@ -140,7 +145,7 @@ /* The return is meaningless. The stub function would not be * executed since no available header from ptp_parse_header. */ - return 0; + return PTP_MSGTYPE_SYNC; } #endif #endif /* _PTP_CLASSIFY_H_ */ diff -Naur --no-dereference a/include/linux/remoteproc.h b/include/linux/remoteproc.h --- a/include/linux/remoteproc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/remoteproc.h 2022-01-06 12:45:53.834318188 -0500 @@ -361,6 +361,7 @@ * @start: power on the device and boot it * @stop: power off the device * @attach: attach to a device that his already powered up + * @detach: detach from a device, leaving it powered up * @kick: kick a virtqueue (virtqueue id given as a parameter) * @da_to_va: optional platform hook to perform address translations * @parse_fw: parse firmware to extract information (e.g. resource table) @@ -368,7 +369,9 @@ * RSC_HANDLED if resource was handled, RSC_IGNORED if not handled and a * negative value on error * @load_rsc_table: load resource table from firmware image - * @find_loaded_rsc_table: find the loaded resouce table + * @find_loaded_rsc_table: find the loaded resource table from firmware image + * @get_loaded_rsc_table: get resource table installed in memory + * by external entity * @load: load firmware to memory, where the remote processor * expects to find it * @sanity_check: sanity check the fw image @@ -382,6 +385,7 @@ int (*start)(struct rproc *rproc); int (*stop)(struct rproc *rproc); int (*attach)(struct rproc *rproc); + int (*detach)(struct rproc *rproc); void (*kick)(struct rproc *rproc, int vqid); void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len); int (*parse_fw)(struct rproc *rproc, const struct firmware *fw); @@ -389,6 +393,8 @@ int offset, int avail); struct resource_table *(*find_loaded_rsc_table)( struct rproc *rproc, const struct firmware *fw); + struct resource_table *(*get_loaded_rsc_table)( + struct rproc *rproc, size_t *size); int (*load)(struct rproc *rproc, const struct firmware *fw); int (*sanity_check)(struct rproc *rproc, const struct firmware *fw); u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw); @@ -403,6 +409,8 @@ * @RPROC_RUNNING: device is up and running * @RPROC_CRASHED: device has crashed; need to start recovery * @RPROC_DELETED: device is deleted + * @RPROC_ATTACHED: device has been booted by another entity and the core + * has attached to it * @RPROC_DETACHED: device has been booted by another entity and waiting * for the core to attach to it * @RPROC_LAST: just keep this one at the end @@ -419,8 +427,9 @@ RPROC_RUNNING = 2, RPROC_CRASHED = 3, RPROC_DELETED = 4, - RPROC_DETACHED = 5, - RPROC_LAST = 6, + RPROC_ATTACHED = 5, + RPROC_DETACHED = 6, + RPROC_LAST = 7, }; /** @@ -503,15 +512,20 @@ * @recovery_disabled: flag that state if recovery was disabled * @max_notifyid: largest allocated notify id. * @table_ptr: pointer to the resource table in effect + * @clean_table: copy of the resource table without modifications. Used + * when a remote processor is attached or detached from the core * @cached_table: copy of the resource table * @table_sz: size of @cached_table * @has_iommu: flag to indicate if remote processor is behind an MMU * @auto_boot: flag to indicate if remote processor should be auto-started - * @autonomous: true if an external entity has booted the remote processor + * @deny_sysfs_ops: flag to not permit sysfs store operations + * @skip_firmware_load: flag to skip requesting and loading the firmware * @dump_segments: list of segments in the firmware * @nb_vdev: number of vdev currently handled by rproc * @char_dev: character device of the rproc * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release + * @detach_on_shutdown: flag to indicate if remoteproc cannot be shutdown in + * attached state and _only_ support detach */ struct rproc { struct list_head node; @@ -540,17 +554,20 @@ bool recovery_disabled; int max_notifyid; struct resource_table *table_ptr; + struct resource_table *clean_table; struct resource_table *cached_table; size_t table_sz; bool has_iommu; bool auto_boot; - bool autonomous; + bool deny_sysfs_ops; + bool skip_firmware_load; struct list_head dump_segments; int nb_vdev; u8 elf_class; u16 elf_machine; struct cdev cdev; bool cdev_put_on_release; + bool detach_on_shutdown; }; /** @@ -653,7 +670,10 @@ int rproc_boot(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc); +int rproc_detach(struct rproc *rproc); +int rproc_set_firmware(struct rproc *rproc, const char *fw_name); void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); +void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len); int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size); int rproc_coredump_add_custom_segment(struct rproc *rproc, dma_addr_t da, size_t size, @@ -663,6 +683,8 @@ size_t size), void *priv); int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine); +int rproc_get_id(struct rproc *rproc); +int rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da); static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) { diff -Naur --no-dereference a/include/linux/rpmsg.h b/include/linux/rpmsg.h --- a/include/linux/rpmsg.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/rpmsg.h 2022-01-06 12:45:53.834318188 -0500 @@ -28,11 +28,13 @@ /** * struct rpmsg_channel_info - channel info representation * @name: name of service + * @desc: description of service * @src: local address * @dst: destination address */ struct rpmsg_channel_info { char name[RPMSG_NAME_SIZE]; + char desc[RPMSG_NAME_SIZE]; u32 src; u32 dst; }; @@ -42,6 +44,7 @@ * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) * @driver_override: driver name to force a match + * @desc: description of remote service * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel @@ -51,6 +54,7 @@ struct device dev; struct rpmsg_device_id id; char *driver_override; + char desc[RPMSG_NAME_SIZE]; u32 src; u32 dst; struct rpmsg_endpoint *ept; @@ -135,6 +139,8 @@ __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); +ssize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept); + #else static inline int register_rpmsg_device(struct rpmsg_device *dev) @@ -242,6 +248,14 @@ return 0; } +static inline ssize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return -ENXIO; +} + #endif /* IS_ENABLED(CONFIG_RPMSG) */ /* use a macro to avoid include chaining to get THIS_MODULE */ diff -Naur --no-dereference a/include/linux/rpmsg-remotedev/rpmsg-remotedev.h b/include/linux/rpmsg-remotedev/rpmsg-remotedev.h --- a/include/linux/rpmsg-remotedev/rpmsg-remotedev.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/linux/rpmsg-remotedev/rpmsg-remotedev.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Subhajit Paul + */ + +#ifndef __RPMSG_REMOTEDEV_H__ +#define __RPMSG_REMOTEDEV_H__ + +#include + +struct rpmsg_remotedev; + +/* Defines for demo device */ +#define REMOTEDEV_DEMO_MSG_LEN (128) + +/* Defines for display device */ +#define RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES (3) +#define RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS (8) +#define RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES (8) +#define RPMSG_REMOTEDEV_DISPLAY_MAX_FORMATS (32) +#define RPMSG_REMOTEDEV_DISPLAY_MAX_ZORDERS (8) + +/* Struct definitions for display device */ +struct rpmsg_remotedev_display_buffer { + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t num_planes; + dma_addr_t planes[RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES]; + uint32_t pitches[RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES]; + struct rpmsg_remotedev *rdev; + void *priv; +}; + +struct rpmsg_remotedev_display_pipe_update { + uint32_t pipe_id; + bool enabled; + uint32_t dst_w; + uint32_t dst_h; + uint32_t dst_x; + uint32_t dst_y; + struct rpmsg_remotedev_display_buffer *buffer; +}; + +struct rpmsg_remotedev_display_commit { + uint32_t disp_id; + uint32_t num_pipe_updates; + struct rpmsg_remotedev_display_pipe_update pipes[RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES]; + struct rpmsg_remotedev *rdev; + void *priv; +}; + +struct rpmsg_remotedev_display_pipe { + uint32_t pipe_id; + bool can_scale; + bool can_mod_win; + uint32_t fixed_win_x; + uint32_t fixed_win_y; + uint32_t fixed_win_w; + uint32_t fixed_win_h; + uint32_t initial_zorder; + uint32_t num_formats; + uint32_t formats[RPMSG_REMOTEDEV_DISPLAY_MAX_FORMATS]; + uint32_t num_allowed_zorders; + uint32_t allowed_zorders[RPMSG_REMOTEDEV_DISPLAY_MAX_ZORDERS]; +}; + +struct rpmsg_remotedev_display_disp { + uint32_t disp_id; + uint32_t width; + uint32_t height; + uint32_t refresh; + uint32_t num_pipes; + struct rpmsg_remotedev_display_pipe pipes[RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES]; +}; + +struct rpmsg_remotedev_display_resinfo { + uint32_t num_disps; + struct rpmsg_remotedev_display_disp disps[RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS]; +}; + +/* callbacks for demo device */ +struct rpmsg_remotedev_demo_cb { + void (*s2c_message)(void *data, ssize_t len, void *cb_data); +}; + +/* callbacks for display device */ +struct rpmsg_remotedev_display_cb { + void (*commit_done)(struct rpmsg_remotedev_display_commit *commit, void *cb_data); + void (*buffer_done)(struct rpmsg_remotedev_display_buffer *buffer, void *cb_data); +}; + +/* requests for demo device */ +struct rpmsg_remotedev_demo_ops { + int (*get_data)(struct rpmsg_remotedev *rdev, void *data, ssize_t len); + int (*ping)(struct rpmsg_remotedev *rdev, void *ping_data, ssize_t ping_len); + int (*c2s_message)(struct rpmsg_remotedev *rdev, void *c2s_msg_data, ssize_t len); +}; + +/* requests for display device */ +struct rpmsg_remotedev_display_ops { + bool (*ready)(struct rpmsg_remotedev *rdev); + int (*get_res_info)(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_resinfo *res); + int (*commit)(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_commit *commit); +}; + +#define RPMSG_RDEV_ETHSWITCH_CPSW_PRIORITY_NUM (8) + +/* features bits */ +#define RPMSG_KDRV_ETHSWITCH_FEATURE_TXCSUM BIT(0) +#define RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS BIT(1) +#define RPMSG_KDRV_ETHSWITCH_FEATURE_MAC_ONLY BIT(2) +#define RPMSG_KDRV_ETHSWITCH_FEATURE_MC_FILTER BIT(3) + +struct rpmsg_rdev_eth_switch_attach_info { + /* MTU of rx packet */ + u32 rx_mtu; + /* MTU of tx packet per priority */ + u32 tx_mtu[RPMSG_RDEV_ETHSWITCH_CPSW_PRIORITY_NUM]; + /* Supported Features mask */ + u32 features; + u32 mac_only_port; +}; + +struct rpmsg_rdev_eth_switch_attach_ext_info { + /* MTU of rx packet */ + u32 rx_mtu; + /* MTU of tx packet per priority */ + u32 tx_mtu[RPMSG_RDEV_ETHSWITCH_CPSW_PRIORITY_NUM]; + /* Supported Features mask */ + u32 features; + u32 flow_idx; + u32 tx_cpsw_psil_dst_id; + u8 mac_addr[ETH_ALEN]; + u32 mac_only_port; +}; + +struct rpmsg_rdev_eth_switch_tx_info { + /* Tx PSIL Peer destination thread id */ + u32 tx_cpsw_psil_dst_id; +}; + +struct rpmsg_rdev_eth_switch_rx_info { + /* Allocated flow's index */ + u32 flow_idx; +}; + +struct rpmsg_remotedev_eth_switch_ops { + void (*get_fw_ver)(struct rpmsg_remotedev *rdev, + char *buf, size_t size); + int (*attach)(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_attach_info *attach_info); + int (*attach_ext)(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_attach_ext_info *attach_ext_info); + int (*detach)(struct rpmsg_remotedev *rdev); + int (*get_tx_info)(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_tx_info *info); + int (*get_rx_info)(struct rpmsg_remotedev *rdev, + struct rpmsg_rdev_eth_switch_rx_info *info); + int (*get_mac)(struct rpmsg_remotedev *rdev, void *mac_addr); + int (*register_mac)(struct rpmsg_remotedev *rdev, + void *mac_addr, u32 flow_idx_offset); + int (*unregister_mac)(struct rpmsg_remotedev *rdev, + void *mac_addr, u32 flow_idx_offset); + int (*register_ipv4)(struct rpmsg_remotedev *rdev, + void *mac_addr, __be32 ipv4); + int (*unregister_ipv4)(struct rpmsg_remotedev *rdev, __be32 ipv4); + int (*ping)(struct rpmsg_remotedev *rdev, const u8 *data, int size); + int (*read_reg)(struct rpmsg_remotedev *rdev, u32 reg_addr, u32 *val); + int (*dbg_dump_stats)(struct rpmsg_remotedev *rdev); + int (*set_promisc_mode)(struct rpmsg_remotedev *rdev, u32 enable); + int (*filter_add_mc)(struct rpmsg_remotedev *rdev, + const void *mac_addr, u16 vlan_id, u32 flow_idx); + int (*filter_del_mc)(struct rpmsg_remotedev *rdev, + const void *mac_addr, u16 vlan_id, u32 flow_idx); +}; + +enum rpmsg_remotedev_type { + RPMSG_REMOTEDEV_DEMO_DEVICE, + RPMSG_REMOTEDEV_DISPLAY_DEVICE, + RPMSG_REMOTEDEV_ETH_SWITCH_DEVICE, +}; + +struct rpmsg_remotedev { + enum rpmsg_remotedev_type type; + union { + struct { + struct rpmsg_remotedev_demo_ops *ops; + struct rpmsg_remotedev_demo_cb *cb_ops; + } demo; + struct { + struct rpmsg_remotedev_display_ops *ops; + struct rpmsg_remotedev_display_cb *cb_ops; + } display; + + struct { + struct rpmsg_remotedev_eth_switch_ops *ops; + } eth_switch; + } device; + void *cb_data; +}; + +#if IS_REACHABLE(CONFIG_RPMSG_KDRV) +extern struct rpmsg_remotedev *rpmsg_remotedev_get_named_device(const char *device_name); +extern void rpmsg_remotedev_put_device(struct rpmsg_remotedev *rdev); +#else +static inline struct rpmsg_remotedev * __maybe_unused rpmsg_remotedev_get_named_device(const char *device_name) +{ + return NULL; +} + +static inline void __maybe_unused rpmsg_remotedev_put_device(struct rpmsg_remotedev *rdev) +{ +} +#endif + +#endif diff -Naur --no-dereference a/include/linux/skbuff.h b/include/linux/skbuff.h --- a/include/linux/skbuff.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/skbuff.h 2022-01-06 12:45:53.834318188 -0500 @@ -506,6 +506,27 @@ struct msghdr *msg, int len, struct ubuf_info *uarg); +/* Bit fields of redundant info io_port */ +#define PTP_MSG_IN (0x3 << 6) +#define PTP_EVT_OUT (0x2 << 6) +#define DIRECTED_TX (0x1 << 6) +#define PORT_B BIT(1) +#define PORT_A BIT(0) + +struct skb_redundant_info { + __u8 io_port; /* tx/rx port of the skb */ + __u8 pathid; /* pathid in tag */ + __u16 ethertype; /* ethertype in tag */ + __u16 lsdu_size; /* lsdu size in tag */ + __u16 seqnr; /* seqnr in tag */ +}; + +#define REDINFO_T(skb) (skb_redinfo(skb)->io_port & (0x3 << 6)) +#define REDINFO_PORTS(skb) (skb_redinfo(skb)->io_port & 0x3) +#define REDINFO_PATHID(skb) (skb_redinfo(skb)->pathid) +#define REDINFO_SEQNR(skb) (skb_redinfo(skb)->seqnr) +#define REDINFO_LSDU_SIZE(skb) (skb_redinfo(skb)->lsdu_size) + /* This data is invariant across clones and lives at * the end of the header data, ie. at skb->end. */ @@ -520,6 +541,8 @@ struct sk_buff *frag_list; struct skb_shared_hwtstamps hwtstamps; unsigned int gso_type; + struct skb_shared_hwtstamps red_hwtstamps; + struct skb_redundant_info redinfo; u32 tskey; /* @@ -1512,6 +1535,17 @@ skb_mark_not_on_list(skb); } +static inline struct skb_redundant_info *skb_redinfo(struct sk_buff *skb) +{ + return &skb_shinfo(skb)->redinfo; +} + +static inline struct skb_shared_hwtstamps * +skb_redinfo_hwtstamps(struct sk_buff *skb) +{ + return &skb_shinfo(skb)->red_hwtstamps; +} + /** * skb_queue_empty - check if a queue is empty * @list: queue head diff -Naur --no-dereference a/include/linux/soc/ti/k3-ringacc.h b/include/linux/soc/ti/k3-ringacc.h --- a/include/linux/soc/ti/k3-ringacc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/soc/ti/k3-ringacc.h 2022-01-06 12:45:53.834318188 -0500 @@ -67,6 +67,10 @@ * few times. It's usable when the same ring is used as Free Host PD ring * for different flows, for example. * Note: Locking should be done by consumer if required + * @dma_dev: Master device which is using and accessing to the ring + * memory when the mode is K3_RINGACC_RING_MODE_RING. Memory allocations + * should be done using this device. + * @asel: Address Space Select value for physical addresses */ struct k3_ring_cfg { u32 size; @@ -74,6 +78,9 @@ enum k3_ring_mode mode; #define K3_RINGACC_RING_SHARED BIT(1) u32 flags; + + struct device *dma_dev; + u32 asel; }; #define K3_RINGACC_RING_ID_ANY (-1) @@ -245,4 +252,19 @@ u32 k3_ringacc_get_tisci_dev_id(struct k3_ring *ring); +/* DMA ring support */ +struct ti_sci_handle; + +/** + * struct struct k3_ringacc_init_data - Initialization data for DMA rings + */ +struct k3_ringacc_init_data { + const struct ti_sci_handle *tisci; + u32 tisci_dev_id; + u32 num_rings; +}; + +struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev, + struct k3_ringacc_init_data *data); + #endif /* __SOC_TI_K3_RINGACC_API_H_ */ diff -Naur --no-dereference a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h --- a/include/linux/soc/ti/ti_sci_protocol.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/soc/ti/ti_sci_protocol.h 2022-01-06 12:45:53.834318188 -0500 @@ -196,6 +196,22 @@ }; /** + * struct ti_sci_resource_desc - Description of TI SCI resource instance range. + * @start: Start index of the first resource range. + * @num: Number of resources in the first range. + * @start_sec: Start index of the second resource range. + * @num_sec: Number of resources in the second range. + * @res_map: Bitmap to manage the allocation of these resources. + */ +struct ti_sci_resource_desc { + u16 start; + u16 num; + u16 start_sec; + u16 num_sec; + unsigned long *res_map; +}; + +/** * struct ti_sci_rm_core_ops - Resource management core operations * @get_range: Get a range of resources belonging to ti sci host. * @get_rage_from_shost: Get a range of resources belonging to @@ -209,15 +225,15 @@ * - dev_id: TISCI device ID. * - subtype: Resource assignment subtype that is being requested * from the given device. - * - range_start: Start index of the resource range - * - range_end: Number of resources in the range + * - desc: Pointer to ti_sci_resource_desc to be updated with the resource + * range start index and number of resources */ struct ti_sci_rm_core_ops { int (*get_range)(const struct ti_sci_handle *handle, u32 dev_id, - u8 subtype, u16 *range_start, u16 *range_num); + u8 subtype, struct ti_sci_resource_desc *desc); int (*get_range_from_shost)(const struct ti_sci_handle *handle, u32 dev_id, u8 subtype, u8 s_host, - u16 *range_start, u16 *range_num); + struct ti_sci_resource_desc *desc); }; #define TI_SCI_RESASG_SUBTYPE_IR_OUTPUT 0 @@ -259,30 +275,46 @@ #define TI_SCI_MSG_VALUE_RM_RING_SIZE_VALID BIT(4) /* RA config.order_id parameter is valid for RM ring configure TISCI message */ #define TI_SCI_MSG_VALUE_RM_RING_ORDER_ID_VALID BIT(5) +/* RA config.virtid parameter is valid for RM ring configure TISCI message */ +#define TI_SCI_MSG_VALUE_RM_RING_VIRTID_VALID BIT(6) +/* RA config.asel parameter is valid for RM ring configure TISCI message */ +#define TI_SCI_MSG_VALUE_RM_RING_ASEL_VALID BIT(7) #define TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER \ (TI_SCI_MSG_VALUE_RM_RING_ADDR_LO_VALID | \ TI_SCI_MSG_VALUE_RM_RING_ADDR_HI_VALID | \ TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID | \ TI_SCI_MSG_VALUE_RM_RING_MODE_VALID | \ - TI_SCI_MSG_VALUE_RM_RING_SIZE_VALID) + TI_SCI_MSG_VALUE_RM_RING_SIZE_VALID | \ + TI_SCI_MSG_VALUE_RM_RING_ASEL_VALID) + +/** + * struct ti_sci_msg_rm_ring_cfg - Ring configuration + * + * Parameters for Navigator Subsystem ring configuration + * See @ti_sci_msg_rm_ring_cfg_req + */ +struct ti_sci_msg_rm_ring_cfg { + u32 valid_params; + u16 nav_id; + u16 index; + u32 addr_lo; + u32 addr_hi; + u32 count; + u8 mode; + u8 size; + u8 order_id; + u16 virtid; + u8 asel; +}; /** * struct ti_sci_rm_ringacc_ops - Ring Accelerator Management operations - * @config: configure the SoC Navigator Subsystem Ring Accelerator ring - * @get_config: get the SoC Navigator Subsystem Ring Accelerator ring - * configuration + * @set_cfg: configure the SoC Navigator Subsystem Ring Accelerator ring */ struct ti_sci_rm_ringacc_ops { - int (*config)(const struct ti_sci_handle *handle, - u32 valid_params, u16 nav_id, u16 index, - u32 addr_lo, u32 addr_hi, u32 count, u8 mode, - u8 size, u8 order_id - ); - int (*get_config)(const struct ti_sci_handle *handle, - u32 nav_id, u32 index, u8 *mode, - u32 *addr_lo, u32 *addr_hi, u32 *count, - u8 *size, u8 *order_id); + int (*set_cfg)(const struct ti_sci_handle *handle, + const struct ti_sci_msg_rm_ring_cfg *params); }; /** @@ -320,6 +352,9 @@ #define TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES 2 #define TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES 3 +#define TI_SCI_RM_BCDMA_EXTENDED_CH_TYPE_TCHAN 0 +#define TI_SCI_RM_BCDMA_EXTENDED_CH_TYPE_BCHAN 1 + /* UDMAP TX/RX channel valid_params common declarations */ #define TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID BIT(0) #define TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID BIT(1) @@ -345,6 +380,8 @@ #define TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID BIT(11) #define TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_CREDIT_COUNT_VALID BIT(12) #define TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FDEPTH_VALID BIT(13) +#define TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID BIT(15) +#define TI_SCI_MSG_VALUE_RM_UDMAP_CH_EXTENDED_CH_TYPE_VALID BIT(16) u16 nav_id; u16 index; u8 tx_pause_on_err; @@ -362,6 +399,8 @@ u16 fdepth; u8 tx_sched_priority; u8 tx_burst_size; + u8 tx_tdtype; + u8 extended_ch_type; }; /** @@ -521,18 +560,6 @@ #define TI_SCI_RESOURCE_NULL 0xffff /** - * struct ti_sci_resource_desc - Description of TI SCI resource instance range. - * @start: Start index of the resource. - * @num: Number of resources. - * @res_map: Bitmap to manage the allocation of these resources. - */ -struct ti_sci_resource_desc { - u16 start; - u16 num; - unsigned long *res_map; -}; - -/** * struct ti_sci_resource - Structure representing a resource assigned * to a device. * @sets: Number of sets available from this resource type diff -Naur --no-dereference a/include/linux/socket.h b/include/linux/socket.h --- a/include/linux/socket.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/socket.h 2022-01-06 12:45:53.834318188 -0500 @@ -223,8 +223,9 @@ * reuses AF_INET address family */ #define AF_XDP 44 /* XDP sockets */ +#define AF_RPMSG 45 /* Remote-processor messaging */ -#define AF_MAX 45 /* For now.. */ +#define AF_MAX 46 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -274,6 +275,7 @@ #define PF_QIPCRTR AF_QIPCRTR #define PF_SMC AF_SMC #define PF_XDP AF_XDP +#define PF_RPMSG AF_RPMSG #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ @@ -360,6 +362,7 @@ #define SOL_KCM 281 #define SOL_TLS 282 #define SOL_XDP 283 +#define SOL_RPMSG 284 /* IPX options */ #define IPX_TYPE 1 diff -Naur --no-dereference a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h --- a/include/linux/spi/spi-mem.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/spi/spi-mem.h 2022-01-06 12:45:53.834318188 -0500 @@ -250,6 +250,12 @@ * the currently mapped area), and the caller of * spi_mem_dirmap_write() is responsible for calling it again in * this case. + * @do_calibration: perform calibration needed for high SPI clock speed + * operation. Should be called after the SPI memory device has + * been completely initialized. The op passed should contain + * a template for the read operation used for the device so + * the controller can decide what type of calibration is + * required for this type of read. * * This interface should be implemented by SPI controllers providing an * high-level interface to execute SPI memory operation, which is usually the @@ -274,6 +280,8 @@ u64 offs, size_t len, void *buf); ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, const void *buf); + void (*do_calibration)(struct spi_mem *mem, + const struct spi_mem_op *op); }; /** @@ -311,6 +319,9 @@ bool spi_mem_default_supports_op(struct spi_mem *mem, const struct spi_mem_op *op); +bool spi_mem_dtr_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op); + #else static inline int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, @@ -334,9 +345,16 @@ return false; } +static inline +bool spi_mem_dtr_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + return false; +} #endif /* CONFIG_SPI_MEM */ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op); +int spi_mem_do_calibration(struct spi_mem *mem, const struct spi_mem_op *op); bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op); diff -Naur --no-dereference a/include/linux/splice.h b/include/linux/splice.h --- a/include/linux/splice.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/splice.h 2022-01-06 12:45:53.834318188 -0500 @@ -93,4 +93,10 @@ extern const struct pipe_buf_operations page_cache_pipe_buf_ops; extern const struct pipe_buf_operations default_pipe_buf_ops; + +extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); +extern long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); #endif diff -Naur --no-dereference a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h --- a/include/linux/wkup_m3_ipc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/linux/wkup_m3_ipc.h 2022-01-06 12:45:53.834318188 -0500 @@ -33,7 +33,13 @@ int mem_type; unsigned long resume_addr; + int vtt_conf; + int isolation_conf; int state; + u32 halt; + + unsigned long volt_scale_offsets; + const char *sd_fw_name; struct completion sync_complete; struct mbox_client mbox_client; @@ -41,6 +47,7 @@ struct wkup_m3_ipc_ops *ops; int is_rtc_only; + struct dentry *dbg_path; }; struct wkup_m3_wakeup_src { @@ -48,6 +55,12 @@ char src[10]; }; +struct wkup_m3_scale_data_header { + u16 magic; + u8 sleep_offset; + u8 wake_offset; +} __packed; + struct wkup_m3_ipc_ops { void (*set_mem_type)(struct wkup_m3_ipc *m3_ipc, int mem_type); void (*set_resume_address)(struct wkup_m3_ipc *m3_ipc, void *addr); diff -Naur --no-dereference a/include/media/media-entity.h b/include/media/media-entity.h --- a/include/media/media-entity.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/media/media-entity.h 2022-01-06 12:45:53.834318188 -0500 @@ -78,16 +78,16 @@ * struct media_graph - Media graph traversal state * * @stack: Graph traversal stack; the stack contains information - * on the path the media entities to be walked and the - * links through which they were reached. - * @stack.entity: pointer to &struct media_entity at the graph. + * on the media pads to be walked and the links through + * which they were reached. + * @stack.pad: pointer to &struct media_pad at the graph. * @stack.link: pointer to &struct list_head. * @ent_enum: Visited entities * @top: The top of the stack */ struct media_graph { struct { - struct media_entity *entity; + struct media_pad *pad; struct list_head *link; } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; @@ -180,15 +180,25 @@ * * @graph_obj: Embedded structure containing the media object common data * @entity: Entity this pad belongs to + * @pipe: Pipeline this pad belongs to + * @stream_count: Stream count for the pad * @index: Pad index in the entity pads array, numbered from 0 to n * @sig_type: Type of the signal inside a media pad * @flags: Pad flags, as defined in * :ref:`include/uapi/linux/media.h ` * (seek for ``MEDIA_PAD_FL_*``) + * + * .. note:: + * + * @stream_count reference count must never be negative, but is a signed + * integer on purpose: a simple ``WARN_ON(<0)`` check can be used to + * detect reference count bugs that would make it negative. */ struct media_pad { struct media_gobj graph_obj; /* must be first field in struct */ struct media_entity *entity; + struct media_pipeline *pipe; + int stream_count; u16 index; enum media_pad_signal_type sig_type; unsigned long flags; @@ -205,6 +215,10 @@ * @link_validate: Return whether a link is valid from the entity point of * view. The media_pipeline_start() function * validates all links by calling this operation. Optional. + * @has_route: Return whether a route exists inside the entity between + * two given pads. Pads are passed to the operation ordered + * by index. Optional: If the operation isn't implemented + * all pads will be considered as connected. * * .. note:: * @@ -218,6 +232,8 @@ const struct media_pad *local, const struct media_pad *remote, u32 flags); int (*link_validate)(struct media_link *link); + bool (*has_route)(struct media_entity *entity, unsigned int pad0, + unsigned int pad1); }; /** @@ -267,9 +283,7 @@ * @pads: Pads array with the size defined by @num_pads. * @links: List of data links. * @ops: Entity operations. - * @stream_count: Stream count for the entity. * @use_count: Use count for the entity. - * @pipe: Pipeline this entity belongs to. * @info: Union with devnode information. Kept just for backward * compatibility. * @info.dev: Contains device major and minor info. @@ -282,10 +296,9 @@ * * .. note:: * - * @stream_count and @use_count reference counts must never be - * negative, but are signed integers on purpose: a simple ``WARN_ON(<0)`` - * check can be used to detect reference count bugs that would make them - * negative. + * @use_count reference count must never be negative, but is a signed + * integer on purpose: a simple ``WARN_ON(<0)`` check can be used to + * detect reference count bugs that would make it negative. */ struct media_entity { struct media_gobj graph_obj; /* must be first field in struct */ @@ -304,11 +317,8 @@ const struct media_entity_operations *ops; - int stream_count; int use_count; - struct media_pipeline *pipe; - union { struct { u32 major; @@ -890,6 +900,49 @@ struct media_graph *graph, struct media_device *mdev); /** + * media_entity_has_route - Check if two entity pads are connected internally + * + * @entity: The entity + * @pad0: The first pad index + * @pad1: The second pad index + * + * This function can be used to check whether two pads of an entity are + * connected internally in the entity. + * + * The caller must hold entity->graph_obj.mdev->mutex. + * + * Return: true if the pads are connected internally and false otherwise. + */ +bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1); + +/** + * __media_entity_next_routed_pad - Get next pad connected to @root + * + * @root: The root pad to which the iterated pads have a route + * @iter: The iterator pad + * + * Get next pad which has a route to @root. + */ +struct media_pad *__media_entity_next_routed_pad(struct media_pad *root, + struct media_pad *iter); + +/** + * media_entity_for_each_routed_pad - Iterate over entity pads connected by routes + * + * @root: The root pad to which the iterated pads have a route + * @iter: The iterator pad + * + * Iterate over all pads of an entity which have an internal route to @root pad. + * The iteration will include the @root pad itself. + */ +#define media_entity_for_each_routed_pad(root, iter) \ + for (iter = __media_entity_next_routed_pad(root, \ + (root)->entity->pads); \ + iter != NULL; \ + iter = __media_entity_next_routed_pad(root, iter + 1)) + +/** * media_graph_walk_cleanup - Release resources used by graph walk. * * @graph: Media graph structure that will be used to walk the graph @@ -897,22 +950,20 @@ void media_graph_walk_cleanup(struct media_graph *graph); /** - * media_graph_walk_start - Start walking the media graph at a - * given entity + * media_graph_walk_start - Start walking the media graph at a given pad * * @graph: Media graph structure that will be used to walk the graph - * @entity: Starting entity + * @pad: Starting pad * * Before using this function, media_graph_walk_init() must be * used to allocate resources used for walking the graph. This * function initializes the graph traversal structure to walk the - * entities graph starting at the given entity. The traversal + * entities graph starting at the given pad. The traversal * structure must not be modified by the caller during graph * traversal. After the graph walk, the resources must be released * using media_graph_walk_cleanup(). */ -void media_graph_walk_start(struct media_graph *graph, - struct media_entity *entity); +void media_graph_walk_start(struct media_graph *graph, struct media_pad *pad); /** * media_graph_walk_next - Get the next entity in the graph @@ -923,60 +974,61 @@ * The graph structure must have been previously initialized with a call to * media_graph_walk_start(). * - * Return: returns the next entity in the graph or %NULL if the whole graph - * have been traversed. + * Return: returns the next entity in the graph, identified by the pad through + * which it has been reached. If the whole graph has been traversed, return + * %NULL. */ -struct media_entity *media_graph_walk_next(struct media_graph *graph); +struct media_pad *media_graph_walk_next(struct media_graph *graph); /** * media_pipeline_start - Mark a pipeline as streaming - * @entity: Starting entity - * @pipe: Media pipeline to be assigned to all entities in the pipeline. + * @pad: Starting pad + * @pipe: Media pipeline to be assigned to all pads in the pipeline. * - * Mark all entities connected to a given entity through enabled links, either - * directly or indirectly, as streaming. The given pipeline object is assigned - * to every entity in the pipeline and stored in the media_entity pipe field. + * Mark all pads connected to a given pad through enabled routes or links, + * either directly or indirectly, as streaming. The given pipeline object is + * assigned to every pad in the pipeline and stored in the media_pad pipe + * field. * * Calls to this function can be nested, in which case the same number of * media_pipeline_stop() calls will be required to stop streaming. The * pipeline pointer must be identical for all nested calls to * media_pipeline_start(). */ -__must_check int media_pipeline_start(struct media_entity *entity, +__must_check int media_pipeline_start(struct media_pad *pad, struct media_pipeline *pipe); /** * __media_pipeline_start - Mark a pipeline as streaming * - * @entity: Starting entity - * @pipe: Media pipeline to be assigned to all entities in the pipeline. + * @pad: Starting pad + * @pipe: Media pipeline to be assigned to all pads in the pipeline. * * ..note:: This is the non-locking version of media_pipeline_start() */ -__must_check int __media_pipeline_start(struct media_entity *entity, +__must_check int __media_pipeline_start(struct media_pad *pad, struct media_pipeline *pipe); /** * media_pipeline_stop - Mark a pipeline as not streaming - * @entity: Starting entity + * @pad: Starting pad * - * Mark all entities connected to a given entity through enabled links, either - * directly or indirectly, as not streaming. The media_entity pipe field is - * reset to %NULL. + * Mark all pads connected to a given pad through enabled routes or links, + * either directly or indirectly, as not streaming. * * If multiple calls to media_pipeline_start() have been made, the same * number of calls to this function are required to mark the pipeline as not - * streaming. + * streaming and reset the media_pad pipe field to %NULL. */ -void media_pipeline_stop(struct media_entity *entity); +void media_pipeline_stop(struct media_pad *pad); /** * __media_pipeline_stop - Mark a pipeline as not streaming * - * @entity: Starting entity + * @pad: Starting pad * * .. note:: This is the non-locking version of media_pipeline_stop() */ -void __media_pipeline_stop(struct media_entity *entity); +void __media_pipeline_stop(struct media_pad *pad); /** * media_devnode_create() - creates and initializes a device node interface @@ -1103,3 +1155,15 @@ (entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD) #endif + +/** + * media_entity_for_each_pad - Iterate on all pads in an entity + * @entity: The entity the pads belong to + * @iter: The iterator pad + * + * Iterate on all pads in a media entity. + */ +#define media_entity_for_each_pad(entity, iter) \ + for (iter = (entity)->pads; \ + iter < &(entity)->pads[(entity)->num_pads]; \ + ++iter) diff -Naur --no-dereference a/include/media/v4l2-common.h b/include/media/v4l2-common.h --- a/include/media/v4l2-common.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/media/v4l2-common.h 2022-01-06 12:45:53.834318188 -0500 @@ -519,6 +519,27 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, u32 width, u32 height); +/** + * v4l2_get_link_freq - Get link rate from transmitter + * + * @handler: The transmitter's control handler + * @mul: The multiplier between pixel rate and link frequency. Bits per pixel on + * D-PHY, samples per clock on parallel. 0 otherwise. + * @div: The divisor between pixel rate and link frequency. Number of data lanes + * times two on D-PHY, 1 on parallel. 0 otherwise. + * + * This function is intended for obtaining the link frequency from the + * transmitter sub-devices. It returns the link rate, either from the + * V4L2_CID_LINK_FREQ control implemented by the transmitter, or value + * calculated based on the V4L2_CID_PIXEL_RATE implemented by the transmitter. + * + * Returns link frequency on success, otherwise a negative error code: + * -ENOENT: Link frequency or pixel rate control not found + * -EINVAL: Invalid link frequency value + */ +s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul, + unsigned int div); + static inline u64 v4l2_buffer_get_timestamp(const struct v4l2_buffer *buf) { /* diff -Naur --no-dereference a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h --- a/include/media/v4l2-subdev.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/media/v4l2-subdev.h 2022-01-06 12:45:53.834318188 -0500 @@ -313,7 +313,18 @@ }; /** - * enum v4l2_mbus_frame_desc_entry - media bus frame description flags + * struct v4l2_mbus_frame_desc_entry_csi2 + * + * @vc: CSI-2 virtual channel + * @dt: CSI-2 data type ID + */ +struct v4l2_mbus_frame_desc_entry_csi2 { + u8 vc; + u8 dt; +}; + +/** + * enum v4l2_mbus_frame_desc_flags - media bus frame description flags * * @V4L2_MBUS_FRAME_DESC_FL_LEN_MAX: * Indicates that &struct v4l2_mbus_frame_desc_entry->length field @@ -331,25 +342,56 @@ * struct v4l2_mbus_frame_desc_entry - media bus frame description structure * * @flags: bitmask flags, as defined by &enum v4l2_mbus_frame_desc_flags. + * @stream: stream in routing configuration * @pixelcode: media bus pixel code, valid if @flags * %FRAME_DESC_FL_BLOB is not set. * @length: number of octets per frame, valid if @flags * %V4L2_MBUS_FRAME_DESC_FL_LEN_MAX is set. + * @bus: Bus-specific frame descriptor parameters + * @bus.csi2: CSI-2-specific bus configuration */ struct v4l2_mbus_frame_desc_entry { enum v4l2_mbus_frame_desc_flags flags; + u32 stream; u32 pixelcode; u32 length; + union { + struct v4l2_mbus_frame_desc_entry_csi2 csi2; + } bus; }; -#define V4L2_FRAME_DESC_ENTRY_MAX 4 + /* + * FIXME: If this number is too small, it should be dropped altogether and the + * API switched to a dynamic number of frame descriptor entries. + */ +#define V4L2_FRAME_DESC_ENTRY_MAX 8 + +/** + * enum v4l2_mbus_frame_desc_type - media bus frame description type + * + * @V4L2_MBUS_FRAME_DESC_TYPE_UNDEFINED: + * Undefined frame desc type. Drivers should not use this, it is + * for backwards compatibility. + * @V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL: + * Parallel media bus. + * @V4L2_MBUS_FRAME_DESC_TYPE_CSI2: + * CSI-2 media bus. Frame desc parameters must be set in + * &struct v4l2_mbus_frame_desc_entry->csi2. + */ +enum v4l2_mbus_frame_desc_type { + V4L2_MBUS_FRAME_DESC_TYPE_UNDEFINED = 0, + V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL, + V4L2_MBUS_FRAME_DESC_TYPE_CSI2, +}; /** * struct v4l2_mbus_frame_desc - media bus data frame description + * @type: type of the bus (enum v4l2_mbus_frame_desc_type) * @entry: frame descriptors array * @num_entries: number of entries in @entry array */ struct v4l2_mbus_frame_desc { + enum v4l2_mbus_frame_desc_type type; struct v4l2_mbus_frame_desc_entry entry[V4L2_FRAME_DESC_ENTRY_MAX]; unsigned short num_entries; }; @@ -628,6 +670,70 @@ }; /** + * struct v4l2_subdev_stream_config - Used for storing stream configuration. + * + * @pad: pad number + * @stream: stream number + * @fmt: &struct v4l2_mbus_framefmt + * @crop: &struct v4l2_rect to be used for crop + * @compose: &struct v4l2_rect to be used for compose + * + * This structure stores configuration for a stream. + */ +struct v4l2_subdev_stream_config { + u32 pad; + u32 stream; + + struct v4l2_mbus_framefmt fmt; + struct v4l2_rect crop; + struct v4l2_rect compose; +}; + +/** + * struct v4l2_subdev_stream_configs - A collection of stream configs. + * + * @num_configs: number of entries in @config. + * @config: an array of &struct v4l2_subdev_stream_configs. + */ +struct v4l2_subdev_stream_configs { + u32 num_configs; + struct v4l2_subdev_stream_config *configs; +}; + +/** + * struct v4l2_subdev_krouting - subdev routing table + * + * @which: format type (from enum v4l2_subdev_format_whence) + * @routes: &struct v4l2_subdev_route + * @num_routes: number of routes + * + * This structure is used to translate arguments received from + * VIDIOC_SUBDEV_G/S_ROUTING() ioctl to subdev device drivers operations. + */ +struct v4l2_subdev_krouting { + u32 which; + struct v4l2_subdev_route *routes; + unsigned int num_routes; +}; + +/** + * struct v4l2_subdev_state - Used for storing subdev information. + * + * @pads: &struct v4l2_subdev_pad_config array + * @routing: routing table for the subdev + * @stream_configs: stream configurations (only for V4L2_SUBDEV_FL_MULTIPLEXED) + * + * This structure only needs to be passed to the pad op if the 'which' field + * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For + * %V4L2_SUBDEV_FORMAT_ACTIVE it is safe to pass %NULL. + */ +struct v4l2_subdev_state { + struct v4l2_subdev_pad_config *pads; + struct v4l2_subdev_krouting routing; + struct v4l2_subdev_stream_configs stream_configs; +}; + +/** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations * * @init_cfg: initialize the pad config to default values @@ -688,30 +794,34 @@ * applied to the hardware. The operation shall fail if the * pad index it has been called on is not valid or in case of * unrecoverable failures. + * + * @get_routing: get the subdevice routing table. + * @set_routing: enable or disable data connection routes described in the + * subdevice routing table. */ struct v4l2_subdev_pad_ops { int (*init_cfg)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg); + struct v4l2_subdev_state *state); int (*enum_mbus_code)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code); int (*enum_frame_size)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse); int (*enum_frame_interval)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_interval_enum *fie); int (*get_fmt)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format); int (*set_fmt)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format); int (*get_selection)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel); int (*set_selection)(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel); int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid); int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid); @@ -732,6 +842,12 @@ struct v4l2_mbus_config *config); int (*set_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config); + int (*get_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *route); + int (*set_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *route); }; /** @@ -806,6 +922,12 @@ * should set this flag. */ #define V4L2_SUBDEV_FL_HAS_EVENTS (1U << 3) +/* + * Set this flag if this subdev supports multiplexed streams. This means + * that the driver supports routing and handles the stream parameter in its + * v4l2_subdev_pad_ops handlers. + */ +#define V4L2_SUBDEV_FL_MULTIPLEXED (1U << 4) struct regulator_bulk_data; @@ -922,14 +1044,14 @@ * struct v4l2_subdev_fh - Used for storing subdev information per file handle * * @vfh: pointer to &struct v4l2_fh - * @pad: pointer to &struct v4l2_subdev_pad_config + * @state: pointer to &struct v4l2_subdev_state * @owner: module pointer to the owner of this file handle */ struct v4l2_subdev_fh { struct v4l2_fh vfh; struct module *owner; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - struct v4l2_subdev_pad_config *pad; + struct v4l2_subdev_state *state; #endif }; @@ -949,17 +1071,17 @@ * &struct v4l2_subdev_pad_config->try_fmt * * @sd: pointer to &struct v4l2_subdev - * @cfg: pointer to &struct v4l2_subdev_pad_config array. - * @pad: index of the pad in the @cfg array. + * @state: pointer to &struct v4l2_subdev_state. + * @pad: index of the pad in the @state array. */ static inline struct v4l2_mbus_framefmt * v4l2_subdev_get_try_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &cfg[pad].try_fmt; + return &state->pads[pad].try_fmt; } /** @@ -967,17 +1089,17 @@ * &struct v4l2_subdev_pad_config->try_crop * * @sd: pointer to &struct v4l2_subdev - * @cfg: pointer to &struct v4l2_subdev_pad_config array. - * @pad: index of the pad in the @cfg array. + * @state: pointer to &struct v4l2_subdev_state. + * @pad: index of the pad in the @state array. */ static inline struct v4l2_rect * v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &cfg[pad].try_crop; + return &state->pads[pad].try_crop; } /** @@ -985,17 +1107,17 @@ * &struct v4l2_subdev_pad_config->try_compose * * @sd: pointer to &struct v4l2_subdev - * @cfg: pointer to &struct v4l2_subdev_pad_config array. - * @pad: index of the pad in the @cfg array. + * @state: pointer to &struct v4l2_subdev_state. + * @pad: index of the pad in the @state array. */ static inline struct v4l2_rect * v4l2_subdev_get_try_compose(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *state, unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &cfg[pad].try_compose; + return &state->pads[pad].try_compose; } #endif @@ -1097,20 +1219,17 @@ int v4l2_subdev_link_validate(struct media_link *link); /** - * v4l2_subdev_alloc_pad_config - Allocates memory for pad config + * v4l2_subdev_alloc_state - allocate v4l2_subdev_state * - * @sd: pointer to struct v4l2_subdev + * Must call v4l2_subdev_free_state() when state is no longer needed. */ -struct -v4l2_subdev_pad_config *v4l2_subdev_alloc_pad_config(struct v4l2_subdev *sd); +struct v4l2_subdev_state *v4l2_subdev_alloc_state(struct v4l2_subdev *sd); /** - * v4l2_subdev_free_pad_config - Frees memory allocated by - * v4l2_subdev_alloc_pad_config(). - * - * @cfg: pointer to &struct v4l2_subdev_pad_config + * v4l2_subdev_free_state - uninitialize v4l2_subdev_state */ -void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg); +void v4l2_subdev_free_state(struct v4l2_subdev_state *state); + #endif /* CONFIG_MEDIA_CONTROLLER */ /** @@ -1178,4 +1297,97 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, const struct v4l2_event *ev); +/** + * v4l2_subdev_get_routing() - Get routing from a subdevice + * + * @sd: The subdev from which to get the routing + * @state: Pointer to &struct v4l2_subdev_state + * @routing: Pointer to the target &struct v4l2_subdev_krouting + * + * Get a copy of the subdevice's routing table. + * + * Must be freed with v4l2_subdev_free_routing after use. + */ +int v4l2_subdev_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing); + +/** + * v4l2_subdev_free_routing() - Free the routing + * + * @routing: The routing to be freed + * + * Frees the routing data in @routing. + */ +void v4l2_subdev_free_routing(struct v4l2_subdev_krouting *routing); + +/** + * v4l2_subdev_cpy_routing() - Copy the routing + * + * @dst: The destination routing + * @src: The source routing + * + * Copies routing from @src to @dst without allocating space. If @dst does not + * have enough space, set dst->num_routes to the required number of routes, and + * return -ENOSPC. + * + * Can be used in subdevice's v4l2_subdev_pad_ops.get_routing() callback. + */ +int v4l2_subdev_cpy_routing(struct v4l2_subdev_krouting *dst, + const struct v4l2_subdev_krouting *src); + +/** + * v4l2_subdev_dup_routing() - Duplicate the routing + * + * @dst: The destination routing + * @src: The source routing + * + * Makes a duplicate of the routing from @src to @dst by allocating enough + * memory and making a copy of the routing. + * + * Can be used in subdevice's v4l2_subdev_pad_ops.set_routing() callback + * to store the given routing. + * + * Must be freed with v4l2_subdev_free_routing after use. + */ +int v4l2_subdev_dup_routing(struct v4l2_subdev_krouting *dst, + const struct v4l2_subdev_krouting *src); + +/** + * v4l2_subdev_has_route() - Check if there is a route between two pads + * + * @routing: The subdevice's routing + * @pad0: First pad + * @pad1: Second pad + * + * Returns whether there is a route between @pad0 and @pad1 of the same + * subdevice according to the given routing. + */ +bool v4l2_subdev_has_route(struct v4l2_subdev_krouting *routing, + unsigned int pad0, unsigned int pad1); + + +/** + * v4l2_init_stream_configs() - Initialize stream configs according to routing + * + * @stream_configs: The stream configs to initialize + * @routing: The routing used for the stream configs + * + * Initializes @stream_configs according to @routing, allocating enough + * space to hold configuration for each route endpoint. + * + * Must be freed with v4l2_uninit_stream_configs(). + */ +int v4l2_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, + const struct v4l2_subdev_krouting *routing); + +/** + * v4l2_uninit_stream_configs() - Uninitialize stream configs + * + * @stream_configs: The stream configs to uninitialize + * + * Frees any allocated memory in @stream_configs. + */ +void v4l2_uninit_stream_configs(struct v4l2_subdev_stream_configs *stream_configs); + #endif diff -Naur --no-dereference a/include/net/cfg80211.h b/include/net/cfg80211.h --- a/include/net/cfg80211.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/net/cfg80211.h 2022-01-06 12:45:53.834318188 -0500 @@ -5202,6 +5202,7 @@ * netdev and may otherwise be used by driver read-only, will be update * by cfg80211 on change_interface * @mgmt_registrations: list of registrations for management frames + * @mgmt_registrations_lock: lock for the list * @mgmt_registrations_need_update: mgmt registrations were updated, * need to propagate the update to the driver * @mtx: mutex used to lock data in this struct, may be used by drivers @@ -5248,6 +5249,7 @@ u32 identifier; struct list_head mgmt_registrations; + spinlock_t mgmt_registrations_lock; u8 mgmt_registrations_need_update:1; struct mutex mtx; @@ -5622,7 +5624,7 @@ */ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, const u8 *addr, enum nl80211_iftype iftype, - u8 data_offset, bool is_amsdu); + u8 data_offset); /** * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 @@ -5634,7 +5636,7 @@ static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype) { - return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0, false); + return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0); } /** @@ -6843,6 +6845,8 @@ * not known. This value is used only if @status < 0 to indicate that the * failure is due to a timeout and not due to explicit rejection by the AP. * This value is ignored in other cases (@status >= 0). + * @authorized: Indicates whether the connection is ready to transport + * data packets. */ struct cfg80211_connect_resp_params { int status; @@ -6854,6 +6858,7 @@ size_t resp_ie_len; struct cfg80211_fils_resp_params fils; enum nl80211_timeout_reason timeout_reason; + bool authorized; }; /** @@ -7003,6 +7008,9 @@ * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length * @fils: FILS related roaming information. + * @authorized: true if the 802.1X authentication was done by the driver or is + * not needed (e.g., when Fast Transition protocol was used), false + * otherwise. Ignored for networks that don't use 802.1X authentication. */ struct cfg80211_roam_info { struct ieee80211_channel *channel; @@ -7013,6 +7021,7 @@ const u8 *resp_ie; size_t resp_ie_len; struct cfg80211_fils_resp_params fils; + bool authorized; }; /** diff -Naur --no-dereference a/include/net/lredev.h b/include/net/lredev.h --- a/include/net/lredev.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/net/lredev.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * lre device API + * Copyright (c) 2020 Texas Instruments Incorporated - http://www.ti.com/ + */ +#ifndef _LINUX_LREDEV_H_ +#define _LINUX_LREDEV_H_ + +#include + +/* PRP duplicate discard modes */ +enum iec62439_3_dd_modes { + IEC62439_3_DA = 1, + IEC62439_3_DD, +}; + +/* HSR modes */ +enum iec62439_3_hsr_modes { + IEC62439_3_HSR_MODE_H = 1, + IEC62439_3_HSR_MODE_N, + IEC62439_3_HSR_MODE_T, + IEC62439_3_HSR_MODE_U, + IEC62439_3_HSR_MODE_M, +}; + +/* PRP Transparent reception modes */ +enum iec62439_3_tr_modes { + IEC62439_3_TR_REMOVE_RCT = 1, + IEC62439_3_TR_PASS_RCT, +}; + +enum iec62439_3_clear_nt_cmd { + IEC62439_3_CLEAR_NT_NO_OP, + IEC62439_3_CLEAR_NT, +}; + +enum lredev_attr_id { + LREDEV_ATTR_ID_UNDEFINED, + /* For HSR only */ + LREDEV_ATTR_ID_HSR_MODE, + /* Duplicate discard for PRP */ + LREDEV_ATTR_ID_DD_MODE, + /* Duplicate List Reside Max time for HSR & PRP */ + LREDEV_ATTR_ID_DLRMT, + /* Transparent reception for PRP */ + LREDEV_ATTR_ID_PRP_TR, + /* Clear node table - for HSR & PRP */ + LREDEV_ATTR_ID_CLEAR_NT, +}; + +struct lredev_attr { + enum lredev_attr_id id; + union { + enum iec62439_3_hsr_modes mode; + enum iec62439_3_dd_modes dd_mode; + /* in msec */ + u32 dl_reside_max_time; + enum iec62439_3_tr_modes tr_mode; + enum iec62439_3_clear_nt_cmd clear_nt_cmd; + }; +}; + +enum iec62439_node_type { + IEC62439_3_DANP, + IEC62439_3_REDBOXP, + IEC62439_3_VDANP, + IEC62439_3_DANH, + IEC62439_3_REDBOXH, + IEC62439_3_VDANH, +}; + +struct lre_node_table_entry { + u8 mac_address[ETH_ALEN]; + /* in 1/100 seconds */ + u32 time_last_seen_a; + /* in 1/100 seconds */ + u32 time_last_seen_b; + enum iec62439_node_type node_type; +}; + +#define LRE_MAX_NT_ENTRIES 256 + +struct lre_stats { + u32 cnt_tx_a; + u32 cnt_tx_b; + u32 cnt_tx_c; + u32 cnt_errwronglan_a; + u32 cnt_errwronglan_b; + u32 cnt_errwronglan_c; + u32 cnt_rx_a; + u32 cnt_rx_b; + u32 cnt_rx_c; + u32 cnt_errors_a; + u32 cnt_errors_b; + u32 cnt_errors_c; + u32 cnt_nodes; + u32 cnt_proxy_nodes; + u32 cnt_unique_rx_a; + u32 cnt_unique_rx_b; + u32 cnt_unique_rx_c; + u32 cnt_duplicate_rx_a; + u32 cnt_duplicate_rx_b; + u32 cnt_duplicate_rx_c; + u32 cnt_multiple_rx_a; + u32 cnt_multiple_rx_b; + u32 cnt_multiple_rx_c; + u32 cnt_own_rx_a; + u32 cnt_own_rx_b; +}; + +/** + * struct lredev_ops - lredev operations + * + * @lredev_attr_get: Get a attribute (see switchdev_attr). + * + * @lredev_port_attr_set: Set a attribute (see switchdev_attr). + */ +struct lredev_ops { + int (*lredev_attr_get)(struct net_device *dev, + struct lredev_attr *attr); + int (*lredev_attr_set)(struct net_device *dev, + struct lredev_attr *attr); + int (*lredev_get_node_table)(struct net_device *dev, + struct lre_node_table_entry table[], + int size); + int (*lredev_get_stats)(struct net_device *dev, + struct lre_stats *stats); + int (*lredev_set_sv_vlan_id)(struct net_device *dev, u16 vid); +}; + +#endif diff -Naur --no-dereference a/include/net/sock.h b/include/net/sock.h --- a/include/net/sock.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/net/sock.h 2022-01-06 12:45:53.834318188 -0500 @@ -1694,12 +1694,14 @@ u64 transmit_time; u32 mark; u16 tsflags; + struct skb_redundant_info redinfo; }; static inline void sockcm_init(struct sockcm_cookie *sockc, const struct sock *sk) { *sockc = (struct sockcm_cookie) { .tsflags = sk->sk_tsflags }; + memset(&sockc->redinfo, 0, sizeof(sockc->redinfo)); } int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, @@ -2446,6 +2448,8 @@ struct sk_buff *skb); void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, struct sk_buff *skb); +void __sock_recv_redinfo_timestamp(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb); static inline void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) @@ -2529,6 +2533,31 @@ } DECLARE_STATIC_KEY_FALSE(tcp_rx_skb_cache_key); +static inline void sock_recv_redundant_info(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb) +{ + struct skb_redundant_info *sred; + + sred = skb_redinfo(skb); + if (sred->lsdu_size) + put_cmsg(msg, SOL_SOCKET, SCM_REDUNDANT, sizeof(*sred), sred); + + __sock_recv_redinfo_timestamp(msg, sk, skb); + +} + +static inline void sock_tx_redundant_info(const struct sock *sk, + struct skb_redundant_info *redinfo, + struct sk_buff *skb) +{ + struct skb_redundant_info *sred; + + if (redinfo->io_port) { + sred = skb_redinfo(skb); + memcpy(sred, redinfo, sizeof(*sred)); + } +} + /** * sk_eat_skb - Release a skb if it is no longer needed * @sk: socket to eat this skb from diff -Naur --no-dereference a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h --- a/include/uapi/asm-generic/socket.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/asm-generic/socket.h 2022-01-06 12:45:53.834318188 -0500 @@ -144,4 +144,10 @@ #endif +#define SO_REDUNDANT 80 +#define SCM_REDUNDANT SO_REDUNDANT + +#define SO_RED_TIMESTAMPING 81 +#define SCM_RED_TIMESTAMPING SO_RED_TIMESTAMPING + #endif /* __ASM_GENERIC_SOCKET_H */ diff -Naur --no-dereference a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h --- a/include/uapi/linux/aufs_type.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/uapi/linux/aufs_type.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,452 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __AUFS_TYPE_H__ +#define __AUFS_TYPE_H__ + +#define AUFS_NAME "aufs" + +#ifdef __KERNEL__ +/* + * define it before including all other headers. + * sched.h may use pr_* macros before defining "current", so define the + * no-current version first, and re-define later. + */ +#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ +#include +#undef pr_fmt +#define pr_fmt(fmt) \ + AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ + (int)sizeof(current->comm), current->comm, current->pid +#include +#else +#include +#include +#include +#endif /* __KERNEL__ */ + +#define AUFS_VERSION "5.10.82-20211129" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') + +/* ---------------------------------------------------------------------- */ + +#ifdef __KERNEL__ +#ifdef CONFIG_AUFS_BRANCH_MAX_127 +typedef int8_t aufs_bindex_t; +#define AUFS_BRANCH_MAX 127 +#else +typedef int16_t aufs_bindex_t; +#ifdef CONFIG_AUFS_BRANCH_MAX_511 +#define AUFS_BRANCH_MAX 511 +#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) +#define AUFS_BRANCH_MAX 1023 +#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) +#define AUFS_BRANCH_MAX 32767 +#endif +#endif + +#ifndef AUFS_BRANCH_MAX +#error unknown CONFIG_AUFS_BRANCH_MAX value +#endif +#endif /* __KERNEL__ */ + +/* ---------------------------------------------------------------------- */ + +#define AUFS_FSTYPE AUFS_NAME + +#define AUFS_ROOT_INO 2 +#define AUFS_FIRST_INO 11 + +#define AUFS_WH_PFX ".wh." +#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) +#define AUFS_WH_TMP_LEN 4 +/* a limit for rmdir/rename a dir and copyup */ +#define AUFS_MAX_NAMELEN (NAME_MAX \ + - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ + - 1 /* dot */\ + - AUFS_WH_TMP_LEN) /* hex */ +#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" +#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME +#define AUFS_XINO_DEF_SEC 30 /* seconds */ +#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ +#define AUFS_DIRWH_DEF 3 +#define AUFS_RDCACHE_DEF 10 /* seconds */ +#define AUFS_RDCACHE_MAX 3600 /* seconds */ +#define AUFS_RDBLK_DEF 512 /* bytes */ +#define AUFS_RDHASH_DEF 32 +#define AUFS_WKQ_NAME AUFS_NAME "d" +#define AUFS_MFS_DEF_SEC 30 /* seconds */ +#define AUFS_MFS_MAX_SEC 3600 /* seconds */ +#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ +#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ + +/* pseudo-link maintenace under /proc */ +#define AUFS_PLINK_MAINT_NAME "plink_maint" +#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME +#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME + +/* dirren, renamed dir */ +#define AUFS_DR_INFO_PFX AUFS_WH_PFX ".dr." +#define AUFS_DR_BRHINO_NAME AUFS_WH_PFX "hino" +/* whiteouted doubly */ +#define AUFS_WH_DR_INFO_PFX AUFS_WH_PFX AUFS_DR_INFO_PFX +#define AUFS_WH_DR_BRHINO AUFS_WH_PFX AUFS_DR_BRHINO_NAME + +#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ +#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME + +#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME +#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" +#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" + +/* doubly whiteouted */ +#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME +#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME +#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME + +/* branch permissions and attributes */ +#define AUFS_BRPERM_RW "rw" +#define AUFS_BRPERM_RO "ro" +#define AUFS_BRPERM_RR "rr" +#define AUFS_BRATTR_COO_REG "coo_reg" +#define AUFS_BRATTR_COO_ALL "coo_all" +#define AUFS_BRATTR_FHSM "fhsm" +#define AUFS_BRATTR_UNPIN "unpin" +#define AUFS_BRATTR_ICEX "icex" +#define AUFS_BRATTR_ICEX_SEC "icexsec" +#define AUFS_BRATTR_ICEX_SYS "icexsys" +#define AUFS_BRATTR_ICEX_TR "icextr" +#define AUFS_BRATTR_ICEX_USR "icexusr" +#define AUFS_BRATTR_ICEX_OTH "icexoth" +#define AUFS_BRRATTR_WH "wh" +#define AUFS_BRWATTR_NLWH "nolwh" +#define AUFS_BRWATTR_MOO "moo" + +#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ +#define AuBrPerm_RO (1 << 1) /* readonly */ +#define AuBrPerm_RR (1 << 2) /* natively readonly */ +#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) + +#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ +#define AuBrAttr_COO_ALL (1 << 4) +#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) + +#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ +#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of + branch. meaningless since + linux-3.18-rc1 */ + +/* ignore error in copying XATTR */ +#define AuBrAttr_ICEX_SEC (1 << 7) +#define AuBrAttr_ICEX_SYS (1 << 8) +#define AuBrAttr_ICEX_TR (1 << 9) +#define AuBrAttr_ICEX_USR (1 << 10) +#define AuBrAttr_ICEX_OTH (1 << 11) +#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ + | AuBrAttr_ICEX_SYS \ + | AuBrAttr_ICEX_TR \ + | AuBrAttr_ICEX_USR \ + | AuBrAttr_ICEX_OTH) + +#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ +#define AuBrRAttr_Mask AuBrRAttr_WH + +#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ +#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ +#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) + +#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) + +/* #warning test userspace */ +#ifdef __KERNEL__ +#ifndef CONFIG_AUFS_FHSM +#undef AuBrAttr_FHSM +#define AuBrAttr_FHSM 0 +#endif +#ifndef CONFIG_AUFS_XATTR +#undef AuBrAttr_ICEX +#define AuBrAttr_ICEX 0 +#undef AuBrAttr_ICEX_SEC +#define AuBrAttr_ICEX_SEC 0 +#undef AuBrAttr_ICEX_SYS +#define AuBrAttr_ICEX_SYS 0 +#undef AuBrAttr_ICEX_TR +#define AuBrAttr_ICEX_TR 0 +#undef AuBrAttr_ICEX_USR +#define AuBrAttr_ICEX_USR 0 +#undef AuBrAttr_ICEX_OTH +#define AuBrAttr_ICEX_OTH 0 +#endif +#endif + +/* the longest combination */ +/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ +#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ + "+" AUFS_BRATTR_COO_REG \ + "+" AUFS_BRATTR_FHSM \ + "+" AUFS_BRATTR_UNPIN \ + "+" AUFS_BRATTR_ICEX_SEC \ + "+" AUFS_BRATTR_ICEX_SYS \ + "+" AUFS_BRATTR_ICEX_USR \ + "+" AUFS_BRATTR_ICEX_OTH \ + "+" AUFS_BRWATTR_NLWH) + +typedef struct { + char a[AuBrPermStrSz]; +} au_br_perm_str_t; + +static inline int au_br_writable(int brperm) +{ + return brperm & AuBrPerm_RW; +} + +static inline int au_br_whable(int brperm) +{ + return brperm & (AuBrPerm_RW | AuBrRAttr_WH); +} + +static inline int au_br_wh_linkable(int brperm) +{ + return !(brperm & AuBrWAttr_NoLinkWH); +} + +static inline int au_br_cmoo(int brperm) +{ + return brperm & AuBrAttr_CMOO_Mask; +} + +static inline int au_br_fhsm(int brperm) +{ + return brperm & AuBrAttr_FHSM; +} + +/* ---------------------------------------------------------------------- */ + +/* ioctl */ +enum { + /* readdir in userspace */ + AuCtl_RDU, + AuCtl_RDU_INO, + + AuCtl_WBR_FD, /* pathconf wrapper */ + AuCtl_IBUSY, /* busy inode */ + AuCtl_MVDOWN, /* move-down */ + AuCtl_BR, /* info about branches */ + AuCtl_FHSM_FD /* connection for fhsm */ +}; + +/* borrowed from linux/include/linux/kernel.h */ +#ifndef ALIGN +#ifdef _GNU_SOURCE +#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) +#else +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#endif +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +#endif + +/* borrowed from linux/include/linux/compiler-gcc3.h */ +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif + +#ifdef __KERNEL__ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif +#endif + +struct au_rdu_cookie { + uint64_t h_pos; + int16_t bindex; + uint8_t flags; + uint8_t pad; + uint32_t generation; +} __aligned(8); + +struct au_rdu_ent { + uint64_t ino; + int16_t bindex; + uint8_t type; + uint8_t nlen; + uint8_t wh; + char name[]; +} __aligned(8); + +static inline int au_rdu_len(int nlen) +{ + /* include the terminating NULL */ + return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, + sizeof(uint64_t)); +} + +union au_rdu_ent_ul { + struct au_rdu_ent __user *e; + uint64_t ul; +}; + +enum { + AufsCtlRduV_SZ, + AufsCtlRduV_End +}; + +struct aufs_rdu { + /* input */ + union { + uint64_t sz; /* AuCtl_RDU */ + uint64_t nent; /* AuCtl_RDU_INO */ + }; + union au_rdu_ent_ul ent; + uint16_t verify[AufsCtlRduV_End]; + + /* input/output */ + uint32_t blk; + + /* output */ + union au_rdu_ent_ul tail; + /* number of entries which were added in a single call */ + uint64_t rent; + uint8_t full; + uint8_t shwh; + + struct au_rdu_cookie cookie; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +/* dirren. the branch is identified by the filename who contains this */ +struct au_drinfo { + uint64_t ino; + union { + uint8_t oldnamelen; + uint64_t _padding; + }; + uint8_t oldname[]; +} __aligned(8); + +struct au_drinfo_fdata { + uint32_t magic; + struct au_drinfo drinfo; +} __aligned(8); + +#define AUFS_DRINFO_MAGIC_V1 ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x01) +/* future */ +#define AUFS_DRINFO_MAGIC_V2 ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x02) + +/* ---------------------------------------------------------------------- */ + +struct aufs_wbr_fd { + uint32_t oflags; + int16_t brid; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +struct aufs_ibusy { + uint64_t ino, h_ino; + int16_t bindex; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +/* error code for move-down */ +/* the actual message strings are implemented in aufs-util.git */ +enum { + EAU_MVDOWN_OPAQUE = 1, + EAU_MVDOWN_WHITEOUT, + EAU_MVDOWN_UPPER, + EAU_MVDOWN_BOTTOM, + EAU_MVDOWN_NOUPPER, + EAU_MVDOWN_NOLOWERBR, + EAU_Last +}; + +/* flags for move-down */ +#define AUFS_MVDOWN_DMSG 1 +#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ +#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ +#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ +#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ +#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ +#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ +#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ +#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ +#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ +#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ +#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ +#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ + +/* index for move-down */ +enum { + AUFS_MVDOWN_UPPER, + AUFS_MVDOWN_LOWER, + AUFS_MVDOWN_NARRAY +}; + +/* + * additional info of move-down + * number of free blocks and inodes. + * subset of struct kstatfs, but smaller and always 64bit. + */ +struct aufs_stfs { + uint64_t f_blocks; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; +}; + +struct aufs_stbr { + int16_t brid; /* optional input */ + int16_t bindex; /* output */ + struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ +} __aligned(8); + +struct aufs_mvdown { + uint32_t flags; /* input/output */ + struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ + int8_t au_errno; /* output */ +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +union aufs_brinfo { + /* PATH_MAX may differ between kernel-space and user-space */ + char _spacer[4096]; + struct { + int16_t id; + int perm; + char path[]; + }; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +#define AuCtlType 'A' +#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) +#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) +#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ + struct aufs_wbr_fd) +#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) +#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ + struct aufs_mvdown) +#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) +#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) + +#endif /* __AUFS_TYPE_H__ */ diff -Naur --no-dereference a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h --- a/include/uapi/linux/if_link.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/linux/if_link.h 2022-01-06 12:45:53.834318188 -0500 @@ -1106,6 +1106,9 @@ IFLA_HSR_PROTOCOL, /* Indicate different protocol than * HSR. For example PRP. */ + IFLA_HSR_SV_VID, /* Supervision frames VLAN ID */ + IFLA_HSR_SV_DEI, /* Supervision frames VLAN DEI */ + IFLA_HSR_SV_PCP, /* Supervision frames VLAN PCP */ __IFLA_HSR_MAX, }; diff -Naur --no-dereference a/include/uapi/linux/keystone_dsp_mem.h b/include/uapi/linux/keystone_dsp_mem.h --- a/include/uapi/linux/keystone_dsp_mem.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/uapi/linux/keystone_dsp_mem.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef _UAPI_KEYSTONE_DSP_MEM_H_ +#define _UAPI_KEYSTONE_DSP_MEM_H_ + +#define KEYSTONE_DSP_MEM_MAP_INDEX_MASK (0x7) +#define KEYSTONE_DSP_MEM_MAP_OFFSET_SHIFT (3) + +#endif /* _UAPI_KEYSTONE_DSP_MEM_H_ */ diff -Naur --no-dereference a/include/uapi/linux/keystone_remoteproc.h b/include/uapi/linux/keystone_remoteproc.h --- a/include/uapi/linux/keystone_remoteproc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/uapi/linux/keystone_remoteproc.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_ +#define _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_ + +#include +#include + +/** + * enum keystone_rproc_state - keystone remoteproc state setting values + * + * @KEYSTONE_RPROC_OFFLINE: request to configure the remoteproc into an offline + * state + * @KEYSTONE_RPROC_RUNNING: request to configure the remoteproc into a ready + * state + */ +enum keystone_rproc_state { + KEYSTONE_RPROC_OFFLINE, + KEYSTONE_RPROC_RUNNING, +}; + +/** + * struct keystone_rproc_set_state_params - keystone remoteproc set state + * parameters structure + * + * @state: enumerated state value to set + * @boot_addr: boot address/entry point for the remote processor + */ +struct keystone_rproc_set_state_params { + enum keystone_rproc_state state; + __u32 boot_addr; +}; + +/* Macros used within mmap function */ +#define KEYSTONE_RPROC_UIO_MAP_INDEX_MASK (0x7) +#define KEYSTONE_RPROC_UIO_MAP_OFFSET_SHIFT (3) + +/* IOCTL definitions */ +#define KEYSTONE_RPROC_IOC_MAGIC 'I' +#define KEYSTONE_RPROC_IOC_SET_RSC_TABLE _IOW(KEYSTONE_RPROC_IOC_MAGIC, \ + 0, void *) +#define KEYSTONE_RPROC_IOC_SET_STATE _IOW(KEYSTONE_RPROC_IOC_MAGIC, \ + 1, \ + struct keystone_rproc_set_state_params) +#define KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE _IOW(KEYSTONE_RPROC_IOC_MAGIC, \ + 2, __u32) +#define KEYSTONE_RPROC_IOC_DSP_RESET _IO(KEYSTONE_RPROC_IOC_MAGIC, 3) +#define KEYSTONE_RPROC_IOC_DSP_BOOT _IOW(KEYSTONE_RPROC_IOC_MAGIC, \ + 4, __u32) + +#define KEYSTONE_RPROC_IOC_MAXNR (5) + +#endif /* _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_ */ diff -Naur --no-dereference a/include/uapi/linux/net_switch_config.h b/include/uapi/linux/net_switch_config.h --- a/include/uapi/linux/net_switch_config.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/uapi/linux/net_switch_config.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Texas Instruments Ethernet Switch Driver + * + * Copyright (C) 2014-2020 Texas Instruments, Inc + * + * Userspace API for Switch Configuration + */ + +#ifndef __NET_CONFIG_SWITCH_H__ +#define __NET_CONFIG_SWITCH_H__ + +#include +#include + +enum { + SWITCH_INVALID, + SWITCH_ADD_MULTICAST, + SWITCH_DEL_MULTICAST, + SWITCH_ADD_VLAN, + SWITCH_DEL_VLAN, + SWITCH_SET_PORT_CONFIG, + SWITCH_GET_PORT_CONFIG, + SWITCH_ADD_UNKNOWN_VLAN_INFO, + SWITCH_GET_PORT_STATE, + SWITCH_SET_PORT_STATE, + SWITCH_GET_PORT_VLAN_CONFIG, + SWITCH_SET_PORT_VLAN_CONFIG, + SWITCH_RATELIMIT, +}; + +enum { + PORT_STATE_DISABLED = 0, + PORT_STATE_BLOCKED, + PORT_STATE_LEARN, + PORT_STATE_FORWARD, +}; + +/* + * Pass all unused parameters as zero is recomented. + */ +struct net_switch_config { + unsigned int cmd; /* API to be invoked by the kernel driver */ + + unsigned int port; + unsigned int vid; /* VLAN identifier */ + unsigned char unreg_multi; /* unreg multicast Egress Ports */ + unsigned char reg_multi; /* register multicast Egress ports */ + unsigned char untag_port; /* Untag ports */ + unsigned char addr[6]; + unsigned int super; + struct ethtool_cmd ecmd; + unsigned char unknown_vlan_member; + unsigned char unknown_vlan_untag; + unsigned int unknown_vlan_unreg_multi; + unsigned int unknown_vlan_reg_multi; + unsigned int port_state; + unsigned int prio; + int vlan_cfi; + unsigned int bcast_rate_limit; + unsigned int mcast_rate_limit; + int direction; + + unsigned int ret_type; /* Return Success/Failure */ +}; + +#endif /* __NET_CONFIG_SWITCH_H__*/ diff -Naur --no-dereference a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h --- a/include/uapi/linux/nl80211.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/linux/nl80211.h 2022-01-06 12:45:53.834318188 -0500 @@ -2384,7 +2384,10 @@ * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it * wants to use the supported offload of the 4-way handshake. * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. - * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) + * @NL80211_ATTR_PORT_AUTHORIZED: flag attribute used in %NL80211_CMD_ROAMED + * notification indicating that that 802.1X authentication was done by + * the driver or is not needed (because roaming used the Fast Transition + * protocol). * * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external * authentication operation (u32 attribute with an diff -Naur --no-dereference a/include/uapi/linux/rpmsg_rpc.h b/include/uapi/linux/rpmsg_rpc.h --- a/include/uapi/linux/rpmsg_rpc.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/uapi/linux/rpmsg_rpc.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Remote Processor Procedure Call Driver + * + * Copyright (C) 2012-2021 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef _UAPI_LINUX_RPMSG_RPC_H_ +#define _UAPI_LINUX_RPMSG_RPC_H_ + +#ifndef __KERNEL__ +#include +#include +#endif +#include +#include + +/** + * struct rppc_buf_fds - rppc buffer registration/deregistration + * @num: number of file descriptors + * @fds: pointer to the array holding the file descriptors + */ +struct rppc_buf_fds { + __u32 num; + __s32 *fds; +}; + +/* + * ioctl definitions + */ +#define RPPC_IOC_MAGIC 'r' +#define RPPC_IOC_CREATE _IOW(RPPC_IOC_MAGIC, 1, char *) +#define RPPC_IOC_BUFREGISTER _IOW(RPPC_IOC_MAGIC, 2, struct rppc_buf_fds) +#define RPPC_IOC_BUFUNREGISTER _IOW(RPPC_IOC_MAGIC, 3, struct rppc_buf_fds) +#define RPPC_IOC_MAXNR (4) + +#define RPPC_MAX_PARAMETERS (10) +#define RPPC_MAX_TRANSLATIONS (1024) +#define RPPC_MAX_INST_NAMELEN (48) + +/** + * enum rppc_param_type - RPC function parameter type + * @RPPC_PARAM_TYPE_UNKNOWN: unrecognized parameter + * @RPPC_PARAM_TYPE_ATOMIC: an atomic data type, 1 byte to architecture limit + * sized bytes + * @RPPC_PARAM_TYPE_PTR: a pointer to shared memory. The fd field in the + * structures rppc_param and rppc_param_translation must + * contain the file descriptor of the associated dma_buf + * @RPPC_PARAM_TYPE_STRUCT: (unsupported) a structure type. Will be architecture + * width aligned in memory + * + * These enum values are used to identify the parameter type for every + * parameter argument of the remote function. + */ +enum rppc_param_type { + RPPC_PARAM_TYPE_UNKNOWN = 0, + RPPC_PARAM_TYPE_ATOMIC, + RPPC_PARAM_TYPE_PTR, + RPPC_PARAM_TYPE_STRUCT, +}; + +/** + * struct rppc_param_translation - pointer translation helper structure + * @index: index of the parameter where the translation needs to be done in. + * used for computing the primary offset and mapping into kernel + * the page from the buffer referred to in the corresponding parameter + * @offset: offset from the primary base pointer to the pointer to translate. + * This is the secondary offset, and used either for mentioning the + * offset from an structure array element base, or within a single + * structure which itself is at an offset in an allocated buffer + * @base: the base user virtual address of the pointer to translate (used to + * calculate translated pointer offset) + * @fd: dma_buf file descriptor of the allocated buffer pointer within which + * the translated pointer is present + */ +struct rppc_param_translation { + __u32 index; + ptrdiff_t offset; + size_t base; + __s32 fd; +}; + +/** + * struct rppc_param - descriptor structure for each parameter + * @type: type of the parameter, as dictated by enum rppc_param_type + * @size: size of the data (for atomic types) or size of the containing + * structure in which translations are performed + * @data: either the parameter value itself (for atomic type) or + * the actual user space pointer address to the data (for pointer type) + * @base: the base user space pointer address of the original allocated buffer, + * providing a reference if data has the pointer that is at an offset + * from the original pointer + * @fd: file descriptor of the exported allocation (will be used to + * import the associated dma_buf within the driver). + */ +struct rppc_param { + __u32 type; + size_t size; + size_t data; + size_t base; + __s32 fd; +}; + +/** + * struct rppc_function - descriptor structure for the remote function + * @fxn_id: index of the function to invoke on the opened rppc device + * @num_params: number of parameters filled in the params field + * @params: array of parameter descriptor structures + * @num_translations: number of in-place translations to be performed within + * the arguments. + * @translations: an open array of the translation descriptor structures, whose + * length is given in @num_translations. Used for translating + * the pointers within the function data. + * + * This is the primary descriptor structure passed down from the userspace, + * describing the function, its parameter arguments and the needed translations. + */ +struct rppc_function { + __u32 fxn_id; + __u32 num_params; + struct rppc_param params[RPPC_MAX_PARAMETERS]; + __u32 num_translations; + struct rppc_param_translation translations[0]; +}; + +/** + * struct rppc_function_return - function return status descriptor structure + * @fxn_id: index of the function invoked on the opened rppc device + * @status: return value of the executed function + */ +struct rppc_function_return { + __u32 fxn_id; + __u32 status; +}; + +/** + * struct rppc_create_instance - rppc channel connector helper + * @name: Name of the rppc server device to establish a connection with + */ +struct rppc_create_instance { + char name[RPPC_MAX_INST_NAMELEN]; +}; + +/* + * helper macros for manipulating the function index in the marshalled packet + */ +#define RPPC_DESC_EXEC_SYNC (0x0100) +#define RPPC_DESC_TYPE_MASK (0x0F00) + +/* + * helper macros for manipulating the function index in the marshalled packet. + * The remote functions are offset by one relative to the client + * XXX: Remove the relative offset + */ +#define RPPC_SET_FXN_IDX(idx) (((idx) + 1) | 0x80000000) +#define RPPC_FXN_MASK(idx) (((idx) - 1) & 0x7FFFFFFF) + +/** + * struct rppc_packet - the actual marshalled packet + * @desc: type of function execution, currently only synchronous function + * invocations are supported + * @msg_id: an incremental message index identifier + * @flags: a combination of job id and pool id of the worker threads + * of the server + * @fxn_id: id of the function to execute + * @result: result of the remotely executed function + * @data_size: size of the payload packet + * @data: variable payload, containing the marshalled function data. + * + * This is actually a condensed structure of the Remote Command Messaging + * (RCM) structure. The initial fields of the structure are used by the + * remote-side server to schedule the execution of the function. The actual + * variable payload data starts from the .data field. This marshalled packet + * is the payload for a rpmsg message. + * + * XXX: remove or mask unneeded fields, some fields can be stripped down + */ +struct rppc_packet { + __u16 desc; + __u16 msg_id; + __u32 flags; + __u32 fxn_id; + __s32 result; + __u32 data_size; + __u8 data[0]; +} __packed; + +#endif /* _UAPI_LINUX_RPMSG_RPC_H_ */ diff -Naur --no-dereference a/include/uapi/linux/rpmsg_socket.h b/include/uapi/linux/rpmsg_socket.h --- a/include/uapi/linux/rpmsg_socket.h 1969-12-31 19:00:00.000000000 -0500 +++ b/include/uapi/linux/rpmsg_socket.h 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Remote processor messaging sockets + * + * Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + * Ohad Ben-Cohen + * Suman Anna + */ + +#ifndef _UAPI_RPMSG_SOCKET_H +#define _UAPI_RPMSG_SOCKET_H + +#include +#include + +/* user space needs this */ +#ifndef AF_RPMSG +#define AF_RPMSG 45 +#define PF_RPMSG AF_RPMSG +#endif + +struct sockaddr_rpmsg { + __kernel_sa_family_t family; + __u32 vproc_id; + __u32 addr; +}; + +#define RPMSG_LOCALHOST ((__u32)~0UL) + +#endif /* _UAPI_RPMSG_SOCKET_H */ diff -Naur --no-dereference a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h --- a/include/uapi/linux/serial_core.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/linux/serial_core.h 2022-01-06 12:45:53.834318188 -0500 @@ -279,4 +279,7 @@ /* Freescale LINFlexD UART */ #define PORT_LINFLEXUART 122 +/* PRU SW UART */ +#define PORT_PSUART 123 + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff -Naur --no-dereference a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h --- a/include/uapi/linux/sockios.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/linux/sockios.h 2022-01-06 12:45:53.834318188 -0500 @@ -153,6 +153,9 @@ #define SIOCSHWTSTAMP 0x89b0 /* set and get config */ #define SIOCGHWTSTAMP 0x89b1 /* get config */ +/* Switch config calls: parameters in linux/net_switch_config.h */ +#define SIOCSWITCHCONFIG 0x89c0 + /* Device private ioctl calls */ /* diff -Naur --no-dereference a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h --- a/include/uapi/linux/v4l2-subdev.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/linux/v4l2-subdev.h 2022-01-06 12:45:53.834318188 -0500 @@ -49,7 +49,8 @@ __u32 which; __u32 pad; struct v4l2_mbus_framefmt format; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -62,7 +63,8 @@ __u32 which; __u32 pad; struct v4l2_rect rect; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; #define V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE 0x00000001 @@ -85,7 +87,8 @@ __u32 code; __u32 which; __u32 flags; - __u32 reserved[7]; + __u32 stream; + __u32 reserved[6]; }; /** @@ -104,7 +107,8 @@ __u32 min_height; __u32 max_height; __u32 which; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -115,7 +119,8 @@ struct v4l2_subdev_frame_interval { __u32 pad; struct v4l2_fract interval; - __u32 reserved[9]; + __u32 stream; + __u32 reserved[8]; }; /** @@ -136,7 +141,8 @@ __u32 height; struct v4l2_fract interval; __u32 which; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -160,7 +166,8 @@ __u32 target; __u32 flags; struct v4l2_rect r; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -178,6 +185,59 @@ /* The v4l2 sub-device video device node is registered in read-only mode. */ #define V4L2_SUBDEV_CAP_RO_SUBDEV 0x00000001 +/** + * Is the route active? An active route will start when streaming is enabled + * on a video node. + */ +#define V4L2_SUBDEV_ROUTE_FL_ACTIVE BIT(0) + +/** + * Is the route immutable, i.e. can it be activated and inactivated? + * Set by the driver. + */ +#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE BIT(1) + +/** + * Is the route a source endpoint? A source endpoint route doesn't come + * from "anywhere", and the sink_pad and sink_stream fields are unused. + * Set by the driver. + */ +#define V4L2_SUBDEV_ROUTE_FL_SOURCE BIT(2) + +/** + * struct v4l2_subdev_route - A route inside a subdev + * + * @sink_pad: the sink pad index + * @sink_stream: the sink stream identifier + * @source_pad: the source pad index + * @source_stream: the source stream identifier + * @flags: route flags V4L2_SUBDEV_ROUTE_FL_* + * @reserved: drivers and applications must zero this array + */ +struct v4l2_subdev_route { + __u32 sink_pad; + __u32 sink_stream; + __u32 source_pad; + __u32 source_stream; + __u32 flags; + __u32 reserved[5]; +}; + +/** + * struct v4l2_subdev_routing - Subdev routing information + * + * @which: configuration type (from enum v4l2_subdev_format_whence) + * @routes: pointer to the routes array + * @num_routes: the total number of routes in the routes array + * @reserved: drivers and applications must zero this array + */ +struct v4l2_subdev_routing { + __u32 which; + __u64 routes; + __u32 num_routes; + __u32 reserved[5]; +}; + /* Backwards compatibility define --- to be removed */ #define v4l2_subdev_edid v4l2_edid @@ -193,6 +253,8 @@ #define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) #define VIDIOC_SUBDEV_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection) #define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) +#define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing) +#define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing) /* The following ioctls are identical to the ioctls in videodev2.h */ #define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id) #define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id) diff -Naur --no-dereference a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h --- a/include/uapi/linux/videodev2.h 2021-12-17 04:14:42.000000000 -0500 +++ b/include/uapi/linux/videodev2.h 2022-01-06 12:45:53.834318188 -0500 @@ -740,6 +740,8 @@ #define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */ #define V4L2_PIX_FMT_SUNXI_TILED_NV12 v4l2_fourcc('S', 'T', '1', '2') /* Sunxi Tiled NV12 Format */ #define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */ +#define V4L2_PIX_FMT_TI1210 v4l2_fourcc('T', 'I', '1', '2') /* TI NV12 10-bit, two bytes per channel */ +#define V4L2_PIX_FMT_TI1610 v4l2_fourcc('T', 'I', '1', '6') /* TI NV16 10-bit, two bytes per channel */ /* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */ #define V4L2_PIX_FMT_IPU3_SBGGR10 v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */ diff -Naur --no-dereference a/jenkins_build.sh b/jenkins_build.sh --- a/jenkins_build.sh 1969-12-31 19:00:00.000000000 -0500 +++ b/jenkins_build.sh 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,47 @@ +#!/bin/bash + +#git clone -b 5.10 https://github.com/beagleboard/linux --depth=10 +#cd ./linux + +if [ ! -d ./gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/ ] ; then + rm -rf ./gcc-* || true + #wget -c ${site}/${version}/${filename} + wget -c http://192.168.3.125/jenkins/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz + tar xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz +fi + +export CC=`pwd`/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- + +make ARCH=arm CROSS_COMPILE=${CC} clean +make ARCH=arm CROSS_COMPILE=${CC} bb.org_defconfig + +echo "[make ARCH=arm -j4 CROSS_COMPILE=\"${binary}\" zImage]" +make ARCH=arm -j4 CROSS_COMPILE="ccache ${CC}" zImage +if [ ! -f arch/arm/boot/zImage ] ; then + echo "failed: [arch/arm/boot/zImage]" + exit 1 +fi + +echo "[make ARCH=arm -j4 CROSS_COMPILE=\"${binary}\" modules]" +make ARCH=arm -j4 CROSS_COMPILE="ccache ${CC}" modules +if [ ! -f drivers/spi/spidev.ko ] ; then + echo "failed: [drivers/spi/spidev.ko]" + exit 1 +fi + +echo "[make ARCH=arm CROSS_COMPILE=\"${binary}\" dtbs]" +make ARCH=arm CROSS_COMPILE="ccache ${CC}" dtbs +if [ ! -f arch/arm/boot/dts/am335x-boneblack.dtb ] ; then + echo "failed: [arch/arm/boot/dts/am335x-boneblack.dtb]" + exit 1 +else + if [ -f arch/arm/boot/dts/am335x-pocketbeagle.dts ] ; then + if [ ! -f arch/arm/boot/dts/am335x-pocketbeagle.dtb ] ; then + echo "failed: [arch/arm/boot/dts/am335x-pocketbeagle.dtb]" + exit 1 + fi + fi +fi + +make ARCH=arm CROSS_COMPILE=${CC} clean +rm -rf ./gcc-* || true diff -Naur --no-dereference a/Jenkinsfile b/Jenkinsfile --- a/Jenkinsfile 1969-12-31 19:00:00.000000000 -0500 +++ b/Jenkinsfile 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,11 @@ +pipeline { + agent { label 'amd64'} + + stages { + stage('Build') { + steps { + sh '/bin/bash ./jenkins_build.sh' + } + } + } +} diff -Naur --no-dereference a/kernel/fork.c b/kernel/fork.c --- a/kernel/fork.c 2021-12-17 04:14:42.000000000 -0500 +++ b/kernel/fork.c 2022-01-06 12:45:53.834318188 -0500 @@ -554,7 +554,7 @@ struct inode *inode = file_inode(file); struct address_space *mapping = file->f_mapping; - get_file(file); + vma_get_file(tmp); if (tmp->vm_flags & VM_DENYWRITE) put_write_access(inode); i_mmap_lock_write(mapping); diff -Naur --no-dereference a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c --- a/kernel/irq/irqdomain.c 2021-12-17 04:14:42.000000000 -0500 +++ b/kernel/irq/irqdomain.c 2022-01-06 12:45:53.834318188 -0500 @@ -734,9 +734,8 @@ return 0; } -static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, - unsigned int count, - struct irq_fwspec *fwspec) +void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, unsigned int count, + struct irq_fwspec *fwspec) { int i; @@ -746,6 +745,7 @@ for (i = 0; i < count; i++) fwspec->param[i] = args[i]; } +EXPORT_SYMBOL_GPL(of_phandle_args_to_fwspec); unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec) { diff -Naur --no-dereference a/kernel/kthread.c b/kernel/kthread.c --- a/kernel/kthread.c 2021-12-17 04:14:42.000000000 -0500 +++ b/kernel/kthread.c 2022-01-06 12:45:53.834318188 -0500 @@ -243,15 +243,8 @@ if (!test_bit(KTHREAD_SHOULD_PARK, &self->flags)) break; - /* - * Thread is going to call schedule(), do not preempt it, - * or the caller of kthread_park() may spend more time in - * wait_task_inactive(). - */ - preempt_disable(); complete(&self->parked); - schedule_preempt_disabled(); - preempt_enable(); + schedule(); } __set_current_state(TASK_RUNNING); } @@ -297,14 +290,8 @@ /* OK, tell user we're spawned, wait for stop or wakeup */ __set_current_state(TASK_UNINTERRUPTIBLE); create->result = current; - /* - * Thread is going to call schedule(), do not preempt it, - * or the creator may spend more time in wait_task_inactive(). - */ - preempt_disable(); complete(done); - schedule_preempt_disabled(); - preempt_enable(); + schedule(); ret = -EINTR; if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) { diff -Naur --no-dereference a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c --- a/kernel/locking/lockdep.c 2021-12-17 04:14:42.000000000 -0500 +++ b/kernel/locking/lockdep.c 2022-01-06 12:45:53.834318188 -0500 @@ -188,7 +188,7 @@ struct lock_class lock_classes[MAX_LOCKDEP_KEYS]; static DECLARE_BITMAP(lock_classes_in_use, MAX_LOCKDEP_KEYS); -static inline struct lock_class *hlock_class(struct held_lock *hlock) +inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock) { unsigned int class_idx = hlock->class_idx; @@ -209,6 +209,8 @@ */ return lock_classes + class_idx; } +EXPORT_SYMBOL_GPL(lockdep_hlock_class); +#define hlock_class(hlock) lockdep_hlock_class(hlock) #ifdef CONFIG_LOCK_STAT static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], cpu_lock_stats); diff -Naur --no-dereference a/kernel/task_work.c b/kernel/task_work.c --- a/kernel/task_work.c 2021-12-17 04:14:42.000000000 -0500 +++ b/kernel/task_work.c 2022-01-06 12:45:53.834318188 -0500 @@ -154,3 +154,4 @@ } while (work); } } +EXPORT_SYMBOL_GPL(task_work_run); diff -Naur --no-dereference a/lib/crypto/sha256.c b/lib/crypto/sha256.c --- a/lib/crypto/sha256.c 2021-12-17 04:14:42.000000000 -0500 +++ b/lib/crypto/sha256.c 2022-01-06 12:45:53.834318188 -0500 @@ -43,7 +43,7 @@ W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; } -static void sha256_transform(u32 *state, const u8 *input) +void sha256_transform(u32 *state, const u8 *input) { u32 a, b, c, d, e, f, g, h, t1, t2; u32 W[64]; @@ -205,6 +205,7 @@ a = b = c = d = e = f = g = h = t1 = t2 = 0; memzero_explicit(W, 64 * sizeof(u32)); } +EXPORT_SYMBOL(sha256_transform); void sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len) { diff -Naur --no-dereference a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS 2021-12-17 04:14:42.000000000 -0500 +++ b/MAINTAINERS 2022-01-06 12:45:53.806318073 -0500 @@ -2609,8 +2609,8 @@ F: drivers/power/reset/keystone-reset.c ARM/TEXAS INSTRUMENTS K3 ARCHITECTURE -M: Tero Kristo M: Nishanth Menon +M: Tero Kristo L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/arm/ti/k3.yaml @@ -3009,6 +3009,19 @@ F: include/uapi/linux/audit.h F: kernel/audit* +AUFS (advanced multi layered unification filesystem) FILESYSTEM +M: "J. R. Okajima" +L: aufs-users@lists.sourceforge.net (members only) +L: linux-unionfs@vger.kernel.org +S: Supported +W: http://aufs.sourceforge.net +T: git://github.com/sfjro/aufs4-linux.git +F: Documentation/ABI/testing/debugfs-aufs +F: Documentation/ABI/testing/sysfs-aufs +F: Documentation/filesystems/aufs/ +F: fs/aufs/ +F: include/uapi/linux/aufs_type.h + AUXILIARY DISPLAY DRIVERS M: Miguel Ojeda Sandonis S: Maintained @@ -3864,7 +3877,8 @@ CADENCE USB3 DRD IP DRIVER M: Peter Chen M: Pawel Laszczak -M: Roger Quadros +R: Roger Quadros +R: Aswath Govindraju L: linux-usb@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git @@ -3911,7 +3925,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git F: Documentation/devicetree/bindings/net/can/ +F: Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml F: drivers/net/can/ +F: drivers/phy/phy-can-transceiver.c F: include/linux/can/dev.h F: include/linux/can/led.h F: include/linux/can/platform/ @@ -6413,9 +6429,9 @@ F: drivers/edac/skx_*.c EDAC-TI -M: Tero Kristo +M: Tero Kristo L: linux-edac@vger.kernel.org -S: Maintained +S: Odd Fixes F: drivers/edac/ti_edac.c EDIROL UA-101/UA-1000 DRIVER @@ -12654,7 +12670,7 @@ F: include/uapi/misc/ocxl.h OMAP AUDIO SUPPORT -M: Peter Ujfalusi +M: Peter Ujfalusi M: Jarkko Nikula L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linux-omap@vger.kernel.org @@ -12698,7 +12714,7 @@ F: drivers/video/fbdev/omap/ OMAP GENERAL PURPOSE MEMORY CONTROLLER SUPPORT -M: Roger Quadros +M: Roger Quadros M: Tony Lindgren L: linux-omap@vger.kernel.org S: Maintained @@ -17282,7 +17298,7 @@ F: drivers/irqchip/irq-xtensa-* TEXAS INSTRUMENTS ASoC DRIVERS -M: Peter Ujfalusi +M: Peter Ujfalusi L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/soc/ti/ @@ -17294,9 +17310,22 @@ F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt F: drivers/iio/dac/ti-dac7612.c +TEXAS INSTRUMENTS DMA DRIVERS +M: Peter Ujfalusi +L: dmaengine@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt +F: Documentation/devicetree/bindings/dma/ti-edma.txt +F: Documentation/devicetree/bindings/dma/ti/ +F: drivers/dma/ti/ +X: drivers/dma/ti/cppi41.c +F: include/linux/dma/k3-udma-glue.h +F: include/linux/dma/ti-cppi5.h +F: include/linux/dma/k3-psil.h + TEXAS INSTRUMENTS' SYSTEM CONTROL INTERFACE (TISCI) PROTOCOL DRIVER M: Nishanth Menon -M: Tero Kristo +M: Tero Kristo M: Santosh Shilimkar L: linux-arm-kernel@lists.infradead.org S: Maintained @@ -17434,12 +17463,19 @@ F: drivers/clk/clk-cdce706.c TI CLOCK DRIVER -M: Tero Kristo +M: Tero Kristo L: linux-omap@vger.kernel.org -S: Maintained +S: Odd Fixes F: drivers/clk/ti/ F: include/linux/clk/ti.h +TI J721E CSI2RX DRIVER +M: Pratyush Yadav +L: linux-media@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/media/ti,j721e-csi2rx.yaml +F: drivers/media/platform/ti/j721e-csi2rx/ + TI DAVINCI MACHINE SUPPORT M: Sekhar Nori R: Bartosz Golaszewski @@ -17462,7 +17498,7 @@ M: Keerthy L: linux-gpio@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/gpio/gpio-davinci.txt +F: Documentation/devicetree/bindings/gpio/gpio-davinci.yaml F: drivers/gpio/gpio-davinci.c TI DAVINCI SERIES MEDIA DRIVER @@ -17573,7 +17609,7 @@ F: drivers/nfc/trf7970a.c TI TWL4030 SERIES SOC CODEC DRIVER -M: Peter Ujfalusi +M: Peter Ujfalusi L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/soc/codecs/twl4030* @@ -17586,7 +17622,8 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ F: Documentation/devicetree/bindings/media/ti,cal.yaml F: Documentation/devicetree/bindings/media/ti,vpe.yaml -F: drivers/media/platform/ti-vpe/ +F: drivers/media/platform/ti/cal/ +F: drivers/media/platform/ti/vpe/ TI WILINK WIRELESS DRIVERS L: linux-wireless@vger.kernel.org diff -Naur --no-dereference a/Makefile b/Makefile --- a/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/Makefile 2022-01-06 12:45:53.806318073 -0500 @@ -1354,6 +1354,9 @@ %.dtb: include/config/kernel.release scripts_dtc $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@ +%.dtbo: include/config/kernel.release scripts_dtc + $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@ + PHONY += dtbs dtbs_install dtbs_check dtbs: include/config/kernel.release scripts_dtc $(Q)$(MAKE) $(build)=$(dtstree) @@ -1833,7 +1836,7 @@ @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ \( -name '*.[aios]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '*.ko.*' \ - -o -name '*.dtb' -o -name '*.dtb.S' -o -name '*.dt.yaml' \ + -o -name '*.dtb' -o -name '*.dtbo' -o -name '*.dtb.S' -o -name '*.dt.yaml' \ -o -name '*.dwo' -o -name '*.lst' \ -o -name '*.su' -o -name '*.mod' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ diff -Naur --no-dereference a/mm/filemap.c b/mm/filemap.c --- a/mm/filemap.c 2021-12-17 04:14:42.000000000 -0500 +++ b/mm/filemap.c 2022-01-06 12:45:53.834318188 -0500 @@ -2913,7 +2913,7 @@ vm_fault_t ret = VM_FAULT_LOCKED; sb_start_pagefault(inode->i_sb); - file_update_time(vmf->vma->vm_file); + vma_file_update_time(vmf->vma); lock_page(page); if (page->mapping != inode->i_mapping) { unlock_page(page); diff -Naur --no-dereference a/mm/Makefile b/mm/Makefile --- a/mm/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/mm/Makefile 2022-01-06 12:45:53.834318188 -0500 @@ -52,7 +52,7 @@ mm_init.o percpu.o slab_common.o \ compaction.o vmacache.o \ interval_tree.o list_lru.o workingset.o \ - debug.o gup.o $(mmu-y) + prfile.o debug.o gup.o $(mmu-y) # Give 'page_alloc' its own module-parameter namespace page-alloc-y := page_alloc.o diff -Naur --no-dereference a/mm/mmap.c b/mm/mmap.c --- a/mm/mmap.c 2021-12-17 04:14:42.000000000 -0500 +++ b/mm/mmap.c 2022-01-06 12:45:53.834318188 -0500 @@ -179,7 +179,7 @@ if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) - fput(vma->vm_file); + vma_fput(vma); mpol_put(vma_policy(vma)); vm_area_free(vma); return next; @@ -951,7 +951,7 @@ if (remove_next) { if (file) { uprobe_munmap(next, next->vm_start, next->vm_end); - fput(file); + vma_fput(vma); } if (next->anon_vma) anon_vma_merge(vma, next); @@ -1897,8 +1897,8 @@ return addr; unmap_and_free_vma: + vma_fput(vma); vma->vm_file = NULL; - fput(file); /* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); @@ -2757,7 +2757,7 @@ goto out_free_mpol; if (new->vm_file) - get_file(new->vm_file); + vma_get_file(new); if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); @@ -2776,7 +2776,7 @@ if (new->vm_ops && new->vm_ops->close) new->vm_ops->close(new); if (new->vm_file) - fput(new->vm_file); + vma_fput(new); unlink_anon_vmas(new); out_free_mpol: mpol_put(vma_policy(new)); @@ -2969,7 +2969,7 @@ struct vm_area_struct *vma; unsigned long populate = 0; unsigned long ret = -EINVAL; - struct file *file; + struct file *file, *prfile; pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.rst.\n", current->comm, current->pid); @@ -3044,10 +3044,27 @@ } } - file = get_file(vma->vm_file); + vma_get_file(vma); + file = vma->vm_file; + prfile = vma->vm_prfile; ret = do_mmap(vma->vm_file, start, size, prot, flags, pgoff, &populate, NULL); + if (!IS_ERR_VALUE(ret) && file && prfile) { + struct vm_area_struct *new_vma; + + new_vma = find_vma(mm, ret); + if (!new_vma->vm_prfile) + new_vma->vm_prfile = prfile; + if (new_vma != vma) + get_file(prfile); + } + /* + * two fput()s instead of vma_fput(vma), + * coz vma may not be available anymore. + */ fput(file); + if (prfile) + fput(prfile); out: mmap_write_unlock(mm); if (populate) @@ -3334,7 +3351,7 @@ if (anon_vma_clone(new_vma, vma)) goto out_free_mempol; if (new_vma->vm_file) - get_file(new_vma->vm_file); + vma_get_file(new_vma); if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); diff -Naur --no-dereference a/mm/nommu.c b/mm/nommu.c --- a/mm/nommu.c 2021-12-17 04:14:42.000000000 -0500 +++ b/mm/nommu.c 2022-01-06 12:45:53.834318188 -0500 @@ -533,7 +533,7 @@ up_write(&nommu_region_sem); if (region->vm_file) - fput(region->vm_file); + vmr_fput(region); /* IO memory and memory shared directly out of the pagecache * from ramfs/tmpfs mustn't be released here */ @@ -665,7 +665,7 @@ if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) - fput(vma->vm_file); + vma_fput(vma); put_nommu_region(vma->vm_region); vm_area_free(vma); } @@ -1188,7 +1188,7 @@ goto error_just_free; } } - fput(region->vm_file); + vmr_fput(region); kmem_cache_free(vm_region_jar, region); region = pregion; result = start; @@ -1265,10 +1265,10 @@ up_write(&nommu_region_sem); error: if (region->vm_file) - fput(region->vm_file); + vmr_fput(region); kmem_cache_free(vm_region_jar, region); if (vma->vm_file) - fput(vma->vm_file); + vma_fput(vma); vm_area_free(vma); return ret; diff -Naur --no-dereference a/mm/prfile.c b/mm/prfile.c --- a/mm/prfile.c 1969-12-31 19:00:00.000000000 -0500 +++ b/mm/prfile.c 2022-01-06 12:45:53.834318188 -0500 @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mainly for aufs which mmap(2) different file and wants to print different + * path in /proc/PID/maps. + * Call these functions via macros defined in linux/mm.h. + * + * See Documentation/filesystems/aufs/design/06mmap.txt + * + * Copyright (c) 2014-2021 Junjro R. Okajima + * Copyright (c) 2014 Ian Campbell + */ + +#include +#include +#include + +/* #define PRFILE_TRACE */ +static inline void prfile_trace(struct file *f, struct file *pr, + const char func[], int line, const char func2[]) +{ +#ifdef PRFILE_TRACE + if (pr) + pr_info("%s:%d: %s, %pD2\n", func, line, func2, f); +#endif +} + +void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], + int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + file_update_time(f); + if (f && pr) + file_update_time(pr); +} + +struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], + int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + return (f && pr) ? pr : f; +} + +void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + get_file(f); + if (f && pr) + get_file(pr); +} + +void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + fput(f); + if (f && pr) + fput(pr); +} + +#ifndef CONFIG_MMU +struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], + int line) +{ + struct file *f = region->vm_file, *pr = region->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + return (f && pr) ? pr : f; +} + +void vmr_do_fput(struct vm_region *region, const char func[], int line) +{ + struct file *f = region->vm_file, *pr = region->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + fput(f); + if (f && pr) + fput(pr); +} +#endif /* !CONFIG_MMU */ diff -Naur --no-dereference a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c --- a/net/core/dev_ioctl.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/core/dev_ioctl.c 2022-01-06 12:45:53.834318188 -0500 @@ -332,6 +332,7 @@ cmd == SIOCBRDELIF || cmd == SIOCSHWTSTAMP || cmd == SIOCGHWTSTAMP || + cmd == SIOCSWITCHCONFIG || cmd == SIOCWANDEV) { err = dev_do_ioctl(dev, ifr, cmd); } else @@ -517,6 +518,12 @@ case SIOCSIFLINK: return -ENOTTY; + case SIOCSWITCHCONFIG: + rtnl_lock(); + ret = dev_ifsioc(net, ifr, cmd); + rtnl_unlock(); + return ret; + /* * Unknown or private ioctl. */ diff -Naur --no-dereference a/net/core/skbuff.c b/net/core/skbuff.c --- a/net/core/skbuff.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/core/skbuff.c 2022-01-06 12:45:53.834318188 -0500 @@ -4730,6 +4730,7 @@ { struct sk_buff *skb; bool tsonly, opt_stats = false; + struct skb_redundant_info *sred, *orig_sred; if (!sk) return; @@ -4764,6 +4765,13 @@ skb_shinfo(skb)->tskey = skb_shinfo(orig_skb)->tskey; } + /* FIXME: should check sk flags */ + orig_sred = skb_redinfo(orig_skb); + if (orig_sred->lsdu_size) { + sred = skb_redinfo(skb); + memcpy(sred, orig_sred, sizeof(*sred)); + } + if (hwtstamps) *skb_hwtstamps(skb) = *hwtstamps; else diff -Naur --no-dereference a/net/core/sock.c b/net/core/sock.c --- a/net/core/sock.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/core/sock.c 2022-01-06 12:45:53.834318188 -0500 @@ -224,7 +224,7 @@ x "AF_IEEE802154", x "AF_CAIF" , x "AF_ALG" , \ x "AF_NFC" , x "AF_VSOCK" , x "AF_KCM" , \ x "AF_QIPCRTR", x "AF_SMC" , x "AF_XDP" , \ - x "AF_MAX" + x "AF_RPMSG" , x "AF_MAX" static const char *const af_family_key_strings[AF_MAX+1] = { _sock_locks("sk_lock-") @@ -2367,6 +2367,7 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, struct sockcm_cookie *sockc) { + struct skb_redundant_info *cred; u32 tsflags; switch (cmsg->cmsg_type) { @@ -2399,6 +2400,15 @@ case SCM_RIGHTS: case SCM_CREDENTIALS: break; + case SCM_REDUNDANT: + if (cmsg->cmsg_len != + CMSG_LEN(sizeof(struct skb_redundant_info))) + return -EINVAL; + + cred = (struct skb_redundant_info *)CMSG_DATA(cmsg); + memcpy(&sockc->redinfo, cred, + sizeof(struct skb_redundant_info)); + break; default: return -EINVAL; } @@ -3182,6 +3192,7 @@ struct sock_exterr_skb *serr; struct sk_buff *skb; int copied, err; + struct skb_redundant_info *sred; err = -EAGAIN; skb = sock_dequeue_err_skb(sk); @@ -3199,6 +3210,9 @@ sock_recv_timestamp(msg, sk, skb); + sred = skb_redinfo(skb); + put_cmsg(msg, SOL_SOCKET, SCM_REDUNDANT, sizeof(*sred), sred); + serr = SKB_EXT_ERR(skb); put_cmsg(msg, level, type, sizeof(serr->ee), &serr->ee); diff -Naur --no-dereference a/net/ethtool/common.c b/net/ethtool/common.c --- a/net/ethtool/common.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/ethtool/common.c 2022-01-06 12:45:53.838318206 -0500 @@ -68,6 +68,10 @@ [NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload", [NETIF_F_GRO_FRAGLIST_BIT] = "rx-gro-list", [NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload", + [NETIF_F_HW_HSR_TAG_INS_BIT] = "hsr-tag-ins-offload", + [NETIF_F_HW_HSR_TAG_RM_BIT] = "hsr-tag-rm-offload", + [NETIF_F_HW_HSR_FWD_BIT] = "hsr-fwd-offload", + [NETIF_F_HW_HSR_DUP_BIT] = "hsr-dup-offload", }; const char @@ -330,6 +334,7 @@ = legacy_settings->eth_tp_mdix_ctrl; return retval; } +EXPORT_SYMBOL_GPL(convert_legacy_settings_to_link_ksettings); int __ethtool_get_link(struct net_device *dev) { diff -Naur --no-dereference a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c --- a/net/ethtool/ioctl.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/ethtool/ioctl.c 2022-01-06 12:45:53.838318206 -0500 @@ -366,7 +366,7 @@ /* return false if ksettings link modes had higher bits * set. legacy_settings always updated (best effort) */ -static bool +bool convert_link_ksettings_to_legacy_settings( struct ethtool_cmd *legacy_settings, const struct ethtool_link_ksettings *link_ksettings) @@ -408,6 +408,7 @@ = link_ksettings->base.transceiver; return retval; } +EXPORT_SYMBOL_GPL(convert_link_ksettings_to_legacy_settings); /* number of 32-bit words to store the user's link mode bitmaps */ #define __ETHTOOL_LINK_MODE_MASK_NU32 \ diff -Naur --no-dereference a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c --- a/net/hsr/hsr_debugfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_debugfs.c 2022-01-06 12:45:53.838318206 -0500 @@ -20,7 +20,7 @@ #include "hsr_main.h" #include "hsr_framereg.h" -static struct dentry *hsr_debugfs_root_dir; +static struct dentry *hsr_debugfs_node_tbl_root; /* hsr_node_table_show - Formats and prints node_table entries */ static int @@ -67,28 +67,78 @@ struct hsr_priv *priv = netdev_priv(dev); struct dentry *d; - d = debugfs_rename(hsr_debugfs_root_dir, priv->node_tbl_root, - hsr_debugfs_root_dir, dev->name); + d = debugfs_rename(hsr_debugfs_node_tbl_root, priv->node_tbl_root, + hsr_debugfs_node_tbl_root, dev->name); if (IS_ERR(d)) netdev_warn(dev, "failed to rename\n"); else priv->node_tbl_root = d; } -/* hsr_debugfs_init - create hsr node_table file for dumping - * the node table +/* hsr_lre_info_show - Formats and prints debug info in the device + */ +static int +hsr_lre_info_show(struct seq_file *sfp, void *data) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + bool prp = priv->prot_version > HSR_V1; + + seq_puts(sfp, "LRE debug information\n"); + seq_printf(sfp, "Protocol : %s\n", prp ? "PRP" : "HSR"); + seq_printf(sfp, "net_id: %d\n", priv->net_id); + seq_printf(sfp, "Rx Offloaded: %s\n", + priv->rx_offloaded ? "Yes" : "No"); + seq_printf(sfp, "vlan tag used in sv frame : %s\n", + priv->use_vlan_for_sv ? "Yes" : "No"); + if (priv->use_vlan_for_sv) { + seq_printf(sfp, "SV Frame VID : %d\n", + priv->sv_frame_vid); + seq_printf(sfp, "SV Frame PCP : %d\n", + priv->sv_frame_pcp); + seq_printf(sfp, "SV Frame DEI : %d\n", + priv->sv_frame_dei); + } + seq_printf(sfp, "cnt_tx_sup = %d\n", priv->dbg_stats.cnt_tx_sup); + seq_printf(sfp, "cnt_rx_sup_A = %d\n", priv->dbg_stats.cnt_rx_sup_a); + seq_printf(sfp, "cnt_rx_sup_B = %d\n", priv->dbg_stats.cnt_rx_sup_b); + seq_printf(sfp, "disable SV Frame = %d\n", priv->disable_sv_frame); + seq_puts(sfp, "\n"); + return 0; +} + +/* hsr_lre_info_open - open lre info file + * + * Description: + * This routine opens a debugfs file lre_info of specific hsr or + * prp device + */ +static int +hsr_lre_info_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hsr_lre_info_show, inode->i_private); +} + +static const struct file_operations hsr_lre_info_fops = { + .owner = THIS_MODULE, + .open = hsr_lre_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* hsr_debugfs_init - create debugfs to dump lre specific debug information * * Description: - * When debugfs is configured this routine sets up the node_table file per - * hsr device for dumping the node_table entries + * dump lre info of hsr or prp device */ void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) { struct dentry *de = NULL; - de = debugfs_create_dir(hsr_dev->name, hsr_debugfs_root_dir); + de = debugfs_create_dir(hsr_dev->name, hsr_debugfs_node_tbl_root); if (IS_ERR(de)) { - pr_err("Cannot create hsr debugfs directory\n"); + pr_err("Cannot create hsr debugfs root directory %s\n", + hsr_dev->name); return; } @@ -99,11 +149,28 @@ &hsr_node_table_fops); if (IS_ERR(de)) { pr_err("Cannot create hsr node_table file\n"); - debugfs_remove(priv->node_tbl_root); - priv->node_tbl_root = NULL; - return; + goto error_nt; } -} + + priv->node_tbl_file = de; + + de = debugfs_create_file("lre_info", S_IFREG | 0444, + priv->node_tbl_root, priv, &hsr_lre_info_fops); + if (IS_ERR(de)) { + pr_err("Cannot create hsr-prp lre_info file\n"); + goto error_lre_info; + } + priv->lre_info_file = de; + + return; + +error_lre_info: + debugfs_remove(priv->node_tbl_file); + priv->node_tbl_file = NULL; +error_nt: + debugfs_remove(priv->node_tbl_root); + priv->node_tbl_root = NULL; +} /* end of hst_debugfs_init */ /* hsr_debugfs_term - Tear down debugfs intrastructure * @@ -114,21 +181,25 @@ void hsr_debugfs_term(struct hsr_priv *priv) { - debugfs_remove_recursive(priv->node_tbl_root); + debugfs_remove(priv->node_tbl_file); + priv->node_tbl_file = NULL; + debugfs_remove(priv->lre_info_file); + priv->lre_info_file = NULL; + debugfs_remove(priv->node_tbl_root); priv->node_tbl_root = NULL; } void hsr_debugfs_create_root(void) { - hsr_debugfs_root_dir = debugfs_create_dir("hsr", NULL); - if (IS_ERR(hsr_debugfs_root_dir)) { + hsr_debugfs_node_tbl_root = debugfs_create_dir("hsr", NULL); + if (IS_ERR(hsr_debugfs_node_tbl_root)) { pr_err("Cannot create hsr debugfs root directory\n"); - hsr_debugfs_root_dir = NULL; + hsr_debugfs_node_tbl_root = NULL; } } void hsr_debugfs_remove_root(void) { /* debugfs_remove() internally checks NULL and ERROR */ - debugfs_remove(hsr_debugfs_root_dir); + debugfs_remove(hsr_debugfs_node_tbl_root); } diff -Naur --no-dereference a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c --- a/net/hsr/hsr_device.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_device.c 2022-01-06 12:45:53.838318206 -0500 @@ -12,12 +12,19 @@ #include #include #include +#include #include "hsr_device.h" #include "hsr_slave.h" #include "hsr_framereg.h" #include "hsr_main.h" #include "hsr_forward.h" +static inline bool is_slave_port(struct hsr_port *p) +{ + return (p->type == HSR_PT_SLAVE_A) || + (p->type == HSR_PT_SLAVE_B); +} + static bool is_admin_up(struct net_device *dev) { return dev && (dev->flags & IFF_UP); @@ -118,9 +125,142 @@ if (mtu_max < HSR_HLEN) return 0; - return mtu_max - HSR_HLEN; + + /* For offloaded keep the mtu same as ETH_DATA_LEN as + * h/w is expected to extend the frame to accommodate RCT + * or TAG + */ + if (!hsr->rx_offloaded) + return mtu_max - HSR_HLEN; + + return mtu_max; +} + +int hsr_lredev_attr_get(struct hsr_priv *hsr, struct lredev_attr *attr) +{ + struct hsr_port *port_a = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + struct net_device *slave_a_dev; + + if (!port_a) + return -EINVAL; + + slave_a_dev = port_a->dev; + if (slave_a_dev && slave_a_dev->lredev_ops && + slave_a_dev->lredev_ops->lredev_attr_get) + return slave_a_dev->lredev_ops->lredev_attr_get(slave_a_dev, + attr); + return -EINVAL; +} + +int hsr_lredev_attr_set(struct hsr_priv *hsr, struct lredev_attr *attr) +{ + struct hsr_port *port_a = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + struct net_device *slave_a_dev; + + if (!port_a) + return -EINVAL; + + slave_a_dev = port_a->dev; + if (slave_a_dev && slave_a_dev->lredev_ops && + slave_a_dev->lredev_ops->lredev_attr_set) + return slave_a_dev->lredev_ops->lredev_attr_set(slave_a_dev, + attr); + return -EINVAL; +} + +static int _hsr_lredev_get_node_table(struct hsr_priv *hsr, + struct lre_node_table_entry table[], + int size) +{ + struct hsr_node *node; + int i = 0; + + rcu_read_lock(); + + list_for_each_entry_rcu(node, &hsr->node_db, mac_list) { + if (hsr_addr_is_self(hsr, node->macaddress_A)) + continue; + /* SANs are not shown as part of Node Table */ + if (node->san_a || node->san_b) + continue; + memcpy(&table[i].mac_address[0], + &node->macaddress_A[0], ETH_ALEN); + table[i].time_last_seen_a = node->time_in[HSR_PT_SLAVE_A]; + table[i].time_last_seen_b = node->time_in[HSR_PT_SLAVE_B]; + if (hsr->prot_version == PRP_V1) + table[i].node_type = IEC62439_3_DANP; + else if (hsr->prot_version <= HSR_V1) + table[i].node_type = IEC62439_3_DANH; + else + continue; + i++; + } + rcu_read_unlock(); + + return i; +} + +int hsr_lredev_get_node_table(struct hsr_priv *hsr, + struct lre_node_table_entry table[], + int size) +{ + struct hsr_port *port_a = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + struct net_device *slave_a_dev; + int ret = -EINVAL; + + if (!port_a) + return ret; + + if (!hsr->rx_offloaded) + return _hsr_lredev_get_node_table(hsr, table, size); + + slave_a_dev = port_a->dev; + + if (slave_a_dev && slave_a_dev->lredev_ops && + slave_a_dev->lredev_ops->lredev_get_node_table) + ret = + slave_a_dev->lredev_ops->lredev_get_node_table(slave_a_dev, + table, + size); + return ret; +} + +static int hsr_set_sv_frame_vid(struct hsr_priv *hsr, u16 vid) +{ + struct hsr_port *port_a = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + struct net_device *slave_a_dev; + int ret = -EINVAL; + + if (!port_a) + return ret; + + slave_a_dev = port_a->dev; + + /* TODO can we use vlan_vid_add() here?? */ + if (slave_a_dev && slave_a_dev->lredev_ops && + slave_a_dev->lredev_ops->lredev_set_sv_vlan_id) + slave_a_dev->lredev_ops->lredev_set_sv_vlan_id(slave_a_dev, + vid); + return 0; } +int hsr_lredev_get_lre_stats(struct hsr_priv *hsr, struct lre_stats *stats) +{ + struct hsr_port *port_a = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + struct net_device *slave_a_dev; + int ret = -EINVAL; + + if (!port_a) + return ret; + + slave_a_dev = port_a->dev; + + if (slave_a_dev && slave_a_dev->lredev_ops && + slave_a_dev->lredev_ops->lredev_get_stats) + ret = + slave_a_dev->lredev_ops->lredev_get_stats(slave_a_dev, stats); + return ret; +} static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) { struct hsr_priv *hsr; @@ -173,7 +313,24 @@ static int hsr_dev_close(struct net_device *dev) { - /* Nothing to do here. */ + struct hsr_port *port; + struct hsr_priv *hsr; + + hsr = netdev_priv(dev); + hsr_for_each_port(hsr, port) { + if (port->type == HSR_PT_MASTER) + continue; + switch (port->type) { + case HSR_PT_SLAVE_A: + case HSR_PT_SLAVE_B: + dev_uc_unsync(port->dev, dev); + dev_mc_unsync(port->dev, dev); + break; + default: + break; + } + } + return 0; } @@ -220,6 +377,7 @@ skb_reset_mac_header(skb); skb_reset_mac_len(skb); hsr_forward_skb(skb, master); + INC_CNT_RX_C(hsr); } else { atomic_long_inc(&dev->tx_dropped); dev_kfree_skb_any(skb); @@ -237,24 +395,39 @@ struct hsr_priv *hsr = master->hsr; struct sk_buff *skb; int hlen, tlen; + int len; + + hsr = master->hsr; + + if (hsr->disable_sv_frame) + return NULL; hlen = LL_RESERVED_SPACE(master->dev); tlen = master->dev->needed_tailroom; + len = sizeof(struct hsr_tag) + + sizeof(struct hsr_sup_tag) + + sizeof(struct hsr_sup_payload) + hlen + tlen; + + if (hsr->use_vlan_for_sv) + len += VLAN_HLEN; + /* skb size is same for PRP/HSR frames, only difference * being, for PRP it is a trailer and for HSR it is a * header */ - skb = dev_alloc_skb(sizeof(struct hsr_tag) + - sizeof(struct hsr_sup_tag) + - sizeof(struct hsr_sup_payload) + hlen + tlen); - + skb = dev_alloc_skb(len); if (!skb) return skb; skb_reserve(skb, hlen); skb->dev = master->dev; + if (hsr->use_vlan_for_sv) { + proto = ETH_P_8021Q; + skb->priority = hsr->sv_frame_pcp; + } else { + skb->priority = TC_PRIO_CONTROL; + } skb->protocol = htons(proto); - skb->priority = TC_PRIO_CONTROL; if (dev_hard_header(skb, skb->dev, proto, hsr->sup_multicast_addr, @@ -284,6 +457,8 @@ unsigned long irqflags; struct sk_buff *skb; u16 proto; + struct vlan_hdr *vhdr; + u16 vlan_tci = 0; *interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL); if (hsr->announce_count < 3 && hsr->prot_version == 0) { @@ -303,6 +478,16 @@ return; } + if (hsr->use_vlan_for_sv) { + vhdr = skb_put(skb, VLAN_HLEN); + vlan_tci = hsr->sv_frame_vid; + vlan_tci |= (hsr->sv_frame_pcp << VLAN_PRIO_SHIFT); + if (hsr->sv_frame_dei) + vlan_tci |= VLAN_CFI_MASK; + vhdr->h_vlan_TCI = htons(vlan_tci); + vhdr->h_vlan_encapsulated_proto = htons(proto); + } + if (hsr->prot_version > 0) { hsr_tag = skb_put(skb, sizeof(struct hsr_tag)); hsr_tag->encap_proto = htons(ETH_P_PRP); @@ -324,7 +509,7 @@ hsr_stag->sequence_nr = htons(hsr->sequence_nr); hsr->sequence_nr++; } - spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); + spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags); hsr_stag->HSR_TLV_type = type; /* TODO: Why 12 in HSRv0? */ @@ -335,10 +520,16 @@ hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload)); ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr); - if (skb_put_padto(skb, ETH_ZLEN + HSR_HLEN)) - return; + if (!hsr->use_vlan_for_sv) { + if (skb_put_padto(skb, ETH_ZLEN + HSR_HLEN)) + return; + } else { + if (skb_put_padto(skb, ETH_ZLEN + HSR_HLEN + VLAN_HLEN)) + return; + } hsr_forward_skb(skb, master); + INC_CNT_TX_SUP(hsr); return; } @@ -390,6 +581,7 @@ spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); hsr_forward_skb(skb, master); + INC_CNT_TX_SUP(hsr); } /* Announce (supervision frame) timer function @@ -412,10 +604,13 @@ rcu_read_unlock(); } -void hsr_del_ports(struct hsr_priv *hsr) +void hsr_del_ports(struct hsr_priv *hsr, struct net_device *hsr_dev) { struct hsr_port *port; + hsr_remove_procfs(hsr, hsr_dev); + hsr_debugfs_term(hsr); + port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); if (port) hsr_del_port(port); @@ -429,12 +624,175 @@ hsr_del_port(port); } +static void hsr_ndo_set_rx_mode(struct net_device *dev) +{ + struct hsr_port *port; + struct hsr_priv *hsr; + + hsr = netdev_priv(dev); + + hsr_for_each_port(hsr, port) { + if (port->type == HSR_PT_MASTER) + continue; + switch (port->type) { + case HSR_PT_SLAVE_A: + case HSR_PT_SLAVE_B: + dev_mc_sync_multiple(port->dev, dev); + dev_uc_sync_multiple(port->dev, dev); + break; + default: + break; + } + } +} + +static void hsr_change_rx_flags(struct net_device *dev, int change) +{ + struct hsr_port *port; + struct hsr_priv *hsr; + + hsr = netdev_priv(dev); + + hsr_for_each_port(hsr, port) { + if (port->type == HSR_PT_MASTER) + continue; + switch (port->type) { + case HSR_PT_SLAVE_A: + case HSR_PT_SLAVE_B: + if (change & IFF_ALLMULTI) + dev_set_allmulti(port->dev, + dev->flags & + IFF_ALLMULTI ? 1 : -1); + break; + default: + break; + } + } +} + +static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev, + __be16 proto, u16 vid) +{ + struct hsr_port *port; + struct hsr_priv *hsr; + int ret = 0; + + hsr = netdev_priv(dev); + + hsr_for_each_port(hsr, port) { + if (port->type == HSR_PT_MASTER) + continue; + + ret = vlan_vid_add(port->dev, proto, vid); + switch (port->type) { + case HSR_PT_SLAVE_A: + if (ret) { + netdev_err(dev, "add vid failed for Slave-A\n"); + return ret; + } + break; + + case HSR_PT_SLAVE_B: + if (ret) { + /* clean up Slave-A */ + netdev_err(dev, "add vid failed for Slave-B\n"); + vlan_vid_del(port->dev, proto, vid); + return ret; + } + break; + default: + break; + }; + } + + return 0; +} + +static int hsr_dev_ioctl(struct net_device *hsr_dev, struct ifreq *req, int cmd) +{ + struct hsr_priv *priv = netdev_priv(hsr_dev); + const struct net_device_ops *ops; + struct hsr_port *port; + int ret = -EOPNOTSUPP; + + if (cmd != SIOCSHWTSTAMP && cmd != SIOCGHWTSTAMP) + return ret; + + hsr_for_each_port(priv, port) { + if (is_slave_port(port)) { + ops = port->dev->netdev_ops; + if (ops && ops->ndo_do_ioctl) { + ret = ops->ndo_do_ioctl(port->dev, req, cmd); + + if (cmd == SIOCGHWTSTAMP || cmd < 0) + return ret; + } + } + } + + return ret; +} + +static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev, + __be16 proto, u16 vid) +{ + struct hsr_port *port; + struct hsr_priv *hsr; + + hsr = netdev_priv(dev); + + hsr_for_each_port(hsr, port) { + if (port->type == HSR_PT_MASTER) + continue; + switch (port->type) { + case HSR_PT_SLAVE_A: + case HSR_PT_SLAVE_B: + vlan_vid_del(port->dev, proto, vid); + break; + default: + break; + }; + } + + return 0; +} + static const struct net_device_ops hsr_device_ops = { .ndo_change_mtu = hsr_dev_change_mtu, .ndo_open = hsr_dev_open, .ndo_stop = hsr_dev_close, .ndo_start_xmit = hsr_dev_xmit, + .ndo_change_rx_flags = hsr_change_rx_flags, .ndo_fix_features = hsr_fix_features, + .ndo_set_rx_mode = hsr_ndo_set_rx_mode, + .ndo_vlan_rx_add_vid = hsr_ndo_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = hsr_ndo_vlan_rx_kill_vid, + .ndo_do_ioctl = hsr_dev_ioctl, +}; + +static int hsr_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) +{ + struct hsr_priv *priv = netdev_priv(dev); + struct hsr_port *port; + const struct ethtool_ops *ops; + int ret = -EOPNOTSUPP; + + hsr_for_each_port(priv, port) { + if (is_slave_port(port)) { + ops = port->dev->ethtool_ops; + if (ops && ops->get_ts_info) { + ret = ops->get_ts_info(port->dev, info); + return ret; + } + } + } + + return ret; +} + +static const struct ethtool_ops hsr_ethtool_ops = { + .get_link = ethtool_op_get_link, + .get_ts_info = hsr_get_ts_info, }; static struct device_type hsr_type = { @@ -445,6 +803,7 @@ .send_sv_frame = send_hsr_supervision_frame, .create_tagged_frame = hsr_create_tagged_frame, .get_untagged_frame = hsr_get_untagged_frame, + .drop_frame = hsr_drop_frame, .fill_frame_info = hsr_fill_frame_info, .invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame, }; @@ -467,6 +826,7 @@ dev->min_mtu = 0; dev->header_ops = &hsr_header_ops; dev->netdev_ops = &hsr_device_ops; + dev->ethtool_ops = &hsr_ethtool_ops; SET_NETDEV_DEVTYPE(dev, &hsr_type); dev->priv_flags |= IFF_NO_QUEUE; @@ -474,16 +834,13 @@ dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | - NETIF_F_HW_VLAN_CTAG_TX; + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_FILTER; dev->features = dev->hw_features; /* Prevent recursive tx locking */ dev->features |= NETIF_F_LLTX; - /* VLAN on top of HSR needs testing and probably some work on - * hsr_header_create() etc. - */ - dev->features |= NETIF_F_VLAN_CHALLENGED; /* Not sure about this. Taken from bridge code. netdev_features.h says * it means "Does not change network namespaces". */ @@ -492,10 +849,11 @@ /* Return true if dev is a HSR master; return false otherwise. */ -inline bool is_hsr_master(struct net_device *dev) +bool is_hsr_master(struct net_device *dev) { return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); } +EXPORT_SYMBOL(is_hsr_master); /* Default multicast address for HSR Supervision frames */ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { @@ -504,7 +862,8 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], unsigned char multicast_spec, u8 protocol_version, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, bool sv_vlan_tag_needed, unsigned short vid, + unsigned char pcp, unsigned char dei) { bool unregister = false; struct hsr_priv *hsr; @@ -521,12 +880,15 @@ /* initialize protocol specific functions */ if (protocol_version == PRP_V1) { /* For PRP, lan_id has most significant 3 bits holding - * the net_id of PRP_LAN_ID + * the net_id of PRP_LAN_ID and also duplicate discard + * mode set. */ hsr->net_id = PRP_LAN_ID << 1; hsr->proto_ops = &prp_ops; + hsr->dd_mode = IEC62439_3_DD; } else { hsr->proto_ops = &hsr_ops; + hsr->hsr_mode = IEC62439_3_HSR_MODE_H; } /* Make sure we recognize frames from ourselves in hsr_rcv() */ @@ -541,12 +903,18 @@ hsr->sup_sequence_nr = HSR_SUP_SEQNR_START; timer_setup(&hsr->announce_timer, hsr_announce, 0); - timer_setup(&hsr->prune_timer, hsr_prune_nodes, 0); + if (!hsr->rx_offloaded) + timer_setup(&hsr->prune_timer, hsr_prune_nodes, 0); ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr); hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec; hsr->prot_version = protocol_version; + /* update vlan tag infor for SV frames */ + hsr->use_vlan_for_sv = sv_vlan_tag_needed; + hsr->sv_frame_vid = vid; + hsr->sv_frame_dei = dei; + hsr->sv_frame_pcp = pcp; /* FIXME: should I modify the value of these? * @@ -565,6 +933,15 @@ if (res) goto err_add_master; + /* HSR/PRP LRE Rx offload supported in lower device? */ + if ((slave[0]->features & NETIF_F_HW_HSR_TAG_RM) && + (slave[1]->features & NETIF_F_HW_HSR_TAG_RM)) + hsr->rx_offloaded = true; + + if ((slave[0]->features & NETIF_F_HW_VLAN_CTAG_FILTER) && + (slave[1]->features & NETIF_F_HW_VLAN_CTAG_FILTER)) + hsr_dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + res = register_netdevice(hsr_dev); if (res) goto err_unregister; @@ -580,15 +957,43 @@ goto err_unregister; hsr_debugfs_init(hsr, hsr_dev); - mod_timer(&hsr->prune_timer, jiffies + msecs_to_jiffies(PRUNE_PERIOD)); - return 0; + /* For LRE rx offload, pruning is expected to happen + * at the hardware or firmware . So don't do this in software + */ + if (!hsr->rx_offloaded) + mod_timer(&hsr->prune_timer, + jiffies + msecs_to_jiffies(PRUNE_PERIOD)); + /* for offloaded case, expect both slaves have the + * same MAC address configured. If not fail. + */ + if (hsr->rx_offloaded && + !ether_addr_equal(slave[0]->dev_addr, + slave[1]->dev_addr)) { + netdev_err(hsr_dev, + "Slave's MAC addr must be same. So change it\n"); + res = -EINVAL; + goto err_add_slaves; + } + + res = hsr_create_procfs(hsr, hsr_dev); + if (res) + goto err_add_slaves; + if (hsr->use_vlan_for_sv) + res = hsr_set_sv_frame_vid(hsr, hsr->sv_frame_vid); + + if (res) + goto err_procfs; + + return 0; +err_procfs: + hsr_remove_procfs(hsr, hsr_dev); err_unregister: - hsr_del_ports(hsr); + hsr_del_ports(hsr, hsr_dev); err_add_master: hsr_del_self_node(hsr); - +err_add_slaves: if (unregister) unregister_netdevice(hsr_dev); return res; diff -Naur --no-dereference a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h --- a/net/hsr/hsr_device.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_device.h 2022-01-06 12:45:53.838318206 -0500 @@ -13,12 +13,12 @@ #include #include "hsr_main.h" -void hsr_del_ports(struct hsr_priv *hsr); +void hsr_del_ports(struct hsr_priv *hsr, struct net_device *hsr_dev); void hsr_dev_setup(struct net_device *dev); int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], unsigned char multicast_spec, u8 protocol_version, - struct netlink_ext_ack *extack); + struct netlink_ext_ack *extack, bool sv_vlan_tag_needed, unsigned short vid, + unsigned char pcp, unsigned char cfi); void hsr_check_carrier_and_operstate(struct hsr_priv *hsr); -bool is_hsr_master(struct net_device *dev); int hsr_get_max_mtu(struct hsr_priv *hsr); #endif /* __HSR_DEVICE_H */ diff -Naur --no-dereference a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c --- a/net/hsr/hsr_forward.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_forward.c 2022-01-06 12:45:53.838318206 -0500 @@ -17,6 +17,16 @@ struct hsr_node; +static inline int is_hsr_l2ptp(struct sk_buff *skb) +{ + struct hsr_ethhdr *hsr_ethhdr; + + hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb); + + return (hsr_ethhdr->ethhdr.h_proto == htons(ETH_P_HSR) && + hsr_ethhdr->hsr_tag.encap_proto == htons(ETH_P_1588)); +} + /* The uses I can see for these HSR supervision frames are: * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = * 22") to reset any sequence_nr counters belonging to that node. Useful if @@ -34,9 +44,12 @@ */ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) { - struct ethhdr *eth_hdr; + struct hsrv1_ethhdr_vlan_sp *hsr_v1_vlan_hdr; struct hsr_sup_tag *hsr_sup_tag; - struct hsrv1_ethhdr_sp *hsr_V1_hdr; + struct hsrv1_ethhdr_sp *hsr_v1_hdr; + struct ethhdr *eth_hdr; + bool vlan = false; + __be16 proto; WARN_ON_ONCE(!skb_mac_header_was_set(skb)); eth_hdr = (struct ethhdr *)skb_mac_header(skb); @@ -46,21 +59,40 @@ hsr->sup_multicast_addr)) return false; + if (skb_vlan_tagged(skb)) { + proto = vlan_get_protocol(skb); + vlan = true; + } else { + proto = eth_hdr->h_proto; + } + /* Correct ether type?. */ if (!(eth_hdr->h_proto == htons(ETH_P_PRP) || eth_hdr->h_proto == htons(ETH_P_HSR))) return false; /* Get the supervision header from correct location. */ - if (eth_hdr->h_proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */ - hsr_V1_hdr = (struct hsrv1_ethhdr_sp *)skb_mac_header(skb); - if (hsr_V1_hdr->hsr.encap_proto != htons(ETH_P_PRP)) - return false; - - hsr_sup_tag = &hsr_V1_hdr->hsr_sup; + if (proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */ + if (!vlan) { + hsr_v1_hdr = (struct hsrv1_ethhdr_sp *)eth_hdr; + if (hsr_v1_hdr->hsr.encap_proto != htons(ETH_P_PRP)) + return false; + hsr_sup_tag = &hsr_v1_hdr->hsr_sup; + } else { + hsr_v1_vlan_hdr = + (struct hsrv1_ethhdr_vlan_sp *)eth_hdr; + if (hsr_v1_vlan_hdr->hsr.encap_proto != + htons(ETH_P_PRP)) + return false; + hsr_sup_tag = &hsr_v1_vlan_hdr->hsr_sup; + } } else { - hsr_sup_tag = - &((struct hsrv0_ethhdr_sp *)skb_mac_header(skb))->hsr_sup; + if (!vlan) + hsr_sup_tag = + &((struct hsrv0_ethhdr_sp *)eth_hdr)->hsr_sup; + else + hsr_sup_tag = + &((struct hsrv0_ethhdr_vlan_sp *)eth_hdr)->hsr_sup; } if (hsr_sup_tag->HSR_TLV_type != HSR_TLV_ANNOUNCE && @@ -125,11 +157,15 @@ struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, struct hsr_port *port) { + struct hsr_priv *hsr = port->hsr; + if (!frame->skb_std) { if (frame->skb_prp) { - /* trim the skb by len - HSR_HLEN to exclude RCT */ - skb_trim(frame->skb_prp, - frame->skb_prp->len - HSR_HLEN); + if (!hsr->rx_offloaded) { + /* trim the skb by len - HSR_HLEN to exclude RCT */ + skb_trim(frame->skb_prp, + frame->skb_prp->len - HSR_HLEN); + } frame->skb_std = __pskb_copy(frame->skb_prp, skb_headroom(frame->skb_prp), @@ -145,8 +181,30 @@ return skb_clone(frame->skb_std, GFP_ATOMIC); } -static void prp_set_lan_id(struct prp_rct *trailer, - struct hsr_port *port) +/* only prp skb should be passed in */ +static void prp_update_lre_error_stats(struct sk_buff *skb, + struct hsr_port *port) +{ + int lan_id; + struct prp_rct *trailer = skb_get_PRP_rct(skb); + + if (!trailer) { + INC_CNT_RX_ERROR_AB(port->type, port->hsr); + return; + } + + lan_id = get_prp_lan_id(trailer); + + if (port->type == HSR_PT_SLAVE_A) { + if (lan_id & 1) + INC_CNT_RX_WRONG_LAN_AB(port->type, port->hsr); + } else { + if (!(lan_id & 1)) + INC_CNT_RX_WRONG_LAN_AB(port->type, port->hsr); + } +} + +static void prp_set_lan_id(struct prp_rct *trailer, struct hsr_port *port) { int lane_id; @@ -208,6 +266,7 @@ struct hsr_port *port, u8 proto_version) { struct hsr_ethhdr *hsr_ethhdr; + unsigned char *pc; int lsdu_size; /* pad to minimum packet size which is 60 + 6 (HSR tag) */ @@ -218,9 +277,24 @@ if (frame->is_vlan) lsdu_size -= 4; - hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb); + pc = skb_mac_header(skb); + if (frame->is_vlan) + /* This 4-byte shift (size of a vlan tag) does not + * mean that the ethhdr starts there. But rather it + * provides the proper environment for accessing + * the fields, such as hsr_tag etc., just like + * when the vlan tag is not there. This is because + * the hsr tag is after the vlan tag. + */ + hsr_ethhdr = (struct hsr_ethhdr *)(pc + VLAN_HLEN); + else + hsr_ethhdr = (struct hsr_ethhdr *)pc; + + if (REDINFO_T(skb) == DIRECTED_TX) + set_hsr_tag_path(&hsr_ethhdr->hsr_tag, REDINFO_PATHID(skb)); + else + hsr_set_path_id(hsr_ethhdr, port); - hsr_set_path_id(hsr_ethhdr, port); set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size); hsr_ethhdr->hsr_tag.sequence_nr = htons(frame->sequence_nr); hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto; @@ -236,17 +310,36 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame, struct hsr_port *port) { + struct skb_redundant_info *sred; + struct hsr_ethhdr *hsr_ethhdr; unsigned char *dst, *src; struct sk_buff *skb; + u16 s; int movelen; if (frame->skb_hsr) { + u8 *pc; struct hsr_ethhdr *hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(frame->skb_hsr); + /* This case is for SV frame created by this device */ + pc = (u8 *)hsr_ethhdr; + if (frame->is_vlan) + /* This 4-byte shift (size of a vlan tag) does not + * mean that the ethhdr starts there. But rather it + * provides the proper environment for accessing + * the fields, such as hsr_tag etc., just like + * when the vlan tag is not there. This is because + * the hsr tag is after the vlan tag. + */ + hsr_ethhdr = (struct hsr_ethhdr *)(pc + VLAN_HLEN); + else + hsr_ethhdr = (struct hsr_ethhdr *)pc; /* set the lane id properly */ hsr_set_path_id(hsr_ethhdr, port); return skb_clone(frame->skb_hsr, GFP_ATOMIC); + } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { + return skb_clone(frame->skb_std, GFP_ATOMIC); } /* Create the new skb with enough headroom to fit the HSR tag */ @@ -271,7 +364,30 @@ /* skb_put_padto free skb on error and hsr_fill_tag returns NULL in * that case */ - return hsr_fill_tag(skb, frame, port, port->hsr->prot_version); + skb = hsr_fill_tag(skb, frame, port, port->hsr->prot_version); + if (!skb) + return NULL; + + if (REDINFO_T(skb) == DIRECTED_TX) + return skb; + + skb_shinfo(skb)->tx_flags |= skb_shinfo(frame->skb_std)->tx_flags & SKBTX_ANY_TSTAMP; + skb->sk = frame->skb_std->sk; + + /* TODO: should check socket option instead? */ + if (is_hsr_l2ptp(skb)) { + sred = skb_redinfo(skb); + /* assumes no vlan */ + hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb); + sred->io_port = (PTP_EVT_OUT | BIT(port->type - 1)); + sred->ethertype = ntohs(hsr_ethhdr->ethhdr.h_proto); + s = ntohs(hsr_ethhdr->hsr_tag.path_and_LSDU_size); + sred->lsdu_size = s & 0xfff; + sred->pathid = (s >> 12) & 0xf; + sred->seqnr = hsr_get_skb_sequence_nr(skb); + } + + return skb; } struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame, @@ -289,9 +405,11 @@ return NULL; } return skb_clone(frame->skb_prp, GFP_ATOMIC); + } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { + return skb_clone(frame->skb_std, GFP_ATOMIC); } - skb = skb_copy_expand(frame->skb_std, 0, + skb = skb_copy_expand(frame->skb_std, skb_headroom(frame->skb_std), skb_tailroom(frame->skb_std) + HSR_HLEN, GFP_ATOMIC); prp_fill_rct(skb, frame, port); @@ -299,14 +417,21 @@ return skb; } -static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev, - struct hsr_node *node_src) +static void hsr_deliver_master(struct sk_buff *skb, struct hsr_node *node_src, + struct hsr_port *port) { + struct hsr_priv *hsr = port->hsr; + struct net_device *dev = port->dev; bool was_multicast_frame; int res; was_multicast_frame = (skb->pkt_type == PACKET_MULTICAST); - hsr_addr_subst_source(node_src, skb); + /* For LRE offloaded case, assume same MAC address is on both + * interfaces of the remote node and hence no need to substitute + * the source MAC address. + */ + if (!port->hsr->rx_offloaded) + hsr_addr_subst_source(node_src, skb); skb_pull(skb, ETH_HLEN); res = netif_rx(skb); if (res == NET_RX_DROP) { @@ -316,13 +441,15 @@ dev->stats.rx_bytes += skb->len; if (was_multicast_frame) dev->stats.multicast++; + INC_CNT_TX_C(hsr); } } static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port, struct hsr_frame_info *frame) { - if (frame->port_rcv->type == HSR_PT_MASTER) { + if (!port->hsr->rx_offloaded && + frame->port_rcv->type == HSR_PT_MASTER) { hsr_addr_subst_dest(frame->node_src, skb, port); /* Address substitution (IEC62439-3 pp 26, 50): replace mac @@ -330,6 +457,7 @@ */ ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr); } + INC_CNT_TX_AB(port->type, port->hsr); return dev_queue_xmit(skb); } @@ -341,6 +469,65 @@ port->type == HSR_PT_SLAVE_A)); } +bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port) +{ + if (port->dev->features & NETIF_F_HW_HSR_FWD) + return prp_drop_frame(frame, port); + + return false; +}; + +static void stripped_skb_get_shared_info(struct sk_buff *skb_stripped, + struct hsr_frame_info *frame) +{ + struct hsr_port *port_rcv = frame->port_rcv; + struct skb_redundant_info *sred; + struct sk_buff *skb_hsr, *skb; + struct hsr_ethhdr *hsr_ethhdr; + u16 s; + + if (port_rcv->hsr->prot_version > HSR_V1) + return; + + if (!frame->skb_hsr) + return; + + skb_hsr = frame->skb_hsr; + skb = skb_stripped; + + if (is_hsr_l2ptp(skb_hsr)) { + skb_hwtstamps(skb)->hwtstamp = skb_hwtstamps(skb_hsr)->hwtstamp; + skb_redinfo_hwtstamps(skb)->hwtstamp = + skb_redinfo_hwtstamps(skb_hsr)->hwtstamp; + + sred = skb_redinfo(skb); + /* assumes no vlan */ + hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb_hsr); + sred->io_port = (PTP_MSG_IN | BIT(port_rcv->type - 1)); + sred->ethertype = ntohs(hsr_ethhdr->ethhdr.h_proto); + s = ntohs(hsr_ethhdr->hsr_tag.path_and_LSDU_size); + sred->lsdu_size = s & 0xfff; + sred->pathid = (s >> 12) & 0xf; + sred->seqnr = frame->sequence_nr; + } +} + +static unsigned int +hsr_directed_tx_ports(struct hsr_frame_info *frame) +{ + struct sk_buff *skb; + + if (frame->skb_std) + skb = frame->skb_std; + else + return 0; + + if (REDINFO_T(skb) == DIRECTED_TX) + return REDINFO_PORTS(skb); + + return 0; +} + /* Forward the frame through all devices except: * - Back through the receiving device * - If it's a HSR frame: through a device where it has passed before @@ -355,8 +542,10 @@ */ static void hsr_forward_do(struct hsr_frame_info *frame) { + struct sk_buff *skb = NULL; + unsigned int dir_ports = 0; struct hsr_port *port; - struct sk_buff *skb; + bool sent = false; hsr_for_each_port(frame->port_rcv->hsr, port) { struct hsr_priv *hsr = port->hsr; @@ -372,16 +561,35 @@ if (port->type != HSR_PT_MASTER && frame->is_local_exclusive) continue; + /* If hardware duplicate generation is enabled, only send out + * one port. + */ + if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent) + continue; + /* Don't send frame over port where it has been sent before. - * Also fro SAN, this shouldn't be done. + * Also if rx LRE is offloaded, hardware does duplication + * detection and discard and send only one copy to the upper + * device and thus discard duplicate detection. For PRP, frame + * could be from a SAN for which bypass duplicate discard here. */ - if (!frame->is_from_san && + if (!port->hsr->rx_offloaded && !frame->is_from_san && hsr_register_frame_out(port, frame->node_src, frame->sequence_nr)) continue; - if (frame->is_supervision && port->type == HSR_PT_MASTER) { - hsr_handle_sup_frame(frame); + /* In LRE offloaded case, don't expect supervision frames from + * slave ports for host as they get processed at the h/w or + * firmware. + */ + if (frame->is_supervision && + port->type == HSR_PT_MASTER && !port->hsr->rx_offloaded) { + if (frame->skb_hsr) + skb = frame->skb_hsr; + else if (frame->skb_prp) + skb = frame->skb_prp; + if (skb) + hsr_handle_sup_frame(frame); continue; } @@ -392,21 +600,33 @@ hsr->proto_ops->drop_frame(frame, port)) continue; - if (port->type != HSR_PT_MASTER) + dir_ports = hsr_directed_tx_ports(frame); + if (dir_ports && !(dir_ports & BIT(port->type - 1))) + continue; + + if (port->type != HSR_PT_MASTER) { skb = hsr->proto_ops->create_tagged_frame(frame, port); - else + } else { skb = hsr->proto_ops->get_untagged_frame(frame, port); + stripped_skb_get_shared_info(skb, frame); + } if (!skb) { frame->port_rcv->dev->stats.rx_dropped++; + if (frame->port_rcv->type == HSR_PT_SLAVE_A || + frame->port_rcv->type == HSR_PT_SLAVE_B) + INC_CNT_RX_ERROR_AB(frame->port_rcv->type, + port->hsr); continue; } skb->dev = port->dev; - if (port->type == HSR_PT_MASTER) - hsr_deliver_master(skb, port->dev, frame->node_src); - else - hsr_xmit(skb, port, frame); + if (port->type == HSR_PT_MASTER) { + hsr_deliver_master(skb, frame->node_src, port); + } else { + if (!hsr_xmit(skb, port, frame)) + sent = true; + } } } @@ -443,11 +663,16 @@ if (port->type != HSR_PT_MASTER) { frame->is_from_san = true; } else { - /* Sequence nr for the master node */ - spin_lock_irqsave(&hsr->seqnr_lock, irqflags); - frame->sequence_nr = hsr->sequence_nr; - hsr->sequence_nr++; - spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags); + if ((REDINFO_T(skb) == DIRECTED_TX) && + (REDINFO_LSDU_SIZE(skb))) { + frame->sequence_nr = REDINFO_SEQNR(skb); + } else { + /* Sequence nr for the master node */ + spin_lock_irqsave(&hsr->seqnr_lock, irqflags); + frame->sequence_nr = hsr->sequence_nr; + hsr->sequence_nr++; + spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags); + } } } @@ -503,16 +728,37 @@ int ret; /* Check if skb contains ethhdr */ - if (skb->mac_len < sizeof(struct ethhdr)) + if (!hsr->rx_offloaded && skb->mac_len < sizeof(struct ethhdr)) return -EINVAL; memset(frame, 0, sizeof(*frame)); - frame->is_supervision = is_supervision_frame(port->hsr, skb); - frame->node_src = hsr_get_node(port, &hsr->node_db, skb, - frame->is_supervision, - port->type); - if (!frame->node_src) - return -1; /* Unknown node and !is_supervision, or no mem */ + frame->is_supervision = is_supervision_frame(hsr, skb); + + /* When offloaded, don't expect Supervision frame which + * is terminated at h/w or f/w that offload the LRE + */ + if (frame->is_supervision && hsr->rx_offloaded && + port->type != HSR_PT_MASTER) + return -1; + + if (frame->is_supervision) { + if (port->type == HSR_PT_SLAVE_A) + INC_CNT_RX_SUP_A(hsr); + else if (port->type == HSR_PT_SLAVE_B) + INC_CNT_RX_SUP_B(hsr); + } + + /* For Offloaded case, there is no need for node list since + * firmware/hardware implements LRE function. + */ + if (!hsr->rx_offloaded) { + frame->node_src = hsr_get_node(port, &hsr->node_db, skb, + frame->is_supervision, + port->type); + /* Unknown node and !is_supervision, or no mem */ + if (!frame->node_src) + return -1; + } ethhdr = (struct ethhdr *)skb_mac_header(skb); frame->is_vlan = false; @@ -524,8 +770,6 @@ if (frame->is_vlan) { vlan_hdr = (struct hsr_vlan_ethhdr *)ethhdr; proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto; - /* FIXME: */ - netdev_warn_once(skb->dev, "VLAN not yet supported"); } frame->is_from_san = false; @@ -547,7 +791,17 @@ if (fill_frame_info(&frame, skb, port) < 0) goto out_drop; - hsr_register_frame_in(frame.node_src, port, frame.sequence_nr); + /* Check for LAN_ID only for PRP */ + if (frame.skb_prp) { + if (port->type == HSR_PT_SLAVE_A || + port->type == HSR_PT_SLAVE_B) + prp_update_lre_error_stats(frame.skb_prp, port); + } + + /* No need to register frame when rx offload is supported */ + if (!port->hsr->rx_offloaded) + hsr_register_frame_in(frame.node_src, port, + frame.sequence_nr); hsr_forward_do(&frame); /* Gets called for ingress frames as well as egress from master port. * So check and increment stats for master port only here. @@ -563,6 +817,7 @@ return; out_drop: + INC_CNT_RX_ERROR_AB(port->type, port->hsr); port->dev->stats.tx_dropped++; kfree_skb(skb); } diff -Naur --no-dereference a/net/hsr/hsr_forward.h b/net/hsr/hsr_forward.h --- a/net/hsr/hsr_forward.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_forward.h 2022-01-06 12:45:53.838318206 -0500 @@ -23,6 +23,7 @@ struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, struct hsr_port *port); bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port); +bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port); int prp_fill_frame_info(__be16 proto, struct sk_buff *skb, struct hsr_frame_info *frame); int hsr_fill_frame_info(__be16 proto, struct sk_buff *skb, diff -Naur --no-dereference a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c --- a/net/hsr/hsr_framereg.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_framereg.c 2022-01-06 12:45:53.838318206 -0500 @@ -279,6 +279,8 @@ skb = frame->skb_hsr; else if (frame->skb_prp) skb = frame->skb_prp; + else if (frame->skb_std) + skb = frame->skb_std; if (!skb) return; diff -Naur --no-dereference a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h --- a/net/hsr/hsr_framereg.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_framereg.h 2022-01-06 12:45:53.838318206 -0500 @@ -73,6 +73,10 @@ unsigned char macaddress_B[ETH_ALEN]; /* Local slave through which AddrB frames are received from this node */ enum hsr_port_type addr_B_port; + u32 cnt_received_a; + u32 cnt_received_b; + u32 cnt_err_wrong_lan_a; + u32 cnt_err_wrong_lan_b; unsigned long time_in[HSR_PT_PORTS]; bool time_in_stale[HSR_PT_PORTS]; unsigned long time_out[HSR_PT_PORTS]; diff -Naur --no-dereference a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c --- a/net/hsr/hsr_main.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_main.c 2022-01-06 12:45:53.838318206 -0500 @@ -131,6 +131,17 @@ return NULL; } +int hsr_get_version(struct net_device *dev, enum hsr_version *ver) +{ + struct hsr_priv *hsr; + + hsr = netdev_priv(dev); + *ver = hsr->prot_version; + + return 0; +} +EXPORT_SYMBOL(hsr_get_version); + static struct notifier_block hsr_nb = { .notifier_call = hsr_netdev_notify, /* Slave event notifications */ }; diff -Naur --no-dereference a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h --- a/net/hsr/hsr_main.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_main.h 2022-01-06 12:45:53.838318206 -0500 @@ -13,6 +13,8 @@ #include #include #include +#include +#include /* Time constants as specified in the HSR specification (IEC-62439-3 2010) * Table 8. @@ -122,12 +124,25 @@ struct hsr_sup_tag hsr_sup; } __packed; +struct hsrv0_ethhdr_vlan_sp { + struct ethhdr ethhdr; + struct vlan_hdr vlanHdr; + struct hsr_sup_tag hsr_sup; +} __packed; + struct hsrv1_ethhdr_sp { struct ethhdr ethhdr; struct hsr_tag hsr; struct hsr_sup_tag hsr_sup; } __packed; +struct hsrv1_ethhdr_vlan_sp { + struct ethhdr ethhdr; + struct vlan_hdr vlanHdr; + struct hsr_tag hsr; + struct hsr_sup_tag hsr_sup; +} __packed; + enum hsr_port_type { HSR_PT_NONE = 0, /* Must be 0, used by framereg */ HSR_PT_SLAVE_A, @@ -172,13 +187,6 @@ enum hsr_port_type type; }; -/* used by driver internally to differentiate various protocols */ -enum hsr_version { - HSR_V0 = 0, - HSR_V1, - PRP_V1, -}; - struct hsr_frame_info; struct hsr_node; @@ -198,6 +206,12 @@ void (*update_san_info)(struct hsr_node *node, bool is_sup); }; +struct hsr_prp_debug_stats { + u32 cnt_tx_sup; + u32 cnt_rx_sup_a; + u32 cnt_rx_sup_b; +}; + struct hsr_priv { struct rcu_head rcu_head; struct list_head ports; @@ -205,6 +219,9 @@ struct list_head self_node_db; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ struct timer_list prune_timer; + unsigned int rx_offloaded : 1; /* lre handle in hw */ + struct hsr_prp_debug_stats dbg_stats; /* debug stats */ + struct lre_stats lre_stats; /* lre interface stats */ int announce_count; u16 sequence_nr; u16 sup_sequence_nr; /* For HSRv1 separate seq_nr for supervision */ @@ -218,9 +235,38 @@ u8 net_id; /* for PRP, it occupies most significant 3 bits * of lan_id */ + /* Below are used when SV frames are to be sent with VLAN tag */ + u8 use_vlan_for_sv; + u16 sv_frame_vid; + u8 sv_frame_dei; + u8 sv_frame_pcp; + /* To enable/disable SV frame transmission */ + u8 disable_sv_frame; + /* value of hsr mode */ + enum iec62439_3_hsr_modes hsr_mode; + /* PRP Transparent Reception */ + enum iec62439_3_tr_modes prp_tr; + /* Duplicate discard mode */ + enum iec62439_3_dd_modes dd_mode; + /* Clear Node Table command */ + enum iec62439_3_clear_nt_cmd clear_nt_cmd; + u32 dlrmt; /* duplicate list reside max time */ unsigned char sup_multicast_addr[ETH_ALEN]; #ifdef CONFIG_DEBUG_FS struct dentry *node_tbl_root; + struct dentry *node_tbl_file; + struct dentry *lre_info_file; +#endif +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *dir; + struct proc_dir_entry *hsr_mode_file; + struct proc_dir_entry *dd_mode_file; + struct proc_dir_entry *prp_tr_file; + struct proc_dir_entry *clear_nt_file; + struct proc_dir_entry *dlrmt_file; + struct proc_dir_entry *lre_stats_file; + struct proc_dir_entry *node_table_file; + struct proc_dir_entry *disable_sv_file; #endif }; @@ -281,6 +327,28 @@ return (expected_lsdu_size == get_prp_LSDU_size(rct)); } +#define INC_CNT_TX_SUP(priv) ((priv)->dbg_stats.cnt_tx_sup++) +#define INC_CNT_RX_SUP_A(priv) ((priv)->dbg_stats.cnt_rx_sup_a++) +#define INC_CNT_RX_SUP_B(priv) ((priv)->dbg_stats.cnt_rx_sup_b++) + +#define INC_CNT_TX_AB(type, priv) (((type) == HSR_PT_SLAVE_A) ? \ + (priv)->lre_stats.cnt_tx_a++ : \ + (priv)->lre_stats.cnt_tx_b++) +#define INC_CNT_TX_C(priv) ((priv)->lre_stats.cnt_tx_c++) +#define INC_CNT_RX_WRONG_LAN_AB(type, priv) (((type) == HSR_PT_SLAVE_A) ? \ + (priv)->lre_stats.cnt_errwronglan_a++ : \ + (priv)->lre_stats.cnt_errwronglan_b++) +#define INC_CNT_RX_AB(type, priv) (((type) == HSR_PT_SLAVE_A) ? \ + (priv)->lre_stats.cnt_rx_a++ : \ + (priv)->lre_stats.cnt_rx_b++) +#define INC_CNT_RX_C(priv) ((priv)->lre_stats.cnt_rx_c++) +#define INC_CNT_RX_ERROR_AB(type, priv) (((type) == HSR_PT_SLAVE_A) ? \ + (priv)->lre_stats.cnt_errors_a++ : \ + (priv)->lre_stats.cnt_errors_b++) +#define INC_CNT_OWN_RX_AB(type, priv) (((type) == HSR_PT_SLAVE_A) ? \ + (priv)->lre_stats.cnt_own_rx_a++ : \ + (priv)->lre_stats.cnt_own_rx_b++) + #if IS_ENABLED(CONFIG_DEBUG_FS) void hsr_debugfs_rename(struct net_device *dev); void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev); @@ -302,4 +370,28 @@ {} #endif -#endif /* __HSR_PRIVATE_H */ +#ifdef CONFIG_PROC_FS +int hsr_create_procfs(struct hsr_priv *hsr, struct net_device *ndev); +void hsr_remove_procfs(struct hsr_priv *hsr, struct net_device *ndev); +#else +static inline int hsr_create_procfs(struct hsr_priv *hsr, + struct net_device *ndev) +{ + return 0; +} + +static inline void hsr_remove_procfs(struct hsr_priv *hsr, + struct net_device *ndev) +{} +#endif + +int hsr_lredev_attr_set(struct hsr_priv *hsr, + struct lredev_attr *attr); +int hsr_lredev_attr_get(struct hsr_priv *hsr, + struct lredev_attr *attr); +int hsr_lredev_get_node_table(struct hsr_priv *hsr, + struct lre_node_table_entry table[], + int size); +int hsr_lredev_get_lre_stats(struct hsr_priv *hsr, + struct lre_stats *stats); +#endif /* __HSR_PRP_MAIN_H */ diff -Naur --no-dereference a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c --- a/net/hsr/hsr_netlink.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_netlink.c 2022-01-06 12:45:53.838318206 -0500 @@ -23,6 +23,9 @@ [IFLA_HSR_SUPERVISION_ADDR] = { .len = ETH_ALEN }, [IFLA_HSR_SEQ_NR] = { .type = NLA_U16 }, [IFLA_HSR_PROTOCOL] = { .type = NLA_U8 }, + [IFLA_HSR_SV_VID] = { .type = NLA_U16 }, + [IFLA_HSR_SV_PCP] = { .type = NLA_U8 }, + [IFLA_HSR_SV_DEI] = { .type = NLA_U8 }, }; /* Here, it seems a netdevice has already been allocated for us, and the @@ -32,10 +35,14 @@ struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { + bool sv_vlan_tag_needed = false; enum hsr_version proto_version; + unsigned char pcp = 0, dei = 0; unsigned char multicast_spec; u8 proto = HSR_PROTOCOL_HSR; struct net_device *link[2]; + unsigned short vid = 0; + char *sproto = "HSR"; if (!data) { NL_SET_ERR_MSG_MOD(extack, "No slave devices specified"); @@ -96,10 +103,36 @@ } } - if (proto == HSR_PROTOCOL_PRP) + if (proto == HSR_PROTOCOL_PRP) { + sproto = "PRP"; proto_version = PRP_V1; + } + + if (data[IFLA_HSR_SV_VID]) { + sv_vlan_tag_needed = true; + vid = nla_get_u16(data[IFLA_HSR_SV_VID]); + } + + if (data[IFLA_HSR_SV_PCP]) { + sv_vlan_tag_needed = true; + pcp = nla_get_u8(data[IFLA_HSR_SV_PCP]); + } + + if (data[IFLA_HSR_SV_DEI]) { + sv_vlan_tag_needed = true; + dei = nla_get_u8(data[IFLA_HSR_SV_DEI]); + } + + if (sv_vlan_tag_needed && + (vid >= (VLAN_N_VID - 1) || dei > 1 || pcp > 7)) { + netdev_info(dev, + "%s: wrong vlan params: vid %d, pcp %d, dei %d\n", + sproto, vid, pcp, dei); + return -EINVAL; + } - return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack); + return hsr_dev_finalize(dev, link, multicast_spec, proto_version, + extack, sv_vlan_tag_needed, vid, pcp, dei); } static void hsr_dellink(struct net_device *dev, struct list_head *head) @@ -110,7 +143,7 @@ del_timer_sync(&hsr->announce_timer); hsr_debugfs_term(hsr); - hsr_del_ports(hsr); + hsr_del_ports(hsr, dev); hsr_del_self_node(hsr); hsr_del_nodes(&hsr->node_db); diff -Naur --no-dereference a/net/hsr/hsr_proc.c b/net/hsr/hsr_proc.c --- a/net/hsr/hsr_proc.c 1969-12-31 19:00:00.000000000 -0500 +++ b/net/hsr/hsr_proc.c 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,673 @@ +// SPDX-License-Identifier: GPL-2.0 +/* hsr_proc.c : procfs file for HSR and PRP driver + * + * Copyright (C) 2018-2020 Texas Instruments Incorporated - http://www.ti.com + * + * Author(s): + * Murali Karicheri + */ +#include +#include + +#include "hsr_main.h" + +#define BUF_SIZE 64 +#define LRE_STAT_OFS(m) offsetof(struct lre_stats, m) + +static const char *const hsr_lre_stats[] = { + "lreTxA", + "lreTxB", + "lreTxC", + "lreErrWrongLanA", + "lreErrWrongLanB", + "lreErrWrongLanC", + "lreRxA", + "lreRxB", + "lreRxC", + "lreErrorsA", + "lreErrorsB", + "lreErrorsC", + "lreNodes", + "lreProxyNodes", + "lreUniqueRxA", + "lreUniqueRxB", + "lreUniqueRxC", + "lreDuplicateRxA", + "lreDuplicateRxB", + "lreDuplicateRxC", + "lreMultiRxA", + "lreMultiRxB", + "lreMultiRxC", + "lreOwnRxA", + "lreOwnRxB", +}; + +static int hsr_lre_stats_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lre_stats lower_stats, *upper_stats; + int ret = 0, i; + u32 *ptr; + + upper_stats = &priv->lre_stats; + if (priv->rx_offloaded) { + ret = hsr_lredev_get_lre_stats(priv, &lower_stats); + if (ret < 0) { + seq_puts(sfp, "Error in retrieving the stats\n"); + return 0; + } + ptr = (u32 *)&lower_stats; + } else { + ptr = (u32 *)upper_stats; + } + + seq_puts(sfp, "LRE statistics:\n"); + seq_printf(sfp, "Rx Offloaded: %d\n", priv->rx_offloaded); + for (i = 0; i < ARRAY_SIZE(hsr_lre_stats); i++) { + /* for rx_c and tx_c, retrieve stats from hsr/prp device + * lre stats. Rest of the stats are retrieved from + * lower device. + */ + if (!strcmp("lreTxC", hsr_lre_stats[i])) { + seq_printf(sfp, "\n %s: %d", + hsr_lre_stats[i], + upper_stats->cnt_tx_c); + continue; + } + + if (!strcmp("lreRxC", hsr_lre_stats[i])) { + seq_printf(sfp, "\n %s: %d", + hsr_lre_stats[i], + upper_stats->cnt_rx_c); + continue; + } + seq_printf(sfp, "\n %s: %d", hsr_lre_stats[i], + *(ptr + i)); + } + seq_puts(sfp, "\n"); + + return 0; +} + +static int hsr_lre_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, hsr_lre_stats_show, PDE_DATA(inode)); +} + +static const struct proc_ops hsr_lre_stats_fops = { + .proc_open = hsr_lre_stats_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int hsr_node_show(struct seq_file *sfp, int index, + struct lre_node_table_entry *entry) +{ + seq_printf(sfp, "\nNode[%u]:\n", index); + seq_printf(sfp, "MAC ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + entry->mac_address[0], + entry->mac_address[1], + entry->mac_address[2], + entry->mac_address[3], + entry->mac_address[4], + entry->mac_address[5]); + + switch (entry->node_type) { + case IEC62439_3_DANP: + seq_puts(sfp, "DANP\n"); + break; + case IEC62439_3_REDBOXP: + seq_puts(sfp, "REDBOXP\n"); + break; + case IEC62439_3_VDANP: + seq_puts(sfp, "VDANP\n"); + break; + case IEC62439_3_DANH: + seq_puts(sfp, "DANH\n"); + break; + case IEC62439_3_REDBOXH: + seq_puts(sfp, "REDBOXH\n"); + break; + case IEC62439_3_VDANH: + seq_puts(sfp, "VDANH\n"); + break; + default: + seq_printf(sfp, "Unknown node type %u\n", entry->node_type); + break; + }; + + seq_printf(sfp, "Time Last Seen: RxA=%u RxB=%u\n", + entry->time_last_seen_a, + entry->time_last_seen_b); + return 0; +} + +static int hsr_node_table_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lre_node_table_entry *nt_table; + int ret = 0, count, i; + + nt_table = kcalloc(LRE_MAX_NT_ENTRIES, sizeof(*nt_table), GFP_KERNEL); + if (!nt_table) + return -ENODEV; + + count = hsr_lredev_get_node_table(priv, nt_table, + LRE_MAX_NT_ENTRIES); + if (count < 0) + count = 0; + + seq_printf(sfp, "\nRemote nodes in network: %u\n", count); + + if (!count) + return ret; + + for (i = 0; i < count; i++) + hsr_node_show(sfp, i, &nt_table[i]); + return ret; +} + +static int hsr_node_table_open(struct inode *inode, struct file *file) +{ + return single_open(file, hsr_node_table_show, PDE_DATA(inode)); +} + +static const struct proc_ops hsr_node_table_fops = { + .proc_open = hsr_node_table_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static inline int get_set_param(struct hsr_priv *priv, + const char __user *buffer, size_t count, + enum lredev_attr_id id) +{ + struct lredev_attr temp_attr; + char cmd_buffer[BUF_SIZE]; + int ret = -EINVAL; + u32 val; + + if (count > (sizeof(cmd_buffer) - 1)) + goto err; + + if (copy_from_user(cmd_buffer, buffer, count)) { + ret = -EFAULT; + goto err; + } + cmd_buffer[count] = '\0'; + ret = kstrtou32(cmd_buffer, 0, &val); + if (ret < 0) + goto err; + + /* TODO. Update mode. Check if anything else needed for + * non offload case + */ + temp_attr.id = id; + switch (id) { + case LREDEV_ATTR_ID_HSR_MODE: + if (val > IEC62439_3_HSR_MODE_M) { + ret = -EINVAL; + goto err; + } + if (!priv->rx_offloaded) { + priv->hsr_mode = (enum iec62439_3_hsr_modes)val; + return 0; + } + temp_attr.mode = (enum iec62439_3_hsr_modes)val; + break; + + case LREDEV_ATTR_ID_PRP_TR: + if (val > IEC62439_3_TR_PASS_RCT) { + ret = -EINVAL; + goto err; + } + if (!priv->rx_offloaded) { + priv->prp_tr = (enum iec62439_3_tr_modes)val; + goto out; + } + temp_attr.tr_mode = (enum iec62439_3_tr_modes)val; + break; + + case LREDEV_ATTR_ID_DD_MODE: + if (val > IEC62439_3_DD) { + ret = -EINVAL; + goto err; + } + if (!priv->rx_offloaded) { + priv->dd_mode = (enum iec62439_3_dd_modes)val; + goto out; + } + temp_attr.dd_mode = (enum iec62439_3_dd_modes)val; + break; + + case LREDEV_ATTR_ID_DLRMT: + if (!priv->rx_offloaded) { + priv->dlrmt = val; + goto out; + } + temp_attr.dl_reside_max_time = val; + break; + + case LREDEV_ATTR_ID_CLEAR_NT: + if (val > IEC62439_3_CLEAR_NT) { + ret = -EINVAL; + goto err; + } + if (!priv->rx_offloaded) { + priv->clear_nt_cmd = + (enum iec62439_3_clear_nt_cmd)val; + goto out; + } + temp_attr.clear_nt_cmd = (enum iec62439_3_clear_nt_cmd)val; + break; + default: + ret = -EINVAL; + goto err; + } + + /* pass this to lower layer device, i.e slave-1 */ + ret = hsr_lredev_attr_set(priv, &temp_attr); + if (ret) + return ret; + + /* update the local copy */ + switch (id) { + case LREDEV_ATTR_ID_HSR_MODE: + priv->hsr_mode = temp_attr.mode; + break; + case LREDEV_ATTR_ID_PRP_TR: + priv->prp_tr = temp_attr.tr_mode; + break; + case LREDEV_ATTR_ID_DD_MODE: + priv->dd_mode = temp_attr.dd_mode; + break; + case LREDEV_ATTR_ID_CLEAR_NT: + priv->clear_nt_cmd = temp_attr.clear_nt_cmd; + break; + default: /* LREDEV_ATTR_ID_DLRMT */ + priv->dlrmt = temp_attr.dl_reside_max_time; + break; + } +out: + return 0; +err: + return ret; +} + +static int hsr_mode_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lredev_attr temp_attr; + int err; + + if (!priv->rx_offloaded) { + seq_printf(sfp, "%u\n", priv->hsr_mode); + return 0; + } + + temp_attr.id = LREDEV_ATTR_ID_HSR_MODE; + err = hsr_lredev_attr_get(priv, &temp_attr); + if (err) + return err; + + seq_printf(sfp, "%u\n", temp_attr.mode); + return 0; +} + +static int hsr_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, hsr_mode_show, PDE_DATA(inode)); +} + +static ssize_t hsr_mode_store(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct hsr_priv *priv = (struct hsr_priv *)PDE_DATA(file_inode(file)); + int err; + + err = get_set_param(priv, buffer, count, LREDEV_ATTR_ID_HSR_MODE); + if (err) + return err; + + return count; +} + +static const struct proc_ops hsr_mode_fops = { + .proc_open = hsr_mode_open, + .proc_read = seq_read, + .proc_write = hsr_mode_store, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int prp_tr_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lredev_attr temp_attr; + int err; + + if (!priv->rx_offloaded) { + seq_printf(sfp, "%u\n", priv->prp_tr); + return 0; + } + + temp_attr.id = LREDEV_ATTR_ID_PRP_TR; + err = hsr_lredev_attr_get(priv, &temp_attr); + if (err) + return err; + + seq_printf(sfp, "%u\n", temp_attr.tr_mode); + return 0; +} + +static int prp_tr_open(struct inode *inode, struct file *file) +{ + return single_open(file, prp_tr_show, PDE_DATA(inode)); +} + +static ssize_t prp_tr_store(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct hsr_priv *priv = (struct hsr_priv *)PDE_DATA(file_inode(file)); + int err; + + err = get_set_param(priv, buffer, count, LREDEV_ATTR_ID_PRP_TR); + if (err) + return err; + + return count; +} + +static const struct proc_ops prp_tr_fops = { + .proc_open = prp_tr_open, + .proc_read = seq_read, + .proc_write = prp_tr_store, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int dlrmt_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lredev_attr temp_attr; + int err; + + if (!priv->rx_offloaded) { + seq_printf(sfp, "%u\n", priv->dlrmt); + return 0; + } + + temp_attr.id = LREDEV_ATTR_ID_DLRMT; + err = hsr_lredev_attr_get(priv, &temp_attr); + if (err) + return err; + + seq_printf(sfp, "%u\n", temp_attr.dl_reside_max_time); + return 0; +} + +static int dlrmt_open(struct inode *inode, struct file *file) +{ + return single_open(file, dlrmt_show, PDE_DATA(inode)); +} + +static ssize_t dlrmt_store(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct hsr_priv *priv = (struct hsr_priv *)PDE_DATA(file_inode(file)); + int err; + + err = get_set_param(priv, buffer, count, LREDEV_ATTR_ID_DLRMT); + if (err) + return err; + + return count; +} + +static const struct proc_ops dlrmt_fops = { + .proc_open = dlrmt_open, + .proc_read = seq_read, + .proc_write = dlrmt_store, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int dd_mode_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lredev_attr temp_attr; + int err; + + if (!priv->rx_offloaded) { + seq_printf(sfp, "%u\n", priv->dd_mode); + return 0; + } + + temp_attr.id = LREDEV_ATTR_ID_DD_MODE; + err = hsr_lredev_attr_get(priv, &temp_attr); + if (err) + return err; + + seq_printf(sfp, "%u\n", temp_attr.dd_mode); + return 0; +} + +static int dd_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, dd_mode_show, PDE_DATA(inode)); +} + +static ssize_t dd_mode_store(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct hsr_priv *priv = (struct hsr_priv *)PDE_DATA(file_inode(file)); + int err; + + err = get_set_param(priv, buffer, count, LREDEV_ATTR_ID_DD_MODE); + if (err) + return err; + + return count; +} + +static const struct proc_ops dd_mode_fops = { + .proc_open = dd_mode_open, + .proc_read = seq_read, + .proc_write = dd_mode_store, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int clear_nt_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + struct lredev_attr temp_attr; + int err; + + if (!priv->rx_offloaded) { + seq_printf(sfp, "%u\n", priv->clear_nt_cmd); + return 0; + } + + temp_attr.id = LREDEV_ATTR_ID_CLEAR_NT; + err = hsr_lredev_attr_get(priv, &temp_attr); + if (err) + return err; + + seq_printf(sfp, "%u\n", temp_attr.clear_nt_cmd); + return 0; +} + +static int clear_nt_open(struct inode *inode, struct file *file) +{ + return single_open(file, clear_nt_show, PDE_DATA(inode)); +} + +static ssize_t clear_nt_store(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct hsr_priv *priv = (struct hsr_priv *)PDE_DATA(file_inode(file)); + int err; + + err = get_set_param(priv, buffer, count, LREDEV_ATTR_ID_CLEAR_NT); + if (err) + return err; + + return count; +} + +static const struct proc_ops clear_nt_fops = { + .proc_open = clear_nt_open, + .proc_read = seq_read, + .proc_write = clear_nt_store, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int disable_sv_show(struct seq_file *sfp, void *v) +{ + struct hsr_priv *priv = (struct hsr_priv *)sfp->private; + + seq_printf(sfp, "%u\n", priv->disable_sv_frame); + return 0; +} + +static int disable_sv_open(struct inode *inode, struct file *file) +{ + return single_open(file, disable_sv_show, PDE_DATA(inode)); +} + +static ssize_t disable_sv_store(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct hsr_priv *priv = (struct hsr_priv *)PDE_DATA(file_inode(file)); + char cmd_buffer[BUF_SIZE]; + int ret = -EINVAL; + u32 val; + + if (count > (sizeof(cmd_buffer) - 1)) + goto err; + + if (copy_from_user(cmd_buffer, buffer, count)) { + ret = -EFAULT; + goto err; + } + cmd_buffer[count] = '\0'; + ret = kstrtou32(cmd_buffer, 0, &val); + if (ret < 0 || val > 1) + goto err; + + priv->disable_sv_frame = val; + + return count; +err: + return ret; +} + +static const struct proc_ops disable_sv_fops = { + .proc_open = disable_sv_open, + .proc_read = seq_read, + .proc_write = disable_sv_store, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +int hsr_create_procfs(struct hsr_priv *priv, struct net_device *ndev) +{ + int ret = -ENODEV; + + priv->dir = proc_mkdir(ndev->name, NULL); + if (!priv->dir) + return ret; + + priv->lre_stats_file = proc_create_data("lre-stats", 0444, priv->dir, + &hsr_lre_stats_fops, + (void *)priv); + if (!priv->lre_stats_file) + goto fail_lre_stats; + + priv->node_table_file = proc_create_data("node-table", 0444, priv->dir, + &hsr_node_table_fops, + (void *)priv); + if (!priv->node_table_file) + goto fail_node_table; + + priv->hsr_mode_file = proc_create_data("hsr-mode", 0644, priv->dir, + &hsr_mode_fops, (void *)priv); + if (!priv->hsr_mode_file) + goto fail_hsr_mode; + + priv->dd_mode_file = proc_create_data("dd-mode", 0644, priv->dir, + &dd_mode_fops, (void *)priv); + if (!priv->dd_mode_file) + goto fail_dd_mode; + + priv->prp_tr_file = proc_create_data("prp-tr", 0644, priv->dir, + &prp_tr_fops, (void *)priv); + if (!priv->prp_tr_file) + goto fail_prp_tr; + + priv->clear_nt_file = proc_create_data("clear-nt", 0644, priv->dir, + &clear_nt_fops, (void *)priv); + if (!priv->clear_nt_file) + goto fail_clear_nt; + + priv->dlrmt_file = proc_create_data("dlrmt", 0644, priv->dir, + &dlrmt_fops, (void *)priv); + if (!priv->dlrmt_file) + goto fail_dlrmt; + + priv->disable_sv_file = proc_create_data("disable-sv-frame", 0644, + priv->dir, &disable_sv_fops, + (void *)priv); + if (!priv->disable_sv_file) + goto fail_disable_sv; + + return 0; +fail_disable_sv: + if (priv->dlrmt_file) + remove_proc_entry("dlrmt", priv->dir); +fail_dlrmt: + if (priv->clear_nt_file) + remove_proc_entry("clear-nt", priv->dir); +fail_clear_nt: + if (priv->prp_tr_file) + remove_proc_entry("prp-tr", priv->dir); +fail_prp_tr: + if (priv->dd_mode_file) + remove_proc_entry("dd-mode", priv->dir); +fail_dd_mode: + if (priv->hsr_mode_file) + remove_proc_entry("hsr-mode", priv->dir); +fail_hsr_mode: + if (priv->node_table_file) + remove_proc_entry("node-table", priv->dir); +fail_node_table: + if (priv->lre_stats_file) + remove_proc_entry("lre-stats", priv->dir); +fail_lre_stats: + remove_proc_entry(ndev->name, NULL); + return ret; +} + +void hsr_remove_procfs(struct hsr_priv *priv, struct net_device *ndev) +{ + remove_proc_entry("disable-sv-frame", priv->dir); + remove_proc_entry("dlrmt", priv->dir); + remove_proc_entry("clear-nt", priv->dir); + remove_proc_entry("prp-tr", priv->dir); + remove_proc_entry("dd-mode", priv->dir); + remove_proc_entry("hsr-mode", priv->dir); + remove_proc_entry("lre-stats", priv->dir); + remove_proc_entry("node-table", priv->dir); + remove_proc_entry(ndev->name, NULL); + priv->dir = NULL; +} diff -Naur --no-dereference a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c --- a/net/hsr/hsr_slave.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/hsr_slave.c 2022-01-06 12:45:53.838318206 -0500 @@ -23,9 +23,9 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) { + struct hsr_priv *hsr = NULL; struct sk_buff *skb = *pskb; struct hsr_port *port; - struct hsr_priv *hsr; __be16 protocol; /* Packets from dev_loopback_xmit() do not have L2 header, bail out */ @@ -44,16 +44,19 @@ if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) { /* Directly kill frames sent by ourselves */ + INC_CNT_RX_ERROR_AB(port->type, port->hsr); kfree_skb(skb); goto finish_consume; } - /* For HSR, only tagged frames are expected, but for PRP - * there could be non tagged frames as well from Single - * attached nodes (SANs). + /* For HSR, only tagged frames are expected (unless the device offloads + * HSR tag removal), but for PRP there could be non tagged frames as + * well from Single attached nodes (SANs). */ protocol = eth_hdr(skb)->h_proto; - if (hsr->proto_ops->invalid_dan_ingress_frame && + + if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) && + hsr->proto_ops->invalid_dan_ingress_frame && hsr->proto_ops->invalid_dan_ingress_frame(protocol)) goto finish_pass; @@ -64,12 +67,15 @@ skb_set_network_header(skb, ETH_HLEN + HSR_HLEN); skb_reset_mac_len(skb); + INC_CNT_RX_AB(port->type, hsr); hsr_forward_skb(skb, port); finish_consume: return RX_HANDLER_CONSUMED; finish_pass: + if (hsr) + INC_CNT_RX_ERROR_AB(port->type, hsr); return RX_HANDLER_PASS; } @@ -129,9 +135,14 @@ struct hsr_port *master; int res; - res = dev_set_promiscuity(dev, 1); - if (res) - return res; + /* Don't use promiscuous mode for offload since L2 frame forward + * happens at the offloaded hardware. + */ + if (!port->hsr->rx_offloaded) { + res = dev_set_promiscuity(dev, 1); + if (res) + return res; + } master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); hsr_dev = master->dev; @@ -150,7 +161,9 @@ fail_rx_handler: netdev_upper_dev_unlink(dev, hsr_dev); fail_upper_dev_link: - dev_set_promiscuity(dev, -1); + if (!port->hsr->rx_offloaded) + dev_set_promiscuity(dev, -1); + return res; } diff -Naur --no-dereference a/net/hsr/Makefile b/net/hsr/Makefile --- a/net/hsr/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/net/hsr/Makefile 2022-01-06 12:45:53.838318206 -0500 @@ -8,3 +8,4 @@ hsr-y := hsr_main.o hsr_framereg.o hsr_device.o \ hsr_netlink.o hsr_slave.o hsr_forward.o hsr-$(CONFIG_DEBUG_FS) += hsr_debugfs.o +hsr-$(CONFIG_PROC_FS) += hsr_proc.o diff -Naur --no-dereference a/net/Kconfig b/net/Kconfig --- a/net/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/net/Kconfig 2022-01-06 12:45:53.834318188 -0500 @@ -391,6 +391,7 @@ source "net/rfkill/Kconfig" source "net/9p/Kconfig" source "net/caif/Kconfig" +source "net/rpmsg/Kconfig" source "net/ceph/Kconfig" source "net/nfc/Kconfig" source "net/psample/Kconfig" diff -Naur --no-dereference a/net/mac80211/aead_api.c b/net/mac80211/aead_api.c --- a/net/mac80211/aead_api.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/aead_api.c 2022-01-06 12:45:53.838318206 -0500 @@ -23,7 +23,6 @@ struct aead_request *aead_req; int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); u8 *__aad; - int ret; aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); if (!aead_req) @@ -41,10 +40,10 @@ aead_request_set_crypt(aead_req, sg, sg, data_len, b_0); aead_request_set_ad(aead_req, sg[0].length); - ret = crypto_aead_encrypt(aead_req); + crypto_aead_encrypt(aead_req); kfree_sensitive(aead_req); - return ret; + return 0; } int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, diff -Naur --no-dereference a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c --- a/net/mac80211/aes_gmac.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/aes_gmac.c 2022-01-06 12:45:53.838318206 -0500 @@ -22,7 +22,6 @@ struct aead_request *aead_req; int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); const __le16 *fc; - int ret; if (data_len < GMAC_MIC_LEN) return -EINVAL; @@ -60,10 +59,10 @@ aead_request_set_crypt(aead_req, sg, sg, 0, iv); aead_request_set_ad(aead_req, GMAC_AAD_LEN + data_len); - ret = crypto_aead_encrypt(aead_req); + crypto_aead_encrypt(aead_req); kfree_sensitive(aead_req); - return ret; + return 0; } struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], diff -Naur --no-dereference a/net/mac80211/cfg.c b/net/mac80211/cfg.c --- a/net/mac80211/cfg.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/cfg.c 2022-01-06 12:45:53.838318206 -0500 @@ -152,8 +152,6 @@ struct vif_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; - struct sta_info *sta; int ret; ret = ieee80211_if_change_type(sdata, type); @@ -164,24 +162,7 @@ RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); ieee80211_check_fast_rx_iface(sdata); } else if (type == NL80211_IFTYPE_STATION && params->use_4addr >= 0) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - if (params->use_4addr == ifmgd->use_4addr) - return 0; - sdata->u.mgd.use_4addr = params->use_4addr; - if (!ifmgd->associated) - return 0; - - mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ifmgd->bssid); - if (sta) - drv_sta_set_4addr(local, sdata, &sta->sta, - params->use_4addr); - mutex_unlock(&local->sta_mtx); - - if (params->use_4addr) - ieee80211_send_4addr_nullfunc(local, sdata); } if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { @@ -1808,10 +1789,8 @@ } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - sta->sdata->u.vlan.sta) { - ieee80211_clear_fast_rx(sta); + sta->sdata->u.vlan.sta) RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL); - } if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) ieee80211_vif_dec_num_mcast(sta->sdata); @@ -2982,14 +2961,14 @@ continue; for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) { - if (sdata->rc_rateidx_mcs_mask[i][j] != 0xff) { + if (~sdata->rc_rateidx_mcs_mask[i][j]) { sdata->rc_has_mcs_mask[i] = true; break; } } for (j = 0; j < NL80211_VHT_NSS_MAX; j++) { - if (sdata->rc_rateidx_vht_mcs_mask[i][j] != 0xffff) { + if (~sdata->rc_rateidx_vht_mcs_mask[i][j]) { sdata->rc_has_vht_mcs_mask[i] = true; break; } diff -Naur --no-dereference a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c --- a/net/mac80211/debugfs.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/debugfs.c 2022-01-06 12:45:53.838318206 -0500 @@ -120,17 +120,18 @@ { struct ieee80211_local *local = file->private_data; char buf[100]; + size_t len; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = '\0'; + len = strlen(buf); + if (len > 0 && buf[len-1] == '\n') + buf[len-1] = 0; if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1) return count; @@ -176,17 +177,18 @@ { struct ieee80211_local *local = file->private_data; char buf[16]; + size_t len; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; if (kstrtou16(buf, 0, &local->airtime_flags)) return -EINVAL; @@ -235,19 +237,20 @@ { struct ieee80211_local *local = file->private_data; char buf[100]; + size_t len; u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old; struct sta_info *sta; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) return -EINVAL; @@ -303,17 +306,18 @@ { struct ieee80211_local *local = file->private_data; char buf[3]; + size_t len; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = '\0'; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; if (buf[0] == '0' && buf[1] == '\0') local->force_tx_status = 0; diff -Naur --no-dereference a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c --- a/net/mac80211/driver-ops.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/driver-ops.c 2022-01-06 12:45:53.838318206 -0500 @@ -125,11 +125,8 @@ } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { ret = drv_sta_add(local, sdata, &sta->sta); - if (ret == 0) { + if (ret == 0) sta->uploaded = true; - if (rcu_access_pointer(sta->sta.rates)) - drv_sta_rate_tbl_update(local, sdata, &sta->sta); - } } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { drv_sta_remove(local, sdata, &sta->sta); diff -Naur --no-dereference a/net/mac80211/ibss.c b/net/mac80211/ibss.c --- a/net/mac80211/ibss.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/ibss.c 2022-01-06 12:45:53.838318206 -0500 @@ -1874,8 +1874,6 @@ /* remove beacon */ kfree(sdata->u.ibss.ie); - sdata->u.ibss.ie = NULL; - sdata->u.ibss.ie_len = 0; /* on the next join, re-program HT parameters */ memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); diff -Naur --no-dereference a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h --- a/net/mac80211/ieee80211_i.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/ieee80211_i.h 2022-01-06 12:45:53.838318206 -0500 @@ -50,6 +50,12 @@ #define IEEE80211_ENCRYPT_HEADROOM 8 #define IEEE80211_ENCRYPT_TAILROOM 18 +/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent + * reception of at least three fragmented frames. This limit can be increased + * by changing this define, at the cost of slower frame reassembly and + * increased memory use (about 2 kB of RAM per entry). */ +#define IEEE80211_FRAGMENT_MAX 4 + /* power level hasn't been configured (or set to automatic) */ #define IEEE80211_UNSET_POWER_LEVEL INT_MIN @@ -82,6 +88,18 @@ #define IEEE80211_MAX_NAN_INSTANCE_ID 255 +struct ieee80211_fragment_entry { + struct sk_buff_head skb_list; + unsigned long first_frag_time; + u16 seq; + u16 extra_len; + u16 last_frag; + u8 rx_queue; + bool check_sequential_pn; /* needed for CCMP/GCMP */ + u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ +}; + + struct ieee80211_bss { u32 device_ts_beacon, device_ts_presp; @@ -223,15 +241,8 @@ */ int security_idx; - union { - struct { - u32 iv32; - u16 iv16; - } tkip; - struct { - u8 pn[IEEE80211_CCMP_PN_LEN]; - } ccm_gcm; - }; + u32 tkip_iv32; + u16 tkip_iv16; }; struct ieee80211_csa_settings { @@ -895,7 +906,9 @@ char name[IFNAMSIZ]; - struct ieee80211_fragment_cache frags; + /* Fragment table for host-based reassembly */ + struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; + unsigned int fragment_next; /* TID bitmap for NoAck policy */ u16 noack_map; @@ -1069,7 +1082,6 @@ IEEE80211_QUEUE_STOP_REASON_FLUSH, IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN, IEEE80211_QUEUE_STOP_REASON_RESERVE_TID, - IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE, IEEE80211_QUEUE_STOP_REASONS, }; @@ -1445,7 +1457,7 @@ rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) { + if (WARN_ON_ONCE(!chanctx_conf)) { rcu_read_unlock(); return NULL; } @@ -2051,8 +2063,6 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, bool powersave); -void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr, bool ack, u16 tx_time); @@ -2316,7 +2326,4 @@ #define debug_noinline #endif -void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); -void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); - #endif /* IEEE80211_I_H */ diff -Naur --no-dereference a/net/mac80211/iface.c b/net/mac80211/iface.c --- a/net/mac80211/iface.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/iface.c 2022-01-06 12:45:53.838318206 -0500 @@ -8,7 +8,7 @@ * Copyright 2008, Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (c) 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include #include @@ -679,12 +679,16 @@ */ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { + int i; + /* free extra data */ ieee80211_free_keys(sdata, false); ieee80211_debugfs_remove_netdev(sdata); - ieee80211_destroy_frag_cache(&sdata->frags); + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) + __skb_queue_purge(&sdata->fragments[i].skb_list); + sdata->fragment_next = 0; if (ieee80211_vif_is_mesh(&sdata->vif)) ieee80211_mesh_teardown_sdata(sdata); @@ -1650,10 +1654,6 @@ if (ret) return ret; - ieee80211_stop_vif_queues(local, sdata, - IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE); - synchronize_net(); - ieee80211_do_stop(sdata, false); ieee80211_teardown_sdata(sdata); @@ -1676,8 +1676,6 @@ err = ieee80211_do_open(&sdata->wdev, false); WARN(err, "type change: do_open returned %d", err); - ieee80211_wake_vif_queues(local, sdata, - IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE); return ret; } @@ -1946,7 +1944,8 @@ sdata->wdev.wiphy = local->hw.wiphy; sdata->local = local; - ieee80211_init_frag_cache(&sdata->frags); + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) + skb_queue_head_init(&sdata->fragments[i].skb_list); INIT_LIST_HEAD(&sdata->key_list); @@ -2000,16 +1999,9 @@ netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); - /* MTU range is normally 256 - 2304, where the upper limit is - * the maximum MSDU size. Monitor interfaces send and receive - * MPDU and A-MSDU frames which may be much larger so we do - * not impose an upper limit in that case. - */ + /* MTU range: 256 - 2304 */ ndev->min_mtu = 256; - if (type == NL80211_IFTYPE_MONITOR) - ndev->max_mtu = 0; - else - ndev->max_mtu = local->hw.max_mtu; + ndev->max_mtu = local->hw.max_mtu; ret = register_netdevice(ndev); if (ret) { diff -Naur --no-dereference a/net/mac80211/Kconfig b/net/mac80211/Kconfig --- a/net/mac80211/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/Kconfig 2022-01-06 12:45:53.838318206 -0500 @@ -69,7 +69,7 @@ config MAC80211_LEDS bool "Enable LED triggers" depends on MAC80211 - depends on LEDS_CLASS=y || LEDS_CLASS=MAC80211 + depends on LEDS_CLASS select LEDS_TRIGGERS help This option enables a few LED triggers for different diff -Naur --no-dereference a/net/mac80211/key.c b/net/mac80211/key.c --- a/net/mac80211/key.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/key.c 2022-01-06 12:45:53.838318206 -0500 @@ -799,7 +799,6 @@ struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { - static atomic_t key_color = ATOMIC_INIT(0); struct ieee80211_key *old_key; int idx = key->conf.keyidx; bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; @@ -851,12 +850,6 @@ key->sdata = sdata; key->sta = sta; - /* - * Assign a unique ID to every key so we can easily prevent mixed - * key and fragment cache attacks. - */ - key->color = atomic_inc_return(&key_color); - increment_tailroom_need_count(sdata); ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key); diff -Naur --no-dereference a/net/mac80211/key.h b/net/mac80211/key.h --- a/net/mac80211/key.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/key.h 2022-01-06 12:45:53.838318206 -0500 @@ -128,8 +128,6 @@ } debugfs; #endif - unsigned int color; - /* * key config, must be last because it contains key * material as variable length member diff -Naur --no-dereference a/net/mac80211/main.c b/net/mac80211/main.c --- a/net/mac80211/main.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/main.c 2022-01-06 12:45:53.838318206 -0500 @@ -982,19 +982,8 @@ continue; if (!dflt_chandef.chan) { - /* - * Assign the first enabled channel to dflt_chandef - * from the list of channels - */ - for (i = 0; i < sband->n_channels; i++) - if (!(sband->channels[i].flags & - IEEE80211_CHAN_DISABLED)) - break; - /* if none found then use the first anyway */ - if (i == sband->n_channels) - i = 0; cfg80211_chandef_create(&dflt_chandef, - &sband->channels[i], + &sband->channels[0], NL80211_CHAN_NO_HT); /* init channel we're on */ if (!local->use_chanctx && !local->_oper_chandef.chan) { @@ -1150,11 +1139,8 @@ if (local->hw.wiphy->max_scan_ie_len) local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len; - if (WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, - local->hw.n_cipher_schemes))) { - result = -EINVAL; - goto fail_workqueue; - } + WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, + local->hw.n_cipher_schemes)); result = ieee80211_init_cipher_suites(local); if (result < 0) diff -Naur --no-dereference a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c --- a/net/mac80211/mesh_hwmp.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/mesh_hwmp.c 2022-01-06 12:45:53.838318206 -0500 @@ -356,7 +356,7 @@ */ tx_time = (device_constant + 10 * test_frame_len / rate); estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err)); - result = ((u64)tx_time * estimated_retx) >> (2 * ARITH_SHIFT); + result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT); return (u32)result; } diff -Naur --no-dereference a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c --- a/net/mac80211/mesh_pathtbl.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/mesh_pathtbl.c 2022-01-06 12:45:53.838318206 -0500 @@ -60,10 +60,7 @@ atomic_set(&newtbl->entries, 0); spin_lock_init(&newtbl->gates_lock); spin_lock_init(&newtbl->walk_lock); - if (rhashtable_init(&newtbl->rhead, &mesh_rht_params)) { - kfree(newtbl); - return NULL; - } + rhashtable_init(&newtbl->rhead, &mesh_rht_params); return newtbl; } diff -Naur --no-dereference a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c --- a/net/mac80211/mesh_ps.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/mesh_ps.c 2022-01-06 12:45:53.838318206 -0500 @@ -2,7 +2,6 @@ /* * Copyright 2012-2013, Marco Porsch * Copyright 2012-2013, cozybit Inc. - * Copyright (C) 2021 Intel Corporation */ #include "mesh.h" @@ -589,7 +588,7 @@ /* only transmit to PS STA with announced, non-zero awake window */ if (test_sta_flag(sta, WLAN_STA_PS_STA) && - (!elems->awake_window || !get_unaligned_le16(elems->awake_window))) + (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) return; if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) diff -Naur --no-dereference a/net/mac80211/mlme.c b/net/mac80211/mlme.c --- a/net/mac80211/mlme.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/mlme.c 2022-01-06 12:45:53.838318206 -0500 @@ -1094,6 +1094,11 @@ struct ieee80211_hdr_3addr *nullfunc; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + /* Don't send NDPs when STA is connected HE */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + !(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) + return; + skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, !ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP)); if (!skb) @@ -1115,8 +1120,8 @@ ieee80211_tx_skb(sdata, skb); } -void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { struct sk_buff *skb; struct ieee80211_hdr *nullfunc; @@ -1125,6 +1130,10 @@ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return; + /* Don't send NDPs when connected HE */ + if (!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE)) + return; + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); if (!skb) return; @@ -1286,11 +1295,6 @@ sdata->vif.csa_active = false; ifmgd->csa_waiting_bcn = false; - /* - * If the CSA IE is still present on the beacon after the switch, - * we need to consider it as a new CSA (possibly to self). - */ - ifmgd->beacon_crc_valid = false; ret = drv_post_channel_switch(sdata); if (ret) { @@ -4010,14 +4014,10 @@ if (elems.mbssid_config_ie) bss_conf->profile_periodicity = elems.mbssid_config_ie->profile_periodicity; - else - bss_conf->profile_periodicity = 0; if (elems.ext_capab_len >= 11 && (elems.ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) bss_conf->ema_ap = true; - else - bss_conf->ema_ap = false; /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; @@ -4660,10 +4660,7 @@ timeout = sta->rx_stats.last_rx; timeout += IEEE80211_CONNECTION_IDLE_TIME; - /* If timeout is after now, then update timer to fire at - * the later date, but do not actually probe at this time. - */ - if (time_is_after_jiffies(timeout)) { + if (time_is_before_jiffies(timeout)) { mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout)); return; } @@ -5026,7 +5023,7 @@ he_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_OPERATION, ies->data, ies->len); if (he_oper_ie && - he_oper_ie[1] >= ieee80211_he_oper_size(&he_oper_ie[3])) + he_oper_ie[1] == ieee80211_he_oper_size(&he_oper_ie[3])) he_oper = (void *)(he_oper_ie + 3); else he_oper = NULL; @@ -5744,16 +5741,12 @@ beacon_ies->data, beacon_ies->len); if (elem && elem->datalen >= 3) sdata->vif.bss_conf.profile_periodicity = elem->data[2]; - else - sdata->vif.bss_conf.profile_periodicity = 0; elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, beacon_ies->data, beacon_ies->len); if (elem && elem->datalen >= 11 && (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) sdata->vif.bss_conf.ema_ap = true; - else - sdata->vif.bss_conf.ema_ap = false; } else { assoc_data->timeout = jiffies; assoc_data->timeout_started = true; diff -Naur --no-dereference a/net/mac80211/rate.c b/net/mac80211/rate.c --- a/net/mac80211/rate.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/rate.c 2022-01-06 12:45:53.838318206 -0500 @@ -960,8 +960,7 @@ if (old) kfree_rcu(old, rcu_head); - if (sta->uploaded) - drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta); + drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta); ieee80211_sta_set_expected_throughput(pubsta, sta_get_expected_throughput(sta)); diff -Naur --no-dereference a/net/mac80211/rx.c b/net/mac80211/rx.c --- a/net/mac80211/rx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/rx.c 2022-01-06 12:45:53.838318206 -0500 @@ -6,7 +6,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include @@ -1945,8 +1945,7 @@ int keyid = rx->sta->ptk_idx; sta_ptk = rcu_dereference(rx->sta->ptk[keyid]); - if (ieee80211_has_protected(fc) && - !(status->flag & RX_FLAG_IV_STRIPPED)) { + if (ieee80211_has_protected(fc)) { cs = rx->sta->cipher_scheme; keyid = ieee80211_get_keyid(rx->skb, cs); @@ -2134,34 +2133,19 @@ return result; } -void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cache->entries); i++) - skb_queue_head_init(&cache->entries[i].skb_list); -} - -void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cache->entries); i++) - __skb_queue_purge(&cache->entries[i].skb_list); -} - static inline struct ieee80211_fragment_entry * -ieee80211_reassemble_add(struct ieee80211_fragment_cache *cache, +ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, unsigned int frag, unsigned int seq, int rx_queue, struct sk_buff **skb) { struct ieee80211_fragment_entry *entry; - entry = &cache->entries[cache->next++]; - if (cache->next >= IEEE80211_FRAGMENT_MAX) - cache->next = 0; + entry = &sdata->fragments[sdata->fragment_next++]; + if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) + sdata->fragment_next = 0; - __skb_queue_purge(&entry->skb_list); + if (!skb_queue_empty(&entry->skb_list)) + __skb_queue_purge(&entry->skb_list); __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ *skb = NULL; @@ -2176,14 +2160,14 @@ } static inline struct ieee80211_fragment_entry * -ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache, +ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, unsigned int frag, unsigned int seq, int rx_queue, struct ieee80211_hdr *hdr) { struct ieee80211_fragment_entry *entry; int i, idx; - idx = cache->next; + idx = sdata->fragment_next; for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { struct ieee80211_hdr *f_hdr; struct sk_buff *f_skb; @@ -2192,7 +2176,7 @@ if (idx < 0) idx = IEEE80211_FRAGMENT_MAX - 1; - entry = &cache->entries[idx]; + entry = &sdata->fragments[idx]; if (skb_queue_empty(&entry->skb_list) || entry->seq != seq || entry->rx_queue != rx_queue || entry->last_frag + 1 != frag) @@ -2220,27 +2204,15 @@ return NULL; } -static bool requires_sequential_pn(struct ieee80211_rx_data *rx, __le16 fc) -{ - return rx->key && - (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || - rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || - rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || - rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && - ieee80211_has_protected(fc); -} - static ieee80211_rx_result debug_noinline ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) { - struct ieee80211_fragment_cache *cache = &rx->sdata->frags; struct ieee80211_hdr *hdr; u16 sc; __le16 fc; unsigned int frag, seq; struct ieee80211_fragment_entry *entry; struct sk_buff *skb; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); hdr = (struct ieee80211_hdr *)rx->skb->data; fc = hdr->frame_control; @@ -2251,15 +2223,14 @@ sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; - if (rx->sta) - cache = &rx->sta->frags; + if (is_multicast_ether_addr(hdr->addr1)) { + I802_DEBUG_INC(rx->local->dot11MulticastReceivedFrameCount); + goto out_no_led; + } if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) goto out; - if (is_multicast_ether_addr(hdr->addr1)) - return RX_DROP_MONITOR; - I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) @@ -2275,17 +2246,20 @@ if (frag == 0) { /* This is the first fragment of a new frame. */ - entry = ieee80211_reassemble_add(cache, frag, seq, + entry = ieee80211_reassemble_add(rx->sdata, frag, seq, rx->seqno_idx, &(rx->skb)); - if (requires_sequential_pn(rx, fc)) { + if (rx->key && + (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || + rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || + rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || + rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && + ieee80211_has_protected(fc)) { int queue = rx->security_idx; /* Store CCMP/GCMP PN so that we can verify that the * next fragment has a sequential PN value. */ entry->check_sequential_pn = true; - entry->is_protected = true; - entry->key_color = rx->key->color; memcpy(entry->last_pn, rx->key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN); @@ -2297,11 +2271,6 @@ sizeof(rx->key->u.gcmp.rx_pn[queue])); BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); - } else if (rx->key && - (ieee80211_has_protected(fc) || - (status->flag & RX_FLAG_DECRYPTED))) { - entry->is_protected = true; - entry->key_color = rx->key->color; } return RX_QUEUED; } @@ -2309,7 +2278,7 @@ /* This is a fragment for a frame that should already be pending in * fragment cache. Add this fragment to the end of the pending entry. */ - entry = ieee80211_reassemble_find(cache, frag, seq, + entry = ieee80211_reassemble_find(rx->sdata, frag, seq, rx->seqno_idx, hdr); if (!entry) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); @@ -2324,39 +2293,25 @@ if (entry->check_sequential_pn) { int i; u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; + int queue; - if (!requires_sequential_pn(rx, fc)) - return RX_DROP_UNUSABLE; - - /* Prevent mixed key and fragment cache attacks */ - if (entry->key_color != rx->key->color) + if (!rx->key || + (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256 && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP_256)) return RX_DROP_UNUSABLE; - memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { pn[i]++; if (pn[i]) break; } - - rpn = rx->ccm_gcm.pn; + queue = rx->security_idx; + rpn = rx->key->u.ccmp.rx_pn[queue]; if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) return RX_DROP_UNUSABLE; memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN); - } else if (entry->is_protected && - (!rx->key || - (!ieee80211_has_protected(fc) && - !(status->flag & RX_FLAG_DECRYPTED)) || - rx->key->color != entry->key_color)) { - /* Drop this as a mixed key or fragment cache attack, even - * if for TKIP Michael MIC should protect us, and WEP is a - * lost cause anyway. - */ - return RX_DROP_UNUSABLE; - } else if (entry->is_protected && rx->key && - entry->key_color != rx->key->color && - (status->flag & RX_FLAG_DECRYPTED)) { - return RX_DROP_UNUSABLE; } skb_pull(rx->skb, ieee80211_hdrlen(fc)); @@ -2385,6 +2340,7 @@ out: ieee80211_led_rx(rx->local); + out_no_led: if (rx->sta) rx->sta->rx_stats.packets++; return RX_CONTINUE; @@ -2548,13 +2504,13 @@ struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; /* - * Allow EAPOL frames to us/the PAE group address regardless of - * whether the frame was encrypted or not, and always disallow - * all other destination addresses for them. + * Allow EAPOL frames to us/the PAE group address regardless + * of whether the frame was encrypted or not. */ - if (unlikely(ehdr->h_proto == rx->sdata->control_port_protocol)) - return ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || - ether_addr_equal(ehdr->h_dest, pae_group_addr); + if (ehdr->h_proto == rx->sdata->control_port_protocol && + (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || + ether_addr_equal(ehdr->h_dest, pae_group_addr))) + return true; if (ieee80211_802_1x_port_control(rx) || ieee80211_drop_unencrypted(rx, fc)) @@ -2579,28 +2535,8 @@ cfg80211_rx_control_port(dev, skb, noencrypt); dev_kfree_skb(skb); } else { - struct ethhdr *ehdr = (void *)skb_mac_header(skb); - memset(skb->cb, 0, sizeof(skb->cb)); - /* - * 802.1X over 802.11 requires that the authenticator address - * be used for EAPOL frames. However, 802.1X allows the use of - * the PAE group address instead. If the interface is part of - * a bridge and we pass the frame with the PAE group address, - * then the bridge will forward it to the network (even if the - * client was not associated yet), which isn't supposed to - * happen. - * To avoid that, rewrite the destination address to our own - * address, so that the authenticator (e.g. hostapd) will see - * the frame, but bridge won't forward it anywhere else. Note - * that due to earlier filtering, the only other address can - * be the PAE group address. - */ - if (unlikely(skb->protocol == sdata->control_port_protocol && - !ether_addr_equal(ehdr->h_dest, sdata->vif.addr))) - ether_addr_copy(ehdr->h_dest, sdata->vif.addr); - /* deliver to local stack */ if (rx->list) list_add_tail(&skb->list, rx->list); @@ -2640,7 +2576,6 @@ if ((sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && - ehdr->h_proto != rx->sdata->control_port_protocol && (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { if (is_multicast_ether_addr(ehdr->h_dest) && ieee80211_vif_get_num_mcast_if(sdata) != 0) { @@ -2750,7 +2685,7 @@ if (ieee80211_data_to_8023_exthdr(skb, ðhdr, rx->sdata->vif.addr, rx->sdata->vif.type, - data_offset, true)) + data_offset)) return RX_DROP_UNUSABLE; ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, @@ -2807,23 +2742,6 @@ if (is_multicast_ether_addr(hdr->addr1)) return RX_DROP_UNUSABLE; - if (rx->key) { - /* - * We should not receive A-MSDUs on pre-HT connections, - * and HT connections cannot use old ciphers. Thus drop - * them, as in those cases we couldn't even have SPP - * A-MSDUs or such. - */ - switch (rx->key->conf.cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - case WLAN_CIPHER_SUITE_TKIP: - return RX_DROP_UNUSABLE; - default: - break; - } - } - return __ieee80211_rx_h_amsdu(rx, 0); } @@ -4065,8 +3983,7 @@ if (!bssid) return false; if (ether_addr_equal(sdata->vif.addr, hdr->addr2) || - ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2) || - !is_valid_ether_addr(hdr->addr2)) + ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2)) return false; if (ieee80211_is_beacon(hdr->frame_control)) return true; @@ -4274,8 +4191,6 @@ rcu_read_lock(); key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); if (key) { switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: diff -Naur --no-dereference a/net/mac80211/scan.c b/net/mac80211/scan.c --- a/net/mac80211/scan.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/scan.c 2022-01-06 12:45:53.838318206 -0500 @@ -251,24 +251,13 @@ struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_bss *bss; struct ieee80211_channel *channel; - size_t min_hdr_len = offsetof(struct ieee80211_mgmt, - u.probe_resp.variable); - - if (!ieee80211_is_probe_resp(mgmt->frame_control) && - !ieee80211_is_beacon(mgmt->frame_control) && - !ieee80211_is_s1g_beacon(mgmt->frame_control)) - return; if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { - if (ieee80211_is_s1g_short_beacon(mgmt->frame_control)) - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_short_beacon.variable); - else - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_beacon); - } - - if (skb->len < min_hdr_len) + if (skb->len < 15) + return; + } else if (skb->len < 24 || + (!ieee80211_is_probe_resp(mgmt->frame_control) && + !ieee80211_is_beacon(mgmt->frame_control))) return; sdata1 = rcu_dereference(local->scan_sdata); diff -Naur --no-dereference a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c --- a/net/mac80211/spectmgmt.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/spectmgmt.c 2022-01-06 12:45:53.838318206 -0500 @@ -133,20 +133,16 @@ } if (wide_bw_chansw_ie) { - u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; struct ieee80211_vht_operation vht_oper = { .chan_width = wide_bw_chansw_ie->new_channel_width, .center_freq_seg0_idx = wide_bw_chansw_ie->new_center_freq_seg0, - .center_freq_seg1_idx = new_seg1, + .center_freq_seg1_idx = + wide_bw_chansw_ie->new_center_freq_seg1, /* .basic_mcs_set doesn't matter */ }; - struct ieee80211_ht_operation ht_oper = { - .operation_mode = - cpu_to_le16(new_seg1 << - IEEE80211_HT_OP_MODE_CCFS2_SHIFT), - }; + struct ieee80211_ht_operation ht_oper = {}; /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, * to the previously parsed chandef diff -Naur --no-dereference a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c --- a/net/mac80211/sta_info.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/sta_info.c 2022-01-06 12:45:53.838318206 -0500 @@ -4,7 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include @@ -392,8 +392,6 @@ u64_stats_init(&sta->rx_stats.syncp); - ieee80211_init_frag_cache(&sta->frags); - sta->sta_state = IEEE80211_STA_NONE; /* Mark TID as unreserved */ @@ -1104,8 +1102,6 @@ ieee80211_sta_debugfs_remove(sta); - ieee80211_destroy_frag_cache(&sta->frags); - cleanup_single_sta(sta); } @@ -1398,6 +1394,11 @@ struct ieee80211_tx_info *info; struct ieee80211_chanctx_conf *chanctx_conf; + /* Don't send NDPs when STA is connected HE */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + !(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE)) + return; + if (qos) { fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | @@ -2088,9 +2089,10 @@ sta_get_last_rx_stats(struct sta_info *sta) { struct ieee80211_sta_rx_stats *stats = &sta->rx_stats; + struct ieee80211_local *local = sta->local; int cpu; - if (!sta->pcpu_rx_stats) + if (!ieee80211_hw_check(&local->hw, USES_RSS)) return stats; for_each_possible_cpu(cpu) { @@ -2190,7 +2192,9 @@ int cpu; if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) { - tidstats->rx_msdu += sta_get_tidstats_msdu(&sta->rx_stats, tid); + if (!ieee80211_hw_check(&local->hw, USES_RSS)) + tidstats->rx_msdu += + sta_get_tidstats_msdu(&sta->rx_stats, tid); if (sta->pcpu_rx_stats) { for_each_possible_cpu(cpu) { @@ -2269,6 +2273,7 @@ sinfo->rx_beacon = sdata->u.mgd.count_beacon_signal; drv_sta_statistics(local, sdata, &sta->sta, sinfo); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) | BIT_ULL(NL80211_STA_INFO_STA_FLAGS) | BIT_ULL(NL80211_STA_INFO_BSS_PARAM) | @@ -2303,7 +2308,8 @@ if (!(sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES64) | BIT_ULL(NL80211_STA_INFO_RX_BYTES)))) { - sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats); + if (!ieee80211_hw_check(&local->hw, USES_RSS)) + sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats); if (sta->pcpu_rx_stats) { for_each_possible_cpu(cpu) { diff -Naur --no-dereference a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h --- a/net/mac80211/sta_info.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/sta_info.h 2022-01-06 12:45:53.838318206 -0500 @@ -3,7 +3,7 @@ * Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright(c) 2020-2021 Intel Corporation + * Copyright(c) 2020 Intel Corporation */ #ifndef STA_INFO_H @@ -437,34 +437,6 @@ }; /* - * IEEE 802.11-2016 (10.6 "Defragmentation") recommends support for "concurrent - * reception of at least one MSDU per access category per associated STA" - * on APs, or "at least one MSDU per access category" on other interface types. - * - * This limit can be increased by changing this define, at the cost of slower - * frame reassembly and increased memory use while fragments are pending. - */ -#define IEEE80211_FRAGMENT_MAX 4 - -struct ieee80211_fragment_entry { - struct sk_buff_head skb_list; - unsigned long first_frag_time; - u16 seq; - u16 extra_len; - u16 last_frag; - u8 rx_queue; - u8 check_sequential_pn:1, /* needed for CCMP/GCMP */ - is_protected:1; - u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ - unsigned int key_color; -}; - -struct ieee80211_fragment_cache { - struct ieee80211_fragment_entry entries[IEEE80211_FRAGMENT_MAX]; - unsigned int next; -}; - -/* * The bandwidth threshold below which the per-station CoDel parameters will be * scaled to be more lenient (to prevent starvation of slow stations). This * value will be scaled by the number of active stations when it is being @@ -557,7 +529,6 @@ * @status_stats.last_ack_signal: last ACK signal * @status_stats.ack_signal_filled: last ACK signal validity * @status_stats.avg_ack_signal: average ACK signal - * @frags: fragment cache */ struct sta_info { /* General information, mostly static */ @@ -666,8 +637,6 @@ struct cfg80211_chan_def tdls_chandef; - struct ieee80211_fragment_cache frags; - /* keep last! */ struct ieee80211_sta sta; }; diff -Naur --no-dereference a/net/mac80211/tx.c b/net/mac80211/tx.c --- a/net/mac80211/tx.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/tx.c 2022-01-06 12:45:53.838318206 -0500 @@ -662,7 +662,7 @@ if (!skip_hw && tx->key && tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) info->control.hw_key = &tx->key->conf; - } else if (ieee80211_is_data_present(hdr->frame_control) && tx->sta && + } else if (!ieee80211_is_mgmt(hdr->frame_control) && tx->sta && test_sta_flag(tx->sta, WLAN_STA_USES_ENCRYPTION)) { return TX_DROP; } @@ -2030,26 +2030,6 @@ ieee80211_tx(sdata, sta, skb, false); } -static bool ieee80211_validate_radiotap_len(struct sk_buff *skb) -{ - struct ieee80211_radiotap_header *rthdr = - (struct ieee80211_radiotap_header *)skb->data; - - /* check for not even having the fixed radiotap header part */ - if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) - return false; /* too short to be possibly valid */ - - /* is it a header version we can trust to find length from? */ - if (unlikely(rthdr->it_version)) - return false; /* only version 0 is supported */ - - /* does the skb contain enough to deliver on the alleged length? */ - if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data))) - return false; /* skb too short for claimed rt header extent */ - - return true; -} - bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, struct net_device *dev) { @@ -2058,6 +2038,8 @@ struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[info->band]; int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); u16 txflags; @@ -2070,8 +2052,17 @@ u8 vht_mcs = 0, vht_nss = 0; int i; - if (!ieee80211_validate_radiotap_len(skb)) - return false; + /* check for not even having the fixed radiotap header part */ + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + return false; /* too short to be possibly valid */ + + /* is it a header version we can trust to find length from? */ + if (unlikely(rthdr->it_version)) + return false; /* only version 0 is supported */ + + /* does the skb contain enough to deliver on the alleged length? */ + if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data))) + return false; /* skb too short for claimed rt header extent */ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_DONTFRAG; @@ -2177,11 +2168,7 @@ } vht_mcs = iterator.this_arg[4] >> 4; - if (vht_mcs > 11) - vht_mcs = 0; vht_nss = iterator.this_arg[4] & 0xF; - if (!vht_nss || vht_nss > 8) - vht_nss = 1; break; /* @@ -2199,9 +2186,6 @@ return false; if (rate_found) { - struct ieee80211_supported_band *sband = - local->hw.wiphy->bands[info->band]; - info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { @@ -2215,7 +2199,7 @@ } else if (rate_flags & IEEE80211_TX_RC_VHT_MCS) { ieee80211_rate_set_vht(info->control.rates, vht_mcs, vht_nss); - } else if (sband) { + } else { for (i = 0; i < sband->n_bitrates; i++) { if (rate * 5 != sband->bitrates[i].bitrate) continue; @@ -2252,8 +2236,8 @@ info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_CTL_INJECTED; - /* Sanity-check the length of the radiotap header */ - if (!ieee80211_validate_radiotap_len(skb)) + /* Sanity-check and process the injection radiotap header */ + if (!ieee80211_parse_tx_radiotap(skb, dev)) goto fail; /* we now know there is a radiotap header with a length we can use */ @@ -2369,14 +2353,6 @@ info->band = chandef->chan->band; - /* - * Process the radiotap header. This will now take into account the - * selected chandef above to accurately set injection rates and - * retransmissions. - */ - if (!ieee80211_parse_tx_radiotap(skb, dev)) - goto fail_rcu; - /* remove the injection radiotap header */ skb_pull(skb, len_rthdr); @@ -3233,9 +3209,7 @@ if (info->control.flags & IEEE80211_TX_CTRL_AMSDU) return true; - if (!ieee80211_amsdu_realloc_pad(local, skb, - sizeof(*amsdu_hdr) + - local->hw.extra_tx_headroom)) + if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr))) return false; data = skb_push(skb, sizeof(*amsdu_hdr)); @@ -3369,14 +3343,6 @@ if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head)) goto out; - /* If n == 2, the "while (*frag_tail)" loop above didn't execute - * and frag_tail should be &skb_shinfo(head)->frag_list. - * However, ieee80211_amsdu_prepare_head() can reallocate it. - * Reload frag_tail to have it pointing to the correct place. - */ - if (n == 2) - frag_tail = &skb_shinfo(head)->frag_list; - /* * Pad out the previous subframe to a multiple of 4 by adding the * padding to the next one, that's being added. Note that head->len @@ -3639,7 +3605,7 @@ test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags)) goto out; - if (vif->txqs_stopped[txq->ac]) { + if (vif->txqs_stopped[ieee80211_ac_from_tid(txq->tid)]) { set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags); goto out; } @@ -3870,7 +3836,7 @@ * get immediately moved to the back of the list on the next * call to ieee80211_next_txq(). */ - if (txqi->txq.sta && local->airtime_flags && + if (txqi->txq.sta && wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) list_add(&txqi->schedule_order, @@ -4312,6 +4278,7 @@ struct ethhdr *ehdr = (struct ethhdr *)skb->data; struct ieee80211_key *key; struct sta_info *sta; + bool offload = true; if (unlikely(skb->len < ETH_HLEN)) { kfree_skb(skb); @@ -4327,22 +4294,18 @@ if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || - sdata->control_port_protocol == ehdr->h_proto)) - goto skip_offload; - - key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); - - if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || - key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) - goto skip_offload; + sdata->control_port_protocol == ehdr->h_proto)) + offload = false; + else if ((key = rcu_dereference(sta->ptk[sta->ptk_idx])) && + (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || + key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) + offload = false; - ieee80211_8023_xmit(sdata, dev, sta, key, skb); - goto out; + if (offload) + ieee80211_8023_xmit(sdata, dev, sta, key, skb); + else + ieee80211_subif_start_xmit(skb, dev); -skip_offload: - ieee80211_subif_start_xmit(skb, dev); out: rcu_read_unlock(); diff -Naur --no-dereference a/net/mac80211/util.c b/net/mac80211/util.c --- a/net/mac80211/util.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/util.c 2022-01-06 12:45:53.838318206 -0500 @@ -954,7 +954,7 @@ switch (elem->data[0]) { case WLAN_EID_EXT_HE_MU_EDCA: - if (len >= sizeof(*elems->mu_edca_param_set)) { + if (len == sizeof(*elems->mu_edca_param_set)) { elems->mu_edca_param_set = data; if (crc) *crc = crc32_be(*crc, (void *)elem, @@ -967,7 +967,7 @@ break; case WLAN_EID_EXT_HE_OPERATION: if (len >= sizeof(*elems->he_operation) && - len >= ieee80211_he_oper_size(data) - 1) { + len == ieee80211_he_oper_size(data) - 1) { if (crc) *crc = crc32_be(*crc, (void *)elem, elem->datalen + 2); @@ -975,7 +975,7 @@ } break; case WLAN_EID_EXT_UORA: - if (len >= 1) + if (len == 1) elems->uora_element = data; break; case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: @@ -983,7 +983,7 @@ elems->max_channel_switch_time = data; break; case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: - if (len >= sizeof(*elems->mbssid_config_ie)) + if (len == sizeof(*elems->mbssid_config_ie)) elems->mbssid_config_ie = data; break; case WLAN_EID_EXT_HE_SPR: @@ -992,7 +992,7 @@ elems->he_spr = data; break; case WLAN_EID_EXT_HE_6GHZ_CAPA: - if (len >= sizeof(*elems->he_6ghz_capa)) + if (len == sizeof(*elems->he_6ghz_capa)) elems->he_6ghz_capa = data; break; } @@ -1081,14 +1081,14 @@ switch (id) { case WLAN_EID_LINK_ID: - if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { + if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) { elem_parse_failed = true; break; } elems->lnk_id = (void *)(pos - 2); break; case WLAN_EID_CHAN_SWITCH_TIMING: - if (elen < sizeof(struct ieee80211_ch_switch_timing)) { + if (elen != sizeof(struct ieee80211_ch_switch_timing)) { elem_parse_failed = true; break; } @@ -1251,7 +1251,7 @@ elems->sec_chan_offs = (void *)pos; break; case WLAN_EID_CHAN_SWITCH_PARAM: - if (elen < + if (elen != sizeof(*elems->mesh_chansw_params_ie)) { elem_parse_failed = true; break; @@ -1260,7 +1260,7 @@ break; case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: if (!action || - elen < sizeof(*elems->wide_bw_chansw_ie)) { + elen != sizeof(*elems->wide_bw_chansw_ie)) { elem_parse_failed = true; break; } @@ -1279,7 +1279,7 @@ ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, pos, elen); if (ie) { - if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie)) + if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) elems->wide_bw_chansw_ie = (void *)(ie + 2); else @@ -1323,7 +1323,7 @@ elems->cisco_dtpc_elem = pos; break; case WLAN_EID_ADDBA_EXT: - if (elen < sizeof(struct ieee80211_addba_ext_ie)) { + if (elen != sizeof(struct ieee80211_addba_ext_ie)) { elem_parse_failed = true; break; } @@ -1349,7 +1349,7 @@ elem, elems); break; case WLAN_EID_S1G_CAPABILITIES: - if (elen >= sizeof(*elems->s1g_capab)) + if (elen == sizeof(*elems->s1g_capab)) elems->s1g_capab = (void *)pos; else elem_parse_failed = true; diff -Naur --no-dereference a/net/mac80211/wpa.c b/net/mac80211/wpa.c --- a/net/mac80211/wpa.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/mac80211/wpa.c 2022-01-06 12:45:53.838318206 -0500 @@ -3,7 +3,6 @@ * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2008, Jouni Malinen * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2020-2021 Intel Corporation */ #include @@ -168,8 +167,8 @@ update_iv: /* update IV in key information to be able to detect replays */ - rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip.iv32; - rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip.iv16; + rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; + rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16; return RX_CONTINUE; @@ -295,8 +294,8 @@ key, skb->data + hdrlen, skb->len - hdrlen, rx->sta->sta.addr, hdr->addr1, hwaccel, rx->security_idx, - &rx->tkip.iv32, - &rx->tkip.iv16); + &rx->tkip_iv32, + &rx->tkip_iv16); if (res != TKIP_DECRYPT_OK) return RX_DROP_UNUSABLE; @@ -520,9 +519,6 @@ return RX_DROP_UNUSABLE; } - /* reload hdr - skb might have been reallocated */ - hdr = (void *)rx->skb->data; - data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len; if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; @@ -557,8 +553,6 @@ } memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); - if (unlikely(ieee80211_is_frag(hdr))) - memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN); } /* Remove CCMP header and MIC */ @@ -752,9 +746,6 @@ return RX_DROP_UNUSABLE; } - /* reload hdr - skb might have been reallocated */ - hdr = (void *)rx->skb->data; - data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len; if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; @@ -790,8 +781,6 @@ } memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN); - if (unlikely(ieee80211_is_frag(hdr))) - memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN); } /* Remove GCMP header and MIC */ diff -Naur --no-dereference a/net/Makefile b/net/Makefile --- a/net/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/net/Makefile 2022-01-06 12:45:53.834318188 -0500 @@ -88,3 +88,4 @@ obj-$(CONFIG_NET_NCSI) += ncsi/ obj-$(CONFIG_XDP_SOCKETS) += xdp/ obj-$(CONFIG_MPTCP) += mptcp/ +obj-$(CONFIG_RPMSG_PROTO) += rpmsg/ diff -Naur --no-dereference a/net/packet/af_packet.c b/net/packet/af_packet.c --- a/net/packet/af_packet.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/packet/af_packet.c 2022-01-06 12:45:53.838318206 -0500 @@ -2986,6 +2986,8 @@ skb_setup_tx_timestamp(skb, sockc.tsflags); + sock_tx_redundant_info(sk, &sockc.redinfo, skb); + if (!vnet_hdr.gso_type && (len > dev->mtu + reserve + extra_len) && !packet_extra_vlan_len_allowed(dev, skb)) { err = -EMSGSIZE; @@ -3428,6 +3430,8 @@ sock_recv_ts_and_drops(msg, sk, skb); + sock_recv_redundant_info(msg, sk, skb); + if (msg->msg_name) { int copy_len; diff -Naur --no-dereference a/net/rpmsg/Kconfig b/net/rpmsg/Kconfig --- a/net/rpmsg/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ b/net/rpmsg/Kconfig 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# RPMsg Sockets +# + +config RPMSG_PROTO + tristate "RPMsg Sockets over virtio-rpmsg transport" + default n + depends on RPMSG_VIRTIO + depends on REMOTEPROC + help + An rpmsg driver that provides support for remote processor messaging + sockets over the virtio rpmsg transport. This exposes a socket + interface to user space to allow applications to communicate with + various remote processors over this transport. This is currently + designed to work with the TI IPC stack on various available TI SoCs, + but can be generalized easily enough. + + If unsure, say N. diff -Naur --no-dereference a/net/rpmsg/Makefile b/net/rpmsg/Makefile --- a/net/rpmsg/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ b/net/rpmsg/Makefile 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_RPMSG_PROTO) += rpmsg_proto.o diff -Naur --no-dereference a/net/rpmsg/rpmsg_proto.c b/net/rpmsg/rpmsg_proto.c --- a/net/rpmsg/rpmsg_proto.c 1969-12-31 19:00:00.000000000 -0500 +++ b/net/rpmsg/rpmsg_proto.c 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,657 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* AF_RPMSG: Remote processor messaging sockets + * + * Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/ + * + * Ohad Ben-Cohen + * Robert Tivy + * G Anthony + * Suman Anna + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_CB(skb) (*(struct sockaddr_rpmsg *)&((skb)->cb)) + +/* Maximum buffer size supported by virtio rpmsg transport. + * Must match value as in drivers/rpmsg/virtio_rpmsg_bus.c + */ +#define RPMSG_BUF_SIZE (512) + +struct rpmsg_socket { + struct sock sk; + struct rpmsg_device *rpdev; + struct rpmsg_endpoint *endpt; + int rproc_id; +}; + +/* Connection and socket states */ +enum { + RPMSG_CONNECTED = 1, + RPMSG_OPEN, + RPMSG_LISTENING, + RPMSG_CLOSED, +}; + +/* A single-level radix-tree-based scheme is used to maintain the rpmsg + * channels we're exposing to userland. The radix tree maps a rproc index + * id to its published rpmsg-proto channel. Only a single rpmsg device is + * supported at the moment from each remote processor. This can be easily + * scaled to multiple devices using unique destination addresses but this + *_will_ require additional semantic changes on bind() and connect(). + */ +static RADIX_TREE(rpmsg_channels, GFP_KERNEL); + +/* Synchronization of access to the tree is achieved using a mutex, + * because we're using non-atomic radix tree allocations. + */ +static DEFINE_MUTEX(rpmsg_channels_lock); + +static int rpmsg_sock_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src); + +static struct proto rpmsg_proto = { + .name = "RPMSG", + .owner = THIS_MODULE, + .obj_size = sizeof(struct rpmsg_socket), +}; + +/* Retrieve the rproc instance so that it can be used for retrieving + * the processor id associated with the rpmsg channel. + */ +static inline struct rproc *rpdev_to_rproc(struct rpmsg_device *rpdev) +{ + return rproc_get_by_child(&rpdev->dev); +} + +/* Retrieve the rproc id. The rproc id _relies_ on aliases being defined + * in the DT blob for each of the remoteproc devices, and is essentially + * the alias id. These are assumed to match to be fixed for a particular + * SoC, and this provides a means to have a fixed interface to identify + * a remote processor. + */ +static int rpmsg_sock_get_proc_id(struct rpmsg_device *rpdev) +{ + struct rproc *rproc = rpdev_to_rproc(rpdev); + int id; + + if (!rproc) { + WARN_ON(1); + return -EINVAL; + } + + id = rproc_get_id(rproc); + WARN_ON(id < 0); + + return id; +} + +static int rpmsg_sock_connect(struct socket *sock, struct sockaddr *addr, + int alen, int flags) +{ + struct sock *sk = sock->sk; + struct rpmsg_socket *rpsk; + struct sockaddr_rpmsg *sa; + int err = 0; + struct rpmsg_device *rpdev; + + if (sk->sk_state != RPMSG_OPEN) + return -EBADFD; + + if (sk->sk_type != SOCK_SEQPACKET) + return -EINVAL; + + if (!addr || addr->sa_family != AF_RPMSG) + return -EINVAL; + + if (alen < sizeof(*sa)) + return -EINVAL; + + sa = (struct sockaddr_rpmsg *)addr; + + lock_sock(sk); + + rpsk = container_of(sk, struct rpmsg_socket, sk); + + mutex_lock(&rpmsg_channels_lock); + + /* find the set of channels exposed by this remote processor */ + rpdev = radix_tree_lookup(&rpmsg_channels, sa->vproc_id); + if (!rpdev) { + err = -EINVAL; + goto out; + } + + rpsk->rproc_id = sa->vproc_id; + rpsk->rpdev = rpdev; + + /* XXX take care of disconnection state too */ + sk->sk_state = RPMSG_CONNECTED; + +out: + mutex_unlock(&rpmsg_channels_lock); + release_sock(sk); + return err; +} + +static int rpmsg_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) +{ + struct sock *sk = sock->sk; + struct rpmsg_socket *rpsk; + ssize_t max_payload; + char payload[RPMSG_BUF_SIZE];/* todo: sane payload length methodology */ + int err; + + /* XXX check for sock_error as well ? */ + /* XXX handle noblock ? */ + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + /* no payload ? */ + if (!msg->msg_iter.iov->iov_base) + return -EINVAL; + + lock_sock(sk); + + /* we don't support loopback at this point */ + if (sk->sk_state != RPMSG_CONNECTED) { + err = -ENOTCONN; + goto out; + } + + rpsk = container_of(sk, struct rpmsg_socket, sk); + + max_payload = rpmsg_get_mtu(rpsk->rpdev->ept); + if (max_payload < 0) { + err = -EINVAL; + goto out; + } + + /* make sure the length is valid for copying into kernel buffer */ + if (len > max_payload) { + err = -EMSGSIZE; + goto out; + } + + /* XXX for now, ignore the peer address. later use it + * with rpmsg_sendto, but only if user is root + */ + err = memcpy_from_msg(payload, msg, len); + if (err) + goto out; + + err = rpmsg_send(rpsk->rpdev->ept, payload, len); + if (err) + pr_err("rpmsg_send failed: %d\n", err); + else + err = len; + +out: + release_sock(sk); + return err; +} + +static int rpmsg_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_rpmsg *sa; + struct sk_buff *skb; + int noblock = flags & MSG_DONTWAIT; + int ret; + + if (flags & MSG_OOB) { + pr_err("MSG_OOB: %d\n", EOPNOTSUPP); + return -EOPNOTSUPP; + } + + msg->msg_namelen = 0; + + skb = skb_recv_datagram(sk, flags, noblock, &ret); + if (!skb) { + /* check for shutdown ? */ + pr_err("skb_recv_datagram: %d\n", ret); + return ret; + } + + if (msg->msg_name) { + msg->msg_namelen = sizeof(*sa); + sa = (struct sockaddr_rpmsg *)msg->msg_name; + sa->vproc_id = RPMSG_CB(skb).vproc_id; + sa->addr = RPMSG_CB(skb).addr; + sa->family = AF_RPMSG; + } + + if (len > skb->len) { + len = skb->len; + } else if (len < skb->len) { + pr_warn("user buffer is too small\n"); + /* XXX truncate or error ? */ + msg->msg_flags |= MSG_TRUNC; + } + + ret = skb_copy_datagram_msg(skb, 0, msg, len); + if (ret) { + pr_err("error copying skb data: %d\n", ret); + goto out_free; + } + + ret = len; + +out_free: + skb_free_datagram(sk, skb); + return ret; +} + +static __poll_t rpmsg_sock_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk = sock->sk; + __poll_t mask = 0; + + poll_wait(file, sk_sleep(sk), wait); + + /* exceptional events? */ + if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) + mask |= EPOLLERR; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= EPOLLRDHUP; + if (sk->sk_shutdown == SHUTDOWN_MASK) + mask |= EPOLLHUP; + + /* readable? */ + if (!skb_queue_empty(&sk->sk_receive_queue) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + mask |= EPOLLIN | EPOLLRDNORM; + + if (sk->sk_state == RPMSG_CLOSED) + mask |= EPOLLHUP; + + /* XXX is writable ? + * this depends on the destination processor. + * if loopback: we're writable unless no memory + * if to remote: we need enabled rpmsg buffer or user supplied bufs + * for now, let's always be writable. + */ + mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + + return mask; +} + +/* return bound socket address information, either local or remote */ +static int rpmsg_sock_getname(struct socket *sock, struct sockaddr *addr, + int peer) +{ + struct sock *sk = sock->sk; + struct rpmsg_socket *rpsk; + struct rpmsg_device *rpdev; + struct sockaddr_rpmsg *sa; + int ret; + + rpsk = container_of(sk, struct rpmsg_socket, sk); + + lock_sock(sk); + rpdev = rpsk->rpdev; + if (!rpdev) { + ret = peer ? -ENOTCONN : -EINVAL; + goto out; + } + + addr->sa_family = AF_RPMSG; + sa = (struct sockaddr_rpmsg *)addr; + ret = sizeof(*sa); + + if (peer) { + sa->vproc_id = rpsk->rproc_id; + sa->addr = rpdev->dst; + } else { + sa->vproc_id = RPMSG_LOCALHOST; + sa->addr = rpsk->endpt ? rpsk->endpt->addr : rpsk->rpdev->src; + } + +out: + release_sock(sk); + return ret; +} + +static int rpmsg_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk); + + if (!sk) + return 0; + + /* function can be called with NULL endpoints, so it is effective for + * Rx sockets and a no-op for Tx sockets + */ + rpmsg_destroy_ept(rpsk->endpt); + + sock_put(sock->sk); + + return 0; +} + +/* Notes: + * - calling connect after bind isn't currently supported (is it even needed?). + * - userspace arguments to bind aren't intuitive: one needs to provide + * the vproc id of the remote processor that the channel needs to be shared + * with, and the -local- source address the channel is to be bound with + */ +static int +rpmsg_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sock *sk = sock->sk; + struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk); + struct rpmsg_device *rpdev; + struct rpmsg_endpoint *endpt; + struct rpmsg_channel_info chinfo = {}; + struct sockaddr_rpmsg *sa = (struct sockaddr_rpmsg *)uaddr; + + if (sock->state == SS_CONNECTED) + return -EINVAL; + + if (addr_len != sizeof(*sa)) + return -EINVAL; + + if (sa->family != AF_RPMSG) + return -EINVAL; + + if (rpsk->endpt) + return -EBUSY; + + if (sk->sk_state != RPMSG_OPEN) + return -EINVAL; + + rpdev = radix_tree_lookup(&rpmsg_channels, sa->vproc_id); + if (!rpdev) + return -EINVAL; + + /* bind this socket with a receiving endpoint */ + chinfo.src = sa->addr; + chinfo.dst = RPMSG_ADDR_ANY; + endpt = rpmsg_create_ept(rpdev, rpmsg_sock_cb, sk, chinfo); + if (!endpt) + return -EINVAL; + + rpsk->rpdev = rpdev; + rpsk->endpt = endpt; + rpsk->rproc_id = sa->vproc_id; + + sk->sk_state = RPMSG_LISTENING; + + return 0; +} + +static const struct proto_ops rpmsg_sock_ops = { + .family = PF_RPMSG, + .owner = THIS_MODULE, + + .release = rpmsg_sock_release, + .connect = rpmsg_sock_connect, + .getname = rpmsg_sock_getname, + .sendmsg = rpmsg_sock_sendmsg, + .recvmsg = rpmsg_sock_recvmsg, + .poll = rpmsg_sock_poll, + .bind = rpmsg_sock_bind, + + .listen = sock_no_listen, + .accept = sock_no_accept, + .ioctl = sock_no_ioctl, + .mmap = sock_no_mmap, + .socketpair = sock_no_socketpair, + .shutdown = sock_no_shutdown, +}; + +static void rpmsg_sock_destruct(struct sock *sk) +{ +} + +static int rpmsg_sock_create(struct net *net, struct socket *sock, int proto, + int kern) +{ + struct sock *sk; + struct rpmsg_socket *rpsk; + + if (sock->type != SOCK_SEQPACKET) + return -ESOCKTNOSUPPORT; + if (proto != 0) + return -EPROTONOSUPPORT; + + sk = sk_alloc(net, PF_RPMSG, GFP_KERNEL, &rpmsg_proto, kern); + if (!sk) + return -ENOMEM; + + sock->state = SS_UNCONNECTED; + sock->ops = &rpmsg_sock_ops; + sock_init_data(sock, sk); + + sk->sk_destruct = rpmsg_sock_destruct; + sk->sk_protocol = proto; + + sk->sk_state = RPMSG_OPEN; + + rpsk = container_of(sk, struct rpmsg_socket, sk); + /* use RPMSG_LOCALHOST to serve as an invalid value */ + rpsk->rproc_id = RPMSG_LOCALHOST; + + return 0; +} + +static const struct net_proto_family rpmsg_proto_family = { + .family = PF_RPMSG, + .create = rpmsg_sock_create, + .owner = THIS_MODULE, +}; + +static int __rpmsg_sock_cb(struct device *dev, int from_vproc_id, void *data, + int len, struct sock *sk, u32 src) +{ + struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk); + struct sk_buff *skb; + int ret; + +#if defined(CONFIG_DYNAMIC_DEBUG) + dynamic_hex_dump("rpmsg_proto Rx: ", DUMP_PREFIX_NONE, 16, 1, data, + len, true); +#endif + + lock_sock(sk); + + switch (sk->sk_state) { + case RPMSG_CONNECTED: + if (rpsk->rpdev->dst != src) + dev_warn(dev, "unexpected source address: %d\n", src); + break; + case RPMSG_LISTENING: + /* When an inbound message is received while we're listening, + * we implicitly become connected + */ + sk->sk_state = RPMSG_CONNECTED; + rpsk->rpdev->dst = src; + break; + default: + dev_warn(dev, "unexpected inbound message (from %d)\n", src); + break; + } + + skb = sock_alloc_send_skb(sk, len, 1, &ret); + if (!skb) { + dev_err(dev, "sock_alloc_send_skb failed: %d\n", ret); + ret = -ENOMEM; + goto out; + } + + RPMSG_CB(skb).vproc_id = from_vproc_id; + RPMSG_CB(skb).addr = src; + RPMSG_CB(skb).family = AF_RPMSG; + + memcpy(skb_put(skb, len), data, len); + + ret = sock_queue_rcv_skb(sk, skb); + if (ret) { + dev_err(dev, "sock_queue_rcv_skb failed: %d\n", ret); + kfree_skb(skb); + } + +out: + release_sock(sk); + return ret; +} + +static int rpmsg_sock_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int id = rpmsg_sock_get_proc_id(rpdev); + + return __rpmsg_sock_cb(&rpdev->dev, id, data, len, priv, src); +} + +static int rpmsg_proto_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + dev_err(&rpdev->dev, "rpmsg_proto device not designed to receive any messages\n"); + return 0; +} + +static int rpmsg_proto_probe(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + int ret, dst = rpdev->dst, id; + struct rpmsg_device *vrp_dev; + + if (WARN_ON(dst == RPMSG_ADDR_ANY)) + return -EINVAL; + + id = rpmsg_sock_get_proc_id(rpdev); + if (id < 0) + return -EINVAL; + + mutex_lock(&rpmsg_channels_lock); + + /* are we exposing a rpmsg proto device for this remote processor yet? + * If not, associate id/device for later lookup in rpmsg_sock_bind(). + * Multiple devices per remote processor are not supported. + */ + vrp_dev = radix_tree_lookup(&rpmsg_channels, id); + if (!vrp_dev) { + ret = radix_tree_insert(&rpmsg_channels, id, rpdev); + if (ret) { + dev_err(dev, "radix_tree_insert failed: %d\n", ret); + goto out; + } + } else { + ret = -ENODEV; + dev_err(dev, "multiple rpmsg-proto devices from the same rproc is not supported.\n"); + goto out; + } + +out: + mutex_unlock(&rpmsg_channels_lock); + + return ret; +} + +static void rpmsg_proto_remove(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + int id, dst = rpdev->dst; + struct rpmsg_device *vrp_dev; + + if (dst == RPMSG_ADDR_ANY) + return; + + id = rpmsg_sock_get_proc_id(rpdev); + + mutex_lock(&rpmsg_channels_lock); + + vrp_dev = radix_tree_lookup(&rpmsg_channels, id); + if (!vrp_dev) { + dev_err(dev, "can't find rpmsg device for rproc %d\n", id); + goto out; + } + if (vrp_dev != rpdev) + dev_err(dev, "can't match the stored rpdev for rproc %d\n", id); + + if (!radix_tree_delete(&rpmsg_channels, id)) + dev_err(dev, "failed to delete rpdev for rproc %d\n", id); + +out: + mutex_unlock(&rpmsg_channels_lock); +} + +static struct rpmsg_device_id rpmsg_proto_id_table[] = { + { .name = "rpmsg-proto" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_proto_id_table); + +static struct rpmsg_driver rpmsg_proto_driver = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_proto_id_table, + .probe = rpmsg_proto_probe, + .callback = rpmsg_proto_cb, + .remove = rpmsg_proto_remove, +}; + +static int __init rpmsg_proto_init(void) +{ + int ret; + + ret = proto_register(&rpmsg_proto, 0); + if (ret) { + pr_err("proto_register failed: %d\n", ret); + return ret; + } + + ret = sock_register(&rpmsg_proto_family); + if (ret) { + pr_err("sock_register failed: %d\n", ret); + goto proto_unreg; + } + + ret = register_rpmsg_driver(&rpmsg_proto_driver); + if (ret) { + pr_err("register_rpmsg_driver failed: %d\n", ret); + goto sock_unreg; + } + + return 0; + +sock_unreg: + sock_unregister(PF_RPMSG); +proto_unreg: + proto_unregister(&rpmsg_proto); + return ret; +} + +static void __exit rpmsg_proto_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_proto_driver); + sock_unregister(PF_RPMSG); + proto_unregister(&rpmsg_proto); +} + +module_init(rpmsg_proto_init); +module_exit(rpmsg_proto_exit); + +MODULE_DESCRIPTION("Remote processor messaging protocol"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("rpmsg:rpmsg-proto"); +MODULE_ALIAS_NETPROTO(AF_RPMSG); diff -Naur --no-dereference a/net/socket.c b/net/socket.c --- a/net/socket.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/socket.c 2022-01-06 12:45:53.838318206 -0500 @@ -843,6 +843,34 @@ } EXPORT_SYMBOL_GPL(__sock_recv_timestamp); +void __sock_recv_redinfo_timestamp(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb) +{ + struct scm_timestamping_internal tss = { 0 }; + int empty = 1; + struct skb_shared_hwtstamps *red_shhwtstamps = + skb_redinfo_hwtstamps(skb); + + if (red_shhwtstamps && + (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) && + ktime_to_timespec64_cond(red_shhwtstamps->hwtstamp, tss.ts + 2)) + empty = 0; + + if (!empty) { + struct scm_timestamping tss1; + int i; + + for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { + tss1.ts[i].tv_sec = tss.ts[i].tv_sec; + tss1.ts[i].tv_nsec = tss.ts[i].tv_nsec; + } + + put_cmsg(msg, SOL_SOCKET, + SCM_RED_TIMESTAMPING, sizeof(tss1), &tss1); + } +} +EXPORT_SYMBOL_GPL(__sock_recv_redinfo_timestamp); + void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { diff -Naur --no-dereference a/net/wireless/core.c b/net/wireless/core.c --- a/net/wireless/core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/core.c 2022-01-06 12:45:53.838318206 -0500 @@ -501,7 +501,6 @@ INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); INIT_WORK(&rdev->mgmt_registrations_update_wk, cfg80211_mgmt_registrations_update_wk); - spin_lock_init(&rdev->mgmt_registrations_lock); #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -1257,6 +1256,7 @@ INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); + spin_lock_init(&wdev->mgmt_registrations_lock); INIT_LIST_HEAD(&wdev->pmsr_list); spin_lock_init(&wdev->pmsr_lock); INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); diff -Naur --no-dereference a/net/wireless/core.h b/net/wireless/core.h --- a/net/wireless/core.h 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/core.h 2022-01-06 12:45:53.838318206 -0500 @@ -101,8 +101,6 @@ struct work_struct propagate_cac_done_wk; struct work_struct mgmt_registrations_update_wk; - /* lock for all wdev lists */ - spinlock_t mgmt_registrations_lock; /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ diff -Naur --no-dereference a/net/wireless/mlme.c b/net/wireless/mlme.c --- a/net/wireless/mlme.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/mlme.c 2022-01-06 12:45:53.838318206 -0500 @@ -448,9 +448,9 @@ ASSERT_RTNL(); - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); if (!wdev->mgmt_registrations_need_update) { - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); return; } @@ -475,7 +475,7 @@ rcu_read_unlock(); wdev->mgmt_registrations_need_update = 0; - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); rdev_update_mgmt_frame_registrations(rdev, wdev, &upd); } @@ -499,7 +499,6 @@ int match_len, bool multicast_rx, struct netlink_ext_ack *extack) { - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_mgmt_registration *reg, *nreg; int err = 0; u16 mgmt_type; @@ -545,7 +544,7 @@ if (!nreg) return -ENOMEM; - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry(reg, &wdev->mgmt_registrations, list) { int mlen = min(match_len, reg->match_len); @@ -580,7 +579,7 @@ list_add(&nreg->list, &wdev->mgmt_registrations); } wdev->mgmt_registrations_need_update = 1; - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); cfg80211_mgmt_registrations_update(wdev); @@ -588,7 +587,7 @@ out: kfree(nreg); - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); return err; } @@ -599,7 +598,7 @@ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_mgmt_registration *reg, *tmp; - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { if (reg->nlportid != nlportid) @@ -612,7 +611,7 @@ schedule_work(&rdev->mgmt_registrations_update_wk); } - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); if (nlportid && rdev->crit_proto_nlportid == nlportid) { rdev->crit_proto_nlportid = 0; @@ -625,16 +624,15 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) { - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_mgmt_registration *reg, *tmp; - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { list_del(®->list); kfree(reg); } wdev->mgmt_registrations_need_update = 1; - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); cfg80211_mgmt_registrations_update(wdev); } @@ -782,7 +780,7 @@ data = buf + ieee80211_hdrlen(mgmt->frame_control); data_len = len - ieee80211_hdrlen(mgmt->frame_control); - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry(reg, &wdev->mgmt_registrations, list) { if (reg->frame_type != ftype) @@ -806,7 +804,7 @@ break; } - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); trace_cfg80211_return_bool(result); return result; diff -Naur --no-dereference a/net/wireless/nl80211.c b/net/wireless/nl80211.c --- a/net/wireless/nl80211.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/nl80211.c 2022-01-06 12:45:53.838318206 -0500 @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include @@ -209,13 +209,9 @@ unsigned int len = nla_len(attr); const struct element *elem; const struct ieee80211_mgmt *mgmt = (void *)data; + bool s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control); unsigned int fixedlen, hdrlen; - bool s1g_bcn; - if (len < offsetofend(typeof(*mgmt), frame_control)) - goto err; - - s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control); if (s1g_bcn) { fixedlen = offsetof(struct ieee80211_ext, u.s1g_beacon.variable); @@ -4625,10 +4621,11 @@ sband->ht_cap.mcs.rx_mask, sizeof(mask->control[i].ht_mcs)); - if (sband->vht_cap.vht_supported) { - vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); - vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); - } + if (!sband->vht_cap.vht_supported) + continue; + + vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype); if (!he_cap) @@ -5323,7 +5320,7 @@ rdev, info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], ¶ms); if (err) - goto out; + return err; } nl80211_calculate_ap_params(¶ms); @@ -16049,6 +16046,8 @@ (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) || nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, cr->timeout_reason))) || + (cr->authorized && + nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED)) || (cr->req_ie && nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || (cr->resp_ie && @@ -16115,7 +16114,9 @@ (info->fils.pmk && nla_put(msg, NL80211_ATTR_PMK, info->fils.pmk_len, info->fils.pmk)) || (info->fils.pmkid && - nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid))) + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid)) || + (info->authorized && + nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED))) goto nla_put_failure; genlmsg_end(msg, hdr); diff -Naur --no-dereference a/net/wireless/pmsr.c b/net/wireless/pmsr.c --- a/net/wireless/pmsr.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/pmsr.c 2022-01-06 12:45:53.838318206 -0500 @@ -324,7 +324,6 @@ gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct cfg80211_pmsr_request *tmp, *prev, *to_free = NULL; struct sk_buff *msg; void *hdr; @@ -355,20 +354,9 @@ nlmsg_free(msg); free_request: spin_lock_bh(&wdev->pmsr_lock); - /* - * cfg80211_pmsr_process_abort() may have already moved this request - * to the free list, and will free it later. In this case, don't free - * it here. - */ - list_for_each_entry_safe(tmp, prev, &wdev->pmsr_list, list) { - if (tmp == req) { - list_del(&req->list); - to_free = req; - break; - } - } + list_del(&req->list); spin_unlock_bh(&wdev->pmsr_lock); - kfree(to_free); + kfree(req); } EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete); diff -Naur --no-dereference a/net/wireless/scan.c b/net/wireless/scan.c --- a/net/wireless/scan.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/scan.c 2022-01-06 12:45:53.838318206 -0500 @@ -418,17 +418,14 @@ } ssid_len = ssid[1]; ssid = ssid + 2; + rcu_read_unlock(); /* check if nontrans_bss is in the list */ list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) { - if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len)) { - rcu_read_unlock(); + if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len)) return 0; - } } - rcu_read_unlock(); - /* add to the list */ list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list); return 0; @@ -1749,14 +1746,14 @@ * be grouped with this beacon for updates ... */ if (!cfg80211_combine_bsses(rdev, new)) { - bss_ref_put(rdev, new); + kfree(new); goto drop; } } if (rdev->bss_entries >= bss_entries_limit && !cfg80211_bss_expire_oldest(rdev)) { - bss_ref_put(rdev, new); + kfree(new); goto drop; } @@ -2354,16 +2351,14 @@ return NULL; if (ext) { - const struct ieee80211_s1g_bcn_compat_ie *compat; - const struct element *elem; + struct ieee80211_s1g_bcn_compat_ie *compat; + u8 *ie; - elem = cfg80211_find_elem(WLAN_EID_S1G_BCN_COMPAT, - variable, ielen); - if (!elem) - return NULL; - if (elem->datalen < sizeof(*compat)) + ie = (void *)cfg80211_find_ie(WLAN_EID_S1G_BCN_COMPAT, + variable, ielen); + if (!ie) return NULL; - compat = (void *)elem->data; + compat = (void *)(ie + 2); bssid = ext->u.s1g_beacon.sa; capability = le16_to_cpu(compat->compat_info); beacon_int = le16_to_cpu(compat->beacon_int); diff -Naur --no-dereference a/net/wireless/sme.c b/net/wireless/sme.c --- a/net/wireless/sme.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/sme.c 2022-01-06 12:45:53.838318206 -0500 @@ -530,7 +530,7 @@ cfg80211_sme_free(wdev); } - if (wdev->conn) + if (WARN_ON(wdev->conn)) return -EINPROGRESS; wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); @@ -888,6 +888,7 @@ ev->cr.bss = params->bss; ev->cr.status = params->status; ev->cr.timeout_reason = params->timeout_reason; + ev->cr.authorized = params->authorized; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); @@ -1022,6 +1023,7 @@ if (info->fils.update_erp_next_seq_num) ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; ev->rm.bss = info->bss; + ev->rm.authorized = info->authorized; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); diff -Naur --no-dereference a/net/wireless/util.c b/net/wireless/util.c --- a/net/wireless/util.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/util.c 2022-01-06 12:45:53.838318206 -0500 @@ -541,7 +541,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, const u8 *addr, enum nl80211_iftype iftype, - u8 data_offset, bool is_amsdu) + u8 data_offset) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct { @@ -629,7 +629,7 @@ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)); tmp.h_proto = payload.proto; - if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) && + if (likely((ether_addr_equal(payload.hdr, rfc1042_header) && tmp.h_proto != htons(ETH_P_AARP) && tmp.h_proto != htons(ETH_P_IPX)) || ether_addr_equal(payload.hdr, bridge_tunnel_header))) @@ -771,9 +771,6 @@ remaining = skb->len - offset; if (subframe_len > remaining) goto purge; - /* mitigate A-MSDU aggregation injection attacks */ - if (ether_addr_equal(eth.h_dest, rfc1042_header)) - goto purge; offset += sizeof(struct ethhdr); last = remaining <= subframe_len + padding; @@ -1028,14 +1025,14 @@ !(rdev->wiphy.interface_modes & (1 << ntype))) return -EOPNOTSUPP; - if (ntype != otype) { - /* if it's part of a bridge, reject changing type to station/ibss */ - if (netif_is_bridge_port(dev) && - (ntype == NL80211_IFTYPE_ADHOC || - ntype == NL80211_IFTYPE_STATION || - ntype == NL80211_IFTYPE_P2P_CLIENT)) - return -EBUSY; + /* if it's part of a bridge, reject changing type to station/ibss */ + if (netif_is_bridge_port(dev) && + (ntype == NL80211_IFTYPE_ADHOC || + ntype == NL80211_IFTYPE_STATION || + ntype == NL80211_IFTYPE_P2P_CLIENT)) + return -EBUSY; + if (ntype != otype) { dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; wdev_lock(dev->ieee80211_ptr); @@ -1044,7 +1041,6 @@ switch (otype) { case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: cfg80211_stop_ap(rdev, dev, true); break; case NL80211_IFTYPE_ADHOC: @@ -1060,9 +1056,6 @@ case NL80211_IFTYPE_MESH_POINT: /* mesh should be handled? */ break; - case NL80211_IFTYPE_OCB: - cfg80211_leave_ocb(rdev, dev); - break; default: break; } diff -Naur --no-dereference a/net/wireless/wext-core.c b/net/wireless/wext-core.c --- a/net/wireless/wext-core.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/wext-core.c 2022-01-06 12:45:53.838318206 -0500 @@ -896,9 +896,8 @@ int call_commit_handler(struct net_device *dev) { #ifdef CONFIG_WIRELESS_EXT - if (netif_running(dev) && - dev->wireless_handlers && - dev->wireless_handlers->standard[0]) + if ((netif_running(dev)) && + (dev->wireless_handlers->standard[0] != NULL)) /* Call the commit handler on the driver */ return dev->wireless_handlers->standard[0](dev, NULL, NULL, NULL); diff -Naur --no-dereference a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c --- a/net/wireless/wext-spy.c 2021-12-17 04:14:42.000000000 -0500 +++ b/net/wireless/wext-spy.c 2022-01-06 12:45:53.838318206 -0500 @@ -120,8 +120,8 @@ return -EOPNOTSUPP; /* Just do it */ - spydata->spy_thr_low = threshold->low; - spydata->spy_thr_high = threshold->high; + memcpy(&(spydata->spy_thr_low), &(threshold->low), + 2 * sizeof(struct iw_quality)); /* Clear flag */ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); @@ -147,8 +147,8 @@ return -EOPNOTSUPP; /* Just do it */ - threshold->low = spydata->spy_thr_low; - threshold->high = spydata->spy_thr_high; + memcpy(&(threshold->low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); return 0; } @@ -173,10 +173,10 @@ memcpy(threshold.addr.sa_data, address, ETH_ALEN); threshold.addr.sa_family = ARPHRD_ETHER; /* Copy stats */ - threshold.qual = *wstats; + memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); /* Copy also thresholds */ - threshold.low = spydata->spy_thr_low; - threshold.high = spydata->spy_thr_high; + memcpy(&(threshold.low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); diff -Naur --no-dereference a/README.md b/README.md --- a/README.md 1969-12-31 19:00:00.000000000 -0500 +++ b/README.md 2022-01-06 12:45:53.806318073 -0500 @@ -0,0 +1,6 @@ +| kernel | normal | rt | xenomai | +|:---:|:---:|:---:|:---:| +|4.14 | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.14/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.14/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.14-rt/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.14-rt/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.14-xenomai/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.14-xenomai/) | +|4.19 | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.19/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.19/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.19-rt/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.19-rt/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.19-xenomai/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/4.19-xenomai/) | +|5.4 | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.4/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.4/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.4-rt/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.4-rt/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.4-xenomai/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.4-xenomai/) | +|5.10 | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.10/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.10/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.10-rt/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.10-rt/) | [![Build Status](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.10-xenomai/badge/icon)](http://gfnd.rcn-ee.org:8080/view/BeagleBoard/job/beagleboard_kernel_builder/job/5.10-xenomai/) | diff -Naur --no-dereference a/samples/rpmsg/rpmsg_client_sample.c b/samples/rpmsg/rpmsg_client_sample.c --- a/samples/rpmsg/rpmsg_client_sample.c 2021-12-17 04:14:42.000000000 -0500 +++ b/samples/rpmsg/rpmsg_client_sample.c 2022-01-06 12:45:53.838318206 -0500 @@ -79,6 +79,7 @@ static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = { { .name = "rpmsg-client-sample" }, + { .name = "ti.ipc4.ping-pong" }, { }, }; MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table); diff -Naur --no-dereference a/scripts/dtb-merge b/scripts/dtb-merge --- a/scripts/dtb-merge 1969-12-31 19:00:00.000000000 -0500 +++ b/scripts/dtb-merge 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,53 @@ +#!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/ +# +# Author: Tero Kristo +# + +use strict; + +my $debug = 0; + +die "Usage: $ARGV[0] \n" + if @ARGV != 5; + +my $srctree = shift @ARGV; +my $objtree = shift @ARGV; +my $dtb_tgt = shift @ARGV; +my $fdtoverlay = shift @ARGV; +my $dir = shift @ARGV; + +open my $in, "<$srctree/$dir/dtb-merge.cfg" or die "Unable to open $srctree/$dir/dtb-merge.cfg"; + +my $tgt; +my @dtbs; + +# Generate target config string +if ($dtb_tgt =~ /\/([^\/]+)\.dtb$/) { + $tgt = $1; +} + +foreach (<$in>) { + if (/$tgt: (.*)$/) { + @dtbs = split " ", $1; + } +} + +close $in; + +die "No config found for $tgt\n" if !@dtbs; + +print "Found config for $tgt:\n" if $debug; + +# Generate the merged dtb file +my $cmd = "$fdtoverlay"; +$cmd .= " -v" if $debug; +$cmd .= " -o $dtb_tgt -i"; + +foreach my $dtb (@dtbs) { + $cmd .= " $objtree/$dir/$dtb"; +} + +exit system($cmd); diff -Naur --no-dereference a/scripts/dtc/data.c b/scripts/dtc/data.c --- a/scripts/dtc/data.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/data.c 2022-01-06 12:45:53.838318206 -0500 @@ -21,10 +21,10 @@ free(d.val); } -struct data data_grow_for(struct data d, int xlen) +struct data data_grow_for(struct data d, unsigned int xlen) { struct data nd; - int newsize; + unsigned int newsize; if (xlen == 0) return d; @@ -84,7 +84,7 @@ while (!feof(f) && (d.len < maxlen)) { size_t chunksize, ret; - if (maxlen == -1) + if (maxlen == (size_t)-1) chunksize = 4096; else chunksize = maxlen - d.len; diff -Naur --no-dereference a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c --- a/scripts/dtc/dtc.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/dtc.c 2022-01-06 12:45:53.838318206 -0500 @@ -122,6 +122,8 @@ return "dts"; if (!strcasecmp(s, ".yaml")) return "yaml"; + if (!strcasecmp(s, ".dtbo")) + return "dtb"; if (!strcasecmp(s, ".dtb")) return "dtb"; return fallback; @@ -357,6 +359,8 @@ #endif } else if (streq(outform, "dtb")) { dt_to_blob(outf, dti, outversion); + } else if (streq(outform, "dtbo")) { + dt_to_blob(outf, dti, outversion); } else if (streq(outform, "asm")) { dt_to_asm(outf, dti, outversion); } else if (streq(outform, "null")) { diff -Naur --no-dereference a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h --- a/scripts/dtc/dtc.h 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/dtc.h 2022-01-06 12:45:53.838318206 -0500 @@ -105,13 +105,13 @@ struct marker { enum markertype type; - int offset; + unsigned int offset; char *ref; struct marker *next; }; struct data { - int len; + unsigned int len; char *val; struct marker *markers; }; @@ -129,7 +129,7 @@ void data_free(struct data d); -struct data data_grow_for(struct data d, int xlen); +struct data data_grow_for(struct data d, unsigned int xlen); struct data data_copy_mem(const char *mem, int len); struct data data_copy_escape_string(const char *s, int len); @@ -253,7 +253,7 @@ const char *get_unitname(struct node *node); struct property *get_property(struct node *node, const char *propname); cell_t propval_cell(struct property *prop); -cell_t propval_cell_n(struct property *prop, int n); +cell_t propval_cell_n(struct property *prop, unsigned int n); struct property *get_property_by_label(struct node *tree, const char *label, struct node **node); struct marker *get_marker_label(struct node *tree, const char *label, diff -Naur --no-dereference a/scripts/dtc/fdtoverlay.c b/scripts/dtc/fdtoverlay.c --- a/scripts/dtc/fdtoverlay.c 1969-12-31 19:00:00.000000000 -0500 +++ b/scripts/dtc/fdtoverlay.c 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2017 Konsulko Group Inc. All rights reserved. + * + * Author: + * Pantelis Antoniou + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" + +#define BUF_INCREMENT 65536 + +/* Usage related data. */ +static const char usage_synopsis[] = + "apply a number of overlays to a base blob\n" + " fdtoverlay [ []]\n" + "\n" + USAGE_TYPE_MSG; +static const char usage_short_opts[] = "i:o:v" USAGE_COMMON_SHORT_OPTS; +static struct option const usage_long_opts[] = { + {"input", required_argument, NULL, 'i'}, + {"output", required_argument, NULL, 'o'}, + {"verbose", no_argument, NULL, 'v'}, + USAGE_COMMON_LONG_OPTS, +}; +static const char * const usage_opts_help[] = { + "Input base DT blob", + "Output DT blob", + "Verbose messages", + USAGE_COMMON_OPTS_HELP +}; + +int verbose = 0; + +static void *apply_one(char *base, const char *overlay, size_t *buf_len, + const char *name) +{ + char *tmp = NULL; + char *tmpo; + int ret; + + /* + * We take a copies first, because a a failed apply can trash + * both the base blob and the overlay + */ + tmpo = xmalloc(fdt_totalsize(overlay)); + + do { + tmp = xrealloc(tmp, *buf_len); + ret = fdt_open_into(base, tmp, *buf_len); + if (ret) { + fprintf(stderr, + "\nFailed to make temporary copy: %s\n", + fdt_strerror(ret)); + goto fail; + } + + memcpy(tmpo, overlay, fdt_totalsize(overlay)); + + ret = fdt_overlay_apply(tmp, tmpo); + if (ret == -FDT_ERR_NOSPACE) { + *buf_len += BUF_INCREMENT; + } + } while (ret == -FDT_ERR_NOSPACE); + + if (ret) { + fprintf(stderr, "\nFailed to apply '%s': %s\n", + name, fdt_strerror(ret)); + goto fail; + } + + free(base); + free(tmpo); + return tmp; + +fail: + free(tmpo); + if (tmp) + free(tmp); + + return NULL; +} +static int do_fdtoverlay(const char *input_filename, + const char *output_filename, + int argc, char *argv[]) +{ + char *blob = NULL; + char **ovblob = NULL; + size_t buf_len; + int i, ret = -1; + + blob = utilfdt_read(input_filename, &buf_len); + if (!blob) { + fprintf(stderr, "\nFailed to read '%s'\n", input_filename); + goto out_err; + } + if (fdt_totalsize(blob) > buf_len) { + fprintf(stderr, + "\nBase blob is incomplete (%lu / %" PRIu32 " bytes read)\n", + (unsigned long)buf_len, fdt_totalsize(blob)); + goto out_err; + } + + /* allocate blob pointer array */ + ovblob = xmalloc(sizeof(*ovblob) * argc); + memset(ovblob, 0, sizeof(*ovblob) * argc); + + /* read and keep track of the overlay blobs */ + for (i = 0; i < argc; i++) { + size_t ov_len; + ovblob[i] = utilfdt_read(argv[i], &ov_len); + if (!ovblob[i]) { + fprintf(stderr, "\nFailed to read '%s'\n", argv[i]); + goto out_err; + } + if (fdt_totalsize(ovblob[i]) > ov_len) { + fprintf(stderr, +"\nOverlay '%s' is incomplete (%lu / %" PRIu32 " bytes read)\n", + argv[i], (unsigned long)ov_len, + fdt_totalsize(ovblob[i])); + goto out_err; + } + } + + buf_len = fdt_totalsize(blob); + + /* apply the overlays in sequence */ + for (i = 0; i < argc; i++) { + blob = apply_one(blob, ovblob[i], &buf_len, argv[i]); + if (!blob) + goto out_err; + } + + fdt_pack(blob); + ret = utilfdt_write(output_filename, blob); + if (ret) + fprintf(stderr, "\nFailed to write '%s'\n", + output_filename); + +out_err: + if (ovblob) { + for (i = 0; i < argc; i++) { + if (ovblob[i]) + free(ovblob[i]); + } + free(ovblob); + } + free(blob); + + return ret; +} + +int main(int argc, char *argv[]) +{ + int opt, i; + char *input_filename = NULL; + char *output_filename = NULL; + + while ((opt = util_getopt_long()) != EOF) { + switch (opt) { + case_USAGE_COMMON_FLAGS + + case 'i': + input_filename = optarg; + break; + case 'o': + output_filename = optarg; + break; + case 'v': + verbose = 1; + break; + } + } + + if (!input_filename) + usage("missing input file"); + + if (!output_filename) + usage("missing output file"); + + argv += optind; + argc -= optind; + + if (argc <= 0) + usage("missing overlay file(s)"); + + if (verbose) { + printf("input = %s\n", input_filename); + printf("output = %s\n", output_filename); + for (i = 0; i < argc; i++) + printf("overlay[%d] = %s\n", i, argv[i]); + } + + if (do_fdtoverlay(input_filename, output_filename, argc, argv)) + return 1; + + return 0; +} diff -Naur --no-dereference a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c --- a/scripts/dtc/flattree.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/flattree.c 2022-01-06 12:45:53.838318206 -0500 @@ -149,7 +149,7 @@ static void asm_emit_data(void *e, struct data d) { FILE *f = e; - int off = 0; + unsigned int off = 0; struct marker *m = d.markers; for_each_marker_of_type(m, LABEL) @@ -219,7 +219,7 @@ static int stringtable_insert(struct data *d, const char *str) { - int i; + unsigned int i; /* FIXME: do this more efficiently? */ @@ -345,7 +345,7 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version) { struct version_info *vi = NULL; - int i; + unsigned int i; struct data blob = empty_data; struct data reservebuf = empty_data; struct data dtbuf = empty_data; @@ -446,7 +446,7 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version) { struct version_info *vi = NULL; - int i; + unsigned int i; struct data strbuf = empty_data; struct reserve_info *re; const char *symprefix = "dt"; diff -Naur --no-dereference a/scripts/dtc/.gitignore b/scripts/dtc/.gitignore --- a/scripts/dtc/.gitignore 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/.gitignore 2022-01-06 12:45:53.838318206 -0500 @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only dtc +fdtoverlay diff -Naur --no-dereference a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c --- a/scripts/dtc/libfdt/fdt.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/libfdt/fdt.c 2022-01-06 12:45:53.838318206 -0500 @@ -22,6 +22,10 @@ if (can_assume(VALID_DTB)) return totalsize; + /* The device tree must be at an 8-byte aligned address */ + if ((uintptr_t)fdt & 7) + return -FDT_ERR_ALIGNMENT; + if (fdt_magic(fdt) == FDT_MAGIC) { /* Complete tree */ if (!can_assume(LATEST)) { diff -Naur --no-dereference a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c --- a/scripts/dtc/libfdt/fdt_ro.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/libfdt/fdt_ro.c 2022-01-06 12:45:53.838318206 -0500 @@ -181,8 +181,8 @@ if (!can_assume(VALID_INPUT) && !re) return -FDT_ERR_BADOFFSET; - *address = fdt64_ld(&re->address); - *size = fdt64_ld(&re->size); + *address = fdt64_ld_(&re->address); + *size = fdt64_ld_(&re->size); return 0; } @@ -192,7 +192,7 @@ const struct fdt_reserve_entry *re; for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { - if (fdt64_ld(&re->size) == 0) + if (fdt64_ld_(&re->size) == 0) return i; } return -FDT_ERR_TRUNCATED; @@ -370,7 +370,7 @@ prop = fdt_offset_ptr_(fdt, offset); if (lenp) - *lenp = fdt32_ld(&prop->len); + *lenp = fdt32_ld_(&prop->len); return prop; } @@ -408,7 +408,7 @@ offset = -FDT_ERR_INTERNAL; break; } - if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), + if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff), name, namelen)) { if (poffset) *poffset = offset; @@ -461,7 +461,7 @@ /* Handle realignment */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && - (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) + (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) return prop->data + 4; return prop->data; } @@ -479,7 +479,7 @@ int namelen; if (!can_assume(VALID_INPUT)) { - name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), + name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff), &namelen); if (!name) { if (lenp) @@ -488,13 +488,13 @@ } *namep = name; } else { - *namep = fdt_string(fdt, fdt32_ld(&prop->nameoff)); + *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff)); } } /* Handle realignment */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && - (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) + (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) return prop->data + 4; return prop->data; } @@ -519,7 +519,7 @@ return 0; } - return fdt32_ld(php); + return fdt32_ld_(php); } const char *fdt_get_alias_namelen(const void *fdt, diff -Naur --no-dereference a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c --- a/scripts/dtc/libfdt/fdt_rw.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/libfdt/fdt_rw.c 2022-01-06 12:45:53.838318206 -0500 @@ -428,12 +428,14 @@ if (can_assume(LATEST) || fdt_version(fdt) >= 17) { struct_size = fdt_size_dt_struct(fdt); - } else { + } else if (fdt_version(fdt) == 16) { struct_size = 0; while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) ; if (struct_size < 0) return struct_size; + } else { + return -FDT_ERR_BADVERSION; } if (can_assume(LIBFDT_ORDER) || diff -Naur --no-dereference a/scripts/dtc/libfdt/fdt_sw.c b/scripts/dtc/libfdt/fdt_sw.c --- a/scripts/dtc/libfdt/fdt_sw.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/libfdt/fdt_sw.c 2022-01-06 12:45:53.838318206 -0500 @@ -377,7 +377,7 @@ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); /* And fix up fields that were keeping intermediate state. */ - fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION); fdt_set_magic(fdt, FDT_MAGIC); return 0; diff -Naur --no-dereference a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h --- a/scripts/dtc/libfdt/libfdt.h 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/libfdt/libfdt.h 2022-01-06 12:45:53.838318206 -0500 @@ -14,6 +14,7 @@ #endif #define FDT_FIRST_SUPPORTED_VERSION 0x02 +#define FDT_LAST_COMPATIBLE_VERSION 0x10 #define FDT_LAST_SUPPORTED_VERSION 0x11 /* Error codes: informative error codes */ @@ -101,7 +102,11 @@ /* FDT_ERR_BADFLAGS: The function was passed a flags field that * contains invalid flags or an invalid combination of flags. */ -#define FDT_ERR_MAX 18 +#define FDT_ERR_ALIGNMENT 19 + /* FDT_ERR_ALIGNMENT: The device tree base address is not 8-byte + * aligned. */ + +#define FDT_ERR_MAX 19 /* constants */ #define FDT_MAX_PHANDLE 0xfffffffe @@ -122,12 +127,10 @@ uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); /* - * Alignment helpers: - * These helpers access words from a device tree blob. They're - * built to work even with unaligned pointers on platforms (ike - * ARM) that don't like unaligned loads and stores + * External helpers to access words from a device tree blob. They're built + * to work even with unaligned pointers on platforms (such as ARMv5) that don't + * like unaligned loads and stores. */ - static inline uint32_t fdt32_ld(const fdt32_t *p) { const uint8_t *bp = (const uint8_t *)p; @@ -184,23 +187,23 @@ /** * fdt_first_subnode() - get offset of first direct subnode - * * @fdt: FDT blob * @offset: Offset of node to check - * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + * + * Return: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none */ int fdt_first_subnode(const void *fdt, int offset); /** * fdt_next_subnode() - get offset of next direct subnode + * @fdt: FDT blob + * @offset: Offset of previous subnode * * After first calling fdt_first_subnode(), call this function repeatedly to * get direct subnodes of a parent node. * - * @fdt: FDT blob - * @offset: Offset of previous subnode - * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more - * subnodes + * Return: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes */ int fdt_next_subnode(const void *fdt, int offset); @@ -225,7 +228,6 @@ * Note that this is implemented as a macro and @node is used as * iterator in the loop. The parent variable be constant or even a * literal. - * */ #define fdt_for_each_subnode(node, fdt, parent) \ for (node = fdt_first_subnode(fdt, parent); \ @@ -269,17 +271,21 @@ /** * fdt_header_size - return the size of the tree's header * @fdt: pointer to a flattened device tree + * + * Return: size of DTB header in bytes */ size_t fdt_header_size(const void *fdt); /** - * fdt_header_size_ - internal function which takes a version number + * fdt_header_size_ - internal function to get header size from a version number + * @version: devicetree version number + * + * Return: size of DTB header in bytes */ size_t fdt_header_size_(uint32_t version); /** * fdt_check_header - sanity check a device tree header - * @fdt: pointer to data which might be a flattened device tree * * fdt_check_header() checks that the given buffer contains what @@ -404,8 +410,7 @@ * highest phandle value in the device tree blob) will be returned in the * @phandle parameter. * - * Returns: - * 0 on success or a negative error-code on failure + * Return: 0 on success or a negative error-code on failure */ int fdt_generate_phandle(const void *fdt, uint32_t *phandle); @@ -425,9 +430,11 @@ /** * fdt_get_mem_rsv - retrieve one memory reserve map entry * @fdt: pointer to the device tree blob - * @address, @size: pointers to 64-bit variables + * @n: index of reserve map entry + * @address: pointer to 64-bit variable to hold the start address + * @size: pointer to 64-bit variable to hold the size of the entry * - * On success, *address and *size will contain the address and size of + * On success, @address and @size will contain the address and size of * the n-th reserve map entry from the device tree blob, in * native-endian format. * @@ -450,6 +457,8 @@ * namelen characters of name for matching the subnode name. This is * useful for finding subnodes based on a portion of a larger string, * such as a full path. + * + * Return: offset of the subnode or -FDT_ERR_NOTFOUND if name not found. */ #ifndef SWIG /* Not available in Python */ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, @@ -489,6 +498,8 @@ * * Identical to fdt_path_offset(), but only consider the first namelen * characters of path as the path name. + * + * Return: offset of the node or negative libfdt error value otherwise */ #ifndef SWIG /* Not available in Python */ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); @@ -588,9 +599,9 @@ /** * fdt_for_each_property_offset - iterate over all properties of a node * - * @property_offset: property offset (int, lvalue) - * @fdt: FDT blob (const void *) - * @node: node offset (int) + * @property: property offset (int, lvalue) + * @fdt: FDT blob (const void *) + * @node: node offset (int) * * This is actually a wrapper around a for loop and would be used like so: * @@ -653,6 +664,9 @@ * * Identical to fdt_get_property(), but only examine the first namelen * characters of name for matching the property name. + * + * Return: pointer to the structure representing the property, or NULL + * if not found */ #ifndef SWIG /* Not available in Python */ const struct fdt_property *fdt_get_property_namelen(const void *fdt, @@ -745,6 +759,8 @@ * * Identical to fdt_getprop(), but only examine the first namelen * characters of name for matching the property name. + * + * Return: pointer to the property's value or NULL on error */ #ifndef SWIG /* Not available in Python */ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, @@ -766,10 +782,10 @@ * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop() retrieves a pointer to the value of the property - * named 'name' of the node at offset nodeoffset (this will be a + * named @name of the node at offset @nodeoffset (this will be a * pointer to within the device blob itself, not a copy of the value). - * If lenp is non-NULL, the length of the property value is also - * returned, in the integer pointed to by lenp. + * If @lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by @lenp. * * returns: * pointer to the property's value @@ -814,8 +830,11 @@ * @name: name of the alias th look up * @namelen: number of characters of name to consider * - * Identical to fdt_get_alias(), but only examine the first namelen - * characters of name for matching the alias name. + * Identical to fdt_get_alias(), but only examine the first @namelen + * characters of @name for matching the alias name. + * + * Return: a pointer to the expansion of the alias named @name, if it exists, + * NULL otherwise */ #ifndef SWIG /* Not available in Python */ const char *fdt_get_alias_namelen(const void *fdt, @@ -828,7 +847,7 @@ * @name: name of the alias th look up * * fdt_get_alias() retrieves the value of a given alias. That is, the - * value of the property named 'name' in the node /aliases. + * value of the property named @name in the node /aliases. * * returns: * a pointer to the expansion of the alias named 'name', if it exists @@ -1004,14 +1023,13 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); /** - * fdt_node_check_compatible: check a node's compatible property + * fdt_node_check_compatible - check a node's compatible property * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @compatible: string to match against * - * * fdt_node_check_compatible() returns 0 if the given node contains a - * 'compatible' property with the given string as one of its elements, + * @compatible property with the given string as one of its elements, * it returns non-zero otherwise, or on error. * * returns: @@ -1075,7 +1093,7 @@ * one or more strings, each terminated by \0, as is found in a device tree * "compatible" property. * - * @return: 1 if the string is found in the list, 0 not found, or invalid list + * Return: 1 if the string is found in the list, 0 not found, or invalid list */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); @@ -1084,7 +1102,8 @@ * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list - * @return: + * + * Return: * the number of strings in the given property * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist @@ -1104,7 +1123,7 @@ * small-valued cell properties, such as #address-cells, when searching for * the empty string. * - * @return: + * return: * the index of the string in the list of strings * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist or does not contain @@ -1128,7 +1147,7 @@ * If non-NULL, the length of the string (on success) or a negative error-code * (on failure) will be stored in the integer pointer to by lenp. * - * @return: + * Return: * A pointer to the string at the given index in the string list or NULL on * failure. On success the length of the string will be stored in the memory * location pointed to by the lenp parameter, if non-NULL. On failure one of @@ -1217,6 +1236,8 @@ * starting from the given index, and using only the first characters * of the name. It is useful when you want to manipulate only one value of * an array and you have a string that doesn't end with \0. + * + * Return: 0 on success, negative libfdt error value otherwise */ #ifndef SWIG /* Not available in Python */ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, @@ -1330,8 +1351,13 @@ /** * fdt_setprop_inplace_cell - change the value of a single-cell property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node containing the property + * @name: name of the property to change the value of + * @val: new value of the 32-bit cell * * This is an alternative name for fdt_setprop_inplace_u32() + * Return: 0 on success, negative libfdt error number otherwise. */ static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) @@ -1403,7 +1429,7 @@ /** * fdt_create_with_flags - begin creation of a new fdt - * @fdt: pointer to memory allocated where fdt will be created + * @buf: pointer to memory allocated where fdt will be created * @bufsize: size of the memory space at fdt * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. * @@ -1421,7 +1447,7 @@ /** * fdt_create - begin creation of a new fdt - * @fdt: pointer to memory allocated where fdt will be created + * @buf: pointer to memory allocated where fdt will be created * @bufsize: size of the memory space at fdt * * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. @@ -1486,7 +1512,8 @@ /** * fdt_add_mem_rsv - add one memory reserve map entry * @fdt: pointer to the device tree blob - * @address, @size: 64-bit values (native endian) + * @address: 64-bit start address of the reserve map entry + * @size: 64-bit size of the reserved region * * Adds a reserve map entry to the given blob reserving a region at * address address of length size. @@ -1691,8 +1718,14 @@ /** * fdt_setprop_cell - set a property to a single cell value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) * * This is an alternative name for fdt_setprop_u32() + * + * Return: 0 on success, negative libfdt error value otherwise. */ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) @@ -1863,8 +1896,14 @@ /** * fdt_appendprop_cell - append a single cell value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) * * This is an alternative name for fdt_appendprop_u32() + * + * Return: 0 on success, negative libfdt error value otherwise. */ static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) @@ -1967,13 +2006,16 @@ * fdt_add_subnode_namelen - creates a new node based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node - * @name: name of the subnode to locate + * @name: name of the subnode to create * @namelen: number of characters of name to consider * - * Identical to fdt_add_subnode(), but use only the first namelen - * characters of name as the name of the new node. This is useful for + * Identical to fdt_add_subnode(), but use only the first @namelen + * characters of @name as the name of the new node. This is useful for * creating subnodes based on a portion of a larger string, such as a * full path. + * + * Return: structure block offset of the created subnode (>=0), + * negative libfdt error value otherwise */ #ifndef SWIG /* Not available in Python */ int fdt_add_subnode_namelen(void *fdt, int parentoffset, @@ -1992,7 +2034,7 @@ * * This function will insert data into the blob, and will therefore * change the offsets of some existing nodes. - + * * returns: * structure block offset of the created nodeequested subnode (>=0), on * success diff -Naur --no-dereference a/scripts/dtc/libfdt/libfdt_internal.h b/scripts/dtc/libfdt/libfdt_internal.h --- a/scripts/dtc/libfdt/libfdt_internal.h 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/libfdt/libfdt_internal.h 2022-01-06 12:45:53.838318206 -0500 @@ -46,6 +46,25 @@ return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); } +/* + * Internal helpers to access tructural elements of the device tree + * blob (rather than for exaple reading integers from within property + * values). We assume that we are either given a naturally aligned + * address for the platform or if we are not, we are on a platform + * where unaligned memory reads will be handled in a graceful manner. + * If not the external helpers fdtXX_ld() from libfdt.h can be used + * instead. + */ +static inline uint32_t fdt32_ld_(const fdt32_t *p) +{ + return fdt32_to_cpu(*p); +} + +static inline uint64_t fdt64_ld_(const fdt64_t *p) +{ + return fdt64_to_cpu(*p); +} + #define FDT_SW_MAGIC (~FDT_MAGIC) /**********************************************************************/ diff -Naur --no-dereference a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c --- a/scripts/dtc/livetree.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/livetree.c 2022-01-06 12:45:53.838318206 -0500 @@ -438,7 +438,7 @@ return fdt32_to_cpu(*((fdt32_t *)prop->val.val)); } -cell_t propval_cell_n(struct property *prop, int n) +cell_t propval_cell_n(struct property *prop, unsigned int n) { assert(prop->val.len / sizeof(cell_t) >= n); return fdt32_to_cpu(*((fdt32_t *)prop->val.val + n)); diff -Naur --no-dereference a/scripts/dtc/Makefile b/scripts/dtc/Makefile --- a/scripts/dtc/Makefile 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/Makefile 2022-01-06 12:45:53.838318206 -0500 @@ -1,13 +1,20 @@ # SPDX-License-Identifier: GPL-2.0 # scripts/dtc makefile -hostprogs-always-$(CONFIG_DTC) += dtc +# *** Also keep .gitignore in sync when changing *** +hostprogs-always-$(CONFIG_DTC) += dtc fdtoverlay hostprogs-always-$(CHECK_DT_BINDING) += dtc dtc-objs := dtc.o flattree.o fstree.o data.o livetree.o treesource.o \ srcpos.o checks.o util.o dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o +# The upstream project builds libfdt as a separate library. We are choosing to +# instead directly link the libfdt object files into fdtoverlay. +libfdt-objs := fdt.o fdt_ro.o fdt_wip.o fdt_sw.o fdt_rw.o fdt_strerror.o fdt_empty_tree.o fdt_addresses.o fdt_overlay.o +libfdt = $(addprefix libfdt/,$(libfdt-objs)) +fdtoverlay-objs := $(libfdt) fdtoverlay.o util.o + # Source files need to get at the userspace version of libfdt_env.h to compile HOST_EXTRACFLAGS += -I $(srctree)/$(src)/libfdt diff -Naur --no-dereference a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c --- a/scripts/dtc/srcpos.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/srcpos.c 2022-01-06 12:45:53.838318206 -0500 @@ -20,7 +20,7 @@ static struct search_path *search_path_head, **search_path_tail; /* Detect infinite include recursion. */ -#define MAX_SRCFILE_DEPTH (100) +#define MAX_SRCFILE_DEPTH (200) static int srcfile_depth; /* = 0 */ static char *get_dirname(const char *path) diff -Naur --no-dereference a/scripts/dtc/update-dtc-source.sh b/scripts/dtc/update-dtc-source.sh --- a/scripts/dtc/update-dtc-source.sh 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/update-dtc-source.sh 2022-01-06 12:45:53.838318206 -0500 @@ -37,6 +37,7 @@ LIBFDT_SOURCE="fdt.c fdt.h fdt_addresses.c fdt_empty_tree.c \ fdt_overlay.c fdt_ro.c fdt_rw.c fdt_strerror.c fdt_sw.c \ fdt_wip.c libfdt.h libfdt_env.h libfdt_internal.h" +FDTOVERLAY_SOURCE=fdtoverlay.c get_last_dtc_version() { git log --oneline scripts/dtc/ | grep 'upstream' | head -1 | sed -e 's/^.* \(.*\)/\1/' @@ -54,7 +55,7 @@ # Copy the files into the Linux tree cd $DTC_LINUX_PATH -for f in $DTC_SOURCE; do +for f in $DTC_SOURCE $FDTOVERLAY_SOURCE; do cp ${DTC_UPSTREAM_PATH}/${f} ${f} git add ${f} done diff -Naur --no-dereference a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h --- a/scripts/dtc/version_gen.h 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/version_gen.h 2022-01-06 12:45:53.838318206 -0500 @@ -1 +1 @@ -#define DTC_VERSION "DTC 1.6.0-gcbca977e" +#define DTC_VERSION "DTC 1.6.0-g183df9e9" diff -Naur --no-dereference a/scripts/dtc/yamltree.c b/scripts/dtc/yamltree.c --- a/scripts/dtc/yamltree.c 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/dtc/yamltree.c 2022-01-06 12:45:53.838318206 -0500 @@ -29,11 +29,11 @@ (emitter)->problem, __func__, __LINE__); \ }) -static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, int len, int width) +static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, unsigned int len, int width) { yaml_event_t event; void *tag; - int off, start_offset = markers->offset; + unsigned int off, start_offset = markers->offset; switch(width) { case 1: tag = "!u8"; break; @@ -112,7 +112,7 @@ static void yaml_propval(yaml_emitter_t *emitter, struct property *prop) { yaml_event_t event; - int len = prop->val.len; + unsigned int len = prop->val.len; struct marker *m = prop->val.markers; /* Emit the property name */ diff -Naur --no-dereference a/scripts/Makefile.dtbinst b/scripts/Makefile.dtbinst --- a/scripts/Makefile.dtbinst 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/Makefile.dtbinst 2022-01-06 12:45:53.838318206 -0500 @@ -18,9 +18,10 @@ include $(src)/Makefile dtbs := $(addprefix $(dst)/, $(dtb-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-))) +dtbos := $(addprefix $(dst)/, $(dtbo-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-))) subdirs := $(addprefix $(obj)/, $(subdir-y) $(subdir-m)) -__dtbs_install: $(dtbs) $(subdirs) +__dtbs_install: $(dtbs) $(dtbos) $(subdirs) @: quiet_cmd_dtb_install = INSTALL $@ @@ -29,6 +30,9 @@ $(dst)/%.dtb: $(obj)/%.dtb $(call cmd,dtb_install) +$(dst)/%.dtbo: $(obj)/%.dtbo + $(call cmd,dtb_install) + PHONY += $(subdirs) $(subdirs): $(Q)$(MAKE) $(dtbinst)=$@ dst=$(patsubst $(obj)/%,$(dst)/%,$@) diff -Naur --no-dereference a/scripts/Makefile.lib b/scripts/Makefile.lib --- a/scripts/Makefile.lib 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/Makefile.lib 2022-01-06 12:45:53.838318206 -0500 @@ -341,6 +341,24 @@ $(obj)/%.dt.yaml: $(src)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE $(call if_changed_rule,dtc,yaml) +quiet_cmd_dtco = DTCO $@ +cmd_dtco = mkdir -p $(dir ${dtc-tmp}) ; \ + $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \ + $(DTC) -@ -H epapr -O dtb -o $@ -b 0 \ + -i $(dir $<) $(DTC_FLAGS) \ + -Wno-interrupts_property \ + -Wno-label_is_string \ + -Wno-reg_format \ + -Wno-pci_device_bus_num \ + -Wno-i2c_bus_reg \ + -Wno-spi_bus_reg \ + -Wno-avoid_default_addr_size \ + -d $(depfile).dtc.tmp $(dtc-tmp) ; \ + cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) + +$(obj)/%.dtbo: $(src)/%.dts FORCE + $(call if_changed_dep,dtco) + dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) # Bzip2 diff -Naur --no-dereference a/scripts/package/builddeb b/scripts/package/builddeb --- a/scripts/package/builddeb 2021-12-17 04:14:42.000000000 -0500 +++ b/scripts/package/builddeb 2022-01-06 12:45:53.838318206 -0500 @@ -153,6 +153,7 @@ # Only some architectures with OF support have this target if [ -d "${srctree}/arch/$SRCARCH/boot/dts" ]; then $MAKE -f $srctree/Makefile INSTALL_DTBS_PATH="$tmpdir/usr/lib/$packagename" dtbs_install + $MAKE -f $srctree/Makefile INSTALL_DTBS_PATH="$tmpdir/boot/dtbs/$version" dtbs_install fi fi diff -Naur --no-dereference a/security/security.c b/security/security.c --- a/security/security.c 2021-12-17 04:14:42.000000000 -0500 +++ b/security/security.c 2022-01-06 12:45:53.838318206 -0500 @@ -1094,6 +1094,7 @@ return 0; return call_int_hook(path_rmdir, 0, dir, dentry); } +EXPORT_SYMBOL_GPL(security_path_rmdir); int security_path_unlink(const struct path *dir, struct dentry *dentry) { @@ -1110,6 +1111,7 @@ return 0; return call_int_hook(path_symlink, 0, dir, dentry, old_name); } +EXPORT_SYMBOL_GPL(security_path_symlink); int security_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) @@ -1118,6 +1120,7 @@ return 0; return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry); } +EXPORT_SYMBOL_GPL(security_path_link); int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry, @@ -1145,6 +1148,7 @@ return 0; return call_int_hook(path_truncate, 0, path); } +EXPORT_SYMBOL_GPL(security_path_truncate); int security_path_chmod(const struct path *path, umode_t mode) { @@ -1152,6 +1156,7 @@ return 0; return call_int_hook(path_chmod, 0, path, mode); } +EXPORT_SYMBOL_GPL(security_path_chmod); int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { @@ -1159,6 +1164,7 @@ return 0; return call_int_hook(path_chown, 0, path, uid, gid); } +EXPORT_SYMBOL_GPL(security_path_chown); int security_path_chroot(const struct path *path) { @@ -1259,6 +1265,7 @@ return 0; return call_int_hook(inode_permission, 0, inode, mask); } +EXPORT_SYMBOL_GPL(security_inode_permission); int security_inode_setattr(struct dentry *dentry, struct iattr *attr) { @@ -1451,6 +1458,7 @@ return fsnotify_perm(file, mask); } +EXPORT_SYMBOL_GPL(security_file_permission); int security_file_alloc(struct file *file) { diff -Naur --no-dereference a/security/selinux/hooks.c b/security/selinux/hooks.c --- a/security/selinux/hooks.c 2021-12-17 04:14:42.000000000 -0500 +++ b/security/selinux/hooks.c 2022-01-06 12:45:53.838318206 -0500 @@ -1271,7 +1271,9 @@ return SECCLASS_SMC_SOCKET; case PF_XDP: return SECCLASS_XDP_SOCKET; -#if PF_MAX > 45 + case PF_RPMSG: + return SECCLASS_RPMSG_SOCKET; +#if PF_MAX > 46 #error New address family defined, please update this function. #endif } diff -Naur --no-dereference a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h --- a/security/selinux/include/classmap.h 2021-12-17 04:14:42.000000000 -0500 +++ b/security/selinux/include/classmap.h 2022-01-06 12:45:53.838318206 -0500 @@ -237,6 +237,8 @@ { COMMON_SOCK_PERMS, NULL } }, { "smc_socket", { COMMON_SOCK_PERMS, NULL } }, + { "rpmsg_socket", + { COMMON_SOCK_PERMS, NULL } }, { "infiniband_pkey", { "access", NULL } }, { "infiniband_endport", @@ -253,6 +255,6 @@ { NULL } }; -#if PF_MAX > 45 +#if PF_MAX > 46 #error New address family defined, please update secclass_map. #endif diff -Naur --no-dereference a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig --- a/sound/soc/codecs/Kconfig 2021-12-17 04:14:42.000000000 -0500 +++ b/sound/soc/codecs/Kconfig 2022-01-06 12:45:53.838318206 -0500 @@ -1003,7 +1003,7 @@ select REGMAP_SPI config SND_SOC_PCM5102A - tristate + tristate "Texas Instruments PCM5102A CODEC" config SND_SOC_PCM512x tristate diff -Naur --no-dereference a/ti_config_fragments/am33xx_only.cfg b/ti_config_fragments/am33xx_only.cfg --- a/ti_config_fragments/am33xx_only.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/am33xx_only.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,19 @@ +################################################## +# TI AM33XX specific config options +################################################## + +# Disable Socs other than AM33xx +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_SOC_OMAP5=n +CONFIG_SOC_AM43XX=n +CONFIG_SOC_DRA7XX=n +CONFIG_ARCH_KEYSTONE=n + +#Disable CONFIG_SMP +CONFIG_SMP=n +CONFIG_CPUSETS=n + +# Increase CMA for HDMI +CONFIG_CMA_SIZE_MBYTES=48 diff -Naur --no-dereference a/ti_config_fragments/am43xx_only.cfg b/ti_config_fragments/am43xx_only.cfg --- a/ti_config_fragments/am43xx_only.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/am43xx_only.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,19 @@ +################################################## +# TI AM43XX specific config options +################################################## + +# Disable Socs other than AM43xx +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_SOC_OMAP5=n +CONFIG_SOC_AM33XX=n +CONFIG_SOC_DRA7XX=n +CONFIG_ARCH_KEYSTONE=n + +#Disable CONFIG_SMP +CONFIG_SMP=n +CONFIG_CPUSETS=n + +# Increase CMA for HDMI +CONFIG_CMA_SIZE_MBYTES=48 diff -Naur --no-dereference a/ti_config_fragments/arm64_prune.cfg b/ti_config_fragments/arm64_prune.cfg --- a/ti_config_fragments/arm64_prune.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/arm64_prune.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,82 @@ +CONFIG_ARCH_ACTIONS=n +CONFIG_ARCH_SUNXI=n +CONFIG_ARCH_AGILEX=n +CONFIG_ARCH_ALPINE=n +CONFIG_ARCH_BCM2835=n +CONFIG_ARCH_BCM_IPROC=n +CONFIG_ARCH_BERLIN=n +CONFIG_ARCH_BRCMSTB=n +CONFIG_ARCH_EXYNOS=n +CONFIG_ARCH_LAYERSCAPE=n +CONFIG_ARCH_LG1K=n +CONFIG_ARCH_HISI=n +CONFIG_ARCH_MEDIATEK=n +CONFIG_ARCH_MESON=n +CONFIG_ARCH_MVEBU=n +CONFIG_ARCH_MXC=n +CONFIG_ARCH_QCOM=n +CONFIG_ARCH_ROCKCHIP=n +CONFIG_ARCH_SEATTLE=n +CONFIG_ARCH_RENESAS=n +CONFIG_ARCH_R8A7795=n +CONFIG_ARCH_R8A7796=n +CONFIG_ARCH_S32=n +CONFIG_ARCH_STRATIX10=n +CONFIG_ARCH_SYNQUACER=n +CONFIG_ARCH_TEGRA=n +CONFIG_ARCH_SPRD=n +CONFIG_ARCH_THUNDER=n +CONFIG_ARCH_THUNDER2=n +CONFIG_ARCH_UNIPHIER=n +CONFIG_ARCH_VEXPRESS=n +CONFIG_ARCH_VISCONTI=n +CONFIG_ARCH_XGENE=n +CONFIG_ARCH_ZX=n +CONFIG_ARCH_ZYNQMP=n +CONFIG_ARCH_TEGRA_132_SOC=n +CONFIG_ARCH_TEGRA_210_SOC=n +CONFIG_ARCH_TEGRA_186_SOC=n + +CONFIG_QCOM_FALKOR_ERRATUM_1003=n +CONFIG_QCOM_FALKOR_ERRATUM_1009=n +CONFIG_QCOM_QDF2400_ERRATUM_0065=n +CONFIG_QCOM_FALKOR_ERRATUM_E1041=n + +CONFIG_CAVIUM_ERRATUM_22375=n +CONFIG_CAVIUM_ERRATUM_23144=n +CONFIG_CAVIUM_ERRATUM_23154=n +CONFIG_CAVIUM_ERRATUM_27456=n +CONFIG_CAVIUM_ERRATUM_30115=n + +CONFIG_HISILICON_ERRATUM_161600802=n + +CONFIG_SERIAL_8250_FSL=n +CONFIG_SERIAL_8250_DW=n +CONFIG_SERIAL_AMBA_PL011=n +CONFIG_SERIAL_AMBA_PL011_CONSOLE=n +CONFIG_SERIAL_XILINX_PS_UART=n +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=n + +CONFIG_ARM_SCPI_PROTOCOL=n +CONFIG_ARM_SCPI_POWER_DOMAIN=n +CONFIG_ARM_SCPI_CPUFREQ=n +CONFIG_SENSORS_ARM_SCPI=n +CONFIG_COMMON_CLK_SCPI=n + +CONFIG_ARM_MHU=n +CONFIG_PLATFORM_MHU=n + +CONFIG_ACPI=n + +CONFIG_NUMA=n + +CONFIG_XEN=n +CONFIG_KVM=n + +CONFIG_ARM_BIG_LITTLE_CPUFREQ=n + +CONFIG_PCI_XGENE=n +CONFIG_PCI_HISI=n +CONFIG_PCIE_KIRIN=n + +CONFIG_DEBUG_INFO=n diff -Naur --no-dereference a/ti_config_fragments/audio_display.cfg b/ti_config_fragments/audio_display.cfg --- a/ti_config_fragments/audio_display.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/audio_display.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,183 @@ +################################################## +# TI Audio/Display config options +################################################## + +CONFIG_CMA=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=24 + +# backlight + +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TLC591XX=y + +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y + +# fbdev + +CONFIG_FB_SSD1307=y +CONFIG_FRAMEBUFFER_CONSOLE=y + +# drm + +CONFIG_DRM=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_TI_TFP410=y +CONFIG_DRM_TI_TPD12S015=y +CONFIG_DRM_LVDS_CODEC=y +CONFIG_DRM_TOSHIBA_TC358767=y +CONFIG_DRM_TOSHIBA_TC358768=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS=y +# Firmware loading only works if built as module +CONFIG_DRM_CDNS_MHDP8546=m +CONFIG_DRM_CDNS_DSI=n + +CONFIG_PHY_J721E_WIZ=y +CONFIG_PHY_CADENCE_TORRENT=y + +# SGX driver needs legacy support +CONFIG_DRM_LEGACY=y +CONFIG_DRM_VM=y + +# tilcdc + +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_TILCDC=y + +# omapdrm + +CONFIG_DRM_OMAP=y +CONFIG_DRM_OMAP_WB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_DSS_DEBUGFS=y +CONFIG_OMAP2_DSS_DPI=y +CONFIG_OMAP2_DSS_VENC=n +CONFIG_OMAP4_DSS_HDMI=y +CONFIG_OMAP5_DSS_HDMI=y +CONFIG_OMAP2_DSS_SDI=n +CONFIG_OMAP2_DSS_DSI=n + +# tidss +CONFIG_DRM_TIDSS=y +CONFIG_DRM_TIDSS_WB=y +CONFIG_DRM_TIDSS_DSS6=y +CONFIG_DRM_TIDSS_DSS7=y + +# Disable unneeded features + +CONFIG_VGA_ARB=n +CONFIG_DRM_I2C_ADV7511=n +CONFIG_DRM_NOUVEAU=n +CONFIG_DRM_EXYNOS=n +CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=n +CONFIG_DRM_NXP_PTN3460=n +CONFIG_DRM_PARADE_PS8622=n +CONFIG_DRM_STI=n +CONFIG_DRM_PANEL_SAMSUNG_LD9040=n +CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=n +CONFIG_DRM_DUMB_VGA_DAC=n +CONFIG_LCD_CLASS_DEVICE=n +CONFIG_FB_EFI=n +CONFIG_FB_SIMPLE=n +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=n +CONFIG_DRM_HISI_KIRIN=n +CONFIG_FB_ARMCLCD=n +CONFIG_XEN_FBDEV_FRONTEND=n +CONFIG_DRM_RCAR_LVDS=n +CONFIG_DRM_FSL_DCU=n +CONFIG_DRM_SII9234=n +CONFIG_DRM_MXSFB=n +CONFIG_DRM_HISI_HIBMC=n +CONFIG_DRM_ATMEL_HLCDC=n +CONFIG_DRM_STM=n +CONFIG_DRM_PANEL_ORISETECH_OTM8009A=n +CONFIG_DRM_PANEL_RAYDIUM_RM68200=n +CONFIG_DRM_TOSHIBA_TC358764=n +CONFIG_DRM_ETNAVIV=n +CONFIG_DRM_PL111=n +CONFIG_DRM_LIMA=n +CONFIG_DRM_PANFROST=n + +# Touchscreen + +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_GOODIX=m + +CONFIG_HID_MULTITOUCH=m + +# V4L2 + +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y + +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_AM437X_VPFE=m +CONFIG_VIDEO_TI_CAL=m +CONFIG_VIDEO_TI_VIP=m + +CONFIG_VIDEO_CADENCE=y +CONFIG_VIDEO_CADENCE_CSI2RX=m +CONFIG_PHY_CADENCE_DPHY=m +CONFIG_VIDEO_TI_J721E_CSI2RX=m + +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_TI_VPE=m + +CONFIG_MEDIA_SUBDRV_AUTOSELECT=n +CONFIG_VIDEO_OV2659=m +CONFIG_VIDEO_OV1063X=m +CONFIG_VIDEO_MT9T11X=m +CONFIG_VIDEO_OV490=m +CONFIG_VIDEO_OV5640=m + +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y + +CONFIG_MEDIA_ANALOG_TV_SUPPORT=n +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=n +CONFIG_RC_CORE=n + +# sound - START +CONFIG_SND_ARM=n +CONFIG_SND_SPI=n +CONFIG_SND_ATMEL_SOC=n +CONFIG_SND_SOC_FSL_SAI=n +CONFIG_SND_SOC_AK4642=n +CONFIG_SND_SOC_CPCAP=n +CONFIG_SND_SOC_SGTL5000=n +CONFIG_SND_SOC_STI_SAS=n +CONFIG_SND_SOC_WM8978=n +CONFIG_SND_SOC_AK4613=n +CONFIG_SND_SOC_ES7134=n +CONFIG_SND_SOC_ES7241=n +CONFIG_SND_SOC_TAS571X=n + +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_SIMPLE_SCU_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +CONFIG_SND_AUDIO_GRAPH_SCU_CARD=m + +CONFIG_SND_SOC_DAVINCI_MCASP=y +CONFIG_SND_SOC_OMAP_HDMI=m +CONFIG_SND_SOC_J721E_EVM=m + +CONFIG_SND_SOC_TLV320AIC3X=m +CONFIG_SND_SOC_TLV320AIC31XX=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_HDMI_CODEC=m +# sound - END + +# display sharing +CONFIG_RPMSG_KDRV_DISPLAY=y diff -Naur --no-dereference a/ti_config_fragments/baseport.cfg b/ti_config_fragments/baseport.cfg --- a/ti_config_fragments/baseport.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/baseport.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,591 @@ +################################################## +# TI Baseport Config Options +################################################## + +# Kernel compression +CONFIG_KERNEL_GZIP=n +CONFIG_KERNEL_LZMA=y +CONFIG_KERNEL_LZ4=n +CONFIG_KERNEL_XZ=n +CONFIG_KERNEL_LZO=n + +# Enable process accounting +CONFIG_BSD_PROCESS_ACCT=y + +# Have some way to pick up kernel config later on +# Always useful to look at /proc/config.gz +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y + +# Add Bin2c +CONFIG_BUILD_BIN2C=y + +# Add base Cgroups functions +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=n +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_PAGE_COUNTER=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_SWAP_ENABLED=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +CONFIG_DEBUG_BLK_CGROUP=n +CONFIG_CGROUP_WRITEBACK=y + +# Choose CONFIG_EMBEDDED +CONFIG_NAMESPACES=n +CONFIG_EMBEDDED=y +CONFIG_EXPERT=y +CONFIG_ARM_PATCH_PHYS_VIRT=y + +# Enable all kernel symbols please +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y + +# Enable AEABI +CONFIG_AEABI=y + +# How do we want kernel Modules to work? +CONFIG_BASE_FULL=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_MODULE_SIG=n +CONFIG_MODULE_COMPRESS=n +CONFIG_MODULES_TREE_LOOKUP=y + +# Boot options +CONFIG_USE_OF=y +CONFIG_ATAGS=y +CONFIG_DEPRECATED_PARAM_STRUCT=n +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND=n +CONFIG_CMDLINE="" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +CONFIG_CMDLINE_EXTEND=n +CONFIG_CMDLINE_FORCE=n +CONFIG_ATAGS_PROC=y +CONFIG_CRASH_DUMP=n +CONFIG_AUTO_ZRELADDR=y + +# Mem allocator +CONFIG_SLAB=n +CONFIG_SLUB=y + +# Enable NEON +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y + +# POSIX Message queue +CONFIG_POSIX_MQUEUE=y + +# No Multi Cluster systems in TI yet.. +CONFIG_MCPM=n + +# Serial +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP=y +CONFIG_SERIAL_8250_NR_UARTS=10 +CONFIG_SERIAL_8250_RUNTIME_UARTS=10 +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_OMAP=n +CONFIG_SERIAL_8250_DW=n +CONFIG_SERIAL_8250_EM=n +CONFIG_SERIAL_AMBA_PL011=n +CONFIG_SERIAL_XILINX_PS_UART=n +CONFIG_SERIAL_FSL_LPUART=n +CONFIG_SERIAL_CONEXANT_DIGICOLOR=n +CONFIG_SERIAL_ST_ASC=n + +CONFIG_JUMP_LABEL=y + +# Disable Extra debug options +CONFIG_SCHEDSTATS=n +CONFIG_TIMER_STATS=n +CONFIG_DEBUG_SPINLOCK=n +CONFIG_DEBUG_MUTEXES=n +CONFIG_DEBUG_LOCK_ALLOC=n +CONFIG_PROVE_LOCKING=n +CONFIG_LOCKDEP=n +CONFIG_STACKTRACE=n +CONFIG_SCHED_DEBUG=n +CONFIG_FTRACE=n +CONFIG_ARM_UNWIND=n +CONFIG_DEBUG_PREEMPT=n +CONFIG_SLUB_DEBUG=n +CONFIG_LOCKUP_DETECTOR=n +CONFIG_DETECT_HUNG_TASK=n +CONFIG_IRQ_DOMAIN_DEBUG=n +CONFIG_DEBUG_BUGVERBOSE=n + +CONFIG_PREEMPT=y +CONFIG_DEBUG_FS=y + +# Enable System V IPC +CONFIG_SYSVIPC=y + +# Power management options +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_SUSPEND_SKIP_SYNC=n +CONFIG_HIBERNATION=n +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_AUTOSLEEP=n +CONFIG_PM_WAKELOCKS=n +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_APM_EMULATION=n +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=n +CONFIG_PM_GENERIC_DOMAINS_SLEEP=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_CPU_PM=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y + +# Clock framework stuff we need +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_SI5351=n +CONFIG_COMMON_CLK_SI514=n +CONFIG_COMMON_CLK_SI570=n +CONFIG_COMMON_CLK_CDCE925=n +CONFIG_CLK_TWL6040=n +CONFIG_CLK_QORIQ=n +CONFIG_COMMON_CLK_PALMAS=y +CONFIG_COMMON_CLK_PWM=n +CONFIG_COMMON_CLK_PXA=n +CONFIG_COMMON_CLK_CDCE706=n +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_PROBE=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLKSRC_TI_32K=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GLOBAL_TIMER=n +CONFIG_ARM_TIMER_SP804=n +CONFIG_TI_SYSCON_CLK=y +# Clock cleanup please +CONFIG_OMAP_RESET_CLOCKS=y + +# CPU Idle +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_DT_IDLE_STATES=y + +# ARM CPU Idle Drivers +CONFIG_ARM_CPUIDLE=y +CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED=n + +# CPU Frequency scaling +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y + +# CPUFreq Driver Options +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_BIG_LITTLE_CPUFREQ=n +CONFIG_ARM_KIRKWOOD_CPUFREQ=n +CONFIG_ARM_OMAP2PLUS_CPUFREQ=n +CONFIG_ARM_TI_CPUFREQ=y +CONFIG_QORIQ_CPUFREQ=n + +# AMx3 Power Config Options +CONFIG_MAILBOX=y +CONFIG_OMAP2PLUS_MBOX=y +CONFIG_WKUP_M3_RPROC=m +CONFIG_SOC_TI=y +CONFIG_SOC_BRCMSTB=n +CONFIG_WKUP_M3_IPC=m +CONFIG_AMX3_PM=m +CONFIG_SRAM=y +CONFIG_TI_EMIF_SRAM=m +CONFIG_SUNXI_SRAM=n + +CONFIG_PM_DEVFREQ=n + +# K2G Power config options +# CONFIG_MAILBOX is enabled in AMx3 +CONFIG_TI_MESSAGE_MANAGER=y +CONFIG_TI_SCI_PROTOCOL=y +CONFIG_TI_SCI_PM_DOMAINS=y +CONFIG_TI_SCI_CLK=y + +# Enable Reset Controllers +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_TI_SYSCON=y +CONFIG_RESET_TI_SCI=y + +# CPUFreq and OPP drivers +CONFIG_OPP_DOMAIN_TI=y + +# Thermal +CONFIG_THERMAL=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_TI_SOC_THERMAL=y +CONFIG_TI_THERMAL=y +CONFIG_OMAP4_THERMAL=y +CONFIG_OMAP5_THERMAL=y +CONFIG_DRA752_THERMAL=y + +# Since HWMON is needed by Sensors.. +CONFIG_HWMON=y +CONFIG_SENSORS_TMP102=y +CONFIG_SENSORS_GPIO_FAN=y + +# Enable the reset framework +CONFIG_POWER_RESET=y +CONFIG_POWER_SUPPLY=y + +# Pinctrl +CONFIG_PINCTRL_TI_IODELAY=y +CONFIG_PINCTRL_SINGLE=y + +# +# Multifunction device drivers +CONFIG_MFD_CORE=y +CONFIG_MFD_AS3711=n +CONFIG_MFD_AS3722=n +CONFIG_PMIC_ADP5520=n +CONFIG_MFD_AAT2870_CORE=n +CONFIG_MFD_ATMEL_FLEXCOM=n +CONFIG_MFD_ATMEL_HLCDC=n +CONFIG_MFD_BCM590XX=n +CONFIG_MFD_AXP20X=n +CONFIG_MFD_AXP20X_I2C=n +CONFIG_MFD_CROS_EC=n +CONFIG_MFD_CROS_EC_I2C=n +CONFIG_MFD_CROS_EC_SPI=n +CONFIG_MFD_ASIC3=n +CONFIG_PMIC_DA903X=n +CONFIG_MFD_DA9052_SPI=n +CONFIG_MFD_DA9052_I2C=n +CONFIG_MFD_DA9055=n +CONFIG_MFD_DA9062=n +CONFIG_MFD_DA9063=n +CONFIG_MFD_DA9150=n +CONFIG_MFD_DLN2=n +CONFIG_MFD_MC13XXX_SPI=n +CONFIG_MFD_MC13XXX_I2C=n +CONFIG_MFD_HI6421_PMIC=n +CONFIG_HTC_EGPIO=n +CONFIG_HTC_PASIC3=n +CONFIG_HTC_I2CPLD=n +CONFIG_LPC_ICH=n +CONFIG_LPC_SCH=n +CONFIG_INTEL_SOC_PMIC=n +CONFIG_MFD_JANZ_CMODIO=n +CONFIG_MFD_KEMPLD=n +CONFIG_MFD_88PM800=n +CONFIG_MFD_88PM805=n +CONFIG_MFD_88PM860X=n +CONFIG_MFD_MAX14577=n +CONFIG_MFD_MAX77686=n +CONFIG_MFD_MAX77693=n +CONFIG_MFD_MAX77843=n +CONFIG_MFD_MAX8907=n +CONFIG_MFD_MAX8925=n +CONFIG_MFD_MAX8997=n +CONFIG_MFD_MAX8998=n +CONFIG_MFD_MT6397=n +CONFIG_MFD_MENF21BMC=n +CONFIG_EZX_PCAP=n +CONFIG_MFD_VIPERBOARD=n +CONFIG_MFD_RETU=n +CONFIG_MFD_PCF50633=n +CONFIG_MFD_PM8XXX=n +CONFIG_MFD_PM8921_CORE=n +CONFIG_MFD_RDC321X=n +CONFIG_MFD_RTSX_PCI=n +CONFIG_MFD_RT5033=n +CONFIG_MFD_RTSX_USB=n +CONFIG_MFD_RC5T583=n +CONFIG_MFD_RK808=n +CONFIG_MFD_RN5T618=n +CONFIG_MFD_SEC_CORE=n +CONFIG_MFD_SI476X_CORE=n +CONFIG_MFD_SM501=n +CONFIG_MFD_SKY81452=n +CONFIG_MFD_SMSC=n +CONFIG_ABX500_CORE=n +CONFIG_AB3100_CORE=n +CONFIG_MFD_STMPE=n +CONFIG_STMPE_I2C=n +CONFIG_STMPE_SPI=n +CONFIG_MFD_SYSCON=y +CONFIG_MFD_TI_AM335X_TSCADC=m +CONFIG_MFD_LP3943=n +CONFIG_MFD_LP8788=n +CONFIG_MFD_OMAP_USB_HOST=y +CONFIG_MFD_PALMAS=y +CONFIG_TPS6105X=n +CONFIG_TPS65010=n +CONFIG_TPS6507X=n +CONFIG_MFD_TPS65086=n +CONFIG_MFD_TPS65090=y +CONFIG_MFD_TPS65217=y +CONFIG_MFD_TPS65218=y +CONFIG_MFD_TPS6586X=y +CONFIG_MFD_TPS65910=y +CONFIG_MFD_TPS65912_I2C=n +CONFIG_MFD_TPS65912_SPI=n +CONFIG_MFD_TPS80031=n +CONFIG_TWL4030_CORE=n +CONFIG_TWL4030_POWER=n +CONFIG_MFD_TWL4030_AUDIO=n +CONFIG_TWL6040_CORE=y +CONFIG_MFD_WL1273_CORE=n +CONFIG_MFD_LM3533=n +CONFIG_MFD_TI_LP873X=y +CONFIG_MFD_TI_LP87565=y +CONFIG_MFD_TC3589X=n +CONFIG_MFD_TMIO=n +CONFIG_MFD_T7L66XB=n +CONFIG_MFD_TC6387XB=n +CONFIG_MFD_TC6393XB=n +CONFIG_MFD_VX855=n +CONFIG_MFD_ARIZONA_I2C=n +CONFIG_MFD_ARIZONA_SPI=n +CONFIG_MFD_WM8400=n +CONFIG_MFD_WM831X_I2C=n +CONFIG_MFD_WM831X_SPI=n +CONFIG_MFD_WM8350_I2C=n +CONFIG_MFD_WM8994=n +CONFIG_MFD_VEXPRESS_SYSREG=n + +# Regulators +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_DEBUG=n +CONFIG_REGULATOR_VIRTUAL_CONSUMER=n +CONFIG_REGULATOR_USERSPACE_CONSUMER=n +CONFIG_REGULATOR_ACT8865=n +CONFIG_REGULATOR_AD5398=n +CONFIG_REGULATOR_ANATOP=n +CONFIG_REGULATOR_AS3711=n +CONFIG_REGULATOR_AS3722=n +CONFIG_REGULATOR_AXP20X=n +CONFIG_REGULATOR_BCM590XX=n +CONFIG_REGULATOR_DA9210=n +CONFIG_REGULATOR_DA9211=n +CONFIG_REGULATOR_FAN53555=n +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_ISL9305=n +CONFIG_REGULATOR_ISL6271A=n +CONFIG_REGULATOR_LP3971=n +CONFIG_REGULATOR_LP3972=n +CONFIG_REGULATOR_LP872X=n +CONFIG_REGULATOR_LP8755=n +CONFIG_REGULATOR_LP873X=y +CONFIG_REGULATOR_LP87565=y +CONFIG_REGULATOR_LTC3589=n +CONFIG_REGULATOR_MAX14577=n +CONFIG_REGULATOR_MAX1586=n +CONFIG_REGULATOR_MAX8649=n +CONFIG_REGULATOR_MAX8660=n +CONFIG_REGULATOR_MAX8907=n +CONFIG_REGULATOR_MAX8952=n +CONFIG_REGULATOR_MAX8973=n +CONFIG_REGULATOR_MAX77686=n +CONFIG_REGULATOR_MAX77693=n +CONFIG_REGULATOR_MAX77802=n +CONFIG_REGULATOR_MT6311=n +CONFIG_REGULATOR_PALMAS=y +CONFIG_REGULATOR_PBIAS=y +CONFIG_REGULATOR_PFUZE100=n +CONFIG_REGULATOR_PWM=n +CONFIG_REGULATOR_RK808=n +CONFIG_REGULATOR_S2MPA01=n +CONFIG_REGULATOR_S2MPS11=n +CONFIG_REGULATOR_S5M8767=n +CONFIG_REGULATOR_TI_ABB=y +CONFIG_REGULATOR_TPS51632=n +CONFIG_REGULATOR_TPS62360=y +CONFIG_REGULATOR_TPS65023=y +CONFIG_REGULATOR_TPS6507X=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_REGULATOR_TPS65217=y +CONFIG_REGULATOR_TPS65218=y +CONFIG_REGULATOR_TPS6524X=y +CONFIG_REGULATOR_TPS6586X=y +CONFIG_REGULATOR_TPS65910=y +CONFIG_REGULATOR_TWL4030=n +CONFIG_REGULATOR_VEXPRESS=n + +# Crypto Modules +CONFIG_CRYPTO_DEV_OMAP=m +CONFIG_CRYPTO_DEV_OMAP_SHAM=m +CONFIG_CRYPTO_DEV_OMAP_AES=m +CONFIG_CRYPTO_DEV_OMAP_DES=m +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_SHA256_ARM=m +CONFIG_CRYPTO_SHA512_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m + +# +# RTC drivers +# +CONFIG_RTC_DRV_ABB5ZES3=n +CONFIG_RTC_DRV_ABX80X=n +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_DS1374=n +CONFIG_RTC_DRV_DS1672=n +CONFIG_RTC_DRV_DS3232=n +CONFIG_RTC_DRV_HYM8563=n +CONFIG_RTC_DRV_MAX6900=n +CONFIG_RTC_DRV_RS5C372=n +CONFIG_RTC_DRV_ISL1208=n +CONFIG_RTC_DRV_ISL12022=n +CONFIG_RTC_DRV_ISL12057=n +CONFIG_RTC_DRV_X1205=n +CONFIG_RTC_DRV_PALMAS=m +CONFIG_RTC_DRV_PCF2127=n +CONFIG_RTC_DRV_PCF8523=n +CONFIG_RTC_DRV_PCF8563=n +CONFIG_RTC_DRV_PCF85063=n +CONFIG_RTC_DRV_PCF8583=n +CONFIG_RTC_DRV_M41T80=n +CONFIG_RTC_DRV_BQ32K=n +CONFIG_RTC_DRV_TPS6586X=m +CONFIG_RTC_DRV_TPS65910=m +CONFIG_RTC_DRV_TWL92330=y +CONFIG_RTC_DRV_TWL4030=n +CONFIG_RTC_DRV_S35390A=n +CONFIG_RTC_DRV_FM3130=n +CONFIG_RTC_DRV_RX8581=n +CONFIG_RTC_DRV_RX8025=n +CONFIG_RTC_DRV_EM3027=n +CONFIG_RTC_DRV_RV3029C2=n +CONFIG_RTC_DRV_RV8803=n + +CONFIG_RTC_DRV_M41T93=n +CONFIG_RTC_DRV_M41T94=n +CONFIG_RTC_DRV_DS1305=n +CONFIG_RTC_DRV_DS1343=n +CONFIG_RTC_DRV_DS1347=n +CONFIG_RTC_DRV_DS1390=n +CONFIG_RTC_DRV_MAX6902=n +CONFIG_RTC_DRV_R9701=n +CONFIG_RTC_DRV_RS5C348=n +CONFIG_RTC_DRV_DS3234=n +CONFIG_RTC_DRV_PCF2123=n +CONFIG_RTC_DRV_RX4581=n +CONFIG_RTC_DRV_MCP795=n + +CONFIG_RTC_DRV_CMOS=n +CONFIG_RTC_DRV_DS1286=n +CONFIG_RTC_DRV_DS1511=n +CONFIG_RTC_DRV_DS1553=n +CONFIG_RTC_DRV_DS1685_FAMILY=n +CONFIG_RTC_DRV_DS1742=n +CONFIG_RTC_DRV_DS2404=n +CONFIG_RTC_DRV_STK17TA8=n +CONFIG_RTC_DRV_M48T86=n +CONFIG_RTC_DRV_M48T35=n +CONFIG_RTC_DRV_M48T59=n +CONFIG_RTC_DRV_MSM6242=n +CONFIG_RTC_DRV_BQ4802=n +CONFIG_RTC_DRV_RP5C01=n +CONFIG_RTC_DRV_V3020=n +CONFIG_RTC_DRV_ZYNQMP=n + +CONFIG_RTC_DRV_OMAP=m +CONFIG_RTC_DRV_SNVS=n + +CONFIG_RTC_DRV_HID_SENSOR_TIME=n + +# WatchDog +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=n + +# Watchdog Device Drivers +CONFIG_SOFT_WATCHDOG=n +CONFIG_GPIO_WATCHDOG=n +CONFIG_XILINX_WATCHDOG=n +CONFIG_CADENCE_WATCHDOG=n +CONFIG_DW_WATCHDOG=n +CONFIG_OMAP_WATCHDOG=m +CONFIG_TWL4030_WATCHDOG=m +CONFIG_MAX63XX_WATCHDOG=n +CONFIG_ALIM7101_WDT=n +CONFIG_I6300ESB_WDT=n +CONFIG_BCM7038_WDT=n +CONFIG_MEN_A21_WDT=n + +# No Staging drivers please +CONFIG_STAGING=n + +# GPIO +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_DAVINCI=y + +# GPIO disable unused options +CONFIG_GPIO_DWAPB=n +CONFIG_GPIO_EM=n +CONFIG_GPIO_GENERIC_PLATFORM=n +CONFIG_GPIO_XILINX=n +CONFIG_GPIO_TPS6586X=n + +# Enable options to facilitate testing +CONFIG_CRYPTO_TEST=m +CONFIG_RTC_DEBUG=y +CONFIG_THERMAL_EMULATION=y + +# OPTEE Driver +CONFIG_TEE=y +CONFIG_OPTEE=y + +# DMA-BUF Heaps +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_CARVEOUT=y +CONFIG_SRAM_DMA_HEAP=y + +# DMA-BUF exporter +CONFIG_DMA_BUF_PHYS=y diff -Naur --no-dereference a/ti_config_fragments/connectivity.cfg b/ti_config_fragments/connectivity.cfg --- a/ti_config_fragments/connectivity.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/connectivity.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,514 @@ +################################################## +# TI Connectivity Configs +################################################## + +# Disable unused I2C options +CONFIG_I2C_MUX=n +CONFIG_I2C_DESIGNWARE_PLATFORM=n +CONFIG_I2C_GPIO=n +CONFIG_I2C_RK3X=n +CONFIG_I2C_XILINX=n + +# I2C controllers +CONFIG_I2C=y +CONFIG_I2C_OMAP=y + +# I2C GPIO expanders +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_PCF857X=y +CONFIG_GPIO_TPIC2810=m + +#I2C EEPROMS +CONFIG_EEPROM_AT24=m + +#SPI EEPROMS +CONFIG_EEPROM_93XX46=m + +# PTP +CONFIG_PTP_1588_CLOCK=y + +#Networking drivers +CONFIG_NET_VENDOR_TI=y +CONFIG_KEYSTONE_NAVIGATOR_QMSS=y +CONFIG_KEYSTONE_NAVIGATOR_DMA=y +CONFIG_TI_KEYSTONE_NETCP=y +CONFIG_TI_KEYSTONE_NETCP_ETHSS=y +CONFIG_TI_DAVINCI_EMAC=y +CONFIG_TI_DAVINCI_MDIO=y +CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y +CONFIG_TI_CPTS=y +CONFIG_TI_RDEV_ETH_SWITCH_VIRT_EMAC=m +CONFIG_TI_PRUETH=m +CONFIG_TI_ICSSG_PRUETH=m +CONFIG_TI_K3_AM65_CPSW_SWITCHDEV=y +# non-TI Net vendors +CONFIG_NET_DSA_BCM_SF2=n +CONFIG_B53=n +CONFIG_SYSTEMPORT=n +CONFIG_NET_VENDOR_3COM=n +CONFIG_NET_VENDOR_ADAPTEC=n +CONFIG_NET_VENDOR_AGERE=n +CONFIG_NET_VENDOR_ALTEON=n +CONFIG_NET_VENDOR_AMAZON=n +CONFIG_NET_VENDOR_AMD=n +CONFIG_NET_VENDOR_ARC=n +CONFIG_NET_VENDOR_ATHEROS=n +CONFIG_NET_VENDOR_BROCADE=n +CONFIG_NET_VENDOR_CAVIUM=n +CONFIG_NET_VENDOR_CHELSIO=n +CONFIG_BCMGENET=n +CONFIG_NET_VENDOR_CIRRUS=n +CONFIG_NET_VENDOR_CISCO=n +CONFIG_NET_VENDOR_DEC=n +CONFIG_NET_VENDOR_DLINK=n +CONFIG_NET_VENDOR_EMULEX=n +CONFIG_NET_VENDOR_EZCHIP=n +CONFIG_NET_VENDOR_FARADAY=n +CONFIG_NET_VENDOR_HISILICON=n +CONFIG_NET_VENDOR_HP=n +CONFIG_IGB=n +CONFIG_NET_VENDOR_I825XX=n +CONFIG_NET_VENDOR_MELLANOX=n +CONFIG_NET_VENDOR_MICROCHIP=n +CONFIG_NET_VENDOR_MYRI=n +CONFIG_NET_VENDOR_NATSEMI=n +CONFIG_NET_VENDOR_NETRONOME=n +CONFIG_NET_VENDOR_NVIDIA=n +CONFIG_NET_VENDOR_OKI=n +CONFIG_NET_VENDOR_8390=n +CONFIG_NET_VENDOR_QLOGIC=n +CONFIG_NET_VENDOR_QUALCOMM=n +CONFIG_NET_VENDOR_REALTEK=n +CONFIG_NET_VENDOR_RENESAS=n +CONFIG_NET_VENDOR_RDC=n +CONFIG_NET_VENDOR_ROCKER=n +CONFIG_NET_VENDOR_SAMSUNG=n +CONFIG_NET_VENDOR_SILAN=n +CONFIG_NET_VENDOR_SIS=n +CONFIG_NET_VENDOR_SEEQ=n +CONFIG_NET_VENDOR_STMICRO=n +CONFIG_NET_VENDOR_SUN=n +CONFIG_NET_VENDOR_SYNOPSYS=n +CONFIG_NET_VENDOR_TEHUTI=n +CONFIG_NET_VENDOR_VIA=n +CONFIG_NET_VENDOR_WIZNET=n +#Wireless LAN +CONFIG_WLCORE=m +CONFIG_WLCORE_SDIO=m +CONFIG_WL18XX=m +CONFIG_NL80211_TESTMODE=y +CONFIG_MAC80211_MESH=y + +#MDIO phys +CONFIG_MARVELL_PHY=y +CONFIG_MICREL_PHY=y +# unused PHY drivers +CONFIG_BROADCOM_PHY=n +CONFIG_ICPLUS_PHY=n +#PRU MII PHYs for Industrial Boards +CONFIG_DP83848_PHY=y +# Enable phy for DRA72 evm +CONFIG_DP83867_PHY=y +# Enable phy for AM64 evm +CONFIG_DP83869_PHY=y +# Control onboard MDIO muxes +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y + +#MTD +CONFIG_MTD=y +CONFIG_MEMORY=y +CONFIG_TI_AEMIF=y +CONFIG_OMAP_GPMC=y +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_OMAP_BCH=y +CONFIG_MTD_NAND_OMAP_BCH_BUILD=y +CONFIG_MTD_NAND_DAVINCI=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_UBI=y +CONFIG_MTD_HYPERBUS=y +CONFIG_HBMC_AM654=y + +#PCIE +CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_PCI_EPF_TEST=y +CONFIG_PCI_EPF_NTB=y +CONFIG_PCI_ENDPOINT_TEST=m +CONFIG_PCI_DRA7XX=y +CONFIG_PCI_DRA7XX_HOST=y +CONFIG_PCI_DRA7XX_EP=y +CONFIG_PCI_KEYSTONE=y +CONFIG_PCI_KEYSTONE_HOST=y +CONFIG_PCI_KEYSTONE_EP=y +CONFIG_PCI_J721E=y +CONFIG_PCI_J721E_HOST=y +CONFIG_PCI_J721E_EP=y +CONFIG_PCIE_CADENCE=y +CONFIG_PCIE_CADENCE_HOST=y +CONFIG_PCIE_CADENCE_EP=y + +#NTB +CONFIG_NTB=m +CONFIG_NTB_EPF=m +CONFIG_NTB_TRANSPORT=m +CONFIG_NTB_NETDEV=m + +#NVME +CONFIG_NVME_CORE=m +CONFIG_BLK_DEV_NVME=m + +#These drivers have been used with DRA7x/AM57x PCIe RC with some success +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_INTEL=y +CONFIG_TIGON3=m +CONFIG_SKGE=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_B43=m +#Generic Phys +CONFIG_PHY_AM654_SERDES=y +CONFIG_PHY_TI_KEYSTONE_SERDES=y +CONFIG_PHY_J721E_WIZ=y +CONFIG_PHY_CADENCE_SIERRA=y + +# Networking +CONFIG_HSR=m +CONFIG_NF_CONNTRACK=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_BRIDGE=m +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_INET_AH=m +CONFIG_INET6_AH=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_SCH_FIFO=y +CONFIG_IP_SCTP=m +CONFIG_VLAN_8021Q=m +CONFIG_IP_MULTICAST=y +CONFIG_NET_SWITCHDEV=y + +# Ethernet drivers +CONFIG_TI_K3_AM65_CPSW_NUSS=y +CONFIG_TI_AM65_CPSW_TAS=y +CONFIG_TI_K3_AM65_CPTS=y +#MMC/SD support +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_OMAP_HS=y +CONFIG_MMC_SDHCI_OMAP=y +CONFIG_MMC_DW=n +CONFIG_MMC_SDHCI_AM654=y + +#SPI +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_TI_QSPI=y +CONFIG_SPI_OMAP24XX=y +#Disable unused SPI controllers +CONFIG_SPI_BITBANG=n +CONFIG_SPI_CADENCE=n +CONFIG_SPI_ROCKCHIP=n +CONFIG_SPI_XILINX=n +CONFIG_SPI_SPIDEV=n + +#Disable SPI NOR 4K SECTORS +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=n + +#SPI GPIO expanders +CONFIG_GPIO_PISOSR=m + +#SATA +CONFIG_SATA_AHCI_PLATFORM=m +CONFIG_SATA_AHCI=m +CONFIG_ATA=m + +#USB PHY +CONFIG_OMAP_USB2=m + +#USB gadgets +CONFIG_USB_GADGET=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_WEBCAM=m +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=32 +CONFIG_USB_ZERO=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y + +# USB DWC3 +CONFIG_USB_DWC3=m +CONFIG_USB_DWC3_DUAL_ROLE=y +CONFIG_USB_DWC3_OMAP=m +CONFIG_USB_DWC3_KEYSTONE=m +CONFIG_USB_DWC3_PCI=n +CONFIG_USB_DWC2=n +CONFIG_USB_CHIPIDEA=n + +# USB CDNS3 +CONFIG_USB_CDNS3=m +CONFIG_USB_CDNS3_GADGET=y +CONFIG_USB_CDNS3_HOST=y +CONFIG_USB_CDNS3_TI=m + +#USB PHY +CONFIG_NOP_USB_XCEIV=m + +#USB MUSB +CONFIG_USB_MUSB_HDRC=m +CONFIG_USB_MUSB_DUAL_ROLE=y +CONFIG_USB_MUSB_OMAP2PLUS=m +CONFIG_USB_MUSB_AM35X=m +CONFIG_USB_MUSB_DSPS=m +CONFIG_USB_MUSB_AM335X_CHILD=m +CONFIG_TI_CPPI41=y +CONFIG_USB_TI_CPPI41_DMA=y +CONFIG_AM335X_CONTROL_USB=y +CONFIG_AM335X_PHY_USB=y + +#USB EHCI +CONFIG_USB=m +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_PCI=m +CONFIG_USB_EHCI_HCD_OMAP=m + +#USB Networking +CONFIG_USB_NET_DRIVERS=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_CDC_SUBSET_ENABLE=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_NET_ZAURUS=m + +#USB testing +CONFIG_USB_TEST=m +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +#USB Serial +CONFIG_USB_ACM=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OPTION=m + +# Extcon drivers +CONFIG_EXTCON=m +CONFIG_EXTCON_PALMAS=m +CONFIG_EXTCON_USB_GPIO=m + +# PWM +CONFIG_PWM_TIECAP=y +CONFIG_PWM_TIEHRPWM=y +CONFIG_PWM_TIPWMSS=y +CONFIG_PWM_OMAP_DMTIMER=y + +# 1-wire Bus Masters +CONFIG_W1=m +CONFIG_HDQ_MASTER_OMAP=m + +# Matrix keypad +CONFIG_KEYBOARD_MATRIX=m + +#Touchscreen/ADC +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_MFD_TI_AM335X_TSCADC=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=m +CONFIG_TI_AM335X_ADC=m + +#CAN +CONFIG_CAN=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_M_CAN=m +CONFIG_CAN_M_CAN_PLATFORM=m + +# CAN Transceiver +CONFIG_PHY_CAN_TRANSCEIVER=m + +# Rotary Encoder +CONFIG_INPUT_GPIO_DECODER=m + +# Filesystem extra options +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_UBIFS_FS=y + +# HD-Audio +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m + +#UFS +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFS_BSG=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_CDNS_PLATFORM=y +CONFIG_SCSI_UFS_TI_J721E=y + +#PRUSS-UART +CONFIG_SERIAL_8250_PRUSS=m + +# MUX Drivers +CONFIG_MUX_GPIO=y + +# PRU Soft UART driver +CONFIG_SERIAL_PRU_SWUART=m diff -Naur --no-dereference a/ti_config_fragments/debug_ftrace.cfg b/ti_config_fragments/debug_ftrace.cfg --- a/ti_config_fragments/debug_ftrace.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/debug_ftrace.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,14 @@ +################################################## +# Debug FTRACE options +################################################## + +CONFIG_FTRACE=y +CONFIG_DYNAMIC_FTRACE=y + +CONFIG_PREEMPT_TRACER=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_PREEMPT_TRACER=y +CONFIG_SCHED_TRACER=y +CONFIG_STACK_TRACER=y diff -Naur --no-dereference a/ti_config_fragments/debug_options.cfg b/ti_config_fragments/debug_options.cfg --- a/ti_config_fragments/debug_options.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/debug_options.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,97 @@ +################################################## +# Common Debug options considered generically +# useful for kernel debug. This is not meant to +# be an exhaustive list of options and will probably +# have performance impact by using the same. +# +# IMPORTANT NOTE: Do not collect reference performance +# data with this configuration. +################################################## + +CONFIG_DYNAMIC_DEBUG=y + +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_WANT_PAGE_DEBUG_FLAGS=y +CONFIG_PAGE_POISONING=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_SELFTEST=n +CONFIG_DEBUG_OBJECTS_FREE=n +CONFIG_DEBUG_OBJECTS_TIMERS=n +CONFIG_DEBUG_OBJECTS_WORK=n +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=n +CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1 +CONFIG_DEBUG_SLAB=y +CONFIG_DEBUG_SLAB_LEAK=y +CONFIG_SLUB_DEBUG=y + +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=8000 +CONFIG_DEBUG_KMEMLEAK_TEST=n +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=n +CONFIG_DEBUG_INFO=y + +CONFIG_DEBUG_SHIRQ=n +CONFIG_DEBUG_PREEMPT=y + +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=300 +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1 +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 + +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_PI_LIST=y +CONFIG_RT_MUTEX_TESTER=n +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y +CONFIG_LOCKDEP=y +CONFIG_LOCK_STAT=n +CONFIG_DEBUG_LOCKDEP=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_LOCKING_API_SELFTESTS=n +CONFIG_STACKTRACE=y +CONFIG_DEBUG_KOBJECT=n +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_WRITECOUNT=n +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_SG=y +CONFIG_DEBUG_NOTIFIERS=y +CONFIG_DEBUG_CREDENTIALS=n + +CONFIG_SPARSE_RCU_POINTER=y +CONFIG_RCU_TORTURE_TEST=n +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_INFO=y +CONFIG_RCU_TRACE=y +CONFIG_PROVE_RCU=y +CONFIG_PROVE_RCU_REPEATEDLY=y + +CONFIG_DMA_API_DEBUG=y + +CONFIG_ARM_PTDUMP=y +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y + +# Cgroups debug +CONFIG_CGROUP_DEBUG=y +CONFIG_DEBUG_BLK_CGROUP=y + +# IRQ Domain debug +CONFIG_IRQ_DOMAIN_DEBUG=y + +# Debug HighMem +CONFIG_DEBUG_HIGHMEM=y + +# Crypto self-tests +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=n + +# Enable Debug configuration for EFI if enabled +CONFIG_DEBUG_EFI=y diff -Naur --no-dereference a/ti_config_fragments/defconfig_builder.sh b/ti_config_fragments/defconfig_builder.sh --- a/ti_config_fragments/defconfig_builder.sh 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/defconfig_builder.sh 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,510 @@ +#!/bin/bash +# +# defconfig_builder.sh +# +# Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ +# ALL RIGHTS RESERVED +# +# This script will perform a merge of config fragment files into a defconfig +# based on a map file. The map file defines the defconfig options that have +# been tested by TI to boot and compile. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For more information type defconfig_builder.sh -? +# +# IMPORTANT NOTE: All modifications to this script must be verified by running +# 'shellcheck --shell=bash defconfig_builder.sh' See 'Installing' section in: +# https://github.com/koalaman/shellcheck/blob/master/README.md +# Also note that shellcheck must be at least at revision 0.3.3 + +DEBUG_CONFIG_TAG="debug_options" +CONFIG_FRAGMENT_TAG="config-fragment=" +DISCLAIMER="\n*Please be advised that the Debug Option defconfigs may\nimpact \ +performance and should only be used for debugging.\n" + +# Template for temporary build files.. use PID to differentiate +TMP_PREFIX=ti_defconfig_builder_$$ +TMP_TEMPLATE="$TMP_PREFIX"_XXXXX.tmp + +set_working_directory() { + SCRIPT_PATH=$(readlink -f $(dirname "${BASH_SOURCE[0]}")) + + if [ ! -v KERNEL_PATH ]; then + if [ -f $(pwd)/MAINTAINERS ]; then + KERNEL_PATH=$(pwd) + else + KERNEL_PATH=$SCRIPT_PATH/../../linux/ + fi + fi + + KERNEL_PATH=$(readlink -f $KERNEL_PATH) + + if [ ! -f $KERNEL_PATH/MAINTAINERS ]; then + echo "Error: $KERNEL_PATH does not look like a kernel directory" + exit 1 + fi +} + +prepare_for_exit() { + D=$(dirname "$PROCESSOR_FILE") + rm -f "$PROCESSOR_FILE" + rm -f "$BUILD_TYPE_FILE" + rm -f "$TEMP_TYPE_FILE" + if [ -s "$OLD_CONFIG" ]; then + mv "$OLD_CONFIG" "$KERNEL_PATH"/.config + fi + # Clean everyone else up if we missed any + rm -f "$D"/"$TMP_PREFIX"*.tmp + exit +} + +check_for_config_existance() { + # Check to make sure that the config fragments exist + TEMP_EXTRA_CONFIG_FILE=$(echo "$BUILD_DETAILS" | cut -d: -f6) + if [ -z "$TEMP_EXTRA_CONFIG_FILE" ]; then + CONFIG_FRAGMENTS= + else + for CONFIG_FRAGMENT_FILE in $TEMP_EXTRA_CONFIG_FILE; + do + # If we do already point to existing file, we are good. + if [ -e "$CONFIG_FRAGMENT_FILE" ]; then + CONFIG_FRAG="$CONFIG_FRAGMENT_FILE" + else + # Assume it is present in TI working path + CONFIG_FRAG="$SCRIPT_PATH/$CONFIG_FRAGMENT_FILE" + fi + if [ ! -e "$CONFIG_FRAG" ]; then + CONFIG_FRAGMENTS="N/A" + fi + done + fi + + if ! grep -qc "$BT_TEMP" "$BUILD_TYPE_FILE"; then + # If the config file and config fragments are available + # add it to the list. + CONFIG_FILE=$(echo "$BUILD_DETAILS" | awk '{print$8}') + if [ "$CONFIG_FILE" = "None" ]; then + CONFIG_FILE= + else + if [ -e "$SCRIPT_PATH""/""$CONFIG_FILE" ]; then + CONFIG_FILE= + fi + fi + # If the check for the config file and the config fragments + # pass then these two variables should be empty. If + # they fail then they should be N/A. + if [ -z "$CONFIG_FILE" -a -z "$CONFIG_FRAGMENTS" ]; then + max_configs=$((max_configs+1)) + echo -e '\t'"$max_configs". "$BT_TEMP" >> "$BUILD_TYPE_FILE" + fi + fi +} + +choose_build_type() { + TEMP_BT_FILE=$(mktemp -t $TMP_TEMPLATE) + TEMP_BUILD_FILE=$(mktemp -t $TMP_TEMPLATE) + + grep "$DEFCONFIG_FILTER" "$DEFCONFIG_MAP_FILE" | grep "^classification:" | awk '{print$4}' > "$TEMP_BUILD_FILE" + + max_configs=0 + while true; + do + CONFIG_FILE= + CONFIG_FRAGMENTS= + + BT_TEMP=$(head -n 1 "$TEMP_BUILD_FILE") + if [ -z "$BT_TEMP" ]; then + break + fi + BUILD_DETAILS=$(grep -w "$BT_TEMP" "$DEFCONFIG_MAP_FILE") + check_for_config_existance + sed -i "1d" "$TEMP_BUILD_FILE" + done + + NUM_OF_BUILDS=$(wc -l "$BUILD_TYPE_FILE" | awk '{print$1}') + if [ "$NUM_OF_BUILDS" -eq 0 ]; then + echo "Sorry no build targets for this configuration. Are you on the right branch?" + prepare_for_exit + fi + + # Force the user to answer. Maybe the user does not want to continue + while true; + do + echo -e "Available ""$DEFCONFIG_FILTER"" defconfig build options:\n" + cat "$BUILD_TYPE_FILE" + echo "" + read -p "Please enter the number of the defconfig to build or 'q' to exit: " REPLY + + if [ "$REPLY" = "q" -o "$REPLY" = "Q" ]; then + prepare_for_exit + elif ! [[ "$REPLY" =~ ^[0-9]+$ ]]; then + echo -e "\n$REPLY is not a number of the defconfig. Please try again!\n" + continue + elif [ "$REPLY" -gt '0' -a "$REPLY" -le "$NUM_OF_BUILDS" ]; then + CHOSEN_BUILD_TYPE=$(grep -w "$REPLY" "$BUILD_TYPE_FILE" | awk '{print$2}') + break + else + echo -e "\n'$REPLY' is not a valid choice. Please \ +choose a value between '1' and '$max_configs':\n" + fi + done + rm "$TEMP_BT_FILE" + rm "$TEMP_BUILD_FILE" +} + +list_all_targets() { + + TMP_MAP=$(mktemp -t $TMP_TEMPLATE) + + count=0 + max_types=0 + while [ "x${SUPPORTED_ARCH[max_types]}" != "x" ] + do + DEFCONFIG_MAP_FILE=${SUPPORTED_ARCH[(count * 3) + 2]} + count=$(( $count + 1 )) + max_types=$(( $max_types + 3 )) + if [ ! -e "$DEFCONFIG_MAP_FILE" ]; then + continue + fi + + cat "$DEFCONFIG_MAP_FILE" > "$TMP_MAP" + while true; + do + CONFIG_FILE= + CONFIG_FRAGMENTS= + + BT_TEMP=$(head -n 1 "$TMP_MAP" | awk '{print$4}') + BUILD_DETAILS=$(head -n 1 "$TMP_MAP") + if [ -z "$BUILD_DETAILS" ]; then + break + fi + check_for_config_existance + sed -i "1d" "$TMP_MAP" + done + rm "$TMP_MAP" + done + cat "$BUILD_TYPE_FILE" +} + +get_build_details() { + count=0 + max_types=0 + while [ "x${SUPPORTED_ARCH[max_types]}" != "x" ] + do + DEFCONFIG_MAP_FILE=${SUPPORTED_ARCH[(count * 3) + 2]} + if [ -e "$DEFCONFIG_MAP_FILE" ]; then + BUILD_DETAILS=$(grep -w "$CHOSEN_BUILD_TYPE" "$DEFCONFIG_MAP_FILE") + if [ ! -z "$BUILD_DETAILS" ]; then + if [ -z ${DEFCONFIG_KERNEL_PATH} ]; then + DEFCONFIG_KERNEL_PATH=${SUPPORTED_ARCH[(count * 3) + 1]} + fi + break + fi + fi + + count=$(( $count + 1 )) + max_types=$(( $max_types + 3 )) + done + + if [ -z "$BUILD_DETAILS" ]; then + echo "Cannot find the build type or a match for $CHOSEN_BUILD_TYPE" + TEMP_BUILD_FILE=$(mktemp -t $TMP_TEMPLATE) + grep "$CHOSEN_BUILD_TYPE" "$DEFCONFIG_MAP_FILE" > "$TEMP_BUILD_FILE" + while true; + do + CONFIG_FILE= + CONFIG_FRAGMENTS= + + BT_TEMP=$(head -n 1 "$TEMP_BUILD_FILE" | awk '{print$4}') + if [ -z "$BT_TEMP" ]; then + break + fi + BUILD_DETAILS=$(grep -w "$BT_TEMP" "$DEFCONFIG_MAP_FILE") + check_for_config_existance + sed -i "1d" "$TEMP_BUILD_FILE" + done + rm -rf "$TEMP_BUILD_FILE" + + NUM_OF_BUILDS=$(wc -l "$BUILD_TYPE_FILE" | awk '{print$1}') + if [ "$NUM_OF_BUILDS" -eq 0 ]; then + echo "Maybe try one of the following:" + list_all_targets + else + echo "Did you mean any of the following?" + cat "$BUILD_TYPE_FILE" + fi + + return 1 + fi + + DEFCONFIG=$(echo "$BUILD_DETAILS" | awk '{print$6}') + DEFCONFIG="$DEFCONFIG_KERNEL_PATH""/""$DEFCONFIG" + CONFIG_FILE=$(echo "$BUILD_DETAILS" | awk '{print$8}') + # There may be a need to just build with the config fragments themselves + if [ "$CONFIG_FILE" = "None" ]; then + CONFIG_FILE= + fi + + if [ ! -e "$SCRIPT_PATH/$CONFIG_FILE" ]; then + echo "$SCRIPT_PATH/$CONFIG_FILE does not exist" + return 1 + fi + + TEMP_EXTRA_CONFIG_FILE=$(echo "$BUILD_DETAILS" | cut -d: -f6) + for CONFIG_FRAGMENT_FILE in $TEMP_EXTRA_CONFIG_FILE; + do + # If we do already point to existing file, we are good. + if [ -e "$CONFIG_FRAGMENT_FILE" ]; then + CONFIG_FRAG="$CONFIG_FRAGMENT_FILE" + else + # Assume it is present in TI working path + CONFIG_FRAG="$SCRIPT_PATH/$CONFIG_FRAGMENT_FILE" + fi + if [ -e "$CONFIG_FRAG" ]; then + EXTRA_CONFIG_FILE="$EXTRA_CONFIG_FILE $CONFIG_FRAG" + else + echo "$CONFIG_FRAG" does not exist + fi + done +} + +build_defconfig() { + + if [ ! -z "$CONFIG_FILE" -a -e "$SCRIPT_PATH/$CONFIG_FILE" ]; then + CONFIGS=$(grep "$CONFIG_FRAGMENT_TAG" "$SCRIPT_PATH/$CONFIG_FILE" | cut -d= -f2) + fi + + "$KERNEL_PATH"/scripts/kconfig/merge_config.sh -m -r "$DEFCONFIG" \ + "$CONFIGS" "$EXTRA_CONFIG_FILE" > /dev/null + + if [ "$?" = "0" ];then + if [ -z ${DEFCONFIG_OUT} ]; then + echo "Creating defconfig file ""$DEFCONFIG_KERNEL_PATH/""$CHOSEN_BUILD_TYPE"_defconfig + mv .config "$DEFCONFIG_KERNEL_PATH"/"$CHOSEN_BUILD_TYPE"_defconfig + else + if [ ! -d ${DEFCONFIG_OUT} ]; then + mkdir -p ${DEFCONFIG_OUT} + fi + echo "Creating defconfig file ""$DEFCONFIG_OUT/""$CHOSEN_BUILD_TYPE"_defconfig + mv .config ${DEFCONFIG_OUT}/${CHOSEN_BUILD_TYPE}_defconfig + fi + else + echo "Defconfig creation failed" + return 1 + fi +} + +choose_defconfig_type() { + + TEMP_TYPE_FILE=$(mktemp -t $TMP_TEMPLATE) + + TYPE_FILE=$(awk '{print$2}' "$DEFCONFIG_MAP_FILE" | sort -u | grep -i ${TYPE}) + + max_types=0 + for TYPE_TMP in $TYPE_FILE; + do + max_types=$((max_types+1)) + echo -e '\t' "$max_types." "$TYPE_TMP" >> "$TEMP_TYPE_FILE" + done + echo >> "$TEMP_TYPE_FILE" + + while true; + do + cat "$TEMP_TYPE_FILE" + read -p "Please choose a defconfig type to build for or 'q' to exit: " REPLY + if [ "$REPLY" = "q" -o "$REPLY" = "Q" ]; then + prepare_for_exit + elif ! [[ "$REPLY" =~ ^[0-9]+$ ]]; then + echo -e "\n'$REPLY' is not a number for the build type. Please try again!\n" + continue + elif [ "$REPLY" -gt '0' -a "$REPLY" -le "$max_types" ]; then + REPLY="$REPLY""." + DEFCONFIG_FILTER=$(awk '{if ($1 == "'"$REPLY"'") print $2;}' "$TEMP_TYPE_FILE") + break + else + echo -e "\n'$REPLY' is not a valid choice. Please \ +choose a value between '1' and '$max_types':\n" + fi + done + + DEBUG_BUILD=$(grep "$DEFCONFIG_FILTER" "$DEFCONFIG_MAP_FILE" | grep -wc "$DEBUG_CONFIG_TAG" ) + if [ "$DEBUG_BUILD" -gt '0' ]; then + echo -e "$DISCLAIMER" + fi +} + +choose_architecture() { + + TEMP_ARCH_FILE=$(mktemp -t $TMP_TEMPLATE) + + max_types=0 + count=0 + while [ "x${SUPPORTED_ARCH[max_types]}" != "x" ] + do + ARCH_TYPE=${SUPPORTED_ARCH[max_types]} + if [ -e "${SUPPORTED_ARCH[max_types + 2]}" ]; then + count=$(( $count + 1 )) + echo -e '\t' "$count." "$ARCH_TYPE" >> "$TEMP_ARCH_FILE" + fi + max_types=$(( $max_types + 3 )) + done + + if [ "$count" -eq 1 ]; then + REPLY=$count + else + while true; + do + cat "$TEMP_ARCH_FILE" + read -p "Please choose an architecture to build for or 'q' to exit: " REPLY + if [ "$REPLY" = "q" -o "$REPLY" = "Q" ]; then + prepare_for_exit + elif ! [[ "$REPLY" =~ ^[0-9]+$ ]]; then + echo -e "\n'$REPLY' is not a number for the build type. Please try again!\n" + continue + elif [ "$REPLY" -gt '0' -a "$REPLY" -le "$max_types" ]; then + REPLY_DISP="$REPLY""." + ARCH_TO_BUILD=$(awk '{if ($1 == "'"$REPLY_DISP"'") print $2;}' "$TEMP_ARCH_FILE") + # System test configs are specific and contain + # the v7 and v8 tags so we have to do something + # special. + if [ ${ARCH_TO_BUILD} == "System" ]; then + SYSTEM_TEST_ARCH=$(awk '{if ($1 == "'"$REPLY_DISP"'") print $4;}' "$TEMP_ARCH_FILE") + ARCH_TEST="System Test "${SYSTEM_TEST_ARCH} + else + ARCH_TEST=${ARCH_TO_BUILD} + fi + break + else + echo -e "\n'$REPLY' is not a valid choice. Please \ + choose a value between '1' and '$max_types':\n" + fi + done + fi + + max_types=0 + while [ "x${SUPPORTED_ARCH[max_types]}" != "x" ] + do + ARCH_TYPE=${SUPPORTED_ARCH[max_types]} + ARCH_COUNTER=$(grep -c "$ARCH_TEST" <<< $ARCH_TYPE) + if [ "$ARCH_COUNTER" -gt 0 ]; then + break + fi + max_types=$(( $max_types + 3 )) + done + + DEFCONFIG_KERNEL_PATH=${SUPPORTED_ARCH[max_types + 1]} + DEFCONFIG_MAP_FILE=${SUPPORTED_ARCH[max_types + 2]} +} + +usage() { +cat << EOF + +This script will perform a merge of config fragment files into a defconfig +based on a map file. The map file defines the defconfig options that have +been tested by TI to boot and compile. + +Optional: + -k - Location of the Linux kernel source + -w - Same as -k (deprecated) + -t - Indicates the type of defconfig to build. This will force the + defconfig to build without user interaction. + -l - List all buildable defconfig options + -o - Outputs the built defconfigs to a different directory. + +Command line example to generate the TI SDK AM335x processor defconfig automatically +without user interaction: + + ti_config_fragments/defconfig_builder.sh -t ti_sdk_am3x_release + +Command line Example if building from the ti_config_fragments directory: + defconfig_builder.sh -w ../. + +User interactive command line example: + ti_config_fragments/defconfig_builder.sh +EOF +} + +######################################### +# Script Start +######################################### +while getopts "?k:w:t:o:l" OPTION +do + case $OPTION in + k|w) + KERNEL_PATH=$OPTARG;; + t) + CHOSEN_BUILD_TYPE=$OPTARG;; + l) + LIST_TARGETS="y";; + o) + DEFCONFIG_OUT=$OPTARG;; + ?) + usage + exit;; + esac +done + +trap prepare_for_exit SIGHUP EXIT SIGINT SIGTERM + +set_working_directory + +SUPPORTED_ARCH=( +"v7 ARM Architecture" "$KERNEL_PATH/arch/arm/configs" "$SCRIPT_PATH/defconfig_map.txt" +"v8 ARM Architecture" "$KERNEL_PATH/arch/arm64/configs" "$SCRIPT_PATH/v8_defconfig_map.txt" +"System Test v7 Builds" "$KERNEL_PATH/arch/arm/configs" "$SCRIPT_PATH/system_test/system_test_map.txt" +"System Test v8 Builds" "$KERNEL_PATH/arch/arm64/configs" "$SCRIPT_PATH/system_test/v8_system_test_map.txt") + +BUILD_TYPE_FILE=$(mktemp -t $TMP_TEMPLATE) + +if [ ! -z "$LIST_TARGETS" ]; then + echo "The following are a list of buildable defconfigs:" + list_all_targets + exit 0 +fi + + +PROCESSOR_FILE=$(mktemp -t $TMP_TEMPLATE) +OLD_CONFIG=$(mktemp -t $TMP_TEMPLATE) +if [ -f "$KERNEL_PATH"/.config ]; then + mv "$KERNEL_PATH"/.config "$OLD_CONFIG" +fi + +if [ ! -z "$CHOSEN_BUILD_TYPE" ]; then + get_build_details + if [ "$?" -gt 0 ]; then + exit 1 + fi + + build_defconfig + if [ "$?" -gt 0 ]; then + exit 1 + fi + exit 0 +fi + +choose_architecture + +if [ ! -e "$DEFCONFIG_MAP_FILE" ]; then + echo "No defconfig map file found" + exit 1 +fi + +if [ ${ARCH_TO_BUILD} == "System" ]; then + TYPE="System_" +else + TYPE="SDK_" + choose_defconfig_type +fi + +choose_build_type +get_build_details + +build_defconfig diff -Naur --no-dereference a/ti_config_fragments/defconfig_map.txt b/ti_config_fragments/defconfig_map.txt --- a/ti_config_fragments/defconfig_map.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/defconfig_map.txt 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,34 @@ +# Release Defconfigs +classification: SDK_Release_Defconfigs type: ti_sdk_am3x_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am33xx_only.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_am4x_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am43xx_only.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_dra7x_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg lpae.cfg dra7_only.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_omap2_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_k2g_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg k2g_only.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_keystone_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_omapl138_release defconfig: davinci_all_defconfig config_file: None extra_configs: baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg systemd.cfg +# Debug Defconfigs +classification: SDK_Debug_Defconfigs type: ti_sdk_am3x_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am33xx_only.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_am4x_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am43xx_only.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_dra7x_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg lpae.cfg dra7_only.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_omap2_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_k2g_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg k2g_only.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_keystone_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_omapl138_debug defconfig: davinci_all_defconfig config_file: None extra_configs: baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg systemd.cfg debug_options.cfg +# RT Defconfigs +classification: SDK_Release_Defconfigs type: ti_sdk_am3x_rt_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am33xx_only.cfg real_time.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_am4x_rt_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am43xx_only.cfg real_time.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_dra7x_rt_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg lpae.cfg dra7_only.cfg real_time.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_omap2_rt_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg real_time.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_k2g_rt_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg k2g_only.cfg real_time.cfg systemd.cfg +classification: SDK_Release_Defconfigs type: ti_sdk_keystone_rt_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg real_time.cfg systemd.cfg +# RT Debug Defconfigs +classification: SDK_Debug_Defconfigs type: ti_sdk_am3x_rt_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am33xx_only.cfg real_time.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_am4x_rt_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am43xx_only.cfg real_time.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_dra7x_rt_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg lpae.cfg dra7_only.cfg real_time.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_omap2_rt_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg real_time.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_k2g_rt_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg k2g_only.cfg real_time.cfg systemd.cfg debug_options.cfg +classification: SDK_Debug_Defconfigs type: ti_sdk_keystone_rt_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg keystone.cfg lpae.cfg real_time.cfg systemd.cfg debug_options.cfg +# Android Defconfigs +classification: SDK_Release_Defconfigs type: ti_sdk_am57x_android_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg lpae.cfg dra7_only.cfg systemd.cfg ../kernel/configs/android-base.config ../kernel/configs/android-recommended.config android.cfg +# Android Debug Defconfigs +classification: SDK_Debug_Defconfigs type: ti_sdk_am57x_android_debug defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg lpae.cfg dra7_only.cfg systemd.cfg debug_options.cfg ../kernel/configs/android-base.config ../kernel/configs/android-recommended.config android.cfg diff -Naur --no-dereference a/ti_config_fragments/dra7_only.cfg b/ti_config_fragments/dra7_only.cfg --- a/ti_config_fragments/dra7_only.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/dra7_only.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,25 @@ +################################################## +# TI DRA7 specific config options +################################################## + +# Supported ARM CPUs +CONFIG_ARCH_MULTI_V6=n +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=n +CONFIG_CPU_V6=n + +# Enable CONFIG_SMP +CONFIG_SMP=y + +# Disable Socs other than DRA7 +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_SOC_OMAP5=n +CONFIG_SOC_AM33XX=n +CONFIG_SOC_AM43XX=n +CONFIG_SOC_DRA7XX=y + +CONFIG_ARCH_KEYSTONE=n + +CONFIG_ARM_LPAE=y diff -Naur --no-dereference a/ti_config_fragments/ipc.cfg b/ti_config_fragments/ipc.cfg --- a/ti_config_fragments/ipc.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/ipc.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,39 @@ +################################################## +# TI RPMsg/IPC Config Options +################################################## +# HwSpinLock +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_OMAP=y + +# Mailbox +CONFIG_MAILBOX=y +CONFIG_OMAP2PLUS_MBOX=y + +# SoC +CONFIG_TI_PRUSS=m + +# IrqChip Drivers +CONFIG_TI_PRUSS_INTC=m + +# IOMMU +CONFIG_IOMMU_SUPPORT=y +CONFIG_OMAP_IOMMU=y +CONFIG_OMAP_IOMMU_DEBUG=y + +# Remoteproc +CONFIG_OMAP_REMOTEPROC=m +CONFIG_OMAP_REMOTEPROC_WATCHDOG=y +CONFIG_PRU_REMOTEPROC=m +CONFIG_KEYSTONE_REMOTEPROC=m + +# RPMsg +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_VIRTIO=m +CONFIG_RPMSG_PROTO=m +CONFIG_RPMSG_PRU=m + +# DSP Memory Mapper for Keystone MPM +CONFIG_KEYSTONE_DSP_MEM=m + +# UIO Module +CONFIG_UIO=m diff -Naur --no-dereference a/ti_config_fragments/k2g_only.cfg b/ti_config_fragments/k2g_only.cfg --- a/ti_config_fragments/k2g_only.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/k2g_only.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,11 @@ +################################################## +# TI K2G (UP) specific config options +################################################## + +# HACK: Enable SMP for working around Coherency problems +CONFIG_SMP=y +CONFIG_CPUSETS=n +CONFIG_POWER_SUPPLY=n + +# Increase CMA for HDMI +CONFIG_CMA_SIZE_MBYTES=48 diff -Naur --no-dereference a/ti_config_fragments/k3_soc.cfg b/ti_config_fragments/k3_soc.cfg --- a/ti_config_fragments/k3_soc.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/k3_soc.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,3 @@ +################################################## +# TI Base K3 SoCs +################################################## diff -Naur --no-dereference a/ti_config_fragments/keystone.cfg b/ti_config_fragments/keystone.cfg --- a/ti_config_fragments/keystone.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/keystone.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,29 @@ +################################################## +# TI Base Keystone generation SoCs generic default +################################################## + +# EDAC +CONFIG_RAS=y +CONFIG_EDAC=y +CONFIG_EDAC_TI=m + +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_KEYSTONE=y +CONFIG_KEYSTONE2_DMA_COHERENT=y +CONFIG_ARCH_OMAP=n +CONFIG_ARCH_OMAP2PLUS=n +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_SOC_OMAP5=n +CONFIG_SOC_AM33XX=n +CONFIG_SOC_AM43XX=n +CONFIG_SOC_DRA7XX=n +CONFIG_ARM_PSCI=y +CONFIG_POWER_RESET_KEYSTONE=y + +# Maximum Number of processors +CONFIG_NR_CPUS=8 + +CONFIG_HIGHMEM=y diff -Naur --no-dereference a/ti_config_fragments/lpae.cfg b/ti_config_fragments/lpae.cfg --- a/ti_config_fragments/lpae.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/lpae.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1 @@ +CONFIG_ARM_LPAE=y diff -Naur --no-dereference a/ti_config_fragments/multimedia.cfg b/ti_config_fragments/multimedia.cfg --- a/ti_config_fragments/multimedia.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/multimedia.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,10 @@ +################################################## +# TI Multimedia (Video Decode, Encode) Configs +################################################## +# IMG D5500 v4l2 Driver Config Options + +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_IMG_VXD_DEC=m + +# IMG VXE384 v4l2 Driver Config Options +CONFIG_VIDEO_IMG_VXE_ENC=m diff -Naur --no-dereference a/ti_config_fragments/multi_v7_prune.cfg b/ti_config_fragments/multi_v7_prune.cfg --- a/ti_config_fragments/multi_v7_prune.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/multi_v7_prune.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,65 @@ +# Add config flags here that only appear in the multi_v7_defconfig +CONFIG_ARCH_ALPINE=n +CONFIG_ARCH_ARTPEC=n +CONFIG_ARCH_ASPEED=n +CONFIG_ARCH_AT91=n +CONFIG_ARCH_BCM=n +CONFIG_ARCH_BCM_CYGNUS=n +CONFIG_ARCH_BCM_NSP=n +CONFIG_ARCH_BCM_5301X=n +CONFIG_ARCH_BCM_281XX=n +CONFIG_ARCH_BCM_21664=n +CONFIG_ARCH_BRCMSTB=n +CONFIG_ARCH_BERLIN=n +CONFIG_ARCH_DIGICOLOR=n +CONFIG_ARCH_HIGHBANK=n +CONFIG_ARCH_HISI=n +CONFIG_ARCH_HI3xxx=n +CONFIG_ARCH_HIP01=n +CONFIG_ARCH_HIP04=n +CONFIG_ARCH_HIX5HD2=n +CONFIG_ARCH_MESON=n +CONFIG_ARCH_MXC=n +CONFIG_ARCH_MEDIATEK=n +CONFIG_ARCH_MILBEAUT=n +CONFIG_ARCH_QCOM=n +CONFIG_ARCH_MMP=n +CONFIG_ARCH_MSM8X60=n +CONFIG_ARCH_MSM8960=n +CONFIG_ARCH_MSM8974=n +CONFIG_ARCH_ROCKCHIP=n +CONFIG_ARCH_SOCFPGA=n +CONFIG_ARCH_SPEAR13XX=n +CONFIG_ARCH_STI=n +CONFIG_ARCH_EXYNOS=n +CONFIG_ARCH_SHMOBILE_MULTI=n +CONFIG_ARCH_EMEV2=n +CONFIG_ARCH_R7S72100=n +CONFIG_ARCH_R8A73A4=n +CONFIG_ARCH_R8A7740=n +CONFIG_ARCH_R8A7778=n +CONFIG_ARCH_R8A7779=n +CONFIG_ARCH_R8A7790=n +CONFIG_ARCH_R8A7791=n +CONFIG_ARCH_R8A7793=n +CONFIG_ARCH_R8A7794=n +CONFIG_ARCH_RENESAS=n +CONFIG_ARCH_SH73A0=n +CONFIG_ARCH_SUNXI=n +CONFIG_ARCH_STM32=n +CONFIG_ARCH_SIRF=n +CONFIG_ARCH_TEGRA=n +CONFIG_ARCH_TEGRA_2x_SOC=n +CONFIG_ARCH_TEGRA_3x_SOC=n +CONFIG_ARCH_TEGRA_114_SOC=n +CONFIG_ARCH_TEGRA_124_SOC=n +CONFIG_ARCH_UNIPHIER=n +CONFIG_ARCH_U8500=n +CONFIG_ARCH_VEXPRESS=n +CONFIG_ARCH_VEXPRESS_TC2_PM=n +CONFIG_ARCH_WM8850=n +CONFIG_ARCH_ZYNQ=n +CONFIG_ARCH_VIRT=n +CONFIG_ARCH_MVEBU=n +CONFIG_PLAT_SPEAR=n +CONFIG_CHROME_PLATFORMS=n diff -Naur --no-dereference a/ti_config_fragments/omap5_soc.cfg b/ti_config_fragments/omap5_soc.cfg --- a/ti_config_fragments/omap5_soc.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/omap5_soc.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,14 @@ +################################################## +# TI OMAP5 EVM specific config options +################################################## + +#ETHERNET +CONFIG_USB_USBNET=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_NET_SMSC95XX=y + +#USB HOST +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_PCI=y +CONFIG_USB_EHCI_HCD_OMAP=y diff -Naur --no-dereference a/ti_config_fragments/omap_soc.cfg b/ti_config_fragments/omap_soc.cfg --- a/ti_config_fragments/omap_soc.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/omap_soc.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,31 @@ +################################################## +# TI Base OMAP generation SoCs generic default +################################################## + +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_KEYSTONE=n +CONFIG_KEYSTONE2_DMA_COHERENT=n +CONFIG_ARCH_OMAP=y + +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_SOC_OMAP5=y +CONFIG_SOC_AM33XX=y +CONFIG_SOC_AM43XX=y +CONFIG_SOC_DRA7XX=y +CONFIG_ARCH_OMAP2PLUS=y +CONFIG_OMAP_INTERCONNECT_BARRIER=y +CONFIG_ARM_PSCI=n + +# Maximum Number of processors +CONFIG_NR_CPUS=2 + +# Kexec +CONFIG_KEXEC=y + +# EDAC +CONFIG_RAS=y +CONFIG_EDAC=y +CONFIG_EDAC_TI=m diff -Naur --no-dereference a/ti_config_fragments/README b/ti_config_fragments/README --- a/ti_config_fragments/README 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/README 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,96 @@ +################################## +# Introduction +################################## + +Using config fragments in the Linux kernel is a supported model in the +Linux kernel mainline. TI has taken advantage of this model to be able +to take a base defconfig and add or remove configuration flags contained +within a config fragment. This allows the base defconfig to be left intact +and helps facilitate and understanding of what config flags can be enabled +or disabled. The config fragments drastically reduces the number of merge +conflicts when the Linux stable dot release (LTS) is merged back to the +integration point as well as merges for TI domain trees. + +But alas this creates another issue as with the config fragments there is no +direct defconfig for customers to build just many, many, many options. + +Therefore the defconfig_builder script was derived to take a defconfig map file +and present a command line interface to the customer to to create a standard +defconfig based off the TI integration branches. The customer is walked through +steps and if successful a defconfig will be created in the arch/arm/configs +directory for ARM based builds and arch/arm64/configs directory for 64-bit ARM. + +################################## +# Requirements +################################## + +* awk v1.2 or greater. +* bash shell 4.3.11(1)-release or greater. +* Coreutils package v8.1 or greater. +* grep (GNU grep) v2.16 or greater. +* Linux kernel with merge_config script. +* sed (GNU sed) v4.2.2 or greater. + +################################## +# Usage +################################## + +There is only one option for this script. This option defines the working +path to where the Linux kernel resides. + + -w - Location of the TI Linux kernel + -t - Indicates the type of defconfig to build. This will force the + defconfig to build without user interaction. + -l - List all buildable defconfig options + +Command line example to generate the TI SDK AM335x processor defconfig +automatically without user interaction: + + ti_config_fragments/defconfig_builder.sh -t ti_sdk_am3x_release + +Command line Example if building from the ti_config_fragments directory: + defconfig_builder.sh -w ../. + +User interactive command line example: + +################################## +# Defconfig Map File +################################## + +The defconfig map file contains the mapping of the defconfig options and the +assembly instructions for the defconfig to be created. + +The file contains keywords and all are *required* to be filled out. + +classification: - Defines the class of the defconfig which can be Release, + Debug, System Test. +type: - Defines the type of build. Like debug build, LSK build or RT build +defconfig: - Defines the base defconfig config to append the config fragments to +config_file: - Defines a file that contains a list of config fragments. + If there is not a list then declare "None" for this field. +extra_configs: - These are individual config fragments that can be appended to + the defconfig by themselves or in addition to the config_file. + +A Note on ordering of "extra_configs": + +In general, config fragments should be listed such that the fragment +with more restrictive options goes towards the end of the list. This +makes sure that those options are not inadvertently overridden by more +generically applicable options. More concretely, the following ordering +is recommended: + +1) Pruning of non-TI features +2) Introduce domain specific features +3) Optimize for an SoC family +4) Optimize for a particular SoC +5) Introduce distribution specific features. (example: Android) + +Example: + +This is an example of an AM335x entry that will create a defconfig only for +AM335x processors, based on the multi_v7_defconfig. The defconfig_fragment file +adds features that have been enabled in the TI integrated kernel. The +extra_configs will add in the debug options as well as undefine all processors +that are not AM335x. + +classification: SDK_release type: ti_sdk_am3x_release defconfig: multi_v7_defconfig config_file: None extra_configs: multi_v7_prune.cfg baseport.cfg ipc.cfg connectivity.cfg audio_display.cfg omap_soc.cfg am33xx_only.cfg systemd.cfg diff -Naur --no-dereference a/ti_config_fragments/real_time.cfg b/ti_config_fragments/real_time.cfg --- a/ti_config_fragments/real_time.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/real_time.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,39 @@ +################################################## +# Real Time Linux Configs +################################################## + +CONFIG_PREEMPT=y +CONFIG_PREEMPT_RT=y + +#Disable PM features +CONFIG_CPU_IDLE=n +CONFIG_CPU_FREQ=n +CONFIG_SUSPEND=n +CONFIG_HOTPLUG_CPU=y + +# SCHED_DEBUG provides a lot of additional options for scheduler +# tuning in /proc/sys/kernel/ and allows to see scheduler statistic in +# /proc/sched_debug +# CONFIG_SCHED_DEBUG + +# --- RCU optimization options to reduce OS jitter +# RCU_BOOST : This option boosts the priority of preempted RCU readers that +# block the current preemptible RCU grace period for too long. +# This option also prevents heavy loads from blocking RCU +# callback invocation for all flavors of RCU +# CONFIG_RCU_BOOST=n + +# RCU_NOCB_CPU : Use this option to reduce OS jitter for aggressive HPC or +# real-time workloads. It can also be used to offload RCU +# callback invocation to energy-efficient CPUs in battery-powered +# asymmetric multiprocessors. +# CONFIG_RCU_NOCB_CPU=y + +# RCU_NOCB_CPU_ALL: his option forces all CPUs to be no-CBs CPUs. +# The rcu_nocbs= boot parameter will be ignored. All CPUs' RCU callbacks will +# be executed in the context of per-CPU rcuo kthreads created for +# this purpose. +# CONFIG_RCU_NOCB_CPU_ALL=y + +# disable 64K pages as they proved to be source of RT latencies +CONFIG_ARM64_64K_PAGES=n diff -Naur --no-dereference a/ti_config_fragments/systemd.cfg b/ti_config_fragments/systemd.cfg --- a/ti_config_fragments/systemd.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/systemd.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,88 @@ +############################################## +# +# Kernel options needed for systemd enabled TI SDKs +# See https://cgit.freedesktop.org/systemd/systemd/tree/README#n38 for details +# +############################################## +CONFIG_TMPFS=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_PERF=y + +CONFIG_INOTIFY_USER=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EPOLL=y +CONFIG_NET=y +CONFIG_SYSFS=y +CONFIG_PROC_FS=y +CONFIG_FHANDLE=y + +# udev will fail to work with the legacy sysfs layout: +CONFIG_SYSFS_DEPRECATED=n + +# Legacy hotplug slows down the system and confuses udev: +CONFIG_UEVENT_HELPER_PATH="" + +# Userspace firmware loading is not supported and should +# be disabled in the kernel: +CONFIG_FW_LOADER_USER_HELPER=n + +# Some udev rules and virtualization detection relies on it: +# Only for UEFI based systems +# http://cateee.net/lkddb/web-lkddb/DMI.html +CONFIG_DMIID=n + +# Support for some SCSI devices serial number retrieval, to +# create additional symlinks in /dev/disk/ and /dev/tape: +CONFIG_BLK_DEV_BSG=y + +# Required for PrivateNetwork and PrivateDevices in service units: +# Note that systemd-localed.service and other systemd units use +# PrivateNetwork and PrivateDevices so this is effectively required. +CONFIG_NAMESPACES=y +CONFIG_NET_NS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y + +# Optional but strongly recommended options: those are nice to have and +# indeed recommended, but not necessarily systemd required. These to be +# enabled in corresponding domain fragments since they are not specific +# to supporting systemd. + +CONFIG_SECCOMP=y +# for kcmp syscall +CONFIG_CHECKPOINT_RESTORE=y + +# Required for CPUShares= in resource control unit settings +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y + +# Required for CPUQuota= in resource control unit settings +CONFIG_CFS_BANDWIDTH=y + +# For systemd-bootchart, several proc debug interfaces are required: +# Systemd-debug.cfg? +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_DEBUG=y + +# We recommend to turn off Real-Time group scheduling in the +# kernel when using systemd. RT group scheduling effectively +# makes RT scheduling unavailable for most userspace, since it +# requires explicit assignment of RT budgets to each unit whose +# processes making use of RT. As there's no sensible way to +# assign these budgets automatically this cannot really be +# fixed, and it's best to disable group scheduling hence. +CONFIG_RT_GROUP_SCHED=n + +# Note that kernel auditing is broken when used with systemd's +# container code. When using systemd in conjunction with +# containers, please make sure to either turn off auditing at +# runtime using the kernel command line option "audit=0", or +# turn it off at kernel compile time using: +CONFIG_AUDIT=n diff -Naur --no-dereference a/ti_config_fragments/v8_baseport.cfg b/ti_config_fragments/v8_baseport.cfg --- a/ti_config_fragments/v8_baseport.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/v8_baseport.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,170 @@ +################################################## +# TI Baseport Config Options +################################################## + +# ARM configuration +CONFIG_ARM64_4K_PAGES=n +CONFIG_ARM64_64K_PAGES=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_ARM64_VA_BITS=48 + +# Serial +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_8250_NR_UARTS=16 +CONFIG_SERIAL_8250_RUNTIME_UARTS=16 + +# K3 Power config options +CONFIG_MAILBOX=y +CONFIG_PM=y +CONFIG_TI_MESSAGE_MANAGER=y +CONFIG_TI_SCI_PROTOCOL=y +CONFIG_TI_SCI_PM_DOMAINS=y +CONFIG_TI_SCI_CLK=y + +# Enable Reset Controllers +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_TI_SYSCON=y +CONFIG_RESET_TI_SCI=y + +# Enable Interrupt Controllers +CONFIG_TI_SCI_INTR_IRQCHIP=y +CONFIG_TI_SCI_INTA_IRQCHIP=y + +# Enable options for NAVSS (DMA support) +CONFIG_TI_K3_RINGACC=y +CONFIG_TI_K3_UDMA=y +CONFIG_TI_K3_UDMA_GLUE_LAYER=y + +# OPTEE Driver +CONFIG_TEE=y +CONFIG_OPTEE=y + +#----- +# Generic Kernel Options +#----- + +# Enable process accounting +CONFIG_BSD_PROCESS_ACCT=y + +# Enable support for perf +CONFIG_PERF_EVENTS=y + +# Have some way to pick up kernel config later on +# Always useful to look at /proc/config.gz +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y + +# Add Bin2c +CONFIG_BUILD_BIN2C=y + +# Add base Cgroups functions +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=n +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_PAGE_COUNTER=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_SWAP_ENABLED=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +CONFIG_DEBUG_BLK_CGROUP=n +CONFIG_CGROUP_WRITEBACK=y + +# Choose CONFIG_EMBEDDED +CONFIG_NAMESPACES=n +CONFIG_EMBEDDED=y +CONFIG_EXPERT=y + +# Enable all kernel symbols please +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y + +# How do we want kernel Modules to work? +CONFIG_BASE_FULL=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_MODULE_SIG=n +CONFIG_MODULE_COMPRESS=n +CONFIG_MODULES_TREE_LOOKUP=y + +# Boot options +CONFIG_CMDLINE="" +CONFIG_CMDLINE_FORCE=n +CONFIG_CRASH_DUMP=n +CONFIG_AUTO_ZRELADDR=y + +# Mem allocator +CONFIG_SLAB=n +CONFIG_SLUB=y +CONFIG_SLUB_DEBUG=n + +# Enable EFI +# Sane configurations are enabled via CONFIG_EFI on ARM64 +CONFIG_EFI=y + +# Pinctrl +CONFIG_PINCTRL=y +CONFIG_PINCTRL_SINGLE=y + +# GPIO +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_DAVINCI=y + +# Crypto SA2UL +CONFIG_CRYPTO_DEV_SA2UL=m + +# Enable options to facilitate testing +CONFIG_CRYPTO_TEST=m + +# Pseudo filesystems +CONFIG_TMPFS=y + +# Thermal +CONFIG_K3_THERMAL=y + +# Disable extra debug options +CONFIG_PROFILING=n + +# Block Layer +CONFIG_BLK_DEV_RAM=y + +# PMIC +CONFIG_MFD_PALMAS=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_PALMAS=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y + +# Watchdog support +CONFIG_K3_RTI_WATCHDOG=m + +# UIO and overlay support for Jailhouse +CONFIG_UIO=y +CONFIG_OF_OVERLAY=y + +# DMA-BUF Heaps +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_CARVEOUT=y +CONFIG_SRAM_DMA_HEAP=y + +# DMA-BUF exporter +CONFIG_DMA_BUF_PHYS=y + +# TI PAT +CONFIG_TI_PAT=y diff -Naur --no-dereference a/ti_config_fragments/v8_defconfig_map.txt b/ti_config_fragments/v8_defconfig_map.txt --- a/ti_config_fragments/v8_defconfig_map.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/v8_defconfig_map.txt 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,12 @@ +# Release Defconfigs +classification: SDK_Release_Defconfigs type: ti_sdk_arm64_release defconfig: defconfig config_file: None extra_configs: arm64_prune.cfg v8_baseport.cfg v8_ipc.cfg connectivity.cfg audio_display.cfg multimedia.cfg k3_soc.cfg systemd.cfg +# Debug Defconfigs +classification: SDK_Debug_Defconfigs type: ti_sdk_arm64_debug defconfig: defconfig config_file: None extra_configs: arm64_prune.cfg v8_baseport.cfg v8_ipc.cfg connectivity.cfg audio_display.cfg multimedia.cfg k3_soc.cfg systemd.cfg debug_options.cfg +# RT Defconfigs +classification: SDK_Release_Defconfigs type: ti_sdk_arm64_rt_release defconfig: defconfig config_file: None extra_configs: arm64_prune.cfg v8_baseport.cfg v8_ipc.cfg connectivity.cfg audio_display.cfg multimedia.cfg k3_soc.cfg real_time.cfg systemd.cfg +# RT Debug Defconfigs +classification: SDK_Debug_Defconfigs type: ti_sdk_arm64_rt_debug defconfig: defconfig config_file: None extra_configs: arm64_prune.cfg v8_baseport.cfg v8_ipc.cfg connectivity.cfg audio_display.cfg k3_soc.cfg multimedia.cfg real_time.cfg systemd.cfg debug_options.cfg +# Android Defconfigs +classification: SDK_Release_Defconfigs type: ti_sdk_arm64_android_release defconfig: defconfig config_file: None extra_configs: arm64_prune.cfg v8_baseport.cfg v8_ipc.cfg connectivity.cfg audio_display.cfg k3_soc.cfg ../kernel/configs/android-base.config ../kernel/configs/android-recommended.config v8_android.cfg +# Android Debug Defconfigs +classification: SDK_Debug_Defconfigs type: ti_sdk_arm64_android_debug defconfig: defconfig config_file: None extra_configs: arm64_prune.cfg v8_baseport.cfg v8_ipc.cfg connectivity.cfg audio_display.cfg k3_soc.cfg debug_options.cfg ../kernel/configs/android-base.config ../kernel/configs/android-recommended.config v8_android.cfg diff -Naur --no-dereference a/ti_config_fragments/v8_ipc.cfg b/ti_config_fragments/v8_ipc.cfg --- a/ti_config_fragments/v8_ipc.cfg 1969-12-31 19:00:00.000000000 -0500 +++ b/ti_config_fragments/v8_ipc.cfg 2022-01-06 12:45:53.838318206 -0500 @@ -0,0 +1,30 @@ +################################################## +# TI RPMsg/IPC Config Options +################################################## +# HwSpinLock +CONFIG_HWSPINLOCK_OMAP=y + +# Mailbox +CONFIG_MAILBOX=y +CONFIG_OMAP2PLUS_MBOX=y + +# SoC Drivers +CONFIG_TI_PRUSS=m + +# IrqChip Drivers +CONFIG_TI_PRUSS_INTC=m + +# Remoteproc +CONFIG_TI_K3_R5_REMOTEPROC=m +CONFIG_TI_K3_DSP_REMOTEPROC=m +CONFIG_PRU_REMOTEPROC=m +CONFIG_TI_K3_M4_REMOTEPROC=m + +# RPMsg +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_VIRTIO=m +CONFIG_RPMSG_PRU=m + +# RPMsg Samples +CONFIG_SAMPLES=y +CONFIG_SAMPLE_RPMSG_CLIENT=m